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
@@ -0,0 +1,92 @@
1
+ import { table } from '../commands/common.js';
2
+ /** Control characters (C0, DEL, C1) never reach the terminal: a directory or tool name could carry escape sequences. */
3
+ const safe = (s) => s.replace(/[\u0000-\u001f\u007f-\u009f]/g, ' ');
4
+ /** Lines around the table: header, blank, column heads, blank, footer. */
5
+ const FRAME_LINES = 5;
6
+ const pad2 = (n) => String(n).padStart(2, '0');
7
+ /** 45s, 3m12s, 1h04m (negative durations count as 0). */
8
+ export function formatElapsed(ms) {
9
+ const s = Math.floor(Math.max(0, ms) / 1000);
10
+ if (s < 60)
11
+ return `${s}s`;
12
+ if (s < 3600)
13
+ return `${Math.floor(s / 60)}m${pad2(s % 60)}s`;
14
+ return `${Math.floor(s / 3600)}h${pad2(Math.floor((s % 3600) / 60))}m`;
15
+ }
16
+ /** 950, 12.3k, 1.2M */
17
+ export function formatTokens(n) {
18
+ const v = Math.max(0, Math.round(n));
19
+ if (v < 1000)
20
+ return String(v);
21
+ const k = v / 1000;
22
+ if (k < 999.95)
23
+ return `${k.toFixed(1)}k`;
24
+ return `${(v / 1_000_000).toFixed(1)}M`;
25
+ }
26
+ /** ≈ $0.42, ≈ $0.42+ (some models have no price), n/a (none has) */
27
+ export function formatCost(c) {
28
+ if (c.usd === null)
29
+ return 'n/a';
30
+ return `≈ $${c.usd.toFixed(2)}${c.partial ? '+' : ''}`;
31
+ }
32
+ const clip = (s, max) => (s.length <= max ? s : max <= 1 ? s.slice(0, max) : `${s.slice(0, max - 1)}…`);
33
+ const HEAD = ['STATUS', 'HARNESS', 'PROJECT', 'TITLE', 'TOKENS', '≈COST', 'TOOLS', 'AGENTS', 'LAST TOOL'];
34
+ const TITLE = 3;
35
+ /** Columns kept on a narrow terminal: status, harness, project, title, tokens, cost. */
36
+ const MIN_COLS = 6;
37
+ const MIN_TITLE = 8;
38
+ const WANT_TITLE = 20;
39
+ const MAX_TITLE = 60;
40
+ const MAX_NAME = 24;
41
+ const NARROW_NAME = 14;
42
+ /** Widest cell of each of the first `n` columns. */
43
+ const widths = (matrix, n) => Array.from({ length: n }, (_, i) => Math.max(...matrix.map((r) => r[i].length)));
44
+ /** The frame as lines (no trailing newlines): header, table or "No sessions running", footer. */
45
+ export function renderFrame(rows, o) {
46
+ const shown = rows.filter((r) => r.status !== 'stale');
47
+ const stale = rows.length - shown.length;
48
+ const d = new Date(o.now);
49
+ const clock = `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
50
+ const lines = [`ai-obs live · ${shown.length} running${o.scope ? ` in ${safe(o.scope)}` : ''} · ${clock} · q quits`, ''];
51
+ // On a short terminal the header and footer stay; the sessions that do not fit are counted instead.
52
+ const listed = o.rows === undefined ? shown : shown.slice(0, Math.max(0, o.rows - FRAME_LINES));
53
+ const more = shown.length - listed.length;
54
+ if (!shown.length)
55
+ lines.push('No sessions running');
56
+ else if (listed.length) {
57
+ const layout = (projectMax) => {
58
+ const matrix = [
59
+ HEAD,
60
+ ...listed.map((r) => [
61
+ `${r.status} ${formatElapsed(o.now - r.since)}`,
62
+ safe(r.harness),
63
+ clip(safe(r.project || '-'), projectMax),
64
+ safe(r.title ?? '-'),
65
+ formatTokens(r.tokens),
66
+ formatCost(r.cost),
67
+ String(r.tools),
68
+ String(r.subagents),
69
+ clip(safe(r.lastTool ?? '-'), MAX_NAME),
70
+ ]),
71
+ ];
72
+ // What the other columns leave for the title when the first `n` columns are shown.
73
+ const room = (n) => {
74
+ const w = widths(matrix, n);
75
+ return o.width - (w.reduce((a, b) => a + b, 0) - w[TITLE]) - 2 * (n - 1);
76
+ };
77
+ // On a narrow terminal the least important columns (last tool, agents, tools) go first.
78
+ let cols = HEAD.length;
79
+ while (cols > MIN_COLS && room(cols) < WANT_TITLE)
80
+ cols--;
81
+ return { matrix, cols, room: room(cols) };
82
+ };
83
+ let l = layout(MAX_NAME);
84
+ if (l.room < WANT_TITLE)
85
+ l = layout(NARROW_NAME); // still no room for a title: shorten the project names too
86
+ const titleMax = Math.min(MAX_TITLE, Math.max(MIN_TITLE, l.room));
87
+ lines.push(...table(l.matrix.map((r) => r.slice(0, l.cols).map((c, i) => (i === TITLE ? clip(c, titleMax) : c)))));
88
+ }
89
+ lines.push('', [...(more ? [`${more} more not shown`] : []), ...(stale ? [`${stale} stale hidden`] : []), '≈ at default list prices'].join(' · '));
90
+ return lines.map((l) => (l.length > o.width ? l.slice(0, Math.max(0, o.width)).trimEnd() : l));
91
+ }
92
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/live/render.ts"],"names":[],"mappings":"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"}
package/dist/lock.js ADDED
@@ -0,0 +1,93 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { closeSync, openSync, readFileSync, rmSync, statSync, utimesSync, writeSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { ensureDir } from './fsutil.js';
5
+ function isAlive(pid) {
6
+ try {
7
+ process.kill(pid, 0);
8
+ return true;
9
+ }
10
+ catch (err) {
11
+ return err.code === 'EPERM';
12
+ }
13
+ }
14
+ function isStale(path, staleMs) {
15
+ try {
16
+ const age = Date.now() - statSync(path).mtimeMs;
17
+ const pid = Number.parseInt(readFileSync(path, 'utf8').split(' ')[0] ?? '', 10);
18
+ if (Number.isFinite(pid) && pid > 0 && !isAlive(pid))
19
+ return true;
20
+ return age > staleMs;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /**
27
+ * Exclusive lock file (O_EXCL). Returns a release function, or null when another live process holds
28
+ * it. A lock whose owner is dead, or that was not refreshed for `staleMs`, is taken over. The holder
29
+ * refreshes the lock's mtime while it holds it (heartbeat), so a long run (summaries, a large backlog)
30
+ * is never taken over by age while its process lives (C24). Release removes the file only while it is
31
+ * still this holder's.
32
+ */
33
+ export function tryLock(path, staleMs = 10 * 60_000) {
34
+ ensureDir(dirname(path));
35
+ for (let attempt = 0; attempt < 2; attempt++) {
36
+ try {
37
+ const fd = openSync(path, 'wx', 0o600);
38
+ const stamp = `${process.pid} ${new Date().toISOString()} ${randomBytes(6).toString('hex')}`;
39
+ writeSync(fd, stamp);
40
+ closeSync(fd);
41
+ const owned = () => {
42
+ try {
43
+ return readFileSync(path, 'utf8') === stamp;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ };
49
+ const beat = setInterval(() => {
50
+ if (!owned())
51
+ return;
52
+ try {
53
+ const t = new Date();
54
+ utimesSync(path, t, t);
55
+ }
56
+ catch {
57
+ /* removed meanwhile */
58
+ }
59
+ }, Math.max(1000, Math.min(60_000, Math.floor(staleMs / 4))));
60
+ beat.unref();
61
+ let released = false;
62
+ return () => {
63
+ if (released)
64
+ return;
65
+ released = true;
66
+ clearInterval(beat);
67
+ if (owned())
68
+ rmSync(path, { force: true });
69
+ };
70
+ }
71
+ catch (err) {
72
+ if (err.code !== 'EEXIST')
73
+ throw err;
74
+ if (attempt === 0 && isStale(path, staleMs)) {
75
+ rmSync(path, { force: true });
76
+ continue;
77
+ }
78
+ return null;
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+ /** Polls for the lock up to `waitMs`. */
84
+ export async function acquireLock(path, waitMs, staleMs) {
85
+ const deadline = Date.now() + waitMs;
86
+ for (;;) {
87
+ const release = tryLock(path, staleMs);
88
+ if (release || Date.now() >= deadline)
89
+ return release;
90
+ await new Promise((r) => setTimeout(r, 100));
91
+ }
92
+ }
93
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
package/dist/log.js ADDED
@@ -0,0 +1,36 @@
1
+ import { appendFileSync, renameSync, statSync } from 'node:fs';
2
+ import { ensureDir } from './fsutil.js';
3
+ const MAX_LOG_BYTES = 1_000_000;
4
+ /**
5
+ * File logger for ~/.ai-obs/log/collector.log (rotated to .1 at 1 MB). Never throws: hooks and the
6
+ * worker must not fail because logging failed. Never log prompts, messages, code or file paths.
7
+ */
8
+ export function createLogger(paths, scope = 'collector') {
9
+ const write = (level, msg, meta) => {
10
+ try {
11
+ ensureDir(paths.logDir);
12
+ try {
13
+ if (statSync(paths.logFile).size > MAX_LOG_BYTES)
14
+ renameSync(paths.logFile, `${paths.logFile}.1`);
15
+ }
16
+ catch {
17
+ /* no log yet */
18
+ }
19
+ const extra = meta ? ` ${JSON.stringify(meta)}` : '';
20
+ appendFileSync(paths.logFile, `${new Date().toISOString()} ${level} [${scope}#${process.pid}] ${msg}${extra}\n`, {
21
+ mode: 0o600,
22
+ });
23
+ }
24
+ catch {
25
+ /* logging must never break the caller */
26
+ }
27
+ };
28
+ return {
29
+ info: (m, meta) => write('INFO', m, meta),
30
+ warn: (m, meta) => write('WARN', m, meta),
31
+ error: (m, meta) => write('ERROR', m, meta),
32
+ };
33
+ }
34
+ /** Logger that discards everything (tests). */
35
+ export const nullLogger = { info() { }, warn() { }, error() { } };
36
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AASxC,MAAM,aAAa,GAAG,SAAS,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAA6C,EAAE,KAAK,GAAG,WAAW;IAC7F,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,GAAW,EAAE,IAA8B,EAAE,EAAE;QAC3E,IAAI,CAAC;YACH,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACxB,IAAI,CAAC;gBACH,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,GAAG,aAAa;oBAAE,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;YACpG,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,KAAK,KAAK,KAAK,IAAI,OAAO,CAAC,GAAG,KAAK,GAAG,GAAG,KAAK,IAAI,EAAE;gBAC/G,IAAI,EAAE,KAAK;aACZ,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,yCAAyC;QAC3C,CAAC;IACH,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC;QACzC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC;QACzC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,UAAU,GAAW,EAAE,IAAI,KAAI,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,KAAK,KAAI,CAAC,EAAE,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Upgrading a 0.2 – 0.4 home to 0.5 (brief appendix E): config.json v2 → v3 (the v2 file kept as
3
+ * config.json.v2), projects.json set aside as projects.json.v2, `o-` / `t-` queue streams converted to `s-`
4
+ * streams. An older copied hook runtime (`<home>/app`) is refreshed first: it would read config v3 as "no
5
+ * token" and drop every hook job. Idempotent; the caller holds worker.lock (the worker at the start of every
6
+ * run, CLI commands). queue/dead/pre-upgrade and the 0.1.x root segments are left as they are.
7
+ */
8
+ import { chmodSync, copyFileSync, existsSync, readdirSync, renameSync, rmSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { loadConfigFile, saveConfig } from './config.js';
11
+ import { recordDropped } from './delivery.js';
12
+ import { errorMessage } from './fsutil.js';
13
+ import { nullLogger } from './log.js';
14
+ import { QueueStore } from './queue.js';
15
+ import { loadRouting } from './routing.js';
16
+ import { installedRuntimeVersion, installRuntime } from './runtime.js';
17
+ import { ulid } from './ulid.js';
18
+ import { COLLECTOR_VERSION, compareVersions } from './version.js';
19
+ /** Whether anything is left to migrate (cheap: one config read, one stat, two directory listings). */
20
+ export function needsMigration(paths) {
21
+ const loaded = loadConfigFile(paths);
22
+ if (loaded?.migration)
23
+ return true;
24
+ if (existsSync(paths.legacyProjects))
25
+ return true;
26
+ return new QueueStore(paths.queue, { routing: () => null }).hasLegacy();
27
+ }
28
+ const olderThanThis = (v) => v !== null && compareVersions(v, COLLECTOR_VERSION) < 0;
29
+ /** `path` if free, else `path.<ulid>`. */
30
+ const freeName = (path, now) => (existsSync(path) ? `${path}.${ulid(now.getTime())}` : path);
31
+ /**
32
+ * Runs the migration and returns one line per change (empty when there was nothing to do). Notes are also
33
+ * logged. Never throws: a step that fails is logged and retried by the next run.
34
+ */
35
+ export function migrateHome(paths, opts = {}) {
36
+ const log = opts.log ?? nullLogger;
37
+ const now = opts.now ?? new Date();
38
+ const notes = [];
39
+ const loaded = loadConfigFile(paths);
40
+ if (loaded?.migration) {
41
+ // The hooks' runtime must read config v3 before it is written; else it drops every hook job until `init`.
42
+ if (olderThanThis(installedRuntimeVersion(paths))) {
43
+ try {
44
+ (opts.refreshRuntime ?? installRuntime)(paths);
45
+ }
46
+ catch (err) {
47
+ log.error('migrate: cannot refresh the hook runtime', { error: errorMessage(err) });
48
+ }
49
+ const still = installedRuntimeVersion(paths);
50
+ if (olderThanThis(still)) {
51
+ const note = `~/.ai-obs not migrated yet: the hook runtime in ${paths.app} is ${still} and could not be updated to ${COLLECTOR_VERSION}; run \`ai-obs init\``;
52
+ log.warn(`migrate: ${note}`);
53
+ return [note];
54
+ }
55
+ }
56
+ try {
57
+ const backup = freeName(`${paths.config}.v${loaded.migration.from}`, now);
58
+ copyFileSync(paths.config, backup);
59
+ chmodSync(backup, 0o600);
60
+ saveConfig(paths, loaded.config);
61
+ // per-token caches of 0.2 – 0.4 are named after the token; the 0.5 caches are named after the host
62
+ try {
63
+ for (const f of readdirSync(paths.collectorConfigDir))
64
+ rmSync(join(paths.collectorConfigDir, f), { force: true });
65
+ }
66
+ catch {
67
+ /* no cache dir */
68
+ }
69
+ rmSync(join(paths.state, 'collector-config.json'), { force: true });
70
+ const n = loaded.config.servers.length;
71
+ notes.push(`config.json migrated to version 3: ${n} server${n === 1 ? '' : 's'} (${loaded.config.servers.map((s) => s.endpoint).join(', ') || 'none'})`);
72
+ notes.push(...loaded.migration.notes);
73
+ notes.push(`the old config.json is kept as ${backup} (mode 0600; it holds the old tokens: delete it when you no longer need them)`);
74
+ }
75
+ catch (err) {
76
+ log.error('migrate: cannot write config.json v3', { error: errorMessage(err) });
77
+ return notes;
78
+ }
79
+ }
80
+ if (existsSync(paths.legacyProjects)) {
81
+ const target = freeName(`${paths.legacyProjects}.v2`, now);
82
+ try {
83
+ renameSync(paths.legacyProjects, target);
84
+ notes.push(`projects.json is no longer used (repositories choose their team in .ai-obs.json): kept as ${target}`);
85
+ }
86
+ catch (err) {
87
+ log.error('migrate: cannot rename projects.json', { error: errorMessage(err) });
88
+ }
89
+ }
90
+ const config = loaded?.config;
91
+ if (config) {
92
+ const store = new QueueStore(paths.queue, { log, routing: () => loadRouting(paths), dropped: (d) => recordDropped(paths, d) });
93
+ if (store.hasLegacy()) {
94
+ try {
95
+ const m = store.migrateLegacy(config);
96
+ if (m.streams) {
97
+ notes.push(`${m.events} queued events in ${m.streams} streams converted${m.unassignedEvents ? `; ${m.unassignedEvents} of them wait for a server (several are registered): run \`ai-obs link\` in their repository` : ''}`);
98
+ }
99
+ }
100
+ catch (err) {
101
+ log.error('migrate: cannot convert queue streams', { error: errorMessage(err) });
102
+ }
103
+ }
104
+ }
105
+ for (const n of notes)
106
+ log.info(`migrate: ${n}`);
107
+ return notes;
108
+ }
109
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAe,MAAM,UAAU,CAAC;AAEnD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAElE,sGAAsG;AACtG,MAAM,UAAU,cAAc,CAAC,KAAiB;IAC9C,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,MAAM,EAAE,SAAS;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;AAC1E,CAAC;AASD,MAAM,aAAa,GAAG,CAAC,CAAgB,EAAW,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,eAAe,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAE7G,0CAA0C;AAC1C,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,GAAS,EAAU,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEnH;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,IAAI,GAAmB,EAAE;IACtE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACnC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;QACtB,0GAA0G;QAC1G,IAAI,aAAa,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC;gBACH,CAAC,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,CAAC,KAAK,CAAC,CAAC;YACjD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,0CAA0C,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,MAAM,KAAK,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;YAC7C,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,mDAAmD,KAAK,CAAC,GAAG,OAAO,KAAK,gCAAgC,iBAAiB,uBAAuB,CAAC;gBAC9J,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;gBAC7B,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;YAC1E,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACnC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACzB,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACjC,mGAAmG;YACnG,IAAI,CAAC;gBACH,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC;oBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACpH,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB;YACpB,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,uBAAuB,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACpE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC;YACzJ,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,kCAAkC,MAAM,+EAA+E,CAAC,CAAC;QACtI,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,IAAI,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,KAAK,CAAC,cAAc,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3D,IAAI,CAAC;YACH,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,6FAA6F,MAAM,EAAE,CAAC,CAAC;QACpH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC;IAC9B,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/H,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBACtC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;oBACd,KAAK,CAAC,IAAI,CACR,GAAG,CAAC,CAAC,MAAM,qBAAqB,CAAC,CAAC,OAAO,qBAAqB,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,gBAAgB,8FAA8F,CAAC,CAAC,CAAC,EAAE,EAAE,CAChN,CAAC;gBACJ,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,uCAAuC,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/dist/nudge.js ADDED
@@ -0,0 +1,231 @@
1
+ /**
2
+ * The SessionStart nudge: one short text the hook puts into the agent's context, only when (a) the
3
+ * repository has no dot-file server and two or more servers are registered (run `ai-obs link`), (b) the
4
+ * repository's server has no token, or an expired or refused one (run `ai-obs login --server …`), (c) its
5
+ * token expires within 14 days, (d) queued events were dropped, (e) the server ignored this project's
6
+ * dot-file team. Identifiers only (hosts, URLs, team slugs, the sanitized directory), at most 1000
7
+ * characters, once per session per text. Imports only fs-level modules: the hook must stay fast.
8
+ */
9
+ import { createHash } from 'node:crypto';
10
+ import { mkdirSync, readFileSync } from 'node:fs';
11
+ import { dirname } from 'node:path';
12
+ import { shellQuote } from './adapters/hook-command.js';
13
+ import { daysLeft, findServer, isExpired, loadConfig } from './config.js';
14
+ import { droppedTotal, markDroppedShown, readDelivery, readDropped, serverDelivery } from './delivery.js';
15
+ import { lookupDotFile } from './dotfile.js';
16
+ import { writeFileAtomic } from './fsutil.js';
17
+ import { nudgeMarkerPath } from './paths.js';
18
+ import { cachedPk, readPkCache } from './pk-cache.js';
19
+ import { TEAM_SLUG_RE } from './protocol-types.js';
20
+ import { readWarnings } from './warnings.js';
21
+ /**
22
+ * How SessionStart output reaches the agent. Claude Code: JSON `hookSpecificOutput.additionalContext`.
23
+ * Codex (codex-cli 0.154.0): plain stdout; the JSON form is rejected there (see adapters/codex/FORMAT.md).
24
+ * `none` keeps the hook silent (the CLI still shows everything).
25
+ */
26
+ export const SESSION_START_OUTPUT = { 'claude-code': 'json', codex: 'text' };
27
+ export function formatSessionStartOutput(harness, text) {
28
+ const mode = SESSION_START_OUTPUT[harness];
29
+ if (mode === 'json')
30
+ return `${JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: text } })}\n`;
31
+ if (mode === 'text')
32
+ return `${text}\n`;
33
+ return null;
34
+ }
35
+ /** Hook-name normalization: `SessionStart`, `session_start` and `session-start` are the same event. */
36
+ export const isSessionStart = (hook) => hook.replace(/[_-]/g, '').toLowerCase() === 'sessionstart';
37
+ export const NUDGE_MAX_CHARS = 1000;
38
+ /** A token expiring within this many days is mentioned (brief Q6). */
39
+ export const EXPIRY_NUDGE_DAYS = 14;
40
+ const SAFE = /^[A-Za-z0-9._~:/@+ -]+$/;
41
+ const HOST_RE = /^[a-z0-9.-]+(:\d+)?$/;
42
+ const MAX_HOSTS = 10;
43
+ const ASK = ' If you cannot ask the user (for example in a non-interactive session), do nothing.';
44
+ const DAY_MS = 86_400_000;
45
+ /** A host as the text may show it, or null. */
46
+ export function safeHost(endpoint) {
47
+ try {
48
+ const h = new URL(endpoint).host.toLowerCase();
49
+ return HOST_RE.test(h) ? h : null;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ /** Pure. Null when there is nothing to say. */
56
+ export function buildNudge(input) {
57
+ const cmdSafe = input.selfCommand !== null && input.selfCommand.length > 0 && input.selfCommand.every((a) => SAFE.test(a));
58
+ const fullCmd = cmdSafe ? input.selfCommand.map(shellQuote).join(' ') : 'ai-obs';
59
+ const dirSafe = input.dir !== null && SAFE.test(input.dir);
60
+ const build = ({ cmdForm, withDir, listMax, hostOnly }) => {
61
+ const form = cmdSafe && fullCmd !== 'ai-obs' ? cmdForm : 'plain';
62
+ const cmd = form === 'full' ? fullCmd : 'ai-obs';
63
+ const cmdNote = form === 'once' ? ` (\`ai-obs\` here means \`${fullCmd}\`)` : form === 'plain' && !(cmdSafe && fullCmd === 'ai-obs') ? ' (if `ai-obs` is not on PATH, ask the user to run the command)' : '';
64
+ const dirOk = dirSafe && withDir;
65
+ const dirFlag = dirOk ? ` --project-dir ${shellQuote(input.dir)}` : '';
66
+ const dirNote = dirOk ? '' : " Run it from this repository's root directory.";
67
+ const server = (endpoint) => !hostOnly && SAFE.test(endpoint) ? { text: endpoint, arg: shellQuote(endpoint) } : { text: safeHost(endpoint) ?? 'the server', arg: '<url>' };
68
+ const parts = [];
69
+ const m = input.main;
70
+ if (m) {
71
+ switch (m.kind) {
72
+ case 'link': {
73
+ const shown = m.hosts.slice(0, listMax);
74
+ const more = m.hosts.length - shown.length;
75
+ parts.push(`ai-obs: this repository has no .ai-obs.json and this machine is logged in to ${m.hosts.length} ai-obs servers (${shown.join(', ')}${more > 0 ? ` and ${more} more` : ''}), so its AI usage data is kept on this machine and not reported. Ask the user which server and team this repository reports to, then run \`${cmd} link --server <url> --team <team>${dirFlag}\`.${dirNote} Do not choose without asking; ask only once in this session.${cmdNote}${ASK}`);
76
+ break;
77
+ }
78
+ case 'login': {
79
+ const s = server(m.server);
80
+ const what = m.reason === 'no-token'
81
+ ? `this repository reports to ${s.text} (its .ai-obs.json), but this machine is not logged in to that server`
82
+ : m.reason === 'expired'
83
+ ? `the ai-obs token for ${s.text} has expired`
84
+ : `${s.text} rejected this machine's ai-obs token (expired or revoked)`;
85
+ parts.push(`ai-obs: ${what}, so usage data of this repository is kept on this machine. Tell the user to run \`${cmd} login --server ${s.arg}\` themselves (it opens the browser to approve the login; never ask for a token).${cmdNote}`);
86
+ break;
87
+ }
88
+ case 'expiring': {
89
+ const s = server(m.server);
90
+ parts.push(`ai-obs: the ai-obs token for ${s.text} expires ${m.days <= 0 ? 'today' : `in ${m.days} day${m.days === 1 ? '' : 's'}`} (${m.date}). Tell the user to run \`${cmd} login --server ${s.arg}\` before then.${cmdNote}`);
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ const w = input.warning;
96
+ if (w && TEAM_SLUG_RE.test(w.team)) {
97
+ parts.push(`ai-obs: the server ignored team "${w.team}" from this repository's .ai-obs.json (${w.code === 'team_not_member' ? 'the user is not a member of that team' : 'no team has that slug'}), so the user's primary team is used, or no team if they have none. Tell the user (\`${cmd} status\`).`);
98
+ }
99
+ if (input.droppedEvents && input.droppedEvents > 0)
100
+ parts.push(`Note: ai-obs deleted ${Math.floor(input.droppedEvents)} kept events because its local queue reached the 50 MB cap; tell the user (\`${cmd} status\`).`);
101
+ if (input.droppedSendable && input.droppedSendable > 0)
102
+ parts.push(`Note: ai-obs deleted ${Math.floor(input.droppedSendable)} events it could not send in time (30-day / 50 MB queue caps); tell the user (\`${cmd} status\`).`);
103
+ return parts.length ? parts.join(' ') : null;
104
+ };
105
+ // Shorten: the repeated command first (once, then not at all), then --project-dir, then the hosts named.
106
+ const attempts = [
107
+ { cmdForm: 'full', withDir: true, listMax: MAX_HOSTS },
108
+ { cmdForm: 'once', withDir: true, listMax: MAX_HOSTS },
109
+ { cmdForm: 'plain', withDir: true, listMax: MAX_HOSTS },
110
+ { cmdForm: 'plain', withDir: false, listMax: MAX_HOSTS },
111
+ ];
112
+ for (let n = MAX_HOSTS - 1; n >= 1; n--)
113
+ attempts.push({ cmdForm: 'plain', withDir: false, listMax: n });
114
+ attempts.push({ cmdForm: 'plain', withDir: false, listMax: 1, hostOnly: true }); // a long server URL: name its host only
115
+ let text = null;
116
+ for (const a of attempts) {
117
+ text = build(a);
118
+ if (text === null || text.length <= NUDGE_MAX_CHARS)
119
+ return text;
120
+ }
121
+ return text === null ? null : text.slice(0, NUDGE_MAX_CHARS);
122
+ }
123
+ /** Git is tried only while fewer than this many ms have elapsed since hook entry, and for at most this long (C26). */
124
+ export const GIT_START_LIMIT_MS = 40;
125
+ export const GIT_BUDGET_MS = 40;
126
+ export const HOOK_BUDGET_MS = 80;
127
+ /** (a) – (c): no network, local files only. */
128
+ function decideMain(ctx) {
129
+ const config = loadConfig(ctx.paths);
130
+ if (!config)
131
+ return null;
132
+ const dot = lookupDotFile(ctx.cwd, { cache: ctx.paths });
133
+ const root = dot.root;
134
+ const servers = config.servers;
135
+ if (!dot.server && servers.length >= 2) {
136
+ const hosts = servers.map((s) => safeHost(s.endpoint)).filter((h) => h !== null);
137
+ return { main: { kind: 'link', hosts }, endpoint: '', root };
138
+ }
139
+ const endpoint = dot.server ?? (servers.length === 1 ? servers[0].endpoint : null);
140
+ if (!endpoint)
141
+ return { main: null, endpoint: null, root };
142
+ const entry = findServer(config, endpoint);
143
+ if (!entry)
144
+ return { main: { kind: 'login', server: endpoint, reason: 'no-token' }, endpoint, root };
145
+ if (isExpired(entry, ctx.now))
146
+ return { main: { kind: 'login', server: endpoint, reason: 'expired' }, endpoint, root };
147
+ let blocked = false;
148
+ try {
149
+ blocked = !!serverDelivery(readDelivery(ctx.paths), entry).blocked;
150
+ }
151
+ catch {
152
+ blocked = false;
153
+ }
154
+ if (blocked)
155
+ return { main: { kind: 'login', server: endpoint, reason: 'rejected' }, endpoint, root };
156
+ const days = daysLeft(entry, ctx.now);
157
+ if (days !== null && Date.parse(entry.expires_at) - ctx.now <= EXPIRY_NUDGE_DAYS * DAY_MS) {
158
+ return { main: { kind: 'expiring', server: endpoint, days, date: entry.expires_at.slice(0, 10) }, endpoint, root };
159
+ }
160
+ return { main: null, endpoint, root };
161
+ }
162
+ /** (e): the latest warning of this project, for the server its new sessions go to. Git only when a warning exists. */
163
+ async function decideWarning(ctx, endpoint) {
164
+ if (!endpoint)
165
+ return null;
166
+ const warnings = readWarnings(ctx.paths);
167
+ if (!Object.keys(warnings).length)
168
+ return null;
169
+ let pk = cachedPk(readPkCache(ctx.paths), ctx.cwd);
170
+ if (pk === null) {
171
+ const elapsed = ctx.perfNow() - ctx.startedAt;
172
+ if (elapsed < GIT_START_LIMIT_MS)
173
+ pk = await ctx.resolveProjectKey(ctx.cwd, { timeoutMs: Math.min(GIT_BUDGET_MS, HOOK_BUDGET_MS - elapsed), bounded: true });
174
+ }
175
+ const w = pk !== null && Object.prototype.hasOwnProperty.call(warnings, pk) ? warnings[pk] : undefined;
176
+ return w && w.endpoint === endpoint ? { code: w.code, team: w.team } : null;
177
+ }
178
+ /** The SessionStart nudge for this session, or null. Never throws. */
179
+ export async function computeNudge(ctx) {
180
+ const d = decideMain(ctx);
181
+ if (!d)
182
+ return null;
183
+ const warning = await decideWarning(ctx, d.endpoint);
184
+ let droppedEvents = 0;
185
+ let droppedSendable = 0;
186
+ let droppedShown = false;
187
+ const dropped = readDropped(ctx.paths);
188
+ if (droppedTotal(dropped) > 0 && dropped.last_at && (!dropped.shown_at || dropped.last_at > dropped.shown_at)) {
189
+ // the counters are lifetime totals: report only what was dropped since the text last mentioned it
190
+ droppedEvents = Math.max(0, dropped.held_events - dropped.shown_held);
191
+ droppedSendable = Math.max(0, dropped.sendable_events - dropped.shown_sendable);
192
+ droppedShown = true;
193
+ }
194
+ const text = buildNudge({ main: d.main, warning, droppedEvents, droppedSendable, selfCommand: ctx.selfCommand, dir: d.root ?? ctx.cwd });
195
+ if (text === null)
196
+ return null;
197
+ const markerFile = nudgeMarkerPath(ctx.paths, ctx.sessionId);
198
+ const digest = createHash('sha1').update(text).digest('hex');
199
+ if (markerFile) {
200
+ try {
201
+ if (readFileSync(markerFile, 'utf8').trim() === digest)
202
+ return null; // already shown in this session
203
+ }
204
+ catch {
205
+ /* no marker yet */
206
+ }
207
+ }
208
+ return {
209
+ text,
210
+ commit: () => {
211
+ if (markerFile) {
212
+ try {
213
+ mkdirSync(dirname(markerFile), { recursive: true, mode: 0o700 });
214
+ writeFileAtomic(markerFile, digest, 0o600);
215
+ }
216
+ catch {
217
+ /* a missing marker only repeats the text */
218
+ }
219
+ }
220
+ if (droppedShown) {
221
+ try {
222
+ markDroppedShown(ctx.paths, new Date(ctx.now), dropped);
223
+ }
224
+ catch {
225
+ /* ignore */
226
+ }
227
+ }
228
+ },
229
+ };
230
+ }
231
+ //# sourceMappingURL=nudge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nudge.js","sourceRoot":"","sources":["../src/nudge.ts"],"names":[],"mappings":"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"}