@cursorpool-dev/cli 0.5.6
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.
- package/bin/cursor-pool.mjs +9 -0
- package/bin/cursor-pool.ts +169 -0
- package/node_modules/@cursor-pool/extension/dist/extension.js +2910 -0
- package/node_modules/@cursor-pool/extension/package.json +64 -0
- package/node_modules/@cursor-pool/extension/resources/cursor-pool.svg +6 -0
- package/node_modules/@cursor-pool/extension/src/api.ts +545 -0
- package/node_modules/@cursor-pool/extension/src/extension.ts +104 -0
- package/node_modules/@cursor-pool/extension/src/index.ts +1 -0
- package/node_modules/@cursor-pool/extension/src/panel.ts +569 -0
- package/node_modules/@cursor-pool/extension/src/runtime.ts +22 -0
- package/node_modules/@cursor-pool/extension/test/panel.test.ts +1785 -0
- package/node_modules/@cursor-pool/patcher/package.json +17 -0
- package/node_modules/@cursor-pool/patcher/src/alwaysLocalMarker.ts +86 -0
- package/node_modules/@cursor-pool/patcher/src/hash.ts +7 -0
- package/node_modules/@cursor-pool/patcher/src/index.ts +55 -0
- package/node_modules/@cursor-pool/patcher/src/marker.ts +159 -0
- package/node_modules/@cursor-pool/patcher/src/patchCursorAgentExec.ts +154 -0
- package/node_modules/@cursor-pool/patcher/src/patchCursorAlwaysLocal.ts +142 -0
- package/node_modules/@cursor-pool/patcher/src/patchCursorWorkbenchAuthGate.ts +140 -0
- package/node_modules/@cursor-pool/patcher/src/restoreCursorAgentExec.ts +52 -0
- package/node_modules/@cursor-pool/patcher/src/restoreCursorAlwaysLocal.ts +52 -0
- package/node_modules/@cursor-pool/patcher/src/restoreCursorWorkbenchAuthGate.ts +70 -0
- package/node_modules/@cursor-pool/patcher/src/workbenchAuthGateMarker.ts +243 -0
- package/node_modules/@cursor-pool/patcher/test/patchCursorAgentExec.test.ts +630 -0
- package/node_modules/@cursor-pool/patcher/test/patchCursorAlwaysLocal.test.ts +144 -0
- package/node_modules/@cursor-pool/patcher/test/patchCursorWorkbench.test.ts +770 -0
- package/node_modules/@cursor-pool/patcher/test/restoreCursorAgentExec.test.ts +139 -0
- package/node_modules/@cursor-pool/service/package.json +17 -0
- package/node_modules/@cursor-pool/service/src/canary.ts +61 -0
- package/node_modules/@cursor-pool/service/src/diagnostics.ts +385 -0
- package/node_modules/@cursor-pool/service/src/entry.ts +161 -0
- package/node_modules/@cursor-pool/service/src/health.ts +10 -0
- package/node_modules/@cursor-pool/service/src/index.ts +29 -0
- package/node_modules/@cursor-pool/service/src/metadata.ts +22 -0
- package/node_modules/@cursor-pool/service/src/platformSession.ts +1178 -0
- package/node_modules/@cursor-pool/service/src/requestCheck.ts +81 -0
- package/node_modules/@cursor-pool/service/src/requestGate.ts +100 -0
- package/node_modules/@cursor-pool/service/src/requestGateway.ts +441 -0
- package/node_modules/@cursor-pool/service/src/runtime.ts +48 -0
- package/node_modules/@cursor-pool/service/src/server.ts +939 -0
- package/node_modules/@cursor-pool/service/src/takeover.ts +111 -0
- package/node_modules/@cursor-pool/service/test/canary.test.ts +140 -0
- package/node_modules/@cursor-pool/service/test/diagnostics.test.ts +506 -0
- package/node_modules/@cursor-pool/service/test/metadata.test.ts +63 -0
- package/node_modules/@cursor-pool/service/test/platformSession.test.ts +2428 -0
- package/node_modules/@cursor-pool/service/test/requestCheck.test.ts +152 -0
- package/node_modules/@cursor-pool/service/test/requestGate.test.ts +207 -0
- package/node_modules/@cursor-pool/service/test/requestGateway.test.ts +466 -0
- package/node_modules/@cursor-pool/service/test/runtime.test.ts +47 -0
- package/node_modules/@cursor-pool/service/test/server.test.ts +2570 -0
- package/node_modules/@cursor-pool/shared/package.json +17 -0
- package/node_modules/@cursor-pool/shared/src/clientConfig.ts +49 -0
- package/node_modules/@cursor-pool/shared/src/index.ts +14 -0
- package/node_modules/@cursor-pool/shared/src/manifest.ts +36 -0
- package/node_modules/@cursor-pool/shared/src/metadata.ts +19 -0
- package/node_modules/@cursor-pool/shared/src/paths.ts +5 -0
- package/node_modules/@cursor-pool/shared/src/runtime.ts +3 -0
- package/node_modules/@cursor-pool/shared/test/index.test.ts +56 -0
- package/node_modules/@cursor-pool/shared/test/manifest.test.ts +65 -0
- package/node_modules/@cursor-pool/shared/test/metadata.test.ts +25 -0
- package/node_modules/@cursor-pool/shared/test/runtime.test.ts +8 -0
- package/package.json +28 -0
- package/src/adHocResign.ts +65 -0
- package/src/autostart.ts +240 -0
- package/src/compat.ts +282 -0
- package/src/confirm.ts +76 -0
- package/src/cursor.ts +94 -0
- package/src/diagnostics.ts +558 -0
- package/src/environment.ts +18 -0
- package/src/extensionBundle.ts +111 -0
- package/src/extensionLink.ts +168 -0
- package/src/index.ts +23 -0
- package/src/install.ts +614 -0
- package/src/installRecord.ts +105 -0
- package/src/launch.ts +182 -0
- package/src/patchSet.ts +182 -0
- package/src/platform.ts +132 -0
- package/src/repair.ts +383 -0
- package/src/restore.ts +153 -0
- package/src/serviceCommands.ts +79 -0
- package/src/serviceProcess.ts +188 -0
- package/src/status.ts +241 -0
- package/src/target.ts +37 -0
- package/src/trial.ts +133 -0
- package/src/uninstall.ts +213 -0
- package/test/autostart.test.ts +151 -0
- package/test/compat.test.ts +192 -0
- package/test/confirm.test.ts +114 -0
- package/test/cursor-pool-bin.test.ts +658 -0
- package/test/cursor.test.ts +20 -0
- package/test/diagnostics.test.ts +709 -0
- package/test/e2e-install.test.ts +773 -0
- package/test/extensionBundle.test.ts +161 -0
- package/test/extensionLink.test.ts +209 -0
- package/test/install.test.ts +862 -0
- package/test/installRecord.test.ts +107 -0
- package/test/launch.test.ts +138 -0
- package/test/platform.test.ts +226 -0
- package/test/repair.test.ts +575 -0
- package/test/restore.test.ts +211 -0
- package/test/serviceCommands.test.ts +135 -0
- package/test/serviceProcess.test.ts +280 -0
- package/test/status.test.ts +615 -0
- package/test/target.test.ts +49 -0
- package/test/trial.test.ts +146 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import {
|
|
7
|
+
appendAgentCanaryDiagnostic,
|
|
8
|
+
appendAgentGatewayDiagnostic,
|
|
9
|
+
appendAgentRequestCheckDiagnostic,
|
|
10
|
+
} from '../src/diagnostics';
|
|
11
|
+
import type { AgentRequestCheck } from '../src/requestCheck';
|
|
12
|
+
|
|
13
|
+
const allowedGate = {
|
|
14
|
+
state: 'allowed' as const,
|
|
15
|
+
productId: 'prod_basic',
|
|
16
|
+
modeStartedAt: '2026-05-31T00:05:00.000Z',
|
|
17
|
+
};
|
|
18
|
+
const releasedGate = {
|
|
19
|
+
state: 'blocked' as const,
|
|
20
|
+
reason: 'mode-released' as const,
|
|
21
|
+
releaseReason: 'invalid-token' as const,
|
|
22
|
+
releasedAt: '2026-05-31T00:10:00.000Z',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const canary = (requestId: string) => ({
|
|
26
|
+
requestId,
|
|
27
|
+
requestType: 'agent' as const,
|
|
28
|
+
source: 'cursor-agent-exec' as const,
|
|
29
|
+
model: 'unknown',
|
|
30
|
+
receivedAt: `2026-05-30T00:00:0${requestId}.000Z`,
|
|
31
|
+
runtimeId: 'runtime-1',
|
|
32
|
+
gate: allowedGate,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const requestCheck = (requestId: string): AgentRequestCheck => ({
|
|
36
|
+
requestId,
|
|
37
|
+
requestType: 'agent',
|
|
38
|
+
source: 'manual-check',
|
|
39
|
+
model: 'unknown',
|
|
40
|
+
receivedAt: `2026-05-31T00:00:0${requestId}.000Z`,
|
|
41
|
+
runtimeId: 'runtime-1',
|
|
42
|
+
decision: { state: 'blocked', reason: 'logged-out' },
|
|
43
|
+
route: { state: 'missing' },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const gateway = (requestId: string) => ({
|
|
47
|
+
requestId,
|
|
48
|
+
requestType: 'agent' as const,
|
|
49
|
+
source: 'manual-check' as const,
|
|
50
|
+
model: 'gpt-test',
|
|
51
|
+
receivedAt: `2026-05-31T00:20:0${requestId}.000Z`,
|
|
52
|
+
runtimeId: 'runtime-1',
|
|
53
|
+
decision: {
|
|
54
|
+
state: 'accepted' as const,
|
|
55
|
+
productId: 'prod_basic',
|
|
56
|
+
modeStartedAt: '2026-05-31T00:05:00.000Z',
|
|
57
|
+
route: { state: 'ready' as const, expiresAt: '2999-05-31T00:10:00.000Z' },
|
|
58
|
+
},
|
|
59
|
+
forward: { state: 'not-configured' as const },
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('appendAgentCanaryDiagnostic writes only sanitized canary JSONL fields', async () => {
|
|
63
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
64
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await appendAgentCanaryDiagnostic(
|
|
68
|
+
{
|
|
69
|
+
...canary('1'),
|
|
70
|
+
prompt: 'do not store',
|
|
71
|
+
messages: [{ role: 'user', content: 'do not store' }],
|
|
72
|
+
apiKey: 'secret',
|
|
73
|
+
authorization: 'Bearer secret',
|
|
74
|
+
cookie: 'secret',
|
|
75
|
+
body: { hidden: true },
|
|
76
|
+
unknown: 'discard-me',
|
|
77
|
+
},
|
|
78
|
+
{ diagnosticsFile },
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
82
|
+
const lines = content.trim().split('\n');
|
|
83
|
+
assert.equal(lines.length, 1);
|
|
84
|
+
|
|
85
|
+
const saved = JSON.parse(lines[0]) as Record<string, unknown>;
|
|
86
|
+
assert.deepEqual(saved, canary('1'));
|
|
87
|
+
assert.equal(Object.hasOwn(saved, 'prompt'), false);
|
|
88
|
+
assert.equal(Object.hasOwn(saved, 'messages'), false);
|
|
89
|
+
assert.equal(Object.hasOwn(saved, 'apiKey'), false);
|
|
90
|
+
assert.equal(Object.hasOwn(saved, 'authorization'), false);
|
|
91
|
+
assert.equal(Object.hasOwn(saved, 'cookie'), false);
|
|
92
|
+
assert.equal(Object.hasOwn(saved, 'body'), false);
|
|
93
|
+
assert.equal(Object.hasOwn(saved, 'unknown'), false);
|
|
94
|
+
} finally {
|
|
95
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('appendAgentCanaryDiagnostic writes released gate diagnostics fields', async () => {
|
|
100
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
101
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await appendAgentCanaryDiagnostic(
|
|
105
|
+
{
|
|
106
|
+
...canary('1'),
|
|
107
|
+
gate: releasedGate,
|
|
108
|
+
},
|
|
109
|
+
{ diagnosticsFile },
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
113
|
+
const saved = JSON.parse(content.trim()) as Record<string, unknown>;
|
|
114
|
+
|
|
115
|
+
assert.deepEqual(saved.gate, releasedGate);
|
|
116
|
+
} finally {
|
|
117
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('appendAgentCanaryDiagnostic keeps only the most recent diagnostics entries', async () => {
|
|
122
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
123
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await appendAgentCanaryDiagnostic(canary('1'), { diagnosticsFile, maxEntries: 2 });
|
|
127
|
+
await appendAgentCanaryDiagnostic(canary('2'), { diagnosticsFile, maxEntries: 2 });
|
|
128
|
+
await appendAgentCanaryDiagnostic(canary('3'), { diagnosticsFile, maxEntries: 2 });
|
|
129
|
+
|
|
130
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
131
|
+
const saved = content
|
|
132
|
+
.trim()
|
|
133
|
+
.split('\n')
|
|
134
|
+
.map((line) => JSON.parse(line) as { requestId: string });
|
|
135
|
+
|
|
136
|
+
assert.deepEqual(
|
|
137
|
+
saved.map((entry) => entry.requestId),
|
|
138
|
+
['2', '3'],
|
|
139
|
+
);
|
|
140
|
+
} finally {
|
|
141
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('appendAgentCanaryDiagnostic drops invalid historical lines when trimming', async () => {
|
|
146
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
147
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
await writeFile(diagnosticsFile, 'not-json\n{"requestId":"old"}\n', 'utf8');
|
|
151
|
+
|
|
152
|
+
await appendAgentCanaryDiagnostic(canary('1'), { diagnosticsFile, maxEntries: 2 });
|
|
153
|
+
|
|
154
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
155
|
+
const saved = content
|
|
156
|
+
.trim()
|
|
157
|
+
.split('\n')
|
|
158
|
+
.map((line) => JSON.parse(line) as { requestId: string });
|
|
159
|
+
|
|
160
|
+
assert.deepEqual(
|
|
161
|
+
saved.map((entry) => entry.requestId),
|
|
162
|
+
['1'],
|
|
163
|
+
);
|
|
164
|
+
} finally {
|
|
165
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('appendAgentCanaryDiagnostic sanitizes historical diagnostics before rewrite', async () => {
|
|
170
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
171
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await writeFile(
|
|
175
|
+
diagnosticsFile,
|
|
176
|
+
`${JSON.stringify({
|
|
177
|
+
...canary('1'),
|
|
178
|
+
prompt: 'do not keep',
|
|
179
|
+
apiKey: 'secret',
|
|
180
|
+
gate: {
|
|
181
|
+
...allowedGate,
|
|
182
|
+
extra: 'discard-me',
|
|
183
|
+
},
|
|
184
|
+
})}\n`,
|
|
185
|
+
'utf8',
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
await appendAgentCanaryDiagnostic(canary('2'), { diagnosticsFile, maxEntries: 2 });
|
|
189
|
+
|
|
190
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
191
|
+
const saved = content
|
|
192
|
+
.trim()
|
|
193
|
+
.split('\n')
|
|
194
|
+
.map((line) => JSON.parse(line) as Record<string, unknown>);
|
|
195
|
+
|
|
196
|
+
assert.equal(saved.length, 2);
|
|
197
|
+
assert.deepEqual(saved[0], canary('1'));
|
|
198
|
+
assert.deepEqual(saved[1], canary('2'));
|
|
199
|
+
for (const entry of saved) {
|
|
200
|
+
assert.equal(Object.hasOwn(entry, 'prompt'), false);
|
|
201
|
+
assert.equal(Object.hasOwn(entry, 'apiKey'), false);
|
|
202
|
+
assert.equal(Object.hasOwn(entry.gate as Record<string, unknown>, 'extra'), false);
|
|
203
|
+
}
|
|
204
|
+
} finally {
|
|
205
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('appendAgentRequestCheckDiagnostic writes only sanitized request-check JSONL fields', async () => {
|
|
210
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
211
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
await appendAgentRequestCheckDiagnostic(
|
|
215
|
+
{
|
|
216
|
+
...requestCheck('1'),
|
|
217
|
+
prompt: 'do not store',
|
|
218
|
+
messages: [{ role: 'user', content: 'do not store' }],
|
|
219
|
+
headers: { authorization: 'Bearer secret' },
|
|
220
|
+
apiKey: 'secret',
|
|
221
|
+
cookie: 'secret',
|
|
222
|
+
body: { hidden: true },
|
|
223
|
+
unknown: 'discard-me',
|
|
224
|
+
} as AgentRequestCheck,
|
|
225
|
+
{ diagnosticsFile },
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
229
|
+
const saved = JSON.parse(content.trim()) as Record<string, unknown>;
|
|
230
|
+
|
|
231
|
+
assert.deepEqual(saved, {
|
|
232
|
+
kind: 'agent-request-check',
|
|
233
|
+
...requestCheck('1'),
|
|
234
|
+
});
|
|
235
|
+
assert.equal(Object.hasOwn(saved, 'prompt'), false);
|
|
236
|
+
assert.equal(Object.hasOwn(saved, 'messages'), false);
|
|
237
|
+
assert.equal(Object.hasOwn(saved, 'headers'), false);
|
|
238
|
+
assert.equal(Object.hasOwn(saved, 'apiKey'), false);
|
|
239
|
+
assert.equal(Object.hasOwn(saved, 'cookie'), false);
|
|
240
|
+
assert.equal(Object.hasOwn(saved, 'body'), false);
|
|
241
|
+
assert.equal(Object.hasOwn(saved, 'unknown'), false);
|
|
242
|
+
} finally {
|
|
243
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('appendAgentRequestCheckDiagnostic keeps canary and request-check diagnostics together', async () => {
|
|
248
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
249
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
await appendAgentCanaryDiagnostic(canary('1'), { diagnosticsFile, maxEntries: 3 });
|
|
253
|
+
await appendAgentRequestCheckDiagnostic(requestCheck('2'), { diagnosticsFile, maxEntries: 3 });
|
|
254
|
+
|
|
255
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
256
|
+
const saved = content
|
|
257
|
+
.trim()
|
|
258
|
+
.split('\n')
|
|
259
|
+
.map((line) => JSON.parse(line) as Record<string, unknown>);
|
|
260
|
+
|
|
261
|
+
assert.equal(saved.length, 2);
|
|
262
|
+
assert.equal(Object.hasOwn(saved[0], 'kind'), false);
|
|
263
|
+
assert.equal(saved[1].kind, 'agent-request-check');
|
|
264
|
+
assert.deepEqual(saved[1], {
|
|
265
|
+
kind: 'agent-request-check',
|
|
266
|
+
...requestCheck('2'),
|
|
267
|
+
});
|
|
268
|
+
} finally {
|
|
269
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('appendAgentRequestCheckDiagnostic writes safe route state without token', async () => {
|
|
274
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
275
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
await appendAgentRequestCheckDiagnostic({
|
|
279
|
+
requestId: 'req-route',
|
|
280
|
+
requestType: 'agent',
|
|
281
|
+
source: 'manual-check',
|
|
282
|
+
model: 'gpt-test',
|
|
283
|
+
receivedAt: '2026-05-31T00:10:00.000Z',
|
|
284
|
+
runtimeId: 'runtime-1',
|
|
285
|
+
decision: {
|
|
286
|
+
state: 'allowed',
|
|
287
|
+
productId: 'prod_basic',
|
|
288
|
+
modeStartedAt: '2026-05-31T00:05:00.000Z',
|
|
289
|
+
},
|
|
290
|
+
route: {
|
|
291
|
+
state: 'ready',
|
|
292
|
+
expiresAt: '2026-05-31T00:20:00.000Z',
|
|
293
|
+
},
|
|
294
|
+
}, { diagnosticsFile });
|
|
295
|
+
|
|
296
|
+
const saved = JSON.parse((await readFile(diagnosticsFile, 'utf8')).trim()) as Record<string, unknown>;
|
|
297
|
+
assert.deepEqual(saved.route, {
|
|
298
|
+
state: 'ready',
|
|
299
|
+
expiresAt: '2026-05-31T00:20:00.000Z',
|
|
300
|
+
});
|
|
301
|
+
assert.doesNotMatch(JSON.stringify(saved), /rt_|secret|token/i);
|
|
302
|
+
} finally {
|
|
303
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('appendAgentRequestCheckDiagnostic sanitizes historical mixed diagnostics before rewrite', async () => {
|
|
308
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
309
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
await writeFile(
|
|
313
|
+
diagnosticsFile,
|
|
314
|
+
[
|
|
315
|
+
JSON.stringify({ ...canary('1'), prompt: 'drop historical prompt' }),
|
|
316
|
+
JSON.stringify({ kind: 'agent-request-check', ...requestCheck('2'), apiKey: 'drop historical api key' }),
|
|
317
|
+
'not-json',
|
|
318
|
+
].join('\n'),
|
|
319
|
+
'utf8',
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
await appendAgentRequestCheckDiagnostic(requestCheck('3'), { diagnosticsFile, maxEntries: 3 });
|
|
323
|
+
|
|
324
|
+
const content = await readFile(diagnosticsFile, 'utf8');
|
|
325
|
+
const saved = content
|
|
326
|
+
.trim()
|
|
327
|
+
.split('\n')
|
|
328
|
+
.map((line) => JSON.parse(line) as Record<string, unknown>);
|
|
329
|
+
|
|
330
|
+
assert.equal(saved.length, 3);
|
|
331
|
+
assert.equal(Object.hasOwn(saved[0], 'kind'), false);
|
|
332
|
+
assert.equal(saved[1].kind, 'agent-request-check');
|
|
333
|
+
assert.equal(saved[2].kind, 'agent-request-check');
|
|
334
|
+
for (const entry of saved) {
|
|
335
|
+
assert.equal(Object.hasOwn(entry, 'prompt'), false);
|
|
336
|
+
assert.equal(Object.hasOwn(entry, 'apiKey'), false);
|
|
337
|
+
}
|
|
338
|
+
} finally {
|
|
339
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('appendAgentGatewayDiagnostic writes only sanitized gateway JSONL fields', async () => {
|
|
344
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
345
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
await appendAgentGatewayDiagnostic({
|
|
349
|
+
...gateway('1'),
|
|
350
|
+
prompt: 'do not print',
|
|
351
|
+
apiKey: 'secret',
|
|
352
|
+
routeToken: 'rt_secret_value',
|
|
353
|
+
} as never, { diagnosticsFile });
|
|
354
|
+
|
|
355
|
+
const saved = JSON.parse((await readFile(diagnosticsFile, 'utf8')).trim()) as Record<string, unknown>;
|
|
356
|
+
|
|
357
|
+
assert.deepEqual(saved, {
|
|
358
|
+
kind: 'agent-request-gateway',
|
|
359
|
+
...gateway('1'),
|
|
360
|
+
});
|
|
361
|
+
assert.doesNotMatch(JSON.stringify(saved), /prompt|apiKey|secret|routeToken|rt_secret_value/);
|
|
362
|
+
} finally {
|
|
363
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('appendAgentGatewayDiagnostic downgrades malformed runtime gateway decisions', async () => {
|
|
368
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
369
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
await appendAgentGatewayDiagnostic({
|
|
373
|
+
...gateway('1'),
|
|
374
|
+
decision: {
|
|
375
|
+
state: 'accepted',
|
|
376
|
+
productId: { apiKey: 'secret' },
|
|
377
|
+
modeStartedAt: 'token-secret',
|
|
378
|
+
route: {
|
|
379
|
+
state: 'ready',
|
|
380
|
+
expiresAt: { routeToken: 'rt_secret_value' },
|
|
381
|
+
token: 'secret',
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
} as never, { diagnosticsFile });
|
|
385
|
+
|
|
386
|
+
const saved = JSON.parse((await readFile(diagnosticsFile, 'utf8')).trim()) as Record<string, unknown>;
|
|
387
|
+
|
|
388
|
+
assert.deepEqual(saved.decision, { state: 'unknown' });
|
|
389
|
+
assert.doesNotMatch(
|
|
390
|
+
JSON.stringify(saved),
|
|
391
|
+
/secret|token|apiKey|routeToken|rt_secret_value|token-secret|expiresAt/i,
|
|
392
|
+
);
|
|
393
|
+
} finally {
|
|
394
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test('appendAgentGatewayDiagnostic writes sanitized forward state', async () => {
|
|
399
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
400
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
await appendAgentGatewayDiagnostic({
|
|
404
|
+
...gateway('1'),
|
|
405
|
+
forward: {
|
|
406
|
+
state: 'forwarded',
|
|
407
|
+
upstreamRequestId: 'gw_req_1',
|
|
408
|
+
acceptedAt: '2026-05-31T00:30:00.000Z',
|
|
409
|
+
routeToken: 'rt_secret_value',
|
|
410
|
+
},
|
|
411
|
+
} as never, { diagnosticsFile });
|
|
412
|
+
|
|
413
|
+
const saved = JSON.parse((await readFile(diagnosticsFile, 'utf8')).trim()) as Record<string, unknown>;
|
|
414
|
+
assert.deepEqual(saved.forward, {
|
|
415
|
+
state: 'forwarded',
|
|
416
|
+
upstreamRequestId: 'gw_req_1',
|
|
417
|
+
acceptedAt: '2026-05-31T00:30:00.000Z',
|
|
418
|
+
});
|
|
419
|
+
assert.doesNotMatch(JSON.stringify(saved), /rt_secret_value/);
|
|
420
|
+
} finally {
|
|
421
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('appendAgentGatewayDiagnostic writes insufficient credits as safe reject reason', async () => {
|
|
426
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
427
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
await appendAgentGatewayDiagnostic({
|
|
431
|
+
...gateway('8'),
|
|
432
|
+
forward: {
|
|
433
|
+
state: 'rejected',
|
|
434
|
+
reason: 'insufficient-credits',
|
|
435
|
+
},
|
|
436
|
+
credits: 0,
|
|
437
|
+
routeToken: 'rt_secret_value',
|
|
438
|
+
} as never, { diagnosticsFile });
|
|
439
|
+
|
|
440
|
+
const saved = JSON.parse((await readFile(diagnosticsFile, 'utf8')).trim()) as Record<string, unknown>;
|
|
441
|
+
assert.deepEqual(saved.forward, {
|
|
442
|
+
state: 'rejected',
|
|
443
|
+
reason: 'insufficient-credits',
|
|
444
|
+
});
|
|
445
|
+
assert.equal(Object.hasOwn(saved, 'credits'), false);
|
|
446
|
+
assert.equal(Object.hasOwn(saved, 'routeToken'), false);
|
|
447
|
+
assert.doesNotMatch(JSON.stringify(saved), /rt_secret_value/);
|
|
448
|
+
} finally {
|
|
449
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test('appendAgentGatewayDiagnostic downgrades malformed forward state', async () => {
|
|
454
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
455
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
await appendAgentGatewayDiagnostic({
|
|
459
|
+
...gateway('1'),
|
|
460
|
+
forward: {
|
|
461
|
+
state: 'forwarded',
|
|
462
|
+
upstreamRequestId: 'gw_req_1 token=rt_secret_value',
|
|
463
|
+
acceptedAt: 'not-a-date',
|
|
464
|
+
},
|
|
465
|
+
} as never, { diagnosticsFile });
|
|
466
|
+
|
|
467
|
+
const saved = JSON.parse((await readFile(diagnosticsFile, 'utf8')).trim()) as Record<string, unknown>;
|
|
468
|
+
assert.deepEqual(saved.forward, { state: 'unknown' });
|
|
469
|
+
} finally {
|
|
470
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test('appendAgentGatewayDiagnostic sanitizes historical gateway entries before rewrite', async () => {
|
|
475
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-diagnostics-'));
|
|
476
|
+
const diagnosticsFile = join(tempDir, 'diagnostics.jsonl');
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
await writeFile(
|
|
480
|
+
diagnosticsFile,
|
|
481
|
+
`${JSON.stringify({ kind: 'agent-request-gateway', ...gateway('1'), routeToken: 'rt_secret_value' })}\n`,
|
|
482
|
+
'utf8',
|
|
483
|
+
);
|
|
484
|
+
await appendAgentGatewayDiagnostic({
|
|
485
|
+
...gateway('2'),
|
|
486
|
+
decision: {
|
|
487
|
+
state: 'route-missing',
|
|
488
|
+
productId: 'prod_basic',
|
|
489
|
+
modeStartedAt: '2026-05-31T00:05:00.000Z',
|
|
490
|
+
route: { state: 'missing' },
|
|
491
|
+
},
|
|
492
|
+
}, { diagnosticsFile });
|
|
493
|
+
|
|
494
|
+
const saved = (await readFile(diagnosticsFile, 'utf8'))
|
|
495
|
+
.trim()
|
|
496
|
+
.split('\n')
|
|
497
|
+
.map((line) => JSON.parse(line) as Record<string, unknown>);
|
|
498
|
+
|
|
499
|
+
assert.equal(saved.length, 2);
|
|
500
|
+
assert.equal(saved[0].kind, 'agent-request-gateway');
|
|
501
|
+
assert.equal(saved[1].kind, 'agent-request-gateway');
|
|
502
|
+
assert.doesNotMatch(JSON.stringify(saved), /routeToken|rt_secret_value/);
|
|
503
|
+
} finally {
|
|
504
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
505
|
+
}
|
|
506
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { sanitizeServiceMetadata, sanitizeExtensionStatus } from '../src/metadata';
|
|
4
|
+
|
|
5
|
+
test('sanitizeServiceMetadata retains whitelisted request fields and defaults unknown model', () => {
|
|
6
|
+
assert.deepEqual(
|
|
7
|
+
sanitizeServiceMetadata({
|
|
8
|
+
requestId: 'req-1',
|
|
9
|
+
requestType: 'agent',
|
|
10
|
+
source: 'cursor-agent-exec',
|
|
11
|
+
prompt: 'secret prompt',
|
|
12
|
+
apiKey: 'secret-key',
|
|
13
|
+
authorization: 'bearer secret',
|
|
14
|
+
}),
|
|
15
|
+
{
|
|
16
|
+
requestId: 'req-1',
|
|
17
|
+
model: 'unknown',
|
|
18
|
+
requestType: 'agent',
|
|
19
|
+
source: 'cursor-agent-exec',
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('sanitizeServiceMetadata keeps allowed optional metadata fields', () => {
|
|
25
|
+
assert.deepEqual(
|
|
26
|
+
sanitizeServiceMetadata({
|
|
27
|
+
requestId: 'req-2',
|
|
28
|
+
model: 'claude-4',
|
|
29
|
+
requestType: 'chat',
|
|
30
|
+
source: 'cursor',
|
|
31
|
+
cursorVersion: '1.2.3',
|
|
32
|
+
clientVersion: '0.1.0',
|
|
33
|
+
entrypoint: 'agent',
|
|
34
|
+
messages: ['secret'],
|
|
35
|
+
}),
|
|
36
|
+
{
|
|
37
|
+
requestId: 'req-2',
|
|
38
|
+
model: 'claude-4',
|
|
39
|
+
requestType: 'chat',
|
|
40
|
+
source: 'cursor',
|
|
41
|
+
cursorVersion: '1.2.3',
|
|
42
|
+
clientVersion: '0.1.0',
|
|
43
|
+
entrypoint: 'agent',
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('sanitizeExtensionStatus discards forbidden extension status fields', () => {
|
|
49
|
+
assert.deepEqual(
|
|
50
|
+
sanitizeExtensionStatus({
|
|
51
|
+
connected: true,
|
|
52
|
+
cursorVersion: '1.0.0',
|
|
53
|
+
clientVersion: '0.1.0',
|
|
54
|
+
token: 'secret',
|
|
55
|
+
prompt: 'secret prompt',
|
|
56
|
+
}),
|
|
57
|
+
{
|
|
58
|
+
connected: true,
|
|
59
|
+
cursorVersion: '1.0.0',
|
|
60
|
+
clientVersion: '0.1.0',
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
});
|