@weave_protocol/adapter-opencode 0.0.0-stage → 0.2.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/README.md CHANGED
@@ -1,3 +1,88 @@
1
- # Temporary Holding Version
1
+ # 🛡️ @weave_protocol/adapter-opencode
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > Enforce [WARD.md](https://github.com/Tyox-all/Weave_Protocol/tree/main/ward) in **OpenCode** with a plugin. Every tool call is checked against your policy before it runs, whichever model you're using: Claude, GPT, Gemini, DeepSeek, Kimi, Qwen or a local one.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@weave_protocol/adapter-opencode?color=000&style=flat-square)](https://www.npmjs.com/package/@weave_protocol/adapter-opencode)
6
+
7
+ The same WARD.md you use with Claude Code, Antigravity, Microsoft Agent Framework, Gemini CLI and Codex CLI.
8
+
9
+ ## Quick start
10
+
11
+ Add the plugin to OpenCode's config, either by hand:
12
+
13
+ ```json
14
+ {
15
+ "$schema": "https://opencode.ai/config.json",
16
+ "plugin": ["@weave_protocol/adapter-opencode"]
17
+ }
18
+ ```
19
+
20
+ or with the CLI, which edits `~/.config/opencode/opencode.json` for you:
21
+
22
+ ```bash
23
+ npx -p @weave_protocol/adapter-opencode weave-ward-opencode init
24
+ ```
25
+
26
+ Restart OpenCode; it installs the plugin on startup. Add a `WARD.md` to your project (`npx @weave_protocol/ward init`), or a global one at `~/.config/opencode/WARD.md`.
27
+
28
+ The command is `weave-ward-opencode`, not `weave-opencode`. The `weave-opencode` npm name belongs to W&B Weave's opencode integration, which is a different product.
29
+
30
+ ## What it checks
31
+
32
+ The plugin runs in OpenCode's `tool.execute.before` hook and uses Ward's shared evaluator (`evaluateToolCall`), so the rules match the other adapters exactly:
33
+
34
+ | Tool | Checked |
35
+ |---|---|
36
+ | `bash` | `shell_exec`, plus every URL and every absolute or `~` path in the command (relative paths resolve against `workdir`) |
37
+ | `read`, `write`, `edit` | `filePath` (read or write) |
38
+ | `glob`, `grep` | `path` (list or read) |
39
+ | `webfetch` | `url` |
40
+ | `apply_patch` | the files in its `patchText` (`*** Add/Update File` and `*** Move to` are writes, `*** Delete File` is a delete); patch content is never treated as a target |
41
+ | `todowrite`, `question` | the `task_planning` capability |
42
+ | MCP tools (`<server>_<tool>`) and custom tools | by name, plus every path- and URL-named argument |
43
+
44
+ A blocked call throws, and the agent sees `WARD: <reason>` as the tool error. A call that needs approval is blocked with a reason saying so.
45
+
46
+ **Policy loading:**
47
+ - **WARD.md is re-read on every call**, so edits take effect without restarting OpenCode.
48
+ - **An invalid WARD.md blocks every call**, so a typo can't silently switch enforcement off.
49
+ - **No WARD.md anywhere means no enforcement.**
50
+
51
+ WARD.md is looked up in this order:
52
+ 1. `$WEAVE_WARD_PATH`
53
+ 2. `<project>/WARD.md`
54
+ 3. `<project>/.opencode/WARD.md`
55
+ 4. `<project>/.weave/WARD.md`
56
+ 5. `~/.config/opencode/WARD.md`
57
+
58
+ ## Commands
59
+
60
+ ```bash
61
+ weave-ward-opencode init # add the plugin to ~/.config/opencode/opencode.json
62
+ weave-ward-opencode disable # remove it
63
+ weave-ward-opencode status
64
+ weave-ward-opencode test read --input='{"filePath":"secrets/key"}' # dry run
65
+ ```
66
+
67
+ `init` backs up `opencode.json` and edits it atomically, leaving your other plugins and settings alone. It won't rewrite an `opencode.jsonc` file (it has comments); it tells you what to add instead.
68
+
69
+ ## For plugin authors
70
+
71
+ The package's main entry default-exports only the plugin, `{ id: "weave-ward", server }`, as OpenCode's loader requires. The helpers are at `@weave_protocol/adapter-opencode/lib`.
72
+
73
+ ## Dashboard
74
+
75
+ Set `WEAVE_DASHBOARD_URL` (and `WEAVE_API_KEY`) where you run the agent, and each decision is reported to a local [Weave API](https://github.com/Tyox-all/Weave_Protocol/tree/main/api) dashboard (0.2.0+). Only the decision and the tool name are sent, never command or argument contents. Reporting never changes a decision and gives up after 250 ms.
76
+
77
+ ## Verified
78
+
79
+ Built against OpenCode's [plugin docs](https://opencode.ai/docs/plugins/), plus the plugin loader and tool schemas in OpenCode v1.19.5. 12 fixture tests.
80
+
81
+ Live-checked in OpenCode 1.19.5 (on Gemini 3.5 Flash-Lite), loaded as a local plugin:
82
+ - a `read` of `secrets/key` was blocked, and the model received `WARD: Denied by filesystem rule …`;
83
+ - a `write` of `TODO.md` outside the allowed folder was blocked, and no file was created;
84
+ - allowed `glob` and `read` calls went through.
85
+
86
+ ## License
87
+
88
+ Apache-2.0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * weave-ward-opencode CLI
4
+ *
5
+ * weave-ward-opencode init Add the plugin to ~/.config/opencode/opencode.json
6
+ * weave-ward-opencode disable Remove it
7
+ * weave-ward-opencode status Plugin + active WARD.md
8
+ * weave-ward-opencode test <tool> [--input=JSON] Dry-run a tool call
9
+ *
10
+ * (Not "weave-opencode": that npm name belongs to W&B Weave's opencode integration.)
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
package/dist/cli.js ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * weave-ward-opencode CLI
4
+ *
5
+ * weave-ward-opencode init Add the plugin to ~/.config/opencode/opencode.json
6
+ * weave-ward-opencode disable Remove it
7
+ * weave-ward-opencode status Plugin + active WARD.md
8
+ * weave-ward-opencode test <tool> [--input=JSON] Dry-run a tool call
9
+ *
10
+ * (Not "weave-opencode": that npm name belongs to W&B Weave's opencode integration.)
11
+ */
12
+ import { installPlugin, removePlugin, isPluginInstalled, readConfig, opencodeJsonPath, PACKAGE } from './config.js';
13
+ import { resolveWard, evaluateCall, opencodeConfigDir } from './policy.js';
14
+ import { join } from 'node:path';
15
+ const tty = process.stderr.isTTY && !process.env.NO_COLOR;
16
+ const c = {
17
+ reset: tty ? '\x1b[0m' : '', bold: tty ? '\x1b[1m' : '', red: tty ? '\x1b[31m' : '',
18
+ green: tty ? '\x1b[32m' : '', yellow: tty ? '\x1b[33m' : '', cyan: tty ? '\x1b[36m' : '', gray: tty ? '\x1b[90m' : '',
19
+ };
20
+ const say = (s = '') => console.error(s);
21
+ const option = (args, name) => args.find((a) => a.startsWith(`--${name}=`))?.slice(name.length + 3);
22
+ function help() {
23
+ say(`\n${c.cyan}${c.bold}🛡️ weave-ward-opencode${c.reset} ${c.gray}WARD.md enforcement for OpenCode${c.reset}\n`);
24
+ say(`${c.bold}Commands:${c.reset}`);
25
+ say(` ${c.cyan}init${c.reset} Add the plugin to ${opencodeJsonPath()}`);
26
+ say(` ${c.cyan}disable${c.reset} Remove it`);
27
+ say(` ${c.cyan}status${c.reset} Show the plugin and the active WARD.md`);
28
+ say(` ${c.cyan}test${c.reset} ${c.gray}<tool> [--input=JSON]${c.reset} Dry-run a tool call against WARD.md\n`);
29
+ return 0;
30
+ }
31
+ function safeResolve() {
32
+ try {
33
+ return resolveWard(process.cwd());
34
+ }
35
+ catch (err) {
36
+ say(`${c.red}✗${c.reset} ${err instanceof Error ? err.message : String(err)}`);
37
+ return null;
38
+ }
39
+ }
40
+ function init() {
41
+ try {
42
+ installPlugin();
43
+ }
44
+ catch (err) {
45
+ say(`${c.red}✗${c.reset} ${err instanceof Error ? err.message : String(err)}`);
46
+ return 1;
47
+ }
48
+ say(`${c.green}✓${c.reset} Added "${PACKAGE}" to the plugin list in ${opencodeJsonPath()}`);
49
+ if (!safeResolve()) {
50
+ say(`${c.yellow}!${c.reset} No WARD.md found here or at ${join(opencodeConfigDir(), 'WARD.md')}; calls are allowed until you add one.`);
51
+ }
52
+ say(`${c.gray}Restart OpenCode; it installs the plugin on startup.${c.reset}`);
53
+ return 0;
54
+ }
55
+ function disable() {
56
+ const { removed } = removePlugin();
57
+ say(removed ? `${c.green}✓${c.reset} Removed the plugin from ${opencodeJsonPath()}` : 'The plugin was not configured.');
58
+ return 0;
59
+ }
60
+ function status() {
61
+ const installed = isPluginInstalled(readConfig());
62
+ say(`Plugin: ${installed ? `${c.green}configured${c.reset}` : `${c.yellow}not configured${c.reset}`} (${opencodeJsonPath()})`);
63
+ const ward = safeResolve();
64
+ say(`WARD.md: ${ward ? ward.source : `${c.yellow}none found${c.reset}`}`);
65
+ return installed && ward ? 0 : 1;
66
+ }
67
+ function test(args) {
68
+ const tool = args.find((a) => !a.startsWith('--'));
69
+ if (!tool) {
70
+ say('Usage: weave-ward-opencode test <tool> [--input=JSON]');
71
+ return 2;
72
+ }
73
+ const ward = safeResolve();
74
+ if (!ward) {
75
+ say('No WARD.md found; nothing to test against.');
76
+ return 2;
77
+ }
78
+ const result = evaluateCall(ward.policy, tool, JSON.parse(option(args, 'input') ?? '{}'), process.cwd());
79
+ const color = result.decision === 'allow' ? c.green : result.decision === 'deny' ? c.red : c.yellow;
80
+ say(`${color}${result.decision}${c.reset} ${tool}`);
81
+ for (const r of result.reasons)
82
+ say(` ${c.gray}${r}${c.reset}`);
83
+ return result.decision === 'allow' ? 0 : 1;
84
+ }
85
+ const [cmd, ...rest] = process.argv.slice(2);
86
+ const code = cmd === 'init' ? init()
87
+ : cmd === 'disable' || cmd === 'uninstall' ? disable()
88
+ : cmd === 'status' ? status()
89
+ : cmd === 'test' ? test(rest)
90
+ : help();
91
+ process.exit(code);
92
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACpH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC1D,MAAM,CAAC,GAAG;IACR,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;IACnF,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;CACtH,CAAC;AACF,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACzC,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAEtH,SAAS,IAAI;IACX,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,2BAA2B,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,mCAAmC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACpH,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACpC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,mDAAmD,gBAAgB,EAAE,EAAE,CAAC,CAAC;IACtG,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,KAAK,sCAAsC,CAAC,CAAC;IACxE,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,KAAK,oEAAoE,CAAC,CAAC;IACrG,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,wBAAwB,CAAC,CAAC,KAAK,+CAA+C,CAAC,CAAC;IACvH,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,IAAI;IACX,IAAI,CAAC;QACH,aAAa,EAAE,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,WAAW,OAAO,2BAA2B,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC5F,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACnB,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,gCAAgC,IAAI,CAAC,iBAAiB,EAAE,EAAE,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAC1I,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,uDAAuD,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,OAAO;IACd,MAAM,EAAE,OAAO,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,4BAA4B,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC;IACxH,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,MAAM;IACb,MAAM,SAAS,GAAG,iBAAiB,CAAC,UAAU,EAAE,CAAC,CAAC;IAClD,GAAG,CAAC,aAAa,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,iBAAiB,CAAC,CAAC,KAAK,EAAE,KAAK,gBAAgB,EAAE,GAAG,CAAC,CAAC;IACjI,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3E,OAAO,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,IAAI,CAAC,IAAc;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,uDAAuD,CAAC,CAAC;QAC7D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzG,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACpG,GAAG,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO;QAAE,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACjE,OAAO,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC7C,MAAM,IAAI,GACR,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE;IACvB,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE;QACtD,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE;YAC7B,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC7B,CAAC,CAAC,IAAI,EAAE,CAAC;AACX,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Add or remove the plugin in OpenCode's global opencode.json
3
+ * ($XDG_CONFIG_HOME/opencode/opencode.json, default ~/.config/opencode).
4
+ * OpenCode installs npm plugins listed there on startup.
5
+ *
6
+ * Safety rules:
7
+ * - Back up opencode.json before changing it, and write atomically
8
+ * - Idempotent: init twice leaves one entry
9
+ * - Other plugins and settings are untouched
10
+ * - opencode.jsonc (JSON with comments) is never rewritten: we'd lose the
11
+ * comments, so init says what to add instead
12
+ */
13
+ export declare const PACKAGE = "@weave_protocol/adapter-opencode";
14
+ export declare function opencodeJsonPath(): string;
15
+ export declare function opencodeJsoncPath(): string;
16
+ export interface OpencodeConfig {
17
+ $schema?: string;
18
+ plugin?: Array<string | [string, unknown]>;
19
+ [other: string]: unknown;
20
+ }
21
+ export declare function readConfig(path?: string): OpencodeConfig;
22
+ export declare function writeConfig(config: OpencodeConfig, path?: string): void;
23
+ export declare function isPluginInstalled(config?: OpencodeConfig): boolean;
24
+ /** Add the plugin to opencode.json. Throws if only an opencode.jsonc exists. */
25
+ export declare function installPlugin(spec?: string): OpencodeConfig;
26
+ export declare function removePlugin(): {
27
+ removed: number;
28
+ config: OpencodeConfig;
29
+ };
30
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,eAAO,MAAM,OAAO,qCAAqC,CAAC;AAE1D,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,wBAAgB,UAAU,CAAC,IAAI,SAAqB,GAAG,cAAc,CASpE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,SAAqB,GAAG,IAAI,CAMnF;AAQD,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,cAA6B,GAAG,OAAO,CAEhF;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,IAAI,SAAU,GAAG,cAAc,CAS5D;AAED,wBAAgB,YAAY,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAW1E"}
package/dist/config.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Add or remove the plugin in OpenCode's global opencode.json
3
+ * ($XDG_CONFIG_HOME/opencode/opencode.json, default ~/.config/opencode).
4
+ * OpenCode installs npm plugins listed there on startup.
5
+ *
6
+ * Safety rules:
7
+ * - Back up opencode.json before changing it, and write atomically
8
+ * - Idempotent: init twice leaves one entry
9
+ * - Other plugins and settings are untouched
10
+ * - opencode.jsonc (JSON with comments) is never rewritten: we'd lose the
11
+ * comments, so init says what to add instead
12
+ */
13
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, copyFileSync } from 'node:fs';
14
+ import { dirname, join } from 'node:path';
15
+ import { opencodeConfigDir } from './policy.js';
16
+ export const PACKAGE = '@weave_protocol/adapter-opencode';
17
+ export function opencodeJsonPath() {
18
+ return join(opencodeConfigDir(), 'opencode.json');
19
+ }
20
+ export function opencodeJsoncPath() {
21
+ return join(opencodeConfigDir(), 'opencode.jsonc');
22
+ }
23
+ export function readConfig(path = opencodeJsonPath()) {
24
+ if (!existsSync(path))
25
+ return {};
26
+ try {
27
+ return JSON.parse(readFileSync(path, 'utf8'));
28
+ }
29
+ catch (err) {
30
+ throw new Error(`Failed to parse ${path}; fix the JSON or move it aside and re-run. ${err instanceof Error ? err.message : String(err)}`);
31
+ }
32
+ }
33
+ export function writeConfig(config, path = opencodeJsonPath()) {
34
+ mkdirSync(dirname(path), { recursive: true });
35
+ if (existsSync(path))
36
+ copyFileSync(path, `${path}.weave-backup`);
37
+ const tmp = `${path}.tmp`;
38
+ writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', 'utf8');
39
+ renameSync(tmp, path);
40
+ }
41
+ const entryName = (e) => (Array.isArray(e) ? e[0] : e);
42
+ const isOurs = (e) => {
43
+ const name = entryName(e);
44
+ return name === PACKAGE || name.startsWith(`${PACKAGE}@`);
45
+ };
46
+ export function isPluginInstalled(config = readConfig()) {
47
+ return (config.plugin ?? []).some(isOurs);
48
+ }
49
+ /** Add the plugin to opencode.json. Throws if only an opencode.jsonc exists. */
50
+ export function installPlugin(spec = PACKAGE) {
51
+ if (!existsSync(opencodeJsonPath()) && existsSync(opencodeJsoncPath())) {
52
+ throw new Error(`${opencodeJsoncPath()} has comments we won't rewrite. Add "${spec}" to its "plugin" list by hand.`);
53
+ }
54
+ const config = readConfig();
55
+ config.$schema ??= 'https://opencode.ai/config.json';
56
+ config.plugin = [...(config.plugin ?? []).filter((e) => !isOurs(e)), spec];
57
+ writeConfig(config);
58
+ return config;
59
+ }
60
+ export function removePlugin() {
61
+ const config = readConfig();
62
+ const before = config.plugin ?? [];
63
+ const after = before.filter((e) => !isOurs(e));
64
+ const removed = before.length - after.length;
65
+ if (removed > 0) {
66
+ if (after.length)
67
+ config.plugin = after;
68
+ else
69
+ delete config.plugin;
70
+ writeConfig(config);
71
+ }
72
+ return { removed, config };
73
+ }
74
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @weave_protocol/adapter-opencode — the OpenCode plugin.
3
+ *
4
+ * Add the package to the "plugin" list in opencode.json (weave-ward-opencode
5
+ * init does this) and every tool call is checked against WARD.md in
6
+ * tool.execute.before; a blocked call throws, and the agent sees the reason.
7
+ *
8
+ * This entry default-exports only the plugin (OpenCode's v1 plugin format:
9
+ * { id, server }). Helpers live at "@weave_protocol/adapter-opencode/lib".
10
+ *
11
+ * WARD.md is read on every call, so policy edits apply without a restart.
12
+ * An invalid WARD.md blocks every call rather than switching enforcement off.
13
+ */
14
+ interface PluginInput {
15
+ directory?: string;
16
+ worktree?: string;
17
+ }
18
+ interface ToolBefore {
19
+ tool: string;
20
+ sessionID?: string;
21
+ callID?: string;
22
+ }
23
+ declare const WardPlugin: ({ directory, worktree }: PluginInput) => Promise<{
24
+ 'tool.execute.before': (input: ToolBefore, output: {
25
+ args?: unknown;
26
+ }) => Promise<void>;
27
+ }>;
28
+ declare const _default: {
29
+ id: string;
30
+ server: typeof WardPlugin;
31
+ };
32
+ export default _default;
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,QAAA,MAAM,UAAU,4BAAmC,WAAW;mCAGrB,UAAU,UAAU;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE;EAe9E,CAAC;;IAEe,EAAE;IAAgB,MAAM"}
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @weave_protocol/adapter-opencode — the OpenCode plugin.
3
+ *
4
+ * Add the package to the "plugin" list in opencode.json (weave-ward-opencode
5
+ * init does this) and every tool call is checked against WARD.md in
6
+ * tool.execute.before; a blocked call throws, and the agent sees the reason.
7
+ *
8
+ * This entry default-exports only the plugin (OpenCode's v1 plugin format:
9
+ * { id, server }). Helpers live at "@weave_protocol/adapter-opencode/lib".
10
+ *
11
+ * WARD.md is read on every call, so policy edits apply without a restart.
12
+ * An invalid WARD.md blocks every call rather than switching enforcement off.
13
+ */
14
+ import { reportEvent } from '@weave_protocol/ward';
15
+ import { resolveWard, evaluateCall, blockMessage } from './policy.js';
16
+ const WardPlugin = async ({ directory, worktree }) => {
17
+ const dir = directory || worktree || process.cwd();
18
+ return {
19
+ 'tool.execute.before': async (input, output) => {
20
+ let ward;
21
+ try {
22
+ ward = resolveWard(dir);
23
+ }
24
+ catch (err) {
25
+ throw new Error(`WARD policy could not be loaded: ${err instanceof Error ? err.message : String(err)}. Fix WARD.md to continue.`);
26
+ }
27
+ if (!ward)
28
+ return;
29
+ const result = evaluateCall(ward.policy, input.tool, output.args, dir);
30
+ // Dashboard reporting (only when WEAVE_DASHBOARD_URL is set; never blocks)
31
+ void reportEvent({ surface: 'opencode', decision: result.decision, subject: input.tool, reason: result.reasons[0] });
32
+ const message = blockMessage(result, ward.source);
33
+ if (message)
34
+ throw new Error(message);
35
+ },
36
+ };
37
+ };
38
+ export default { id: 'weave-ward', server: WardPlugin };
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAatE,MAAM,UAAU,GAAG,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAe,EAAE,EAAE;IAChE,MAAM,GAAG,GAAG,SAAS,IAAI,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACnD,OAAO;QACL,qBAAqB,EAAE,KAAK,EAAE,KAAiB,EAAE,MAA0B,EAAE,EAAE;YAC7E,IAAI,IAAI,CAAC;YACT,IAAI,CAAC;gBACH,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YAC1B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,oCAAoC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;YACpI,CAAC;YACD,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACvE,2EAA2E;YAC3E,KAAK,WAAW,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACrH,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAClD,IAAI,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC"}
package/dist/lib.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Helpers behind the OpenCode plugin ("@weave_protocol/adapter-opencode/lib").
3
+ * The package's main entry exports only the plugin, as OpenCode requires.
4
+ */
5
+ export { TOOL_MAPPINGS, opencodeConfigDir, resolveWard, evaluateCall, blockMessage } from './policy.js';
6
+ export type { ResolvedWard, WardDecision } from './policy.js';
7
+ export { patchTargets } from './patch.js';
8
+ export type { PatchTargets } from './patch.js';
9
+ export { PACKAGE, opencodeJsonPath, opencodeJsoncPath, readConfig, writeConfig, installPlugin, removePlugin, isPluginInstalled, } from './config.js';
10
+ export type { OpencodeConfig } from './config.js';
11
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxG,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
package/dist/lib.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Helpers behind the OpenCode plugin ("@weave_protocol/adapter-opencode/lib").
3
+ * The package's main entry exports only the plugin, as OpenCode requires.
4
+ */
5
+ export { TOOL_MAPPINGS, opencodeConfigDir, resolveWard, evaluateCall, blockMessage } from './policy.js';
6
+ export { patchTargets } from './patch.js';
7
+ export { PACKAGE, opencodeJsonPath, opencodeJsoncPath, readConfig, writeConfig, installPlugin, removePlugin, isPluginInstalled, } from './config.js';
8
+ //# sourceMappingURL=lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The files an OpenCode apply_patch touches (its patchText). The patch
3
+ * format, shared with Codex, marks each file:
4
+ *
5
+ * *** Add File: <path> new file → write
6
+ * *** Update File: <path> edit in place → write
7
+ * *** Move to: <path> rename target → write
8
+ * *** Delete File: <path> removal → delete
9
+ *
10
+ * Paths are relative to the session cwd unless absolute.
11
+ */
12
+ export interface PatchTargets {
13
+ writes: string[];
14
+ deletes: string[];
15
+ }
16
+ export declare function patchTargets(patch: string): PatchTargets;
17
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CASxD"}
package/dist/patch.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The files an OpenCode apply_patch touches (its patchText). The patch
3
+ * format, shared with Codex, marks each file:
4
+ *
5
+ * *** Add File: <path> new file → write
6
+ * *** Update File: <path> edit in place → write
7
+ * *** Move to: <path> rename target → write
8
+ * *** Delete File: <path> removal → delete
9
+ *
10
+ * Paths are relative to the session cwd unless absolute.
11
+ */
12
+ const MARKER = /^\*\*\* (Add File|Update File|Move to|Delete File):\s*(.+?)\s*$/;
13
+ export function patchTargets(patch) {
14
+ const writes = [];
15
+ const deletes = [];
16
+ for (const line of patch.split(/\r?\n/)) {
17
+ const m = MARKER.exec(line);
18
+ if (!m)
19
+ continue;
20
+ (m[1] === 'Delete File' ? deletes : writes).push(m[2]);
21
+ }
22
+ return { writes, deletes };
23
+ }
24
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,MAAM,GAAG,iEAAiE,CAAC;AAEjF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * WARD decisions for OpenCode tool calls, with Ward's shared evaluator.
3
+ *
4
+ * Tool and argument names are OpenCode 1.19's (packages/opencode/src/tool):
5
+ * bash { command, workdir }, read/write/edit { filePath }, glob/grep
6
+ * { path }, webfetch { url }, apply_patch { patchText }. MCP tools arrive
7
+ * as <server>_<tool>; Ward checks their path and URL arguments by name.
8
+ */
9
+ import { type ToolMapping, type WardPolicy } from '@weave_protocol/ward';
10
+ export declare const TOOL_MAPPINGS: Record<string, ToolMapping>;
11
+ export interface ResolvedWard {
12
+ policy: WardPolicy;
13
+ source: string;
14
+ }
15
+ export interface WardDecision {
16
+ decision: 'allow' | 'deny' | 'require_approval';
17
+ reasons: string[];
18
+ }
19
+ /** OpenCode's global config directory ($XDG_CONFIG_HOME/opencode, default ~/.config/opencode). */
20
+ export declare function opencodeConfigDir(): string;
21
+ /**
22
+ * WARD.md resolution: $WEAVE_WARD_PATH, then <dir>/WARD.md,
23
+ * <dir>/.opencode/WARD.md, <dir>/.weave/WARD.md, then the global
24
+ * ~/.config/opencode/WARD.md. Throws on an invalid policy.
25
+ */
26
+ export declare function resolveWard(dir: string): ResolvedWard | null;
27
+ /** Decide an OpenCode tool call. */
28
+ export declare function evaluateCall(policy: WardPolicy, tool: string, args: unknown, dir: string): WardDecision;
29
+ /** The error message the agent sees for a blocked call, or null to let it run. */
30
+ export declare function blockMessage(result: WardDecision, source: string): string | null;
31
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAsC,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAG7G,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAarD,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,kBAAkB,CAAC;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmB5D;AAID,oCAAoC;AACpC,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAsBvG;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF"}
package/dist/policy.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * WARD decisions for OpenCode tool calls, with Ward's shared evaluator.
3
+ *
4
+ * Tool and argument names are OpenCode 1.19's (packages/opencode/src/tool):
5
+ * bash { command, workdir }, read/write/edit { filePath }, glob/grep
6
+ * { path }, webfetch { url }, apply_patch { patchText }. MCP tools arrive
7
+ * as <server>_<tool>; Ward checks their path and URL arguments by name.
8
+ */
9
+ import { readFileSync, existsSync } from 'node:fs';
10
+ import { homedir } from 'node:os';
11
+ import { isAbsolute, join, resolve } from 'node:path';
12
+ import { parseAndValidate, evaluateToolCall } from '@weave_protocol/ward';
13
+ import { patchTargets } from './patch.js';
14
+ export const TOOL_MAPPINGS = {
15
+ bash: { capability: 'shell_exec', commandField: 'command', pathField: 'workdir', fsOp: 'read' },
16
+ read: { capability: 'file_read', pathField: 'filePath', fsOp: 'read' },
17
+ write: { capability: 'file_write', pathField: 'filePath', fsOp: 'write' },
18
+ edit: { capability: 'file_write', pathField: 'filePath', fsOp: 'write' },
19
+ glob: { capability: 'file_list', pathField: 'path', fsOp: 'list' },
20
+ grep: { capability: 'file_read', pathField: 'path', fsOp: 'read' },
21
+ webfetch: { capability: 'http_request', urlField: 'url' },
22
+ websearch: { capability: 'web_search' },
23
+ task: { capability: 'subagent' },
24
+ skill: { capability: 'skill' },
25
+ todowrite: { capability: 'task_planning' },
26
+ question: { capability: 'task_planning' },
27
+ };
28
+ /** OpenCode's global config directory ($XDG_CONFIG_HOME/opencode, default ~/.config/opencode). */
29
+ export function opencodeConfigDir() {
30
+ return join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode');
31
+ }
32
+ /**
33
+ * WARD.md resolution: $WEAVE_WARD_PATH, then <dir>/WARD.md,
34
+ * <dir>/.opencode/WARD.md, <dir>/.weave/WARD.md, then the global
35
+ * ~/.config/opencode/WARD.md. Throws on an invalid policy.
36
+ */
37
+ export function resolveWard(dir) {
38
+ const candidates = [
39
+ process.env.WEAVE_WARD_PATH,
40
+ dir && join(dir, 'WARD.md'),
41
+ dir && join(dir, '.opencode', 'WARD.md'),
42
+ dir && join(dir, '.weave', 'WARD.md'),
43
+ join(opencodeConfigDir(), 'WARD.md'),
44
+ ].filter((p) => !!p);
45
+ for (const path of candidates) {
46
+ const full = resolve(path);
47
+ if (!existsSync(full))
48
+ continue;
49
+ const result = parseAndValidate(readFileSync(full, 'utf8'));
50
+ if (!result.valid || !result.policy) {
51
+ throw new Error(`Invalid WARD.md at ${full}: ${result.errors.map((e) => e.message).join('; ')}`);
52
+ }
53
+ return { policy: result.policy, source: full };
54
+ }
55
+ return null;
56
+ }
57
+ const RANK = { allow: 0, require_approval: 1, deny: 2 };
58
+ /** Decide an OpenCode tool call. */
59
+ export function evaluateCall(policy, tool, args, dir) {
60
+ const input = args && typeof args === 'object' && !Array.isArray(args) ? args : {};
61
+ // apply_patch: the files the patch writes and deletes, not its text
62
+ if (tool === 'apply_patch') {
63
+ const { writes, deletes } = patchTargets(String(input.patchText ?? ''));
64
+ const check = (target, fsOp) => evaluateToolCall(policy, tool, { target }, {
65
+ cwd: dir,
66
+ mappings: { apply_patch: { capability: 'file_write', pathField: 'target', fsOp } },
67
+ });
68
+ const results = [check(writes, 'write'), ...(deletes.length ? [check(deletes, 'delete')] : [])];
69
+ const worst = results.reduce((a, b) => (RANK[b.decision] > RANK[a.decision] ? b : a));
70
+ return { decision: worst.decision, reasons: results.flatMap((r) => r.reasons) };
71
+ }
72
+ // bash runs in workdir when one is given
73
+ const cwd = tool === 'bash' && typeof input.workdir === 'string' && input.workdir
74
+ ? (isAbsolute(input.workdir) ? input.workdir : join(dir, input.workdir))
75
+ : dir;
76
+ const { decision, reasons } = evaluateToolCall(policy, tool, input, { mappings: TOOL_MAPPINGS, cwd });
77
+ return { decision, reasons };
78
+ }
79
+ /** The error message the agent sees for a blocked call, or null to let it run. */
80
+ export function blockMessage(result, source) {
81
+ if (result.decision === 'deny')
82
+ return `WARD: ${result.reasons.join(' | ')} (policy: ${source})`;
83
+ if (result.decision === 'require_approval') {
84
+ return `WARD requires approval before this call: ${result.reasons.join(' | ')} (policy: ${source}). Ask the user to approve it or update WARD.md.`;
85
+ }
86
+ return null;
87
+ }
88
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "@weave_protocol/adapter-opencode",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Weave Protocol plugin for OpenCode - enforces WARD.md policies on every tool call via tool.execute.before",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "weave-ward-opencode": "dist/cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "test": "npm run build && node --test test/*.test.mjs",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": [
18
+ "weave-protocol",
19
+ "ward",
20
+ "opencode",
21
+ "opencode-plugin",
22
+ "agent-security",
23
+ "ai-security",
24
+ "plugin",
25
+ "harness-adapter",
26
+ "policy-enforcement",
27
+ "ward-md"
28
+ ],
29
+ "author": "Trent Yoxall",
30
+ "license": "Apache-2.0",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/Tyox-all/Weave_Protocol.git",
34
+ "directory": "adapter-opencode"
35
+ },
36
+ "homepage": "https://github.com/Tyox-all/Weave_Protocol/tree/main/adapter-opencode#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/Tyox-all/Weave_Protocol/issues"
39
+ },
40
+ "dependencies": {
41
+ "@weave_protocol/ward": "^0.3.1"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^26.6.6",
45
+ "typescript": "^7.0.2"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "files": [
51
+ "dist/",
52
+ "README.md"
53
+ ],
54
+ "exports": {
55
+ ".": {
56
+ "types": "./dist/index.d.ts",
57
+ "import": "./dist/index.js"
58
+ },
59
+ "./lib": {
60
+ "types": "./dist/lib.d.ts",
61
+ "import": "./dist/lib.js"
62
+ }
63
+ }
64
+ }