@bitkyc08/opencodex 2.84.0 → 2.86.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 +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-BjlttVLy.js +58 -0
- package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DyR9yBee.js} +1 -1
- package/gui/dist/assets/index-BMssP4k3.js +94 -0
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
- package/gui/dist/index.html +1 -1
- package/package.json +1 -2
- package/src/adapters/anthropic/beta-allowlist.ts +87 -8
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +33 -4
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/turn.ts +22 -9
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +2 -2
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/qoder/adapter.ts +40 -1
- package/src/advisor/consult.ts +220 -0
- package/src/advisor/context.ts +226 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/runtime.ts +305 -0
- package/src/advisor/settings.ts +135 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +5 -0
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/desktop-3p-library.ts +43 -3
- package/src/claude/desktop-3p.ts +22 -8
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/inbound.ts +121 -50
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +13 -6
- package/src/cli/advisor.ts +151 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-agents-routing.ts +18 -0
- package/src/cli/capabilities-base.ts +1 -1
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +46 -6
- package/src/cli/dispatch.ts +5 -1
- package/src/cli/doctor.ts +9 -0
- package/src/cli/ensure-desktop-cold-start.ts +329 -0
- package/src/cli/export-command.ts +1 -1
- package/src/cli/help.ts +2 -1
- package/src/cli/index.ts +13 -4
- package/src/cli/integration-plan-dto.ts +2 -0
- package/src/cli/registry.ts +15 -3
- package/src/cli/restart-scope.ts +3 -1
- package/src/clients/config-export/contracts.ts +1 -0
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +17 -4
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-context.ts +79 -5
- package/src/codex/autostart-health.ts +4 -0
- package/src/codex/catalog/build-entries.ts +11 -2
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +4 -1
- package/src/codex/catalog/model-hints.ts +21 -3
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +5 -0
- package/src/codex/catalog/provider-models.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +22 -4
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/config-write-lock.ts +40 -2
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +4 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +55 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +173 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +7 -2
- package/src/config/diagnostics.ts +2 -1
- package/src/config/live-reconcile.ts +13 -0
- package/src/config/load-degrade.ts +6 -1
- package/src/config/paths.ts +10 -3
- package/src/config/schema/config-schema.ts +17 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +451 -163
- package/src/generated/model-metadata.ts +1 -1
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +1 -0
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/store.ts +3 -0
- package/src/integrations/writer.ts +10 -1
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +107 -0
- package/src/lib/errors.ts +7 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/oauth/generic-account-failover.ts +29 -5
- package/src/oauth/index.ts +4 -2
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +7 -1
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/model-rename-migration.ts +5 -3
- package/src/providers/registry/entries-core.ts +7 -6
- package/src/providers/registry/entries-extended.ts +19 -0
- package/src/responses/parser.ts +30 -19
- package/src/responses/state.ts +32 -29
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +248 -216
- package/src/routing/history/ingest.ts +238 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/server/admission-model-scope.ts +4 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +4 -0
- package/src/server/chat-completions.ts +75 -9
- package/src/server/claude-messages.ts +110 -20
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/serve-options.ts +2 -2
- package/src/server/index/startup-warnings.ts +9 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +13 -10
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +191 -0
- package/src/server/management/agent-settings-routes.ts +22 -4
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +4 -0
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/model-routes.ts +2 -2
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/route-registry.ts +11 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +6 -0
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native.ts +5 -1
- package/src/server/request-log.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +35 -18
- package/src/server/responses/adapter-delivery.ts +24 -2
- package/src/server/responses/adapter-dispatch.ts +29 -2
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-slot.ts +336 -0
- package/src/server/responses/code-mode-collab-surface.ts +116 -8
- package/src/server/responses/core-combo-failure.ts +10 -1
- package/src/server/responses/core-combo.ts +21 -0
- package/src/server/responses/core-lifetime.ts +2 -0
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-options.ts +46 -0
- package/src/server/responses/core-replay.ts +7 -4
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/passthrough-delivery.ts +16 -10
- package/src/server/responses/passthrough-dispatch.ts +14 -3
- package/src/server/responses/request-prepare.ts +68 -5
- package/src/server/responses/request-transport.ts +16 -0
- package/src/server/responses/run-turn-execution.ts +70 -8
- package/src/server/responses/sidecar-execution.ts +78 -3
- package/src/server/responses/terminal-guard.ts +2 -1
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +16 -2
- package/src/server/startup-health-cache.ts +1 -0
- package/src/service/desktop-startup.ts +14 -0
- package/src/service/health-sentinel-main.ts +23 -4
- package/src/service/health-sentinel-probe.ts +24 -7
- package/src/service/logging.ts +72 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +63 -6
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +15 -0
- package/src/types/tools.ts +2 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/expected-prices.ts +12 -0
- package/src/web-search/gemini-executor.ts +2 -2
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-CZtqiGcw.js +0 -56
- package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
- package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
package/src/types/config.ts
CHANGED
|
@@ -30,6 +30,11 @@ export interface OcxProtocolsConfig {
|
|
|
30
30
|
managedMessagesNativeOAuth?: boolean;
|
|
31
31
|
/** Chat and Messages clients are encoded directly from adapter events. */
|
|
32
32
|
directEncoders?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Chat requests to a concrete Google Antigravity route decode straight into the IR and are
|
|
35
|
+
* answered through the direct encoders, with no internal Responses body in either direction.
|
|
36
|
+
*/
|
|
37
|
+
antigravityDirect?: boolean;
|
|
33
38
|
/** Compare the dispatch plan with the observed path; never sends a second request. */
|
|
34
39
|
shadowPlan?: boolean;
|
|
35
40
|
};
|
|
@@ -581,12 +586,13 @@ export interface OcxConfig {
|
|
|
581
586
|
/**
|
|
582
587
|
* Opt-in Ultra Fast service tier, default off.
|
|
583
588
|
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
* `ultrafast` the
|
|
589
|
-
*
|
|
589
|
+
* On native GPT-6 Astra and GPT-6.1 Sol rows (bare and account-bound), the flag adds the
|
|
590
|
+
* Ultrafast speed choice OpenAI documents for those two models, so the Codex picker offers
|
|
591
|
+
* it even though `model_catalog_json` replaces ChatGPT's live, plan-aware model list
|
|
592
|
+
* (`src/codex/catalog/native-ultrafast.ts`). On routed rows it still never invents a tier:
|
|
593
|
+
* it keeps an `ultrafast` the operator configured from being stripped on regeneration
|
|
594
|
+
* (PR #2994 was closed for adding one the wire could not honor). Turning it off withdraws
|
|
595
|
+
* the generated native tier on the next catalog write.
|
|
590
596
|
*/
|
|
591
597
|
ultraFastTier?: boolean;
|
|
592
598
|
/**
|
|
@@ -599,6 +605,13 @@ export interface OcxConfig {
|
|
|
599
605
|
codexMainAccountHardLock?: boolean;
|
|
600
606
|
/** Per-window percentages: short defaults to 90, long to 98; integers 80..100, short <= long. */
|
|
601
607
|
codexMainAccountHardLockThresholds?: { short?: number; long?: number };
|
|
608
|
+
/**
|
|
609
|
+
* Who refreshes the native main ChatGPT login in CODEX_HOME (#2999). "codex" asks the Codex
|
|
610
|
+
* binary through app-server `getAuthStatus` and never writes auth.json; "opencodex" keeps the
|
|
611
|
+
* guarded writer. Absent means "codex" when a Codex binary at or above the supported version
|
|
612
|
+
* is found, otherwise "opencodex". "codex" also falls back when no such binary exists.
|
|
613
|
+
*/
|
|
614
|
+
nativeMainRefresh?: "codex" | "opencodex";
|
|
602
615
|
/** Explicit top-level deletion intent used by stale whole-config rebases. */
|
|
603
616
|
configRebaseProvenance?: OcxConfigRebaseProvenance | Record<string, unknown>;
|
|
604
617
|
/** OpenAI provider-contract migration marker (v2 = single `openai` provider with account mode). */
|
|
@@ -770,6 +783,8 @@ export interface OcxConfig {
|
|
|
770
783
|
};
|
|
771
784
|
/** Opt-in failure-only recovery; never replaces the initial compaction model. */
|
|
772
785
|
compactionRecovery?: { enabled: boolean; model: string; allowDevinInvalidArgument?: boolean };
|
|
786
|
+
/** Explicit opt-in; resumes Observe only using an existing trusted certificate and endpoints. */
|
|
787
|
+
desktopCompatibility?: { startOnProxyStart: boolean };
|
|
773
788
|
/**
|
|
774
789
|
* Destination model for Codex's own memory pipeline, per phase
|
|
775
790
|
* (src/server/responses/memory-models.ts).
|
|
@@ -1028,6 +1043,16 @@ export interface OcxConfig {
|
|
|
1028
1043
|
cacheRetention?: "none" | "short" | "long";
|
|
1029
1044
|
/** Web-search sidecar: route web_search for non-OpenAI models through a gpt-mini via ChatGPT passthrough. */
|
|
1030
1045
|
webSearchSidecar?: OcxWebSearchSidecarConfig;
|
|
1046
|
+
/**
|
|
1047
|
+
* Advisor sidecar: an OpenCodex-owned expert consultation runtime. The proxy injects a synthetic
|
|
1048
|
+
* `advisor` tool into routed worker turns, executes the configured expert model itself (loopback
|
|
1049
|
+
* through the normal routing authority, so the advisor may be ANY routable provider/model), and
|
|
1050
|
+
* reinjects the advice so the original worker continues. `policy: "preflight"` additionally
|
|
1051
|
+
* attempts one automatic consultation per task without worker cooperation, once the task has
|
|
1052
|
+
* produced orientation evidence. Task context is sent only after
|
|
1053
|
+
* `contextSharingConsent` is the current version; `enabled` does not grant that consent.
|
|
1054
|
+
*/
|
|
1055
|
+
advisor?: OcxAdvisorConfig;
|
|
1031
1056
|
/** Vision sidecar: describe images via a gpt vision model so text-only models can "see" them. */
|
|
1032
1057
|
visionSidecar?: OcxVisionSidecarConfig;
|
|
1033
1058
|
/** /v1/images relay for codex's built-in image_gen tool. */
|
|
@@ -1552,6 +1577,38 @@ export interface OcxVisionSidecarConfig {
|
|
|
1552
1577
|
timeoutMs?: number;
|
|
1553
1578
|
}
|
|
1554
1579
|
|
|
1580
|
+
/**
|
|
1581
|
+
* Advisor sidecar configuration. Kept deliberately small for PR1: no adaptive trigger knobs
|
|
1582
|
+
* (failedValidations / noProgressCycles / semanticClassifier) — those belong to a later PR.
|
|
1583
|
+
*/
|
|
1584
|
+
export interface OcxAdvisorConfig {
|
|
1585
|
+
/** Master switch. Default: false — the advisor stays off the request path entirely. */
|
|
1586
|
+
enabled?: boolean;
|
|
1587
|
+
/**
|
|
1588
|
+
* The expert model consulted by the sidecar. Any model string the routing authority accepts:
|
|
1589
|
+
* a bare native model ("gpt-6-astra"), an explicit "provider/model" ("anthropic/claude-...",
|
|
1590
|
+
* "xai/grok-..."), or an account-qualified native model ("<namespace>/gpt-6-astra").
|
|
1591
|
+
*/
|
|
1592
|
+
model?: string;
|
|
1593
|
+
/** Reasoning effort for the advisor call. Validated against the canonical effort ladder. */
|
|
1594
|
+
effort?: "low" | "medium" | "high" | "xhigh" | "max" | "ultra";
|
|
1595
|
+
/**
|
|
1596
|
+
* When the advisor is consulted. "manual" (default): only when the worker explicitly calls the
|
|
1597
|
+
* synthetic `advisor` tool. "preflight": OpenCodex additionally attempts one automatic
|
|
1598
|
+
* consultation per task once the task has produced orientation evidence (an assistant tool call
|
|
1599
|
+
* or a tool result after the latest user message).
|
|
1600
|
+
*/
|
|
1601
|
+
policy?: "manual" | "preflight";
|
|
1602
|
+
/** Advisor fetch timeout (ms). Default 120000. */
|
|
1603
|
+
timeoutMs?: number;
|
|
1604
|
+
/**
|
|
1605
|
+
* Operator consent to send task conversation and tool results to the configured Advisor
|
|
1606
|
+
* provider. Only `"v1"` is current. Absent or any other value means the runtime must not
|
|
1607
|
+
* send task context. `enabled: true` does not grant this, and upgrades do not write it.
|
|
1608
|
+
*/
|
|
1609
|
+
contextSharingConsent?: "v1";
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1555
1612
|
export interface OcxWebSearchSidecarConfig {
|
|
1556
1613
|
/** Explicit Anthropic account pool; unset inherits the request's instance. */
|
|
1557
1614
|
anthropicInstance?: AnthropicInstanceId;
|
package/src/types/provider.ts
CHANGED
|
@@ -622,6 +622,11 @@ export interface OcxProviderConfig {
|
|
|
622
622
|
contextWindow?: number;
|
|
623
623
|
/** Per-model fallback when context metadata is absent; otherwise caps the reported window. */
|
|
624
624
|
modelContextWindows?: Record<string, number>;
|
|
625
|
+
/**
|
|
626
|
+
* Custom providers only, default off: fill a missing context window from bundled model-ID
|
|
627
|
+
* metadata before the Context cap / 128k fallback. An estimate, not verified relay capacity.
|
|
628
|
+
*/
|
|
629
|
+
inferContextWindow?: boolean;
|
|
625
630
|
/** Per-model Copilot upstream tier; only the github-copilot route sends it. */
|
|
626
631
|
modelContextTiers?: Record<string, "default" | "long_context">;
|
|
627
632
|
/** Model-specific Codex catalog input modalities, e.g. ["text"] or ["text", "image"]. */
|
|
@@ -639,6 +644,8 @@ export interface OcxProviderConfig {
|
|
|
639
644
|
* Responses `max_output_tokens`. Adapters still let an explicit request win.
|
|
640
645
|
*/
|
|
641
646
|
defaultMaxOutputTokens?: number;
|
|
647
|
+
/** Opt-in Azure Responses floor for explicit positive output budgets; may raise the caller cap. */
|
|
648
|
+
minMaxOutputTokens?: number;
|
|
642
649
|
/** Model-specific fallback output token budgets. Exact/model-pattern entries beat the provider default. */
|
|
643
650
|
modelMaxOutputTokens?: Record<string, number>;
|
|
644
651
|
/**
|
package/src/types/request.ts
CHANGED
|
@@ -164,6 +164,19 @@ export interface OcxParsedRequest {
|
|
|
164
164
|
* provider-private continuation caches again on every later turn.
|
|
165
165
|
*/
|
|
166
166
|
_contextCompactionBoundary?: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Core-owned registration slot for the optional advisor subsystem (src/advisor). Set per request
|
|
169
|
+
* by the sidecar planner, never at module load: the core Responses path only sees this
|
|
170
|
+
* structural shape, so an advisor-disabled install imports no advisor module. When present, the
|
|
171
|
+
* adapter delivery wraps the worker's event stream with it — the guard holds synthetic
|
|
172
|
+
* `advisor` tool calls, consults the configured expert model, and re-dispatches the worker.
|
|
173
|
+
*/
|
|
174
|
+
_advisorGuard?: (options: {
|
|
175
|
+
parsed: OcxParsedRequest;
|
|
176
|
+
firstEvents: AsyncIterable<AdapterEvent>;
|
|
177
|
+
/** One bounded worker continuation re-dispatch (same machinery as the terminal guard). */
|
|
178
|
+
continuation: (parsed: OcxParsedRequest) => AsyncIterable<AdapterEvent> | Promise<AsyncIterable<AdapterEvent>>;
|
|
179
|
+
}) => AsyncGenerator<AdapterEvent>;
|
|
167
180
|
}
|
|
168
181
|
|
|
169
182
|
export interface OcxContext {
|
|
@@ -441,6 +454,8 @@ export type AdapterEvent =
|
|
|
441
454
|
/** Responses error type and code when the adapter has a structured provider failure. */
|
|
442
455
|
errorType?: string;
|
|
443
456
|
code?: string;
|
|
457
|
+
/** Proxy-internal permission to consider API-key recovery; never serialized. */
|
|
458
|
+
apiKeyRecovery?: true;
|
|
444
459
|
retryable?: boolean;
|
|
445
460
|
/**
|
|
446
461
|
* Proxy-internal, never serialized: the upstream failed the turn before producing text or a
|
package/src/types/tools.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface OcxTool {
|
|
|
25
25
|
imageGeneration?: boolean;
|
|
26
26
|
/** Synthetic video_gen tool: executed by the xAI video bridge sidecar. */
|
|
27
27
|
videoGeneration?: boolean;
|
|
28
|
+
/** Synthetic advisor tool: the model's call is intercepted by the advisor sidecar (see src/advisor), never relayed to the client. */
|
|
29
|
+
advisor?: boolean;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
|
@@ -51,7 +51,7 @@ export async function latestVersionAsync(
|
|
|
51
51
|
installer: Installer,
|
|
52
52
|
deps: AsyncLookupDeps = defaultDeps,
|
|
53
53
|
): Promise<string | null> {
|
|
54
|
-
if (installer === "source" || installer === "mise") return null;
|
|
54
|
+
if (installer === "source" || installer === "standalone" || installer === "mise") return null;
|
|
55
55
|
let owner: PnpmGlobalOwner | null | undefined;
|
|
56
56
|
try { owner = installer === "pnpm" ? await deps.ownerFn() : undefined; }
|
|
57
57
|
catch { return null; }
|
package/src/update/badge.ts
CHANGED
|
@@ -44,11 +44,11 @@ export function readUpdateBadge(deps: UpdateBadgeDeps = defaultDeps): UpdateBadg
|
|
|
44
44
|
latestVersion: null,
|
|
45
45
|
channel,
|
|
46
46
|
installer,
|
|
47
|
-
canUpdate: installer !== "source" && installer !== "mise",
|
|
47
|
+
canUpdate: installer !== "source" && installer !== "standalone" && installer !== "mise",
|
|
48
48
|
unknown: true,
|
|
49
49
|
};
|
|
50
50
|
// A source checkout has nothing to compare against, so "unknown" is not useful there.
|
|
51
|
-
if (installer === "source" || current === "?" || isSourceBuildVersion(current)) {
|
|
51
|
+
if (installer === "source" || installer === "standalone" || current === "?" || isSourceBuildVersion(current)) {
|
|
52
52
|
return { ...base, canUpdate: false, unknown: false };
|
|
53
53
|
}
|
|
54
54
|
|
package/src/update/index.ts
CHANGED
|
@@ -17,13 +17,10 @@ import { unprivilegedOwnershipMutationEnvironment } from "../service/ownership-m
|
|
|
17
17
|
import { withUpdateOwnershipLease, readUpdateRuntimeTarget } from "./ownership-transaction";
|
|
18
18
|
import { npmInvocation } from "./npm-invocation.mjs";
|
|
19
19
|
import { pnpmInvocation, pnpmInvocationForPath, resolvePnpmCommands } from "./pnpm-invocation.mjs";
|
|
20
|
-
import {
|
|
21
|
-
detectInstallFromPath,
|
|
22
|
-
detectInstallOwnershipFromPath,
|
|
23
|
-
} from "./install-detection.mjs";
|
|
20
|
+
import { detectInstallOwnershipFromPath } from "./install-detection.mjs";
|
|
24
21
|
import type {
|
|
25
22
|
DetectedInstall,
|
|
26
|
-
InstallOwnership,
|
|
23
|
+
InstallOwnership as PackageInstallOwnership,
|
|
27
24
|
MiseInstallOwner,
|
|
28
25
|
} from "./install-detection.d.mts";
|
|
29
26
|
import {
|
|
@@ -42,8 +39,13 @@ import { handoffWindowsTrayForUpdate, planWindowsTrayUpdate } from "./tray-updat
|
|
|
42
39
|
import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
|
|
43
40
|
import { withoutSiblingMarker } from "../codex/sibling-start";
|
|
44
41
|
import { packageVersion } from "../lib/package-version";
|
|
42
|
+
import { isStandaloneModuleUrl } from "../lib/standalone";
|
|
45
43
|
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
46
44
|
import { PNPM_READ_CWD, withPnpmCommandCwd, pnpmReadEnvironment } from "./pnpm-read-policy.mjs";
|
|
45
|
+
import {
|
|
46
|
+
freshServingPid, hasRestartableServiceManager, readParentPid, readServiceManagerPid, restartServiceManagerJob,
|
|
47
|
+
supervisionVerdict, unsettledManagerWarning, unsupervisedRuntimeWarning,
|
|
48
|
+
} from "./supervised-recovery.mjs";
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* A `codex-history-backup-*.json` surviving a stop means the native-history restore was
|
|
@@ -62,18 +64,21 @@ export function historyRestoreIncomplete(configDir = getConfigDir()): boolean {
|
|
|
62
64
|
export const PKG = "@bitkyc08/opencodex";
|
|
63
65
|
const HERE = dirname(fileURLToPath(import.meta.url)); // .../opencodex/src/update
|
|
64
66
|
|
|
65
|
-
export type Installer = DetectedInstall;
|
|
67
|
+
export type Installer = DetectedInstall | "standalone";
|
|
68
|
+
export const STANDALONE_UPDATE_HINT = "Update via OpenCodex Desktop (Check for Updates…) or reinstall the standalone binary from GitHub Releases.";
|
|
66
69
|
export type Channel = "latest" | "preview";
|
|
67
|
-
export type {
|
|
70
|
+
export type InstallOwnership = PackageInstallOwnership | { installer: "standalone" };
|
|
71
|
+
export type { MiseInstallOwner };
|
|
68
72
|
|
|
69
73
|
/** Infer how opencodex is installed from the running module's path. */
|
|
70
|
-
export function detectInstall(): Installer {
|
|
71
|
-
return
|
|
74
|
+
export function detectInstall(moduleUrl = import.meta.url): Installer {
|
|
75
|
+
return detectInstallOwnership(moduleUrl).installer;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
/** Resolve installer ownership and verified mise update guidance for this package. */
|
|
75
|
-
export function detectInstallOwnership(): InstallOwnership {
|
|
76
|
-
|
|
79
|
+
export function detectInstallOwnership(moduleUrl = import.meta.url): InstallOwnership {
|
|
80
|
+
if (isStandaloneModuleUrl(moduleUrl)) return { installer: "standalone" };
|
|
81
|
+
return detectInstallOwnershipFromPath(dirname(fileURLToPath(moduleUrl)), { exists: existsSync });
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
export function miseUpdateCommand(
|
|
@@ -234,6 +239,7 @@ export function registrySpawnTarget(
|
|
|
234
239
|
args: readonly string[],
|
|
235
240
|
owner?: PnpmGlobalOwner,
|
|
236
241
|
): SpawnTarget | null {
|
|
242
|
+
if (installer === "standalone") return null;
|
|
237
243
|
// A pnpm command without an owner would silently fall back to the first PATH
|
|
238
244
|
// candidate. That is unsafe when multiple PNPM_HOME installations expose the
|
|
239
245
|
// same version, so registry queries use the same hard binding as mutation.
|
|
@@ -310,6 +316,7 @@ export function latestVersion(
|
|
|
310
316
|
|
|
311
317
|
/** The global-install command opencodex would run to update on this channel. */
|
|
312
318
|
export function updateCommand(installer: Installer, tag: Channel, resolvedVersion?: string | null): { bin: string; args: string[] } {
|
|
319
|
+
if (installer === "standalone") throw new Error(STANDALONE_UPDATE_HINT);
|
|
313
320
|
if (installer === "mise") {
|
|
314
321
|
throw new Error("mise-owned installations must be upgraded through mise");
|
|
315
322
|
}
|
|
@@ -427,6 +434,12 @@ export async function runUpdate(): Promise<void> {
|
|
|
427
434
|
return;
|
|
428
435
|
}
|
|
429
436
|
|
|
437
|
+
if (installer === "standalone") {
|
|
438
|
+
console.error(STANDALONE_UPDATE_HINT);
|
|
439
|
+
process.exitCode = 1;
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
430
443
|
if (installer === "source") {
|
|
431
444
|
console.log("Running from a source checkout — update with: git pull && bun install");
|
|
432
445
|
return;
|
|
@@ -551,7 +564,8 @@ export async function runUpdate(): Promise<void> {
|
|
|
551
564
|
return before?.owner === after?.owner && before?.installId === after?.installId
|
|
552
565
|
&& before?.consentGeneration === after?.consentGeneration;
|
|
553
566
|
};
|
|
554
|
-
|
|
567
|
+
/** `"vetoed"`: another owner holds the runtime, so not starting is the correct outcome. */
|
|
568
|
+
const startProxyDirectly = async (): Promise<boolean | "vetoed"> => {
|
|
555
569
|
const ownership = await resolvedRuntimeOwnership();
|
|
556
570
|
const supervision = observeSupervision();
|
|
557
571
|
const plan = planUpdateRuntimeHandling({ ...ownership, serviceInstalled: false, supervision });
|
|
@@ -559,7 +573,7 @@ export async function runUpdate(): Promise<void> {
|
|
|
559
573
|
console.warn(supervision
|
|
560
574
|
? "OpenCodex Desktop supervises the proxy; no CLI runtime was restored."
|
|
561
575
|
: plan.notice);
|
|
562
|
-
return
|
|
576
|
+
return "vetoed";
|
|
563
577
|
}
|
|
564
578
|
if (!postUpdateLauncherUsable || !existsSync(postUpdateLauncher)) return false;
|
|
565
579
|
// An ordinary owner: a stray sibling marker would otherwise mark it before any probe.
|
|
@@ -582,6 +596,51 @@ export async function runUpdate(): Promise<void> {
|
|
|
582
596
|
console.warn("⚠️ Recovery did not report a healthy proxy before its deadline; run 'ocx service repair' or 'ocx start'.");
|
|
583
597
|
return false;
|
|
584
598
|
};
|
|
599
|
+
// #7043: before any detached start, ask launchd/systemd to restart the job it already holds,
|
|
600
|
+
// and count it only when the serving proxy is that job. Runs unleased: the manager's child
|
|
601
|
+
// cannot join this lease (#5760). `"unsettled"` means the manager may still finish the restart,
|
|
602
|
+
// so no competing proxy may be started.
|
|
603
|
+
const restartThroughServiceManager = async (): Promise<"supervised" | "failed" | "unsettled"> => {
|
|
604
|
+
if (!hasRestartableServiceManager()) return "failed";
|
|
605
|
+
console.log("🔁 Asking the service manager to restart the background service...");
|
|
606
|
+
const before = readUpdateRuntimeTarget(getRuntimePortPath(), capturedListen.hostname);
|
|
607
|
+
const staleRecordPid = before.kind === "target" ? before.target.pid : null;
|
|
608
|
+
const restarted = restartServiceManagerJob();
|
|
609
|
+
const unsettled = !restarted.ok && restarted.unsettled === true;
|
|
610
|
+
let verdict: ReturnType<typeof supervisionVerdict> | { kind: "failed"; reason: string };
|
|
611
|
+
if (!restarted.ok && !unsettled) {
|
|
612
|
+
verdict = { kind: "failed", reason: restarted.reason };
|
|
613
|
+
} else {
|
|
614
|
+
let servingPid: number | null = null;
|
|
615
|
+
const deadline = Date.now() + 30_000;
|
|
616
|
+
while (servingPid === null && Date.now() < deadline) {
|
|
617
|
+
const current = readUpdateRuntimeTarget(getRuntimePortPath(), capturedListen.hostname);
|
|
618
|
+
if (current.kind === "target" && current.target.port === capturedListen.port
|
|
619
|
+
&& freshServingPid(current.target.pid, staleRecordPid) !== null
|
|
620
|
+
&& await proxyIdentityAt(current.target.port, { hostname: current.target.hostname, expectedPid: current.target.pid },
|
|
621
|
+
{ timeoutMs: 1500, deadlineAt: deadline, attempts: 1 })) servingPid = current.target.pid;
|
|
622
|
+
else await Bun.sleep(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
623
|
+
}
|
|
624
|
+
verdict = supervisionVerdict({ servingPid, managerPid: readServiceManagerPid(), parentPid: pid => readParentPid(pid) });
|
|
625
|
+
}
|
|
626
|
+
if (verdict.kind === "supervised") {
|
|
627
|
+
console.log(`✅ Proxy is healthy on port ${capturedListen.port} under the service manager (pid ${verdict.managerPid}).`);
|
|
628
|
+
return "supervised";
|
|
629
|
+
}
|
|
630
|
+
if (unsettled) {
|
|
631
|
+
console.warn(unsettledManagerWarning(capturedListen.port));
|
|
632
|
+
return "unsettled";
|
|
633
|
+
}
|
|
634
|
+
console.warn(`⚠️ The service manager did not recover a supervised proxy: ${verdict.reason}.`);
|
|
635
|
+
return "failed";
|
|
636
|
+
};
|
|
637
|
+
/** Direct start as the last resort for a managed install: never reported as full recovery. */
|
|
638
|
+
const startUnsupervisedFallback = async (): Promise<"vetoed" | "started" | "failed"> => {
|
|
639
|
+
const started = await startProxyDirectly();
|
|
640
|
+
if (started === "vetoed") return "vetoed";
|
|
641
|
+
if (started) console.warn(unsupervisedRuntimeWarning(capturedListen.port));
|
|
642
|
+
return started ? "started" : "failed";
|
|
643
|
+
};
|
|
585
644
|
const recoverStoppedRuntime = async (reason: string): Promise<void> => {
|
|
586
645
|
try {
|
|
587
646
|
const planRecovery = async () => {
|
|
@@ -617,6 +676,12 @@ export async function runUpdate(): Promise<void> {
|
|
|
617
676
|
env: mutation.controlEnvironment({ ...process.env, OCX_BAKE_PORT: String(capturedListen.port) }),
|
|
618
677
|
});
|
|
619
678
|
if (service.status !== 0 || !isServiceViable()) {
|
|
679
|
+
// Ownership may have moved while the refresh ran unleased; only a recovery plan that
|
|
680
|
+
// still names the service may restart the manager's job.
|
|
681
|
+
if ((await planRecovery()).action === "service") {
|
|
682
|
+
const managed = await restartThroughServiceManager();
|
|
683
|
+
if (managed !== "failed") return;
|
|
684
|
+
}
|
|
620
685
|
// The refresh ran unleased; a claim could have landed meanwhile, so take
|
|
621
686
|
// the lease back before re-reading ownership and authorizing a direct start.
|
|
622
687
|
mutation.reacquire();
|
|
@@ -626,7 +691,7 @@ export async function runUpdate(): Promise<void> {
|
|
|
626
691
|
liveness: currentPackageRuntimeLiveness(), serviceInstalled: false,
|
|
627
692
|
launcherUsable: postUpdateLauncherUsable && existsSync(postUpdateLauncher), hadRuntimeState,
|
|
628
693
|
});
|
|
629
|
-
if (fallback.action === "direct") await
|
|
694
|
+
if (fallback.action === "direct") await startUnsupervisedFallback();
|
|
630
695
|
else console.warn("⚠️ Service recovery was not confirmed; no second proxy was started.");
|
|
631
696
|
}
|
|
632
697
|
} else if (recovery.action === "direct") {
|
|
@@ -902,6 +967,12 @@ export async function runUpdate(): Promise<void> {
|
|
|
902
967
|
}
|
|
903
968
|
}
|
|
904
969
|
if (!serviceRefreshed || !serviceViable) {
|
|
970
|
+
// #7043: the manager first. Only a proxy it owns counts as recovered; everything
|
|
971
|
+
// below this line leaves an unsupervised runtime and exits non-zero.
|
|
972
|
+
if (!await mayRefresh()) return;
|
|
973
|
+
const managed = await restartThroughServiceManager();
|
|
974
|
+
if (managed === "supervised") return;
|
|
975
|
+
if (managed === "unsettled") return 1;
|
|
905
976
|
// Repair normally avoids elevation for a healthy scheduler task, but a stale
|
|
906
977
|
// definition may require guarded create/elevation. It can also fail — or exit 0
|
|
907
978
|
// while leaving stale/missing assets that never start
|
|
@@ -956,7 +1027,9 @@ export async function runUpdate(): Promise<void> {
|
|
|
956
1027
|
console.warn(process.platform === "win32"
|
|
957
1028
|
? " Run 'ocx service repair' to refresh the background service."
|
|
958
1029
|
: " Run 'ocx service repair' to refresh the background service and see why it failed.");
|
|
959
|
-
|
|
1030
|
+
// An ownership veto is the correct outcome, not a degraded one.
|
|
1031
|
+
if (await startUnsupervisedFallback() === "vetoed") return;
|
|
1032
|
+
return 1;
|
|
960
1033
|
}
|
|
961
1034
|
}
|
|
962
1035
|
} finally {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { readRuntimePort } from "../config/process-state";
|
|
2
|
+
import { proxyIdentityAt } from "../server/proxy-liveness";
|
|
3
|
+
import {
|
|
4
|
+
freshServingPid, hasRestartableServiceManager, readParentPid, readServiceManagerPid, restartServiceManagerJob,
|
|
5
|
+
supervisionVerdict, unsettledManagerWarning, unsupervisedRuntimeWarning,
|
|
6
|
+
} from "./supervised-recovery.mjs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manager-first recovery for the dashboard update worker (#7043), the `src/update/job.ts` side of
|
|
10
|
+
* what #7074 did for `ocx update`. When the post-update service refresh fails, is not viable, or
|
|
11
|
+
* never serves, the worker asks launchd/systemd to restart the job it already holds before any
|
|
12
|
+
* detached `ocx start`, and counts it only when the serving proxy is that job. It lives beside the
|
|
13
|
+
* worker because `src/update/job.ts` sits at the repository's size cap.
|
|
14
|
+
*/
|
|
15
|
+
export type ManagerRestartOutcome = "supervised" | "failed" | "unsettled";
|
|
16
|
+
|
|
17
|
+
export interface ManagerRestartDeps {
|
|
18
|
+
hasManager?: () => boolean;
|
|
19
|
+
restartJob?: () => { ok: true } | { ok: false; reason: string; unsettled?: true };
|
|
20
|
+
managerPid?: () => number | null;
|
|
21
|
+
parentPid?: (pid: number) => number | null;
|
|
22
|
+
readRecord?: () => { pid: number; port: number; hostname?: string } | null;
|
|
23
|
+
probeServing?: (port: number, hostname: string, pid: number, deadlineAt: number) => Promise<boolean>;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
sleepMs?: (ms: number) => Promise<void>;
|
|
26
|
+
deadlineMs?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type ManagerRestartFn = (
|
|
30
|
+
target: { port: number; hostname: string; release?: () => void },
|
|
31
|
+
log: (line: string) => void,
|
|
32
|
+
) => Promise<ManagerRestartOutcome>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ask the manager to restart its job and prove the serving proxy is that job. Never throws.
|
|
36
|
+
* `release` frees the update lease first: the manager's child cannot join it (#5760), and the
|
|
37
|
+
* worker reaches this point without a release when it skipped the service repair.
|
|
38
|
+
*/
|
|
39
|
+
export async function restartServiceManagerAfterUpdate(
|
|
40
|
+
target: { port: number; hostname: string; release?: () => void },
|
|
41
|
+
log: (line: string) => void,
|
|
42
|
+
deps: ManagerRestartDeps = {},
|
|
43
|
+
): Promise<ManagerRestartOutcome> {
|
|
44
|
+
if (!(deps.hasManager ?? hasRestartableServiceManager)()) return "failed";
|
|
45
|
+
target.release?.();
|
|
46
|
+
log("Asking the service manager to restart the background service...");
|
|
47
|
+
const readRecord = deps.readRecord ?? (() => readRuntimePort());
|
|
48
|
+
const probe = deps.probeServing ?? (async (port: number, hostname: string, pid: number, deadlineAt: number) => (
|
|
49
|
+
!!(await proxyIdentityAt(port, { hostname, expectedPid: pid }, { timeoutMs: 1500, deadlineAt, attempts: 1 }))
|
|
50
|
+
));
|
|
51
|
+
const now = deps.now ?? (() => Date.now());
|
|
52
|
+
const sleep = deps.sleepMs ?? ((ms: number) => new Promise<void>(r => setTimeout(r, ms)));
|
|
53
|
+
const staleRecordPid = readRecord()?.pid ?? null;
|
|
54
|
+
const restarted = (deps.restartJob ?? restartServiceManagerJob)();
|
|
55
|
+
const unsettled = !restarted.ok && restarted.unsettled === true;
|
|
56
|
+
let verdict: ReturnType<typeof supervisionVerdict>;
|
|
57
|
+
if (!restarted.ok && !unsettled) {
|
|
58
|
+
// The refusal reason is the manager's stderr: external text, so it never reaches the persisted job.
|
|
59
|
+
verdict = { kind: "failed", reason: "the service manager refused the restart" };
|
|
60
|
+
} else {
|
|
61
|
+
let servingPid: number | null = null;
|
|
62
|
+
const deadline = now() + (deps.deadlineMs ?? 30_000);
|
|
63
|
+
while (servingPid === null && now() < deadline) {
|
|
64
|
+
const current = readRecord();
|
|
65
|
+
if (current && current.port === target.port && freshServingPid(current.pid, staleRecordPid) !== null
|
|
66
|
+
&& await probe(current.port, current.hostname ?? target.hostname, current.pid, deadline)) servingPid = current.pid;
|
|
67
|
+
else await sleep(Math.min(100, Math.max(0, deadline - now())));
|
|
68
|
+
}
|
|
69
|
+
verdict = supervisionVerdict({
|
|
70
|
+
servingPid,
|
|
71
|
+
managerPid: (deps.managerPid ?? readServiceManagerPid)(),
|
|
72
|
+
parentPid: deps.parentPid ?? (pid => readParentPid(pid)),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (verdict.kind === "supervised") {
|
|
76
|
+
log(`Proxy is healthy on port ${target.port} under the service manager (pid ${verdict.managerPid}).`);
|
|
77
|
+
return "supervised";
|
|
78
|
+
}
|
|
79
|
+
if (unsettled) return "unsettled";
|
|
80
|
+
log(`The service manager did not recover a supervised proxy: ${verdict.reason}.`);
|
|
81
|
+
return "failed";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Job patch and log line when the manager may still finish its restart: no second proxy is started. */
|
|
85
|
+
export function unsettledManagerRestartFailure(port: number) {
|
|
86
|
+
const patch = {
|
|
87
|
+
status: "failed" as const,
|
|
88
|
+
restarted: false,
|
|
89
|
+
error: "the service manager did not finish restarting the proxy; no second proxy was started",
|
|
90
|
+
};
|
|
91
|
+
return [patch, oneLine(unsettledManagerWarning(port))] as const;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Job patch and log line when only a detached fallback ran for a service-managed install. */
|
|
95
|
+
export function unsupervisedRestartFailure(port: number) {
|
|
96
|
+
const patch = {
|
|
97
|
+
status: "failed" as const,
|
|
98
|
+
restarted: false,
|
|
99
|
+
error: `the proxy on port ${port} is running UNSUPERVISED after the update; run 'ocx service repair'`,
|
|
100
|
+
};
|
|
101
|
+
return [patch, oneLine(unsupervisedRuntimeWarning(port))] as const;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The job store withholds multi-line text as possible vendor output, so join the shared warning. */
|
|
105
|
+
function oneLine(text: string): string {
|
|
106
|
+
return text.split("\n").map(line => line.trim()).join(" ");
|
|
107
|
+
}
|