@tinycloud/courier 0.2.0-beta.37 → 0.2.0-beta.38

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 (87) hide show
  1. package/README.md +331 -12
  2. package/dist/a2a.d.ts +6 -0
  3. package/dist/a2a.js +10 -0
  4. package/dist/a2a.js.map +1 -1
  5. package/dist/approval.d.ts +6 -5
  6. package/dist/approval.js +7 -16
  7. package/dist/approval.js.map +1 -1
  8. package/dist/args.js +1 -1
  9. package/dist/args.js.map +1 -1
  10. package/dist/broker-config.d.ts +24 -0
  11. package/dist/broker-config.js +99 -0
  12. package/dist/broker-config.js.map +1 -0
  13. package/dist/broker.d.ts +77 -0
  14. package/dist/broker.js +253 -0
  15. package/dist/broker.js.map +1 -0
  16. package/dist/commands.js +5 -2
  17. package/dist/commands.js.map +1 -1
  18. package/dist/consumed.d.ts +10 -0
  19. package/dist/consumed.js +54 -0
  20. package/dist/consumed.js.map +1 -0
  21. package/dist/guard/binaries.d.ts +17 -0
  22. package/dist/guard/binaries.js +89 -0
  23. package/dist/guard/binaries.js.map +1 -0
  24. package/dist/guard/cleanhome.d.ts +27 -0
  25. package/dist/guard/cleanhome.js +111 -0
  26. package/dist/guard/cleanhome.js.map +1 -0
  27. package/dist/guard/command.d.ts +13 -0
  28. package/dist/guard/command.js +158 -0
  29. package/dist/guard/command.js.map +1 -0
  30. package/dist/guard/expansion.d.ts +10 -0
  31. package/dist/guard/expansion.js +74 -0
  32. package/dist/guard/expansion.js.map +1 -0
  33. package/dist/guard/hook.d.ts +38 -0
  34. package/dist/guard/hook.js +124 -0
  35. package/dist/guard/hook.js.map +1 -0
  36. package/dist/guard/install.d.ts +67 -0
  37. package/dist/guard/install.js +193 -0
  38. package/dist/guard/install.js.map +1 -0
  39. package/dist/guard/network.d.ts +7 -0
  40. package/dist/guard/network.js +78 -0
  41. package/dist/guard/network.js.map +1 -0
  42. package/dist/guard/normalize.d.ts +28 -0
  43. package/dist/guard/normalize.js +257 -0
  44. package/dist/guard/normalize.js.map +1 -0
  45. package/dist/guard/paseo.d.ts +30 -0
  46. package/dist/guard/paseo.js +148 -0
  47. package/dist/guard/paseo.js.map +1 -0
  48. package/dist/guard/paths.d.ts +31 -0
  49. package/dist/guard/paths.js +180 -0
  50. package/dist/guard/paths.js.map +1 -0
  51. package/dist/guard/policy.d.ts +65 -0
  52. package/dist/guard/policy.js +1179 -0
  53. package/dist/guard/policy.js.map +1 -0
  54. package/dist/guard/shell.d.ts +55 -0
  55. package/dist/guard/shell.js +727 -0
  56. package/dist/guard/shell.js.map +1 -0
  57. package/dist/index.d.ts +2 -0
  58. package/dist/index.js +2 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/keys.d.ts +4 -1
  61. package/dist/keys.js +7 -2
  62. package/dist/keys.js.map +1 -1
  63. package/dist/main.js +44 -4
  64. package/dist/main.js.map +1 -1
  65. package/dist/node.d.ts +1 -1
  66. package/dist/node.js +3 -3
  67. package/dist/node.js.map +1 -1
  68. package/dist/notify.d.ts +39 -0
  69. package/dist/notify.js +88 -0
  70. package/dist/notify.js.map +1 -0
  71. package/dist/pause.d.ts +26 -0
  72. package/dist/pause.js +99 -0
  73. package/dist/pause.js.map +1 -0
  74. package/dist/profile.d.ts +3 -3
  75. package/dist/profile.js +12 -12
  76. package/dist/profile.js.map +1 -1
  77. package/dist/wake.d.ts +17 -0
  78. package/dist/wake.js +74 -13
  79. package/dist/wake.js.map +1 -1
  80. package/dist/watch-cmd.d.ts +3 -0
  81. package/dist/watch-cmd.js +56 -2
  82. package/dist/watch-cmd.js.map +1 -1
  83. package/dist/watch.d.ts +70 -0
  84. package/dist/watch.js +292 -11
  85. package/dist/watch.js.map +1 -1
  86. package/package.json +1 -1
  87. package/skills/courier/SKILL.md +94 -8
@@ -0,0 +1,158 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { CourierError } from '../errors.js';
6
+ import { cmd } from '../program.js';
7
+ import { profileDir } from '../profile.js';
8
+ import { writeOut } from '../inbox.js';
9
+ import { binaryName } from './binaries.js';
10
+ import { brokerRoot } from '../broker-config.js';
11
+ import { decide, hookOutput } from './hook.js';
12
+ import { guardSettings, writeGuardSettings } from './install.js';
13
+ import { installRoots, protectedSet, realpathBest } from './paths.js';
14
+ const MAX_FILE = 1_000_000;
15
+ const MAX_STDIN = 4_000_000;
16
+ /** The whole decision, network included, must finish well inside the hook timeout (60 s) or the call is refused. */
17
+ const DEADLINE_MS = 25_000;
18
+ /** Where this install lives: what the hook refuses to let a command name or an edit tool write. */
19
+ export function installLiterals(home, entry) {
20
+ return [realpathBest(home), ...installRoots(entry)].filter((p) => p.length >= 8);
21
+ }
22
+ export function realEnv(cwd, home = profileDir(), entry = process.argv[1] ?? '') {
23
+ const censusFile = path.join(home, 'guard/binaries.json');
24
+ let installed;
25
+ try {
26
+ installed = JSON.parse(fs.readFileSync(censusFile, 'utf8'));
27
+ }
28
+ catch {
29
+ throw new Error('invalid installed executable census');
30
+ }
31
+ if (!Array.isArray(installed) || installed.some(b => !b || typeof b.name !== 'string' || typeof b.path !== 'string' || !Number.isFinite(b.dev) || !Number.isFinite(b.ino)))
32
+ throw new Error('invalid installed executable census');
33
+ const searchPath = process.env.PATH ?? '';
34
+ return {
35
+ cwd,
36
+ binaries: installed,
37
+ resolveProgram: (word, dir) => binaryName(word, dir, searchPath, installed),
38
+ literals: [...installLiterals(home, entry), brokerRoot()],
39
+ protectedRoots: [home, brokerRoot(), path.join(os.homedir(), '.courier')],
40
+ realHome: process.env.COURIER_REAL_HOME || os.userInfo().homedir,
41
+ courierEntries: installed.filter(b => b.path === realpathBest(entry) && ['courier', 'tc'].includes(b.name)).map(b => b.path),
42
+ readFile(file) {
43
+ try {
44
+ const st = fs.statSync(file);
45
+ if (!st.isFile())
46
+ return { state: 'absent' };
47
+ if (st.size > MAX_FILE)
48
+ return { state: 'unreadable' };
49
+ return { state: 'text', text: fs.readFileSync(file, 'utf8') };
50
+ }
51
+ catch (error) {
52
+ const code = error?.code;
53
+ return code === 'ENOENT' || code === 'ENOTDIR' ? { state: 'absent' } : { state: 'unreadable' };
54
+ }
55
+ },
56
+ git(dir, args) {
57
+ const git = installed.find(b => b.name === 'git');
58
+ if (!git || binaryName(git.path, dir, searchPath, installed) !== 'git')
59
+ return { ok: false, out: '' };
60
+ const r = spawnSync(git.path, args, {
61
+ cwd: dir,
62
+ encoding: 'utf8',
63
+ timeout: 5000,
64
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_OPTIONAL_LOCKS: '0' },
65
+ });
66
+ return { ok: r.status === 0 && !r.error, out: typeof r.stdout === 'string' ? r.stdout : '' };
67
+ },
68
+ getenv: (name) => process.env[name],
69
+ };
70
+ }
71
+ function readStdin() {
72
+ const chunks = [];
73
+ const buf = Buffer.alloc(65536);
74
+ let total = 0;
75
+ for (;;) {
76
+ let n = 0;
77
+ try {
78
+ n = fs.readSync(0, buf, 0, buf.length, null);
79
+ }
80
+ catch (error) {
81
+ const code = error?.code;
82
+ if (code === 'EAGAIN')
83
+ continue;
84
+ if (code === 'EOF')
85
+ break;
86
+ throw error;
87
+ }
88
+ if (n === 0)
89
+ break;
90
+ total += n;
91
+ if (total > MAX_STDIN)
92
+ throw new Error('hook input too large');
93
+ chunks.push(Buffer.from(buf.subarray(0, n)));
94
+ }
95
+ return Buffer.concat(chunks).toString('utf8');
96
+ }
97
+ function refuse(message) {
98
+ // Exit 2 is the only non-zero status Claude Code treats as "block the tool call"; flush stderr first.
99
+ fs.writeSync(2, `${message}\n`);
100
+ process.exit(2);
101
+ }
102
+ export const guardUsage = () => `Usage: ${cmd('guard')} (Claude Code PreToolUse hook: reads the tool call JSON on stdin)\n` +
103
+ ` ${cmd('guard settings [--out file] [--claude-settings file]')} Print or write the Claude Code settings that install the hook`;
104
+ /**
105
+ * `courier guard`: the Claude Code PreToolUse hook. Reads one tool call on stdin, prints a permission decision. Any
106
+ * failure refuses the call (exit 2). `courier guard settings` prints the settings that wire it in.
107
+ */
108
+ export async function cmdGuard(args) {
109
+ if (args.positional[0] === 'settings')
110
+ return guardSettingsCommand(args);
111
+ if (args.positional.length)
112
+ throw new CourierError('usage', guardUsage());
113
+ try {
114
+ const input = JSON.parse(readStdin());
115
+ const home = profileDir();
116
+ const entry = process.argv[1] ?? '';
117
+ // The hook runs with the agent's environment: HOME is the agent's clean one, so the owner's real home comes from the settings.
118
+ const realHome = process.env.COURIER_REAL_HOME || os.homedir();
119
+ const deps = {
120
+ env: realEnv(process.cwd(), home, entry),
121
+ protectedSet: { ...protectedSet(home, entry, { agentHome: os.homedir() }), roots: [...protectedSet(home, entry).roots, brokerRoot(), path.join(os.homedir(), '.courier')], homes: [...new Set([realHome, os.homedir()])], realHome },
122
+ };
123
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('timed out')), DEADLINE_MS).unref());
124
+ const decision = await Promise.race([decide(input, deps), timeout]);
125
+ await writeOut(hookOutput(decision));
126
+ }
127
+ catch (error) {
128
+ refuse(`Courier guard refused: ${String(error?.message ?? error).slice(0, 200)}`);
129
+ }
130
+ }
131
+ /** The command (`node <entry> [tc prefix] guard`) a hook should run, derived from how this process was started. */
132
+ export function guardArgv(invocation = process.argv) {
133
+ const rest = invocation.slice(2);
134
+ const at = rest.indexOf('watch');
135
+ const gi = rest.indexOf('guard');
136
+ const prefix = rest.slice(0, at >= 0 ? at : gi >= 0 ? gi : rest.length > 0 && rest[0] === 'courier' ? 1 : 0);
137
+ return [process.execPath, invocation[1], ...prefix, 'guard'];
138
+ }
139
+ async function guardSettingsCommand(args) {
140
+ const home = profileDir();
141
+ const argv = guardArgv();
142
+ const entry = process.argv[1] ?? '';
143
+ const user = args.get('claude-settings');
144
+ if (user === 'true')
145
+ throw new CourierError('usage', '--claude-settings needs a file.');
146
+ const out = args.get('out');
147
+ if (out === 'true')
148
+ throw new CourierError('usage', '--out needs a file.');
149
+ if (out === undefined && user === undefined) {
150
+ await writeOut(`${JSON.stringify(guardSettings({ argv, home, entry }), null, 2)}\n`);
151
+ return;
152
+ }
153
+ const file = writeGuardSettings({ argv, home, entry, ...(user !== undefined ? { userSettings: user } : {}) });
154
+ if (out !== undefined)
155
+ fs.copyFileSync(file, path.resolve(out));
156
+ await writeOut(`${path.resolve(out ?? file)}\n`);
157
+ }
158
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/guard/command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ParseResult } from './shell.js';
2
+ /** Which gated tool or verb appears in `text`, if any. */
3
+ export declare function gatedName(text: string): string | undefined;
4
+ /** The expansion or indirection syntax in `text` (and, from the parse, in a command word), if any. */
5
+ export declare function expansionSyntax(text: string, parsed?: ParseResult): string | undefined;
6
+ /**
7
+ * The refusal for `text`, or undefined if the guard may read it word by word. The message names the rule, so an agent
8
+ * knows what to change: write the command out with plain words.
9
+ */
10
+ export declare function expansionRefusal(text: string, parsed?: ParseResult): string | undefined;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Commands containing runtime expansion or indirection and a gated tool/verb name refuse.
3
+ * Commands without that combination are inspected word by word by the policy.
4
+ */
5
+ /** Syntax whose result is decided by the shell at run time, found in the raw text. */
6
+ const SYNTAX = [
7
+ ['parameter, command or arithmetic expansion ($)', /\$/],
8
+ ['backticks', /`/],
9
+ ['brace expansion', /\{[^{}\n]*(,|\.\.)[^{}\n]*\}/],
10
+ ['eval, source, alias or a function definition', /(^|[\s;&|(])(eval|source|alias|unalias|coproc|function)(\s|$)|(^|[;&|(\n]\s*)\.\s+\S|(^|[\s;&|(])[A-Za-z_][\w-]*\s*\(\)\s*(\{|$)/],
11
+ ['a command prefix (env, command, builtin, exec, sudo, nohup, timeout, …)', /(^|[\s;&|(])(env|command|builtin|exec|sudo|doas|nohup|timeout|setsid|stdbuf|watch|nice|ionice|time|xargs|parallel|trap|script|su|runuser|flock|strace|ltrace|unbuffer|chronic|taskset|chrt|unshare|nsenter|chroot|busybox|toybox|systemd-run|xvfb-run|ssh|at|batch|entr|caffeinate)(\s|$)/i],
12
+ ['find -exec', /\s-(exec|execdir|ok|okdir)(\s|$)/],
13
+ ['env -S', /(^|[\s;&|(])env\s+(-\w*S|--split-string)/],
14
+ ['a here-document, here-string or process substitution', /<<|<\(|>\(/],
15
+ ['text piped into a shell', /\|\s*(sh|bash|dash|zsh|ksh|fish|source|\.|xargs|eval)(\s|$)/],
16
+ ['decoded text', /\b(base64|b64decode|xxd|openssl\s+enc)\b[^|;&]*(-d|--decode|-r)\b/],
17
+ ];
18
+ const NAMES = [
19
+ 'gh', 'npm', 'pnpm', 'yarn', 'bun', 'bunx', 'npx', 'changeset', 'wrangler', 'phala', 'tc', 'tinycloud', 'lerna',
20
+ 'secret-bridge', 'secret_bridge', 'secretbridge', 'paseo', 'claude', 'codex', 'omp', 'opencode', 'gemini', 'cursor-agent', 'aider', 'goose', 'amp',
21
+ ];
22
+ const VERBS = ['merge', 'publish', 'deploy', 'push', 'release', 'secrets', 'secret', 'pair', 'revoke'];
23
+ /** `name` as a word of its own: not part of a longer identifier and not a `$NAME` variable. */
24
+ const token = (names) => new RegExp(`(?<![A-Za-z0-9_$.-])(${names.map((n) => n.replace(/[-_]/g, '[-_]?')).join('|')})(?![A-Za-z0-9_-])`, 'i');
25
+ const NAME_RE = token(NAMES);
26
+ const VERB_RE = token(VERBS);
27
+ const FETCH_RE = token(['curl', 'wget', 'http', 'https', 'xh', 'xhs', 'httpie', 'aria2c']);
28
+ const GRAPHQL_RE = /mergePullRequest|enablePullRequestAutoMerge|updateRef|createCommitOnBranch|mergeBranch/i;
29
+ const COURIER_HUMAN_RE = token(['courier']);
30
+ const COURIER_SUB_RE = token(['pair', 'revoke', 'init', 'pause', 'resume', 'watch']);
31
+ /** Quotes and backslashes removed, so `g""h`, `'g'h` and `\gh` read as the name they spell. */
32
+ const unquoted = (text) => text.replace(/\$?['"]/g, '').replace(/\\\n/g, '').replace(/\\/g, '');
33
+ /** Which gated tool or verb appears in `text`, if any. */
34
+ export function gatedName(text) {
35
+ for (const t of [text, unquoted(text)]) {
36
+ const name = NAME_RE.exec(t)?.[1] ?? VERB_RE.exec(t)?.[1];
37
+ if (name)
38
+ return name.toLowerCase();
39
+ if (GRAPHQL_RE.test(t))
40
+ return 'a GitHub merge mutation';
41
+ if (FETCH_RE.test(t) && /github/i.test(t))
42
+ return 'a GitHub API call';
43
+ if (COURIER_HUMAN_RE.test(t) && COURIER_SUB_RE.test(t))
44
+ return 'courier pair|revoke|init|pause|resume|watch';
45
+ }
46
+ return undefined;
47
+ }
48
+ /** The expansion or indirection syntax in `text` (and, from the parse, in a command word), if any. */
49
+ export function expansionSyntax(text, parsed) {
50
+ for (const t of [text, unquoted(text)])
51
+ for (const [label, re] of SYNTAX)
52
+ if (re.test(t))
53
+ return label;
54
+ // A glob, `~` or substitution in the command position does not show up as `$`: the parser knows it.
55
+ for (const cmd of parsed?.cmds ?? []) {
56
+ if (cmd.words[0]?.parts.some((part) => part.t === 'dyn'))
57
+ return 'a glob, tilde or substitution in the command word';
58
+ }
59
+ return undefined;
60
+ }
61
+ /**
62
+ * The refusal for `text`, or undefined if the guard may read it word by word. The message names the rule, so an agent
63
+ * knows what to change: write the command out with plain words.
64
+ */
65
+ export function expansionRefusal(text, parsed) {
66
+ const syntax = expansionSyntax(text, parsed);
67
+ if (!syntax)
68
+ return undefined;
69
+ const name = gatedName(text);
70
+ if (!name)
71
+ return undefined;
72
+ return `expansion syntax with a gated tool name (${syntax}; "${name}"). The guard refuses a command that can be built at run time and mentions a gated tool; write it out with plain words`;
73
+ }
74
+ //# sourceMappingURL=expansion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/guard/expansion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { type ResolveHost } from './network.js';
2
+ import { type PolicyEnv } from './policy.js';
3
+ import { type ProtectedSet } from './paths.js';
4
+ /**
5
+ * Claude Code `PreToolUse` decision for one tool call (spec §7). The hook never allows a gated action: it classifies and
6
+ * refuses. Merges run through `courier run-approved <requestId>` (the broker, `broker.ts`), which checks the owner's
7
+ * approval itself and runs a fixed command with the owner's credential outside the agent's environment. Two outcomes:
8
+ * - `pass`: not a gated command; say nothing, so Claude's own permission rules decide as usual;
9
+ * - `deny`: everything else, including every error.
10
+ * Only the tool call itself is read. Message text, task text and the owner's records never are, and the hook never opens the approval store. DNS destination checks fail closed; no approval is consumed here.
11
+ */
12
+ export type Decision = {
13
+ kind: 'pass';
14
+ } | {
15
+ kind: 'deny';
16
+ reason: string;
17
+ };
18
+ export interface GuardDeps {
19
+ env: PolicyEnv;
20
+ /** Real paths no tool call may write (courier home, install roots) and the homes whose tool configuration is protected. */
21
+ protectedSet: ProtectedSet;
22
+ /** MCP tool names (exact, or `mcp__server__*`) a woken agent may use. Default: none. */
23
+ mcpAllow?: readonly string[];
24
+ resolveHost?: ResolveHost;
25
+ }
26
+ /** The file-ish and command-ish strings of a tool call. */
27
+ export declare function toolCommand(input: unknown): {
28
+ tool: string;
29
+ command?: string;
30
+ files: string[];
31
+ patterns: string[];
32
+ url?: string;
33
+ } | undefined;
34
+ export declare function decide(input: unknown, deps: GuardDeps): Promise<Decision>;
35
+ /** The merge verb with a PR target is the one action the broker runs; everything else is for the owner to do. */
36
+ export declare const isBrokered: (verb: string, target: string) => boolean;
37
+ /** What Claude Code reads from the hook's stdout. `pass` writes nothing. */
38
+ export declare function hookOutput(decision: Decision): string;
@@ -0,0 +1,124 @@
1
+ import { registeredFile } from './binaries.js';
2
+ import { blockedDestination, dnsRefusal } from './network.js';
3
+ import path from 'node:path';
4
+ import { isRecord } from '../a2a.js';
5
+ import { cmd } from '../program.js';
6
+ import { analyze } from './policy.js';
7
+ import { isCredentialFile, isProtectedFile, isProtectedRead } from './paths.js';
8
+ const WRITE_TOOLS = new Set(['edit', 'write', 'multiedit', 'notebookedit']);
9
+ const READ_TOOLS = new Set(['read', 'grep', 'glob', 'notebookread', 'ls', 'lsp']);
10
+ const short = (text, n = 160) => text.replace(/\s+/g, ' ').slice(0, n);
11
+ const deny = (reason) => ({ kind: 'deny', reason });
12
+ /** The file-ish and command-ish strings of a tool call. */
13
+ export function toolCommand(input) {
14
+ if (!isRecord(input) || typeof input.tool_name !== 'string')
15
+ return undefined;
16
+ const toolInput = isRecord(input.tool_input) ? input.tool_input : {};
17
+ const str = (key) => (typeof toolInput[key] === 'string' ? toolInput[key] : undefined);
18
+ const files = ['file_path', 'notebook_path', 'path'].map(str).filter((v) => v !== undefined);
19
+ const patterns = ['pattern', 'glob'].map(str).filter((v) => v !== undefined);
20
+ return { tool: input.tool_name, command: str('command'), files, patterns, ...(str('url') !== undefined ? { url: str('url') } : {}) };
21
+ }
22
+ function mcpAllowed(tool, allow) {
23
+ return allow.some((entry) => entry === tool || (entry.endsWith('*') && tool.startsWith(entry.slice(0, -1))));
24
+ }
25
+ export async function decide(input, deps) {
26
+ try {
27
+ const call = toolCommand(input);
28
+ if (!call)
29
+ return deny('Courier guard could not read this tool call; refusing.');
30
+ const cwd = isRecord(input) && typeof input.cwd === 'string' && path.isAbsolute(input.cwd) ? input.cwd : deps.env.cwd;
31
+ const tool = call.tool.toLowerCase();
32
+ if (tool.startsWith('mcp__')) {
33
+ return mcpAllowed(call.tool, deps.mcpAllow ?? [])
34
+ ? { kind: 'pass' }
35
+ : deny('MCP tools are not available to a woken agent (they run with their own credentials, outside the Courier guard).');
36
+ }
37
+ if (WRITE_TOOLS.has(tool)) {
38
+ if (call.files.length === 0)
39
+ return deny('Courier guard could not read the file path of this edit; refusing.');
40
+ for (const file of call.files) {
41
+ if (registeredFile(file, cwd, deps.env.binaries ?? []))
42
+ return deny('Registered executable files cannot be edited by a woken agent.');
43
+ if (isProtectedFile(file, cwd, deps.protectedSet))
44
+ return deny('That file belongs to the Courier guard, the Courier profile or the tool configuration (settings, hooks, MCP, git hooks) and cannot be edited from a woken agent.');
45
+ if (isCredentialFile(file, cwd))
46
+ return deny('That file holds credentials and cannot be written from a woken agent.');
47
+ }
48
+ return { kind: 'pass' };
49
+ }
50
+ if (READ_TOOLS.has(tool)) {
51
+ for (const file of call.files.length ? call.files : [cwd])
52
+ if (isCredentialFile(file, cwd) || isProtectedRead(file, cwd, deps.protectedSet))
53
+ return deny('That path holds credentials and cannot be read from a woken agent.');
54
+ for (const pattern of call.patterns)
55
+ if (isCredentialFile(pattern, cwd) || isProtectedRead(pattern, cwd, deps.protectedSet))
56
+ return deny('That pattern reaches credential paths and cannot be used from a woken agent.');
57
+ return { kind: 'pass' };
58
+ }
59
+ if (tool === 'webfetch' || tool === 'websearch') {
60
+ if (call.url) {
61
+ try {
62
+ new URL(call.url);
63
+ }
64
+ catch {
65
+ return deny('Courier guard could not read that URL; refusing.');
66
+ }
67
+ if (blockedDestination(call.url) || await dnsRefusal(call.url, deps.resolveHost))
68
+ return deny('Requests to this machine (loopback) are not available to a woken agent: local services such as the Paseo daemon can start agents outside the guard.');
69
+ }
70
+ return { kind: 'pass' };
71
+ }
72
+ if (tool === 'powershell')
73
+ return deny('PowerShell cannot be inspected by the Courier guard; use Bash.');
74
+ if (call.command === undefined) {
75
+ if (tool === 'bash')
76
+ return deny('Courier guard could not read the Bash command; refusing.');
77
+ return { kind: 'pass' };
78
+ }
79
+ const decision = decideCommand(call.command, { ...deps, env: { ...deps.env, cwd } });
80
+ if (decision.kind === 'deny')
81
+ return decision;
82
+ if (await dnsRefusal(call.command, deps.resolveHost))
83
+ return deny('Network destination resolves to this machine or cannot be verified; refusing.');
84
+ return decision;
85
+ }
86
+ catch (error) {
87
+ return deny(`Courier guard failed (${short(String(error?.message ?? error), 120)}); refusing.`);
88
+ }
89
+ }
90
+ /** The merge verb with a PR target is the one action the broker runs; everything else is for the owner to do. */
91
+ export const isBrokered = (verb, target) => verb === 'merge' && /#\d+$/.test(target);
92
+ function decideCommand(command, deps) {
93
+ const findings = analyze(command, deps.env);
94
+ const never = findings.filter((f) => f.kind === 'never');
95
+ if (never.length)
96
+ return deny(`Blocked: ${never[0].label}. This is not something an owner approval can unlock.`);
97
+ const unresolved = findings.filter((f) => f.kind === 'unresolved');
98
+ if (unresolved.length) {
99
+ return deny(`Blocked: ${unresolved[0].label}. The Courier guard cannot tell this command apart from a gated one (merge, deploy, publish, push to main, secrets). ` +
100
+ 'Write the command out with plain words, one action per call.');
101
+ }
102
+ const gated = findings.filter((f) => f.kind === 'gated');
103
+ if (!gated.length)
104
+ return { kind: 'pass' };
105
+ const [g] = gated;
106
+ const more = gated.length > 1 ? ` (and ${gated.length - 1} more gated action${gated.length > 2 ? 's' : ''} in the same call)` : '';
107
+ const how = isBrokered(g.verb, g.target)
108
+ ? `Merging goes through the broker: ${cmd(`request <threadId> --peer <peer> --task <taskId> --verb ${g.verb} --target ${g.target} --summary "…"`)}, stop and wait for your owner's approval, then run exactly ${cmd('run-approved <requestId>')}.`
109
+ : `Agents do not run this action in v0: ask your owner with ${cmd(`request <threadId> --peer <peer> --task <taskId> --verb ${g.verb} --target ${g.target} --summary "…"`)}; your owner runs it.`;
110
+ return deny(`Blocked: ${g.verb} ${g.target}${more} is a gated action and a woken agent never runs it directly. ${how}`);
111
+ }
112
+ /** What Claude Code reads from the hook's stdout. `pass` writes nothing. */
113
+ export function hookOutput(decision) {
114
+ if (decision.kind === 'pass')
115
+ return '';
116
+ return `${JSON.stringify({
117
+ hookSpecificOutput: {
118
+ hookEventName: 'PreToolUse',
119
+ permissionDecision: 'deny',
120
+ permissionDecisionReason: decision.reason,
121
+ },
122
+ })}\n`;
123
+ }
124
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/guard/hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /** Where this OS user keeps credentials that a woken agent can read through the Read tool (and a shell) unless HOME differs. */
2
+ export declare const CREDENTIAL_FILES: string[];
3
+ /** The command Claude Code runs for the hook. `|| exit 2` turns any crash into a blocking error (exit 1 would fail open). */
4
+ export declare function hookCommand(argv: string[], home: string, realHome?: string): string;
5
+ /** Everything the guard keeps for a profile lives here; no tool call may write it. */
6
+ export declare const guardDir: (home: string) => string;
7
+ export declare const guardSettingsFile: (home: string) => string;
8
+ /** `TC_HOME` for woken agents: an empty directory, so the owner's `tc` profile is not found. */
9
+ export declare const profileId: (home: string) => string;
10
+ export declare const agentHome: (home: string) => string;
11
+ export declare const noOwnerProfileDir: (home: string) => string;
12
+ /** Paths no tool call may write: the courier profile (keys, grant, state), the guard files, the hook program and what it imports. */
13
+ export declare function protectedPaths(home: string, entry: string): string[];
14
+ export interface GuardSettings {
15
+ hooks: {
16
+ PreToolUse: {
17
+ matcher: string;
18
+ hooks: {
19
+ type: 'command';
20
+ command: string;
21
+ timeout: number;
22
+ }[];
23
+ }[];
24
+ };
25
+ permissions: {
26
+ deny: string[];
27
+ };
28
+ disableAllHooks: false;
29
+ }
30
+ export declare function guardSettings(options: {
31
+ argv: string[];
32
+ home: string;
33
+ entry: string;
34
+ }): GuardSettings;
35
+ /** The guard's own hook command, as {@link hookCommand} writes it. */
36
+ export declare const GUARD_HOOK_RE: RegExp;
37
+ /**
38
+ * `ours` merged over a user's settings: the guard's hook goes first, its deny rules are added, `disableAllHooks` is
39
+ * forced to false, and an older copy of the guard's hook in `theirs` is replaced, not duplicated.
40
+ */
41
+ export declare function mergeGuardSettings(ours: GuardSettings, theirs: unknown): Record<string, unknown>;
42
+ /**
43
+ * The settings the guard installs in a woken agent's project directory. The file is the guard's: only the guard's hook
44
+ * (as the one PreToolUse entry, with no other hook event) and the union of deny rules survive. Everything else in it
45
+ * (other hooks, `env`, `apiKeyHelper`, `statusLine`, MCP servers, allow rules) is replaced, because any of it can run a
46
+ * command or widen what the agent may do (B1).
47
+ */
48
+ export declare function projectSettings(ours: GuardSettings, theirs: unknown): Record<string, unknown>;
49
+ /**
50
+ * Write the guard settings for `home`. A settings file the owner passed with `--claude-settings` is merged in, with the
51
+ * guard's hook and deny rules added to (never replacing) whatever it holds. Returns the path to give `claude --settings`.
52
+ */
53
+ export declare function writeGuardSettings(options: {
54
+ argv: string[];
55
+ home: string;
56
+ entry: string;
57
+ userSettings?: string;
58
+ }): string;
59
+ /** Private shell/config roots shared by the direct adapters and stock Paseo overrides. */
60
+ export declare function agentHomeEnv(home: string): Record<string, string>;
61
+ /**
62
+ * Environment for a woken agent: an allowlist (see {@link ALLOWED_ENV}) plus `extra` names the operator passed with
63
+ * `--env-pass`. The owner's `tc` profile is hidden by pointing `TC_HOME` at an empty directory.
64
+ */
65
+ export declare function courierOnlyEnv(base: NodeJS.ProcessEnv, home: string, extra?: readonly string[]): NodeJS.ProcessEnv;
66
+ /** Environment overrides for Paseo: blank credential names and set the clean agent HOME. */
67
+ export declare function courierEnvOverrides(base: NodeJS.ProcessEnv, home: string, extra?: readonly string[]): Record<string, string>;