@bitkyc08/opencodex 2.76.0 → 2.78.0-preview.20261005
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/bin/ocx.mjs +11 -10
- package/gui/dist/assets/App-CppMz-6w.js +54 -0
- package/gui/dist/assets/App-lCIslZ1L.css +1 -0
- package/gui/dist/assets/{Tray-DjNwwrWS.js → Tray-CJWQqtVv.js} +1 -1
- package/gui/dist/assets/index-DQt9srNi.js +94 -0
- package/gui/dist/assets/index-DmQB8D_R.css +1 -0
- package/gui/dist/assets/quota-summary-BMsyoZH2.js +1 -0
- package/gui/dist/index.html +4 -3
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +19 -2
- package/src/adapters/anthropic/client-identity.ts +13 -4
- package/src/adapters/anthropic/native-client-preamble.ts +31 -0
- package/src/adapters/anthropic/passthrough.ts +103 -25
- package/src/adapters/antigravity-refusal.ts +28 -0
- package/src/adapters/cursor/protobuf-request.ts +6 -0
- package/src/adapters/cursor/request-builder.ts +3 -0
- package/src/adapters/cursor/structured-output.ts +13 -0
- package/src/adapters/cursor/types.ts +2 -0
- package/src/adapters/devin/cloud-direct/chat.ts +7 -2
- package/src/adapters/devin/trajectory.ts +49 -0
- package/src/adapters/devin.ts +13 -0
- package/src/adapters/ollama-native.ts +62 -19
- package/src/adapters/openai-responses/passthrough.ts +133 -103
- package/src/adapters/openai-responses/request-strips.ts +10 -4
- package/src/adapters/zed.ts +26 -4
- package/src/bridge/response-json.ts +14 -14
- package/src/bridge/sse.ts +15 -5
- package/src/chatgpt/app-server-shim/bundle-trust.ts +40 -10
- package/src/chatgpt/app-server-shim/gate-rewrite.ts +3 -0
- package/src/claude/agents-inject.ts +4 -43
- package/src/claude/gateway-cache.ts +16 -13
- package/src/claude/inbound-model-options.ts +9 -2
- package/src/claude/intercept/cli-catalog.ts +27 -16
- package/src/claude/intercept/client-hello.ts +103 -0
- package/src/claude/intercept/model-bindings.ts +3 -2
- package/src/claude/intercept/picker-bootstrap.ts +37 -16
- package/src/claude/intercept/picker-budget.ts +41 -0
- package/src/claude/intercept/picker-listener.ts +279 -66
- package/src/claude/outbound.ts +2 -0
- package/src/claude/subagent-force-status.ts +53 -0
- package/src/claude/subagent-model.ts +93 -0
- package/src/cli/access-audio-input.ts +84 -0
- package/src/cli/access-audio-live.ts +99 -0
- package/src/cli/access-audio.ts +123 -0
- package/src/cli/access-data-client.ts +127 -0
- package/src/cli/access-data-plane.ts +104 -0
- package/src/cli/access-data-response.ts +76 -0
- package/src/cli/access.ts +65 -2
- package/src/cli/account-api.ts +33 -6
- package/src/cli/account-auth.ts +111 -42
- package/src/cli/account-key-quota.ts +118 -0
- package/src/cli/account-main.ts +11 -4
- package/src/cli/account-next-actions.ts +63 -0
- package/src/cli/account-policy-dto.ts +68 -0
- package/src/cli/account-policy.ts +167 -0
- package/src/cli/account-target.ts +8 -0
- package/src/cli/account.ts +37 -9
- package/src/cli/agent-runtime-settings.ts +130 -0
- package/src/cli/agent-settings.ts +101 -0
- package/src/cli/agent.ts +29 -2
- package/src/cli/alias.ts +7 -2
- package/src/cli/aside-profiles.ts +2 -2
- package/src/cli/capabilities-access-remote.ts +147 -0
- package/src/cli/capabilities-accounts.ts +195 -0
- package/src/cli/capabilities-agents-routing.ts +414 -0
- package/src/cli/capabilities-base.ts +783 -0
- package/src/cli/capabilities-command.ts +1 -0
- package/src/cli/capabilities-integrations.ts +264 -0
- package/src/cli/capabilities-lab.ts +214 -0
- package/src/cli/capabilities-observe-system.ts +662 -0
- package/src/cli/capabilities-provider-models.ts +302 -0
- package/src/cli/capabilities.ts +30 -1190
- package/src/cli/capability-types.ts +54 -0
- package/src/cli/catalog-command-result.ts +57 -0
- package/src/cli/chatgpt-command.ts +35 -9
- package/src/cli/claude-agent-startup-sync.ts +17 -0
- package/src/cli/claude-desktop-profile.ts +100 -0
- package/src/cli/claude-desktop.ts +39 -6
- package/src/cli/claude.ts +22 -6
- package/src/cli/codex-shim-autorestore.ts +2 -1
- package/src/cli/codex-shim-readiness.ts +109 -0
- package/src/cli/combo-input.ts +88 -0
- package/src/cli/combo-result.ts +52 -0
- package/src/cli/combo-stats.ts +123 -0
- package/src/cli/combo.ts +37 -14
- package/src/cli/companion-timeline.ts +142 -0
- package/src/cli/companion-usage.ts +146 -0
- package/src/cli/companion.ts +12 -3
- package/src/cli/config-command.ts +24 -8
- package/src/cli/connect.ts +19 -0
- package/src/cli/dispatch.ts +55 -82
- package/src/cli/doctor.ts +22 -4
- package/src/cli/export-command.ts +20 -9
- package/src/cli/gui.ts +8 -3
- package/src/cli/help-catalog.ts +101 -0
- package/src/cli/help-models-context.ts +17 -0
- package/src/cli/help-navigation.ts +50 -0
- package/src/cli/help-recovery.ts +66 -0
- package/src/cli/help.ts +69 -11
- package/src/cli/index.ts +38 -27
- package/src/cli/injection-follow.ts +35 -0
- package/src/cli/inspect.ts +5 -0
- package/src/cli/integration-aside-sync.ts +69 -0
- package/src/cli/integration-cursor.ts +106 -0
- package/src/cli/integration-input.ts +60 -0
- package/src/cli/integration-journal.ts +49 -0
- package/src/cli/integration-plan-dto.ts +156 -0
- package/src/cli/integration-preview.ts +215 -0
- package/src/cli/integrations.ts +24 -18
- package/src/cli/json-input.ts +134 -0
- package/src/cli/link.ts +32 -4
- package/src/cli/local-sync-result.ts +41 -0
- package/src/cli/log-follow.ts +84 -0
- package/src/cli/log-view-filter.ts +146 -0
- package/src/cli/logout-command.ts +44 -0
- package/src/cli/model-picker-ordering.ts +137 -0
- package/src/cli/models-custom-input.ts +107 -0
- package/src/cli/models-custom-runtime.ts +112 -0
- package/src/cli/models-order.ts +194 -0
- package/src/cli/models-runtime-subcommands.ts +2 -0
- package/src/cli/models-runtime.ts +9 -1
- package/src/cli/models.ts +66 -81
- package/src/cli/observe-stream.ts +142 -0
- package/src/cli/observe.ts +75 -34
- package/src/cli/opencode.ts +54 -10
- package/src/cli/provider-batch.ts +77 -0
- package/src/cli/provider-lifecycle-runtime.ts +170 -0
- package/src/cli/provider-result.ts +124 -0
- package/src/cli/provider-runtime.ts +15 -12
- package/src/cli/provider-settings.ts +143 -0
- package/src/cli/provider.ts +116 -64
- package/src/cli/registry.ts +90 -24
- package/src/cli/remote-workspace-hub.ts +83 -0
- package/src/cli/remote-workspace.ts +6 -1
- package/src/cli/restart-failure.ts +27 -0
- package/src/cli/root.ts +42 -9
- package/src/cli/route-policy-write.ts +137 -0
- package/src/cli/route-policy.ts +8 -0
- package/src/cli/runtime-api.ts +57 -80
- package/src/cli/secret-args.ts +149 -0
- package/src/cli/settings-result.ts +30 -0
- package/src/cli/storage.ts +77 -7
- package/src/cli/system-command.ts +60 -2
- package/src/cli/system-restart-client.ts +20 -3
- package/src/cli/system-settings-parity.ts +83 -0
- package/src/cli/tray-proxy.ts +3 -2
- package/src/cli/update-restart-candidate.ts +21 -0
- package/src/cli/update-restart-child.ts +81 -0
- package/src/cli/update-restart-home.ts +38 -0
- package/src/cli/update-restart-transport.ts +193 -0
- package/src/cli/update-restart.ts +260 -0
- package/src/cli/usage-model-search.ts +58 -0
- package/src/cli/usage-report.ts +13 -5
- package/src/cli/v2-input.ts +66 -0
- package/src/cli/v2-local-output.ts +37 -0
- package/src/cli/v2-runtime.ts +90 -0
- package/src/cli/v2.ts +102 -144
- package/src/clients/config-export/contracts.ts +100 -0
- package/src/clients/config-export/droid.ts +54 -2
- package/src/clients/config-export/dsh.ts +21 -0
- package/src/clients/config-export/kilo.ts +12 -2
- package/src/clients/config-export/model-metadata.ts +10 -4
- package/src/clients/config-export/reasoning-metadata.ts +47 -0
- package/src/clients/config-export.ts +53 -67
- package/src/clients/omo-role-models.ts +17 -3
- package/src/codex/account-credit-use.ts +4 -2
- package/src/codex/account-runtime-state.ts +21 -0
- package/src/codex/account-store.ts +11 -47
- package/src/codex/account-usability.ts +11 -8
- package/src/codex/auth-api/account-list.ts +73 -63
- package/src/codex/auth-api/main-account-probe.ts +25 -12
- package/src/codex/auth-api/pool-mode-gate.ts +6 -9
- package/src/codex/auth-api/pool-quota-probe.ts +2 -4
- package/src/codex/auth-api/reset-credit-service.ts +3 -2
- package/src/codex/auth-context.ts +30 -11
- package/src/codex/catalog/aggregation.ts +3 -0
- package/src/codex/catalog/build-entries.ts +1 -1
- package/src/codex/catalog/bundled.ts +9 -2
- package/src/codex/catalog/heal-observation.ts +66 -0
- package/src/codex/catalog/model-hints.ts +2 -1
- package/src/codex/catalog/model-visibility.ts +6 -3
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-models.ts +13 -4
- package/src/codex/catalog/publication-observer.ts +19 -0
- package/src/codex/catalog/remote.ts +13 -15
- package/src/codex/catalog/restore.ts +19 -6
- package/src/codex/catalog/retained-sync.ts +60 -11
- package/src/codex/catalog/routed-removal.ts +116 -0
- package/src/codex/catalog/write-audit-contract.ts +21 -0
- package/src/codex/catalog/write-audit.ts +171 -0
- package/src/codex/catalog-self-heal.ts +284 -0
- package/src/codex/catalog-write-serialization.ts +71 -7
- package/src/codex/chatgpt-refresh-failure.ts +56 -0
- package/src/codex/codex-home-owner.ts +124 -0
- package/src/codex/convergence.ts +57 -16
- package/src/codex/features.ts +8 -1
- package/src/codex/inject/restore.ts +46 -4
- package/src/codex/inject.ts +11 -1
- package/src/codex/internal/catalog-writer.ts +120 -13
- package/src/codex/journal.ts +49 -13
- package/src/codex/main-account.ts +144 -11
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/quota-refresh-outcome.ts +17 -2
- package/src/codex/quota-types.ts +36 -1
- package/src/codex/quota.ts +67 -16
- package/src/codex/refresh.ts +20 -9
- package/src/codex/routing/cooldown-math.ts +10 -4
- package/src/codex/routing/selection.ts +11 -4
- package/src/codex/routing.ts +9 -9
- package/src/codex/runtime.ts +7 -1
- package/src/codex/shim-diagnostics.ts +80 -0
- package/src/codex/shim-fingerprint.ts +7 -2
- package/src/codex/shim-migration.ts +153 -0
- package/src/codex/shim-overlay.ts +509 -0
- package/src/codex/shim-path-resolution.ts +145 -0
- package/src/codex/shim-probe.ts +4 -3
- package/src/codex/shim-state-file.ts +93 -7
- package/src/codex/shim.ts +54 -107
- package/src/codex/subagent-model-fallback.ts +2 -1
- package/src/codex/sync.ts +24 -4
- package/src/combos/failover.ts +101 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/jev.ts +13 -3
- package/src/combos/resolve.ts +12 -0
- package/src/config/atomic-write.ts +13 -8
- package/src/config/diagnostics.ts +23 -1
- package/src/config/load-degrade.ts +7 -0
- package/src/config/provider-validation.ts +3 -0
- package/src/config/schema/chatgpt-desktop.ts +22 -0
- package/src/config/schema/compaction-triggers.ts +8 -0
- package/src/config/schema/config-schema.ts +43 -14
- package/src/config/schema/leaf-validators.ts +8 -3
- package/src/config/subagent-models.ts +12 -0
- package/src/generated/compatibility-version.json +645 -237
- package/src/grok/session-identity.ts +33 -0
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/merge.ts +13 -5
- package/src/integrations/mutation-plan.ts +50 -5
- package/src/integrations/omp-yaml-source.ts +181 -1
- package/src/integrations/ownership-policy.ts +66 -0
- package/src/integrations/registry.ts +50 -1
- package/src/integrations/state.ts +106 -19
- package/src/integrations/target.ts +25 -2
- package/src/integrations/writer.ts +107 -35
- package/src/lib/gui-pair-capability.ts +17 -0
- package/src/lib/package-tree-integrity.ts +42 -1
- package/src/lib/provider-client-headers.ts +62 -0
- package/src/lib/proxy-env.ts +3 -0
- package/src/lib/test-home-guard.ts +2 -1
- package/src/oauth/anthropic-account-refusal.ts +19 -8
- package/src/oauth/anthropic-continuity.ts +65 -0
- package/src/oauth/anthropic-identity.ts +75 -0
- package/src/oauth/anthropic-routing.ts +20 -15
- package/src/oauth/anthropic.ts +34 -3
- package/src/oauth/generic-account-failover.ts +21 -0
- package/src/oauth/health.ts +32 -5
- package/src/oauth/index.ts +42 -35
- package/src/oauth/local-token-detect.ts +7 -0
- package/src/oauth/pool-settings-capability.ts +9 -2
- package/src/oauth/refresh-policy.ts +2 -0
- package/src/oauth/store.ts +55 -6
- package/src/oauth/types.ts +9 -0
- package/src/protocols/encoders/messages.ts +4 -3
- package/src/protocols/plan-snapshot.ts +1 -1
- package/src/protocols/settings.ts +28 -4
- package/src/providers/antigravity-effort-families.ts +75 -0
- package/src/providers/antigravity-models.ts +54 -16
- package/src/providers/antigravity-wire-snapshot.ts +66 -0
- package/src/providers/derive.ts +2 -0
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/new-model-policy.ts +48 -10
- package/src/providers/registry/entries-extended.ts +3 -0
- package/src/providers/registry/model-ids.ts +2 -0
- package/src/providers/registry/model-seeds.ts +5 -0
- package/src/providers/registry/types.ts +4 -0
- package/src/responses/compaction.ts +19 -0
- package/src/router.ts +6 -0
- package/src/server/auth-cors.ts +9 -0
- package/src/server/caller-session-identity.ts +32 -0
- package/src/server/chat-completions.ts +10 -0
- package/src/server/claude-messages.ts +29 -15
- package/src/server/droid-reasoning-default.ts +50 -0
- package/src/server/github-copilot-responses-repair.ts +3 -6
- package/src/server/grok-responses-control-frame.ts +2 -2
- package/src/server/grok-responses-snapshot-repair.ts +4 -5
- package/src/server/gui-session.ts +33 -11
- package/src/server/index/claude-intercept-lifecycle.ts +4 -0
- package/src/server/index/serve-options.ts +13 -7
- package/src/server/index.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +12 -134
- package/src/server/management/config-routes.ts +1 -1
- package/src/server/management/context.ts +8 -1
- package/src/server/management/cursor-integration-routes.ts +3 -3
- package/src/server/management/integration-routes.ts +69 -11
- package/src/server/management/link-routes.ts +20 -13
- package/src/server/management/model-routes.ts +3 -3
- package/src/server/management/model-row-export-metadata.ts +150 -0
- package/src/server/management/model-rows.ts +148 -9
- package/src/server/management/native-integration-routes.ts +2 -2
- package/src/server/management/oauth-account-routes.ts +96 -30
- package/src/server/management/route-registry.ts +29 -27
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/subagent-model-routes.ts +214 -0
- package/src/server/messages-native-eligibility.ts +3 -6
- package/src/server/messages-native-oauth.ts +82 -35
- package/src/server/messages-native.ts +78 -16
- package/src/server/relay.ts +14 -44
- package/src/server/request-log-generation-window.ts +38 -0
- package/src/server/request-log.ts +13 -0
- package/src/server/responses/adapter-dispatch.ts +152 -28
- package/src/server/responses/codex-auth-error.ts +10 -2
- package/src/server/responses/combo-stream-preflight.ts +29 -3
- package/src/server/responses/compaction-routing.ts +10 -1
- package/src/server/responses/core-auth.ts +67 -30
- package/src/server/responses/core-codex-account.ts +6 -3
- package/src/server/responses/core-combo-failure.ts +5 -1
- package/src/server/responses/core-combo-native.ts +7 -1
- package/src/server/responses/core-combo.ts +64 -7
- package/src/server/responses/core-normalize.ts +7 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/fetch-helpers.ts +9 -0
- package/src/server/responses/passthrough-delivery.ts +1 -1
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/request-prepare.ts +5 -0
- package/src/server/responses/request-spend.ts +30 -30
- package/src/server/responses-custom-tool-repair.ts +2 -2
- package/src/server/responses-hosted-image-display.ts +70 -9
- package/src/server/responses-terminal-repair.ts +15 -2
- package/src/server/sse-frame-buffer.ts +38 -36
- package/src/server/sse-payload-rewrite.ts +78 -49
- package/src/server/ws-bridge.ts +1 -11
- package/src/service/cli.ts +6 -1
- package/src/service/ownership-mutation-lease.d.mts +26 -0
- package/src/service/ownership-mutation-lease.mjs +76 -1
- package/src/service/windows-taskxml.ts +1 -2
- package/src/service-manager-probe.ts +36 -2
- package/src/types/config.ts +6 -0
- package/src/types/provider.ts +28 -10
- package/src/update/index.ts +2 -1
- package/src/update/npm-invocation.mjs +35 -3
- package/src/update/proxy-liveness-probe.mjs +38 -8
- package/src/update/update-failure-guidance.d.mts +6 -0
- package/src/update/update-failure-guidance.mjs +30 -5
- package/src/usage/expected-prices.ts +9 -0
- package/src/usage/log.ts +7 -0
- package/src/usage/summary.ts +82 -1
- package/src/web-search/loop.ts +11 -8
- package/src/web-search/run-turn-loop.ts +10 -7
- package/gui/dist/assets/App-CddorHdA.js +0 -51
- package/gui/dist/assets/App-DMudtISW.css +0 -1
- package/gui/dist/assets/index-Btze1I3T.css +0 -1
- package/gui/dist/assets/index-CvnY7E6i.js +0 -86
- package/gui/dist/assets/quota-summary-B_AWpHfP.js +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/** Saved companion display filters applied to two bounded management reads. */
|
|
2
|
+
import { ObservationError, withObserveStream, type ObserveStreamDeps } from "./observe-stream";
|
|
3
|
+
import { printData, rejectArgs, runCliAction, takeFlag } from "./runtime-api";
|
|
4
|
+
|
|
5
|
+
const USAGE = "Usage: ocx companion usage [--json]";
|
|
6
|
+
const TOTAL_FIELDS = ["requests", "totalTokens", "inputTokens", "outputTokens", "cachedInputTokens", "cacheReadInputTokens", "estimatedCostUsd", "measuredRequests", "pricedRequests", "coverageRatio"] as const;
|
|
7
|
+
type Totals = Partial<Record<typeof TOTAL_FIELDS[number], number>>;
|
|
8
|
+
type Model = Totals & { model: string; provider: string };
|
|
9
|
+
interface Usage {
|
|
10
|
+
summary: Totals;
|
|
11
|
+
models: Model[];
|
|
12
|
+
customWindow?: boolean;
|
|
13
|
+
since?: number | null;
|
|
14
|
+
until?: number;
|
|
15
|
+
usageIncomplete?: boolean;
|
|
16
|
+
historyTruncated?: boolean;
|
|
17
|
+
entriesTruncated?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface Filters { models: string[] | null; hiddenProviders: string[] }
|
|
20
|
+
interface Settings { filters: Filters; updatedAt: number | null; corrupt: boolean }
|
|
21
|
+
type RangeResult = { status: "available"; data: Usage } | { status: "unavailable" };
|
|
22
|
+
|
|
23
|
+
function object(value: unknown): Record<string, unknown> {
|
|
24
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new ObservationError("Invalid companion response. Check the runtime and retry the command.");
|
|
25
|
+
return value as Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stringList(value: unknown): value is string[] {
|
|
29
|
+
return Array.isArray(value) && value.length <= 100 && value.every(item => typeof item === "string" && item.length > 0 && !/[\x00-\x1f\x7f-\x9f]/.test(item));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseSettings(value: unknown): Settings {
|
|
33
|
+
const body = object(value), settings = object(body.settings);
|
|
34
|
+
if ((settings.models !== null && !stringList(settings.models)) || !stringList(settings.hiddenProviders)
|
|
35
|
+
|| (body.updatedAt !== null && (typeof body.updatedAt !== "number" || !Number.isFinite(body.updatedAt)))
|
|
36
|
+
|| (body.corrupt !== undefined && typeof body.corrupt !== "boolean")) {
|
|
37
|
+
throw new ObservationError("Invalid companion settings. Check the runtime and retry the command.");
|
|
38
|
+
}
|
|
39
|
+
return { filters: { models: settings.models, hiddenProviders: settings.hiddenProviders }, updatedAt: body.updatedAt, corrupt: body.corrupt === true };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseTotals(value: unknown): Totals {
|
|
43
|
+
const row = object(value), totals: Totals = {};
|
|
44
|
+
for (const key of TOTAL_FIELDS) {
|
|
45
|
+
const metric = row[key];
|
|
46
|
+
if (metric === undefined) continue;
|
|
47
|
+
if (typeof metric !== "number" || !Number.isFinite(metric) || metric < 0) throw new ObservationError("Invalid companion usage metrics. Check the runtime and retry the command.");
|
|
48
|
+
totals[key] = metric;
|
|
49
|
+
}
|
|
50
|
+
return totals;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseUsage(value: unknown): Usage {
|
|
54
|
+
const body = object(value);
|
|
55
|
+
if (body.error || !Array.isArray(body.models)) throw new ObservationError("Invalid companion usage report. Check the runtime and retry the command.");
|
|
56
|
+
const usage: Usage = {
|
|
57
|
+
summary: parseTotals(body.summary),
|
|
58
|
+
models: body.models.map(raw => {
|
|
59
|
+
const row = object(raw);
|
|
60
|
+
if (typeof row.model !== "string" || typeof row.provider !== "string") throw new ObservationError("Invalid companion model row. Check the runtime and retry the command.");
|
|
61
|
+
return { ...parseTotals(row), model: row.model, provider: row.provider };
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
for (const key of ["customWindow", "usageIncomplete", "historyTruncated", "entriesTruncated"] as const) {
|
|
65
|
+
if (body[key] === undefined) continue;
|
|
66
|
+
if (typeof body[key] !== "boolean") throw new ObservationError("Invalid companion usage metadata. Check the runtime and retry the command.");
|
|
67
|
+
usage[key] = body[key];
|
|
68
|
+
}
|
|
69
|
+
for (const key of ["since", "until"] as const) {
|
|
70
|
+
const timestamp = body[key];
|
|
71
|
+
if (timestamp === undefined) continue;
|
|
72
|
+
if (key === "since" && timestamp === null) { usage.since = null; continue; }
|
|
73
|
+
if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) throw new ObservationError("Invalid companion usage window. Check the runtime and retry the command.");
|
|
74
|
+
usage[key] = timestamp;
|
|
75
|
+
}
|
|
76
|
+
return usage;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Keep in conformance with tray-data's display rules without a GUI runtime import. */
|
|
80
|
+
function filterUsage(usage: Usage, filters: Filters): Usage {
|
|
81
|
+
const hidden = new Set(filters.hiddenProviders), selected = filters.models === null ? null : new Set(filters.models);
|
|
82
|
+
const models = usage.models.filter(row => !hidden.has(row.provider)
|
|
83
|
+
&& (selected === null || selected.has(`${row.provider}/${row.model}`) || selected.has(row.model)));
|
|
84
|
+
if (filters.models === null && filters.hiddenProviders.length === 0) return { ...usage, models };
|
|
85
|
+
if (filters.models?.length !== 0 && usage.models.some(row => !row.provider || !row.model || (row.provider === "other" && row.model === "other"))) {
|
|
86
|
+
return { ...usage, models: models.filter(row => row.provider !== "other" || row.model !== "other"), summary: {}, usageIncomplete: true };
|
|
87
|
+
}
|
|
88
|
+
const summary: Totals = {};
|
|
89
|
+
for (const key of TOTAL_FIELDS) {
|
|
90
|
+
if (key === "coverageRatio") continue;
|
|
91
|
+
if (models.length && models.every(row => row[key] !== undefined)) summary[key] = models.reduce((sum, row) => sum + row[key]!, 0);
|
|
92
|
+
}
|
|
93
|
+
return { ...usage, models, summary };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function measuredTotals<T extends Totals>(data: T): T {
|
|
97
|
+
const next = { ...data };
|
|
98
|
+
if ((data.requests ?? 0) > 0 && (data.measuredRequests === 0 || data.coverageRatio === 0)) {
|
|
99
|
+
for (const key of ["totalTokens", "inputTokens", "outputTokens", "cachedInputTokens", "cacheReadInputTokens"] as const) delete next[key];
|
|
100
|
+
}
|
|
101
|
+
if ((data.requests ?? 0) > 0 && data.pricedRequests === 0) delete next.estimatedCostUsd;
|
|
102
|
+
return next;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatRange(label: string, result: RangeResult): string[] {
|
|
106
|
+
if (result.status === "unavailable") return [`${label}: unavailable`];
|
|
107
|
+
const count = (value: number | undefined) => value === undefined ? "unknown" : value.toLocaleString("en-US");
|
|
108
|
+
const cost = (value: number | undefined) => value === undefined ? "unknown" : `~$${value.toFixed(4)}`;
|
|
109
|
+
const data = result.data;
|
|
110
|
+
return [`${label}: ${count(data.summary.requests)} requests; ${count(data.summary.totalTokens)} tokens; cost ${cost(data.summary.estimatedCostUsd)}`,
|
|
111
|
+
...(data.usageIncomplete || data.historyTruncated || data.entriesTruncated ? [" Incomplete observation; totals include only attributable measured data."] : []),
|
|
112
|
+
...data.models.map(row => ` ${row.provider}/${row.model}: ${count(row.requests)} requests; ${count(row.totalTokens)} tokens; cost ${cost(row.estimatedCostUsd)}`)];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function handleCompanionUsageCommand(argv: string[], deps: ObserveStreamDeps = {}): Promise<number> {
|
|
116
|
+
let outcome = 0;
|
|
117
|
+
const parseExit = await runCliAction(async () => {
|
|
118
|
+
const args = [...argv], wantsJson = takeFlag(args, "--json");
|
|
119
|
+
rejectArgs(args, USAGE, { redactValues: true });
|
|
120
|
+
outcome = await withObserveStream(deps, async stream => {
|
|
121
|
+
const settings = parseSettings(await stream.get("/api/companion/settings", new URLSearchParams()));
|
|
122
|
+
const ranges: Record<"today" | "30d", RangeResult> = { today: { status: "unavailable" }, "30d": { status: "unavailable" } };
|
|
123
|
+
for (const range of ["today", "30d"] as const) {
|
|
124
|
+
try {
|
|
125
|
+
const data = filterUsage(parseUsage(await stream.get("/api/usage", new URLSearchParams({ range }))), settings.filters);
|
|
126
|
+
ranges[range] = { status: "available", data: { ...data, summary: measuredTotals(data.summary), models: data.models.map(measuredTotals) } };
|
|
127
|
+
} catch {
|
|
128
|
+
stream.signal.throwIfAborted();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
stream.signal.throwIfAborted();
|
|
132
|
+
const partial = ranges.today.status === "unavailable" || ranges["30d"].status === "unavailable";
|
|
133
|
+
const result = { schemaVersion: 1, filters: settings.filters, settingsUpdatedAt: settings.updatedAt, settingsCorrupt: settings.corrupt,
|
|
134
|
+
settingsFallback: settings.corrupt || settings.updatedAt === null, ranges, partial };
|
|
135
|
+
const lines = ["Companion usage — saved display filters (sequential observations)",
|
|
136
|
+
`Models: ${settings.filters.models === null ? "all" : settings.filters.models.length === 0 ? "none" : settings.filters.models.join(", ")}`,
|
|
137
|
+
`Hidden providers: ${settings.filters.hiddenProviders.join(", ") || "none"}`,
|
|
138
|
+
...(settings.corrupt ? ["WARNING: Saved settings are corrupt; the runtime supplied fallback settings."] : []),
|
|
139
|
+
...formatRange("Today", ranges.today), ...formatRange("30 days", ranges["30d"])];
|
|
140
|
+
printData(result, wantsJson, wantsJson ? undefined : lines);
|
|
141
|
+
if (partial) console.error("Error: Some companion usage ranges are unavailable. Check the runtime and retry the command.");
|
|
142
|
+
return partial ? 1 : 0;
|
|
143
|
+
}, { kind: "snapshot" });
|
|
144
|
+
});
|
|
145
|
+
return parseExit || outcome;
|
|
146
|
+
}
|
package/src/cli/companion.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { handleCompanionTimelineCommand } from "./companion-timeline";
|
|
2
|
+
import { handleCompanionUsageCommand } from "./companion-usage";
|
|
3
|
+
import type { ObserveStreamDeps } from "./observe-stream";
|
|
1
4
|
import { CliUsageError, printData, rejectArgs, runCliAction, runtimeRequest, takeFlag, type RuntimeApiDeps } from "./runtime-api";
|
|
2
5
|
|
|
3
6
|
const USAGE = `Usage:
|
|
4
7
|
ocx companion [show] [--json]
|
|
5
8
|
ocx companion set <key>=<value> [...] [--json]
|
|
6
|
-
ocx companion reset [--json]
|
|
9
|
+
ocx companion reset [--json]
|
|
10
|
+
ocx companion usage [--json]
|
|
11
|
+
ocx companion timeline [--hours <6|24|72|168>] [--bucket-minutes <1..1440>]
|
|
12
|
+
[--metric <total|input|output|cached>] [--aggregation <sum|average|max>]
|
|
13
|
+
[--grouping <model|modelAccount>] [--model <provider/model>]... [--hide-provider <name>]... [--json]`;
|
|
7
14
|
|
|
8
15
|
function parseValue(raw: string): unknown {
|
|
9
16
|
if (raw === "null") return null;
|
|
@@ -43,9 +50,11 @@ async function reset(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
43
50
|
}, deps), wantsJson, ["Companion settings reset."]);
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
export async function handleCompanionCommand(argv: string[], deps:
|
|
53
|
+
export async function handleCompanionCommand(argv: string[], deps: ObserveStreamDeps = {}): Promise<number> {
|
|
54
|
+
const [sub = "show", ...rest] = argv;
|
|
55
|
+
if (sub === "timeline") return handleCompanionTimelineCommand(rest, deps);
|
|
56
|
+
if (sub === "usage") return handleCompanionUsageCommand(rest, deps);
|
|
47
57
|
return runCliAction(async () => {
|
|
48
|
-
const [sub = "show", ...rest] = argv;
|
|
49
58
|
if (sub === "show") await show(rest, deps);
|
|
50
59
|
else if (sub === "set") await set(rest, deps);
|
|
51
60
|
else if (sub === "reset") await reset(rest, deps);
|
|
@@ -14,7 +14,7 @@ const USAGE = `Usage:
|
|
|
14
14
|
ocx config set <dot.path> <json-or-string> [--json]
|
|
15
15
|
ocx config unset <dot.path> [--json]
|
|
16
16
|
ocx config validate [path|-] [--json]
|
|
17
|
-
ocx config export <path|->
|
|
17
|
+
ocx config export <path|-> [--json]
|
|
18
18
|
ocx config import <path|-> --yes [--json]`;
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -205,14 +205,21 @@ function validate(value: unknown): OcxConfig {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
208
|
-
|
|
208
|
+
let outcome = 0;
|
|
209
|
+
const status = await runCliAction(async () => {
|
|
209
210
|
const args = [...argv];
|
|
210
|
-
const action = (args.shift() ?? "show").toLowerCase();
|
|
211
211
|
const wantsJson = takeFlag(args, "--json");
|
|
212
|
+
const source = takeFlag(args, "--source");
|
|
213
|
+
if (args.includes("--json") || args.includes("--source")) throw new CliUsageError("config flags may only be specified once", USAGE);
|
|
214
|
+
const action = (args.shift() ?? "show").toLowerCase();
|
|
215
|
+
if (source && action !== "show") throw new CliUsageError("--source is only supported for config show", USAGE);
|
|
212
216
|
if (action === "show") {
|
|
213
|
-
const source = takeFlag(args, "--source");
|
|
214
217
|
rejectArgs(args, USAGE);
|
|
215
218
|
const diagnostics = readConfigDiagnostics();
|
|
219
|
+
if (diagnostics.source === "fallback" || diagnostics.error) {
|
|
220
|
+
console.error(`Warning: Config at ${getConfigPath()} is invalid or unreadable; defaults are being shown for invalid settings. Run: ocx config validate. Inspect: ocx config show --source.`);
|
|
221
|
+
if (!source) outcome = 1;
|
|
222
|
+
}
|
|
216
223
|
const redacted = redact(diagnostics.config);
|
|
217
224
|
const note = await readRemoteHubConfigNote(diagnostics.config);
|
|
218
225
|
// First key, not last: it has to be read before the empty `providers` map that misled a
|
|
@@ -228,7 +235,12 @@ export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
|
228
235
|
const path = args.shift();
|
|
229
236
|
if (!path) throw new CliUsageError("config path is required", USAGE);
|
|
230
237
|
rejectArgs(args, USAGE);
|
|
231
|
-
const
|
|
238
|
+
const diagnostics = readConfigDiagnostics();
|
|
239
|
+
if (diagnostics.source === "fallback" || diagnostics.error) {
|
|
240
|
+
console.error(`Warning: Config at ${getConfigPath()} is invalid or unreadable; defaults are being shown for invalid settings. Run: ocx config validate. Inspect: ocx config show --source.`);
|
|
241
|
+
outcome = 1;
|
|
242
|
+
}
|
|
243
|
+
const value = redact(getPath(diagnostics.config, path), pathSegments(path).at(-1));
|
|
232
244
|
if (wantsJson || typeof value === "object") console.log(JSON.stringify(value, null, 2));
|
|
233
245
|
else console.log(String(value));
|
|
234
246
|
return;
|
|
@@ -236,7 +248,7 @@ export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
|
236
248
|
if (action === "set" || action === "unset") {
|
|
237
249
|
const path = args.shift();
|
|
238
250
|
const raw = action === "set" ? args.shift() : undefined;
|
|
239
|
-
if (!path || (action === "set" && raw === undefined)) throw new CliUsageError("config path and value are required", USAGE);
|
|
251
|
+
if (!path || (action === "set" && raw === undefined)) throw new CliUsageError(action === "unset" ? "config path is required" : "config path and value are required", USAGE);
|
|
240
252
|
rejectArgs(args, USAGE);
|
|
241
253
|
// #1835/#1838: the read used to happen OUTSIDE the mutation lock, so a concurrent
|
|
242
254
|
// edit landing between it and the save was reverted by this whole-snapshot write.
|
|
@@ -288,7 +300,7 @@ export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
|
288
300
|
})();
|
|
289
301
|
printData(result.ok ? { ok: true, source: path ?? getConfigPath() } : result, wantsJson,
|
|
290
302
|
[result.ok ? "Config is valid." : `Config is invalid: ${result.error}`]);
|
|
291
|
-
if (!result.ok)
|
|
303
|
+
if (!result.ok) outcome = 1;
|
|
292
304
|
return;
|
|
293
305
|
}
|
|
294
306
|
if (action === "export") {
|
|
@@ -297,7 +309,10 @@ export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
|
297
309
|
rejectArgs(args, USAGE);
|
|
298
310
|
const content = `${JSON.stringify(readConfigDiagnostics().config, null, 2)}\n`;
|
|
299
311
|
if (path === "-") process.stdout.write(content);
|
|
300
|
-
else {
|
|
312
|
+
else {
|
|
313
|
+
writeFileSync(path, content, { encoding: "utf8", mode: 0o600 });
|
|
314
|
+
printData({ ok: true, path }, wantsJson, [`Exported config to ${path}.`]);
|
|
315
|
+
}
|
|
301
316
|
return;
|
|
302
317
|
}
|
|
303
318
|
if (action === "import") {
|
|
@@ -312,6 +327,7 @@ export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
|
312
327
|
}
|
|
313
328
|
throw new CliUsageError(`unknown config command ${action}`, USAGE);
|
|
314
329
|
});
|
|
330
|
+
return status || outcome;
|
|
315
331
|
}
|
|
316
332
|
|
|
317
333
|
export const CONFIG_USAGE = USAGE;
|
package/src/cli/connect.ts
CHANGED
|
@@ -40,6 +40,10 @@ import {
|
|
|
40
40
|
terminalSafeText,
|
|
41
41
|
type RuntimeApiDeps,
|
|
42
42
|
} from "./runtime-api";
|
|
43
|
+
import {
|
|
44
|
+
inspectCodexShimForConnect,
|
|
45
|
+
type CodexConnectShimReadiness,
|
|
46
|
+
} from "./codex-shim-readiness";
|
|
43
47
|
|
|
44
48
|
export interface ClientCommandDeps extends RuntimeApiDeps {
|
|
45
49
|
lifecycleLockDeps?: ClientLifecycleLockDeps;
|
|
@@ -277,6 +281,19 @@ export type ConnectCompletionReport = {
|
|
|
277
281
|
readonly failure: string | null;
|
|
278
282
|
};
|
|
279
283
|
|
|
284
|
+
export function codexShimReadinessLine(
|
|
285
|
+
selectedClients: readonly OcxConnectedClientId[],
|
|
286
|
+
inspect: () => CodexConnectShimReadiness = inspectCodexShimForConnect,
|
|
287
|
+
): string | null {
|
|
288
|
+
if (!selectedClients.includes("codex")) return null;
|
|
289
|
+
return `Codex autostart shim: ${terminalSafeText(inspect().message)}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function printCodexShimReadiness(selectedClients: readonly OcxConnectedClientId[]): void {
|
|
293
|
+
const line = codexShimReadinessLine(selectedClients);
|
|
294
|
+
if (line) console.log(line);
|
|
295
|
+
}
|
|
296
|
+
|
|
280
297
|
/**
|
|
281
298
|
* What `ocx connect` says once the hub and the credential are settled, and whether the command
|
|
282
299
|
* still fails (#4207).
|
|
@@ -414,6 +431,7 @@ async function runHubConnect(argv: string[], deps: ClientCommandDeps): Promise<v
|
|
|
414
431
|
const readiness = inspectInstalledCatalogReadiness(installedCatalogFileState(), deps.catalogProbeDeps ?? {});
|
|
415
432
|
const report = connectCompletionReport(connection, clients, readiness);
|
|
416
433
|
for (const line of report.lines) console.log(line);
|
|
434
|
+
printCodexShimReadiness(clients);
|
|
417
435
|
if (report.failure) throw new Error(report.failure);
|
|
418
436
|
}
|
|
419
437
|
|
|
@@ -463,6 +481,7 @@ async function runLinkConnect(argv: string[], deps: ClientCommandDeps): Promise<
|
|
|
463
481
|
const readiness = inspectInstalledCatalogReadiness(installedCatalogFileState(), deps.catalogProbeDeps ?? {});
|
|
464
482
|
const report = connectCompletionReport(connection, clients, readiness);
|
|
465
483
|
for (const line of report.lines) console.log(line);
|
|
484
|
+
printCodexShimReadiness(clients);
|
|
466
485
|
if (report.failure) throw new Error(report.failure);
|
|
467
486
|
}
|
|
468
487
|
|
package/src/cli/dispatch.ts
CHANGED
|
@@ -10,13 +10,13 @@ import type { ProxyRestartStartOutcome } from "./tray-proxy";
|
|
|
10
10
|
* never needs to import the entry module back (no cycle).
|
|
11
11
|
*/
|
|
12
12
|
import { CLI_COMMANDS } from "./registry";
|
|
13
|
-
import {
|
|
14
|
-
import type { CliHead } from "./root";
|
|
13
|
+
import { uninstallArgsError, type CliHead } from "./root";
|
|
15
14
|
import type { ReadyArgs } from "./ready";
|
|
16
15
|
import type { LivenessIo, LiveProxy } from "../server/proxy-liveness";
|
|
17
16
|
import type { OcxConfig } from "../types";
|
|
18
17
|
import type { OwnedIntegrationRefreshOutcome } from "../integrations/owned-refresh";
|
|
19
18
|
import { hasHelpFlag, printSubcommandUsage, printUsage } from "./help";
|
|
19
|
+
import { printUnknownCommand } from "./help-recovery";
|
|
20
20
|
import {
|
|
21
21
|
HUB_GATED_SKIP_MESSAGE,
|
|
22
22
|
localClientSkipMessage,
|
|
@@ -299,6 +299,11 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
299
299
|
return Number(process.exitCode ?? 0);
|
|
300
300
|
},
|
|
301
301
|
uninstall: async deps => {
|
|
302
|
+
const error = uninstallArgsError("uninstall", deps.args);
|
|
303
|
+
if (error) {
|
|
304
|
+
console.error(error);
|
|
305
|
+
return 2;
|
|
306
|
+
}
|
|
302
307
|
await deps.handleUninstall();
|
|
303
308
|
return Number(process.exitCode ?? 0);
|
|
304
309
|
},
|
|
@@ -375,62 +380,8 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
375
380
|
return 0;
|
|
376
381
|
},
|
|
377
382
|
logout: async deps => {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
// `ocx logout --json` called removeCredential("--json"), printed "Logged out of
|
|
381
|
-
// --json." and exited 0 -- a silent false success, the worst outcome for a caller
|
|
382
|
-
// that can only see the exit code.
|
|
383
|
-
//
|
|
384
|
-
// That is not merely a wasted call. `normalizeAuthStore` copies every top-level key
|
|
385
|
-
// it finds, so a hand-edited, legacy, or corrupted auth.json containing a `--json`
|
|
386
|
-
// key would have its active account deleted -- and the key dropped entirely if that
|
|
387
|
-
// was its last account. A flag must never reach the store as a provider name.
|
|
388
|
-
const logoutArgs = deps.args.slice(1);
|
|
389
|
-
const wantsJson = logoutArgs.includes("--json");
|
|
390
|
-
// Any leading dash is an option, not a provider. Matching only `--` left the same defect
|
|
391
|
-
// one dash shorter: `ocx logout -j` treated `-j` as the provider name and, with a `-j` key
|
|
392
|
-
// present in the store, deleted it and exited 0.
|
|
393
|
-
const isOption = (arg: string): boolean => arg.startsWith("-");
|
|
394
|
-
const positionals = logoutArgs.filter(arg => !isOption(arg));
|
|
395
|
-
const unknownFlags = logoutArgs.filter(arg => isOption(arg) && arg !== "--json");
|
|
396
|
-
const name = (positionals[0] ?? "").trim().toLowerCase();
|
|
397
|
-
|
|
398
|
-
// Usage failures exit 2 and touch nothing. A missing provider is a usage error; a
|
|
399
|
-
// provider that simply has no credential is a not-found (4) further down, because the
|
|
400
|
-
// vocabulary distinguishes "you called this wrong" from "the thing is not there".
|
|
401
|
-
//
|
|
402
|
-
// The shape check is `isValidProviderName`, not another dash test. Rejecting a leading
|
|
403
|
-
// ASCII `-` fixed `-j` and still let `logout —json` through with a Unicode dash, which is
|
|
404
|
-
// the same defect a third time: each patch named one spelling instead of the class. The
|
|
405
|
-
// canonical validator states the rule positively -- start and end alphanumeric, internal
|
|
406
|
-
// `._-` allowed -- so `github-copilot` and `google-antigravity` pass while every dash
|
|
407
|
-
// variant, empty string, and reserved name fails. Anything that is not a possible
|
|
408
|
-
// provider id cannot reach the store at all.
|
|
409
|
-
const malformedName = Boolean(name) && !isValidProviderName(name);
|
|
410
|
-
if (unknownFlags.length > 0 || positionals.length > 1 || !name || malformedName) {
|
|
411
|
-
const problem = unknownFlags.length > 0
|
|
412
|
-
? `unknown option ${unknownFlags[0]}`
|
|
413
|
-
: positionals.length > 1 ? "too many arguments"
|
|
414
|
-
: malformedName ? `not a valid provider name: ${name}`
|
|
415
|
-
: "missing provider";
|
|
416
|
-
console.error(`Usage: ocx logout <provider> [--json] (${problem})`);
|
|
417
|
-
return 2;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// The disposition comes from inside the store mutation, not from a read-then-remove
|
|
421
|
-
// preflight. `mutateStore` serializes writes, so a preflight leaves a window where a
|
|
422
|
-
// concurrent logout removes the same account and BOTH callers exit 0 claiming a removal --
|
|
423
|
-
// a false success again, just a narrower one than the flag bug above.
|
|
424
|
-
const { removeCredential } = await import("../oauth/store");
|
|
425
|
-
const outcome = await removeCredential(name);
|
|
426
|
-
if (outcome === "not-found") {
|
|
427
|
-
if (wantsJson) console.log(JSON.stringify({ schemaVersion: 1, ok: false, provider: name, removed: false, reason: "not_found" }, null, 2));
|
|
428
|
-
else console.error(`No stored credential for '${name}'.`);
|
|
429
|
-
return 4;
|
|
430
|
-
}
|
|
431
|
-
if (wantsJson) console.log(JSON.stringify({ schemaVersion: 1, ok: true, provider: name, removed: true }, null, 2));
|
|
432
|
-
else console.log(`Logged out of ${name}.`);
|
|
433
|
-
return 0;
|
|
383
|
+
const { handleLogoutCommand } = await import("./logout-command");
|
|
384
|
+
return handleLogoutCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
434
385
|
},
|
|
435
386
|
sync: async deps => {
|
|
436
387
|
const syncArgs = deps.args.slice(1);
|
|
@@ -514,7 +465,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
514
465
|
},
|
|
515
466
|
config,
|
|
516
467
|
port: live.port,
|
|
517
|
-
}, ["mcode", "pi", "raycast", "omo", "cline", "droid"]));
|
|
468
|
+
}, ["mcode", "pi", "raycast", "omo", "cline", "droid", "opencode", "kilo"]));
|
|
518
469
|
} catch (error) {
|
|
519
470
|
console.warn(`Client integrations were not refreshed: ${error instanceof Error ? error.message : String(error)}`);
|
|
520
471
|
}
|
|
@@ -537,7 +488,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
537
488
|
},
|
|
538
489
|
v2: async deps => {
|
|
539
490
|
const { cmdV2 } = await import("./v2");
|
|
540
|
-
return await cmdV2(deps.args.slice(1), {}, async () => (await deps.findLiveProxy())?.port);
|
|
491
|
+
return await cmdV2(deps.args.slice(1), { runtimeApi: { findLiveProxy: deps.findLiveProxy } }, async () => (await deps.findLiveProxy())?.port);
|
|
541
492
|
},
|
|
542
493
|
connect: async deps => {
|
|
543
494
|
const { handleConnectCommand } = await import("./connect");
|
|
@@ -549,7 +500,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
549
500
|
},
|
|
550
501
|
"remote-workspace": async deps => {
|
|
551
502
|
const { runRemoteWorkspaceCommand } = await import("./remote-workspace");
|
|
552
|
-
return await runRemoteWorkspaceCommand(deps.args.slice(1));
|
|
503
|
+
return await runRemoteWorkspaceCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
553
504
|
},
|
|
554
505
|
disconnect: async deps => {
|
|
555
506
|
const { handleDisconnectCommand } = await import("./connect");
|
|
@@ -569,7 +520,8 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
569
520
|
const cacheGateSnapshot = deps.loadConfig();
|
|
570
521
|
const desiredDisabled = !shouldSyncCodexOnStart(cacheGateSnapshot);
|
|
571
522
|
const invalidated = withCatalogWriteSerialization(owningCodexHome, permit =>
|
|
572
|
-
invalidateCodexModelsCacheWithPermitOutcome(permit, owningCodexHome, { allowWhenDesiredDisabled: true })
|
|
523
|
+
invalidateCodexModelsCacheWithPermitOutcome(permit, owningCodexHome, { allowWhenDesiredDisabled: true }),
|
|
524
|
+
{ intent: "cache", writer: "sync-cache" });
|
|
573
525
|
const cacheJson = cacheArgs.includes("--json");
|
|
574
526
|
const jsonSafeLog = cacheJson
|
|
575
527
|
? { log: (...values: unknown[]) => console.error(...values), error: (...values: unknown[]) => console.error(...values) }
|
|
@@ -632,6 +584,10 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
632
584
|
console.log("No Codex catalog to derive a cache from; nothing to sync.");
|
|
633
585
|
} else if (unchanged) {
|
|
634
586
|
console.log("Codex model cache is already current; nothing to sync.");
|
|
587
|
+
} else if (invalidated.kind === "unavailable"
|
|
588
|
+
&& (invalidated.reason === "foreign-owner" || invalidated.reason === "owner-unknown")) {
|
|
589
|
+
const { FOREIGN_CODEX_HOME_OWNER_MESSAGE, UNKNOWN_CODEX_HOME_OWNER_MESSAGE } = await import("../codex/catalog/routed-removal");
|
|
590
|
+
console.error(invalidated.reason === "foreign-owner" ? FOREIGN_CODEX_HOME_OWNER_MESSAGE : UNKNOWN_CODEX_HOME_OWNER_MESSAGE);
|
|
635
591
|
} else if (!ok) {
|
|
636
592
|
console.error(`Cache refresh did not complete (${invalidated.kind}). The Codex model cache was not rewritten.`);
|
|
637
593
|
}
|
|
@@ -696,19 +652,33 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
696
652
|
switch (deps.args[1]) {
|
|
697
653
|
case "install": {
|
|
698
654
|
const r = installCodexShim();
|
|
699
|
-
const { healthy, summary } = diagnoseCodexShim();
|
|
655
|
+
const { healthy, runnable, active, summary } = diagnoseCodexShim();
|
|
656
|
+
const success = !r.refused && (runnable ?? healthy);
|
|
700
657
|
const { collectCodexShimReadinessWarnings } = await import("./codex-shim-readiness");
|
|
701
|
-
const warnings =
|
|
658
|
+
const warnings = success
|
|
702
659
|
? collectCodexShimReadinessWarnings()
|
|
703
660
|
: [];
|
|
704
|
-
console.log(`${
|
|
661
|
+
console.log(`${success && healthy && warnings.length === 0 ? "✅ " : "⚠️ "}${r.message}`);
|
|
705
662
|
for (const warning of warnings) console.warn(` ${warning}`);
|
|
706
|
-
if (
|
|
707
|
-
|
|
663
|
+
if (success && active !== undefined && active !== true) {
|
|
664
|
+
const { overlayActivationHint } = await import("../codex/shim-overlay");
|
|
665
|
+
console.warn(` ${overlayActivationHint()}`);
|
|
666
|
+
}
|
|
667
|
+
if (!success) console.error(`${r.refused ? "Codex shim installation was refused" : "Codex shim installation is unhealthy"}: ${summary}`);
|
|
668
|
+
return success ? 0 : 1;
|
|
708
669
|
}
|
|
709
|
-
case "status":
|
|
670
|
+
case "status": {
|
|
671
|
+
const extra = deps.args.slice(2);
|
|
672
|
+
if (extra.length > 0) {
|
|
673
|
+
console.error(extra.some(isJsonOption)
|
|
674
|
+
? "ocx codex-shim status does not support --json; use ocx status --json (codexShim)."
|
|
675
|
+
: "ocx codex-shim status does not accept arguments or options.");
|
|
676
|
+
console.error("Usage: ocx codex-shim status");
|
|
677
|
+
return 2;
|
|
678
|
+
}
|
|
710
679
|
console.log(codexShimStatus());
|
|
711
680
|
break;
|
|
681
|
+
}
|
|
712
682
|
case "uninstall":
|
|
713
683
|
case "remove": {
|
|
714
684
|
const r = uninstallCodexShim();
|
|
@@ -730,7 +700,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
730
700
|
}
|
|
731
701
|
const { runUpdate } = await import("../update");
|
|
732
702
|
await runUpdate();
|
|
733
|
-
return 0;
|
|
703
|
+
return Number(process.exitCode ?? 0);
|
|
734
704
|
},
|
|
735
705
|
"__refresh-version": async deps => {
|
|
736
706
|
// Hidden, detached helper spawned by the update prompt to refresh the
|
|
@@ -785,6 +755,10 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
785
755
|
},
|
|
786
756
|
health: async deps => {
|
|
787
757
|
const healthArgs = deps.args.slice(1);
|
|
758
|
+
if (healthArgs.length > 1 || (healthArgs.length === 1 && healthArgs[0] !== "--json")) {
|
|
759
|
+
console.error("Usage: ocx health [--json]\nSee: ocx help health");
|
|
760
|
+
return 2;
|
|
761
|
+
}
|
|
788
762
|
const wantsHealthJson = healthArgs.includes("--json");
|
|
789
763
|
// A proxy that has only just bound can miss a single probe while its event loop
|
|
790
764
|
// is still settling startup work — the same just-started race the stop paths
|
|
@@ -843,7 +817,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
843
817
|
},
|
|
844
818
|
companion: async deps => {
|
|
845
819
|
const { handleCompanionCommand } = await import("./companion");
|
|
846
|
-
return await handleCompanionCommand(deps.args.slice(1));
|
|
820
|
+
return await handleCompanionCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
847
821
|
},
|
|
848
822
|
route: async deps => {
|
|
849
823
|
if (deps.args[1] !== "combo" && deps.args[1] !== "policy") {
|
|
@@ -868,7 +842,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
868
842
|
},
|
|
869
843
|
observe: async deps => {
|
|
870
844
|
const { handleObserveCommand } = await import("./observe");
|
|
871
|
-
return await handleObserveCommand(deps.args.slice(1));
|
|
845
|
+
return await handleObserveCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
872
846
|
},
|
|
873
847
|
inspect: async deps => {
|
|
874
848
|
const { handleInspectCommand } = await import("./inspect");
|
|
@@ -876,11 +850,11 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
876
850
|
},
|
|
877
851
|
logs: async deps => {
|
|
878
852
|
const { handleObserveCommand } = await import("./observe");
|
|
879
|
-
return await handleObserveCommand([deps.command!, ...deps.args.slice(1)]);
|
|
853
|
+
return await handleObserveCommand([deps.command!, ...deps.args.slice(1)], { findLiveProxy: deps.findLiveProxy });
|
|
880
854
|
},
|
|
881
855
|
usage: async deps => {
|
|
882
856
|
const { handleObserveCommand } = await import("./observe");
|
|
883
|
-
return await handleObserveCommand([deps.command!, ...deps.args.slice(1)]);
|
|
857
|
+
return await handleObserveCommand([deps.command!, ...deps.args.slice(1)], { findLiveProxy: deps.findLiveProxy });
|
|
884
858
|
},
|
|
885
859
|
storage: async deps => {
|
|
886
860
|
// `ocx storage` used to be a pure alias of `observe storage`, which reached only the report
|
|
@@ -896,11 +870,11 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
896
870
|
},
|
|
897
871
|
access: async deps => {
|
|
898
872
|
const { handleAccessCommand } = await import("./access");
|
|
899
|
-
return await handleAccessCommand(deps.args.slice(1));
|
|
873
|
+
return await handleAccessCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
900
874
|
},
|
|
901
875
|
"api-key": async deps => {
|
|
902
876
|
const { handleAccessCommand } = await import("./access");
|
|
903
|
-
return await handleAccessCommand(["key", ...deps.args.slice(1)]);
|
|
877
|
+
return await handleAccessCommand(["key", ...deps.args.slice(1)], { findLiveProxy: deps.findLiveProxy });
|
|
904
878
|
},
|
|
905
879
|
api: async deps => {
|
|
906
880
|
const { handleApiCommand } = await import("./api-protocols");
|
|
@@ -924,21 +898,21 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
924
898
|
// integrations `client` manages, so they get their own subcommand rather than being
|
|
925
899
|
// folded into one that means something else.
|
|
926
900
|
const { handleIntegrationCommand } = await import("./inspect");
|
|
927
|
-
return await handleIntegrationCommand(deps.args.slice(1));
|
|
901
|
+
return await handleIntegrationCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
928
902
|
} else if (integration === "claude") {
|
|
929
903
|
const { handleClaudeConfigCommand } = await import("./integrations");
|
|
930
904
|
return await handleClaudeConfigCommand(deps.args.slice(2));
|
|
931
905
|
} else if (integration === "client") {
|
|
932
906
|
const { handleClientIntegrationCommand } = await import("./integrations");
|
|
933
|
-
return await handleClientIntegrationCommand(deps.args.slice(2));
|
|
907
|
+
return await handleClientIntegrationCommand(deps.args.slice(2), { findLiveProxy: deps.findLiveProxy });
|
|
934
908
|
} else {
|
|
935
|
-
console.error("Usage: ocx integration <claude|grok|client> <subcommand
|
|
909
|
+
console.error("Usage: ocx integration <claude|grok|client|native> <subcommand>\nSee: ocx help integration");
|
|
936
910
|
return 2;
|
|
937
911
|
}
|
|
938
912
|
},
|
|
939
913
|
system: async deps => {
|
|
940
914
|
const { handleSystemCommand } = await import("./system-command");
|
|
941
|
-
return await handleSystemCommand(deps.args.slice(1));
|
|
915
|
+
return await handleSystemCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
942
916
|
},
|
|
943
917
|
config: async deps => {
|
|
944
918
|
const { handleConfigCommand } = await import("./config-command");
|
|
@@ -957,7 +931,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
957
931
|
// "ocx claude desktop" → write Desktop 3P config
|
|
958
932
|
if (deps.args[1] === "desktop") {
|
|
959
933
|
const { handleClaudeDesktopCommand } = await import("./claude-desktop");
|
|
960
|
-
const exitCode = await handleClaudeDesktopCommand(deps.args.slice(2));
|
|
934
|
+
const exitCode = await handleClaudeDesktopCommand(deps.args.slice(2), { findLiveProxyImpl: deps.findLiveProxy });
|
|
961
935
|
if (exitCode !== 0) return exitCode;
|
|
962
936
|
return 0;
|
|
963
937
|
}
|
|
@@ -1167,8 +1141,7 @@ export async function dispatchCommand(head: CliHead, deps: CliDispatchDeps): Pro
|
|
|
1167
1141
|
}
|
|
1168
1142
|
const runner = commandRunners[resolveDispatchCommand(command) ?? ""];
|
|
1169
1143
|
if (!runner) {
|
|
1170
|
-
|
|
1171
|
-
printUsage();
|
|
1144
|
+
printUnknownCommand(command);
|
|
1172
1145
|
return 1;
|
|
1173
1146
|
}
|
|
1174
1147
|
return await runner(deps);
|