@agentwares/agentguard 0.1.6 → 0.1.9
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 +89 -0
- package/README.md +223 -16
- package/dist/audit-2YCMHMCP.js +17 -0
- package/dist/audit-2YCMHMCP.js.map +1 -0
- package/dist/chunk-3O6KWT3E.js +345 -0
- package/dist/chunk-3O6KWT3E.js.map +1 -0
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/chunk-6S7LYVOC.js +1033 -0
- package/dist/chunk-6S7LYVOC.js.map +1 -0
- package/dist/chunk-6TUQD666.js +828 -0
- package/dist/chunk-6TUQD666.js.map +1 -0
- package/dist/chunk-BKXXTWIS.js +119 -0
- package/dist/chunk-BKXXTWIS.js.map +1 -0
- package/dist/chunk-D66U3PVB.js +529 -0
- package/dist/chunk-D66U3PVB.js.map +1 -0
- package/dist/chunk-HPYZ3JDX.js +206 -0
- package/dist/chunk-HPYZ3JDX.js.map +1 -0
- package/dist/chunk-KRKZGAJ3.js +769 -0
- package/dist/chunk-KRKZGAJ3.js.map +1 -0
- package/dist/chunk-SUXQTT3X.js +1268 -0
- package/dist/chunk-SUXQTT3X.js.map +1 -0
- package/dist/chunk-TW5KJREH.js +190 -0
- package/dist/chunk-TW5KJREH.js.map +1 -0
- package/dist/chunk-VQWQ5TOM.js +113 -0
- package/dist/chunk-VQWQ5TOM.js.map +1 -0
- package/dist/chunk-X4M7YWQA.js +270 -0
- package/dist/chunk-X4M7YWQA.js.map +1 -0
- package/dist/chunk-ZSLWPKXI.js +1144 -0
- package/dist/chunk-ZSLWPKXI.js.map +1 -0
- package/dist/cli.js +8 -4725
- package/dist/cli.js.map +1 -1
- package/dist/fixtures/crm-server.js +6 -1272
- package/dist/fixtures/crm-server.js.map +1 -1
- package/dist/fixtures/demo-agent.js +4 -115
- package/dist/fixtures/demo-agent.js.map +1 -1
- package/dist/index.d.ts +76 -10
- package/dist/index.js +246 -5087
- package/dist/index.js.map +1 -1
- package/dist/init-CWROGV5I.js +15 -0
- package/dist/init-CWROGV5I.js.map +1 -0
- package/dist/proxy-3Z5YEZ4R.js +154 -0
- package/dist/proxy-3Z5YEZ4R.js.map +1 -0
- package/dist/tools-3LLDG3XQ.js +15 -0
- package/dist/tools-3LLDG3XQ.js.map +1 -0
- package/llms.txt +4 -0
- package/package.json +9 -3
- package/server.json +2 -2
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLIENT_ALIASES,
|
|
3
|
+
hookSettingsFiles,
|
|
4
|
+
hooksCommand
|
|
5
|
+
} from "./chunk-3O6KWT3E.js";
|
|
6
|
+
import {
|
|
7
|
+
PROXY_SERVER_NAME,
|
|
8
|
+
describeConfigPath,
|
|
9
|
+
detectClientConfigs,
|
|
10
|
+
isGeminiConfig,
|
|
11
|
+
mergeServerIntoConfig
|
|
12
|
+
} from "./chunk-TW5KJREH.js";
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_POLICY_FILE,
|
|
15
|
+
approvalUrlFor,
|
|
16
|
+
createFileGuard,
|
|
17
|
+
defaultIo,
|
|
18
|
+
describeError,
|
|
19
|
+
flagBool,
|
|
20
|
+
flagList,
|
|
21
|
+
flagNumber,
|
|
22
|
+
flagString,
|
|
23
|
+
homeFrom,
|
|
24
|
+
loadPolicyFor,
|
|
25
|
+
parseArgs,
|
|
26
|
+
policyPathFrom,
|
|
27
|
+
readRegistration,
|
|
28
|
+
version
|
|
29
|
+
} from "./chunk-SUXQTT3X.js";
|
|
30
|
+
|
|
31
|
+
// src/commands/connect.ts
|
|
32
|
+
import { existsSync } from "fs";
|
|
33
|
+
import { relative } from "path";
|
|
34
|
+
var DEFAULT_PROXY_URL = "https://agentwares-agentguard-proxy.vercel.app";
|
|
35
|
+
function fail(e) {
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
38
|
+
async function connectCommand(args, io, fetchImpl = fetch) {
|
|
39
|
+
const key = args.positionals[0] ?? io.env.AGENTGUARD_KEY;
|
|
40
|
+
if (!key) {
|
|
41
|
+
io.err("usage: agentguard connect <key> [--write] [--client <path>] [--url <proxy-url>]");
|
|
42
|
+
io.err(" the key is on your dashboard; it starts with agk_");
|
|
43
|
+
return 2;
|
|
44
|
+
}
|
|
45
|
+
const base = (flagString(args, "url") ?? io.env.AGENTGUARD_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\/+$/, "");
|
|
46
|
+
let res;
|
|
47
|
+
try {
|
|
48
|
+
res = await fetchImpl(`${base}/connect`, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: { "content-type": "application/json" },
|
|
51
|
+
body: JSON.stringify({ key })
|
|
52
|
+
});
|
|
53
|
+
} catch (err) {
|
|
54
|
+
fail({
|
|
55
|
+
code: "PROXY_UNREACHABLE",
|
|
56
|
+
cause: `could not reach ${base}: ${err instanceof Error ? err.message : String(err)}`,
|
|
57
|
+
fix: "check the network, or pass --url if you run your own proxy",
|
|
58
|
+
retryable: true
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const text = await res.text();
|
|
62
|
+
let body;
|
|
63
|
+
try {
|
|
64
|
+
body = JSON.parse(text);
|
|
65
|
+
} catch {
|
|
66
|
+
fail({
|
|
67
|
+
code: "PROXY_BAD_RESPONSE",
|
|
68
|
+
cause: `${base}/connect returned ${res.status} that was not JSON: ${text.slice(0, 200)}`,
|
|
69
|
+
fix: "check --url points at an agentguard proxy, not at the dashboard",
|
|
70
|
+
retryable: false
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
const e = body;
|
|
75
|
+
fail({
|
|
76
|
+
code: e.code ?? "CONNECT_REFUSED",
|
|
77
|
+
cause: e.cause ?? `${base}/connect returned ${res.status}`,
|
|
78
|
+
fix: e.fix ?? "check the key on your dashboard; revoked keys stop working immediately",
|
|
79
|
+
retryable: e.retryable ?? false
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const conn = body;
|
|
83
|
+
if (flagBool(args, "json")) {
|
|
84
|
+
io.out(JSON.stringify(conn, null, 2));
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
const rel = (p) => p.startsWith(io.cwd + "/") ? relative(io.cwd, p) : p;
|
|
88
|
+
const entry = conn.mcpServers[PROXY_SERVER_NAME] ?? {
|
|
89
|
+
type: "http",
|
|
90
|
+
url: conn.mcpUrl,
|
|
91
|
+
headers: conn.headers
|
|
92
|
+
};
|
|
93
|
+
if (!flagBool(args, "write")) {
|
|
94
|
+
io.out(JSON.stringify({ mcpServers: { [PROXY_SERVER_NAME]: entry } }, null, 2));
|
|
95
|
+
} else {
|
|
96
|
+
const explicit = flagString(args, "client");
|
|
97
|
+
const configs = explicit ? [describeConfigPath(explicit)] : detectClientConfigs(io.cwd, homeFrom(io), io.env);
|
|
98
|
+
const projectLevel = configs.filter((c) => c.path.startsWith(io.cwd + "/"));
|
|
99
|
+
let chosen;
|
|
100
|
+
if (explicit || flagBool(args, "all")) chosen = configs;
|
|
101
|
+
else chosen = projectLevel.slice(0, 1);
|
|
102
|
+
if (chosen.length === 0) {
|
|
103
|
+
io.err(
|
|
104
|
+
"no MCP config found here (.mcp.json, .cursor/mcp.json, .vscode/mcp.json). Create one, or paste this into your client:"
|
|
105
|
+
);
|
|
106
|
+
io.out(JSON.stringify({ mcpServers: { [PROXY_SERVER_NAME]: entry } }, null, 2));
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
for (const c of chosen) {
|
|
110
|
+
if (!existsSync(c.path)) {
|
|
111
|
+
io.err(`no MCP config at ${c.path}`);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
const forClient = isGeminiConfig(c) ? { httpUrl: entry.url, ...entry.headers ? { headers: entry.headers } : {} } : entry;
|
|
115
|
+
const { backup, replaced } = mergeServerIntoConfig(c, PROXY_SERVER_NAME, forClient);
|
|
116
|
+
io.out(
|
|
117
|
+
`${replaced ? "updated" : "wrote "} ${rel(c.path)} (${c.label}) \u2192 ${PROXY_SERVER_NAME} = ${conn.mcpUrl}`
|
|
118
|
+
);
|
|
119
|
+
io.out(`backup ${rel(backup)}`);
|
|
120
|
+
}
|
|
121
|
+
for (const c of configs.filter((c2) => !chosen.includes(c2)))
|
|
122
|
+
io.out(
|
|
123
|
+
`found ${c.path} (${c.label}, user-level) \u2014 not touched; pass --client ${JSON.stringify(c.path)} to write it too`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
const band = conn.band ? ` band=${conn.band.tier} (${conn.band.quantity} calls/mo)` : "";
|
|
127
|
+
io.out(`
|
|
128
|
+
proxy ${conn.slug} (${conn.proxyId}) mode=${conn.mode}${band}`);
|
|
129
|
+
if (conn.upstreams?.length) io.out(`upstreams: ${conn.upstreams.join(", ")}`);
|
|
130
|
+
if (conn.dashboardUrl) io.out(`dashboard: ${conn.dashboardUrl}`);
|
|
131
|
+
if (conn.claudeCode) io.out(`claude code: ${conn.claudeCode}`);
|
|
132
|
+
if (!flagBool(args, "write"))
|
|
133
|
+
io.out(`
|
|
134
|
+
run again with --write to add this to your MCP client config`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/commands/control.ts
|
|
139
|
+
async function pokeHttp(io, reg, path, body) {
|
|
140
|
+
if (!reg) return;
|
|
141
|
+
try {
|
|
142
|
+
const res = await fetch(`${reg.baseUrl}${path}`, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { Authorization: `Bearer ${reg.token}`, "Content-Type": "application/json" },
|
|
145
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
146
|
+
signal: AbortSignal.timeout(2e3)
|
|
147
|
+
});
|
|
148
|
+
if (!res.ok)
|
|
149
|
+
io.err(
|
|
150
|
+
`running proxy at ${reg.baseUrl} answered ${res.status} (the file switch still applies)`
|
|
151
|
+
);
|
|
152
|
+
} catch {
|
|
153
|
+
io.err(
|
|
154
|
+
`no proxy answering at ${reg.baseUrl} (stale ${reg.pid}?) \u2014 the file switch still applies`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function killCommand(args, io) {
|
|
159
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
160
|
+
const { kill } = createFileGuard(loaded, { alerts: false, env: io.env });
|
|
161
|
+
const reason = args.positionals.join(" ") || flagString(args, "reason") || `agentguard kill by ${io.env.USER ?? "operator"}`;
|
|
162
|
+
kill.kill(reason);
|
|
163
|
+
await pokeHttp(io, readRegistration(loaded), "/kill", { reason });
|
|
164
|
+
io.out(
|
|
165
|
+
`KILLED: ${kill.path} written \u2014 every tool call through this policy now returns KILLED. Clear with: agentguard resume`
|
|
166
|
+
);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
async function resumeCommand(args, io) {
|
|
170
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
171
|
+
const { kill } = createFileGuard(loaded, { alerts: false, env: io.env });
|
|
172
|
+
kill.resume();
|
|
173
|
+
await pokeHttp(io, readRegistration(loaded), "/resume");
|
|
174
|
+
io.out(`resumed: ${kill.path} removed`);
|
|
175
|
+
if (io.env[loaded.policy.kill.env])
|
|
176
|
+
io.err(
|
|
177
|
+
`note: ${loaded.policy.kill.env} is set in this shell's environment; proxies started from it stay killed`
|
|
178
|
+
);
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
181
|
+
async function decideCommand(decision, args, io) {
|
|
182
|
+
const id = args.positionals[0];
|
|
183
|
+
if (!id) {
|
|
184
|
+
io.err(
|
|
185
|
+
`usage: agentguard ${decision === "approved" ? "approve" : "deny"} <approval-id> [--by name] [--note text]`
|
|
186
|
+
);
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
190
|
+
const { guard } = createFileGuard(loaded, { alerts: false, env: io.env });
|
|
191
|
+
const record = await guard.decide(
|
|
192
|
+
id,
|
|
193
|
+
decision,
|
|
194
|
+
flagString(args, "by") ?? io.env.USER ?? "cli",
|
|
195
|
+
flagString(args, "note")
|
|
196
|
+
);
|
|
197
|
+
if (!record) {
|
|
198
|
+
io.err(`no approval ${id} \u2014 pending ones: agentguard approvals`);
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
io.out(
|
|
202
|
+
`${record.status}: ${record.tool} (${record.id}, run ${record.run_id})${record.status === "approved" ? " \u2014 the agent may now retry the identical call once" : ""}`
|
|
203
|
+
);
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
async function approvalsCommand(args, io) {
|
|
207
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
208
|
+
const { guard } = createFileGuard(loaded, { alerts: false, env: io.env });
|
|
209
|
+
const list = await guard.approvals.list(flagBool(args, "all") ? void 0 : "pending");
|
|
210
|
+
if (flagBool(args, "json")) {
|
|
211
|
+
io.out(JSON.stringify(list, null, 2));
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
if (list.length === 0) {
|
|
215
|
+
io.out(flagBool(args, "all") ? "no approvals recorded" : "no pending approvals");
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
for (const r of list) {
|
|
219
|
+
io.out(
|
|
220
|
+
`${r.id} ${r.status.padEnd(9)} ${r.tool} run ${r.run_id}${r.agent ? ` agent ${r.agent}` : ""} expires ${r.expires_at}`
|
|
221
|
+
);
|
|
222
|
+
io.out(` args: ${JSON.stringify(r.args).slice(0, 300)}`);
|
|
223
|
+
if (r.status === "pending")
|
|
224
|
+
io.out(` \u2192 agentguard approve ${r.id} | agentguard deny ${r.id}`);
|
|
225
|
+
}
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/commands/hook.ts
|
|
230
|
+
import { existsSync as existsSync3 } from "fs";
|
|
231
|
+
import { tmpdir } from "os";
|
|
232
|
+
import { dirname, join, resolve } from "path";
|
|
233
|
+
import {
|
|
234
|
+
assessHookAction,
|
|
235
|
+
detectHookClient,
|
|
236
|
+
hookActionFromInput
|
|
237
|
+
} from "@agentwares/agentguard-core";
|
|
238
|
+
|
|
239
|
+
// src/hooks/transcript-file.ts
|
|
240
|
+
import { closeSync, existsSync as existsSync2, openSync, readSync, statSync } from "fs";
|
|
241
|
+
import { scanTranscript } from "@agentwares/agentguard-core";
|
|
242
|
+
var STEPS = [1 << 20, 8 << 20, 64 << 20];
|
|
243
|
+
function readTail(path, bytes, size) {
|
|
244
|
+
const length = Math.min(bytes, size);
|
|
245
|
+
const fd = openSync(path, "r");
|
|
246
|
+
try {
|
|
247
|
+
const buf = Buffer.alloc(length);
|
|
248
|
+
readSync(fd, buf, 0, length, size - length);
|
|
249
|
+
return buf.toString("utf8");
|
|
250
|
+
} finally {
|
|
251
|
+
closeSync(fd);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function humanTurnFromTranscript(path, opts = {}) {
|
|
255
|
+
if (!path) return { missing: "the hook input carried no transcript_path" };
|
|
256
|
+
if (!existsSync2(path)) return { missing: `no transcript at ${path}` };
|
|
257
|
+
let size;
|
|
258
|
+
try {
|
|
259
|
+
size = statSync(path).size;
|
|
260
|
+
} catch (err) {
|
|
261
|
+
return {
|
|
262
|
+
missing: `transcript unreadable: ${err instanceof Error ? err.message : String(err)}`
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
let scan;
|
|
266
|
+
for (const step of STEPS) {
|
|
267
|
+
try {
|
|
268
|
+
scan = scanTranscript(readTail(path, step, size), opts);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
return {
|
|
271
|
+
missing: `transcript unreadable: ${err instanceof Error ? err.message : String(err)}`
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (scan.turn || step >= size) break;
|
|
275
|
+
}
|
|
276
|
+
if (scan?.turn) return { turn: scan.turn, scan };
|
|
277
|
+
return {
|
|
278
|
+
missing: size > STEPS[STEPS.length - 1] ? "none in the last 64 MB of the transcript" : void 0,
|
|
279
|
+
scan
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/commands/hook.ts
|
|
284
|
+
import { loadPolicyFile } from "@agentwares/agentguard-core/node";
|
|
285
|
+
var RUN_PREFIX = {
|
|
286
|
+
"claude-code": "cc",
|
|
287
|
+
codex: "codex",
|
|
288
|
+
"gemini-cli": "gemini"
|
|
289
|
+
};
|
|
290
|
+
function findPolicyUpwards(start) {
|
|
291
|
+
let dir = resolve(start);
|
|
292
|
+
for (let i = 0; i < 64; i += 1) {
|
|
293
|
+
const candidate = join(dir, DEFAULT_POLICY_FILE);
|
|
294
|
+
if (existsSync3(candidate)) return candidate;
|
|
295
|
+
const parent = dirname(dir);
|
|
296
|
+
if (parent === dir) return void 0;
|
|
297
|
+
dir = parent;
|
|
298
|
+
}
|
|
299
|
+
return void 0;
|
|
300
|
+
}
|
|
301
|
+
function projectDir(io, inputCwd) {
|
|
302
|
+
return io.env.CLAUDE_PROJECT_DIR || io.env.GEMINI_PROJECT_DIR || inputCwd || void 0;
|
|
303
|
+
}
|
|
304
|
+
function resolveHookPolicy(args, io, inputCwd) {
|
|
305
|
+
const flag = flagString(args, "config");
|
|
306
|
+
if (flag) return { path: resolve(io.cwd, flag), found: "flag" };
|
|
307
|
+
if (io.env.AGENTGUARD_CONFIG)
|
|
308
|
+
return { path: resolve(io.cwd, io.env.AGENTGUARD_CONFIG), found: "env" };
|
|
309
|
+
const start = projectDir(io, inputCwd) ?? io.cwd;
|
|
310
|
+
const near = findPolicyUpwards(start);
|
|
311
|
+
if (near) return { path: near, found: "project" };
|
|
312
|
+
const fallback = flagString(args, "fallback-config");
|
|
313
|
+
if (fallback && existsSync3(resolve(io.cwd, fallback)))
|
|
314
|
+
return { path: resolve(io.cwd, fallback), found: "fallback" };
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
function out(io, value) {
|
|
318
|
+
io.out(JSON.stringify(value));
|
|
319
|
+
}
|
|
320
|
+
function warn(io, message) {
|
|
321
|
+
out(io, { systemMessage: message });
|
|
322
|
+
}
|
|
323
|
+
function respond(io, client, result, summary, approveCommand) {
|
|
324
|
+
if (result.decision === "deny" && result.error) {
|
|
325
|
+
const e = result.error;
|
|
326
|
+
const reason = JSON.stringify(e);
|
|
327
|
+
const id = result.approvalId ? `, approval ${result.approvalId}` : "";
|
|
328
|
+
const approve = e.code === "APPROVAL_REQUIRED" && result.approvalId ? ` To let this exact command run once: ${approveCommand(result.approvalId)}` : "";
|
|
329
|
+
const systemMessage = `agentguard refused "${summary.slice(0, 120)}" (${e.code}${id}): ${e.cause}${approve}`;
|
|
330
|
+
if (client === "gemini-cli") out(io, { decision: "deny", reason, systemMessage });
|
|
331
|
+
else
|
|
332
|
+
out(io, {
|
|
333
|
+
hookSpecificOutput: {
|
|
334
|
+
hookEventName: "PreToolUse",
|
|
335
|
+
permissionDecision: "deny",
|
|
336
|
+
permissionDecisionReason: reason
|
|
337
|
+
},
|
|
338
|
+
systemMessage
|
|
339
|
+
});
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (result.would) {
|
|
343
|
+
out(io, {
|
|
344
|
+
systemMessage: `agentguard (hooks.mode: dry-run) would have stopped "${summary.slice(0, 120)}" with ${result.would.code}: ${result.would.cause} \u2014 it ran; nothing is blocked until hooks.mode is enforce`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async function hookCommand(args, io) {
|
|
349
|
+
const event = args.positionals[0];
|
|
350
|
+
if (event !== "pre-tool-use") {
|
|
351
|
+
io.err(
|
|
352
|
+
"usage: agentguard hook pre-tool-use [--client claude|codex|gemini] [--config agentguard.yaml] [--fallback-config path]\n run by a coding agent's pre-execution hook, with the hook's JSON on stdin; `agentguard hooks install` sets it up"
|
|
353
|
+
);
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
const flagClient = flagString(args, "client");
|
|
357
|
+
let client = flagClient && CLIENT_ALIASES[flagClient] || "claude-code";
|
|
358
|
+
try {
|
|
359
|
+
const raw = await io.stdin?.() ?? "";
|
|
360
|
+
const input = JSON.parse(raw);
|
|
361
|
+
if (!flagClient) client = detectHookClient(input);
|
|
362
|
+
const inputCwd = typeof input.cwd === "string" ? input.cwd : void 0;
|
|
363
|
+
const policyRef = resolveHookPolicy(args, io, inputCwd);
|
|
364
|
+
if (!policyRef) return 0;
|
|
365
|
+
const loaded = loadPolicyFile(policyRef.path, { env: io.env, allowMissingEnv: true });
|
|
366
|
+
const action = hookActionFromInput(client, input, inputCwd ?? io.cwd);
|
|
367
|
+
if (!action) return 0;
|
|
368
|
+
const home = homeFrom(io);
|
|
369
|
+
const workspace = projectDir(io, void 0) ?? (policyRef.found === "project" ? dirname(policyRef.path) : void 0) ?? action.cwd;
|
|
370
|
+
const tmpDirs = [
|
|
371
|
+
tmpdir(),
|
|
372
|
+
io.env.TMPDIR,
|
|
373
|
+
typeof input.scratchpad_dir === "string" ? input.scratchpad_dir : void 0,
|
|
374
|
+
"/tmp",
|
|
375
|
+
"/private/tmp",
|
|
376
|
+
"/var/folders",
|
|
377
|
+
"/private/var/folders"
|
|
378
|
+
].filter((d) => Boolean(d));
|
|
379
|
+
const ctx = {
|
|
380
|
+
workspace: resolve(workspace),
|
|
381
|
+
home,
|
|
382
|
+
tmpDirs,
|
|
383
|
+
stateDir: loaded.stateDir,
|
|
384
|
+
guardFiles: [loaded.path, ...hookSettingsFiles({ cwd: resolve(workspace), home })]
|
|
385
|
+
};
|
|
386
|
+
const assessment = assessHookAction(loaded.policy, action, ctx);
|
|
387
|
+
const reg = readRegistration(loaded);
|
|
388
|
+
const { guard } = createFileGuard(loaded, {
|
|
389
|
+
env: io.env,
|
|
390
|
+
approvalUrl: approvalUrlFor(reg),
|
|
391
|
+
log: (line) => io.err(line)
|
|
392
|
+
});
|
|
393
|
+
const configHint = policyRef.found === "project" && dirname(policyRef.path) === resolve(workspace) ? "" : ` --config ${JSON.stringify(policyRef.path)}`;
|
|
394
|
+
const approveCommand = (id) => `npx @agentwares/agentguard approve ${id}${configHint}`;
|
|
395
|
+
const result = await guard.preflight({
|
|
396
|
+
action,
|
|
397
|
+
assessment,
|
|
398
|
+
runId: `${RUN_PREFIX[client]}_${action.sessionId}`,
|
|
399
|
+
humanTurn: () => client === "claude-code" ? humanTurnFromTranscript(action.transcriptPath, {
|
|
400
|
+
requireOrigin: loaded.policy.hooks.irreversible.require_origin
|
|
401
|
+
}) : {
|
|
402
|
+
missing: `${client} transcripts are not a verified format for this check, so an irreversible command needs \`agentguard approve\``
|
|
403
|
+
},
|
|
404
|
+
approveCommand
|
|
405
|
+
});
|
|
406
|
+
respond(io, client, result, assessment.summary, approveCommand);
|
|
407
|
+
return 0;
|
|
408
|
+
} catch (err) {
|
|
409
|
+
warn(
|
|
410
|
+
io,
|
|
411
|
+
`agentguard hook error \u2014 this call was NOT checked: ${describeError(err).replace(/\s+/g, " ").slice(0, 400)}`
|
|
412
|
+
);
|
|
413
|
+
return 0;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/commands/keys.ts
|
|
418
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
419
|
+
import YAML, { isMap, isSeq, YAMLSeq } from "yaml";
|
|
420
|
+
import { generateAgentKey, hashAgentKey } from "@agentwares/agentguard-core";
|
|
421
|
+
async function keyCommand(args, io) {
|
|
422
|
+
const [sub, name] = args.positionals;
|
|
423
|
+
const path = policyPathFrom(args, io);
|
|
424
|
+
const doc = YAML.parseDocument(readFileSync(path, "utf8"));
|
|
425
|
+
let agents = doc.get("agents");
|
|
426
|
+
if (!isSeq(agents)) {
|
|
427
|
+
agents = new YAMLSeq();
|
|
428
|
+
doc.set("agents", agents);
|
|
429
|
+
}
|
|
430
|
+
const seq = agents;
|
|
431
|
+
const items = seq.items.filter(isMap);
|
|
432
|
+
if (sub === "list" || sub === void 0) {
|
|
433
|
+
if (items.length === 0)
|
|
434
|
+
io.out(
|
|
435
|
+
"no agents in agentguard.yaml \u2014 create one: agentguard key create <name> --allow 'crm_get_*'"
|
|
436
|
+
);
|
|
437
|
+
for (const a of items) {
|
|
438
|
+
const allow2 = a.get("allow");
|
|
439
|
+
io.out(
|
|
440
|
+
`${String(a.get("name")).padEnd(20)} key: ${a.get("key_hash") ? "set" : "none (stdio --agent only)"} allow: ${isSeq(allow2) ? allow2.items.map(String).join(", ") : "all"}`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
if (!name) {
|
|
446
|
+
io.err(
|
|
447
|
+
"usage: agentguard key create <agent> [--allow pattern]... [--deny pattern]... [--writes n] [--spend n] [--deletes n] [--emails n] [--tool-calls n] [--mode dry-run|enforce] [--upstream name]..."
|
|
448
|
+
);
|
|
449
|
+
return 1;
|
|
450
|
+
}
|
|
451
|
+
if (sub === "revoke") {
|
|
452
|
+
const idx = items.findIndex((a) => a.get("name") === name);
|
|
453
|
+
if (idx < 0) {
|
|
454
|
+
io.err(`no agent "${name}"`);
|
|
455
|
+
return 1;
|
|
456
|
+
}
|
|
457
|
+
seq.items.splice(seq.items.indexOf(items[idx]), 1);
|
|
458
|
+
writeFileSync(path, doc.toString());
|
|
459
|
+
io.out(`revoked ${name}: removed from agentguard.yaml (running proxies pick it up on restart)`);
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
if (sub !== "create") {
|
|
463
|
+
io.err(`unknown subcommand "${sub}" \u2014 use create, list or revoke`);
|
|
464
|
+
return 1;
|
|
465
|
+
}
|
|
466
|
+
const key = generateAgentKey();
|
|
467
|
+
const keyHash = await hashAgentKey(key);
|
|
468
|
+
const existing = items.find((a) => a.get("name") === name);
|
|
469
|
+
const entry = { name, key_hash: keyHash };
|
|
470
|
+
const allow = flagList(args, "allow");
|
|
471
|
+
const deny = flagList(args, "deny");
|
|
472
|
+
if (allow.length) entry.allow = allow;
|
|
473
|
+
if (deny.length) entry.deny = deny;
|
|
474
|
+
const upstreams = flagList(args, "upstream");
|
|
475
|
+
if (upstreams.length) entry.upstreams = upstreams;
|
|
476
|
+
const mode = flagString(args, "mode");
|
|
477
|
+
if (mode) entry.mode = mode;
|
|
478
|
+
const perRun = {};
|
|
479
|
+
for (const [flag, counter] of [
|
|
480
|
+
["writes", "writes"],
|
|
481
|
+
["spend", "spend_usd"],
|
|
482
|
+
["deletes", "deletes"],
|
|
483
|
+
["emails", "emails"],
|
|
484
|
+
["tool-calls", "tool_calls"]
|
|
485
|
+
]) {
|
|
486
|
+
const n = flagNumber(args, flag);
|
|
487
|
+
if (n !== void 0) perRun[counter] = n;
|
|
488
|
+
}
|
|
489
|
+
if (Object.keys(perRun).length) entry.caps = { per_run: perRun };
|
|
490
|
+
if (existing) {
|
|
491
|
+
for (const [k, v] of Object.entries(entry)) existing.set(k, v);
|
|
492
|
+
} else {
|
|
493
|
+
seq.add(doc.createNode(entry));
|
|
494
|
+
}
|
|
495
|
+
writeFileSync(path, doc.toString());
|
|
496
|
+
io.err(
|
|
497
|
+
`${existing ? "rotated" : "created"} agent "${name}" in ${path}${allow.length ? ` (allow: ${allow.join(", ")})` : " (allow: all tools)"}`
|
|
498
|
+
);
|
|
499
|
+
io.err(
|
|
500
|
+
"the key is shown once; give it to the agent as `Authorization: Bearer <key>` (HTTP) \u2014 over stdio use `agentguard proxy --agent " + name + "`"
|
|
501
|
+
);
|
|
502
|
+
io.out(key);
|
|
503
|
+
return 0;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/commands/permission-diff.ts
|
|
507
|
+
import { execFileSync } from "child_process";
|
|
508
|
+
import {
|
|
509
|
+
diffPermissionFile,
|
|
510
|
+
renderPermissionDiffMarkdown
|
|
511
|
+
} from "@agentwares/agentguard-core";
|
|
512
|
+
var DEFAULT_PERMISSION_PATHS = [
|
|
513
|
+
"agentguard.yaml",
|
|
514
|
+
"agentguard.yml",
|
|
515
|
+
".claude/settings.json",
|
|
516
|
+
".claude/settings.local.json",
|
|
517
|
+
".mcp.json",
|
|
518
|
+
"mcp.json",
|
|
519
|
+
".cursor/mcp.json",
|
|
520
|
+
".vscode/mcp.json"
|
|
521
|
+
];
|
|
522
|
+
var PERMISSION_FILE = /(^|\/)(agentguard[^/]*\.ya?ml|\.claude\/settings(\.local)?\.json|[^/]*mcp[^/]*\.json|claude_desktop_config\.json)$/;
|
|
523
|
+
function git(cwd, argv) {
|
|
524
|
+
try {
|
|
525
|
+
return execFileSync("git", argv, {
|
|
526
|
+
cwd,
|
|
527
|
+
encoding: "utf8",
|
|
528
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
529
|
+
});
|
|
530
|
+
} catch {
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function showAt(cwd, ref, path) {
|
|
535
|
+
return git(cwd, ["show", `${ref}:${path}`]);
|
|
536
|
+
}
|
|
537
|
+
function changedPermissionFiles(cwd, base, head) {
|
|
538
|
+
const out2 = git(cwd, ["diff", "--name-only", base, head]) ?? "";
|
|
539
|
+
return out2.split("\n").map((l) => l.trim()).filter((l) => l && PERMISSION_FILE.test(l));
|
|
540
|
+
}
|
|
541
|
+
function resolveBase(cwd, explicit) {
|
|
542
|
+
if (explicit) return explicit;
|
|
543
|
+
for (const candidate of ["origin/main", "main", "origin/master", "master"]) {
|
|
544
|
+
if (git(cwd, ["rev-parse", "--verify", "--quiet", candidate]) !== void 0) return candidate;
|
|
545
|
+
}
|
|
546
|
+
return "HEAD~1";
|
|
547
|
+
}
|
|
548
|
+
function runPermissionDiff(cwd, opts) {
|
|
549
|
+
const base = resolveBase(cwd, opts.base);
|
|
550
|
+
const head = opts.head ?? "HEAD";
|
|
551
|
+
const files = [
|
|
552
|
+
.../* @__PURE__ */ new Set([
|
|
553
|
+
...opts.paths ?? DEFAULT_PERMISSION_PATHS,
|
|
554
|
+
...changedPermissionFiles(cwd, base, head)
|
|
555
|
+
])
|
|
556
|
+
];
|
|
557
|
+
const findings = [];
|
|
558
|
+
const touched = [];
|
|
559
|
+
for (const file of files) {
|
|
560
|
+
const before = showAt(cwd, base, file);
|
|
561
|
+
const after = showAt(cwd, head, file);
|
|
562
|
+
if (before === void 0 && after === void 0) continue;
|
|
563
|
+
if (before === after) continue;
|
|
564
|
+
touched.push(file);
|
|
565
|
+
findings.push(...diffPermissionFile(file, before, after));
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
base,
|
|
569
|
+
head,
|
|
570
|
+
files: touched,
|
|
571
|
+
findings,
|
|
572
|
+
markdown: renderPermissionDiffMarkdown(findings, { title: opts.title })
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
async function permissionDiffCommand(args, io) {
|
|
576
|
+
const paths = flagList(args, "paths");
|
|
577
|
+
const result = runPermissionDiff(io.cwd, {
|
|
578
|
+
base: flagString(args, "base"),
|
|
579
|
+
head: flagString(args, "head"),
|
|
580
|
+
paths: paths.length ? paths : void 0
|
|
581
|
+
});
|
|
582
|
+
const format = flagString(args, "format") ?? (flagBool(args, "json") ? "json" : "markdown");
|
|
583
|
+
if (format === "json") io.out(JSON.stringify(result, null, 2));
|
|
584
|
+
else io.out(result.markdown);
|
|
585
|
+
const widen = result.findings.filter((f) => f.severity === "widen").length;
|
|
586
|
+
if (flagBool(args, "fail-on-widen") && widen > 0) return 1;
|
|
587
|
+
return 0;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/commands/report.ts
|
|
591
|
+
import { resolve as resolve2 } from "path";
|
|
592
|
+
import {
|
|
593
|
+
buildReport,
|
|
594
|
+
latestRunId,
|
|
595
|
+
listRuns,
|
|
596
|
+
renderMutationDiff,
|
|
597
|
+
renderReportMarkdown
|
|
598
|
+
} from "@agentwares/agentguard-core";
|
|
599
|
+
import { readAuditFile, verifyAuditFile } from "@agentwares/agentguard-core/node";
|
|
600
|
+
function pickRun(args, entries) {
|
|
601
|
+
if (flagBool(args, "all")) return void 0;
|
|
602
|
+
return flagString(args, "run") ?? latestRunId(entries);
|
|
603
|
+
}
|
|
604
|
+
async function reportCommand(args, io) {
|
|
605
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
606
|
+
const entries = readAuditFile(loaded.auditPath);
|
|
607
|
+
const runId = pickRun(args, entries);
|
|
608
|
+
const report = buildReport(entries, { runId });
|
|
609
|
+
if (flagBool(args, "json")) {
|
|
610
|
+
io.out(
|
|
611
|
+
JSON.stringify(
|
|
612
|
+
{
|
|
613
|
+
...report,
|
|
614
|
+
mutations: report.mutations.map((m) => ({
|
|
615
|
+
seq: m.seq,
|
|
616
|
+
ts: m.ts,
|
|
617
|
+
tool: m.tool,
|
|
618
|
+
mutation: m.mutation
|
|
619
|
+
}))
|
|
620
|
+
},
|
|
621
|
+
null,
|
|
622
|
+
2
|
|
623
|
+
)
|
|
624
|
+
);
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
627
|
+
io.out(renderReportMarkdown(report));
|
|
628
|
+
const chain = await verifyAuditFile(loaded.auditPath);
|
|
629
|
+
io.out(
|
|
630
|
+
chain.ok ? `Audit chain: ${chain.entries} entries, verified (${loaded.auditPath}).` : `Audit chain BROKEN at entry ${chain.brokenAt}: ${chain.reason}`
|
|
631
|
+
);
|
|
632
|
+
if (runId && listRuns(entries).length > 1) io.out(`Other runs: agentguard report --all`);
|
|
633
|
+
return 0;
|
|
634
|
+
}
|
|
635
|
+
async function diffCommand(args, io) {
|
|
636
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
637
|
+
const entries = readAuditFile(loaded.auditPath);
|
|
638
|
+
const runId = pickRun(args, entries);
|
|
639
|
+
io.out(renderMutationDiff(entries, { runId }).trimEnd());
|
|
640
|
+
return 0;
|
|
641
|
+
}
|
|
642
|
+
async function verifyCommand(args, io) {
|
|
643
|
+
let path = args.positionals[0];
|
|
644
|
+
if (!path) path = loadPolicyFor(args, io, { allowMissingEnv: true }).auditPath;
|
|
645
|
+
path = resolve2(io.cwd, path);
|
|
646
|
+
const result = await verifyAuditFile(path);
|
|
647
|
+
if (flagBool(args, "json")) io.out(JSON.stringify({ path, ...result }));
|
|
648
|
+
else if (result.ok)
|
|
649
|
+
io.out(
|
|
650
|
+
`ok: ${result.entries} entries, chain intact, head ${result.head?.slice(0, 16)}\u2026 (${path})`
|
|
651
|
+
);
|
|
652
|
+
else io.out(`BROKEN at entry ${result.brokenAt}: ${result.reason} (${path})`);
|
|
653
|
+
return result.ok ? 0 : 1;
|
|
654
|
+
}
|
|
655
|
+
async function statusCommand(args, io) {
|
|
656
|
+
const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
|
|
657
|
+
const { guard, kill } = createFileGuard(loaded, { alerts: false, env: io.env });
|
|
658
|
+
const entries = readAuditFile(loaded.auditPath);
|
|
659
|
+
const runId = flagString(args, "run") ?? latestRunId(entries) ?? "(none yet)";
|
|
660
|
+
const status = await guard.status(runId);
|
|
661
|
+
const killed = kill.check();
|
|
662
|
+
const reg = readRegistration(loaded);
|
|
663
|
+
const pending = await guard.approvals.list("pending");
|
|
664
|
+
if (flagBool(args, "json")) {
|
|
665
|
+
io.out(
|
|
666
|
+
JSON.stringify(
|
|
667
|
+
{
|
|
668
|
+
policy: loaded.path,
|
|
669
|
+
mode: loaded.policy.mode,
|
|
670
|
+
runId,
|
|
671
|
+
...status,
|
|
672
|
+
killed,
|
|
673
|
+
pendingApprovals: pending.length,
|
|
674
|
+
http: reg ? { url: reg.url, pid: reg.pid } : void 0
|
|
675
|
+
},
|
|
676
|
+
null,
|
|
677
|
+
2
|
|
678
|
+
)
|
|
679
|
+
);
|
|
680
|
+
return 0;
|
|
681
|
+
}
|
|
682
|
+
io.out(
|
|
683
|
+
`policy ${loaded.path} (mode: ${loaded.policy.mode}, ${loaded.policy.upstreams.length} upstreams)`
|
|
684
|
+
);
|
|
685
|
+
io.out(
|
|
686
|
+
`kill ${killed.killed ? `ON \u2014 ${killed.reason} (agentguard resume to clear)` : "off"}`
|
|
687
|
+
);
|
|
688
|
+
io.out(
|
|
689
|
+
`http ${reg ? `${reg.url} (pid ${reg.pid})` : "not running (agentguard proxy --http)"}`
|
|
690
|
+
);
|
|
691
|
+
io.out(`audit ${entries.length} entries in ${loaded.auditPath}`);
|
|
692
|
+
io.out(
|
|
693
|
+
`pending ${pending.length} approval${pending.length === 1 ? "" : "s"}${pending.length ? ` \u2014 agentguard approvals` : ""}`
|
|
694
|
+
);
|
|
695
|
+
io.out(`run ${runId}`);
|
|
696
|
+
const fmt = (scope) => Object.entries(status.caps[scope]).map(([k, limit]) => `${k} ${status.usage[scope][k] ?? 0}/${limit}`).join(" ") || "no caps";
|
|
697
|
+
io.out(` per_run ${fmt("per_run")}`);
|
|
698
|
+
io.out(` per_day ${fmt("per_day")}`);
|
|
699
|
+
return 0;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/cli.ts
|
|
703
|
+
var proxyCommand = async (args, io) => (await import("./proxy-3Z5YEZ4R.js")).proxyCommand(args, io);
|
|
704
|
+
var initCommand = async (args, io) => (await import("./init-CWROGV5I.js")).initCommand(args, io);
|
|
705
|
+
var toolsCommand = async (args, io) => (await import("./tools-3LLDG3XQ.js")).toolsCommand(args, io);
|
|
706
|
+
var auditCommand = async (args, io) => (await import("./audit-2YCMHMCP.js")).auditCommand(args, io);
|
|
707
|
+
var HELP = `agentguard \u2014 MCP policy proxy for agents that touch production
|
|
708
|
+
|
|
709
|
+
npx @agentwares/agentguard init read your MCP config, write agentguard.yaml (dry-run), route every server through the proxy
|
|
710
|
+
agentguard report [--run id|--all] what this run did / would have destroyed / spent, where it was halted
|
|
711
|
+
agentguard diff [--run id] record-by-record mutation diff from a dry run
|
|
712
|
+
agentguard verify [audit.jsonl] prove the hash-chained audit log was not edited
|
|
713
|
+
agentguard connect <key> [--write] point this machine at your hosted proxy (paid tiers)
|
|
714
|
+
|
|
715
|
+
agentguard hooks install [--project|--user] [--client claude|codex|gemini|all]
|
|
716
|
+
check a coding agent's own shell commands and file edits against agentguard.yaml (dry-run first);
|
|
717
|
+
irreversible commands (push, merge, publish, send, rm outside the repo) only on a turn a person typed
|
|
718
|
+
agentguard hooks uninstall | status
|
|
719
|
+
agentguard hook pre-tool-use what the installed hook runs (reads the harness's JSON on stdin)
|
|
720
|
+
|
|
721
|
+
agentguard audit [--since 7d|30d|all] [--client claude|codex|all] [--json]
|
|
722
|
+
which rules in CLAUDE.md / AGENTS.md / GEMINI.md / .cursor/rules your coding agents broke,
|
|
723
|
+
from this machine's Claude Code and Codex sessions in this repo (nothing is sent anywhere)
|
|
724
|
+
agentguard audit --enforce [--preview]
|
|
725
|
+
write the checkable rules into agentguard.yaml as hook rules, in dry-run, and install the hook
|
|
726
|
+
|
|
727
|
+
agentguard proxy [--http --port 8788] [--agent name] [--run-id id] [--mode dry-run|enforce]
|
|
728
|
+
agentguard tools [--json] the tools your agent will see, with class and why
|
|
729
|
+
agentguard status [--run id] counters vs caps, kill state, pending approvals
|
|
730
|
+
|
|
731
|
+
agentguard kill [reason] halt every run now (file + running HTTP proxy); agentguard resume
|
|
732
|
+
agentguard approvals [--all] pending approvals; agentguard approve <id> | deny <id> [--note text]
|
|
733
|
+
agentguard key create <agent> [--allow 'crm_get_*']... [--deny p] [--writes n] [--spend n] [--mode m]
|
|
734
|
+
agentguard key list | revoke <agent>
|
|
735
|
+
agentguard permission-diff [--base ref] [--head ref] [--paths a,b] [--format markdown|json] [--fail-on-widen]
|
|
736
|
+
|
|
737
|
+
Options: --config <agentguard.yaml> (or AGENTGUARD_CONFIG), --json, --help, --version
|
|
738
|
+
Docs: https://github.com/agentwares/agentguard/tree/main/apps/agentguard-cli`;
|
|
739
|
+
async function main(argv, io = defaultIo()) {
|
|
740
|
+
const args = parseArgs(argv);
|
|
741
|
+
if (args.flags.version) {
|
|
742
|
+
io.out(version());
|
|
743
|
+
return 0;
|
|
744
|
+
}
|
|
745
|
+
if (args.command === void 0 && !args.flags.help && io.tty === false) {
|
|
746
|
+
try {
|
|
747
|
+
return await proxyCommand(args, io);
|
|
748
|
+
} catch (err) {
|
|
749
|
+
io.err(describeError(err));
|
|
750
|
+
return 1;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (args.flags.help || args.command === void 0 || args.command === "help") {
|
|
754
|
+
io.out(HELP);
|
|
755
|
+
return args.command === void 0 && !args.flags.help ? 1 : 0;
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
switch (args.command) {
|
|
759
|
+
case "init":
|
|
760
|
+
return await initCommand(args, io);
|
|
761
|
+
case "proxy":
|
|
762
|
+
case "serve":
|
|
763
|
+
return await proxyCommand(args, io);
|
|
764
|
+
case "report":
|
|
765
|
+
return await reportCommand(args, io);
|
|
766
|
+
case "diff":
|
|
767
|
+
return await diffCommand(args, io);
|
|
768
|
+
case "verify":
|
|
769
|
+
return await verifyCommand(args, io);
|
|
770
|
+
case "connect":
|
|
771
|
+
return await connectCommand(args, io);
|
|
772
|
+
case "status":
|
|
773
|
+
return await statusCommand(args, io);
|
|
774
|
+
case "tools":
|
|
775
|
+
return await toolsCommand(args, io);
|
|
776
|
+
case "kill":
|
|
777
|
+
return await killCommand(args, io);
|
|
778
|
+
case "resume":
|
|
779
|
+
return await resumeCommand(args, io);
|
|
780
|
+
case "approve":
|
|
781
|
+
return await decideCommand("approved", args, io);
|
|
782
|
+
case "deny":
|
|
783
|
+
return await decideCommand("denied", args, io);
|
|
784
|
+
case "approvals":
|
|
785
|
+
return await approvalsCommand(args, io);
|
|
786
|
+
case "key":
|
|
787
|
+
case "keys":
|
|
788
|
+
return await keyCommand(args, io);
|
|
789
|
+
case "permission-diff":
|
|
790
|
+
return await permissionDiffCommand(args, io);
|
|
791
|
+
case "hook":
|
|
792
|
+
return await hookCommand(args, io);
|
|
793
|
+
case "hooks":
|
|
794
|
+
return await hooksCommand(args, io);
|
|
795
|
+
case "audit":
|
|
796
|
+
return await auditCommand(args, io);
|
|
797
|
+
default:
|
|
798
|
+
io.err(`unknown command "${args.command}"
|
|
799
|
+
`);
|
|
800
|
+
io.err(HELP);
|
|
801
|
+
return 1;
|
|
802
|
+
}
|
|
803
|
+
} catch (err) {
|
|
804
|
+
io.err(describeError(err));
|
|
805
|
+
return 1;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
var isMain = process.argv[1] !== void 0 && /(^|\/)(cli\.[cm]?[jt]s|agentguard)$/.test(process.argv[1]);
|
|
809
|
+
if (isMain) {
|
|
810
|
+
main(process.argv.slice(2)).then(
|
|
811
|
+
(code) => {
|
|
812
|
+
if (code !== 0) process.exitCode = code;
|
|
813
|
+
},
|
|
814
|
+
(err) => {
|
|
815
|
+
process.stderr.write(`${describeError(err)}
|
|
816
|
+
`);
|
|
817
|
+
process.exitCode = 1;
|
|
818
|
+
}
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
export {
|
|
823
|
+
DEFAULT_PERMISSION_PATHS,
|
|
824
|
+
runPermissionDiff,
|
|
825
|
+
HELP,
|
|
826
|
+
main
|
|
827
|
+
};
|
|
828
|
+
//# sourceMappingURL=chunk-6TUQD666.js.map
|