@garuda-agent/garuda 0.16.1 → 0.18.0
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 +319 -0
- package/README.md +56 -1074
- package/SECURITY.md +9 -2
- package/dist/{acpCommand-OTAYW7BS.js → acpCommand-IUZPLPLI.js} +25 -23
- package/dist/{anthropic-NQDBCHJU.js → anthropic-NQIZLD64.js} +12 -6
- package/dist/{chunk-NP6RIC25.js → chunk-2FIV2VZP.js} +11 -7
- package/dist/{chunk-YQTGMCU6.js → chunk-3MRQJKUO.js} +29 -5
- package/dist/{chunk-HE2K657U.js → chunk-4BVCYCQT.js} +47 -16
- package/dist/{chunk-WMOLXTBU.js → chunk-4LFJFDCF.js} +2 -1
- package/dist/{chunk-44QN5SWZ.js → chunk-55CFEEIN.js} +22 -2
- package/dist/{chunk-USCUXCAU.js → chunk-5B5HDTUR.js} +3 -3
- package/dist/chunk-65NUEOI7.js +143 -0
- package/dist/{chunk-TUUEBHG2.js → chunk-6XHON3Q6.js} +2 -2
- package/dist/chunk-7KBOIPUT.js +157 -0
- package/dist/{chunk-B6FA4I7Q.js → chunk-7TH3NHCQ.js} +2 -2
- package/dist/{chunk-OUCV3FCT.js → chunk-AOGFYUKG.js} +3 -3
- package/dist/{chunk-GXZACJUI.js → chunk-B4SJY7KT.js} +322 -39
- package/dist/{chunk-3EQU2GUX.js → chunk-BHZ6G65V.js} +5 -1
- package/dist/chunk-C57GHLKP.js +9 -0
- package/dist/chunk-DOCDCQVM.js +484 -0
- package/dist/chunk-FOPBOB7W.js +332 -0
- package/dist/{chunk-TILFPYWO.js → chunk-NHWHG3SW.js} +1930 -992
- package/dist/{chunk-BQTUOO4G.js → chunk-OQ26W5L4.js} +81 -32
- package/dist/{chunk-HUIK3WXO.js → chunk-P6XWWIEK.js} +20 -3
- package/dist/chunk-Q4P3W657.js +6372 -0
- package/dist/{chunk-ZAE25JM2.js → chunk-QANRZW7W.js} +8 -1
- package/dist/chunk-UIRJOQQS.js +118 -0
- package/dist/chunk-UKIE4AAM.js +472 -0
- package/dist/{chunk-KKB5W47S.js → chunk-VTMD5IHD.js} +26 -2
- package/dist/cli/index.js +148 -57
- package/dist/config-WV2H64TF.js +23 -0
- package/dist/{create-VXIH2GUZ.js → create-W6BV75QX.js} +18 -12
- package/dist/{deadline-F5RJJ2QW.js → deadline-TP7ZGLT2.js} +4 -2
- package/dist/evalCommand-SKJQ6CW2.js +686 -0
- package/dist/evalTune-CZRCPSZ3.js +295 -0
- package/dist/{export-HN3CT7AV.js → export-SJ3DUP6W.js} +2 -2
- package/dist/{inkChat-WEDGQMCY.js → inkChat-WYYAFZXD.js} +20 -19
- package/dist/install-TKWN5AYH.js +19 -0
- package/dist/{job-3NYDMP2M.js → job-7JOI4YIQ.js} +6 -2
- package/dist/jobCommand-2ODQLHB3.js +33 -0
- package/dist/{launchd-SSX362VG.js → launchd-3A3ONEW5.js} +9 -8
- package/dist/{lspCommand-JRXKRSL7.js → lspCommand-ZZ6R4PA2.js} +7 -6
- package/dist/{manager-773PUDFE.js → manager-TCYTA5XU.js} +15 -14
- package/dist/{nightCommand-65MUEV32.js → nightCommand-WJU6VZA6.js} +26 -20
- package/dist/{openaiCompatible-CJWVJP5M.js → openaiCompatible-6TLDDW5U.js} +29 -15
- package/dist/{policy-EJPLEEXP.js → policy-ZJ7RMOFN.js} +10 -4
- package/dist/{remove-6GDSTAIO.js → remove-42WQNVPX.js} +8 -7
- package/dist/{run-ML6GSPN4.js → run-PTMRQCIU.js} +5 -5
- package/dist/{server-NGKVIQEV.js → server-VEYIJHEU.js} +2 -2
- package/dist/{setupCommand-VA7D5MZK.js → setupCommand-YMF6I3MD.js} +45 -13
- package/dist/text-LS35WSRL.js +17 -0
- package/dist/tune-YVI2KWQF.js +31 -0
- package/native/bin/garuda-reap +0 -0
- package/native/garuda-reap.c +113 -0
- package/package.json +39 -13
- package/dist/chunk-EIXUSVFX.js +0 -116
- package/dist/chunk-R7I2NBPX.js +0 -71
- package/dist/chunk-ULAJPMY7.js +0 -107
- package/dist/chunk-VBZCNMUD.js +0 -9
- package/dist/evalCommand-RGMZRYDR.js +0 -2812
- package/dist/install-EWDSPFFZ.js +0 -18
- package/dist/jobCommand-ACSGSBQ4.js +0 -32
- package/dist/text-J2GD647J.js +0 -11
package/SECURITY.md
CHANGED
|
@@ -6,9 +6,15 @@ Garuda runs commands and edits files for a model, so we take security reports se
|
|
|
6
6
|
|
|
7
7
|
| Version | Supported |
|
|
8
8
|
| --- | --- |
|
|
9
|
-
| 0.
|
|
9
|
+
| 0.18.x | Yes |
|
|
10
10
|
| Older | No |
|
|
11
11
|
|
|
12
|
+
## Node and dependencies
|
|
13
|
+
|
|
14
|
+
Run Garuda on Node 22.23.4, 24.22.1 or 26.12.1, or a later release of the same line. Garuda warns
|
|
15
|
+
at start on an older Node. CI, the release workflow and the dependency audit refuse a Node or a
|
|
16
|
+
package with a known security fix missing. See [docs/security-deps.md](docs/security-deps.md).
|
|
17
|
+
|
|
12
18
|
## Report a vulnerability
|
|
13
19
|
|
|
14
20
|
Do not open a public issue. Use GitHub's private vulnerability reporting: in this repository, open
|
|
@@ -28,5 +34,6 @@ In scope: the permission engine and the team policy, the OS sandbox (Seatbelt, b
|
|
|
28
34
|
network allowlist and its proxy, secret redaction, the audit log, and the consent for project
|
|
29
35
|
content (hooks, MCP servers, settings, commands, skills, agents).
|
|
30
36
|
|
|
31
|
-
Known limits are listed in
|
|
37
|
+
Known limits are listed in [docs/guide/status.md](docs/guide/status.md) and the
|
|
38
|
+
[issue register](docs/quality-baseline/issue-register.md). A report about one of them is still
|
|
32
39
|
welcome when it shows a new way to reach the same effect.
|
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
FileSessionStore,
|
|
4
|
-
Runtime,
|
|
5
|
-
auditDirFor
|
|
6
|
-
} from "./chunk-TILFPYWO.js";
|
|
7
|
-
import "./chunk-G2AX7MGS.js";
|
|
8
|
-
import "./chunk-A2QAIRQL.js";
|
|
9
|
-
import "./chunk-ULAJPMY7.js";
|
|
10
2
|
import {
|
|
11
3
|
chooseModel,
|
|
12
4
|
keepProviderKeys,
|
|
13
5
|
loadModelsConfig,
|
|
14
6
|
providerKeySource,
|
|
15
7
|
resolveModel
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import
|
|
18
|
-
|
|
8
|
+
} from "./chunk-2FIV2VZP.js";
|
|
9
|
+
import {
|
|
10
|
+
Runtime,
|
|
11
|
+
auditDirFor
|
|
12
|
+
} from "./chunk-NHWHG3SW.js";
|
|
13
|
+
import "./chunk-65NUEOI7.js";
|
|
14
|
+
import "./chunk-G2AX7MGS.js";
|
|
15
|
+
import "./chunk-A2QAIRQL.js";
|
|
16
|
+
import {
|
|
17
|
+
FileSessionStore
|
|
18
|
+
} from "./chunk-UKIE4AAM.js";
|
|
19
|
+
import "./chunk-7KBOIPUT.js";
|
|
19
20
|
import {
|
|
20
21
|
loadSearchConfig
|
|
21
22
|
} from "./chunk-B25VL4RU.js";
|
|
22
23
|
import "./chunk-23FXBNBB.js";
|
|
23
24
|
import {
|
|
24
25
|
VERSION
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-C57GHLKP.js";
|
|
26
27
|
import "./chunk-5DEWP3YX.js";
|
|
27
|
-
import "./chunk-ZAE25JM2.js";
|
|
28
|
-
import "./chunk-ATW7BNIG.js";
|
|
29
28
|
import "./chunk-PZS3V6P3.js";
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import "./chunk-
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-QANRZW7W.js";
|
|
30
|
+
import "./chunk-ATW7BNIG.js";
|
|
31
|
+
import "./chunk-AOGFYUKG.js";
|
|
32
|
+
import "./chunk-B4SJY7KT.js";
|
|
33
|
+
import "./chunk-BHZ6G65V.js";
|
|
34
|
+
import "./chunk-P6XWWIEK.js";
|
|
35
|
+
import "./chunk-UIRJOQQS.js";
|
|
36
|
+
import "./chunk-4LFJFDCF.js";
|
|
35
37
|
import "./chunk-3NJNVZTK.js";
|
|
36
38
|
|
|
37
39
|
// src/cli/acpCommand.ts
|
|
@@ -45,12 +47,12 @@ async function acpCommand(options) {
|
|
|
45
47
|
`);
|
|
46
48
|
return 1;
|
|
47
49
|
}
|
|
48
|
-
const { setupCommand, terminalIO } = await import("./setupCommand-
|
|
50
|
+
const { setupCommand, terminalIO } = await import("./setupCommand-YMF6I3MD.js");
|
|
49
51
|
return setupCommand({}, { io: await terminalIO() });
|
|
50
52
|
}
|
|
51
53
|
const output = protocolOutput(process.stdout, process.stderr);
|
|
52
54
|
const input = Readable.toWeb(process.stdin);
|
|
53
|
-
const { acpServer, setupNeeded } = await import("./server-
|
|
55
|
+
const { acpServer, setupNeeded } = await import("./server-VEYIJHEU.js");
|
|
54
56
|
const server = acpServer({
|
|
55
57
|
version: VERSION,
|
|
56
58
|
// Not on native Windows: the setup there could not keep a key private (no 0600).
|
|
@@ -114,7 +116,7 @@ async function prepare(options, from = {}) {
|
|
|
114
116
|
const why = credentialsWarning === void 0 ? "" : ` ${credentialsWarning}`;
|
|
115
117
|
return fail(`Garuda has no key for ${resolved.spec} (${keyName}).${why} ${SETUP_HINT}`, true);
|
|
116
118
|
}
|
|
117
|
-
const { loadTeamPolicy } = await import("./policy-
|
|
119
|
+
const { loadTeamPolicy } = await import("./policy-ZJ7RMOFN.js");
|
|
118
120
|
let team;
|
|
119
121
|
try {
|
|
120
122
|
team = await loadTeamPolicy("managed" in from ? { home, managed: from.managed } : { home });
|
|
@@ -188,4 +190,4 @@ export {
|
|
|
188
190
|
prepare,
|
|
189
191
|
protocolOutput
|
|
190
192
|
};
|
|
191
|
-
//# sourceMappingURL=acpCommand-
|
|
193
|
+
//# sourceMappingURL=acpCommand-IUZPLPLI.js.map
|
|
@@ -14,10 +14,12 @@ var AnthropicClient = class {
|
|
|
14
14
|
this.client = options.client ?? new Anthropic(clientOptions(options.apiKey));
|
|
15
15
|
}
|
|
16
16
|
async *stream(request, options) {
|
|
17
|
-
const stream = this.client.messages.stream(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const stream = this.client.messages.stream(toWireParams(this.model, request), {
|
|
18
|
+
...options?.signal === void 0 ? {} : { signal: options.signal },
|
|
19
|
+
// Under the token budget (0.17) the SDK must not send the request again on its own: the loop
|
|
20
|
+
// retries, and each of its attempts reserves first.
|
|
21
|
+
...options?.meter === void 0 ? {} : { maxRetries: 0 }
|
|
22
|
+
});
|
|
21
23
|
for await (const event of stream) {
|
|
22
24
|
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
|
|
23
25
|
yield { type: "text_delta", text: event.delta.text };
|
|
@@ -238,7 +240,11 @@ var AnthropicBatchClient = class {
|
|
|
238
240
|
);
|
|
239
241
|
const batch = await this.client.messages.batches.create(
|
|
240
242
|
{ requests: [{ custom_id: "garuda", params }] },
|
|
241
|
-
|
|
243
|
+
{
|
|
244
|
+
...signal === void 0 ? {} : { signal },
|
|
245
|
+
// Batch submission is a generation attempt too. The loop, not the SDK, owns retries.
|
|
246
|
+
...options?.meter === void 0 ? {} : { maxRetries: 0 }
|
|
247
|
+
}
|
|
242
248
|
);
|
|
243
249
|
const started = Date.now();
|
|
244
250
|
this.onWait?.({ batchId: batch.id, waitedMs: 0, status: "created" });
|
|
@@ -324,4 +330,4 @@ export {
|
|
|
324
330
|
toWireTools,
|
|
325
331
|
withHiddenThinking
|
|
326
332
|
};
|
|
327
|
-
//# sourceMappingURL=anthropic-
|
|
333
|
+
//# sourceMappingURL=anthropic-NQIZLD64.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
keepSecretForRedaction,
|
|
4
3
|
writeFileAtomic
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-65NUEOI7.js";
|
|
6
5
|
import {
|
|
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];
|
|
@@ -269,7 +272,7 @@ function resolveModel(spec, config = { providers: {}, models: {} }) {
|
|
|
269
272
|
...def.type === "anthropic" ? {
|
|
270
273
|
async createBatch(env = process.env, options = {}) {
|
|
271
274
|
const apiKey = providerKey(env, def.apiKeyEnv ?? "ANTHROPIC_API_KEY");
|
|
272
|
-
const { AnthropicBatchClient } = await import("./anthropic-
|
|
275
|
+
const { AnthropicBatchClient } = await import("./anthropic-NQIZLD64.js");
|
|
273
276
|
return new AnthropicBatchClient({ model, ...apiKey ? { apiKey } : {}, ...options });
|
|
274
277
|
}
|
|
275
278
|
} : {},
|
|
@@ -282,10 +285,10 @@ function resolveModel(spec, config = { providers: {}, models: {} }) {
|
|
|
282
285
|
}
|
|
283
286
|
if (def.type === "anthropic") {
|
|
284
287
|
const key = providerKey(env, def.apiKeyEnv ?? "ANTHROPIC_API_KEY");
|
|
285
|
-
const { AnthropicClient } = await import("./anthropic-
|
|
288
|
+
const { AnthropicClient } = await import("./anthropic-NQIZLD64.js");
|
|
286
289
|
return new AnthropicClient({ model, ...key ? { apiKey: key } : {} });
|
|
287
290
|
}
|
|
288
|
-
const { OpenAICompatibleClient } = await import("./openaiCompatible-
|
|
291
|
+
const { OpenAICompatibleClient } = await import("./openaiCompatible-6TLDDW5U.js");
|
|
289
292
|
return new OpenAICompatibleClient({
|
|
290
293
|
provider,
|
|
291
294
|
baseUrl: def.baseUrl,
|
|
@@ -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-2FIV2VZP.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
passed,
|
|
4
4
|
testSummary
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AOGFYUKG.js";
|
|
6
6
|
|
|
7
7
|
// src/jobs/text.ts
|
|
8
8
|
function jobPrompt(request, plan) {
|
|
@@ -22,7 +22,28 @@ function jobPrompt(request, plan) {
|
|
|
22
22
|
"</plan>"
|
|
23
23
|
].join("\n");
|
|
24
24
|
}
|
|
25
|
-
var cost = (usd
|
|
25
|
+
var cost = (usd, incomplete = false) => formatCost({ usd, incomplete });
|
|
26
|
+
function formatCost(c) {
|
|
27
|
+
if (c.usd === void 0) return "unknown";
|
|
28
|
+
return c.incomplete ? `\u2265 $${c.usd.toFixed(4)} (incomplete)` : `$${c.usd.toFixed(4)}`;
|
|
29
|
+
}
|
|
30
|
+
function sumCosts(costs) {
|
|
31
|
+
let usd = 0;
|
|
32
|
+
let incomplete = false;
|
|
33
|
+
for (const c of costs) {
|
|
34
|
+
usd = usd === void 0 || c.usd === void 0 ? void 0 : usd + c.usd;
|
|
35
|
+
incomplete ||= c.incomplete;
|
|
36
|
+
}
|
|
37
|
+
return { usd, incomplete };
|
|
38
|
+
}
|
|
39
|
+
function jobCost(r) {
|
|
40
|
+
const run = { usd: r.costUsd, incomplete: r.costIncomplete === true };
|
|
41
|
+
const proof = r.proof;
|
|
42
|
+
const review = proof?.review;
|
|
43
|
+
const reviewIncomplete = proof?.reviewCostIncomplete === true;
|
|
44
|
+
if (review === void 0) return sumCosts([run, { usd: 0, incomplete: reviewIncomplete }]);
|
|
45
|
+
return sumCosts([run, { usd: review.costUsd, incomplete: reviewIncomplete }]);
|
|
46
|
+
}
|
|
26
47
|
function jobReport(job) {
|
|
27
48
|
const r = job.result;
|
|
28
49
|
const lines = [`# Garuda job ${job.id}: ${job.title}`, ""];
|
|
@@ -40,7 +61,7 @@ function jobReport(job) {
|
|
|
40
61
|
if (job.startedAt !== void 0) lines.push(`- Started: ${job.startedAt}`);
|
|
41
62
|
if (r === void 0) return lines.join("\n");
|
|
42
63
|
lines.push(
|
|
43
|
-
`- Steps: ${r.steps} \xB7 tokens: ${Math.round(r.tokens / 100) / 10}k \xB7 cost: ${cost(r.costUsd)} \xB7 time: ${Math.round(r.durationMs / 1e3)} s`
|
|
64
|
+
`- Steps: ${r.steps} \xB7 tokens: ${Math.round(r.tokens / 100) / 10}k \xB7 cost: ${cost(r.costUsd, r.costIncomplete === true)} \xB7 time: ${Math.round(r.durationMs / 1e3)} s`
|
|
44
65
|
);
|
|
45
66
|
if (r.sessionId !== void 0) lines.push(`- Session: ${r.sessionId}`);
|
|
46
67
|
if (r.modelCalls !== void 0) {
|
|
@@ -96,7 +117,7 @@ function proofLines(job, proof) {
|
|
|
96
117
|
if (proof.flags.length === 0) out.push("None.");
|
|
97
118
|
for (const f of proof.flags) out.push(`- ${f.level === "stop" ? "STOP" : "look"}: ${f.text}`);
|
|
98
119
|
if (proof.review !== void 0) {
|
|
99
|
-
const cost2 = proof.review.costUsd === void 0 ? "" : ` (${
|
|
120
|
+
const cost2 = proof.review.costUsd === void 0 ? "" : ` (${formatCost({ usd: proof.review.costUsd, incomplete: proof.reviewCostIncomplete === true })})`;
|
|
100
121
|
out.push("", `## Review by a principal engineer${cost2}`, "", proof.review.text);
|
|
101
122
|
} else if (proof.reviewError !== void 0) {
|
|
102
123
|
out.push("", "## Review", "", `The review failed: ${proof.reviewError}`);
|
|
@@ -106,6 +127,9 @@ function proofLines(job, proof) {
|
|
|
106
127
|
|
|
107
128
|
export {
|
|
108
129
|
jobPrompt,
|
|
130
|
+
formatCost,
|
|
131
|
+
sumCosts,
|
|
132
|
+
jobCost,
|
|
109
133
|
jobReport
|
|
110
134
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
135
|
+
//# sourceMappingURL=chunk-3MRQJKUO.js.map
|
|
@@ -4,11 +4,12 @@ import {
|
|
|
4
4
|
pickChannel
|
|
5
5
|
} from "./chunk-N4I6YS3E.js";
|
|
6
6
|
import {
|
|
7
|
-
gateProjectSettings
|
|
8
|
-
|
|
7
|
+
gateProjectSettings,
|
|
8
|
+
isCostIncomplete
|
|
9
|
+
} from "./chunk-UKIE4AAM.js";
|
|
9
10
|
import {
|
|
10
11
|
totalTokens
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-7KBOIPUT.js";
|
|
12
13
|
import {
|
|
13
14
|
TrustStore
|
|
14
15
|
} from "./chunk-5DEWP3YX.js";
|
|
@@ -16,15 +17,16 @@ import {
|
|
|
16
17
|
cleanArgs,
|
|
17
18
|
commitJob,
|
|
18
19
|
jobChanges,
|
|
20
|
+
outsideScope,
|
|
19
21
|
prepareWorktree
|
|
20
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-55CFEEIN.js";
|
|
21
23
|
import {
|
|
22
24
|
defaultAgentEnv,
|
|
23
25
|
removeAgent
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-5B5HDTUR.js";
|
|
25
27
|
import {
|
|
26
28
|
jobReport
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-3MRQJKUO.js";
|
|
28
30
|
import {
|
|
29
31
|
jobVerdict,
|
|
30
32
|
parseReview,
|
|
@@ -33,21 +35,24 @@ import {
|
|
|
33
35
|
riskFlags,
|
|
34
36
|
stackOf,
|
|
35
37
|
testSummary
|
|
36
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-AOGFYUKG.js";
|
|
37
39
|
import {
|
|
38
40
|
JobGitError,
|
|
39
41
|
git,
|
|
40
42
|
hostCommand
|
|
41
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-6XHON3Q6.js";
|
|
42
44
|
import {
|
|
43
45
|
createExecutor,
|
|
44
46
|
loadSettings
|
|
45
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-B4SJY7KT.js";
|
|
46
48
|
import {
|
|
47
49
|
JOBS_DIR,
|
|
48
50
|
loadJob,
|
|
49
51
|
saveJob
|
|
50
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-VTMD5IHD.js";
|
|
53
|
+
import {
|
|
54
|
+
prepareWriteScope
|
|
55
|
+
} from "./chunk-UIRJOQQS.js";
|
|
51
56
|
import {
|
|
52
57
|
parseRule
|
|
53
58
|
} from "./chunk-3NJNVZTK.js";
|
|
@@ -93,6 +98,7 @@ async function proveJob(prepared, result, runtime, renderer, git2) {
|
|
|
93
98
|
};
|
|
94
99
|
if (job.review !== false && result.commit !== void 0) {
|
|
95
100
|
renderer.info("Reviewing the diff (a principal engineer's review)\u2026");
|
|
101
|
+
const unreportedBefore = runtime.session?.unreportedTokens ?? 0;
|
|
96
102
|
try {
|
|
97
103
|
const diff = (await git(git2, job.root, ["diff", "--no-renames", job.base, job.branch], {
|
|
98
104
|
check: false
|
|
@@ -118,8 +124,10 @@ async function proveJob(prepared, result, runtime, renderer, git2) {
|
|
|
118
124
|
} catch (error) {
|
|
119
125
|
proof.reviewError = error.message;
|
|
120
126
|
}
|
|
127
|
+
if ((runtime.session?.unreportedTokens ?? 0) > unreportedBefore)
|
|
128
|
+
proof.reviewCostIncomplete = true;
|
|
121
129
|
}
|
|
122
|
-
proof.verdict = jobVerdict(flags, proof.review?.verdict);
|
|
130
|
+
proof.verdict = jobVerdict(flags, proof.review?.verdict, proof.reviewError !== void 0);
|
|
123
131
|
return proof;
|
|
124
132
|
}
|
|
125
133
|
async function prepareJob(mainRoot, id, at, renderer, git2 = createExecutor("host").executor, launchd = {}, trustHome) {
|
|
@@ -159,6 +167,15 @@ async function prepare(mainRoot, id, at, renderer, git2, trustHome) {
|
|
|
159
167
|
renderer.error(`The job's worktree failed: ${error.message}`);
|
|
160
168
|
return 1;
|
|
161
169
|
}
|
|
170
|
+
if (job.writeScope !== void 0) {
|
|
171
|
+
const problem = prepareWriteScope(job.worktree, job.writeScope);
|
|
172
|
+
if (problem !== void 0) {
|
|
173
|
+
renderer.error(
|
|
174
|
+
`The job's write scope cannot be used: ${problem}. Fix "writeScope" in ${JOBS_DIR}/${id}.json.`
|
|
175
|
+
);
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
162
179
|
const gate = await gateProjectSettings({
|
|
163
180
|
root: job.root,
|
|
164
181
|
settings: await loadSettings(job.worktree),
|
|
@@ -172,6 +189,7 @@ async function prepare(mainRoot, id, at, renderer, git2, trustHome) {
|
|
|
172
189
|
...projectRest,
|
|
173
190
|
...job.network === void 0 ? {} : { network: { allow: job.network } },
|
|
174
191
|
allow: [...project.allow, ...job.allow.map((rule) => parseRule(rule))],
|
|
192
|
+
...job.writeScope === void 0 ? {} : { writeScope: job.writeScope },
|
|
175
193
|
maxSteps: job.maxSteps,
|
|
176
194
|
sandbox: {
|
|
177
195
|
...project.sandbox,
|
|
@@ -185,7 +203,8 @@ async function prepare(mainRoot, id, at, renderer, git2, trustHome) {
|
|
|
185
203
|
renderer.info(
|
|
186
204
|
`Job ${job.id}: ${job.title}
|
|
187
205
|
Branch ${job.branch} \xB7 worktree ${job.worktree}
|
|
188
|
-
Approved: ${job.allow.length === 0 ? "nothing beyond the sandbox" : job.allow.join(", ")}
|
|
206
|
+
Approved: ${job.allow.length === 0 ? "nothing beyond the sandbox" : job.allow.join(", ")}
|
|
207
|
+
${job.writeScope === void 0 ? "Write scope: none. Commands may write anywhere in the worktree." : `Write scope: ${job.writeScope.length === 0 ? "nothing in the worktree" : job.writeScope.join(", ")} (the OS sandbox enforces it for commands).`}`
|
|
189
208
|
);
|
|
190
209
|
return { job, root: job.worktree, settings, denied: [], startedAt: Date.now() };
|
|
191
210
|
}
|
|
@@ -224,8 +243,11 @@ function msUntil(hour, minute, now = /* @__PURE__ */ new Date()) {
|
|
|
224
243
|
if (next.getTime() <= now.getTime()) next.setDate(next.getDate() + 1);
|
|
225
244
|
return next.getTime() - now.getTime();
|
|
226
245
|
}
|
|
246
|
+
var ScopeViolation = class extends Error {
|
|
247
|
+
};
|
|
227
248
|
async function finishJob(prepared, outcome, runtime, renderer, git2 = createExecutor("host").executor, launchd = {}) {
|
|
228
249
|
const { job } = prepared;
|
|
250
|
+
let scopeFailure;
|
|
229
251
|
const hadAgent = job.launchd !== void 0 || launchd.fromLaunchd === true;
|
|
230
252
|
delete job.launchd;
|
|
231
253
|
const result = {
|
|
@@ -233,6 +255,7 @@ async function finishJob(prepared, outcome, runtime, renderer, git2 = createExec
|
|
|
233
255
|
steps: outcome.kind === "done" ? outcome.result.steps : 0,
|
|
234
256
|
tokens: runtime.session === void 0 ? 0 : totalTokens(runtime.session.usage),
|
|
235
257
|
...runtime.session?.costUsd === void 0 ? {} : { costUsd: runtime.session.costUsd },
|
|
258
|
+
...runtime.session !== void 0 && isCostIncomplete(runtime.session) ? { costIncomplete: true } : {},
|
|
236
259
|
durationMs: Date.now() - prepared.startedAt,
|
|
237
260
|
...runtime.session === void 0 ? {} : { sessionId: runtime.session.id },
|
|
238
261
|
files: [],
|
|
@@ -249,6 +272,12 @@ async function finishJob(prepared, outcome, runtime, renderer, git2 = createExec
|
|
|
249
272
|
...outcome.kind === "error" ? { error: outcome.message } : {}
|
|
250
273
|
};
|
|
251
274
|
try {
|
|
275
|
+
const outside = job.writeScope === void 0 ? [] : await outsideScope(git2, job);
|
|
276
|
+
if (outside.length > 0) {
|
|
277
|
+
scopeFailure = `changes outside the approved write scope, so nothing was committed: ${outside.slice(0, 20).join(", ")}${outside.length > 20 ? ", \u2026" : ""}`;
|
|
278
|
+
result.error = [result.error, scopeFailure].filter(Boolean).join("; ");
|
|
279
|
+
throw new ScopeViolation();
|
|
280
|
+
}
|
|
252
281
|
const commit = await commitJob(
|
|
253
282
|
git2,
|
|
254
283
|
job,
|
|
@@ -257,13 +286,15 @@ async function finishJob(prepared, outcome, runtime, renderer, git2 = createExec
|
|
|
257
286
|
if (commit !== void 0) result.commit = commit;
|
|
258
287
|
result.files = await jobChanges(git2, job);
|
|
259
288
|
} catch (error) {
|
|
260
|
-
if (!(error instanceof
|
|
261
|
-
|
|
289
|
+
if (!(error instanceof ScopeViolation)) {
|
|
290
|
+
if (!(error instanceof JobGitError)) throw error;
|
|
291
|
+
result.error = [result.error, `commit: ${error.message}`].filter(Boolean).join("; ");
|
|
292
|
+
}
|
|
262
293
|
}
|
|
263
294
|
if (outcome.kind !== "interrupted") {
|
|
264
295
|
result.proof = await proveJob(prepared, result, runtime, renderer, git2);
|
|
265
296
|
}
|
|
266
|
-
job.status = outcome.kind === "error" ? "failed" : outcome.kind === "interrupted" ? "stopped" : "done";
|
|
297
|
+
job.status = outcome.kind === "error" || scopeFailure !== void 0 ? "failed" : outcome.kind === "interrupted" ? "stopped" : "done";
|
|
267
298
|
job.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
268
299
|
job.result = result;
|
|
269
300
|
await saveJob(job);
|
|
@@ -319,4 +350,4 @@ export {
|
|
|
319
350
|
msUntil,
|
|
320
351
|
finishJob
|
|
321
352
|
};
|
|
322
|
-
//# sourceMappingURL=chunk-
|
|
353
|
+
//# sourceMappingURL=chunk-4BVCYCQT.js.map
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
JobGitError,
|
|
4
4
|
git
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6XHON3Q6.js";
|
|
6
|
+
import {
|
|
7
|
+
inWriteScope
|
|
8
|
+
} from "./chunk-UIRJOQQS.js";
|
|
6
9
|
|
|
7
10
|
// src/jobs/worktree.ts
|
|
8
11
|
import { createHash } from "crypto";
|
|
@@ -86,6 +89,22 @@ async function commitJob(executor, job, message) {
|
|
|
86
89
|
await git(executor, job.worktree, ["commit", "--no-verify", "-q", "-m", message]);
|
|
87
90
|
return (await git(executor, job.worktree, ["rev-parse", "HEAD"])).stdout.trim();
|
|
88
91
|
}
|
|
92
|
+
async function outsideScope(executor, job) {
|
|
93
|
+
if (job.writeScope === void 0) return [];
|
|
94
|
+
const exclude = [...NEVER_COMMIT, ...job.links].map((p) => `:(exclude)${p}`);
|
|
95
|
+
const status = await git(executor, job.worktree, [
|
|
96
|
+
"status",
|
|
97
|
+
"--porcelain=v1",
|
|
98
|
+
"-z",
|
|
99
|
+
"--untracked-files=all",
|
|
100
|
+
"--no-renames",
|
|
101
|
+
"--",
|
|
102
|
+
".",
|
|
103
|
+
...exclude
|
|
104
|
+
]);
|
|
105
|
+
const scope = job.writeScope;
|
|
106
|
+
return status.stdout.split("\0").filter((entry) => entry.length > 3).map((entry) => entry.slice(3)).filter((path) => !inWriteScope(path, scope));
|
|
107
|
+
}
|
|
89
108
|
async function jobChanges(executor, job) {
|
|
90
109
|
const range = [job.base, job.branch];
|
|
91
110
|
const names = await git(executor, job.root, [
|
|
@@ -133,6 +152,7 @@ export {
|
|
|
133
152
|
linkCandidates,
|
|
134
153
|
prepareWorktree,
|
|
135
154
|
commitJob,
|
|
155
|
+
outsideScope,
|
|
136
156
|
jobChanges
|
|
137
157
|
};
|
|
138
|
-
//# sourceMappingURL=chunk-
|
|
158
|
+
//# sourceMappingURL=chunk-55CFEEIN.js.map
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
hostCommand,
|
|
4
4
|
shellWord
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6XHON3Q6.js";
|
|
6
6
|
import {
|
|
7
7
|
JOBS_DIR
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VTMD5IHD.js";
|
|
9
9
|
|
|
10
10
|
// src/jobs/launchd.ts
|
|
11
11
|
import { createHash } from "crypto";
|
|
@@ -154,4 +154,4 @@ export {
|
|
|
154
154
|
nightAgentTime,
|
|
155
155
|
removeAgent
|
|
156
156
|
};
|
|
157
|
-
//# sourceMappingURL=chunk-
|
|
157
|
+
//# sourceMappingURL=chunk-5B5HDTUR.js.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
knownModels
|
|
4
|
+
} from "./chunk-7KBOIPUT.js";
|
|
5
|
+
|
|
6
|
+
// src/model/catalog.ts
|
|
7
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
var CATALOG_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
11
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
12
|
+
var MAX_PAGES = 10;
|
|
13
|
+
function catalogPath(home = homedir()) {
|
|
14
|
+
return join(home, ".garuda", "cache", "anthropic-models.json");
|
|
15
|
+
}
|
|
16
|
+
async function fetchCatalog(options) {
|
|
17
|
+
const fetcher = options.fetcher ?? fetch;
|
|
18
|
+
const base = (options.baseUrl ?? "https://api.anthropic.com").replace(/\/+$/, "");
|
|
19
|
+
const headers = { "anthropic-version": "2023-06-01" };
|
|
20
|
+
if (options.key !== void 0) headers["x-api-key"] = options.key;
|
|
21
|
+
const models = [];
|
|
22
|
+
let after;
|
|
23
|
+
try {
|
|
24
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
25
|
+
const url = `${base}/v1/models?limit=1000${after === void 0 ? "" : `&after_id=${encodeURIComponent(after)}`}`;
|
|
26
|
+
const response = await fetcher(url, {
|
|
27
|
+
headers,
|
|
28
|
+
redirect: "error",
|
|
29
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
await response.body?.cancel();
|
|
33
|
+
return { problem: `the Models API answered HTTP ${response.status}` };
|
|
34
|
+
}
|
|
35
|
+
const body = await response.json();
|
|
36
|
+
for (const m of body.data ?? []) {
|
|
37
|
+
if (typeof m.id !== "string" || !/^[A-Za-z0-9._:-]+$/.test(m.id)) continue;
|
|
38
|
+
models.push({
|
|
39
|
+
id: m.id,
|
|
40
|
+
...typeof m.display_name === "string" ? { displayName: m.display_name } : {},
|
|
41
|
+
...typeof m.max_input_tokens === "number" && m.max_input_tokens > 0 ? { contextWindow: m.max_input_tokens } : {}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (body.has_more !== true || typeof body.last_id !== "string") break;
|
|
45
|
+
after = body.last_id;
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return { problem: `Garuda could not read the Models API (${error.message})` };
|
|
49
|
+
}
|
|
50
|
+
return { catalog: { fetchedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), models } };
|
|
51
|
+
}
|
|
52
|
+
async function saveCatalog(catalog, home = homedir()) {
|
|
53
|
+
const path = catalogPath(home);
|
|
54
|
+
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
55
|
+
await writeFile(path, `${JSON.stringify(catalog, null, 2)}
|
|
56
|
+
`, { mode: 384 });
|
|
57
|
+
}
|
|
58
|
+
async function loadCatalog(home = homedir()) {
|
|
59
|
+
try {
|
|
60
|
+
const data = JSON.parse(await readFile(catalogPath(home), "utf8"));
|
|
61
|
+
if (typeof data.fetchedAt !== "string" || !Array.isArray(data.models)) return void 0;
|
|
62
|
+
return {
|
|
63
|
+
fetchedAt: data.fetchedAt,
|
|
64
|
+
models: data.models.filter((m) => typeof m?.id === "string")
|
|
65
|
+
};
|
|
66
|
+
} catch {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function mergedModels(catalog) {
|
|
71
|
+
const table = knownModels();
|
|
72
|
+
const exact = new Map(table.map((m) => [m.id, m.info]));
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const m of catalog?.models ?? []) {
|
|
75
|
+
const info = exact.get(m.id) ?? prefixInfo(m.id);
|
|
76
|
+
if (info !== void 0) out.push({ id: m.id, info, known: true });
|
|
77
|
+
else out.push({ id: m.id, info: { contextWindow: m.contextWindow ?? 2e5 }, known: false });
|
|
78
|
+
}
|
|
79
|
+
for (const m of table) {
|
|
80
|
+
if (!out.some((o) => o.id === m.id)) out.push({ id: m.id, info: m.info, known: true });
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function prefixInfo(id) {
|
|
85
|
+
let best;
|
|
86
|
+
for (const m of knownModels()) {
|
|
87
|
+
if (id.startsWith(`${m.id}-`) && /^\d{8}$/.test(id.slice(m.id.length + 1))) {
|
|
88
|
+
if (m.id.length > (best?.id.length ?? 0)) best = m;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return best?.info;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/tools/atomicWrite.ts
|
|
95
|
+
import { randomBytes } from "crypto";
|
|
96
|
+
import { chmod, link, realpath, rename, stat, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
97
|
+
import { basename, dirname as dirname2, join as join2 } from "path";
|
|
98
|
+
async function writeFileAtomic(path, content, { createOnly }) {
|
|
99
|
+
const target = createOnly ? path : await realpath(path);
|
|
100
|
+
const temp = join2(
|
|
101
|
+
dirname2(target),
|
|
102
|
+
`.${basename(target)}.garuda-${randomBytes(4).toString("hex")}.tmp`
|
|
103
|
+
);
|
|
104
|
+
const mode = createOnly ? void 0 : (await stat(target)).mode & 4095;
|
|
105
|
+
await writeFile2(temp, content, { flag: "wx", ...mode === void 0 ? {} : { mode } });
|
|
106
|
+
try {
|
|
107
|
+
if (createOnly) {
|
|
108
|
+
try {
|
|
109
|
+
await link(temp, target);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const code = error.code;
|
|
112
|
+
if (code !== "EPERM" && code !== "ENOTSUP" && code !== "EXDEV") throw error;
|
|
113
|
+
if (await exists(target)) throw Object.assign(new Error("EEXIST"), { code: "EEXIST" });
|
|
114
|
+
await rename(temp, target);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
await chmod(temp, mode ?? 420);
|
|
119
|
+
await rename(temp, target);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
await unlink(temp).catch(() => {
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function exists(path) {
|
|
128
|
+
try {
|
|
129
|
+
await stat(path);
|
|
130
|
+
return true;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
writeFileAtomic,
|
|
138
|
+
fetchCatalog,
|
|
139
|
+
saveCatalog,
|
|
140
|
+
loadCatalog,
|
|
141
|
+
mergedModels
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=chunk-65NUEOI7.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-B4SJY7KT.js";
|
|
5
5
|
|
|
6
6
|
// src/jobs/git.ts
|
|
7
7
|
var GIT_TIMEOUT_MS = 12e4;
|
|
@@ -46,4 +46,4 @@ export {
|
|
|
46
46
|
git,
|
|
47
47
|
hostCommand
|
|
48
48
|
};
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
49
|
+
//# sourceMappingURL=chunk-6XHON3Q6.js.map
|