use std::path::{Path, PathBuf};
pub mod codex_app;
pub mod pi;
mod slots;
pub use tapes_capture::gateway::{
GATEWAY_NONCE_ENV, GATEWAY_NONCE_HEADER, GATEWAY_PROVIDER_ROUTE_PREFIX,
GATEWAY_PROVIDER_ROUTES_ENV, GATEWAY_PROVIDER_ROUTES_ON, GATEWAY_SCHEMA_ENV, GATEWAY_URL_ENV,
nonce_matches, provider_route, split_provider_route,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PluginArtifact {
file_name: &'static str,
install_dir: &'static [&'static str],
superseded_file_names: &'static [&'static str],
contents: &'static str,
}
impl PluginArtifact {
#[must_use]
pub const fn file_name(&self) -> &'static str {
self.file_name
}
#[must_use]
pub const fn install_dir_components(&self) -> &'static [&'static str] {
self.install_dir
}
#[must_use]
pub const fn contents(&self) -> &'static str {
self.contents
}
#[must_use]
pub fn install_dir(&self, home: &Path) -> PathBuf {
self.install_dir
.iter()
.fold(home.to_path_buf(), |path, component| path.join(component))
}
#[must_use]
pub fn install_path(&self, home: &Path) -> PathBuf {
self.install_dir(home).join(self.file_name)
}
#[must_use]
pub const fn superseded_file_names(&self) -> &'static [&'static str] {
self.superseded_file_names
}
#[must_use]
pub fn superseded_paths(&self, home: &Path) -> Vec<PathBuf> {
let dir = self.install_dir(home);
self.superseded_file_names
.iter()
.map(|name| dir.join(name))
.collect()
}
fn staged_path(&self, dir: &Path) -> PathBuf {
dir.join(format!(".{}.{}.tmp", self.file_name, std::process::id()))
}
pub fn install(&self, home: &Path) -> std::io::Result<PathBuf> {
let dir = self.install_dir(home);
std::fs::create_dir_all(&dir)?;
let staged = self.staged_path(&dir);
std::fs::write(&staged, self.contents)?;
for superseded in self.superseded_paths(home) {
match std::fs::remove_file(&superseded) {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => {
drop(std::fs::remove_file(&staged));
return Err(error);
}
}
}
let path = dir.join(self.file_name);
if let Err(error) = std::fs::rename(&staged, &path) {
drop(std::fs::remove_file(&staged));
return Err(error);
}
Ok(path)
}
}
pub const PI_GATEWAY_EXTENSION: PluginArtifact = PluginArtifact {
file_name: "tapes-gateway.ts",
install_dir: &[".pi", "agent", "extensions"],
superseded_file_names: &["paper-gateway.ts"],
contents: include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/assets/pi/tapes-gateway.ts"
)),
};
pub(crate) const PI_ARTIFACTS: &[PluginArtifact] = &[PI_GATEWAY_EXTENSION];
pub const OPENCODE_GATEWAY_EXTENSION: PluginArtifact = PluginArtifact {
file_name: "tapes-gateway.ts",
install_dir: &[".config", "opencode", "plugins"],
superseded_file_names: &[],
contents: include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/assets/opencode/tapes-gateway.ts"
)),
};
pub(crate) const OPENCODE_ARTIFACTS: &[PluginArtifact] = &[OPENCODE_GATEWAY_EXTENSION];
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
use crate::harness::{PluginDelivery, REGISTRY};
use tapes_capture::envelope::{
HARNESS_ID_OPENCODE, HARNESS_ID_PI, X_TAPES_HARNESS_ID, X_TAPES_HARNESS_SESSION_ID,
};
fn all_artifacts() -> Vec<&'static PluginArtifact> {
REGISTRY
.iter()
.flat_map(|harness| harness.plugin_artifacts())
.collect()
}
#[test]
fn the_registry_reaches_at_least_one_artifact() {
assert!(
!all_artifacts().is_empty(),
"no harness in the registry declares a plugin artifact"
);
assert!(all_artifacts().contains(&&PI_GATEWAY_EXTENSION));
assert!(all_artifacts().contains(&&OPENCODE_GATEWAY_EXTENSION));
}
#[test]
fn no_artifact_path_component_can_leave_the_home_directory() {
for artifact in all_artifacts() {
let components = artifact
.install_dir_components()
.iter()
.chain(std::iter::once(&artifact.file_name()))
.chain(artifact.superseded_file_names())
.copied()
.collect::<Vec<_>>();
for component in components {
assert!(!component.is_empty(), "empty component in {artifact:?}");
assert!(
!component.contains('/') && !component.contains('\\'),
"{component:?} is a path, not a component"
);
assert!(
component != ".." && component != ".",
"{component:?} traverses"
);
}
}
}
#[test]
fn an_artifact_resolves_beneath_the_home_it_is_given() {
let home = Path::new("/home/u");
assert_eq!(
PI_GATEWAY_EXTENSION.install_path(home),
PathBuf::from("/home/u/.pi/agent/extensions/tapes-gateway.ts"),
);
assert_eq!(
PI_GATEWAY_EXTENSION.install_dir(home),
PathBuf::from("/home/u/.pi/agent/extensions"),
);
assert!(
PI_GATEWAY_EXTENSION
.install_path(Path::new("/tmp/t"))
.starts_with("/tmp/t"),
);
}
#[test]
fn every_client_installs_identical_bytes_to_one_path() {
let (first, second) = (tempfile::tempdir().unwrap(), tempfile::tempdir().unwrap());
let one = PI_GATEWAY_EXTENSION.install(first.path()).unwrap();
let two = PI_GATEWAY_EXTENSION.install(second.path()).unwrap();
assert_eq!(
one.strip_prefix(first.path()),
two.strip_prefix(second.path()),
"two installs disagree about where the pi extension goes"
);
assert_eq!(
std::fs::read_to_string(&one).unwrap(),
std::fs::read_to_string(&two).unwrap(),
"two installs wrote different bytes; a second reader can exist again"
);
assert_eq!(
std::fs::read_to_string(&one).unwrap(),
PI_GATEWAY_EXTENSION.contents(),
);
}
#[test]
fn installing_the_pi_extension_removes_a_superseded_branded_copy() {
let home = tempfile::tempdir().unwrap();
let dir = PI_GATEWAY_EXTENSION.install_dir(home.path());
std::fs::create_dir_all(&dir).unwrap();
let superseded = dir.join("paper-gateway.ts");
std::fs::write(&superseded, "// an older client's rendering\n").unwrap();
let installed = PI_GATEWAY_EXTENSION.install(home.path()).unwrap();
assert!(
!superseded.exists(),
"the superseded extension survived the install; pi would load both"
);
assert_eq!(
std::fs::read_to_string(&installed).unwrap(),
PI_GATEWAY_EXTENSION.contents(),
);
assert_eq!(
std::fs::read_dir(&dir).unwrap().count(),
1,
"installing removed or added something it was not asked to"
);
}
#[test]
fn a_superseded_copy_that_cannot_be_removed_leaves_nothing_installed() {
let home = tempfile::tempdir().unwrap();
let dir = PI_GATEWAY_EXTENSION.install_dir(home.path());
std::fs::create_dir_all(&dir).unwrap();
let superseded = dir.join("paper-gateway.ts");
std::fs::create_dir_all(&superseded).unwrap();
std::fs::write(superseded.join("occupant"), "unremovable\n").unwrap();
let error = PI_GATEWAY_EXTENSION.install(home.path()).unwrap_err();
assert_ne!(
error.kind(),
std::io::ErrorKind::NotFound,
"the blocker was not in place; the test proves nothing"
);
assert!(
!PI_GATEWAY_EXTENSION.install_path(home.path()).exists(),
"the install wrote its extension anyway, so pi would load two"
);
assert!(
superseded.exists(),
"the blocker vanished; the removal did not actually fail"
);
assert_eq!(
std::fs::read_dir(&dir).unwrap().count(),
1,
"a failed install left debris in the extension directory"
);
}
#[test]
fn the_staged_name_is_not_one_a_harness_loads() {
let dir = Path::new("/home/u/.pi/agent/extensions");
for artifact in all_artifacts() {
let staged = artifact.staged_path(dir);
let name = staged.file_name().unwrap().to_str().unwrap();
assert!(
!name.ends_with(".ts"),
"{name:?} would be auto-loaded as an extension mid-write"
);
assert!(
!name.ends_with(".js"),
"{name:?} would be auto-loaded as a plugin mid-write"
);
assert_ne!(
name,
artifact.file_name(),
"staging onto the destination is not staging at all"
);
assert_eq!(
staged.parent(),
Some(dir),
"the staged file must be a sibling of its destination"
);
}
}
#[test]
fn the_pi_artifact_names_the_branded_copy_an_upgrading_user_has() {
assert!(
PI_GATEWAY_EXTENSION
.superseded_file_names()
.contains(&"paper-gateway.ts"),
"nothing removes the file an older paper installed"
);
assert_eq!(
PI_GATEWAY_EXTENSION.superseded_paths(Path::new("/home/u")),
vec![PathBuf::from(
"/home/u/.pi/agent/extensions/paper-gateway.ts"
)],
);
}
#[test]
fn installing_creates_the_directory_and_tolerates_nothing_to_supersede() {
let home = tempfile::tempdir().unwrap();
let installed = PI_GATEWAY_EXTENSION.install(home.path()).unwrap();
assert_eq!(
std::fs::read_to_string(&installed).unwrap(),
PI_GATEWAY_EXTENSION.contents(),
);
}
#[test]
fn no_artifact_supersedes_the_file_it_installs() {
for artifact in all_artifacts() {
assert!(
!artifact
.superseded_file_names()
.contains(&artifact.file_name()),
"{} would delete itself on install",
artifact.file_name(),
);
}
}
#[test]
fn every_artifact_carries_its_bytes() {
for artifact in all_artifacts() {
assert!(
!artifact.contents().trim().is_empty(),
"{} is empty",
artifact.file_name(),
);
}
}
#[test]
fn no_artifact_carries_vendor_branding() {
for artifact in all_artifacts() {
let lowered = artifact.contents().to_ascii_lowercase();
for token in ["paper", "papercompute"] {
assert!(
!lowered.contains(token),
"{} mentions {token:?}; a crate-owned asset must be vendor-neutral",
artifact.file_name(),
);
}
}
}
#[test]
fn the_pi_extension_reads_the_gateway_environment_contract() {
let contents = PI_GATEWAY_EXTENSION.contents();
assert!(
contents.contains(&format!("const GATEWAY_URL_ENV = \"{GATEWAY_URL_ENV}\";")),
"the asset does not read {GATEWAY_URL_ENV}"
);
assert!(
contents.contains(&format!(
"const GATEWAY_SCHEMA_ENV = \"{GATEWAY_SCHEMA_ENV}\";"
)),
"the asset does not read {GATEWAY_SCHEMA_ENV}"
);
}
#[test]
fn the_pi_extension_echoes_the_capture_nonce_contract() {
let contents = PI_GATEWAY_EXTENSION.contents();
assert!(
contents.contains(&format!(
"const GATEWAY_NONCE_ENV = \"{GATEWAY_NONCE_ENV}\";"
)),
"the asset does not read {GATEWAY_NONCE_ENV}"
);
assert!(
contents.contains(GATEWAY_NONCE_HEADER),
"the asset does not echo the nonce in {GATEWAY_NONCE_HEADER}"
);
assert!(
contents.contains("process.env[GATEWAY_NONCE_ENV]"),
"the asset does not read the nonce from the environment"
);
assert!(
contents.contains("[GATEWAY_NONCE_HEADER]: nonce"),
"the asset does not place the nonce value under the header name"
);
}
#[test]
fn the_pi_extension_deletes_the_nonce_from_its_environment_at_load() {
let contents = PI_GATEWAY_EXTENSION.contents();
assert!(
contents.contains("delete process.env[GATEWAY_NONCE_ENV]"),
"the asset does not delete the nonce from its environment; \
shell-tool subprocesses would inherit the secret"
);
let read = contents
.find("process.env[GATEWAY_NONCE_ENV]")
.unwrap_or(usize::MAX);
let delete = contents
.find("delete process.env[GATEWAY_NONCE_ENV]")
.unwrap_or(0);
assert!(
read < delete,
"the asset must capture the nonce before deleting it"
);
}
#[test]
fn the_pi_extension_stamps_the_envelope_this_crate_defines() {
let lowered = PI_GATEWAY_EXTENSION.contents().to_ascii_lowercase();
assert!(
lowered.contains(&format!("\"{X_TAPES_HARNESS_ID}\": \"{HARNESS_ID_PI}\"")),
"the asset does not stamp {X_TAPES_HARNESS_ID}: {HARNESS_ID_PI}"
);
assert!(
lowered.contains(X_TAPES_HARNESS_SESSION_ID),
"the asset does not stamp {X_TAPES_HARNESS_SESSION_ID}"
);
}
#[test]
fn the_pi_extension_has_no_built_in_endpoint() {
let contents = PI_GATEWAY_EXTENSION.contents();
for literal in ["127.0.0.1:", "localhost:", "http://127.0.0.1"] {
assert!(
!contents.contains(literal),
"the asset hard-codes {literal:?}; it must be inert without {GATEWAY_URL_ENV}"
);
}
}
#[test]
fn opencode_installs_where_opencode_discovers_plugins() {
let home = Path::new("/home/u");
assert_eq!(
OPENCODE_GATEWAY_EXTENSION.install_path(home),
PathBuf::from("/home/u/.config/opencode/plugins/tapes-gateway.ts"),
);
}
#[test]
fn the_opencode_plugin_reads_the_gateway_environment_contract() {
let contents = OPENCODE_GATEWAY_EXTENSION.contents();
assert!(
contents.contains(GATEWAY_URL_ENV),
"the asset does not read {GATEWAY_URL_ENV}"
);
assert!(
contents.contains(GATEWAY_SCHEMA_ENV),
"the asset does not read {GATEWAY_SCHEMA_ENV}"
);
}
#[test]
fn the_opencode_plugin_echoes_the_capture_nonce_contract() {
let contents = OPENCODE_GATEWAY_EXTENSION.contents();
assert!(
contents.contains(GATEWAY_NONCE_ENV),
"the asset does not read {GATEWAY_NONCE_ENV}"
);
assert!(
contents.contains(GATEWAY_NONCE_HEADER),
"the asset does not echo the nonce in {GATEWAY_NONCE_HEADER}"
);
assert!(
contents.contains("process.env[GATEWAY_NONCE_ENV]"),
"the asset does not read the nonce from the environment"
);
assert!(
contents.contains("output.headers[GATEWAY_NONCE_HEADER] = nonce"),
"the asset does not place the nonce value under the header name"
);
}
#[test]
fn the_opencode_plugin_deletes_the_nonce_from_its_environment_at_load() {
let contents = OPENCODE_GATEWAY_EXTENSION.contents();
assert!(
contents.contains("delete process.env[GATEWAY_NONCE_ENV]"),
"the asset does not delete the nonce from its environment; \
shell-tool subprocesses would inherit the secret"
);
let read = contents
.find("process.env[GATEWAY_NONCE_ENV]")
.unwrap_or(usize::MAX);
let delete = contents
.find("delete process.env[GATEWAY_NONCE_ENV]")
.unwrap_or(0);
assert!(
read < delete,
"the asset must capture the nonce before deleting it"
);
}
#[test]
fn the_opencode_plugin_stamps_the_envelope_this_crate_defines() {
let lowered = OPENCODE_GATEWAY_EXTENSION.contents().to_ascii_lowercase();
assert!(
lowered.contains(&format!(
"\"{X_TAPES_HARNESS_ID}\": \"{HARNESS_ID_OPENCODE}\""
)),
"the asset does not stamp {X_TAPES_HARNESS_ID}: {HARNESS_ID_OPENCODE}"
);
assert!(
lowered.contains(X_TAPES_HARNESS_SESSION_ID),
"the asset does not stamp {X_TAPES_HARNESS_SESSION_ID}"
);
}
#[test]
fn the_opencode_plugin_stamps_nothing_toward_a_real_upstream() {
let contents = OPENCODE_GATEWAY_EXTENSION.contents();
assert!(
contents.contains("isGatewayAddress(resolvedBaseUrl, baseUrl)"),
"the asset does not verify the resolved provider endpoint is the \
gateway before stamping the nonce and envelope"
);
}
#[test]
fn the_opencode_plugins_gateway_check_is_a_url_boundary_not_a_string_prefix() {
let contents = OPENCODE_GATEWAY_EXTENSION.contents();
assert!(
!contents.contains("startsWith(baseUrl)"),
"the asset compares the resolved endpoint to the gateway as a string \
prefix; a lookalike host sharing that prefix would be handed the \
capture nonce and the session envelope"
);
assert!(
contents.contains("url.origin !== gateway.origin"),
"the asset does not compare parsed origins, which is what makes the \
host boundary — scheme, host and port — actually hold"
);
assert!(
contents.contains("url.pathname.startsWith(`${mount}/`)"),
"the asset does not bound the gateway's mount path on a separator"
);
}
#[test]
fn the_opencode_plugin_has_no_built_in_endpoint() {
let contents = OPENCODE_GATEWAY_EXTENSION.contents();
for literal in ["127.0.0.1:", "localhost:", "http://127.0.0.1"] {
assert!(
!contents.contains(literal),
"the asset hard-codes {literal:?}; it must be inert without {GATEWAY_URL_ENV}"
);
}
}
#[test]
fn artifacts_are_declared_exactly_where_the_registry_says() {
for harness in REGISTRY {
match harness.plugin() {
PluginDelivery::None => assert!(
harness.plugin_artifacts().is_empty(),
"{} needs no plugin but ships artifacts",
harness.id(),
),
PluginDelivery::BundledExtension(_) => assert!(
!harness.plugin_artifacts().is_empty(),
"{} declares a bundled extension with no artifacts",
harness.id(),
),
PluginDelivery::HookManifestTemplates(templates) => {
assert!(
harness.plugin_artifacts().is_empty(),
"{} must not expose templates as copyable artifacts",
harness.id(),
);
assert!(
!templates.plugin_manifest.trim().is_empty()
&& !templates.hooks_manifest.trim().is_empty(),
"{} declares empty manifest templates",
harness.id(),
);
}
}
}
}
#[test]
fn no_two_artifacts_install_to_the_same_path() {
let home = Path::new("/home/u");
let mut paths: Vec<PathBuf> = all_artifacts()
.iter()
.map(|artifact| artifact.install_path(home))
.collect();
let total = paths.len();
paths.sort();
paths.dedup();
assert_eq!(paths.len(), total, "two artifacts share an install path");
}
}