@agentwares/agentguard 0.1.9 → 0.1.10
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/CHANGELOG.md +33 -0
- package/dist/audit-5LUDXZLG.js +18 -0
- package/dist/chunk-2QDPPTPY.js +179 -0
- package/dist/chunk-2QDPPTPY.js.map +1 -0
- package/dist/{chunk-SUXQTT3X.js → chunk-4WTFMFRY.js} +18 -16
- package/dist/chunk-4WTFMFRY.js.map +1 -0
- package/dist/{chunk-VQWQ5TOM.js → chunk-DEVXG3IB.js} +4 -4
- package/dist/chunk-DEVXG3IB.js.map +1 -0
- package/dist/chunk-F73VDY3W.js +202 -0
- package/dist/chunk-F73VDY3W.js.map +1 -0
- package/dist/{chunk-X4M7YWQA.js → chunk-IEJK7N3B.js} +32 -27
- package/dist/chunk-IEJK7N3B.js.map +1 -0
- package/dist/{chunk-ZSLWPKXI.js → chunk-IS3DAHZI.js} +562 -8
- package/dist/chunk-IS3DAHZI.js.map +1 -0
- package/dist/{chunk-KRKZGAJ3.js → chunk-JSELR4SG.js} +66 -55
- package/dist/chunk-JSELR4SG.js.map +1 -0
- package/dist/{chunk-TW5KJREH.js → chunk-OM5L7A32.js} +20 -11
- package/dist/chunk-OM5L7A32.js.map +1 -0
- package/dist/{chunk-HPYZ3JDX.js → chunk-SWZQMPDG.js} +3 -3
- package/dist/{chunk-HPYZ3JDX.js.map → chunk-SWZQMPDG.js.map} +1 -1
- package/dist/{chunk-D66U3PVB.js → chunk-TJXZBOHL.js} +6 -6
- package/dist/chunk-TJXZBOHL.js.map +1 -0
- package/dist/{chunk-BKXXTWIS.js → chunk-XOBFANL4.js} +5 -5
- package/dist/chunk-XOBFANL4.js.map +1 -0
- package/dist/chunk-XPNBYSIN.js +23 -0
- package/dist/chunk-XPNBYSIN.js.map +1 -0
- package/dist/{chunk-6S7LYVOC.js → chunk-YBVD6HTT.js} +27 -24
- package/dist/chunk-YBVD6HTT.js.map +1 -0
- package/dist/{chunk-6TUQD666.js → chunk-ZG4LGUJE.js} +100 -81
- package/dist/chunk-ZG4LGUJE.js.map +1 -0
- package/dist/cli.js +6 -4
- package/dist/fixtures/crm-server.js +2 -2
- package/dist/fixtures/demo-agent.js +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +23 -21
- package/dist/index.js.map +1 -1
- package/dist/init-MKPQU2JG.js +17 -0
- package/dist/{proxy-3Z5YEZ4R.js → proxy-6GP63TLD.js} +21 -29
- package/dist/proxy-6GP63TLD.js.map +1 -0
- package/dist/tools-XS7PMTLT.js +15 -0
- package/llms.txt +8 -6
- package/package.json +10 -4
- package/server.json +9 -9
- package/dist/audit-2YCMHMCP.js +0 -17
- package/dist/chunk-3O6KWT3E.js +0 -345
- package/dist/chunk-3O6KWT3E.js.map +0 -1
- package/dist/chunk-6S7LYVOC.js.map +0 -1
- package/dist/chunk-6TUQD666.js.map +0 -1
- package/dist/chunk-BKXXTWIS.js.map +0 -1
- package/dist/chunk-D66U3PVB.js.map +0 -1
- package/dist/chunk-KRKZGAJ3.js.map +0 -1
- package/dist/chunk-SUXQTT3X.js.map +0 -1
- package/dist/chunk-TW5KJREH.js.map +0 -1
- package/dist/chunk-VQWQ5TOM.js.map +0 -1
- package/dist/chunk-X4M7YWQA.js.map +0 -1
- package/dist/chunk-ZSLWPKXI.js.map +0 -1
- package/dist/init-CWROGV5I.js +0 -15
- package/dist/proxy-3Z5YEZ4R.js.map +0 -1
- package/dist/tools-3LLDG3XQ.js +0 -15
- /package/dist/{audit-2YCMHMCP.js.map → audit-5LUDXZLG.js.map} +0 -0
- /package/dist/{init-CWROGV5I.js.map → init-MKPQU2JG.js.map} +0 -0
- /package/dist/{tools-3LLDG3XQ.js.map → tools-XS7PMTLT.js.map} +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLIENT_ALIASES,
|
|
3
|
+
hookTarget,
|
|
4
|
+
installHook,
|
|
5
|
+
installedHandlers,
|
|
6
|
+
uninstallHook
|
|
7
|
+
} from "./chunk-2QDPPTPY.js";
|
|
8
|
+
import {
|
|
9
|
+
npxCommand
|
|
10
|
+
} from "./chunk-XPNBYSIN.js";
|
|
11
|
+
import {
|
|
12
|
+
flagBool,
|
|
13
|
+
flagList,
|
|
14
|
+
flagNumber,
|
|
15
|
+
flagString,
|
|
16
|
+
homeFrom,
|
|
17
|
+
version
|
|
18
|
+
} from "./chunk-4WTFMFRY.js";
|
|
19
|
+
|
|
20
|
+
// src/commands/hooks.ts
|
|
21
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
22
|
+
import { dirname, join, relative, resolve } from "path";
|
|
23
|
+
import {
|
|
24
|
+
LEGACY_POLICY_FILE,
|
|
25
|
+
LEGACY_STATE_DIR,
|
|
26
|
+
POLICY_FILE,
|
|
27
|
+
STATE_DIR
|
|
28
|
+
} from "@agentwares/agentguard-core";
|
|
29
|
+
import { findPolicyFile } from "@agentwares/agentguard-core/node";
|
|
30
|
+
function userPolicyPath(home) {
|
|
31
|
+
const next = join(home, STATE_DIR, POLICY_FILE);
|
|
32
|
+
const legacy = join(home, LEGACY_STATE_DIR, LEGACY_POLICY_FILE);
|
|
33
|
+
return !existsSync(next) && existsSync(legacy) ? legacy : next;
|
|
34
|
+
}
|
|
35
|
+
var HOOKS_POLICY_BLOCK = `
|
|
36
|
+
# Hook mode: your coding agent's own shell commands and file edits (Claude Code, Codex, Gemini CLI),
|
|
37
|
+
# checked before they run. \`okgate hooks install\` wired it; \`okgate report\` shows the decisions.
|
|
38
|
+
hooks:
|
|
39
|
+
# dry-run: every decision is logged and nothing is blocked. Read \`okgate report\`, then set
|
|
40
|
+
# enforce. The kill switch (\`okgate kill\`) stops guarded calls in both modes.
|
|
41
|
+
mode: dry-run
|
|
42
|
+
# Irreversible commands run only on a turn a person typed: after the session's previous
|
|
43
|
+
# irreversible command, recent, and naming the action ("push it", or "yes" to "shall I push?").
|
|
44
|
+
# Text the model writes, tool output and injected messages never count. Otherwise the agent
|
|
45
|
+
# gets APPROVAL_REQUIRED and an id; \`okgate approve <id>\` lets that exact command run once.
|
|
46
|
+
# Built in: git-push, merge, tag-delete, rm-outside-workspace, publish, send, guard-config.
|
|
47
|
+
irreversible:
|
|
48
|
+
builtins: true
|
|
49
|
+
window_s: 1800 # the authorizing turn is at most 30 minutes old
|
|
50
|
+
require_mention: true # ...and names the action
|
|
51
|
+
require_origin: false # true: only entries Claude Code labels as typed by a person
|
|
52
|
+
mentions: {} # extra words per class, e.g. { git-push: [deploy] }
|
|
53
|
+
rules: []
|
|
54
|
+
# - name: deploy
|
|
55
|
+
# shell: ["vercel --prod*", "fly deploy*"]
|
|
56
|
+
# mentions: [deploy]
|
|
57
|
+
shell:
|
|
58
|
+
deny: [] # e.g. ["terraform destroy*", "curl * | sh"]
|
|
59
|
+
approval: [] # e.g. ["kubectl delete *"]
|
|
60
|
+
paths:
|
|
61
|
+
deny: [] # e.g. [".env", "*.pem"]
|
|
62
|
+
approval: [] # e.g. ["**/migrations/**"]
|
|
63
|
+
`;
|
|
64
|
+
function renderHooksStarterPolicy(generatedAt = /* @__PURE__ */ new Date()) {
|
|
65
|
+
return `# okgate.yaml \u2014 generated by \`okgate hooks install\` on ${generatedAt.toISOString().slice(0, 10)}
|
|
66
|
+
# Docs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli#hook-mode-a-coding-agents-own-commands
|
|
67
|
+
version: 1
|
|
68
|
+
|
|
69
|
+
# MCP proxy mode (okgate init) reads upstreams; hook mode needs none.
|
|
70
|
+
mode: dry-run
|
|
71
|
+
upstreams: []
|
|
72
|
+
${HOOKS_POLICY_BLOCK}
|
|
73
|
+
# Kill switch: \`okgate kill\` creates this file; every guarded call is refused until
|
|
74
|
+
# \`okgate resume\`. OKGATE_KILL=1 (or AGENTGUARD_KILL=1) in the environment does the same.
|
|
75
|
+
kill:
|
|
76
|
+
file: .okgate/KILL
|
|
77
|
+
|
|
78
|
+
# Hash-chained JSONL of every decision; \`okgate verify\` proves it was not edited.
|
|
79
|
+
audit:
|
|
80
|
+
path: .okgate/audit.jsonl
|
|
81
|
+
redact: true
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
function quoteArg(arg) {
|
|
85
|
+
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);
|
|
86
|
+
}
|
|
87
|
+
function clientsFrom(args) {
|
|
88
|
+
const names = flagList(args, "client");
|
|
89
|
+
if (names.length === 0) return ["claude-code"];
|
|
90
|
+
if (names.includes("all")) return ["claude-code", "codex", "gemini-cli"];
|
|
91
|
+
return names.map((n) => {
|
|
92
|
+
const c = CLIENT_ALIASES[n.trim()];
|
|
93
|
+
if (!c) throw new Error(`unknown --client "${n}" (claude, codex, gemini or all)`);
|
|
94
|
+
return c;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function hasTopLevelHooks(yamlText) {
|
|
98
|
+
return /^hooks\s*:/m.test(yamlText);
|
|
99
|
+
}
|
|
100
|
+
async function hooksCommand(args, io) {
|
|
101
|
+
const sub = args.positionals[0] ?? "status";
|
|
102
|
+
const scope = flagBool(args, "user") ? "user" : "project";
|
|
103
|
+
const home = homeFrom(io);
|
|
104
|
+
const where = { cwd: io.cwd, home };
|
|
105
|
+
const rel = (p) => {
|
|
106
|
+
const r = relative(io.cwd, p);
|
|
107
|
+
return r.startsWith("..") ? p : r || ".";
|
|
108
|
+
};
|
|
109
|
+
const clients = clientsFrom(args);
|
|
110
|
+
const named = flagString(args, "config");
|
|
111
|
+
const policyPath = named !== void 0 ? resolve(io.cwd, named) : scope === "user" ? userPolicyPath(home) : findPolicyFile(io.cwd);
|
|
112
|
+
const base = flagString(args, "command") ?? npxCommand(version());
|
|
113
|
+
const commandFor = (client) => {
|
|
114
|
+
let cmd = `${base} hook pre-tool-use --client ${client === "claude-code" ? "claude" : client === "gemini-cli" ? "gemini" : "codex"}`;
|
|
115
|
+
if (flagString(args, "config")) cmd += ` --config ${quoteArg(policyPath)}`;
|
|
116
|
+
else if (scope === "user") cmd += ` --fallback-config ${quoteArg(policyPath)}`;
|
|
117
|
+
return cmd;
|
|
118
|
+
};
|
|
119
|
+
const timeoutS = Math.max(30, flagNumber(args, "timeout") ?? 30);
|
|
120
|
+
if (sub === "install") {
|
|
121
|
+
if (!existsSync(policyPath)) {
|
|
122
|
+
mkdirSync(dirname(policyPath), { recursive: true });
|
|
123
|
+
writeFileSync(policyPath, renderHooksStarterPolicy());
|
|
124
|
+
io.out(`wrote ${rel(policyPath)} (hooks.mode: dry-run)`);
|
|
125
|
+
} else if (!hasTopLevelHooks(readFileSync(policyPath, "utf8"))) {
|
|
126
|
+
appendFileSync(policyPath, HOOKS_POLICY_BLOCK);
|
|
127
|
+
io.out(`added a hooks: block to ${rel(policyPath)} (hooks.mode: dry-run)`);
|
|
128
|
+
} else io.out(`kept ${rel(policyPath)} (it already has a hooks: block)`);
|
|
129
|
+
for (const client of clients) {
|
|
130
|
+
const target = hookTarget(client, scope, where, commandFor(client), timeoutS);
|
|
131
|
+
const { status, backup } = installHook(target);
|
|
132
|
+
const verb = status === "unchanged" ? "kept " : status === "updated" ? "updated" : "added ";
|
|
133
|
+
io.out(
|
|
134
|
+
`${verb} ${target.label} ${target.event} hook in ${rel(target.settingsPath)}${backup ? ` (backup: ${rel(backup)})` : ""}`
|
|
135
|
+
);
|
|
136
|
+
if (target.note) io.out(` ${target.note}`);
|
|
137
|
+
}
|
|
138
|
+
if (scope === "project" && existsSync(join(io.cwd, ".git"))) {
|
|
139
|
+
const gitignore = join(io.cwd, ".gitignore");
|
|
140
|
+
const text = existsSync(gitignore) ? readFileSync(gitignore, "utf8") : "";
|
|
141
|
+
if (!/^\/?\.okgate\/?$/m.test(text)) {
|
|
142
|
+
appendFileSync(gitignore, `${text && !text.endsWith("\n") ? "\n" : ""}.okgate/
|
|
143
|
+
`);
|
|
144
|
+
io.out(`added .okgate/ to .gitignore (approvals, counters and the audit log stay local)`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
io.out("");
|
|
148
|
+
io.out("Next:");
|
|
149
|
+
io.out(" 1. start a new session of your coding agent (hooks are read at start)");
|
|
150
|
+
io.out(" 2. work as usual \u2014 hooks.mode is dry-run: decisions are logged, nothing is blocked");
|
|
151
|
+
io.out(" 3. okgate report # what enforce mode would have stopped, and why");
|
|
152
|
+
io.out(` 4. set \`hooks.mode: enforce\` in ${rel(policyPath)} when it looks right`);
|
|
153
|
+
io.out(" undo: okgate hooks uninstall" + (scope === "user" ? " --user" : ""));
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
if (sub === "uninstall") {
|
|
157
|
+
let total = 0;
|
|
158
|
+
for (const client of flagString(args, "client") ? clients : ["claude-code", "codex", "gemini-cli"]) {
|
|
159
|
+
const target = hookTarget(client, scope, where, commandFor(client), timeoutS);
|
|
160
|
+
const removed = uninstallHook(target);
|
|
161
|
+
total += removed;
|
|
162
|
+
if (removed) io.out(`removed ${target.label} hook from ${rel(target.settingsPath)}`);
|
|
163
|
+
}
|
|
164
|
+
if (total === 0) io.out(`no okgate hooks in ${scope}-level settings`);
|
|
165
|
+
else
|
|
166
|
+
io.out(
|
|
167
|
+
`kept ${rel(policyPath)} and its state directory (${STATE_DIR}/ or ${LEGACY_STATE_DIR}/); delete them yourself if you want them gone`
|
|
168
|
+
);
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
if (sub === "status") {
|
|
172
|
+
for (const s of ["project", "user"]) {
|
|
173
|
+
for (const client of ["claude-code", "codex", "gemini-cli"]) {
|
|
174
|
+
const target = hookTarget(client, s, where, "", timeoutS);
|
|
175
|
+
let handlers;
|
|
176
|
+
try {
|
|
177
|
+
handlers = installedHandlers(target);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
io.out(
|
|
180
|
+
`${s.padEnd(7)} ${target.label.padEnd(11)} unreadable: ${err instanceof Error ? err.message : String(err)}`
|
|
181
|
+
);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
io.out(
|
|
185
|
+
`${s.padEnd(7)} ${target.label.padEnd(11)} ${handlers.length ? `installed: ${String(handlers[0].command)}` : "\u2014"} (${rel(target.settingsPath)})`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
io.err(
|
|
192
|
+
"usage: okgate hooks install|uninstall|status [--project|--user] [--client claude|codex|gemini|all] [--command cmd] [--config path]"
|
|
193
|
+
);
|
|
194
|
+
return 1;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export {
|
|
198
|
+
HOOKS_POLICY_BLOCK,
|
|
199
|
+
renderHooksStarterPolicy,
|
|
200
|
+
hooksCommand
|
|
201
|
+
};
|
|
202
|
+
//# sourceMappingURL=chunk-F73VDY3W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/hooks.ts"],"sourcesContent":["/**\n * `okgate hooks install|uninstall|status [--project|--user] [--client claude|codex|gemini]`\n *\n * Install writes the coding agent's pre-execution hook entry (Claude Code `PreToolUse`, Codex\n * `PreToolUse`, Gemini CLI `BeforeTool`) pointing at `okgate hook pre-tool-use`, and makes\n * sure the policy has a `hooks:` block in dry-run. Uninstall removes only okgate's entries.\n */\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport {\n LEGACY_POLICY_FILE,\n LEGACY_STATE_DIR,\n POLICY_FILE,\n STATE_DIR,\n type HookClient,\n} from \"@agentwares/agentguard-core\";\nimport { findPolicyFile } from \"@agentwares/agentguard-core/node\";\nimport { flagBool, flagList, flagNumber, flagString, type ParsedArgs } from \"../args.js\";\nimport { homeFrom, type Io } from \"../context.js\";\nimport {\n CLIENT_ALIASES,\n hookTarget,\n installHook,\n installedHandlers,\n uninstallHook,\n type InstallScope,\n} from \"../hooks/clients.js\";\nimport { npxCommand } from \"../install.js\";\nimport { version } from \"../version.js\";\n\n/** `~/.okgate/okgate.yaml`, or the `~/.agentguard/agentguard.yaml` an earlier install wrote. */\nexport function userPolicyPath(home: string): string {\n const next = join(home, STATE_DIR, POLICY_FILE);\n const legacy = join(home, LEGACY_STATE_DIR, LEGACY_POLICY_FILE);\n return !existsSync(next) && existsSync(legacy) ? legacy : next;\n}\n\n/** The commented `hooks:` block: the file people read, so every knob explains itself. */\nexport const HOOKS_POLICY_BLOCK = `\n# Hook mode: your coding agent's own shell commands and file edits (Claude Code, Codex, Gemini CLI),\n# checked before they run. \\`okgate hooks install\\` wired it; \\`okgate report\\` shows the decisions.\nhooks:\n # dry-run: every decision is logged and nothing is blocked. Read \\`okgate report\\`, then set\n # enforce. The kill switch (\\`okgate kill\\`) stops guarded calls in both modes.\n mode: dry-run\n # Irreversible commands run only on a turn a person typed: after the session's previous\n # irreversible command, recent, and naming the action (\"push it\", or \"yes\" to \"shall I push?\").\n # Text the model writes, tool output and injected messages never count. Otherwise the agent\n # gets APPROVAL_REQUIRED and an id; \\`okgate approve <id>\\` lets that exact command run once.\n # Built in: git-push, merge, tag-delete, rm-outside-workspace, publish, send, guard-config.\n irreversible:\n builtins: true\n window_s: 1800 # the authorizing turn is at most 30 minutes old\n require_mention: true # ...and names the action\n require_origin: false # true: only entries Claude Code labels as typed by a person\n mentions: {} # extra words per class, e.g. { git-push: [deploy] }\n rules: []\n # - name: deploy\n # shell: [\"vercel --prod*\", \"fly deploy*\"]\n # mentions: [deploy]\n shell:\n deny: [] # e.g. [\"terraform destroy*\", \"curl * | sh\"]\n approval: [] # e.g. [\"kubectl delete *\"]\n paths:\n deny: [] # e.g. [\".env\", \"*.pem\"]\n approval: [] # e.g. [\"**/migrations/**\"]\n`;\n\n/** A policy for a project that has no okgate.yaml yet: hooks only, no MCP upstreams. */\nexport function renderHooksStarterPolicy(generatedAt = new Date()): string {\n return `# okgate.yaml — generated by \\`okgate hooks install\\` on ${generatedAt.toISOString().slice(0, 10)}\n# Docs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli#hook-mode-a-coding-agents-own-commands\nversion: 1\n\n# MCP proxy mode (okgate init) reads upstreams; hook mode needs none.\nmode: dry-run\nupstreams: []\n${HOOKS_POLICY_BLOCK}\n# Kill switch: \\`okgate kill\\` creates this file; every guarded call is refused until\n# \\`okgate resume\\`. OKGATE_KILL=1 (or AGENTGUARD_KILL=1) in the environment does the same.\nkill:\n file: .okgate/KILL\n\n# Hash-chained JSONL of every decision; \\`okgate verify\\` proves it was not edited.\naudit:\n path: .okgate/audit.jsonl\n redact: true\n`;\n}\n\nfunction quoteArg(arg: string): string {\n return /^[A-Za-z0-9_@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);\n}\n\nfunction clientsFrom(args: ParsedArgs): HookClient[] {\n const names = flagList(args, \"client\");\n if (names.length === 0) return [\"claude-code\"];\n if (names.includes(\"all\")) return [\"claude-code\", \"codex\", \"gemini-cli\"];\n return names.map((n) => {\n const c = CLIENT_ALIASES[n.trim()];\n if (!c) throw new Error(`unknown --client \"${n}\" (claude, codex, gemini or all)`);\n return c;\n });\n}\n\nfunction hasTopLevelHooks(yamlText: string): boolean {\n return /^hooks\\s*:/m.test(yamlText);\n}\n\nexport async function hooksCommand(args: ParsedArgs, io: Io): Promise<number> {\n const sub = args.positionals[0] ?? \"status\";\n const scope: InstallScope = flagBool(args, \"user\") ? \"user\" : \"project\";\n const home = homeFrom(io);\n const where = { cwd: io.cwd, home };\n const rel = (p: string): string => {\n const r = relative(io.cwd, p);\n return r.startsWith(\"..\") ? p : r || \".\";\n };\n const clients = clientsFrom(args);\n const named = flagString(args, \"config\");\n const policyPath =\n named !== undefined\n ? resolve(io.cwd, named)\n : scope === \"user\"\n ? userPolicyPath(home)\n : findPolicyFile(io.cwd);\n const base = flagString(args, \"command\") ?? npxCommand(version());\n const commandFor = (client: HookClient): string => {\n let cmd = `${base} hook pre-tool-use --client ${client === \"claude-code\" ? \"claude\" : client === \"gemini-cli\" ? \"gemini\" : \"codex\"}`;\n if (flagString(args, \"config\")) cmd += ` --config ${quoteArg(policyPath)}`;\n else if (scope === \"user\") cmd += ` --fallback-config ${quoteArg(policyPath)}`;\n return cmd;\n };\n const timeoutS = Math.max(30, flagNumber(args, \"timeout\") ?? 30);\n\n if (sub === \"install\") {\n // the policy first: a hook with no policy to read guards nothing\n if (!existsSync(policyPath)) {\n mkdirSync(dirname(policyPath), { recursive: true });\n writeFileSync(policyPath, renderHooksStarterPolicy());\n io.out(`wrote ${rel(policyPath)} (hooks.mode: dry-run)`);\n } else if (!hasTopLevelHooks(readFileSync(policyPath, \"utf8\"))) {\n appendFileSync(policyPath, HOOKS_POLICY_BLOCK);\n io.out(`added a hooks: block to ${rel(policyPath)} (hooks.mode: dry-run)`);\n } else io.out(`kept ${rel(policyPath)} (it already has a hooks: block)`);\n\n for (const client of clients) {\n const target = hookTarget(client, scope, where, commandFor(client), timeoutS);\n const { status, backup } = installHook(target);\n const verb =\n status === \"unchanged\" ? \"kept \" : status === \"updated\" ? \"updated\" : \"added \";\n io.out(\n `${verb} ${target.label} ${target.event} hook in ${rel(target.settingsPath)}${backup ? ` (backup: ${rel(backup)})` : \"\"}`,\n );\n if (target.note) io.out(` ${target.note}`);\n }\n\n if (scope === \"project\" && existsSync(join(io.cwd, \".git\"))) {\n const gitignore = join(io.cwd, \".gitignore\");\n const text = existsSync(gitignore) ? readFileSync(gitignore, \"utf8\") : \"\";\n if (!/^\\/?\\.okgate\\/?$/m.test(text)) {\n appendFileSync(gitignore, `${text && !text.endsWith(\"\\n\") ? \"\\n\" : \"\"}.okgate/\\n`);\n io.out(`added .okgate/ to .gitignore (approvals, counters and the audit log stay local)`);\n }\n }\n\n io.out(\"\");\n io.out(\"Next:\");\n io.out(\" 1. start a new session of your coding agent (hooks are read at start)\");\n io.out(\" 2. work as usual — hooks.mode is dry-run: decisions are logged, nothing is blocked\");\n io.out(\" 3. okgate report # what enforce mode would have stopped, and why\");\n io.out(` 4. set \\`hooks.mode: enforce\\` in ${rel(policyPath)} when it looks right`);\n io.out(\" undo: okgate hooks uninstall\" + (scope === \"user\" ? \" --user\" : \"\"));\n return 0;\n }\n\n if (sub === \"uninstall\") {\n let total = 0;\n for (const client of flagString(args, \"client\")\n ? clients\n : ([\"claude-code\", \"codex\", \"gemini-cli\"] as HookClient[])) {\n const target = hookTarget(client, scope, where, commandFor(client), timeoutS);\n const removed = uninstallHook(target);\n total += removed;\n if (removed) io.out(`removed ${target.label} hook from ${rel(target.settingsPath)}`);\n }\n if (total === 0) io.out(`no okgate hooks in ${scope}-level settings`);\n else\n io.out(\n `kept ${rel(policyPath)} and its state directory (${STATE_DIR}/ or ${LEGACY_STATE_DIR}/); delete them yourself if you want them gone`,\n );\n return 0;\n }\n\n if (sub === \"status\") {\n for (const s of [\"project\", \"user\"] as InstallScope[]) {\n for (const client of [\"claude-code\", \"codex\", \"gemini-cli\"] as HookClient[]) {\n const target = hookTarget(client, s, where, \"\", timeoutS);\n let handlers: Record<string, unknown>[];\n try {\n handlers = installedHandlers(target);\n } catch (err) {\n io.out(\n `${s.padEnd(7)} ${target.label.padEnd(11)} unreadable: ${err instanceof Error ? err.message : String(err)}`,\n );\n continue;\n }\n io.out(\n `${s.padEnd(7)} ${target.label.padEnd(11)} ${handlers.length ? `installed: ${String(handlers[0]!.command)}` : \"—\"} (${rel(target.settingsPath)})`,\n );\n }\n }\n return 0;\n }\n\n io.err(\n \"usage: okgate hooks install|uninstall|status [--project|--user] [--client claude|codex|gemini|all] [--command cmd] [--config path]\",\n );\n return 1;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
probeUpstreams
|
|
3
|
-
} from "./chunk-VQWQ5TOM.js";
|
|
4
1
|
import {
|
|
5
2
|
describeConfigPath,
|
|
6
3
|
detectClientConfigs,
|
|
@@ -8,13 +5,19 @@ import {
|
|
|
8
5
|
restoreClientConfig,
|
|
9
6
|
rewriteClientConfig,
|
|
10
7
|
serversToUpstreams
|
|
11
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OM5L7A32.js";
|
|
9
|
+
import {
|
|
10
|
+
probeUpstreams
|
|
11
|
+
} from "./chunk-DEVXG3IB.js";
|
|
12
|
+
import {
|
|
13
|
+
RUN
|
|
14
|
+
} from "./chunk-XPNBYSIN.js";
|
|
12
15
|
import {
|
|
13
16
|
flagBool,
|
|
14
17
|
flagString,
|
|
15
18
|
homeFrom,
|
|
16
19
|
policyPathFrom
|
|
17
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-4WTFMFRY.js";
|
|
18
21
|
|
|
19
22
|
// src/commands/init.ts
|
|
20
23
|
import { existsSync, writeFileSync } from "fs";
|
|
@@ -38,22 +41,22 @@ function renderStarterPolicy(opts) {
|
|
|
38
41
|
(acc, t) => ({ ...acc, [t.classification.class]: (acc[t.classification.class] ?? 0) + 1 }),
|
|
39
42
|
{}
|
|
40
43
|
);
|
|
41
|
-
const detected = tools.length === 0 ? ["# (no upstreams probed \u2014 run `
|
|
44
|
+
const detected = tools.length === 0 ? ["# (no upstreams probed \u2014 run `okgate tools` after fixing the upstreams above)"] : tools.map(
|
|
42
45
|
(t) => `# ${t.name.padEnd(36)} ${t.classification.class.padEnd(8)} ${t.classification.verb.padEnd(8)} ${t.classification.source}: ${t.classification.reason}`
|
|
43
46
|
);
|
|
44
47
|
const probeNotes = (opts.probeErrors ?? []).map((e) => `# ! ${e}`);
|
|
45
48
|
const upstreamsYaml = opts.upstreams.length ? dump({ upstreams: opts.upstreams.map(cleanUpstream) }) : 'upstreams: []\n # - name: crm\n # url: https://mcp.example.com/mcp\n # auth: ${CRM_TOKEN}\n # - name: files\n # command: npx\n # args: [-y, "@modelcontextprotocol/server-filesystem", "."]';
|
|
46
|
-
return `#
|
|
49
|
+
return `# okgate.yaml \u2014 generated by \`okgate init\` on ${date}
|
|
47
50
|
# Every knob is explained inline. Docs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli#policy
|
|
48
51
|
version: 1
|
|
49
52
|
|
|
50
53
|
# dry-run: reads pass through; writes and spends are FAKED (plausible success, nothing executes)
|
|
51
|
-
# and recorded. Run your agent once, then \`
|
|
54
|
+
# and recorded. Run your agent once, then \`okgate report\` and \`okgate diff\`.
|
|
52
55
|
# Flip to \`enforce\` when the diff looks right. Caps and the loop breaker apply in both modes.
|
|
53
56
|
mode: ${opts.mode ?? "dry-run"}
|
|
54
57
|
|
|
55
|
-
# The servers your agent used to talk to directly.
|
|
56
|
-
# credentials; your agent only talks to
|
|
58
|
+
# The servers your agent used to talk to directly. okgate connects to them and holds their
|
|
59
|
+
# credentials; your agent only talks to okgate. Use \${ENV_VAR} for secrets.
|
|
57
60
|
${upstreamsYaml}
|
|
58
61
|
|
|
59
62
|
# Detected tools (${tools.length}${tools.length ? `: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ")}` : ""}):
|
|
@@ -100,7 +103,7 @@ dry_run:
|
|
|
100
103
|
synthesize: true
|
|
101
104
|
|
|
102
105
|
# Destructive actions that need a human. The agent gets APPROVAL_REQUIRED with an id; you run
|
|
103
|
-
# \`
|
|
106
|
+
# \`okgate approve <id>\` (or click the link in Slack); the agent retries the identical call once.
|
|
104
107
|
approval:
|
|
105
108
|
tools: ${flow(destructive)}
|
|
106
109
|
wait_s: 0 # > 0 holds the call open this long waiting for the decision
|
|
@@ -108,12 +111,12 @@ approval:
|
|
|
108
111
|
# notify:
|
|
109
112
|
# slack: \${SLACK_WEBHOOK_URL}
|
|
110
113
|
|
|
111
|
-
# Kill switch: \`
|
|
112
|
-
# returns KILLED until \`
|
|
114
|
+
# Kill switch: \`okgate kill\` creates this file (or POST /kill on the HTTP proxy); every call
|
|
115
|
+
# returns KILLED until \`okgate resume\`. OKGATE_KILL=1 (or AGENTGUARD_KILL=1) in the environment does the same.
|
|
113
116
|
kill:
|
|
114
|
-
file: .
|
|
117
|
+
file: .okgate/KILL
|
|
115
118
|
|
|
116
|
-
# Scoped agent keys for the HTTP proxy: \`
|
|
119
|
+
# Scoped agent keys for the HTTP proxy: \`okgate key create deployer --allow 'crm_get_*'\`.
|
|
117
120
|
# Only the key's hash is stored here; the agent sends the key as a Bearer token.
|
|
118
121
|
agents: []
|
|
119
122
|
|
|
@@ -122,9 +125,9 @@ agents: []
|
|
|
122
125
|
# slack: \${SLACK_WEBHOOK_URL}
|
|
123
126
|
# on: [LOOP_DETECTED, CAP_EXCEEDED, KILLED, APPROVAL_REQUIRED]
|
|
124
127
|
|
|
125
|
-
# Hash-chained JSONL of every call; \`
|
|
128
|
+
# Hash-chained JSONL of every call; \`okgate verify\` proves it was not edited.
|
|
126
129
|
audit:
|
|
127
|
-
path: .
|
|
130
|
+
path: .okgate/audit.jsonl
|
|
128
131
|
redact: true
|
|
129
132
|
include_results: false
|
|
130
133
|
`;
|
|
@@ -159,7 +162,9 @@ async function initCommand(args, io) {
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
if (restored === 0)
|
|
162
|
-
io.err(
|
|
165
|
+
io.err(
|
|
166
|
+
"nothing to undo (no *.okgate-backup or *.agentguard-backup files next to your MCP configs)"
|
|
167
|
+
);
|
|
163
168
|
return 0;
|
|
164
169
|
}
|
|
165
170
|
const projectLevel = configs.filter((c) => c.path.startsWith(io.cwd + "/"));
|
|
@@ -171,7 +176,7 @@ async function initCommand(args, io) {
|
|
|
171
176
|
if (!explicit && !flagBool(args, "all")) {
|
|
172
177
|
for (const c of userLevel)
|
|
173
178
|
io.out(
|
|
174
|
-
`found ${c.path} (${c.label}, user-level) \u2014 not touched; pass --client ${JSON.stringify(c.path)} to route it through
|
|
179
|
+
`found ${c.path} (${c.label}, user-level) \u2014 not touched; pass --client ${JSON.stringify(c.path)} to route it through okgate`
|
|
175
180
|
);
|
|
176
181
|
}
|
|
177
182
|
if (!chosen.every((c) => existsSync(c.path))) {
|
|
@@ -195,7 +200,7 @@ async function initCommand(args, io) {
|
|
|
195
200
|
for (const s of skipped) io.err(`skipped ${s.name}: ${s.reason}`);
|
|
196
201
|
if (skipped.some((s) => s.reason.includes("already")) && upstreams.length === 0 && existsSync(policyPath)) {
|
|
197
202
|
io.out(
|
|
198
|
-
`already initialized: ${rel(policyPath)} exists and ${chosen.map((c) => rel(c.path)).join(", ")} already points at
|
|
203
|
+
`already initialized: ${rel(policyPath)} exists and ${chosen.map((c) => rel(c.path)).join(", ")} already points at okgate`
|
|
199
204
|
);
|
|
200
205
|
return 0;
|
|
201
206
|
}
|
|
@@ -213,7 +218,7 @@ async function initCommand(args, io) {
|
|
|
213
218
|
if (u.connected) io.out(` ${u.name}: ${u.tools} tools`);
|
|
214
219
|
else {
|
|
215
220
|
io.err(
|
|
216
|
-
` ${u.name}: could not connect (${u.error}) \u2014 the proxy will keep retrying at start; fix the entry in
|
|
221
|
+
` ${u.name}: could not connect (${u.error}) \u2014 the proxy will keep retrying at start; fix the entry in okgate.yaml`
|
|
217
222
|
);
|
|
218
223
|
probeErrors.push(`${u.name}: ${u.error}`);
|
|
219
224
|
}
|
|
@@ -236,14 +241,14 @@ async function initCommand(args, io) {
|
|
|
236
241
|
for (const c of chosen) {
|
|
237
242
|
if (resolve(c.path) === resolve(policyPath)) {
|
|
238
243
|
throw new Error(
|
|
239
|
-
`refusing: ${rel(c.path)} is both the policy file (--config) and a client config. Rewriting it would replace the policy with the proxy entry and leave the proxy pointing at itself. Use --config for the policy path and --client for the client's config, or let --config default to
|
|
244
|
+
`refusing: ${rel(c.path)} is both the policy file (--config) and a client config. Rewriting it would replace the policy with the proxy entry and leave the proxy pointing at itself. Use --config for the policy path and --client for the client's config, or let --config default to okgate.yaml.`
|
|
240
245
|
);
|
|
241
246
|
}
|
|
242
247
|
const { backup, replaced } = rewriteClientConfig(c, policyPath, {
|
|
243
248
|
agent: flagString(args, "agent")
|
|
244
249
|
});
|
|
245
250
|
io.out(
|
|
246
|
-
`rewrote ${rel(c.path)} \u2192 ${replaced.length ? `${replaced.join(", ")} now go through` : "points at"}
|
|
251
|
+
`rewrote ${rel(c.path)} \u2192 ${replaced.length ? `${replaced.join(", ")} now go through` : "points at"} \`${RUN} proxy\` (backup: ${rel(backup)})`
|
|
247
252
|
);
|
|
248
253
|
}
|
|
249
254
|
}
|
|
@@ -255,11 +260,11 @@ async function initCommand(args, io) {
|
|
|
255
260
|
io.out(
|
|
256
261
|
` 2. run your agent once \u2014 ${mode === "dry-run" ? "writes are faked; nothing executes upstream" : "enforce mode: caps, loop breaker and approvals are live"}`
|
|
257
262
|
);
|
|
258
|
-
io.out(" 3.
|
|
259
|
-
io.out("
|
|
263
|
+
io.out(" 3. okgate report # what this run would have destroyed / spent");
|
|
264
|
+
io.out(" okgate diff # the record-by-record mutation diff");
|
|
260
265
|
if (mode === "dry-run")
|
|
261
266
|
io.out(` 4. set \`mode: enforce\` in ${rel(policyPath)} when it looks right`);
|
|
262
|
-
io.out(" undo any time:
|
|
267
|
+
io.out(" undo any time: okgate init --undo");
|
|
263
268
|
return 0;
|
|
264
269
|
}
|
|
265
270
|
|
|
@@ -267,4 +272,4 @@ export {
|
|
|
267
272
|
renderStarterPolicy,
|
|
268
273
|
initCommand
|
|
269
274
|
};
|
|
270
|
-
//# sourceMappingURL=chunk-
|
|
275
|
+
//# sourceMappingURL=chunk-IEJK7N3B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["/**\n * `okgate init` — the 60-second install. Reads the agent's existing MCP config, probes the\n * servers, writes `okgate.yaml` (dry-run by default) and rewrites the config so everything\n * goes through the proxy. `--undo` restores the backup.\n */\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { relative, resolve } from \"node:path\";\nimport YAML from \"yaml\";\nimport { type Upstream } from \"@agentwares/agentguard-core\";\nimport { flagBool, flagString, type ParsedArgs } from \"../args.js\";\nimport {\n describeConfigPath,\n detectClientConfigs,\n readServers,\n restoreClientConfig,\n rewriteClientConfig,\n serversToUpstreams,\n type ClientConfigFile,\n} from \"../configs.js\";\nimport { homeFrom, policyPathFrom, type Io } from \"../context.js\";\nimport { RUN } from \"../install.js\";\nimport { probeUpstreams, type ProbedTool } from \"./tools.js\";\n\nexport interface StarterPolicyOptions {\n upstreams: Upstream[];\n tools?: ProbedTool[];\n mode?: \"dry-run\" | \"enforce\";\n generatedAt?: Date;\n probeErrors?: string[];\n}\n\nfunction dump(value: unknown, indent = 0): string {\n const text = YAML.stringify(value, { indent: 2, lineWidth: 100 }).trimEnd();\n const pad = \" \".repeat(indent);\n return text\n .split(\"\\n\")\n .map((l) => pad + l)\n .join(\"\\n\");\n}\n\nfunction flow(list: string[]): string {\n return list.length === 0 ? \"[]\" : `[${list.map((s) => JSON.stringify(s)).join(\", \")}]`;\n}\n\n/** The commented starter policy — the file people read, so every knob explains itself. */\nexport function renderStarterPolicy(opts: StarterPolicyOptions): string {\n const tools = opts.tools ?? [];\n const unknown = tools.filter((t) => t.classification.class === \"unknown\").map((t) => t.name);\n const destructive = tools\n .filter((t) => t.classification.destructive || t.classification.verb === \"delete\")\n .map((t) => t.name);\n const spend = tools.filter((t) => t.classification.class === \"spend\").map((t) => t.name);\n const date = (opts.generatedAt ?? new Date()).toISOString().slice(0, 10);\n const counts = tools.reduce<Record<string, number>>(\n (acc, t) => ({ ...acc, [t.classification.class]: (acc[t.classification.class] ?? 0) + 1 }),\n {},\n );\n const detected =\n tools.length === 0\n ? [\"# (no upstreams probed — run `okgate tools` after fixing the upstreams above)\"]\n : tools.map(\n (t) =>\n `# ${t.name.padEnd(36)} ${t.classification.class.padEnd(8)} ${t.classification.verb.padEnd(8)} ${t.classification.source}: ${t.classification.reason}`,\n );\n const probeNotes = (opts.probeErrors ?? []).map((e) => `# ! ${e}`);\n const upstreamsYaml = opts.upstreams.length\n ? dump({ upstreams: opts.upstreams.map(cleanUpstream) })\n : 'upstreams: []\\n # - name: crm\\n # url: https://mcp.example.com/mcp\\n # auth: ${CRM_TOKEN}\\n # - name: files\\n # command: npx\\n # args: [-y, \"@modelcontextprotocol/server-filesystem\", \".\"]';\n\n return `# okgate.yaml — generated by \\`okgate init\\` on ${date}\n# Every knob is explained inline. Docs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli#policy\nversion: 1\n\n# dry-run: reads pass through; writes and spends are FAKED (plausible success, nothing executes)\n# and recorded. Run your agent once, then \\`okgate report\\` and \\`okgate diff\\`.\n# Flip to \\`enforce\\` when the diff looks right. Caps and the loop breaker apply in both modes.\nmode: ${opts.mode ?? \"dry-run\"}\n\n# The servers your agent used to talk to directly. okgate connects to them and holds their\n# credentials; your agent only talks to okgate. Use \\${ENV_VAR} for secrets.\n${upstreamsYaml}\n\n# Detected tools (${tools.length}${\n tools.length\n ? `: ${Object.entries(counts)\n .map(([k, v]) => `${v} ${k}`)\n .join(\", \")}`\n : \"\"\n }):\n${[...detected, ...probeNotes].join(\"\\n\")}\n# readOnlyHint annotations and verb heuristics classify most tools. List anything that is wrong here;\n# these patterns win over annotations. \\`*\\` matches anything.\nclassify:\n read: []\n write: ${flow(unknown)}${unknown.length ? \" # could not be classified — treated as writes; move to read: if they only read\" : \"\"}\n spend: ${flow(spend)}\n unknown: write # a tool nobody can classify counts as a write (alternatives: read | block)\n\n# Blast-radius caps. The call that would exceed a cap gets CAP_EXCEEDED with the remaining budget.\n# \\`writes\\` counts every write or spend; \\`deletes\\`/\\`emails\\` count the subsets matched by \\`counters\\`.\ncaps:\n per_run:\n tool_calls: 400\n writes: 50\n deletes: 10\n emails: 5\n spend_usd: 25\n per_day:\n spend_usd: 200\n\n# Dollar amounts. Declare how to read the amount from a spend tool's arguments; results reporting\n# cost_usd / amount_usd are picked up automatically. Unknown spend tools cost \\`default_usd\\`.\nspend:\n tools: {}\n # stripe_create_charge: { amount_arg: amount, divisor: 100, currency_arg: currency }\n # openai_*: { fixed_usd: 0.01 }\n default_usd: 0\n\n# Semantic loop breaker: the same (tool, normalized arguments) ${3} times in the last 30 calls, or a\n# repeating A→B→A→B cycle, returns LOOP_DETECTED. Timestamps, ids and whitespace are ignored.\nloop:\n window: 30\n max_repeats: 3\n max_cycle_len: 4\n max_read_repeats: 10\n\n# Always fake these, even in enforce mode. Remove entries as you gain trust.\ndry_run:\n tools: []\n synthesize: true\n\n# Destructive actions that need a human. The agent gets APPROVAL_REQUIRED with an id; you run\n# \\`okgate approve <id>\\` (or click the link in Slack); the agent retries the identical call once.\napproval:\n tools: ${flow(destructive)}\n wait_s: 0 # > 0 holds the call open this long waiting for the decision\n ttl_s: 3600\n # notify:\n # slack: \\${SLACK_WEBHOOK_URL}\n\n# Kill switch: \\`okgate kill\\` creates this file (or POST /kill on the HTTP proxy); every call\n# returns KILLED until \\`okgate resume\\`. OKGATE_KILL=1 (or AGENTGUARD_KILL=1) in the environment does the same.\nkill:\n file: .okgate/KILL\n\n# Scoped agent keys for the HTTP proxy: \\`okgate key create deployer --allow 'crm_get_*'\\`.\n# Only the key's hash is stored here; the agent sends the key as a Bearer token.\nagents: []\n\n# Alerts on halts (Slack incoming webhook or any HTTPS endpoint; signed with X-Agentwares-Signature).\n# alerts:\n# slack: \\${SLACK_WEBHOOK_URL}\n# on: [LOOP_DETECTED, CAP_EXCEEDED, KILLED, APPROVAL_REQUIRED]\n\n# Hash-chained JSONL of every call; \\`okgate verify\\` proves it was not edited.\naudit:\n path: .okgate/audit.jsonl\n redact: true\n include_results: false\n`;\n}\n\nfunction cleanUpstream(u: Upstream): Record<string, unknown> {\n const out: Record<string, unknown> = { name: u.name };\n if (u.url) {\n out.url = u.url;\n if (Object.keys(u.headers).length) out.headers = u.headers;\n } else {\n out.command = u.command;\n if (u.args.length) out.args = u.args;\n if (Object.keys(u.env).length) out.env = u.env;\n if (u.cwd) out.cwd = u.cwd;\n }\n return out;\n}\n\nexport async function initCommand(args: ParsedArgs, io: Io): Promise<number> {\n const policyPath = policyPathFrom(args, io);\n const explicit = flagString(args, \"client\");\n const configs: ClientConfigFile[] = explicit\n ? [describeConfigPath(resolve(io.cwd, explicit))]\n : detectClientConfigs(io.cwd, homeFrom(io), io.env);\n const rel = (p: string): string => {\n const r = relative(io.cwd, p);\n return r.startsWith(\"..\") ? p : r || \".\";\n };\n\n if (flagBool(args, \"undo\")) {\n let restored = 0;\n for (const c of configs) {\n if (restoreClientConfig(c)) {\n io.out(`restored ${rel(c.path)} from backup`);\n restored += 1;\n }\n }\n if (restored === 0)\n io.err(\n \"nothing to undo (no *.okgate-backup or *.agentguard-backup files next to your MCP configs)\",\n );\n return 0;\n }\n\n // Only project-level configs are rewritten by default. User-level files (Claude Desktop,\n // ~/.cursor, Windsurf, ~/.claude.json) affect every project, so they need an explicit --client.\n const projectLevel = configs.filter((c) => c.path.startsWith(io.cwd + \"/\"));\n const userLevel = configs.filter((c) => !projectLevel.includes(c));\n let chosen: ClientConfigFile[];\n if (explicit) chosen = configs;\n else if (flagBool(args, \"all\")) chosen = configs;\n else chosen = projectLevel.slice(0, 1);\n if (!explicit && !flagBool(args, \"all\")) {\n for (const c of userLevel)\n io.out(\n `found ${c.path} (${c.label}, user-level) — not touched; pass --client ${JSON.stringify(c.path)} to route it through okgate`,\n );\n }\n if (!chosen.every((c) => existsSync(c.path))) {\n io.err(`no MCP config at ${chosen.map((c) => c.path).join(\", \")}`);\n return 1;\n }\n\n const servers: Record<string, ReturnType<typeof readServers>[string]> = {};\n for (const c of chosen) {\n const found = readServers(c);\n io.out(\n `found ${rel(c.path)} (${c.label}) → ${Object.keys(found).length} server${Object.keys(found).length === 1 ? \"\" : \"s\"}: ${Object.keys(found).join(\", \") || \"none\"}`,\n );\n for (const [name, entry] of Object.entries(found))\n if (!(name in servers)) servers[name] = entry;\n }\n if (chosen.length === 0)\n io.out(\n \"found no MCP config (Claude Code .mcp.json, Cursor, VS Code, Claude Desktop, Windsurf, Gemini CLI) — writing a policy with no upstreams\",\n );\n const { upstreams, skipped } = serversToUpstreams(servers);\n for (const s of skipped) io.err(`skipped ${s.name}: ${s.reason}`);\n if (\n skipped.some((s) => s.reason.includes(\"already\")) &&\n upstreams.length === 0 &&\n existsSync(policyPath)\n ) {\n io.out(\n `already initialized: ${rel(policyPath)} exists and ${chosen.map((c) => rel(c.path)).join(\", \")} already points at okgate`,\n );\n return 0;\n }\n\n let probed: ProbedTool[] = [];\n const probeErrors: string[] = [];\n if (flagBool(args, \"probe\") !== false && upstreams.length > 0) {\n io.out(`probing ${upstreams.length} upstream${upstreams.length === 1 ? \"\" : \"s\"}…`);\n const result = await probeUpstreams(upstreams, {\n log: () => undefined,\n env: io.env,\n timeoutMs: 20_000,\n });\n probed = result.tools;\n for (const u of result.upstreams) {\n if (u.connected) io.out(` ${u.name}: ${u.tools} tools`);\n else {\n io.err(\n ` ${u.name}: could not connect (${u.error}) — the proxy will keep retrying at start; fix the entry in okgate.yaml`,\n );\n probeErrors.push(`${u.name}: ${u.error}`);\n }\n }\n }\n\n const mode = (flagString(args, \"mode\") as \"dry-run\" | \"enforce\" | undefined) ?? \"dry-run\";\n if (existsSync(policyPath) && !flagBool(args, \"force\")) {\n io.out(`kept ${rel(policyPath)} (exists; pass --force to regenerate)`);\n } else {\n writeFileSync(policyPath, renderStarterPolicy({ upstreams, tools: probed, mode, probeErrors }));\n const counts = probed.reduce<Record<string, number>>(\n (acc, t) => ({ ...acc, [t.classification.class]: (acc[t.classification.class] ?? 0) + 1 }),\n {},\n );\n io.out(\n `wrote ${rel(policyPath)} (mode: ${mode}, ${upstreams.length} upstream${upstreams.length === 1 ? \"\" : \"s\"}${\n probed.length\n ? `, ${probed.length} tools: ${Object.entries(counts)\n .map(([k, v]) => `${v} ${k}`)\n .join(\", \")}`\n : \"\"\n })`,\n );\n }\n\n if (flagBool(args, \"rewrite\") !== false) {\n for (const c of chosen) {\n // `--config` names the policy file, and `--client` names the client's. Point both at the\n // same file — easy, because an MCP client's file is also called a config — and the\n // rewrite replaces the policy with the proxy entry and then points the proxy at that.\n // The upstreams survive only in the backup, and the proxy starts with nothing to guard.\n if (resolve(c.path) === resolve(policyPath)) {\n throw new Error(\n `refusing: ${rel(c.path)} is both the policy file (--config) and a client config. ` +\n `Rewriting it would replace the policy with the proxy entry and leave the proxy ` +\n `pointing at itself. Use --config for the policy path and --client for the ` +\n `client's config, or let --config default to okgate.yaml.`,\n );\n }\n const { backup, replaced } = rewriteClientConfig(c, policyPath, {\n agent: flagString(args, \"agent\"),\n });\n io.out(\n `rewrote ${rel(c.path)} → ${replaced.length ? `${replaced.join(\", \")} now go through` : \"points at\"} \\`${RUN} proxy\\` (backup: ${rel(backup)})`,\n );\n }\n }\n\n io.out(\"\");\n io.out(\"Next:\");\n io.out(\n ` 1. restart your MCP client so it re-reads ${chosen.map((c) => rel(c.path)).join(\", \") || \"its config\"}`,\n );\n io.out(\n ` 2. run your agent once — ${mode === \"dry-run\" ? \"writes are faked; nothing executes upstream\" : \"enforce mode: caps, loop breaker and approvals are live\"}`,\n );\n io.out(\" 3. okgate report # what this run would have destroyed / spent\");\n io.out(\" okgate diff # the record-by-record mutation diff\");\n if (mode === \"dry-run\")\n io.out(` 4. set \\`mode: enforce\\` in ${rel(policyPath)} when it looks right`);\n io.out(\" undo any time: okgate init --undo\");\n return 0;\n}\n"],"mappings":"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","names":[]}
|