@roamcode.ai/server 1.0.0 → 1.0.2
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/dist/index.d.ts +2406 -82
- package/dist/index.js +8324 -44
- package/dist/managed-update-helper.js +631 -26
- package/dist/mcp-send.js +76 -90
- package/dist/start.d.ts +591 -9
- package/dist/start.js +7629 -353
- package/package.json +3 -3
- package/dist/auth.d.ts +0 -63
- package/dist/auth.js +0 -133
- package/dist/claude-auth-service.d.ts +0 -76
- package/dist/claude-auth-service.js +0 -217
- package/dist/claude-latest-service.d.ts +0 -34
- package/dist/claude-latest-service.js +0 -61
- package/dist/config.d.ts +0 -78
- package/dist/config.js +0 -59
- package/dist/data-dir.d.ts +0 -42
- package/dist/data-dir.js +0 -70
- package/dist/diag.d.ts +0 -43
- package/dist/diag.js +0 -83
- package/dist/fs-service.d.ts +0 -90
- package/dist/fs-service.js +0 -290
- package/dist/managed-runtime.d.ts +0 -51
- package/dist/managed-runtime.js +0 -411
- package/dist/managed-update-helper.d.ts +0 -2
- package/dist/mcp-send.d.ts +0 -33
- package/dist/origin-check.d.ts +0 -37
- package/dist/origin-check.js +0 -101
- package/dist/pane-status.d.ts +0 -61
- package/dist/pane-status.js +0 -145
- package/dist/providers/claude-metadata-service.d.ts +0 -58
- package/dist/providers/claude-metadata-service.js +0 -352
- package/dist/providers/claude-provider.d.ts +0 -11
- package/dist/providers/claude-provider.js +0 -166
- package/dist/providers/codex-activity.d.ts +0 -21
- package/dist/providers/codex-activity.js +0 -122
- package/dist/providers/codex-app-server-client.d.ts +0 -90
- package/dist/providers/codex-app-server-client.js +0 -485
- package/dist/providers/codex-latest-service.d.ts +0 -50
- package/dist/providers/codex-latest-service.js +0 -174
- package/dist/providers/codex-metadata-service.d.ts +0 -161
- package/dist/providers/codex-metadata-service.js +0 -686
- package/dist/providers/codex-profile-client.d.ts +0 -16
- package/dist/providers/codex-profile-client.js +0 -52
- package/dist/providers/codex-profile-security.d.ts +0 -23
- package/dist/providers/codex-profile-security.js +0 -161
- package/dist/providers/codex-provider.d.ts +0 -15
- package/dist/providers/codex-provider.js +0 -174
- package/dist/providers/codex-thread-coordinator.d.ts +0 -18
- package/dist/providers/codex-thread-coordinator.js +0 -93
- package/dist/providers/codex-thread-persistence.d.ts +0 -9
- package/dist/providers/codex-thread-persistence.js +0 -45
- package/dist/providers/codex-thread-resolver.d.ts +0 -59
- package/dist/providers/codex-thread-resolver.js +0 -322
- package/dist/providers/options.d.ts +0 -7
- package/dist/providers/options.js +0 -155
- package/dist/providers/provider-artifacts.d.ts +0 -3
- package/dist/providers/provider-artifacts.js +0 -30
- package/dist/providers/registry.d.ts +0 -7
- package/dist/providers/registry.js +0 -23
- package/dist/providers/types.d.ts +0 -95
- package/dist/providers/types.js +0 -8
- package/dist/push-dispatch.d.ts +0 -81
- package/dist/push-dispatch.js +0 -100
- package/dist/push-store.d.ts +0 -25
- package/dist/push-store.js +0 -79
- package/dist/rate-limit.d.ts +0 -52
- package/dist/rate-limit.js +0 -72
- package/dist/server-config.d.ts +0 -60
- package/dist/server-config.js +0 -77
- package/dist/service-install.d.ts +0 -60
- package/dist/service-install.js +0 -221
- package/dist/session-defaults.d.ts +0 -26
- package/dist/session-defaults.js +0 -60
- package/dist/session-store.d.ts +0 -81
- package/dist/session-store.js +0 -654
- package/dist/static-routes.d.ts +0 -101
- package/dist/static-routes.js +0 -188
- package/dist/terminal-capability.d.ts +0 -5
- package/dist/terminal-capability.js +0 -27
- package/dist/terminal-manager.d.ts +0 -224
- package/dist/terminal-manager.js +0 -917
- package/dist/terminal-process.d.ts +0 -85
- package/dist/terminal-process.js +0 -238
- package/dist/terminal-shared.d.ts +0 -36
- package/dist/terminal-shared.js +0 -43
- package/dist/tmux-list.d.ts +0 -11
- package/dist/tmux-list.js +0 -39
- package/dist/transport.d.ts +0 -123
- package/dist/transport.js +0 -1559
- package/dist/updater.d.ts +0 -161
- package/dist/updater.js +0 -451
- package/dist/usage-service.d.ts +0 -118
- package/dist/usage-service.js +0 -173
- package/dist/vapid.d.ts +0 -17
- package/dist/vapid.js +0 -31
- package/dist/web-push-send.d.ts +0 -20
- package/dist/web-push-send.js +0 -21
- package/dist/ws-ticket.d.ts +0 -47
- package/dist/ws-ticket.js +0 -62
|
@@ -1,485 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
export const CODEX_METADATA_ERROR_CODE = "CODEX_METADATA_UNAVAILABLE";
|
|
5
|
-
export const DEFAULT_CODEX_APP_SERVER_TIMEOUT_MS = 15_000;
|
|
6
|
-
export const DEFAULT_CODEX_APP_SERVER_MAX_STDOUT_LINE_BYTES = 1024 * 1024;
|
|
7
|
-
export const DEFAULT_CODEX_APP_SERVER_MAX_STDOUT_BUFFER_BYTES = 2 * 1024 * 1024;
|
|
8
|
-
export const DEFAULT_CODEX_APP_SERVER_MAX_STDERR_BYTES = 64 * 1024;
|
|
9
|
-
export class CodexMetadataUnavailableError extends Error {
|
|
10
|
-
code = CODEX_METADATA_ERROR_CODE;
|
|
11
|
-
constructor() {
|
|
12
|
-
super("Codex metadata is unavailable");
|
|
13
|
-
this.name = "CodexMetadataUnavailableError";
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
const JsonRpcSuccessSchema = z.object({
|
|
17
|
-
jsonrpc: z.literal("2.0").optional(),
|
|
18
|
-
id: z.number().int().nonnegative(),
|
|
19
|
-
result: z.unknown(),
|
|
20
|
-
});
|
|
21
|
-
const JsonRpcErrorSchema = z.object({
|
|
22
|
-
jsonrpc: z.literal("2.0").optional(),
|
|
23
|
-
id: z.number().int().nonnegative(),
|
|
24
|
-
error: z.object({
|
|
25
|
-
code: z.number().int(),
|
|
26
|
-
message: z.string(),
|
|
27
|
-
data: z.unknown().optional(),
|
|
28
|
-
}),
|
|
29
|
-
});
|
|
30
|
-
const JsonRpcNotificationSchema = z.object({
|
|
31
|
-
jsonrpc: z.literal("2.0").optional(),
|
|
32
|
-
method: z.string().min(1),
|
|
33
|
-
params: z.unknown().optional(),
|
|
34
|
-
});
|
|
35
|
-
const InitializeResultSchema = z.unknown();
|
|
36
|
-
function unavailable() {
|
|
37
|
-
return new CodexMetadataUnavailableError();
|
|
38
|
-
}
|
|
39
|
-
// Node writable streams may invoke a failed write callback and emit `error` afterwards. This
|
|
40
|
-
// closure-free sink deliberately stays on a dead transport so that late stream errors can never
|
|
41
|
-
// become uncaught host-process exceptions after the generation's active listeners are removed.
|
|
42
|
-
const absorbLateTransportError = () => { };
|
|
43
|
-
function positiveInteger(value, fallback) {
|
|
44
|
-
return value !== undefined && Number.isSafeInteger(value) && value > 0 ? value : fallback;
|
|
45
|
-
}
|
|
46
|
-
function defaultSpawnTransport(command, args, options) {
|
|
47
|
-
return spawn(command, [...args], { env: options?.env, stdio: ["pipe", "pipe", "pipe"] });
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Bounded JSON-lines client for the auxiliary Codex app-server metadata channel.
|
|
51
|
-
* It never participates in terminal streaming and deliberately knows no method-specific result schemas.
|
|
52
|
-
*/
|
|
53
|
-
export class CodexAppServerClient {
|
|
54
|
-
limits;
|
|
55
|
-
codexBin;
|
|
56
|
-
profile;
|
|
57
|
-
env;
|
|
58
|
-
spawnTransport;
|
|
59
|
-
notificationListeners = new Set();
|
|
60
|
-
active;
|
|
61
|
-
startPromise;
|
|
62
|
-
stopPromise;
|
|
63
|
-
nextRequestId = 1;
|
|
64
|
-
nextGeneration = 1;
|
|
65
|
-
lastDiagnostics = { stderrBytes: 0, stderrTruncated: false };
|
|
66
|
-
constructor(options = {}) {
|
|
67
|
-
this.codexBin = options.codexBin ?? "codex";
|
|
68
|
-
this.env = options.env ? { ...options.env } : undefined;
|
|
69
|
-
if (options.profile !== undefined &&
|
|
70
|
-
(options.profile.length === 0 ||
|
|
71
|
-
options.profile.length > 128 ||
|
|
72
|
-
!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(options.profile))) {
|
|
73
|
-
throw unavailable();
|
|
74
|
-
}
|
|
75
|
-
this.profile = options.profile;
|
|
76
|
-
this.spawnTransport = options.spawnTransport ?? defaultSpawnTransport;
|
|
77
|
-
const maxStdoutLineBytes = positiveInteger(options.maxStdoutLineBytes, DEFAULT_CODEX_APP_SERVER_MAX_STDOUT_LINE_BYTES);
|
|
78
|
-
this.limits = {
|
|
79
|
-
timeoutMs: positiveInteger(options.timeoutMs, DEFAULT_CODEX_APP_SERVER_TIMEOUT_MS),
|
|
80
|
-
maxStdoutLineBytes,
|
|
81
|
-
maxStdoutBufferBytes: positiveInteger(options.maxStdoutBufferBytes, DEFAULT_CODEX_APP_SERVER_MAX_STDOUT_BUFFER_BYTES),
|
|
82
|
-
maxStderrBytes: positiveInteger(options.maxStderrBytes, DEFAULT_CODEX_APP_SERVER_MAX_STDERR_BYTES),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
get diagnostics() {
|
|
86
|
-
const generation = this.active;
|
|
87
|
-
return generation
|
|
88
|
-
? {
|
|
89
|
-
...this.lastDiagnostics,
|
|
90
|
-
stderrBytes: generation.stderrBytes,
|
|
91
|
-
stderrTruncated: generation.stderrTruncated,
|
|
92
|
-
}
|
|
93
|
-
: { ...this.lastDiagnostics };
|
|
94
|
-
}
|
|
95
|
-
start() {
|
|
96
|
-
if (this.active?.phase === "running" && !this.active.closed)
|
|
97
|
-
return Promise.resolve();
|
|
98
|
-
if (this.startPromise)
|
|
99
|
-
return this.startPromise;
|
|
100
|
-
if (this.stopPromise)
|
|
101
|
-
return this.stopPromise.then(() => this.start());
|
|
102
|
-
let transport;
|
|
103
|
-
try {
|
|
104
|
-
transport = this.spawnTransport(this.codexBin, this.profile ? ["--profile", this.profile, "app-server", "--stdio"] : ["app-server", "--stdio"], { env: this.env ? { ...this.env } : undefined });
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
this.recordIssue("spawn");
|
|
108
|
-
return Promise.reject(unavailable());
|
|
109
|
-
}
|
|
110
|
-
const generation = this.createGeneration(transport);
|
|
111
|
-
this.active = generation;
|
|
112
|
-
this.lastDiagnostics = { stderrBytes: 0, stderrTruncated: false };
|
|
113
|
-
this.installListeners(generation);
|
|
114
|
-
const promise = this.sendRequest(generation, "initialize", {
|
|
115
|
-
clientInfo: { name: "roamcode", title: "RoamCode", version: "0.0.0" },
|
|
116
|
-
capabilities: {},
|
|
117
|
-
}, InitializeResultSchema)
|
|
118
|
-
.then(() => this.writeFrame(generation, { method: "initialized", params: {} }))
|
|
119
|
-
.then(() => {
|
|
120
|
-
if (generation.closed || this.active !== generation)
|
|
121
|
-
throw unavailable();
|
|
122
|
-
generation.phase = "running";
|
|
123
|
-
})
|
|
124
|
-
.catch(() => {
|
|
125
|
-
if (!generation.closed)
|
|
126
|
-
this.failGeneration(generation, "transport_error", true);
|
|
127
|
-
throw unavailable();
|
|
128
|
-
});
|
|
129
|
-
this.startPromise = promise;
|
|
130
|
-
void promise.then(() => {
|
|
131
|
-
if (this.startPromise === promise)
|
|
132
|
-
this.startPromise = undefined;
|
|
133
|
-
}, () => {
|
|
134
|
-
if (this.startPromise === promise)
|
|
135
|
-
this.startPromise = undefined;
|
|
136
|
-
});
|
|
137
|
-
return promise;
|
|
138
|
-
}
|
|
139
|
-
request(method, params, schema) {
|
|
140
|
-
const generation = this.active;
|
|
141
|
-
if (!generation || generation.closed || generation.phase !== "running" || !method) {
|
|
142
|
-
return Promise.reject(unavailable());
|
|
143
|
-
}
|
|
144
|
-
return this.sendRequest(generation, method, params, schema);
|
|
145
|
-
}
|
|
146
|
-
onNotification(listener) {
|
|
147
|
-
this.notificationListeners.add(listener);
|
|
148
|
-
return () => this.notificationListeners.delete(listener);
|
|
149
|
-
}
|
|
150
|
-
stop() {
|
|
151
|
-
if (this.stopPromise)
|
|
152
|
-
return this.stopPromise;
|
|
153
|
-
const promise = Promise.resolve();
|
|
154
|
-
this.stopPromise = promise;
|
|
155
|
-
const generation = this.active;
|
|
156
|
-
if (generation)
|
|
157
|
-
this.failGeneration(generation, "transport_error", true, true);
|
|
158
|
-
void promise.then(() => {
|
|
159
|
-
if (this.stopPromise === promise)
|
|
160
|
-
this.stopPromise = undefined;
|
|
161
|
-
});
|
|
162
|
-
return promise;
|
|
163
|
-
}
|
|
164
|
-
createGeneration(transport) {
|
|
165
|
-
const generation = {
|
|
166
|
-
number: this.nextGeneration++,
|
|
167
|
-
transport,
|
|
168
|
-
pending: new Map(),
|
|
169
|
-
writeQueue: [],
|
|
170
|
-
stdoutBuffer: Buffer.alloc(0),
|
|
171
|
-
stderrBytes: 0,
|
|
172
|
-
stderrTruncated: false,
|
|
173
|
-
phase: "starting",
|
|
174
|
-
closed: false,
|
|
175
|
-
writing: false,
|
|
176
|
-
listeners: {
|
|
177
|
-
stdoutData: (chunk) => this.onStdout(generation, chunk),
|
|
178
|
-
stderrData: (chunk) => this.onStderr(generation, chunk),
|
|
179
|
-
exit: () => this.failGeneration(generation, "exit", false),
|
|
180
|
-
error: () => this.failGeneration(generation, "transport_error", true),
|
|
181
|
-
stdinError: () => this.failGeneration(generation, "write_error", true),
|
|
182
|
-
stdoutError: () => this.failGeneration(generation, "transport_error", true),
|
|
183
|
-
stderrError: () => this.failGeneration(generation, "transport_error", true),
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
return generation;
|
|
187
|
-
}
|
|
188
|
-
installListeners(generation) {
|
|
189
|
-
const { transport, listeners } = generation;
|
|
190
|
-
transport.on("error", absorbLateTransportError);
|
|
191
|
-
transport.stdin.on("error", absorbLateTransportError);
|
|
192
|
-
transport.stdout.on("error", absorbLateTransportError);
|
|
193
|
-
transport.stderr.on("error", absorbLateTransportError);
|
|
194
|
-
transport.stdout.on("data", listeners.stdoutData);
|
|
195
|
-
transport.stderr.on("data", listeners.stderrData);
|
|
196
|
-
transport.on("exit", listeners.exit);
|
|
197
|
-
transport.on("error", listeners.error);
|
|
198
|
-
transport.stdin.on("error", listeners.stdinError);
|
|
199
|
-
transport.stdout.on("error", listeners.stdoutError);
|
|
200
|
-
transport.stderr.on("error", listeners.stderrError);
|
|
201
|
-
}
|
|
202
|
-
sendRequest(generation, method, params, schema) {
|
|
203
|
-
if (generation.closed || this.active !== generation)
|
|
204
|
-
return Promise.reject(unavailable());
|
|
205
|
-
const id = this.nextRequestId++;
|
|
206
|
-
const response = new Promise((resolve, reject) => {
|
|
207
|
-
const timer = setTimeout(() => this.failGeneration(generation, "transport_error", true), this.limits.timeoutMs);
|
|
208
|
-
generation.pending.set(id, {
|
|
209
|
-
schema: schema,
|
|
210
|
-
resolve: (value) => resolve(value),
|
|
211
|
-
reject,
|
|
212
|
-
timer,
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
const written = this.writeFrame(generation, { id, method, params }).catch(() => {
|
|
216
|
-
if (!generation.closed) {
|
|
217
|
-
this.failGeneration(generation, "write_error", true);
|
|
218
|
-
}
|
|
219
|
-
throw unavailable();
|
|
220
|
-
});
|
|
221
|
-
return Promise.all([written, response]).then(([, value]) => value);
|
|
222
|
-
}
|
|
223
|
-
writeFrame(generation, value) {
|
|
224
|
-
if (generation.closed || this.active !== generation)
|
|
225
|
-
return Promise.reject(unavailable());
|
|
226
|
-
let frame;
|
|
227
|
-
try {
|
|
228
|
-
frame = `${JSON.stringify(value)}\n`;
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
return Promise.reject(unavailable());
|
|
232
|
-
}
|
|
233
|
-
return new Promise((resolve, reject) => {
|
|
234
|
-
const timer = setTimeout(() => {
|
|
235
|
-
if (!generation.closed)
|
|
236
|
-
this.failGeneration(generation, "write_error", true);
|
|
237
|
-
}, this.limits.timeoutMs);
|
|
238
|
-
generation.writeQueue.push({ frame, resolve, reject, timer });
|
|
239
|
-
this.flushWrites(generation);
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
flushWrites(generation) {
|
|
243
|
-
if (generation.closed || generation.writing)
|
|
244
|
-
return;
|
|
245
|
-
const entry = generation.writeQueue[0];
|
|
246
|
-
if (!entry)
|
|
247
|
-
return;
|
|
248
|
-
generation.writing = true;
|
|
249
|
-
let callbackDone = false;
|
|
250
|
-
let drainDone = false;
|
|
251
|
-
let writeReturned = false;
|
|
252
|
-
let settled = false;
|
|
253
|
-
const finish = () => {
|
|
254
|
-
if (settled || generation.closed || !writeReturned || !callbackDone || !drainDone)
|
|
255
|
-
return;
|
|
256
|
-
settled = true;
|
|
257
|
-
generation.writeQueue.shift();
|
|
258
|
-
generation.writing = false;
|
|
259
|
-
clearTimeout(entry.timer);
|
|
260
|
-
entry.resolve();
|
|
261
|
-
this.flushWrites(generation);
|
|
262
|
-
};
|
|
263
|
-
try {
|
|
264
|
-
const accepted = generation.transport.stdin.write(entry.frame, (error) => {
|
|
265
|
-
if (generation.closed)
|
|
266
|
-
return;
|
|
267
|
-
if (error) {
|
|
268
|
-
this.failGeneration(generation, "write_error", true);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
callbackDone = true;
|
|
272
|
-
finish();
|
|
273
|
-
});
|
|
274
|
-
writeReturned = true;
|
|
275
|
-
if (generation.closed)
|
|
276
|
-
return;
|
|
277
|
-
if (accepted) {
|
|
278
|
-
drainDone = true;
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
const onDrain = () => {
|
|
282
|
-
if (generation.drainListener === onDrain)
|
|
283
|
-
generation.drainListener = undefined;
|
|
284
|
-
drainDone = true;
|
|
285
|
-
finish();
|
|
286
|
-
};
|
|
287
|
-
generation.drainListener = onDrain;
|
|
288
|
-
generation.transport.stdin.once("drain", onDrain);
|
|
289
|
-
}
|
|
290
|
-
finish();
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
this.failGeneration(generation, "write_error", true);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
onStdout(generation, value) {
|
|
297
|
-
if (generation.closed || this.active !== generation)
|
|
298
|
-
return;
|
|
299
|
-
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
300
|
-
if (generation.stdoutBuffer.length + chunk.length > this.limits.maxStdoutBufferBytes) {
|
|
301
|
-
this.failGeneration(generation, "stdout_limit", true);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
generation.stdoutBuffer = Buffer.concat([generation.stdoutBuffer, chunk]);
|
|
305
|
-
for (;;) {
|
|
306
|
-
const newline = generation.stdoutBuffer.indexOf(0x0a);
|
|
307
|
-
if (newline < 0) {
|
|
308
|
-
if (generation.stdoutBuffer.length > this.limits.maxStdoutLineBytes) {
|
|
309
|
-
this.failGeneration(generation, "stdout_limit", true);
|
|
310
|
-
}
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
let line = generation.stdoutBuffer.subarray(0, newline);
|
|
314
|
-
generation.stdoutBuffer = generation.stdoutBuffer.subarray(newline + 1);
|
|
315
|
-
if (line.at(-1) === 0x0d)
|
|
316
|
-
line = line.subarray(0, -1);
|
|
317
|
-
if (line.length > this.limits.maxStdoutLineBytes) {
|
|
318
|
-
this.failGeneration(generation, "stdout_limit", true);
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
if (line.length === 0)
|
|
322
|
-
continue;
|
|
323
|
-
let parsed;
|
|
324
|
-
try {
|
|
325
|
-
parsed = JSON.parse(line.toString("utf8"));
|
|
326
|
-
}
|
|
327
|
-
catch {
|
|
328
|
-
this.failGeneration(generation, "malformed_json", true);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
this.onMessage(generation, parsed);
|
|
332
|
-
if (generation.closed)
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
onMessage(generation, message) {
|
|
337
|
-
if (typeof message !== "object" || message === null || Array.isArray(message)) {
|
|
338
|
-
this.recordIssue("unknown_message", generation);
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
const hasId = Object.hasOwn(message, "id");
|
|
342
|
-
const hasResult = Object.hasOwn(message, "result");
|
|
343
|
-
const hasError = Object.hasOwn(message, "error");
|
|
344
|
-
const hasMethod = Object.hasOwn(message, "method");
|
|
345
|
-
if (hasId && hasResult && !hasError && !hasMethod) {
|
|
346
|
-
const success = JsonRpcSuccessSchema.safeParse(message);
|
|
347
|
-
if (success.success) {
|
|
348
|
-
this.resolveResponse(generation, success.data.id, success.data.result);
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
else if (hasId && hasError && !hasResult && !hasMethod) {
|
|
353
|
-
const failure = JsonRpcErrorSchema.safeParse(message);
|
|
354
|
-
if (failure.success) {
|
|
355
|
-
this.rejectResponse(generation, failure.data.id);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
else if (hasMethod && !hasId && !hasResult && !hasError) {
|
|
360
|
-
const notification = JsonRpcNotificationSchema.safeParse(message);
|
|
361
|
-
if (!notification.success) {
|
|
362
|
-
this.recordIssue("unknown_message", generation);
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
const value = {
|
|
366
|
-
method: notification.data.method,
|
|
367
|
-
...(Object.hasOwn(notification.data, "params") ? { params: notification.data.params } : {}),
|
|
368
|
-
};
|
|
369
|
-
for (const listener of this.notificationListeners) {
|
|
370
|
-
try {
|
|
371
|
-
listener(value);
|
|
372
|
-
}
|
|
373
|
-
catch {
|
|
374
|
-
this.recordIssue("notification_listener", generation);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
this.recordIssue("unknown_message", generation);
|
|
380
|
-
}
|
|
381
|
-
resolveResponse(generation, id, result) {
|
|
382
|
-
const pending = generation.pending.get(id);
|
|
383
|
-
if (!pending) {
|
|
384
|
-
this.recordIssue("unknown_message", generation);
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
generation.pending.delete(id);
|
|
388
|
-
clearTimeout(pending.timer);
|
|
389
|
-
try {
|
|
390
|
-
const parsed = pending.schema.safeParse(result);
|
|
391
|
-
if (parsed.success)
|
|
392
|
-
pending.resolve(parsed.data);
|
|
393
|
-
else
|
|
394
|
-
pending.reject(unavailable());
|
|
395
|
-
}
|
|
396
|
-
catch {
|
|
397
|
-
pending.reject(unavailable());
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
rejectResponse(generation, id) {
|
|
401
|
-
const pending = generation.pending.get(id);
|
|
402
|
-
if (!pending) {
|
|
403
|
-
this.recordIssue("unknown_message", generation);
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
generation.pending.delete(id);
|
|
407
|
-
clearTimeout(pending.timer);
|
|
408
|
-
pending.reject(unavailable());
|
|
409
|
-
}
|
|
410
|
-
onStderr(generation, value) {
|
|
411
|
-
if (generation.closed || this.active !== generation)
|
|
412
|
-
return;
|
|
413
|
-
const bytes = Buffer.isBuffer(value) ? value.length : Buffer.byteLength(value);
|
|
414
|
-
const remaining = this.limits.maxStderrBytes - generation.stderrBytes;
|
|
415
|
-
generation.stderrBytes += Math.min(remaining, bytes);
|
|
416
|
-
if (bytes > remaining) {
|
|
417
|
-
generation.stderrTruncated = true;
|
|
418
|
-
this.recordIssue("stderr_limit", generation);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
failGeneration(generation, issue, kill, endStdin = false) {
|
|
422
|
-
if (generation.closed)
|
|
423
|
-
return;
|
|
424
|
-
generation.closed = true;
|
|
425
|
-
generation.writing = false;
|
|
426
|
-
if (this.active === generation)
|
|
427
|
-
this.startPromise = undefined;
|
|
428
|
-
this.recordIssue(issue, generation);
|
|
429
|
-
this.lastDiagnostics = {
|
|
430
|
-
...this.lastDiagnostics,
|
|
431
|
-
stderrBytes: generation.stderrBytes,
|
|
432
|
-
stderrTruncated: generation.stderrTruncated,
|
|
433
|
-
};
|
|
434
|
-
if (this.active === generation)
|
|
435
|
-
this.active = undefined;
|
|
436
|
-
this.removeListeners(generation);
|
|
437
|
-
const error = unavailable();
|
|
438
|
-
for (const pending of generation.pending.values()) {
|
|
439
|
-
clearTimeout(pending.timer);
|
|
440
|
-
pending.reject(error);
|
|
441
|
-
}
|
|
442
|
-
generation.pending.clear();
|
|
443
|
-
for (const write of generation.writeQueue.splice(0)) {
|
|
444
|
-
clearTimeout(write.timer);
|
|
445
|
-
write.reject(error);
|
|
446
|
-
}
|
|
447
|
-
if (endStdin) {
|
|
448
|
-
try {
|
|
449
|
-
generation.transport.stdin.end();
|
|
450
|
-
}
|
|
451
|
-
catch {
|
|
452
|
-
// The same stable unavailable error already rejects every waiter.
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
if (kill) {
|
|
456
|
-
try {
|
|
457
|
-
generation.transport.kill("SIGKILL");
|
|
458
|
-
}
|
|
459
|
-
catch {
|
|
460
|
-
// The transport is already unavailable.
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
removeListeners(generation) {
|
|
465
|
-
const { transport, listeners } = generation;
|
|
466
|
-
transport.stdout.removeListener("data", listeners.stdoutData);
|
|
467
|
-
transport.stderr.removeListener("data", listeners.stderrData);
|
|
468
|
-
transport.removeListener("exit", listeners.exit);
|
|
469
|
-
transport.removeListener("error", listeners.error);
|
|
470
|
-
transport.stdin.removeListener("error", listeners.stdinError);
|
|
471
|
-
transport.stdout.removeListener("error", listeners.stdoutError);
|
|
472
|
-
transport.stderr.removeListener("error", listeners.stderrError);
|
|
473
|
-
if (generation.drainListener) {
|
|
474
|
-
transport.stdin.removeListener("drain", generation.drainListener);
|
|
475
|
-
generation.drainListener = undefined;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
recordIssue(issue, generation) {
|
|
479
|
-
this.lastDiagnostics = {
|
|
480
|
-
lastIssue: issue,
|
|
481
|
-
stderrBytes: generation?.stderrBytes ?? this.lastDiagnostics.stderrBytes,
|
|
482
|
-
stderrTruncated: generation?.stderrTruncated ?? this.lastDiagnostics.stderrTruncated,
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export declare const CODEX_VERSION_UNAVAILABLE: "CODEX_VERSION_UNAVAILABLE";
|
|
2
|
-
export declare const CODEX_VERSION_TIMEOUT_MS = 5000;
|
|
3
|
-
export declare const CODEX_VERSION_MAX_OUTPUT_BYTES = 1024;
|
|
4
|
-
export declare const CODEX_LATEST_MAX_RESPONSE_BYTES = 16384;
|
|
5
|
-
export declare const DEFAULT_CODEX_VERSION_CACHE_TTL_MS: number;
|
|
6
|
-
export type CodexInstallProvenance = "npm" | "chatgpt" | "homebrew" | "unknown";
|
|
7
|
-
export interface BoundedVersionRunOptions {
|
|
8
|
-
readonly timeoutMs: number;
|
|
9
|
-
readonly maxOutputBytes: number;
|
|
10
|
-
}
|
|
11
|
-
export interface BoundedLatestFetchOptions {
|
|
12
|
-
readonly timeoutMs: number;
|
|
13
|
-
readonly maxResponseBytes: number;
|
|
14
|
-
}
|
|
15
|
-
export type RunCodexVersion = (args: readonly string[], options: BoundedVersionRunOptions) => Promise<{
|
|
16
|
-
readonly code: number;
|
|
17
|
-
readonly stdout: string;
|
|
18
|
-
readonly stderr: string;
|
|
19
|
-
}>;
|
|
20
|
-
export type DetectCodexProvenance = () => CodexInstallProvenance | Promise<CodexInstallProvenance>;
|
|
21
|
-
export type FetchNpmLatest = (packageName: string, options: BoundedLatestFetchOptions) => Promise<string>;
|
|
22
|
-
export interface CodexLatestServiceOptions {
|
|
23
|
-
readonly runVersion: RunCodexVersion;
|
|
24
|
-
readonly detectProvenance: DetectCodexProvenance;
|
|
25
|
-
readonly fetchNpmLatest: FetchNpmLatest;
|
|
26
|
-
readonly now?: () => number;
|
|
27
|
-
readonly cacheTtlMs?: number;
|
|
28
|
-
}
|
|
29
|
-
export interface CodexVersionInfo {
|
|
30
|
-
readonly installed: string;
|
|
31
|
-
readonly provenance: CodexInstallProvenance;
|
|
32
|
-
readonly latest?: string;
|
|
33
|
-
readonly updateAvailable?: boolean;
|
|
34
|
-
readonly updateHint?: string;
|
|
35
|
-
}
|
|
36
|
-
export declare class CodexVersionUnavailableError extends Error {
|
|
37
|
-
readonly code: "CODEX_VERSION_UNAVAILABLE";
|
|
38
|
-
constructor();
|
|
39
|
-
}
|
|
40
|
-
export declare function parseCodexVersion(stdout: string): string;
|
|
41
|
-
export declare class CodexLatestService {
|
|
42
|
-
private readonly options;
|
|
43
|
-
private readonly now;
|
|
44
|
-
private readonly cacheTtlMs;
|
|
45
|
-
private cache?;
|
|
46
|
-
private inFlight?;
|
|
47
|
-
constructor(options: CodexLatestServiceOptions);
|
|
48
|
-
getVersion(force?: boolean): Promise<CodexVersionInfo>;
|
|
49
|
-
private loadVersion;
|
|
50
|
-
}
|