@iislee/opencodex 2.31.0 → 2.35.0
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 +3 -3
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -1
- package/src/adapters/anthropic.ts +104 -15
- package/src/adapters/base.ts +29 -1
- package/src/adapters/command-code.ts +42 -6
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +69 -18
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +2 -2
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -2
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +68 -14
- package/src/adapters/cursor/request-builder.ts +40 -10
- package/src/adapters/cursor/tool-definitions.ts +14 -4
- package/src/adapters/cursor/tool-result-normalize.ts +28 -5
- package/src/adapters/cursor/types.ts +20 -0
- package/src/adapters/cursor.ts +209 -38
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +173 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google.ts +136 -24
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-tools.ts +55 -16
- package/src/adapters/kiro.ts +182 -23
- package/src/adapters/openai-chat.ts +300 -242
- package/src/adapters/openai-responses.ts +353 -27
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +4 -3
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +17 -16
- package/src/bridge.ts +90 -37
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/context-windows.ts +16 -9
- package/src/claude/outbound.ts +24 -11
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +116 -19
- package/src/cli/account-extended.ts +262 -29
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +69 -15
- package/src/cli/agent.ts +8 -1
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +256 -28
- package/src/cli/doctor.ts +126 -14
- package/src/cli/help.ts +17 -6
- package/src/cli/index.ts +39 -9
- package/src/cli/init.ts +1 -0
- package/src/cli/inspect.ts +230 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +26 -10
- package/src/cli/observe.ts +31 -8
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/provider.ts +2 -1
- package/src/cli/registry.ts +49 -10
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +41 -1
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +34 -10
- package/src/cli/version-skew.ts +46 -0
- package/src/clients/config-export.ts +85 -3
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +83 -7
- package/src/codex/auth-api.ts +50 -10
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +18 -0
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +72 -27
- package/src/codex/catalog/provider-fetch.ts +187 -31
- package/src/codex/catalog/sync.ts +131 -5
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +27 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +63 -22
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +91 -10
- package/src/codex/routing.ts +9 -0
- package/src/codex/shim.ts +56 -3
- package/src/codex/subagent-model-fallback.ts +104 -25
- package/src/codex/transition-state.ts +116 -8
- package/src/combos/failover.ts +27 -0
- package/src/combos/types.ts +10 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +206 -824
- package/src/generated/compatibility-version.json +373 -213
- package/src/images/loop.ts +5 -4
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +46 -2
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/state.ts +79 -7
- package/src/integrations/writer.ts +26 -2
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +36 -3
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +65 -6
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/local-token-detect.ts +2 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +14 -5
- package/src/oauth/types.ts +15 -0
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/label.ts +34 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +65 -7
- package/src/providers/registry.ts +184 -69
- package/src/providers/slug-codec.ts +52 -0
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +157 -39
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +7 -2
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +5 -1
- package/src/responses/state.ts +94 -4
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +27 -7
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/index.ts +179 -32
- package/src/server/lifecycle.ts +52 -1
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +45 -23
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +59 -12
- package/src/server/management/context.ts +10 -1
- package/src/server/management/logs-usage-routes.ts +30 -24
- package/src/server/management/model-routes.ts +274 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +10 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +81 -11
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/management-api.ts +18 -16
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +29 -5
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +70 -16
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +37 -3
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +6 -21
- package/src/server/responses/core.ts +810 -208
- package/src/server/responses/empty-completion-guard.ts +63 -6
- package/src/server/responses/fetch-helpers.ts +17 -102
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +87 -6
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +14 -2
- package/src/service.ts +42 -4
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/types/config.ts +66 -0
- package/src/types/provider.ts +69 -0
- package/src/types/request.ts +17 -4
- package/src/types/tools.ts +116 -11
- package/src/types.ts +3 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/expected-prices.ts +43 -24
- package/src/usage/log.ts +63 -12
- package/src/usage/summary.ts +211 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-BF38heuV.js +0 -104
- package/gui/dist/assets/index-DMiI18Kv.css +0 -1
package/src/cli/account-main.ts
CHANGED
|
@@ -190,8 +190,8 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
190
190
|
if (args.length > 0 || confirmed || rollback) return reject(args);
|
|
191
191
|
const path = sub === "doctor" ? "/api/native-main-profiles/doctor" : "/api/native-main-profiles";
|
|
192
192
|
const result = await apiJson(deps, baseUrl, "GET", path);
|
|
193
|
-
if (result.status === 0) return proxyUnreachable();
|
|
194
|
-
if (result.status !== 200) return apiError(result.json, `failed to ${sub} native profiles
|
|
193
|
+
if (result.status === 0) return proxyUnreachable(result.transportError);
|
|
194
|
+
if (result.status !== 200) return apiError(result.json, `failed to ${sub} native profiles`, result.status);
|
|
195
195
|
if (wantsJson || sub === "doctor") console.log(JSON.stringify(result.json, null, 2));
|
|
196
196
|
else printProfiles(Array.isArray(result.json.profiles) ? result.json.profiles as PublicProfile[] : []);
|
|
197
197
|
return 0;
|
|
@@ -201,8 +201,8 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
201
201
|
const label = args.shift();
|
|
202
202
|
if (!label || args.length > 0 || confirmed || rollback) return reject(args);
|
|
203
203
|
const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/register", { label });
|
|
204
|
-
if (result.status === 0) return proxyUnreachable();
|
|
205
|
-
if (result.status !== 200) return apiError(result.json, "failed to register the current native login");
|
|
204
|
+
if (result.status === 0) return proxyUnreachable(result.transportError);
|
|
205
|
+
if (result.status !== 200) return apiError(result.json, "failed to register the current native login", result.status);
|
|
206
206
|
if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
|
|
207
207
|
else console.log(`Registered '${label}' for ${effectiveCodexHome(result.json)}.`);
|
|
208
208
|
return 0;
|
|
@@ -212,8 +212,8 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
212
212
|
const label = args.shift();
|
|
213
213
|
if (!label || args.length > 0 || wantsJson || confirmed || rollback) return reject(args);
|
|
214
214
|
const stage = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage", {});
|
|
215
|
-
if (stage.status === 0) return proxyUnreachable();
|
|
216
|
-
if (stage.status !== 200) return apiError(stage.json, "failed to prepare native login staging");
|
|
215
|
+
if (stage.status === 0) return proxyUnreachable(stage.transportError);
|
|
216
|
+
if (stage.status !== 200) return apiError(stage.json, "failed to prepare native login staging", stage.status);
|
|
217
217
|
const stageId = typeof stage.json.stageId === "string" ? stage.json.stageId : "";
|
|
218
218
|
const writerToken = typeof stage.json.writerToken === "string" ? stage.json.writerToken : "";
|
|
219
219
|
const stagingHome = typeof stage.json.stagingCodexHome === "string" ? stage.json.stagingCodexHome : "";
|
|
@@ -258,8 +258,8 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
258
258
|
if (leaseLost) throw new Error("The native-login staging lease was lost before login completed.");
|
|
259
259
|
if (exitCode !== 0) throw new Error("Official Codex login did not complete successfully.");
|
|
260
260
|
const finish = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage/finish", { stageId, writerToken, label });
|
|
261
|
-
if (finish.status === 0) return proxyUnreachable();
|
|
262
|
-
if (finish.status !== 200) return apiError(finish.json, "failed to encrypt the staged native login");
|
|
261
|
+
if (finish.status === 0) return proxyUnreachable(finish.transportError);
|
|
262
|
+
if (finish.status !== 200) return apiError(finish.json, "failed to encrypt the staged native login", finish.status);
|
|
263
263
|
finished = true;
|
|
264
264
|
console.log(`Added encrypted native profile '${label}' for ${effectiveCodexHome(finish.json)}.`);
|
|
265
265
|
return 0;
|
|
@@ -283,8 +283,8 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
283
283
|
return reject(args);
|
|
284
284
|
}
|
|
285
285
|
const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/switch", { target, confirmedStopped: true });
|
|
286
|
-
if (result.status === 0) return proxyUnreachable();
|
|
287
|
-
if (result.status !== 200) return apiError(result.json, "failed to switch the native login");
|
|
286
|
+
if (result.status === 0) return proxyUnreachable(result.transportError);
|
|
287
|
+
if (result.status !== 200) return apiError(result.json, "failed to switch the native login", result.status);
|
|
288
288
|
if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
|
|
289
289
|
else {
|
|
290
290
|
const profile = result.json.activeProfile as PublicProfile | undefined;
|
|
@@ -301,8 +301,8 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
301
301
|
const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/recover", rollback
|
|
302
302
|
? { rollback: true, confirmedStopped: true }
|
|
303
303
|
: { rollback: false });
|
|
304
|
-
if (result.status === 0) return proxyUnreachable();
|
|
305
|
-
if (result.status !== 200) return apiError(result.json, "failed to recover the native-profile transaction");
|
|
304
|
+
if (result.status === 0) return proxyUnreachable(result.transportError);
|
|
305
|
+
if (result.status !== 200) return apiError(result.json, "failed to recover the native-profile transaction", result.status);
|
|
306
306
|
if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
|
|
307
307
|
else {
|
|
308
308
|
const home = effectiveCodexHome(result.json);
|
package/src/cli/account.ts
CHANGED
|
@@ -2,7 +2,20 @@
|
|
|
2
2
|
import { loadConfig } from "../config";
|
|
3
3
|
import { providerCodexAccountMode } from "../providers/registry";
|
|
4
4
|
import type { OcxConfig } from "../types";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
cmdAddKey,
|
|
7
|
+
cmdAlias,
|
|
8
|
+
cmdAutoSwitch,
|
|
9
|
+
cmdClearCooldown,
|
|
10
|
+
cmdImport,
|
|
11
|
+
cmdPause,
|
|
12
|
+
cmdPauseExhausted,
|
|
13
|
+
cmdPriority,
|
|
14
|
+
cmdRefresh,
|
|
15
|
+
cmdRemove,
|
|
16
|
+
cmdSticky,
|
|
17
|
+
cmdStrategy,
|
|
18
|
+
} from "./account-extended";
|
|
6
19
|
import { apiError, apiJson, classifyAccount, fetchRows, proxyUnreachable, resolveBaseUrl, type AccountDeps, type AccountRow, type AccountType, type ApiResult }
|
|
7
20
|
from "./account-api";
|
|
8
21
|
|
|
@@ -16,13 +29,18 @@ const MAIN_CODEX_ID = "__main__";
|
|
|
16
29
|
const REPLACEMENT_STYLE_OAUTH = new Set(["kiro"]);
|
|
17
30
|
|
|
18
31
|
const ACCOUNT_USAGE = `Usage:
|
|
19
|
-
ocx account list [provider] [--json] [--all]
|
|
32
|
+
ocx account list [provider] [--json] [--all] [--quota [--refresh]]
|
|
20
33
|
ocx account current <provider> [--json]
|
|
21
34
|
ocx account use <provider> <account-or-key-id|main> [--json]
|
|
22
35
|
ocx account refresh <provider> [--json]
|
|
23
36
|
ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]
|
|
24
37
|
ocx account alias <provider> <account-or-key-id> <display-name|-> [--json]
|
|
25
38
|
ocx account priority <provider> <account-id|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]
|
|
39
|
+
ocx account pause <provider> <account-id|main> [--json]
|
|
40
|
+
ocx account resume <provider> <account-id|main> [--json]
|
|
41
|
+
ocx account pause-exhausted <provider> [--json]
|
|
42
|
+
ocx account strategy <provider> [<quota|round-robin|fill-first>] [--json]
|
|
43
|
+
ocx account sticky <provider> [<1-100>] [--json]
|
|
26
44
|
ocx account remove <provider> <account-or-key-id|main> --yes [--json]
|
|
27
45
|
ocx account clear-cooldown <provider> <account-id|main> [--json]
|
|
28
46
|
ocx account add-key <provider> [--label <label>] [--json]
|
|
@@ -64,6 +82,10 @@ function displayId(id: string): string {
|
|
|
64
82
|
|
|
65
83
|
function statusText(row: AccountRow): string {
|
|
66
84
|
const parts: string[] = [];
|
|
85
|
+
// `paused` leads, and does NOT replace `selected`. A paused-but-selected account is the
|
|
86
|
+
// state an operator most needs named -- requests route to it while the pool believes it is
|
|
87
|
+
// held out -- so printing only one of the two would hide exactly the confusing case (#2703).
|
|
88
|
+
if (row.paused) parts.push("paused");
|
|
67
89
|
if (row.active) parts.push(row.type === "codex" ? "selected" : "active");
|
|
68
90
|
if (row.needsReauth) parts.push("needs-reauth");
|
|
69
91
|
return parts.join(" ");
|
|
@@ -75,11 +97,29 @@ function priorityText(row: AccountRow): string {
|
|
|
75
97
|
return row.priority > 0 ? `+${row.priority}` : String(row.priority);
|
|
76
98
|
}
|
|
77
99
|
|
|
78
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Compact per-account quota for the opt-in QUOTA column: the two windows an operator actually
|
|
102
|
+
* decides on before a long session. The full breakdown stays in `--json`.
|
|
103
|
+
*/
|
|
104
|
+
function quotaText(row: AccountRow): string {
|
|
105
|
+
if ((row as { quotaUnavailable?: boolean }).quotaUnavailable) return "unavailable";
|
|
106
|
+
const quota = row.quota;
|
|
107
|
+
if (!quota) return "-";
|
|
108
|
+
const parts: string[] = [];
|
|
109
|
+
// Two spellings reach this DTO: the per-account provider probe reports `fiveHourPercent`,
|
|
110
|
+
// while the Codex pool reports the same idea as `shortPercent`.
|
|
111
|
+
const short = quota.fiveHourPercent ?? quota.shortPercent;
|
|
112
|
+
if (typeof short === "number") parts.push(`5h ${short}%`);
|
|
113
|
+
if (typeof quota.weeklyPercent === "number") parts.push(`wk ${quota.weeklyPercent}%`);
|
|
114
|
+
return parts.length > 0 ? parts.join(" ") : "-";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function formatAccountTable(rows: AccountRow[], withQuota = false): string {
|
|
79
118
|
const header = ["PROVIDER", "TYPE", "ID", "PLAN/LABEL", "PRIORITY", "STATUS"];
|
|
119
|
+
if (withQuota) header.push("QUOTA");
|
|
80
120
|
const data = rows.map(r => {
|
|
81
121
|
const keyLabel = r.masked && r.label !== r.masked ? `${r.masked} (${r.label})` : r.masked;
|
|
82
|
-
|
|
122
|
+
const cols = [
|
|
83
123
|
r.provider,
|
|
84
124
|
r.type,
|
|
85
125
|
displayId(r.id),
|
|
@@ -87,6 +127,8 @@ export function formatAccountTable(rows: AccountRow[]): string {
|
|
|
87
127
|
priorityText(r),
|
|
88
128
|
statusText(r),
|
|
89
129
|
];
|
|
130
|
+
if (withQuota) cols.push(quotaText(r));
|
|
131
|
+
return cols;
|
|
90
132
|
});
|
|
91
133
|
const widths = header.map((h, i) => Math.max(h.length, ...data.map(d => d[i]!.length)));
|
|
92
134
|
const line = (cols: string[]) => cols.map((c, i) => c.padEnd(widths[i]!)).join(" ").trimEnd();
|
|
@@ -96,6 +138,10 @@ export function formatAccountTable(rows: AccountRow[]): string {
|
|
|
96
138
|
async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
|
|
97
139
|
const wantsJson = consumeFlag(rest, "--json");
|
|
98
140
|
const showAll = consumeFlag(rest, "--all");
|
|
141
|
+
// Opt-in: the server probes the upstream once per stored credential, so the default listing
|
|
142
|
+
// stays a cheap local read (#2566). --refresh bypasses the server-side TTL.
|
|
143
|
+
const wantsQuota = consumeFlag(rest, "--quota");
|
|
144
|
+
const refreshQuota = consumeFlag(rest, "--refresh");
|
|
99
145
|
const name = rest.shift();
|
|
100
146
|
const leftover = leftoverArgsError(rest);
|
|
101
147
|
if (leftover) {
|
|
@@ -126,8 +172,8 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
126
172
|
};
|
|
127
173
|
push("openai", "codex");
|
|
128
174
|
const providersRes = await apiJson(deps, baseUrl, "GET", "/api/oauth/providers");
|
|
129
|
-
if (providersRes.status === 0) return proxyUnreachable();
|
|
130
|
-
if (providersRes.status !== 200) return apiError(providersRes.json, "failed to list OAuth providers");
|
|
175
|
+
if (providersRes.status === 0) return proxyUnreachable(providersRes.transportError);
|
|
176
|
+
if (providersRes.status !== 200) return apiError(providersRes.json, "failed to list OAuth providers", providersRes.status);
|
|
131
177
|
if (Array.isArray(providersRes.json.providers)) {
|
|
132
178
|
for (const p of providersRes.json.providers) {
|
|
133
179
|
if (typeof p === "string") push(p, "live-oauth-list");
|
|
@@ -139,10 +185,11 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
139
185
|
const rows: AccountRow[] = [];
|
|
140
186
|
const notes: string[] = [];
|
|
141
187
|
for (const t of targets) {
|
|
142
|
-
const r = await fetchRows(deps, baseUrl, t.name, t.type);
|
|
143
|
-
if (r.networkDown) return proxyUnreachable();
|
|
188
|
+
const r = await fetchRows(deps, baseUrl, t.name, t.type, wantsQuota ? { refresh: refreshQuota } : undefined);
|
|
189
|
+
if (r.networkDown) return proxyUnreachable(r.transportError);
|
|
144
190
|
if (r.errorJson) {
|
|
145
|
-
if (name) return apiError(r.errorJson, `failed to list ${t.name}
|
|
191
|
+
if (name) return apiError(r.errorJson, `failed to list ${t.name}`, r.status);
|
|
192
|
+
|
|
146
193
|
const errorText = typeof r.errorJson.error === "string" ? r.errorJson.error : "";
|
|
147
194
|
const skipUnknownKey = t.type === "api-key"
|
|
148
195
|
&& r.status === 404
|
|
@@ -152,7 +199,7 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
152
199
|
&& r.status === 400
|
|
153
200
|
&& errorText.includes("unknown oauth provider");
|
|
154
201
|
if (skipUnknownKey || skipConfigOAuth) continue;
|
|
155
|
-
return apiError(r.errorJson, `failed to list ${t.name}
|
|
202
|
+
return apiError(r.errorJson, `failed to list ${t.name}`, r.status);
|
|
156
203
|
}
|
|
157
204
|
if (r.rows.length === 0) {
|
|
158
205
|
if (showAll) notes.push(`${t.name}: no stored accounts or keys`);
|
|
@@ -174,7 +221,7 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
174
221
|
console.log(JSON.stringify({ accounts: rows, notes }, null, 2));
|
|
175
222
|
return 0;
|
|
176
223
|
}
|
|
177
|
-
if (rows.length > 0) console.log(formatAccountTable(rows));
|
|
224
|
+
if (rows.length > 0) console.log(formatAccountTable(rows, wantsQuota));
|
|
178
225
|
for (const n of notes) console.log(n);
|
|
179
226
|
if (rows.length === 0 && notes.length === 0) console.log("No stored accounts or keys.");
|
|
180
227
|
return 0;
|
|
@@ -198,8 +245,8 @@ async function cmdCurrent(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
198
245
|
const baseUrl = await resolveBaseUrl(deps);
|
|
199
246
|
if (!baseUrl) return proxyUnreachable();
|
|
200
247
|
const r = await fetchRows(deps, baseUrl, name, c.type);
|
|
201
|
-
if (r.networkDown) return proxyUnreachable();
|
|
202
|
-
if (r.errorJson) return apiError(r.errorJson, `failed to read ${name}
|
|
248
|
+
if (r.networkDown) return proxyUnreachable(r.transportError);
|
|
249
|
+
if (r.errorJson) return apiError(r.errorJson, `failed to read ${name}`, r.status);
|
|
203
250
|
|
|
204
251
|
const activeRow = r.rows.find(row => row.active) ?? null;
|
|
205
252
|
if (wantsJson) {
|
|
@@ -253,8 +300,8 @@ async function cmdUse(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
253
300
|
activeId = id;
|
|
254
301
|
res = await apiJson(deps, baseUrl, "PUT", "/api/providers/keys/active", { name, id });
|
|
255
302
|
}
|
|
256
|
-
if (res.status === 0) return proxyUnreachable();
|
|
257
|
-
if (res.status !== 200) return apiError(res.json, `failed to switch ${name}
|
|
303
|
+
if (res.status === 0) return proxyUnreachable(res.transportError);
|
|
304
|
+
if (res.status !== 200) return apiError(res.json, `failed to switch ${name}`, res.status);
|
|
258
305
|
|
|
259
306
|
if (wantsJson) console.log(JSON.stringify({ ok: true, provider: name, type: c.type, activeId }, null, 2));
|
|
260
307
|
else console.log(`${name}: active ${c.type === "api-key" ? "key" : "account"} is now ${displayId(activeId)}`);
|
|
@@ -278,6 +325,13 @@ export async function cmdAccount(args: string[], deps: AccountDeps = {}): Promis
|
|
|
278
325
|
if (sub === "auto-switch") return await cmdAutoSwitch(rest, deps);
|
|
279
326
|
if (sub === "alias" || sub === "rename") return await cmdAlias(rest, deps);
|
|
280
327
|
if (sub === "priority") return await cmdPriority(rest, deps);
|
|
328
|
+
// #2702: the server routes existed and only the CLI caller was missing, so these were
|
|
329
|
+
// dashboard-only capabilities.
|
|
330
|
+
if (sub === "pause") return await cmdPause(rest, deps, true);
|
|
331
|
+
if (sub === "resume") return await cmdPause(rest, deps, false);
|
|
332
|
+
if (sub === "pause-exhausted") return await cmdPauseExhausted(rest, deps);
|
|
333
|
+
if (sub === "strategy") return await cmdStrategy(rest, deps);
|
|
334
|
+
if (sub === "sticky") return await cmdSticky(rest, deps);
|
|
281
335
|
if (sub === "remove") return await cmdRemove(rest, deps);
|
|
282
336
|
if (sub === "clear-cooldown") return await cmdClearCooldown(rest, deps);
|
|
283
337
|
if (sub === "add-key") return await cmdAddKey(rest, deps);
|
package/src/cli/agent.ts
CHANGED
|
@@ -29,7 +29,8 @@ const USAGE = `Usage:
|
|
|
29
29
|
ocx agent fallback <status|set|clear> [model,model...] [--poll-ms <5000-600000>] [--json]
|
|
30
30
|
ocx agent sidecar <status|web|vision> [--list] [--model <id|->]
|
|
31
31
|
[--backend web:<openai|anthropic|xai|gemini|exa|-> vision:<openai|anthropic|routed|->]
|
|
32
|
-
[--reasoning <level>] [--max-descriptions <n>] [--json]
|
|
32
|
+
[--reasoning <level>] [--max-descriptions <n>] [--json]
|
|
33
|
+
ocx agent request-user-input [on|off] [--json]`;
|
|
33
34
|
|
|
34
35
|
function clearable(value: string | undefined): string | null | undefined {
|
|
35
36
|
return value === "-" ? null : value;
|
|
@@ -221,6 +222,12 @@ export async function handleAgentCommand(argv: string[], deps: RuntimeApiDeps =
|
|
|
221
222
|
else if (sub === "subagents" || sub === "roster") await subagents(rest, deps);
|
|
222
223
|
else if (sub === "fallback") await fallback(rest, deps);
|
|
223
224
|
else if (sub === "sidecar") await sidecar(rest, deps);
|
|
225
|
+
// Lives here rather than as a top-level verb because it is an agent-behavior feature flag:
|
|
226
|
+
// it controls whether default mode may ask the operator a question mid-task.
|
|
227
|
+
else if (sub === "request-user-input") {
|
|
228
|
+
const { requestUserInputAction } = await import("./inspect");
|
|
229
|
+
await requestUserInputAction(rest, deps);
|
|
230
|
+
}
|
|
224
231
|
else throw new CliUsageError(`unknown agent command ${sub}`, USAGE);
|
|
225
232
|
});
|
|
226
233
|
}
|
package/src/cli/alias.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CliUsageError, printData, rejectArgs, runtimeRequest, takeFlag, takeOption, type RuntimeApiDeps } from "./runtime-api";
|
|
2
|
+
|
|
3
|
+
const USAGE = `Usage:
|
|
4
|
+
ocx alias list [--json]
|
|
5
|
+
ocx alias set <provider> <alias>
|
|
6
|
+
ocx alias set <provider>/<native-model-id> <alias>
|
|
7
|
+
ocx alias rm <provider>[/<native-model-id>]
|
|
8
|
+
ocx alias defaults <on|off> [--provider <name>]`;
|
|
9
|
+
|
|
10
|
+
function selector(value: string): { provider: string; model?: string } {
|
|
11
|
+
const slash = value.indexOf("/");
|
|
12
|
+
return slash < 0 ? { provider: value } : { provider: value.slice(0, slash), model: value.slice(slash + 1) };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function handleAliasCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
16
|
+
const args = [...argv];
|
|
17
|
+
const action = (args.shift() ?? "list").toLowerCase();
|
|
18
|
+
const wantsJson = takeFlag(args, "--json");
|
|
19
|
+
if (action === "list") {
|
|
20
|
+
rejectArgs(args, USAGE);
|
|
21
|
+
const result = await runtimeRequest<Record<string, unknown>>("/api/aliases", {}, deps);
|
|
22
|
+
const lines: string[] = [];
|
|
23
|
+
for (const [target, alias] of Object.entries((result.providers ?? {}) as Record<string, string>)) lines.push(`provider ${target} ${alias} user`);
|
|
24
|
+
for (const [provider, rows] of Object.entries((result.models ?? {}) as Record<string, Record<string, { alias: string; source: string }>>)) {
|
|
25
|
+
for (const [model, value] of Object.entries(rows)) lines.push(`model ${provider}/${model} ${value.alias} ${value.source}`);
|
|
26
|
+
}
|
|
27
|
+
printData(result, wantsJson, lines.length ? lines : ["No aliases configured."]);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
if (action === "defaults") {
|
|
31
|
+
const state = args.shift()?.toLowerCase();
|
|
32
|
+
const provider = takeOption(args, "--provider");
|
|
33
|
+
rejectArgs(args, USAGE);
|
|
34
|
+
if (state !== "on" && state !== "off") throw new CliUsageError("defaults requires on or off", USAGE);
|
|
35
|
+
const result = await runtimeRequest("/api/default-aliases", { method: "PUT", body: JSON.stringify({ enabled: state === "on", ...(provider ? { provider } : {}) }) }, deps);
|
|
36
|
+
printData(result, wantsJson, [`Default aliases ${state}${provider ? ` for ${provider}` : " globally"}.`]);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
const target = args.shift()?.trim();
|
|
40
|
+
if (!target) throw new CliUsageError("alias target is required", USAGE);
|
|
41
|
+
const parsed = selector(target);
|
|
42
|
+
if (!parsed.provider || parsed.model === "") throw new CliUsageError("target must be provider or provider/native-model-id", USAGE);
|
|
43
|
+
if (action === "set") {
|
|
44
|
+
const alias = args.shift()?.trim();
|
|
45
|
+
rejectArgs(args, USAGE);
|
|
46
|
+
if (!alias) throw new CliUsageError("alias value is required", USAGE);
|
|
47
|
+
const path = parsed.model === undefined
|
|
48
|
+
? `/api/providers/${encodeURIComponent(parsed.provider)}/alias`
|
|
49
|
+
: `/api/providers/${encodeURIComponent(parsed.provider)}/model-aliases`;
|
|
50
|
+
const body = parsed.model === undefined ? { alias } : { set: { [parsed.model]: alias } };
|
|
51
|
+
const result = await runtimeRequest(path, { method: "PUT", body: JSON.stringify(body) }, deps);
|
|
52
|
+
printData(result, wantsJson, [`${target} → ${alias}`]);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
if (action === "rm") {
|
|
56
|
+
rejectArgs(args, USAGE);
|
|
57
|
+
const path = parsed.model === undefined
|
|
58
|
+
? `/api/providers/${encodeURIComponent(parsed.provider)}/alias`
|
|
59
|
+
: `/api/providers/${encodeURIComponent(parsed.provider)}/model-aliases`;
|
|
60
|
+
const body = parsed.model === undefined ? { alias: null } : { remove: [parsed.model] };
|
|
61
|
+
const result = await runtimeRequest(path, { method: "PUT", body: JSON.stringify(body) }, deps);
|
|
62
|
+
printData(result, wantsJson, [`Removed alias for ${target}.`]);
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
throw new CliUsageError(`unknown alias action '${action}'`, USAGE);
|
|
66
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ocx capabilities` -- the surface index an agent reads first.
|
|
3
|
+
*
|
|
4
|
+
* The point of this verb is that driving `ocx` programmatically should not require
|
|
5
|
+
* parsing help text. `--json` emits the capability table with the management routes each
|
|
6
|
+
* capability drives; `--route` answers the inverse question.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
CAPABILITIES,
|
|
10
|
+
HEAD_CAPABILITIES,
|
|
11
|
+
capabilitiesForRoute,
|
|
12
|
+
capabilityInvocation,
|
|
13
|
+
type Capability,
|
|
14
|
+
} from "./capabilities";
|
|
15
|
+
import { takeFlag } from "./runtime-api";
|
|
16
|
+
|
|
17
|
+
function takeValueFlag(args: string[], flag: string): string | undefined {
|
|
18
|
+
// Order-independent by construction: scan for the flag anywhere in argv rather than
|
|
19
|
+
// reading a fixed position. Positional flag handling is exactly why
|
|
20
|
+
// `ocx restore back --json` ignored its flag.
|
|
21
|
+
const idx = args.indexOf(flag);
|
|
22
|
+
if (idx === -1) return undefined;
|
|
23
|
+
const value = args[idx + 1];
|
|
24
|
+
args.splice(idx, value === undefined || value.startsWith("-") ? 1 : 2);
|
|
25
|
+
if (value === undefined || value.startsWith("-")) return "";
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function renderHuman(caps: readonly Capability[], includeHead: boolean): void {
|
|
30
|
+
for (const cap of caps) {
|
|
31
|
+
const marker = cap.mutates ? "!" : " ";
|
|
32
|
+
console.log(`${marker} ${capabilityInvocation(cap)}`);
|
|
33
|
+
console.log(` ${cap.summary}`);
|
|
34
|
+
if (cap.routes.length > 0) {
|
|
35
|
+
console.log(` routes: ${cap.routes.map(r => `${r.method} ${r.path}`).join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
if (cap.flags.length > 0) {
|
|
38
|
+
console.log(` flags: ${cap.flags.map(f => f.name).join(" ")}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!includeHead) return;
|
|
42
|
+
for (const head of HEAD_CAPABILITIES) {
|
|
43
|
+
console.log(` ocx ${head.invocations[0]}`);
|
|
44
|
+
console.log(` ${head.summary}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runCapabilities(argv: string[]): Promise<number> {
|
|
49
|
+
const args = [...argv];
|
|
50
|
+
const json = takeFlag(args, "--json");
|
|
51
|
+
const mutatingOnly = takeFlag(args, "--mutating-only");
|
|
52
|
+
const route = takeValueFlag(args, "--route");
|
|
53
|
+
|
|
54
|
+
if (route !== undefined && route.length === 0) {
|
|
55
|
+
console.error("Usage: ocx capabilities --route <path>");
|
|
56
|
+
return 64;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let selected: readonly Capability[] = route === undefined ? CAPABILITIES : capabilitiesForRoute(route);
|
|
60
|
+
if (mutatingOnly) selected = selected.filter(cap => cap.mutates);
|
|
61
|
+
|
|
62
|
+
if (route !== undefined && selected.length === 0) {
|
|
63
|
+
// An unmatched route is a real answer, not an error: the route may be exempt or may
|
|
64
|
+
// not exist. Say which, rather than exiting 0 with silence.
|
|
65
|
+
if (json) {
|
|
66
|
+
console.log(JSON.stringify({ schemaVersion: 1, route, capabilities: [] }, null, 2));
|
|
67
|
+
} else {
|
|
68
|
+
console.error(`No CLI capability drives ${route}.`);
|
|
69
|
+
}
|
|
70
|
+
return 4;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (json) {
|
|
74
|
+
console.log(JSON.stringify({
|
|
75
|
+
schemaVersion: 1,
|
|
76
|
+
...(route === undefined ? {} : { route }),
|
|
77
|
+
capabilities: selected.map(cap => ({
|
|
78
|
+
command: cap.command,
|
|
79
|
+
invocation: capabilityInvocation(cap),
|
|
80
|
+
summary: cap.summary,
|
|
81
|
+
routes: cap.routes,
|
|
82
|
+
flags: cap.flags,
|
|
83
|
+
mutates: cap.mutates,
|
|
84
|
+
json: cap.json,
|
|
85
|
+
...(cap.details ? { details: cap.details } : {}),
|
|
86
|
+
})),
|
|
87
|
+
...(route === undefined && !mutatingOnly ? { headCapabilities: HEAD_CAPABILITIES } : {}),
|
|
88
|
+
}, null, 2));
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
renderHuman(selected, route === undefined && !mutatingOnly);
|
|
93
|
+
return 0;
|
|
94
|
+
}
|