border-customs 0.4.1 → 0.5.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 fileURLToPath5, pathToFileURL } from "node:url";
13032
+ import { fileURLToPath as fileURLToPath6, pathToFileURL } from "node:url";
13033
13033
 
13034
13034
  // src/cli.ts
13035
13035
  import { parseArgs } from "node:util";
@@ -37940,12 +37940,12 @@ function packOnce(o) {
37940
37940
  throw new EngineRunError(`npm pack reported '${report.filename}' but no tarball landed in ${o.distDir}`, null);
37941
37941
  }
37942
37942
  const data = readFileSync3(tarballAbs);
37943
- const sha2563 = createHash3("sha256").update(data).digest("hex");
37943
+ const sha2564 = createHash3("sha256").update(data).digest("hex");
37944
37944
  const sha1 = createHash3("sha1").update(data).digest("hex");
37945
37945
  if (sha1 !== report.shasum || data.byteLength !== report.size) {
37946
37946
  throw new EngineRunError(`npm pack report disagrees with the tarball bytes for ${report.filename} \u2014 corrupted or swapped artifact, failing closed`, null);
37947
37947
  }
37948
- return { filename: report.filename, sha256: sha2563, bytes: data.byteLength, tarballAbs };
37948
+ return { filename: report.filename, sha256: sha2564, bytes: data.byteLength, tarballAbs };
37949
37949
  }
37950
37950
  function publintFinding(o, tarballAbs, identity) {
37951
37951
  const candidates = o.publintBinPath !== void 0 ? [o.publintBinPath] : [
@@ -38950,7 +38950,7 @@ async function runNpmArtifactStage(o) {
38950
38950
  const stateDir = join10(repoDir, BORDER_STATE_DIR);
38951
38951
  const distDir = join10(stateDir, "dist");
38952
38952
  mkdirSync3(distDir, { recursive: true });
38953
- const { filename, sha256: sha2563, bytes, tarballAbs } = packOnce({
38953
+ const { filename, sha256: sha2564, bytes, tarballAbs } = packOnce({
38954
38954
  repoDir,
38955
38955
  distDir,
38956
38956
  ...o.env !== void 0 ? { env: o.env } : {},
@@ -38958,7 +38958,7 @@ async function runNpmArtifactStage(o) {
38958
38958
  ...o.npmTimeoutMs !== void 0 ? { npmTimeoutMs: o.npmTimeoutMs } : {}
38959
38959
  });
38960
38960
  const findings = [];
38961
- const extractDir = join10(stateDir, "tmp", sha2563.slice(0, 8));
38961
+ const extractDir = join10(stateDir, "tmp", sha2564.slice(0, 8));
38962
38962
  rmSync3(extractDir, { recursive: true, force: true });
38963
38963
  mkdirSync3(extractDir, { recursive: true });
38964
38964
  try {
@@ -39063,7 +39063,7 @@ async function runNpmArtifactStage(o) {
39063
39063
  if (publint !== null) findings.push(publint);
39064
39064
  return {
39065
39065
  findings,
39066
- artifact: { file: `${BORDER_STATE_DIR}/dist/${filename}`, sha256: sha2563, bytes }
39066
+ artifact: { file: `${BORDER_STATE_DIR}/dist/${filename}`, sha256: sha2564, bytes }
39067
39067
  };
39068
39068
  }
39069
39069
 
@@ -39707,6 +39707,7 @@ import { spawnSync as spawnSync7 } from "node:child_process";
39707
39707
  import { createHash as createHash7 } from "node:crypto";
39708
39708
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, readdirSync as readdirSync4, rmSync as rmSync5 } from "node:fs";
39709
39709
  import { join as join13 } from "node:path";
39710
+ import { gunzipSync as gunzipSync2 } from "node:zlib";
39710
39711
 
39711
39712
  // src/artifacts/rubygems.ts
39712
39713
  import { createHash as createHash6 } from "node:crypto";
@@ -40144,9 +40145,9 @@ async function runRubygemsArtifactStage(o) {
40144
40145
  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);
40145
40146
  }
40146
40147
  const bytes = readFileSync7(dist);
40147
- const sha2563 = createHash6("sha256").update(bytes).digest("hex");
40148
+ const sha2564 = createHash6("sha256").update(bytes).digest("hex");
40148
40149
  const findings = [];
40149
- const extractDir = join12(stateDir, "tmp", sha2563.slice(0, 8));
40150
+ const extractDir = join12(stateDir, "tmp", sha2564.slice(0, 8));
40150
40151
  rmSync4(extractDir, { recursive: true, force: true });
40151
40152
  mkdirSync4(extractDir, { recursive: true });
40152
40153
  const dataDir = `${extractDir}-data`;
@@ -40224,7 +40225,7 @@ async function runRubygemsArtifactStage(o) {
40224
40225
  }
40225
40226
  return {
40226
40227
  findings,
40227
- artifact: { file: `${BORDER_STATE_DIR}/dist/${gemName}`, sha256: sha2563, bytes: bytes.length }
40228
+ artifact: { file: `${BORDER_STATE_DIR}/dist/${gemName}`, sha256: sha2564, bytes: bytes.length }
40228
40229
  };
40229
40230
  }
40230
40231
 
@@ -40235,6 +40236,64 @@ function artifactTargets() {
40235
40236
  function sha256File(path4) {
40236
40237
  return createHash7("sha256").update(readFileSync8(path4)).digest("hex");
40237
40238
  }
40239
+ function octalField(block, start, end) {
40240
+ const text = block.subarray(start, end).toString("utf8").replace(/[\0 ].*$/, "");
40241
+ if (text === "") return 0;
40242
+ if (!/^[0-7]+$/.test(text)) return null;
40243
+ return Number.parseInt(text, 8);
40244
+ }
40245
+ function tarEntries(buf) {
40246
+ const magic = buf.subarray(257, 263).toString("latin1");
40247
+ if (!magic.startsWith("ustar")) return null;
40248
+ const out = [];
40249
+ let off = 0;
40250
+ while (off + 512 <= buf.length) {
40251
+ const header = buf.subarray(off, off + 512);
40252
+ if (header.every((b) => b === 0)) return out;
40253
+ const size = octalField(header, 124, 136);
40254
+ if (size === null) return null;
40255
+ const mode = octalField(header, 100, 108);
40256
+ if (mode === null) return null;
40257
+ const typeFlag = header[156] ?? 48;
40258
+ const body = off + 512;
40259
+ if (body + size > buf.length) return null;
40260
+ const name = header.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
40261
+ const linkName = header.subarray(157, 257).toString("utf8").replace(/\0.*$/, "");
40262
+ if (typeFlag !== 120 && typeFlag !== 103 && typeFlag !== 76 && typeFlag !== 75) {
40263
+ out.push({ name, member: { content: buf.subarray(body, body + size), typeFlag, mode, linkName } });
40264
+ }
40265
+ off = body + Math.ceil(size / 512) * 512;
40266
+ }
40267
+ return out;
40268
+ }
40269
+ var GEM_METADATA_STAMP_RE = /^(rubygems_version|gem_version|date):/;
40270
+ var GEM_LEGACY_EMPTY_RE = /^(autorequire|description|email|homepage|post_install_message|signing_key):[ ]*$/;
40271
+ function sha256Hex(buf) {
40272
+ return createHash7("sha256").update(buf).digest("hex");
40273
+ }
40274
+ function gemContentDigest(bytes) {
40275
+ const outer = tarEntries(bytes);
40276
+ if (outer === null) return null;
40277
+ const meta3 = outer.find((e) => e.name === "metadata.gz")?.member;
40278
+ const data = outer.find((e) => e.name === "data.tar.gz")?.member;
40279
+ if (meta3 === void 0 || data === void 0) return null;
40280
+ let metadata;
40281
+ let inner;
40282
+ try {
40283
+ metadata = gunzipSync2(meta3.content).toString("utf8");
40284
+ inner = gunzipSync2(data.content);
40285
+ } catch {
40286
+ return null;
40287
+ }
40288
+ const innerEntries = tarEntries(inner);
40289
+ if (innerEntries === null) return null;
40290
+ const chain = createHash7("sha256");
40291
+ for (const { name, member: m } of innerEntries) {
40292
+ chain.update(name).update("\0").update(String(m.typeFlag)).update("\0").update(String(m.mode & 73)).update("\0").update(m.linkName).update("\0").update(sha256Hex(m.content)).update("\0");
40293
+ }
40294
+ const metaStripped = metadata.split("\n").filter((line) => !GEM_METADATA_STAMP_RE.test(line) && !GEM_LEGACY_EMPTY_RE.test(line)).join("\n");
40295
+ return createHash7("sha256").update("border-gem-content-v1\0").update(chain.digest("hex")).update("\0").update(metaStripped).digest("hex");
40296
+ }
40238
40297
  var packSeq = 0;
40239
40298
  function packNpmArtifacts(repoDir, o = {}) {
40240
40299
  packSeq += 1;
@@ -40292,7 +40351,10 @@ function packRubygemsArtifacts(repoDir, o = {}) {
40292
40351
  if (r.error !== void 0 || r.status !== 0) return null;
40293
40352
  const files = readdirSync4(dest).filter((f) => f.endsWith(".gem")).sort();
40294
40353
  if (files.length === 0) return null;
40295
- return files.map((f) => ({ file: f, sha256: sha256File(join13(dest, f)) }));
40354
+ return files.map((f) => {
40355
+ const raw = readFileSync8(join13(dest, f));
40356
+ return { file: f, sha256: sha256Hex(raw), contentDigest: gemContentDigest(raw) };
40357
+ });
40296
40358
  } finally {
40297
40359
  rmSync5(dest, { recursive: true, force: true });
40298
40360
  }
@@ -40305,11 +40367,30 @@ var REPACKERS = {
40305
40367
  function repackChannels() {
40306
40368
  return publishChannels().filter((c) => c.freshness === "repack").map((c) => c.id);
40307
40369
  }
40308
- function sameArtifacts(a, b, extensions) {
40309
- const digests = (xs) => xs.filter((x) => extensions.some((ext) => x.file.slice(x.file.lastIndexOf("/") + 1).endsWith(ext))).map((x) => `${x.file.slice(x.file.lastIndexOf("/") + 1)}\0${x.sha256}`).sort();
40370
+ function gemParity(entry, repoDir) {
40371
+ if (entry.contentDigest !== void 0) return entry.contentDigest;
40372
+ try {
40373
+ return gemContentDigest(readFileSync8(join13(repoDir, entry.file)));
40374
+ } catch {
40375
+ return null;
40376
+ }
40377
+ }
40378
+ function sameArtifacts(a, b, extensions, repoDir) {
40379
+ const digests = (xs) => {
40380
+ const out = [];
40381
+ for (const x of xs) {
40382
+ const base = x.file.slice(x.file.lastIndexOf("/") + 1);
40383
+ const ext = extensions.find((e) => base.endsWith(e));
40384
+ if (ext === void 0) continue;
40385
+ const parity = ext === ".gem" ? gemParity(x, repoDir) : x.sha256;
40386
+ if (parity === null) return null;
40387
+ out.push(`${base}\0${parity}`);
40388
+ }
40389
+ return out.sort();
40390
+ };
40310
40391
  const ka = digests(a);
40311
40392
  const kb = digests(b);
40312
- return ka.length === kb.length && ka.every((x, i) => x === kb[i]);
40393
+ return ka !== null && kb !== null && ka.length === kb.length && ka.every((x, i) => x === kb[i]);
40313
40394
  }
40314
40395
  function verifyArtifactFreshness(record2, ctx, repoDir, o = {}) {
40315
40396
  if (!record2.effectiveTargets.some((t) => artifactTargets().includes(t))) return true;
@@ -40331,7 +40412,8 @@ function verifyArtifactFreshness(record2, ctx, repoDir, o = {}) {
40331
40412
  needed.flatMap((id) => {
40332
40413
  const channel = publishChannels().find((c) => c.id === id);
40333
40414
  return channel?.artifactExtensions ?? [];
40334
- })
40415
+ }),
40416
+ repoDir
40335
40417
  );
40336
40418
  }
40337
40419
 
@@ -40809,17 +40891,17 @@ function requirePassedRecord(kind, i) {
40809
40891
  function rehashRecordedArtifacts(kind, extensions, record2, repoDir) {
40810
40892
  const conventions = extensions;
40811
40893
  const entries = (record2.artifacts ?? []).filter((a) => conventions.some((ext) => a.file.endsWith(ext)));
40812
- const fail3 = (detail) => ({ ok: false, message: `artifact changed since check \u2014 ${detail}` });
40894
+ const fail4 = (detail) => ({ ok: false, message: `artifact changed since check \u2014 ${detail}` });
40813
40895
  if (entries.length === 0) {
40814
- return fail3(`the PASSED check recorded no ${kind} artifacts (${conventions.join("/")}) to re-verify`);
40896
+ return fail4(`the PASSED check recorded no ${kind} artifacts (${conventions.join("/")}) to re-verify`);
40815
40897
  }
40816
40898
  const files = [];
40817
40899
  for (const a of entries) {
40818
40900
  const rel = join17(DIST_DIR, basename4(a.file));
40819
40901
  const abs = join17(repoDir, rel);
40820
- if (!existsSync11(abs)) return fail3(`${rel} is missing from the working tree`);
40902
+ if (!existsSync11(abs)) return fail4(`${rel} is missing from the working tree`);
40821
40903
  const digest2 = createHash10("sha256").update(readFileSync10(abs)).digest("hex");
40822
- if (digest2 !== a.sha256) return fail3(`${rel} no longer hashes to the digest recorded by the check`);
40904
+ if (digest2 !== a.sha256) return fail4(`${rel} no longer hashes to the digest recorded by the check`);
40823
40905
  files.push(rel);
40824
40906
  }
40825
40907
  return { ok: true, files: files.sort() };
@@ -41278,7 +41360,7 @@ function npmExposureCoords(cfg, read) {
41278
41360
  return { name, version: version2 };
41279
41361
  }
41280
41362
  function classifyNpmVersionView(o) {
41281
- const fail3 = (why) => {
41363
+ const fail4 = (why) => {
41282
41364
  throw new EngineRunError(
41283
41365
  `npm registry probe inconclusive (${why}); refusing to assume the version is absent \u2014 registry unreachable or returned unexpected output, push blocked until the probe succeeds`,
41284
41366
  o.status
@@ -41286,19 +41368,19 @@ function classifyNpmVersionView(o) {
41286
41368
  };
41287
41369
  if (o.status === 0) {
41288
41370
  const text = o.stdout.trim();
41289
- if (text.length === 0) return fail3("exit 0 with empty stdout");
41371
+ if (text.length === 0) return fail4("exit 0 with empty stdout");
41290
41372
  let body;
41291
41373
  try {
41292
41374
  body = JSON.parse(text);
41293
41375
  } catch {
41294
- return fail3("exit 0 with malformed JSON body");
41376
+ return fail4("exit 0 with malformed JSON body");
41295
41377
  }
41296
- if (typeof body !== "object" || body === null) return fail3("exit 0 with non-object JSON body");
41378
+ if (typeof body !== "object" || body === null) return fail4("exit 0 with non-object JSON body");
41297
41379
  return "present";
41298
41380
  }
41299
41381
  if (/code E404\b/.test(o.stderr) || /\b404 Not Found\b/.test(o.stderr)) return "absent";
41300
41382
  const m = /code (E[A-Z0-9_]+|ERR_[A-Z0-9_]+)/.exec(o.stderr);
41301
- return fail3(m === null ? `exit ${String(o.status)} without a 404 marker` : `npm error ${m[1]}`);
41383
+ return fail4(m === null ? `exit ${String(o.status)} without a 404 marker` : `npm error ${m[1]}`);
41302
41384
  }
41303
41385
  function npmNameClaimed(o) {
41304
41386
  if (o.status === 0) return true;
@@ -41705,9 +41787,9 @@ async function runCargoArtifactStage(o) {
41705
41787
  const dist = join18(distDir, crateName);
41706
41788
  renameSync(packed, dist);
41707
41789
  const bytes = readFileSync11(dist);
41708
- const sha2563 = createHash11("sha256").update(bytes).digest("hex");
41790
+ const sha2564 = createHash11("sha256").update(bytes).digest("hex");
41709
41791
  const findings = [];
41710
- const extractDir = join18(stateDir, "tmp", sha2563.slice(0, 8));
41792
+ const extractDir = join18(stateDir, "tmp", sha2564.slice(0, 8));
41711
41793
  rmSync8(extractDir, { recursive: true, force: true });
41712
41794
  mkdirSync8(extractDir, { recursive: true });
41713
41795
  try {
@@ -41760,7 +41842,7 @@ async function runCargoArtifactStage(o) {
41760
41842
  }
41761
41843
  return {
41762
41844
  findings,
41763
- artifact: { file: `${BORDER_STATE_DIR}/dist/${crateName}`, sha256: sha2563, bytes: bytes.length }
41845
+ artifact: { file: `${BORDER_STATE_DIR}/dist/${crateName}`, sha256: sha2564, bytes: bytes.length }
41764
41846
  };
41765
41847
  } finally {
41766
41848
  rmSync8(targetDir, { recursive: true, force: true });
@@ -43618,6 +43700,193 @@ async function runLlmRequest(ctx) {
43618
43700
  return runLlmRequestCore(ctx);
43619
43701
  }
43620
43702
 
43703
+ // src/commands/opencode.ts
43704
+ import { createHash as createHash15 } from "node:crypto";
43705
+ import { existsSync as existsSync17, lstatSync, mkdirSync as mkdirSync11, readFileSync as readFileSync16, rmSync as rmSync12, writeFileSync as writeFileSync7 } from "node:fs";
43706
+ import os3 from "node:os";
43707
+ import { dirname as dirname12, join as join26 } from "node:path";
43708
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
43709
+ var PLUGIN_MARKER = "// border-opencode-plugin v";
43710
+ var COMMAND_MARKER = "<!-- border-opencode-command -->";
43711
+ function opencodeConfigRoot(env2) {
43712
+ const xdg = env2["XDG_CONFIG_HOME"] ?? "";
43713
+ if (xdg.length > 0) return xdg;
43714
+ const home = env2["HOME"] ?? "";
43715
+ return join26(home.length > 0 ? home : os3.homedir(), ".config");
43716
+ }
43717
+ function resolvePluginAsset(moduleUrl, fileName) {
43718
+ const here = dirname12(fileURLToPath5(moduleUrl));
43719
+ const candidates = [
43720
+ join26(here, "..", "plugin", fileName),
43721
+ // dist mode (bundled): <root>/plugin/<f>
43722
+ join26(here, "..", "..", "plugin", fileName)
43723
+ // src mode: <root>/plugin/<f>
43724
+ ];
43725
+ return candidates.find((p) => existsSync17(p)) ?? candidates[1];
43726
+ }
43727
+ function targetsFor(env2) {
43728
+ const root = join26(opencodeConfigRoot(env2), "opencode");
43729
+ return [
43730
+ { fileName: "border.ts", destination: join26(root, "plugins", "border.ts"), marker: PLUGIN_MARKER },
43731
+ { fileName: "border-command.md", destination: join26(root, "commands", "border.md"), marker: COMMAND_MARKER }
43732
+ ];
43733
+ }
43734
+ function sha2563(bytes) {
43735
+ return createHash15("sha256").update(bytes).digest("hex");
43736
+ }
43737
+ function firstLine(bytes) {
43738
+ const text = bytes.toString("utf8");
43739
+ const newline = text.indexOf("\n");
43740
+ return newline < 0 ? text : text.slice(0, newline);
43741
+ }
43742
+ function carriesMarker(bytes, marker) {
43743
+ return firstLine(bytes).startsWith(marker);
43744
+ }
43745
+ function fail(ctx, message) {
43746
+ ctx.stderr(`border: opencode: ${message}`);
43747
+ return EXIT_ERROR;
43748
+ }
43749
+ function readPackagedAsset(ctx, fileName) {
43750
+ const source = resolvePluginAsset(import.meta.url, fileName);
43751
+ try {
43752
+ return readFileSync16(source);
43753
+ } catch {
43754
+ ctx.stderr(`border: opencode: packaged asset is missing (${source})`);
43755
+ ctx.stderr("border: opencode: the border install looks broken \u2014 `npm i -g border-customs` or a fresh checkout should carry plugin/.");
43756
+ return null;
43757
+ }
43758
+ }
43759
+ function readTarget(destination) {
43760
+ try {
43761
+ return readFileSync16(destination);
43762
+ } catch {
43763
+ return null;
43764
+ }
43765
+ }
43766
+ function requirePlainDestination(ctx, destination) {
43767
+ let shape;
43768
+ try {
43769
+ shape = lstatSync(destination);
43770
+ } catch {
43771
+ return null;
43772
+ }
43773
+ if (!shape.isFile() || shape.isSymbolicLink()) {
43774
+ return fail(
43775
+ ctx,
43776
+ `refusing to touch ${destination} \u2014 the path exists but is not a plain regular file (directories and symlinks are never followed, entered, or destroyed)`
43777
+ );
43778
+ }
43779
+ return null;
43780
+ }
43781
+ function classify(target, current, packaged) {
43782
+ if (current === null) return "absent";
43783
+ if (!carriesMarker(current, target.marker)) return "foreign";
43784
+ return current.equals(packaged) ? "up-to-date" : "outdated";
43785
+ }
43786
+ function requireNoForeign(ctx, targets) {
43787
+ for (const target of targets) {
43788
+ const shape = requirePlainDestination(ctx, target.destination);
43789
+ if (shape !== null) return shape;
43790
+ const current = readTarget(target.destination);
43791
+ if (current !== null && !carriesMarker(current, target.marker)) {
43792
+ return fail(
43793
+ ctx,
43794
+ `refusing to touch ${target.destination} \u2014 the file exists but is not ours (its first line lacks the marker '${target.marker}'); move it aside manually, nothing was written anywhere`
43795
+ );
43796
+ }
43797
+ }
43798
+ return null;
43799
+ }
43800
+ function prepareTargets(ctx, targets) {
43801
+ const prepared = [];
43802
+ for (const target of targets) {
43803
+ const packaged = readPackagedAsset(ctx, target.fileName);
43804
+ if (packaged === null) return null;
43805
+ prepared.push({ target, packaged });
43806
+ }
43807
+ return prepared;
43808
+ }
43809
+ function opencodeInstall(ctx) {
43810
+ const targets = targetsFor(ctx.env);
43811
+ const prepared = prepareTargets(ctx, targets);
43812
+ if (prepared === null) return EXIT_ERROR;
43813
+ const blocked = requireNoForeign(ctx, targets);
43814
+ if (blocked !== null) return blocked;
43815
+ for (const { target, packaged } of prepared) {
43816
+ const current = readTarget(target.destination);
43817
+ const state = classify(target, current, packaged);
43818
+ if (state === "foreign") {
43819
+ return fail(
43820
+ ctx,
43821
+ `refusing to touch ${target.destination} \u2014 the file exists but is not ours (its first line lacks the marker '${target.marker}'); move it aside manually, nothing was written anywhere`
43822
+ );
43823
+ }
43824
+ if (state === "up-to-date") {
43825
+ ctx.stdout(`${target.destination}: up to date`);
43826
+ continue;
43827
+ }
43828
+ mkdirSync11(dirname12(target.destination), { recursive: true });
43829
+ writeFileSync7(target.destination, packaged);
43830
+ ctx.stdout(`${target.destination}: ${state === "outdated" ? "updated" : "installed"}`);
43831
+ }
43832
+ ctx.stdout("note: restart opencode to load the plugin \u2014 tools and commands are scanned at startup.");
43833
+ return EXIT_PASS;
43834
+ }
43835
+ function opencodeStatus(ctx) {
43836
+ const targets = targetsFor(ctx.env);
43837
+ const prepared = prepareTargets(ctx, targets);
43838
+ if (prepared === null) return EXIT_ERROR;
43839
+ for (const { target, packaged } of prepared) {
43840
+ const current = readTarget(target.destination);
43841
+ const state = classify(target, current, packaged);
43842
+ const installedSha = current === null ? "-" : sha2563(current);
43843
+ ctx.stdout(`${target.destination}: ${state} installed=${installedSha} packaged=${sha2563(packaged)}`);
43844
+ }
43845
+ return EXIT_PASS;
43846
+ }
43847
+ function opencodeUninstall(ctx) {
43848
+ const targets = targetsFor(ctx.env);
43849
+ const blocked = requireNoForeign(ctx, targets);
43850
+ if (blocked !== null) return blocked;
43851
+ for (const target of targets) {
43852
+ const current = readTarget(target.destination);
43853
+ if (current === null) {
43854
+ ctx.stdout(`${target.destination}: absent (nothing to remove)`);
43855
+ continue;
43856
+ }
43857
+ if (!carriesMarker(current, target.marker)) {
43858
+ return fail(
43859
+ ctx,
43860
+ `refusing to remove ${target.destination} \u2014 the file is not ours (its first line lacks the marker '${target.marker}'); nothing was removed anywhere`
43861
+ );
43862
+ }
43863
+ rmSync12(target.destination);
43864
+ ctx.stdout(`${target.destination}: removed`);
43865
+ }
43866
+ ctx.stdout("note: restart opencode for the tool and command to disappear.");
43867
+ return EXIT_PASS;
43868
+ }
43869
+ function runOpencode(ctx) {
43870
+ const [sub, ...rest] = ctx.positionals;
43871
+ if (rest.length > 0) {
43872
+ throw new UnknownArgError(
43873
+ `opencode ${sub ?? "<none>"}: unexpected argument '${rest[0] ?? ""}' \u2014 usage: border opencode install | status | uninstall`
43874
+ );
43875
+ }
43876
+ switch (sub) {
43877
+ case "install":
43878
+ return opencodeInstall(ctx);
43879
+ case "status":
43880
+ return opencodeStatus(ctx);
43881
+ case "uninstall":
43882
+ return opencodeUninstall(ctx);
43883
+ default:
43884
+ throw new UnknownArgError(
43885
+ `opencode: unknown subcommand '${sub ?? "<none>"}' \u2014 usage: border opencode install | status | uninstall`
43886
+ );
43887
+ }
43888
+ }
43889
+
43621
43890
  // src/push/git.ts
43622
43891
  import { spawnSync as spawnSync15 } from "node:child_process";
43623
43892
  var DIVERGED_MESSAGE = "history diverged; border never force-pushes; resolve manually";
@@ -43883,17 +44152,17 @@ async function executeYesPush(ctx, loaded) {
43883
44152
  }
43884
44153
 
43885
44154
  // src/roundtrip/orchestrate.ts
43886
- import { mkdtempSync as mkdtempSync5, rmSync as rmSync12, writeFileSync as writeFileSync7 } from "node:fs";
44155
+ import { mkdtempSync as mkdtempSync5, rmSync as rmSync13, writeFileSync as writeFileSync8 } from "node:fs";
43887
44156
  import { randomBytes } from "node:crypto";
43888
44157
  import { tmpdir as tmpdir3 } from "node:os";
43889
- import { join as join26 } from "node:path";
44158
+ import { join as join27 } from "node:path";
43890
44159
 
43891
44160
  // src/scan/fetch.ts
43892
- import { createHash as createHash15 } from "node:crypto";
44161
+ import { createHash as createHash16 } from "node:crypto";
43893
44162
  import { basename as basename5 } from "node:path";
43894
44163
  var SCAN_MAX_ARTIFACT_BYTES = 200 * 1024 * 1024;
43895
44164
  var SCAN_REQUEST_TIMEOUT_MS = 6e4;
43896
- var SCAN_USER_AGENT = "border-customs/0.4.1 (+https://github.com/sumteclab/border; border scan)";
44165
+ var SCAN_USER_AGENT = "border-customs/0.5.0 (+https://github.com/sumteclab/border; border scan)";
43897
44166
  var CRATES_DOWNLOAD_HOST = "https://static.crates.io";
43898
44167
  var defaultScanFetcher = async (url2, init) => {
43899
44168
  if (!/^https?:\/\//.test(url2)) throw new EngineRunError(`border scan refuses non-http(s) URL: ${url2}`, null);
@@ -43918,19 +44187,19 @@ var defaultScanFetcher = async (url2, init) => {
43918
44187
  }
43919
44188
  };
43920
44189
  };
43921
- var fail = (why) => {
44190
+ var fail2 = (why) => {
43922
44191
  throw new EngineRunError(`border scan could not fetch the artifact (${why}) \u2014 refusing to scan a partial or absent download (fail-closed)`, null);
43923
44192
  };
43924
44193
  async function readCapped(res, url2, cap = SCAN_MAX_ARTIFACT_BYTES) {
43925
- if (res.status !== 200) fail(`HTTP ${String(res.status)} for ${url2}`);
44194
+ if (res.status !== 200) fail2(`HTTP ${String(res.status)} for ${url2}`);
43926
44195
  if (res.contentLength !== null && res.contentLength > cap) {
43927
- fail(`${url2} announces ${String(res.contentLength)} bytes, over the 200 MiB scan size cap`);
44196
+ fail2(`${url2} announces ${String(res.contentLength)} bytes, over the 200 MiB scan size cap`);
43928
44197
  }
43929
44198
  const parts = [];
43930
44199
  let total = 0;
43931
44200
  for await (const chunk of res.chunks()) {
43932
44201
  total += chunk.byteLength;
43933
- if (total > cap) fail(`${url2} streams past the 200 MiB scan size cap after ${String(total)} bytes`);
44202
+ if (total > cap) fail2(`${url2} streams past the 200 MiB scan size cap after ${String(total)} bytes`);
43934
44203
  parts.push(Buffer.from(chunk));
43935
44204
  }
43936
44205
  return Buffer.concat(parts);
@@ -43944,7 +44213,7 @@ async function fetchJson(fetcher, url2, timeoutMs) {
43944
44213
  try {
43945
44214
  return JSON.parse(body.toString("utf8"));
43946
44215
  } catch {
43947
- fail(`metadata response from ${url2} is not valid JSON`);
44216
+ fail2(`metadata response from ${url2} is not valid JSON`);
43948
44217
  }
43949
44218
  }
43950
44219
  var isRecord2 = (v) => typeof v === "object" && v !== null;
@@ -43980,11 +44249,11 @@ async function fetchArtifact(spec, o) {
43980
44249
  const meta3 = await fetchJson(o.fetcher, npmMetadataUrl(spec.name, spec.version), timeoutMs);
43981
44250
  const dist = isRecord2(meta3) && isRecord2(meta3["dist"]) ? meta3["dist"] : {};
43982
44251
  const tarball = dist["tarball"];
43983
- if (typeof tarball !== "string" || tarball.length === 0) fail(`npm metadata for ${label} has no dist.tarball`);
43984
- if (!/^https?:\/\//.test(tarball)) fail(`npm dist.tarball for ${label} is not an http(s) URL`);
44252
+ if (typeof tarball !== "string" || tarball.length === 0) fail2(`npm metadata for ${label} has no dist.tarball`);
44253
+ if (!/^https?:\/\//.test(tarball)) fail2(`npm dist.tarball for ${label} is not an http(s) URL`);
43985
44254
  const bytes = await fetchBytes(o.fetcher, tarball, timeoutMs);
43986
44255
  const filename = safeArtifactName(tarball, `${spec.name.replace("@", "").replace("/", "-")}-${spec.version}.tgz`);
43987
- if (!filename.endsWith(".tgz") && !filename.endsWith(".tar.gz")) fail(`npm tarball ${filename} is not a .tgz/.tar.gz`);
44256
+ if (!filename.endsWith(".tgz") && !filename.endsWith(".tar.gz")) fail2(`npm tarball ${filename} is not a .tgz/.tar.gz`);
43988
44257
  return { bytes, filename };
43989
44258
  }
43990
44259
  case "pypi": {
@@ -43994,11 +44263,11 @@ async function fetchArtifact(spec, o) {
43994
44263
  const sdists = files.filter((f) => f["packagetype"] === "sdist");
43995
44264
  const sdist = sdists.find((f) => typeof f["filename"] === "string" && f["filename"].endsWith(".tar.gz"));
43996
44265
  if (sdist === void 0) {
43997
- fail(`no .tar.gz sdist published for ${label} \u2014 wheels are pre-built artifacts and out of scan scope (setup code lives in the sdist)`);
44266
+ fail2(`no .tar.gz sdist published for ${label} \u2014 wheels are pre-built artifacts and out of scan scope (setup code lives in the sdist)`);
43998
44267
  }
43999
44268
  const url2 = sdist["url"];
44000
44269
  const name = sdist["filename"];
44001
- if (typeof url2 !== "string" || typeof name !== "string") fail(`PyPI sdist entry for ${label} is malformed`);
44270
+ if (typeof url2 !== "string" || typeof name !== "string") fail2(`PyPI sdist entry for ${label} is malformed`);
44002
44271
  const bytes = await fetchBytes(o.fetcher, url2, timeoutMs);
44003
44272
  return { bytes, filename: safeArtifactName(url2, name) };
44004
44273
  }
@@ -44018,8 +44287,8 @@ async function fetchArtifact(spec, o) {
44018
44287
  }
44019
44288
  }
44020
44289
  }
44021
- function sha256Hex(data) {
44022
- return createHash15("sha256").update(data).digest("hex");
44290
+ function sha256Hex2(data) {
44291
+ return createHash16("sha256").update(data).digest("hex");
44023
44292
  }
44024
44293
 
44025
44294
  // src/scan/spec.ts
@@ -44062,8 +44331,8 @@ function parseScanSpec(raw) {
44062
44331
  }
44063
44332
 
44064
44333
  // src/roundtrip/localArtifact.ts
44065
- import { createReadStream, existsSync as existsSync17, statSync as statSync2 } from "node:fs";
44066
- import { createHash as createHash16 } from "node:crypto";
44334
+ import { createReadStream, existsSync as existsSync18, statSync as statSync2 } from "node:fs";
44335
+ import { createHash as createHash17 } from "node:crypto";
44067
44336
  import { basename as basename6, resolve as resolve15 } from "node:path";
44068
44337
  var EXT_TABLE = /* @__PURE__ */ new Map([
44069
44338
  [".whl", "pypi"],
@@ -44111,7 +44380,7 @@ var GRAMMAR_HINT = {
44111
44380
  };
44112
44381
  function classifyLocalInput(raw, cwd, parsesAsRegistrySpec) {
44113
44382
  const abs = resolve15(cwd, raw);
44114
- if (existsSync17(abs)) {
44383
+ if (existsSync18(abs)) {
44115
44384
  const st = statSync2(abs);
44116
44385
  if (st.isDirectory()) {
44117
44386
  bad3(`'${raw}' resolves to a directory (${abs}) \u2014 local mode needs a single artifact FILE, not a directory`);
@@ -44146,7 +44415,7 @@ function classifyLocalInput(raw, cwd, parsesAsRegistrySpec) {
44146
44415
  return null;
44147
44416
  }
44148
44417
  async function readLocalArtifact(absPath, cap = SCAN_MAX_ARTIFACT_BYTES) {
44149
- const hash2 = createHash16("sha256");
44418
+ const hash2 = createHash17("sha256");
44150
44419
  const parts = [];
44151
44420
  let total = 0;
44152
44421
  for await (const chunk of createReadStream(absPath)) {
@@ -44372,7 +44641,7 @@ var CLAIM_SCANNER_PY = [
44372
44641
  ].join("\n");
44373
44642
 
44374
44643
  // src/roundtrip/manifest.ts
44375
- import { createHash as createHash17 } from "node:crypto";
44644
+ import { createHash as createHash18 } from "node:crypto";
44376
44645
  var RT_ENGINE = "roundtrip";
44377
44646
  var RT_RESIDUE_RULE = "roundtrip-residue-file";
44378
44647
  var RT_MODIFY_RULE = "roundtrip-modified-file";
@@ -44423,7 +44692,7 @@ var PERSISTENCE_PATTERNS = [
44423
44692
  function isPersistenceSurface(path4) {
44424
44693
  return PERSISTENCE_PATTERNS.some((re) => re.test(path4));
44425
44694
  }
44426
- var digestOf = (what) => createHash17("sha256").update(what).digest("hex");
44695
+ var digestOf = (what) => createHash18("sha256").update(what).digest("hex");
44427
44696
  function classifyResidue(label, diff, attribution = null) {
44428
44697
  const findings = [];
44429
44698
  for (const e of diff.added) {
@@ -44470,7 +44739,7 @@ function classifyResidue(label, diff, attribution = null) {
44470
44739
 
44471
44740
  // src/roundtrip/orchestrate.ts
44472
44741
  function containerNameFor(label, rand = randomBytes(3).toString("hex")) {
44473
- return `rt-${sha256Hex(label).slice(0, 8)}-${rand}`;
44742
+ return `rt-${sha256Hex2(label).slice(0, 8)}-${rand}`;
44474
44743
  }
44475
44744
  var shArgv = (name) => {
44476
44745
  if (!/^[A-Za-z0-9@._+/-]+$/.test(name)) {
@@ -44591,7 +44860,7 @@ function runPipeline2(exec, profile, spec, label, hostPath, container, closure,
44591
44860
  return {
44592
44861
  findings: classifyResidue(label, diffManifests(m1, m3), attribution),
44593
44862
  installDelta,
44594
- baselineDigest: sha256Hex([...m1.keys()].join("\n"))
44863
+ baselineDigest: sha256Hex2([...m1.keys()].join("\n"))
44595
44864
  };
44596
44865
  } finally {
44597
44866
  try {
@@ -44625,25 +44894,25 @@ async function runRoundtripCore(ctx, deps = {}) {
44625
44894
  ctx.stderr("roundtrip: docker unavailable \u2014 cannot verify");
44626
44895
  return EXIT_ERROR;
44627
44896
  }
44628
- const baseDir = mkdtempSync5(join26(tmpdir3(), "border-roundtrip-"));
44897
+ const baseDir = mkdtempSync5(join27(tmpdir3(), "border-roundtrip-"));
44629
44898
  try {
44630
44899
  const localRead = local !== null ? await readLocalArtifact(local.absPath) : null;
44631
44900
  const bytes = localRead?.bytes ?? (await fetchArtifact(spec, { fetcher: deps.fetcher ?? defaultScanFetcher })).bytes;
44632
- const artifactSha256 = localRead !== null ? localRead.sha256 : sha256Hex(bytes);
44633
- const hostPath = join26(baseDir, "artifact");
44634
- writeFileSync7(hostPath, bytes);
44901
+ const artifactSha256 = localRead !== null ? localRead.sha256 : sha256Hex2(bytes);
44902
+ const hostPath = join27(baseDir, "artifact");
44903
+ writeFileSync8(hostPath, bytes);
44635
44904
  const container = containerNameFor(label);
44636
44905
  const closure = spec.ecosystem === "pypi" ? resolvePipClosure(exec, profile, hostPath, label, spec) : null;
44637
44906
  const { findings, installDelta, baselineDigest } = runPipeline2(exec, profile, spec, label, hostPath, container, closure, (msg) => ctx.stderr(msg));
44638
44907
  const verdict = computeVerdict(findings);
44639
44908
  const report = {
44640
44909
  schemaVersion: 1,
44641
- key: sha256Hex(`roundtrip:${label}:${baselineDigest}:${verdict}`),
44910
+ key: sha256Hex2(`roundtrip:${label}:${baselineDigest}:${verdict}`),
44642
44911
  head: baselineDigest.slice(0, 40),
44643
44912
  dirty: false,
44644
44913
  exposureSet: [label],
44645
44914
  refSet: [],
44646
- rulesHash: sha256Hex(`${profile.image}|${profile.install(profile.artifactPath, spec).join(" ")}|${profile.uninstall(spec).join(" ")}`),
44915
+ rulesHash: sha256Hex2(`${profile.image}|${profile.install(profile.artifactPath, spec).join(" ")}|${profile.uninstall(spec).join(" ")}`),
44647
44916
  verdict,
44648
44917
  counts: countFindings(findings),
44649
44918
  findings,
@@ -44675,7 +44944,7 @@ async function runRoundtripCore(ctx, deps = {}) {
44675
44944
  }
44676
44945
  return exitCodeFromVerdict(verdict);
44677
44946
  } finally {
44678
- rmSync12(baseDir, { recursive: true, force: true });
44947
+ rmSync13(baseDir, { recursive: true, force: true });
44679
44948
  }
44680
44949
  }
44681
44950
  function parsesRegistrySpec(raw) {
@@ -44733,7 +45002,7 @@ var runRoundtrip = (ctx) => runRoundtripCore(ctx);
44733
45002
  // src/scan/scan.ts
44734
45003
  import { mkdtempSync as mkdtempSync6 } from "node:fs";
44735
45004
  import { tmpdir as tmpdir4 } from "node:os";
44736
- import { join as join28 } from "node:path";
45005
+ import { join as join29 } from "node:path";
44737
45006
 
44738
45007
  // src/scan/config.ts
44739
45008
  function buildScanConfigYaml(eco) {
@@ -44760,10 +45029,10 @@ function synthesizeScanConfig(eco) {
44760
45029
  }
44761
45030
 
44762
45031
  // src/scan/materialize.ts
44763
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, readdirSync as readdirSync10, readFileSync as readFileSync16, rmSync as rmSync13, writeFileSync as writeFileSync8 } from "node:fs";
44764
- import { join as join27 } from "node:path";
45032
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, readdirSync as readdirSync10, readFileSync as readFileSync17, rmSync as rmSync14, writeFileSync as writeFileSync9 } from "node:fs";
45033
+ import { join as join28 } from "node:path";
44765
45034
  var CRATES_ENVELOPE_FILES = ["Cargo.toml.orig", ".cargo-ok", ".cargo_vcs_info.json"];
44766
- var fail2 = (why) => {
45035
+ var fail3 = (why) => {
44767
45036
  throw new Error(`border scan cannot materialize the artifact (${why}) \u2014 refusing to stage an unusable tree (fail-closed)`);
44768
45037
  };
44769
45038
  var oneLine = (err) => (err instanceof Error ? err.message : String(err)).replace(/\s+/g, " ").trim();
@@ -44771,20 +45040,20 @@ function extractFlat(archive, destDir) {
44771
45040
  try {
44772
45041
  extractArchive(archive, destDir);
44773
45042
  } catch (err) {
44774
- fail2(`extraction of ${archive} failed: ${oneLine(err)}`);
45043
+ fail3(`extraction of ${archive} failed: ${oneLine(err)}`);
44775
45044
  }
44776
45045
  }
44777
45046
  function unwrapSingleDir(dir) {
44778
45047
  const entries = readdirSync10(dir, { withFileTypes: true });
44779
45048
  const dirs = entries.filter((e) => e.isDirectory());
44780
45049
  if (entries.length === 1 && dirs.length === 1 && dirs[0] !== void 0) {
44781
- return join27(dir, dirs[0].name);
45050
+ return join28(dir, dirs[0].name);
44782
45051
  }
44783
45052
  return dir;
44784
45053
  }
44785
45054
  function requireManifest(repoDir, ecosystem, candidates) {
44786
- if (!candidates.some((rel) => existsSync18(join27(repoDir, rel)))) {
44787
- fail2(`${ecosystem} package tree has none of [${candidates.join(", ")}] at its root`);
45055
+ if (!candidates.some((rel) => existsSync19(join28(repoDir, rel)))) {
45056
+ fail3(`${ecosystem} package tree has none of [${candidates.join(", ")}] at its root`);
44788
45057
  }
44789
45058
  }
44790
45059
  function materializePackage(o) {
@@ -44793,19 +45062,19 @@ function materializePackage(o) {
44793
45062
  `);
44794
45063
  });
44795
45064
  const safe = safeArtifactName(o.filename, `${o.ecosystem}-artifact`);
44796
- const archive = join27(o.baseDir, safe);
44797
- writeFileSync8(archive, o.bytes);
44798
- const extractDir = join27(o.baseDir, "extract");
44799
- mkdirSync11(extractDir, { recursive: true });
45065
+ const archive = join28(o.baseDir, safe);
45066
+ writeFileSync9(archive, o.bytes);
45067
+ const extractDir = join28(o.baseDir, "extract");
45068
+ mkdirSync12(extractDir, { recursive: true });
44800
45069
  extractFlat(archive, extractDir);
44801
45070
  let repoDir = unwrapSingleDir(extractDir);
44802
45071
  if (o.ecosystem === "rubygems") {
44803
45072
  const outer = repoDir;
44804
45073
  const dataCandidates = findNativeMissArchives(outer).filter((f) => f.endsWith("data.tar.gz"));
44805
- const inner = dataCandidates[0] ?? join27(outer, "data.tar.gz");
44806
- if (!existsSync18(inner)) fail2(".gem archive contains no data.tar.gz");
44807
- const gemTree = join27(o.baseDir, "gem-tree");
44808
- mkdirSync11(gemTree, { recursive: true });
45074
+ const inner = dataCandidates[0] ?? join28(outer, "data.tar.gz");
45075
+ if (!existsSync19(inner)) fail3(".gem archive contains no data.tar.gz");
45076
+ const gemTree = join28(o.baseDir, "gem-tree");
45077
+ mkdirSync12(gemTree, { recursive: true });
44809
45078
  extractFlat(inner, gemTree);
44810
45079
  repoDir = unwrapSingleDir(gemTree);
44811
45080
  } else if (o.ecosystem === "npm") {
@@ -44815,11 +45084,11 @@ function materializePackage(o) {
44815
45084
  } else {
44816
45085
  requireManifest(repoDir, o.ecosystem, ["Cargo.toml"]);
44817
45086
  for (const file2 of CRATES_ENVELOPE_FILES) {
44818
- const path4 = join27(repoDir, file2);
44819
- if (!existsSync18(path4)) continue;
44820
- const original = readFileSync16(path4);
44821
- rmSync13(path4, { force: true });
44822
- note(`border scan: crates envelope normalized: ${file2} sha256=${sha256Hex(original)}`);
45087
+ const path4 = join28(repoDir, file2);
45088
+ if (!existsSync19(path4)) continue;
45089
+ const original = readFileSync17(path4);
45090
+ rmSync14(path4, { force: true });
45091
+ note(`border scan: crates envelope normalized: ${file2} sha256=${sha256Hex2(original)}`);
44823
45092
  }
44824
45093
  }
44825
45094
  const env2 = o.env;
@@ -44845,7 +45114,7 @@ async function runScanCore(ctx, deps = {}) {
44845
45114
  }
44846
45115
  const cfg = synthesizeScanConfig(spec.ecosystem);
44847
45116
  const fetcher = deps.fetcher ?? defaultScanFetcher;
44848
- const baseDir = mkdtempSync6(join28(tmpdir4(), "border-scan-"));
45117
+ const baseDir = mkdtempSync6(join29(tmpdir4(), "border-scan-"));
44849
45118
  try {
44850
45119
  const { bytes, filename } = await fetchArtifact(spec, { fetcher });
44851
45120
  const env2 = { ...ctx.env };
@@ -44856,12 +45125,12 @@ async function runScanCore(ctx, deps = {}) {
44856
45125
  const verdict = computeVerdict(findings);
44857
45126
  const report = {
44858
45127
  schemaVersion: 1,
44859
- key: sha256Hex(`scan:${spec.ecosystem}:${spec.name}@${spec.version}:${head}`),
45128
+ key: sha256Hex2(`scan:${spec.ecosystem}:${spec.name}@${spec.version}:${head}`),
44860
45129
  head,
44861
45130
  dirty: false,
44862
45131
  exposureSet: [`${spec.ecosystem}:${spec.name}@${spec.version}`],
44863
45132
  refSet: [],
44864
- rulesHash: sha256Hex(buildScanConfigYaml(spec.ecosystem)),
45133
+ rulesHash: sha256Hex2(buildScanConfigYaml(spec.ecosystem)),
44865
45134
  verdict,
44866
45135
  counts: countFindings(findings),
44867
45136
  findings,
@@ -44970,11 +45239,12 @@ var handlers = {
44970
45239
  "llm-request": runLlmRequest,
44971
45240
  "llm-ingest": runLlmIngest,
44972
45241
  scan: runScan2,
44973
- roundtrip: runRoundtrip
45242
+ roundtrip: runRoundtrip,
45243
+ opencode: runOpencode
44974
45244
  };
44975
45245
 
44976
45246
  // src/cli/types.ts
44977
- var SUBCOMMANDS = ["check", "push", "status", "llm-request", "llm-ingest", "scan", "roundtrip"];
45247
+ var SUBCOMMANDS = ["check", "push", "status", "llm-request", "llm-ingest", "scan", "roundtrip", "opencode"];
44978
45248
 
44979
45249
  // src/cli.ts
44980
45250
  var VALID_TARGETS = targetIds();
@@ -44992,6 +45262,7 @@ function usage() {
44992
45262
  " llm-ingest record provenance captured by the harness",
44993
45263
  " scan inspect a third-party package ([ecosystem:]name@version) for residue",
44994
45264
  " roundtrip install+uninstall a package (registry spec or local artifact file) in a throwaway container, report uninstall residue",
45265
+ " opencode install|status|uninstall the official opencode plugin adapter (files under $XDG_CONFIG_HOME/opencode)",
44995
45266
  "",
44996
45267
  "flags:",
44997
45268
  " --config <path> config file (default: ./border.yaml, then git-remote fallback)",
@@ -45006,7 +45277,7 @@ function usage() {
45006
45277
  "",
45007
45278
  "exit codes: 0 pass (MEDIUM/INFO/LOW allowed) | 1 gate-blocked (CRITICAL/HIGH) or partial push | 2 config/tool error",
45008
45279
  "",
45009
- "subcommands: check, push, status, llm-request, llm-ingest, scan, roundtrip"
45280
+ "subcommands: check, push, status, llm-request, llm-ingest, scan, roundtrip, opencode"
45010
45281
  ].join("\n");
45011
45282
  }
45012
45283
  function isSubcommand(value) {
@@ -45130,7 +45401,7 @@ function isMainModule({ argv1, moduleUrl, realpath }) {
45130
45401
  return p;
45131
45402
  }
45132
45403
  };
45133
- return pathToFileURL(canonical(argv1)).href === pathToFileURL(canonical(fileURLToPath5(moduleUrl))).href;
45404
+ return pathToFileURL(canonical(argv1)).href === pathToFileURL(canonical(fileURLToPath6(moduleUrl))).href;
45134
45405
  }
45135
45406
  if (isMainModule({ argv1: process.argv[1], moduleUrl: import.meta.url, realpath: realpathSync.native })) {
45136
45407
  const outcome = run(process.argv.slice(2));