@optimuslabs/runtime 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/capture-prompt.d.ts +14 -0
  4. package/dist/capture-prompt.d.ts.map +1 -0
  5. package/dist/capture-prompt.js +173 -0
  6. package/dist/capture-prompt.js.map +1 -0
  7. package/dist/index.d.ts +9 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +203 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/client-event.d.ts +12 -0
  12. package/dist/lib/client-event.d.ts.map +1 -0
  13. package/dist/lib/client-event.js +140 -0
  14. package/dist/lib/client-event.js.map +1 -0
  15. package/dist/lib/endpoint-config.d.ts +2 -0
  16. package/dist/lib/endpoint-config.d.ts.map +1 -0
  17. package/dist/lib/endpoint-config.js +2 -0
  18. package/dist/lib/endpoint-config.js.map +1 -0
  19. package/dist/lib/hardware-uuid.d.ts +7 -0
  20. package/dist/lib/hardware-uuid.d.ts.map +1 -0
  21. package/dist/lib/hardware-uuid.js +52 -0
  22. package/dist/lib/hardware-uuid.js.map +1 -0
  23. package/dist/lib/machine-auth.d.ts +11 -0
  24. package/dist/lib/machine-auth.d.ts.map +1 -0
  25. package/dist/lib/machine-auth.js +46 -0
  26. package/dist/lib/machine-auth.js.map +1 -0
  27. package/dist/lib/pending-events.d.ts +49 -0
  28. package/dist/lib/pending-events.d.ts.map +1 -0
  29. package/dist/lib/pending-events.js +135 -0
  30. package/dist/lib/pending-events.js.map +1 -0
  31. package/dist/lib/startup-sync.d.ts +13 -0
  32. package/dist/lib/startup-sync.d.ts.map +1 -0
  33. package/dist/lib/startup-sync.js +164 -0
  34. package/dist/lib/startup-sync.js.map +1 -0
  35. package/dist/lib/streaming.d.ts +35 -0
  36. package/dist/lib/streaming.d.ts.map +1 -0
  37. package/dist/lib/streaming.js +326 -0
  38. package/dist/lib/streaming.js.map +1 -0
  39. package/dist/lib/telemetry/build-event.d.ts +10 -0
  40. package/dist/lib/telemetry/build-event.d.ts.map +1 -0
  41. package/dist/lib/telemetry/build-event.js +34 -0
  42. package/dist/lib/telemetry/build-event.js.map +1 -0
  43. package/dist/lib/telemetry/canonical.d.ts +26 -0
  44. package/dist/lib/telemetry/canonical.d.ts.map +1 -0
  45. package/dist/lib/telemetry/canonical.js +153 -0
  46. package/dist/lib/telemetry/canonical.js.map +1 -0
  47. package/dist/lib/telemetry/index.d.ts +9 -0
  48. package/dist/lib/telemetry/index.d.ts.map +1 -0
  49. package/dist/lib/telemetry/index.js +9 -0
  50. package/dist/lib/telemetry/index.js.map +1 -0
  51. package/dist/lib/telemetry/serialize.d.ts +3 -0
  52. package/dist/lib/telemetry/serialize.d.ts.map +1 -0
  53. package/dist/lib/telemetry/serialize.js +15 -0
  54. package/dist/lib/telemetry/serialize.js.map +1 -0
  55. package/dist/lib/telemetry/types.d.ts +35 -0
  56. package/dist/lib/telemetry/types.d.ts.map +1 -0
  57. package/dist/lib/telemetry/types.js +14 -0
  58. package/dist/lib/telemetry/types.js.map +1 -0
  59. package/dist/lib/telemetry/wire-metadata.d.ts +10 -0
  60. package/dist/lib/telemetry/wire-metadata.d.ts.map +1 -0
  61. package/dist/lib/telemetry/wire-metadata.js +28 -0
  62. package/dist/lib/telemetry/wire-metadata.js.map +1 -0
  63. package/dist/lib/telemetry.d.ts +2 -0
  64. package/dist/lib/telemetry.d.ts.map +1 -0
  65. package/dist/lib/telemetry.js +2 -0
  66. package/dist/lib/telemetry.js.map +1 -0
  67. package/dist/lib/tofu.d.ts +16 -0
  68. package/dist/lib/tofu.d.ts.map +1 -0
  69. package/dist/lib/tofu.js +44 -0
  70. package/dist/lib/tofu.js.map +1 -0
  71. package/dist/lib/user-profile.d.ts +17 -0
  72. package/dist/lib/user-profile.d.ts.map +1 -0
  73. package/dist/lib/user-profile.js +187 -0
  74. package/dist/lib/user-profile.js.map +1 -0
  75. package/dist/test/canonical.test.d.ts +2 -0
  76. package/dist/test/canonical.test.d.ts.map +1 -0
  77. package/dist/test/canonical.test.js +138 -0
  78. package/dist/test/canonical.test.js.map +1 -0
  79. package/dist/test/endpoint-config.test.d.ts +2 -0
  80. package/dist/test/endpoint-config.test.d.ts.map +1 -0
  81. package/dist/test/endpoint-config.test.js +73 -0
  82. package/dist/test/endpoint-config.test.js.map +1 -0
  83. package/dist/test/index.test.d.ts +2 -0
  84. package/dist/test/index.test.d.ts.map +1 -0
  85. package/dist/test/index.test.js +376 -0
  86. package/dist/test/index.test.js.map +1 -0
  87. package/dist/test/integration-canonical.test.d.ts +2 -0
  88. package/dist/test/integration-canonical.test.d.ts.map +1 -0
  89. package/dist/test/integration-canonical.test.js +176 -0
  90. package/dist/test/integration-canonical.test.js.map +1 -0
  91. package/dist/test/pending-events.test.d.ts +13 -0
  92. package/dist/test/pending-events.test.d.ts.map +1 -0
  93. package/dist/test/pending-events.test.js +265 -0
  94. package/dist/test/pending-events.test.js.map +1 -0
  95. package/dist/test/startup-sync.test.d.ts +2 -0
  96. package/dist/test/startup-sync.test.d.ts.map +1 -0
  97. package/dist/test/startup-sync.test.js +57 -0
  98. package/dist/test/startup-sync.test.js.map +1 -0
  99. package/dist/test/streaming-http.test.d.ts +11 -0
  100. package/dist/test/streaming-http.test.d.ts.map +1 -0
  101. package/dist/test/streaming-http.test.js +181 -0
  102. package/dist/test/streaming-http.test.js.map +1 -0
  103. package/dist/test/streaming.test.d.ts +2 -0
  104. package/dist/test/streaming.test.d.ts.map +1 -0
  105. package/dist/test/streaming.test.js +223 -0
  106. package/dist/test/streaming.test.js.map +1 -0
  107. package/dist/test/telemetry.test.d.ts +2 -0
  108. package/dist/test/telemetry.test.d.ts.map +1 -0
  109. package/dist/test/telemetry.test.js +201 -0
  110. package/dist/test/telemetry.test.js.map +1 -0
  111. package/package.json +54 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=integration-canonical.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-canonical.test.d.ts","sourceRoot":"","sources":["../../src/test/integration-canonical.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,176 @@
1
+ import { test, beforeEach, afterEach } from "node:test";
2
+ import assert from "node:assert";
3
+ import { buildTelemetryEvent } from "../lib/telemetry.js";
4
+ import { normalizeToCanonical } from "../lib/telemetry/canonical.js";
5
+ let _savedAgentEnv = {};
6
+ beforeEach(() => {
7
+ _savedAgentEnv = {
8
+ CLAUDE_CODE_SESSION_ID: process.env.CLAUDE_CODE_SESSION_ID,
9
+ CURSOR_SESSION_ID: process.env.CURSOR_SESSION_ID,
10
+ CURSOR: process.env.CURSOR,
11
+ };
12
+ delete process.env.CLAUDE_CODE_SESSION_ID;
13
+ delete process.env.CURSOR_SESSION_ID;
14
+ delete process.env.CURSOR;
15
+ });
16
+ afterEach(() => {
17
+ for (const [key, val] of Object.entries(_savedAgentEnv)) {
18
+ if (val !== undefined)
19
+ process.env[key] = val;
20
+ else
21
+ delete process.env[key];
22
+ }
23
+ });
24
+ /**
25
+ * Integration tests verifying that Claude and Cursor produce identical
26
+ * telemetry events for the same tool invocations.
27
+ */
28
+ test("Integration: Read /etc/passwd from both agents produces same event", () => {
29
+ // Claude hook payload
30
+ const claudeRaw = {
31
+ hook_event_name: "PreToolUse",
32
+ tool_name: "Read",
33
+ tool_input: { file_path: "/etc/passwd" },
34
+ session_id: "session-123",
35
+ cwd: "/home/user",
36
+ };
37
+ // Cursor hook payload (same action, different format)
38
+ const cursorRaw = {
39
+ toolUse: { name: "Read", input: { file_path: "/etc/passwd" } },
40
+ sessionId: "session-123",
41
+ cwd: "/home/user",
42
+ };
43
+ // Normalize both
44
+ const claudeCanonical = normalizeToCanonical(claudeRaw);
45
+ const cursorCanonical = normalizeToCanonical(cursorRaw);
46
+ // Build telemetry events
47
+ const claudeEvent = buildTelemetryEvent({
48
+ tool_name: claudeCanonical.tool_name,
49
+ tool_input: claudeCanonical.tool_input,
50
+ session_id: claudeCanonical.session_id,
51
+ cwd: claudeCanonical.cwd,
52
+ hook_event_name: claudeCanonical.hook_event_name,
53
+ raw_event: claudeCanonical.raw_event,
54
+ }, "claude", claudeCanonical.session_id);
55
+ const cursorEvent = buildTelemetryEvent({
56
+ tool_name: cursorCanonical.tool_name,
57
+ tool_input: cursorCanonical.tool_input,
58
+ session_id: cursorCanonical.session_id,
59
+ cwd: cursorCanonical.cwd,
60
+ hook_event_name: cursorCanonical.hook_event_name,
61
+ raw_event: cursorCanonical.raw_event,
62
+ }, "cursor", cursorCanonical.session_id);
63
+ // Verify critical fields match (except agent_identity which should differ)
64
+ assert(claudeEvent);
65
+ assert(cursorEvent);
66
+ assert.strictEqual(claudeEvent.event_type, cursorEvent.event_type);
67
+ assert.strictEqual(claudeEvent.session_id, cursorEvent.session_id);
68
+ assert.strictEqual(claudeEvent.working_directory, cursorEvent.working_directory);
69
+ assert.strictEqual(claudeEvent.tool_call_metadata.tool_name, cursorEvent.tool_call_metadata.tool_name);
70
+ assert.deepEqual(claudeEvent.tool_call_metadata.tool_input, cursorEvent.tool_call_metadata.tool_input);
71
+ // Agent identity should be different
72
+ assert.strictEqual(claudeEvent.agent_identity, "claude");
73
+ assert.strictEqual(cursorEvent.agent_identity, "cursor");
74
+ });
75
+ test("Integration: Dangerous shell command from both agents", () => {
76
+ // Claude preToolUse with bash command
77
+ const claudeRaw = {
78
+ hook_event_name: "PreToolUse",
79
+ tool_name: "Bash",
80
+ tool_input: { command: "rm -rf /var/log" },
81
+ user_prompt: "clean up logs",
82
+ session_id: "claude-sess",
83
+ };
84
+ // Cursor preToolUse with bash command (different field names)
85
+ const cursorRaw = {
86
+ toolUse: {
87
+ name: "Bash",
88
+ input: { command: "rm -rf /var/log" },
89
+ },
90
+ user_prompt: "clean up logs",
91
+ sessionId: "cursor-sess",
92
+ };
93
+ const claudeCanonical = normalizeToCanonical(claudeRaw);
94
+ const cursorCanonical = normalizeToCanonical(cursorRaw);
95
+ // Tool information should be identical
96
+ assert.strictEqual(claudeCanonical.tool_name, cursorCanonical.tool_name);
97
+ assert.strictEqual(claudeCanonical.tool_name, "Bash");
98
+ assert.deepEqual(claudeCanonical.tool_input.command, cursorCanonical.tool_input.command);
99
+ assert.strictEqual(claudeCanonical.user_prompt, cursorCanonical.user_prompt);
100
+ // But agents should be detected correctly
101
+ assert.strictEqual(claudeCanonical.agent, "claude");
102
+ assert.strictEqual(cursorCanonical.agent, "cursor");
103
+ });
104
+ test("Integration: Web operations from both agents", () => {
105
+ const claudeRaw = {
106
+ hook_event_name: "PreToolUse",
107
+ tool_name: "WebFetch",
108
+ tool_input: { url: "https://api.github.com/user", prompt: "check auth" },
109
+ user_prompt: "verify github access",
110
+ };
111
+ const cursorRaw = {
112
+ toolName: "WebFetch",
113
+ toolInput: { url: "https://api.github.com/user", prompt: "check auth" },
114
+ userPrompt: "verify github access",
115
+ };
116
+ const claudeCanonical = normalizeToCanonical(claudeRaw);
117
+ const cursorCanonical = normalizeToCanonical(cursorRaw);
118
+ assert.strictEqual(claudeCanonical.tool_name, "WebFetch");
119
+ assert.strictEqual(cursorCanonical.tool_name, "WebFetch");
120
+ assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
121
+ });
122
+ test("Integration: MCP tool invocations from both agents", () => {
123
+ const claudeRaw = {
124
+ hook_event_name: "PreToolUse",
125
+ tool_name: "mcp__sequential_thinking__think",
126
+ tool_input: { thought: "this is complex" },
127
+ };
128
+ const cursorRaw = {
129
+ toolName: "mcp__sequential_thinking__think",
130
+ toolInput: { thought: "this is complex" },
131
+ };
132
+ const claudeCanonical = normalizeToCanonical(claudeRaw);
133
+ const cursorCanonical = normalizeToCanonical(cursorRaw);
134
+ assert.strictEqual(claudeCanonical.tool_name, cursorCanonical.tool_name);
135
+ assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
136
+ });
137
+ test("Integration: Cursor beforeShellExecution maps to same canonical as Claude preToolUse Bash", () => {
138
+ // Claude preToolUse with Bash
139
+ const claudeRaw = {
140
+ hook_event_name: "PreToolUse",
141
+ tool_name: "Bash",
142
+ tool_input: { command: "curl -X GET https://api.example.com" },
143
+ };
144
+ // Cursor beforeShellExecution with same command
145
+ const cursorRaw = {
146
+ command: "curl -X GET https://api.example.com",
147
+ };
148
+ const claudeCanonical = normalizeToCanonical(claudeRaw);
149
+ const cursorCanonical = normalizeToCanonical(cursorRaw);
150
+ // Both should identify as shell operations
151
+ assert.strictEqual(claudeCanonical.tool_name, "Bash");
152
+ assert.strictEqual(cursorCanonical.tool_name, "Bash");
153
+ // Tool input should be normalized the same way
154
+ assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
155
+ assert.strictEqual(claudeCanonical.tool_input.command, "curl -X GET https://api.example.com");
156
+ });
157
+ test("Integration: Write operations with sensitivity", () => {
158
+ const claudeRaw = {
159
+ hook_event_name: "PreToolUse",
160
+ tool_name: "Write",
161
+ tool_input: { file_path: "~/.ssh/authorized_keys" },
162
+ user_prompt: "add SSH key",
163
+ };
164
+ const cursorRaw = {
165
+ tool: { name: "Write", input: { file_path: "~/.ssh/authorized_keys" } },
166
+ userPrompt: "add SSH key",
167
+ };
168
+ const claudeCanonical = normalizeToCanonical(claudeRaw);
169
+ const cursorCanonical = normalizeToCanonical(cursorRaw);
170
+ // Sensitive path should be preserved identically
171
+ assert.strictEqual(claudeCanonical.tool_name, "Write");
172
+ assert.strictEqual(cursorCanonical.tool_name, "Write");
173
+ assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
174
+ assert.strictEqual(claudeCanonical.tool_input.file_path, "~/.ssh/authorized_keys");
175
+ });
176
+ //# sourceMappingURL=integration-canonical.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-canonical.test.js","sourceRoot":"","sources":["../../src/test/integration-canonical.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tests for pending-events.ts and streaming.ts failure routing.
3
+ *
4
+ * pending-events.ts functions are tested against a temp dir via
5
+ * OPTIMUS_TELEMETRY_DIR so we exercise the real code paths without
6
+ * touching ~/.optimuslabs/management/telemetry on the developer's machine.
7
+ *
8
+ * routeFailure + ClientError are tested to prove that 4xx errors go to
9
+ * client_errors.jsonl and network errors go to pending_events.jsonl without
10
+ * needing to mock the full HTTP stack.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=pending-events.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-events.test.d.ts","sourceRoot":"","sources":["../../src/test/pending-events.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Tests for pending-events.ts and streaming.ts failure routing.
3
+ *
4
+ * pending-events.ts functions are tested against a temp dir via
5
+ * OPTIMUS_TELEMETRY_DIR so we exercise the real code paths without
6
+ * touching ~/.optimuslabs/management/telemetry on the developer's machine.
7
+ *
8
+ * routeFailure + ClientError are tested to prove that 4xx errors go to
9
+ * client_errors.jsonl and network errors go to pending_events.jsonl without
10
+ * needing to mock the full HTTP stack.
11
+ */
12
+ import { test, before, after, beforeEach } from "node:test";
13
+ import assert from "node:assert";
14
+ import fs from "fs";
15
+ import os from "os";
16
+ import path from "path";
17
+ // --- Temp dir setup ---
18
+ const tmpDir = path.join(os.tmpdir(), `optimus-pending-test-${process.pid}`);
19
+ before(() => {
20
+ fs.mkdirSync(tmpDir, { recursive: true });
21
+ process.env.OPTIMUS_TELEMETRY_DIR = tmpDir;
22
+ });
23
+ after(() => {
24
+ delete process.env.OPTIMUS_TELEMETRY_DIR;
25
+ fs.rmSync(tmpDir, { recursive: true, force: true });
26
+ });
27
+ // Clear any files between tests so they don't interfere with each other.
28
+ beforeEach(() => {
29
+ for (const f of fs.readdirSync(tmpDir)) {
30
+ fs.rmSync(path.join(tmpDir, f), { force: true });
31
+ }
32
+ });
33
+ // Imports must come AFTER env var is set so getPendingDir() resolves correctly.
34
+ // Node ESM caches modules, but getPendingDir() is called lazily so the env
35
+ // var set in `before()` is picked up on each function call.
36
+ import { appendPendingEvents, drainPendingEvents, appendClientErrors, getPendingDir, getPendingFile, getClientErrorsFile, } from "../lib/pending-events.js";
37
+ import { ClientError, routeFailure, getBatchSize } from "../lib/streaming.js";
38
+ function mockEvent(id) {
39
+ return {
40
+ timestamp: "2024-01-01T00:00:00Z",
41
+ agent_identity: "test",
42
+ event_type: "tool_call",
43
+ tool_use_id: id,
44
+ tool_call_metadata: { tool_name: "Bash", tool_input: { command: "ls" } },
45
+ };
46
+ }
47
+ // ── getPendingDir / path helpers ────────────────────────────────────────────
48
+ test("getPendingDir returns OPTIMUS_TELEMETRY_DIR when set", () => {
49
+ assert.strictEqual(getPendingDir(), tmpDir);
50
+ });
51
+ test("getPendingFile points inside OPTIMUS_TELEMETRY_DIR", () => {
52
+ assert.strictEqual(path.dirname(getPendingFile()), tmpDir);
53
+ assert.ok(getPendingFile().endsWith("pending_events.jsonl"));
54
+ });
55
+ test("getClientErrorsFile points inside OPTIMUS_TELEMETRY_DIR", () => {
56
+ assert.strictEqual(path.dirname(getClientErrorsFile()), tmpDir);
57
+ assert.ok(getClientErrorsFile().endsWith("client_errors.jsonl"));
58
+ });
59
+ // ── appendPendingEvents ──────────────────────────────────────────────────────
60
+ test("appendPendingEvents creates file on first call", () => {
61
+ appendPendingEvents([mockEvent("p1")]);
62
+ assert.ok(fs.existsSync(getPendingFile()));
63
+ });
64
+ test("appendPendingEvents writes one JSONL line per event", () => {
65
+ appendPendingEvents([mockEvent("a"), mockEvent("b")]);
66
+ const lines = fs.readFileSync(getPendingFile(), "utf8").trim().split("\n");
67
+ assert.strictEqual(lines.length, 2);
68
+ assert.strictEqual(JSON.parse(lines[0]).tool_use_id, "a");
69
+ assert.strictEqual(JSON.parse(lines[1]).tool_use_id, "b");
70
+ });
71
+ test("appendPendingEvents accumulates across multiple calls", () => {
72
+ appendPendingEvents([mockEvent("x1")]);
73
+ appendPendingEvents([mockEvent("x2"), mockEvent("x3")]);
74
+ const events = drainPendingEvents();
75
+ assert.strictEqual(events.length, 3);
76
+ assert.strictEqual(events[0].tool_use_id, "x1");
77
+ assert.strictEqual(events[2].tool_use_id, "x3");
78
+ });
79
+ test("appendPendingEvents is a no-op for empty array", () => {
80
+ appendPendingEvents([]);
81
+ assert.ok(!fs.existsSync(getPendingFile()));
82
+ });
83
+ // ── drainPendingEvents ───────────────────────────────────────────────────────
84
+ test("drainPendingEvents returns [] when no file exists", () => {
85
+ assert.deepStrictEqual(drainPendingEvents(), []);
86
+ });
87
+ test("drainPendingEvents returns all written events", () => {
88
+ appendPendingEvents([mockEvent("r1"), mockEvent("r2")]);
89
+ const events = drainPendingEvents();
90
+ assert.strictEqual(events.length, 2);
91
+ assert.strictEqual(events[0].tool_use_id, "r1");
92
+ assert.strictEqual(events[1].tool_use_id, "r2");
93
+ });
94
+ test("drainPendingEvents removes the file", () => {
95
+ appendPendingEvents([mockEvent("d1")]);
96
+ drainPendingEvents();
97
+ assert.ok(!fs.existsSync(getPendingFile()));
98
+ });
99
+ test("drainPendingEvents is idempotent — second drain returns []", () => {
100
+ appendPendingEvents([mockEvent("i1")]);
101
+ drainPendingEvents();
102
+ assert.deepStrictEqual(drainPendingEvents(), []);
103
+ });
104
+ test("drainPendingEvents skips malformed lines", () => {
105
+ fs.writeFileSync(getPendingFile(), `${JSON.stringify(mockEvent("ok1"))}\nnot-json\n${JSON.stringify(mockEvent("ok2"))}\n`, "utf8");
106
+ const events = drainPendingEvents();
107
+ assert.strictEqual(events.length, 2);
108
+ assert.strictEqual(events[0].tool_use_id, "ok1");
109
+ assert.strictEqual(events[1].tool_use_id, "ok2");
110
+ });
111
+ test("drainPendingEvents second call returns [] after successful drain", () => {
112
+ appendPendingEvents([mockEvent("sole")]);
113
+ const first = drainPendingEvents();
114
+ assert.strictEqual(first.length, 1);
115
+ assert.strictEqual(first[0].tool_use_id, "sole");
116
+ assert.deepStrictEqual(drainPendingEvents(), []);
117
+ assert.ok(!fs.existsSync(getPendingFile() + ".draining"));
118
+ });
119
+ // ── appendClientErrors ───────────────────────────────────────────────────────
120
+ test("appendClientErrors creates client_errors.jsonl", () => {
121
+ appendClientErrors([mockEvent("c1")], 400);
122
+ assert.ok(fs.existsSync(getClientErrorsFile()));
123
+ });
124
+ test("appendClientErrors stores http_status alongside event", () => {
125
+ appendClientErrors([mockEvent("c2")], 422);
126
+ const line = fs.readFileSync(getClientErrorsFile(), "utf8").trim();
127
+ const entry = JSON.parse(line);
128
+ assert.strictEqual(entry.http_status, 422);
129
+ assert.strictEqual(entry.event.tool_use_id, "c2");
130
+ });
131
+ test("appendClientErrors stores a valid failed_at ISO timestamp", () => {
132
+ appendClientErrors([mockEvent("c3")], 403);
133
+ const line = fs.readFileSync(getClientErrorsFile(), "utf8").trim();
134
+ const entry = JSON.parse(line);
135
+ assert.ok(!isNaN(new Date(entry.failed_at).getTime()));
136
+ });
137
+ test("appendClientErrors accumulates multiple calls", () => {
138
+ appendClientErrors([mockEvent("d1")], 400);
139
+ appendClientErrors([mockEvent("d2")], 403);
140
+ const lines = fs.readFileSync(getClientErrorsFile(), "utf8").trim().split("\n");
141
+ assert.strictEqual(lines.length, 2);
142
+ assert.strictEqual(JSON.parse(lines[0]).http_status, 400);
143
+ assert.strictEqual(JSON.parse(lines[1]).http_status, 403);
144
+ });
145
+ test("appendClientErrors does not touch pending_events.jsonl", () => {
146
+ appendClientErrors([mockEvent("e1")], 400);
147
+ assert.ok(!fs.existsSync(getPendingFile()));
148
+ });
149
+ test("appendClientErrors is a no-op for empty array", () => {
150
+ appendClientErrors([], 400);
151
+ assert.ok(!fs.existsSync(getClientErrorsFile()));
152
+ });
153
+ // ── ClientError ──────────────────────────────────────────────────────────────
154
+ test("ClientError stores httpStatus", () => {
155
+ const err = new ClientError(400, "bad request");
156
+ assert.strictEqual(err.httpStatus, 400);
157
+ assert.ok(err instanceof Error);
158
+ });
159
+ test("ClientError is instanceof Error and ClientError", () => {
160
+ const err = new ClientError(422, "unprocessable");
161
+ assert.ok(err instanceof ClientError);
162
+ assert.ok(err instanceof Error);
163
+ });
164
+ // ── routeFailure — the critical branching logic ──────────────────────────────
165
+ test("routeFailure writes to client_errors.jsonl for ClientError (4xx)", () => {
166
+ const events = [mockEvent("route-4xx")];
167
+ routeFailure(events, new ClientError(400, "bad request"));
168
+ assert.ok(fs.existsSync(getClientErrorsFile()), "client_errors.jsonl should exist");
169
+ assert.ok(!fs.existsSync(getPendingFile()), "pending_events.jsonl should NOT exist");
170
+ const line = JSON.parse(fs.readFileSync(getClientErrorsFile(), "utf8").trim());
171
+ assert.strictEqual(line.http_status, 400);
172
+ assert.strictEqual(line.event.tool_use_id, "route-4xx");
173
+ });
174
+ test("routeFailure writes to pending_events.jsonl for network error", () => {
175
+ const events = [mockEvent("route-net")];
176
+ routeFailure(events, new Error("fetch failed"));
177
+ assert.ok(fs.existsSync(getPendingFile()), "pending_events.jsonl should exist");
178
+ assert.ok(!fs.existsSync(getClientErrorsFile()), "client_errors.jsonl should NOT exist");
179
+ const drained = drainPendingEvents();
180
+ assert.strictEqual(drained.length, 1);
181
+ assert.strictEqual(drained[0].tool_use_id, "route-net");
182
+ });
183
+ test("routeFailure with 401 goes to client_errors, not pending", () => {
184
+ routeFailure([mockEvent("route-401")], new ClientError(401, "unauthorized"));
185
+ assert.ok(fs.existsSync(getClientErrorsFile()));
186
+ assert.ok(!fs.existsSync(getPendingFile()));
187
+ });
188
+ test("routeFailure with 422 records correct status in client_errors", () => {
189
+ routeFailure([mockEvent("route-422")], new ClientError(422, "validation failed"));
190
+ const line = JSON.parse(fs.readFileSync(getClientErrorsFile(), "utf8").trim());
191
+ assert.strictEqual(line.http_status, 422);
192
+ });
193
+ test("routeFailure with timeout error goes to pending for retry", () => {
194
+ routeFailure([mockEvent("timeout")], new Error("Request timeout"));
195
+ const drained = drainPendingEvents();
196
+ assert.strictEqual(drained.length, 1);
197
+ assert.strictEqual(drained[0].tool_use_id, "timeout");
198
+ });
199
+ test("routeFailure separates: network failure then 4xx both recorded correctly", () => {
200
+ routeFailure([mockEvent("net1")], new Error("ECONNREFUSED"));
201
+ routeFailure([mockEvent("client1")], new ClientError(400, "bad"));
202
+ // pending has the network failure event
203
+ const drained = drainPendingEvents();
204
+ assert.strictEqual(drained.length, 1);
205
+ assert.strictEqual(drained[0].tool_use_id, "net1");
206
+ // client_errors has the 4xx event
207
+ const errLine = JSON.parse(fs.readFileSync(getClientErrorsFile(), "utf8").trim());
208
+ assert.strictEqual(errLine.event.tool_use_id, "client1");
209
+ });
210
+ // ── drain crash recovery ────────────────────────────────────────────────────
211
+ test("drainPendingEvents recovers orphan .draining when pending file is missing", () => {
212
+ const draining = getPendingFile() + ".draining";
213
+ fs.writeFileSync(draining, `${JSON.stringify(mockEvent("orphan1"))}\n${JSON.stringify(mockEvent("orphan2"))}\n`, "utf8");
214
+ assert.ok(!fs.existsSync(getPendingFile()));
215
+ const events = drainPendingEvents();
216
+ assert.strictEqual(events.length, 2);
217
+ assert.strictEqual(events[0].tool_use_id, "orphan1");
218
+ assert.strictEqual(events[1].tool_use_id, "orphan2");
219
+ assert.ok(!fs.existsSync(draining));
220
+ assert.ok(!fs.existsSync(getPendingFile()));
221
+ });
222
+ test("drainPendingEvents restores pending file when read fails after claim", (t) => {
223
+ const originalRead = fs.readFileSync.bind(fs);
224
+ let failNextDrainingRead = true;
225
+ t.mock.method(fs, "readFileSync", (p, enc) => {
226
+ if (String(p).endsWith(".draining") && failNextDrainingRead) {
227
+ failNextDrainingRead = false;
228
+ throw new Error("simulated read failure");
229
+ }
230
+ return originalRead(p, enc);
231
+ });
232
+ appendPendingEvents([mockEvent("restore-me")]);
233
+ assert.deepStrictEqual(drainPendingEvents(), []);
234
+ assert.ok(fs.existsSync(getPendingFile()), "pending file should be restored for retry");
235
+ const body = fs.readFileSync(getPendingFile(), "utf8");
236
+ assert.ok(body.includes("restore-me"));
237
+ const retry = drainPendingEvents();
238
+ assert.strictEqual(retry.length, 1);
239
+ assert.strictEqual(retry[0].tool_use_id, "restore-me");
240
+ });
241
+ // ── getBatchSize (streaming) ───────────────────────────────────────────────
242
+ test("getBatchSize coerces invalid OPTIMUS_BATCH_SIZE to 1", () => {
243
+ const prev = process.env.OPTIMUS_BATCH_SIZE;
244
+ try {
245
+ process.env.OPTIMUS_BATCH_SIZE = "not-a-number";
246
+ assert.strictEqual(getBatchSize(), 1);
247
+ process.env.OPTIMUS_BATCH_SIZE = "0";
248
+ assert.strictEqual(getBatchSize(), 1);
249
+ process.env.OPTIMUS_BATCH_SIZE = "-5";
250
+ assert.strictEqual(getBatchSize(), 1);
251
+ process.env.OPTIMUS_BATCH_SIZE = "Infinity";
252
+ assert.strictEqual(getBatchSize(), 1);
253
+ process.env.OPTIMUS_BATCH_SIZE = "4";
254
+ assert.strictEqual(getBatchSize(), 4);
255
+ }
256
+ finally {
257
+ if (prev === undefined) {
258
+ delete process.env.OPTIMUS_BATCH_SIZE;
259
+ }
260
+ else {
261
+ process.env.OPTIMUS_BATCH_SIZE = prev;
262
+ }
263
+ }
264
+ });
265
+ //# sourceMappingURL=pending-events.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-events.test.js","sourceRoot":"","sources":["../../src/test/pending-events.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=startup-sync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-sync.test.d.ts","sourceRoot":"","sources":["../../src/test/startup-sync.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { test, beforeEach, after } from "node:test";
2
+ import assert from "node:assert";
3
+ import fs from "fs";
4
+ import os from "os";
5
+ import path from "path";
6
+ import { ensureStartupMetadataSynced } from "../lib/startup-sync.js";
7
+ const tmpDir = path.join(os.tmpdir(), `optimus-startup-sync-${process.pid}`);
8
+ const orgUuid = "55555555-5555-5555-5555-555555555555";
9
+ const originalFetch = globalThis.fetch;
10
+ let capturedBody = null;
11
+ beforeEach(() => {
12
+ fs.rmSync(tmpDir, { recursive: true, force: true });
13
+ fs.mkdirSync(path.join(tmpDir, ".optimuslabs", "management", "org"), { recursive: true });
14
+ fs.writeFileSync(path.join(tmpDir, ".optimuslabs", "management", "org", "organization-uuid.txt"), `${orgUuid}\n`);
15
+ process.env.HOME = tmpDir;
16
+ process.env.OPTIMUS_ENDPOINT = "https://optimus.test/";
17
+ process.env.OPTIMUS_STARTUP_SYNC_INTERVAL_MS = "0";
18
+ process.env.OPTIMUS_USER_PROFILE_JSON = JSON.stringify({
19
+ generateduuid: "profile-id",
20
+ machineuuid: "hw-startup-sync",
21
+ mac_username: "alice",
22
+ github_username: "",
23
+ claude_email: "",
24
+ cursor_email: "",
25
+ });
26
+ delete process.env.OPTIMUS_ORGANIZATION_UUID;
27
+ capturedBody = null;
28
+ globalThis.fetch = (async (_url, init) => {
29
+ capturedBody = JSON.parse(String(init?.body || "{}"));
30
+ return new Response(JSON.stringify({ status: "accepted" }), {
31
+ status: 200,
32
+ headers: { "Content-Type": "application/json" },
33
+ });
34
+ });
35
+ });
36
+ after(() => {
37
+ globalThis.fetch = originalFetch;
38
+ delete process.env.OPTIMUS_ENDPOINT;
39
+ delete process.env.OPTIMUS_STARTUP_SYNC_INTERVAL_MS;
40
+ delete process.env.OPTIMUS_USER_PROFILE_JSON;
41
+ delete process.env.OPTIMUS_ORGANIZATION_UUID;
42
+ fs.rmSync(tmpDir, { recursive: true, force: true });
43
+ });
44
+ test("startup metadata sync sends organization_uuid from management file", async () => {
45
+ await ensureStartupMetadataSynced("hw-startup-sync");
46
+ assert.ok(capturedBody, "startup request should be posted");
47
+ const metadata = capturedBody.metadata;
48
+ assert.strictEqual(metadata.organization_uuid, orgUuid);
49
+ });
50
+ test("OPTIMUS_ORGANIZATION_UUID overrides management file", async () => {
51
+ process.env.OPTIMUS_ORGANIZATION_UUID = "66666666-6666-6666-6666-666666666666";
52
+ await ensureStartupMetadataSynced("hw-startup-sync");
53
+ assert.ok(capturedBody, "startup request should be posted");
54
+ const metadata = capturedBody.metadata;
55
+ assert.strictEqual(metadata.organization_uuid, "66666666-6666-6666-6666-666666666666");
56
+ });
57
+ //# sourceMappingURL=startup-sync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-sync.test.js","sourceRoot":"","sources":["../../src/test/startup-sync.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * End-to-end HTTP routing tests for streaming.ts.
3
+ *
4
+ * These tests prove that the actual fetch response (4xx, 5xx, network error)
5
+ * causes events to land in the right local file — without mocking the routing
6
+ * logic itself. globalThis.fetch is replaced with a controllable stub, and a
7
+ * fake auth key is pre-written so the auth handshake passes without hitting
8
+ * a real backend.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=streaming-http.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-http.test.d.ts","sourceRoot":"","sources":["../../src/test/streaming-http.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}