feat: support OpenCode 2 and scrub secret values from tool output
- 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).
This commit is contained in:
@@ -125,13 +125,14 @@ async function getTools(params: { directory: string; worktree: string }): Promis
|
||||
project: {} as unknown as PluginInput["project"],
|
||||
directory: params.directory,
|
||||
worktree: params.worktree,
|
||||
experimental_workspace: { register() {} },
|
||||
serverUrl: new URL("http://localhost"),
|
||||
$: (() => {
|
||||
throw new Error("not used in tests");
|
||||
}) as unknown as PluginInput["$"],
|
||||
};
|
||||
|
||||
const hooks = (await EnvSitterGuard(pluginInput)) as unknown as {
|
||||
const hooks = (await EnvSitterGuard.server(pluginInput)) as unknown as {
|
||||
tool: ToolApi;
|
||||
};
|
||||
|
||||
@@ -305,6 +306,24 @@ test("tool execution rejects paths outside worktree", async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("envsitter tools accept named env files such as api.env", async () => {
|
||||
await withPepper(async () => {
|
||||
const worktree = await createTmpDir();
|
||||
await fs.writeFile(path.join(worktree, "api.env"), "API_TOKEN=secret-token\n");
|
||||
|
||||
const tools = await getTools({ directory: worktree, worktree });
|
||||
const out = await tools.envsitter_keys.execute({ filePath: "api.env" });
|
||||
|
||||
assert.ok(!out.includes("secret-token"));
|
||||
assert.deepEqual((JSON.parse(out) as { keys: string[] }).keys, ["API_TOKEN"]);
|
||||
|
||||
await assert.rejects(
|
||||
() => tools.envsitter_keys.execute({ filePath: "src/index.ts" }),
|
||||
(err: unknown) => err instanceof Error && err.message.includes("Only dotenv files are allowed"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("tool execution blocks .envsitter/pepper", async () => {
|
||||
const worktree = await createTmpDir();
|
||||
const tools = await getTools({ directory: worktree, worktree });
|
||||
|
||||
Reference in New Issue
Block a user