border-customs 0.8.0 → 0.8.1

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/README.md CHANGED
@@ -118,7 +118,18 @@ it, and the two registry-facing legs (pre-flight and publish) both have to agree
118
118
  against the history of every ref *and* against the object set a remote does not already
119
119
  have (the transmit set). Oversized-file, checked-in-binary, and notebook-output rules
120
120
  round it out.
121
- 7. **Registry pre-flight** (npm, PyPI, crates.io, and RubyGems targets). Three outcomes per
121
+ 7. **Landing-grade tip lens.** The exact machinery `border push` uses to verify a landing
122
+ (`scanTipTree`: the tip tree materialized from git objects into a throwaway directory,
123
+ scanned by the native exfil family plus both twins filtered to `exfil-*` ids) also runs
124
+ here, against local HEAD — the tree a push would transmit. This closes the
125
+ disk-versus-tip gap: content that exists only in HEAD (introduced by a merge commit's
126
+ resolution and since deleted from disk, say) is invisible to the history and working-tree
127
+ legs above but cannot hide from the gate. Tip findings merge before the allow-list, so a
128
+ config `allow` pin exempts them by rule+file+digest exactly like every other finding, and
129
+ the post-push landing check honors the *same* pins — a green packet means green landing, in
130
+ both directions. The tip leg's git plumbing is fail-closed: a git failure aborts the run
131
+ (exit 2), never a silent clean.
132
+ 8. **Registry pre-flight** (npm, PyPI, crates.io, and RubyGems targets). Three outcomes per
122
133
  target: version already published ⇒ CRITICAL `version-exists` ("bump version required");
123
134
  name owned by someone outside your `rules.authors` allow-list ⇒ CRITICAL
124
135
  `name-foreign-owner`; ambiguous ownership signals ⇒ also CRITICAL, because guessing wrong
@@ -129,7 +140,7 @@ it, and the two registry-facing legs (pre-flight and publish) both have to agree
129
140
  one of that API's exact *absent* messages (its 404s are text, not JSON, and an
130
141
  unrecognized one is never guessed), a timeout, or unparseable JSON all fail closed as
131
142
  exit 2. An unreachable registry blocks the push instead of letting it run blind.
132
- 8. **Artifact stage.** If registry targets are configured, packages are built **once** into
143
+ 9. **Artifact stage.** If registry targets are configured, packages are built **once** into
133
144
  `.border/dist/` (`npm pack --ignore-scripts`, `python -m build --no-isolation`,
134
145
  `cargo package --allow-dirty --no-verify`, `gem build <name>.gemspec -o .border/dist/`),
135
146
  that exact byte-stream is scanned (gitleaks + secretlint over the extracted contents),
@@ -145,14 +156,16 @@ it, and the two registry-facing legs (pre-flight and publish) both have to agree
145
156
  re-runs the check's exact `cargo package` into a throwaway directory and refuses to
146
157
  publish if the fresh digest differs from the certified `.crate`. `gem push` uploads the
147
158
  recorded `.gem` byte-for-byte, so it needs no such re-assertion.
148
- 9. **Report.** Findings carry `valueDigest` (sha256 of the matched value) and a masked
159
+ 10. **Report.** Findings carry `valueDigest` (sha256 of the matched value) and a masked
149
160
  snippet: fully blocked for short values, else `first4…last4`. Raw secret bytes never
150
161
  leave process memory. A per-run `TextSanitizer` holds the digest-to-value registry and
151
162
  replaces every flagged literal in *all* rendered text, including agent-written strings in
152
163
  the LLM layer, so a secret cannot reappear inside a message about itself. `report.json`
153
164
  (canonical, byte-stable for machines) and `report.md` land under `.border/runs/`.
154
- 10. **Ledger record.** A PASS is appended to `.border/ledger.jsonl` together with the key
155
- and artifact digests. Degraded and NO-OP runs can never write a PASS.
165
+ 11. **Ledger record.** A PASS is appended to `.border/ledger.jsonl` together with the key
166
+ and artifact digests. Degraded and NO-OP runs can never write a PASS. The key folds in a
167
+ *lens identity*, so a PASS recorded under an older, weaker set of legs never certifies a
168
+ push under the current lens — upgrading the gate forces one fresh full check.
156
169
 
157
170
  ```bash
158
171
  $ border check # full scan, ~seconds on a mid-size repo
@@ -251,6 +264,7 @@ lives inside. The exfil lens adds five rules over the surfaces about to become p
251
264
  | `exfil-home-path` | `/home/<user>` and `C:\Users\<user>` shapes |
252
265
  | `exfil-cred-location` | `~/.pypirc`, `SSHPASS`, `*.env` credential-location shapes |
253
266
  | `exfil-host-profile` | multi-signal host fingerprints (os+version / no-docker / no-passwordless-sudo co-occurrences in one paragraph) |
267
+ | `exfil-machine-binding` | vendor inference-model ids pinned on portable surfaces next to model/default/provider context (L-MACHINE-LOCAL; born 2026-10-02) |
254
268
 
255
269
  The severity a reader **observes** is the (rule × facet × channel) matrix, and its single
256
270
  home is [src/exfil/severity.ts](src/exfil/severity.ts) — the check pipeline, the
@@ -264,11 +278,13 @@ home is [src/exfil/severity.ts](src/exfil/severity.ts) — the check pipeline, t
264
278
  | `exfil-home-path` | tree | native | MEDIUM |
265
279
  | `exfil-cred-location` | tree | native | MEDIUM |
266
280
  | `exfil-host-profile` | tree | native | MEDIUM |
281
+ | `exfil-machine-binding` | tree | native | MEDIUM |
267
282
  | `exfil-rfc1918` | message | native | HIGH |
268
283
  | `exfil-ssh-target` | message | native | HIGH |
269
284
  | `exfil-home-path` | message | native | MEDIUM |
270
285
  | `exfil-cred-location` | message | native | MEDIUM |
271
286
  | `exfil-host-profile` | message | native | MEDIUM |
287
+ | `exfil-machine-binding` | message | native | MEDIUM |
272
288
 
273
289
  Why an observed `CRITICAL` stands where the intent table says HIGH: the twin channels have
274
290
  no HIGH rung — every ingested gitleaks finding carries CRITICAL, and secretlint's `error`
@@ -411,7 +427,10 @@ never from cache:
411
427
  and exits 1; a rerun of `border push --yes` picks up only the still-PENDING targets.
412
428
  - After every git leg that moved, border **verifies the landing**: it re-observes the remote
413
429
  tip (`git ls-remote`, then a fetch of exactly that tip when it is not local) and scans the
414
- landed tree with the same exfil machinery as `border exfil`. Three honest outcomes: clean
430
+ landed tree with the same exfil machinery as `border exfil` — the same lens `border check`
431
+ now runs pre-push, and the same config `allow` pins apply: blocking counts are computed
432
+ after the allow-list, and any exemption is echoed loudly (rule, entry index, count), never
433
+ silent. Three honest outcomes: clean
415
434
  appends a `t:"landing"` ledger record and preserves the push exit; a blocking hit exits 1
416
435
  with `ALREADY PUBLIC; border detects, never erases` — the bytes are out, and border's job
417
436
  there is detection, recording the fact, never rewriting history; an unobservable tip exits
@@ -717,7 +736,7 @@ plugin entry ships as `exports["./server"]`):
717
736
  ```jsonc
718
737
  {
719
738
  // pin the exact version — an @latest channel hits the registry on every cold start
720
- "plugin": ["border-customs@0.8.0"]
739
+ "plugin": ["border-customs@0.8.1"]
721
740
  }
722
741
  ```
723
742
 
@@ -827,6 +846,13 @@ which is now a thin consumer of `inspect --json` with the rc contract unchanged
827
846
 
828
847
  ## Changelog
829
848
 
849
+ ### 0.8.1 (2026-10-06)
850
+ - Maintenance release: certificate-chain rebuild off the scrubbed lineage (INCIDENT-20261006
851
+ identity-exposure cleanup). No functional change vs 0.8.0; 0.8.0's tarball is byte-intact but
852
+ its OIDC provenance pin moved with the history rewrite, so a fresh tag + publish restores a
853
+ verifiable chain. Rotation: package.json + lock roots + plugin marker + scan UA + README pins +
854
+ this entry.
855
+
830
856
  ### 0.8.0 (2026-09-30)
831
857
  - Add: **`border opencode inspect [--json]`** — the load-surface self-audit becomes a gate-owned
832
858
  surface: six read-only aspects (cache self-consistency, spec freshness vs pin/registry, identity
@@ -868,7 +894,7 @@ which is now a thin consumer of `inspect --json` with the rc contract unchanged
868
894
 
869
895
  ### 0.6.0 (2026-09-25)
870
896
  - Add: **the exfil lens** — five rules (`exfil-rfc1918`, `exfil-ssh-target`,
871
- `exfil-home-path`, `exfil-cred-location`, `exfil-host-profile`) that flag
897
+ `exfil-home-path`, `exfil-cred-location`, `exfil-host-profile`, `exfil-machine-binding`) that flag
872
898
  exfil-shaped literals on about-to-be-public surfaces, the gap classic secret
873
899
  scanners leave open because an internal IP is not a credential. The observed
874
900
  severity of every (rule × facet × channel) cell lives in one machine-readable
@@ -1065,4 +1091,4 @@ border 是一个 fail-closed(失败即拦截)的推送前门禁 CLI:`npm install
1065
1091
 
1066
1092
  OpenCode 插件适配层(0.5.0):零手工装载两条路线——主路是在 `opencode.jsonc` 声明 `"plugin": ["border-customs@0.5.0"]`(建议钉具体版本,@latest 每次冷启动都打注册表),opencode 自行下载构件,插件入口走 `exports["./server"]`;插件自带 CLI 解析,顺序为 `BORDER_BIN` 覆盖(独占,坏了就报错、绝不静默回退)> 包内同侧 `dist/index.js`(shebang 直接执行,不可执行则回退 PATH 上的 `node` 承载)> PATH 上的 `border`,免全局安装、免 PATH 配置;0.5.1 起任何候选都必须先过**身份握手**(以 `--help` 应答出 border 横幅与 `usage: border` 才算数),通过之前绝不投递用户 argv,握手不过即 cannot-answer exit 2、不运行任何其他二进制——0.5.0 曾用 `process.execPath` 承载包内 dist,而在插件宿主里 execPath 是 opencode 本体,边检门被静默冒充为宿主(2026-09-22 事故,见 0.5.1 变更日志)。备路是 `border opencode install|status|uninstall` 文件投递,落盘 `$XDG_CONFIG_HOME/opencode` 下的 `plugins/border.ts` 与 `commands/border.md`(无 XDG 回退 HOME/.config):受管文件凭 marker 行识别身份,同名外人文件 exit 2 拒绝、绝不覆盖,卸载只删自己带 marker 的文件,字节相同即 `up to date`。装载面只有一个 `border` 工具(argv-only 生成、无 shell、300 秒与每流 64 KiB 上限,命令闭集 check/push/status/llm-request/llm-ingest/scan/roundtrip/exfil/--help)与一条 `/border` 斜杠命令(config 钩子以 `??=` 自注册,用户同名文件命令保持权威)。边界如实说:allowlist 是 UX/防呆而非安全边界——插件进程与 opencode 进程同权,门禁的牙齿在 CLI 与账本,不在适配层;`push --yes` 按设计留在终端人审,工具端机械拒绝,会话内裸 push 本就是 CLI 合同的 DRY-RUN。装载验证走 opt-in 探针 `BORDER_OPENCODE_PROBE=1`(临时 HOME/XDG 起 `opencode serve` 对照组,断言 `border` 工具 id 与恰好一条 border 命令,重复即 FAIL;插件加载失败是静默的,只认 HTTP 响应不认退出码)。两条路线装载后都需重启 opencode:工具与命令在启动时扫描。
1067
1093
 
1068
- 外泄透镜(0.6.0):经典密钥扫描器看不见内网 IP、`user@内网主机`、家目录、凭据文件位置、主机画像这类"外泄形状"字面量——它们不是密钥,却如实描述公网仓库身后的私网。0.6.0 加五条规则 `exfil-rfc1918/exfil-ssh-target/exfil-home-path/exfil-cred-location/exfil-host-profile`,每个(规则×分面×通道)格子的**观察严重度**只有单一事实源 `src/exfil/severity.ts`:树面 HIGH 族走双生规则(gitleaks TOML + secretlint pattern,id 逐字节相等、三通道判决对等性有测试钉死),因两引擎都没有 HIGH 档而如实记为 CRITICAL(不硬掰,如实表);树面 MEDIUM 族走原生腿;提交消息面原生独占、id 带 `:message` 后缀,allow 条目按 id 精确匹配、跨面豁免在结构上不可能;tag note 分面归既有引擎腿 `tag-message-secret`,核心绝不重复发。规则源文件并入 rulesHash,改一个匹配器即令全部缓存 PASS 失效。独立只读入口 `border exfil <rev|url>`(默认树尖,`--deep` 补全史 blob 与全部可达提交消息;URL 模式落临时克隆、退出即销毁)。push 之后新增**落地核验**:ls-remote 复读远端尖、必要时精确 fetch 该对象,用同一套机制扫已公开树——干净则追加 `t:"landing"` 账本行并保留原退出码;有阻断发现则 exit 1 并明说 ALREADY PUBLIC;border 只检测、从不抹除;远端不可观测则 exit 2、一条记录都不造(没核验就没事实),已执行的 push 记录原样矗立——账本 append-only,任何情况下不改写历史。测试语料遵守夹具纪律:10.200.0.0/16 为预留夹具段(树面全通道静默、双生规则原生镜像该豁免,消息面例外照红),其余泄漏形状一律由 `assembleOctet`/`assembleHost` 运行时拼装,签出字节里没有完整泄漏 token——border 的透镜对自己的仓库绿,红故事照常武装。真值通道 `BORDER_EXFIL_TRUTH=1` 为 opt-in,红锚点带生命周期条款:上游作者清理后按设计转绿、留档不判败。
1094
+ 外泄透镜(0.6.0):经典密钥扫描器看不见内网 IP、`user@内网主机`、家目录、凭据文件位置、主机画像这类"外泄形状"字面量——它们不是密钥,却如实描述公网仓库身后的私网。0.6.0 加五条规则 `exfil-rfc1918/exfil-ssh-target/exfil-home-path/exfil-cred-location/exfil-host-profile`,每个(规则×分面×通道)格子的**观察严重度**只有单一事实源 `src/exfil/severity.ts`:树面 HIGH 族走双生规则(gitleaks TOML + secretlint pattern,id 逐字节相等、三通道判决对等性有测试钉死),因两引擎都没有 HIGH 档而如实记为 CRITICAL(不硬掰,如实表);树面 MEDIUM 族走原生腿;提交消息面原生独占、id 带 `:message` 后缀,allow 条目按 id 精确匹配、跨面豁免在结构上不可能;tag note 分面归既有引擎腿 `tag-message-secret`,核心绝不重复发。规则源文件并入 rulesHash,改一个匹配器即令全部缓存 PASS 失效。独立只读入口 `border exfil <rev|url>`(默认树尖,`--deep` 补全史 blob 与全部可达提交消息;URL 模式落临时克隆、退出即销毁)。push 之后新增**落地核验**:ls-remote 复读远端尖、必要时精确 fetch 该对象,用同一套机制扫已公开树——干净则追加 `t:"landing"` 账本行并保留原退出码;有阻断发现则 exit 1 并明说 ALREADY PUBLIC;border 只检测、从不抹除;远端不可观测则 exit 2、一条记录都不造(没核验就没事实),已执行的 push 记录原样矗立——账本 append-only,任何情况下不改写历史。2026-10-04 补上透镜对等:check 流水线在原生规则之后、注册表预检之前多跑一条**树尖腿**,用与落地核验完全同一套 scanTipTree 机制预扫本地 HEAD(push 将传输的那棵树),历史与工作面腿看不见的"只在树尖存在"内容(如合并提交解析引入、随后从磁盘删除的文件)从此无处藏;树尖发现汇入 allow 前合并点,按 rule+file+digest 与所有发现同权豁免——落地核验同样消费 cfg allow 并把每一次豁免大声列出(规则、条目号、条数),绝不沉默;绿色交接单与绿色落地双向一致。ledger key 折入透镜身份,旧一代(无树尖腿)的缓存 PASS 不再为新透镜背书,升级后强制一次全新全扫。树尖腿 git 故障即 exit 2,没有静默清白。测试语料遵守夹具纪律:10.200.0.0/16 为预留夹具段(树面全通道静默、双生规则原生镜像该豁免,消息面例外照红),其余泄漏形状一律由 `assembleOctet`/`assembleHost` 运行时拼装,签出字节里没有完整泄漏 token——border 的透镜对自己的仓库绿,红故事照常武装。真值通道 `BORDER_EXFIL_TRUTH=1` 为 opt-in,红锚点带生命周期条款:上游作者清理后按设计转绿、留档不判败。
@@ -176,4 +176,4 @@ export function matchSshTarget(text: string, o: RuleOptions): RuleHit[] {
176
176
  // matchHomePath / matchCredLocation / matchHostProfile live in rulesPath.ts
177
177
  // and are re-exported here so every consumer keeps importing the five
178
178
  // predicates from one module. Type-only imports keep that pair acyclic.
179
- export { matchCredLocation, matchHomePath, matchHostProfile } from "./rulesPath.ts";
179
+ export { matchCredLocation, matchHomePath, matchHostProfile, matchMachineBinding } from "./rulesPath.ts";
@@ -110,3 +110,31 @@ export function matchHostProfile(text: string, _o: RuleOptions): RuleHit[] {
110
110
  flush();
111
111
  return hits;
112
112
  }
113
+
114
+ // ---------------------------------------------------------------- exfil-machine-binding
115
+
116
+ /**
117
+ * L-MACHINE-LOCAL (owner ruling 2026-10-01; rule born 2026-10-02 after a
118
+ * vendor model id sat in another repo's portable README+code default for six
119
+ * releases unnoticed). Vendor inference-model identifiers pinned on portable
120
+ * surfaces couple a package to ONE operator's device authorization. Context
121
+ * gate: the line must ALSO speak of model/default/provider — prose mentions
122
+ * of a model name stay green (location, not vocabulary — mirrors the
123
+ * cred-location "position, not value" discipline).
124
+ * Self-stealth: literals are built in fragments so THIS file never trips
125
+ * its own regex.
126
+ */
127
+ const BINDING_TOKEN = String.raw`\b(?:q` + "wen" + String.raw`|deep` + "seek" + String.raw`|kimi|g` + "lm" + String.raw`|ernie|dou` + "bao" + String.raw`|hunyuan|sp` + "ark" + String.raw`)[-\.\d][\w.-]*\b`;
128
+ const BINDING_VENDOR_RE = new RegExp(BINDING_TOKEN, "gi");
129
+ const BINDING_CONTEXT_RE = /(?:model|模型|default|默认|provider)/i;
130
+
131
+ export function matchMachineBinding(text: string, _o: RuleOptions): RuleHit[] {
132
+ const hits: RuleHit[] = [];
133
+ text.split("\n").forEach((body, idx) => {
134
+ if (!BINDING_CONTEXT_RE.test(body)) return;
135
+ for (const m of body.matchAll(BINDING_VENDOR_RE)) {
136
+ hits.push({ rule: "exfil-machine-binding", matched: m[0] ?? "", line: idx + 1, message: "Machine binding: a vendor inference-model id pinned next to model/default/provider context — device authorization belongs in operator-local config, not portable surfaces (L-MACHINE-LOCAL)." });
137
+ }
138
+ });
139
+ return hits;
140
+ }
@@ -40,6 +40,7 @@ import {
40
40
  import {
41
41
  matchCredLocation,
42
42
  matchHomePath,
43
+ matchMachineBinding,
43
44
  matchHostProfile,
44
45
  matchRfc1918,
45
46
  matchSshTarget,
@@ -99,6 +100,7 @@ const PREDICATES: Readonly<Record<ExfilRuleId, (text: string, o: RuleOptions) =>
99
100
  "exfil-home-path": matchHomePath,
100
101
  "exfil-cred-location": matchCredLocation,
101
102
  "exfil-host-profile": matchHostProfile,
103
+ "exfil-machine-binding": matchMachineBinding,
102
104
  };
103
105
 
104
106
  /**
@@ -44,13 +44,14 @@
44
44
  export const EXFIL_SEVERITIES = ["INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"] as const;
45
45
  export type ExfilSeverity = (typeof EXFIL_SEVERITIES)[number];
46
46
 
47
- /** The five exfil-lens rule ids, verbatim; twins must ship these ids byte-equal (T2 guard). */
47
+ /** The exfil-lens rule ids, verbatim; twins must ship these ids byte-equal (T2 guard). */
48
48
  export const EXFIL_RULE_IDS = [
49
49
  "exfil-rfc1918",
50
50
  "exfil-ssh-target",
51
51
  "exfil-home-path",
52
52
  "exfil-cred-location",
53
53
  "exfil-host-profile",
54
+ "exfil-machine-binding",
54
55
  ] as const;
55
56
  export type ExfilRuleId = (typeof EXFIL_RULE_IDS)[number];
56
57
 
@@ -71,6 +72,7 @@ export const EXFIL_TABLE_SEVERITY: Readonly<Record<ExfilRuleId, ExfilSeverity>>
71
72
  "exfil-home-path": "MEDIUM",
72
73
  "exfil-cred-location": "MEDIUM",
73
74
  "exfil-host-profile": "MEDIUM",
75
+ "exfil-machine-binding": "MEDIUM",
74
76
  };
75
77
 
76
78
  /** HIGH family: the two identity-carrying location rules; tree face belongs to the twins. */
@@ -85,7 +87,7 @@ export const EXFIL_HIGH_FAMILY: readonly ExfilRuleId[] = ["exfil-rfc1918", "exfi
85
87
  */
86
88
  export const EXFIL_SECRET_VALUE_RULES: readonly ExfilRuleId[] = EXFIL_HIGH_FAMILY;
87
89
  /** MEDIUM family: privacy-shape rules; native carries them on both scanned facets. */
88
- export const EXFIL_MEDIUM_FAMILY: readonly ExfilRuleId[] = ["exfil-home-path", "exfil-cred-location", "exfil-host-profile"];
90
+ export const EXFIL_MEDIUM_FAMILY: readonly ExfilRuleId[] = ["exfil-home-path", "exfil-cred-location", "exfil-host-profile", "exfil-machine-binding"];
89
91
 
90
92
  /** null = FORBIDDEN cell: that channel must never emit that rule on that facet. */
91
93
  export type ObservedSeverity = ExfilSeverity | null;
@@ -96,6 +98,7 @@ const TWIN_HIGH: Readonly<Record<ExfilRuleId, ObservedSeverity>> = {
96
98
  "exfil-home-path": null,
97
99
  "exfil-cred-location": null,
98
100
  "exfil-host-profile": null,
101
+ "exfil-machine-binding": null,
99
102
  };
100
103
  const NULL_ROW: Readonly<Record<ExfilRuleId, ObservedSeverity>> = {
101
104
  "exfil-rfc1918": null,
@@ -103,13 +106,14 @@ const NULL_ROW: Readonly<Record<ExfilRuleId, ObservedSeverity>> = {
103
106
  "exfil-home-path": null,
104
107
  "exfil-cred-location": null,
105
108
  "exfil-host-profile": null,
109
+ "exfil-machine-binding": null,
106
110
  };
107
111
 
108
112
  /** The full (facet × channel × rule) truth table — the plan's 分面契约, machine-readable. */
109
113
  export const EXFIL_MATRIX: Readonly<Record<ExfilFacet, Readonly<Record<ExfilChannel, Readonly<Record<ExfilRuleId, ObservedSeverity>>>>>> = {
110
114
  tree: {
111
115
  // native tree leg = MEDIUM family补发 only (v2 double-emission revoked).
112
- native: { ...NULL_ROW, "exfil-home-path": "MEDIUM", "exfil-cred-location": "MEDIUM", "exfil-host-profile": "MEDIUM" },
116
+ native: { ...NULL_ROW, "exfil-home-path": "MEDIUM", "exfil-cred-location": "MEDIUM", "exfil-host-profile": "MEDIUM", "exfil-machine-binding": "MEDIUM" },
113
117
  // gitleaks.ts:81 hardcodes CRITICAL for every ingested finding.
114
118
  "gitleaks-twin": TWIN_HIGH,
115
119
  // secretlint.ts:299-309 has no HIGH rung — error maps to CRITICAL as-is.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "border-customs",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "border-customs",
9
- "version": "0.8.0",
9
+ "version": "0.8.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@opencode-ai/plugin": "^1.17.4",