@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
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `ocx` can do, as data an agent can read without parsing help text.
|
|
3
|
+
*
|
|
4
|
+
* This is the machine-readable index behind `ocx capabilities`. It relates each CLI
|
|
5
|
+
* capability to the management route(s) it drives, which nothing in this repository did
|
|
6
|
+
* before: help lived in twenty per-module `USAGE` constants and a hand-written banner
|
|
7
|
+
* that a test explicitly licensed to drift from the command registry.
|
|
8
|
+
*
|
|
9
|
+
* LEAF MODULE. It imports nothing from `src/cli/`, and nothing here may import a command
|
|
10
|
+
* module. That is not tidiness. Each command module declares its usage text as a
|
|
11
|
+
* top-level `const USAGE`, evaluated at import time, so a cycle back into this table
|
|
12
|
+
* would resolve to `undefined` under ESM rather than throwing -- silently emptying the
|
|
13
|
+
* usage text that `rejectArgs` hands to `CliUsageError`, in the exact error-reporting
|
|
14
|
+
* surface the CLI-operability issues are about. `tests/cli-capabilities.test.ts` asserts
|
|
15
|
+
* the absence of those imports and that every rendered usage string is non-empty, so the
|
|
16
|
+
* failure mode is loud instead of degraded.
|
|
17
|
+
*
|
|
18
|
+
* Head-handled surfaces (`--version`, `help`) are declared separately in
|
|
19
|
+
* `HEAD_CAPABILITIES`. They exit in the CLI head (`root.ts`) before dispatch and have no
|
|
20
|
+
* runner key, so listing them as ordinary capabilities would break the registry parity
|
|
21
|
+
* assertion that every canonical entry is a direct runner. `help` is excluded from
|
|
22
|
+
* `CLI_COMMANDS` deliberately -- `tests/cli-registry.test.ts` documents it as a
|
|
23
|
+
* head-handled pseudo-case -- and that decision is preserved here rather than reversed.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** A management route a capability drives. Path text only; never a handler reference. */
|
|
27
|
+
export interface CapabilityRoute {
|
|
28
|
+
readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
29
|
+
readonly path: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CapabilityFlag {
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly value?: "string" | "number" | "boolean";
|
|
35
|
+
readonly required?: boolean;
|
|
36
|
+
readonly summary: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* How a capability emits JSON.
|
|
41
|
+
*
|
|
42
|
+
* - `payload`: the API payload, largely unwrapped.
|
|
43
|
+
* - `envelope`: a CLI-shaped object with its own schema.
|
|
44
|
+
* - `none`: no `--json` mode.
|
|
45
|
+
*/
|
|
46
|
+
export type CapabilityJsonMode = "payload" | "envelope" | "none";
|
|
47
|
+
|
|
48
|
+
export interface Capability {
|
|
49
|
+
/** Command path, e.g. `["account", "pause"]`. */
|
|
50
|
+
readonly command: readonly string[];
|
|
51
|
+
readonly summary: string;
|
|
52
|
+
readonly routes: readonly CapabilityRoute[];
|
|
53
|
+
readonly flags: readonly CapabilityFlag[];
|
|
54
|
+
readonly mutates: boolean;
|
|
55
|
+
readonly json: CapabilityJsonMode;
|
|
56
|
+
readonly details?: readonly string[];
|
|
57
|
+
/**
|
|
58
|
+
* Extra banner rows this capability owns, for surfaces the banner shows separately
|
|
59
|
+
* from the bare command (`ocx restore back`, `ocx doctor --reclaim-response-temps`).
|
|
60
|
+
* Without this the banner cannot equal the capability set: it legitimately carries more
|
|
61
|
+
* rows than there are commands.
|
|
62
|
+
*/
|
|
63
|
+
readonly bannerLines?: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Surfaces resolved in the CLI head, before dispatch.
|
|
68
|
+
*
|
|
69
|
+
* They belong in `ocx capabilities` output and in the banner, but not in `CLI_COMMANDS`:
|
|
70
|
+
* `--version`, `-v`, and `version` are answered at `root.ts` and exit, so none of them is
|
|
71
|
+
* a runner key to parity-check against.
|
|
72
|
+
*/
|
|
73
|
+
export interface HeadCapability {
|
|
74
|
+
readonly invocations: readonly string[];
|
|
75
|
+
readonly summary: string;
|
|
76
|
+
readonly bannerLine: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const HEAD_CAPABILITIES: readonly HeadCapability[] = [
|
|
80
|
+
{
|
|
81
|
+
invocations: ["--version", "-v", "version"],
|
|
82
|
+
summary: "Print the CLI version and exit.",
|
|
83
|
+
bannerLine: "ocx --version | -v Print version",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
invocations: ["help", "--help", "-h"],
|
|
87
|
+
summary: "Print the command list, or one command's usage with `ocx help <command>`.",
|
|
88
|
+
bannerLine: "ocx help [command] Show help for a command",
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Capabilities declared so far. Incomplete by design: later phases add verbs.
|
|
94
|
+
* `ocx capabilities` is the index of what is listed here, not of every CLI command.
|
|
95
|
+
* A capability must not name a route the command does not actually fetch.
|
|
96
|
+
*/
|
|
97
|
+
export const CAPABILITIES: readonly Capability[] = [
|
|
98
|
+
{
|
|
99
|
+
command: ["status"],
|
|
100
|
+
summary: "Proxy status, injection state, and version skew between this CLI and the running proxy.",
|
|
101
|
+
// No management route: `collectStatus` identity-probes `/healthz` through
|
|
102
|
+
// `findLiveProxy` and reads local config. Declaring `GET /api/status` here was wrong
|
|
103
|
+
// -- that route does not exist, and the registry cross-check caught it.
|
|
104
|
+
routes: [],
|
|
105
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the status envelope as JSON." }],
|
|
106
|
+
mutates: false,
|
|
107
|
+
json: "envelope",
|
|
108
|
+
details: ["Reads /healthz plus local config; drives no management API route."],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
command: ["capabilities"],
|
|
112
|
+
summary: "List the declared CLI capabilities and the management routes they drive.",
|
|
113
|
+
routes: [],
|
|
114
|
+
flags: [
|
|
115
|
+
{ name: "--json", value: "boolean", summary: "Emit the full capability table as JSON." },
|
|
116
|
+
{ name: "--mutating-only", value: "boolean", summary: "Restrict output to capabilities that mutate state." },
|
|
117
|
+
{ name: "--route", value: "string", summary: "Show which capabilities drive a management route." },
|
|
118
|
+
],
|
|
119
|
+
mutates: false,
|
|
120
|
+
json: "envelope",
|
|
121
|
+
details: ["Start here when driving ocx programmatically: it is the declared surface index, not a complete verb list."],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
command: ["provider", "list"],
|
|
125
|
+
summary: "Configured providers with connectivity and selected models.",
|
|
126
|
+
// Local config + PROVIDER_REGISTRY. Does not call GET /api/providers.
|
|
127
|
+
routes: [],
|
|
128
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the provider list as JSON." }],
|
|
129
|
+
mutates: false,
|
|
130
|
+
json: "envelope",
|
|
131
|
+
details: ["Reads local config; drives no management API route."],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
command: ["account", "list"],
|
|
135
|
+
summary: "Codex OAuth accounts with pool priority and pause state.",
|
|
136
|
+
routes: [{ method: "GET", path: "/api/codex-auth/accounts" }],
|
|
137
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the account list as JSON." }],
|
|
138
|
+
mutates: false,
|
|
139
|
+
json: "payload",
|
|
140
|
+
details: [
|
|
141
|
+
"STATUS names `paused` alongside `selected`: a paused-but-selected account still receives requests.",
|
|
142
|
+
"`--quota` shows cached Codex windows (including 5h); `--refresh` bypasses the server TTL.",
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
command: ["usage"],
|
|
147
|
+
summary: "Token and estimated-cost report over a time range.",
|
|
148
|
+
routes: [{ method: "GET", path: "/api/usage" }],
|
|
149
|
+
flags: [
|
|
150
|
+
{ name: "--range", value: "string", summary: "today | 1d | 7d | 30d | all" },
|
|
151
|
+
{ name: "--provider", value: "string", summary: "Restrict to one provider." },
|
|
152
|
+
{ name: "--model", value: "string", summary: "Restrict to one model id." },
|
|
153
|
+
{ name: "--json", value: "boolean", summary: "Emit the usage report as JSON." },
|
|
154
|
+
],
|
|
155
|
+
mutates: false,
|
|
156
|
+
json: "payload",
|
|
157
|
+
details: [
|
|
158
|
+
"Per-account totals are withheld under `--provider` or `--model`: account rows cannot be honestly re-partitioned by provider, so the report says so rather than printing an empty table.",
|
|
159
|
+
"An `(ambiguous)` account row aggregates several accounts; do not read it as one identity.",
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
command: ["account", "pause"],
|
|
164
|
+
summary: "Stop routing new requests to one account in the Codex pool.",
|
|
165
|
+
// One route, both directions: `resume` is the same PUT with `paused: false`.
|
|
166
|
+
routes: [{ method: "PUT", path: "/api/codex-auth/accounts/pause" }],
|
|
167
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the pause result as JSON." }],
|
|
168
|
+
mutates: true,
|
|
169
|
+
json: "envelope",
|
|
170
|
+
details: [
|
|
171
|
+
"Pausing also unbinds threads pinned to the account and selects a fallback if it was active -- side effects of the route, not of the word `pause`.",
|
|
172
|
+
"The issue that requested this reported the route as POST; it is PUT.",
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
command: ["account", "resume"],
|
|
177
|
+
summary: "Return a paused account to the Codex pool.",
|
|
178
|
+
routes: [{ method: "PUT", path: "/api/codex-auth/accounts/pause" }],
|
|
179
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the resume result as JSON." }],
|
|
180
|
+
mutates: true,
|
|
181
|
+
json: "envelope",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
command: ["account", "pause-exhausted"],
|
|
185
|
+
summary: "Pause every Codex account whose quota is spent.",
|
|
186
|
+
routes: [{ method: "PUT", path: "/api/codex-auth/accounts/pause-exhausted" }],
|
|
187
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit paused ids and the checked/failed counts as JSON." }],
|
|
188
|
+
mutates: true,
|
|
189
|
+
json: "envelope",
|
|
190
|
+
details: [
|
|
191
|
+
"The route refreshes quota per account and can partially fail; a non-zero failed count exits 1 and sets ok:false, because silence would read as `none were exhausted`.",
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
command: ["account", "strategy"],
|
|
196
|
+
summary: "Show or set how an account pool picks the next account.",
|
|
197
|
+
// Both pools, because both have the setting. The Codex pool reads its applied values
|
|
198
|
+
// from the active payload; the Anthropic pool has its own GET.
|
|
199
|
+
routes: [
|
|
200
|
+
{ method: "GET", path: "/api/codex-auth/active" },
|
|
201
|
+
{ method: "PUT", path: "/api/codex-auth/pool-strategy" },
|
|
202
|
+
{ method: "GET", path: "/api/oauth/accounts/pool" },
|
|
203
|
+
{ method: "PUT", path: "/api/oauth/accounts/pool" },
|
|
204
|
+
],
|
|
205
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the applied strategy and sticky limit as JSON." }],
|
|
206
|
+
mutates: true,
|
|
207
|
+
json: "envelope",
|
|
208
|
+
details: [
|
|
209
|
+
"A bare invocation reads and never writes.",
|
|
210
|
+
"The APPLIED value is echoed, not the requested one, so a server-side normalization stays visible.",
|
|
211
|
+
"Values are not re-validated in the CLI: the server owns the strategy names and the 1-100 sticky bound.",
|
|
212
|
+
"`anthropic` is the only OAuth pool with this setting; other OAuth providers are refused without a round-trip.",
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
command: ["account", "sticky"],
|
|
217
|
+
summary: "Show or set how many consecutive requests stay on one account.",
|
|
218
|
+
routes: [
|
|
219
|
+
{ method: "GET", path: "/api/codex-auth/active" },
|
|
220
|
+
{ method: "PUT", path: "/api/codex-auth/pool-strategy" },
|
|
221
|
+
{ method: "GET", path: "/api/oauth/accounts/pool" },
|
|
222
|
+
{ method: "PUT", path: "/api/oauth/accounts/pool" },
|
|
223
|
+
],
|
|
224
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the applied strategy and sticky limit as JSON." }],
|
|
225
|
+
mutates: true,
|
|
226
|
+
json: "envelope",
|
|
227
|
+
details: ["Only meaningful under the sticky-capable strategies; the pool strategy is the other half of this setting."],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
command: ["logs"],
|
|
231
|
+
summary: "Recent request log rows, filterable by provider, model, conversation, and status.",
|
|
232
|
+
routes: [{ method: "GET", path: "/api/logs" }],
|
|
233
|
+
flags: [
|
|
234
|
+
{ name: "--provider", value: "string", summary: "Restrict to one provider, matching failover attempts too." },
|
|
235
|
+
{ name: "--model", value: "string", summary: "Restrict to one model id, matching failover attempts too." },
|
|
236
|
+
{ name: "--conversation", value: "string", summary: "Restrict to one conversation id (`--conversationId` is accepted too)." },
|
|
237
|
+
{ name: "--status", value: "string", summary: "An exact code (429) or a class (5xx)." },
|
|
238
|
+
{ name: "--limit", value: "number", summary: "Row cap; defaults to 200." },
|
|
239
|
+
{ name: "--follow", value: "boolean", summary: "Stream new rows as JSONL; implies --jsonl." },
|
|
240
|
+
{ name: "--json", value: "boolean", summary: "Emit the server payload as JSON." },
|
|
241
|
+
{ name: "--jsonl", value: "boolean", summary: "Emit one row per line." },
|
|
242
|
+
],
|
|
243
|
+
mutates: false,
|
|
244
|
+
json: "payload",
|
|
245
|
+
details: [
|
|
246
|
+
"`--provider` and `--model` both match a failover attempt, so a request is findable by what actually served it, not only by what was asked for.",
|
|
247
|
+
"Rows print `conv=<id>` when the entry carries one, so a conversation filter can be told apart from an empty result.",
|
|
248
|
+
"`--follow` deduplicates by row id and cannot be combined with `--json`.",
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
command: ["storage", "report"],
|
|
253
|
+
summary: "Disk usage under CODEX_HOME, with the log-guard protection report.",
|
|
254
|
+
routes: [{ method: "GET", path: "/api/storage" }],
|
|
255
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the storage report as JSON." }],
|
|
256
|
+
mutates: false,
|
|
257
|
+
json: "payload",
|
|
258
|
+
bannerLines: ["ocx storage Storage report (default subcommand)"],
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
command: ["storage", "cleanup"],
|
|
262
|
+
summary: "Preview or delete the oldest archived sessions by percentage.",
|
|
263
|
+
// Both routes, because the verb always previews: the mutating route requires the digest the
|
|
264
|
+
// preview returns and rejects a stale one, so the two are one operation.
|
|
265
|
+
routes: [
|
|
266
|
+
{ method: "POST", path: "/api/storage/cleanup/preview" },
|
|
267
|
+
{ method: "POST", path: "/api/storage/cleanup" },
|
|
268
|
+
],
|
|
269
|
+
flags: [
|
|
270
|
+
{ name: "--percent", value: "number", summary: "Portion of the oldest archived sessions to target (0-100)." },
|
|
271
|
+
{ name: "--mode", value: "string", summary: "quarantine (recoverable from trash) or permanent." },
|
|
272
|
+
{ name: "--yes", value: "boolean", summary: "Required to actually delete; without it this is a preview." },
|
|
273
|
+
{ name: "--json", value: "boolean", summary: "Emit the preview or result as JSON." },
|
|
274
|
+
],
|
|
275
|
+
mutates: true,
|
|
276
|
+
json: "payload",
|
|
277
|
+
details: [
|
|
278
|
+
"Without `--yes` it prints what WOULD be freed and exits 0 having changed nothing.",
|
|
279
|
+
"There is no interactive confirmation: a prompt an agent can answer is not a safety boundary.",
|
|
280
|
+
"`--mode quarantine` moves files to trash, so `storage trash restore` can undo it; `permanent` cannot be undone.",
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
command: ["storage", "trash"],
|
|
285
|
+
summary: "List quarantined cleanup batches, or restore one.",
|
|
286
|
+
routes: [
|
|
287
|
+
{ method: "GET", path: "/api/storage/trash" },
|
|
288
|
+
{ method: "POST", path: "/api/storage/trash/restore" },
|
|
289
|
+
],
|
|
290
|
+
flags: [
|
|
291
|
+
{ name: "--yes", value: "boolean", summary: "Required for restore, which moves files and reconciles database rows." },
|
|
292
|
+
{ name: "--json", value: "boolean", summary: "Emit the trash list or restore result as JSON." },
|
|
293
|
+
],
|
|
294
|
+
mutates: true,
|
|
295
|
+
json: "payload",
|
|
296
|
+
details: ["Restore fails with a named 409 when the destination already exists, rather than overwriting it."],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
command: ["storage", "policy"],
|
|
300
|
+
summary: "Show, change, or run the automatic archived-session cleanup policy.",
|
|
301
|
+
routes: [
|
|
302
|
+
{ method: "GET", path: "/api/storage/cleanup-policy" },
|
|
303
|
+
{ method: "PUT", path: "/api/storage/cleanup-policy" },
|
|
304
|
+
{ method: "POST", path: "/api/storage/cleanup-policy/run" },
|
|
305
|
+
],
|
|
306
|
+
flags: [
|
|
307
|
+
{ name: "--enabled", value: "string", summary: "true or false." },
|
|
308
|
+
{ name: "--percent", value: "number", summary: "Portion of oldest archived sessions each run targets." },
|
|
309
|
+
{ name: "--mode", value: "string", summary: "quarantine or permanent." },
|
|
310
|
+
{ name: "--schedule", value: "string", summary: "startup, daily, weekly, or manual." },
|
|
311
|
+
{ name: "--yes", value: "boolean", summary: "Required for `policy run`, which deletes immediately." },
|
|
312
|
+
{ name: "--json", value: "boolean", summary: "Emit the policy or run state as JSON." },
|
|
313
|
+
],
|
|
314
|
+
mutates: true,
|
|
315
|
+
json: "payload",
|
|
316
|
+
details: [
|
|
317
|
+
"`policy set` never enables implicitly: omitting `--enabled` keeps the stored value.",
|
|
318
|
+
"`policy run` forces a run regardless of schedule, so it needs `--yes`.",
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
command: ["inspect", "config"],
|
|
323
|
+
summary: "The effective merged configuration the proxy is running.",
|
|
324
|
+
routes: [{ method: "GET", path: "/api/config" }],
|
|
325
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the config as JSON." }],
|
|
326
|
+
mutates: false,
|
|
327
|
+
json: "payload",
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
command: ["inspect", "catalog"],
|
|
331
|
+
summary: "The generated model catalog served to clients.",
|
|
332
|
+
routes: [{ method: "GET", path: "/api/catalog" }],
|
|
333
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the catalog as JSON." }],
|
|
334
|
+
mutates: false,
|
|
335
|
+
json: "payload",
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
command: ["inspect", "routing-analytics"],
|
|
339
|
+
summary: "Aggregate routing outcomes per provider and model.",
|
|
340
|
+
routes: [{ method: "GET", path: "/api/routing-analytics" }],
|
|
341
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the analytics payload as JSON." }],
|
|
342
|
+
mutates: false,
|
|
343
|
+
json: "payload",
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
command: ["inspect", "pacing"],
|
|
347
|
+
summary: "Request-pacing state for one provider or all of them.",
|
|
348
|
+
routes: [{ method: "GET", path: "/api/provider-request-pacing" }],
|
|
349
|
+
flags: [
|
|
350
|
+
{ name: "--name", value: "string", summary: "Restrict to one provider; omitted means every provider." },
|
|
351
|
+
{ name: "--json", value: "boolean", summary: "Emit the pacing state as JSON." },
|
|
352
|
+
],
|
|
353
|
+
mutates: false,
|
|
354
|
+
json: "payload",
|
|
355
|
+
details: ["An unknown provider name is a 404 rather than an empty result."],
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
command: ["inspect", "key-providers"],
|
|
359
|
+
summary: "Providers that authenticate with an API key rather than OAuth.",
|
|
360
|
+
routes: [{ method: "GET", path: "/api/key-providers" }],
|
|
361
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the provider list as JSON." }],
|
|
362
|
+
mutates: false,
|
|
363
|
+
json: "payload",
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
command: ["inspect", "codex-prompt"],
|
|
367
|
+
summary: "The Codex system prompt state, or the prompt text itself.",
|
|
368
|
+
routes: [
|
|
369
|
+
{ method: "GET", path: "/api/codex-prompt" },
|
|
370
|
+
{ method: "GET", path: "/api/codex-prompt/text" },
|
|
371
|
+
],
|
|
372
|
+
flags: [
|
|
373
|
+
{ name: "--text", value: "boolean", summary: "Print the prompt body verbatim instead of its metadata." },
|
|
374
|
+
{ name: "--json", value: "boolean", summary: "Emit the prompt metadata as JSON." },
|
|
375
|
+
],
|
|
376
|
+
mutates: false,
|
|
377
|
+
json: "payload",
|
|
378
|
+
details: ["Read-only by design: the six mutating prompt routes require a dashboard session."],
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
command: ["inspect", "client-config"],
|
|
382
|
+
summary: "The generated configuration snippet for a supported client.",
|
|
383
|
+
routes: [{ method: "GET", path: "/api/client-config" }],
|
|
384
|
+
flags: [
|
|
385
|
+
{ name: "--client", value: "string", summary: "Required client id; the route names every accepted value on error." },
|
|
386
|
+
{ name: "--json", value: "boolean", summary: "Emit the snippet payload as JSON." },
|
|
387
|
+
],
|
|
388
|
+
mutates: false,
|
|
389
|
+
json: "payload",
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
command: ["inspect", "star"],
|
|
393
|
+
summary: "Whether this repository is starred by the signed-in GitHub account.",
|
|
394
|
+
// GET only, permanently. The POST spends the operator identity and requires a dashboard
|
|
395
|
+
// session precisely so an agent cannot answer that question for them.
|
|
396
|
+
routes: [{ method: "GET", path: "/api/github/star" }],
|
|
397
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the star status as JSON." }],
|
|
398
|
+
mutates: false,
|
|
399
|
+
json: "payload",
|
|
400
|
+
details: ["Starring is never available from the CLI; the verb says so rather than offering a flag that cannot work."],
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
command: ["inspect", "windows-tray"],
|
|
404
|
+
summary: "Windows tray helper state.",
|
|
405
|
+
routes: [{ method: "GET", path: "/api/windows-tray" }],
|
|
406
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the tray state as JSON." }],
|
|
407
|
+
mutates: false,
|
|
408
|
+
json: "payload",
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
command: ["system", "codex-app-server"],
|
|
412
|
+
summary: "Codex app-server reachability and process state, as the dashboard sees it.",
|
|
413
|
+
routes: [{ method: "GET", path: "/api/system/codex-app-server" }],
|
|
414
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the app-server state as JSON." }],
|
|
415
|
+
mutates: false,
|
|
416
|
+
json: "payload",
|
|
417
|
+
details: [
|
|
418
|
+
"The GUI reads this state directly; without a verb an agent could not tell whether the Codex app-server was reachable at all.",
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
command: ["system", "codex-restart"],
|
|
423
|
+
summary: "Restart the Codex app-server.",
|
|
424
|
+
routes: [{ method: "POST", path: "/api/system/codex-restart" }],
|
|
425
|
+
flags: [
|
|
426
|
+
{ name: "--yes", value: "boolean", summary: "Required: restarts the operator's running Codex app-server." },
|
|
427
|
+
{ name: "--json", value: "boolean", summary: "Emit the restart result as JSON." },
|
|
428
|
+
],
|
|
429
|
+
mutates: true,
|
|
430
|
+
json: "payload",
|
|
431
|
+
details: [
|
|
432
|
+
"`sync --restart-codex` is not a substitute: it restarts only as a side effect after a catalog or cache write, so it cannot restart a healthy install on request.",
|
|
433
|
+
"--yes is mandatory because this interrupts a running editor session, which must never happen because an agent guessed a subcommand.",
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
command: ["claude", "desktop", "status"],
|
|
438
|
+
summary: "Applied-vs-desired Claude Desktop state, including staleness, drift, and health.",
|
|
439
|
+
routes: [{ method: "GET", path: "/api/claude-desktop/status" }],
|
|
440
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the live status as JSON." }],
|
|
441
|
+
mutates: false,
|
|
442
|
+
json: "payload",
|
|
443
|
+
details: [
|
|
444
|
+
"Distinct from `claude desktop show`, which reports what this machine WOULD write; this reports what is actually in effect, which only the running proxy knows.",
|
|
445
|
+
],
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
command: ["integration", "native"],
|
|
449
|
+
summary: "Show or toggle the native Claude, Claude Desktop, Codex, and Grok integrations.",
|
|
450
|
+
routes: [
|
|
451
|
+
{ method: "GET", path: "/api/native-integrations" },
|
|
452
|
+
{ method: "PUT", path: "/api/native-integrations/claude" },
|
|
453
|
+
{ method: "PUT", path: "/api/native-integrations/claude-desktop" },
|
|
454
|
+
{ method: "PUT", path: "/api/native-integrations/codex" },
|
|
455
|
+
{ method: "PUT", path: "/api/native-integrations/grok" },
|
|
456
|
+
],
|
|
457
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the client rows or toggle result as JSON." }],
|
|
458
|
+
mutates: true,
|
|
459
|
+
json: "payload",
|
|
460
|
+
details: [
|
|
461
|
+
"The list renders per-client state, installed, and desired columns; a blocked disable is named rather than left silent.",
|
|
462
|
+
"Each client has its own route because a toggle rewrites that client's own config file.",
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
command: ["agent", "request-user-input"],
|
|
467
|
+
summary: "Show or set whether default mode may ask the operator a question mid-task.",
|
|
468
|
+
routes: [
|
|
469
|
+
{ method: "GET", path: "/api/codex-auth/features/default-mode-request-user-input" },
|
|
470
|
+
{ method: "PUT", path: "/api/codex-auth/features/default-mode-request-user-input" },
|
|
471
|
+
],
|
|
472
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit the feature state as JSON." }],
|
|
473
|
+
mutates: true,
|
|
474
|
+
json: "payload",
|
|
475
|
+
details: ["A bare invocation reads and never writes."],
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
/** Capabilities that drive `route`, for `ocx capabilities --route`. */
|
|
480
|
+
export function capabilitiesForRoute(path: string): Capability[] {
|
|
481
|
+
return CAPABILITIES.filter(cap => cap.routes.some(r => r.path === path));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Every `(method, path)` pair any capability drives. */
|
|
485
|
+
export function capabilityRouteKeys(): Set<string> {
|
|
486
|
+
const keys = new Set<string>();
|
|
487
|
+
for (const cap of CAPABILITIES) {
|
|
488
|
+
for (const route of cap.routes) keys.add(`${route.method} ${route.path}`);
|
|
489
|
+
}
|
|
490
|
+
return keys;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** Rendered command path, e.g. `ocx account pause`. */
|
|
494
|
+
export function capabilityInvocation(cap: Capability): string {
|
|
495
|
+
return `ocx ${cap.command.join(" ")}`;
|
|
496
|
+
}
|
|
@@ -14,7 +14,7 @@ import { writeDesktop3pConfig, type Desktop3pConfigMode, parseDesktop3pModeArgs
|
|
|
14
14
|
import { filterCatalogVisibleModels, desktopVisibleNativeSlugs, nativeContextLimits } from "../codex/catalog";
|
|
15
15
|
import { buildClaudeDesktopState, fetchAllModels } from "../server/management-api";
|
|
16
16
|
import { findLiveProxy } from "../server/proxy-liveness";
|
|
17
|
-
import { runtimeRequest } from "./runtime-api";
|
|
17
|
+
import { CliUsageError, runtimeRequest, takeJsonFlag } from "./runtime-api";
|
|
18
18
|
import { OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
|
|
19
19
|
|
|
20
20
|
function isFamily(value: string | undefined): value is DesktopFamily {
|
|
@@ -25,6 +25,7 @@ function printDesktopHelp(): void {
|
|
|
25
25
|
console.log(`Usage:
|
|
26
26
|
ocx claude desktop [apply] [--static|--hybrid|--discovery-only]
|
|
27
27
|
ocx claude desktop show [--json]
|
|
28
|
+
ocx claude desktop status [--json]
|
|
28
29
|
ocx claude desktop move <provider/model> <opus|fable|sonnet|haiku> [--default]
|
|
29
30
|
ocx claude desktop default <family> <provider/model|none>
|
|
30
31
|
ocx claude desktop export <path|->
|
|
@@ -118,10 +119,10 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
118
119
|
const nonMode = argv.filter(arg => !["apply", "--static", "--hybrid", "--discovery-only"].includes(arg));
|
|
119
120
|
if (nonMode.length > 0) {
|
|
120
121
|
console.error(`알 수 없는 인자: ${nonMode.join(" ")}`);
|
|
121
|
-
return
|
|
122
|
+
return 2;
|
|
122
123
|
}
|
|
123
124
|
const parsedMode = parseDesktop3pModeArgs(legacyFlags);
|
|
124
|
-
if ("error" in parsedMode) { console.error(parsedMode.error); return
|
|
125
|
+
if ("error" in parsedMode) { console.error(parsedMode.error); return 2; }
|
|
125
126
|
try {
|
|
126
127
|
const config = loadConfig();
|
|
127
128
|
const state = await buildClaudeDesktopState(config);
|
|
@@ -145,10 +146,29 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
145
146
|
|
|
146
147
|
try {
|
|
147
148
|
const config = loadConfig();
|
|
149
|
+
// `status` is API-backed and must NOT build local state first: the whole point of the
|
|
150
|
+
// route the GUI polls (/api/claude-desktop/status) is the applied-vs-desired comparison,
|
|
151
|
+
// including staleness, drift and health, which only the running proxy knows. `show`
|
|
152
|
+
// reports what this machine would write; `status` reports what is actually in effect.
|
|
153
|
+
if (command === "status") {
|
|
154
|
+
const rest = argv.slice(1);
|
|
155
|
+
const wantsJson = takeJsonFlag(rest);
|
|
156
|
+
if (rest.length > 0) throw new CliUsageError("Usage: ocx claude desktop status [--json]");
|
|
157
|
+
const live = await runtimeRequest<Record<string, unknown>>("/api/claude-desktop/status", {});
|
|
158
|
+
if (wantsJson) console.log(JSON.stringify(live, null, 2));
|
|
159
|
+
else {
|
|
160
|
+
for (const [key, value] of Object.entries(live)) {
|
|
161
|
+
console.log(`${key}: ${typeof value === "object" ? JSON.stringify(value) : String(value)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
148
166
|
const state = await buildClaudeDesktopState(config);
|
|
149
167
|
if (command === "show") {
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
const rest = argv.slice(1);
|
|
169
|
+
const wantsJson = takeJsonFlag(rest);
|
|
170
|
+
if (rest.length > 0) throw new CliUsageError("Usage: ocx claude desktop show [--json]");
|
|
171
|
+
if (wantsJson) console.log(JSON.stringify(state));
|
|
152
172
|
else {
|
|
153
173
|
for (const family of DESKTOP_FAMILIES) {
|
|
154
174
|
console.log(`${family.toUpperCase()}${state.profile.defaults[family] ? ` (default: ${state.profile.defaults[family]})` : ""}`);
|
|
@@ -161,7 +181,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
161
181
|
}
|
|
162
182
|
if (command === "move") {
|
|
163
183
|
const [, route, familyRaw, ...flags] = argv;
|
|
164
|
-
if (!route || !isFamily(familyRaw) || flags.some(flag => flag !== "--default")) throw new
|
|
184
|
+
if (!route || !isFamily(familyRaw) || flags.some(flag => flag !== "--default")) throw new CliUsageError("Usage: ocx claude desktop move <route> <family> [--default]");
|
|
165
185
|
if (!state.models.some(model => model.route === route && model.available)) throw new Error(`현재 사용할 수 없는 모델입니다: ${route}`);
|
|
166
186
|
const profile = moveDesktopRoute(state.profile, route, familyRaw, flags.includes("--default"));
|
|
167
187
|
config.claudeCode = { ...(config.claudeCode ?? {}), desktopProfile: profile };
|
|
@@ -171,7 +191,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
171
191
|
}
|
|
172
192
|
if (command === "default") {
|
|
173
193
|
const [, familyRaw, routeRaw] = argv;
|
|
174
|
-
if (!isFamily(familyRaw) || !routeRaw || argv.length !== 3) throw new
|
|
194
|
+
if (!isFamily(familyRaw) || !routeRaw || argv.length !== 3) throw new CliUsageError("Usage: ocx claude desktop default <family> <route|none>");
|
|
175
195
|
const route = routeRaw === "none" ? null : routeRaw;
|
|
176
196
|
if (route && !state.models.some(model => model.route === route && model.available)) throw new Error(`현재 사용할 수 없는 모델입니다: ${route}`);
|
|
177
197
|
const profile = setDesktopFamilyDefault(state.profile, familyRaw, route);
|
|
@@ -182,7 +202,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
182
202
|
}
|
|
183
203
|
if (command === "export") {
|
|
184
204
|
const target = argv[1];
|
|
185
|
-
if (!target || argv.length !== 2) throw new
|
|
205
|
+
if (!target || argv.length !== 2) throw new CliUsageError("Usage: ocx claude desktop export <path|->");
|
|
186
206
|
const json = JSON.stringify(state.profile, null, 2) + "\n";
|
|
187
207
|
if (target === "-") process.stdout.write(json);
|
|
188
208
|
else writeFileSync(resolve(target), json, { encoding: "utf8", mode: 0o600 });
|
|
@@ -191,7 +211,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
191
211
|
if (command === "import") {
|
|
192
212
|
const source = argv[1];
|
|
193
213
|
const flags = argv.slice(2);
|
|
194
|
-
if (!source || flags.some(flag => flag !== "--apply")) throw new
|
|
214
|
+
if (!source || flags.some(flag => flag !== "--apply")) throw new CliUsageError("Usage: ocx claude desktop import <path> [--apply]");
|
|
195
215
|
const profile = parseDesktopProfile(JSON.parse(readFileSync(resolve(source), "utf8")));
|
|
196
216
|
const reconciled = (await buildClaudeDesktopState(config, profile)).profile;
|
|
197
217
|
config.claudeCode = { ...(config.claudeCode ?? {}), desktopProfile: reconciled };
|
|
@@ -205,9 +225,9 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
|
|
|
205
225
|
return 0;
|
|
206
226
|
}
|
|
207
227
|
printDesktopHelp();
|
|
208
|
-
return
|
|
228
|
+
return 2;
|
|
209
229
|
} catch (error) {
|
|
210
230
|
console.error(error instanceof Error ? error.message : String(error));
|
|
211
|
-
return 1;
|
|
231
|
+
return error instanceof CliUsageError ? 2 : 1;
|
|
212
232
|
}
|
|
213
233
|
}
|
package/src/cli/claude.ts
CHANGED
|
@@ -34,6 +34,8 @@ export type ClaudeEnvDeps = {
|
|
|
34
34
|
authDetect?: Omit<Partial<AuthDetectDeps>, "env" | "ownTokens">;
|
|
35
35
|
/** Test seam; production uses the authenticated Node-launcher context. */
|
|
36
36
|
preBunAnthropicSlots?: readonly AnthropicParentEnvSlot[] | null;
|
|
37
|
+
/** Explicit unsafe opt-in from a root `--dangerously-skip-permissions` launch. */
|
|
38
|
+
allowRootSkipPermissions?: boolean;
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
function isClaudeLoopbackHostname(hostname: string): boolean {
|
|
@@ -115,6 +117,9 @@ export function buildClaudeEnv(
|
|
|
115
117
|
if (env[name] !== undefined && env[name] !== "") return; // user wins
|
|
116
118
|
env[name] = value;
|
|
117
119
|
};
|
|
120
|
+
if (deps.allowRootSkipPermissions === true) {
|
|
121
|
+
setDefault("IS_SANDBOX", "1");
|
|
122
|
+
}
|
|
118
123
|
setDefault("ANTHROPIC_BASE_URL", `http://127.0.0.1:${port}`);
|
|
119
124
|
const existingBaseUrl = env.ANTHROPIC_BASE_URL;
|
|
120
125
|
if (existingBaseUrl) {
|
|
@@ -301,6 +306,22 @@ export function claudeNotFoundHint(
|
|
|
301
306
|
return platform === "win32" && code === 9009 && !signal ? CLAUDE_INSTALL_HINT : null;
|
|
302
307
|
}
|
|
303
308
|
|
|
309
|
+
export function shouldAllowRootSkipPermissions(
|
|
310
|
+
args: readonly string[],
|
|
311
|
+
getuid: (() => number) | null | undefined = process.getuid,
|
|
312
|
+
): boolean {
|
|
313
|
+
return args.includes("--dangerously-skip-permissions")
|
|
314
|
+
&& typeof getuid === "function"
|
|
315
|
+
&& getuid() === 0;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function rootSkipPermissionsNotice(env: ClaudeLaunchEnv): string {
|
|
319
|
+
if (env.IS_SANDBOX === "1") {
|
|
320
|
+
return "⚠ Root --dangerously-skip-permissions requested: OpenCodex set IS_SANDBOX=1 to bypass Claude Code's root guard. OpenCodex did not create an OS sandbox; prefer running as a non-root user.";
|
|
321
|
+
}
|
|
322
|
+
return `⚠ Root --dangerously-skip-permissions requested: preserving user IS_SANDBOX=${env.IS_SANDBOX}; Claude Code's root guard remains in control.`;
|
|
323
|
+
}
|
|
324
|
+
|
|
304
325
|
export async function cmdClaude(args: string[]): Promise<number> {
|
|
305
326
|
const config = loadConfig();
|
|
306
327
|
if (config.claudeCode?.enabled === false) {
|
|
@@ -313,7 +334,11 @@ export async function cmdClaude(args: string[]): Promise<number> {
|
|
|
313
334
|
return 1;
|
|
314
335
|
}
|
|
315
336
|
const contextWindows = await fetchClaudeContextWindows(config, port);
|
|
316
|
-
const
|
|
337
|
+
const allowRootSkipPermissions = shouldAllowRootSkipPermissions(args);
|
|
338
|
+
const env = buildClaudeEnv(config, port, process.env, contextWindows, { allowRootSkipPermissions });
|
|
339
|
+
if (allowRootSkipPermissions) {
|
|
340
|
+
console.error(rootSkipPermissionsNotice(env));
|
|
341
|
+
}
|
|
317
342
|
// Pre-write the CLI's gateway-model cache (devlog 030): without a token the CLI
|
|
318
343
|
// never refreshes it, so the picker would keep showing yesterday's aliases.
|
|
319
344
|
try {
|