@garuda-agent/garuda 0.17.0 → 0.18.1
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 +243 -0
- package/README.md +3 -2
- package/SECURITY.md +1 -1
- package/dist/{acpCommand-7EY3NHPG.js → acpCommand-JAYNMIMP.js} +19 -16
- package/dist/advise-HDKJGNBL.js +195 -0
- package/dist/brief-RWKESOPT.js +91 -0
- package/dist/buildCheck-HB46NWLX.js +19 -0
- package/dist/buildInfo-K73FFI5S.js +8 -0
- package/dist/chunk-2OYTNNUJ.js +216 -0
- package/dist/{chunk-Z66BWTMD.js → chunk-34WBDQBL.js} +135 -78
- package/dist/chunk-4QRTTRVK.js +88 -0
- package/dist/{chunk-2DYMJMRQ.js → chunk-5UDYQ5MK.js} +405 -70
- package/dist/{chunk-J4346IDH.js → chunk-7KBOIPUT.js} +2 -1
- package/dist/chunk-CWVXOSAA.js +894 -0
- package/dist/{chunk-FARS4R67.js → chunk-DQSCCE77.js} +18 -1
- package/dist/chunk-DUYLVGPY.js +821 -0
- package/dist/chunk-GKU62MHQ.js +5258 -0
- package/dist/chunk-H7LIBGZT.js +9 -0
- package/dist/chunk-IKPGHM54.js +334 -0
- package/dist/chunk-IYGJWX65.js +9 -0
- package/dist/{chunk-U65DQZDB.js → chunk-JMH5W5DG.js} +2 -45
- package/dist/chunk-L55VUSX3.js +54 -0
- package/dist/{chunk-B4SJY7KT.js → chunk-LMKGSYJC.js} +77 -8
- package/dist/{chunk-23FXBNBB.js → chunk-MNE7HHX2.js} +2 -1
- package/dist/chunk-NH72AELP.js +48 -0
- package/dist/{chunk-735BKABA.js → chunk-NLS7GMF7.js} +12 -106
- package/dist/{chunk-ZAE25JM2.js → chunk-QANRZW7W.js} +8 -1
- package/dist/{chunk-JRVWH27M.js → chunk-QLBCM2YQ.js} +7 -3
- package/dist/{chunk-6XHON3Q6.js → chunk-QXTHEFKP.js} +3 -3
- package/dist/{chunk-TJIWD7R7.js → chunk-R7RCW4BQ.js} +10 -8
- package/dist/chunk-TLA2PDGM.js +156 -0
- package/dist/{chunk-5B5HDTUR.js → chunk-TWQ7V2L6.js} +2 -2
- package/dist/chunk-ULAJPMY7.js +107 -0
- package/dist/chunk-VISKX36R.js +306 -0
- package/dist/{chunk-55CFEEIN.js → chunk-WK5NE6RY.js} +2 -2
- package/dist/chunk-Y62J7PTD.js +90 -0
- package/dist/chunk-YAQ6QLKB.js +40 -0
- package/dist/{chunk-7TH3NHCQ.js → chunk-ZKT4P2PC.js} +2 -2
- package/dist/cli/index.js +289 -65
- package/dist/config-WV2H64TF.js +23 -0
- package/dist/{create-W6BV75QX.js → create-OACSGJ37.js} +5 -5
- package/dist/evalCommand-BLJJ4G76.js +696 -0
- package/dist/evalTune-W53KAD4V.js +302 -0
- package/dist/{export-SJ3DUP6W.js → export-E677HIQS.js} +27 -4
- package/dist/exportHtml-BMV73YRO.js +21 -0
- package/dist/{inkChat-FNUBK43U.js → inkChat-EVLYDC3K.js} +263 -47
- package/dist/{install-TKWN5AYH.js → install-4ST5BNRG.js} +3 -3
- package/dist/{jobCommand-FE6XN3YD.js → jobCommand-ILOTYOX3.js} +9 -8
- package/dist/{launchd-3A3ONEW5.js → launchd-QG6R5RAK.js} +4 -4
- package/dist/{lspCommand-ZZ6R4PA2.js → lspCommand-X4FTTONR.js} +3 -3
- package/dist/{manager-73MPYFTP.js → manager-L25IUXHW.js} +11 -11
- package/dist/{nightCommand-OODJNWDT.js → nightCommand-XPWT6CQZ.js} +9 -8
- package/dist/receiptView-YFDHV253.js +19 -0
- package/dist/{remove-42WQNVPX.js → remove-STX5UO7X.js} +3 -3
- package/dist/{run-ML6GSPN4.js → run-W4TK52WT.js} +6 -6
- package/dist/{server-VEYIJHEU.js → server-ASM73QVY.js} +3 -3
- package/dist/{setupCommand-GBQIVYJC.js → setupCommand-OSOMFHDQ.js} +10 -9
- package/dist/tour-CEVMAC5S.js +18 -0
- package/dist/tune-VX2WQTHS.js +52 -0
- package/dist/uiState-YFJQ43D7.js +16 -0
- package/package.json +1 -1
- package/dist/chunk-4JSUUDFQ.js +0 -118
- package/dist/chunk-BSK5T2ZG.js +0 -9
- package/dist/evalCommand-YVQAWUZE.js +0 -2833
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/tools/atomicWrite.ts
|
|
4
|
+
import { randomBytes } from "crypto";
|
|
5
|
+
import { chmod, link, realpath, rename, stat, unlink, writeFile } from "fs/promises";
|
|
6
|
+
import { basename, dirname, join } from "path";
|
|
7
|
+
async function writeFileAtomic(path, content, { createOnly }) {
|
|
8
|
+
const target = createOnly ? path : await realpath(path);
|
|
9
|
+
const temp = join(
|
|
10
|
+
dirname(target),
|
|
11
|
+
`.${basename(target)}.garuda-${randomBytes(4).toString("hex")}.tmp`
|
|
12
|
+
);
|
|
13
|
+
const mode = createOnly ? void 0 : (await stat(target)).mode & 4095;
|
|
14
|
+
await writeFile(temp, content, { flag: "wx", ...mode === void 0 ? {} : { mode } });
|
|
15
|
+
try {
|
|
16
|
+
if (createOnly) {
|
|
17
|
+
try {
|
|
18
|
+
await link(temp, target);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
const code = error.code;
|
|
21
|
+
if (code !== "EPERM" && code !== "ENOTSUP" && code !== "EXDEV") throw error;
|
|
22
|
+
if (await exists(target)) throw Object.assign(new Error("EEXIST"), { code: "EEXIST" });
|
|
23
|
+
await rename(temp, target);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
await chmod(temp, mode ?? 420);
|
|
28
|
+
await rename(temp, target);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
} finally {
|
|
32
|
+
await unlink(temp).catch(() => {
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function exists(path) {
|
|
37
|
+
try {
|
|
38
|
+
await stat(path);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
writeFileAtomic
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=chunk-NH72AELP.js.map
|
|
@@ -3,18 +3,12 @@ import {
|
|
|
3
3
|
Redactor,
|
|
4
4
|
contextSize,
|
|
5
5
|
totalTokens
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
cleanLine
|
|
9
|
-
} from "./chunk-PZS3V6P3.js";
|
|
6
|
+
} from "./chunk-7KBOIPUT.js";
|
|
10
7
|
import {
|
|
11
8
|
ZERO_USAGE,
|
|
12
9
|
addUsage,
|
|
13
10
|
reportedUsage
|
|
14
11
|
} from "./chunk-BHZ6G65V.js";
|
|
15
|
-
import {
|
|
16
|
-
formatRule
|
|
17
|
-
} from "./chunk-3NJNVZTK.js";
|
|
18
12
|
|
|
19
13
|
// src/session/store.ts
|
|
20
14
|
import { randomBytes } from "crypto";
|
|
@@ -227,7 +221,15 @@ function addSnapshot(session, tree, prompt, durationMs) {
|
|
|
227
221
|
const messages = session.messages.length;
|
|
228
222
|
pushPoint(session.undo, tree, messages, prompt);
|
|
229
223
|
const point = session.undo.points.at(-1);
|
|
230
|
-
|
|
224
|
+
point.at = Date.now();
|
|
225
|
+
session.journal?.write({
|
|
226
|
+
type: "snapshot",
|
|
227
|
+
tree,
|
|
228
|
+
messages,
|
|
229
|
+
prompt: point.prompt,
|
|
230
|
+
durationMs,
|
|
231
|
+
at: point.at
|
|
232
|
+
});
|
|
231
233
|
}
|
|
232
234
|
function undoTurn(session, after) {
|
|
233
235
|
const point = popPoint(session.undo, session.messages, after);
|
|
@@ -343,101 +345,6 @@ function addSideUsage(session, purpose, response, costUsd) {
|
|
|
343
345
|
});
|
|
344
346
|
}
|
|
345
347
|
|
|
346
|
-
// src/permissions/projectSettings.ts
|
|
347
|
-
import { createHash as createHash2 } from "crypto";
|
|
348
|
-
function settingsRisk(settings) {
|
|
349
|
-
const risk = {
|
|
350
|
-
executorHost: settings.executor === "host",
|
|
351
|
-
allow: settings.allow.map(formatRule),
|
|
352
|
-
writePaths: [...settings.sandbox?.writePaths ?? []],
|
|
353
|
-
envAllow: [...settings.envAllow],
|
|
354
|
-
allowLocalhost: settings.web?.allowLocalhost === true
|
|
355
|
-
};
|
|
356
|
-
const formatterCommands = Object.entries(settings.formatters?.commands ?? {}).flatMap(
|
|
357
|
-
([name, f]) => f === false ? [] : [`${name}: ${f.command.join(" ")}`]
|
|
358
|
-
);
|
|
359
|
-
if (formatterCommands.length > 0) risk.formatterCommands = formatterCommands;
|
|
360
|
-
const none = !risk.executorHost && risk.allow.length === 0 && risk.writePaths.length === 0 && risk.envAllow.length === 0 && !risk.allowLocalhost && risk.formatterCommands === void 0;
|
|
361
|
-
return none ? void 0 : risk;
|
|
362
|
-
}
|
|
363
|
-
function settingsRiskHash(risk) {
|
|
364
|
-
return createHash2("sha256").update(JSON.stringify(risk)).digest("hex");
|
|
365
|
-
}
|
|
366
|
-
function settingsRiskLines(risk) {
|
|
367
|
-
const lines = [];
|
|
368
|
-
if (risk.executorHost) lines.push('executor: "host" (commands run with no OS sandbox)');
|
|
369
|
-
if (risk.allow.length > 0) {
|
|
370
|
-
lines.push(`permissions.allow (no question for these): ${risk.allow.join(", ")}`);
|
|
371
|
-
}
|
|
372
|
-
if (risk.writePaths.length > 0) {
|
|
373
|
-
lines.push(`sandbox.writePaths (commands may write there): ${risk.writePaths.join(", ")}`);
|
|
374
|
-
}
|
|
375
|
-
if (risk.envAllow.length > 0) {
|
|
376
|
-
lines.push(`env.allow (commands see these variables): ${risk.envAllow.join(", ")}`);
|
|
377
|
-
}
|
|
378
|
-
if (risk.allowLocalhost) lines.push("web.allowLocalhost (web_fetch may reach this machine)");
|
|
379
|
-
if (risk.formatterCommands !== void 0) {
|
|
380
|
-
lines.push(
|
|
381
|
-
`formatters.commands (run after each edit): ${risk.formatterCommands.map(cleanLine).join("; ")}`
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
return lines;
|
|
385
|
-
}
|
|
386
|
-
function withoutRisk(settings) {
|
|
387
|
-
const { sandbox, web, formatters, ...rest } = settings;
|
|
388
|
-
const { writePaths: _writePaths, ...sandboxRest } = sandbox ?? {};
|
|
389
|
-
const keptCommands = Object.fromEntries(
|
|
390
|
-
Object.entries(formatters?.commands ?? {}).filter(([, f]) => f === false)
|
|
391
|
-
);
|
|
392
|
-
return {
|
|
393
|
-
...rest,
|
|
394
|
-
...formatters === void 0 ? {} : { formatters: { ...formatters, commands: keptCommands } },
|
|
395
|
-
executor: settings.executor === "host" ? "auto" : settings.executor,
|
|
396
|
-
allow: [],
|
|
397
|
-
envAllow: [],
|
|
398
|
-
...sandbox === void 0 ? {} : { sandbox: sandboxRest },
|
|
399
|
-
...web === void 0 ? {} : { web: { ...web, allowLocalhost: false } }
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
async function gateProjectSettings(options) {
|
|
403
|
-
const risk = settingsRisk(options.settings);
|
|
404
|
-
if (risk === void 0) return { settings: options.settings };
|
|
405
|
-
const hash2 = settingsRiskHash(risk);
|
|
406
|
-
const known = options.trust?.settingsHash(options.root);
|
|
407
|
-
if (known === hash2) return { settings: options.settings };
|
|
408
|
-
const lines = settingsRiskLines(risk);
|
|
409
|
-
if (options.ask !== void 0) {
|
|
410
|
-
const choice = await options.ask({
|
|
411
|
-
tool: "settings",
|
|
412
|
-
target: { kind: "input", json: "" },
|
|
413
|
-
title: "Project settings that loosen safety",
|
|
414
|
-
preview: [
|
|
415
|
-
`This project's .garuda/settings.json ${known === void 0 ? "asks for" : "changed and now asks for"}:`,
|
|
416
|
-
...lines.map((line) => ` - ${line}`),
|
|
417
|
-
"A cloned repository can ship this file. Without your yes, Garuda uses safe defaults for these",
|
|
418
|
-
"parts; the rest of the file applies."
|
|
419
|
-
].join("\n"),
|
|
420
|
-
isolation: options.isolation ?? "os",
|
|
421
|
-
question: "Use these project settings?",
|
|
422
|
-
choices: ["once", "session", "deny"],
|
|
423
|
-
labels: {
|
|
424
|
-
once: "Yes, for this run",
|
|
425
|
-
session: "Yes, and remember (asks again when they change)",
|
|
426
|
-
deny: "No, use safe defaults"
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
if (choice === "session") await options.trust?.setSettingsHash(options.root, hash2);
|
|
430
|
-
if (choice !== "deny") return { settings: options.settings };
|
|
431
|
-
}
|
|
432
|
-
return {
|
|
433
|
-
settings: withoutRisk(options.settings),
|
|
434
|
-
notice: [
|
|
435
|
-
`Project settings not applied (not approved): ${lines.join("; ")}.`,
|
|
436
|
-
"Start the chat in a terminal and answer yes to use them."
|
|
437
|
-
].join(" ")
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
|
|
441
348
|
export {
|
|
442
349
|
FileTracker,
|
|
443
350
|
FileSessionStore,
|
|
@@ -466,7 +373,6 @@ export {
|
|
|
466
373
|
addChildDebit,
|
|
467
374
|
addChildReport,
|
|
468
375
|
settleChildDebits,
|
|
469
|
-
addSideUsage
|
|
470
|
-
gateProjectSettings
|
|
376
|
+
addSideUsage
|
|
471
377
|
};
|
|
472
|
-
//# sourceMappingURL=chunk-
|
|
378
|
+
//# sourceMappingURL=chunk-NLS7GMF7.js.map
|
|
@@ -57,6 +57,12 @@ async function loadMcpConfig({ home, root } = { home: homedir(), root: process.c
|
|
|
57
57
|
problems.push(`${file}: invalid JSON: ${error.message}`);
|
|
58
58
|
return [];
|
|
59
59
|
}
|
|
60
|
+
if (Array.isArray(json?.servers)) {
|
|
61
|
+
problems.push(
|
|
62
|
+
`${file}: "servers" must be an object of name \u2192 server, not a list: { "servers": { "graphify": { "command": "\u2026" }, "linear": { "url": "\u2026" } } }`
|
|
63
|
+
);
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
60
66
|
const parsed = fileSchema.safeParse(json);
|
|
61
67
|
if (!parsed.success) {
|
|
62
68
|
problems.push(`${file}: ${z.prettifyError(parsed.error)}`);
|
|
@@ -147,6 +153,7 @@ function commandLine(def) {
|
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
export {
|
|
156
|
+
MCP_FILE,
|
|
150
157
|
SERVER_NAME,
|
|
151
158
|
isHttp,
|
|
152
159
|
loadMcpConfig,
|
|
@@ -155,4 +162,4 @@ export {
|
|
|
155
162
|
expandEnv,
|
|
156
163
|
commandLine
|
|
157
164
|
};
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
165
|
+
//# sourceMappingURL=chunk-QANRZW7W.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
writeFileAtomic
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NH72AELP.js";
|
|
5
5
|
import {
|
|
6
6
|
keepSecretForRedaction,
|
|
7
7
|
lookupModel
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7KBOIPUT.js";
|
|
9
9
|
import {
|
|
10
10
|
isLoopbackHost
|
|
11
11
|
} from "./chunk-ATW7BNIG.js";
|
|
@@ -228,6 +228,9 @@ async function keepProviderKeys(names, home) {
|
|
|
228
228
|
}
|
|
229
229
|
return stored.warning;
|
|
230
230
|
}
|
|
231
|
+
function providerKeyValue(name) {
|
|
232
|
+
return providerKey(process.env, name);
|
|
233
|
+
}
|
|
231
234
|
function providerKey(env, name) {
|
|
232
235
|
if (name === void 0) return void 0;
|
|
233
236
|
return keptKeys.get(name)?.value ?? env[name];
|
|
@@ -326,6 +329,7 @@ export {
|
|
|
326
329
|
setDefaultModel,
|
|
327
330
|
providerKeySource,
|
|
328
331
|
keepProviderKeys,
|
|
332
|
+
providerKeyValue,
|
|
329
333
|
resolveModel
|
|
330
334
|
};
|
|
331
|
-
//# sourceMappingURL=chunk-
|
|
335
|
+
//# sourceMappingURL=chunk-QLBCM2YQ.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_ENV_ALLOWLIST
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LMKGSYJC.js";
|
|
5
5
|
|
|
6
6
|
// src/jobs/git.ts
|
|
7
7
|
var GIT_TIMEOUT_MS = 12e4;
|
|
@@ -28,7 +28,7 @@ async function hostCommand(executor, cwd, argv, options = {}) {
|
|
|
28
28
|
const command = argv.map(shellWord).join(" ");
|
|
29
29
|
const result = await executor.run(command, policy, {
|
|
30
30
|
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
31
|
-
env: { GIT_TERMINAL_PROMPT: "0" }
|
|
31
|
+
env: { ...options.env, GIT_TERMINAL_PROMPT: "0" }
|
|
32
32
|
});
|
|
33
33
|
if (result.aborted) throw options.signal?.reason ?? new Error("stopped");
|
|
34
34
|
const out = { exitCode: result.exitCode, stdout: result.stdout.text, stderr: result.stderr.text };
|
|
@@ -46,4 +46,4 @@ export {
|
|
|
46
46
|
git,
|
|
47
47
|
hostCommand
|
|
48
48
|
};
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
49
|
+
//# sourceMappingURL=chunk-QXTHEFKP.js.map
|
|
@@ -4,12 +4,14 @@ import {
|
|
|
4
4
|
pickChannel
|
|
5
5
|
} from "./chunk-N4I6YS3E.js";
|
|
6
6
|
import {
|
|
7
|
-
gateProjectSettings
|
|
7
|
+
gateProjectSettings
|
|
8
|
+
} from "./chunk-ULAJPMY7.js";
|
|
9
|
+
import {
|
|
8
10
|
isCostIncomplete
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NLS7GMF7.js";
|
|
10
12
|
import {
|
|
11
13
|
totalTokens
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-7KBOIPUT.js";
|
|
13
15
|
import {
|
|
14
16
|
TrustStore
|
|
15
17
|
} from "./chunk-5DEWP3YX.js";
|
|
@@ -19,11 +21,11 @@ import {
|
|
|
19
21
|
jobChanges,
|
|
20
22
|
outsideScope,
|
|
21
23
|
prepareWorktree
|
|
22
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-WK5NE6RY.js";
|
|
23
25
|
import {
|
|
24
26
|
defaultAgentEnv,
|
|
25
27
|
removeAgent
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-TWQ7V2L6.js";
|
|
27
29
|
import {
|
|
28
30
|
jobReport
|
|
29
31
|
} from "./chunk-3MRQJKUO.js";
|
|
@@ -40,11 +42,11 @@ import {
|
|
|
40
42
|
JobGitError,
|
|
41
43
|
git,
|
|
42
44
|
hostCommand
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-QXTHEFKP.js";
|
|
44
46
|
import {
|
|
45
47
|
createExecutor,
|
|
46
48
|
loadSettings
|
|
47
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-LMKGSYJC.js";
|
|
48
50
|
import {
|
|
49
51
|
JOBS_DIR,
|
|
50
52
|
loadJob,
|
|
@@ -350,4 +352,4 @@ export {
|
|
|
350
352
|
msUntil,
|
|
351
353
|
finishJob
|
|
352
354
|
};
|
|
353
|
-
//# sourceMappingURL=chunk-
|
|
355
|
+
//# sourceMappingURL=chunk-R7RCW4BQ.js.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
summariseCall,
|
|
4
|
+
summariseResult
|
|
5
|
+
} from "./chunk-DQSCCE77.js";
|
|
6
|
+
import {
|
|
7
|
+
hasReceipt
|
|
8
|
+
} from "./chunk-4QRTTRVK.js";
|
|
9
|
+
import {
|
|
10
|
+
serverCallText,
|
|
11
|
+
serverResultSummary
|
|
12
|
+
} from "./chunk-A2QAIRQL.js";
|
|
13
|
+
import {
|
|
14
|
+
receiptText
|
|
15
|
+
} from "./chunk-2OYTNNUJ.js";
|
|
16
|
+
|
|
17
|
+
// src/cli/exportHtml.ts
|
|
18
|
+
function escapeHtml(text) {
|
|
19
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
20
|
+
}
|
|
21
|
+
var OUTPUT_CHARS = 4e3;
|
|
22
|
+
var cutOutput = (text) => text.length <= OUTPUT_CHARS ? text : `${text.slice(0, OUTPUT_CHARS)}
|
|
23
|
+
\u2026 ${text.length - OUTPUT_CHARS} more characters in the session file.`;
|
|
24
|
+
function callChange(call) {
|
|
25
|
+
const input = call.input ?? {};
|
|
26
|
+
const lines = (text, sign) => typeof text === "string" ? text.split("\n").map((l) => `${sign}${l}`) : [];
|
|
27
|
+
if (call.name === "edit_file") {
|
|
28
|
+
return [...lines(input.old_string, "-"), ...lines(input.new_string, "+")].join("\n");
|
|
29
|
+
}
|
|
30
|
+
if (call.name === "write_file") return lines(input.content, "+").join("\n");
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
function diffHtml(text) {
|
|
34
|
+
return text.split("\n").map((line) => {
|
|
35
|
+
const cls = line.startsWith("+") ? "add" : line.startsWith("-") ? "del" : "ctx";
|
|
36
|
+
return `<span class="${cls}">${escapeHtml(line)}</span>`;
|
|
37
|
+
}).join("\n");
|
|
38
|
+
}
|
|
39
|
+
var STYLE = `
|
|
40
|
+
:root { --bg: #fbf7ef; --fg: #222; --dim: #6b6b6b; --line: #e4dccb; --accent: #d97706;
|
|
41
|
+
--add: #e6f4e6; --addfg: #17602a; --del: #fbe7e7; --delfg: #8f1d1d; --you: #0e7490; }
|
|
42
|
+
@media (prefers-color-scheme: dark) { :root { --bg: #1d1d1b; --fg: #e8e4dc; --dim: #a19c92;
|
|
43
|
+
--line: #3a3833; --add: #183522; --addfg: #9ee2ad; --del: #3b1d1d; --delfg: #f4a9a9; } }
|
|
44
|
+
body { background: var(--bg); color: var(--fg); font: 15px/1.55 system-ui, sans-serif;
|
|
45
|
+
max-width: 920px; margin: 32px auto; padding: 0 16px; }
|
|
46
|
+
h1 { font-size: 20px; } .meta { color: var(--dim); font-size: 13px; }
|
|
47
|
+
.you { border-left: 4px solid var(--you); padding: 4px 12px; margin: 24px 0 8px; font-weight: 600;
|
|
48
|
+
white-space: pre-wrap; }
|
|
49
|
+
.answer { white-space: pre-wrap; margin: 8px 0; }
|
|
50
|
+
.answer::before { content: "\u2726 "; color: var(--accent); }
|
|
51
|
+
details { border: 1px solid var(--line); border-radius: 6px; margin: 6px 0; padding: 4px 10px; }
|
|
52
|
+
summary { cursor: pointer; font-family: ui-monospace, monospace; font-size: 13px; }
|
|
53
|
+
pre { font: 12.5px/1.45 ui-monospace, monospace; white-space: pre-wrap; overflow-wrap: anywhere;
|
|
54
|
+
margin: 6px 0; }
|
|
55
|
+
.add { background: var(--add); color: var(--addfg); display: inline-block; width: 100%; }
|
|
56
|
+
.del { background: var(--del); color: var(--delfg); display: inline-block; width: 100%; }
|
|
57
|
+
.receipt { border: 1px solid var(--accent); border-radius: 6px; padding: 6px 10px; }
|
|
58
|
+
.note { color: var(--dim); font-style: italic; }
|
|
59
|
+
.failed summary { color: var(--delfg); }
|
|
60
|
+
`;
|
|
61
|
+
function sessionHtml(records, sessionId) {
|
|
62
|
+
const body = [];
|
|
63
|
+
const start = records.find((r) => r.type === "start");
|
|
64
|
+
body.push(`<h1>Garuda session ${escapeHtml(sessionId)}</h1>`);
|
|
65
|
+
if (start?.type === "start") {
|
|
66
|
+
body.push(
|
|
67
|
+
`<p class="meta">${escapeHtml(start.root)} \xB7 ${escapeHtml(start.model)} \xB7 ${escapeHtml(start.t)}</p>`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const calls = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const record of records) {
|
|
72
|
+
switch (record.type) {
|
|
73
|
+
case "user": {
|
|
74
|
+
const prompt = record.message.content[0];
|
|
75
|
+
body.push(
|
|
76
|
+
`<div class="you">${escapeHtml(prompt?.type === "text" ? prompt.text : "")}</div>`
|
|
77
|
+
);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "assistant":
|
|
81
|
+
for (const block of record.response.content) {
|
|
82
|
+
if (block.type === "text" && block.text.trim() !== "") {
|
|
83
|
+
body.push(`<div class="answer">${escapeHtml(block.text.trim())}</div>`);
|
|
84
|
+
}
|
|
85
|
+
if (block.type === "tool_use") calls.set(block.id, block);
|
|
86
|
+
if (block.type === "server_tool_result") {
|
|
87
|
+
const call = record.response.content.find(
|
|
88
|
+
(b) => b.type === "server_tool_use" && b.id === block.toolUseId
|
|
89
|
+
);
|
|
90
|
+
const query = call?.type === "server_tool_use" ? ` ${serverCallText(call)}` : "";
|
|
91
|
+
const pages = block.results.map((r) => `${r.title}
|
|
92
|
+
${r.url}`).join("\n");
|
|
93
|
+
body.push(
|
|
94
|
+
`<details><summary>${escapeHtml(`\u25CF ${block.name} (Claude)${query} \u2192 ${serverResultSummary(block)}`)}</summary><pre>${escapeHtml(pages)}</pre></details>`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case "tool_results":
|
|
100
|
+
for (const block of record.message.content) {
|
|
101
|
+
if (block.type !== "tool_result") continue;
|
|
102
|
+
const call = calls.get(block.toolUseId);
|
|
103
|
+
if (call === void 0) continue;
|
|
104
|
+
const summary = summariseResult(call, { content: block.content, isError: block.isError });
|
|
105
|
+
const head = `\u25CF ${[call.name, summariseCall(call)].filter((p) => p !== "").join(" ")} \u2192 ${block.isError ? `failed: ${summary}` : summary}`;
|
|
106
|
+
const change = callChange(call);
|
|
107
|
+
const inner = change === void 0 ? `<pre>${escapeHtml(cutOutput(block.content))}</pre>` : `<pre>${diffHtml(cutOutput(change))}</pre>`;
|
|
108
|
+
body.push(
|
|
109
|
+
`<details${block.isError ? ' class="failed"' : ""}><summary>${escapeHtml(head)}</summary>${inner}</details>`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case "receipt":
|
|
114
|
+
if (hasReceipt(record.receipt)) {
|
|
115
|
+
body.push(`<pre class="receipt">${escapeHtml(receiptText(record.receipt))}</pre>`);
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
case "undo":
|
|
119
|
+
body.push('<p class="note">/undo: the last turn was taken back.</p>');
|
|
120
|
+
break;
|
|
121
|
+
case "redo":
|
|
122
|
+
body.push('<p class="note">/redo: the turn came back.</p>');
|
|
123
|
+
break;
|
|
124
|
+
case "compaction":
|
|
125
|
+
body.push('<p class="note">The conversation was compacted here.</p>');
|
|
126
|
+
break;
|
|
127
|
+
case "fork":
|
|
128
|
+
body.push(
|
|
129
|
+
`<p class="note">This session starts as a fork of session ${escapeHtml(record.from.sessionId)}, at turn ${record.from.turn}.</p>`
|
|
130
|
+
);
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return [
|
|
137
|
+
"<!doctype html>",
|
|
138
|
+
'<html lang="en"><head><meta charset="utf-8">',
|
|
139
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
140
|
+
// No script may run, and nothing loads from outside: the page's own policy says so too.
|
|
141
|
+
`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'">`,
|
|
142
|
+
`<title>Garuda session ${escapeHtml(sessionId)}</title>`,
|
|
143
|
+
`<style>${STYLE}</style></head><body>`,
|
|
144
|
+
...body,
|
|
145
|
+
"</body></html>",
|
|
146
|
+
""
|
|
147
|
+
].join("\n");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
escapeHtml,
|
|
152
|
+
OUTPUT_CHARS,
|
|
153
|
+
callChange,
|
|
154
|
+
sessionHtml
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=chunk-TLA2PDGM.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
hostCommand,
|
|
4
4
|
shellWord
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QXTHEFKP.js";
|
|
6
6
|
import {
|
|
7
7
|
JOBS_DIR
|
|
8
8
|
} from "./chunk-VTMD5IHD.js";
|
|
@@ -154,4 +154,4 @@ export {
|
|
|
154
154
|
nightAgentTime,
|
|
155
155
|
removeAgent
|
|
156
156
|
};
|
|
157
|
-
//# sourceMappingURL=chunk-
|
|
157
|
+
//# sourceMappingURL=chunk-TWQ7V2L6.js.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
cleanLine
|
|
4
|
+
} from "./chunk-PZS3V6P3.js";
|
|
5
|
+
import {
|
|
6
|
+
formatRule
|
|
7
|
+
} from "./chunk-3NJNVZTK.js";
|
|
8
|
+
|
|
9
|
+
// src/permissions/projectSettings.ts
|
|
10
|
+
import { createHash } from "crypto";
|
|
11
|
+
function settingsRisk(settings) {
|
|
12
|
+
const risk = {
|
|
13
|
+
executorHost: settings.executor === "host",
|
|
14
|
+
allow: settings.allow.map(formatRule),
|
|
15
|
+
writePaths: [...settings.sandbox?.writePaths ?? []],
|
|
16
|
+
envAllow: [...settings.envAllow],
|
|
17
|
+
allowLocalhost: settings.web?.allowLocalhost === true
|
|
18
|
+
};
|
|
19
|
+
const formatterCommands = Object.entries(settings.formatters?.commands ?? {}).flatMap(
|
|
20
|
+
([name, f]) => f === false ? [] : [`${name}: ${f.command.join(" ")}`]
|
|
21
|
+
);
|
|
22
|
+
if (formatterCommands.length > 0) risk.formatterCommands = formatterCommands;
|
|
23
|
+
const none = !risk.executorHost && risk.allow.length === 0 && risk.writePaths.length === 0 && risk.envAllow.length === 0 && !risk.allowLocalhost && risk.formatterCommands === void 0;
|
|
24
|
+
return none ? void 0 : risk;
|
|
25
|
+
}
|
|
26
|
+
function settingsRiskHash(risk) {
|
|
27
|
+
return createHash("sha256").update(JSON.stringify(risk)).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
function settingsRiskLines(risk) {
|
|
30
|
+
const lines = [];
|
|
31
|
+
if (risk.executorHost) lines.push('executor: "host" (commands run with no OS sandbox)');
|
|
32
|
+
if (risk.allow.length > 0) {
|
|
33
|
+
lines.push(`permissions.allow (no question for these): ${risk.allow.join(", ")}`);
|
|
34
|
+
}
|
|
35
|
+
if (risk.writePaths.length > 0) {
|
|
36
|
+
lines.push(`sandbox.writePaths (commands may write there): ${risk.writePaths.join(", ")}`);
|
|
37
|
+
}
|
|
38
|
+
if (risk.envAllow.length > 0) {
|
|
39
|
+
lines.push(`env.allow (commands see these variables): ${risk.envAllow.join(", ")}`);
|
|
40
|
+
}
|
|
41
|
+
if (risk.allowLocalhost) lines.push("web.allowLocalhost (web_fetch may reach this machine)");
|
|
42
|
+
if (risk.formatterCommands !== void 0) {
|
|
43
|
+
lines.push(
|
|
44
|
+
`formatters.commands (run after each edit): ${risk.formatterCommands.map(cleanLine).join("; ")}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return lines;
|
|
48
|
+
}
|
|
49
|
+
function withoutRisk(settings) {
|
|
50
|
+
const { sandbox, web, formatters, ...rest } = settings;
|
|
51
|
+
const { writePaths: _writePaths, ...sandboxRest } = sandbox ?? {};
|
|
52
|
+
const keptCommands = Object.fromEntries(
|
|
53
|
+
Object.entries(formatters?.commands ?? {}).filter(([, f]) => f === false)
|
|
54
|
+
);
|
|
55
|
+
return {
|
|
56
|
+
...rest,
|
|
57
|
+
...formatters === void 0 ? {} : { formatters: { ...formatters, commands: keptCommands } },
|
|
58
|
+
executor: settings.executor === "host" ? "auto" : settings.executor,
|
|
59
|
+
allow: [],
|
|
60
|
+
envAllow: [],
|
|
61
|
+
...sandbox === void 0 ? {} : { sandbox: sandboxRest },
|
|
62
|
+
...web === void 0 ? {} : { web: { ...web, allowLocalhost: false } }
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function gateProjectSettings(options) {
|
|
66
|
+
const risk = settingsRisk(options.settings);
|
|
67
|
+
if (risk === void 0) return { settings: options.settings };
|
|
68
|
+
const hash = settingsRiskHash(risk);
|
|
69
|
+
const known = options.trust?.settingsHash(options.root);
|
|
70
|
+
if (known === hash) return { settings: options.settings };
|
|
71
|
+
const lines = settingsRiskLines(risk);
|
|
72
|
+
if (options.ask !== void 0) {
|
|
73
|
+
const choice = await options.ask({
|
|
74
|
+
tool: "settings",
|
|
75
|
+
target: { kind: "input", json: "" },
|
|
76
|
+
title: "Project settings that loosen safety",
|
|
77
|
+
preview: [
|
|
78
|
+
`This project's .garuda/settings.json ${known === void 0 ? "asks for" : "changed and now asks for"}:`,
|
|
79
|
+
...lines.map((line) => ` - ${line}`),
|
|
80
|
+
"A cloned repository can ship this file. Without your yes, Garuda uses safe defaults for these",
|
|
81
|
+
"parts; the rest of the file applies."
|
|
82
|
+
].join("\n"),
|
|
83
|
+
isolation: options.isolation ?? "os",
|
|
84
|
+
question: "Use these project settings?",
|
|
85
|
+
choices: ["once", "session", "deny"],
|
|
86
|
+
labels: {
|
|
87
|
+
once: "Yes, for this run",
|
|
88
|
+
session: "Yes, and remember (asks again when they change)",
|
|
89
|
+
deny: "No, use safe defaults"
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
if (choice === "session") await options.trust?.setSettingsHash(options.root, hash);
|
|
93
|
+
if (choice !== "deny") return { settings: options.settings };
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
settings: withoutRisk(options.settings),
|
|
97
|
+
notice: [
|
|
98
|
+
`Project settings not applied (not approved): ${lines.join("; ")}.`,
|
|
99
|
+
"Start the chat in a terminal and answer yes to use them."
|
|
100
|
+
].join(" ")
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
gateProjectSettings
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=chunk-ULAJPMY7.js.map
|