remote-codex 0.1.10 → 0.11.1

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 (46) hide show
  1. package/apps/supervisor-api/dist/chunk-6M32PPHZ.js +24507 -0
  2. package/apps/supervisor-api/dist/chunk-7AA2MFXK.js +24499 -0
  3. package/apps/supervisor-api/dist/chunk-HKBFCPHH.js +24511 -0
  4. package/apps/supervisor-api/dist/index.js +12525 -28436
  5. package/apps/supervisor-api/dist/worker-index.d.ts +2 -0
  6. package/apps/supervisor-api/dist/worker-index.js +33 -0
  7. package/apps/supervisor-web/dist/assets/{highlighted-body-OFNGDK62-CyMcatlD.js → highlighted-body-OFNGDK62-p31aS0f0.js} +1 -1
  8. package/apps/supervisor-web/dist/assets/index-BiuFei_K.css +32 -0
  9. package/apps/supervisor-web/dist/assets/index-D1R9CUnx.js +2161 -0
  10. package/apps/supervisor-web/dist/assets/{xterm-DbYWMNQ0.js → xterm-D92BViLH.js} +1 -1
  11. package/apps/supervisor-web/dist/index.html +2 -2
  12. package/package.json +2 -3
  13. package/packages/agent-runtime/src/index.ts +4 -0
  14. package/packages/agent-runtime/src/management-errors.ts +11 -0
  15. package/packages/agent-runtime/src/model-pricing.ts +325 -0
  16. package/packages/agent-runtime/src/registry.ts +19 -4
  17. package/packages/agent-runtime/src/runtime-errors.ts +97 -0
  18. package/packages/agent-runtime/src/types.ts +36 -3
  19. package/packages/agent-runtime/src/unavailable-runtime.ts +169 -0
  20. package/packages/claude/src/historyItems.ts +41 -5
  21. package/packages/claude/src/runtimeAdapter.test.ts +117 -6
  22. package/packages/claude/src/runtimeAdapter.ts +421 -65
  23. package/packages/codex/src/historyItems.test.ts +137 -0
  24. package/packages/codex/src/historyItems.ts +135 -17
  25. package/packages/codex/src/hookHistory.test.ts +59 -0
  26. package/packages/codex/src/index.ts +7 -0
  27. package/packages/codex/src/local-session-store.ts +390 -0
  28. package/packages/codex/src/management/codex-management-service.ts +454 -0
  29. package/packages/codex/src/management/codexHostConfig.test.ts +88 -0
  30. package/packages/codex/src/management/codexHostConfig.ts +188 -0
  31. package/packages/codex/src/management/errors.ts +20 -0
  32. package/packages/codex/src/modelPricing.test.ts +235 -0
  33. package/packages/codex/src/modelPricing.ts +9 -0
  34. package/packages/codex/src/runtime-errors.test.ts +72 -0
  35. package/packages/codex/src/runtime-errors.ts +37 -0
  36. package/packages/codex/src/runtimeAdapter.ts +15 -0
  37. package/packages/codex/src/thread-title.ts +1 -0
  38. package/packages/opencode/src/historyItems.test.ts +504 -0
  39. package/packages/opencode/src/historyItems.ts +896 -0
  40. package/packages/opencode/src/index.ts +2 -0
  41. package/packages/opencode/src/runtimeAdapter.test.ts +1444 -0
  42. package/packages/opencode/src/runtimeAdapter.ts +1473 -0
  43. package/packages/shared/src/agent-providers.ts +56 -0
  44. package/packages/shared/src/index.ts +240 -35
  45. package/apps/supervisor-web/dist/assets/index-BlAhoIuq.js +0 -379
  46. package/apps/supervisor-web/dist/assets/index-DI0NRNgr.css +0 -32
@@ -0,0 +1,390 @@
1
+ import type { Dirent } from 'node:fs';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ import Database from 'better-sqlite3';
6
+
7
+ import {
8
+ ThreadHistoryItemDto,
9
+ ThreadSourceDto,
10
+ ThreadTurnDto,
11
+ truncateAutoThreadTitle,
12
+ } from '../../shared/src/index';
13
+
14
+ interface LocalStateThreadRow {
15
+ id: string;
16
+ cwd: string;
17
+ title: string | null;
18
+ rolloutPath: string | null;
19
+ model: string | null;
20
+ }
21
+
22
+ interface ParsedTranscript {
23
+ cwd: string | null;
24
+ title: string | null;
25
+ turns: ThreadTurnDto[];
26
+ }
27
+
28
+ export interface LocalCodexSessionRecord {
29
+ sessionId: string;
30
+ cwd: string;
31
+ title: string | null;
32
+ model: string | null;
33
+ rolloutPath: string | null;
34
+ turns: ThreadTurnDto[];
35
+ }
36
+
37
+ export interface LocalCodexImportSession {
38
+ provider: 'codex';
39
+ source: Extract<ThreadSourceDto, 'local_codex_import'>;
40
+ sessionId: string;
41
+ cwd: string;
42
+ title: string;
43
+ model: string | null;
44
+ summaryText: string | null;
45
+ fastMode: boolean;
46
+ }
47
+
48
+ interface MutableTurn {
49
+ id: string;
50
+ startedAt: string | null;
51
+ status: ThreadTurnDto['status'];
52
+ error: string | null;
53
+ items: ThreadHistoryItemDto[];
54
+ }
55
+
56
+ function basenameFromPath(absPath: string) {
57
+ const normalized = absPath.replace(/[\\/]+$/, '');
58
+ return normalized.split(/[\\/]/).at(-1) ?? normalized;
59
+ }
60
+
61
+ function summarizeTitleFromTurns(turns: ThreadTurnDto[]) {
62
+ const firstUserMessage = turns
63
+ .flatMap((turn) => turn.items)
64
+ .find((item) => item.kind === 'userMessage' && item.text.trim());
65
+
66
+ if (!firstUserMessage) {
67
+ return null;
68
+ }
69
+
70
+ return truncateAutoThreadTitle(firstUserMessage.text);
71
+ }
72
+
73
+ function createHistoryItemId(turnId: string, prefix: string, index: number) {
74
+ return `${turnId}-${prefix}-${index}`;
75
+ }
76
+
77
+ function finalizeTurn(turn: MutableTurn | null, turns: ThreadTurnDto[]) {
78
+ if (!turn || turn.items.length === 0) {
79
+ return;
80
+ }
81
+
82
+ turns.push({
83
+ id: turn.id,
84
+ startedAt: turn.startedAt,
85
+ status: turn.status,
86
+ error: turn.error,
87
+ items: turn.items,
88
+ });
89
+ }
90
+
91
+ function parseTranscript(contents: string): ParsedTranscript {
92
+ const turns: ThreadTurnDto[] = [];
93
+ let cwd: string | null = null;
94
+ let currentTurn: MutableTurn | null = null;
95
+ let fallbackTurnCount = 0;
96
+ let agentItemCount = 0;
97
+ let userItemCount = 0;
98
+
99
+ const ensureCurrentTurn = (timestamp?: string) => {
100
+ if (currentTurn) {
101
+ return currentTurn;
102
+ }
103
+
104
+ fallbackTurnCount += 1;
105
+ currentTurn = {
106
+ id: `local-turn-${fallbackTurnCount}`,
107
+ startedAt: timestamp ?? null,
108
+ status: 'inProgress',
109
+ error: null,
110
+ items: [],
111
+ };
112
+ agentItemCount = 0;
113
+ userItemCount = 0;
114
+ return currentTurn;
115
+ };
116
+
117
+ for (const line of contents.split('\n')) {
118
+ if (!line.trim()) {
119
+ continue;
120
+ }
121
+
122
+ let entry: any;
123
+ try {
124
+ entry = JSON.parse(line);
125
+ } catch {
126
+ continue;
127
+ }
128
+
129
+ if (entry.type === 'session_meta') {
130
+ const payload = entry.payload ?? {};
131
+ if (typeof payload.cwd === 'string' && payload.cwd.trim()) {
132
+ cwd = payload.cwd;
133
+ }
134
+ continue;
135
+ }
136
+
137
+ if (entry.type !== 'event_msg') {
138
+ continue;
139
+ }
140
+
141
+ const payload = entry.payload ?? {};
142
+ const payloadType = payload.type;
143
+
144
+ if (payloadType === 'task_started') {
145
+ finalizeTurn(currentTurn, turns);
146
+ currentTurn = {
147
+ id:
148
+ typeof payload.turn_id === 'string' && payload.turn_id.trim()
149
+ ? payload.turn_id
150
+ : `local-turn-${fallbackTurnCount + 1}`,
151
+ startedAt: entry.timestamp ?? null,
152
+ status: 'inProgress',
153
+ error: null,
154
+ items: [],
155
+ };
156
+ agentItemCount = 0;
157
+ userItemCount = 0;
158
+ continue;
159
+ }
160
+
161
+ if (payloadType === 'user_message' && typeof payload.message === 'string') {
162
+ const turn = ensureCurrentTurn(entry.timestamp);
163
+ userItemCount += 1;
164
+ turn.items.push({
165
+ id: createHistoryItemId(turn.id, 'user', userItemCount),
166
+ kind: 'userMessage',
167
+ text: payload.message,
168
+ });
169
+ continue;
170
+ }
171
+
172
+ if (payloadType === 'agent_message' && typeof payload.message === 'string') {
173
+ const turn = ensureCurrentTurn(entry.timestamp);
174
+ agentItemCount += 1;
175
+ turn.items.push({
176
+ id: createHistoryItemId(turn.id, 'agent', agentItemCount),
177
+ kind: 'agentMessage',
178
+ text: payload.message,
179
+ status: typeof payload.phase === 'string' ? payload.phase : null,
180
+ });
181
+ continue;
182
+ }
183
+
184
+ if (payloadType === 'task_complete') {
185
+ const turn = ensureCurrentTurn(entry.timestamp);
186
+ turn.status = turn.error ? 'failed' : 'completed';
187
+ finalizeTurn(turn, turns);
188
+ currentTurn = null;
189
+ continue;
190
+ }
191
+
192
+ if (payloadType === 'error') {
193
+ const turn = ensureCurrentTurn(entry.timestamp);
194
+ turn.status = 'failed';
195
+ turn.error =
196
+ typeof payload.message === 'string'
197
+ ? payload.message
198
+ : 'Local Codex session failed.';
199
+ }
200
+ }
201
+
202
+ finalizeTurn(currentTurn, turns);
203
+
204
+ return {
205
+ cwd,
206
+ title: summarizeTitleFromTurns(turns),
207
+ turns,
208
+ };
209
+ }
210
+
211
+ async function fileExists(filePath: string) {
212
+ try {
213
+ await fs.access(filePath);
214
+ return true;
215
+ } catch {
216
+ return false;
217
+ }
218
+ }
219
+
220
+ export class LocalCodexSessionStore {
221
+ constructor(private readonly codexHome: string) {}
222
+
223
+ async findSession(
224
+ sessionId: string,
225
+ ): Promise<LocalCodexSessionRecord | null> {
226
+ const stateRecord = await this.findSessionInStateDatabases(sessionId);
227
+ const transcriptPath = await this.resolveTranscriptPath(
228
+ stateRecord?.rolloutPath ?? null,
229
+ sessionId,
230
+ );
231
+ const transcript = transcriptPath
232
+ ? parseTranscript(await fs.readFile(transcriptPath, 'utf8'))
233
+ : null;
234
+ const cwd = stateRecord?.cwd ?? transcript?.cwd ?? null;
235
+
236
+ if (!cwd) {
237
+ return null;
238
+ }
239
+
240
+ return {
241
+ sessionId,
242
+ cwd,
243
+ title:
244
+ stateRecord?.title?.trim() ||
245
+ transcript?.title?.trim() ||
246
+ basenameFromPath(cwd),
247
+ model: stateRecord?.model ?? null,
248
+ rolloutPath: transcriptPath,
249
+ turns: transcript?.turns ?? [],
250
+ };
251
+ }
252
+
253
+ async findImportSession(
254
+ sessionId: string,
255
+ input: { fastMode: boolean },
256
+ ): Promise<LocalCodexImportSession | null> {
257
+ const localSession = await this.findSession(sessionId);
258
+ if (!localSession) {
259
+ return null;
260
+ }
261
+
262
+ return {
263
+ provider: 'codex',
264
+ source: 'local_codex_import',
265
+ sessionId: localSession.sessionId,
266
+ cwd: localSession.cwd,
267
+ title: truncateAutoThreadTitle(
268
+ localSession.title?.trim() || 'Untitled imported session',
269
+ ),
270
+ model: localSession.model,
271
+ summaryText:
272
+ localSession.turns
273
+ .flatMap((turn) => turn.items)
274
+ .find((item) => item.kind === 'userMessage')
275
+ ?.text ?? null,
276
+ fastMode: input.fastMode,
277
+ };
278
+ }
279
+
280
+ private async findSessionInStateDatabases(
281
+ sessionId: string,
282
+ ): Promise<LocalStateThreadRow | null> {
283
+ let entries: string[];
284
+ try {
285
+ entries = await fs.readdir(this.codexHome);
286
+ } catch {
287
+ return null;
288
+ }
289
+
290
+ const stateFiles = await Promise.all(
291
+ entries
292
+ .filter((entry) => /^state_\d+\.sqlite$/i.test(entry))
293
+ .map(async (entry) => {
294
+ const absPath = path.join(this.codexHome, entry);
295
+ const stats = await fs.stat(absPath);
296
+ return {
297
+ absPath,
298
+ mtimeMs: stats.mtimeMs,
299
+ };
300
+ }),
301
+ );
302
+
303
+ stateFiles.sort((left, right) => right.mtimeMs - left.mtimeMs);
304
+
305
+ for (const stateFile of stateFiles) {
306
+ const sqlite = new Database(stateFile.absPath, {
307
+ readonly: true,
308
+ fileMustExist: true,
309
+ });
310
+
311
+ try {
312
+ const row = sqlite
313
+ .prepare(
314
+ `
315
+ SELECT
316
+ id,
317
+ cwd,
318
+ title,
319
+ rollout_path AS rolloutPath,
320
+ model
321
+ FROM threads
322
+ WHERE id = ?
323
+ LIMIT 1
324
+ `,
325
+ )
326
+ .get(sessionId) as LocalStateThreadRow | undefined;
327
+
328
+ if (row) {
329
+ return row;
330
+ }
331
+ } catch {
332
+ // Ignore incompatible sqlite files and continue probing.
333
+ } finally {
334
+ sqlite.close();
335
+ }
336
+ }
337
+
338
+ return null;
339
+ }
340
+
341
+ private async resolveTranscriptPath(
342
+ rolloutPath: string | null,
343
+ sessionId: string,
344
+ ): Promise<string | null> {
345
+ if (rolloutPath?.trim()) {
346
+ const absolutePath = path.isAbsolute(rolloutPath)
347
+ ? rolloutPath
348
+ : path.resolve(this.codexHome, rolloutPath);
349
+
350
+ if (await fileExists(absolutePath)) {
351
+ return absolutePath;
352
+ }
353
+ }
354
+
355
+ return this.findTranscriptFile(path.join(this.codexHome, 'sessions'), sessionId);
356
+ }
357
+
358
+ private async findTranscriptFile(
359
+ directory: string,
360
+ sessionId: string,
361
+ ): Promise<string | null> {
362
+ let entries: Dirent[];
363
+ try {
364
+ entries = await fs.readdir(directory, { withFileTypes: true });
365
+ } catch {
366
+ return null;
367
+ }
368
+
369
+ for (const entry of entries) {
370
+ const absPath = path.join(directory, entry.name);
371
+ if (entry.isDirectory()) {
372
+ const nested = await this.findTranscriptFile(absPath, sessionId);
373
+ if (nested) {
374
+ return nested;
375
+ }
376
+ continue;
377
+ }
378
+
379
+ if (
380
+ entry.isFile() &&
381
+ entry.name.endsWith('.jsonl') &&
382
+ entry.name.includes(sessionId)
383
+ ) {
384
+ return absPath;
385
+ }
386
+ }
387
+
388
+ return null;
389
+ }
390
+ }