ai-obs-cli 0.0.0-stage → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +666 -2
  2. package/bin/ai-obs.js +8 -0
  3. package/dist/adapters/claude-code/analyze.js +189 -0
  4. package/dist/adapters/claude-code/analyze.js.map +1 -0
  5. package/dist/adapters/claude-code/detect.js +60 -0
  6. package/dist/adapters/claude-code/detect.js.map +1 -0
  7. package/dist/adapters/claude-code/handlers.js +183 -0
  8. package/dist/adapters/claude-code/handlers.js.map +1 -0
  9. package/dist/adapters/claude-code/index.js +55 -0
  10. package/dist/adapters/claude-code/index.js.map +1 -0
  11. package/dist/adapters/claude-code/run-links.js +53 -0
  12. package/dist/adapters/claude-code/run-links.js.map +1 -0
  13. package/dist/adapters/claude-code/session.js +427 -0
  14. package/dist/adapters/claude-code/session.js.map +1 -0
  15. package/dist/adapters/claude-code/settings.js +161 -0
  16. package/dist/adapters/claude-code/settings.js.map +1 -0
  17. package/dist/adapters/claude-code/state.js +66 -0
  18. package/dist/adapters/claude-code/state.js.map +1 -0
  19. package/dist/adapters/claude-code/subagents.js +246 -0
  20. package/dist/adapters/claude-code/subagents.js.map +1 -0
  21. package/dist/adapters/claude-code/transcript.js +309 -0
  22. package/dist/adapters/claude-code/transcript.js.map +1 -0
  23. package/dist/adapters/codex/accumulator.js +135 -0
  24. package/dist/adapters/codex/accumulator.js.map +1 -0
  25. package/dist/adapters/codex/detect.js +93 -0
  26. package/dist/adapters/codex/detect.js.map +1 -0
  27. package/dist/adapters/codex/hooks-config.js +158 -0
  28. package/dist/adapters/codex/hooks-config.js.map +1 -0
  29. package/dist/adapters/codex/index.js +270 -0
  30. package/dist/adapters/codex/index.js.map +1 -0
  31. package/dist/adapters/codex/locate.js +72 -0
  32. package/dist/adapters/codex/locate.js.map +1 -0
  33. package/dist/adapters/codex/records.js +194 -0
  34. package/dist/adapters/codex/records.js.map +1 -0
  35. package/dist/adapters/codex/rollout.js +72 -0
  36. package/dist/adapters/codex/rollout.js.map +1 -0
  37. package/dist/adapters/codex/session.js +212 -0
  38. package/dist/adapters/codex/session.js.map +1 -0
  39. package/dist/adapters/codex/subagents.js +139 -0
  40. package/dist/adapters/codex/subagents.js.map +1 -0
  41. package/dist/adapters/codex/summarize.js +84 -0
  42. package/dist/adapters/codex/summarize.js.map +1 -0
  43. package/dist/adapters/codex/tools.js +145 -0
  44. package/dist/adapters/codex/tools.js.map +1 -0
  45. package/dist/adapters/hook-command.js +94 -0
  46. package/dist/adapters/hook-command.js.map +1 -0
  47. package/dist/adapters/index.js +12 -0
  48. package/dist/adapters/index.js.map +1 -0
  49. package/dist/adapters/types.js +2 -0
  50. package/dist/adapters/types.js.map +1 -0
  51. package/dist/analysis/corrections.js +79 -0
  52. package/dist/analysis/corrections.js.map +1 -0
  53. package/dist/analysis/digest.js +16 -0
  54. package/dist/analysis/digest.js.map +1 -0
  55. package/dist/analysis/outcome.js +76 -0
  56. package/dist/analysis/outcome.js.map +1 -0
  57. package/dist/analysis/phases.js +203 -0
  58. package/dist/analysis/phases.js.map +1 -0
  59. package/dist/analysis/summaries.js +350 -0
  60. package/dist/analysis/summaries.js.map +1 -0
  61. package/dist/analysis/tokens.js +67 -0
  62. package/dist/analysis/tokens.js.map +1 -0
  63. package/dist/analysis/usage.js +229 -0
  64. package/dist/analysis/usage.js.map +1 -0
  65. package/dist/cc-cache.js +81 -0
  66. package/dist/cc-cache.js.map +1 -0
  67. package/dist/cli.js +128 -0
  68. package/dist/cli.js.map +1 -0
  69. package/dist/commands/common.js +235 -0
  70. package/dist/commands/common.js.map +1 -0
  71. package/dist/commands/doctor.js +193 -0
  72. package/dist/commands/doctor.js.map +1 -0
  73. package/dist/commands/flush.js +131 -0
  74. package/dist/commands/flush.js.map +1 -0
  75. package/dist/commands/init.js +119 -0
  76. package/dist/commands/init.js.map +1 -0
  77. package/dist/commands/link.js +171 -0
  78. package/dist/commands/link.js.map +1 -0
  79. package/dist/commands/live.js +130 -0
  80. package/dist/commands/live.js.map +1 -0
  81. package/dist/commands/login.js +397 -0
  82. package/dist/commands/login.js.map +1 -0
  83. package/dist/commands/status.js +155 -0
  84. package/dist/commands/status.js.map +1 -0
  85. package/dist/commands/token.js +92 -0
  86. package/dist/commands/token.js.map +1 -0
  87. package/dist/commands/uninstall.js +56 -0
  88. package/dist/commands/uninstall.js.map +1 -0
  89. package/dist/config.js +305 -0
  90. package/dist/config.js.map +1 -0
  91. package/dist/context.js +109 -0
  92. package/dist/context.js.map +1 -0
  93. package/dist/delivery.js +151 -0
  94. package/dist/delivery.js.map +1 -0
  95. package/dist/dotfile.js +187 -0
  96. package/dist/dotfile.js.map +1 -0
  97. package/dist/events.js +259 -0
  98. package/dist/events.js.map +1 -0
  99. package/dist/fsutil.js +50 -0
  100. package/dist/fsutil.js.map +1 -0
  101. package/dist/git.js +139 -0
  102. package/dist/git.js.map +1 -0
  103. package/dist/hook.js +147 -0
  104. package/dist/hook.js.map +1 -0
  105. package/dist/http.js +35 -0
  106. package/dist/http.js.map +1 -0
  107. package/dist/jobs.js +53 -0
  108. package/dist/jobs.js.map +1 -0
  109. package/dist/live/claude-code.js +226 -0
  110. package/dist/live/claude-code.js.map +1 -0
  111. package/dist/live/codex.js +91 -0
  112. package/dist/live/codex.js.map +1 -0
  113. package/dist/live/monitor.js +229 -0
  114. package/dist/live/monitor.js.map +1 -0
  115. package/dist/live/render.js +92 -0
  116. package/dist/live/render.js.map +1 -0
  117. package/dist/lock.js +93 -0
  118. package/dist/lock.js.map +1 -0
  119. package/dist/log.js +36 -0
  120. package/dist/log.js.map +1 -0
  121. package/dist/migrate.js +109 -0
  122. package/dist/migrate.js.map +1 -0
  123. package/dist/nudge.js +231 -0
  124. package/dist/nudge.js.map +1 -0
  125. package/dist/paths.js +73 -0
  126. package/dist/paths.js.map +1 -0
  127. package/dist/pk-cache.js +110 -0
  128. package/dist/pk-cache.js.map +1 -0
  129. package/dist/prices.js +45 -0
  130. package/dist/prices.js.map +1 -0
  131. package/dist/project-key.js +144 -0
  132. package/dist/project-key.js.map +1 -0
  133. package/dist/protocol-types.js +30 -0
  134. package/dist/protocol-types.js.map +1 -0
  135. package/dist/queue.js +649 -0
  136. package/dist/queue.js.map +1 -0
  137. package/dist/routing.js +122 -0
  138. package/dist/routing.js.map +1 -0
  139. package/dist/runtime.js +54 -0
  140. package/dist/runtime.js.map +1 -0
  141. package/dist/sender.js +559 -0
  142. package/dist/sender.js.map +1 -0
  143. package/dist/state.js +59 -0
  144. package/dist/state.js.map +1 -0
  145. package/dist/ulid.js +73 -0
  146. package/dist/ulid.js.map +1 -0
  147. package/dist/version.js +26 -0
  148. package/dist/version.js.map +1 -0
  149. package/dist/warnings.js +71 -0
  150. package/dist/warnings.js.map +1 -0
  151. package/dist/worker-main.js +14 -0
  152. package/dist/worker-main.js.map +1 -0
  153. package/dist/worker.js +295 -0
  154. package/dist/worker.js.map +1 -0
  155. package/package.json +25 -4
package/dist/fsutil.js ADDED
@@ -0,0 +1,50 @@
1
+ import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { randomBytes } from 'node:crypto';
4
+ /** mkdir -p with owner-only permissions. */
5
+ export function ensureDir(dir) {
6
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
7
+ }
8
+ /** Writes via a temp file + rename so readers never see a partial file. Mode defaults to 0600. */
9
+ export function writeFileAtomic(path, data, mode = 0o600) {
10
+ ensureDir(dirname(path));
11
+ const tmp = `${path}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`;
12
+ try {
13
+ writeFileSync(tmp, data, { mode });
14
+ chmodSync(tmp, mode);
15
+ renameSync(tmp, path);
16
+ }
17
+ catch (err) {
18
+ rmSync(tmp, { force: true });
19
+ throw err;
20
+ }
21
+ }
22
+ export function writeJsonAtomic(path, value, mode = 0o600) {
23
+ writeFileAtomic(path, `${JSON.stringify(value, null, 2)}\n`, mode);
24
+ }
25
+ /** Reads and parses a JSON file; null when missing or unparsable (never throws). */
26
+ export function readJsonFile(path) {
27
+ try {
28
+ return JSON.parse(readFileSync(path, 'utf8'));
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ export function errorMessage(err) {
35
+ if (err instanceof Error)
36
+ return err.message;
37
+ return String(err);
38
+ }
39
+ /** Rejects when `p` has not settled after `ms` (it is not cancelled: it keeps running in the background). */
40
+ export function withTimeout(p, ms, what) {
41
+ let timer;
42
+ return Promise.race([
43
+ p.finally(() => clearTimeout(timer)),
44
+ new Promise((_, reject) => {
45
+ timer = setTimeout(() => reject(new Error(`${what} timed out after ${ms} ms`)), ms);
46
+ timer.unref?.();
47
+ }),
48
+ ]);
49
+ }
50
+ //# sourceMappingURL=fsutil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fsutil.js","sourceRoot":"","sources":["../src/fsutil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,4CAA4C;AAC5C,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAyB,EAAE,IAAI,GAAG,KAAK;IACnF,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3E,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7B,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAc,EAAE,IAAI,GAAG,KAAK;IACxE,eAAe,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrE,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAAI,IAAY;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,GAAG,YAAY,KAAK;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC7C,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,WAAW,CAAI,CAAa,EAAE,EAAU,EAAE,IAAY;IACpE,IAAI,KAAqB,CAAC;IAC1B,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YAC3B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,IAAI,oBAAoB,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpF,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAClB,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC"}
package/dist/git.js ADDED
@@ -0,0 +1,139 @@
1
+ import { execFile, spawn } from 'node:child_process';
2
+ import { realpathSync } from 'node:fs';
3
+ import { basename, dirname, resolve as resolvePath } from 'node:path';
4
+ const DEFAULT_TIMEOUT_MS = 1500;
5
+ const MAX_OUTPUT = 256 * 1024;
6
+ const gitEnv = () => ({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
7
+ /** Runs git (execFile; on timeout it waits for the child to close). Never throws. */
8
+ export function runGitResult(args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS) {
9
+ return new Promise((resolve) => {
10
+ try {
11
+ execFile('git', args, { cwd, timeout: timeoutMs, windowsHide: true, maxBuffer: MAX_OUTPUT, env: gitEnv() }, (err, stdout) => {
12
+ if (err)
13
+ resolve({ out: null, timedOut: err.killed === true });
14
+ else
15
+ resolve({ out: String(stdout).trim() || null, timedOut: false });
16
+ });
17
+ }
18
+ catch {
19
+ resolve({ out: null, timedOut: false });
20
+ }
21
+ });
22
+ }
23
+ /** Runs git; resolves trimmed stdout, or null on any failure / timeout. Never throws. */
24
+ export async function runGit(args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS) {
25
+ return (await runGitResult(args, cwd, timeoutMs)).out;
26
+ }
27
+ /**
28
+ * Runs git with a hard deadline (C26, the hook path): built on `spawn`; when `deadlineMs` passes it
29
+ * sends SIGKILL, destroys the child's stdio, `unref()`s it and resolves `{out: null, timedOut: true}`
30
+ * at once — without waiting for `close`, so a git stuck in uninterruptible I/O (or a grandchild that
31
+ * keeps the pipe open) cannot hold the caller. Never throws.
32
+ */
33
+ export function runGitBounded(args, cwd, deadlineMs) {
34
+ return new Promise((resolve) => {
35
+ let settled = false;
36
+ const done = (r) => {
37
+ if (settled)
38
+ return;
39
+ settled = true;
40
+ clearTimeout(timer);
41
+ resolve(r);
42
+ };
43
+ if (!(deadlineMs > 0)) {
44
+ resolve({ out: null, timedOut: true });
45
+ return;
46
+ }
47
+ const start = Date.now(); // the (synchronous) spawn itself counts against the deadline
48
+ let child;
49
+ try {
50
+ child = spawn('git', args, { cwd, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true, env: gitEnv() });
51
+ }
52
+ catch {
53
+ resolve({ out: null, timedOut: false });
54
+ return;
55
+ }
56
+ const timer = setTimeout(() => {
57
+ try {
58
+ child.kill('SIGKILL');
59
+ }
60
+ catch {
61
+ /* already gone */
62
+ }
63
+ child.stdout?.destroy();
64
+ child.unref();
65
+ done({ out: null, timedOut: true });
66
+ }, Math.max(0, deadlineMs - (Date.now() - start)));
67
+ let out = '';
68
+ child.stdout?.setEncoding('utf8');
69
+ child.stdout?.on('data', (chunk) => {
70
+ if (out.length < MAX_OUTPUT)
71
+ out += chunk;
72
+ });
73
+ child.stdout?.on('error', () => { });
74
+ child.on('error', () => done({ out: null, timedOut: false }));
75
+ child.on('close', (code) => done({ out: code === 0 ? out.trim() || null : null, timedOut: false }));
76
+ });
77
+ }
78
+ /** git user.email as git resolves it in `cwd` (repo config, includeIf, then global). */
79
+ export function gitEmail(cwd) {
80
+ return runGit(['config', '--get', 'user.email'], cwd);
81
+ }
82
+ function call(args, cwd, d) {
83
+ if (!d)
84
+ return runGitResult(args, cwd);
85
+ const left = Math.ceil(d.until - Date.now());
86
+ if (left <= 0)
87
+ return Promise.resolve({ out: null, timedOut: true });
88
+ return d.bounded ? runGitBounded(args, cwd, left) : runGitResult(args, cwd, left);
89
+ }
90
+ /** Absolute repository root containing `cwd` (with timeout information). */
91
+ export function gitTopLevelResult(cwd, d) {
92
+ return call(['rev-parse', '--show-toplevel'], cwd, d);
93
+ }
94
+ /**
95
+ * Root of the repository containing `cwd`, for path keys (one git call): the top-level, except in a
96
+ * linked worktree (`git worktree add`, `.claude/worktrees/…`), where it is the main checkout, so every
97
+ * worktree of a repository without a remote shares one key. A bare repository's worktrees and
98
+ * submodules keep their own top-level.
99
+ */
100
+ export async function gitRepoRootResult(cwd, d) {
101
+ const r = await call(['rev-parse', '--show-toplevel', '--git-common-dir'], cwd, d);
102
+ if (!r.out)
103
+ return r;
104
+ const [top, common] = r.out.split('\n').map((l) => l.trim());
105
+ if (!top)
106
+ return { out: null, timedOut: false };
107
+ if (common) {
108
+ try {
109
+ const abs = realpathSync(resolvePath(cwd, common));
110
+ const main = dirname(abs);
111
+ if (basename(abs) === '.git' && main !== realpathSync(top))
112
+ return { out: main, timedOut: false };
113
+ }
114
+ catch {
115
+ /* the common dir vanished: the top-level */
116
+ }
117
+ }
118
+ return { out: top, timedOut: false };
119
+ }
120
+ /** URL of `origin`, else of the first remote (with timeout information); the fallback chain shares the deadline. */
121
+ export async function gitRemoteUrlResult(cwd, d) {
122
+ const origin = await call(['remote', 'get-url', 'origin'], cwd, d);
123
+ if (origin.out || origin.timedOut)
124
+ return origin;
125
+ const remotes = await call(['remote'], cwd, d);
126
+ if (remotes.timedOut)
127
+ return remotes;
128
+ const first = remotes.out?.split('\n')[0]?.trim();
129
+ return first ? call(['remote', 'get-url', first], cwd, d) : { out: null, timedOut: false };
130
+ }
131
+ /** Absolute repository root containing `cwd`, or null outside a repo. */
132
+ export async function gitTopLevel(cwd, timeoutMs) {
133
+ return (await gitTopLevelResult(cwd, timeoutMs === undefined ? undefined : { until: Date.now() + timeoutMs })).out;
134
+ }
135
+ /** URL of `origin`, else of the first remote; null when the repo has none. The chain shares one deadline. */
136
+ export async function gitRemoteUrl(cwd, timeoutMs) {
137
+ return (await gitRemoteUrlResult(cwd, timeoutMs === undefined ? undefined : { until: Date.now() + timeoutMs })).out;
138
+ }
139
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
package/dist/hook.js ADDED
@@ -0,0 +1,147 @@
1
+ /**
2
+ * `ai-obs hook <harness> <hookName>` — the FAST PATH run inside the harness. Budget: < 100 ms.
3
+ * Reads the stdin JSON, writes a job file, spawns the detached worker, exits 0. It never blocks or
4
+ * fails the harness: every error is logged to ~/.ai-obs/log and swallowed. Keep imports minimal.
5
+ * On SessionStart only it may also print the nudge (nudge.ts: link / login / expiry / warnings); every other hook stays silent on stdout.
6
+ * The budget is measured in-process from `runHook` entry (Node startup is outside the collector's control).
7
+ */
8
+ import { spawn } from 'node:child_process';
9
+ import { existsSync } from 'node:fs';
10
+ import { homedir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { filterEnv, writeJob } from './jobs.js';
14
+ import { createLogger } from './log.js';
15
+ import { computeNudge, formatSessionStartOutput, isSessionStart, SESSION_START_OUTPUT } from './nudge.js';
16
+ import { aiObsPaths } from './paths.js';
17
+ import { resolveProjectKey } from './project-key.js';
18
+ const HARNESSES = ['claude-code', 'codex'];
19
+ const STDIN_TIMEOUT_MS = 50;
20
+ const STDIN_MAX_BYTES = 4 * 1024 * 1024;
21
+ /** `[node, entry, ...(--home <root> when the home is not the default)]`; null when the entry is unknown. */
22
+ function defaultSelfCommand(paths) {
23
+ const entry = process.argv[1];
24
+ if (!entry)
25
+ return null;
26
+ return [process.execPath, entry, ...(paths.root !== join(homedir(), '.ai-obs') ? ['--home', paths.root] : [])];
27
+ }
28
+ /** Reads all of stdin, giving up after `timeoutMs` without data / end (harness kept the pipe open). */
29
+ export function readStdin(stream, timeoutMs = STDIN_TIMEOUT_MS) {
30
+ return new Promise((resolve) => {
31
+ if (!stream || stream.isTTY)
32
+ return resolve('');
33
+ const chunks = [];
34
+ let size = 0;
35
+ let done = false;
36
+ const finish = () => {
37
+ if (done)
38
+ return;
39
+ done = true;
40
+ clearTimeout(timer);
41
+ stream.removeListener('data', onData);
42
+ stream.removeListener('end', finish);
43
+ stream.removeListener('error', finish);
44
+ resolve(Buffer.concat(chunks).toString('utf8'));
45
+ };
46
+ const onData = (c) => {
47
+ const b = typeof c === 'string' ? Buffer.from(c) : c;
48
+ size += b.length;
49
+ if (size <= STDIN_MAX_BYTES)
50
+ chunks.push(b);
51
+ timer.refresh(); // data is flowing; wait for the end
52
+ };
53
+ const timer = setTimeout(() => {
54
+ finish();
55
+ try {
56
+ stream.destroy?.();
57
+ }
58
+ catch {
59
+ /* ignore */
60
+ }
61
+ }, timeoutMs);
62
+ stream.on('data', onData);
63
+ stream.once('end', finish);
64
+ stream.once('error', finish);
65
+ });
66
+ }
67
+ export function defaultSpawnWorker(paths) {
68
+ const entry = fileURLToPath(new URL('./worker-main.js', import.meta.url));
69
+ const child = spawn(process.execPath, [entry], {
70
+ detached: true,
71
+ stdio: 'ignore',
72
+ windowsHide: true,
73
+ env: { ...process.env, AI_OBS_HOME: paths.root },
74
+ });
75
+ child.on('error', () => { });
76
+ child.unref();
77
+ }
78
+ /** Always resolves 0. `args` = [harness, hookName]. */
79
+ export async function runHook(args, deps = {}) {
80
+ const perfNow = deps.perfNow ?? (() => performance.now());
81
+ const startedAt = perfNow();
82
+ const env = deps.env ?? process.env;
83
+ if (env.AI_OBS_SUMMARIZING)
84
+ return 0;
85
+ const paths = deps.paths ?? aiObsPaths();
86
+ try {
87
+ const [harness, hook] = args;
88
+ if (!harness || !HARNESSES.includes(harness) || !hook) {
89
+ createLogger(paths, 'hook').warn('hook: bad invocation', { harness, hook });
90
+ return 0;
91
+ }
92
+ if (!existsSync(paths.config))
93
+ return 0; // not configured (or uninstalled with --purge)
94
+ const raw = await readStdin(deps.stdin ?? process.stdin);
95
+ let payload = null;
96
+ if (raw.trim()) {
97
+ try {
98
+ payload = JSON.parse(raw);
99
+ }
100
+ catch {
101
+ createLogger(paths, 'hook').warn('hook: stdin is not JSON', { harness, hook, bytes: raw.length });
102
+ }
103
+ }
104
+ const fields = payload && typeof payload === 'object' ? payload : {};
105
+ const payloadCwd = typeof fields.cwd === 'string' ? fields.cwd : null;
106
+ const cwd = payloadCwd ?? deps.cwd ?? process.cwd();
107
+ const now = deps.now?.() ?? Date.now();
108
+ writeJob(paths.jobs, {
109
+ v: 2,
110
+ harness: harness,
111
+ hook,
112
+ payload,
113
+ cwd,
114
+ env: filterEnv(env),
115
+ received_at: new Date(now).toISOString(),
116
+ }, now);
117
+ (deps.spawnWorker ?? defaultSpawnWorker)(paths);
118
+ // Telemetry first, then the nudge: SessionStart of a main agent only, in its own try/catch.
119
+ if (isSessionStart(hook) && fields.agent_id == null && SESSION_START_OUTPUT[harness] !== 'none') {
120
+ try {
121
+ const nudge = await computeNudge({
122
+ paths,
123
+ cwd,
124
+ sessionId: fields.session_id,
125
+ now,
126
+ startedAt,
127
+ perfNow,
128
+ resolveProjectKey: deps.resolveProjectKey ?? resolveProjectKey,
129
+ selfCommand: deps.selfCommand ?? defaultSelfCommand(paths),
130
+ });
131
+ const out = nudge ? formatSessionStartOutput(harness, nudge.text) : null;
132
+ if (nudge && out !== null) {
133
+ (deps.stdout ?? process.stdout).write(out);
134
+ nudge.commit();
135
+ }
136
+ }
137
+ catch (err) {
138
+ createLogger(paths, 'hook').warn('hook: nudge failed', { error: err instanceof Error ? err.message : String(err) });
139
+ }
140
+ }
141
+ }
142
+ catch (err) {
143
+ createLogger(paths, 'hook').error('hook: failed', { error: err instanceof Error ? err.message : String(err) });
144
+ }
145
+ return 0;
146
+ }
147
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"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"}
package/dist/http.js ADDED
@@ -0,0 +1,35 @@
1
+ import { request as httpRequest } from 'node:http';
2
+ import { request as httpsRequest } from 'node:https';
3
+ const MAX_RESPONSE_BYTES = 1024 * 1024;
4
+ /** Minimal HTTP client on node:http(s). Rejects on network errors and on timeout (whole request). */
5
+ export const nodeTransport = (req) => new Promise((resolve, reject) => {
6
+ const url = new URL(req.url);
7
+ const send = url.protocol === 'https:' ? httpsRequest : httpRequest;
8
+ const headers = { ...req.headers };
9
+ if (req.body)
10
+ headers['content-length'] = String(req.body.length);
11
+ const r = send(url, { method: req.method, headers }, (res) => {
12
+ const chunks = [];
13
+ let size = 0;
14
+ res.on('data', (c) => {
15
+ size += c.length;
16
+ if (size <= MAX_RESPONSE_BYTES)
17
+ chunks.push(c);
18
+ });
19
+ res.on('end', () => {
20
+ clearTimeout(timer);
21
+ resolve({ status: res.statusCode ?? 0, headers: res.headers, body: Buffer.concat(chunks).toString('utf8') });
22
+ });
23
+ res.on('error', (err) => {
24
+ clearTimeout(timer);
25
+ reject(err);
26
+ });
27
+ });
28
+ const timer = setTimeout(() => r.destroy(new Error(`timeout after ${req.timeoutMs} ms`)), req.timeoutMs);
29
+ r.on('error', (err) => {
30
+ clearTimeout(timer);
31
+ reject(err);
32
+ });
33
+ r.end(req.body);
34
+ });
35
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,YAAY,CAAC;AAmBrD,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvC,qGAAqG;AACrG,MAAM,CAAC,MAAM,aAAa,GAAc,CAAC,GAAG,EAAE,EAAE,CAC9C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC;IACpE,MAAM,OAAO,GAA2B,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IAC3D,IAAI,GAAG,CAAC,IAAI;QAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;QAC3D,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YAC3B,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;YACjB,IAAI,IAAI,IAAI,kBAAkB;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/G,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,SAAS,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;IACzG,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACpB,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,CAAC;IACd,CAAC,CAAC,CAAC;IACH,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/jobs.js ADDED
@@ -0,0 +1,53 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { writeFileAtomic } from './fsutil.js';
4
+ import { ulid } from './ulid.js';
5
+ /**
6
+ * Job file names: `<ulid>.json` (0.1.x hook) and `<ulid>.v2.json` (this hook). A 0.1.x worker still
7
+ * running after `init` matches only `<ulid>.json`, so it never processes a post-upgrade job (C25).
8
+ */
9
+ export const JOB_RE = /^[0-9A-HJKMNP-TV-Z]{26}(\.v2)?\.json$/;
10
+ /** Writes a v2 job atomically (temp + rename) so the worker never sees a partial file. Returns its path. */
11
+ export function writeJob(jobsDir, job, now = Date.now()) {
12
+ const file = join(jobsDir, `${ulid(now)}.v2.json`);
13
+ writeFileAtomic(file, JSON.stringify({ ...job, v: 2 }));
14
+ return file;
15
+ }
16
+ /** Pending job files of both versions, oldest first (names start with ULIDs; `.json` sorts before `.v2.json` within one ms). */
17
+ export function listJobs(jobsDir) {
18
+ try {
19
+ return readdirSync(jobsDir)
20
+ .filter((n) => JOB_RE.test(n))
21
+ .sort()
22
+ .map((n) => join(jobsDir, n));
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ }
28
+ /** Jobs a previous worker crashed on (renamed to *.json.processing before handling; both versions). */
29
+ export function listCrashedJobs(jobsDir) {
30
+ try {
31
+ return readdirSync(jobsDir)
32
+ .filter((n) => n.endsWith('.json.processing'))
33
+ .map((n) => join(jobsDir, n));
34
+ }
35
+ catch {
36
+ return [];
37
+ }
38
+ }
39
+ export function toInvocation(job) {
40
+ return { harness: job.harness, hook: job.hook, payload: job.payload, cwd: job.cwd, env: job.env, receivedAt: job.received_at };
41
+ }
42
+ const ENV_ALLOW = /^(CLAUDE|CODEX|AI_OBS)_/;
43
+ const ENV_SECRET = /TOKEN|KEY|SECRET|PASSW|AUTH|CREDENTIAL|COOKIE|SESSION_ID_SECRET/i;
44
+ /** Harness-related env vars only; anything that looks like a secret is dropped. */
45
+ export function filterEnv(env) {
46
+ const out = {};
47
+ for (const [k, v] of Object.entries(env)) {
48
+ if (v !== undefined && ENV_ALLOW.test(k) && !ENV_SECRET.test(k))
49
+ out[k] = v.slice(0, 4096);
50
+ }
51
+ return out;
52
+ }
53
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgBjC;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,uCAAuC,CAAC;AAE9D,4GAA4G;AAC5G,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,GAA8C,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACxG,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnD,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gIAAgI;AAChI,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC;aACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAC7B,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC;aACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;AACjI,CAAC;AAED,MAAM,SAAS,GAAG,yBAAyB,CAAC;AAC5C,MAAM,UAAU,GAAG,kEAAkE,CAAC;AAEtF,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,GAAsB;IAC9C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}