0595794385
- Default export is { id, setup, server }: OpenCode 2 calls setup(ctx), OpenCode 1 (1.18.29+) calls server()
- Tools defined once in lib/tools.ts and registered for both versions (V2 with codemode: false)
- Blocking covers path args of any tool, patch/apply_patch envelopes, shell commands naming env files, grep output, and V2 permission checks
- Secret-looking values from the project's env files are replaced in every non-envsitter tool's output
- Protected files come from envsitter's path policy; example/template files stay readable
- npm run test:e2e drives the real OpenCode 1 and 2 CLIs with a scripted model
- @opencode-ai/plugin ^1.19.3, @opencode/plugin ^2.1.0 (peer >=2.0.32), envsitter ^0.1.0, TypeScript 6
BREAKING CHANGE: the named export EnvSitterGuard is now the plugin definition object; call EnvSitterGuard.server(input) instead of EnvSitterGuard(input).
34 lines
1.3 KiB
TypeScript
34 lines
1.3 KiB
TypeScript
import type { Hooks, Plugin } from "@opencode-ai/plugin";
|
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
|
|
import { blockedAccessMessage, findBlockedAccess, redactGrepOutput } from "./policy.js";
|
|
import { createScrubLoader } from "./scrub.js";
|
|
import { guardTools, type ToolEnvironment } from "./tools.js";
|
|
|
|
export const serverV1: Plugin = async ({ directory, worktree }) => {
|
|
const env: ToolEnvironment = { directory, root: worktree };
|
|
const loadScrub = createScrubLoader(worktree);
|
|
|
|
const tools: NonNullable<Hooks["tool"]> = {};
|
|
for (const guardTool of guardTools) {
|
|
tools[guardTool.name] = tool({
|
|
description: guardTool.description,
|
|
args: guardTool.args,
|
|
execute: (args) => guardTool.run(args, env),
|
|
});
|
|
}
|
|
|
|
return {
|
|
tool: tools,
|
|
"tool.execute.before": async (input, output) => {
|
|
const blocked = findBlockedAccess(input.tool, output.args);
|
|
if (blocked) throw new Error(blockedAccessMessage(blocked));
|
|
},
|
|
"tool.execute.after": async (input, output) => {
|
|
if (input.tool.startsWith("envsitter_") || typeof output.output !== "string") return;
|
|
const text = input.tool === "grep" ? redactGrepOutput(output.output).output : output.output;
|
|
output.output = (await loadScrub())(text);
|
|
},
|
|
};
|
|
};
|