@bitkyc08/opencodex 2.80.0 → 2.82.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 +166 -73
- package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
- package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/index-D3FBCNiP.js +94 -0
- package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +8 -3
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -6
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +9 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +19 -17
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +78 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- package/src/cli/account-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +18 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +15 -1
- package/src/cli/doctor.ts +16 -3
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +25 -14
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +48 -6
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/cli/resolve.ts +25 -2
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- package/src/clients/config-export/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +11 -49
- package/src/codex/auth-context.ts +20 -1
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +33 -7
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +37 -6
- package/src/codex/catalog/gather-capture.ts +10 -3
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +58 -44
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -219
- package/src/combos/types.ts +1 -1
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/diagnostics.ts +27 -16
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +42 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +11 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +566 -298
- package/src/generated/model-metadata.ts +7 -6
- package/src/grok/inject.ts +50 -4
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +30 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +19 -3
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +17 -5
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +244 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- package/src/lib/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- package/src/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +90 -18
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +96 -175
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +68 -10
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +23 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +23 -33
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +20 -3
- package/src/responses/state/metrics.ts +95 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +13 -5
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +68 -76
- package/src/responses/task-input.ts +17 -0
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native.ts +64 -28
- package/src/server/claude-messages.ts +59 -8
- package/src/server/gui-static.ts +38 -4
- package/src/server/inbound-body-admission.ts +1 -0
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/serve-options.ts +10 -2
- package/src/server/index/spend-ledger-lifecycle.ts +12 -5
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +5 -2
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +123 -38
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- package/src/server/management/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +85 -14
- package/src/server/management/context.ts +2 -2
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/shared.ts +3 -3
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +91 -45
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +133 -38
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log.ts +36 -2
- package/src/server/responses/adapter-continuation.ts +31 -19
- package/src/server/responses/adapter-dispatch.ts +75 -24
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +24 -4
- package/src/server/responses/core-combo.ts +38 -25
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +8 -1
- package/src/server/responses/core-replay.ts +78 -0
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/passthrough-delivery.ts +30 -4
- package/src/server/responses/passthrough-dispatch.ts +39 -9
- package/src/server/responses/request-prepare.ts +41 -5
- package/src/server/responses/request-send-budget.ts +150 -50
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +13 -3
- package/src/server/responses/sidecar-execution.ts +113 -22
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +33 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +33 -28
- package/src/types/provider.ts +5 -0
- package/src/types/request.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types/wire.ts +1 -0
- package/src/types.ts +4 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- package/src/usage/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -7
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-MbDVfUeC.js +0 -54
- package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
- package/gui/dist/assets/index-UJt6npXk.js +0 -94
- package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
package/src/cli/integrations.ts
CHANGED
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
type RuntimeApiDeps,
|
|
14
14
|
} from "./runtime-api";
|
|
15
15
|
import { clientIntegrationPath, validateAsideProfile } from "./integration-input";
|
|
16
|
+
import { asideProfileRecoveryLines } from "./aside-profile-recovery";
|
|
17
|
+
import type { IntegrationClientId } from "../integrations/registry";
|
|
16
18
|
|
|
17
19
|
const CLAUDE_USAGE = `Usage:
|
|
18
20
|
ocx claude config [status] [--json]
|
|
19
21
|
ocx claude config set [--enabled <on|off>] [--auth-mode <auto|proxy|subscription>]
|
|
20
22
|
[--system-env <on|off>] [--fast-mode <on|off>] [--auto-context <on|off>]
|
|
21
|
-
[--compact-window <tokens|default>] [--inject-agents <on|off>]
|
|
23
|
+
[--compact-window <tokens|default>] [--context-accounting <1m|200k>] [--inject-agents <on|off>]
|
|
22
24
|
[--small-fast-model <id|->] [--model-map <from=to,from=to|->]
|
|
23
25
|
[--blocked-skills <name,name|->] [--web-model <id|->] [--web-backend <openai|anthropic|xai|gemini|exa|->]
|
|
24
26
|
[--vision-model <id|->] [--vision-backend <openai|anthropic|->] [--json]
|
|
@@ -74,6 +76,7 @@ export async function handleClaudeConfigCommand(argv: string[], deps: RuntimeApi
|
|
|
74
76
|
const fastMode = takeBooleanOption(args, "--fast-mode");
|
|
75
77
|
const autoContext = takeBooleanOption(args, "--auto-context");
|
|
76
78
|
const compact = takeOption(args, "--compact-window");
|
|
79
|
+
const accounting = takeOption(args, "--context-accounting");
|
|
77
80
|
const injectAgents = takeBooleanOption(args, "--inject-agents");
|
|
78
81
|
const smallFastModel = takeOption(args, "--small-fast-model");
|
|
79
82
|
const modelMap = takeOption(args, "--model-map");
|
|
@@ -96,6 +99,11 @@ export async function handleClaudeConfigCommand(argv: string[], deps: RuntimeApi
|
|
|
96
99
|
body.autoCompactWindow = value;
|
|
97
100
|
}
|
|
98
101
|
}
|
|
102
|
+
if (accounting !== undefined) {
|
|
103
|
+
const value = accounting.toLowerCase();
|
|
104
|
+
if (value !== "1m" && value !== "200k") throw new CliUsageError("--context-accounting must be 1m or 200k", CLAUDE_USAGE);
|
|
105
|
+
body.contextAccounting = value;
|
|
106
|
+
}
|
|
99
107
|
if (injectAgents !== undefined) body.injectAgents = injectAgents;
|
|
100
108
|
if (smallFastModel !== undefined) body.smallFastModel = smallFastModel === "-" ? "" : smallFastModel;
|
|
101
109
|
if (modelMap !== undefined) body.modelMap = parseMap(modelMap);
|
|
@@ -193,7 +201,10 @@ function raycastBlock(result: unknown): RaycastStatusBlock | null {
|
|
|
193
201
|
*/
|
|
194
202
|
function singleClientStatusLines(result: unknown): string[] {
|
|
195
203
|
const raycast = raycastBlock(result);
|
|
196
|
-
if (!raycast)
|
|
204
|
+
if (!raycast) {
|
|
205
|
+
const aside = (result as { clientId?: unknown } | null)?.clientId === "aside";
|
|
206
|
+
return [...summaryLines(result), ...(aside ? asideProfileRecoveryLines([result]) : [])];
|
|
207
|
+
}
|
|
197
208
|
const rest = Object.fromEntries(Object.entries(result as Record<string, unknown>).filter(([key]) => key !== "raycast"));
|
|
198
209
|
const lines = [...summaryLines(rest), `plan: ${raycast.plan}`];
|
|
199
210
|
if (!raycast.aiDirPresent) {
|
|
@@ -215,7 +226,11 @@ function singleClientStatusLines(result: unknown): string[] {
|
|
|
215
226
|
export async function handleClientIntegrationCommand(
|
|
216
227
|
argv: string[],
|
|
217
228
|
deps: RuntimeApiDeps = {},
|
|
229
|
+
expectedClientId?: IntegrationClientId,
|
|
218
230
|
): Promise<number> {
|
|
231
|
+
if (expectedClientId !== undefined && argv.some(arg => ["--client", "--profile"].some(flag => arg === flag || arg.startsWith(`${flag}=`)))) {
|
|
232
|
+
return runCliAction(async () => { throw new CliUsageError("client-specific restore does not accept --client or --profile", CLIENT_USAGE); });
|
|
233
|
+
}
|
|
219
234
|
if ((argv[0] === "history" || argv[0] === "journal") && argv[1] === "remove") {
|
|
220
235
|
const { handleIntegrationJournalRemove } = await import("./integration-journal");
|
|
221
236
|
return handleIntegrationJournalRemove(argv.slice(2), deps);
|
|
@@ -227,7 +242,7 @@ export async function handleClientIntegrationCommand(
|
|
|
227
242
|
if (argv[0] === "preview" || argv.some(arg => ["--preview", "--plan-fingerprint", "--reasoning-default", "--clear-reasoning-defaults"]
|
|
228
243
|
.some(flag => arg === flag || arg.startsWith(`${flag}=`)))) {
|
|
229
244
|
const { handleIntegrationPreviewCommand } = await import("./integration-preview");
|
|
230
|
-
return handleIntegrationPreviewCommand(argv, deps);
|
|
245
|
+
return handleIntegrationPreviewCommand(argv, deps, expectedClientId);
|
|
231
246
|
}
|
|
232
247
|
return runCliAction(async () => {
|
|
233
248
|
const args = [...argv];
|
|
@@ -247,7 +262,10 @@ export async function handleClientIntegrationCommand(
|
|
|
247
262
|
const profiles = (result as { profiles?: Array<Record<string, unknown>> }).profiles;
|
|
248
263
|
printData(result, wantsJson, profiles
|
|
249
264
|
? profiles.length > 0
|
|
250
|
-
?
|
|
265
|
+
? [
|
|
266
|
+
...profiles.map(row => `${String(row.profileId)} ${String(row.name ?? "Aside")}: ${row.enabled ? "on" : "off"} (${String(row.state)})${row.current ? " [current]" : ""}`),
|
|
267
|
+
...asideProfileRecoveryLines(profiles),
|
|
268
|
+
]
|
|
251
269
|
: [String((result as { error?: string }).error ?? "No Aside profiles found.")]
|
|
252
270
|
: rows
|
|
253
271
|
/*
|
|
@@ -289,9 +307,12 @@ export async function handleClientIntegrationCommand(
|
|
|
289
307
|
if (client !== undefined && profile === undefined) throw new CliUsageError("restore --client requires --profile", CLIENT_USAGE);
|
|
290
308
|
rejectArgs(args, CLIENT_USAGE);
|
|
291
309
|
if (!opId) throw new CliUsageError("--op <opId> is required", CLIENT_USAGE);
|
|
292
|
-
const
|
|
310
|
+
const restorePath = expectedClientId !== undefined ? `${clientIntegrationPath(expectedClientId)}/restore`
|
|
311
|
+
: profile === undefined ? "/api/client-integrations/restore" : `${clientIntegrationPath("aside", profile)}/restore`;
|
|
312
|
+
const result = await runtimeRequest(restorePath, {
|
|
293
313
|
method: "POST",
|
|
294
|
-
|
|
314
|
+
...(expectedClientId === undefined ? {} : { redirect: "error" as const }),
|
|
315
|
+
body: JSON.stringify({ opId, confirmDrift, ...(expectedClientId === undefined ? {} : { expectedClientId }) }),
|
|
295
316
|
}, deps);
|
|
296
317
|
printData(result, wantsJson, [String((result as Record<string, unknown>).message ?? "Restored.")]);
|
|
297
318
|
return;
|
|
@@ -387,3 +408,24 @@ export async function handleClaudeInterceptCommand(argv: string[], deps: Runtime
|
|
|
387
408
|
printData(result, wantsJson, summaryLines(result));
|
|
388
409
|
});
|
|
389
410
|
}
|
|
411
|
+
|
|
412
|
+
const COMMANDCODE_USAGE = "Usage: ocx commandcode [status|show|list|enable|disable|history|journal|restore] [--json]";
|
|
413
|
+
|
|
414
|
+
export async function handleCommandcodeCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
415
|
+
const args = [...argv];
|
|
416
|
+
const verbIndex = args.findIndex(arg => !arg.startsWith("-"));
|
|
417
|
+
const action = (verbIndex === -1 ? "status" : args[verbIndex]).toLowerCase();
|
|
418
|
+
const known = ["status", "show", "list", "enable", "disable", "history", "journal", "restore"];
|
|
419
|
+
if (!known.includes(action)) {
|
|
420
|
+
console.error(`unknown commandcode command ${action}`);
|
|
421
|
+
console.error(COMMANDCODE_USAGE);
|
|
422
|
+
return 2;
|
|
423
|
+
}
|
|
424
|
+
const rest = verbIndex === -1 ? args : [...args.slice(0, verbIndex), ...args.slice(verbIndex + 1)];
|
|
425
|
+
const forwarded = action === "restore" ? [action, ...rest] : [action, ...rest, "--client", "commandcode"];
|
|
426
|
+
const code = await handleClientIntegrationCommand(forwarded, deps, action === "restore" ? "commandcode" : undefined);
|
|
427
|
+
if (code === 0 && (action === "enable" || action === "disable")) {
|
|
428
|
+
console.error("Command Code reads providers on startup.");
|
|
429
|
+
}
|
|
430
|
+
return code;
|
|
431
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MessageKind } from "../messaging/envelope";
|
|
2
|
+
import { isThreadId } from "../messaging/types";
|
|
3
|
+
|
|
4
|
+
export type MessageArgs = { action: "sessions"; json: boolean }
|
|
5
|
+
| { action: "send"; json: boolean; kind: MessageKind; thread?: string; name?: string; inReplyTo?: string };
|
|
6
|
+
|
|
7
|
+
/** Pure pre-parse: unknown/duplicate options and unsupported remote surfaces fail closed. */
|
|
8
|
+
export function parseMessageArgs(argv: readonly string[]): MessageArgs | null {
|
|
9
|
+
const action = argv[0];
|
|
10
|
+
if (action !== "sessions" && action !== "send") return null;
|
|
11
|
+
const seen = new Set<string>();
|
|
12
|
+
const values: Record<string, string> = {};
|
|
13
|
+
for (let index = 1; index < argv.length; index++) {
|
|
14
|
+
const flag = argv[index]!;
|
|
15
|
+
if (seen.has(flag)) return null;
|
|
16
|
+
seen.add(flag);
|
|
17
|
+
if (flag === "--json" || (action === "send" && flag === "--stdin")) continue;
|
|
18
|
+
if (action !== "send" || !["--thread", "--name", "--kind", "--in-reply-to"].includes(flag)) return null;
|
|
19
|
+
const value = argv[++index];
|
|
20
|
+
if (!value || value.startsWith("--")) return null;
|
|
21
|
+
values[flag] = value;
|
|
22
|
+
}
|
|
23
|
+
const json = seen.has("--json");
|
|
24
|
+
if (action === "sessions") return { action, json };
|
|
25
|
+
const thread = values["--thread"], name = values["--name"], inReplyTo = values["--in-reply-to"];
|
|
26
|
+
const kind = values["--kind"] ?? "request";
|
|
27
|
+
if (!seen.has("--stdin") || (thread !== undefined) === (name !== undefined)
|
|
28
|
+
|| (thread !== undefined && !isThreadId(thread))
|
|
29
|
+
|| (name !== undefined && (!name.trim() || name.length > 4096 || /[\x00-\x1f\x7f-\x9f]/.test(name)))
|
|
30
|
+
|| !["request", "response", "notification"].includes(kind)
|
|
31
|
+
|| (kind === "response" ? !isThreadId(inReplyTo) : inReplyTo !== undefined)) return null;
|
|
32
|
+
return { action, json, kind: kind as MessageKind, ...(thread ? { thread } : {}),
|
|
33
|
+
...(name ? { name } : {}), ...(inReplyTo ? { inReplyTo } : {}) };
|
|
34
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MessageBudget } from "../messaging/budget";
|
|
2
|
+
import { readMessageInput } from "../messaging/input";
|
|
3
|
+
import { localSessions, messageFailure, sendLocalMessage } from "../messaging/send";
|
|
4
|
+
import { parseMessageArgs } from "./message-args";
|
|
5
|
+
import { terminalSafeText } from "./runtime-api";
|
|
6
|
+
|
|
7
|
+
/** Run one local messaging operation after pure syntax validation, then release owned resources. */
|
|
8
|
+
export async function runMessageCommand(argv: readonly string[], env: NodeJS.ProcessEnv = process.env): Promise<number> {
|
|
9
|
+
const args = parseMessageArgs(argv);
|
|
10
|
+
if (!args) {
|
|
11
|
+
console.error("Usage: ocx message sessions [--json] | send (--thread <uuid> | --name <exact-name>) --stdin [--kind request|response|notification] [--in-reply-to <uuid>] [--json]");
|
|
12
|
+
return 64;
|
|
13
|
+
}
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const cancel = () => controller.abort();
|
|
16
|
+
process.once("SIGINT", cancel); process.once("SIGTERM", cancel);
|
|
17
|
+
const budget = new MessageBudget(30_000, controller.signal);
|
|
18
|
+
try {
|
|
19
|
+
// Keep home selection on this command path and after syntax validation.
|
|
20
|
+
const { messageCodexHome } = await import("./message-runtime");
|
|
21
|
+
const home = messageCodexHome(env);
|
|
22
|
+
if (args.action === "sessions") {
|
|
23
|
+
const sessions = await localSessions(home, budget);
|
|
24
|
+
if (args.json) console.log(JSON.stringify({ schema: "ocx-message-sessions/1", sessions }));
|
|
25
|
+
else for (const session of sessions) console.log(terminalSafeText(`${session.id}\t${session.status}\t${session.name ?? "(unnamed)"}`));
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
const body = await readMessageInput(Bun.stdin.stream(), budget);
|
|
29
|
+
const receipt = await sendLocalMessage({ ...args, body },
|
|
30
|
+
{ home, senderId: env.CODEX_THREAD_ID }, budget);
|
|
31
|
+
if (args.json) console.log(JSON.stringify(receipt));
|
|
32
|
+
else console.log(terminalSafeText(`${receipt.status}: ${receipt.messageId}${receipt.error ? ` — ${receipt.error.message}` : " (submitted, not proof of processing)"}`));
|
|
33
|
+
return receipt.status === "queued" ? 0 : receipt.status === "unknown" ? 3 : 1;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const failure = { schema: "ocx-message-error/1", ...(args.action === "send" ? { status: "not_sent" } : {}), error: messageFailure(error) };
|
|
36
|
+
if (args.json) console.log(JSON.stringify(failure));
|
|
37
|
+
else console.error(failure.error.message);
|
|
38
|
+
return 1;
|
|
39
|
+
} finally {
|
|
40
|
+
budget.dispose();
|
|
41
|
+
process.removeListener("SIGINT", cancel); process.removeListener("SIGTERM", cancel);
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/cli/observe.ts
CHANGED
|
@@ -129,7 +129,9 @@ async function explain(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
129
129
|
rejectArgs(args, USAGE);
|
|
130
130
|
const encoded = encodeURIComponent(requestId);
|
|
131
131
|
const result = await runtimeRequest(`/api/request-history/${encoded}/route-decision`, {}, deps);
|
|
132
|
-
|
|
132
|
+
// One entry per line: printData escapes control characters per entry, so a single
|
|
133
|
+
// pretty-printed string would print its line breaks as literal `\x0a`.
|
|
134
|
+
printData(result, wantsJson, wantsJson ? undefined : JSON.stringify(result, null, 2).split("\n"));
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
async function rebuildIndex(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
package/src/cli/provider.ts
CHANGED
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* show <name> Show provider config details (secrets masked)
|
|
9
9
|
* set-default <name> Change the default provider
|
|
10
10
|
*/
|
|
11
|
-
import { hasOwnProvider, isValidProviderName, loadConfig, sanitizeModelCostsForDisplay, saveConfig, validateConfigCandidate } from "../config";
|
|
11
|
+
import { hasOwnProvider, isValidProviderName, loadConfig, sanitizeModelCostsForDisplay, saveConfig, validateConfigCandidate, withConfigMutationLockSync } from "../config";
|
|
12
12
|
import { apiKeyTransportConfigError, modelCapabilitiesConfigError, mergeModelCapabilities } from "../config/provider-validation";
|
|
13
13
|
import { hasHelpFlag, printSubcommandUsage } from "./help";
|
|
14
14
|
import { getProviderRegistryEntry, PROVIDER_REGISTRY } from "../providers/registry";
|
|
15
15
|
import { providerConfigSeed } from "../providers/derive";
|
|
16
|
+
import { assertAnthropicInstanceLoginConfig } from "../oauth/store-anthropic-instance";
|
|
16
17
|
import { dropProviderCustomModels } from "../providers/provider-id-rewrite";
|
|
17
18
|
import type { OcxProviderConfig } from "../types";
|
|
18
19
|
import { findLiveProxy } from "../server/proxy-liveness";
|
|
@@ -249,6 +250,17 @@ async function handleAdd(args: string[], deps: ProviderCommandDeps): Promise<voi
|
|
|
249
250
|
|
|
250
251
|
if (responsesPath !== undefined) provConfig.responsesPath = responsesPath;
|
|
251
252
|
if (authMode !== undefined) provConfig.authMode = authMode as OcxProviderConfig["authMode"];
|
|
253
|
+
if (name === "anthropic2") {
|
|
254
|
+
if (provConfig.adapter !== "anthropic" || provConfig.authMode !== "oauth") delete provConfig.anthropicOAuthInstance;
|
|
255
|
+
if (provConfig.anthropicOAuthInstance) {
|
|
256
|
+
try { assertAnthropicInstanceLoginConfig(config, name); }
|
|
257
|
+
catch {
|
|
258
|
+
console.error("Error: cannot add Pool 2 over an existing custom or unreadable provider configuration; resolve it first.");
|
|
259
|
+
process.exitCode = 2;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
252
264
|
if (name === "openai" && (authMode !== undefined || responsesPath !== undefined)
|
|
253
265
|
&& !isCanonicalOpenAiForwardProvider(provConfig)) {
|
|
254
266
|
console.error("Error: Canonical OpenAI must keep its built-in forward destination and authentication. Use a separate provider name for a custom endpoint.");
|
|
@@ -315,11 +327,22 @@ async function handleAdd(args: string[], deps: ProviderCommandDeps): Promise<voi
|
|
|
315
327
|
return;
|
|
316
328
|
}
|
|
317
329
|
const { initializeProviderModelSelection } = await import("../providers/initial-model-selection");
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
330
|
+
let markerCollision = false;
|
|
331
|
+
withConfigMutationLockSync(() => {
|
|
332
|
+
if (provConfig.anthropicOAuthInstance) {
|
|
333
|
+
try { assertAnthropicInstanceLoginConfig(config, name); }
|
|
334
|
+
catch { markerCollision = true; return; }
|
|
335
|
+
}
|
|
336
|
+
initializeProviderModelSelection(name, provConfig, existingProvider, config);
|
|
337
|
+
config.providers[name] = provConfig;
|
|
338
|
+
if (setDefault) config.defaultProvider = name;
|
|
339
|
+
validateAndSave(config);
|
|
340
|
+
});
|
|
341
|
+
if (markerCollision) {
|
|
342
|
+
console.error("Error: provider configuration changed while adding Pool 2; resolve the ownership collision first.");
|
|
343
|
+
process.exitCode = 2;
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
323
346
|
|
|
324
347
|
let sync: LocalSyncResult | undefined;
|
|
325
348
|
if (wantsSync) {
|
package/src/cli/registry.ts
CHANGED
|
@@ -8,6 +8,12 @@ export interface CliCommandEntry {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
11
|
+
{
|
|
12
|
+
name: "message",
|
|
13
|
+
usage: "ocx message <sessions|send> [options]",
|
|
14
|
+
summary: "Discover loaded local Codex sessions and submit one queued peer message.",
|
|
15
|
+
details: ["Local Unix transport only; starts no daemon. queued is submission, not processing; never replay an unknown send."],
|
|
16
|
+
},
|
|
11
17
|
{
|
|
12
18
|
name: "chatgpt",
|
|
13
19
|
usage: "ocx chatgpt <launch|restore|status>",
|
|
@@ -467,8 +473,8 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
467
473
|
},
|
|
468
474
|
{
|
|
469
475
|
name: "export",
|
|
470
|
-
usage: "ocx export --client <opencode|pi|omp|hermes|openclaw|kimi|gajae|dsh|mcode|zcode|prime|aside|raycast|omo|cline|kilo|droid> [--json] [--out <path>] [--force]",
|
|
471
|
-
summary: "Print a client config (OpenCode, Pi, OMP, Hermes, OpenClaw, Kimi Code, gjc, DeepSeek Harness, MiniMax Code, ZCode, Prime Agent, Aside, Raycast, omo, Cline, Kilo, Factory Droid) wired to the running proxy.",
|
|
476
|
+
usage: "ocx export --client <opencode|pi|omp|hermes|openclaw|kimi|gajae|dsh|mcode|zcode|commandcode|prime|aside|raycast|omo|cline|kilo|droid> [--json] [--out <path>] [--force]",
|
|
477
|
+
summary: "Print a client config (OpenCode, Pi, OMP, Hermes, OpenClaw, Kimi Code, gjc, DeepSeek Harness, MiniMax Code, ZCode, Command Code, Prime Agent, Aside, Raycast, omo, Cline, Kilo, Factory Droid) wired to the running proxy.",
|
|
472
478
|
details: [
|
|
473
479
|
"--json prints the generated document as JSON on stdout; use --out for the client's native format.",
|
|
474
480
|
"--out <path> writes the native config there and refuses to replace an existing file without --force.",
|
|
@@ -598,6 +604,22 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
598
604
|
"Select OpenCodex Proxy/<provider>/<model> from ZCode's model picker.",
|
|
599
605
|
],
|
|
600
606
|
},
|
|
607
|
+
{
|
|
608
|
+
name: "commandcode",
|
|
609
|
+
usage: "ocx commandcode [status|show|list|enable|disable|history|journal|restore --op <opId>] [--json]",
|
|
610
|
+
summary: "Connect Command Code CLI to the proxy via its managed provider.",
|
|
611
|
+
details: [
|
|
612
|
+
"Alias of ocx integration client <sub> --client commandcode.",
|
|
613
|
+
"enable manages the opencodex block under the existing provider or providers root in ~/.commandcode/providers.json; disable removes only that block.",
|
|
614
|
+
"Command Code reads providers on startup.",
|
|
615
|
+
"`restore --op <opId>` replays the exact previous managed block; take the id from `ocx commandcode history`.",
|
|
616
|
+
],
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
name: "cmd",
|
|
620
|
+
usage: "ocx cmd [status|show|list|enable|disable|history|journal|restore --op <opId>] [--json]",
|
|
621
|
+
summary: "Alias of ocx commandcode.",
|
|
622
|
+
},
|
|
601
623
|
{
|
|
602
624
|
name: "restart",
|
|
603
625
|
usage: "ocx restart",
|
package/src/cli/resolve.ts
CHANGED
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
} from "../service/ownership-compatibility";
|
|
60
60
|
import { observeManagingClis } from "../service/managing-cli";
|
|
61
61
|
import { SERVICE_OWNERSHIP_MINIMUM_CLI_VERSION } from "../service/install-state-contract.mjs";
|
|
62
|
+
import { inspectDesktopSupervision } from "../service/desktop-supervision.mjs";
|
|
62
63
|
import { computeVersionSkew, type VersionSkew } from "./version-skew";
|
|
63
64
|
|
|
64
65
|
/** Wire version of the resolve document. Bump only on an incompatible shape change. */
|
|
@@ -123,6 +124,12 @@ export interface ResolveJson {
|
|
|
123
124
|
* takeover. Additive within ocx-resolve/1; older shells ignore it and read as unknown.
|
|
124
125
|
*/
|
|
125
126
|
versionSkew?: VersionSkew;
|
|
127
|
+
supervisor?: {
|
|
128
|
+
kind: "desktop" | "none" | "unknown" | "unsupported";
|
|
129
|
+
supervisorPid?: number;
|
|
130
|
+
runtimePid?: number;
|
|
131
|
+
app?: string;
|
|
132
|
+
};
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
export type ResolveTakeover =
|
|
@@ -160,6 +167,7 @@ export interface ResolveIo {
|
|
|
160
167
|
observeManagers?: (
|
|
161
168
|
state: ServiceInstallState | null,
|
|
162
169
|
) => Readonly<Record<ManagingCliRole, ManagingCliObservation>>;
|
|
170
|
+
inspectSupervision?: typeof inspectDesktopSupervision;
|
|
163
171
|
stdout?: { log: (s: string) => void };
|
|
164
172
|
stderr?: { error: (s: string) => void };
|
|
165
173
|
}
|
|
@@ -187,6 +195,7 @@ export function buildResolveJson(
|
|
|
187
195
|
cliVersion: string,
|
|
188
196
|
ownership: ServiceOwnershipResolution,
|
|
189
197
|
takeover: ResolveTakeover,
|
|
198
|
+
supervision?: ReturnType<typeof inspectDesktopSupervision>,
|
|
190
199
|
): ResolveJson {
|
|
191
200
|
const configured = config.port ?? RESOLVE_DEFAULT_PORT;
|
|
192
201
|
return {
|
|
@@ -199,6 +208,9 @@ export function buildResolveJson(
|
|
|
199
208
|
source: live ? live.source : "config",
|
|
200
209
|
},
|
|
201
210
|
liveness: livenessJson(live),
|
|
211
|
+
...(live && supervision ? { supervisor: supervision.kind === "desktop"
|
|
212
|
+
? { kind: supervision.kind, supervisorPid: supervision.supervisorPid, runtimePid: supervision.runtimePid, app: supervision.app }
|
|
213
|
+
: { kind: supervision.kind } } : {}),
|
|
202
214
|
ownership,
|
|
203
215
|
takeover,
|
|
204
216
|
// A proven absence has nothing to skew against; the field is omitted there rather
|
|
@@ -216,7 +228,8 @@ function reportHuman(json: ResolveJson, stdout: { log: (s: string) => void }): v
|
|
|
216
228
|
if (live.status === "live") {
|
|
217
229
|
const pidText = live.pid === null ? "unknown" : String(live.pid);
|
|
218
230
|
const versionText = live.version ?? "unknown version";
|
|
219
|
-
|
|
231
|
+
const supervisor = json.supervisor?.kind === "desktop" ? `; supervisor: desktop (pid ${json.supervisor.supervisorPid})` : "";
|
|
232
|
+
stdout.log(`Proxy live on port ${json.port.effective} (PID ${pidText}, ${versionText}); effective port ${json.port.effective}${supervisor}.`);
|
|
220
233
|
} else {
|
|
221
234
|
stdout.log(`No live proxy (absence proven); effective port ${json.port.effective} (configured).`);
|
|
222
235
|
}
|
|
@@ -353,7 +366,17 @@ export async function runResolve(args: ResolveArgs, io: ResolveIo = {}): Promise
|
|
|
353
366
|
};
|
|
354
367
|
}
|
|
355
368
|
}
|
|
356
|
-
|
|
369
|
+
let supervision: ReturnType<typeof inspectDesktopSupervision> | undefined;
|
|
370
|
+
if (live) {
|
|
371
|
+
try {
|
|
372
|
+
supervision = live.pid === null ? { kind: "unknown", reason: "pid-unavailable", desktopSeen: false }
|
|
373
|
+
: (io.inspectSupervision ?? inspectDesktopSupervision)({ targetPid: live.pid });
|
|
374
|
+
} catch {
|
|
375
|
+
// Supervision is independent evidence; its failure cannot invalidate established liveness.
|
|
376
|
+
supervision = { kind: "unknown", reason: "probe-failed", desktopSeen: false };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const json = buildResolveJson(diagnostics.config, live, configHome, cliVersion(), ownership, takeover, supervision);
|
|
357
380
|
if (args.json) stdout.log(JSON.stringify(json));
|
|
358
381
|
else reportHuman(json, stdout);
|
|
359
382
|
return 0;
|
package/src/cli/root.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI head: version/help early exits,
|
|
3
|
-
* Codex-shim auto-restore preflight
|
|
2
|
+
* CLI head: version/help early exits, ready/resolve pre-parse, lifecycle version
|
|
3
|
+
* notice, then bounded Codex-shim auto-restore preflight (Phase 1 of the CLI
|
|
4
4
|
* deepening — moved out of src/cli/index.ts).
|
|
5
5
|
*
|
|
6
6
|
* `parseCliHead` is pure (no I/O, no process access) so the ordering and the
|
|
@@ -16,6 +16,7 @@ import { maybeAutoRestoreCodexShim } from "./codex-shim-autorestore";
|
|
|
16
16
|
import { findCommand } from "./registry";
|
|
17
17
|
import { printUnknownCommand } from "./help-recovery";
|
|
18
18
|
import { noteCredentialArgv, redactSecretArgs } from "./secret-args";
|
|
19
|
+
import { maybeNoticeVersionSkew } from "./version-skew-notice";
|
|
19
20
|
|
|
20
21
|
export interface CliHead {
|
|
21
22
|
kind: "version" | "help" | "ready" | "resolve" | "command";
|
|
@@ -141,6 +142,7 @@ export async function runCli(argv: string[]): Promise<CliHead> {
|
|
|
141
142
|
console.error(uninstallError);
|
|
142
143
|
process.exit(2);
|
|
143
144
|
}
|
|
145
|
+
await maybeNoticeVersionSkew(head.command, head.args);
|
|
144
146
|
maybeAutoRestoreCodexShim(head.command, head.args);
|
|
145
147
|
return head;
|
|
146
148
|
}
|
package/src/cli/status.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { collectCliPathDiagnostics, formatCliStatusHealthLabel, type CliPathDiagnostics } from "./cli-path-diagnostics";
|
|
1
2
|
import type { CodexMainAccountPolicyHealth } from "../oauth/health";
|
|
2
3
|
import { durableBunRuntime } from "../lib/bun-runtime";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -7,6 +8,7 @@ import { diagnoseCodexBundledPlugins, type CodexPluginsDiagnostic } from "../cod
|
|
|
7
8
|
import { findLiveProxy, probeHostname } from "../server/proxy-liveness";
|
|
8
9
|
import type { OcxConfig } from "../types";
|
|
9
10
|
import { diagnoseService, serviceLogPath } from "../service";
|
|
11
|
+
import { inspectDesktopSupervision } from "../service/desktop-supervision.mjs";
|
|
10
12
|
import { collectStartupHealth, startupHealthReadBudgetMs, type StartupHealth } from "../codex/autostart-health";
|
|
11
13
|
import { getCodexRoutingKind } from "../codex/inject";
|
|
12
14
|
import { missingOwnedCatalogPath } from "../codex/inject/config-toml";
|
|
@@ -215,6 +217,8 @@ export type CliStatusJson = {
|
|
|
215
217
|
* ignores the key is unaffected, and `proxyVersion` is null when nothing is live.
|
|
216
218
|
*/
|
|
217
219
|
versionSkew: VersionSkew;
|
|
220
|
+
cliCommand: CliPathDiagnostics;
|
|
221
|
+
startupSource: "live" | "local" | "local-supervision-override";
|
|
218
222
|
};
|
|
219
223
|
|
|
220
224
|
export type CliStatusView = {
|
|
@@ -259,12 +263,28 @@ export async function fetchLiveStartupHealth(
|
|
|
259
263
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
|
|
260
264
|
const row = payload as Record<string, unknown>;
|
|
261
265
|
if (row.status !== "native" && row.status !== "protected" && row.status !== "at-risk") return null;
|
|
262
|
-
if (row.protection !== "service" && row.protection !== "shim" && row.protection !== "none") return null;
|
|
266
|
+
if (row.protection !== "service" && row.protection !== "desktop" && row.protection !== "shim" && row.protection !== "none") return null;
|
|
263
267
|
if (row.routingKind !== "native" && row.routingKind !== "opencodex-local"
|
|
264
268
|
&& row.routingKind !== "custom-local" && row.routingKind !== "custom-remote" && row.routingKind !== "unknown") return null;
|
|
265
269
|
if (row.shimCoverage !== "full" && row.shimCoverage !== "cli-only" && row.shimCoverage !== "none") return null;
|
|
266
270
|
if (typeof row.platform !== "string") return null;
|
|
267
271
|
if (row.recommendedCommand !== null && typeof row.recommendedCommand !== "string") return null;
|
|
272
|
+
if (row.recommendedAction !== undefined && row.recommendedAction !== null
|
|
273
|
+
&& (typeof row.recommendedAction !== "string" || row.recommendedAction.length > 512)) return null;
|
|
274
|
+
if (row.desktop !== undefined) {
|
|
275
|
+
if (!row.desktop || typeof row.desktop !== "object" || Array.isArray(row.desktop)) return null;
|
|
276
|
+
const desktop = row.desktop as Record<string, unknown>;
|
|
277
|
+
for (const key of ["owned", "loginEnabled", "running", "viable"] as const) {
|
|
278
|
+
if (typeof desktop[key] !== "boolean") return null;
|
|
279
|
+
}
|
|
280
|
+
if (desktop.supervisor !== undefined) {
|
|
281
|
+
if (!desktop.supervisor || typeof desktop.supervisor !== "object" || Array.isArray(desktop.supervisor)) return null;
|
|
282
|
+
const supervisor = desktop.supervisor as Record<string, unknown>;
|
|
283
|
+
if (typeof supervisor.supervisorPid !== "number" || !Number.isFinite(supervisor.supervisorPid)
|
|
284
|
+
|| typeof supervisor.runtimePid !== "number" || !Number.isFinite(supervisor.runtimePid)
|
|
285
|
+
|| typeof supervisor.app !== "string") return null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
268
288
|
if (!row.commands || typeof row.commands !== "object" || Array.isArray(row.commands)) return null;
|
|
269
289
|
for (const key of ["installService", "repairService", "installShim", "restoreNative"] as const) {
|
|
270
290
|
if (typeof (row.commands as Record<string, unknown>)[key] !== "string") return null;
|
|
@@ -286,12 +306,36 @@ export async function fetchLiveStartupHealth(
|
|
|
286
306
|
return payload as StartupHealth;
|
|
287
307
|
}
|
|
288
308
|
|
|
289
|
-
/**
|
|
309
|
+
/** Older runtimes cannot report supervision; only that missing capability permits a local override. */
|
|
290
310
|
export function selectStatusStartupHealth(
|
|
291
311
|
liveStartup: StartupHealth | null,
|
|
292
312
|
fallback: () => StartupHealth,
|
|
293
|
-
)
|
|
294
|
-
|
|
313
|
+
supervision?: () => ReturnType<typeof inspectDesktopSupervision>,
|
|
314
|
+
livePid?: number | null,
|
|
315
|
+
): { startup: StartupHealth; startupSource: CliStatusJson["startupSource"] } {
|
|
316
|
+
if (!liveStartup) return { startup: fallback(), startupSource: "local" };
|
|
317
|
+
if (liveStartup.desktop === undefined && supervision) {
|
|
318
|
+
let evidence: ReturnType<typeof inspectDesktopSupervision> | undefined;
|
|
319
|
+
try { evidence = supervision(); }
|
|
320
|
+
catch { /* Failed optional evidence cannot replace an attested live verdict. */ }
|
|
321
|
+
if (evidence?.kind === "desktop" && evidence.runtimePid === livePid) {
|
|
322
|
+
const local = fallback();
|
|
323
|
+
const supervisor = local.desktop?.supervisor;
|
|
324
|
+
// The local probe runs later; it must still describe the same unowned process pair.
|
|
325
|
+
if (local.desktop?.owned === false && supervisor
|
|
326
|
+
&& supervisor.runtimePid === evidence.runtimePid && supervisor.supervisorPid === evidence.supervisorPid) {
|
|
327
|
+
return { startup: local, startupSource: "local-supervision-override" };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return { startup: liveStartup, startupSource: "live" };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function runtimeSupervisorLine(startup: StartupHealth): string | null {
|
|
335
|
+
const supervisor = startup.desktop?.supervisor;
|
|
336
|
+
return supervisor
|
|
337
|
+
? `Runtime supervisor: OpenCodex Desktop (pid ${supervisor.supervisorPid}, ${supervisor.app}); durable owner: ${startup.desktop?.owned ? "desktop" : "none"}`
|
|
338
|
+
: null;
|
|
295
339
|
}
|
|
296
340
|
|
|
297
341
|
/** Build the service summary from the same startup source that `ocx status` selected. */
|
|
@@ -299,18 +343,23 @@ export function statusServiceSummary(
|
|
|
299
343
|
liveStartup: StartupHealth | null,
|
|
300
344
|
service: Pick<ReturnType<typeof diagnoseService>, "installed" | "summary">,
|
|
301
345
|
live: boolean,
|
|
346
|
+
// Which verdict was selected. A live proxy can still be described by the local reading when
|
|
347
|
+
// the runtime predates supervision reporting, so the label follows the verdict, not liveness.
|
|
348
|
+
startupSource?: CliStatusJson["startupSource"],
|
|
302
349
|
): string {
|
|
303
|
-
if (liveStartup) {
|
|
350
|
+
if (live && liveStartup) {
|
|
351
|
+
const supervisor = liveStartup.desktop?.supervisor ? "OpenCodex Desktop supervises the running proxy; " : "";
|
|
304
352
|
if (liveStartup.protection === "service" && liveStartup.serviceViable) {
|
|
305
|
-
return
|
|
353
|
+
return `${supervisor}running under the live managed service (logs: ${serviceLogPath()})`;
|
|
306
354
|
}
|
|
307
355
|
const state = [
|
|
308
356
|
liveStartup.serviceInstalled ? "installed" : "absent",
|
|
309
357
|
liveStartup.serviceRunning ? "running" : "not running",
|
|
310
358
|
liveStartup.serviceViable ? "viable" : "not viable",
|
|
311
359
|
].join(", ");
|
|
312
|
-
const action = liveStartup.recommendedCommand ? `; run '${liveStartup.recommendedCommand}'` : "";
|
|
313
|
-
|
|
360
|
+
const action = !liveStartup.desktop?.supervisor && liveStartup.recommendedCommand ? `; run '${liveStartup.recommendedCommand}'` : "";
|
|
361
|
+
const label = (startupSource ?? (live ? "live" : "local")) === "live" ? "live" : "local";
|
|
362
|
+
return `${supervisor}${label} startup reports service ${state}${action} (logs: ${serviceLogPath()})`;
|
|
314
363
|
}
|
|
315
364
|
return service.installed && !live
|
|
316
365
|
? `${service.summary} — registered but NOT serving; see ${serviceLogPath()} and re-run 'ocx service repair'`
|
|
@@ -680,6 +729,7 @@ export function missingCodexCatalogLines(missingCatalogPath: string | null): str
|
|
|
680
729
|
|
|
681
730
|
/** `mainAccountPolicy`: only `--json` asks; the human report reads the same accounts once via OAuth health. */
|
|
682
731
|
export async function collectStatus(options: { mainAccountPolicy?: boolean } = {}): Promise<CliStatusView> {
|
|
732
|
+
const cliCommand = collectCliPathDiagnostics();
|
|
683
733
|
const configDiagnostics = readConfigDiagnostics();
|
|
684
734
|
const config = configDiagnostics.config;
|
|
685
735
|
const claudeDesktop = {
|
|
@@ -757,14 +807,14 @@ export async function collectStatus(options: { mainAccountPolicy?: boolean } = {
|
|
|
757
807
|
// A service can be registered and still not serve: the manager reports the job
|
|
758
808
|
// either way. When the identity-probed live proxy provides an attested startup verdict,
|
|
759
809
|
// prefer it over a shell-local service-manager probe that lacks the service environment.
|
|
760
|
-
const serviceSummary = statusServiceSummary(liveStartup, service, Boolean(live));
|
|
761
810
|
const codexShim = diagnoseCodexShim();
|
|
762
811
|
const codexShimSummary = codexShim.summary;
|
|
763
|
-
const startup = selectStatusStartupHealth(liveStartup, () => collectStartupHealth(config, {
|
|
812
|
+
const { startup, startupSource } = selectStatusStartupHealth(liveStartup, () => collectStartupHealth(config, {
|
|
764
813
|
service,
|
|
765
814
|
shim: codexShim,
|
|
766
815
|
routingKind: getCodexRoutingKind(),
|
|
767
|
-
}));
|
|
816
|
+
}), live?.pid != null ? () => inspectDesktopSupervision({ targetPid: live.pid! }) : undefined, live?.pid);
|
|
817
|
+
const serviceSummary = statusServiceSummary(startup, service, Boolean(live), startupSource);
|
|
768
818
|
const codexPlugins = diagnoseCodexBundledPlugins();
|
|
769
819
|
const lastClamp = loadLastEffortClamp();
|
|
770
820
|
const clampActive = effortClampAppliesToRuntime(lastClamp, resolvedRuntime.runtime);
|
|
@@ -869,7 +919,7 @@ export async function collectStatus(options: { mainAccountPolicy?: boolean } = {
|
|
|
869
919
|
|
|
870
920
|
return {
|
|
871
921
|
proxyLabel,
|
|
872
|
-
healthLabel: health.label,
|
|
922
|
+
healthLabel: formatCliStatusHealthLabel(health.label, cliCommand, remoteHub.connected),
|
|
873
923
|
json: {
|
|
874
924
|
...(mainAccountHardLock ? { mainAccountHardLock } : {}),
|
|
875
925
|
schemaVersion: 1,
|
|
@@ -932,6 +982,8 @@ export async function collectStatus(options: { mainAccountPolicy?: boolean } = {
|
|
|
932
982
|
// fact about this install, not about the Codex runtime, and filing it there would
|
|
933
983
|
// print it under the wrong heading (#2701).
|
|
934
984
|
versionSkew,
|
|
985
|
+
cliCommand,
|
|
986
|
+
startupSource,
|
|
935
987
|
},
|
|
936
988
|
};
|
|
937
989
|
}
|
|
@@ -16,7 +16,8 @@ function parseMarker(raw: string): UpdateRestartChildMarker {
|
|
|
16
16
|
if (!value || !Number.isSafeInteger(value.deadlineAt) || !Number.isInteger(value.port)
|
|
17
17
|
|| typeof value.hostname !== "string" || value.port < 1 || value.port > 65535 || typeof value.version !== "string"
|
|
18
18
|
|| value.version === "0.0.0" || !parseStrictSemver(value.version) || !value.home
|
|
19
|
-
|| !Number.isSafeInteger(value.home.revision)
|
|
19
|
+
|| !Number.isSafeInteger(value.home.revision) || value.home.serviceRecord?.schema !== 1
|
|
20
|
+
|| typeof value.home.serviceRecord.digest !== "string" || !/^[a-f0-9]{64}$/.test(value.home.serviceRecord.digest)) throw new Error("update_restart_child_marker_invalid");
|
|
20
21
|
for (const directory of [value.home.config, value.home.codex]) {
|
|
21
22
|
// Windows reports 64-bit NTFS file ids (MFT sequence in the high 16 bits) as doubles above
|
|
22
23
|
// 2^53. Parent and child read them through the same stat API, so any finite integer is a
|
|
@@ -56,7 +57,7 @@ export function admitUpdateRestartChild(argv: string[], io: UpdateRestartChildIo
|
|
|
56
57
|
if (now() >= marker.deadlineAt) throw new Error("update_restart_deadline_expired");
|
|
57
58
|
const bindHost = (host: string) => !host.trim() || /^localhost\.?$/i.test(host.trim()) ? "127.0.0.1" : host.trim();
|
|
58
59
|
if (port !== marker.port || bindHost(hostname) !== bindHost(marker.hostname) || (io.version ?? packageVersion)() !== marker.version) throw new Error("update_restart_child_identity_changed");
|
|
59
|
-
(io.checkHome ?? assertUpdateRestartHome)(marker.home);
|
|
60
|
+
(io.checkHome ?? (home => assertUpdateRestartHome(home, marker.deadlineAt)))(marker.home);
|
|
60
61
|
(io.checkState ?? (expected => {
|
|
61
62
|
assertUpdateRestartConfiguration(expected.hostname);
|
|
62
63
|
const current = readRuntimePort();
|
|
@@ -65,6 +66,7 @@ export function admitUpdateRestartChild(argv: string[], io: UpdateRestartChildIo
|
|
|
65
66
|
throw new Error("update_restart_competing_runtime");
|
|
66
67
|
}
|
|
67
68
|
}))(marker);
|
|
69
|
+
if (now() >= marker.deadlineAt) throw new Error("update_restart_deadline_expired");
|
|
68
70
|
};
|
|
69
71
|
check();
|
|
70
72
|
const lease = (io.acquire ?? (waitMs => acquireOwnershipMutationLease(serviceStatePaths(), { waitMs })))(Math.min(2000, marker.deadlineAt - now()));
|