@tracemarketplace/cli 0.0.13 → 0.0.17

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 (94) hide show
  1. package/dist/api-client.d.ts +9 -2
  2. package/dist/api-client.d.ts.map +1 -1
  3. package/dist/api-client.js +80 -15
  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 +48 -18
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands/auto-submit.d.ts +2 -1
  12. package/dist/commands/auto-submit.d.ts.map +1 -1
  13. package/dist/commands/auto-submit.js +43 -56
  14. package/dist/commands/auto-submit.js.map +1 -1
  15. package/dist/commands/daemon.d.ts +8 -1
  16. package/dist/commands/daemon.d.ts.map +1 -1
  17. package/dist/commands/daemon.js +184 -63
  18. package/dist/commands/daemon.js.map +1 -1
  19. package/dist/commands/history.d.ts +3 -1
  20. package/dist/commands/history.d.ts.map +1 -1
  21. package/dist/commands/history.js +8 -4
  22. package/dist/commands/history.js.map +1 -1
  23. package/dist/commands/login.d.ts +5 -1
  24. package/dist/commands/login.d.ts.map +1 -1
  25. package/dist/commands/login.js +25 -9
  26. package/dist/commands/login.js.map +1 -1
  27. package/dist/commands/register.d.ts +1 -0
  28. package/dist/commands/register.d.ts.map +1 -1
  29. package/dist/commands/register.js +4 -39
  30. package/dist/commands/register.js.map +1 -1
  31. package/dist/commands/remove-daemon.d.ts +6 -0
  32. package/dist/commands/remove-daemon.d.ts.map +1 -0
  33. package/dist/commands/remove-daemon.js +66 -0
  34. package/dist/commands/remove-daemon.js.map +1 -0
  35. package/dist/commands/remove-hook.d.ts +6 -0
  36. package/dist/commands/remove-hook.d.ts.map +1 -0
  37. package/dist/commands/remove-hook.js +174 -0
  38. package/dist/commands/remove-hook.js.map +1 -0
  39. package/dist/commands/setup-hook.d.ts +2 -0
  40. package/dist/commands/setup-hook.d.ts.map +1 -1
  41. package/dist/commands/setup-hook.js +85 -41
  42. package/dist/commands/setup-hook.js.map +1 -1
  43. package/dist/commands/status.d.ts +3 -1
  44. package/dist/commands/status.d.ts.map +1 -1
  45. package/dist/commands/status.js +8 -4
  46. package/dist/commands/status.js.map +1 -1
  47. package/dist/commands/submit.d.ts +1 -0
  48. package/dist/commands/submit.d.ts.map +1 -1
  49. package/dist/commands/submit.js +138 -83
  50. package/dist/commands/submit.js.map +1 -1
  51. package/dist/commands/whoami.d.ts +3 -1
  52. package/dist/commands/whoami.d.ts.map +1 -1
  53. package/dist/commands/whoami.js +8 -4
  54. package/dist/commands/whoami.js.map +1 -1
  55. package/dist/config.d.ts +38 -6
  56. package/dist/config.d.ts.map +1 -1
  57. package/dist/config.js +175 -17
  58. package/dist/config.js.map +1 -1
  59. package/dist/constants.d.ts +8 -0
  60. package/dist/constants.d.ts.map +1 -0
  61. package/dist/constants.js +16 -0
  62. package/dist/constants.js.map +1 -0
  63. package/dist/flush.d.ts +49 -0
  64. package/dist/flush.d.ts.map +1 -0
  65. package/dist/flush.js +405 -0
  66. package/dist/flush.js.map +1 -0
  67. package/dist/flush.test.d.ts +2 -0
  68. package/dist/flush.test.d.ts.map +1 -0
  69. package/dist/flush.test.js +330 -0
  70. package/dist/flush.test.js.map +1 -0
  71. package/dist/submitter.d.ts.map +1 -1
  72. package/dist/submitter.js +5 -2
  73. package/dist/submitter.js.map +1 -1
  74. package/package.json +8 -7
  75. package/src/api-client.test.ts +47 -0
  76. package/src/api-client.ts +100 -16
  77. package/src/cli.ts +55 -19
  78. package/src/commands/auto-submit.ts +80 -40
  79. package/src/commands/daemon.ts +243 -60
  80. package/src/commands/history.ts +9 -4
  81. package/src/commands/login.ts +37 -9
  82. package/src/commands/remove-daemon.ts +75 -0
  83. package/src/commands/remove-hook.ts +194 -0
  84. package/src/commands/setup-hook.ts +93 -43
  85. package/src/commands/status.ts +8 -4
  86. package/src/commands/submit.ts +191 -83
  87. package/src/commands/whoami.ts +8 -4
  88. package/src/config.ts +241 -21
  89. package/src/constants.ts +18 -0
  90. package/src/flush.test.ts +395 -0
  91. package/src/flush.ts +591 -0
  92. package/vitest.config.ts +8 -0
  93. package/src/commands/register.ts +0 -52
  94. package/src/submitter.ts +0 -110
@@ -0,0 +1,330 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { collectIdleSessionSources, createFreshSessionState, migrateLegacySessionState, planSessionUploads, verifyUnconfirmedChunks, } from "./flush.js";
3
+ import { migrateSessionUploadState } from "./config.js";
4
+ function makeTurn(turnId, role, timestamp, outputTokens = 0) {
5
+ return {
6
+ turn_id: turnId,
7
+ parent_turn_id: null,
8
+ role,
9
+ actor: role === "user" ? "human" : "assistant",
10
+ timestamp,
11
+ model: "test-model",
12
+ usage: {
13
+ input_tokens: 0,
14
+ output_tokens: outputTokens,
15
+ cache_read_input_tokens: null,
16
+ cache_creation_input_tokens: null,
17
+ reasoning_tokens: null,
18
+ },
19
+ source_metadata: {},
20
+ content: [
21
+ {
22
+ type: "text",
23
+ text: `${turnId}:${role}`,
24
+ },
25
+ ],
26
+ };
27
+ }
28
+ function makeTrace(sessionId, turns, endedAt = turns[turns.length - 1]?.timestamp ?? "2026-03-21T00:00:00.000Z") {
29
+ const outputTokens = turns.reduce((sum, turn) => sum + (turn.usage?.output_tokens ?? 0), 0);
30
+ return {
31
+ trace_id: `trace-${sessionId}`,
32
+ schema_version: "1.0",
33
+ source_tool: "codex_cli",
34
+ source_session_id: sessionId,
35
+ source_version: null,
36
+ submitted_by: "tester@example.com",
37
+ submitted_at: "2026-03-21T00:00:00.000Z",
38
+ extracted_at: endedAt,
39
+ git_branch: null,
40
+ cwd_hash: null,
41
+ working_language: null,
42
+ started_at: turns[0]?.timestamp ?? endedAt,
43
+ ended_at: endedAt,
44
+ turns,
45
+ turn_count: turns.length,
46
+ tool_call_count: 0,
47
+ has_tool_calls: false,
48
+ has_thinking_blocks: false,
49
+ has_file_changes: false,
50
+ has_shell_commands: false,
51
+ total_input_tokens: 0,
52
+ total_output_tokens: outputTokens,
53
+ total_cache_read_tokens: null,
54
+ content_fidelity: "full",
55
+ env_state: null,
56
+ score: null,
57
+ raw_r2_key: "",
58
+ normalized_r2_key: "",
59
+ };
60
+ }
61
+ function makeSource(tool, locator) {
62
+ return { tool, locator, label: locator };
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
+ }
77
+ describe("planSessionUploads", () => {
78
+ it("flushes a sealed 100k chunk and keeps the tail pending", () => {
79
+ const trace = makeTrace("session-100k", [
80
+ makeTurn("u1", "user", "2026-03-21T00:00:00.000Z"),
81
+ makeTurn("a1", "assistant", "2026-03-21T00:01:00.000Z", 100_200),
82
+ makeTurn("u2", "user", "2026-03-21T00:02:00.000Z"),
83
+ makeTurn("a2", "assistant", "2026-03-21T00:03:00.000Z", 25),
84
+ ]);
85
+ const cursor = createFreshSessionState(makeSource("codex_cli", "/tmp/session.jsonl"), trace);
86
+ const plan = planSessionUploads(trace, cursor, new Date("2026-03-21T00:04:00.000Z"));
87
+ expect(plan.uploads).toHaveLength(1);
88
+ expect(plan.uploads[0]?.trace.chunk_index).toBe(0);
89
+ expect(plan.uploads[0]?.trace.chunk_start_turn).toBe(0);
90
+ expect(plan.uploads[0]?.trace.chunk_complete).toBe(true);
91
+ expect(plan.uploads[0]?.trace.chunk_close_reason).toBe("100k_tokens");
92
+ expect(plan.pending).toBe(true);
93
+ expect(plan.uploads[0]?.nextState.openChunkStartTurn).toBe(2);
94
+ expect(plan.uploads[0]?.nextState.nextChunkIndex).toBe(1);
95
+ });
96
+ it("flushes an under-100k tail after two days of inactivity", () => {
97
+ const trace = makeTrace("session-idle", [
98
+ makeTurn("u1", "user", "2026-03-21T00:00:00.000Z"),
99
+ makeTurn("a1", "assistant", "2026-03-21T00:05:00.000Z", 40),
100
+ ], "2026-03-21T00:05:00.000Z");
101
+ const cursor = createFreshSessionState(makeSource("codex_cli", "/tmp/session.jsonl"), trace);
102
+ const plan = planSessionUploads(trace, cursor, new Date("2026-03-23T00:06:00.000Z"));
103
+ expect(plan.uploads).toHaveLength(1);
104
+ expect(plan.uploads[0]?.trace.chunk_index).toBe(0);
105
+ expect(plan.uploads[0]?.trace.chunk_close_reason).toBe("idle_2d");
106
+ expect(plan.pending).toBe(false);
107
+ expect(plan.uploads[0]?.nextState.openChunkStartTurn).toBe(trace.turn_count);
108
+ });
109
+ it("creates a later chunk when a session resumes after an idle-finalized chunk", () => {
110
+ const initialTrace = makeTrace("session-resume", [
111
+ makeTurn("u1", "user", "2026-03-21T00:00:00.000Z"),
112
+ makeTurn("a1", "assistant", "2026-03-21T00:05:00.000Z", 20),
113
+ ], "2026-03-21T00:05:00.000Z");
114
+ const source = makeSource("codex_cli", "/tmp/session.jsonl");
115
+ const initialCursor = createFreshSessionState(source, initialTrace);
116
+ const initialPlan = planSessionUploads(initialTrace, initialCursor, new Date("2026-03-23T00:06:00.000Z"));
117
+ const finalizedCursor = initialPlan.uploads[0]?.nextState;
118
+ if (!finalizedCursor) {
119
+ throw new Error("expected initial chunk upload");
120
+ }
121
+ const resumedTrace = makeTrace("session-resume", [
122
+ ...initialTrace.turns,
123
+ makeTurn("u2", "user", "2026-03-24T00:00:00.000Z"),
124
+ makeTurn("a2", "assistant", "2026-03-24T00:10:00.000Z", 15),
125
+ ], "2026-03-24T00:10:00.000Z");
126
+ const resumedPlan = planSessionUploads(resumedTrace, finalizedCursor, new Date("2026-03-26T00:11:00.000Z"));
127
+ expect(resumedPlan.uploads).toHaveLength(1);
128
+ expect(resumedPlan.uploads[0]?.trace.chunk_index).toBe(1);
129
+ expect(resumedPlan.uploads[0]?.trace.chunk_start_turn).toBe(2);
130
+ expect(resumedPlan.uploads[0]?.trace.chunk_close_reason).toBe("idle_2d");
131
+ expect(resumedPlan.pending).toBe(false);
132
+ });
133
+ it("does not re-upload a legacy session on first migration", () => {
134
+ const trace = makeTrace("legacy-session", [
135
+ makeTurn("u1", "user", "2026-03-21T00:00:00.000Z"),
136
+ makeTurn("a1", "assistant", "2026-03-21T00:05:00.000Z", 55),
137
+ ]);
138
+ const migratedCursor = migrateLegacySessionState(makeSource("codex_cli", "/tmp/legacy.jsonl"), trace, 0);
139
+ const plan = planSessionUploads(trace, migratedCursor, new Date("2026-03-23T00:06:00.000Z"));
140
+ expect(plan.uploads).toHaveLength(0);
141
+ expect(plan.pending).toBe(false);
142
+ expect(plan.observedState.nextChunkIndex).toBe(1);
143
+ expect(plan.observedState.openChunkStartTurn).toBe(trace.turn_count);
144
+ });
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
+ });
287
+ describe("collectIdleSessionSources", () => {
288
+ it("returns only tracked sessions with an open tail older than two days", () => {
289
+ const sources = collectIdleSessionSources({
290
+ "codex_cli:idle": makeSessionState({
291
+ sourceTool: "codex_cli",
292
+ sourceSessionId: "idle",
293
+ locator: "/tmp/idle.jsonl",
294
+ nextChunkIndex: 1,
295
+ openChunkStartTurn: 2,
296
+ lastSeenTurnCount: 4,
297
+ lastActivityAt: "2026-03-21T00:00:00.000Z",
298
+ lastFlushedTurnId: "a1",
299
+ }),
300
+ "codex_cli:closed": makeSessionState({
301
+ sourceTool: "codex_cli",
302
+ sourceSessionId: "closed",
303
+ locator: "/tmp/closed.jsonl",
304
+ nextChunkIndex: 1,
305
+ openChunkStartTurn: 4,
306
+ lastSeenTurnCount: 4,
307
+ lastActivityAt: "2026-03-21T00:00:00.000Z",
308
+ lastFlushedTurnId: "a2",
309
+ }),
310
+ "codex_cli:fresh": makeSessionState({
311
+ sourceTool: "codex_cli",
312
+ sourceSessionId: "fresh",
313
+ locator: "/tmp/fresh.jsonl",
314
+ nextChunkIndex: 0,
315
+ openChunkStartTurn: 0,
316
+ lastSeenTurnCount: 2,
317
+ lastActivityAt: "2026-03-22T23:59:59.000Z",
318
+ lastFlushedTurnId: null,
319
+ }),
320
+ }, new Date("2026-03-23T00:01:00.000Z"));
321
+ expect(sources).toEqual([
322
+ {
323
+ tool: "codex_cli",
324
+ locator: "/tmp/idle.jsonl",
325
+ label: "codex_cli:idle",
326
+ },
327
+ ]);
328
+ });
329
+ });
330
+ //# sourceMappingURL=flush.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flush.test.js","sourceRoot":"","sources":["../src/flush.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"submitter.d.ts","sourceRoot":"","sources":["../src/submitter.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,aAAa,GAAG,WAAW,EACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CAkBvB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CAQvB"}
1
+ {"version":3,"file":"submitter.d.ts","sourceRoot":"","sources":["../src/submitter.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,aAAa,GAAG,WAAW,EACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CAkBvB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CAavB"}
package/dist/submitter.js CHANGED
@@ -41,10 +41,13 @@ export async function submitCursorSession(sessionId, config) {
41
41
  catch (err) {
42
42
  return { accepted: false, superseded: false, duplicate: false, turnCount: 0, payoutCents: 0, traceId: null, error: `Cursor extraction failed: ${err}` };
43
43
  }
44
+ if (trace.turn_count === 0) {
45
+ return { accepted: false, superseded: false, duplicate: false, turnCount: 0, payoutCents: 0, traceId: null, error: "Empty session" };
46
+ }
44
47
  return submitTrace(trace, config);
45
48
  }
46
49
  async function submitTrace(trace, config) {
47
- const state = loadState();
50
+ const state = loadState(config.profile);
48
51
  const key = stateKey(trace.source_tool, trace.source_session_id);
49
52
  const lastSubmittedChunk = state.chunks[key] ?? -1;
50
53
  // Chunk the session and only send new chunks
@@ -63,7 +66,7 @@ async function submitTrace(trace, config) {
63
66
  // Persist the highest chunk index we successfully submitted
64
67
  const maxChunk = Math.max(...newChunks.map((c) => c.chunk_index ?? 0));
65
68
  state.chunks[key] = maxChunk;
66
- saveState(state);
69
+ saveState(state, config.profile);
67
70
  const totalPayout = result.traces?.reduce((s, t) => s + (t.payout_cents ?? 0), 0) ?? 0;
68
71
  const first = result.traces?.[0];
69
72
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"submitter.js","sourceRoot":"","sources":["../src/submitter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"submitter.js","sourceRoot":"","sources":["../src/submitter.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.13",
3
+ "version": "0.0.17",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tracemp": "dist/cli.js"
@@ -8,24 +8,25 @@
8
8
  "scripts": {
9
9
  "build": "tsc",
10
10
  "dev": "tsx src/cli.ts",
11
- "typecheck": "tsc --noEmit"
11
+ "typecheck": "tsc --noEmit",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest"
12
14
  },
13
15
  "dependencies": {
14
- "@tracemarketplace/shared": "^0.0.8",
16
+ "@tracemarketplace/shared": "^0.0.11",
15
17
  "better-sqlite3": "^12.8.0",
16
18
  "chalk": "^5.3.0",
17
19
  "commander": "^12.0.0",
18
- "fzstd": "^0.1.1",
19
20
  "inquirer": "^9.2.0",
20
21
  "open": "^11.0.0",
21
- "ora": "^8.0.0",
22
- "p-limit": "^5.0.0"
22
+ "ora": "^8.0.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/better-sqlite3": "^7.6.0",
26
26
  "@types/inquirer": "^9.0.7",
27
27
  "@types/node": "^20.0.0",
28
28
  "tsx": "^4.7.0",
29
- "typescript": "^5.4.0"
29
+ "typescript": "^5.4.0",
30
+ "vitest": "^1.0.0"
30
31
  }
31
32
  }
@@ -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,33 +1,117 @@
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,
4
- private apiKey: string
16
+ private apiKey?: string
5
17
  ) {}
6
18
 
7
19
  async get(path: string): Promise<unknown> {
8
- const res = await fetch(`${this.baseUrl}${path}`, {
9
- headers: { "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",
23
- "X-Api-Key": this.apiKey,
51
+ ...(method === "POST" ? { "Content-Type": "application/json" } : {}),
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
  }