Build and deploy apps through your company's DKOD: policy, templates, Deploy questions and Deliver, with a DKOD band above the prompt and the DKOD rules on…

Build and deploy apps through your company's DKOD from Claude Code.
It adds three things:
dkod: your org's policy, templates, Deploy questions, Deploy, build answers, Deliver and Remove.dkod: tells Claude how to use them, in the right order./plugin marketplace add dkod-ai/dkod-plugin
/plugin install dkod@dkod-ai
Then run /mcp, pick dkod and choose Authenticate. You sign in with your company's own sign-in. DKOD never sees a password. Until you sign in, the band above the prompt is yellow.
| Dkoder | Guard | |
|---|---|---|
| What | This plugin, in your agent | Your organization's policy, as rules |
| Rules | The floor, the same for every organization | Your organization's own, from its governance repository |
| Needs | Nothing. Works signed out; the band asks you to sign in | Sign-in through /mcp, or your company's device install |
The rules need to know your org: which GitHub owners are yours, which repository is the governance repository, and the org's own blocked commands. Dkoder gets those facts from DKOD when you sign in through /mcp, if your organization turned Guard on. Your company can also install Dkoder on its devices from the DKOD dashboard; that copy cannot be turned off. Then, in the org's repositories, the floor refuses:
git push, gh pr merge and GitHub API writes to an app repository. App code ships through Deliver.--no-verify, core.hooksPath).Guard adds your organization's own rules on top, such as blocked commands. A refusal says which one refused: the Dkoder floor, or a Guard rule of your organization.
Before you sign in, nothing names your org, so a repository with a remote is left alone. A folder with no remote still gets the secret check. This plugin sends no events and needs no install file.
The rules keep Claude on the Deliver path. They are not a security boundary against a person.
To stop direct pushes for real, protect the app repository on GitHub (branch protection or rulesets). DKOD's Deliver does not need a person to push.
This repository is generated from DKOD's source. Do not edit it here: changes are overwritten by the next release.
MIT
hooks/register.js 2529 lines1// Built by packages/dkoder-plugin/scripts/build-claude.ts from dkod-app. Do not edit: run `bun run --cwd packages/dkoder-plugin build:claude`.
2var MAX_DEPTH = 4;
3var MAX_LINE = 64 * 1024;
4var OPERATORS = ["&&", "||", ";;", "|&", ";", "|", "&", "(", ")", `
5`];
6function tokenize(line, subs) {
7 const out = [];
8 let word = "";
9 let inWord = false;
10 const flush = () => {
11 if (inWord)
12 out.push({ kind: "word", text: word });
13 word = "";
14 inWord = false;
15 };
16 let i = 0;
17 const n = Math.min(line.length, MAX_LINE);
18 const readParen = (start) => {
19 let depth = 1;
20 let j = start;
21 while (j < n && depth > 0) {
22 const c = line[j];
23 if (c === "\\") {
24 j += 2;
25 continue;
26 }
27 if (c === "'") {
28 const end = line.indexOf("'", j + 1);
29 j = end < 0 ? n : end + 1;
30 continue;
31 }
32 if (c === '"') {
33 j += 1;
34 while (j < n && line[j] !== '"')
35 j += line[j] === "\\" ? 2 : 1;
36 j += 1;
37 continue;
38 }
39 if (c === "(")
40 depth += 1;
41 else if (c === ")")
42 depth -= 1;
43 j += 1;
44 }
45 return j;
46 };
47 const readBacktick = (start) => {
48 let j = start;
49 while (j < n && line[j] !== "`")
50 j += line[j] === "\\" ? 2 : 1;
51 return j;
52 };
53 while (i < n) {
54 const c = line[i];
55 if (c === "\\") {
56 if (line[i + 1] === `
57`) {
58 i += 2;
59 continue;
60 }
61 word += line[i + 1] ?? "";
62 inWord = true;
63 i += 2;
64 continue;
65 }
66 if (c === "'") {
67 const end = line.indexOf("'", i + 1);
68 const stop = end < 0 ? n : end;
69 word += line.slice(i + 1, stop);
70 inWord = true;
71 i = stop + 1;
72 continue;
73 }
74 if (c === "$" && line[i + 1] === "'") {
75 let j = i + 2;
76 while (j < n && line[j] !== "'") {
77 if (line[j] === "\\" && j + 1 < n) {
78 const e = line[j + 1];
79 word += e === "n" ? `
80` : e === "t" ? "\t" : e;
81 j += 2;
82 } else {
83 word += line[j];
84 j += 1;
85 }
86 }
87 inWord = true;
88 i = j + 1;
89 continue;
90 }
91 if (c === '"') {
92 let j = i + 1;
93 while (j < n && line[j] !== '"') {
94 const d = line[j];
95 if (d === "\\" && j + 1 < n && '"\\$`\n'.includes(line[j + 1])) {
96 if (line[j + 1] !== `
97`)
98 word += line[j + 1];
99 j += 2;
100 continue;
101 }
102 if (d === "$" && line[j + 1] === "(") {
103 const end = readParen(j + 2);
104 subs.push(line.slice(j + 2, end - 1));
105 word += line.slice(j, end);
106 j = end;
107 continue;
108 }
109 if (d === "`") {
110 const end = readBacktick(j + 1);
111 subs.push(line.slice(j + 1, end));
112 word += line.slice(j, end + 1);
113 j = end + 1;
114 continue;
115 }
116 word += d;
117 j += 1;
118 }
119 inWord = true;
120 i = j + 1;
121 continue;
122 }
123 if (c === "$" && line[i + 1] === "(") {
124 const end = readParen(i + 2);
125 subs.push(line.slice(i + 2, end - 1));
126 word += line.slice(i, end);
127 inWord = true;
128 i = end;
129 continue;
130 }
131 if (c === "`") {
132 const end = readBacktick(i + 1);
133 subs.push(line.slice(i + 1, end));
134 word += line.slice(i, end + 1);
135 inWord = true;
136 i = end + 1;
137 continue;
138 }
139 if (c === "#" && !inWord) {
140 const end = line.indexOf(`
141`, i);
142 i = end < 0 ? n : end;
143 continue;
144 }
145 if (c === " " || c === "\t" || c === "\r") {
146 flush();
147 i += 1;
148 continue;
149 }
150 const op = OPERATORS.find((o) => line.startsWith(o, i));
151 if (op !== undefined) {
152 flush();
153 out.push({ kind: "op", text: op });
154 i += op.length;
155 continue;
156 }
157 if (c === ">" || c === "<") {
158 if (inWord && /^\d+$/.test(word)) {
159 word = "";
160 inWord = false;
161 }
162 flush();
163 if (line.startsWith("<<", i) && line[i + 2] !== "<") {
164 const m = /^<<(-?)[ \t]*(?:'([^'\n]*)'|"([^"\n]*)"|\\?([A-Za-z0-9_.-]+))/.exec(line.slice(i, i + 200));
165 if (m) {
166 const delim = m[2] ?? m[3] ?? m[4] ?? "";
167 const nl = line.indexOf(`
168`, i);
169 let bodyEnd = n;
170 if (nl >= 0) {
171 let k = nl + 1;
172 while (k < n) {
173 const e = line.indexOf(`
174`, k);
175 const stop = e < 0 ? n : e;
176 const text = m[1] === "-" ? line.slice(k, stop).replace(/^\t+/, "") : line.slice(k, stop);
177 if (text === delim) {
178 bodyEnd = stop;
179 break;
180 }
181 k = stop + 1;
182 }
183 const rest = line.slice(i + m[0].length, nl);
184 out.push(...tokenize(rest, subs));
185 out.push({ kind: "op", text: `
186` });
187 }
188 i = bodyEnd;
189 continue;
190 }
191 }
192 let j = i;
193 while (j < n && (line[j] === ">" || line[j] === "<" || line[j] === "&" || line[j] === "|" || line[j] === "-"))
194 j += 1;
195 const op = line.slice(i, j);
196 out.push({ kind: "op", text: "redirect", write: op.includes(">") && !op.endsWith("&") });
197 i = j;
198 continue;
199 }
200 word += c;
201 inWord = true;
202 i += 1;
203 }
204 flush();
205 return out;
206}
207var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
208function programName(word) {
209 const slash = word.lastIndexOf("/");
210 return slash < 0 ? word : word.slice(slash + 1);
211}
212function joinCd(current, target) {
213 if (target.startsWith("/") || target.startsWith("~"))
214 return target;
215 if (current === "" || current === ".")
216 return target;
217 return `${current.replace(/\/$/, "")}/${target}`;
218}
219var KEYWORDS = new Set(["!", "if", "elif", "then", "else", "while", "until", "do", "time", "coproc"]);
220var SHELLS = new Set(["bash", "sh", "zsh", "dash", "ksh"]);
221function unwrap(argv, env) {
222 let a = argv;
223 for (let guard = 0;guard < 8 && a.length > 0; guard += 1) {
224 const prog = programName(a[0]);
225 if (prog === "env") {
226 let i = 1;
227 while (i < a.length && (a[i].startsWith("-") || ASSIGNMENT.test(a[i]))) {
228 if (ASSIGNMENT.test(a[i]))
229 env = [...env, a[i]];
230 if (a[i] === "-u" || a[i] === "-C" || a[i] === "-S")
231 i += 1;
232 i += 1;
233 }
234 a = a.slice(i);
235 continue;
236 }
237 if (prog === "command" || prog === "builtin" || prog === "exec" || prog === "nohup" || prog === "time" || prog === "nice") {
238 let i = 1;
239 while (i < a.length && a[i].startsWith("-"))
240 i += a[i] === "-n" && prog === "nice" ? 2 : 1;
241 a = a.slice(i);
242 continue;
243 }
244 if (prog === "sudo" || prog === "doas") {
245 let i = 1;
246 while (i < a.length && a[i].startsWith("-"))
247 i += ["-u", "-g", "-C", "-D", "-h", "-p", "-U"].includes(a[i]) ? 2 : 1;
248 a = a.slice(i);
249 continue;
250 }
251 if (prog === "timeout") {
252 let i = 1;
253 while (i < a.length && a[i].startsWith("-"))
254 i += a[i] === "-s" || a[i] === "-k" ? 2 : 1;
255 a = a.slice(i + 1);
256 continue;
257 }
258 if (prog === "xargs") {
259 let i = 1;
260 while (i < a.length && a[i].startsWith("-"))
261 i += ["-I", "-n", "-P", "-L", "-s", "-d", "-E"].includes(a[i]) ? 2 : 1;
262 a = a.slice(i);
263 continue;
264 }
265 if (SHELLS.has(prog)) {
266 const c = a.findIndex((w, i) => i > 0 && /^-[a-z]*c[a-z]*$/.test(w));
267 if (c > 0 && a[c + 1] !== undefined)
268 return { argv: a, env, script: a[c + 1] };
269 return { argv: a, env, script: null };
270 }
271 if (prog === "eval")
272 return { argv: a, env, script: a.slice(1).join(" ") };
273 break;
274 }
275 return { argv: a, env, script: null };
276}
277function parseCommands(line, depth = 0) {
278 if (depth > MAX_DEPTH)
279 return [];
280 const subs = [];
281 const tokens = tokenize(line, subs);
282 const out = [];
283 let cd = "";
284 let words = [];
285 let skipNext = false;
286 let writeNext = false;
287 let writes = [];
288 const end = () => {
289 const targets = writes;
290 writes = [];
291 if (words.length === 0)
292 return;
293 let i = 0;
294 const env = [];
295 while (i < words.length && ASSIGNMENT.test(words[i]))
296 env.push(words[i++]);
297 const raw = words.slice(i);
298 words = [];
299 if (raw.length === 0)
300 return;
301 const { argv, env: allEnv, script } = unwrap(raw, env);
302 if (argv.length === 0)
303 return;
304 if (programName(argv[0]) === "cd" || programName(argv[0]) === "pushd") {
305 const target = argv.slice(1).find((w) => !w.startsWith("-"));
306 cd = target === undefined ? "~" : joinCd(cd, target);
307 return;
308 }
309 out.push(targets.length > 0 ? { argv, env: allEnv, cd, writes: targets } : { argv, env: allEnv, cd });
310 if (script !== null) {
311 for (const inner of parseCommands(script, depth + 1))
312 out.push({ ...inner, cd: inner.cd === "" ? cd : joinCd(cd, inner.cd) });
313 }
314 };
315 for (const t of tokens) {
316 if (t.kind === "op") {
317 if (t.text === "redirect") {
318 skipNext = true;
319 writeNext = t.write === true;
320 continue;
321 }
322 end();
323 continue;
324 }
325 if (skipNext) {
326 skipNext = false;
327 if (writeNext)
328 writes.push(t.text);
329 continue;
330 }
331 if (t.text === "{" || t.text === "}") {
332 end();
333 continue;
334 }
335 if (words.length === 0 && KEYWORDS.has(t.text))
336 continue;
337 words.push(t.text);
338 }
339 end();
340 for (const body of subs)
341 for (const inner of parseCommands(body, depth + 1))
342 out.push(inner);
343 return out;
344}
345
346var join = (a, b) => b === "" ? a : a === "" ? b : b.startsWith("/") || b.startsWith("~") ? b : `${a.replace(/\/$/, "")}/${b}`;
347var GIT_GLOBAL_VALUE = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--exec-path", "--config-env", "--super-prefix", "--list-cmds", "--attr-source"]);
348var GIT_GLOBAL_HARMLESS = new Set(["-C", "-P", "--no-pager", "--paginate", "-p", "--no-optional-locks", "--literal-pathspecs", "--glob-pathspecs", "--noglob-pathspecs", "--icase-pathspecs", "--no-replace-objects", "--no-lazy-fetch", "--no-advice", "--bare"]);
349var PUSH_VALUE = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo", "--signed"]);
350var COMMIT_VALUE = new Set(["-m", "--message", "-F", "--file", "-C", "--reuse-message", "-c", "--reedit-message", "--author", "--date", "-t", "--template", "--fixup", "--squash", "--cleanup", "--trailer", "--pathspec-from-file"]);
351var GIT_BUILTINS = new Set("add am annotate apply archive bisect blame branch bundle cat-file check-attr check-ignore checkout cherry cherry-pick citool clean clone column commit commit-graph commit-tree config count-objects credential describe diff diff-files diff-index diff-tree difftool fetch for-each-ref format-patch fsck gc grep hash-object help init interpret-trailers log ls-files ls-remote ls-tree maintenance merge merge-base merge-file merge-tree mergetool mktag mktree mv name-rev notes pack-objects prune pull push range-diff read-tree rebase reflog remote repack replace request-pull rerere reset restore rev-list rev-parse revert rm send-pack shortlog show show-branch show-ref sparse-checkout stash status submodule switch symbolic-ref tag update-index update-ref var verify-commit verify-pack verify-tag version whatchanged worktree write-tree lfs".split(" "));
352var GH_GROUPS = new Set("alias api attestation auth browse cache co codespace completion config copilot extension gist gpg-key help issue label org pr preview project release repo ruleset run search secret ssh-key status variable workflow".split(" "));
353var HOOKS_PATH = /hookspath/i;
354var HARMLESS_GIT_ENV = /^GIT_(SSH_COMMAND|SSH|TERMINAL_PROMPT|ASKPASS|PAGER|EDITOR|SEQUENCE_EDITOR|TRACE[A-Z_]*|AUTHOR_[A-Z]+|COMMITTER_[A-Z]+|PROGRESS_DELAY|REDACT_COOKIES|CURL_VERBOSE|HTTP_LOW_SPEED_[A-Z]+|OPTIONAL_LOCKS|FLUSH)=/;
355var RISKY_ALIAS = /(^|\s)(push|send-pack|merge|api)(\s|$)|^!/;
356function pushAction(cmd, dir, rest, opaque, out) {
357 let force = false;
358 let remote = null;
359 let sawRemote = false;
360 for (let j = 0;j < rest.length; j += 1) {
361 const w = rest[j];
362 if (w === "--")
363 continue;
364 if (w.startsWith("--repo=")) {
365 remote = w.slice("--repo=".length);
366 sawRemote = true;
367 continue;
368 }
369 if (w === "--repo") {
370 remote = rest[j + 1] ?? null;
371 sawRemote = true;
372 j += 1;
373 continue;
374 }
375 if (w === "--no-verify") {
376 out.push({ kind: "hook-bypass", cmd, dir, how: "git push --no-verify" });
377 continue;
378 }
379 if (w === "--force" || w.startsWith("--force-with-lease") || w === "--force-if-includes" || w === "--mirror" || w === "--delete") {
380 force = true;
381 continue;
382 }
383 if (w.startsWith("--receive-pack") || w.startsWith("--exec"))
384 opaque = opaque ?? "a custom receive-pack";
385 if (/^-[A-Za-z]+$/.test(w)) {
386 if (w.includes("f") || w.includes("d"))
387 force = true;
388 if (PUSH_VALUE.has(w))
389 j += 1;
390 continue;
391 }
392 if (w.startsWith("-")) {
393 if (PUSH_VALUE.has(w))
394 j += 1;
395 continue;
396 }
397 if (!sawRemote) {
398 remote = w;
399 sawRemote = true;
400 continue;
401 }
402 if (w.startsWith("+") || w.startsWith(":"))
403 force = true;
404 }
405 out.push({ kind: "push", cmd, dir, remote, force, opaque });
406}
407var remoteKey = (dir, name) => `${dir.replace(/^\.\/?/, "").replace(/\/+$/, "")}\x00${name}`;
408var CLONE_VALUE = new Set(["-b", "--branch", "-o", "--origin", "--depth", "-c", "--config", "--reference", "--reference-if-able", "--template", "-u", "--upload-pack", "--separate-git-dir", "--filter", "-j", "--jobs", "--shallow-since", "--shallow-exclude", "--bundle-uri"]);
409function gitActions(cmd, depth, made = new Map) {
410 const out = [];
411 let dir = cmd.cd;
412 const direct = programName(cmd.argv[0] ?? "").slice("git-".length);
413 const a = direct === "" ? cmd.argv : ["git", direct, ...cmd.argv.slice(1)];
414 let opaque = null;
415 for (const e of cmd.env) {
416 if (/^GIT_CONFIG/.test(e) && HOOKS_PATH.test(e))
417 out.push({ kind: "hook-bypass", cmd, dir, how: "a hooks path set in the environment" });
418 if (/^GIT_/.test(e) && !HARMLESS_GIT_ENV.test(e))
419 opaque = opaque ?? `${e.split("=")[0]} in the environment`;
420 }
421 const local = new Map;
422 const prefix = ["git"];
423 let i = 1;
424 while (i < a.length && a[i].startsWith("-")) {
425 const opt = a[i];
426 const eq = opt.indexOf("=");
427 const name = eq < 0 ? opt : opt.slice(0, eq);
428 const value = eq < 0 ? a[i + 1] : opt.slice(eq + 1);
429 const width = GIT_GLOBAL_VALUE.has(name) && eq < 0 ? 2 : 1;
430 if (name === "-C" && value !== undefined)
431 dir = join(dir, value);
432 else
433 prefix.push(...a.slice(i, i + width));
434 if ((name === "-c" || name === "--config-env") && value !== undefined) {
435 if (HOOKS_PATH.test(value))
436 out.push({ kind: "hook-bypass", cmd, dir, how: "git -c core.hooksPath" });
437 const alias = /^alias\.([^=]+)=(.*)$/s.exec(value);
438 if (alias)
439 local.set(alias[1].toLowerCase(), alias[2]);
440 }
441 if (!GIT_GLOBAL_HARMLESS.has(name))
442 opaque = opaque ?? `git ${name}`;
443 i += width;
444 }
445 const sub = a[i];
446 const rest = a.slice(i + 1);
447 if (sub === undefined)
448 return out;
449 const inline = local.get(sub.toLowerCase());
450 if (inline !== undefined && !GIT_BUILTINS.has(sub)) {
451 out.push(...expandAlias("git", inline, prefix, rest, { ...cmd, cd: dir }, depth));
452 return out;
453 }
454 if (sub === "clone") {
455 let origin = "origin";
456 const pos = [];
457 for (let j = 0;j < rest.length; j += 1) {
458 const w = rest[j];
459 const eq = w.indexOf("=");
460 if (w === "-o" || w === "--origin")
461 origin = rest[j + 1] ?? origin;
462 else if (w.startsWith("--origin="))
463 origin = w.slice(eq + 1);
464 if (w.startsWith("-")) {
465 if (eq < 0 && CLONE_VALUE.has(w))
466 j += 1;
467 continue;
468 }
469 pos.push(w);
470 }
471 const [url, target] = pos;
472 if (url !== undefined) {
473 const base = target ?? url.replace(/\/+$/, "").replace(/\.git$/, "").split(/[/:]/).pop() ?? "";
474 if (base !== "")
475 made.set(remoteKey(join(dir, base), origin), url);
476 }
477 if (made.size === 0)
478 made.set("", "");
479 return out;
480 }
481 if (sub === "remote" && (rest[0] === "add" || rest[0] === "set-url")) {
482 const pos = rest.slice(1).filter((w) => !w.startsWith("-"));
483 made.set(remoteKey(dir, pos[0] ?? ""), pos[pos.length - 1] ?? "");
484 return out;
485 }
486 if (sub === "push") {
487 pushAction(cmd, dir, rest, opaque, out);
488 const p = out[out.length - 1];
489 if (p.kind === "push" && made.size > 0) {
490 p.lineRemote = true;
491 const url = made.get(remoteKey(dir, p.remote ?? "origin"));
492 if (url !== undefined)
493 p.also = [url];
494 }
495 return out;
496 }
497 if (sub === "send-pack") {
498 const remote = rest.find((w) => !w.startsWith("-")) ?? null;
499 out.push({ kind: "push", cmd, dir, remote, force: rest.includes("--force"), opaque });
500 return out;
501 }
502 if (sub === "commit") {
503 let all = false;
504 for (let j = 0;j < rest.length; j += 1) {
505 const w = rest[j];
506 if (w === "--no-verify") {
507 out.push({ kind: "hook-bypass", cmd, dir, how: "git commit --no-verify" });
508 continue;
509 }
510 if (w === "--all" || w === "--include" || w === "--only") {
511 all = true;
512 continue;
513 }
514 if (w === "--") {
515 if (j + 1 < rest.length)
516 all = true;
517 break;
518 }
519 if (/^-[A-Za-z]+$/.test(w)) {
520 for (const ch of w.slice(1)) {
521 if (ch === "n")
522 out.push({ kind: "hook-bypass", cmd, dir, how: "git commit -n" });
523 if (ch === "a" || ch === "i" || ch === "o")
524 all = true;
525 if (COMMIT_VALUE.has(`-${ch}`)) {
526 if (w.endsWith(ch))
527 j += 1;
528 break;
529 }
530 }
531 continue;
532 }
533 if (w.startsWith("-")) {
534 if (!w.includes("=") && COMMIT_VALUE.has(w))
535 j += 1;
536 continue;
537 }
538 all = true;
539 }
540 out.push({ kind: "commit", cmd, dir, all });
541 return out;
542 }
543 if (sub === "config") {
544 if (rest.some((w) => HOOKS_PATH.test(w)))
545 out.push({ kind: "hook-bypass", cmd, dir, how: "git config core.hooksPath" });
546 const key = rest.findIndex((w) => /^alias\./i.test(w));
547 if (key >= 0) {
548 const value = rest.slice(key + 1).join(" ");
549 if (RISKY_ALIAS.test(value.trim()))
550 out.push({ kind: "hook-bypass", cmd, dir, how: `a git alias for "${value.slice(0, 40)}"` });
551 }
552 return out;
553 }
554 if (["merge", "rebase", "am", "cherry-pick", "revert", "pull"].includes(sub) && rest.includes("--no-verify")) {
555 out.push({ kind: "hook-bypass", cmd, dir, how: `git ${sub} --no-verify` });
556 return out;
557 }
558 if (!GIT_BUILTINS.has(sub))
559 out.push({ kind: "alias", tool: "git", name: sub, prefix, args: rest, cmd, dir });
560 return out;
561}
562var MAX_ALIAS_DEPTH = 3;
563function expandAlias(tool, value, prefix, args, cmd, depth = 0) {
564 if (depth >= MAX_ALIAS_DEPTH)
565 return [{ kind: "hook-bypass", cmd, dir: cmd.cd, how: `a ${tool} alias nested too deep to read` }];
566 const quoted = args.map((w) => `'${w.replace(/'/g, `'\\''`)}'`).join(" ");
567 if (value.startsWith("!")) {
568 const inner = parseCommands(`${value.slice(1)} ${quoted}`).map((c) => ({ ...c, cd: join(cmd.cd, c.cd) }));
569 return actionsOf(inner, depth + 1);
570 }
571 const words = parseCommands(value)[0]?.argv ?? [];
572 const argv = [...prefix, ...words, ...args];
573 return actionsOf([{ argv, env: cmd.env, cd: cmd.cd }], depth + 1);
574}
575var REPO_ARG = /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100}$/;
576function repoFromRef(ref) {
577 const url = /^https?:\/\/[^/]+\/(?:api\/v3\/)?(?:repos\/)?([^/\s]+)\/([^/\s#?]+)/.exec(ref);
578 if (url)
579 return `${url[1]}/${url[2].replace(/\.git$/, "")}`;
580 const host = /^(?:[^/\s]+\/)?([A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100})$/.exec(ref);
581 if (host && REPO_ARG.test(host[1]))
582 return host[1];
583 return null;
584}
585function rawPath(endpoint) {
586 let p = endpoint.trim().replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*/i, "");
587 if (!endpoint.includes("://"))
588 p = p.replace(/^api\.github\.com(?::\d+)?/i, "");
589 try {
590 p = decodeURIComponent(p);
591 } catch {}
592 return p.replace(/[?#].*$/, "").replace(/\/{2,}/g, "/").replace(/^\//, "");
593}
594function apiPath(endpoint) {
595 return rawPath(endpoint).replace(/^api\/v3\//i, "");
596}
597function repoFromApiPath(endpoint) {
598 const m = /^repos\/([^/]+)\/([^/]+)/i.exec(apiPath(endpoint));
599 if (!m)
600 return null;
601 return m[1] === "{owner}" || m[2] === "{repo}" || m[1] === ":owner" || m[2] === ":repo" ? "current" : `${m[1]}/${m[2]}`;
602}
603var WRITE_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
604var GH_REPO_WRITES = new Set(["delete", "edit", "rename", "archive", "unarchive", "sync", "set-default"]);
605var isDynamic = (w) => /[$`]/.test(w);
606function ghActions(cmd) {
607 const a = cmd.argv;
608 const dir = cmd.cd;
609 const envRepo = cmd.env.filter((e) => e.startsWith("GH_REPO=")).pop()?.slice("GH_REPO=".length) ?? null;
610 let repoFlag = envRepo !== null && envRepo !== "" ? envRepo : null;
611 const words = [];
612 for (let i = 1;i < a.length; i += 1) {
613 const w = a[i];
614 if (w === "-R" || w === "--repo") {
615 repoFlag = a[i + 1] ?? null;
616 i += 1;
617 continue;
618 }
619 if (w.startsWith("--repo=")) {
620 repoFlag = w.slice("--repo=".length);
621 continue;
622 }
623 if (/^-R./.test(w)) {
624 repoFlag = w.slice(w[2] === "=" ? 3 : 2);
625 continue;
626 }
627 words.push(w);
628 }
629 const repo = repoFlag === null ? null : isDynamic(repoFlag) ? "unknown" : repoFromRef(repoFlag) ?? repoFlag;
630 const targetRepo = (target) => target === undefined ? repo : isDynamic(target) ? "unknown" : repoFromRef(target) ?? repo;
631 const [group, verb] = words;
632 if (group === undefined)
633 return [];
634 if (group === "pr" && verb === "merge") {
635 const target = words.slice(2).find((w) => !w.startsWith("-"));
636 if (target !== undefined && isDynamic(target))
637 return [{ kind: "merge", cmd, dir, repo: "unknown" }];
638 const fromUrl = target !== undefined && target.includes("://") ? repoFromRef(target) : null;
639 return [{ kind: "merge", cmd, dir, repo: fromUrl ?? repo }];
640 }
641 if (group === "repo" && verb !== undefined && GH_REPO_WRITES.has(verb)) {
642 return [{ kind: "repo-write", cmd, dir, repo: targetRepo(words.slice(2).find((w) => !w.startsWith("-"))) }];
643 }
644 if (group === "repo" && verb === "create" && words.includes("--push")) {
645 return [{ kind: "repo-write", cmd, dir, repo: targetRepo(words.slice(2).find((w) => !w.startsWith("-"))) }];
646 }
647 if (group === "alias" && (verb === "set" || verb === "import")) {
648 const value = words.slice(3).filter((w) => !w.startsWith("-")).join(" ");
649 if (verb === "import" || RISKY_ALIAS.test(value.trim()) || words.includes("--shell") || words.includes("-s")) {
650 return [{ kind: "hook-bypass", cmd, dir, how: verb === "import" ? "gh alias import" : `a gh alias for "${value.slice(0, 40)}"` }];
651 }
652 return [];
653 }
654 if (group === "api") {
655 let method = null;
656 let hasFields = false;
657 let endpoint = null;
658 let mutation = false;
659 for (let i = 1;i < words.length; i += 1) {
660 const w = words[i];
661 if (w === "-X" || w === "--method") {
662 method = (words[i + 1] ?? "").toUpperCase();
663 i += 1;
664 continue;
665 }
666 if (w.startsWith("--method=")) {
667 method = w.slice("--method=".length).toUpperCase();
668 continue;
669 }
670 if (/^-X[A-Za-z]+$/.test(w)) {
671 method = w.slice(2).toUpperCase();
672 continue;
673 }
674 if (w === "-f" || w === "-F" || w === "--field" || w === "--raw-field" || w === "--input") {
675 hasFields = true;
676 if (/^query=\s*mutation\b/.test(words[i + 1] ?? ""))
677 mutation = true;
678 i += 1;
679 continue;
680 }
681 if (/^--(?:raw-)?field=/.test(w) || w.startsWith("--input=")) {
682 hasFields = true;
683 if (/=query=\s*mutation\b/.test(w))
684 mutation = true;
685 continue;
686 }
687 if (w === "-H" || w === "--header" || w === "-q" || w === "--jq" || w === "-t" || w === "--template" || w === "--cache" || w === "-p" || w === "--preview" || w === "--hostname") {
688 i += 1;
689 continue;
690 }
691 if (w.startsWith("-"))
692 continue;
693 if (endpoint === null)
694 endpoint = w;
695 }
696 const isWrite = method !== null ? WRITE_METHODS.has(method) : hasFields;
697 if (!isWrite || endpoint === null)
698 return [];
699 if (isDynamic(endpoint))
700 return [{ kind: "api-write", cmd, dir, repo: "unknown" }];
701 if (/^graphql\/?$/i.test(apiPath(endpoint)))
702 return mutation || hasFields ? [{ kind: "api-write", cmd, dir, repo }] : [];
703 const named = repoFromApiPath(endpoint);
704 if (named === null)
705 return /^[a-z]/i.test(apiPath(endpoint)) && !/^repos\b/i.test(apiPath(endpoint)) ? [] : [{ kind: "api-write", cmd, dir, repo: "unknown" }];
706 return [{ kind: "api-write", cmd, dir, repo: named === "current" ? repo : named }];
707 }
708 if (!GH_GROUPS.has(group))
709 return [{ kind: "alias", tool: "gh", name: group, prefix: ["gh", ...repoFlag !== null ? ["-R", repoFlag] : []], args: words.slice(1), cmd, dir }];
710 return [];
711}
712var HTTP_TOOLS = new Set(["curl", "wget", "http", "https", "xh", "xhs"]);
713function isGitHubApi(w) {
714 const m = /^(?:[a-z][a-z0-9+.-]*:\/\/)?(?:[^@/]*@)?([^/:?#]+)/i.exec(w.trim());
715 const host = (m?.[1] ?? "").toLowerCase().replace(/\.$/, "");
716 if (host === "api.github.com")
717 return true;
718 return /^[a-z][a-z0-9+.-]*:\/\//i.test(w.trim()) && /^api\/v3(\/|$)/i.test(rawPath(w));
719}
720var CURL_VALUE = new Set(["-H", "--header", "-o", "--output", "-u", "--user", "-A", "--user-agent", "-e", "--referer", "-b", "--cookie", "-c", "--cookie-jar", "-w", "--write-out", "-m", "--max-time", "--connect-timeout", "-x", "--proxy", "--retry", "-r", "--range", "-E", "--cert", "--cacert", "--key", "--resolve", "--connect-to", "--oauth2-bearer"]);
721var CURL_SHORT_VALUE = "HouAebcwmxrEK";
722function httpActions(cmd) {
723 const prog = programName(cmd.argv[0] ?? "");
724 const args = cmd.argv.slice(1);
725 let write = false;
726 let opaque = false;
727 const urls = [];
728 for (let i = 0;i < args.length; i += 1) {
729 const w = args[i];
730 if (prog === "curl") {
731 if (w === "--url") {
732 urls.push(args[i + 1] ?? "");
733 i += 1;
734 continue;
735 }
736 if (w.startsWith("--url=")) {
737 urls.push(w.slice(6));
738 continue;
739 }
740 if (w === "-K" || w === "--config") {
741 opaque = true;
742 i += 1;
743 continue;
744 }
745 if (w.startsWith("--config=")) {
746 opaque = true;
747 continue;
748 }
749 if (w === "--request" || w === "-X") {
750 write = write || WRITE_METHODS.has((args[i + 1] ?? "").toUpperCase());
751 i += 1;
752 continue;
753 }
754 if (w.startsWith("--request=")) {
755 write = write || WRITE_METHODS.has(w.slice(10).toUpperCase());
756 continue;
757 }
758 if (/^--(data|json|form|upload-file)/.test(w)) {
759 write = true;
760 if (!w.includes("="))
761 i += 1;
762 continue;
763 }
764 if (CURL_VALUE.has(w)) {
765 i += 1;
766 continue;
767 }
768 if (/^-[A-Za-z]/.test(w)) {
769 const body = w.slice(1);
770 for (let k = 0;k < body.length; k += 1) {
771 const ch = body[k];
772 const tail = body.slice(k + 1);
773 const value = tail !== "" ? tail : args[i + 1] ?? "";
774 if (ch === "X") {
775 write = write || WRITE_METHODS.has(value.toUpperCase());
776 if (tail === "")
777 i += 1;
778 break;
779 }
780 if (ch === "d" || ch === "F" || ch === "T") {
781 write = true;
782 if (tail === "")
783 i += 1;
784 break;
785 }
786 if (ch === "K") {
787 opaque = true;
788 if (tail === "")
789 i += 1;
790 break;
791 }
792 if (CURL_SHORT_VALUE.includes(ch)) {
793 if (tail === "")
794 i += 1;
795 break;
796 }
797 }
798 continue;
799 }
800 if (w.startsWith("-"))
801 continue;
802 urls.push(w);
803 } else if (prog === "wget") {
804 if (/^--method=/i.test(w))
805 write = write || WRITE_METHODS.has(w.slice(9).toUpperCase());
806 else if (w === "--method") {
807 write = write || WRITE_METHODS.has((args[i + 1] ?? "").toUpperCase());
808 i += 1;
809 } else if (/^--(post|body)-(data|file)/.test(w))
810 write = true;
811 else if (!w.startsWith("-"))
812 urls.push(w);
813 } else {
814 if (/^[A-Za-z]+$/.test(w) && WRITE_METHODS.has(w.toUpperCase())) {
815 write = true;
816 continue;
817 }
818 if (w.startsWith("-"))
819 continue;
820 if (isGitHubApi(w) || /^[a-z][a-z0-9+.-]*:\/\//i.test(w)) {
821 urls.push(w);
822 continue;
823 }
824 if (/^[^=:@]+(:=|==|=|@)/.test(w))
825 write = true;
826 }
827 }
828 if (opaque)
829 return [{ kind: "api-write", cmd, dir: cmd.cd, repo: "unknown" }];
830 const api = urls.filter(isGitHubApi);
831 if (!write || api.length === 0)
832 return [];
833 const out = [];
834 for (const u of api) {
835 const path = apiPath(u);
836 const repo = repoFromApiPath(u);
837 if (repo !== null && repo !== "current")
838 out.push({ kind: "api-write", cmd, dir: cmd.cd, repo });
839 else if (/^graphql\/?$/i.test(path) || !/^[a-z]/i.test(path) || /^repos\b/i.test(path))
840 out.push({ kind: "api-write", cmd, dir: cmd.cd, repo: "unknown" });
841 }
842 return out;
843}
844var EXPORTERS = new Set(["export", "declare", "typeset", "readonly", "local", "set"]);
845function actionsOf(cmds, depth = 0) {
846 const out = [];
847 const made = new Map;
848 const lineEnv = [];
849 for (const cmd of cmds) {
850 const prog = programName(cmd.argv[0] ?? "");
851 lineEnv.push(...cmd.env.filter((e) => e.startsWith("GH_REPO=")).filter(() => cmd.argv.length === 0));
852 if (EXPORTERS.has(prog))
853 lineEnv.push(...cmd.argv.slice(1).filter((w) => w.startsWith("GH_REPO=")));
854 if (prog === "git" || prog.startsWith("git-") && GIT_BUILTINS.has(prog.slice(4)))
855 out.push(...gitActions(cmd, depth, made));
856 else if (prog === "gh")
857 out.push(...ghActions(lineEnv.length > 0 ? { ...cmd, env: [...lineEnv, ...cmd.env] } : cmd));
858 else if (HTTP_TOOLS.has(prog))
859 out.push(...httpActions(cmd));
860 }
861 return out;
862}
863function repoFromRemoteUrl(url) {
864 const u = url.trim();
865 const full = /^(?:https?|ssh|git):\/\/(?:[^@/]+@)?[^/:]+(?::\d+)?\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(u);
866 if (full)
867 return `${full[1]}/${full[2]}`;
868 const scp = /^(?:[^@/:]+@)?[^/:]+:\/?([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(u);
869 if (scp && !u.startsWith("/") && !u.startsWith("."))
870 return `${scp[1]}/${scp[2]}`;
871 return null;
872}
873
874var TICK_MS = 120;
875var CYCLE_TICKS = 34;
876var PULSE_TICKS = 12;
877var PULSE_TEXT_MS = 8000;
878var WORD = ["D", "K", "O", "D"];
879var COLORS = {
880 cyan: "#22d3ee",
881 cyanLight: "#cffafe",
882 yellow: "#facc15",
883 yellowLight: "#fef9c3",
884 green: "#4ade80",
885 coral: "#fb7185",
886 dim: "#94a3b8"
887};
888function signedIn(auth) {
889 return auth === "ok" || auth === "checking";
890}
891function wordmark(frame, auth) {
892 const base = signedIn(auth) ? COLORS.cyan : COLORS.yellow;
893 const light = signedIn(auth) ? COLORS.cyanLight : COLORS.yellowLight;
894 return WORD.map((ch, i) => i === frame ? { ch, color: light, bold: true } : { ch, color: base, bold: true });
895}
896function wordmarkFrame(tick) {
897 const at = (tick % CYCLE_TICKS + CYCLE_TICKS) % CYCLE_TICKS;
898 return at < WORD.length ? at : -1;
899}
900var PULSE_GLYPHS = ["◇", "◈", "◆", "◈"];
901function icon(pulse, pulseFrame) {
902 if (pulse === null || pulseFrame < 0 || pulseFrame >= PULSE_TICKS)
903 return { glyph: "◈", color: COLORS.cyan };
904 const color = pulse.decision === "allowed" ? COLORS.green : pulse.decision === "blocked" ? COLORS.coral : COLORS.yellow;
905 return { glyph: PULSE_GLYPHS[pulseFrame % PULSE_GLYPHS.length], color };
906}
907function message(auth, pulse, now, status) {
908 if (pulse !== null && now - pulse.at < PULSE_TEXT_MS) {
909 const color = pulse.decision === "allowed" ? COLORS.green : pulse.decision === "blocked" ? COLORS.coral : COLORS.yellow;
910 return { text: pulse.text, color };
911 }
912 if (auth === "signin")
913 return { text: "Sign in to DKOD for Deliver and your organization's Guard rules: type /mcp, pick dkod, then Authenticate", color: COLORS.yellow };
914 if (auth === "device")
915 return { text: "This device is not enrolled in your organization's Guard. Ask your DKOD admin.", color: COLORS.yellow };
916 if (auth === "offline")
917 return { text: "DKOD is not connected. Type /mcp and connect dkod.", color: COLORS.yellow };
918 return { text: status, color: COLORS.dim };
919}
920function pulseText(decision, action, repo) {
921 const verb = decision === "allowed" ? "checked" : decision === "blocked" ? "blocked" : "declined";
922 const what = action === "blocked-command" ? "a command" : action.replace(/-/g, " ");
923 return repo === null ? `${verb} ${what}` : `${verb} ${what} · ${repo}`;
924}
925function bandTree(el, view) {
926 const letters = wordmark(view.frame, view.auth).map((l) => h(el.Text, { color: l.color, bold: l.bold }, l.ch));
927 const mark = icon(view.pulse, view.pulseFrame);
928 const words = message(view.auth, view.pulse, view.now, view.status);
929 return h(el.Box, { key: "dkod-band", flexDirection: "row" }, h(el.Text, null, ...letters), h(el.Text, { color: mark.color, bold: true }, ` ${mark.glyph} `), h(el.Text, { color: COLORS.dim }, "Dkoder "), h(el.Text, { color: words.color, wrap: "truncate-end" }, words.text));
930}
931function withBelow(el, ours, below) {
932 if (below === null || below === undefined || below === false)
933 return ours;
934 return h(el.Box, { key: "dkod-stack", flexDirection: "column" }, ours, below);
935}
936
937var IGNORED_BASENAMES = new Set(["readme.md", "license", "license.md", "licence", "licence.md"]);
938var MAX_ASSET_BYTES = 512 * 1024;
939
940var NAMESPACES = new Set(["app", "policy", "answers", "dkod"]);
941
942var INPUT_TYPES = new Set(["text", "number", "choice", "boolean", "list"]);
943var MAX_REPO_PATH = 256;
944function safeRepoPath(p) {
945 if (typeof p !== "string")
946 return false;
947 if (p.trim() === "")
948 return false;
949 if (p.length > MAX_REPO_PATH)
950 return false;
951 if (p.startsWith("/"))
952 return false;
953 if (p.includes("\\"))
954 return false;
955 if (/[\n\r\u2028\u2029\0]/.test(p))
956 return false;
957 return !p.split("/").some((segment) => segment === "..");
958}
959
960var REMOVED_SECRET = "DKOD_REMOVED_SECRET";
961var ASSIGNMENT_RE = /^[\s\-*]*(?:(?:export|ENV|ARG|const|let|var|readonly|private|public|static|final)\s+)*["']?([A-Za-z_][A-Za-z0-9_.-]*)["']?[ \t]*(?::[ \t]*[A-Za-z_][A-Za-z0-9_<>[\]|]*[ \t]*)?[:=](.*)$/;
962var SECRET_KEY_RE = /^(.*_)?(secret|password|passwd|token|api_key|apikey|private_key)$/i;
963function normaliseKey(key) {
964 return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[.-]/g, "_").toLowerCase();
965}
966function looksLikeReference(value) {
967 const v = value.trim().replace(/^["'`]/, "").replace(/["'`],?;?$/, "").trim();
968 if (v === "" || v === "null" || v === "~" || v === "true" || v === "false" || v === "{}" || v === "[]")
969 return true;
970 if (v.includes(REMOVED_SECRET))
971 return true;
972 if (v === "|" || v === ">" || v === "|-" || v === ">-")
973 return true;
974 if (/^\{\{[^}]*\}\}$/.test(v))
975 return true;
976 if (/\$\{[^}]*\}/.test(v) || /^\$[A-Za-z_][A-Za-z0-9_]*$/.test(v) || v.startsWith("$("))
977 return true;
978 if (/^%\([^)]*\)s$/.test(v))
979 return true;
980 if (/(process|Bun|Deno|import\.meta)\.env\b/.test(v) || v.includes("env[") || /\bos\.environ\b/.test(v) || /\bgetenv\b/i.test(v))
981 return true;
982 if (/\bsecretKeyRef\b|\bvalueFrom\b|\bexternalSecret\b/.test(v))
983 return true;
984 if (/^<[^<>]+>$/.test(v))
985 return true;
986 return false;
987}
988var CODE_FILE_RE = /\.(?:[cm]?[jt]sx?|py|go|java|kt|kts|rb|rs|cs|php|swift|scala|dart)$/;
989function looksLikeCodeExpression(path, value) {
990 if (isConfigFile(path) || !CODE_FILE_RE.test(basename(path)))
991 return false;
992 const v = value.trim().replace(/[,;]$/, "").trim();
993 if (!v.includes("(") && !v.includes(".") && !/^(?:await|new)[ \t]/.test(v))
994 return false;
995 return /^(?:await[ \t]+|new[ \t]+)?[A-Za-z_$][\w$]*(?:\??\.[A-Za-z_$][\w$]*)*[ \t]*(?:[!?]?\(.*)?$/.test(v);
996}
997function nameWords(name) {
998 return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase().split(/[_.-]/).filter((w) => w !== "");
999}
1000var SECRET_LAST_WORDS = new Set(["secret", "token", "apikey", "credential", "credentials"]);
1001var PASSWORD_WORDS = new Set(["password", "passwd", "pwd", "pass", "passphrase"]);
1002var KEY_QUALIFIERS = new Set(["api", "private", "secret", "access", "signing", "encryption", "master", "client"]);
1003var DESCRIBING_LAST_WORDS = new Set([
1004 "id",
1005 "ids",
1006 "hash",
1007 "ref",
1008 "refs",
1009 "name",
1010 "names",
1011 "path",
1012 "paths",
1013 "url",
1014 "uri",
1015 "file",
1016 "dir",
1017 "enc",
1018 "encrypted",
1019 "count",
1020 "kind",
1021 "type",
1022 "header",
1023 "headers",
1024 "state",
1025 "scope",
1026 "scopes",
1027 "label",
1028 "prefix",
1029 "length",
1030 "len",
1031 "env",
1032 "var",
1033 "field",
1034 "tag",
1035 "version",
1036 "digest",
1037 "ttl",
1038 "expiry",
1039 "expires",
1040 "at"
1041]);
1042function secretNameKind(name) {
1043 const words = nameWords(name);
1044 const last = words.at(-1);
1045 if (last === undefined || DESCRIBING_LAST_WORDS.has(last))
1046 return null;
1047 if (words.join("_").endsWith("key_name"))
1048 return null;
1049 if (PASSWORD_WORDS.has(last))
1050 return "password";
1051 if (SECRET_LAST_WORDS.has(last))
1052 return "secret";
1053 if (last === "base" && words.at(-2) === "key" && words.at(-3) === "secret")
1054 return "secret";
1055 if (last !== "key" || words.length < 2)
1056 return null;
1057 return KEY_QUALIFIERS.has(words.at(-2)) ? "secret" : "any_key";
1058}
1059function isSecretName(name) {
1060 return secretNameKind(name) !== null;
1061}
1062function isFakeValue(value) {
1063 return /example/i.test(value) || /^(.)\1*$/.test(value) || /x{8}/i.test(value);
1064}
1065function entropy(value) {
1066 const counts = new Map;
1067 for (const ch of value)
1068 counts.set(ch, (counts.get(ch) ?? 0) + 1);
1069 let bits = 0;
1070 for (const n of counts.values()) {
1071 const p = n / value.length;
1072 bits -= p * Math.log2(p);
1073 }
1074 return bits;
1075}
1076function charClasses(value) {
1077 return [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((re) => re.test(value)).length;
1078}
1079var TIER_B_PLACEHOLDER = /test|example|sample|placeholder|dummy|fake|changeme|change_me|your|xxx|redacted|secret-value|todo|<|>|\.\.\./i;
1080var DEFAULT_PASSWORD = /^(password|passwd|secret|admin|root|postgres|mysql|redis|guest|default|changeit)$/i;
1081function looksRandom(value) {
1082 if (/^[0-9a-fA-F]+$/.test(value))
1083 return value.length >= 16 && entropy(value) >= 3;
1084 return entropy(value) >= 3.5 && charClasses(value) >= 2;
1085}
1086function notSecretShape(value) {
1087 if (value.includes(REMOVED_SECRET) || looksLikeReference(value))
1088 return true;
1089 if (/^(\/|\.\/|\.\.\/|~\/)/.test(value) || /\.[a-z0-9]{1,5}$/.test(value))
1090 return true;
1091 if (value.includes("://"))
1092 return true;
1093 if (/\d\.\d/.test(value) || /sha256:/i.test(value))
1094 return true;
1095 if (/\$\{|\$\(|\{\{|process\.env|os\.environ|import\.meta\.env|%/.test(value))
1096 return true;
1097 return TIER_B_PLACEHOLDER.test(value);
1098}
1099var IDENTIFIER_PART = "(?:[a-z]+(?:[0-9]+[a-z]*)?|[0-9]+[a-z]*)";
1100var IDENTIFIER_VALUE = new RegExp(`^${IDENTIFIER_PART}(?:[._-]${IDENTIFIER_PART})*$`);
1101function looksLikeSecretLiteral(value, kind) {
1102 const password = kind === "password";
1103 if (value.length < (password ? 8 : 16) || /\s/.test(value))
1104 return false;
1105 if (notSecretShape(value))
1106 return false;
1107 if (password)
1108 return !DEFAULT_PASSWORD.test(value);
1109 if (/^[A-Z][A-Z0-9_]+$/.test(value))
1110 return false;
1111 if (IDENTIFIER_VALUE.test(value))
1112 return false;
1113 return looksRandom(value);
1114}
1115var TEST_OR_DOC_DIR = /(^|\/)(test|tests|__tests__|spec|specs|fixtures?|testdata|examples?|docs?)\//i;
1116var TEST_OR_DOC_FILE = /\.(test|spec)\.[a-z]+$|_test\.(go|py|rs)$|\.(md|mdx|txt|rst)$/i;
1117function isTestOrDocPath(path) {
1118 return TEST_OR_DOC_DIR.test(path) || TEST_OR_DOC_FILE.test(path);
1119}
1120var PEM_LABEL = "PRIV" + "ATE KEY";
1121var PEM_BEGIN_RE = new RegExp(`-----BEGIN ([A-Z0-9 ]{0,40})${PEM_LABEL}( BLOCK)?-----`, "g");
1122var PEM_BODY_RE = /(?:[A-Za-z0-9+/=\s]|\\[nr]){40,}/y;
1123var hasDigit = (v) => /\d/.test(v);
1124var VALUE_PATTERNS = [
1125 { rule: "aws_access_key_id", re: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g },
1126 { rule: "github_token", re: /\bgh[pousr]_[A-Za-z0-9]{36,255}/g },
1127 { rule: "github_token", re: /\bgithub_pat_[A-Za-z0-9_]{50,255}/g },
1128 { rule: "gitlab_token", re: /\bglpat-[A-Za-z0-9_-]{20,}/g },
1129 { rule: "anthropic_key", re: /\bsk-ant-[A-Za-z0-9_-]{20,}/g },
1130 { rule: "openrouter_key", re: /\bsk-or-[A-Za-z0-9_-]{20,}/g },
1131 { rule: "openai_key", re: /\bsk-proj-[A-Za-z0-9_-]{20,}/g },
1132 { rule: "openai_key", re: /\bsk-[A-Za-z0-9_-]{20,}/g, accept: hasDigit },
1133 { rule: "xai_key", re: /\bxai-[A-Za-z0-9]{20,}/g },
1134 { rule: "groq_key", re: /\bgsk_[A-Za-z0-9]{20,}/g },
1135 { rule: "huggingface_token", re: /\bhf_[A-Za-z0-9]{30,}/g },
1136 { rule: "npm_token", re: /\bnpm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g },
1137 { rule: "pypi_token", re: /\bpypi-[A-Za-z0-9_-]{50,}/g },
1138 { rule: "sendgrid_key", re: /\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g },
1139 { rule: "shopify_token", re: /\bshp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}(?![a-fA-F0-9])/g },
1140 { rule: "aws_secret_access_key", re: /aws_secret_access_key["']?[ \t]*[:=][ \t]*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/dgi, group: 1 },
1141 { rule: "stripe_key", re: /\b(?:sk|rk)_live_[A-Za-z0-9]{16,}/g },
1142 { rule: "slack_token", re: /\bxox[abposr]-[A-Za-z0-9-]{10,}/g },
1143 { rule: "google_api_key", re: /\bAIza[0-9A-Za-z_-]{35}/g },
1144 { rule: "jwt", re: /\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g },
1145 { rule: "registry_auth", re: /:_(?:authToken|auth|password)[ \t]*=[ \t]*"?([^\s"#;]+)/dg, group: 1 },
1146 { rule: "db_url_password", re: /:\/\/[^\s:@/'"`]+:([^\s@/'"`]+)@/dg, group: 1 }
1147];
1148var AWS_SECRET_RE = /(?<![A-Za-z0-9/+=])[A-Za-z0-9/+=]{40}(?![A-Za-z0-9/+=])/g;
1149var AWS_PAIR_WINDOW = 400;
1150var awsSecretShaped = (v) => /^[A-Za-z0-9/+=]{40}$/.test(v) && /[a-z]/.test(v) && /[A-Z]/.test(v) && /[0-9/+]/.test(v);
1151var DOCKER_AUTH_RE = /"auth"[ \t]*:[ \t]*"([A-Za-z0-9+/=]{8,})"/dg;
1152var INLINE_ASSIGNMENT_RE = /(?<![A-Za-z0-9_.$-])["']?([A-Za-z_][A-Za-z0-9_.-]*)["']?[ \t]*(?::[ \t]*[A-Za-z_][A-Za-z0-9_<>[\]|]*[ \t]*)?[:=][ \t]*/g;
1153var LINE_START_PREFIX = /^[\s\-*]*(?:(?:export|ENV|ARG)\s+)*$/;
1154var BEARER_RE = /\bBearer[ \t]+([A-Za-z0-9._~+/-]{20,}=*)/dg;
1155var QUOTED = {
1156 '"': /^"((?:\\.|[^"\\\n])*)"/,
1157 "'": /^'((?:\\.|[^'\\\n])*)'/,
1158 "`": /^`((?:\\.|[^`\\\n])*)`/
1159};
1160var ASSIGNMENT_LINE_CAP = 20 * 1024;
1161function basename(path) {
1162 return (path.split("/").pop() ?? path).toLowerCase();
1163}
1164function isConfigFile(path) {
1165 const base = basename(path);
1166 if (base === "dockerfile" || base.startsWith("dockerfile.") || base.startsWith(".env") || base.endsWith(".env"))
1167 return true;
1168 if ([".npmrc", ".yarnrc", ".yarnrc.yml", ".pypirc", ".netrc", "_netrc", ".dev.vars", ".flaskenv", ".pgpass", ".my.cnf"].includes(base))
1169 return true;
1170 return /\.(ya?ml|properties|ini|cfg|conf|toml|env|envrc|example|sample|template|tfvars|vars)$/.test(base);
1171}
1172function lineStarts(content) {
1173 const starts = [0];
1174 for (let i = 0;i < content.length; i += 1)
1175 if (content.charCodeAt(i) === 10)
1176 starts.push(i + 1);
1177 return starts;
1178}
1179function lineOf(starts, offset) {
1180 let lo = 0;
1181 let hi = starts.length - 1;
1182 while (lo < hi) {
1183 const mid = lo + hi + 1 >> 1;
1184 if (starts[mid] <= offset)
1185 lo = mid;
1186 else
1187 hi = mid - 1;
1188 }
1189 return lo + 1;
1190}
1191function pemHits(content) {
1192 const out = [];
1193 PEM_BEGIN_RE.lastIndex = 0;
1194 for (let m = PEM_BEGIN_RE.exec(content);m; m = PEM_BEGIN_RE.exec(content)) {
1195 const start = m.index;
1196 const after = start + m[0].length;
1197 const endMarker = `-----END ${m[1] ?? ""}${PEM_LABEL}${m[2] ?? ""}-----`;
1198 const endAt = content.indexOf(endMarker, after);
1199 if (endAt >= 0) {
1200 out.push({ start, end: endAt + endMarker.length });types/index.d.ts 15 lines1// DKOD Guard's band values (packages/dkoder-plugin/src/claude-band.ts), held by the host for the session.
2export type GuardPulse = { decision: 'allowed' | 'blocked' | 'declined'; text: string; at: number }
3
4declare module 'claude-code' {
5 interface PluginState {
6 'dkod': {
7 auth: 'checking' | 'ok' | 'signin' | 'offline' | 'device'
8 frame: number
9 pulse: GuardPulse | null
10 pulseFrame: number
11 status: string
12 }
13 }
14}
15