@agentdeck/hooks 0.2.0 → 0.2.3

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.
@@ -0,0 +1,64 @@
1
+ export declare const DEFAULT_CODEX_CONFIG_PATH: string;
2
+ /** Sidecar script for the Windows notify fallback. Codex execs `notify`
3
+ * as a bare argv array (no shell) and appends the JSON payload as the
4
+ * last element; `powershell -File` is the only Windows invocation that
5
+ * binds trailing argv into `$args` (`-Command` concatenates them into
6
+ * the command text and `-EncodedCommand` rejects them outright), and
7
+ * `-File` requires a real script on disk. */
8
+ export declare const DEFAULT_WINDOWS_NOTIFY_SCRIPT_PATH: string;
9
+ export interface InstallOptions {
10
+ /** Override the codex config path (tests + non-default homes). */
11
+ configPath?: string;
12
+ /** Daemon HTTP port to bake into the OTel exporter endpoint. If
13
+ * omitted, the installer reads `~/.agentdeck/daemon.json`. */
14
+ daemonHttpPort?: number;
15
+ /** Override platform for tests. Defaults to process.platform. */
16
+ platform?: NodeJS.Platform;
17
+ /** Override the Windows notify sidecar script path (tests). */
18
+ notifyScriptPath?: string;
19
+ }
20
+ export interface InstallResult {
21
+ installed: boolean;
22
+ /** Human-readable reason when `installed === false`. */
23
+ reason?: string;
24
+ /** Non-fatal degradation while `installed === true` (e.g. the Windows
25
+ * notify sidecar could not be written, so notify was omitted). */
26
+ warning?: string;
27
+ }
28
+ interface ManagedBlockOptions {
29
+ includeNotify?: boolean;
30
+ includeOtel?: boolean;
31
+ otelEndpoint?: string;
32
+ daemonHttpPort?: number;
33
+ platform?: NodeJS.Platform;
34
+ notifyScriptPath?: string;
35
+ }
36
+ /** Assemble the body of the AgentDeck-managed fence. Tests call this
37
+ * directly to assert schema regressions without driving the file
38
+ * installer. */
39
+ export declare function managedBlockBody(opts?: ManagedBlockOptions): string;
40
+ /** Body of the Windows notify sidecar script (`-File` execution binds
41
+ * Codex's appended payload argv into `$args`; take the last element to
42
+ * mirror POSIX `$1`). ASCII-only so PowerShell 5.1's BOM-less ANSI
43
+ * fallback reads it identically to UTF-8. Exported for tests. */
44
+ export declare function windowsNotifyScriptContent(event?: string): string;
45
+ /** Install AgentDeck's Codex observation entries into `~/.codex/config.toml`
46
+ * unless the user opted out, the user has authored a conflicting top-level
47
+ * `[features]` / `[hooks]` table outside the fence, or the file write
48
+ * failed. Idempotent: re-running with the same daemon port produces a
49
+ * byte-identical file, so safe to call from setup, `agentdeck codex`,
50
+ * and `agentdeck daemon install`. */
51
+ export declare function installCodexHooksIfNeeded(opts?: InstallOptions): InstallResult;
52
+ /** Strip the AgentDeck-managed fence. Idempotent — no-op when the fence
53
+ * is absent. Does not delete the config file even if it becomes empty
54
+ * (Codex may rely on its existence). */
55
+ export declare function uninstallCodexHooks(opts?: {
56
+ configPath?: string;
57
+ notifyScriptPath?: string;
58
+ }): void;
59
+ /** Re-apply the managed block when the daemon port (or any other resolved
60
+ * field) might have changed. Equivalent to install but renamed for the
61
+ * daemon-restart code path so the intent is clearer at the call site. */
62
+ export declare function migrateCodexHooks(opts?: InstallOptions): InstallResult;
63
+ export {};
64
+ //# sourceMappingURL=codex-install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-install.d.ts","sourceRoot":"","sources":["../src/codex-install.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,yBAAyB,QAA2C,CAAC;AAClF;;;;;8CAK8C;AAC9C,eAAO,MAAM,kCAAkC,QAAoD,CAAC;AAGpG,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;mEAC+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;uEACmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgCD,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;iBAEiB;AACjB,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,mBAAwB,GAAG,MAAM,CAsCvE;AAgHD;;;kEAGkE;AAClE,wBAAgB,0BAA0B,CAAC,KAAK,SAAwB,GAAG,MAAM,CAEhF;AAsED;;;;;sCAKsC;AACtC,wBAAgB,yBAAyB,CAAC,IAAI,GAAE,cAAmB,GAAG,aAAa,CAqDlF;AAED;;yCAEyC;AACzC,wBAAgB,mBAAmB,CAAC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,IAAI,CASvG;AAED;;0EAE0E;AAC1E,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,cAAmB,GAAG,aAAa,CAE1E"}
@@ -0,0 +1,342 @@
1
+ // codex-install.ts — Install AgentDeck observation entries into
2
+ // ~/.codex/config.toml so Codex CLI sessions report turn boundaries and
3
+ // tool calls to the daemon via lifecycle hooks.
4
+ //
5
+ // Port of apple/AgentDeck/Daemon/Core/CodexConfigInstaller.swift, with
6
+ // the macOS-specific consent flow (NSAlert / NSOpenPanel / security-
7
+ // scoped bookmarks) stripped — Node CLI runs without sandboxing, so
8
+ // invocation is implicit consent. An opt-out env var
9
+ // `AGENTDECK_NO_CODEX_HOOKS=1` (or the `--no-codex-hooks` CLI flag) is
10
+ // honoured by callers.
11
+ //
12
+ // The fence sentinels and PORT-resolution shell are byte-identical with
13
+ // the Swift installer, so a config installed by either side can be
14
+ // migrated / uninstalled by the other without conflict.
15
+ //
16
+ // MUST stay in sync with apple/AgentDeck/Daemon/Core/CodexConfigInstaller.swift
17
+ // (managedBlockBody schema + lifecycle hook table layout).
18
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from 'fs';
19
+ import { dirname, join } from 'path';
20
+ import { homedir } from 'os';
21
+ import { applyManagedBlock, removeManagedBlock, hasTopLevelKeyOutsideFence, hasTableOutsideFence, quoted, } from './codex-mini-toml.js';
22
+ export const DEFAULT_CODEX_CONFIG_PATH = join(homedir(), '.codex', 'config.toml');
23
+ /** Sidecar script for the Windows notify fallback. Codex execs `notify`
24
+ * as a bare argv array (no shell) and appends the JSON payload as the
25
+ * last element; `powershell -File` is the only Windows invocation that
26
+ * binds trailing argv into `$args` (`-Command` concatenates them into
27
+ * the command text and `-EncodedCommand` rejects them outright), and
28
+ * `-File` requires a real script on disk. */
29
+ export const DEFAULT_WINDOWS_NOTIFY_SCRIPT_PATH = join(homedir(), '.agentdeck', 'codex-notify.ps1');
30
+ const DEFAULT_DAEMON_PORT = 9120;
31
+ /** Honour `AGENTDECK_NO_CODEX_HOOKS=1` from the environment. Callers
32
+ * should also accept a `--no-codex-hooks` flag and short-circuit before
33
+ * calling install. */
34
+ function envOptOut() {
35
+ return process.env.AGENTDECK_NO_CODEX_HOOKS === '1';
36
+ }
37
+ /** Read `~/.agentdeck/daemon.json` and return the daemon's HTTP port,
38
+ * preferring `httpPort` over `port`. The Apple build splits HTTP and
39
+ * WebSocket across different ports; the Node CLI uses a single port,
40
+ * so this still resolves correctly. Returns null if the file is
41
+ * missing or malformed. */
42
+ function currentDaemonHttpPort() {
43
+ const path = join(homedir(), '.agentdeck', 'daemon.json');
44
+ if (!existsSync(path))
45
+ return null;
46
+ try {
47
+ const obj = JSON.parse(readFileSync(path, 'utf-8'));
48
+ if (typeof obj.httpPort === 'number' && obj.httpPort > 0)
49
+ return obj.httpPort;
50
+ if (typeof obj.port === 'number' && obj.port > 0)
51
+ return obj.port;
52
+ }
53
+ catch { /* malformed JSON — fall through */ }
54
+ return null;
55
+ }
56
+ function buildOtelEndpoint(daemonHttpPort) {
57
+ const port = daemonHttpPort ?? currentDaemonHttpPort() ?? DEFAULT_DAEMON_PORT;
58
+ return `http://127.0.0.1:${port}/otel/v1/traces`;
59
+ }
60
+ /** Assemble the body of the AgentDeck-managed fence. Tests call this
61
+ * directly to assert schema regressions without driving the file
62
+ * installer. */
63
+ export function managedBlockBody(opts = {}) {
64
+ const includeNotify = opts.includeNotify ?? true;
65
+ const includeOtel = opts.includeOtel ?? true;
66
+ const platform = opts.platform ?? process.platform;
67
+ const lines = [
68
+ '# Codex lifecycle hooks. Command hooks receive JSON on stdin;',
69
+ '# each snippet forwards that stdin body unchanged to AgentDeck.',
70
+ '[features]',
71
+ 'hooks = true',
72
+ ];
73
+ lines.push('');
74
+ lines.push(...buildLifecycleHookTables(platform));
75
+ if (includeNotify) {
76
+ lines.push('');
77
+ lines.push('# Optional turn-complete notification fallback.');
78
+ if (platform === 'win32') {
79
+ lines.push('# Codex appends the JSON payload as the last argv entry;');
80
+ lines.push('# powershell -File binds it into $args. (-Command cannot:');
81
+ lines.push('# it concatenates trailing argv into the command text.)');
82
+ }
83
+ else {
84
+ lines.push('# Codex appends the JSON payload as the last argv entry,');
85
+ lines.push('# so the 4th array element acts as $0 and payload lands at $1.');
86
+ }
87
+ lines.push(buildNotifyAssignment('codex_turn_complete', platform, opts.notifyScriptPath));
88
+ }
89
+ if (includeOtel) {
90
+ lines.push('');
91
+ lines.push('# OTel trace exporter — best-effort live progress signal.');
92
+ lines.push('# Schema: [otel.trace_exporter.otlp-http].');
93
+ lines.push('[otel.trace_exporter.otlp-http]');
94
+ lines.push(`endpoint = ${quoted(opts.otelEndpoint ?? buildOtelEndpoint(opts.daemonHttpPort))}`);
95
+ lines.push('protocol = "json"');
96
+ }
97
+ return lines.join('\n');
98
+ }
99
+ /** `notify = ["sh", "-c", "<snippet>", "agentdeck-notify"]`. Two design
100
+ * choices stacked here:
101
+ * 1. `"sh"` uses PATH lookup so no absolute shell path lands in the
102
+ * installer config.
103
+ * 2. The trailing `"agentdeck-notify"` is a dummy `$0`. Codex invokes
104
+ * `notify` by appending the JSON payload as the last argv entry.
105
+ * Without our 4th element, `sh -c "<snippet>" <json>` would assign
106
+ * `<json>` to `$0` and leave `$1` empty. With the dummy in place,
107
+ * `<json>` lands at `$1` as the snippet expects. */
108
+ function buildNotifyAssignment(event, platform, notifyScriptPath) {
109
+ if (platform === 'win32') {
110
+ const scriptPath = notifyScriptPath ?? DEFAULT_WINDOWS_NOTIFY_SCRIPT_PATH;
111
+ return `notify = ["powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", ${quoted(scriptPath)}]`;
112
+ }
113
+ return `notify = ["sh", "-c", ${quoted(buildNotifySnippet(event))}, "agentdeck-notify"]`;
114
+ }
115
+ /** Codex notify snippet. PORT-resolution lines are byte-identical with
116
+ * `hooks/src/install.ts:buildHookCommand` so the two integrations share
117
+ * one canonical port-discovery contract. Only the trailing curl line
118
+ * differs: Claude hooks pipe stdin (`-d @-`); Codex notify hands the
119
+ * JSON payload as `$1` (`--data-raw "$1"`). */
120
+ function buildNotifySnippet(event) {
121
+ return [
122
+ `PORT="\${AGENTDECK_PORT:-}"`,
123
+ `if [ -z "$PORT" ]; then`,
124
+ ` for F in "$HOME/.agentdeck/daemon.json" "$HOME/Library/Containers/bound.serendipity.agent.deck/Data/Library/Application Support/AgentDeck/daemon.json" "$HOME/Library/Group Containers/group.bound.serendipity.agent.deck/daemon.json"; do`,
125
+ ` [ -f "$F" ] || continue`,
126
+ ` P=$(python3 -c "import json;d=json.load(open('$F'));print(d.get('httpPort') or d.get('port',''))" 2>/dev/null)`,
127
+ ` [ -n "$P" ] && curl -sf --max-time 0.3 "http://127.0.0.1:$P/health" >/dev/null 2>&1 && { PORT="$P"; break; }`,
128
+ ` done`,
129
+ `fi`,
130
+ `PORT="\${PORT:-9120}"`,
131
+ `curl -sf --connect-timeout 0.2 --max-time 0.8 -X POST "http://127.0.0.1:$PORT/hooks/${event}" -H 'Content-Type: application/json' --data-raw "$1" 2>/dev/null || true`,
132
+ ].join('\n');
133
+ }
134
+ const LIFECYCLE_HOOKS = [
135
+ { codexEvent: 'SessionStart', agentDeckEvent: 'codex_session_start', matcher: 'startup|resume|clear' },
136
+ { codexEvent: 'UserPromptSubmit', agentDeckEvent: 'codex_user_prompt_submit', matcher: null },
137
+ { codexEvent: 'PreToolUse', agentDeckEvent: 'codex_tool_start', matcher: '*' },
138
+ { codexEvent: 'PostToolUse', agentDeckEvent: 'codex_tool_end', matcher: '*' },
139
+ { codexEvent: 'Stop', agentDeckEvent: 'codex_stop', matcher: null },
140
+ ];
141
+ function buildLifecycleHookTables(platform) {
142
+ const lines = [];
143
+ for (let idx = 0; idx < LIFECYCLE_HOOKS.length; idx++) {
144
+ const hook = LIFECYCLE_HOOKS[idx];
145
+ if (idx > 0)
146
+ lines.push('');
147
+ lines.push(`[[hooks.${hook.codexEvent}]]`);
148
+ if (hook.matcher !== null) {
149
+ lines.push(`matcher = ${quoted(hook.matcher)}`);
150
+ }
151
+ lines.push(`[[hooks.${hook.codexEvent}.hooks]]`);
152
+ lines.push(`type = "command"`);
153
+ lines.push(`command = ${quoted(buildLifecycleHookCommand(hook.agentDeckEvent, platform))}`);
154
+ lines.push(`timeout = 5`);
155
+ }
156
+ return lines;
157
+ }
158
+ /** Official Codex lifecycle hooks pass their JSON payload on stdin.
159
+ * Keep stdout quiet so Stop / UserPromptSubmit hooks do not accidentally
160
+ * feed the daemon's acknowledgement back into Codex as hook output. */
161
+ function buildLifecycleHookCommand(event, platform) {
162
+ if (platform === 'win32') {
163
+ return buildWindowsLifecycleHookCommand(event);
164
+ }
165
+ return `sh -c ${shellSingleQuoted(buildStdinPostSnippet(event))}`;
166
+ }
167
+ function buildStdinPostSnippet(event) {
168
+ return [
169
+ `PORT="\${AGENTDECK_PORT:-}"`,
170
+ `if [ -z "$PORT" ]; then`,
171
+ ` for F in "$HOME/.agentdeck/daemon.json" "$HOME/Library/Containers/bound.serendipity.agent.deck/Data/Library/Application Support/AgentDeck/daemon.json" "$HOME/Library/Group Containers/group.bound.serendipity.agent.deck/daemon.json"; do`,
172
+ ` [ -f "$F" ] || continue`,
173
+ ` P=$(python3 -c "import json;d=json.load(open('$F'));print(d.get('httpPort') or d.get('port',''))" 2>/dev/null)`,
174
+ ` [ -n "$P" ] && curl -sf --max-time 0.3 "http://127.0.0.1:$P/health" >/dev/null 2>&1 && { PORT="$P"; break; }`,
175
+ ` done`,
176
+ `fi`,
177
+ `PORT="\${PORT:-9120}"`,
178
+ `curl -sf --connect-timeout 0.2 --max-time 0.8 -X POST "http://127.0.0.1:$PORT/hooks/${event}" -H 'Content-Type: application/json' -d @- >/dev/null 2>&1 || true`,
179
+ ].join('\n');
180
+ }
181
+ function shellSingleQuoted(s) {
182
+ return `'${s.replace(/'/g, "'\"'\"'")}'`;
183
+ }
184
+ function buildWindowsLifecycleHookCommand(event) {
185
+ return `powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${windowsEncodedCommand(buildWindowsStdinPostSnippet(event))}`;
186
+ }
187
+ function buildWindowsStdinPostSnippet(event) {
188
+ // Read raw stdin as UTF-8: [Console]::In decodes with the console's OEM
189
+ // codepage (e.g. CP949) and would garble non-ASCII payload text.
190
+ return buildWindowsPostSnippet(event, '(New-Object System.IO.StreamReader([Console]::OpenStandardInput(), [System.Text.Encoding]::UTF8)).ReadToEnd()');
191
+ }
192
+ /** Body of the Windows notify sidecar script (`-File` execution binds
193
+ * Codex's appended payload argv into `$args`; take the last element to
194
+ * mirror POSIX `$1`). ASCII-only so PowerShell 5.1's BOM-less ANSI
195
+ * fallback reads it identically to UTF-8. Exported for tests. */
196
+ export function windowsNotifyScriptContent(event = 'codex_turn_complete') {
197
+ return buildWindowsPostSnippet(event, `$(if ($args.Count -gt 0) { [string]$args[$args.Count - 1] } else { '' })`) + '\n';
198
+ }
199
+ function buildWindowsPostSnippet(event, bodyExpression) {
200
+ return [
201
+ `$ErrorActionPreference = 'SilentlyContinue'`,
202
+ `$ProgressPreference = 'SilentlyContinue'`,
203
+ `$port = $env:AGENTDECK_PORT`,
204
+ `if ([string]::IsNullOrWhiteSpace($port)) {`,
205
+ ` $daemonFile = Join-Path $env:USERPROFILE '.agentdeck\\daemon.json'`,
206
+ ` if (Test-Path -LiteralPath $daemonFile) {`,
207
+ ` try {`,
208
+ ` $daemon = Get-Content -LiteralPath $daemonFile -Raw | ConvertFrom-Json`,
209
+ ` $candidate = if ($daemon.httpPort) { $daemon.httpPort } else { $daemon.port }`,
210
+ ` if ($candidate) {`,
211
+ ` try { Invoke-WebRequest -UseBasicParsing -TimeoutSec 1 -Uri ('http://127.0.0.1:' + $candidate + '/health') | Out-Null; $port = [string]$candidate } catch {}`,
212
+ ` }`,
213
+ ` } catch {}`,
214
+ ` }`,
215
+ `}`,
216
+ `if ([string]::IsNullOrWhiteSpace($port)) { $port = '9120' }`,
217
+ `$body = ${bodyExpression}`,
218
+ // Post UTF-8 bytes: Invoke-RestMethod encodes string bodies as
219
+ // ISO-8859-1 when the content type carries no charset, replacing
220
+ // non-ASCII payload characters with '?'.
221
+ `$bytes = [System.Text.Encoding]::UTF8.GetBytes([string]$body)`,
222
+ `try { Invoke-RestMethod -Method Post -TimeoutSec 1 -Uri ('http://127.0.0.1:' + $port + '/hooks/${event}') -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null } catch {}`,
223
+ `exit 0`,
224
+ ].join('\n');
225
+ }
226
+ function windowsEncodedCommand(s) {
227
+ return Buffer.from(s, 'utf16le').toString('base64');
228
+ }
229
+ // ─── File I/O ───────────────────────────────────────────────────────────
230
+ function readText(path) {
231
+ if (!existsSync(path))
232
+ return '';
233
+ try {
234
+ return readFileSync(path, 'utf-8');
235
+ }
236
+ catch {
237
+ return '';
238
+ }
239
+ }
240
+ /** Write the notify sidecar only when its content changed, so repeated
241
+ * installs stay idempotent (no mtime churn). Returns false when the
242
+ * script cannot be guaranteed on disk. */
243
+ function writeScriptIfChanged(content, path) {
244
+ try {
245
+ if (existsSync(path) && readFileSync(path, 'utf-8') === content)
246
+ return true;
247
+ }
248
+ catch { /* unreadable — fall through to rewrite */ }
249
+ return writeTextAtomic(content, path);
250
+ }
251
+ function writeTextAtomic(text, path) {
252
+ const dir = dirname(path);
253
+ try {
254
+ if (!existsSync(dir))
255
+ mkdirSync(dir, { recursive: true });
256
+ const tmp = `${path}.agentdeck.tmp`;
257
+ writeFileSync(tmp, text, 'utf-8');
258
+ renameSync(tmp, path);
259
+ return true;
260
+ }
261
+ catch {
262
+ return false;
263
+ }
264
+ }
265
+ // ─── Public install / uninstall / migrate ──────────────────────────────
266
+ /** Install AgentDeck's Codex observation entries into `~/.codex/config.toml`
267
+ * unless the user opted out, the user has authored a conflicting top-level
268
+ * `[features]` / `[hooks]` table outside the fence, or the file write
269
+ * failed. Idempotent: re-running with the same daemon port produces a
270
+ * byte-identical file, so safe to call from setup, `agentdeck codex`,
271
+ * and `agentdeck daemon install`. */
272
+ export function installCodexHooksIfNeeded(opts = {}) {
273
+ if (envOptOut())
274
+ return { installed: false, reason: 'AGENTDECK_NO_CODEX_HOOKS=1' };
275
+ const path = opts.configPath ?? DEFAULT_CODEX_CONFIG_PATH;
276
+ const original = readText(path);
277
+ // Refuse to clobber user-authored lifecycle hook config. This line-mode
278
+ // editor cannot safely merge existing `[features]` or `[hooks]` tables
279
+ // without a real TOML parser, and duplicate tables would make Codex
280
+ // reject config.toml.
281
+ if (hasTableOutsideFence(original, 'features')) {
282
+ return { installed: false, reason: 'user-authored [features] present' };
283
+ }
284
+ if (hasTableOutsideFence(original, 'hooks')) {
285
+ return { installed: false, reason: 'user-authored [hooks] present' };
286
+ }
287
+ const platform = opts.platform ?? process.platform;
288
+ let includeNotify = !hasTopLevelKeyOutsideFence(original, 'notify');
289
+ // OTel exporter stays POSIX-only for now — deliberately omitted on
290
+ // win32 (unverified there); lifecycle hooks + notify carry the signal.
291
+ const includeOtel = platform !== 'win32' && !hasTableOutsideFence(original, 'otel');
292
+ let warning;
293
+ const notifyScriptPath = opts.notifyScriptPath ?? DEFAULT_WINDOWS_NOTIFY_SCRIPT_PATH;
294
+ if (includeNotify && platform === 'win32') {
295
+ // The Stop lifecycle hook already reports turn-complete, so a failed
296
+ // sidecar write downgrades to lifecycle-only rather than aborting.
297
+ if (!writeScriptIfChanged(windowsNotifyScriptContent(), notifyScriptPath)) {
298
+ includeNotify = false;
299
+ warning = `notify sidecar write failed (${notifyScriptPath}) — notify fallback omitted`;
300
+ }
301
+ }
302
+ const otelEndpoint = includeOtel ? buildOtelEndpoint(opts.daemonHttpPort) : undefined;
303
+ const body = managedBlockBody({
304
+ includeNotify,
305
+ includeOtel,
306
+ otelEndpoint,
307
+ daemonHttpPort: opts.daemonHttpPort,
308
+ platform,
309
+ notifyScriptPath,
310
+ });
311
+ const updated = applyManagedBlock(original, body);
312
+ if (updated === original) {
313
+ return { installed: true, warning };
314
+ }
315
+ if (writeTextAtomic(updated, path)) {
316
+ return { installed: true, warning };
317
+ }
318
+ return { installed: false, reason: `write failed: ${path}` };
319
+ }
320
+ /** Strip the AgentDeck-managed fence. Idempotent — no-op when the fence
321
+ * is absent. Does not delete the config file even if it becomes empty
322
+ * (Codex may rely on its existence). */
323
+ export function uninstallCodexHooks(opts = {}) {
324
+ try {
325
+ unlinkSync(opts.notifyScriptPath ?? DEFAULT_WINDOWS_NOTIFY_SCRIPT_PATH);
326
+ }
327
+ catch { /* absent on POSIX installs — nothing to remove */ }
328
+ const path = opts.configPath ?? DEFAULT_CODEX_CONFIG_PATH;
329
+ if (!existsSync(path))
330
+ return;
331
+ const original = readText(path);
332
+ const stripped = removeManagedBlock(original);
333
+ if (stripped !== original)
334
+ writeTextAtomic(stripped, path);
335
+ }
336
+ /** Re-apply the managed block when the daemon port (or any other resolved
337
+ * field) might have changed. Equivalent to install but renamed for the
338
+ * daemon-restart code path so the intent is clearer at the call site. */
339
+ export function migrateCodexHooks(opts = {}) {
340
+ return installCodexHooksIfNeeded(opts);
341
+ }
342
+ //# sourceMappingURL=codex-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-install.js","sourceRoot":"","sources":["../src/codex-install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare const OPEN_FENCE = "# >>> AgentDeck managed (do not edit) <<<";
2
+ export declare const CLOSE_FENCE = "# <<< AgentDeck managed (do not edit) >>>";
3
+ /** Replace the AgentDeck-managed fenced block (or append one when none
4
+ * exists). The body is wrapped between OPEN_FENCE / CLOSE_FENCE so
5
+ * removeManagedBlock can strip it cleanly later. Returns the full
6
+ * updated TOML text. */
7
+ export declare function applyManagedBlock(text: string, body: string): string;
8
+ /** Strip the AgentDeck-managed block entirely. Idempotent — no-op when
9
+ * the fence is absent. */
10
+ export declare function removeManagedBlock(text: string): string;
11
+ /** Detect a top-level `<key> = ...` definition outside the fence. Codex
12
+ * `notify` is a top-level key; if the user already wrote one our fenced
13
+ * `notify` would be a duplicate-key TOML error. */
14
+ export declare function hasTopLevelKeyOutsideFence(text: string, key: string): boolean;
15
+ /** Detect a `[<table>]`, `[<table>.subkey]`, or matching array-of-table
16
+ * header outside the fence. Codex `[otel]` / `[features]` / `[hooks]`
17
+ * tables collide with the fence we'd write. */
18
+ export declare function hasTableOutsideFence(text: string, table: string): boolean;
19
+ /** Quote a string as a TOML basic string. Escape backslash, double quote,
20
+ * and control characters so the output is always single-line safe. */
21
+ export declare function quoted(s: string): string;
22
+ //# sourceMappingURL=codex-mini-toml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-mini-toml.d.ts","sourceRoot":"","sources":["../src/codex-mini-toml.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,UAAU,8CAA8C,CAAC;AACtE,eAAO,MAAM,WAAW,8CAA8C,CAAC;AAEvE;;;yBAGyB;AACzB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAwBpE;AAED;2BAC2B;AAC3B,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED;;oDAEoD;AACpD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAkB7E;AAED;;gDAEgD;AAChD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAczE;AAED;uEACuE;AACvE,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAcxC"}
@@ -0,0 +1,209 @@
1
+ // codex-mini-toml.ts — minimal lossless TOML editor for ~/.codex/config.toml.
2
+ //
3
+ // Direct port of apple/AgentDeck/Daemon/Core/MiniToml.swift. Must stay
4
+ // byte-compatible: the fence sentinels are shared between the App Store
5
+ // Swift daemon and the Node CLI bridge so a config installed by either
6
+ // side can be uninstalled / re-applied by the other.
7
+ //
8
+ // We deliberately do NOT parse TOML semantically. Codex configs contain
9
+ // user-authored keys, comments, profile tables, and MCP server tables that
10
+ // we have no business round-tripping through a semantic serializer.
11
+ // AgentDeck-managed entries live inside a fenced block:
12
+ //
13
+ // # >>> AgentDeck managed (do not edit) <<<
14
+ // <our keys>
15
+ // # <<< AgentDeck managed (do not edit) >>>
16
+ //
17
+ // applyManagedBlock replaces (or appends) the fence; removeManagedBlock
18
+ // strips it. Everything outside the fence is preserved byte-for-byte.
19
+ export const OPEN_FENCE = '# >>> AgentDeck managed (do not edit) <<<';
20
+ export const CLOSE_FENCE = '# <<< AgentDeck managed (do not edit) >>>';
21
+ /** Replace the AgentDeck-managed fenced block (or append one when none
22
+ * exists). The body is wrapped between OPEN_FENCE / CLOSE_FENCE so
23
+ * removeManagedBlock can strip it cleanly later. Returns the full
24
+ * updated TOML text. */
25
+ export function applyManagedBlock(text, body) {
26
+ const lines = splitLines(text);
27
+ const fenceRange = locateFence(lines);
28
+ const bodyLines = body.length === 0 ? [] : splitLines(body);
29
+ let replacement = [OPEN_FENCE, ...bodyLines, CLOSE_FENCE];
30
+ if (fenceRange) {
31
+ const preservedHookState = extractCodexHookState(lines.slice(fenceRange.start + 1, fenceRange.end - 1));
32
+ if (preservedHookState.length > 0) {
33
+ replacement = [...replacement, '', ...preservedHookState];
34
+ }
35
+ lines.splice(fenceRange.start, fenceRange.end - fenceRange.start, ...replacement);
36
+ }
37
+ else {
38
+ // Pad with a blank line for readability when appending to a non-empty
39
+ // file. Avoids glueing our fence onto the user's last key.
40
+ if (lines.length > 0 && lines[lines.length - 1] !== '') {
41
+ lines.push('');
42
+ }
43
+ lines.push(...replacement);
44
+ }
45
+ return lines.join('\n');
46
+ }
47
+ /** Strip the AgentDeck-managed block entirely. Idempotent — no-op when
48
+ * the fence is absent. */
49
+ export function removeManagedBlock(text) {
50
+ const lines = splitLines(text);
51
+ const fenceRange = locateFence(lines);
52
+ if (!fenceRange)
53
+ return text;
54
+ lines.splice(fenceRange.start, fenceRange.end - fenceRange.start);
55
+ // Collapse a trailing blank line that we may have inserted in
56
+ // applyManagedBlock so removeManagedBlock truly returns the file to
57
+ // its pre-apply shape.
58
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
59
+ lines.pop();
60
+ }
61
+ // Restore a single trailing newline if the original ended with one.
62
+ if (text.endsWith('\n'))
63
+ lines.push('');
64
+ return lines.join('\n');
65
+ }
66
+ /** Detect a top-level `<key> = ...` definition outside the fence. Codex
67
+ * `notify` is a top-level key; if the user already wrote one our fenced
68
+ * `notify` would be a duplicate-key TOML error. */
69
+ export function hasTopLevelKeyOutsideFence(text, key) {
70
+ const escaped = escapeRegex(key);
71
+ const regex = new RegExp(`^\\s*${escaped}\\s*=`);
72
+ let insideFence = false;
73
+ let insideTable = false;
74
+ for (const line of splitLines(text)) {
75
+ if (line === OPEN_FENCE) {
76
+ insideFence = true;
77
+ continue;
78
+ }
79
+ if (line === CLOSE_FENCE) {
80
+ insideFence = false;
81
+ continue;
82
+ }
83
+ if (insideFence)
84
+ continue;
85
+ const trimmed = line.trim();
86
+ if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
87
+ insideTable = true;
88
+ continue;
89
+ }
90
+ if (insideTable)
91
+ continue;
92
+ if (regex.test(line))
93
+ return true;
94
+ }
95
+ return false;
96
+ }
97
+ /** Detect a `[<table>]`, `[<table>.subkey]`, or matching array-of-table
98
+ * header outside the fence. Codex `[otel]` / `[features]` / `[hooks]`
99
+ * tables collide with the fence we'd write. */
100
+ export function hasTableOutsideFence(text, table) {
101
+ const escaped = escapeRegex(table);
102
+ // Match exactly `[otel]`, `[otel.something]`, `[[otel.something]]`,
103
+ // but not `[otelfoo]`. Whitespace inside brackets is permissive.
104
+ const regex = new RegExp(`^\\s*\\[\\[?\\s*${escaped}(\\.[A-Za-z0-9_\\-]+)*\\s*\\]\\]?\\s*$`);
105
+ let insideFence = false;
106
+ for (const line of splitLines(text)) {
107
+ if (line === OPEN_FENCE) {
108
+ insideFence = true;
109
+ continue;
110
+ }
111
+ if (line === CLOSE_FENCE) {
112
+ insideFence = false;
113
+ continue;
114
+ }
115
+ if (insideFence)
116
+ continue;
117
+ if (table === 'hooks' && isCodexHookStateHeader(line))
118
+ continue;
119
+ if (regex.test(line))
120
+ return true;
121
+ }
122
+ return false;
123
+ }
124
+ /** Quote a string as a TOML basic string. Escape backslash, double quote,
125
+ * and control characters so the output is always single-line safe. */
126
+ export function quoted(s) {
127
+ let out = '"';
128
+ for (const ch of s) {
129
+ const cp = ch.codePointAt(0);
130
+ if (ch === '\\')
131
+ out += '\\\\';
132
+ else if (ch === '"')
133
+ out += '\\"';
134
+ else if (ch === '\n')
135
+ out += '\\n';
136
+ else if (ch === '\r')
137
+ out += '\\r';
138
+ else if (ch === '\t')
139
+ out += '\\t';
140
+ else if (cp < 0x20)
141
+ out += `\\u${cp.toString(16).padStart(4, '0')}`;
142
+ else
143
+ out += ch;
144
+ }
145
+ out += '"';
146
+ return out;
147
+ }
148
+ // ─── internals ──────────────────────────────────────────────────────────
149
+ function splitLines(text) {
150
+ // String.split('\n') keeps trailing-empty so an input ending in "\n"
151
+ // round-trips cleanly when re-joined with "\n".
152
+ return text.split('\n');
153
+ }
154
+ function locateFence(lines) {
155
+ const start = lines.indexOf(OPEN_FENCE);
156
+ if (start === -1)
157
+ return null;
158
+ // Find first close fence at-or-after start. Defensive against truncated
159
+ // files: if no close fence is found, treat everything from the open
160
+ // fence to the end as managed.
161
+ let end = -1;
162
+ for (let i = start; i < lines.length; i++) {
163
+ if (lines[i] === CLOSE_FENCE) {
164
+ end = i;
165
+ break;
166
+ }
167
+ }
168
+ if (end === -1)
169
+ end = lines.length - 1;
170
+ return { start, end: end + 1 };
171
+ }
172
+ function escapeRegex(s) {
173
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
174
+ }
175
+ function extractCodexHookState(lines) {
176
+ const out = [];
177
+ let capturing = false;
178
+ for (const line of lines) {
179
+ const tableHeader = isTableHeader(line);
180
+ if (isCodexHookStateHeader(line)) {
181
+ capturing = true;
182
+ out.push(line);
183
+ continue;
184
+ }
185
+ if (capturing && tableHeader) {
186
+ break;
187
+ }
188
+ if (capturing) {
189
+ out.push(line);
190
+ }
191
+ }
192
+ while (out.length > 0 && isTrailingNonDataLine(out[out.length - 1])) {
193
+ out.pop();
194
+ }
195
+ return out;
196
+ }
197
+ function isCodexHookStateHeader(line) {
198
+ const trimmed = line.trim();
199
+ return trimmed === '[hooks.state]' || (trimmed.startsWith('[hooks.state.') && trimmed.endsWith(']'));
200
+ }
201
+ function isTableHeader(line) {
202
+ const trimmed = line.trim();
203
+ return trimmed.startsWith('[') && trimmed.endsWith(']');
204
+ }
205
+ function isTrailingNonDataLine(line) {
206
+ const trimmed = line.trim();
207
+ return trimmed.length === 0 || trimmed.startsWith('#');
208
+ }
209
+ //# sourceMappingURL=codex-mini-toml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-mini-toml.js","sourceRoot":"","sources":["../src/codex-mini-toml.ts"],"names":[],"mappings":"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"}
package/dist/install.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ export { installCodexHooksIfNeeded, uninstallCodexHooks, migrateCodexHooks, managedBlockBody as codexManagedBlockBody, DEFAULT_CODEX_CONFIG_PATH, } from './codex-install.js';
2
+ export type { InstallOptions as CodexInstallOptions, InstallResult as CodexInstallResult } from './codex-install.js';
3
+ export { installOpenCodeHooksIfNeeded, uninstallOpenCodeHooks, opencodePluginPath, opencodePluginSource, } from './opencode-install.js';
4
+ export type { OpenCodeInstallOptions, OpenCodeInstallResult } from './opencode-install.js';
1
5
  export declare const HOOK_EVENTS: readonly ["SessionStart", "SessionEnd", "PreToolUse", "PostToolUse", "Stop", "Notification", "UserPromptSubmit"];
2
6
  /**
3
7
  * Shell snippet that resolves the AgentDeck daemon's HTTP port at hook
@@ -10,15 +14,31 @@ export declare const HOOK_EVENTS: readonly ["SessionStart", "SessionEnd", "PreTo
10
14
  * so the hook targets the *owning* daemon even when several daemons
11
15
  * are running in parallel)
12
16
  * 2. `~/.agentdeck/daemon.json` (CLI)
13
- * 3. App Store group container `daemon.json` (Swift)
14
- * 4. `9120` fallback (legacy, never reached in practice)
17
+ * 3. App Store sandbox container `daemon.json` (Swift)
18
+ * 4. Legacy App Store group container `daemon.json` (Swift)
19
+ * 5. `9120` fallback (legacy, never reached in practice)
15
20
  *
16
- * For (2) and (3) the snippet prefers the daemon's `httpPort` field when
21
+ * For (2)-(4) the snippet prefers the daemon's `httpPort` field when
17
22
  * present — Swift runs WS and HTTP on separate ports. Each candidate is
18
23
  * verified with a short `/health` probe so a stale daemon.json from a
19
24
  * crashed daemon doesn't swallow the hook.
20
25
  */
21
26
  export declare function buildHookCommand(eventName: string): string;
27
+ /**
28
+ * Windows variant of `buildHookCommand`. Claude Code v2.1+ executes hook
29
+ * commands through `cmd.exe` on Windows, so we shell out to PowerShell for
30
+ * the JSON read + HTTP POST. Discovery is narrower than POSIX since the
31
+ * macOS App Store sandbox paths don't exist on Windows:
32
+ *
33
+ * 1. `$env:AGENTDECK_PORT`
34
+ * 2. `%USERPROFILE%\.agentdeck\daemon.json` (verified with `/health` probe)
35
+ * 3. `9120` fallback
36
+ *
37
+ * Single quotes are used inside the PowerShell script so the entire `-Command`
38
+ * argument can stay double-quoted under cmd.exe. Errors are swallowed so a
39
+ * dead daemon never blocks the host session.
40
+ */
41
+ export declare function buildHookCommandWin(eventName: string): string;
22
42
  export declare function buildHookEntry(eventName: string): {
23
43
  matcher: string;
24
44
  hooks: any[];
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,kHAQd,CAAC;AAEX;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAa1D;AAGD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM;;;EAa/C;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAuB7C;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAwB9C;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,GAAG,GAAG;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAyChF;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,IAAI,IAAI,CAkBnC;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,IAAI,IAAI,CASrC;AAED;iEACiE;AACjE,wBAAgB,oBAAoB,IAAI,IAAI,CAwB3C"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,IAAI,qBAAqB,EACzC,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,cAAc,IAAI,mBAAmB,EAAE,aAAa,IAAI,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAIrH,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE3F,eAAO,MAAM,WAAW,kHAQd,CAAC;AAEX;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA0B1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAe7D;AAGD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM;;;EAgB/C;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAuB7C;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAwB9C;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,GAAG,GAAG;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAyChF;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,IAAI,IAAI,CAkBnC;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,IAAI,IAAI,CASrC;AAED;iEACiE;AACjE,wBAAgB,oBAAoB,IAAI,IAAI,CAwB3C"}
package/dist/install.js CHANGED
@@ -1,6 +1,12 @@
1
1
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { homedir } from 'os';
4
+ // Re-export the Codex installer surface so callers reach it via the
5
+ // canonical `@agentdeck/hooks` entry point alongside the Claude installer.
6
+ export { installCodexHooksIfNeeded, uninstallCodexHooks, migrateCodexHooks, managedBlockBody as codexManagedBlockBody, DEFAULT_CODEX_CONFIG_PATH, } from './codex-install.js';
7
+ // Re-export the OpenCode plugin installer (standalone-session observation
8
+ // via opencode_* lifecycle hooks) through the same canonical entry point.
9
+ export { installOpenCodeHooksIfNeeded, uninstallOpenCodeHooks, opencodePluginPath, opencodePluginSource, } from './opencode-install.js';
4
10
  export const HOOK_EVENTS = [
5
11
  'SessionStart',
6
12
  'SessionEnd',
@@ -21,33 +27,80 @@ export const HOOK_EVENTS = [
21
27
  * so the hook targets the *owning* daemon even when several daemons
22
28
  * are running in parallel)
23
29
  * 2. `~/.agentdeck/daemon.json` (CLI)
24
- * 3. App Store group container `daemon.json` (Swift)
25
- * 4. `9120` fallback (legacy, never reached in practice)
30
+ * 3. App Store sandbox container `daemon.json` (Swift)
31
+ * 4. Legacy App Store group container `daemon.json` (Swift)
32
+ * 5. `9120` fallback (legacy, never reached in practice)
26
33
  *
27
- * For (2) and (3) the snippet prefers the daemon's `httpPort` field when
34
+ * For (2)-(4) the snippet prefers the daemon's `httpPort` field when
28
35
  * present — Swift runs WS and HTTP on separate ports. Each candidate is
29
36
  * verified with a short `/health` probe so a stale daemon.json from a
30
37
  * crashed daemon doesn't swallow the hook.
31
38
  */
32
39
  export function buildHookCommand(eventName) {
33
- return [
40
+ const preamble = [
34
41
  `PORT="\${AGENTDECK_PORT:-}"`,
35
42
  `if [ -z "$PORT" ]; then`,
36
- ` for F in "$HOME/.agentdeck/daemon.json" "$HOME/Library/Group Containers/group.bound.serendipity.agentdeck.dashboard/daemon.json"; do`,
43
+ ` for F in "$HOME/.agentdeck/daemon.json" "$HOME/Library/Containers/bound.serendipity.agent.deck/Data/Library/Application Support/AgentDeck/daemon.json" "$HOME/Library/Group Containers/group.bound.serendipity.agent.deck/daemon.json"; do`,
37
44
  ` [ -f "$F" ] || continue`,
38
45
  ` P=$(python3 -c "import json;d=json.load(open('$F'));print(d.get('httpPort') or d.get('port',''))" 2>/dev/null)`,
39
46
  ` [ -n "$P" ] && curl -sf --max-time 0.3 "http://127.0.0.1:$P/health" >/dev/null 2>&1 && { PORT="$P"; break; }`,
40
47
  ` done`,
41
48
  `fi`,
42
49
  `PORT="\${PORT:-9120}"`,
50
+ ];
51
+ // PreToolUse is request-response: the daemon may hold the connection open and
52
+ // return a permission decision (device approval). Capture the body and echo it
53
+ // to stdout so Claude can gate the tool; empty output (timeout/error/disabled)
54
+ // = Claude's normal permission flow. `--max-time 60` exceeds the daemon's
55
+ // internal 45s "ask" fallback so that fallback reaches Claude before curl quits.
56
+ if (eventName === 'PreToolUse') {
57
+ return preamble.concat([
58
+ `RESP=$(curl -s -X POST "http://127.0.0.1:$PORT/hooks/PreToolUse" -H 'Content-Type: application/json' --max-time 60 -d @- 2>/dev/null)`,
59
+ `printf '%s' "\${RESP:-}"`,
60
+ ]).join('\n');
61
+ }
62
+ return preamble.concat([
43
63
  `curl -sf -X POST "http://127.0.0.1:$PORT/hooks/${eventName}" -H 'Content-Type: application/json' -d @- 2>/dev/null || true`,
44
- ].join('\n');
64
+ ]).join('\n');
65
+ }
66
+ /**
67
+ * Windows variant of `buildHookCommand`. Claude Code v2.1+ executes hook
68
+ * commands through `cmd.exe` on Windows, so we shell out to PowerShell for
69
+ * the JSON read + HTTP POST. Discovery is narrower than POSIX since the
70
+ * macOS App Store sandbox paths don't exist on Windows:
71
+ *
72
+ * 1. `$env:AGENTDECK_PORT`
73
+ * 2. `%USERPROFILE%\.agentdeck\daemon.json` (verified with `/health` probe)
74
+ * 3. `9120` fallback
75
+ *
76
+ * Single quotes are used inside the PowerShell script so the entire `-Command`
77
+ * argument can stay double-quoted under cmd.exe. Errors are swallowed so a
78
+ * dead daemon never blocks the host session.
79
+ */
80
+ export function buildHookCommandWin(eventName) {
81
+ const ps = [
82
+ `$ev='${eventName}'`,
83
+ `$port=$env:AGENTDECK_PORT`,
84
+ `if(-not $port){$f=Join-Path $env:USERPROFILE '.agentdeck\\daemon.json'; if(Test-Path $f){try{$d=Get-Content -Raw $f|ConvertFrom-Json; $p=if($d.httpPort){$d.httpPort}else{$d.port}; if($p){try{Invoke-RestMethod -Uri ('http://127.0.0.1:'+$p+'/health') -TimeoutSec 1 -ErrorAction Stop|Out-Null; $port=$p}catch{}}}catch{}}}`,
85
+ `if(-not $port){$port=9120}`,
86
+ // Read stdin as UTF-8: [Console]::In decodes piped stdin with the console OEM
87
+ // codepage (e.g. CP949), garbling non-ASCII payload text.
88
+ `$body=(New-Object System.IO.StreamReader([Console]::OpenStandardInput(),[System.Text.Encoding]::UTF8)).ReadToEnd()`,
89
+ // Post UTF-8 bytes: Invoke-RestMethod encodes a string body as ISO-8859-1 when
90
+ // the content type carries no charset, replacing non-ASCII characters with '?'.
91
+ `$bytes=[System.Text.Encoding]::UTF8.GetBytes([string]$body)`,
92
+ `try{Invoke-RestMethod -Uri ('http://127.0.0.1:'+$port+'/hooks/'+$ev) -Method Post -Body $bytes -ContentType 'application/json; charset=utf-8' -TimeoutSec 2 -ErrorAction Stop|Out-Null}catch{}`,
93
+ ].join('; ');
94
+ return `powershell -NoProfile -ExecutionPolicy Bypass -Command "${ps}"`;
45
95
  }
46
96
  // Claude Code v2.1+ requires 3-level nesting: event → matcher group → hook handler.
47
97
  export function buildHookEntry(eventName) {
98
+ const command = process.platform === 'win32'
99
+ ? buildHookCommandWin(eventName)
100
+ : buildHookCommand(eventName);
48
101
  const handler = {
49
102
  type: 'command',
50
- command: buildHookCommand(eventName),
103
+ command,
51
104
  };
52
105
  // Tool-specific hooks (PreToolUse, PostToolUse) need a glob matcher to fire.
53
106
  // Empty string "" means "match nothing" for tool events — use "" for non-tool
@@ -194,12 +247,22 @@ export function migrateHooksIfNeeded() {
194
247
  }
195
248
  }
196
249
  // CLI execution
197
- const isMainModule = import.meta.url === `file://${process.argv[1]}` ||
198
- import.meta.url === new URL(process.argv[1], 'file://').href;
250
+ // Use pathToFileURL so the comparison is correct on Windows too — process.argv[1]
251
+ // is a native path (E:\dev\...\install.js), but import.meta.url is a file:// URL
252
+ // (file:///E:/dev/.../install.js). Manually building `file://${argv[1]}` only
253
+ // matches on POSIX, which is why the installer used to be a silent no-op on
254
+ // Windows.
255
+ import { pathToFileURL } from 'url';
256
+ const isMainModule = process.argv[1] ? import.meta.url === pathToFileURL(process.argv[1]).href : false;
199
257
  if (isMainModule) {
200
258
  const action = process.argv[2] || 'install';
201
259
  if (action === 'uninstall') {
202
260
  uninstallHooks();
261
+ // The OpenCode observer plugin is AgentDeck-owned in its entirety, so
262
+ // uninstall removes the file (unlike ~/.codex/config.toml, where only
263
+ // the fenced block is AgentDeck's and removal has its own dedicated
264
+ // flow to avoid touching user TOML).
265
+ import('./opencode-install.js').then((m) => m.uninstallOpenCodeHooks()).catch(() => { });
203
266
  }
204
267
  else {
205
268
  installHooks();
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export declare const OPENCODE_PLUGIN_FILENAME = "agentdeck.js";
2
+ /** OpenCode's global config root: `$XDG_CONFIG_HOME/opencode` falling back
3
+ * to `~/.config/opencode`. Plugins load from the `plugins/` subdirectory
4
+ * (current OpenCode releases; older builds also scanned `plugin/`). */
5
+ export declare function opencodePluginPath(): string;
6
+ export interface OpenCodeInstallOptions {
7
+ /** Override the plugin file path (tests + non-default homes). */
8
+ pluginPath?: string;
9
+ }
10
+ export interface OpenCodeInstallResult {
11
+ installed: boolean;
12
+ /** Human-readable reason when `installed === false`. */
13
+ reason?: string;
14
+ }
15
+ /** Assemble the plugin source. Exported so tests can assert schema
16
+ * regressions (event names, payload fields, managed-session guard)
17
+ * without driving the file installer. */
18
+ export declare function opencodePluginSource(): string;
19
+ /** Install (or refresh) the AgentDeck OpenCode plugin. Idempotent: writes
20
+ * only when the on-disk content differs from the current template. Safe to
21
+ * call from setup, `agentdeck opencode`, and `agentdeck daemon install`. */
22
+ export declare function installOpenCodeHooksIfNeeded(opts?: OpenCodeInstallOptions): OpenCodeInstallResult;
23
+ /** Remove the AgentDeck OpenCode plugin. Idempotent — no-op when absent. */
24
+ export declare function uninstallOpenCodeHooks(opts?: OpenCodeInstallOptions): void;
25
+ //# sourceMappingURL=opencode-install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-install.d.ts","sourceRoot":"","sources":["../src/opencode-install.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,wBAAwB,iBAAiB,CAAC;AAEvD;;wEAEwE;AACxE,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAUD;;0CAE0C;AAC1C,wBAAgB,oBAAoB,IAAI,MAAM,CAwJ7C;AAeD;;6EAE6E;AAC7E,wBAAgB,4BAA4B,CAAC,IAAI,GAAE,sBAA2B,GAAG,qBAAqB,CAerG;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,sBAA2B,GAAG,IAAI,CAK9E"}
@@ -0,0 +1,241 @@
1
+ // opencode-install.ts — Install the AgentDeck observation plugin into
2
+ // OpenCode's global plugin directory so standalone `opencode` sessions
3
+ // report turn boundaries to the daemon as opencode_* lifecycle hooks.
4
+ //
5
+ // Why a plugin and not PTY parsing or an SSE attach: OpenCode has no
6
+ // lifecycle-hook config (unlike Claude Code / Codex), and a standalone
7
+ // `opencode` TUI does not listen on any TCP port (the embedded server
8
+ // only exists when launched with `--port`, i.e. managed sessions), so
9
+ // the documented plugin API is the only stable, update-resilient way to
10
+ // observe sessions the user starts outside `agentdeck opencode`. This is
11
+ // the hooks-first direction: no terminal-output parsing anywhere.
12
+ //
13
+ // The plugin file is wholly AgentDeck-owned (header sentinel + version).
14
+ // Install is idempotent: re-running writes only when content changed.
15
+ // Managed sessions are excluded inside the plugin itself via the
16
+ // AGENTDECK_PORT env marker (see plugin source below) — their session
17
+ // bridge already ingests the richer SSE stream, and double-posting would
18
+ // double-count every turn.
19
+ //
20
+ // Counterpart consumers:
21
+ // - bridge/src/daemon-server.ts /hooks/ handler (opencode_* → shared
22
+ // observed-session pipeline: APME run/turn + chat_start/chat_response)
23
+ // - apple .../DaemonServer.swift ignores opencode_* events (timeline
24
+ // parity on the Swift daemon is a documented follow-up).
25
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from 'fs';
26
+ import { dirname, join } from 'path';
27
+ import { homedir } from 'os';
28
+ export const OPENCODE_PLUGIN_FILENAME = 'agentdeck.js';
29
+ /** OpenCode's global config root: `$XDG_CONFIG_HOME/opencode` falling back
30
+ * to `~/.config/opencode`. Plugins load from the `plugins/` subdirectory
31
+ * (current OpenCode releases; older builds also scanned `plugin/`). */
32
+ export function opencodePluginPath() {
33
+ const xdg = process.env.XDG_CONFIG_HOME;
34
+ const configRoot = xdg && xdg.trim() ? xdg : join(homedir(), '.config');
35
+ return join(configRoot, 'opencode', 'plugins', OPENCODE_PLUGIN_FILENAME);
36
+ }
37
+ /** Honour `AGENTDECK_NO_OPENCODE_HOOKS=1` from the environment (same
38
+ * opt-out contract as AGENTDECK_NO_CODEX_HOOKS). */
39
+ function envOptOut() {
40
+ return process.env.AGENTDECK_NO_OPENCODE_HOOKS === '1';
41
+ }
42
+ const PLUGIN_VERSION = 1;
43
+ /** Assemble the plugin source. Exported so tests can assert schema
44
+ * regressions (event names, payload fields, managed-session guard)
45
+ * without driving the file installer. */
46
+ export function opencodePluginSource() {
47
+ return `// agentdeck-opencode-plugin v${PLUGIN_VERSION} — AUTO-GENERATED by AgentDeck.
48
+ // Do not edit: reinstalls overwrite this file. Remove it (or set
49
+ // AGENTDECK_NO_OPENCODE_HOOKS=1 before installing) to opt out.
50
+ //
51
+ // Forwards OpenCode lifecycle events to the AgentDeck daemon as
52
+ // opencode_* hooks — the same observed-session pipeline Claude Code and
53
+ // Codex use — so standalone \`opencode\` sessions appear on the AgentDeck
54
+ // timeline with the same prompt → response turn shape. Every POST is
55
+ // fire-and-forget with a hard timeout; OpenCode never blocks on AgentDeck.
56
+
57
+ import { readFileSync } from "node:fs";
58
+ import { homedir } from "node:os";
59
+ import { join } from "node:path";
60
+
61
+ const PORT_TTL_MS = 60000;
62
+ let cachedPort = null;
63
+ let cachedAt = 0;
64
+
65
+ async function resolvePort() {
66
+ const now = Date.now();
67
+ if (cachedPort && now - cachedAt < PORT_TTL_MS) return cachedPort;
68
+ const home = homedir();
69
+ const candidates = [];
70
+ for (const f of [
71
+ join(home, ".agentdeck", "daemon.json"),
72
+ join(home, "Library/Containers/bound.serendipity.agent.deck/Data/Library/Application Support/AgentDeck/daemon.json"),
73
+ join(home, "Library/Group Containers/group.bound.serendipity.agent.deck/daemon.json"),
74
+ ]) {
75
+ try {
76
+ const d = JSON.parse(readFileSync(f, "utf-8"));
77
+ const p = d.httpPort || d.port;
78
+ if (typeof p === "number" && p > 0 && !candidates.includes(p)) candidates.push(p);
79
+ } catch { /* missing/malformed — try next */ }
80
+ }
81
+ if (!candidates.includes(9120)) candidates.push(9120);
82
+ for (const p of candidates) {
83
+ try {
84
+ const r = await fetch("http://127.0.0.1:" + p + "/health", { signal: AbortSignal.timeout(300) });
85
+ if (r.ok) { cachedPort = p; cachedAt = now; return p; }
86
+ } catch { /* dead port — try next */ }
87
+ }
88
+ return null;
89
+ }
90
+
91
+ function post(event, body) {
92
+ resolvePort().then((port) => {
93
+ if (!port) return;
94
+ return fetch("http://127.0.0.1:" + port + "/hooks/" + event, {
95
+ method: "POST",
96
+ headers: { "Content-Type": "application/json" },
97
+ body: JSON.stringify(body),
98
+ signal: AbortSignal.timeout(800),
99
+ });
100
+ }).catch(() => { /* daemon down — observation is best-effort */ });
101
+ }
102
+
103
+ export const AgentDeckObserver = async ({ directory }) => {
104
+ // Managed \`agentdeck opencode\` sessions already stream structured SSE
105
+ // events through their session bridge; posting hooks too would
106
+ // double-count every turn. AGENTDECK_PORT marks a managed PTY.
107
+ if (process.env.AGENTDECK_PORT) return {};
108
+
109
+ const cwd = directory;
110
+ const announced = new Set(); // sessionIDs whose opencode_session_start was posted
111
+ const userMsgs = new Map(); // user messageID -> sessionID
112
+ const promptSent = new Set(); // user messageIDs whose prompt_submit was posted
113
+ const promptTimers = new Map();// user messageID -> flush timer
114
+ const responses = new Map(); // sessionID -> latest assistant text
115
+ const startedAt = new Map(); // sessionID -> turn start ms
116
+ const toolPhase = new Map(); // tool callID -> "start" | "end"
117
+
118
+ function announce(sessionID) {
119
+ if (!sessionID || announced.has(sessionID)) return;
120
+ announced.add(sessionID);
121
+ post("opencode_session_start", { session_id: sessionID, cwd });
122
+ }
123
+
124
+ function sendPrompt(messageID, sessionID, prompt) {
125
+ if (promptSent.has(messageID)) return;
126
+ promptSent.add(messageID);
127
+ const t = promptTimers.get(messageID);
128
+ if (t) { clearTimeout(t); promptTimers.delete(messageID); }
129
+ if (!startedAt.has(sessionID)) startedAt.set(sessionID, Date.now());
130
+ post("opencode_user_prompt_submit", { session_id: sessionID, prompt: prompt || "", cwd });
131
+ }
132
+
133
+ return {
134
+ event: async ({ event }) => {
135
+ try {
136
+ const type = event.type;
137
+ const props = event.properties || {};
138
+ if (type === "message.updated") {
139
+ const info = props.info || {};
140
+ const sessionID = info.sessionID;
141
+ announce(sessionID);
142
+ if (info.role === "user" && info.id) {
143
+ userMsgs.set(info.id, sessionID);
144
+ // Prompt text usually streams via message.part.updated moments
145
+ // later; flush immediately when the info carries it, otherwise
146
+ // wait briefly so the chat_start row shows the real prompt.
147
+ const direct = typeof info.text === "string" ? info.text
148
+ : (typeof info.content === "string" ? info.content : "");
149
+ if (direct.trim()) {
150
+ sendPrompt(info.id, sessionID, direct.trim());
151
+ } else if (!promptTimers.has(info.id) && !promptSent.has(info.id)) {
152
+ const timer = setTimeout(() => sendPrompt(info.id, sessionID, ""), 800);
153
+ if (typeof timer.unref === "function") timer.unref();
154
+ promptTimers.set(info.id, timer);
155
+ }
156
+ }
157
+ } else if (type === "message.part.updated") {
158
+ const part = props.part || {};
159
+ const sessionID = part.sessionID;
160
+ announce(sessionID);
161
+ if (part.type === "text" && typeof part.text === "string") {
162
+ const owner = userMsgs.get(part.messageID);
163
+ if (owner) sendPrompt(part.messageID, owner, part.text.trim());
164
+ else responses.set(sessionID, part.text);
165
+ } else if (part.type === "tool" && part.callID) {
166
+ const status = (part.state && part.state.status) || "running";
167
+ const phase = toolPhase.get(part.callID);
168
+ if (status === "completed" || status === "error") {
169
+ if (phase !== "end") {
170
+ toolPhase.set(part.callID, "end");
171
+ post("opencode_tool_end", { session_id: sessionID, tool_name: part.tool || "tool", cwd });
172
+ }
173
+ } else if (!phase) {
174
+ toolPhase.set(part.callID, "start");
175
+ post("opencode_tool_start", { session_id: sessionID, tool_name: part.tool || "tool", cwd });
176
+ }
177
+ if (toolPhase.size > 512) toolPhase.clear();
178
+ }
179
+ } else if (type === "session.idle") {
180
+ const sessionID = props.sessionID;
181
+ announce(sessionID);
182
+ post("opencode_stop", {
183
+ session_id: sessionID,
184
+ last_assistant_message: responses.get(sessionID) || "",
185
+ cwd,
186
+ });
187
+ responses.delete(sessionID);
188
+ startedAt.delete(sessionID);
189
+ for (const [mid, sid] of userMsgs) {
190
+ if (sid === sessionID) { userMsgs.delete(mid); promptSent.delete(mid); }
191
+ }
192
+ }
193
+ } catch { /* never throw into OpenCode's event loop */ }
194
+ },
195
+ };
196
+ };
197
+ `;
198
+ }
199
+ function writeTextAtomic(text, path) {
200
+ const dir = dirname(path);
201
+ try {
202
+ if (!existsSync(dir))
203
+ mkdirSync(dir, { recursive: true });
204
+ const tmp = `${path}.agentdeck.tmp`;
205
+ writeFileSync(tmp, text, 'utf-8');
206
+ renameSync(tmp, path);
207
+ return true;
208
+ }
209
+ catch {
210
+ return false;
211
+ }
212
+ }
213
+ /** Install (or refresh) the AgentDeck OpenCode plugin. Idempotent: writes
214
+ * only when the on-disk content differs from the current template. Safe to
215
+ * call from setup, `agentdeck opencode`, and `agentdeck daemon install`. */
216
+ export function installOpenCodeHooksIfNeeded(opts = {}) {
217
+ if (envOptOut())
218
+ return { installed: false, reason: 'AGENTDECK_NO_OPENCODE_HOOKS=1' };
219
+ const path = opts.pluginPath ?? opencodePluginPath();
220
+ const desired = opencodePluginSource();
221
+ try {
222
+ if (existsSync(path) && readFileSync(path, 'utf-8') === desired) {
223
+ return { installed: true };
224
+ }
225
+ }
226
+ catch { /* unreadable — rewrite below */ }
227
+ if (writeTextAtomic(desired, path)) {
228
+ return { installed: true };
229
+ }
230
+ return { installed: false, reason: `write failed: ${path}` };
231
+ }
232
+ /** Remove the AgentDeck OpenCode plugin. Idempotent — no-op when absent. */
233
+ export function uninstallOpenCodeHooks(opts = {}) {
234
+ const path = opts.pluginPath ?? opencodePluginPath();
235
+ try {
236
+ if (existsSync(path))
237
+ unlinkSync(path);
238
+ }
239
+ catch { /* best-effort */ }
240
+ }
241
+ //# sourceMappingURL=opencode-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-install.js","sourceRoot":"","sources":["../src/opencode-install.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,8 @@
1
1
  {
2
2
  "name": "@agentdeck/hooks",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
+ "private": false,
4
5
  "type": "module",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/puritysb/AgentDeck.git",
9
- "directory": "hooks"
10
- },
11
6
  "files": [
12
7
  "dist",
13
8
  "!dist/__tests__"