@garuda-agent/garuda 0.15.0 → 0.16.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 +35 -0
- package/README.md +30 -17
- package/dist/{acpCommand-FZ2P5JFO.js → acpCommand-OTAYW7BS.js} +64 -26
- package/dist/chunk-3EQU2GUX.js +27 -0
- package/dist/{chunk-K3SIUOAG.js → chunk-44QN5SWZ.js} +2 -2
- package/dist/{chunk-INRZR772.js → chunk-B6FA4I7Q.js} +2 -2
- package/dist/{chunk-PQNT34LY.js → chunk-BQTUOO4G.js} +4 -4
- package/dist/chunk-EIXUSVFX.js +116 -0
- package/dist/{chunk-SI2QJYCR.js → chunk-GXZACJUI.js} +6 -25
- package/dist/{chunk-2S62ZNTC.js → chunk-HE2K657U.js} +9 -7
- package/dist/chunk-NP6RIC25.js +331 -0
- package/dist/chunk-R7I2NBPX.js +71 -0
- package/dist/{chunk-K7KK7U44.js → chunk-TILFPYWO.js} +125 -226
- package/dist/{chunk-2TAXSFWN.js → chunk-TUUEBHG2.js} +2 -2
- package/dist/{chunk-HRGX53VC.js → chunk-ULAJPMY7.js} +1 -67
- package/dist/{chunk-S77XDSD4.js → chunk-USCUXCAU.js} +2 -2
- package/dist/chunk-VBZCNMUD.js +9 -0
- package/dist/cli/index.js +50 -33
- package/dist/{create-N4I7TIZE.js → create-VXIH2GUZ.js} +6 -5
- package/dist/{evalCommand-3SSFMZQD.js → evalCommand-RGMZRYDR.js} +31 -17
- package/dist/{inkChat-34BTFG2K.js → inkChat-WEDGQMCY.js} +10 -7
- package/dist/{install-VRPBZY5H.js → install-EWDSPFFZ.js} +4 -3
- package/dist/{jobCommand-XJ5GULS5.js → jobCommand-ACSGSBQ4.js} +9 -7
- package/dist/{launchd-QVHQ3VNN.js → launchd-SSX362VG.js} +5 -4
- package/dist/{lspCommand-XNTBHXLA.js → lspCommand-JRXKRSL7.js} +4 -3
- package/dist/{manager-CSI76AWF.js → manager-773PUDFE.js} +4 -3
- package/dist/{nightCommand-CR6ZHYGP.js → nightCommand-65MUEV32.js} +9 -7
- package/dist/{remove-LE7Q7K46.js → remove-6GDSTAIO.js} +4 -3
- package/dist/{server-R3GNIC33.js → server-NGKVIQEV.js} +28 -6
- package/dist/setupCommand-VA7D5MZK.js +303 -0
- package/package.json +1 -1
- package/dist/chunk-EPKWDCV4.js +0 -9
- package/dist/chunk-YFQJXRIJ.js +0 -193
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.1 (2026-10-06)
|
|
4
|
+
|
|
5
|
+
- `garuda acp` offers its sign-in (Terminal Auth) also to clients that declare the capability in
|
|
6
|
+
its older form, `_meta["terminal-auth"]`. The ACP Registry's checker sends only that form, so
|
|
7
|
+
0.16.0 answered it with no sign-in method and failed the registry's auth check.
|
|
8
|
+
|
|
9
|
+
## 0.16.0 (2026-10-06)
|
|
10
|
+
|
|
11
|
+
Setup for editors and the ACP Registry (design: [docs/lld/setup.md](docs/lld/setup.md)).
|
|
12
|
+
|
|
13
|
+
- Stored provider keys: Garuda reads `~/.garuda/credentials` (JSON, key name → key) when no
|
|
14
|
+
environment variable sets the key. It reads the file only when it is a regular file (not a link),
|
|
15
|
+
owned by you, with mode 0600; else it ignores the file with a warning. The key never enters
|
|
16
|
+
Garuda's environment, and the redactor knows it. `garuda setup` writes this file.
|
|
17
|
+
- Commands in the OS sandbox cannot read `~/.garuda/credentials` (macOS and Linux).
|
|
18
|
+
- `"default"` in `~/.garuda/models.json`: the model when neither `--model` nor `GARUDA_MODEL` names
|
|
19
|
+
one.
|
|
20
|
+
- The redactor removes every provider key that Garuda keeps, also one whose variable name does not
|
|
21
|
+
look secret (a custom provider's `apiKeyEnv`).
|
|
22
|
+
- `garuda setup`: choose a provider and a model, enter the key once (hidden input), check it with
|
|
23
|
+
one free request to the provider, then store the model as the default and the key in
|
|
24
|
+
`~/.garuda/credentials`. `--show` shows where each key comes from (masked); `--forget [NAME]`
|
|
25
|
+
removes stored keys. With no terminal, it stops with a message.
|
|
26
|
+
- The messages for a missing model or key name `garuda setup`.
|
|
27
|
+
- `garuda acp`: Terminal Auth. An editor that can run it (`auth.terminal`) gets a "Set up Garuda"
|
|
28
|
+
method; it runs `garuda acp setup` in a terminal. A session with no model or no key fails with
|
|
29
|
+
ACP's "auth required", so the editor offers the setup. The setup is read again for each new
|
|
30
|
+
session: no restart after `garuda setup`.
|
|
31
|
+
- Native Windows: `garuda acp` sessions and `garuda setup` stop with a message to use WSL.
|
|
32
|
+
- Docs: `garuda setup` in the quick start, the landing page, the README and the Editors page; editor
|
|
33
|
+
entries need no API key in their settings. Tested live in Zed 1.22: with no model and no key, Zed
|
|
34
|
+
offered the setup, ran it in a terminal, and the next thread worked with no restart.
|
|
35
|
+
- `-p --output-format stream-json`: `apiKeySource` in `system/init` is `credentials` for a stored
|
|
36
|
+
Claude key.
|
|
37
|
+
|
|
3
38
|
## 0.15.0 (2026-10-06)
|
|
4
39
|
|
|
5
40
|
Garuda in your editor. Version 0.15.0 is not the old "0.15" branch label of the status table (code
|
package/README.md
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
Website and docs: https://madhu-sv.github.io/garuda/
|
|
6
6
|
|
|
7
|
-
Garuda is a terminal coding agent. This is version 0.
|
|
8
|
-
|
|
7
|
+
Garuda is a terminal coding agent. This is version 0.16.1: `garuda setup` (choose a model and store
|
|
8
|
+
its key once) and the sign-in from editors, on top of 0.15.0 (Garuda in your editor over the Agent
|
|
9
|
+
Client Protocol, `garuda acp`) and 0.14.1 (the 0.14–0.17 branches after the merge gate and Garuda's own
|
|
9
10
|
review; see the notes under the status table). Release notes: [CHANGELOG.md](CHANGELOG.md).
|
|
10
11
|
Design documents: [docs/](docs/README.md) (architecture, high-level design, low-level design per component).
|
|
11
12
|
The requirements doc defines the scope. Code, tests and commits refer to its IDs (F1–F26, N1–N8).
|
|
@@ -36,9 +37,10 @@ The requirements doc defines the scope. Code, tests and commits refer to its IDs
|
|
|
36
37
|
| 0.16: language plugins (Go and Rust built in, user plugins in ~/.garuda/languages, /languages; project plugins not loaded) | Done (0.14.0) |
|
|
37
38
|
| 0.17: Mixture-of-Experts subagents (opt-in), team policy and audit log (W5) | Done (0.14.0) |
|
|
38
39
|
| Editors over the Agent Client Protocol (`garuda acp`: VS Code with an extension, Zed, JetBrains, Neovim, Emacs) | Done (0.15.0) |
|
|
40
|
+
| `garuda setup` (model and stored key) and the editor sign-in (ACP Terminal Auth) | Done (0.16.0) |
|
|
39
41
|
|
|
40
|
-
The rows labelled 0.14–0.17 are branch labels, not versions: the "0.15"
|
|
41
|
-
shipped in 0.14.0. The
|
|
42
|
+
The rows labelled 0.14–0.17 are branch labels, not versions: the "0.15" and "0.16" branches (code
|
|
43
|
+
intelligence, language plugins) shipped in 0.14.0. The last two rows are versions 0.15.0 and 0.16.0.
|
|
42
44
|
|
|
43
45
|
The 0.14–0.17 rows were built on stacked branches that were never released. They ship together as
|
|
44
46
|
0.14.0 after the merge gate: the high findings of the branch review are fixed (policy source, audit
|
|
@@ -50,7 +52,7 @@ headings below keep the branch labels (0.14–0.17).
|
|
|
50
52
|
Before 0.14.0, Garuda reviewed its own code with Fable 5.1, one area at a time (knowledge,
|
|
51
53
|
permissions, tools, sandbox, audit, loop, agents), in plan mode. 0.14.1 adds the cli and extensions
|
|
52
54
|
areas and fixes the open findings of all nine areas (see [CHANGELOG.md](CHANGELOG.md)). Known limits
|
|
53
|
-
in 0.
|
|
55
|
+
in 0.16.1:
|
|
54
56
|
|
|
55
57
|
- G09: project language plugins are not loaded (no consent flow for their imports yet).
|
|
56
58
|
- Subagents: each child has its own step and token budget (capped by the team policy); it does not
|
|
@@ -65,6 +67,9 @@ in 0.15.0:
|
|
|
65
67
|
- Editors (`garuda acp`): Garuda's session history is not in the editor (no `session/load`), the
|
|
66
68
|
editor's MCP servers are not started, Garuda reads saved files (not unsaved buffers), the terminal's
|
|
67
69
|
own commands (`/undo`, `/diff` …) are not available, and an edit is approved as a whole (no hunks).
|
|
70
|
+
- Stored keys are in a file (`~/.garuda/credentials`, mode 0600, hidden from the sandbox), not in the
|
|
71
|
+
OS keychain yet. Keep the file out of backups and dotfile sync.
|
|
72
|
+
- Native Windows is not supported; use WSL.
|
|
68
73
|
|
|
69
74
|
## Use
|
|
70
75
|
|
|
@@ -98,8 +103,7 @@ pnpm link --global # optional: puts `garuda` on your PATH (run `pnpm setup` onc
|
|
|
98
103
|
Then:
|
|
99
104
|
|
|
100
105
|
```sh
|
|
101
|
-
|
|
102
|
-
export GARUDA_MODEL=<model-id>
|
|
106
|
+
garuda setup # once: pick a provider and model, enter the key
|
|
103
107
|
garuda # chat in the current folder
|
|
104
108
|
garuda -p "Explain what this repo does" # run one task and exit
|
|
105
109
|
echo "Explain this repo" | garuda # the same, from stdin
|
|
@@ -107,6 +111,13 @@ echo "Explain this repo" | garuda # the same, from stdin
|
|
|
107
111
|
|
|
108
112
|
From a source build without `pnpm link`, use `node dist/cli/index.js` in place of `garuda`.
|
|
109
113
|
|
|
114
|
+
`garuda setup` (0.16) asks for the provider, the model and the key (hidden input), checks the key
|
|
115
|
+
with one free request to the provider, and stores the model as `"default"` in
|
|
116
|
+
`~/.garuda/models.json` and the key in `~/.garuda/credentials` (mode 0600; commands in the sandbox
|
|
117
|
+
cannot read it). `garuda setup --show` shows the model and where each key comes from (masked);
|
|
118
|
+
`garuda setup --forget [NAME]` removes stored keys. Environment variables still work and win:
|
|
119
|
+
`ANTHROPIC_API_KEY`, `GARUDA_MODEL`. Do not sync or back up `~/.garuda/credentials` to other places.
|
|
120
|
+
|
|
110
121
|
Tools: read_file, glob and grep run with no question.
|
|
111
122
|
write_file, edit_file and bash show a diff or the command first. You pick: allow once, allow for this session, or deny.
|
|
112
123
|
Commands run in an OS sandbox when your machine has one (see Sandbox below). Without a sandbox,
|
|
@@ -150,13 +161,15 @@ Set the context window, price and output limit per model, and add providers, in
|
|
|
150
161
|
```json
|
|
151
162
|
{
|
|
152
163
|
"providers": { "lab": { "type": "openai-compatible", "baseUrl": "https://llm.example.com/v1", "apiKeyEnv": "LAB_KEY" } },
|
|
153
|
-
"models": { "ollama/qwen3-coder:30b": { "contextWindow": 65536, "maxTokens": 8192 } }
|
|
164
|
+
"models": { "ollama/qwen3-coder:30b": { "contextWindow": 65536, "maxTokens": 8192 } },
|
|
165
|
+
"default": "ollama/qwen3-coder:30b"
|
|
154
166
|
}
|
|
155
167
|
```
|
|
156
168
|
|
|
157
169
|
- Only this file in your home folder can define providers; a project cannot send your code elsewhere.
|
|
158
170
|
Keys come from environment variables. Plain http works only to this machine (or with
|
|
159
171
|
`"allowInsecureHttp": true`).
|
|
172
|
+
- `"default"` is the model when neither `--model` nor `GARUDA_MODEL` names one.
|
|
160
173
|
- Some small models write a tool call as JSON text. When the whole reply is such a call to a known
|
|
161
174
|
tool, Garuda runs it as a real call, with the usual approvals. For a model that also writes
|
|
162
175
|
sentences around the call (for example `qwen2.5-coder:7b`), set `"textToolCalls": "lines"` for that
|
|
@@ -252,12 +265,14 @@ Set the context window, price and output limit per model, and add providers, in
|
|
|
252
265
|
|
|
253
266
|
Model text goes to stdout; tool activity and notes go to stderr. So `garuda -p "…" > answer.md` keeps only the answer.
|
|
254
267
|
|
|
255
|
-
## Editors (ACP, 0.15)
|
|
268
|
+
## Editors (ACP, 0.15; sign-in 0.16)
|
|
256
269
|
|
|
257
270
|
`garuda acp` runs Garuda as an agent for editors that speak the Agent Client Protocol. The editor
|
|
258
271
|
starts it, shows the conversation, the tool calls and the questions, and Garuda does the work with
|
|
259
272
|
the same permission engine, OS sandbox, team policy, hooks and audit log as in the terminal.
|
|
260
273
|
|
|
274
|
+
Run `garuda setup` once in a terminal (see Use): then the editor needs no key in its settings.
|
|
275
|
+
|
|
261
276
|
VS Code, with the extension "ACP Client" (`formulahendry.acp-client`), in the user settings
|
|
262
277
|
(Cmd+Shift+P, **Preferences: Open User Settings (JSON)**). Use the full path from `which garuda`:
|
|
263
278
|
VS Code started from the Dock does not get your shell's `PATH`.
|
|
@@ -265,18 +280,16 @@ VS Code started from the Dock does not get your shell's `PATH`.
|
|
|
265
280
|
```json
|
|
266
281
|
{
|
|
267
282
|
"acp.agents": {
|
|
268
|
-
"Garuda": {
|
|
269
|
-
"command": "/opt/homebrew/bin/garuda",
|
|
270
|
-
"args": ["acp"],
|
|
271
|
-
"env": { "GARUDA_MODEL": "claude-sonnet-5", "ANTHROPIC_API_KEY": "..." }
|
|
272
|
-
}
|
|
283
|
+
"Garuda": { "command": "/opt/homebrew/bin/garuda", "args": ["acp"] }
|
|
273
284
|
}
|
|
274
285
|
}
|
|
275
286
|
```
|
|
276
287
|
|
|
277
|
-
Zed: the same command
|
|
278
|
-
|
|
279
|
-
|
|
288
|
+
Zed: the same command and argument under `agent_servers` (with `"type": "custom"`). With no model
|
|
289
|
+
or key, Zed offers **Set up Garuda** (ACP Terminal Auth, 0.16): it runs `garuda acp setup` in a
|
|
290
|
+
terminal, and the next thread works. The website's
|
|
291
|
+
[Editors](https://madhu-sv.github.io/garuda/docs/editors/) page has the steps and what the editor
|
|
292
|
+
shows.
|
|
280
293
|
|
|
281
294
|
- Each edit asks in the editor, with the editor's diff view; "Allow for this session" lasts for this
|
|
282
295
|
session only. Keep an extension's auto-approve off: it answers Garuda's questions for you.
|
|
@@ -1,46 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
keepProviderKey,
|
|
4
|
-
loadModelsConfig,
|
|
5
|
-
resolveModel
|
|
6
|
-
} from "./chunk-YFQJXRIJ.js";
|
|
7
2
|
import {
|
|
8
3
|
FileSessionStore,
|
|
9
4
|
Runtime,
|
|
10
5
|
auditDirFor
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-A2QAIRQL.js";
|
|
13
|
-
import "./chunk-HRGX53VC.js";
|
|
6
|
+
} from "./chunk-TILFPYWO.js";
|
|
14
7
|
import "./chunk-G2AX7MGS.js";
|
|
8
|
+
import "./chunk-A2QAIRQL.js";
|
|
9
|
+
import "./chunk-ULAJPMY7.js";
|
|
10
|
+
import {
|
|
11
|
+
chooseModel,
|
|
12
|
+
keepProviderKeys,
|
|
13
|
+
loadModelsConfig,
|
|
14
|
+
providerKeySource,
|
|
15
|
+
resolveModel
|
|
16
|
+
} from "./chunk-NP6RIC25.js";
|
|
17
|
+
import "./chunk-EIXUSVFX.js";
|
|
18
|
+
import "./chunk-R7I2NBPX.js";
|
|
15
19
|
import {
|
|
16
20
|
loadSearchConfig
|
|
17
21
|
} from "./chunk-B25VL4RU.js";
|
|
18
22
|
import "./chunk-23FXBNBB.js";
|
|
19
23
|
import {
|
|
20
24
|
VERSION
|
|
21
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-VBZCNMUD.js";
|
|
22
26
|
import "./chunk-5DEWP3YX.js";
|
|
23
27
|
import "./chunk-ZAE25JM2.js";
|
|
24
28
|
import "./chunk-ATW7BNIG.js";
|
|
25
29
|
import "./chunk-PZS3V6P3.js";
|
|
26
30
|
import "./chunk-OUCV3FCT.js";
|
|
27
|
-
import "./chunk-
|
|
31
|
+
import "./chunk-GXZACJUI.js";
|
|
28
32
|
import "./chunk-WMOLXTBU.js";
|
|
33
|
+
import "./chunk-3EQU2GUX.js";
|
|
29
34
|
import "./chunk-HUIK3WXO.js";
|
|
30
35
|
import "./chunk-3NJNVZTK.js";
|
|
31
36
|
|
|
32
37
|
// src/cli/acpCommand.ts
|
|
33
38
|
import { homedir } from "os";
|
|
34
39
|
import { Readable } from "stream";
|
|
40
|
+
var WINDOWS_MESSAGE = "Garuda supports macOS and Linux. On Windows, run Garuda in WSL (Windows Subsystem for Linux).";
|
|
35
41
|
async function acpCommand(options) {
|
|
42
|
+
if (options.action !== void 0) {
|
|
43
|
+
if (options.action !== "setup") {
|
|
44
|
+
process.stderr.write(`Unknown argument "${options.action}". Use: garuda acp [setup]
|
|
45
|
+
`);
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
const { setupCommand, terminalIO } = await import("./setupCommand-VA7D5MZK.js");
|
|
49
|
+
return setupCommand({}, { io: await terminalIO() });
|
|
50
|
+
}
|
|
36
51
|
const output = protocolOutput(process.stdout, process.stderr);
|
|
37
52
|
const input = Readable.toWeb(process.stdin);
|
|
38
|
-
const
|
|
39
|
-
const { acpServer } = await import("./server-R3GNIC33.js");
|
|
53
|
+
const { acpServer, setupNeeded } = await import("./server-NGKVIQEV.js");
|
|
40
54
|
const server = acpServer({
|
|
41
55
|
version: VERSION,
|
|
56
|
+
// Not on native Windows: the setup there could not keep a key private (no 0600).
|
|
57
|
+
...process.platform === "win32" ? {} : { terminalAuth: { args: ["setup"] } },
|
|
42
58
|
createRuntime: async ({ root, approver, onEvent, onNotice }) => {
|
|
43
|
-
|
|
59
|
+
const setup = await prepare(options);
|
|
60
|
+
if ("problem" in setup) {
|
|
61
|
+
throw setup.setupNeeded ? setupNeeded(setup.problem) : new Error(setup.problem);
|
|
62
|
+
}
|
|
44
63
|
return createRuntime(setup, { root, approver, onEvent, onNotice });
|
|
45
64
|
}
|
|
46
65
|
});
|
|
@@ -64,11 +83,15 @@ function protocolOutput(stdout, stderr) {
|
|
|
64
83
|
})
|
|
65
84
|
});
|
|
66
85
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
var SETUP_HINT = "Run `garuda setup` in a terminal, or use the editor's sign-in for Garuda.";
|
|
87
|
+
async function prepare(options, from = {}) {
|
|
88
|
+
const fail = (problem, setupNeeded = false) => ({ problem, setupNeeded });
|
|
89
|
+
if ((from.platform ?? process.platform) === "win32") return fail(WINDOWS_MESSAGE);
|
|
90
|
+
const home = from.home ?? homedir();
|
|
91
|
+
const models = await loadModelsConfig(home);
|
|
92
|
+
if (models.problem !== void 0) return fail(models.problem);
|
|
93
|
+
const spec = chooseModel(models.config, options.model, process.env.GARUDA_MODEL);
|
|
94
|
+
if (!spec) return fail(`Garuda has no model. ${SETUP_HINT}`, true);
|
|
72
95
|
let resolved;
|
|
73
96
|
let sub;
|
|
74
97
|
try {
|
|
@@ -76,25 +99,35 @@ async function prepare(options) {
|
|
|
76
99
|
const subSpec = process.env.GARUDA_SUBAGENT_MODEL;
|
|
77
100
|
if (subSpec) sub = resolveModel(subSpec, models.config);
|
|
78
101
|
} catch (error) {
|
|
79
|
-
return error.message;
|
|
102
|
+
return fail(error.message);
|
|
80
103
|
}
|
|
81
|
-
const search = await loadSearchConfig();
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
const search = await loadSearchConfig(home);
|
|
105
|
+
const credentialsWarning = await keepProviderKeys(
|
|
106
|
+
[resolved.def.apiKeyEnv, sub?.def.apiKeyEnv, "ANTHROPIC_API_KEY"],
|
|
107
|
+
home
|
|
108
|
+
);
|
|
109
|
+
if (credentialsWarning !== void 0) process.stderr.write(`garuda acp: ${credentialsWarning}
|
|
110
|
+
`);
|
|
111
|
+
const keyName = resolved.def.type === "anthropic" ? resolved.def.apiKeyEnv ?? "ANTHROPIC_API_KEY" : resolved.def.apiKeyEnv;
|
|
112
|
+
const sdkToken = resolved.def.type === "anthropic" && (process.env.ANTHROPIC_AUTH_TOKEN ?? "") !== "";
|
|
113
|
+
if (keyName !== void 0 && providerKeySource(keyName) === void 0 && !sdkToken) {
|
|
114
|
+
const why = credentialsWarning === void 0 ? "" : ` ${credentialsWarning}`;
|
|
115
|
+
return fail(`Garuda has no key for ${resolved.spec} (${keyName}).${why} ${SETUP_HINT}`, true);
|
|
84
116
|
}
|
|
85
117
|
const { loadTeamPolicy } = await import("./policy-EJPLEEXP.js");
|
|
86
118
|
let team;
|
|
87
119
|
try {
|
|
88
|
-
team = await loadTeamPolicy();
|
|
120
|
+
team = await loadTeamPolicy("managed" in from ? { home, managed: from.managed } : { home });
|
|
89
121
|
} catch (error) {
|
|
90
|
-
return `Team policy: ${error.message}
|
|
122
|
+
return fail(`Team policy: ${error.message}`);
|
|
91
123
|
}
|
|
92
124
|
return {
|
|
93
125
|
resolved,
|
|
94
126
|
...sub === void 0 ? {} : { sub },
|
|
95
127
|
models: models.config,
|
|
96
128
|
...team === void 0 ? {} : { team: { policy: team.policy, sources: team.sources } },
|
|
97
|
-
search
|
|
129
|
+
search,
|
|
130
|
+
...credentialsWarning === void 0 ? {} : { credentialsWarning }
|
|
98
131
|
};
|
|
99
132
|
}
|
|
100
133
|
function createRuntime(setup, input) {
|
|
@@ -144,10 +177,15 @@ function createRuntime(setup, input) {
|
|
|
144
177
|
},
|
|
145
178
|
...input.onEvent === void 0 ? {} : { onEvent: input.onEvent },
|
|
146
179
|
onNotice: input.onNotice
|
|
180
|
+
}).then((runtime) => {
|
|
181
|
+
if (setup.credentialsWarning !== void 0) input.onNotice(setup.credentialsWarning);
|
|
182
|
+
return runtime;
|
|
147
183
|
});
|
|
148
184
|
}
|
|
149
185
|
export {
|
|
186
|
+
WINDOWS_MESSAGE,
|
|
150
187
|
acpCommand,
|
|
188
|
+
prepare,
|
|
151
189
|
protocolOutput
|
|
152
190
|
};
|
|
153
|
-
//# sourceMappingURL=acpCommand-
|
|
191
|
+
//# sourceMappingURL=acpCommand-OTAYW7BS.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/model/types.ts
|
|
4
|
+
var EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
5
|
+
var ZERO_USAGE = {
|
|
6
|
+
inputTokens: 0,
|
|
7
|
+
outputTokens: 0,
|
|
8
|
+
cacheReadTokens: 0,
|
|
9
|
+
cacheWriteTokens: 0
|
|
10
|
+
};
|
|
11
|
+
function addUsage(a, b) {
|
|
12
|
+
const searches = (a.webSearches ?? 0) + (b.webSearches ?? 0);
|
|
13
|
+
return {
|
|
14
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
15
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
16
|
+
cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
|
|
17
|
+
cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens,
|
|
18
|
+
...searches === 0 ? {} : { webSearches: searches }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
EFFORTS,
|
|
24
|
+
ZERO_USAGE,
|
|
25
|
+
addUsage
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-3EQU2GUX.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
JobGitError,
|
|
4
4
|
git
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TUUEBHG2.js";
|
|
6
6
|
|
|
7
7
|
// src/jobs/worktree.ts
|
|
8
8
|
import { createHash } from "crypto";
|
|
@@ -135,4 +135,4 @@ export {
|
|
|
135
135
|
commitJob,
|
|
136
136
|
jobChanges
|
|
137
137
|
};
|
|
138
|
-
//# sourceMappingURL=chunk-
|
|
138
|
+
//# sourceMappingURL=chunk-44QN5SWZ.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-EU7PA45O.js";
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_ENV_ALLOWLIST
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GXZACJUI.js";
|
|
10
10
|
|
|
11
11
|
// src/lsp/install.ts
|
|
12
12
|
import { mkdir } from "fs/promises";
|
|
@@ -70,4 +70,4 @@ export {
|
|
|
70
70
|
installCommand,
|
|
71
71
|
installServer
|
|
72
72
|
};
|
|
73
|
-
//# sourceMappingURL=chunk-
|
|
73
|
+
//# sourceMappingURL=chunk-B6FA4I7Q.js.map
|
|
@@ -17,11 +17,11 @@ import {
|
|
|
17
17
|
repoMapText,
|
|
18
18
|
repoMapTool,
|
|
19
19
|
verifyAuditFile
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-TILFPYWO.js";
|
|
21
21
|
import {
|
|
22
22
|
WEB_SEARCH_USD,
|
|
23
23
|
totalTokens
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-R7I2NBPX.js";
|
|
25
25
|
import {
|
|
26
26
|
BUILTIN_COMMANDS
|
|
27
27
|
} from "./chunk-23FXBNBB.js";
|
|
@@ -489,7 +489,7 @@ async function jobsCommand(runtime, renderer, id) {
|
|
|
489
489
|
return;
|
|
490
490
|
}
|
|
491
491
|
const jobs = await listJobs2(runtime.root);
|
|
492
|
-
const { defaultAgentEnv, nightAgentTime } = await import("./launchd-
|
|
492
|
+
const { defaultAgentEnv, nightAgentTime } = await import("./launchd-SSX362VG.js");
|
|
493
493
|
const night = nightAgentTime(runtime.root, defaultAgentEnv());
|
|
494
494
|
const nightLine = night === void 0 ? [] : [
|
|
495
495
|
`Night shift: launchd starts the queue at ${night.toTimeString().slice(0, 5)} on ${night.toDateString()} (/jobs cancel night removes it).`
|
|
@@ -1244,4 +1244,4 @@ export {
|
|
|
1244
1244
|
statusOf,
|
|
1245
1245
|
CommandArgs
|
|
1246
1246
|
};
|
|
1247
|
-
//# sourceMappingURL=chunk-
|
|
1247
|
+
//# sourceMappingURL=chunk-BQTUOO4G.js.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/session/redact.ts
|
|
4
|
+
var REDACTED = "[REDACTED]";
|
|
5
|
+
var PATTERNS = [
|
|
6
|
+
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,
|
|
7
|
+
/sk-ant-[A-Za-z0-9_-]{16,}/g,
|
|
8
|
+
/\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}/g,
|
|
9
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
10
|
+
/\bgh[pousr]_[A-Za-z0-9]{30,}\b/g,
|
|
11
|
+
/\bgithub_pat_[A-Za-z0-9_]{30,}\b/g,
|
|
12
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
|
|
13
|
+
/\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
14
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
15
|
+
];
|
|
16
|
+
var ASSIGNMENT = /\b([A-Za-z0-9_]*(?:api[_-]?key|secret|token|password|passwd)[A-Za-z0-9_]*)(["']?\s*[:=]\s*["']?)([^\s"'`,;]{8,})/gi;
|
|
17
|
+
var OPAQUE_KEYS = /* @__PURE__ */ new Set(["encrypted_content", "encrypted_index", "signature"]);
|
|
18
|
+
function looksLikeCode(name, value, next) {
|
|
19
|
+
if (/^[\d_.]+$/.test(value)) return !/pass|secret/i.test(name);
|
|
20
|
+
if (value.includes("(") || next === "(") return true;
|
|
21
|
+
return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(value) && !/\d{4,}/.test(value);
|
|
22
|
+
}
|
|
23
|
+
var SECRET_ENV_NAME = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL)/i;
|
|
24
|
+
var movedSecrets = /* @__PURE__ */ new Map();
|
|
25
|
+
function keepSecretForRedaction(name, value) {
|
|
26
|
+
movedSecrets.set(name, value);
|
|
27
|
+
}
|
|
28
|
+
var Redactor = class {
|
|
29
|
+
literals;
|
|
30
|
+
/**
|
|
31
|
+
* `env`: variables whose names look secret have their values removed wherever they appear.
|
|
32
|
+
* Default: this process's environment and the secrets moved out of it.
|
|
33
|
+
*/
|
|
34
|
+
constructor(env = { ...process.env, ...Object.fromEntries(movedSecrets) }) {
|
|
35
|
+
this.literals = Object.entries(env).filter(
|
|
36
|
+
([name, value]) => (SECRET_ENV_NAME.test(name) || value !== void 0 && movedSecrets.get(name) === value) && (value?.length ?? 0) >= 8
|
|
37
|
+
).map(([, value]) => value).sort((a, b) => b.length - a.length);
|
|
38
|
+
}
|
|
39
|
+
text(input) {
|
|
40
|
+
let out = input;
|
|
41
|
+
for (const literal of this.literals) out = out.split(literal).join(REDACTED);
|
|
42
|
+
for (const pattern of PATTERNS) out = out.replace(pattern, REDACTED);
|
|
43
|
+
return out.replace(
|
|
44
|
+
ASSIGNMENT,
|
|
45
|
+
(all, name, sep, value, at) => looksLikeCode(name, value, out[at + all.length]) ? all : `${name}${sep}${REDACTED}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
/** A copy of `value` with every string redacted. */
|
|
49
|
+
value(value) {
|
|
50
|
+
return this.walk(value);
|
|
51
|
+
}
|
|
52
|
+
walk(value) {
|
|
53
|
+
if (typeof value === "string") return this.text(value);
|
|
54
|
+
if (Array.isArray(value)) return value.map((item) => this.walk(item));
|
|
55
|
+
if (value !== null && typeof value === "object") {
|
|
56
|
+
const redactedThinking = value.type === "redacted_thinking";
|
|
57
|
+
return Object.fromEntries(
|
|
58
|
+
Object.entries(value).map(([k, v]) => [
|
|
59
|
+
k,
|
|
60
|
+
(OPAQUE_KEYS.has(k) || redactedThinking && k === "data") && typeof v === "string" ? v : this.walk(v)
|
|
61
|
+
])
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/tools/atomicWrite.ts
|
|
69
|
+
import { randomBytes } from "crypto";
|
|
70
|
+
import { chmod, link, realpath, rename, stat, unlink, writeFile } from "fs/promises";
|
|
71
|
+
import { basename, dirname, join } from "path";
|
|
72
|
+
async function writeFileAtomic(path, content, { createOnly }) {
|
|
73
|
+
const target = createOnly ? path : await realpath(path);
|
|
74
|
+
const temp = join(
|
|
75
|
+
dirname(target),
|
|
76
|
+
`.${basename(target)}.garuda-${randomBytes(4).toString("hex")}.tmp`
|
|
77
|
+
);
|
|
78
|
+
const mode = createOnly ? void 0 : (await stat(target)).mode & 4095;
|
|
79
|
+
await writeFile(temp, content, { flag: "wx", ...mode === void 0 ? {} : { mode } });
|
|
80
|
+
try {
|
|
81
|
+
if (createOnly) {
|
|
82
|
+
try {
|
|
83
|
+
await link(temp, target);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const code = error.code;
|
|
86
|
+
if (code !== "EPERM" && code !== "ENOTSUP" && code !== "EXDEV") throw error;
|
|
87
|
+
if (await exists(target)) throw Object.assign(new Error("EEXIST"), { code: "EEXIST" });
|
|
88
|
+
await rename(temp, target);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
await chmod(temp, mode ?? 420);
|
|
93
|
+
await rename(temp, target);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
} finally {
|
|
97
|
+
await unlink(temp).catch(() => {
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function exists(path) {
|
|
102
|
+
try {
|
|
103
|
+
await stat(path);
|
|
104
|
+
return true;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
REDACTED,
|
|
112
|
+
keepSecretForRedaction,
|
|
113
|
+
Redactor,
|
|
114
|
+
writeFileAtomic
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=chunk-EIXUSVFX.js.map
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
realRelative
|
|
4
4
|
} from "./chunk-WMOLXTBU.js";
|
|
5
|
+
import {
|
|
6
|
+
EFFORTS
|
|
7
|
+
} from "./chunk-3EQU2GUX.js";
|
|
5
8
|
import {
|
|
6
9
|
isCommandDisallowedByPolicy,
|
|
7
10
|
isHostBlockedByPolicy,
|
|
@@ -86,6 +89,8 @@ var DENY_READ_IN_HOME = [
|
|
|
86
89
|
// Garuda's OAuth tokens for remote MCP servers (0.4). The rest of ~/.garuda stays readable:
|
|
87
90
|
// skills and their scripts live there (0.5).
|
|
88
91
|
".garuda/mcp-auth.json",
|
|
92
|
+
// Provider keys that `garuda setup` stored (0.16).
|
|
93
|
+
".garuda/credentials",
|
|
89
94
|
// Claude Code: its config can hold MCP server tokens in env values; its credentials on Linux.
|
|
90
95
|
".claude.json",
|
|
91
96
|
".claude/.credentials.json"
|
|
@@ -698,27 +703,6 @@ function onPath(program) {
|
|
|
698
703
|
import { readFile } from "fs/promises";
|
|
699
704
|
import { join as join3 } from "path";
|
|
700
705
|
import { z } from "zod";
|
|
701
|
-
|
|
702
|
-
// src/model/types.ts
|
|
703
|
-
var EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
704
|
-
var ZERO_USAGE = {
|
|
705
|
-
inputTokens: 0,
|
|
706
|
-
outputTokens: 0,
|
|
707
|
-
cacheReadTokens: 0,
|
|
708
|
-
cacheWriteTokens: 0
|
|
709
|
-
};
|
|
710
|
-
function addUsage(a, b) {
|
|
711
|
-
const searches = (a.webSearches ?? 0) + (b.webSearches ?? 0);
|
|
712
|
-
return {
|
|
713
|
-
inputTokens: a.inputTokens + b.inputTokens,
|
|
714
|
-
outputTokens: a.outputTokens + b.outputTokens,
|
|
715
|
-
cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
|
|
716
|
-
cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens,
|
|
717
|
-
...searches === 0 ? {} : { webSearches: searches }
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// src/permissions/settings.ts
|
|
722
706
|
var SETTINGS_FILE = join3(".garuda", "settings.json");
|
|
723
707
|
var NOTIFY_CHOICES = ["auto", "osc9", "bell", "off"];
|
|
724
708
|
var schema = z.strictObject({
|
|
@@ -1320,9 +1304,6 @@ function describeTarget(target) {
|
|
|
1320
1304
|
}
|
|
1321
1305
|
|
|
1322
1306
|
export {
|
|
1323
|
-
EFFORTS,
|
|
1324
|
-
ZERO_USAGE,
|
|
1325
|
-
addUsage,
|
|
1326
1307
|
isSensitive,
|
|
1327
1308
|
NETWORK_PORTS,
|
|
1328
1309
|
expandAllowlist,
|
|
@@ -1338,4 +1319,4 @@ export {
|
|
|
1338
1319
|
JOB_DENIAL,
|
|
1339
1320
|
PermissionEngine
|
|
1340
1321
|
};
|
|
1341
|
-
//# sourceMappingURL=chunk-
|
|
1322
|
+
//# sourceMappingURL=chunk-GXZACJUI.js.map
|
|
@@ -4,9 +4,11 @@ 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
|
totalTokens
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-R7I2NBPX.js";
|
|
10
12
|
import {
|
|
11
13
|
TrustStore
|
|
12
14
|
} from "./chunk-5DEWP3YX.js";
|
|
@@ -15,11 +17,11 @@ import {
|
|
|
15
17
|
commitJob,
|
|
16
18
|
jobChanges,
|
|
17
19
|
prepareWorktree
|
|
18
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-44QN5SWZ.js";
|
|
19
21
|
import {
|
|
20
22
|
defaultAgentEnv,
|
|
21
23
|
removeAgent
|
|
22
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-USCUXCAU.js";
|
|
23
25
|
import {
|
|
24
26
|
jobReport
|
|
25
27
|
} from "./chunk-YQTGMCU6.js";
|
|
@@ -36,11 +38,11 @@ import {
|
|
|
36
38
|
JobGitError,
|
|
37
39
|
git,
|
|
38
40
|
hostCommand
|
|
39
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-TUUEBHG2.js";
|
|
40
42
|
import {
|
|
41
43
|
createExecutor,
|
|
42
44
|
loadSettings
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-GXZACJUI.js";
|
|
44
46
|
import {
|
|
45
47
|
JOBS_DIR,
|
|
46
48
|
loadJob,
|
|
@@ -317,4 +319,4 @@ export {
|
|
|
317
319
|
msUntil,
|
|
318
320
|
finishJob
|
|
319
321
|
};
|
|
320
|
-
//# sourceMappingURL=chunk-
|
|
322
|
+
//# sourceMappingURL=chunk-HE2K657U.js.map
|