@supen-ai/cli 0.1.14 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/daemon/dist/channels/http-routes.d.ts.map +1 -1
- package/daemon/dist/channels/http-routes.js +394 -35
- package/daemon/dist/channels/http-routes.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 -258
- 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 +198 -95
- 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/computer.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../src/http/routes/system.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AA+M1B,KAAK,sBAAsB,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,yBAAyB,EAAE,CAAC;CAC3C,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAuVF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAoC,GAC3C,MAAM,EAAE,CAuBV;AAuxBD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAgC,EACrC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0anD;
|
|
1
|
+
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../src/http/routes/system.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AA+M1B,KAAK,sBAAsB,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,yBAAyB,EAAE,CAAC;CAC3C,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAuVF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAoC,GAC3C,MAAM,EAAE,CAuBV;AAuxBD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAgC,EACrC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0anD;AAkrCD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA60BlB"}
|
|
@@ -1615,6 +1615,16 @@ function coerceSingleQueryParam(value) {
|
|
|
1615
1615
|
return value[0] || null;
|
|
1616
1616
|
return value || null;
|
|
1617
1617
|
}
|
|
1618
|
+
function codexThreadsListRoute(pathname) {
|
|
1619
|
+
return /^\/api\/computers\/\{computer_id\}\/agents\/[^/]+\/codex\/threads$/.test(pathname);
|
|
1620
|
+
}
|
|
1621
|
+
function codexThreadActionRoute(pathname, action) {
|
|
1622
|
+
const suffix = action === 'messages' ? 'messages' : action;
|
|
1623
|
+
return pathname.match(new RegExp(`^/api/computers/\\{computer_id\\}/agents/[^/]+/codex/threads/([^/]+)/${suffix}$`));
|
|
1624
|
+
}
|
|
1625
|
+
function codexProjectsOpenRoute(pathname) {
|
|
1626
|
+
return /^\/api\/computers\/\{computer_id\}\/agents\/[^/]+\/codex\/projects\/open$/.test(pathname);
|
|
1627
|
+
}
|
|
1618
1628
|
function collectSpaceLogEntries(filters = {}) {
|
|
1619
1629
|
const spaceId = currentSpaceId();
|
|
1620
1630
|
const limit = Math.max(1, Math.min(filters.limit || SPACE_LOG_EVENT_LIMIT, 500));
|
|
@@ -1764,12 +1774,17 @@ const MANAGED_CODING_CLI_INSTALL_COMMANDS = {
|
|
|
1764
1774
|
codex: CODING_CLI_INSTALL_COMMANDS.codex,
|
|
1765
1775
|
gemini: CODING_CLI_INSTALL_COMMANDS.gemini,
|
|
1766
1776
|
};
|
|
1777
|
+
function resolveManagedCodingCliInstallCommand(cli, current) {
|
|
1778
|
+
if (cli === 'codex' && current.install_source === 'homebrew') {
|
|
1779
|
+
return {
|
|
1780
|
+
command: 'sh',
|
|
1781
|
+
args: ['-lc', 'brew upgrade codex || brew upgrade --cask codex || brew reinstall --cask codex'],
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
return MANAGED_CODING_CLI_INSTALL_COMMANDS[cli];
|
|
1785
|
+
}
|
|
1767
1786
|
const CODING_CLI_NAMES = ['codex', 'gemini', 'claude'];
|
|
1768
1787
|
const DETECTABLE_SPACE_APPS = [
|
|
1769
|
-
{ id: 'codex', command: 'codex', managed: true },
|
|
1770
|
-
{ id: 'gemini', command: 'gemini', managed: true },
|
|
1771
|
-
{ id: 'claude', command: 'claude', managed: false },
|
|
1772
|
-
{ id: 'acpx', command: 'acpx', managed: false },
|
|
1773
1788
|
{ id: 'libreoffice', command: 'libreoffice', managed: false },
|
|
1774
1789
|
{ id: 'dotnet', command: 'dotnet', managed: false },
|
|
1775
1790
|
{ id: 'tiwater-docx', command: 'tiwater-docx', managed: false },
|
|
@@ -1975,7 +1990,7 @@ function inferCliInstallSource(name, executablePath, resolvedPath) {
|
|
|
1975
1990
|
return { install_source: 'pnpm', update_supported: false };
|
|
1976
1991
|
}
|
|
1977
1992
|
if (normalized.includes('/homebrew/') || normalized.includes('/opt/homebrew/') || normalized.includes('/cellar/')) {
|
|
1978
|
-
return { install_source: 'homebrew', update_supported:
|
|
1993
|
+
return { install_source: 'homebrew', update_supported: name === 'codex' };
|
|
1979
1994
|
}
|
|
1980
1995
|
if (name === 'codex') {
|
|
1981
1996
|
const npmRoot = detectGlobalNpmRoot();
|
|
@@ -2053,15 +2068,47 @@ function detectCodexAuthStatus(installed) {
|
|
|
2053
2068
|
? email || accountLine || 'Authenticated'
|
|
2054
2069
|
: null;
|
|
2055
2070
|
let accountId = null;
|
|
2071
|
+
let tokenEmail = null;
|
|
2072
|
+
let tokenName = null;
|
|
2056
2073
|
try {
|
|
2057
2074
|
const parsed = JSON.parse(fs.readFileSync(path.join(resolveCodexHome(), 'auth.json'), 'utf8'));
|
|
2058
2075
|
const tokens = parsed.tokens && typeof parsed.tokens === 'object' ? parsed.tokens : {};
|
|
2059
2076
|
accountId = typeof tokens.account_id === 'string' && tokens.account_id.trim() ? tokens.account_id.trim() : null;
|
|
2077
|
+
const identity = codexIdentityClaimsFromIdToken(tokens.id_token);
|
|
2078
|
+
tokenEmail = identity.email;
|
|
2079
|
+
tokenName = identity.name;
|
|
2060
2080
|
}
|
|
2061
2081
|
catch {
|
|
2062
2082
|
accountId = null;
|
|
2063
2083
|
}
|
|
2064
|
-
return {
|
|
2084
|
+
return {
|
|
2085
|
+
authenticated,
|
|
2086
|
+
summary: authenticated ? tokenEmail || email || tokenName || accountLine || 'Authenticated' : null,
|
|
2087
|
+
account_id: accountId,
|
|
2088
|
+
email: tokenEmail || email || null,
|
|
2089
|
+
name: tokenName,
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
function codexIdentityClaimsFromIdToken(idToken) {
|
|
2093
|
+
if (typeof idToken !== 'string' || !idToken.trim())
|
|
2094
|
+
return { email: null, name: null };
|
|
2095
|
+
const payload = idToken.split('.')[1];
|
|
2096
|
+
if (!payload)
|
|
2097
|
+
return { email: null, name: null };
|
|
2098
|
+
try {
|
|
2099
|
+
const decoded = Buffer.from(payload.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
|
|
2100
|
+
const claims = JSON.parse(decoded);
|
|
2101
|
+
const email = typeof claims.email === 'string' && claims.email.trim()
|
|
2102
|
+
? claims.email.trim()
|
|
2103
|
+
: null;
|
|
2104
|
+
const name = typeof claims.name === 'string' && claims.name.trim()
|
|
2105
|
+
? claims.name.trim()
|
|
2106
|
+
: null;
|
|
2107
|
+
return { email, name };
|
|
2108
|
+
}
|
|
2109
|
+
catch {
|
|
2110
|
+
return { email: null, name: null };
|
|
2111
|
+
}
|
|
2065
2112
|
}
|
|
2066
2113
|
function readCodexDefaultModel() {
|
|
2067
2114
|
const codexHome = resolveCodexHome();
|
|
@@ -2277,6 +2324,23 @@ function readCodingCliStatusPayload() {
|
|
|
2277
2324
|
codex_connect: codexConnectSnapshot(),
|
|
2278
2325
|
};
|
|
2279
2326
|
}
|
|
2327
|
+
function readInstalledAppsPayload() {
|
|
2328
|
+
return {
|
|
2329
|
+
installed_apps: DETECTABLE_SPACE_APPS.map((app) => {
|
|
2330
|
+
const detected = detectCliInstalled(app.command);
|
|
2331
|
+
return {
|
|
2332
|
+
id: app.id,
|
|
2333
|
+
installed: detected.installed,
|
|
2334
|
+
version: detected.version,
|
|
2335
|
+
managed: app.managed,
|
|
2336
|
+
path: detected.path,
|
|
2337
|
+
resolved_path: detected.resolved_path,
|
|
2338
|
+
install_source: detected.install_source,
|
|
2339
|
+
update_supported: detected.update_supported,
|
|
2340
|
+
};
|
|
2341
|
+
}),
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2280
2344
|
function readCachedCodingCliStatusPayload(options) {
|
|
2281
2345
|
startCodingCliStatusCache({
|
|
2282
2346
|
build: () => ({
|
|
@@ -2286,6 +2350,111 @@ function readCachedCodingCliStatusPayload(options) {
|
|
|
2286
2350
|
});
|
|
2287
2351
|
return getCodingCliStatusResponse(options);
|
|
2288
2352
|
}
|
|
2353
|
+
function mergeQuotaWindows(...groups) {
|
|
2354
|
+
const byLabel = new Map();
|
|
2355
|
+
for (const group of groups) {
|
|
2356
|
+
for (const window of group) {
|
|
2357
|
+
const key = window.label.trim().toLowerCase();
|
|
2358
|
+
if (!key)
|
|
2359
|
+
continue;
|
|
2360
|
+
byLabel.set(key, window);
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
return Array.from(byLabel.values());
|
|
2364
|
+
}
|
|
2365
|
+
function resetAtFromCodexValue(value) {
|
|
2366
|
+
if (typeof value === 'string' && value.trim())
|
|
2367
|
+
return value.trim();
|
|
2368
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
2369
|
+
const millis = value > 10_000_000_000 ? value : value * 1000;
|
|
2370
|
+
return new Date(millis).toISOString();
|
|
2371
|
+
}
|
|
2372
|
+
return null;
|
|
2373
|
+
}
|
|
2374
|
+
function quotaLabelForCodexWindow(key, record) {
|
|
2375
|
+
const duration = numberValue(record.windowDurationMins);
|
|
2376
|
+
if (duration === 300)
|
|
2377
|
+
return '5-hour limit';
|
|
2378
|
+
if (duration === 10080)
|
|
2379
|
+
return 'Weekly limit';
|
|
2380
|
+
return key === 'primary' ? 'Primary limit' : key === 'secondary' ? 'Secondary limit' : labelForQuotaWindow(key, record);
|
|
2381
|
+
}
|
|
2382
|
+
function quotaWindowFromCodexNestedRecord(key, value) {
|
|
2383
|
+
if (!value || typeof value !== 'object')
|
|
2384
|
+
return null;
|
|
2385
|
+
const record = value;
|
|
2386
|
+
const percent = numberValue(record.usedPercent ?? record.used_percent ?? record.percent);
|
|
2387
|
+
const reset_at = resetAtFromCodexValue(record.resetsAt ?? record.resetAt ?? record.reset_at ?? record.reset);
|
|
2388
|
+
if (percent === null && !reset_at)
|
|
2389
|
+
return null;
|
|
2390
|
+
return {
|
|
2391
|
+
label: quotaLabelForCodexWindow(key, record),
|
|
2392
|
+
used: null,
|
|
2393
|
+
limit: null,
|
|
2394
|
+
remaining: null,
|
|
2395
|
+
percent,
|
|
2396
|
+
reset_at,
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
function normalizeCodexSubscriptionQuotaWindows(subscription) {
|
|
2400
|
+
const rateLimits = subscription.rate_limits && typeof subscription.rate_limits === 'object'
|
|
2401
|
+
? subscription.rate_limits
|
|
2402
|
+
: null;
|
|
2403
|
+
if (!rateLimits)
|
|
2404
|
+
return [];
|
|
2405
|
+
return ['primary', 'secondary']
|
|
2406
|
+
.map((key) => quotaWindowFromCodexNestedRecord(key, rateLimits[key]))
|
|
2407
|
+
.filter((entry) => Boolean(entry));
|
|
2408
|
+
}
|
|
2409
|
+
function codexSubscriptionSummary(subscription) {
|
|
2410
|
+
const rateLimits = subscription.rate_limits && typeof subscription.rate_limits === 'object'
|
|
2411
|
+
? subscription.rate_limits
|
|
2412
|
+
: {};
|
|
2413
|
+
const credits = rateLimits.credits && typeof rateLimits.credits === 'object'
|
|
2414
|
+
? rateLimits.credits
|
|
2415
|
+
: {};
|
|
2416
|
+
return {
|
|
2417
|
+
plan_type: typeof rateLimits.planType === 'string' && rateLimits.planType.trim() ? rateLimits.planType.trim() : null,
|
|
2418
|
+
credits_balance: typeof credits.balance === 'string' && credits.balance.trim() ? credits.balance.trim() : null,
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
async function readCodexAgentStatusPayload() {
|
|
2422
|
+
const status = readCachedCodingCliStatusPayload();
|
|
2423
|
+
const quotaStatus = readLatestSpaceQuotaStatus();
|
|
2424
|
+
try {
|
|
2425
|
+
const subscription = await readCodexSubscription();
|
|
2426
|
+
const subscriptionWindows = mergeQuotaWindows(normalizeCodexSubscriptionQuotaWindows(subscription), normalizeQuotaWindows(subscription.rate_limits), normalizeQuotaWindows(subscription.rate_limits_by_limit_id));
|
|
2427
|
+
return {
|
|
2428
|
+
...status,
|
|
2429
|
+
subscription: {
|
|
2430
|
+
ok: true,
|
|
2431
|
+
fetched_at: subscription.fetched_at,
|
|
2432
|
+
payload: subscription,
|
|
2433
|
+
error: null,
|
|
2434
|
+
},
|
|
2435
|
+
subscription_summary: codexSubscriptionSummary(subscription),
|
|
2436
|
+
quota_status: quotaStatus,
|
|
2437
|
+
quota_windows: mergeQuotaWindows(quotaStatus.windows, subscriptionWindows),
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
catch (err) {
|
|
2441
|
+
return {
|
|
2442
|
+
...status,
|
|
2443
|
+
subscription: {
|
|
2444
|
+
ok: false,
|
|
2445
|
+
fetched_at: null,
|
|
2446
|
+
payload: null,
|
|
2447
|
+
error: err?.message || 'Unable to read Codex subscription details.',
|
|
2448
|
+
},
|
|
2449
|
+
subscription_summary: {
|
|
2450
|
+
plan_type: null,
|
|
2451
|
+
credits_balance: null,
|
|
2452
|
+
},
|
|
2453
|
+
quota_status: quotaStatus,
|
|
2454
|
+
quota_windows: quotaStatus.windows,
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2289
2458
|
function readRuntimeModelStatusPayload() {
|
|
2290
2459
|
const selected_cli = process.env.SUPEN_CODING_CLI || readConfigSummary().coding_cli || 'codex';
|
|
2291
2460
|
const codexTransport = normalizeCodexTransport(process.env.SUPEN_CODEX_TRANSPORT) ||
|
|
@@ -2606,38 +2775,17 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2606
2775
|
return true;
|
|
2607
2776
|
}
|
|
2608
2777
|
if (pathname === '/api/computers/{computer_id}/apps' && method === 'GET') {
|
|
2609
|
-
writeJson(res, 200,
|
|
2610
|
-
return true;
|
|
2611
|
-
}
|
|
2612
|
-
if (pathname === '/api/computers/{computer_id}/runtime-models' && method === 'GET') {
|
|
2613
|
-
writeJson(res, 200, readRuntimeModelStatusPayload());
|
|
2778
|
+
writeJson(res, 200, readInstalledAppsPayload());
|
|
2614
2779
|
return true;
|
|
2615
2780
|
}
|
|
2616
|
-
if (pathname === '/api/computers/{computer_id}/
|
|
2617
|
-
|
|
2618
|
-
const parsed = await readJsonBody(req);
|
|
2619
|
-
const model = typeof parsed === 'object' && parsed && typeof parsed.model === 'string'
|
|
2620
|
-
? String(parsed.model).trim()
|
|
2621
|
-
: '';
|
|
2622
|
-
if (!model) {
|
|
2623
|
-
writeProtocolError(res, 400, 'validation_error', 'missing_model', 'Request body must include a non-empty "model" string.');
|
|
2624
|
-
return true;
|
|
2625
|
-
}
|
|
2626
|
-
writeCodexDefaultModel(model);
|
|
2627
|
-
writeJson(res, 200, {
|
|
2628
|
-
ok: true,
|
|
2629
|
-
status: readRuntimeModelStatusPayload(),
|
|
2630
|
-
});
|
|
2631
|
-
}
|
|
2632
|
-
catch (err) {
|
|
2633
|
-
writeProtocolError(res, 500, 'config_error', 'codex_model_default_failed', err?.message || 'Failed to set Codex default model');
|
|
2634
|
-
}
|
|
2781
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex' && method === 'GET') {
|
|
2782
|
+
writeJson(res, 200, await readCodexAgentStatusPayload());
|
|
2635
2783
|
return true;
|
|
2636
2784
|
}
|
|
2637
|
-
const
|
|
2638
|
-
if (
|
|
2785
|
+
const codexTransportDefaultMatch = pathname.match(/^\/api\/computers\/\{computer_id\}\/agents\/codex\/transports\/([^/]+)\/default$/);
|
|
2786
|
+
if (codexTransportDefaultMatch && method === 'PUT') {
|
|
2639
2787
|
try {
|
|
2640
|
-
const transport = normalizeCodexTransport(decodeURIComponent(
|
|
2788
|
+
const transport = normalizeCodexTransport(decodeURIComponent(codexTransportDefaultMatch[1] || '').trim());
|
|
2641
2789
|
if (!transport) {
|
|
2642
2790
|
writeProtocolError(res, 400, 'validation_error', 'invalid_codex_transport', 'transport must be one of: app-server, exec, acpx');
|
|
2643
2791
|
return true;
|
|
@@ -2654,15 +2802,9 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2654
2802
|
}
|
|
2655
2803
|
return true;
|
|
2656
2804
|
}
|
|
2657
|
-
|
|
2658
|
-
if (spaceAppDefaultMatch && method === 'PUT') {
|
|
2805
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/default' && method === 'PUT') {
|
|
2659
2806
|
try {
|
|
2660
|
-
const
|
|
2661
|
-
if (!cli) {
|
|
2662
|
-
writeProtocolError(res, 400, 'validation_error', 'invalid_cli', 'cli must be one of: codex, gemini, claude');
|
|
2663
|
-
return true;
|
|
2664
|
-
}
|
|
2665
|
-
const result = setDefaultCodingCli(cli);
|
|
2807
|
+
const result = setDefaultCodingCli('codex');
|
|
2666
2808
|
writeJson(res, 200, {
|
|
2667
2809
|
ok: true,
|
|
2668
2810
|
...result,
|
|
@@ -2670,28 +2812,19 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2670
2812
|
});
|
|
2671
2813
|
}
|
|
2672
2814
|
catch (err) {
|
|
2673
|
-
writeProtocolError(res, 500, 'config_error', '
|
|
2815
|
+
writeProtocolError(res, 500, 'config_error', 'coding_agent_default_failed', err?.message || 'Failed to set default coding agent');
|
|
2674
2816
|
}
|
|
2675
2817
|
return true;
|
|
2676
2818
|
}
|
|
2677
|
-
|
|
2678
|
-
if (spaceAppInstallMatch && method === 'POST') {
|
|
2819
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/update' && method === 'POST') {
|
|
2679
2820
|
try {
|
|
2680
|
-
const cli =
|
|
2681
|
-
if (!cli) {
|
|
2682
|
-
writeProtocolError(res, 400, 'validation_error', 'invalid_cli', 'cli must be one of: codex, gemini, claude');
|
|
2683
|
-
return true;
|
|
2684
|
-
}
|
|
2685
|
-
if (!isManagedCliName(cli)) {
|
|
2686
|
-
writeProtocolError(res, 400, 'validation_error', 'install_not_supported', 'Only codex and gemini support managed install at the moment');
|
|
2687
|
-
return true;
|
|
2688
|
-
}
|
|
2821
|
+
const cli = 'codex';
|
|
2689
2822
|
const current = detectCliInstalled(cli);
|
|
2690
2823
|
if (current.installed && !current.update_supported) {
|
|
2691
2824
|
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.`);
|
|
2692
2825
|
return true;
|
|
2693
2826
|
}
|
|
2694
|
-
const installSpec =
|
|
2827
|
+
const installSpec = resolveManagedCodingCliInstallCommand(cli, current);
|
|
2695
2828
|
const result = spawnSync(installSpec.command, installSpec.args, {
|
|
2696
2829
|
encoding: 'utf8',
|
|
2697
2830
|
timeout: 120_000,
|
|
@@ -2749,13 +2882,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2749
2882
|
}
|
|
2750
2883
|
return true;
|
|
2751
2884
|
}
|
|
2752
|
-
|
|
2753
|
-
if (spaceAppConnectMatch && method === 'POST') {
|
|
2754
|
-
const cli = normalizeCliName(decodeURIComponent(spaceAppConnectMatch[1] || '').trim());
|
|
2755
|
-
if (cli !== 'codex') {
|
|
2756
|
-
writeProtocolError(res, 400, 'validation_error', 'connect_not_supported', 'Only codex supports connect flow at the moment');
|
|
2757
|
-
return true;
|
|
2758
|
-
}
|
|
2885
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/connect' && method === 'POST') {
|
|
2759
2886
|
const status = readCachedCodingCliStatusPayload();
|
|
2760
2887
|
const codex = status.clis.find((entry) => entry.name === 'codex');
|
|
2761
2888
|
if (!codex?.installed) {
|
|
@@ -2769,12 +2896,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2769
2896
|
});
|
|
2770
2897
|
return true;
|
|
2771
2898
|
}
|
|
2772
|
-
if (
|
|
2773
|
-
const cli = normalizeCliName(decodeURIComponent(spaceAppConnectMatch[1] || '').trim());
|
|
2774
|
-
if (cli !== 'codex') {
|
|
2775
|
-
writeProtocolError(res, 400, 'validation_error', 'connect_not_supported', 'Only codex supports connect flow at the moment');
|
|
2776
|
-
return true;
|
|
2777
|
-
}
|
|
2899
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/connect' && method === 'DELETE') {
|
|
2778
2900
|
writeJson(res, 200, {
|
|
2779
2901
|
ok: true,
|
|
2780
2902
|
codex_connect: cancelCodexConnectFlow(),
|
|
@@ -2782,13 +2904,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2782
2904
|
});
|
|
2783
2905
|
return true;
|
|
2784
2906
|
}
|
|
2785
|
-
|
|
2786
|
-
if (spaceAppSessionMatch && method === 'DELETE') {
|
|
2787
|
-
const cli = normalizeCliName(decodeURIComponent(spaceAppSessionMatch[1] || '').trim());
|
|
2788
|
-
if (cli !== 'codex') {
|
|
2789
|
-
writeProtocolError(res, 400, 'validation_error', 'session_not_supported', 'Only codex supports session connect/disconnect at the moment');
|
|
2790
|
-
return true;
|
|
2791
|
-
}
|
|
2907
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/session' && method === 'DELETE') {
|
|
2792
2908
|
const cmd = spawnSync('codex', ['logout'], {
|
|
2793
2909
|
encoding: 'utf8',
|
|
2794
2910
|
timeout: 15_000,
|
|
@@ -2824,28 +2940,15 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2824
2940
|
writeJson(res, 200, buildHubSnapshotForSpace(spaceId));
|
|
2825
2941
|
return true;
|
|
2826
2942
|
}
|
|
2827
|
-
if (pathname === '/api/computers/{computer_id}/usage' && method === 'GET') {
|
|
2943
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/usage' && method === 'GET') {
|
|
2828
2944
|
writeJson(res, 200, getGlobalUsage());
|
|
2829
2945
|
return true;
|
|
2830
2946
|
}
|
|
2831
|
-
if (pathname === '/api/computers/{computer_id}/usage/daily' && method === 'GET') {
|
|
2947
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/usage/daily' && method === 'GET') {
|
|
2832
2948
|
writeJson(res, 200, { daily: getDailyUsage() });
|
|
2833
2949
|
return true;
|
|
2834
2950
|
}
|
|
2835
|
-
if (pathname
|
|
2836
|
-
try {
|
|
2837
|
-
writeJson(res, 200, await readCodexSubscription());
|
|
2838
|
-
}
|
|
2839
|
-
catch (err) {
|
|
2840
|
-
writeProtocolError(res, 503, 'service_unavailable', 'codex_subscription_unavailable', err?.message || 'Unable to read Codex subscription details.');
|
|
2841
|
-
}
|
|
2842
|
-
return true;
|
|
2843
|
-
}
|
|
2844
|
-
if (pathname === '/api/computers/{computer_id}/quota-status' && method === 'GET') {
|
|
2845
|
-
writeJson(res, 200, readLatestSpaceQuotaStatus());
|
|
2846
|
-
return true;
|
|
2847
|
-
}
|
|
2848
|
-
if (pathname === '/api/computers/{computer_id}/codex/threads' && method === 'GET') {
|
|
2951
|
+
if (codexThreadsListRoute(pathname) && method === 'GET') {
|
|
2849
2952
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2850
2953
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : MIRRORED_THREAD_LIMIT;
|
|
2851
2954
|
writeJson(res, 200, readMirroredTaskProjects(Number.isFinite(limit) ? limit : MIRRORED_THREAD_LIMIT));
|
|
@@ -2865,7 +2968,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2865
2968
|
serveRemoteFile(req, res, filePath);
|
|
2866
2969
|
return true;
|
|
2867
2970
|
}
|
|
2868
|
-
const mirroredThreadArchiveMatch = pathname
|
|
2971
|
+
const mirroredThreadArchiveMatch = codexThreadActionRoute(pathname, 'archive');
|
|
2869
2972
|
if (mirroredThreadArchiveMatch && method === 'POST') {
|
|
2870
2973
|
const threadId = decodeURIComponent(mirroredThreadArchiveMatch[1] || '').trim();
|
|
2871
2974
|
if (!threadId) {
|
|
@@ -2879,7 +2982,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2879
2982
|
writeJson(res, 200, { ok: true, archived: true });
|
|
2880
2983
|
return true;
|
|
2881
2984
|
}
|
|
2882
|
-
const mirroredThreadStreamMatch = pathname
|
|
2985
|
+
const mirroredThreadStreamMatch = codexThreadActionRoute(pathname, 'stream');
|
|
2883
2986
|
if (mirroredThreadStreamMatch && method === 'GET') {
|
|
2884
2987
|
const threadId = decodeURIComponent(mirroredThreadStreamMatch[1] || '').trim();
|
|
2885
2988
|
if (!threadId) {
|
|
@@ -2922,7 +3025,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2922
3025
|
res.flush?.();
|
|
2923
3026
|
return true;
|
|
2924
3027
|
}
|
|
2925
|
-
const mirroredThreadHistoryMatch = pathname
|
|
3028
|
+
const mirroredThreadHistoryMatch = codexThreadActionRoute(pathname, 'messages');
|
|
2926
3029
|
if (mirroredThreadHistoryMatch && method === 'GET') {
|
|
2927
3030
|
const threadId = decodeURIComponent(mirroredThreadHistoryMatch[1] || '').trim();
|
|
2928
3031
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
@@ -2937,7 +3040,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2937
3040
|
writeJson(res, 200, history);
|
|
2938
3041
|
return true;
|
|
2939
3042
|
}
|
|
2940
|
-
const mirroredThreadAdoptMatch = pathname
|
|
3043
|
+
const mirroredThreadAdoptMatch = codexThreadActionRoute(pathname, 'adopt');
|
|
2941
3044
|
if (mirroredThreadAdoptMatch && method === 'POST') {
|
|
2942
3045
|
const threadId = decodeURIComponent(mirroredThreadAdoptMatch[1] || '').trim();
|
|
2943
3046
|
const parsed = await readJsonBody(req);
|
|
@@ -2954,7 +3057,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2954
3057
|
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(session) });
|
|
2955
3058
|
return true;
|
|
2956
3059
|
}
|
|
2957
|
-
if (pathname
|
|
3060
|
+
if (codexProjectsOpenRoute(pathname) && method === 'POST') {
|
|
2958
3061
|
try {
|
|
2959
3062
|
const parsed = await readJsonBody(req);
|
|
2960
3063
|
const projectPath = parsed && typeof parsed === 'object' && typeof parsed.path === 'string'
|