@garuda-agent/garuda 0.14.1 → 0.16.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +78 -8
  3. package/dist/acpCommand-AJJBKAE5.js +191 -0
  4. package/dist/chunk-23FXBNBB.js +46 -0
  5. package/dist/chunk-3EQU2GUX.js +27 -0
  6. package/dist/{chunk-YIZWYUBG.js → chunk-44QN5SWZ.js} +2 -2
  7. package/dist/chunk-4JSUUDFQ.js +118 -0
  8. package/dist/{chunk-KWRJREUH.js → chunk-B25VL4RU.js} +1 -42
  9. package/dist/{chunk-U4OQVT2T.js → chunk-B6FA4I7Q.js} +2 -2
  10. package/dist/chunk-EIXUSVFX.js +116 -0
  11. package/dist/{chunk-YM6T4J5V.js → chunk-ENLDPN4S.js} +8 -111
  12. package/dist/{chunk-D5O5CIWK.js → chunk-GXZACJUI.js} +8 -25
  13. package/dist/{chunk-QOW5TK4L.js → chunk-HE2K657U.js} +9 -7
  14. package/dist/chunk-NP6RIC25.js +331 -0
  15. package/dist/chunk-NXJ4XLB5.js +19 -0
  16. package/dist/chunk-R7I2NBPX.js +71 -0
  17. package/dist/{chunk-ZGZ2ZADL.js → chunk-TUUEBHG2.js} +2 -2
  18. package/dist/{chunk-W7BPYQMW.js → chunk-ULAJPMY7.js} +1 -67
  19. package/dist/{chunk-35GMLLJW.js → chunk-USCUXCAU.js} +2 -2
  20. package/dist/{chunk-ITITLNAR.js → chunk-WIJL72ZE.js} +150 -234
  21. package/dist/chunk-XOK5OZOC.js +9 -0
  22. package/dist/cli/index.js +68 -39
  23. package/dist/{create-Y5SXJUXO.js → create-VXIH2GUZ.js} +6 -5
  24. package/dist/{evalCommand-5TG2CKZY.js → evalCommand-OX2LQYEK.js} +35 -18
  25. package/dist/{inkChat-3J26R24L.js → inkChat-YMMMGU7Y.js} +16 -10
  26. package/dist/{install-XWLU22OH.js → install-EWDSPFFZ.js} +4 -3
  27. package/dist/{jobCommand-IYWZWTSI.js → jobCommand-ACSGSBQ4.js} +9 -7
  28. package/dist/{launchd-2FL5UN2U.js → launchd-SSX362VG.js} +5 -4
  29. package/dist/{lspCommand-TJEZBV2E.js → lspCommand-JRXKRSL7.js} +4 -3
  30. package/dist/{manager-EBTYO2GR.js → manager-AWU4NHHK.js} +4 -3
  31. package/dist/{nightCommand-DUNWJASI.js → nightCommand-65MUEV32.js} +9 -7
  32. package/dist/{remove-AENULZZE.js → remove-6GDSTAIO.js} +4 -3
  33. package/dist/{run-66Y6R2KU.js → run-ML6GSPN4.js} +5 -3
  34. package/dist/server-KHHNHUYV.js +694 -0
  35. package/dist/setupCommand-VA7D5MZK.js +303 -0
  36. package/package.json +2 -1
  37. package/dist/chunk-6TWBYMY7.js +0 -9
  38. package/dist/chunk-V7WPGQLB.js +0 -207
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-EU7PA45O.js";
7
7
  import {
8
8
  DEFAULT_ENV_ALLOWLIST
9
- } from "./chunk-D5O5CIWK.js";
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-U4OQVT2T.js.map
73
+ //# sourceMappingURL=chunk-B6FA4I7Q.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
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ colorDiff
4
+ } from "./chunk-4JSUUDFQ.js";
2
5
  import {
3
6
  SPECIALIST_SPECS,
4
7
  THINKING_WORDS,
@@ -14,14 +17,14 @@ import {
14
17
  repoMapText,
15
18
  repoMapTool,
16
19
  verifyAuditFile
17
- } from "./chunk-ITITLNAR.js";
20
+ } from "./chunk-WIJL72ZE.js";
18
21
  import {
19
22
  WEB_SEARCH_USD,
20
23
  totalTokens
21
- } from "./chunk-W7BPYQMW.js";
24
+ } from "./chunk-R7I2NBPX.js";
22
25
  import {
23
26
  BUILTIN_COMMANDS
24
- } from "./chunk-KWRJREUH.js";
27
+ } from "./chunk-23FXBNBB.js";
25
28
  import {
26
29
  LSP_LANGUAGES
27
30
  } from "./chunk-EU7PA45O.js";
@@ -29,108 +32,6 @@ import {
29
32
  listJobs
30
33
  } from "./chunk-KKB5W47S.js";
31
34
 
32
- // src/cli/approver.ts
33
- import { styleText } from "util";
34
- var TerminalApprover = class {
35
- /** Set by the CLI: it aborts the current turn. */
36
- onInterrupt = () => {
37
- };
38
- /** One question at a time (0.14.1, review): two prompts at once mixed their keys. */
39
- ask(request, signal) {
40
- const next = this.asking === 0 ? this.askNow(request, signal) : this.queue.then(() => this.askNow(request, signal));
41
- this.asking++;
42
- this.queue = next.catch(() => {
43
- }).finally(() => {
44
- this.asking--;
45
- });
46
- return next;
47
- }
48
- asking = 0;
49
- queue = Promise.resolve();
50
- async askNow(request, signal) {
51
- signal.throwIfAborted();
52
- const out = process.stderr;
53
- out.write(`
54
- ${header(request)}
55
- ${colorPreview(request)}
56
- `);
57
- if (!process.stdin.isTTY) {
58
- out.write("No terminal to ask for approval, so Garuda denies this call.\n");
59
- return "deny";
60
- }
61
- const sessionLabel = request.labels?.session ?? (request.target.kind === "command" ? "Yes, and allow this exact command for this session" : request.target.kind === "url" ? `Yes, and allow ${request.target.host} for this session` : `Yes, and allow all ${request.tool} calls for this session`);
62
- const { select } = await import("@inquirer/prompts");
63
- try {
64
- return await select(
65
- {
66
- message: request.question ?? "Allow?",
67
- choices: [
68
- { name: request.labels?.once ?? "Yes, once", value: "once" },
69
- { name: sessionLabel, value: "session" },
70
- { name: request.labels?.deny ?? "No, deny", value: "deny" }
71
- ].filter((c) => request.choices?.includes(c.value) ?? true)
72
- },
73
- { signal, output: out }
74
- );
75
- } catch (error) {
76
- if (error.name === "ExitPromptError") this.onInterrupt();
77
- throw error;
78
- }
79
- }
80
- };
81
- var SwitchApprover = class {
82
- constructor(current) {
83
- this.current = current;
84
- }
85
- current;
86
- /** Called before each question: the chat's notifier (0.6). */
87
- onAsk = () => {
88
- };
89
- ask(request, signal) {
90
- this.onAsk(request);
91
- return this.current.ask(request, signal);
92
- }
93
- };
94
- function visible(text) {
95
- return text.replace(/[\u0000-\u0008\u000b-\u001f]/g, (c) => String.fromCodePoint(9216 + c.charCodeAt(0))).replace(/\u007f/g, "\u2421").replace(
96
- /[\u0080-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff\u{e0000}-\u{e007f}]/gu,
97
- (c) => `[U+${(c.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}]`
98
- );
99
- }
100
- function hasHidden(text) {
101
- return visible(text) !== text;
102
- }
103
- function header(request) {
104
- const { tool, target, isolation } = request;
105
- const title = request.title === void 0 ? void 0 : visible(request.title);
106
- if (title !== void 0) return styleText("bold", title);
107
- if (target.kind === "command") {
108
- const where = isolation === "none" ? "on your machine, no sandbox" : target.outsideSandbox ? "OUTSIDE the sandbox: network on, writes anywhere" : `sandbox: ${isolation}`;
109
- return styleText("bold", `${tool} wants to run a command (${where}):`);
110
- }
111
- if (target.kind === "input") return styleText("bold", `${tool} wants to run with this input:`);
112
- if (target.kind === "url")
113
- return styleText("bold", `${tool} wants to fetch from ${visible(target.host)}:`);
114
- return styleText("bold", `${tool} wants to change ${visible(target.path)}:`);
115
- }
116
- function colorPreview({ target, preview }) {
117
- const warning = hasHidden(preview) ? `
118
- ${styleText("yellow", " ! This holds hidden or control characters, shown as \u240D, \u241B or [U+\u2026]. Read it with care.")}` : "";
119
- if (target.kind === "command") return `${styleText("cyan", ` $ ${visible(preview)}`)}${warning}`;
120
- if (target.kind === "url") return `${visible(preview)}${warning}`;
121
- return `${colorDiff(visible(preview))}${warning}`;
122
- }
123
- function colorDiff(text) {
124
- return text.split("\n").map((line) => {
125
- if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("diff --git"))
126
- return styleText("bold", line);
127
- if (line.startsWith("+")) return styleText("green", line);
128
- if (line.startsWith("-")) return styleText("red", line);
129
- if (line.startsWith("@@")) return styleText("cyan", line);
130
- return line;
131
- }).join("\n");
132
- }
133
-
134
35
  // src/cli/errors.ts
135
36
  function describeError(error) {
136
37
  const parts = [];
@@ -588,7 +489,7 @@ async function jobsCommand(runtime, renderer, id) {
588
489
  return;
589
490
  }
590
491
  const jobs = await listJobs2(runtime.root);
591
- const { defaultAgentEnv, nightAgentTime } = await import("./launchd-2FL5UN2U.js");
492
+ const { defaultAgentEnv, nightAgentTime } = await import("./launchd-SSX362VG.js");
592
493
  const night = nightAgentTime(runtime.root, defaultAgentEnv());
593
494
  const nightLine = night === void 0 ? [] : [
594
495
  `Night shift: launchd starts the queue at ${night.toTimeString().slice(0, 5)} on ${night.toDateString()} (/jobs cancel night removes it).`
@@ -1322,10 +1223,6 @@ var CommandArgs = class {
1322
1223
  };
1323
1224
 
1324
1225
  export {
1325
- TerminalApprover,
1326
- SwitchApprover,
1327
- header,
1328
- colorPreview,
1329
1226
  describeError,
1330
1227
  FOCUS_REPORTING_ON,
1331
1228
  FOCUS_REPORTING_OFF,
@@ -1347,4 +1244,4 @@ export {
1347
1244
  statusOf,
1348
1245
  CommandArgs
1349
1246
  };
1350
- //# sourceMappingURL=chunk-YM6T4J5V.js.map
1247
+ //# sourceMappingURL=chunk-ENLDPN4S.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({
@@ -1155,6 +1139,8 @@ var PermissionEngine = class {
1155
1139
  target: asked,
1156
1140
  preview: info?.preview ?? describeTarget(asked),
1157
1141
  isolation: this.isolation,
1142
+ ...request.callId === void 0 ? {} : { callId: request.callId },
1143
+ ...info?.change === void 0 ? {} : { change: info.change },
1158
1144
  ...info?.title === void 0 ? {} : { title: info.title },
1159
1145
  ...info?.hunks === true ? {
1160
1146
  selectHunks: (hunks) => {
@@ -1318,9 +1304,6 @@ function describeTarget(target) {
1318
1304
  }
1319
1305
 
1320
1306
  export {
1321
- EFFORTS,
1322
- ZERO_USAGE,
1323
- addUsage,
1324
1307
  isSensitive,
1325
1308
  NETWORK_PORTS,
1326
1309
  expandAllowlist,
@@ -1336,4 +1319,4 @@ export {
1336
1319
  JOB_DENIAL,
1337
1320
  PermissionEngine
1338
1321
  };
1339
- //# sourceMappingURL=chunk-D5O5CIWK.js.map
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-W7BPYQMW.js";
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-YIZWYUBG.js";
20
+ } from "./chunk-44QN5SWZ.js";
19
21
  import {
20
22
  defaultAgentEnv,
21
23
  removeAgent
22
- } from "./chunk-35GMLLJW.js";
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-ZGZ2ZADL.js";
41
+ } from "./chunk-TUUEBHG2.js";
40
42
  import {
41
43
  createExecutor,
42
44
  loadSettings
43
- } from "./chunk-D5O5CIWK.js";
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-QOW5TK4L.js.map
322
+ //# sourceMappingURL=chunk-HE2K657U.js.map