@tracemarketplace/cli 0.0.15 → 0.0.18

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 (42) hide show
  1. package/dist/api-client.d.ts +7 -0
  2. package/dist/api-client.d.ts.map +1 -1
  3. package/dist/api-client.js +79 -14
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/api-client.test.d.ts +2 -0
  6. package/dist/api-client.test.d.ts.map +1 -0
  7. package/dist/api-client.test.js +34 -0
  8. package/dist/api-client.test.js.map +1 -0
  9. package/dist/cli.js +9 -8
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands/daemon.d.ts.map +1 -1
  12. package/dist/commands/daemon.js +71 -6
  13. package/dist/commands/daemon.js.map +1 -1
  14. package/dist/commands/remove-daemon.d.ts +6 -0
  15. package/dist/commands/remove-daemon.d.ts.map +1 -0
  16. package/dist/commands/remove-daemon.js +66 -0
  17. package/dist/commands/remove-daemon.js.map +1 -0
  18. package/dist/commands/submit.d.ts +1 -0
  19. package/dist/commands/submit.d.ts.map +1 -1
  20. package/dist/commands/submit.js +25 -15
  21. package/dist/commands/submit.js.map +1 -1
  22. package/dist/config.d.ts +5 -0
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +12 -0
  25. package/dist/config.js.map +1 -1
  26. package/dist/flush.d.ts +5 -1
  27. package/dist/flush.d.ts.map +1 -1
  28. package/dist/flush.js +107 -27
  29. package/dist/flush.js.map +1 -1
  30. package/dist/flush.test.js +162 -7
  31. package/dist/flush.test.js.map +1 -1
  32. package/package.json +2 -2
  33. package/src/api-client.test.ts +47 -0
  34. package/src/api-client.ts +98 -14
  35. package/src/cli.ts +10 -9
  36. package/src/commands/daemon.ts +82 -6
  37. package/src/commands/remove-daemon.ts +75 -0
  38. package/src/commands/submit.ts +28 -18
  39. package/src/config.ts +18 -0
  40. package/src/flush.test.ts +187 -6
  41. package/src/flush.ts +140 -39
  42. package/src/commands/register.ts +0 -8
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { collectIdleSessionSources, createFreshSessionState, migrateLegacySessionState, planSessionUploads, } from "./flush.js";
2
+ import { collectIdleSessionSources, createFreshSessionState, migrateLegacySessionState, planSessionUploads, verifyUnconfirmedChunks, } from "./flush.js";
3
+ import { migrateSessionUploadState } from "./config.js";
3
4
  function makeTurn(turnId, role, timestamp, outputTokens = 0) {
4
5
  return {
5
6
  turn_id: turnId,
@@ -60,6 +61,19 @@ function makeTrace(sessionId, turns, endedAt = turns[turns.length - 1]?.timestam
60
61
  function makeSource(tool, locator) {
61
62
  return { tool, locator, label: locator };
62
63
  }
64
+ function makeSessionState(overrides) {
65
+ return {
66
+ nextChunkIndex: 0,
67
+ openChunkStartTurn: 0,
68
+ lastSeenTurnCount: 0,
69
+ lastActivityAt: null,
70
+ lastFlushedTurnId: null,
71
+ confirmedChunkIndex: 0,
72
+ confirmedOpenChunkStartTurn: 0,
73
+ unconfirmedSince: null,
74
+ ...overrides,
75
+ };
76
+ }
63
77
  describe("planSessionUploads", () => {
64
78
  it("flushes a sealed 100k chunk and keeps the tail pending", () => {
65
79
  const trace = makeTrace("session-100k", [
@@ -129,10 +143,151 @@ describe("planSessionUploads", () => {
129
143
  expect(plan.observedState.openChunkStartTurn).toBe(trace.turn_count);
130
144
  });
131
145
  });
146
+ describe("migrateSessionUploadState", () => {
147
+ it("fills in missing confirmation fields from next/openChunk values", () => {
148
+ const legacy = {
149
+ sourceTool: "codex_cli",
150
+ sourceSessionId: "s1",
151
+ locator: "/tmp/s1.jsonl",
152
+ nextChunkIndex: 3,
153
+ openChunkStartTurn: 10,
154
+ lastSeenTurnCount: 10,
155
+ lastActivityAt: "2026-03-21T00:00:00.000Z",
156
+ lastFlushedTurnId: "a3",
157
+ // missing: confirmedChunkIndex, confirmedOpenChunkStartTurn, unconfirmedSince
158
+ };
159
+ const migrated = migrateSessionUploadState(legacy);
160
+ expect(migrated.confirmedChunkIndex).toBe(3);
161
+ expect(migrated.confirmedOpenChunkStartTurn).toBe(10);
162
+ expect(migrated.unconfirmedSince).toBeNull();
163
+ });
164
+ it("does not overwrite existing confirmation fields", () => {
165
+ const state = makeSessionState({
166
+ sourceTool: "codex_cli",
167
+ sourceSessionId: "s2",
168
+ locator: "/tmp/s2.jsonl",
169
+ nextChunkIndex: 5,
170
+ openChunkStartTurn: 20,
171
+ confirmedChunkIndex: 3,
172
+ confirmedOpenChunkStartTurn: 12,
173
+ unconfirmedSince: "2026-03-21T01:00:00.000Z",
174
+ });
175
+ const migrated = migrateSessionUploadState(state);
176
+ expect(migrated.confirmedChunkIndex).toBe(3);
177
+ expect(migrated.confirmedOpenChunkStartTurn).toBe(12);
178
+ expect(migrated.unconfirmedSince).toBe("2026-03-21T01:00:00.000Z");
179
+ });
180
+ });
181
+ describe("verifyUnconfirmedChunks", () => {
182
+ function makeSubmitState(sessions) {
183
+ return { version: 2, chunks: {}, sessions };
184
+ }
185
+ function makeMockClient(responses) {
186
+ return {
187
+ async get(path) {
188
+ const url = new URL(`http://x${path}`);
189
+ const tool = url.searchParams.get("source_tool");
190
+ const id = url.searchParams.get("source_session_id");
191
+ const idx = url.searchParams.get("chunk_index");
192
+ const key = `${tool}:${id}:${idx}`;
193
+ const r = responses[key];
194
+ if (!r)
195
+ throw new Error(`Unexpected exists check: ${key}`);
196
+ return r;
197
+ },
198
+ };
199
+ }
200
+ it("advances confirmedChunkIndex when chunks are confirmed", async () => {
201
+ const state = makeSubmitState({
202
+ "codex_cli:sess1": makeSessionState({
203
+ sourceTool: "codex_cli",
204
+ sourceSessionId: "sess1",
205
+ locator: "/tmp/sess1.jsonl",
206
+ nextChunkIndex: 3,
207
+ openChunkStartTurn: 12,
208
+ confirmedChunkIndex: 1,
209
+ confirmedOpenChunkStartTurn: 4,
210
+ unconfirmedSince: "2026-03-21T00:00:00.000Z",
211
+ }),
212
+ });
213
+ const client = makeMockClient({
214
+ "codex_cli:sess1:1": { exists: true },
215
+ "codex_cli:sess1:2": { exists: true },
216
+ });
217
+ await verifyUnconfirmedChunks(state, client, new Date("2026-03-21T01:00:00.000Z"));
218
+ const s = state.sessions["codex_cli:sess1"];
219
+ expect(s.confirmedChunkIndex).toBe(3);
220
+ // all confirmed → unconfirmedSince cleared
221
+ expect(s.unconfirmedSince).toBeNull();
222
+ });
223
+ it("stops advancing at first missing chunk", async () => {
224
+ const state = makeSubmitState({
225
+ "codex_cli:sess2": makeSessionState({
226
+ sourceTool: "codex_cli",
227
+ sourceSessionId: "sess2",
228
+ locator: "/tmp/sess2.jsonl",
229
+ nextChunkIndex: 3,
230
+ openChunkStartTurn: 12,
231
+ confirmedChunkIndex: 1,
232
+ confirmedOpenChunkStartTurn: 4,
233
+ unconfirmedSince: "2026-03-21T00:00:00.000Z",
234
+ }),
235
+ });
236
+ const client = makeMockClient({
237
+ "codex_cli:sess2:1": { exists: true },
238
+ "codex_cli:sess2:2": { exists: false },
239
+ });
240
+ await verifyUnconfirmedChunks(state, client, new Date("2026-03-21T01:00:00.000Z"));
241
+ const s = state.sessions["codex_cli:sess2"];
242
+ expect(s.confirmedChunkIndex).toBe(2);
243
+ // still one unconfirmed chunk → unconfirmedSince preserved
244
+ expect(s.unconfirmedSince).toBe("2026-03-21T00:00:00.000Z");
245
+ });
246
+ it("resets to confirmed state after 2hr timeout", async () => {
247
+ const state = makeSubmitState({
248
+ "codex_cli:sess3": makeSessionState({
249
+ sourceTool: "codex_cli",
250
+ sourceSessionId: "sess3",
251
+ locator: "/tmp/sess3.jsonl",
252
+ nextChunkIndex: 3,
253
+ openChunkStartTurn: 12,
254
+ confirmedChunkIndex: 1,
255
+ confirmedOpenChunkStartTurn: 4,
256
+ unconfirmedSince: "2026-03-21T00:00:00.000Z",
257
+ }),
258
+ });
259
+ const client = makeMockClient({}); // should not be called
260
+ // now = 2h01m after unconfirmedSince
261
+ await verifyUnconfirmedChunks(state, client, new Date("2026-03-21T02:01:00.000Z"));
262
+ const s = state.sessions["codex_cli:sess3"];
263
+ // reset back to confirmed baseline so chunks get re-submitted
264
+ expect(s.nextChunkIndex).toBe(1);
265
+ expect(s.openChunkStartTurn).toBe(4);
266
+ expect(s.unconfirmedSince).toBeNull();
267
+ });
268
+ it("skips sessions that are already fully confirmed", async () => {
269
+ const state = makeSubmitState({
270
+ "codex_cli:sess4": makeSessionState({
271
+ sourceTool: "codex_cli",
272
+ sourceSessionId: "sess4",
273
+ locator: "/tmp/sess4.jsonl",
274
+ nextChunkIndex: 2,
275
+ openChunkStartTurn: 8,
276
+ confirmedChunkIndex: 2,
277
+ confirmedOpenChunkStartTurn: 8,
278
+ unconfirmedSince: null,
279
+ }),
280
+ });
281
+ let callCount = 0;
282
+ const client = { async get() { callCount++; return { exists: true }; } };
283
+ await verifyUnconfirmedChunks(state, client, new Date("2026-03-21T01:00:00.000Z"));
284
+ expect(callCount).toBe(0);
285
+ });
286
+ });
132
287
  describe("collectIdleSessionSources", () => {
133
288
  it("returns only tracked sessions with an open tail older than two days", () => {
134
289
  const sources = collectIdleSessionSources({
135
- "codex_cli:idle": {
290
+ "codex_cli:idle": makeSessionState({
136
291
  sourceTool: "codex_cli",
137
292
  sourceSessionId: "idle",
138
293
  locator: "/tmp/idle.jsonl",
@@ -141,8 +296,8 @@ describe("collectIdleSessionSources", () => {
141
296
  lastSeenTurnCount: 4,
142
297
  lastActivityAt: "2026-03-21T00:00:00.000Z",
143
298
  lastFlushedTurnId: "a1",
144
- },
145
- "codex_cli:closed": {
299
+ }),
300
+ "codex_cli:closed": makeSessionState({
146
301
  sourceTool: "codex_cli",
147
302
  sourceSessionId: "closed",
148
303
  locator: "/tmp/closed.jsonl",
@@ -151,8 +306,8 @@ describe("collectIdleSessionSources", () => {
151
306
  lastSeenTurnCount: 4,
152
307
  lastActivityAt: "2026-03-21T00:00:00.000Z",
153
308
  lastFlushedTurnId: "a2",
154
- },
155
- "codex_cli:fresh": {
309
+ }),
310
+ "codex_cli:fresh": makeSessionState({
156
311
  sourceTool: "codex_cli",
157
312
  sourceSessionId: "fresh",
158
313
  locator: "/tmp/fresh.jsonl",
@@ -161,7 +316,7 @@ describe("collectIdleSessionSources", () => {
161
316
  lastSeenTurnCount: 2,
162
317
  lastActivityAt: "2026-03-22T23:59:59.000Z",
163
318
  lastFlushedTurnId: null,
164
- },
319
+ }),
165
320
  }, new Date("2026-03-23T00:01:00.000Z"));
166
321
  expect(sources).toEqual([
167
322
  {
@@ -1 +1 @@
1
- {"version":3,"file":"flush.test.js","sourceRoot":"","sources":["../src/flush.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flush.test.js","sourceRoot":"","sources":["../src/flush.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tracemarketplace/cli",
3
- "version": "0.0.15",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tracemp": "dist/cli.js"
@@ -13,7 +13,7 @@
13
13
  "test:watch": "vitest"
14
14
  },
15
15
  "dependencies": {
16
- "@tracemarketplace/shared": "^0.0.10",
16
+ "@tracemarketplace/shared": "^0.0.13",
17
17
  "better-sqlite3": "^12.8.0",
18
18
  "chalk": "^5.3.0",
19
19
  "commander": "^12.0.0",
@@ -0,0 +1,47 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { ApiClient, ApiError } from "./api-client.js";
3
+
4
+ describe("ApiClient", () => {
5
+ const originalFetch = global.fetch;
6
+
7
+ afterEach(() => {
8
+ global.fetch = originalFetch;
9
+ vi.restoreAllMocks();
10
+ });
11
+
12
+ it("parses retry-after and json error bodies", async () => {
13
+ global.fetch = vi.fn().mockResolvedValue(
14
+ new Response(JSON.stringify({ error: "busy" }), {
15
+ status: 503,
16
+ headers: {
17
+ "content-type": "application/json",
18
+ "retry-after": "12",
19
+ },
20
+ }),
21
+ ) as typeof fetch;
22
+
23
+ const client = new ApiClient("https://example.test", "token");
24
+
25
+ await expect(client.post("/api/v1/traces/batch", {})).rejects.toEqual(
26
+ expect.objectContaining<Partial<ApiError>>({
27
+ name: "ApiError",
28
+ status: 503,
29
+ retryAfterSeconds: 12,
30
+ body: { error: "busy" },
31
+ }),
32
+ );
33
+ });
34
+
35
+ it("returns parsed json bodies on success", async () => {
36
+ global.fetch = vi.fn().mockResolvedValue(
37
+ new Response(JSON.stringify({ ok: true }), {
38
+ status: 200,
39
+ headers: { "content-type": "application/json" },
40
+ }),
41
+ ) as typeof fetch;
42
+
43
+ const client = new ApiClient("https://example.test", "token");
44
+
45
+ await expect(client.get("/health")).resolves.toEqual({ ok: true });
46
+ });
47
+ });
package/src/api-client.ts CHANGED
@@ -1,3 +1,15 @@
1
+ export class ApiError extends Error {
2
+ constructor(
3
+ message: string,
4
+ readonly status: number,
5
+ readonly body: unknown,
6
+ readonly retryAfterSeconds: number | null,
7
+ ) {
8
+ super(message);
9
+ this.name = "ApiError";
10
+ }
11
+ }
12
+
1
13
  export class ApiClient {
2
14
  constructor(
3
15
  private baseUrl: string,
@@ -5,29 +17,101 @@ export class ApiClient {
5
17
  ) {}
6
18
 
7
19
  async get(path: string): Promise<unknown> {
8
- const res = await fetch(`${this.baseUrl}${path}`, {
9
- headers: this.apiKey ? { "X-Api-Key": this.apiKey } : {},
10
- });
11
- if (!res.ok) {
12
- const text = await res.text();
13
- throw new Error(`API error ${res.status}: ${text}`);
14
- }
15
- return res.json();
20
+ return this.request("GET", path);
16
21
  }
17
22
 
18
23
  async post(path: string, body: unknown): Promise<unknown> {
24
+ return this.request("POST", path, body);
25
+ }
26
+
27
+ private async request(method: "GET" | "POST", path: string, body?: unknown): Promise<unknown> {
28
+ let reqBody: string | Uint8Array | undefined;
29
+ const extraHeaders: Record<string, string> = {};
30
+
31
+ if (method === "POST" && body !== undefined) {
32
+ const json = JSON.stringify(body);
33
+ try {
34
+ const { gzip } = await import("zlib");
35
+ const buf = await new Promise<Buffer>((resolve, reject) =>
36
+ gzip(Buffer.from(json), (err, result) => err ? reject(err) : resolve(result))
37
+ );
38
+ reqBody = new Uint8Array(buf);
39
+ extraHeaders["X-Content-Encoding"] = "gzip";
40
+ const ratio = Math.round((1 - buf.length / json.length) * 100);
41
+ console.error(`[gzip] ${Math.round(json.length / 1024)}KB → ${Math.round(buf.length / 1024)}KB (${ratio}% reduction)`);
42
+ } catch (e) {
43
+ console.error(`[gzip] compression failed, sending uncompressed: ${e}`);
44
+ reqBody = json;
45
+ }
46
+ }
47
+
19
48
  const res = await fetch(`${this.baseUrl}${path}`, {
20
- method: "POST",
49
+ method,
21
50
  headers: {
22
- "Content-Type": "application/json",
51
+ ...(method === "POST" ? { "Content-Type": "application/json" } : {}),
23
52
  ...(this.apiKey ? { "X-Api-Key": this.apiKey } : {}),
53
+ ...extraHeaders,
24
54
  },
25
- body: JSON.stringify(body),
55
+ body: reqBody as BodyInit | undefined,
26
56
  });
57
+
58
+ const parsedBody = await parseResponseBody(res);
27
59
  if (!res.ok) {
28
- const text = await res.text();
29
- throw new Error(`API error ${res.status}: ${text}`);
60
+ throw new ApiError(
61
+ `API error ${res.status}: ${formatErrorBody(parsedBody)}`,
62
+ res.status,
63
+ parsedBody,
64
+ parseRetryAfterHeader(res.headers.get("retry-after")),
65
+ );
30
66
  }
31
- return res.json();
67
+
68
+ return parsedBody;
69
+ }
70
+ }
71
+
72
+ async function parseResponseBody(res: Response): Promise<unknown> {
73
+ const text = await res.text();
74
+ if (!text) {
75
+ return null;
32
76
  }
77
+
78
+ try {
79
+ return JSON.parse(text);
80
+ } catch {
81
+ return text;
82
+ }
83
+ }
84
+
85
+ function formatErrorBody(body: unknown): string {
86
+ if (typeof body === "string") {
87
+ return body;
88
+ }
89
+
90
+ if (body && typeof body === "object") {
91
+ const error = (body as { error?: unknown }).error;
92
+ if (typeof error === "string") {
93
+ return error;
94
+ }
95
+ }
96
+
97
+ return JSON.stringify(body);
98
+ }
99
+
100
+ function parseRetryAfterHeader(value: string | null): number | null {
101
+ if (!value) {
102
+ return null;
103
+ }
104
+
105
+ const numeric = Number.parseInt(value, 10);
106
+ if (Number.isFinite(numeric)) {
107
+ return Math.max(0, numeric);
108
+ }
109
+
110
+ const dateMs = Date.parse(value);
111
+ if (Number.isNaN(dateMs)) {
112
+ return null;
113
+ }
114
+
115
+ const seconds = Math.ceil((dateMs - Date.now()) / 1000);
116
+ return Math.max(0, seconds);
33
117
  }
package/src/cli.ts CHANGED
@@ -4,7 +4,6 @@ import { dirname, join } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { program } from "commander";
6
6
  import { loginCommand } from "./commands/login.js";
7
- import { registerCommand } from "./commands/register.js";
8
7
  import { whoamiCommand } from "./commands/whoami.js";
9
8
  import { submitCommand } from "./commands/submit.js";
10
9
  import { statusCommand } from "./commands/status.js";
@@ -13,6 +12,7 @@ import { autoSubmitCommand } from "./commands/auto-submit.js";
13
12
  import { setupHookCommand } from "./commands/setup-hook.js";
14
13
  import { daemonCommand } from "./commands/daemon.js";
15
14
  import { removeHookCommand } from "./commands/remove-hook.js";
15
+ import { removeDaemonCommand } from "./commands/remove-daemon.js";
16
16
  import { CLI_NAME, DEFAULT_PROFILE, PROD_SERVER_URL } from "./constants.js";
17
17
 
18
18
  const profileOptionDescription = `Config profile (default: ${DEFAULT_PROFILE})`;
@@ -32,13 +32,6 @@ program
32
32
  .option("--server-url <url>", `Server URL (default prod: ${PROD_SERVER_URL})`)
33
33
  .action((opts) => loginCommand({ profile: opts.profile, serverUrl: opts.serverUrl }).catch(handleError));
34
34
 
35
- program
36
- .command("register")
37
- .description("Alias for login — signs in via browser and saves your API key")
38
- .option("--profile <name>", profileOptionDescription)
39
- .option("--server-url <url>", `Server URL (default prod: ${PROD_SERVER_URL})`)
40
- .action((opts) => registerCommand({ profile: opts.profile, serverUrl: opts.serverUrl }).catch(handleError));
41
-
42
35
  program
43
36
  .command("whoami")
44
37
  .description("Show your account info and balance")
@@ -52,6 +45,7 @@ program
52
45
  .option("--tool <tool>", "Only submit from specific tool (claude-code|codex|cursor)")
53
46
  .option("--session <id>", "Only submit a specific session ID")
54
47
  .option("--dry-run", "Preview without submitting")
48
+ .option("--sync", "Wait for server acknowledgement instead of fire-and-forget")
55
49
  .option("--created-since <duration>", "Only include sessions created within duration (e.g. 30d, 12h, 30m)", "30d")
56
50
  .action((opts) =>
57
51
  submitCommand({
@@ -59,6 +53,7 @@ program
59
53
  tool: opts.tool,
60
54
  session: opts.session,
61
55
  dryRun: opts.dryRun,
56
+ sync: opts.sync,
62
57
  since: opts.createdSince,
63
58
  }).catch(handleError)
64
59
  );
@@ -76,7 +71,7 @@ program
76
71
  .action((opts) => historyCommand({ profile: opts.profile }).catch(handleError));
77
72
 
78
73
  program
79
- .command("auto-submit")
74
+ .command("auto-submit", { hidden: true })
80
75
  .description("Submit the current session (called by tool hooks — do not run manually)")
81
76
  .option("--profile <name>", profileOptionDescription)
82
77
  .option("--tool <tool>", "Tool that triggered the hook (claude-code|cursor|codex)")
@@ -113,6 +108,12 @@ program
113
108
  .option("--tool <tool>", "Only remove hook for specific tool (claude-code|cursor|codex)")
114
109
  .action((opts) => removeHookCommand({ tool: opts.tool }).catch(handleError));
115
110
 
111
+ program
112
+ .command("remove-daemon")
113
+ .description("Stop a running tracemp daemon")
114
+ .option("--profile <name>", profileOptionDescription)
115
+ .action((opts) => removeDaemonCommand({ profile: opts.profile }).catch(handleError));
116
+
116
117
  function handleError(e: unknown) {
117
118
  console.error((e as Error).message ?? String(e));
118
119
  process.exit(1);
@@ -6,16 +6,25 @@
6
6
  * pass and exits. `--watch` preserves the old live-watch behavior.
7
7
  *
8
8
  * State: ~/.config/tracemarketplace/daemon-state(.<profile>).json
9
- * { [filePath]: { mtime: number, size: number } }
9
+ * { [filePath]: { mtime: number; size: number } }
10
+ *
11
+ * PID: ~/.config/tracemarketplace/daemon(.<profile>).pid
10
12
  */
11
- import { readFileSync, writeFileSync, mkdirSync, statSync, existsSync } from "fs";
13
+ import { readFileSync, writeFileSync, mkdirSync, statSync, existsSync, unlinkSync } from "fs";
12
14
  import { homedir } from "os";
13
15
  import { join } from "path";
14
16
  import chalk from "chalk";
15
- import { type Config, getConfigDir, getDaemonStatePath, loadConfig, resolveProfile } from "../config.js";
17
+ import {
18
+ type Config,
19
+ getConfigDir,
20
+ getDaemonPidPath,
21
+ getDaemonStatePath,
22
+ loadConfig,
23
+ resolveProfile,
24
+ } from "../config.js";
16
25
  import { findFiles, watchDirs } from "../sessions.js";
17
26
  import { log } from "./auto-submit.js";
18
- import { loginCommandForProfile } from "../constants.js";
27
+ import { CLI_NAME, DEFAULT_PROFILE, loginCommandForProfile } from "../constants.js";
19
28
  import { buildFileSessionSource, flushTrackedSessions } from "../flush.js";
20
29
 
21
30
  type DaemonState = Record<string, { mtime: number; size: number }>;
@@ -29,6 +38,68 @@ interface DaemonOptions {
29
38
  watch?: boolean;
30
39
  }
31
40
 
41
+ function readDaemonPid(profile: string): number | null {
42
+ const pidPath = getDaemonPidPath(profile);
43
+ if (!existsSync(pidPath)) return null;
44
+
45
+ try {
46
+ const parsed = Number.parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
47
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function isProcessRunning(pid: number): boolean {
54
+ try {
55
+ process.kill(pid, 0);
56
+ return true;
57
+ } catch (err) {
58
+ return (err as NodeJS.ErrnoException).code === "EPERM";
59
+ }
60
+ }
61
+
62
+ function removeDaemonPidFile(profile: string, expectedPid?: number) {
63
+ const pidPath = getDaemonPidPath(profile);
64
+ if (!existsSync(pidPath)) return;
65
+
66
+ try {
67
+ if (expectedPid !== undefined) {
68
+ const currentPid = Number.parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
69
+ if (currentPid !== expectedPid) return;
70
+ }
71
+ unlinkSync(pidPath);
72
+ } catch {}
73
+ }
74
+
75
+ function reserveDaemonPid(profile: string): () => void {
76
+ const existingPid = readDaemonPid(profile);
77
+ if (existingPid !== null) {
78
+ if (isProcessRunning(existingPid)) {
79
+ const profileArg = profile === DEFAULT_PROFILE ? "" : ` --profile ${profile}`;
80
+ throw new Error(
81
+ `Daemon already running for profile '${profile}' (pid ${existingPid}). Run: ${CLI_NAME} remove-daemon${profileArg}`
82
+ );
83
+ }
84
+ removeDaemonPidFile(profile);
85
+ } else {
86
+ removeDaemonPidFile(profile);
87
+ }
88
+
89
+ mkdirSync(getConfigDir(), { recursive: true });
90
+ writeFileSync(getDaemonPidPath(profile), `${process.pid}\n`, "utf-8");
91
+
92
+ let released = false;
93
+ const release = () => {
94
+ if (released) return;
95
+ released = true;
96
+ removeDaemonPidFile(profile, process.pid);
97
+ };
98
+
99
+ process.once("exit", release);
100
+ return release;
101
+ }
102
+
32
103
  function loadState(profile: string): DaemonState {
33
104
  try { return JSON.parse(readFileSync(getDaemonStatePath(profile), "utf-8")); } catch { return {}; }
34
105
  }
@@ -93,6 +164,7 @@ export async function daemonCommand(opts: DaemonOptions = {}): Promise<void> {
93
164
 
94
165
  const intervalSeconds = parseIntervalSeconds(opts.interval);
95
166
  let state = loadState(config.profile);
167
+ const releasePid = opts.once ? null : reserveDaemonPid(config.profile);
96
168
 
97
169
  console.log(chalk.bold("tracemp daemon starting"));
98
170
  console.log(chalk.gray(`Profile: ${config.profile}`));
@@ -102,7 +174,7 @@ export async function daemonCommand(opts: DaemonOptions = {}): Promise<void> {
102
174
  if (opts.watch) {
103
175
  console.log(chalk.gray("Mode: live watch"));
104
176
  console.log(chalk.gray("Press Ctrl+C to stop\n"));
105
- await runWatchLoop(config, tools, state);
177
+ await runWatchLoop(config, tools, state, releasePid ?? (() => {}));
106
178
  return;
107
179
  }
108
180
 
@@ -115,6 +187,7 @@ export async function daemonCommand(opts: DaemonOptions = {}): Promise<void> {
115
187
  const stop = () => {
116
188
  if (shuttingDown) return;
117
189
  shuttingDown = true;
190
+ releasePid?.();
118
191
  console.log(chalk.gray("\nDaemon stopped."));
119
192
  process.exit(0);
120
193
  };
@@ -169,7 +242,8 @@ async function runScanPass(
169
242
  async function runWatchLoop(
170
243
  config: Config,
171
244
  tools: Array<"claude_code" | "codex_cli">,
172
- state: DaemonState
245
+ state: DaemonState,
246
+ releasePid: () => void
173
247
  ): Promise<void> {
174
248
  let nextState = await runScanPass(config, tools, state, { logWhenEmpty: true });
175
249
  const stop = watchDirs(tools, async (tool, filePath) => {
@@ -180,11 +254,13 @@ async function runWatchLoop(
180
254
 
181
255
  process.on("SIGINT", () => {
182
256
  stop();
257
+ releasePid();
183
258
  console.log(chalk.gray("\nDaemon stopped."));
184
259
  process.exit(0);
185
260
  });
186
261
  process.on("SIGTERM", () => {
187
262
  stop();
263
+ releasePid();
188
264
  process.exit(0);
189
265
  });
190
266