border-customs 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13029,7 +13029,7 @@ var require_micromatch = __commonJS({
13029
13029
 
13030
13030
  // src/index.ts
13031
13031
  import { realpathSync } from "node:fs";
13032
- import { fileURLToPath as fileURLToPath6, pathToFileURL } from "node:url";
13032
+ import { fileURLToPath as fileURLToPath7, pathToFileURL } from "node:url";
13033
13033
 
13034
13034
  // src/cli.ts
13035
13035
  import { parseArgs } from "node:util";
@@ -38093,12 +38093,12 @@ function packOnce(o) {
38093
38093
  throw new EngineRunError(`npm pack reported '${report.filename}' but no tarball landed in ${o.distDir}`, null);
38094
38094
  }
38095
38095
  const data = readFileSync3(tarballAbs);
38096
- const sha2564 = createHash3("sha256").update(data).digest("hex");
38096
+ const sha2563 = createHash3("sha256").update(data).digest("hex");
38097
38097
  const sha1 = createHash3("sha1").update(data).digest("hex");
38098
38098
  if (sha1 !== report.shasum || data.byteLength !== report.size) {
38099
38099
  throw new EngineRunError(`npm pack report disagrees with the tarball bytes for ${report.filename} \u2014 corrupted or swapped artifact, failing closed`, null);
38100
38100
  }
38101
- return { filename: report.filename, sha256: sha2564, bytes: data.byteLength, tarballAbs };
38101
+ return { filename: report.filename, sha256: sha2563, bytes: data.byteLength, tarballAbs };
38102
38102
  }
38103
38103
  function publintFinding(o, tarballAbs, identity) {
38104
38104
  const candidates = o.publintBinPath !== void 0 ? [o.publintBinPath] : [
@@ -39141,7 +39141,7 @@ async function runNpmArtifactStage(o) {
39141
39141
  const stateDir = join10(repoDir, BORDER_STATE_DIR);
39142
39142
  const distDir = join10(stateDir, "dist");
39143
39143
  mkdirSync3(distDir, { recursive: true });
39144
- const { filename, sha256: sha2564, bytes, tarballAbs } = packOnce({
39144
+ const { filename, sha256: sha2563, bytes, tarballAbs } = packOnce({
39145
39145
  repoDir,
39146
39146
  distDir,
39147
39147
  ...o.env !== void 0 ? { env: o.env } : {},
@@ -39149,7 +39149,7 @@ async function runNpmArtifactStage(o) {
39149
39149
  ...o.npmTimeoutMs !== void 0 ? { npmTimeoutMs: o.npmTimeoutMs } : {}
39150
39150
  });
39151
39151
  const findings = [];
39152
- const extractDir = join10(stateDir, "tmp", sha2564.slice(0, 8));
39152
+ const extractDir = join10(stateDir, "tmp", sha2563.slice(0, 8));
39153
39153
  rmSync3(extractDir, { recursive: true, force: true });
39154
39154
  mkdirSync3(extractDir, { recursive: true });
39155
39155
  try {
@@ -39254,7 +39254,7 @@ async function runNpmArtifactStage(o) {
39254
39254
  if (publint !== null) findings.push(publint);
39255
39255
  return {
39256
39256
  findings,
39257
- artifact: { file: `${BORDER_STATE_DIR}/dist/${filename}`, sha256: sha2564, bytes }
39257
+ artifact: { file: `${BORDER_STATE_DIR}/dist/${filename}`, sha256: sha2563, bytes }
39258
39258
  };
39259
39259
  }
39260
39260
 
@@ -40354,9 +40354,9 @@ async function runRubygemsArtifactStage(o) {
40354
40354
  throw new EngineRunError(`gem build produced no ${gemName} in ${relative3(repoDir, distDir)} \u2014 failing closed (was the gemspec's literal name/version re-read?)`, null);
40355
40355
  }
40356
40356
  const bytes = readFileSync7(dist);
40357
- const sha2564 = createHash6("sha256").update(bytes).digest("hex");
40357
+ const sha2563 = createHash6("sha256").update(bytes).digest("hex");
40358
40358
  const findings = [];
40359
- const extractDir = join12(stateDir, "tmp", sha2564.slice(0, 8));
40359
+ const extractDir = join12(stateDir, "tmp", sha2563.slice(0, 8));
40360
40360
  rmSync4(extractDir, { recursive: true, force: true });
40361
40361
  mkdirSync4(extractDir, { recursive: true });
40362
40362
  const dataDir = `${extractDir}-data`;
@@ -40434,7 +40434,7 @@ async function runRubygemsArtifactStage(o) {
40434
40434
  }
40435
40435
  return {
40436
40436
  findings,
40437
- artifact: { file: `${BORDER_STATE_DIR}/dist/${gemName}`, sha256: sha2564, bytes: bytes.length }
40437
+ artifact: { file: `${BORDER_STATE_DIR}/dist/${gemName}`, sha256: sha2563, bytes: bytes.length }
40438
40438
  };
40439
40439
  }
40440
40440
 
@@ -41996,9 +41996,9 @@ async function runCargoArtifactStage(o) {
41996
41996
  const dist = join18(distDir, crateName);
41997
41997
  renameSync(packed, dist);
41998
41998
  const bytes = readFileSync11(dist);
41999
- const sha2564 = createHash11("sha256").update(bytes).digest("hex");
41999
+ const sha2563 = createHash11("sha256").update(bytes).digest("hex");
42000
42000
  const findings = [];
42001
- const extractDir = join18(stateDir, "tmp", sha2564.slice(0, 8));
42001
+ const extractDir = join18(stateDir, "tmp", sha2563.slice(0, 8));
42002
42002
  rmSync8(extractDir, { recursive: true, force: true });
42003
42003
  mkdirSync8(extractDir, { recursive: true });
42004
42004
  try {
@@ -42051,7 +42051,7 @@ async function runCargoArtifactStage(o) {
42051
42051
  }
42052
42052
  return {
42053
42053
  findings,
42054
- artifact: { file: `${BORDER_STATE_DIR}/dist/${crateName}`, sha256: sha2564, bytes: bytes.length }
42054
+ artifact: { file: `${BORDER_STATE_DIR}/dist/${crateName}`, sha256: sha2563, bytes: bytes.length }
42055
42055
  };
42056
42056
  } finally {
42057
42057
  rmSync8(targetDir, { recursive: true, force: true });
@@ -44482,43 +44482,551 @@ async function runLlmRequest(ctx) {
44482
44482
  }
44483
44483
 
44484
44484
  // src/commands/opencode.ts
44485
+ import { existsSync as existsSync22, lstatSync, mkdirSync as mkdirSync12, readFileSync as readFileSync20, rmSync as rmSync13, writeFileSync as writeFileSync8 } from "node:fs";
44486
+ import { dirname as dirname16, join as join31 } from "node:path";
44487
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
44488
+
44489
+ // src/opencode/inspect.ts
44490
+ import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
44491
+ import { join as join30 } from "node:path";
44492
+
44493
+ // src/opencode/jsonconfig.ts
44494
+ import { existsSync as existsSync19, readdirSync as readdirSync10, readFileSync as readFileSync18 } from "node:fs";
44495
+ import { join as join29 } from "node:path";
44496
+
44497
+ // src/opencode/contract.ts
44485
44498
  import { createHash as createHash17 } from "node:crypto";
44486
- import { existsSync as existsSync18, lstatSync, mkdirSync as mkdirSync12, readFileSync as readFileSync18, rmSync as rmSync13, writeFileSync as writeFileSync8 } from "node:fs";
44499
+ import { existsSync as existsSync18 } from "node:fs";
44487
44500
  import os3 from "node:os";
44488
44501
  import { dirname as dirname15, join as join28 } from "node:path";
44489
44502
  import { fileURLToPath as fileURLToPath5 } from "node:url";
44490
44503
  var PLUGIN_MARKER = "// border-opencode-plugin v";
44491
44504
  var COMMAND_MARKER = "<!-- border-opencode-command -->";
44505
+ var BORDER_PACKAGE = "border-customs";
44506
+ var DEFAULT_REGISTRY_BASE = "https://registry.npmjs.org";
44507
+ var REGISTRY_TIMEOUT_MS2 = 15e3;
44508
+ function sha256Hex3(bytes) {
44509
+ return createHash17("sha256").update(bytes).digest("hex");
44510
+ }
44511
+ function firstLine(bytes) {
44512
+ const text = bytes.toString("utf8");
44513
+ const newline = text.indexOf("\n");
44514
+ return newline < 0 ? text : text.slice(0, newline);
44515
+ }
44492
44516
  function opencodeConfigRoot(env2) {
44493
44517
  const xdg = env2["XDG_CONFIG_HOME"] ?? "";
44494
44518
  if (xdg.length > 0) return xdg;
44495
44519
  const home = env2["HOME"] ?? "";
44496
44520
  return join28(home.length > 0 ? home : os3.homedir(), ".config");
44497
44521
  }
44498
- function resolvePluginAsset(moduleUrl, fileName) {
44522
+ function opencodeCacheRoot(env2) {
44523
+ const xdg = env2["XDG_CACHE_HOME"] ?? "";
44524
+ const base = xdg.length > 0 ? xdg : join28(env2["HOME"] ?? os3.homedir(), ".cache");
44525
+ return join28(base, "opencode", "packages");
44526
+ }
44527
+ function routeAPluginPath(env2) {
44528
+ return join28(opencodeConfigRoot(env2), "opencode", "plugins", "border.ts");
44529
+ }
44530
+ function packagedAssetPath(moduleUrl, fileName) {
44499
44531
  const here = dirname15(fileURLToPath5(moduleUrl));
44500
44532
  const candidates = [
44501
44533
  join28(here, "..", "plugin", fileName),
44502
- // dist mode (bundled): <root>/plugin/<f>
44534
+ // dist (bundled) mode: <root>/plugin/<f>
44503
44535
  join28(here, "..", "..", "plugin", fileName)
44504
44536
  // src mode: <root>/plugin/<f>
44505
44537
  ];
44506
44538
  return candidates.find((p) => existsSync18(p)) ?? candidates[1];
44507
44539
  }
44508
- function targetsFor(env2) {
44509
- const root = join28(opencodeConfigRoot(env2), "opencode");
44510
- return [
44511
- { fileName: "border.ts", destination: join28(root, "plugins", "border.ts"), marker: PLUGIN_MARKER },
44512
- { fileName: "border-command.md", destination: join28(root, "commands", "border.md"), marker: COMMAND_MARKER }
44540
+ function packagedDistEntry(moduleUrl) {
44541
+ const here = dirname15(fileURLToPath5(moduleUrl));
44542
+ const candidates = [
44543
+ join28(here, "index.js"),
44544
+ // dist (bundled) mode: <root>/dist/index.js
44545
+ join28(here, "..", "dist", "index.js"),
44546
+ // src/opencode mode one level under root
44547
+ join28(here, "..", "..", "dist", "index.js")
44548
+ // src mode: <root>/dist/index.js
44513
44549
  ];
44550
+ return candidates.find((p) => existsSync18(p)) ?? candidates[2];
44514
44551
  }
44515
- function sha2563(bytes) {
44516
- return createHash17("sha256").update(bytes).digest("hex");
44552
+
44553
+ // src/opencode/jsonconfig.ts
44554
+ function stripJsoncComments(text) {
44555
+ let out = "";
44556
+ let inString = false;
44557
+ let inLine = false;
44558
+ let inBlock = false;
44559
+ for (let i = 0; i < text.length; i += 1) {
44560
+ const ch = text[i];
44561
+ const next = text[i + 1] ?? "";
44562
+ if (inLine) {
44563
+ if (ch === "\n") {
44564
+ inLine = false;
44565
+ out += ch;
44566
+ }
44567
+ continue;
44568
+ }
44569
+ if (inBlock) {
44570
+ if (ch === "*" && next === "/") {
44571
+ inBlock = false;
44572
+ i += 1;
44573
+ out += " ";
44574
+ }
44575
+ continue;
44576
+ }
44577
+ if (inString) {
44578
+ if (ch === "\\") {
44579
+ out += ch + next;
44580
+ i += 1;
44581
+ continue;
44582
+ }
44583
+ if (ch === '"') inString = false;
44584
+ out += ch;
44585
+ continue;
44586
+ }
44587
+ if (ch === '"') {
44588
+ inString = true;
44589
+ out += ch;
44590
+ continue;
44591
+ }
44592
+ if (ch === "/" && next === "/") {
44593
+ inLine = true;
44594
+ i += 1;
44595
+ continue;
44596
+ }
44597
+ if (ch === "/" && next === "*") {
44598
+ inBlock = true;
44599
+ i += 1;
44600
+ continue;
44601
+ }
44602
+ out += ch;
44603
+ }
44604
+ return out;
44517
44605
  }
44518
- function firstLine(bytes) {
44519
- const text = bytes.toString("utf8");
44520
- const newline = text.indexOf("\n");
44521
- return newline < 0 ? text : text.slice(0, newline);
44606
+ function parseJsonc(text) {
44607
+ return JSON.parse(stripJsoncComments(text).replace(/,(\s*[}\]])/g, "$1"));
44608
+ }
44609
+ var isRecord2 = (v) => typeof v === "object" && v !== null;
44610
+ function splitNpmSpec(entry) {
44611
+ const at = entry.lastIndexOf("@");
44612
+ if (at <= 0) return { name: entry, spec: null };
44613
+ return { name: entry.slice(0, at), spec: entry.slice(at + 1) };
44614
+ }
44615
+ function readPluginDeclarations(path4, pkg) {
44616
+ const none = { path: path4, status: "absent", specs: [] };
44617
+ let text;
44618
+ try {
44619
+ if (!existsSync19(path4)) return none;
44620
+ text = readFileSync18(path4, "utf8");
44621
+ } catch {
44622
+ return { path: path4, status: "error", specs: [] };
44623
+ }
44624
+ let parsed;
44625
+ try {
44626
+ parsed = parseJsonc(text);
44627
+ } catch {
44628
+ return { path: path4, status: "error", specs: [] };
44629
+ }
44630
+ if (!isRecord2(parsed)) return { path: path4, status: "error", specs: [] };
44631
+ const plugin = Array.isArray(parsed["plugin"]) ? parsed["plugin"] : [];
44632
+ const specs = plugin.filter((e) => typeof e === "string" && splitNpmSpec(e).name === pkg);
44633
+ return { path: path4, status: "ok", specs };
44634
+ }
44635
+ function listCacheDirs(cacheRoot, pkg) {
44636
+ let names;
44637
+ try {
44638
+ names = readdirSync10(cacheRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith(`${pkg}@`)).map((e) => e.name).sort();
44639
+ } catch {
44640
+ return [];
44641
+ }
44642
+ return names.map((name) => {
44643
+ const pkgRoot = join29(cacheRoot, name, "node_modules", pkg);
44644
+ let packageVersion = null;
44645
+ try {
44646
+ const meta3 = parseJsonc(readFileSync18(join29(pkgRoot, "package.json"), "utf8"));
44647
+ if (isRecord2(meta3) && typeof meta3["version"] === "string") packageVersion = meta3["version"];
44648
+ } catch {
44649
+ packageVersion = null;
44650
+ }
44651
+ let markerLine = null;
44652
+ try {
44653
+ markerLine = firstLine(readFileSync18(join29(pkgRoot, "plugin", "border.ts")));
44654
+ } catch {
44655
+ markerLine = null;
44656
+ }
44657
+ const markerVersion = markerLine !== null && markerLine.startsWith(PLUGIN_MARKER) ? markerLine.slice(PLUGIN_MARKER.length).trim() : null;
44658
+ return { dir: join29(cacheRoot, name), tag: name.slice(pkg.length + 1), packageVersion, markerVersion, markerLine };
44659
+ });
44660
+ }
44661
+
44662
+ // src/opencode/aspects/cacheVersion.ts
44663
+ var isExactVersion = (spec) => /^\d+\.\d+\.\d+([-+].*)?$/.test(spec);
44664
+ function verifyCacheVersion(facts) {
44665
+ const id = "cache-version";
44666
+ const { cacheDirs, configs, cacheRoot, pkg } = facts;
44667
+ const errored = configs.filter((c) => c.status === "error").map((c) => c.path);
44668
+ const declared = configs.some((c) => c.specs.length > 0);
44669
+ const where = configs.map((c) => `${c.path} [${c.status}]`).join(", ");
44670
+ if (cacheDirs.length === 0) {
44671
+ if (errored.length > 0) {
44672
+ return { id, verdict: "CANNOT", detail: `no cache directory under ${cacheRoot} and the configuration is unreadable (${errored.join(", ")}) \u2014 cannot tell whether a Route-B load is claimed` };
44673
+ }
44674
+ if (declared) {
44675
+ return { id, verdict: "CANNOT", detail: `NOT-CACHED: ${pkg} is declared (${configs.flatMap((c) => [...c.specs]).join(", ")}) but no cache directory exists under ${cacheRoot} \u2014 the load claim has no bytes behind it` };
44676
+ }
44677
+ return { id, verdict: "PASS", detail: `no ${pkg} cache directory under ${cacheRoot} and none declared (${where}) \u2014 Route B not in use, nothing to be stale` };
44678
+ }
44679
+ const pins = new Set(
44680
+ configs.flatMap((c) => [...c.specs]).map((s) => splitNpmSpec(s).spec).filter((s) => s !== null && isExactVersion(s))
44681
+ );
44682
+ const problems = [];
44683
+ for (const d of cacheDirs) {
44684
+ if (d.packageVersion === null) {
44685
+ problems.push(`${d.dir}: package.json missing or unreadable`);
44686
+ }
44687
+ if (d.markerLine === null) {
44688
+ problems.push(`${d.dir}: plugin/border.ts missing or unreadable in the cached package`);
44689
+ } else if (d.markerVersion === null) {
44690
+ problems.push(`${d.dir}: plugin/border.ts first line lacks the marker '${PLUGIN_MARKER}'`);
44691
+ } else if (d.packageVersion !== null && d.markerVersion !== d.packageVersion) {
44692
+ problems.push(`${d.dir}: marker says ${d.markerVersion}, package.json says ${d.packageVersion}`);
44693
+ }
44694
+ if (pins.has(d.tag) && d.packageVersion !== null && d.packageVersion !== d.tag) {
44695
+ problems.push(`${d.dir}: cached under exact spec '${d.tag}' but the package reports ${d.packageVersion}`);
44696
+ }
44697
+ }
44698
+ if (problems.length > 0) {
44699
+ return { id, verdict: "FAIL", detail: problems.join("; ") };
44700
+ }
44701
+ return {
44702
+ id,
44703
+ verdict: "PASS",
44704
+ detail: `cache self-consistent: ${cacheDirs.map((d) => `${d.tag}->${d.packageVersion ?? "?"}`).join(", ")}`
44705
+ };
44706
+ }
44707
+
44708
+ // src/opencode/aspects/dualRoute.ts
44709
+ function verifyDualRoute(facts) {
44710
+ const id = "dual-route";
44711
+ const { configs, routeAInstalled, routeAPlugin, pkg } = facts;
44712
+ const errored = configs.filter((c) => c.status === "error").map((c) => c.path);
44713
+ if (errored.length > 0) {
44714
+ return { id, verdict: "CANNOT", detail: `configuration unreadable (${errored.join(", ")}) \u2014 Route B presence cannot be counted` };
44715
+ }
44716
+ const declared = configs.flatMap((c) => [...c.specs]);
44717
+ const hasB = declared.length > 0;
44718
+ if (routeAInstalled && hasB) {
44719
+ return {
44720
+ id,
44721
+ verdict: "FAIL",
44722
+ detail: `both routes registered: Route A drop at ${routeAPlugin} AND Route B declaration ${declared.map((d) => `'${d}'`).join(", ")} \u2014 pick exactly one route (the dual-registration window blueprint \xA77 forbids)`
44723
+ };
44724
+ }
44725
+ if (routeAInstalled) {
44726
+ return { id, verdict: "PASS", detail: `Route A only: drop at ${routeAPlugin}, no ${pkg} config declaration` };
44727
+ }
44728
+ if (hasB) {
44729
+ return { id, verdict: "PASS", detail: `Route B only: ${declared.map((d) => `'${d}'`).join(", ")}, no file drop at ${routeAPlugin}` };
44730
+ }
44731
+ return { id, verdict: "PASS", detail: "neither route registered \u2014 no border plugin load claimed on the V1 surface" };
44732
+ }
44733
+
44734
+ // src/opencode/aspects/identity.ts
44735
+ import { execFile } from "node:child_process";
44736
+ import { existsSync as existsSync20 } from "node:fs";
44737
+ var HANDSHAKE_ARGV = ["--help"];
44738
+ var HANDSHAKE_BANNER_PREFIX = "border";
44739
+ var HANDSHAKE_USAGE_NEEDLE = "usage: border";
44740
+ var HANDSHAKE_TIMEOUT_MS = 2e4;
44741
+ var MAX_OUTPUT_BYTES = 64 * 1024;
44742
+ var defaultInspectRunner = (target, argv) => new Promise((resolve20) => {
44743
+ execFile(
44744
+ target.bin,
44745
+ [...target.prefix, ...argv],
44746
+ { timeout: HANDSHAKE_TIMEOUT_MS, maxBuffer: MAX_OUTPUT_BYTES, shell: false },
44747
+ (error61, stdout, stderr) => {
44748
+ const out = stdout.slice(0, MAX_OUTPUT_BYTES);
44749
+ const err = stderr.slice(0, MAX_OUTPUT_BYTES);
44750
+ if (error61 === null) {
44751
+ resolve20({ status: 0, stdout: out, stderr: err });
44752
+ return;
44753
+ }
44754
+ const failure = error61;
44755
+ if (failure.code === "ENOENT" || failure.code === "EACCES" || failure.code === "ENOEXEC") {
44756
+ resolve20({ status: null, stdout: out, stderr: err });
44757
+ return;
44758
+ }
44759
+ const status = typeof failure.code === "number" ? failure.code : failure.killed === true || failure.signal != null ? 124 : -1;
44760
+ resolve20({ status, stdout: out, stderr: err });
44761
+ }
44762
+ );
44763
+ });
44764
+ function passesHandshake(outcome) {
44765
+ if (outcome.status !== 0) return false;
44766
+ const firstLine2 = (outcome.stdout.match(/^\s*(.*)/) ?? ["", ""])[1] ?? "";
44767
+ return firstLine2.startsWith(HANDSHAKE_BANNER_PREFIX) && outcome.stdout.includes(HANDSHAKE_USAGE_NEEDLE);
44768
+ }
44769
+ function identityCandidates(env2, distEntry) {
44770
+ const configured = env2["BORDER_BIN"];
44771
+ if (configured !== void 0 && configured.length > 0) {
44772
+ return [{ bin: configured, prefix: [], label: `BORDER_BIN=${configured}` }];
44773
+ }
44774
+ const candidates = [];
44775
+ if (existsSync20(distEntry)) {
44776
+ candidates.push({ bin: distEntry, prefix: [], label: "packaged dist/index.js (shebang exec)" });
44777
+ candidates.push({ bin: "node", prefix: [distEntry], label: "packaged dist/index.js under PATH node" });
44778
+ }
44779
+ candidates.push({ bin: "border", prefix: [], label: "border on PATH" });
44780
+ return candidates;
44781
+ }
44782
+ async function verifyIdentity(deps) {
44783
+ const id = "identity-handshake";
44784
+ const runner = deps.runner ?? defaultInspectRunner;
44785
+ const candidates = identityCandidates(deps.env, deps.distEntry);
44786
+ const reasons = [];
44787
+ let anySpawned = false;
44788
+ for (const candidate of candidates) {
44789
+ const probe = await runner(candidate, HANDSHAKE_ARGV);
44790
+ if (passesHandshake(probe)) {
44791
+ return { id, verdict: "PASS", detail: `${candidate.label} passed the identity handshake` };
44792
+ }
44793
+ if (probe.status !== null) {
44794
+ anySpawned = true;
44795
+ reasons.push(`${candidate.label}: spawned but --help did not answer as border (impostor?)`);
44796
+ } else {
44797
+ reasons.push(`${candidate.label}: unspawnable (ENOENT/EACCES/ENOEXEC)`);
44798
+ }
44799
+ }
44800
+ if (anySpawned) {
44801
+ return { id, verdict: "FAIL", detail: `a border binary was reachable but failed the identity handshake \u2014 impersonation shape: ${reasons.join("; ")}` };
44802
+ }
44803
+ return { id, verdict: "CANNOT", detail: `no border binary reachable \u2014 tried: ${reasons.join("; ")}` };
44804
+ }
44805
+
44806
+ // src/opencode/aspects/routeA.ts
44807
+ function verifyRouteAParity(facts) {
44808
+ const id = "route-a-parity";
44809
+ const { routeAPlugin, routeAInstalled } = facts;
44810
+ if (!routeAInstalled) {
44811
+ return { id, verdict: "PASS", detail: `${routeAPlugin}: absent \u2014 Route A not installed, nothing to be stale` };
44812
+ }
44813
+ if (facts.packagedPlugin === null) {
44814
+ return { id, verdict: "CANNOT", detail: "the packaged plugin asset is unreadable \u2014 the border install itself looks broken, parity cannot be judged" };
44815
+ }
44816
+ const installed = facts.routeABytes;
44817
+ if (installed === null) {
44818
+ return { id, verdict: "CANNOT", detail: `${routeAPlugin}: present but unreadable \u2014 parity cannot be judged` };
44819
+ }
44820
+ const installedSha = sha256Hex3(installed);
44821
+ const packagedSha = sha256Hex3(facts.packagedPlugin);
44822
+ if (installedSha === packagedSha) {
44823
+ return { id, verdict: "PASS", detail: `${routeAPlugin}: up-to-date installed=${installedSha} packaged=${packagedSha}` };
44824
+ }
44825
+ const ours = firstLine(installed).startsWith(PLUGIN_MARKER);
44826
+ const shape = ours ? "outdated (marker ours, bytes differ)" : "foreign (first line lacks the marker)";
44827
+ return { id, verdict: "FAIL", detail: `${routeAPlugin}: ${shape} installed=${installedSha} packaged=${packagedSha}` };
44828
+ }
44829
+
44830
+ // src/opencode/aspects/scope.ts
44831
+ var SCOPE_ID = "scope";
44832
+ var SCOPE_NAME = "v1-load-surface";
44833
+ var SCOPE_UNCOVERED = [
44834
+ "V2 project-local .opencode/plugins/",
44835
+ "V2 npm-prefix/node_modules installs",
44836
+ "V2 lazy activation state"
44837
+ ];
44838
+ function verifyScope() {
44839
+ return {
44840
+ id: SCOPE_ID,
44841
+ verdict: "PASS",
44842
+ detail: `verdict scope = ${SCOPE_NAME}; NOT covered (declared blind, separate ledger item): ${SCOPE_UNCOVERED.join(", ")}`
44843
+ };
44844
+ }
44845
+
44846
+ // src/opencode/registry.ts
44847
+ var isRecord3 = (v) => typeof v === "object" && v !== null;
44848
+ var defaultRegistryFetcher = async (url2, init) => {
44849
+ if (!/^https?:\/\//.test(url2)) throw new Error(`border opencode inspect refuses non-http(s) URL: ${url2}`);
44850
+ const res = await fetch(url2, {
44851
+ redirect: "follow",
44852
+ signal: init.signal,
44853
+ headers: { "user-agent": "border-customs (+https://github.com/TachikomaGundam/Border-Customs; border opencode inspect)" }
44854
+ });
44855
+ const raw = res.headers.get("content-length");
44856
+ const parsed = raw === null ? Number.NaN : Number(raw);
44857
+ return {
44858
+ status: res.status,
44859
+ contentLength: Number.isFinite(parsed) && parsed >= 0 ? parsed : null,
44860
+ chunks: () => {
44861
+ if (res.body === null) {
44862
+ return {
44863
+ async *[Symbol.asyncIterator]() {
44864
+ }
44865
+ };
44866
+ }
44867
+ return res.body;
44868
+ }
44869
+ };
44870
+ };
44871
+ function npmTagUrl(pkg, tag, base = DEFAULT_REGISTRY_BASE) {
44872
+ const enc = pkg.startsWith("@") ? pkg.replace("/", "%2F") : encodeURIComponent(pkg);
44873
+ return `${base}/${enc}/${encodeURIComponent(tag)}`;
44874
+ }
44875
+ var REGISTRY_BODY_CAP = 8 * 1024 * 1024;
44876
+ async function fetchRegistryVersion(fetcher, url2, timeoutMs) {
44877
+ const res = await fetcher(url2, { signal: AbortSignal.timeout(timeoutMs) });
44878
+ if (res.status !== 200) throw new Error(`HTTP ${String(res.status)} from ${url2}`);
44879
+ const parts = [];
44880
+ let total = 0;
44881
+ for await (const chunk of res.chunks()) {
44882
+ total += chunk.byteLength;
44883
+ if (total > REGISTRY_BODY_CAP) throw new Error(`${url2} exceeds the ${String(REGISTRY_BODY_CAP)} B metadata cap`);
44884
+ parts.push(Buffer.from(chunk));
44885
+ }
44886
+ let parsed;
44887
+ try {
44888
+ parsed = JSON.parse(Buffer.concat(parts).toString("utf8"));
44889
+ } catch {
44890
+ throw new Error(`metadata response from ${url2} is not valid JSON`);
44891
+ }
44892
+ if (!isRecord3(parsed) || typeof parsed["version"] !== "string" || parsed["version"].length === 0) {
44893
+ throw new Error(`metadata response from ${url2} carries no "version" string`);
44894
+ }
44895
+ return parsed["version"];
44896
+ }
44897
+
44898
+ // src/opencode/aspects/specFreshness.ts
44899
+ var isExactVersion2 = (spec) => /^\d+\.\d+\.\d+([-+].*)?$/.test(spec);
44900
+ async function verifySpecFreshness(facts, deps = {}) {
44901
+ const id = "spec-freshness";
44902
+ const { configs, cacheDirs, pkg } = facts;
44903
+ const errored = configs.filter((c) => c.status === "error").map((c) => c.path);
44904
+ if (errored.length > 0) {
44905
+ return { id, verdict: "CANNOT", detail: `configuration unreadable (${errored.join(", ")}) \u2014 the expected version cannot be determined` };
44906
+ }
44907
+ const specs = [...new Set(configs.flatMap((c) => [...c.specs]).map((s) => splitNpmSpec(s).spec).filter((s) => s !== null))];
44908
+ if (specs.length === 0) {
44909
+ return { id, verdict: "PASS", detail: `no ${pkg} spec declared in opencode config \u2014 nothing to be fresh` };
44910
+ }
44911
+ const expected = /* @__PURE__ */ new Map();
44912
+ const fetcher = deps.fetcher ?? defaultRegistryFetcher;
44913
+ const timeoutMs = deps.timeoutMs ?? REGISTRY_TIMEOUT_MS2;
44914
+ for (const spec of specs) {
44915
+ if (isExactVersion2(spec)) {
44916
+ expected.set(spec, spec);
44917
+ continue;
44918
+ }
44919
+ try {
44920
+ expected.set(spec, await fetchRegistryVersion(fetcher, npmTagUrl(pkg, spec, deps.registryBase), timeoutMs));
44921
+ } catch (err) {
44922
+ const message = err instanceof Error ? err.message : String(err);
44923
+ return { id, verdict: "CANNOT", detail: `registry unreachable for spec '${pkg}@${spec}': ${message} \u2014 border never reads silence as fresh` };
44924
+ }
44925
+ }
44926
+ if (cacheDirs.length === 0) {
44927
+ return { id, verdict: "PASS", detail: `nothing cached to compare (see cache-version); spec resolves to ${[...new Set(expected.values())].join(", ")}` };
44928
+ }
44929
+ const wanted = new Set(expected.values());
44930
+ const judged = cacheDirs.filter((d) => d.packageVersion !== null);
44931
+ const stale = judged.filter((d) => !wanted.has(d.packageVersion));
44932
+ if (judged.length > 0 && stale.length > 0) {
44933
+ return {
44934
+ id,
44935
+ verdict: "FAIL",
44936
+ detail: `cache holds ${stale.map((d) => `${d.tag}->${d.packageVersion}`).join(",")}, spec expects ${[...wanted].join(",")}`
44937
+ };
44938
+ }
44939
+ return { id, verdict: "PASS", detail: `cache matches spec: ${[...wanted].join(",")}` };
44940
+ }
44941
+
44942
+ // src/opencode/inspect.ts
44943
+ function aggregateVerdict(aspects) {
44944
+ if (aspects.some((a) => a.verdict === "FAIL")) return "FAIL";
44945
+ if (aspects.some((a) => a.verdict === "CANNOT")) return "CANNOT";
44946
+ return "PASS";
44947
+ }
44948
+ function inspectExit(verdict) {
44949
+ switch (verdict) {
44950
+ case "FAIL":
44951
+ return EXIT_BLOCKED;
44952
+ case "CANNOT":
44953
+ return EXIT_ERROR;
44954
+ case "PASS":
44955
+ return EXIT_PASS;
44956
+ }
44957
+ }
44958
+ function readPackagedPlugin(path4) {
44959
+ try {
44960
+ return readFileSync19(path4);
44961
+ } catch {
44962
+ return null;
44963
+ }
44964
+ }
44965
+ function gatherFacts(env2, cwd, deps) {
44966
+ const pkg = deps.package ?? BORDER_PACKAGE;
44967
+ const pluginDrop = routeAPluginPath(env2);
44968
+ const cacheRoot = deps.cacheRoot ?? opencodeCacheRoot(env2);
44969
+ const globalPath = deps.globalConfigPath ?? join30(opencodeConfigRoot(env2), "opencode", "opencode.jsonc");
44970
+ const routeAInstalled = existsSync21(pluginDrop);
44971
+ return {
44972
+ pkg,
44973
+ env: env2,
44974
+ cacheRoot,
44975
+ cacheDirs: listCacheDirs(cacheRoot, pkg),
44976
+ configs: [readPluginDeclarations(globalPath, pkg), readPluginDeclarations(join30(cwd, "opencode.jsonc"), pkg)],
44977
+ packagedPlugin: readPackagedPlugin(packagedAssetPath(import.meta.url, "border.ts")),
44978
+ routeAPlugin: pluginDrop,
44979
+ routeAInstalled,
44980
+ routeABytes: routeAInstalled ? readPackagedPlugin(pluginDrop) : null
44981
+ };
44982
+ }
44983
+ async function runInspectCore(ctx, deps = {}) {
44984
+ const facts = gatherFacts(ctx.env, ctx.cwd, deps);
44985
+ const aspects = [
44986
+ verifyCacheVersion(facts),
44987
+ await verifySpecFreshness(facts, {
44988
+ ...deps.registryFetcher !== void 0 ? { fetcher: deps.registryFetcher } : {},
44989
+ ...deps.registryBase !== void 0 ? { registryBase: deps.registryBase } : {}
44990
+ }),
44991
+ await verifyIdentity({
44992
+ ...deps.runner !== void 0 ? { runner: deps.runner } : {},
44993
+ distEntry: deps.distEntry ?? packagedDistEntry(import.meta.url),
44994
+ env: facts.env
44995
+ }),
44996
+ verifyRouteAParity(facts),
44997
+ verifyDualRoute(facts),
44998
+ verifyScope()
44999
+ ];
45000
+ const verdict = aggregateVerdict(aspects);
45001
+ const report = { schemaVersion: 1, scope: "v1", verdict, aspects };
45002
+ if (ctx.flags.json) {
45003
+ ctx.stdout(JSON.stringify(report));
45004
+ } else {
45005
+ ctx.stdout(`border opencode inspect [${report.scope}] ${verdict}`);
45006
+ for (const a of aspects) {
45007
+ ctx.stdout(` ${a.verdict.padEnd(6)} ${a.id}: ${a.detail}`);
45008
+ }
45009
+ }
45010
+ return inspectExit(verdict);
45011
+ }
45012
+
45013
+ // src/commands/opencode.ts
45014
+ function resolvePluginAsset(moduleUrl, fileName) {
45015
+ const here = dirname16(fileURLToPath6(moduleUrl));
45016
+ const candidates = [
45017
+ join31(here, "..", "plugin", fileName),
45018
+ // dist mode (bundled): <root>/plugin/<f>
45019
+ join31(here, "..", "..", "plugin", fileName)
45020
+ // src mode: <root>/plugin/<f>
45021
+ ];
45022
+ return candidates.find((p) => existsSync22(p)) ?? candidates[1];
45023
+ }
45024
+ function targetsFor(env2) {
45025
+ const root = join31(opencodeConfigRoot(env2), "opencode");
45026
+ return [
45027
+ { fileName: "border.ts", destination: join31(root, "plugins", "border.ts"), marker: PLUGIN_MARKER },
45028
+ { fileName: "border-command.md", destination: join31(root, "commands", "border.md"), marker: COMMAND_MARKER }
45029
+ ];
44522
45030
  }
44523
45031
  function carriesMarker(bytes, marker) {
44524
45032
  return firstLine(bytes).startsWith(marker);
@@ -44530,7 +45038,7 @@ function fail(ctx, message) {
44530
45038
  function readPackagedAsset(ctx, fileName) {
44531
45039
  const source = resolvePluginAsset(import.meta.url, fileName);
44532
45040
  try {
44533
- return readFileSync18(source);
45041
+ return readFileSync20(source);
44534
45042
  } catch {
44535
45043
  ctx.stderr(`border: opencode: packaged asset is missing (${source})`);
44536
45044
  ctx.stderr("border: opencode: the border install looks broken \u2014 `npm i -g border-customs` or a fresh checkout should carry plugin/.");
@@ -44539,7 +45047,7 @@ function readPackagedAsset(ctx, fileName) {
44539
45047
  }
44540
45048
  function readTarget(destination) {
44541
45049
  try {
44542
- return readFileSync18(destination);
45050
+ return readFileSync20(destination);
44543
45051
  } catch {
44544
45052
  return null;
44545
45053
  }
@@ -44606,7 +45114,7 @@ function opencodeInstall(ctx) {
44606
45114
  ctx.stdout(`${target.destination}: up to date`);
44607
45115
  continue;
44608
45116
  }
44609
- mkdirSync12(dirname15(target.destination), { recursive: true });
45117
+ mkdirSync12(dirname16(target.destination), { recursive: true });
44610
45118
  writeFileSync8(target.destination, packaged);
44611
45119
  ctx.stdout(`${target.destination}: ${state === "outdated" ? "updated" : "installed"}`);
44612
45120
  }
@@ -44620,8 +45128,8 @@ function opencodeStatus(ctx) {
44620
45128
  for (const { target, packaged } of prepared) {
44621
45129
  const current = readTarget(target.destination);
44622
45130
  const state = classify(target, current, packaged);
44623
- const installedSha = current === null ? "-" : sha2563(current);
44624
- ctx.stdout(`${target.destination}: ${state} installed=${installedSha} packaged=${sha2563(packaged)}`);
45131
+ const installedSha = current === null ? "-" : sha256Hex3(current);
45132
+ ctx.stdout(`${target.destination}: ${state} installed=${installedSha} packaged=${sha256Hex3(packaged)}`);
44625
45133
  }
44626
45134
  return EXIT_PASS;
44627
45135
  }
@@ -44648,23 +45156,27 @@ function opencodeUninstall(ctx) {
44648
45156
  return EXIT_PASS;
44649
45157
  }
44650
45158
  function runOpencode(ctx) {
44651
- const [sub, ...rest] = ctx.positionals;
44652
- if (rest.length > 0) {
44653
- throw new UnknownArgError(
44654
- `opencode ${sub ?? "<none>"}: unexpected argument '${rest[0] ?? ""}' \u2014 usage: border opencode install | status | uninstall`
44655
- );
44656
- }
45159
+ const [sub, ...restAll] = ctx.positionals;
45160
+ const rest = restAll.filter((arg) => arg !== "--json");
45161
+ const USAGE = "usage: border opencode install | status | uninstall | inspect";
44657
45162
  switch (sub) {
44658
45163
  case "install":
44659
- return opencodeInstall(ctx);
44660
45164
  case "status":
44661
- return opencodeStatus(ctx);
44662
45165
  case "uninstall":
44663
- return opencodeUninstall(ctx);
45166
+ case "inspect": {
45167
+ if (rest.length > 0) {
45168
+ throw new UnknownArgError(`opencode ${sub}: unexpected argument '${rest[0] ?? ""}' \u2014 ${USAGE}`);
45169
+ }
45170
+ const table2 = {
45171
+ install: opencodeInstall,
45172
+ status: opencodeStatus,
45173
+ uninstall: opencodeUninstall,
45174
+ inspect: runInspectCore
45175
+ };
45176
+ return table2[sub](ctx);
45177
+ }
44664
45178
  default:
44665
- throw new UnknownArgError(
44666
- `opencode: unknown subcommand '${sub ?? "<none>"}' \u2014 usage: border opencode install | status | uninstall`
44667
- );
45179
+ throw new UnknownArgError(`opencode: unknown subcommand '${sub ?? "<none>"}' \u2014 ${USAGE}`);
44668
45180
  }
44669
45181
  }
44670
45182
 
@@ -44768,7 +45280,7 @@ function confirmRemoteBranch(i) {
44768
45280
  import { spawnSync as spawnSync18 } from "node:child_process";
44769
45281
  import { mkdtempSync as mkdtempSync6, rmSync as rmSync14 } from "node:fs";
44770
45282
  import { tmpdir as tmpdir4 } from "node:os";
44771
- import { dirname as dirname16, join as join29, resolve as resolve18 } from "node:path";
45283
+ import { dirname as dirname17, join as join32, resolve as resolve18 } from "node:path";
44772
45284
  var LANDINGS_REF_PREFIX = "refs/border-landings/";
44773
45285
  async function runLandingVerification(o) {
44774
45286
  if (o.legs.length === 0) return null;
@@ -44818,7 +45330,7 @@ function gitPlain(repoDir, env2, args) {
44818
45330
  for (const [name, value] of Object.entries(env2)) {
44819
45331
  if (!name.startsWith("GIT_")) clean[name] = value;
44820
45332
  }
44821
- clean.GIT_CEILING_DIRECTORIES = dirname16(resolve18(repoDir));
45333
+ clean.GIT_CEILING_DIRECTORIES = dirname17(resolve18(repoDir));
44822
45334
  clean.GIT_OPTIONAL_LOCKS = "0";
44823
45335
  clean.GIT_TERMINAL_PROMPT = "0";
44824
45336
  const r = spawnSync18("git", ["-C", resolve18(repoDir), ...args], { encoding: "utf8", env: clean, timeout: 55e3, maxBuffer: 32 * 1024 * 1024 });
@@ -44843,7 +45355,7 @@ async function defaultScanRemoteTip(o, remote, branch, remoteSha) {
44843
45355
  ownedTempRef = landingRef;
44844
45356
  rev = git.run(["rev-parse", landingRef]).trim();
44845
45357
  }
44846
- const treeRoot = mkdtempSync6(join29(tmpdir4(), "border-landing-tree-"));
45358
+ const treeRoot = mkdtempSync6(join32(tmpdir4(), "border-landing-tree-"));
44847
45359
  try {
44848
45360
  return await scanTipTree(git, treeRoot, rev, o.env);
44849
45361
  } finally {
@@ -45052,14 +45564,14 @@ async function executeYesPush(ctx, loaded) {
45052
45564
  import { mkdtempSync as mkdtempSync7, rmSync as rmSync15, writeFileSync as writeFileSync9 } from "node:fs";
45053
45565
  import { randomBytes } from "node:crypto";
45054
45566
  import { tmpdir as tmpdir5 } from "node:os";
45055
- import { join as join30 } from "node:path";
45567
+ import { join as join33 } from "node:path";
45056
45568
 
45057
45569
  // src/scan/fetch.ts
45058
45570
  import { createHash as createHash18 } from "node:crypto";
45059
45571
  import { basename as basename5 } from "node:path";
45060
45572
  var SCAN_MAX_ARTIFACT_BYTES = 200 * 1024 * 1024;
45061
45573
  var SCAN_REQUEST_TIMEOUT_MS = 6e4;
45062
- var SCAN_USER_AGENT = "border-customs/0.6.0 (+https://github.com/sumteclab/border; border scan)";
45574
+ var SCAN_USER_AGENT = "border-customs/0.8.0 (+https://github.com/sumteclab/border; border scan)";
45063
45575
  var CRATES_DOWNLOAD_HOST = "https://static.crates.io";
45064
45576
  var defaultScanFetcher = async (url2, init) => {
45065
45577
  if (!/^https?:\/\//.test(url2)) throw new EngineRunError(`border scan refuses non-http(s) URL: ${url2}`, null);
@@ -45113,7 +45625,7 @@ async function fetchJson(fetcher, url2, timeoutMs) {
45113
45625
  fail2(`metadata response from ${url2} is not valid JSON`);
45114
45626
  }
45115
45627
  }
45116
- var isRecord2 = (v) => typeof v === "object" && v !== null;
45628
+ var isRecord4 = (v) => typeof v === "object" && v !== null;
45117
45629
  function channelBaseUrl(eco) {
45118
45630
  const ch = publishChannels().find((c) => c.id === eco);
45119
45631
  if (ch === void 0 || ch.defaultUrl.length === 0) {
@@ -45144,7 +45656,7 @@ async function fetchArtifact(spec, o) {
45144
45656
  switch (spec.ecosystem) {
45145
45657
  case "npm": {
45146
45658
  const meta3 = await fetchJson(o.fetcher, npmMetadataUrl(spec.name, spec.version), timeoutMs);
45147
- const dist = isRecord2(meta3) && isRecord2(meta3["dist"]) ? meta3["dist"] : {};
45659
+ const dist = isRecord4(meta3) && isRecord4(meta3["dist"]) ? meta3["dist"] : {};
45148
45660
  const tarball = dist["tarball"];
45149
45661
  if (typeof tarball !== "string" || tarball.length === 0) fail2(`npm metadata for ${label} has no dist.tarball`);
45150
45662
  if (!/^https?:\/\//.test(tarball)) fail2(`npm dist.tarball for ${label} is not an http(s) URL`);
@@ -45155,8 +45667,8 @@ async function fetchArtifact(spec, o) {
45155
45667
  }
45156
45668
  case "pypi": {
45157
45669
  const meta3 = await fetchJson(o.fetcher, pypiMetadataUrl(spec.name, spec.version), timeoutMs);
45158
- const urls = isRecord2(meta3) && Array.isArray(meta3["urls"]) ? meta3["urls"] : [];
45159
- const files = urls.filter(isRecord2);
45670
+ const urls = isRecord4(meta3) && Array.isArray(meta3["urls"]) ? meta3["urls"] : [];
45671
+ const files = urls.filter(isRecord4);
45160
45672
  const sdists = files.filter((f) => f["packagetype"] === "sdist");
45161
45673
  const sdist = sdists.find((f) => typeof f["filename"] === "string" && f["filename"].endsWith(".tar.gz"));
45162
45674
  if (sdist === void 0) {
@@ -45184,7 +45696,7 @@ async function fetchArtifact(spec, o) {
45184
45696
  }
45185
45697
  }
45186
45698
  }
45187
- function sha256Hex3(data) {
45699
+ function sha256Hex4(data) {
45188
45700
  return createHash18("sha256").update(data).digest("hex");
45189
45701
  }
45190
45702
 
@@ -45228,7 +45740,7 @@ function parseScanSpec(raw) {
45228
45740
  }
45229
45741
 
45230
45742
  // src/roundtrip/localArtifact.ts
45231
- import { createReadStream, existsSync as existsSync19, statSync as statSync3 } from "node:fs";
45743
+ import { createReadStream, existsSync as existsSync23, statSync as statSync3 } from "node:fs";
45232
45744
  import { createHash as createHash19 } from "node:crypto";
45233
45745
  import { basename as basename6, resolve as resolve19 } from "node:path";
45234
45746
  var EXT_TABLE = /* @__PURE__ */ new Map([
@@ -45277,7 +45789,7 @@ var GRAMMAR_HINT = {
45277
45789
  };
45278
45790
  function classifyLocalInput(raw, cwd, parsesAsRegistrySpec) {
45279
45791
  const abs = resolve19(cwd, raw);
45280
- if (existsSync19(abs)) {
45792
+ if (existsSync23(abs)) {
45281
45793
  const st = statSync3(abs);
45282
45794
  if (st.isDirectory()) {
45283
45795
  bad3(`'${raw}' resolves to a directory (${abs}) \u2014 local mode needs a single artifact FILE, not a directory`);
@@ -45636,7 +46148,7 @@ function classifyResidue(label, diff, attribution = null) {
45636
46148
 
45637
46149
  // src/roundtrip/orchestrate.ts
45638
46150
  function containerNameFor(label, rand = randomBytes(3).toString("hex")) {
45639
- return `rt-${sha256Hex3(label).slice(0, 8)}-${rand}`;
46151
+ return `rt-${sha256Hex4(label).slice(0, 8)}-${rand}`;
45640
46152
  }
45641
46153
  var shArgv = (name) => {
45642
46154
  if (!/^[A-Za-z0-9@._+/-]+$/.test(name)) {
@@ -45757,7 +46269,7 @@ function runPipeline2(exec, profile, spec, label, hostPath, container, closure,
45757
46269
  return {
45758
46270
  findings: classifyResidue(label, diffManifests(m1, m3), attribution),
45759
46271
  installDelta,
45760
- baselineDigest: sha256Hex3([...m1.keys()].join("\n"))
46272
+ baselineDigest: sha256Hex4([...m1.keys()].join("\n"))
45761
46273
  };
45762
46274
  } finally {
45763
46275
  try {
@@ -45791,12 +46303,12 @@ async function runRoundtripCore(ctx, deps = {}) {
45791
46303
  ctx.stderr("roundtrip: docker unavailable \u2014 cannot verify");
45792
46304
  return EXIT_ERROR;
45793
46305
  }
45794
- const baseDir = mkdtempSync7(join30(tmpdir5(), "border-roundtrip-"));
46306
+ const baseDir = mkdtempSync7(join33(tmpdir5(), "border-roundtrip-"));
45795
46307
  try {
45796
46308
  const localRead = local !== null ? await readLocalArtifact(local.absPath) : null;
45797
46309
  const bytes = localRead?.bytes ?? (await fetchArtifact(spec, { fetcher: deps.fetcher ?? defaultScanFetcher })).bytes;
45798
- const artifactSha256 = localRead !== null ? localRead.sha256 : sha256Hex3(bytes);
45799
- const hostPath = join30(baseDir, "artifact");
46310
+ const artifactSha256 = localRead !== null ? localRead.sha256 : sha256Hex4(bytes);
46311
+ const hostPath = join33(baseDir, "artifact");
45800
46312
  writeFileSync9(hostPath, bytes);
45801
46313
  const container = containerNameFor(label);
45802
46314
  const closure = spec.ecosystem === "pypi" ? resolvePipClosure(exec, profile, hostPath, label, spec) : null;
@@ -45804,12 +46316,12 @@ async function runRoundtripCore(ctx, deps = {}) {
45804
46316
  const verdict = computeVerdict(findings);
45805
46317
  const report = {
45806
46318
  schemaVersion: 1,
45807
- key: sha256Hex3(`roundtrip:${label}:${baselineDigest}:${verdict}`),
46319
+ key: sha256Hex4(`roundtrip:${label}:${baselineDigest}:${verdict}`),
45808
46320
  head: baselineDigest.slice(0, 40),
45809
46321
  dirty: false,
45810
46322
  exposureSet: [label],
45811
46323
  refSet: [],
45812
- rulesHash: sha256Hex3(`${profile.image}|${profile.install(profile.artifactPath, spec).join(" ")}|${profile.uninstall(spec).join(" ")}`),
46324
+ rulesHash: sha256Hex4(`${profile.image}|${profile.install(profile.artifactPath, spec).join(" ")}|${profile.uninstall(spec).join(" ")}`),
45813
46325
  verdict,
45814
46326
  counts: countFindings(findings),
45815
46327
  findings,
@@ -45899,7 +46411,7 @@ var runRoundtrip = (ctx) => runRoundtripCore(ctx);
45899
46411
  // src/scan/scan.ts
45900
46412
  import { mkdtempSync as mkdtempSync8 } from "node:fs";
45901
46413
  import { tmpdir as tmpdir6 } from "node:os";
45902
- import { join as join32 } from "node:path";
46414
+ import { join as join35 } from "node:path";
45903
46415
 
45904
46416
  // src/scan/config.ts
45905
46417
  function buildScanConfigYaml(eco) {
@@ -45926,8 +46438,8 @@ function synthesizeScanConfig(eco) {
45926
46438
  }
45927
46439
 
45928
46440
  // src/scan/materialize.ts
45929
- import { existsSync as existsSync20, mkdirSync as mkdirSync13, readdirSync as readdirSync10, readFileSync as readFileSync19, rmSync as rmSync16, writeFileSync as writeFileSync10 } from "node:fs";
45930
- import { join as join31 } from "node:path";
46441
+ import { existsSync as existsSync24, mkdirSync as mkdirSync13, readdirSync as readdirSync11, readFileSync as readFileSync21, rmSync as rmSync16, writeFileSync as writeFileSync10 } from "node:fs";
46442
+ import { join as join34 } from "node:path";
45931
46443
  var CRATES_ENVELOPE_FILES = ["Cargo.toml.orig", ".cargo-ok", ".cargo_vcs_info.json"];
45932
46444
  var fail3 = (why) => {
45933
46445
  throw new Error(`border scan cannot materialize the artifact (${why}) \u2014 refusing to stage an unusable tree (fail-closed)`);
@@ -45941,15 +46453,15 @@ function extractFlat(archive, destDir) {
45941
46453
  }
45942
46454
  }
45943
46455
  function unwrapSingleDir(dir) {
45944
- const entries = readdirSync10(dir, { withFileTypes: true });
46456
+ const entries = readdirSync11(dir, { withFileTypes: true });
45945
46457
  const dirs = entries.filter((e) => e.isDirectory());
45946
46458
  if (entries.length === 1 && dirs.length === 1 && dirs[0] !== void 0) {
45947
- return join31(dir, dirs[0].name);
46459
+ return join34(dir, dirs[0].name);
45948
46460
  }
45949
46461
  return dir;
45950
46462
  }
45951
46463
  function requireManifest(repoDir, ecosystem, candidates) {
45952
- if (!candidates.some((rel) => existsSync20(join31(repoDir, rel)))) {
46464
+ if (!candidates.some((rel) => existsSync24(join34(repoDir, rel)))) {
45953
46465
  fail3(`${ecosystem} package tree has none of [${candidates.join(", ")}] at its root`);
45954
46466
  }
45955
46467
  }
@@ -45959,18 +46471,18 @@ function materializePackage(o) {
45959
46471
  `);
45960
46472
  });
45961
46473
  const safe = safeArtifactName(o.filename, `${o.ecosystem}-artifact`);
45962
- const archive = join31(o.baseDir, safe);
46474
+ const archive = join34(o.baseDir, safe);
45963
46475
  writeFileSync10(archive, o.bytes);
45964
- const extractDir = join31(o.baseDir, "extract");
46476
+ const extractDir = join34(o.baseDir, "extract");
45965
46477
  mkdirSync13(extractDir, { recursive: true });
45966
46478
  extractFlat(archive, extractDir);
45967
46479
  let repoDir = unwrapSingleDir(extractDir);
45968
46480
  if (o.ecosystem === "rubygems") {
45969
46481
  const outer = repoDir;
45970
46482
  const dataCandidates = findNativeMissArchives(outer).filter((f) => f.endsWith("data.tar.gz"));
45971
- const inner = dataCandidates[0] ?? join31(outer, "data.tar.gz");
45972
- if (!existsSync20(inner)) fail3(".gem archive contains no data.tar.gz");
45973
- const gemTree = join31(o.baseDir, "gem-tree");
46483
+ const inner = dataCandidates[0] ?? join34(outer, "data.tar.gz");
46484
+ if (!existsSync24(inner)) fail3(".gem archive contains no data.tar.gz");
46485
+ const gemTree = join34(o.baseDir, "gem-tree");
45974
46486
  mkdirSync13(gemTree, { recursive: true });
45975
46487
  extractFlat(inner, gemTree);
45976
46488
  repoDir = unwrapSingleDir(gemTree);
@@ -45981,11 +46493,11 @@ function materializePackage(o) {
45981
46493
  } else {
45982
46494
  requireManifest(repoDir, o.ecosystem, ["Cargo.toml"]);
45983
46495
  for (const file2 of CRATES_ENVELOPE_FILES) {
45984
- const path4 = join31(repoDir, file2);
45985
- if (!existsSync20(path4)) continue;
45986
- const original = readFileSync19(path4);
46496
+ const path4 = join34(repoDir, file2);
46497
+ if (!existsSync24(path4)) continue;
46498
+ const original = readFileSync21(path4);
45987
46499
  rmSync16(path4, { force: true });
45988
- note(`border scan: crates envelope normalized: ${file2} sha256=${sha256Hex3(original)}`);
46500
+ note(`border scan: crates envelope normalized: ${file2} sha256=${sha256Hex4(original)}`);
45989
46501
  }
45990
46502
  }
45991
46503
  const env2 = o.env;
@@ -46011,7 +46523,7 @@ async function runScanCore(ctx, deps = {}) {
46011
46523
  }
46012
46524
  const cfg = synthesizeScanConfig(spec.ecosystem);
46013
46525
  const fetcher = deps.fetcher ?? defaultScanFetcher;
46014
- const baseDir = mkdtempSync8(join32(tmpdir6(), "border-scan-"));
46526
+ const baseDir = mkdtempSync8(join35(tmpdir6(), "border-scan-"));
46015
46527
  try {
46016
46528
  const { bytes, filename } = await fetchArtifact(spec, { fetcher });
46017
46529
  const env2 = { ...ctx.env };
@@ -46022,12 +46534,12 @@ async function runScanCore(ctx, deps = {}) {
46022
46534
  const verdict = computeVerdict(findings);
46023
46535
  const report = {
46024
46536
  schemaVersion: 1,
46025
- key: sha256Hex3(`scan:${spec.ecosystem}:${spec.name}@${spec.version}:${head}`),
46537
+ key: sha256Hex4(`scan:${spec.ecosystem}:${spec.name}@${spec.version}:${head}`),
46026
46538
  head,
46027
46539
  dirty: false,
46028
46540
  exposureSet: [`${spec.ecosystem}:${spec.name}@${spec.version}`],
46029
46541
  refSet: [],
46030
- rulesHash: sha256Hex3(buildScanConfigYaml(spec.ecosystem)),
46542
+ rulesHash: sha256Hex4(buildScanConfigYaml(spec.ecosystem)),
46031
46543
  verdict,
46032
46544
  counts: countFindings(findings),
46033
46545
  findings,
@@ -46168,7 +46680,7 @@ function usage() {
46168
46680
  " llm-ingest record provenance captured by the harness",
46169
46681
  " scan inspect a third-party package ([ecosystem:]name@version) for residue",
46170
46682
  " roundtrip install+uninstall a package (registry spec or local artifact file) in a throwaway container, report uninstall residue",
46171
- " opencode install|status|uninstall the official opencode plugin adapter (files under $XDG_CONFIG_HOME/opencode)",
46683
+ " opencode install|status|uninstall|inspect the official opencode plugin adapter (files under $XDG_CONFIG_HOME/opencode; inspect = read-only V1 load-surface audit)",
46172
46684
  " exfil read-only exfil-lens scan of <ref|url> [ref...]: tip tree + tag notes (--deep adds reachable-history blobs + commit messages)",
46173
46685
  "",
46174
46686
  "flags:",
@@ -46311,7 +46823,7 @@ function isMainModule({ argv1, moduleUrl, realpath }) {
46311
46823
  return p;
46312
46824
  }
46313
46825
  };
46314
- return pathToFileURL(canonical(argv1)).href === pathToFileURL(canonical(fileURLToPath6(moduleUrl))).href;
46826
+ return pathToFileURL(canonical(argv1)).href === pathToFileURL(canonical(fileURLToPath7(moduleUrl))).href;
46315
46827
  }
46316
46828
  if (isMainModule({ argv1: process.argv[1], moduleUrl: import.meta.url, realpath: realpathSync.native })) {
46317
46829
  const outcome = run(process.argv.slice(2));