@supen-ai/cli 1.3.2 → 1.3.4
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/README.md +1 -1
- package/daemon/dist/agent-sdk/driver-output-ui.d.ts +1 -1
- package/daemon/dist/agent-sdk/driver-output-ui.d.ts.map +1 -1
- package/daemon/dist/agent-sdk/driver-output-ui.js +10 -275
- package/daemon/dist/agent-sdk/driver-output-ui.js.map +1 -1
- package/daemon/dist/channels/http-routes.d.ts.map +1 -1
- package/daemon/dist/channels/http-routes.js +394 -26
- package/daemon/dist/channels/http-routes.js.map +1 -1
- package/daemon/dist/core/cortex.d.ts.map +1 -1
- package/daemon/dist/core/cortex.js +0 -17
- package/daemon/dist/core/cortex.js.map +1 -1
- package/daemon/dist/core/thread-runtime-state.d.ts.map +1 -1
- package/daemon/dist/core/thread-runtime-state.js +1 -37
- package/daemon/dist/core/thread-runtime-state.js.map +1 -1
- package/daemon/dist/http/routes/rpc.d.ts.map +1 -1
- package/daemon/dist/http/routes/rpc.js +20 -3
- package/daemon/dist/http/routes/rpc.js.map +1 -1
- package/daemon/dist/http/routes/sessions.d.ts.map +1 -1
- package/daemon/dist/http/routes/sessions.js +2 -225
- package/daemon/dist/http/routes/sessions.js.map +1 -1
- package/daemon/dist/http/routes/system.d.ts.map +1 -1
- package/daemon/dist/http/routes/system.js +101 -390
- package/daemon/dist/http/routes/system.js.map +1 -1
- package/daemon/dist/http/websocket.js +1 -1
- package/daemon/dist/http/websocket.js.map +1 -1
- package/daemon/dist/index.d.ts.map +1 -1
- package/daemon/dist/index.js +4 -2
- package/daemon/dist/index.js.map +1 -1
- package/daemon/package.json +1 -1
- package/dist/computer.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,10 +31,6 @@ const MIRRORED_THREAD_LIMIT = 80;
|
|
|
31
31
|
const MIRRORED_THREAD_HISTORY_LIMIT = 100;
|
|
32
32
|
const MIRRORED_THREAD_HISTORY_MAX_BYTES = 64 * 1024 * 1024;
|
|
33
33
|
const MIRRORED_THREAD_TEXT_LIMIT = 48_000;
|
|
34
|
-
const MIRRORED_THREAD_TOOL_STRING_LIMIT = 24_000;
|
|
35
|
-
const MIRRORED_THREAD_OBJECT_DEPTH_LIMIT = 4;
|
|
36
|
-
const MIRRORED_THREAD_OBJECT_KEY_LIMIT = 80;
|
|
37
|
-
const MIRRORED_THREAD_OBJECT_ARRAY_LIMIT = 80;
|
|
38
34
|
const MIRRORED_THREAD_INLINE_DATA_URL_LIMIT = 120_000;
|
|
39
35
|
const MIRRORED_THREAD_STREAM_REPLAY_LIMIT = 500;
|
|
40
36
|
const MIRRORED_THREAD_STREAM_REPLAY_MAX_BYTES = 8 * 1024 * 1024;
|
|
@@ -44,7 +40,7 @@ const require = createRequire(import.meta.url);
|
|
|
44
40
|
const HOST_DAEMON_INSTALL_DIR = path.join(SUPEN_HOME, 'daemon');
|
|
45
41
|
const HOST_DAEMON_CLI_PACKAGE_ROOT = path.join(HOST_DAEMON_INSTALL_DIR, 'node_modules', '@supen-ai', 'cli');
|
|
46
42
|
const HOST_DAEMON_BIN_PATH = path.join(HOST_DAEMON_CLI_PACKAGE_ROOT, 'daemon', 'scripts', 'supen-daemon.js');
|
|
47
|
-
const HOST_DAEMON_PACKAGE_SPEC = '@supen-ai/cli';
|
|
43
|
+
const HOST_DAEMON_PACKAGE_SPEC = '@supen-ai/cli@latest';
|
|
48
44
|
function readSqliteRows(dbPath, query) {
|
|
49
45
|
try {
|
|
50
46
|
const { DatabaseSync } = require('node:sqlite');
|
|
@@ -953,34 +949,6 @@ function truncateMirroredHistoryString(value, limit, label) {
|
|
|
953
949
|
const omitted = value.length - limit;
|
|
954
950
|
return `${value.slice(0, limit)}\n\n[${label} truncated: ${omitted.toLocaleString()} more characters omitted]`;
|
|
955
951
|
}
|
|
956
|
-
function sanitizeMirroredHistoryPayload(value, depth = 0) {
|
|
957
|
-
if (typeof value === 'string') {
|
|
958
|
-
return truncateMirroredHistoryString(value, MIRRORED_THREAD_TOOL_STRING_LIMIT, 'Tool output');
|
|
959
|
-
}
|
|
960
|
-
if (value === null || typeof value !== 'object')
|
|
961
|
-
return value;
|
|
962
|
-
if (depth >= MIRRORED_THREAD_OBJECT_DEPTH_LIMIT) {
|
|
963
|
-
return Array.isArray(value) ? '[Nested array omitted]' : '[Nested object omitted]';
|
|
964
|
-
}
|
|
965
|
-
if (Array.isArray(value)) {
|
|
966
|
-
const visibleItems = value
|
|
967
|
-
.slice(0, MIRRORED_THREAD_OBJECT_ARRAY_LIMIT)
|
|
968
|
-
.map((item) => sanitizeMirroredHistoryPayload(item, depth + 1));
|
|
969
|
-
if (value.length > MIRRORED_THREAD_OBJECT_ARRAY_LIMIT) {
|
|
970
|
-
visibleItems.push(`[${value.length - MIRRORED_THREAD_OBJECT_ARRAY_LIMIT} more items omitted]`);
|
|
971
|
-
}
|
|
972
|
-
return visibleItems;
|
|
973
|
-
}
|
|
974
|
-
const result = {};
|
|
975
|
-
const entries = Object.entries(value);
|
|
976
|
-
for (const [key, entryValue] of entries.slice(0, MIRRORED_THREAD_OBJECT_KEY_LIMIT)) {
|
|
977
|
-
result[key] = sanitizeMirroredHistoryPayload(entryValue, depth + 1);
|
|
978
|
-
}
|
|
979
|
-
if (entries.length > MIRRORED_THREAD_OBJECT_KEY_LIMIT) {
|
|
980
|
-
result.__truncated_keys = `${entries.length - MIRRORED_THREAD_OBJECT_KEY_LIMIT} more keys omitted`;
|
|
981
|
-
}
|
|
982
|
-
return result;
|
|
983
|
-
}
|
|
984
952
|
function safeMirroredHistoryAttachmentUrl(url) {
|
|
985
953
|
const trimmed = url.trim();
|
|
986
954
|
if (trimmed.startsWith('data:') &&
|
|
@@ -1088,111 +1056,6 @@ function isMirroredGoalContextMessage(text) {
|
|
|
1088
1056
|
const trimmed = text.trim();
|
|
1089
1057
|
return trimmed.startsWith('<goal_context>') && trimmed.endsWith('</goal_context>');
|
|
1090
1058
|
}
|
|
1091
|
-
function parseJsonObject(value) {
|
|
1092
|
-
if (typeof value !== 'string')
|
|
1093
|
-
return value;
|
|
1094
|
-
try {
|
|
1095
|
-
return JSON.parse(value);
|
|
1096
|
-
}
|
|
1097
|
-
catch {
|
|
1098
|
-
return value;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
function readableOutputFromMcpResult(result) {
|
|
1102
|
-
if (!result || typeof result !== 'object')
|
|
1103
|
-
return result;
|
|
1104
|
-
const ok = result.Ok;
|
|
1105
|
-
const error = result.Err;
|
|
1106
|
-
const payload = ok && typeof ok === 'object' ? ok : error;
|
|
1107
|
-
if (!payload || typeof payload !== 'object')
|
|
1108
|
-
return result;
|
|
1109
|
-
const content = payload.content;
|
|
1110
|
-
if (!Array.isArray(content))
|
|
1111
|
-
return payload;
|
|
1112
|
-
const text = content
|
|
1113
|
-
.map((entry) => {
|
|
1114
|
-
if (typeof entry === 'string')
|
|
1115
|
-
return entry;
|
|
1116
|
-
if (entry && typeof entry === 'object' && typeof entry.text === 'string') {
|
|
1117
|
-
return entry.text;
|
|
1118
|
-
}
|
|
1119
|
-
return '';
|
|
1120
|
-
})
|
|
1121
|
-
.filter(Boolean)
|
|
1122
|
-
.join('\n');
|
|
1123
|
-
return text || payload;
|
|
1124
|
-
}
|
|
1125
|
-
function fileNameFromPath(value) {
|
|
1126
|
-
const trimmed = value.trim();
|
|
1127
|
-
if (!trimmed)
|
|
1128
|
-
return '';
|
|
1129
|
-
return path.basename(trimmed);
|
|
1130
|
-
}
|
|
1131
|
-
function displayPatchPath(filePath, workspacePath) {
|
|
1132
|
-
const trimmed = filePath.trim();
|
|
1133
|
-
if (!trimmed || !workspacePath || !path.isAbsolute(trimmed))
|
|
1134
|
-
return trimmed;
|
|
1135
|
-
const relativePath = path.relative(workspacePath, trimmed);
|
|
1136
|
-
if (!relativePath || relativePath.startsWith('..') || path.isAbsolute(relativePath))
|
|
1137
|
-
return trimmed;
|
|
1138
|
-
return relativePath;
|
|
1139
|
-
}
|
|
1140
|
-
function diffStatsFromText(value) {
|
|
1141
|
-
let additions = 0;
|
|
1142
|
-
let deletions = 0;
|
|
1143
|
-
for (const line of value.split(/\r?\n/)) {
|
|
1144
|
-
if (line.startsWith('+++') || line.startsWith('---'))
|
|
1145
|
-
continue;
|
|
1146
|
-
if (line.startsWith('+'))
|
|
1147
|
-
additions += 1;
|
|
1148
|
-
if (line.startsWith('-'))
|
|
1149
|
-
deletions += 1;
|
|
1150
|
-
}
|
|
1151
|
-
return { additions, deletions };
|
|
1152
|
-
}
|
|
1153
|
-
function extractPatchFiles(input) {
|
|
1154
|
-
if (typeof input !== 'string')
|
|
1155
|
-
return [];
|
|
1156
|
-
const files = [];
|
|
1157
|
-
const seen = new Set();
|
|
1158
|
-
for (const line of input.split(/\r?\n/)) {
|
|
1159
|
-
const match = line.match(/^\*\*\* (?:Update|Add|Delete) File:\s+(.+?)\s*$/);
|
|
1160
|
-
if (!match)
|
|
1161
|
-
continue;
|
|
1162
|
-
const filePath = match[1]?.trim();
|
|
1163
|
-
if (!filePath || seen.has(filePath))
|
|
1164
|
-
continue;
|
|
1165
|
-
seen.add(filePath);
|
|
1166
|
-
files.push({ path: filePath, name: fileNameFromPath(filePath) || filePath });
|
|
1167
|
-
}
|
|
1168
|
-
return files;
|
|
1169
|
-
}
|
|
1170
|
-
function extractPatchEndFiles(changes, workspacePath) {
|
|
1171
|
-
if (!changes || typeof changes !== 'object')
|
|
1172
|
-
return [];
|
|
1173
|
-
return Object.entries(changes).map(([filePath, change]) => {
|
|
1174
|
-
const diff = typeof change?.unified_diff === 'string' ? change.unified_diff : '';
|
|
1175
|
-
const displayPath = displayPatchPath(filePath, workspacePath);
|
|
1176
|
-
return {
|
|
1177
|
-
path: displayPath,
|
|
1178
|
-
name: fileNameFromPath(displayPath) || displayPath,
|
|
1179
|
-
...(diff ? { diff } : {}),
|
|
1180
|
-
...diffStatsFromText(diff),
|
|
1181
|
-
};
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
function codexReplayEvent(eventId, timestamp, taskId, raw) {
|
|
1185
|
-
return {
|
|
1186
|
-
id: eventId,
|
|
1187
|
-
timestamp,
|
|
1188
|
-
task_id: taskId,
|
|
1189
|
-
chunk: {
|
|
1190
|
-
type: 'data-codex-event',
|
|
1191
|
-
id: eventId,
|
|
1192
|
-
data: { raw },
|
|
1193
|
-
},
|
|
1194
|
-
};
|
|
1195
|
-
}
|
|
1196
1059
|
function mirroredThreadWorkspacePath(sessionPath, stateEntry) {
|
|
1197
1060
|
const stateCwd = typeof stateEntry?.cwd === 'string' ? stateEntry.cwd : null;
|
|
1198
1061
|
const sessionWorkspace = readThreadWorkspacePath(sessionPath);
|
|
@@ -1216,10 +1079,8 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1216
1079
|
const messages = [];
|
|
1217
1080
|
const events = [];
|
|
1218
1081
|
let messageIndex = 0;
|
|
1219
|
-
let eventIndex = 0;
|
|
1220
1082
|
let latestTimestamp = indexEntry?.updated_at || '';
|
|
1221
1083
|
let recentGoalContext = null;
|
|
1222
|
-
const toolCalls = new Map();
|
|
1223
1084
|
const mirroredUserMessageKey = (text) => sanitizeMirroredUserText(text)
|
|
1224
1085
|
.replace(/\s+/g, ' ')
|
|
1225
1086
|
.trim();
|
|
@@ -1309,39 +1170,6 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1309
1170
|
const messageId = typeof payload.id === 'string' ? payload.id : `${threadId}-web-user-${event.sequence}`;
|
|
1310
1171
|
pushUserMessage(text, timestamp, messageId, attachments);
|
|
1311
1172
|
};
|
|
1312
|
-
const pushEvent = (timestamp, chunk) => {
|
|
1313
|
-
eventIndex += 1;
|
|
1314
|
-
events.push({
|
|
1315
|
-
id: `${threadId}-event-${eventIndex}`,
|
|
1316
|
-
timestamp,
|
|
1317
|
-
task_id: threadId,
|
|
1318
|
-
chunk,
|
|
1319
|
-
});
|
|
1320
|
-
};
|
|
1321
|
-
const pushToolInput = (timestamp, toolCallId, toolName, input) => {
|
|
1322
|
-
const sanitizedInput = sanitizeMirroredHistoryPayload(input);
|
|
1323
|
-
toolCalls.set(toolCallId, { toolName, input: sanitizedInput });
|
|
1324
|
-
pushEvent(timestamp, {
|
|
1325
|
-
type: 'tool-input-available',
|
|
1326
|
-
toolCallId,
|
|
1327
|
-
toolName,
|
|
1328
|
-
input: sanitizedInput,
|
|
1329
|
-
});
|
|
1330
|
-
};
|
|
1331
|
-
const pushToolOutput = (timestamp, toolCallId, output, fallbackToolName, fallbackInput, failed = false) => {
|
|
1332
|
-
const toolCall = toolCalls.get(toolCallId);
|
|
1333
|
-
const toolName = toolCall?.toolName || fallbackToolName;
|
|
1334
|
-
const input = toolCall?.input ?? (fallbackInput === undefined ? undefined : sanitizeMirroredHistoryPayload(fallbackInput));
|
|
1335
|
-
pushEvent(timestamp, {
|
|
1336
|
-
type: failed ? 'tool-output-error' : 'tool-output-available',
|
|
1337
|
-
toolCallId,
|
|
1338
|
-
...(toolName ? { toolName } : {}),
|
|
1339
|
-
...(input !== undefined ? { input } : {}),
|
|
1340
|
-
...(failed
|
|
1341
|
-
? { errorText: typeof output === 'string' ? output : 'Tool failed' }
|
|
1342
|
-
: { output: sanitizeMirroredHistoryPayload(parseJsonObject(output)) }),
|
|
1343
|
-
});
|
|
1344
|
-
};
|
|
1345
1173
|
for (const line of lines) {
|
|
1346
1174
|
const parsed = parseJsonLine(line);
|
|
1347
1175
|
if (typeof parsed?.timestamp === 'string')
|
|
@@ -1356,146 +1184,11 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1356
1184
|
if (payload.type === 'user_message') {
|
|
1357
1185
|
const text = typeof payload.message === 'string' ? payload.message.trim() : '';
|
|
1358
1186
|
pushUserMessage(text, timestamp, undefined, imageAttachmentsFromUnknown(payload.images));
|
|
1359
|
-
continue;
|
|
1360
|
-
}
|
|
1361
|
-
if (payload.type === 'turn_aborted') {
|
|
1362
|
-
if (eventLogThreadId !== threadId) {
|
|
1363
|
-
events.push(codexReplayEvent(`${threadId}-turn-aborted-${eventIndex + 1}`, timestamp, threadId, {
|
|
1364
|
-
method: 'turn/completed',
|
|
1365
|
-
params: {
|
|
1366
|
-
turn: {
|
|
1367
|
-
id: payload.turn_id || payload.turnId || 'turn',
|
|
1368
|
-
status: 'interrupted',
|
|
1369
|
-
},
|
|
1370
|
-
},
|
|
1371
|
-
}));
|
|
1372
|
-
eventIndex += 1;
|
|
1373
|
-
}
|
|
1374
|
-
continue;
|
|
1375
|
-
}
|
|
1376
|
-
if (payload.type === 'context_compacted') {
|
|
1377
|
-
events.push(codexReplayEvent(`${threadId}-context-compacted-${eventIndex + 1}`, timestamp, threadId, {
|
|
1378
|
-
method: 'contextCompaction/completed',
|
|
1379
|
-
params: {
|
|
1380
|
-
threadId,
|
|
1381
|
-
messageCount: payload.message_count || payload.messageCount,
|
|
1382
|
-
tokensSaved: payload.tokens_saved || payload.tokensSaved,
|
|
1383
|
-
},
|
|
1384
|
-
}));
|
|
1385
|
-
eventIndex += 1;
|
|
1386
|
-
continue;
|
|
1387
|
-
}
|
|
1388
|
-
if (payload.type === 'web_search_end') {
|
|
1389
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-web-search-${eventIndex + 1}`;
|
|
1390
|
-
const input = {
|
|
1391
|
-
query: payload.query,
|
|
1392
|
-
action: payload.action,
|
|
1393
|
-
};
|
|
1394
|
-
pushToolInput(timestamp, toolCallId, 'web_search', input);
|
|
1395
|
-
pushToolOutput(timestamp, toolCallId, payload.action || payload.query || 'completed');
|
|
1396
|
-
continue;
|
|
1397
|
-
}
|
|
1398
|
-
if (payload.type === 'mcp_tool_call_end') {
|
|
1399
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-mcp-tool-${eventIndex + 1}`;
|
|
1400
|
-
const invocation = payload.invocation && typeof payload.invocation === 'object'
|
|
1401
|
-
? payload.invocation
|
|
1402
|
-
: {};
|
|
1403
|
-
const server = typeof invocation.server === 'string' ? invocation.server : '';
|
|
1404
|
-
const tool = typeof invocation.tool === 'string' ? invocation.tool : '';
|
|
1405
|
-
const toolName = [server, tool].filter(Boolean).join('.') || 'mcpToolCall';
|
|
1406
|
-
const input = invocation.arguments;
|
|
1407
|
-
const result = payload.result && typeof payload.result === 'object'
|
|
1408
|
-
? payload.result
|
|
1409
|
-
: payload.result;
|
|
1410
|
-
const failed = Boolean(result && typeof result === 'object' && 'Err' in result);
|
|
1411
|
-
pushToolInput(timestamp, toolCallId, toolName, input);
|
|
1412
|
-
pushToolOutput(timestamp, toolCallId, readableOutputFromMcpResult(result), toolName, input, failed);
|
|
1413
|
-
continue;
|
|
1414
|
-
}
|
|
1415
|
-
if (payload.type === 'patch_apply_end') {
|
|
1416
|
-
const files = extractPatchEndFiles(payload.changes, workspacePath);
|
|
1417
|
-
const additions = files.reduce((sum, file) => sum + file.additions, 0);
|
|
1418
|
-
const deletions = files.reduce((sum, file) => sum + file.deletions, 0);
|
|
1419
|
-
events.push(codexReplayEvent(`${threadId}-patch-end-${eventIndex + 1}`, timestamp, threadId, {
|
|
1420
|
-
method: 'codex/patchApply/completed',
|
|
1421
|
-
params: {
|
|
1422
|
-
callId: payload.call_id,
|
|
1423
|
-
success: payload.success !== false,
|
|
1424
|
-
files,
|
|
1425
|
-
fileCount: files.length,
|
|
1426
|
-
additions,
|
|
1427
|
-
deletions,
|
|
1428
|
-
status: payload.status,
|
|
1429
|
-
},
|
|
1430
|
-
}));
|
|
1431
|
-
eventIndex += 1;
|
|
1432
|
-
continue;
|
|
1433
1187
|
}
|
|
1434
|
-
}
|
|
1435
|
-
if (parsed?.type === 'compacted') {
|
|
1436
|
-
events.push(codexReplayEvent(`${threadId}-context-compacted-${eventIndex + 1}`, timestamp, threadId, {
|
|
1437
|
-
method: 'contextCompaction/completed',
|
|
1438
|
-
params: { threadId },
|
|
1439
|
-
}));
|
|
1440
|
-
eventIndex += 1;
|
|
1441
1188
|
continue;
|
|
1442
1189
|
}
|
|
1443
1190
|
if (parsed?.type !== 'response_item')
|
|
1444
1191
|
continue;
|
|
1445
|
-
if (payload.type === 'function_call') {
|
|
1446
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-tool-${eventIndex + 1}`;
|
|
1447
|
-
const toolName = typeof payload.name === 'string' ? payload.name : 'tool';
|
|
1448
|
-
const input = sanitizeMirroredHistoryPayload(parseJsonObject(payload.arguments));
|
|
1449
|
-
pushToolInput(timestamp, toolCallId, toolName, input);
|
|
1450
|
-
continue;
|
|
1451
|
-
}
|
|
1452
|
-
if (payload.type === 'function_call_output') {
|
|
1453
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-tool-${eventIndex + 1}`;
|
|
1454
|
-
pushToolOutput(timestamp, toolCallId, payload.output);
|
|
1455
|
-
continue;
|
|
1456
|
-
}
|
|
1457
|
-
if (payload.type === 'tool_search_call') {
|
|
1458
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-tool-search-${eventIndex + 1}`;
|
|
1459
|
-
pushToolInput(timestamp, toolCallId, 'tool_search', payload.arguments);
|
|
1460
|
-
continue;
|
|
1461
|
-
}
|
|
1462
|
-
if (payload.type === 'tool_search_output') {
|
|
1463
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-tool-search-${eventIndex + 1}`;
|
|
1464
|
-
pushToolOutput(timestamp, toolCallId, {
|
|
1465
|
-
status: payload.status,
|
|
1466
|
-
tools: payload.tools,
|
|
1467
|
-
}, 'tool_search');
|
|
1468
|
-
continue;
|
|
1469
|
-
}
|
|
1470
|
-
if (payload.type === 'custom_tool_call') {
|
|
1471
|
-
const toolName = typeof payload.name === 'string' ? payload.name : 'custom_tool';
|
|
1472
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-tool-${eventIndex + 1}`;
|
|
1473
|
-
if (toolName === 'apply_patch') {
|
|
1474
|
-
const input = typeof payload.input === 'string' ? payload.input : '';
|
|
1475
|
-
const files = extractPatchFiles(input);
|
|
1476
|
-
events.push(codexReplayEvent(`${threadId}-patch-start-${eventIndex + 1}`, timestamp, threadId, {
|
|
1477
|
-
method: 'codex/patchApply/started',
|
|
1478
|
-
params: {
|
|
1479
|
-
callId: payload.call_id,
|
|
1480
|
-
files,
|
|
1481
|
-
fileCount: files.length,
|
|
1482
|
-
...diffStatsFromText(input),
|
|
1483
|
-
},
|
|
1484
|
-
}));
|
|
1485
|
-
eventIndex += 1;
|
|
1486
|
-
}
|
|
1487
|
-
else {
|
|
1488
|
-
pushToolInput(timestamp, toolCallId, toolName, parseJsonObject(payload.input));
|
|
1489
|
-
}
|
|
1490
|
-
continue;
|
|
1491
|
-
}
|
|
1492
|
-
if (payload.type === 'custom_tool_call_output') {
|
|
1493
|
-
const toolCallId = typeof payload.call_id === 'string' ? payload.call_id : `${threadId}-tool-${eventIndex + 1}`;
|
|
1494
|
-
if (toolCalls.has(toolCallId)) {
|
|
1495
|
-
pushToolOutput(timestamp, toolCallId, payload.output);
|
|
1496
|
-
}
|
|
1497
|
-
continue;
|
|
1498
|
-
}
|
|
1499
1192
|
if (payload.type !== 'message')
|
|
1500
1193
|
continue;
|
|
1501
1194
|
messageIndex += 1;
|
|
@@ -1615,6 +1308,16 @@ function coerceSingleQueryParam(value) {
|
|
|
1615
1308
|
return value[0] || null;
|
|
1616
1309
|
return value || null;
|
|
1617
1310
|
}
|
|
1311
|
+
function codexThreadsListRoute(pathname) {
|
|
1312
|
+
return /^\/api\/computers\/\{computer_id\}\/agents\/[^/]+\/codex\/threads$/.test(pathname);
|
|
1313
|
+
}
|
|
1314
|
+
function codexThreadActionRoute(pathname, action) {
|
|
1315
|
+
const suffix = action === 'messages' ? 'messages' : action;
|
|
1316
|
+
return pathname.match(new RegExp(`^/api/computers/\\{computer_id\\}/agents/[^/]+/codex/threads/([^/]+)/${suffix}$`));
|
|
1317
|
+
}
|
|
1318
|
+
function codexProjectsOpenRoute(pathname) {
|
|
1319
|
+
return /^\/api\/computers\/\{computer_id\}\/agents\/[^/]+\/codex\/projects\/open$/.test(pathname);
|
|
1320
|
+
}
|
|
1618
1321
|
function collectSpaceLogEntries(filters = {}) {
|
|
1619
1322
|
const spaceId = currentSpaceId();
|
|
1620
1323
|
const limit = Math.max(1, Math.min(filters.limit || SPACE_LOG_EVENT_LIMIT, 500));
|
|
@@ -1698,7 +1401,7 @@ function threadStreamChunkForEvent(event) {
|
|
|
1698
1401
|
!Array.isArray(event.raw_payload) &&
|
|
1699
1402
|
typeof event.raw_payload.type === 'string' &&
|
|
1700
1403
|
String(event.raw_payload.type).trim()) {
|
|
1701
|
-
return
|
|
1404
|
+
return event.raw_payload;
|
|
1702
1405
|
}
|
|
1703
1406
|
return {
|
|
1704
1407
|
type: 'data-codex-event',
|
|
@@ -1708,28 +1411,6 @@ function threadStreamChunkForEvent(event) {
|
|
|
1708
1411
|
},
|
|
1709
1412
|
};
|
|
1710
1413
|
}
|
|
1711
|
-
function normalizeStoredCodexThreadChunk(chunk) {
|
|
1712
|
-
if (chunk.type !== 'data-supen-event')
|
|
1713
|
-
return chunk;
|
|
1714
|
-
const data = chunk.data && typeof chunk.data === 'object' && !Array.isArray(chunk.data)
|
|
1715
|
-
? chunk.data
|
|
1716
|
-
: {};
|
|
1717
|
-
const raw = data.raw && typeof data.raw === 'object' && !Array.isArray(data.raw)
|
|
1718
|
-
? data.raw
|
|
1719
|
-
: {};
|
|
1720
|
-
return {
|
|
1721
|
-
...chunk,
|
|
1722
|
-
type: 'data-codex-event',
|
|
1723
|
-
data: {
|
|
1724
|
-
...data,
|
|
1725
|
-
eventType: typeof data.eventType === 'string' && data.eventType.trim()
|
|
1726
|
-
? data.eventType
|
|
1727
|
-
: typeof raw.method === 'string' && raw.method.trim()
|
|
1728
|
-
? raw.method
|
|
1729
|
-
: 'codex-event',
|
|
1730
|
-
},
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
1414
|
function buildMcpSettingsResponse() {
|
|
1734
1415
|
const overrides = getMcpEnvOverrides();
|
|
1735
1416
|
const runtimeEnv = { ...process.env };
|
|
@@ -1764,6 +1445,15 @@ const MANAGED_CODING_CLI_INSTALL_COMMANDS = {
|
|
|
1764
1445
|
codex: CODING_CLI_INSTALL_COMMANDS.codex,
|
|
1765
1446
|
gemini: CODING_CLI_INSTALL_COMMANDS.gemini,
|
|
1766
1447
|
};
|
|
1448
|
+
function resolveManagedCodingCliInstallCommand(cli, current) {
|
|
1449
|
+
if (cli === 'codex' && current.install_source === 'homebrew') {
|
|
1450
|
+
return {
|
|
1451
|
+
command: 'sh',
|
|
1452
|
+
args: ['-lc', 'brew upgrade codex || brew upgrade --cask codex || brew reinstall --cask codex'],
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
return MANAGED_CODING_CLI_INSTALL_COMMANDS[cli];
|
|
1456
|
+
}
|
|
1767
1457
|
const CODING_CLI_NAMES = ['codex', 'gemini', 'claude'];
|
|
1768
1458
|
const DETECTABLE_SPACE_APPS = [
|
|
1769
1459
|
{ id: 'libreoffice', command: 'libreoffice', managed: false },
|
|
@@ -1883,7 +1573,6 @@ function readPackageVersion(packageRoot) {
|
|
|
1883
1573
|
}
|
|
1884
1574
|
}
|
|
1885
1575
|
function detectDaemonPackageStatus() {
|
|
1886
|
-
const envVersion = process.env.SUPEN_DAEMON_VERSION || process.env.npm_package_version || null;
|
|
1887
1576
|
let packageRoot = null;
|
|
1888
1577
|
try {
|
|
1889
1578
|
packageRoot = findPackageRootFrom(require.resolve('@supen-ai/daemon'), '@supen-ai/daemon');
|
|
@@ -1926,7 +1615,7 @@ function detectDaemonPackageStatus() {
|
|
|
1926
1615
|
? 'local-package'
|
|
1927
1616
|
: null;
|
|
1928
1617
|
return {
|
|
1929
|
-
version:
|
|
1618
|
+
version: bundledDaemonVersion || packageVersion,
|
|
1930
1619
|
package_root: packageRoot || bundledDaemonRoot,
|
|
1931
1620
|
install_source: installSource,
|
|
1932
1621
|
update_supported: installSource === 'supen-cli-bundle' || installSource === 'npm-global',
|
|
@@ -1971,7 +1660,7 @@ function inferCliInstallSource(name, executablePath, resolvedPath) {
|
|
|
1971
1660
|
return { install_source: 'pnpm', update_supported: false };
|
|
1972
1661
|
}
|
|
1973
1662
|
if (normalized.includes('/homebrew/') || normalized.includes('/opt/homebrew/') || normalized.includes('/cellar/')) {
|
|
1974
|
-
return { install_source: 'homebrew', update_supported:
|
|
1663
|
+
return { install_source: 'homebrew', update_supported: name === 'codex' };
|
|
1975
1664
|
}
|
|
1976
1665
|
if (name === 'codex') {
|
|
1977
1666
|
const npmRoot = detectGlobalNpmRoot();
|
|
@@ -2322,6 +2011,47 @@ function readInstalledAppsPayload() {
|
|
|
2322
2011
|
}),
|
|
2323
2012
|
};
|
|
2324
2013
|
}
|
|
2014
|
+
const CODEX_SUBSCRIPTION_STATUS_WAIT_MS = 1800;
|
|
2015
|
+
const CODEX_SUBSCRIPTION_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
2016
|
+
let cachedCodexSubscription = null;
|
|
2017
|
+
let codexSubscriptionRefreshInFlight = null;
|
|
2018
|
+
function refreshCodexSubscriptionStatus() {
|
|
2019
|
+
if (codexSubscriptionRefreshInFlight)
|
|
2020
|
+
return codexSubscriptionRefreshInFlight;
|
|
2021
|
+
codexSubscriptionRefreshInFlight = readCodexSubscription()
|
|
2022
|
+
.then((payload) => {
|
|
2023
|
+
cachedCodexSubscription = { payload, cachedAt: Date.now() };
|
|
2024
|
+
return payload;
|
|
2025
|
+
})
|
|
2026
|
+
.finally(() => {
|
|
2027
|
+
codexSubscriptionRefreshInFlight = null;
|
|
2028
|
+
});
|
|
2029
|
+
return codexSubscriptionRefreshInFlight;
|
|
2030
|
+
}
|
|
2031
|
+
async function readCodexSubscriptionForStatus() {
|
|
2032
|
+
if (cachedCodexSubscription &&
|
|
2033
|
+
Date.now() - cachedCodexSubscription.cachedAt < CODEX_SUBSCRIPTION_CACHE_TTL_MS) {
|
|
2034
|
+
return { payload: cachedCodexSubscription.payload, error: null };
|
|
2035
|
+
}
|
|
2036
|
+
const refresh = refreshCodexSubscriptionStatus();
|
|
2037
|
+
const timeout = new Promise((resolve) => {
|
|
2038
|
+
setTimeout(() => resolve(null), CODEX_SUBSCRIPTION_STATUS_WAIT_MS).unref?.();
|
|
2039
|
+
});
|
|
2040
|
+
try {
|
|
2041
|
+
const payload = await Promise.race([refresh, timeout]);
|
|
2042
|
+
if (payload)
|
|
2043
|
+
return { payload, error: null };
|
|
2044
|
+
}
|
|
2045
|
+
catch (err) {
|
|
2046
|
+
if (cachedCodexSubscription)
|
|
2047
|
+
return { payload: cachedCodexSubscription.payload, error: null };
|
|
2048
|
+
return { payload: null, error: err?.message || 'Unable to read Codex subscription details.' };
|
|
2049
|
+
}
|
|
2050
|
+
if (cachedCodexSubscription)
|
|
2051
|
+
return { payload: cachedCodexSubscription.payload, error: null };
|
|
2052
|
+
refresh.catch(() => undefined);
|
|
2053
|
+
return { payload: null, error: null };
|
|
2054
|
+
}
|
|
2325
2055
|
function readCachedCodingCliStatusPayload(options) {
|
|
2326
2056
|
startCodingCliStatusCache({
|
|
2327
2057
|
build: () => ({
|
|
@@ -2396,14 +2126,22 @@ function codexSubscriptionSummary(subscription) {
|
|
|
2396
2126
|
: {};
|
|
2397
2127
|
return {
|
|
2398
2128
|
plan_type: typeof rateLimits.planType === 'string' && rateLimits.planType.trim() ? rateLimits.planType.trim() : null,
|
|
2399
|
-
credits_balance: typeof credits.balance === 'string' && credits.balance.trim()
|
|
2129
|
+
credits_balance: typeof credits.balance === 'string' && credits.balance.trim()
|
|
2130
|
+
? credits.balance.trim()
|
|
2131
|
+
: typeof credits.balance === 'number' && Number.isFinite(credits.balance)
|
|
2132
|
+
? String(credits.balance)
|
|
2133
|
+
: null,
|
|
2400
2134
|
};
|
|
2401
2135
|
}
|
|
2402
2136
|
async function readCodexAgentStatusPayload() {
|
|
2403
|
-
const status =
|
|
2137
|
+
const status = {
|
|
2138
|
+
...readCachedCodingCliStatusPayload(),
|
|
2139
|
+
daemon: detectDaemonPackageStatus(),
|
|
2140
|
+
};
|
|
2404
2141
|
const quotaStatus = readLatestSpaceQuotaStatus();
|
|
2405
|
-
|
|
2406
|
-
|
|
2142
|
+
const subscriptionResult = await readCodexSubscriptionForStatus();
|
|
2143
|
+
const subscription = subscriptionResult.payload;
|
|
2144
|
+
if (subscription) {
|
|
2407
2145
|
const subscriptionWindows = mergeQuotaWindows(normalizeCodexSubscriptionQuotaWindows(subscription), normalizeQuotaWindows(subscription.rate_limits), normalizeQuotaWindows(subscription.rate_limits_by_limit_id));
|
|
2408
2146
|
return {
|
|
2409
2147
|
...status,
|
|
@@ -2418,23 +2156,21 @@ async function readCodexAgentStatusPayload() {
|
|
|
2418
2156
|
quota_windows: mergeQuotaWindows(quotaStatus.windows, subscriptionWindows),
|
|
2419
2157
|
};
|
|
2420
2158
|
}
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
};
|
|
2437
|
-
}
|
|
2159
|
+
return {
|
|
2160
|
+
...status,
|
|
2161
|
+
subscription: {
|
|
2162
|
+
ok: false,
|
|
2163
|
+
fetched_at: null,
|
|
2164
|
+
payload: null,
|
|
2165
|
+
error: subscriptionResult.error,
|
|
2166
|
+
},
|
|
2167
|
+
subscription_summary: {
|
|
2168
|
+
plan_type: null,
|
|
2169
|
+
credits_balance: null,
|
|
2170
|
+
},
|
|
2171
|
+
quota_status: quotaStatus,
|
|
2172
|
+
quota_windows: quotaStatus.windows,
|
|
2173
|
+
};
|
|
2438
2174
|
}
|
|
2439
2175
|
function readRuntimeModelStatusPayload() {
|
|
2440
2176
|
const selected_cli = process.env.SUPEN_CODING_CLI || readConfigSummary().coding_cli || 'codex';
|
|
@@ -2763,31 +2499,6 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2763
2499
|
writeJson(res, 200, await readCodexAgentStatusPayload());
|
|
2764
2500
|
return true;
|
|
2765
2501
|
}
|
|
2766
|
-
if (pathname === '/api/computers/{computer_id}/runtime-models' && method === 'GET') {
|
|
2767
|
-
writeJson(res, 200, readRuntimeModelStatusPayload());
|
|
2768
|
-
return true;
|
|
2769
|
-
}
|
|
2770
|
-
if (pathname === '/api/computers/{computer_id}/runtime-models/default' && method === 'PUT') {
|
|
2771
|
-
try {
|
|
2772
|
-
const parsed = await readJsonBody(req);
|
|
2773
|
-
const model = typeof parsed === 'object' && parsed && typeof parsed.model === 'string'
|
|
2774
|
-
? String(parsed.model).trim()
|
|
2775
|
-
: '';
|
|
2776
|
-
if (!model) {
|
|
2777
|
-
writeProtocolError(res, 400, 'validation_error', 'missing_model', 'Request body must include a non-empty "model" string.');
|
|
2778
|
-
return true;
|
|
2779
|
-
}
|
|
2780
|
-
writeCodexDefaultModel(model);
|
|
2781
|
-
writeJson(res, 200, {
|
|
2782
|
-
ok: true,
|
|
2783
|
-
status: readRuntimeModelStatusPayload(),
|
|
2784
|
-
});
|
|
2785
|
-
}
|
|
2786
|
-
catch (err) {
|
|
2787
|
-
writeProtocolError(res, 500, 'config_error', 'codex_model_default_failed', err?.message || 'Failed to set Codex default model');
|
|
2788
|
-
}
|
|
2789
|
-
return true;
|
|
2790
|
-
}
|
|
2791
2502
|
const codexTransportDefaultMatch = pathname.match(/^\/api\/computers\/\{computer_id\}\/agents\/codex\/transports\/([^/]+)\/default$/);
|
|
2792
2503
|
if (codexTransportDefaultMatch && method === 'PUT') {
|
|
2793
2504
|
try {
|
|
@@ -2822,7 +2533,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2822
2533
|
}
|
|
2823
2534
|
return true;
|
|
2824
2535
|
}
|
|
2825
|
-
if (pathname === '/api/computers/{computer_id}/agents/codex/
|
|
2536
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/update' && method === 'POST') {
|
|
2826
2537
|
try {
|
|
2827
2538
|
const cli = 'codex';
|
|
2828
2539
|
const current = detectCliInstalled(cli);
|
|
@@ -2830,7 +2541,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2830
2541
|
writeProtocolError(res, 400, 'validation_error', 'coding_cli_update_not_supported', `${cli} is installed via ${current.install_source || current.resolved_path || current.path || 'an unknown source'}; update it with that installer on this computer.`);
|
|
2831
2542
|
return true;
|
|
2832
2543
|
}
|
|
2833
|
-
const installSpec =
|
|
2544
|
+
const installSpec = resolveManagedCodingCliInstallCommand(cli, current);
|
|
2834
2545
|
const result = spawnSync(installSpec.command, installSpec.args, {
|
|
2835
2546
|
encoding: 'utf8',
|
|
2836
2547
|
timeout: 120_000,
|
|
@@ -2946,15 +2657,15 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2946
2657
|
writeJson(res, 200, buildHubSnapshotForSpace(spaceId));
|
|
2947
2658
|
return true;
|
|
2948
2659
|
}
|
|
2949
|
-
if (pathname === '/api/computers/{computer_id}/usage' && method === 'GET') {
|
|
2660
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/usage' && method === 'GET') {
|
|
2950
2661
|
writeJson(res, 200, getGlobalUsage());
|
|
2951
2662
|
return true;
|
|
2952
2663
|
}
|
|
2953
|
-
if (pathname === '/api/computers/{computer_id}/usage/daily' && method === 'GET') {
|
|
2664
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/usage/daily' && method === 'GET') {
|
|
2954
2665
|
writeJson(res, 200, { daily: getDailyUsage() });
|
|
2955
2666
|
return true;
|
|
2956
2667
|
}
|
|
2957
|
-
if (pathname
|
|
2668
|
+
if (codexThreadsListRoute(pathname) && method === 'GET') {
|
|
2958
2669
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2959
2670
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : MIRRORED_THREAD_LIMIT;
|
|
2960
2671
|
writeJson(res, 200, readMirroredTaskProjects(Number.isFinite(limit) ? limit : MIRRORED_THREAD_LIMIT));
|
|
@@ -2974,7 +2685,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2974
2685
|
serveRemoteFile(req, res, filePath);
|
|
2975
2686
|
return true;
|
|
2976
2687
|
}
|
|
2977
|
-
const mirroredThreadArchiveMatch = pathname
|
|
2688
|
+
const mirroredThreadArchiveMatch = codexThreadActionRoute(pathname, 'archive');
|
|
2978
2689
|
if (mirroredThreadArchiveMatch && method === 'POST') {
|
|
2979
2690
|
const threadId = decodeURIComponent(mirroredThreadArchiveMatch[1] || '').trim();
|
|
2980
2691
|
if (!threadId) {
|
|
@@ -2988,7 +2699,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2988
2699
|
writeJson(res, 200, { ok: true, archived: true });
|
|
2989
2700
|
return true;
|
|
2990
2701
|
}
|
|
2991
|
-
const mirroredThreadStreamMatch = pathname
|
|
2702
|
+
const mirroredThreadStreamMatch = codexThreadActionRoute(pathname, 'stream');
|
|
2992
2703
|
if (mirroredThreadStreamMatch && method === 'GET') {
|
|
2993
2704
|
const threadId = decodeURIComponent(mirroredThreadStreamMatch[1] || '').trim();
|
|
2994
2705
|
if (!threadId) {
|
|
@@ -3031,7 +2742,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3031
2742
|
res.flush?.();
|
|
3032
2743
|
return true;
|
|
3033
2744
|
}
|
|
3034
|
-
const mirroredThreadHistoryMatch = pathname
|
|
2745
|
+
const mirroredThreadHistoryMatch = codexThreadActionRoute(pathname, 'messages');
|
|
3035
2746
|
if (mirroredThreadHistoryMatch && method === 'GET') {
|
|
3036
2747
|
const threadId = decodeURIComponent(mirroredThreadHistoryMatch[1] || '').trim();
|
|
3037
2748
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
@@ -3046,7 +2757,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3046
2757
|
writeJson(res, 200, history);
|
|
3047
2758
|
return true;
|
|
3048
2759
|
}
|
|
3049
|
-
const mirroredThreadAdoptMatch = pathname
|
|
2760
|
+
const mirroredThreadAdoptMatch = codexThreadActionRoute(pathname, 'adopt');
|
|
3050
2761
|
if (mirroredThreadAdoptMatch && method === 'POST') {
|
|
3051
2762
|
const threadId = decodeURIComponent(mirroredThreadAdoptMatch[1] || '').trim();
|
|
3052
2763
|
const parsed = await readJsonBody(req);
|
|
@@ -3063,7 +2774,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3063
2774
|
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(session) });
|
|
3064
2775
|
return true;
|
|
3065
2776
|
}
|
|
3066
|
-
if (pathname
|
|
2777
|
+
if (codexProjectsOpenRoute(pathname) && method === 'POST') {
|
|
3067
2778
|
try {
|
|
3068
2779
|
const parsed = await readJsonBody(req);
|
|
3069
2780
|
const projectPath = parsed && typeof parsed === 'object' && typeof parsed.path === 'string'
|