@bitkyc08/opencodex 2.52.0-preview.20260911 → 2.52.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/gui/dist/assets/index-CWXut3rG.js +115 -0
- package/gui/dist/assets/index-EdoPnm9_.css +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/devin.svg +49 -0
- package/gui/dist/provider-icons/omo.svg +42 -0
- package/package.json +3 -1
- package/src/AGENTS.md +1 -1
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +0 -1
- package/src/adapters/command-code.ts +0 -1
- package/src/adapters/cursor/checkpoint-store.ts +37 -0
- package/src/adapters/cursor/live-transport.ts +74 -2
- package/src/adapters/cursor.ts +6 -0
- package/src/adapters/devin/cloud-direct/auth.ts +264 -0
- package/src/adapters/devin/cloud-direct/catalog.ts +306 -0
- package/src/adapters/devin/cloud-direct/chat.ts +1274 -0
- package/src/adapters/devin/cloud-direct/index.ts +65 -0
- package/src/adapters/devin/cloud-direct/metadata.ts +134 -0
- package/src/adapters/devin/cloud-direct/wire.ts +206 -0
- package/src/adapters/devin/live-models.ts +133 -0
- package/src/adapters/devin-cli/acp.ts +204 -0
- package/src/adapters/devin-cli/adapter.ts +345 -0
- package/src/adapters/devin-cli/binary.ts +69 -0
- package/src/adapters/devin-cli/models.ts +57 -0
- package/src/adapters/devin.ts +326 -0
- package/src/adapters/google.ts +1 -1
- package/src/adapters/openai-chat.ts +31 -10
- package/src/adapters/openai-responses.ts +16 -1
- package/src/adapters/registry.ts +25 -1
- package/src/bridge.ts +8 -2
- package/src/claude/inbound-cache-stabilize.ts +130 -0
- package/src/claude/inbound.ts +45 -5
- package/src/cli/account-auth.ts +60 -1
- package/src/cli/account-extended.ts +23 -30
- package/src/cli/account.ts +2 -1
- package/src/cli/capabilities.ts +50 -8
- package/src/cli/config-command.ts +2 -2
- package/src/cli/dispatch.ts +14 -2
- package/src/cli/export-command.ts +11 -25
- package/src/cli/help.ts +2 -2
- package/src/cli/opencode.ts +5 -0
- package/src/cli/registry.ts +15 -4
- package/src/clients/config-export/cline.ts +71 -0
- package/src/clients/config-export/contracts.ts +10 -1
- package/src/clients/config-export/model-metadata.ts +33 -0
- package/src/clients/config-export/zcode.ts +23 -12
- package/src/clients/config-export.ts +156 -4
- package/src/codex/account-store.ts +7 -2
- package/src/codex/auth-context.ts +1 -1
- package/src/codex/catalog/effort.ts +4 -3
- package/src/codex/catalog/metadata.ts +8 -4
- package/src/codex/catalog/native-models.ts +24 -4
- package/src/codex/catalog/parsing.ts +19 -1
- package/src/codex/catalog/provider-fetch.ts +57 -0
- package/src/codex/catalog/sync.ts +1 -1
- package/src/codex/context-compat.ts +97 -0
- package/src/codex/context-owner.ts +201 -0
- package/src/codex/history-provider.ts +161 -14
- package/src/codex/inject-coordination.ts +3 -2
- package/src/codex/inject.ts +128 -9
- package/src/codex/pool-rotation.ts +8 -292
- package/src/codex/quota.ts +41 -12
- package/src/codex/retired-model-migration.ts +41 -0
- package/src/codex/routing.ts +327 -22
- package/src/codex/warmup.ts +2 -2
- package/src/combos/failover.ts +5 -0
- package/src/combos/resolve.ts +11 -15
- package/src/config.ts +46 -14
- package/src/generated/compatibility-version.json +293 -113
- package/src/grok/grpc-web.ts +120 -0
- package/src/grok/reset-coupon-ledger.ts +139 -0
- package/src/grok/reset-coupons.ts +278 -0
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/cline-document.ts +73 -0
- package/src/integrations/cline-io.ts +149 -0
- package/src/integrations/cline-transaction.ts +42 -0
- package/src/integrations/config-io.ts +21 -2
- package/src/integrations/journal.ts +43 -1
- package/src/integrations/omp-yaml-source.ts +123 -4
- package/src/integrations/ownership-policy.ts +7 -0
- package/src/integrations/ownership.ts +36 -0
- package/src/integrations/registry.ts +27 -0
- package/src/integrations/state.ts +5 -2
- package/src/integrations/store.ts +6 -0
- package/src/integrations/writer.ts +53 -9
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/lib/abort.ts +36 -0
- package/src/lib/local-destinations.ts +1 -1
- package/src/lib/upstream-retry.ts +36 -1
- package/src/oauth/account-quota-rank.ts +11 -0
- package/src/oauth/callback-server.ts +10 -5
- package/src/oauth/devin/api-base.ts +63 -0
- package/src/oauth/devin/login.ts +1 -0
- package/src/oauth/devin/register-user.ts +186 -0
- package/src/oauth/devin/types.ts +71 -0
- package/src/oauth/devin-cli.ts +149 -0
- package/src/oauth/devin.ts +170 -0
- package/src/oauth/generic-account-failover.ts +169 -1
- package/src/oauth/index.ts +20 -1
- package/src/oauth/login-cli.ts +19 -5
- package/src/oauth/pool-kernel.ts +321 -0
- package/src/oauth/pool-settings-capability.ts +127 -9
- package/src/oauth/store.ts +7 -3
- package/src/oauth/token-guardian.ts +1 -1
- package/src/providers/codebuddy-models.ts +0 -3
- package/src/providers/command-code-efforts.ts +23 -4
- package/src/providers/default-aliases.ts +4 -0
- package/src/providers/derive.ts +8 -0
- package/src/providers/devin-cli-authmode-migration.ts +57 -0
- package/src/providers/key-failover.ts +175 -0
- package/src/providers/model-rename-startup.ts +21 -1
- package/src/providers/qoder-models.ts +0 -1
- package/src/providers/quota-key-accounts.ts +50 -0
- package/src/providers/quota-routing-cache.ts +65 -6
- package/src/providers/quota-types.ts +7 -0
- package/src/providers/quota.ts +113 -30
- package/src/providers/registry.ts +237 -77
- package/src/providers/stale-context-window-migration.ts +92 -0
- package/src/providers/zai-responses-migration.ts +45 -0
- package/src/quota/reset-observer.ts +2 -1
- package/src/quota/reset-seen-store.ts +9 -1
- package/src/remote-control/crypto.ts +442 -0
- package/src/remote-control/host.ts +175 -0
- package/src/remote-control/index.ts +100 -0
- package/src/remote-control/protocol.ts +200 -0
- package/src/remote-control/relay.ts +162 -0
- package/src/remote-control/workspace-agent-protocol.ts +246 -0
- package/src/remote-control/workspace-rpc-framing.ts +128 -0
- package/src/remote-control/workspace-tools.ts +237 -0
- package/src/remote-control/workspace-utf8.ts +24 -0
- package/src/responses/custom-tool-compat.ts +23 -0
- package/src/router.ts +6 -1
- package/src/routing/compatibility/behavior.ts +3 -0
- package/src/server/adapter-resolve.ts +6 -2
- package/src/server/auth-cors.ts +71 -6
- package/src/server/chat-completions.ts +5 -3
- package/src/server/chat-native.ts +9 -0
- package/src/server/claude-messages.ts +25 -30
- package/src/server/context-history.ts +207 -0
- package/src/server/images.ts +28 -1
- package/src/server/index.ts +42 -22
- package/src/server/live.ts +2 -1
- package/src/server/management/agent-settings-routes.ts +7 -1
- package/src/server/management/config-routes.ts +3 -3
- package/src/server/management/grok-coupon-routes.ts +287 -0
- package/src/server/management/integration-routes.ts +6 -1
- package/src/server/management/oauth-account-routes.ts +124 -7
- package/src/server/management/provider-routes.ts +77 -2
- package/src/server/management/route-registry.ts +6 -1
- package/src/server/management-api.ts +7 -0
- package/src/server/request-log.ts +4 -0
- package/src/server/responses/codex-ws-exchange.ts +90 -14
- package/src/server/responses/codex-ws-wire.ts +51 -1
- package/src/server/responses/compact.ts +8 -1
- package/src/server/responses/core.ts +188 -22
- package/src/server/responses/ws-upstream.ts +1 -1
- package/src/server/responses-undeclared-tool-guard.ts +261 -12
- package/src/server/zai-responses-startup.ts +21 -0
- package/src/types/config.ts +33 -3
- package/src/types/provider.ts +54 -4
- package/src/types/request.ts +1 -1
- package/src/types/tools.ts +36 -6
- package/src/update/job.ts +33 -11
- package/src/vision/plan.ts +40 -37
- package/src/web-search/index.ts +1 -0
- package/gui/dist/assets/index-BoBRSehJ.css +0 -1
- package/gui/dist/assets/index-Dx0xv2EA.js +0 -115
package/src/claude/inbound.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { resolveInboundModel, effortForThinkingBudget, effortFromOutputConfig, e
|
|
|
17
17
|
import { AnthropicRequestError, isRec, type Rec } from "./inbound-records";
|
|
18
18
|
import { resolveInboundModel, effortForThinkingBudget, effortFromOutputConfig, formatFromOutputConfig } from "./inbound-model-options";
|
|
19
19
|
import { systemToInstructions, toolsToResponses, toolChoiceToResponses } from "./inbound-content-options";
|
|
20
|
+
import { stabilizeClaudeInstructionsForPromptCache } from "./inbound-cache-stabilize";
|
|
20
21
|
import { decodeReasoningEnvelope, encodeReasoningEnvelope, OCX_REASONING_PREFIX } from "../responses/reasoning-envelope";
|
|
21
22
|
import { createTranslatorBudget, type TranslatorBudget } from "../lib/translator-budget";
|
|
22
23
|
|
|
@@ -298,7 +299,10 @@ export interface ClaudeInboundTranslation {
|
|
|
298
299
|
* Translate an Anthropic Messages request body into a /v1/responses request body.
|
|
299
300
|
* Throws AnthropicRequestError (-> 400 invalid_request_error) on malformed input.
|
|
300
301
|
*/
|
|
301
|
-
export function anthropicToResponsesBody(
|
|
302
|
+
export function anthropicToResponsesBody(
|
|
303
|
+
raw: unknown,
|
|
304
|
+
cc?: OcxClaudeCodeConfig,
|
|
305
|
+
): Rec {
|
|
302
306
|
return anthropicToResponsesTranslation(raw, cc).body;
|
|
303
307
|
}
|
|
304
308
|
|
|
@@ -307,7 +311,11 @@ export function anthropicToResponsesBody(raw: unknown, cc?: OcxClaudeCodeConfig)
|
|
|
307
311
|
* OUT-OF-BODY tuple (audit 133 R3#1 — an in-body marker would leak upstream through
|
|
308
312
|
* the native Responses forward and 400).
|
|
309
313
|
*/
|
|
310
|
-
export function anthropicToResponsesTranslation(
|
|
314
|
+
export function anthropicToResponsesTranslation(
|
|
315
|
+
raw: unknown,
|
|
316
|
+
cc?: OcxClaudeCodeConfig,
|
|
317
|
+
budget?: TranslatorBudget,
|
|
318
|
+
): ClaudeInboundTranslation {
|
|
311
319
|
const activeBudget = budget ?? createTranslatorBudget();
|
|
312
320
|
try {
|
|
313
321
|
return translateAnthropicRequest(raw, cc, activeBudget);
|
|
@@ -316,7 +324,11 @@ export function anthropicToResponsesTranslation(raw: unknown, cc?: OcxClaudeCode
|
|
|
316
324
|
}
|
|
317
325
|
}
|
|
318
326
|
|
|
319
|
-
function translateAnthropicRequest(
|
|
327
|
+
function translateAnthropicRequest(
|
|
328
|
+
raw: unknown,
|
|
329
|
+
cc: OcxClaudeCodeConfig | undefined,
|
|
330
|
+
budget: TranslatorBudget,
|
|
331
|
+
): ClaudeInboundTranslation {
|
|
320
332
|
if (!isRec(raw)) throw new AnthropicRequestError("request body must be a JSON object");
|
|
321
333
|
if (typeof raw.model !== "string" || raw.model.length === 0) {
|
|
322
334
|
throw new AnthropicRequestError("model is required");
|
|
@@ -357,7 +369,32 @@ function translateAnthropicRequest(raw: unknown, cc: OcxClaudeCodeConfig | undef
|
|
|
357
369
|
stream: raw.stream === true,
|
|
358
370
|
};
|
|
359
371
|
|
|
360
|
-
|
|
372
|
+
const joinedSystem = systemParts.length > 0 ? systemParts.join("\n\n") : "";
|
|
373
|
+
const stabilizePromptCache = cc?.stabilizePromptCache === true;
|
|
374
|
+
// Desktop fallback hashes raw systemParts unless the caller opted into
|
|
375
|
+
// harness cleanup. Opt-in then hashes the same string as body.instructions.
|
|
376
|
+
let cacheSystem: string | string[] = systemParts;
|
|
377
|
+
if (joinedSystem) {
|
|
378
|
+
if (stabilizePromptCache) {
|
|
379
|
+
// Claude Code appends growing <total_tokens>N tokens left</total_tokens>
|
|
380
|
+
// footers (and occasional TaskCreate nudges) into system text. That churn
|
|
381
|
+
// breaks Muse/Go prefix cache on the Responses instructions prefix even
|
|
382
|
+
// when tools stay stable. Relocation is caller-opted, not inferred from
|
|
383
|
+
// a matching suffix or metadata.user_id.
|
|
384
|
+
const stabilized = stabilizeClaudeInstructionsForPromptCache(joinedSystem);
|
|
385
|
+
if (stabilized.instructions) body.instructions = stabilized.instructions;
|
|
386
|
+
if (stabilized.dynamicNotice) {
|
|
387
|
+
input.push({
|
|
388
|
+
type: "message",
|
|
389
|
+
role: "user",
|
|
390
|
+
content: [{ type: "input_text", text: stabilized.dynamicNotice }],
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
cacheSystem = stabilized.instructions;
|
|
394
|
+
} else {
|
|
395
|
+
body.instructions = joinedSystem;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
361
398
|
|
|
362
399
|
const tools = toolsToResponses(raw.tools);
|
|
363
400
|
if (tools) body.tools = tools;
|
|
@@ -395,11 +432,14 @@ function translateAnthropicRequest(raw: unknown, cc: OcxClaudeCodeConfig | undef
|
|
|
395
432
|
// Exact-prefix matching still isolates content; the key only steers routing
|
|
396
433
|
// affinity. Callers must NOT synthesize a session_id header from this fallback
|
|
397
434
|
// (audit 133 R2#3).
|
|
435
|
+
// Outside opt-in, hash the raw systemParts array (pre-stabilize Desktop
|
|
436
|
+
// key). Opt-in hashes the same string used for body.instructions so the
|
|
437
|
+
// key tracks the cacheable prefix after peel.
|
|
398
438
|
body.prompt_cache_key = createHash("sha256")
|
|
399
439
|
.update(canonicalJson({
|
|
400
440
|
version: 2,
|
|
401
441
|
model: body.model,
|
|
402
|
-
system:
|
|
442
|
+
system: cacheSystem,
|
|
403
443
|
tools: Array.isArray(body.tools) ? body.tools : [],
|
|
404
444
|
}))
|
|
405
445
|
.digest("hex").slice(0, 32);
|
package/src/cli/account-auth.ts
CHANGED
|
@@ -37,6 +37,7 @@ const USAGE = `Usage:
|
|
|
37
37
|
ocx account code <provider> [--flow <flow-id>] [--json] (reads the code from stdin)
|
|
38
38
|
ocx account cancel <provider> [--flow <flow-id>] [--json]
|
|
39
39
|
ocx account reset-credits <account-id|main> [--consume --yes [--operation-id <uuid>]] [--json]
|
|
40
|
+
ocx account grok-reset-coupons [<account-id>] [--consume --yes [--token-id <token-id>] [--operation-id <uuid>]] [--json]
|
|
40
41
|
|
|
41
42
|
--device runs the OpenAI device-code login instead of the browser callback: use
|
|
42
43
|
it when the proxy has no browser or nothing can reach localhost:1455, such as a
|
|
@@ -49,8 +50,20 @@ visible to anyone who can run ps:
|
|
|
49
50
|
pbpaste | ocx account code <provider> --flow <flow-id>
|
|
50
51
|
ocx account login <provider> --code - (same, for the login flow)`;
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* The Codex account pool answers to three spellings, and a user reaches for whichever
|
|
55
|
+
* one they already have a word for. `ocx login codex` routes here as well (dispatch.ts):
|
|
56
|
+
* the pool is deliberately not an `ocx login` provider -- it keeps its own account
|
|
57
|
+
* ledger and runs its browser flow inside the proxy -- but that is an implementation
|
|
58
|
+
* boundary, not something a user should have to know before they can log in.
|
|
59
|
+
*/
|
|
52
60
|
const CODEX_NAMES = new Set(["openai", "codex", "chatgpt"]);
|
|
53
61
|
|
|
62
|
+
/** True for every spelling that means "the Codex account pool" rather than an OAuth provider. */
|
|
63
|
+
export function isCodexAccountLoginName(name: string): boolean {
|
|
64
|
+
return CODEX_NAMES.has(name.trim().toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
interface LoginStart {
|
|
55
68
|
url?: string;
|
|
56
69
|
flowId?: string;
|
|
@@ -100,7 +113,12 @@ async function login(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
100
113
|
const id = takeOption(args, "--id");
|
|
101
114
|
const suppliedCode = takeOptionWithSyntax(args, "--code");
|
|
102
115
|
if (!provider) throw new CliUsageError("provider is required", USAGE);
|
|
103
|
-
|
|
116
|
+
// A bare leftover here is plausibly the authorization code itself: this flow takes one
|
|
117
|
+
// through --code, and a user who pastes it as a positional would otherwise see it echoed
|
|
118
|
+
// back in the usage error. `ocx login codex` reaches this parser too, so the paste lands
|
|
119
|
+
// one word away from a command people run constantly. Flag-shaped leftovers stay visible,
|
|
120
|
+
// because a mistyped flag is exactly what the message has to name.
|
|
121
|
+
rejectArgs(args, USAGE, { redactValues: true });
|
|
104
122
|
// kimi, nous, and github-copilot are already device flows, so --device is a
|
|
105
123
|
// true statement about them and is accepted as a no-op rather than an error.
|
|
106
124
|
// Anything else has no device grant at all and must fail loudly.
|
|
@@ -284,12 +302,53 @@ async function resetCredits(argv: string[], deps: RuntimeApiDeps): Promise<void>
|
|
|
284
302
|
printData(result, wantsJson);
|
|
285
303
|
}
|
|
286
304
|
|
|
305
|
+
async function grokResetCoupons(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
306
|
+
const args = [...argv];
|
|
307
|
+
// The account id is optional here (the server falls back to the selected xAI
|
|
308
|
+
// account), so a flag-shaped first token must not be swallowed as the id:
|
|
309
|
+
// `grok-reset-coupons --consume` has to reach the --yes gate, not become a
|
|
310
|
+
// read of account "--consume".
|
|
311
|
+
const rawId = args[0]?.startsWith("--") ? undefined : args.shift()?.trim();
|
|
312
|
+
const wantsJson = takeFlag(args, "--json");
|
|
313
|
+
const consume = takeFlag(args, "--consume");
|
|
314
|
+
const yes = takeFlag(args, "--yes");
|
|
315
|
+
// Before rejectArgs: takeOption splices its two tokens out of `args`.
|
|
316
|
+
const tokenId = takeOption(args, "--token-id");
|
|
317
|
+
const operationId = takeOption(args, "--operation-id");
|
|
318
|
+
if (consume && !yes) throw new CliUsageError("consuming a Grok reset coupon requires --yes", USAGE);
|
|
319
|
+
if (operationId !== undefined && !consume) {
|
|
320
|
+
throw new CliUsageError("--operation-id requires --consume", USAGE);
|
|
321
|
+
}
|
|
322
|
+
if (tokenId !== undefined && !consume) {
|
|
323
|
+
throw new CliUsageError("--token-id requires --consume", USAGE);
|
|
324
|
+
}
|
|
325
|
+
if (operationId !== undefined && !isCodexResetCreditOperationId(operationId)) {
|
|
326
|
+
throw new CliUsageError("--operation-id must be a UUIDv4", USAGE);
|
|
327
|
+
}
|
|
328
|
+
rejectArgs(args, USAGE);
|
|
329
|
+
const accountId = rawId ? (rawId === "main" ? "__main__" : rawId) : undefined;
|
|
330
|
+
const result = consume
|
|
331
|
+
? await runtimeRequest("/api/grok/reset-coupons/consume", {
|
|
332
|
+
method: "POST",
|
|
333
|
+
// Spread, not `operationId: undefined`: the server distinguishes an absent
|
|
334
|
+
// key from a caller who asked for a stable idempotency identity.
|
|
335
|
+
body: JSON.stringify({ accountId, tokenId, ...(operationId === undefined ? {} : { operationId }) }),
|
|
336
|
+
}, deps)
|
|
337
|
+
: await runtimeRequest(
|
|
338
|
+
`/api/grok/reset-coupons${accountId ? `?accountId=${encodeURIComponent(accountId)}` : ""}`,
|
|
339
|
+
{},
|
|
340
|
+
deps,
|
|
341
|
+
);
|
|
342
|
+
printData(result, wantsJson);
|
|
343
|
+
}
|
|
344
|
+
|
|
287
345
|
export async function handleAccountAuthCommand(sub: string, argv: string[], deps: RuntimeApiDeps = {}): Promise<number | null> {
|
|
288
346
|
let action: (() => Promise<void>) | undefined;
|
|
289
347
|
if (sub === "login" || sub === "reauth") action = () => login(sub === "reauth" ? [...argv, "--reauth"] : argv, deps);
|
|
290
348
|
else if (sub === "code") action = () => code(argv, deps);
|
|
291
349
|
else if (sub === "cancel") action = () => cancel(argv, deps);
|
|
292
350
|
else if (sub === "reset-credits") action = () => resetCredits(argv, deps);
|
|
351
|
+
else if (sub === "grok-reset-coupons") action = () => grokResetCoupons(argv, deps);
|
|
293
352
|
if (!action) return null;
|
|
294
353
|
return runCliAction(action);
|
|
295
354
|
}
|
|
@@ -399,14 +399,22 @@ export async function cmdAutoSwitch(args: string[], deps: AccountDeps): Promise<
|
|
|
399
399
|
const storedThreshold = typeof stored === "number" && Number.isInteger(stored) && stored >= 0 && stored <= 100
|
|
400
400
|
? stored : null;
|
|
401
401
|
const poolEnabled = typeof settings.enabled === "boolean" ? settings.enabled : null;
|
|
402
|
-
|
|
403
|
-
//
|
|
404
|
-
|
|
402
|
+
// Three states, not two. `true` is stored-but-not-applied, `false` is applied by the
|
|
403
|
+
// shared kernel, and absent is a server that does not speak this field at all. Collapsing
|
|
404
|
+
// false into absent would render the live feature as an unknown capability.
|
|
405
|
+
const inert = typeof settings.inert === "boolean" ? settings.inert : null;
|
|
406
|
+
// A stored threshold only steers selection once the pool consumes it, which is exactly
|
|
407
|
+
// what `inert: false` reports.
|
|
408
|
+
const enabled = inert === false && storedThreshold !== null;
|
|
405
409
|
if (wantsJson) {
|
|
406
410
|
console.log(JSON.stringify({ provider: name, autoSwitchThreshold: storedThreshold, enabled, poolEnabled, inert }, null, 2));
|
|
407
411
|
} else {
|
|
408
412
|
const value = storedThreshold === null ? "unset" : `${storedThreshold}%`;
|
|
409
|
-
|
|
413
|
+
const state = inert === false ? (enabled ? "on" : "off") : inert === true ? "inactive" : "unavailable";
|
|
414
|
+
const why = inert === false
|
|
415
|
+
? (enabled ? "applied by this pool" : "no threshold stored")
|
|
416
|
+
: inert === true ? "not applied by this pool" : "threshold support is unknown";
|
|
417
|
+
console.log(`auto-switch: ${state} (stored threshold ${value}; ${why})`);
|
|
410
418
|
}
|
|
411
419
|
return 0;
|
|
412
420
|
}
|
|
@@ -839,21 +847,15 @@ export async function cmdPauseExhausted(args: string[], deps: AccountDeps): Prom
|
|
|
839
847
|
}
|
|
840
848
|
|
|
841
849
|
/**
|
|
842
|
-
*
|
|
850
|
+
* One transport, because there is now one contract.
|
|
843
851
|
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
* | body | bare field | field **plus** a mandatory `provider` |
|
|
852
|
+
* This used to be a table of the differences between the Codex and Anthropic pools -- different
|
|
853
|
+
* read path, different write path, different response keys, and a `provider` field mandatory on
|
|
854
|
+
* one body and forbidden on the other. That table existed only because the two contracts
|
|
855
|
+
* disagreed; `/api/pool/settings` answers with the same keys for every kind, so the table
|
|
856
|
+
* collapses to a single shape and the asymmetry it encoded is gone rather than relocated.
|
|
850
857
|
*
|
|
851
|
-
*
|
|
852
|
-
* (`oauth-account-routes.ts:344`), so the asymmetry has to be encoded somewhere. Encoding it
|
|
853
|
-
* here keeps ONE verb pair working on both pools. The alternative the plan left open -- a second
|
|
854
|
-
* `provider-strategy`/`provider-sticky` pair -- would double the surface an operator must learn
|
|
855
|
-
* to express one idea, and a CLI that can steer one pool and not the other is exactly the trap
|
|
856
|
-
* this unit exists to remove.
|
|
858
|
+
* The legacy paths still work and still have their own goldens. Nothing here reads them.
|
|
857
859
|
*/
|
|
858
860
|
interface PoolTransport {
|
|
859
861
|
readPath: string;
|
|
@@ -865,18 +867,10 @@ interface PoolTransport {
|
|
|
865
867
|
writeBody: (field: "strategy" | "stickyLimit", value: unknown) => Record<string, unknown>;
|
|
866
868
|
}
|
|
867
869
|
|
|
868
|
-
|
|
869
|
-
readPath: "/api/codex-auth/active",
|
|
870
|
-
writePath: "/api/codex-auth/pool-strategy",
|
|
871
|
-
strategyKey: "accountPoolStrategy",
|
|
872
|
-
stickyKey: "accountPoolStickyLimit",
|
|
873
|
-
writeBody: (field, value) => ({ [field]: value }),
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
function anthropicPoolTransport(provider: string): PoolTransport {
|
|
870
|
+
function unifiedPoolTransport(provider: string): PoolTransport {
|
|
877
871
|
return {
|
|
878
|
-
readPath: `/api/
|
|
879
|
-
writePath: "/api/
|
|
872
|
+
readPath: `/api/pool/settings?provider=${encodeURIComponent(provider)}`,
|
|
873
|
+
writePath: "/api/pool/settings",
|
|
880
874
|
strategyKey: "strategy",
|
|
881
875
|
stickyKey: "stickyLimit",
|
|
882
876
|
writeBody: (field, value) => ({ provider, [field]: value }),
|
|
@@ -892,8 +886,7 @@ function poolTransportFor(
|
|
|
892
886
|
classified: { type: "codex" | "oauth" | "api-key" },
|
|
893
887
|
name: string,
|
|
894
888
|
): PoolTransport | string {
|
|
895
|
-
if (classified.type === "codex") return
|
|
896
|
-
if (classified.type === "oauth") return anthropicPoolTransport(name);
|
|
889
|
+
if (classified.type === "codex" || classified.type === "oauth") return unifiedPoolTransport(name);
|
|
897
890
|
return `pool settings apply to OAuth account pools, not the API-key provider "${name}"`;
|
|
898
891
|
}
|
|
899
892
|
|
package/src/cli/account.ts
CHANGED
|
@@ -60,6 +60,7 @@ const ACCOUNT_USAGE = `Usage:
|
|
|
60
60
|
ocx account code <provider> [--flow <flow-id>] [--json] (reads the code from stdin)
|
|
61
61
|
ocx account cancel <provider> [--flow <flow-id>] [--json]
|
|
62
62
|
ocx account reset-credits <account-id|main> [--consume --yes] [--json]
|
|
63
|
+
ocx account grok-reset-coupons [<account-id>] [--consume --yes] [--token-id <token-id>] [--json]
|
|
63
64
|
ocx account main <doctor|list|register|add|switch|recover> ...
|
|
64
65
|
|
|
65
66
|
List and switch provider accounts and API-key pools (masked output only).
|
|
@@ -355,7 +356,7 @@ export async function cmdAccount(args: string[], deps: AccountDeps = {}): Promis
|
|
|
355
356
|
const { cmdNativeMainAccount } = await import("./account-main");
|
|
356
357
|
return await cmdNativeMainAccount(rest, deps);
|
|
357
358
|
}
|
|
358
|
-
if (["login", "reauth", "code", "cancel", "reset-credits"].includes(sub ?? "")) {
|
|
359
|
+
if (["login", "reauth", "code", "cancel", "reset-credits", "grok-reset-coupons"].includes(sub ?? "")) {
|
|
359
360
|
const { handleAccountAuthCommand } = await import("./account-auth");
|
|
360
361
|
return await handleAccountAuthCommand(sub!, rest, deps) ?? 1;
|
|
361
362
|
}
|
package/src/cli/capabilities.ts
CHANGED
|
@@ -259,6 +259,27 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
259
259
|
json: "payload",
|
|
260
260
|
details: ["CLI/admin-token refreshes only observe usage. After quota recovery, a human must click Refresh quotas in the dashboard to authorize model validation. Do not mint a GUI session to work around this consent boundary."],
|
|
261
261
|
},
|
|
262
|
+
{
|
|
263
|
+
command: ["account", "grok-reset-coupons"],
|
|
264
|
+
summary: "Inspect or redeem Grok billing reset coupons; redemption is journaled and idempotent.",
|
|
265
|
+
routes: [
|
|
266
|
+
{ method: "GET", path: "/api/grok/reset-coupons" },
|
|
267
|
+
{ method: "POST", path: "/api/grok/reset-coupons/consume" },
|
|
268
|
+
],
|
|
269
|
+
flags: [
|
|
270
|
+
{ name: "--consume", value: "boolean", summary: "Redeem one reset coupon; requires --yes." },
|
|
271
|
+
{ name: "--yes", value: "boolean", summary: "Explicit confirmation required by --consume." },
|
|
272
|
+
{ name: "--token-id", value: "string", summary: "Redeem a specific reset token instead of the default selection." },
|
|
273
|
+
{ name: "--operation-id", value: "string", summary: "UUIDv4 making a redemption idempotent: retries replay the journaled outcome." },
|
|
274
|
+
{ name: "--json", value: "boolean", summary: "Emit the coupon list or redemption result as JSON." },
|
|
275
|
+
],
|
|
276
|
+
mutates: true,
|
|
277
|
+
json: "payload",
|
|
278
|
+
details: [
|
|
279
|
+
"Without --consume this is a read: remaining coupons and their validity windows.",
|
|
280
|
+
"The operation is journaled before the upstream call, so retrying the same --operation-id replays the recorded outcome instead of spending a second coupon.",
|
|
281
|
+
],
|
|
282
|
+
},
|
|
262
283
|
{
|
|
263
284
|
command: ["usage"],
|
|
264
285
|
summary: "Token and estimated-cost report over a time range.",
|
|
@@ -316,10 +337,9 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
316
337
|
// Both pools, because both have the setting. The Codex pool reads its applied values
|
|
317
338
|
// from the active payload; the Anthropic pool has its own GET.
|
|
318
339
|
routes: [
|
|
319
|
-
{ method: "GET", path: "/api/
|
|
320
|
-
{ method: "PUT", path: "/api/
|
|
321
|
-
{ method: "
|
|
322
|
-
{ method: "PUT", path: "/api/oauth/accounts/pool" },
|
|
340
|
+
{ method: "GET", path: "/api/pool/settings" },
|
|
341
|
+
{ method: "PUT", path: "/api/pool/settings" },
|
|
342
|
+
{ method: "PATCH", path: "/api/pool/settings" },
|
|
323
343
|
],
|
|
324
344
|
flags: [{ name: "--json", value: "boolean", summary: "Emit the applied strategy and sticky limit as JSON." }],
|
|
325
345
|
mutates: true,
|
|
@@ -328,23 +348,45 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
328
348
|
"A bare invocation reads and never writes.",
|
|
329
349
|
"The APPLIED value is echoed, not the requested one, so a server-side normalization stays visible.",
|
|
330
350
|
"Values are not re-validated in the CLI: the server owns the strategy names and the 1-100 sticky bound.",
|
|
331
|
-
"
|
|
351
|
+
"One route answers for every pool kind and declares which fields that kind honours in `supported`, so an unsupported field is a stated null rather than an absence. `anthropic` alone carries `quotaWindow`. Generic-provider settings steer selection only while `pool.kernel` is on. The legacy per-pool paths still work and are unchanged.",
|
|
332
352
|
],
|
|
333
353
|
},
|
|
334
354
|
{
|
|
335
355
|
command: ["account", "sticky"],
|
|
336
356
|
summary: "Show or set how many consecutive requests stay on one account.",
|
|
357
|
+
routes: [
|
|
358
|
+
{ method: "GET", path: "/api/pool/settings" },
|
|
359
|
+
{ method: "PUT", path: "/api/pool/settings" },
|
|
360
|
+
{ method: "PATCH", path: "/api/pool/settings" },
|
|
361
|
+
],
|
|
362
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the applied strategy and sticky limit as JSON." }],
|
|
363
|
+
mutates: true,
|
|
364
|
+
json: "envelope",
|
|
365
|
+
details: ["Only meaningful under the sticky-capable strategies; the pool strategy is the other half of this setting."],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
command: ["account", "auto-switch"],
|
|
369
|
+
summary: "Show or set the usage percentage at which a pool moves to another account.",
|
|
370
|
+
// Declared here rather than riding on `account strategy`, which is what it did before the
|
|
371
|
+
// unified route existed. `auto-switch` genuinely drives these three: the Codex pool reads
|
|
372
|
+
// its applied threshold from the active payload and writes through its own route, and a
|
|
373
|
+
// generic OAuth pool reads and writes the per-provider pool settings.
|
|
337
374
|
routes: [
|
|
338
375
|
{ method: "GET", path: "/api/codex-auth/active" },
|
|
339
|
-
{ method: "PUT", path: "/api/codex-auth/
|
|
376
|
+
{ method: "PUT", path: "/api/codex-auth/auto-switch" },
|
|
340
377
|
{ method: "GET", path: "/api/oauth/accounts/pool" },
|
|
341
378
|
{ method: "PUT", path: "/api/oauth/accounts/pool" },
|
|
342
379
|
],
|
|
343
|
-
flags: [{ name: "--json", value: "boolean", summary: "Emit the
|
|
380
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the stored threshold and whether it is applied." }],
|
|
344
381
|
mutates: true,
|
|
345
382
|
json: "envelope",
|
|
346
|
-
details: [
|
|
383
|
+
details: [
|
|
384
|
+
"A bare invocation reads and never writes.",
|
|
385
|
+
"`on` stores 80%, `off` stores 0%, and `threshold <n>` accepts 0-100.",
|
|
386
|
+
"For a generic OAuth pool, `inert: true` means the threshold is stored but not applied, `inert: false` means the pool is applying it, and an absent `inert` is an unknown capability.",
|
|
387
|
+
],
|
|
347
388
|
},
|
|
389
|
+
|
|
348
390
|
{
|
|
349
391
|
command: ["logs"],
|
|
350
392
|
summary: "Recent request log rows, filterable by provider, model, conversation, account, and status.",
|
|
@@ -144,8 +144,8 @@ function normalizeVisionConfig(config: OcxConfig): OcxConfig {
|
|
|
144
144
|
const vision = config.visionSidecar;
|
|
145
145
|
if (!vision || vision.reasoning === undefined) return config;
|
|
146
146
|
// Keep CLI import/set semantics aligned with the execution path: an omitted or blank model means
|
|
147
|
-
// the bounded OpenAI vision default, gpt-5.
|
|
148
|
-
const model = vision.model || "gpt-5.
|
|
147
|
+
// the bounded OpenAI vision default, gpt-5.6-luna, not the Dashboard's web-search default.
|
|
148
|
+
const model = vision.model || "gpt-5.6-luna";
|
|
149
149
|
const normalized = normalizeVisionReasoningForModel(model, vision.reasoning);
|
|
150
150
|
if (normalized === undefined) delete vision.reasoning;
|
|
151
151
|
else vision.reasoning = normalized;
|
package/src/cli/dispatch.ts
CHANGED
|
@@ -302,8 +302,20 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
302
302
|
return Number(process.exitCode ?? 0);
|
|
303
303
|
},
|
|
304
304
|
login: async deps => {
|
|
305
|
+
const loginArgs = deps.args.slice(1);
|
|
306
|
+
// 'ocx login codex' is the command people type first, and until now it answered with
|
|
307
|
+
// the full provider wall because the Codex pool lives behind 'ocx account login'.
|
|
308
|
+
// Route the three Codex spellings to that flow instead of making the user discover
|
|
309
|
+
// a second noun. Everything else stays on the local OAuth/API-key path.
|
|
310
|
+
const { isCodexAccountLoginName, handleAccountAuthCommand } = await import("./account-auth");
|
|
311
|
+
if (isCodexAccountLoginName(loginArgs[0] ?? "")) {
|
|
312
|
+
// null means "unknown subcommand", which "login" never is; the coalesce exists because
|
|
313
|
+
// the shared signature serves callers that do pass an unknown one.
|
|
314
|
+
const code = await handleAccountAuthCommand("login", loginArgs, { findLiveProxy: deps.findLiveProxy });
|
|
315
|
+
return code ?? 1;
|
|
316
|
+
}
|
|
305
317
|
const { handleLogin } = await import("../oauth/login-cli");
|
|
306
|
-
await handleLogin(
|
|
318
|
+
await handleLogin(loginArgs[0]);
|
|
307
319
|
return 0;
|
|
308
320
|
},
|
|
309
321
|
logout: async deps => {
|
|
@@ -440,7 +452,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
440
452
|
},
|
|
441
453
|
config,
|
|
442
454
|
port: live.port,
|
|
443
|
-
}, ["mcode", "pi", "raycast"]));
|
|
455
|
+
}, ["mcode", "pi", "raycast", "omo", "cline"]));
|
|
444
456
|
} catch (error) {
|
|
445
457
|
console.warn(`Client integrations were not refreshed: ${error instanceof Error ? error.message : String(error)}`);
|
|
446
458
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ocx export --client <id>` — print a client config for the live proxy.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Fourteen clients, five formats. The accepted list is `EXPORT_CLIENT_IDS`, not
|
|
5
|
+
* this comment: OpenCode, Pi, Prime, Aside, ZCode and omo are JSON; OMP,
|
|
6
|
+
* Hermes, gjc, DSH, MiniMax Code and Raycast are YAML; OpenClaw is JSON5; Kimi
|
|
7
|
+
* is TOML.
|
|
6
8
|
*
|
|
7
9
|
* Two consumers, one payload (devlog 260731_client_config_export/020):
|
|
8
10
|
*
|
|
@@ -58,16 +60,6 @@ export interface ExportCommandDeps extends RuntimeApiDeps {
|
|
|
58
60
|
configImpl?: () => OcxConfig;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
/**
|
|
62
|
-
* `/api/models` row plus the modality list Pi consumes. The launcher's row type predates
|
|
63
|
-
* the Pi exporter and stops at the fields OpenCode needs.
|
|
64
|
-
*/
|
|
65
|
-
type ExportProxyModelRow = OpencodeProxyModelRow & {
|
|
66
|
-
inputModalities?: string[];
|
|
67
|
-
reasoningEfforts?: string[];
|
|
68
|
-
defaultReasoningEffort?: string;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
63
|
/**
|
|
72
64
|
* Export rows from proxy `/api/models` rows.
|
|
73
65
|
*
|
|
@@ -78,20 +70,13 @@ type ExportProxyModelRow = OpencodeProxyModelRow & {
|
|
|
78
70
|
* row as the model itself: a second lookup over the raw rows would let a hidden or disabled
|
|
79
71
|
* duplicate donate its ladder to the visible entry.
|
|
80
72
|
*
|
|
81
|
-
*
|
|
73
|
+
* Modalities need no such lookup: `opencodeCatalogFromProxyRows` carries them on the catalog
|
|
74
|
+
* entry, so the clients that filter them are handed the same filtered, deduped row.
|
|
82
75
|
*/
|
|
83
76
|
export function exportModelsFromProxyRows(
|
|
84
|
-
rows: readonly
|
|
77
|
+
rows: readonly OpencodeProxyModelRow[],
|
|
85
78
|
config: OcxConfig,
|
|
86
79
|
): ExportModel[] {
|
|
87
|
-
const modalities = new Map<string, string[]>();
|
|
88
|
-
for (const row of rows) {
|
|
89
|
-
const namespaced = row.namespaced?.trim();
|
|
90
|
-
if (!namespaced || modalities.has(namespaced)) continue;
|
|
91
|
-
if (Array.isArray(row.inputModalities) && row.inputModalities.length > 0) {
|
|
92
|
-
modalities.set(namespaced, [...row.inputModalities]);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
80
|
return opencodeCatalogFromProxyRows(rows, config).map(entry => {
|
|
96
81
|
const model: ExportModel = {
|
|
97
82
|
namespaced: entry.namespaced,
|
|
@@ -106,8 +91,9 @@ export function exportModelsFromProxyRows(
|
|
|
106
91
|
model.reasoningEfforts = [...entry.reasoningEfforts];
|
|
107
92
|
}
|
|
108
93
|
if (entry.defaultReasoningEffort) model.defaultReasoningEffort = entry.defaultReasoningEffort;
|
|
109
|
-
|
|
110
|
-
|
|
94
|
+
if (entry.inputModalities && entry.inputModalities.length > 0) {
|
|
95
|
+
model.inputModalities = [...entry.inputModalities];
|
|
96
|
+
}
|
|
111
97
|
return model;
|
|
112
98
|
});
|
|
113
99
|
}
|
|
@@ -186,7 +172,7 @@ export async function handleExportCommand(argv: string[], deps: ExportCommandDep
|
|
|
186
172
|
}
|
|
187
173
|
built = { document: exported.config, text: exported.text };
|
|
188
174
|
} else {
|
|
189
|
-
const rows = await runtimeRequest<
|
|
175
|
+
const rows = await runtimeRequest<OpencodeProxyModelRow[]>("/api/models", {}, { ...deps, baseUrl: root });
|
|
190
176
|
if (!Array.isArray(rows)) {
|
|
191
177
|
throw new RuntimeApiError("Management API returned an unexpected /api/models payload.", 502, rows);
|
|
192
178
|
}
|
package/src/cli/help.ts
CHANGED
|
@@ -52,7 +52,7 @@ Usage:
|
|
|
52
52
|
ocx doctor --recover-zero-byte-coordinator --yes
|
|
53
53
|
Back up a proven zero-byte Codex coordinator after stopping the proxy
|
|
54
54
|
ocx debug <scope> provider/usage/injection/claude on|off|status|reset
|
|
55
|
-
ocx login <provider> OAuth or API-key provider login
|
|
55
|
+
ocx login <provider> OAuth or API-key provider login (ocx login codex for Codex/ChatGPT)
|
|
56
56
|
ocx logout <provider> Remove a stored OAuth login
|
|
57
57
|
ocx gui [pair --origin <browser-origin> [--json]]
|
|
58
58
|
Open the dashboard or create a single-use remote pairing grant
|
|
@@ -81,7 +81,7 @@ Usage:
|
|
|
81
81
|
ocx memory [--json] Alias of ocx observe memory
|
|
82
82
|
ocx api-key <sub> Alias of ocx access key
|
|
83
83
|
ocx access <sub> External API keys and endpoint information
|
|
84
|
-
ocx export --client <id> Print a client config wired to the running proxy (
|
|
84
|
+
ocx export --client <id> Print a client config wired to the running proxy (15 clients)
|
|
85
85
|
ocx integration client <sub> Enable, disable, inspect or roll back a client integration
|
|
86
86
|
ocx grok <sub> Grok Build model selection and apply
|
|
87
87
|
ocx system <sub> Runtime settings, startup, sync, OpenCodex updates, and Codex CLI inspection
|
package/src/cli/opencode.ts
CHANGED
|
@@ -98,6 +98,8 @@ export interface OpencodeProxyModelRow {
|
|
|
98
98
|
displayName?: string;
|
|
99
99
|
displayNameSource?: "operator" | "provider" | "fallback";
|
|
100
100
|
contextWindow?: number;
|
|
101
|
+
/** Declared input modalities from `/api/models`; carried into opencode model capabilities. */
|
|
102
|
+
inputModalities?: string[];
|
|
101
103
|
/** Declared effort ladder from `/api/models`; carried into opencode model variants. */
|
|
102
104
|
reasoningEfforts?: string[];
|
|
103
105
|
/** Declared default effort from `/api/models`. */
|
|
@@ -396,6 +398,9 @@ export function opencodeCatalogFromProxyRows(
|
|
|
396
398
|
id: row.id,
|
|
397
399
|
contextWindow: row.contextWindow,
|
|
398
400
|
displayName: row.displayNameSource === "fallback" ? undefined : row.displayName,
|
|
401
|
+
...(Array.isArray(row.inputModalities) && row.inputModalities.length > 0
|
|
402
|
+
? { inputModalities: [...row.inputModalities] }
|
|
403
|
+
: {}),
|
|
399
404
|
...(typeof row.fastRowAvailable === "boolean" ? { fastRowAvailable: row.fastRowAvailable } : {}),
|
|
400
405
|
...(Array.isArray(row.reasoningEfforts) && row.reasoningEfforts.length > 0
|
|
401
406
|
? { reasoningEfforts: [...row.reasoningEfforts] }
|
package/src/cli/registry.ts
CHANGED
|
@@ -152,7 +152,16 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
152
152
|
"Env default: OCX_DEBUG=1 (legacy OCX_DEBUG_FRAMES still works)",
|
|
153
153
|
],
|
|
154
154
|
},
|
|
155
|
-
{
|
|
155
|
+
{
|
|
156
|
+
name: "login",
|
|
157
|
+
usage: "ocx login <provider>",
|
|
158
|
+
summary: "OAuth or API-key login for a provider.",
|
|
159
|
+
details: [
|
|
160
|
+
"Codex/ChatGPT: ocx login codex runs the Codex account-pool login (same flow as ocx account login codex).",
|
|
161
|
+
"That one needs a running proxy; the OAuth and API-key providers log in locally.",
|
|
162
|
+
"'chatgpt' and 'openai' are the same route; an OpenAI platform API key is 'ocx login openai-apikey'.",
|
|
163
|
+
],
|
|
164
|
+
},
|
|
156
165
|
{ name: "logout", usage: "ocx logout <provider>", summary: "Remove a stored provider login." },
|
|
157
166
|
{
|
|
158
167
|
name: "gui",
|
|
@@ -212,7 +221,7 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
212
221
|
},
|
|
213
222
|
{
|
|
214
223
|
name: "account",
|
|
215
|
-
usage: "ocx account <list|current|use|refresh|auto-switch|priority|login|reauth|code|cancel|remove|add-key|reset-credits|main> ...",
|
|
224
|
+
usage: "ocx account <list|current|use|refresh|auto-switch|priority|login|reauth|code|cancel|remove|add-key|reset-credits|grok-reset-coupons|main> ...",
|
|
216
225
|
summary: "List and switch provider accounts and API-key pools (GUI parity).",
|
|
217
226
|
details: [
|
|
218
227
|
"list [provider] Codex account pool, OAuth accounts and API keys (identifiers shown masked as the API returns them).",
|
|
@@ -225,6 +234,7 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
225
234
|
"add-key <provider> [--label <label>] Add a key read only from piped stdin.",
|
|
226
235
|
"login/reauth/code/cancel Run browser or manual-code auth from a headless shell.",
|
|
227
236
|
"reset-credits <id|main> [--consume --yes] Inspect or consume Codex reset credits.",
|
|
237
|
+
"grok-reset-coupons [<id>] [--consume --yes] Inspect or redeem Grok reset coupons.",
|
|
228
238
|
"main <subcommand> Manage the physical native Codex login separately from Pool routing.",
|
|
229
239
|
"Switching the active account takes effect immediately; running threads move on their next request, and in-flight requests keep the account they captured.",
|
|
230
240
|
"A selection-order change applies from the next unbound request and never moves a bound thread.",
|
|
@@ -327,11 +337,12 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
327
337
|
{ name: "api-key", usage: "ocx api-key <list|create|rotate|remove> ...", summary: "Alias of ocx access key." },
|
|
328
338
|
{
|
|
329
339
|
name: "export",
|
|
330
|
-
usage: "ocx export --client <opencode|pi|omp|hermes|openclaw|kimi|gajae|dsh|mcode|zcode|prime|aside|raycast> [--json] [--out <path>] [--force]",
|
|
331
|
-
summary: "Print a client config (OpenCode, Pi, OMP, Hermes, OpenClaw, Kimi Code,
|
|
340
|
+
usage: "ocx export --client <opencode|pi|omp|hermes|openclaw|kimi|gajae|dsh|mcode|zcode|prime|aside|raycast|omo|cline> [--json] [--out <path>] [--force]",
|
|
341
|
+
summary: "Print a client config (OpenCode, Pi, OMP, Hermes, OpenClaw, Kimi Code, gjc, DeepSeek Harness, MiniMax Code, ZCode, Prime Agent, Aside, Raycast, omo, Cline) wired to the running proxy.",
|
|
332
342
|
details: [
|
|
333
343
|
"--json prints the generated document as JSON on stdout; use --out for the client's native format.",
|
|
334
344
|
"--out <path> writes the native config there and refuses to replace an existing file without --force.",
|
|
345
|
+
"Cline exports a two-document bundle: settings for providers.json and catalog for sibling models.json. Use integration client enable --client cline for a journaled write.",
|
|
335
346
|
"The config never contains a real key; it carries a documented env reference or a non-secret loopback placeholder.",
|
|
336
347
|
"The destination path is printed for merging by hand — ocx never writes your real client config.",
|
|
337
348
|
],
|