sneakoscope 6.1.0 → 6.2.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 +35 -11
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +18 -9
- package/dist/bin/sks-dispatch.js +2 -2
- package/dist/cli/args.js +2 -0
- package/dist/cli/command-manifest-lite.js +2 -3
- package/dist/cli/command-registry.js +3 -4
- package/dist/cli/help-fast.js +1 -1
- package/dist/cli/install-helpers.js +116 -306
- package/dist/cli/install-tool-helpers.js +287 -0
- package/dist/cli/router.js +40 -4
- package/dist/commands/codex-lb.js +51 -20
- package/dist/commands/codex.js +39 -1
- package/dist/commands/doctor.js +242 -20
- package/dist/commands/tmux.js +5 -1
- package/dist/commands/zellij-monitor-pane.js +2 -0
- package/dist/commands/zellij-viewport-pane.js +3 -1
- package/dist/commands/zellij.js +1 -1
- package/dist/config/skills-manifest.json +59 -59
- package/dist/core/agent-bridge/agent-manifest.js +48 -0
- package/dist/core/agents/agent-command-surface.js +2 -1
- package/dist/core/agents/agent-effort-policy.js +38 -35
- package/dist/core/agents/agent-plan.js +100 -21
- package/dist/core/agents/agent-role-config.js +43 -46
- package/dist/core/agents/agent-runner-codex-exec.js +16 -3
- package/dist/core/agents/agent-schema.js +3 -2
- package/dist/core/agents/native-cli-session-swarm.js +1 -1
- package/dist/core/agents/zellij-right-lane-cockpit.js +2 -2
- package/dist/core/codex/agent-config-file-repair.js +94 -193
- package/dist/core/codex/codex-cli-update.js +723 -0
- package/dist/core/codex/codex-config-guard.js +185 -9
- package/dist/core/codex/codex-config-readability.js +21 -8
- package/dist/core/codex/codex-config-toml.js +14 -11
- package/dist/core/codex/codex-project-config-policy.js +3 -6
- package/dist/core/codex/codex-startup-config-postcheck.js +52 -42
- package/dist/core/codex-adapter.js +35 -22
- package/dist/core/codex-app/codex-agent-role-sync.js +13 -54
- package/dist/core/codex-app/codex-app-launcher.js +48 -17
- package/dist/core/codex-app/codex-app-restart.js +23 -2
- package/dist/core/codex-app/mcp-manager.js +679 -0
- package/dist/core/codex-app/sks-menubar.js +533 -8
- package/dist/core/codex-control/codex-0139-image-path-real-probe.js +15 -4
- package/dist/core/codex-control/codex-0139-web-search-probe.js +17 -6
- package/dist/core/codex-control/codex-lb-launch-recovery.js +293 -0
- package/dist/core/codex-control/codex-reliability-shield.js +89 -29
- package/dist/core/codex-control/codex-sdk-adapter.js +44 -8
- package/dist/core/codex-control/codex-task-runner.js +62 -9
- package/dist/core/codex-control/python-codex-sdk-adapter.js +30 -3
- package/dist/core/codex-exec-output-schema.js +35 -6
- package/dist/core/codex-lb/codex-lb-env.js +1 -1
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +291 -0
- package/dist/core/codex-native/codex-native-feature-broker.js +9 -16
- package/dist/core/codex-native/codex-native-repair-transaction.js +1 -1
- package/dist/core/codex-native/core-skill-manifest.js +8 -4
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -3
- package/dist/core/commands/agent-bridge-command.js +23 -2
- package/dist/core/commands/agent-command.js +19 -5
- package/dist/core/commands/basic-cli.js +47 -6
- package/dist/core/commands/command-utils.js +1 -1
- package/dist/core/commands/mad-sks-command.js +76 -10
- package/dist/core/commands/menubar-command.js +94 -0
- package/dist/core/commands/naruto-command.js +645 -1156
- package/dist/core/commands/research-command.js +293 -231
- package/dist/core/commands/run-command.js +100 -23
- package/dist/core/commands/team-command.js +2 -3
- package/dist/core/commands/team-legacy-observe-command.js +94 -359
- package/dist/core/commands/wiki-command.js +11 -5
- package/dist/core/db-safety.js +2 -2
- package/dist/core/decision-lattice.js +6 -6
- package/dist/core/doctor/codex-startup-config-repair.js +8 -3
- package/dist/core/doctor/doctor-codex-startup-repair.js +6 -141
- package/dist/core/feature-fixture-executor.js +71 -7
- package/dist/core/feature-fixture-runner.js +53 -12
- package/dist/core/feature-fixtures.js +47 -14
- package/dist/core/feature-registry.js +28 -27
- package/dist/core/fsx.js +1 -0
- package/dist/core/harness-conflicts.js +18 -9
- package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +14 -8
- package/dist/core/hooks-runtime/hook-io.js +8 -4
- package/dist/core/hooks-runtime/light-turn.js +70 -0
- package/dist/core/hooks-runtime/naruto-decision-gate.js +184 -0
- package/dist/core/hooks-runtime/payload-signals.js +270 -0
- package/dist/core/hooks-runtime/stop-repeat-guard.js +89 -0
- package/dist/core/hooks-runtime/team-digest.js +0 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +93 -0
- package/dist/core/hooks-runtime.js +438 -354
- package/dist/core/init/skills.js +16 -30
- package/dist/core/init.js +144 -36
- package/dist/core/managed-assets/managed-assets-manifest.js +566 -10
- package/dist/core/mission.js +24 -1
- package/dist/core/pipeline-internals/runtime-core.js +570 -177
- package/dist/core/pipeline-internals/runtime-gates.js +174 -48
- package/dist/core/preflight/parallel-preflight-engine.js +66 -4
- package/dist/core/proof/route-adapter.js +1 -1
- package/dist/core/proof/route-proof-gate.js +7 -2
- package/dist/core/proof/selftest-proof-fixtures.js +3 -5
- package/dist/core/proof-field.js +1 -1
- package/dist/core/provider/model-router.js +42 -31
- package/dist/core/recallpulse/policy.js +12 -28
- package/dist/core/recallpulse.js +11 -6
- package/dist/core/release/npm-pack-proof.js +247 -0
- package/dist/core/release/package-dist-snapshot.js +151 -0
- package/dist/core/release/package-size-budget.js +4 -1
- package/dist/core/release/release-authorization-snapshot.js +115 -0
- package/dist/core/release/release-gate-affected-selector.js +14 -2
- package/dist/core/release/release-gate-cache-v2.js +30 -0
- package/dist/core/release/release-gate-contract.js +161 -0
- package/dist/core/release/release-gate-dag.js +1 -0
- package/dist/core/release/release-real-contract.js +90 -10
- package/dist/core/release-parallel-full-coverage.js +31 -143
- package/dist/core/research/claim-evidence-matrix.js +41 -6
- package/dist/core/research/experiment-plan.js +14 -10
- package/dist/core/research/falsification.js +9 -2
- package/dist/core/research/implementation-blueprint-densifier.js +82 -60
- package/dist/core/research/implementation-blueprint.js +32 -26
- package/dist/core/research/mock-result.js +122 -11
- package/dist/core/research/replication-pack.js +15 -8
- package/dist/core/research/research-adversarial-review.js +1068 -0
- package/dist/core/research/research-claim-builder.js +69 -17
- package/dist/core/research/research-claim-synthesizer.js +343 -0
- package/dist/core/research/research-cycle-runner.js +53 -3
- package/dist/core/research/research-falsification-runner.js +176 -0
- package/dist/core/research/research-final-reviewer.js +44 -125
- package/dist/core/research/research-plan-markdown.js +123 -0
- package/dist/core/research/research-realistic-report.js +14 -6
- package/dist/core/research/research-review-artifact-digest.js +66 -0
- package/dist/core/research/research-source-evidence.js +144 -0
- package/dist/core/research/research-source-layer-catalog.js +68 -0
- package/dist/core/research/research-source-ledger-merge.js +250 -12
- package/dist/core/research/research-source-shards.js +26 -70
- package/dist/core/research/research-stage-runner.js +237 -247
- package/dist/core/research/research-super-search.js +188 -0
- package/dist/core/research/research-synthesis-prompt.js +20 -1
- package/dist/core/research/research-synthesis-writer.js +86 -5
- package/dist/core/research/research-work-graph.js +25 -18
- package/dist/core/research/source-quality-report.js +21 -0
- package/dist/core/research.js +424 -390
- package/dist/core/retention.js +70 -2
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/dollar-manifest-lite.js +11 -11
- package/dist/core/routes.js +149 -69
- package/dist/core/runtime/task-profile.js +66 -0
- package/dist/core/runtime/verification-budget.js +12 -0
- package/dist/core/security/high-risk-contracts.js +6 -6
- package/dist/core/stop-gate/stop-gate-check.js +49 -12
- package/dist/core/subagents/agent-catalog.js +287 -0
- package/dist/core/subagents/model-policy.js +259 -0
- package/dist/core/subagents/naruto-help-contract.js +47 -0
- package/dist/core/subagents/official-subagent-config.js +488 -0
- package/dist/core/subagents/official-subagent-preparation.js +297 -0
- package/dist/core/subagents/official-subagent-prompt.js +159 -0
- package/dist/core/subagents/official-subagent-runner.js +129 -0
- package/dist/core/subagents/subagent-evidence.js +663 -0
- package/dist/core/subagents/terminology.js +16 -0
- package/dist/core/subagents/thread-budget.js +21 -0
- package/dist/core/subagents/triwiki-attention.js +168 -0
- package/dist/core/super-search/runtime-helpers.js +173 -19
- package/dist/core/super-search/runtime.js +140 -15
- package/dist/core/super-search/source-records.js +49 -8
- package/dist/core/team-review-policy.js +15 -0
- package/dist/core/triwiki/code-pack-head-freshness.js +291 -0
- package/dist/core/triwiki/triwiki-module-card.js +1 -1
- package/dist/core/triwiki-wrongness/wrongness-ledger.js +9 -2
- package/dist/core/version-manager.js +51 -1
- package/dist/core/version.js +1 -1
- package/dist/core/work-order-ledger.js +40 -1
- package/dist/core/zellij/zellij-fake-adapter.js +3 -1
- package/dist/core/zellij/zellij-launcher.js +49 -10
- package/dist/core/zellij/zellij-layout-builder.js +14 -5
- package/dist/core/zellij/zellij-official-subagent-activity.js +477 -0
- package/dist/core/zellij/zellij-official-subagent-telemetry.js +264 -0
- package/dist/core/zellij/zellij-pane-proof.js +9 -1
- package/dist/core/zellij/zellij-slot-pane-renderer.js +17 -5
- package/dist/core/zellij/zellij-slot-telemetry.js +15 -3
- package/dist/core/zellij/zellij-ui-mode.js +1 -1
- package/dist/core/zellij/zellij-update.js +14 -1
- package/dist/core/zellij/zellij-viewport-binder.js +3 -0
- package/dist/scripts/agent-role-config-repair-check.js +14 -16
- package/dist/scripts/architecture-guard-check.js +8 -1
- package/dist/scripts/canonical-test-runner.js +7 -1
- package/dist/scripts/check-route-modularity.js +0 -1
- package/dist/scripts/check-ts-contracts.js +1 -1
- package/dist/scripts/codex-control-all-pipelines-check.js +7 -3
- package/dist/scripts/codex-control-tool-output-continuity-audit-check.js +27 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +11 -1
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +9 -2
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +13 -2
- package/dist/scripts/codex-native-agent-role-content-check.js +27 -7
- package/dist/scripts/codex-native-repair-transaction-check.js +3 -1
- package/dist/scripts/codex-sdk-all-pipelines-check.js +7 -4
- package/dist/scripts/codex-sdk-backend-router-check.js +2 -0
- package/dist/scripts/codex-sdk-research-pipeline-check.js +30 -8
- package/dist/scripts/db-route-materialization-check.js +33 -0
- package/dist/scripts/docs-truthfulness-check.js +3 -1
- package/dist/scripts/doctor-codex-startup-repair-check.js +12 -15
- package/dist/scripts/doctor-fix-production-blackbox.js +12 -2
- package/dist/scripts/doctor-startup-config-repair-blackbox.js +8 -2
- package/dist/scripts/doctor-startup-config-repair-check.js +12 -2
- package/dist/scripts/install-update-preserves-config-check.js +6 -1
- package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
- package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +0 -1
- package/dist/scripts/managed-role-manifest-parity-check.js +5 -4
- package/dist/scripts/naruto-codex-e2e-check.js +14 -7
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +4 -201
- package/dist/scripts/npm-publish-performance-check.js +20 -12
- package/dist/scripts/official-subagent-workflow-check.js +145 -0
- package/dist/scripts/package-published-contract-check.js +6 -29
- package/dist/scripts/packlist-performance-check.js +18 -21
- package/dist/scripts/parallel-verification-engine-check.js +2 -2
- package/dist/scripts/prepublish-release-check-or-fast.js +23 -86
- package/dist/scripts/python-codex-sdk-all-pipelines-check.js +8 -0
- package/dist/scripts/release-affected-selector-check.js +74 -1
- package/dist/scripts/release-check-stamp.js +153 -248
- package/dist/scripts/release-dag-full-coverage-check.js +7 -15
- package/dist/scripts/release-dynamic-presets-check.js +2 -1
- package/dist/scripts/release-gate-dag-runner-check.js +0 -1
- package/dist/scripts/release-gate-dag-runner.js +12 -1
- package/dist/scripts/release-gate-existence-audit.js +1 -2
- package/dist/scripts/release-parallel-full-coverage-check.js +12 -6
- package/dist/scripts/release-parallel-speed-budget-check.js +18 -2
- package/dist/scripts/release-real-check.js +26 -4
- package/dist/scripts/release-registry-check.js +61 -16
- package/dist/scripts/research-blueprint-densifier-check.js +2 -2
- package/dist/scripts/research-real-cycle-no-legacy-final-md-check.js +11 -7
- package/dist/scripts/sizecheck.js +2 -2
- package/dist/scripts/sks-3-1-4-directive-check-lib.js +8 -2
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +3 -2
- package/dist/scripts/sks-3-1-6-directive-check-lib.js +5 -3
- package/dist/scripts/sks-menubar-install-check.js +14 -1
- package/dist/scripts/super-search-provider-interface-check.js +31 -18
- package/dist/scripts/trust-fixture-check.js +32 -10
- package/dist/scripts/wrongness-fixture-check.js +1 -1
- package/dist/scripts/zellij-layout-valid-check.js +5 -5
- package/dist/scripts/zellij-spawn-on-demand-layout-check.js +3 -3
- package/docs/demo.tape +1 -1
- package/infra-harness-gates.json +1486 -0
- package/package.json +15 -10
- package/release-gates.v2.json +4060 -0
- package/runtime-required-scripts.json +9 -0
- package/dist/commands/db.js +0 -6
- package/dist/core/commands/db-command.js +0 -146
- package/dist/core/research/prompt.js +0 -15
- package/dist/scripts/codex-0139-feature-probes-check.js +0 -30
- package/dist/scripts/codex-0139-marketplace-source-check.js +0 -13
- package/dist/scripts/codex-control-tool-call-sequence-repair-check.js +0 -14
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ensureDir, exists, packageRoot, readJson, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
5
|
+
export const CODEX_CLI_UPDATE_STATUS_SCHEMA = 'sks.codex-cli-update-status.v1';
|
|
6
|
+
export const CODEX_CLI_UPDATE_RESULT_SCHEMA = 'sks.codex-cli-update-result.v1';
|
|
7
|
+
const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
8
|
+
const CODEX_STANDALONE_INSTALLER_URL = 'https://chatgpt.com/codex/install.sh';
|
|
9
|
+
const CODEX_STANDALONE_INSTALLER_PS1_URL = 'https://chatgpt.com/codex/install.ps1';
|
|
10
|
+
export function codexCliUpdateCachePath(home = process.env.HOME || os.homedir()) {
|
|
11
|
+
return path.join(String(home || os.homedir()), '.sneakoscope', 'cache', 'codex-cli-update.json');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the operator's Codex CLI for menu status/self-update operations.
|
|
15
|
+
*
|
|
16
|
+
* This is intentionally separate from resolveCodexRuntime/getCodexInfo. Runtime
|
|
17
|
+
* execution may use the @openai/codex dependency bundled under Sneakoscope, but
|
|
18
|
+
* self-update must never mutate that package-owned dependency. Explicit operator
|
|
19
|
+
* overrides are considered first, followed by every executable on PATH; any
|
|
20
|
+
* candidate owned by this Sneakoscope package is rejected and resolution continues.
|
|
21
|
+
*/
|
|
22
|
+
export async function resolveOperatorCodexCli(opts = {}) {
|
|
23
|
+
const env = opts.env || process.env;
|
|
24
|
+
const run = opts.deps?.runProcessImpl || runProcess;
|
|
25
|
+
const candidates = await operatorCodexCandidates(opts.codexBin, env);
|
|
26
|
+
const warnings = [];
|
|
27
|
+
for (const candidate of candidates) {
|
|
28
|
+
if (!await exists(candidate.path)) {
|
|
29
|
+
if (candidate.source === 'explicit')
|
|
30
|
+
warnings.push(`codex_cli_explicit_override_missing:${candidate.path}`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const realPath = await fsp.realpath(candidate.path).catch(() => candidate.path);
|
|
34
|
+
const rejectedReason = await nonOperatorCodexReason(candidate.path, realPath);
|
|
35
|
+
if (rejectedReason) {
|
|
36
|
+
warnings.push(`codex_cli_${rejectedReason}_candidate_rejected:${candidate.path}`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const result = await run(candidate.path, ['--version'], {
|
|
40
|
+
timeoutMs: 10_000,
|
|
41
|
+
maxOutputBytes: 16 * 1024,
|
|
42
|
+
env
|
|
43
|
+
}).catch((err) => failedProcessResult(err));
|
|
44
|
+
const rawVersion = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
|
45
|
+
const version = result.code === 0 ? officialCodexCliVersionNumber(rawVersion) : null;
|
|
46
|
+
if (!version) {
|
|
47
|
+
warnings.push(`codex_cli_candidate_identity_or_version_unavailable:${candidate.path}`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
source: candidate.source,
|
|
53
|
+
path: candidate.path,
|
|
54
|
+
version,
|
|
55
|
+
raw_version: rawVersion,
|
|
56
|
+
warnings,
|
|
57
|
+
blockers: []
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
source: 'unavailable',
|
|
63
|
+
path: null,
|
|
64
|
+
version: null,
|
|
65
|
+
raw_version: null,
|
|
66
|
+
warnings,
|
|
67
|
+
blockers: ['operator_codex_cli_missing']
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export async function inspectCodexCliUpdate(opts = {}) {
|
|
71
|
+
const deps = opts.deps || {};
|
|
72
|
+
const now = deps.now ? deps.now() : new Date();
|
|
73
|
+
const home = opts.home || opts.env?.HOME || process.env.HOME || os.homedir();
|
|
74
|
+
const cachePath = codexCliUpdateCachePath(home);
|
|
75
|
+
const codex = await resolveOperatorCodexCli({
|
|
76
|
+
...(opts.codexBin ? { codexBin: opts.codexBin } : {}),
|
|
77
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
78
|
+
deps
|
|
79
|
+
});
|
|
80
|
+
if (!codex.ok || !codex.path || !codex.version) {
|
|
81
|
+
return {
|
|
82
|
+
schema: CODEX_CLI_UPDATE_STATUS_SCHEMA,
|
|
83
|
+
ok: false,
|
|
84
|
+
status: 'missing',
|
|
85
|
+
installed: false,
|
|
86
|
+
bin: null,
|
|
87
|
+
cli_path: null,
|
|
88
|
+
cli_source: 'unavailable',
|
|
89
|
+
current_version: null,
|
|
90
|
+
raw_version: null,
|
|
91
|
+
latest_version: null,
|
|
92
|
+
update_available: null,
|
|
93
|
+
update_command: 'sks codex update',
|
|
94
|
+
source: 'unavailable',
|
|
95
|
+
checked_at: now.toISOString(),
|
|
96
|
+
cache_path: cachePath,
|
|
97
|
+
warnings: codex.warnings,
|
|
98
|
+
blockers: ['codex_cli_missing', ...codex.blockers],
|
|
99
|
+
guidance: ['Install a release build of Codex CLI on the operator PATH, then reopen the SKS menu bar.']
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const ttlMs = Math.max(0, Number(opts.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS));
|
|
103
|
+
const cached = await readJson(cachePath, null).catch(() => null);
|
|
104
|
+
const cachedAt = Date.parse(String(cached?.checked_at || ''));
|
|
105
|
+
const cachedFresh = opts.force !== true
|
|
106
|
+
&& cached?.schema === CODEX_CLI_UPDATE_STATUS_SCHEMA
|
|
107
|
+
&& typeof cached?.latest_version === 'string'
|
|
108
|
+
&& Number.isFinite(cachedAt)
|
|
109
|
+
&& now.getTime() - cachedAt < ttlMs;
|
|
110
|
+
if (cachedFresh) {
|
|
111
|
+
return buildStatus({
|
|
112
|
+
codex,
|
|
113
|
+
latestVersion: cached.latest_version,
|
|
114
|
+
source: 'cache',
|
|
115
|
+
checkedAt: String(cached.checked_at),
|
|
116
|
+
cachePath
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const env = opts.env || process.env;
|
|
120
|
+
const npm = deps.whichImpl
|
|
121
|
+
? await deps.whichImpl('npm').catch(() => null)
|
|
122
|
+
: await resolveOperatorExecutable('npm', env);
|
|
123
|
+
if (!npm)
|
|
124
|
+
return unavailableStatus(codex, now, cachePath, 'npm_not_found');
|
|
125
|
+
const run = deps.runProcessImpl || runProcess;
|
|
126
|
+
const result = await run(npm, ['view', '@openai/codex', 'version'], {
|
|
127
|
+
timeoutMs: 8_000,
|
|
128
|
+
maxOutputBytes: 8 * 1024,
|
|
129
|
+
env: opts.env || process.env
|
|
130
|
+
}).catch((err) => failedProcessResult(err));
|
|
131
|
+
const latestVersion = result.code === 0
|
|
132
|
+
? codexCliVersionNumber(String(result.stdout || '').trim().split(/\s+/).pop() || '')
|
|
133
|
+
: null;
|
|
134
|
+
if (!latestVersion) {
|
|
135
|
+
return unavailableStatus(codex, now, cachePath, String(result.stderr || result.stdout || 'codex_cli_latest_version_unavailable').trim());
|
|
136
|
+
}
|
|
137
|
+
const status = buildStatus({
|
|
138
|
+
codex,
|
|
139
|
+
latestVersion,
|
|
140
|
+
source: 'npm',
|
|
141
|
+
checkedAt: now.toISOString(),
|
|
142
|
+
cachePath
|
|
143
|
+
});
|
|
144
|
+
await ensureDir(path.dirname(cachePath));
|
|
145
|
+
await writeJsonAtomic(cachePath, status).catch(() => null);
|
|
146
|
+
return status;
|
|
147
|
+
}
|
|
148
|
+
export async function updateCodexCliNow(opts = {}) {
|
|
149
|
+
const deps = opts.deps || {};
|
|
150
|
+
const before = await resolveOperatorCodexCli({
|
|
151
|
+
...(opts.codexBin ? { codexBin: opts.codexBin } : {}),
|
|
152
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
153
|
+
deps
|
|
154
|
+
});
|
|
155
|
+
if (!before.ok || !before.path || !before.version) {
|
|
156
|
+
return updateResult({
|
|
157
|
+
ok: false,
|
|
158
|
+
status: 'missing',
|
|
159
|
+
before,
|
|
160
|
+
after: null,
|
|
161
|
+
rawOutput: '',
|
|
162
|
+
updateStatus: null,
|
|
163
|
+
command: null,
|
|
164
|
+
updateMethod: 'unknown',
|
|
165
|
+
blockers: ['codex_cli_missing', ...before.blockers],
|
|
166
|
+
guidance: ['Install a release build of Codex CLI on the operator PATH first; SKS does not invent a package-manager fallback.']
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const env = opts.env || process.env;
|
|
170
|
+
const run = deps.runProcessImpl || runProcess;
|
|
171
|
+
const capability = await run(before.path, ['update', '--help'], {
|
|
172
|
+
timeoutMs: 10_000,
|
|
173
|
+
maxOutputBytes: 32 * 1024,
|
|
174
|
+
env
|
|
175
|
+
}).catch((err) => failedProcessResult(err));
|
|
176
|
+
const capabilityOutput = `${capability.stdout || ''}${capability.stderr || ''}`.trim();
|
|
177
|
+
const nativeSelfUpdateSupported = capability.code === 0
|
|
178
|
+
&& /Usage:\s+codex\s+update\b/i.test(capabilityOutput);
|
|
179
|
+
const updatePlan = nativeSelfUpdateSupported
|
|
180
|
+
? {
|
|
181
|
+
method: 'native-self-update',
|
|
182
|
+
command: 'codex update',
|
|
183
|
+
executable: before.path,
|
|
184
|
+
args: ['update']
|
|
185
|
+
}
|
|
186
|
+
: await detectCodexCliUpdatePlan({ before, env, deps, run });
|
|
187
|
+
if (!updatePlan) {
|
|
188
|
+
return updateResult({
|
|
189
|
+
ok: false,
|
|
190
|
+
status: 'failed',
|
|
191
|
+
before,
|
|
192
|
+
after: null,
|
|
193
|
+
rawOutput: capabilityOutput,
|
|
194
|
+
updateStatus: null,
|
|
195
|
+
command: null,
|
|
196
|
+
updateMethod: 'unknown',
|
|
197
|
+
blockers: ['codex_cli_update_method_unverified'],
|
|
198
|
+
guidance: [
|
|
199
|
+
'The selected Codex CLI neither advertises native self-update nor maps safely to the official standalone installer, npm global package, or Homebrew cask.',
|
|
200
|
+
'Reinstall Codex with an official supported method, then retry.'
|
|
201
|
+
]
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const result = updatePlan.method === 'standalone-installer'
|
|
205
|
+
? await runStandaloneInstallerUpdate({ before, env, deps, run })
|
|
206
|
+
: await run(updatePlan.executable, updatePlan.args, {
|
|
207
|
+
timeoutMs: 180_000,
|
|
208
|
+
maxOutputBytes: 128 * 1024,
|
|
209
|
+
env
|
|
210
|
+
}).catch((err) => failedProcessResult(err));
|
|
211
|
+
const rawOutput = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
|
212
|
+
if (result.code !== 0) {
|
|
213
|
+
return updateResult({
|
|
214
|
+
ok: false,
|
|
215
|
+
status: 'failed',
|
|
216
|
+
before,
|
|
217
|
+
after: null,
|
|
218
|
+
rawOutput,
|
|
219
|
+
updateStatus: null,
|
|
220
|
+
command: updatePlan.command,
|
|
221
|
+
updateMethod: updatePlan.method,
|
|
222
|
+
blockers: [updateFailureBlocker(updatePlan.method)],
|
|
223
|
+
guidance: [`Run the detected updater in a terminal to review its output: ${updatePlan.command}`]
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const after = await resolveOperatorCodexCli({
|
|
227
|
+
...(opts.codexBin ? { codexBin: opts.codexBin } : {}),
|
|
228
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
229
|
+
deps
|
|
230
|
+
});
|
|
231
|
+
if (!after.ok || !after.path || !after.version) {
|
|
232
|
+
return updateResult({
|
|
233
|
+
ok: false,
|
|
234
|
+
status: 'failed',
|
|
235
|
+
before,
|
|
236
|
+
after,
|
|
237
|
+
rawOutput,
|
|
238
|
+
updateStatus: null,
|
|
239
|
+
command: updatePlan.command,
|
|
240
|
+
updateMethod: updatePlan.method,
|
|
241
|
+
blockers: ['codex_cli_post_update_missing', ...after.blockers],
|
|
242
|
+
guidance: ['The updater exited successfully, but the operator Codex CLI or its version disappeared. Reinstall Codex CLI and retry the status check.']
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (after.path !== before.path) {
|
|
246
|
+
return updateResult({
|
|
247
|
+
ok: false,
|
|
248
|
+
status: 'failed',
|
|
249
|
+
before,
|
|
250
|
+
after,
|
|
251
|
+
rawOutput,
|
|
252
|
+
updateStatus: null,
|
|
253
|
+
command: updatePlan.command,
|
|
254
|
+
updateMethod: updatePlan.method,
|
|
255
|
+
blockers: ['codex_cli_post_update_target_changed'],
|
|
256
|
+
guidance: ['The updated Codex CLI path disappeared and resolution fell through to a different installation. Restore or reinstall the original operator CLI before retrying.']
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const versionComparison = compareCodexCliVersions(after.version, before.version);
|
|
260
|
+
if (versionComparison < 0) {
|
|
261
|
+
return updateResult({
|
|
262
|
+
ok: false,
|
|
263
|
+
status: 'failed',
|
|
264
|
+
before,
|
|
265
|
+
after,
|
|
266
|
+
rawOutput,
|
|
267
|
+
updateStatus: null,
|
|
268
|
+
command: updatePlan.command,
|
|
269
|
+
updateMethod: updatePlan.method,
|
|
270
|
+
blockers: ['codex_cli_post_update_version_regressed'],
|
|
271
|
+
guidance: ['The Codex updater returned an older operator CLI version. Restore or reinstall the expected release before retrying.']
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const inspect = deps.inspectCodexCliUpdateImpl || inspectCodexCliUpdate;
|
|
275
|
+
const updateStatus = await inspect({
|
|
276
|
+
...(opts.home ? { home: opts.home } : {}),
|
|
277
|
+
force: true,
|
|
278
|
+
...(opts.codexBin ? { codexBin: opts.codexBin } : {}),
|
|
279
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
280
|
+
deps
|
|
281
|
+
}).catch(() => null);
|
|
282
|
+
const statusName = String(updateStatus?.status || 'missing');
|
|
283
|
+
const statusTrusted = updateStatus?.ok === true
|
|
284
|
+
&& updateStatus.installed === true
|
|
285
|
+
&& Boolean(updateStatus.cli_path)
|
|
286
|
+
&& Boolean(updateStatus.current_version)
|
|
287
|
+
&& statusName !== 'missing'
|
|
288
|
+
&& statusName !== 'failed';
|
|
289
|
+
if (!statusTrusted) {
|
|
290
|
+
return updateResult({
|
|
291
|
+
ok: false,
|
|
292
|
+
status: 'failed',
|
|
293
|
+
before,
|
|
294
|
+
after,
|
|
295
|
+
rawOutput,
|
|
296
|
+
updateStatus,
|
|
297
|
+
command: updatePlan.command,
|
|
298
|
+
updateMethod: updatePlan.method,
|
|
299
|
+
blockers: ['codex_cli_post_update_status_untrusted'],
|
|
300
|
+
guidance: ['The updater exited successfully, but the refreshed operator CLI status was missing or failed. Verify `codex --version` before retrying.']
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
if (updateStatus.cli_path !== after.path || updateStatus.current_version !== after.version) {
|
|
304
|
+
return updateResult({
|
|
305
|
+
ok: false,
|
|
306
|
+
status: 'failed',
|
|
307
|
+
before,
|
|
308
|
+
after,
|
|
309
|
+
rawOutput,
|
|
310
|
+
updateStatus,
|
|
311
|
+
command: updatePlan.command,
|
|
312
|
+
updateMethod: updatePlan.method,
|
|
313
|
+
blockers: ['codex_cli_post_update_status_target_mismatch'],
|
|
314
|
+
guidance: ['The refreshed status described a different Codex CLI path or version. Fix PATH/override precedence before retrying the update.']
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return updateResult({
|
|
318
|
+
ok: true,
|
|
319
|
+
status: versionComparison > 0 ? 'updated' : 'already_current',
|
|
320
|
+
before,
|
|
321
|
+
after,
|
|
322
|
+
rawOutput,
|
|
323
|
+
updateStatus,
|
|
324
|
+
command: updatePlan.command,
|
|
325
|
+
updateMethod: updatePlan.method,
|
|
326
|
+
blockers: [],
|
|
327
|
+
guidance: updateStatus.update_available === true
|
|
328
|
+
? [`The updater completed, but a newer registry version is still reported. Reopen the menu or rerun: ${updatePlan.command}`]
|
|
329
|
+
: []
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
export function codexCliVersionNumber(value) {
|
|
333
|
+
const match = String(value || '').match(/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/);
|
|
334
|
+
return match?.[1] || null;
|
|
335
|
+
}
|
|
336
|
+
export function officialCodexCliVersionNumber(value) {
|
|
337
|
+
const firstLine = String(value || '').trim().split(/\r?\n/).find((line) => line.trim())?.trim() || '';
|
|
338
|
+
if (!/^codex-cli\s+\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\s*$/i.test(firstLine))
|
|
339
|
+
return null;
|
|
340
|
+
return codexCliVersionNumber(firstLine);
|
|
341
|
+
}
|
|
342
|
+
export function compareCodexCliVersions(left, right) {
|
|
343
|
+
const a = codexCliVersionNumber(left);
|
|
344
|
+
const b = codexCliVersionNumber(right);
|
|
345
|
+
if (!a || !b)
|
|
346
|
+
return 0;
|
|
347
|
+
const [aCore = '', aPre = ''] = a.split('-', 2);
|
|
348
|
+
const [bCore = '', bPre = ''] = b.split('-', 2);
|
|
349
|
+
const aParts = aCore.split('.').map(Number);
|
|
350
|
+
const bParts = bCore.split('.').map(Number);
|
|
351
|
+
for (let index = 0; index < 3; index += 1) {
|
|
352
|
+
const delta = (aParts[index] || 0) - (bParts[index] || 0);
|
|
353
|
+
if (delta !== 0)
|
|
354
|
+
return delta > 0 ? 1 : -1;
|
|
355
|
+
}
|
|
356
|
+
if (!aPre && !bPre)
|
|
357
|
+
return 0;
|
|
358
|
+
if (!aPre)
|
|
359
|
+
return 1;
|
|
360
|
+
if (!bPre)
|
|
361
|
+
return -1;
|
|
362
|
+
return aPre.localeCompare(bPre, undefined, { numeric: true });
|
|
363
|
+
}
|
|
364
|
+
async function detectCodexCliUpdatePlan(input) {
|
|
365
|
+
const selectedPath = path.resolve(input.before.path);
|
|
366
|
+
const selectedRealPath = await realPathOrResolved(selectedPath);
|
|
367
|
+
const codexHome = await realPathOrResolved(input.env.CODEX_HOME || path.join(input.env.HOME || os.homedir(), '.codex'));
|
|
368
|
+
const standaloneRoot = await realPathOrResolved(path.join(codexHome, 'packages', 'standalone'));
|
|
369
|
+
if (isWithin(selectedRealPath, standaloneRoot)) {
|
|
370
|
+
return {
|
|
371
|
+
method: 'standalone-installer',
|
|
372
|
+
command: process.platform === 'win32'
|
|
373
|
+
? `powershell -NoProfile -Command "irm ${CODEX_STANDALONE_INSTALLER_PS1_URL} | iex"`
|
|
374
|
+
: `curl -fsSL ${CODEX_STANDALONE_INSTALLER_URL} | CODEX_NON_INTERACTIVE=1 sh`,
|
|
375
|
+
executable: '',
|
|
376
|
+
args: []
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const brew = await resolveUpdateExecutable('brew', input.env, input.deps);
|
|
380
|
+
if (brew) {
|
|
381
|
+
const caskPrefixResult = await input.run(brew, ['--prefix', '--cask', 'codex'], {
|
|
382
|
+
timeoutMs: 10_000,
|
|
383
|
+
maxOutputBytes: 16 * 1024,
|
|
384
|
+
env: input.env
|
|
385
|
+
}).catch((err) => failedProcessResult(err));
|
|
386
|
+
const caskPrefix = caskPrefixResult.code === 0
|
|
387
|
+
? String(caskPrefixResult.stdout || '').trim().split(/\r?\n/).find(Boolean) || ''
|
|
388
|
+
: '';
|
|
389
|
+
if (caskPrefix && isWithin(selectedRealPath, await realPathOrResolved(caskPrefix))) {
|
|
390
|
+
return {
|
|
391
|
+
method: 'homebrew-cask',
|
|
392
|
+
command: 'brew upgrade --cask codex',
|
|
393
|
+
executable: brew,
|
|
394
|
+
args: ['upgrade', '--cask', 'codex']
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const npm = await resolveUpdateExecutable('npm', input.env, input.deps);
|
|
399
|
+
if (npm) {
|
|
400
|
+
const [rootResult, prefixResult] = await Promise.all([
|
|
401
|
+
input.run(npm, ['root', '-g'], {
|
|
402
|
+
timeoutMs: 10_000,
|
|
403
|
+
maxOutputBytes: 16 * 1024,
|
|
404
|
+
env: input.env
|
|
405
|
+
}).catch((err) => failedProcessResult(err)),
|
|
406
|
+
input.run(npm, ['prefix', '-g'], {
|
|
407
|
+
timeoutMs: 10_000,
|
|
408
|
+
maxOutputBytes: 16 * 1024,
|
|
409
|
+
env: input.env
|
|
410
|
+
}).catch((err) => failedProcessResult(err))
|
|
411
|
+
]);
|
|
412
|
+
const npmRoot = rootResult.code === 0
|
|
413
|
+
? String(rootResult.stdout || '').trim().split(/\r?\n/).find(Boolean) || ''
|
|
414
|
+
: '';
|
|
415
|
+
const npmPrefix = prefixResult.code === 0
|
|
416
|
+
? String(prefixResult.stdout || '').trim().split(/\r?\n/).find(Boolean) || ''
|
|
417
|
+
: '';
|
|
418
|
+
const packageDir = npmRoot ? await realPathOrResolved(path.resolve(npmRoot, '@openai', 'codex')) : '';
|
|
419
|
+
const npmBinCandidates = npmPrefix
|
|
420
|
+
? process.platform === 'win32'
|
|
421
|
+
? ['codex.cmd', 'codex.exe', 'codex'].map((name) => path.resolve(npmPrefix, name))
|
|
422
|
+
: [path.resolve(npmPrefix, 'bin', 'codex')]
|
|
423
|
+
: [];
|
|
424
|
+
const packageInstalled = Boolean(packageDir) && await exists(packageDir);
|
|
425
|
+
if (packageInstalled && (isWithin(selectedRealPath, packageDir)
|
|
426
|
+
|| npmBinCandidates.includes(selectedPath))) {
|
|
427
|
+
return {
|
|
428
|
+
method: 'npm-global',
|
|
429
|
+
command: 'npm install -g @openai/codex@latest',
|
|
430
|
+
executable: npm,
|
|
431
|
+
args: ['install', '-g', '@openai/codex@latest']
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
async function runStandaloneInstallerUpdate(input) {
|
|
438
|
+
if (process.platform === 'win32') {
|
|
439
|
+
const powershell = await resolveUpdateExecutable('pwsh', input.env, input.deps)
|
|
440
|
+
|| await resolveUpdateExecutable('powershell', input.env, input.deps);
|
|
441
|
+
if (!powershell)
|
|
442
|
+
return failedProcessResult(new Error('codex_cli_standalone_update_prerequisite_missing'));
|
|
443
|
+
const codexHome = path.resolve(input.env.CODEX_HOME || path.join(input.env.HOME || os.homedir(), '.codex'));
|
|
444
|
+
return input.run(powershell, [
|
|
445
|
+
'-NoProfile',
|
|
446
|
+
'-NonInteractive',
|
|
447
|
+
'-ExecutionPolicy',
|
|
448
|
+
'Bypass',
|
|
449
|
+
'-Command',
|
|
450
|
+
`$ErrorActionPreference='Stop'; Invoke-RestMethod '${CODEX_STANDALONE_INSTALLER_PS1_URL}' | Invoke-Expression`
|
|
451
|
+
], {
|
|
452
|
+
timeoutMs: 180_000,
|
|
453
|
+
maxOutputBytes: 128 * 1024,
|
|
454
|
+
env: {
|
|
455
|
+
...input.env,
|
|
456
|
+
CODEX_HOME: codexHome,
|
|
457
|
+
CODEX_INSTALL_DIR: path.dirname(input.before.path),
|
|
458
|
+
CODEX_NON_INTERACTIVE: '1'
|
|
459
|
+
}
|
|
460
|
+
}).catch((err) => failedProcessResult(err));
|
|
461
|
+
}
|
|
462
|
+
const curl = await resolveUpdateExecutable('curl', input.env, input.deps);
|
|
463
|
+
const shell = await resolveUpdateExecutable('sh', input.env, input.deps);
|
|
464
|
+
if (!curl || !shell) {
|
|
465
|
+
return failedProcessResult(new Error('codex_cli_standalone_update_prerequisite_missing'));
|
|
466
|
+
}
|
|
467
|
+
const download = await input.run(curl, ['-fsSL', CODEX_STANDALONE_INSTALLER_URL], {
|
|
468
|
+
timeoutMs: 30_000,
|
|
469
|
+
maxOutputBytes: 512 * 1024,
|
|
470
|
+
env: input.env
|
|
471
|
+
}).catch((err) => failedProcessResult(err));
|
|
472
|
+
const installer = String(download.stdout || '');
|
|
473
|
+
if (download.code !== 0 || !/^#!\/bin\/sh\s*$/m.test(installer) || !installer.includes('CODEX_NON_INTERACTIVE')) {
|
|
474
|
+
return {
|
|
475
|
+
...download,
|
|
476
|
+
code: download.code === 0 ? 1 : download.code,
|
|
477
|
+
stderr: String(download.stderr || 'codex_cli_standalone_installer_download_untrusted')
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
const codexHome = path.resolve(input.env.CODEX_HOME || path.join(input.env.HOME || os.homedir(), '.codex'));
|
|
481
|
+
return input.run(shell, ['-s', '--'], {
|
|
482
|
+
timeoutMs: 180_000,
|
|
483
|
+
maxOutputBytes: 128 * 1024,
|
|
484
|
+
input: installer,
|
|
485
|
+
env: {
|
|
486
|
+
...input.env,
|
|
487
|
+
CODEX_HOME: codexHome,
|
|
488
|
+
CODEX_INSTALL_DIR: path.dirname(input.before.path),
|
|
489
|
+
CODEX_NON_INTERACTIVE: '1'
|
|
490
|
+
}
|
|
491
|
+
}).catch((err) => failedProcessResult(err));
|
|
492
|
+
}
|
|
493
|
+
async function resolveUpdateExecutable(command, env, deps) {
|
|
494
|
+
return deps.whichImpl
|
|
495
|
+
? deps.whichImpl(command).catch(() => null)
|
|
496
|
+
: resolveOperatorExecutable(command, env);
|
|
497
|
+
}
|
|
498
|
+
function updateFailureBlocker(method) {
|
|
499
|
+
if (method === 'native-self-update')
|
|
500
|
+
return 'codex_cli_self_update_failed';
|
|
501
|
+
if (method === 'standalone-installer')
|
|
502
|
+
return 'codex_cli_standalone_update_failed';
|
|
503
|
+
if (method === 'homebrew-cask')
|
|
504
|
+
return 'codex_cli_homebrew_cask_update_failed';
|
|
505
|
+
return 'codex_cli_npm_global_update_failed';
|
|
506
|
+
}
|
|
507
|
+
async function realPathOrResolved(value) {
|
|
508
|
+
const resolved = path.resolve(value);
|
|
509
|
+
return fsp.realpath(resolved).catch(() => resolved);
|
|
510
|
+
}
|
|
511
|
+
async function operatorCodexCandidates(explicitBin, env) {
|
|
512
|
+
const candidates = [];
|
|
513
|
+
const seen = new Set();
|
|
514
|
+
const add = (source, candidate) => {
|
|
515
|
+
const value = String(candidate || '').trim();
|
|
516
|
+
if (!value)
|
|
517
|
+
return;
|
|
518
|
+
const normalized = path.resolve(value);
|
|
519
|
+
if (seen.has(normalized))
|
|
520
|
+
return;
|
|
521
|
+
seen.add(normalized);
|
|
522
|
+
candidates.push({ source, path: normalized });
|
|
523
|
+
};
|
|
524
|
+
const explicitValues = [
|
|
525
|
+
explicitBin,
|
|
526
|
+
env.SKS_CODEX_UPDATE_BIN,
|
|
527
|
+
env.SKS_CODEX_BIN,
|
|
528
|
+
env.CODEX_BIN,
|
|
529
|
+
env.DCODEX_CODEX_BIN
|
|
530
|
+
];
|
|
531
|
+
for (const value of explicitValues) {
|
|
532
|
+
const raw = String(value || '').trim();
|
|
533
|
+
if (!raw)
|
|
534
|
+
continue;
|
|
535
|
+
if (path.isAbsolute(raw) || raw.includes('/') || raw.includes('\\')) {
|
|
536
|
+
add('explicit', raw);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const matches = executablePathsOnPath(raw, env);
|
|
540
|
+
if (matches.length)
|
|
541
|
+
for (const match of matches)
|
|
542
|
+
add('explicit', match);
|
|
543
|
+
else
|
|
544
|
+
add('explicit', raw);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
for (const match of executablePathsOnPath('codex', env))
|
|
548
|
+
add('path', match);
|
|
549
|
+
for (const match of await homeOperatorExecutablePaths('codex', env))
|
|
550
|
+
add('path', match);
|
|
551
|
+
return candidates;
|
|
552
|
+
}
|
|
553
|
+
async function resolveOperatorExecutable(command, env) {
|
|
554
|
+
const candidates = [
|
|
555
|
+
...executablePathsOnPath(command, env),
|
|
556
|
+
...await homeOperatorExecutablePaths(command, env)
|
|
557
|
+
];
|
|
558
|
+
for (const candidate of candidates) {
|
|
559
|
+
if (await exists(candidate))
|
|
560
|
+
return candidate;
|
|
561
|
+
}
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
async function homeOperatorExecutablePaths(command, env) {
|
|
565
|
+
const home = path.resolve(env.HOME || os.homedir());
|
|
566
|
+
const names = process.platform === 'win32'
|
|
567
|
+
? executableNamesForWindows(command, env.PATHEXT)
|
|
568
|
+
: [command];
|
|
569
|
+
const dirs = [
|
|
570
|
+
path.join(home, '.local', 'bin'),
|
|
571
|
+
path.join(home, '.npm-global', 'bin'),
|
|
572
|
+
path.join(home, '.volta', 'bin'),
|
|
573
|
+
path.join(home, 'Library', 'pnpm'),
|
|
574
|
+
path.join(home, '.local', 'share', 'pnpm')
|
|
575
|
+
];
|
|
576
|
+
const nvmVersionsDir = path.join(home, '.nvm', 'versions', 'node');
|
|
577
|
+
const nvmVersions = await fsp.readdir(nvmVersionsDir, { withFileTypes: true })
|
|
578
|
+
.then((entries) => entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(compareNodeInstallNames).reverse())
|
|
579
|
+
.catch(() => []);
|
|
580
|
+
dirs.unshift(...nvmVersions.map((version) => path.join(nvmVersionsDir, version, 'bin')));
|
|
581
|
+
return dirs.flatMap((dir) => names.map((name) => path.join(dir, name)));
|
|
582
|
+
}
|
|
583
|
+
function compareNodeInstallNames(left, right) {
|
|
584
|
+
const parse = (value) => value.replace(/^v/i, '').split('.').map((part) => Number(part) || 0);
|
|
585
|
+
const a = parse(left);
|
|
586
|
+
const b = parse(right);
|
|
587
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
588
|
+
const delta = (a[index] || 0) - (b[index] || 0);
|
|
589
|
+
if (delta !== 0)
|
|
590
|
+
return delta;
|
|
591
|
+
}
|
|
592
|
+
return left.localeCompare(right);
|
|
593
|
+
}
|
|
594
|
+
function executablePathsOnPath(command, env) {
|
|
595
|
+
const names = process.platform === 'win32'
|
|
596
|
+
? executableNamesForWindows(command, env.PATHEXT)
|
|
597
|
+
: [command];
|
|
598
|
+
const matches = [];
|
|
599
|
+
for (const dir of String(env.PATH || '').split(path.delimiter)) {
|
|
600
|
+
if (!dir)
|
|
601
|
+
continue;
|
|
602
|
+
for (const name of names)
|
|
603
|
+
matches.push(path.join(dir, name));
|
|
604
|
+
}
|
|
605
|
+
return matches;
|
|
606
|
+
}
|
|
607
|
+
function executableNamesForWindows(command, pathExt) {
|
|
608
|
+
if (path.extname(command))
|
|
609
|
+
return [command];
|
|
610
|
+
const extensions = String(pathExt || '.COM;.EXE;.BAT;.CMD')
|
|
611
|
+
.split(';')
|
|
612
|
+
.map((extension) => extension.trim().toLowerCase())
|
|
613
|
+
.filter(Boolean);
|
|
614
|
+
return [command, ...extensions.map((extension) => `${command}${extension}`)];
|
|
615
|
+
}
|
|
616
|
+
async function nonOperatorCodexReason(candidatePath, realPath) {
|
|
617
|
+
const root = packageRoot();
|
|
618
|
+
const ownedRoots = [
|
|
619
|
+
path.join(root, 'node_modules'),
|
|
620
|
+
path.join(root, 'dist', 'vendor', 'openai-codex'),
|
|
621
|
+
path.join(root, 'vendor', 'openai-codex')
|
|
622
|
+
];
|
|
623
|
+
if (ownedRoots.some((ownedRoot) => isWithin(candidatePath, ownedRoot) || isWithin(realPath, ownedRoot))) {
|
|
624
|
+
return 'sneakoscope_bundled';
|
|
625
|
+
}
|
|
626
|
+
const normalizedPaths = [candidatePath, realPath].map((value) => path.resolve(value).replaceAll('\\', '/'));
|
|
627
|
+
if (normalizedPaths.some((value) => /(?:^|\/)node_modules\/sneakoscope(?:\/|$)/.test(value))) {
|
|
628
|
+
return 'sneakoscope_bundled';
|
|
629
|
+
}
|
|
630
|
+
for (const value of normalizedPaths) {
|
|
631
|
+
if (/(?:^|\/)node_modules\/\.bin(?:\/|$)/.test(value))
|
|
632
|
+
return 'project_local';
|
|
633
|
+
const marker = '/node_modules/';
|
|
634
|
+
const index = value.indexOf(marker);
|
|
635
|
+
if (index <= 0)
|
|
636
|
+
continue;
|
|
637
|
+
const ownerRoot = value.slice(0, index);
|
|
638
|
+
if (await exists(path.join(ownerRoot, 'package.json')))
|
|
639
|
+
return 'project_local';
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
function isWithin(candidate, parent) {
|
|
644
|
+
const relative = path.relative(path.resolve(parent), path.resolve(candidate));
|
|
645
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
646
|
+
}
|
|
647
|
+
function buildStatus(input) {
|
|
648
|
+
const updateAvailable = compareCodexCliVersions(input.latestVersion, input.codex.version) > 0;
|
|
649
|
+
return {
|
|
650
|
+
schema: CODEX_CLI_UPDATE_STATUS_SCHEMA,
|
|
651
|
+
ok: true,
|
|
652
|
+
status: updateAvailable ? 'update_available' : 'current',
|
|
653
|
+
installed: true,
|
|
654
|
+
bin: input.codex.path,
|
|
655
|
+
cli_path: input.codex.path,
|
|
656
|
+
cli_source: input.codex.source,
|
|
657
|
+
current_version: input.codex.version,
|
|
658
|
+
raw_version: input.codex.raw_version,
|
|
659
|
+
latest_version: input.latestVersion,
|
|
660
|
+
update_available: updateAvailable,
|
|
661
|
+
update_command: 'sks codex update',
|
|
662
|
+
source: input.source,
|
|
663
|
+
checked_at: input.checkedAt,
|
|
664
|
+
cache_path: input.cachePath,
|
|
665
|
+
warnings: input.codex.warnings,
|
|
666
|
+
blockers: [],
|
|
667
|
+
guidance: updateAvailable ? ['Run `sks codex update` or choose Update Codex CLI Now in the SKS menu bar.'] : []
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function unavailableStatus(codex, now, cachePath, warning) {
|
|
671
|
+
return {
|
|
672
|
+
schema: CODEX_CLI_UPDATE_STATUS_SCHEMA,
|
|
673
|
+
ok: true,
|
|
674
|
+
status: 'update_check_unavailable',
|
|
675
|
+
installed: true,
|
|
676
|
+
bin: codex.path,
|
|
677
|
+
cli_path: codex.path,
|
|
678
|
+
cli_source: codex.source,
|
|
679
|
+
current_version: codex.version,
|
|
680
|
+
raw_version: codex.raw_version,
|
|
681
|
+
latest_version: null,
|
|
682
|
+
update_available: null,
|
|
683
|
+
update_command: 'sks codex update',
|
|
684
|
+
source: 'unavailable',
|
|
685
|
+
checked_at: now.toISOString(),
|
|
686
|
+
cache_path: cachePath,
|
|
687
|
+
warnings: [...codex.warnings, warning],
|
|
688
|
+
blockers: [],
|
|
689
|
+
guidance: ['Current Codex CLI version is shown, but the latest-version check could not be completed.']
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function updateResult(input) {
|
|
693
|
+
return {
|
|
694
|
+
schema: CODEX_CLI_UPDATE_RESULT_SCHEMA,
|
|
695
|
+
ok: input.ok,
|
|
696
|
+
status: input.status,
|
|
697
|
+
command: input.command,
|
|
698
|
+
update_method: input.updateMethod,
|
|
699
|
+
bin: input.before.path,
|
|
700
|
+
cli_path: input.before.path,
|
|
701
|
+
cli_source: input.before.source,
|
|
702
|
+
post_update_cli_path: input.after?.path || null,
|
|
703
|
+
post_update_cli_source: input.after?.source || 'unavailable',
|
|
704
|
+
before_version: input.before.version,
|
|
705
|
+
after_version: input.after?.version || null,
|
|
706
|
+
raw_output: input.rawOutput,
|
|
707
|
+
update_status: input.updateStatus,
|
|
708
|
+
blockers: input.blockers,
|
|
709
|
+
guidance: input.guidance
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function failedProcessResult(err) {
|
|
713
|
+
return {
|
|
714
|
+
code: 1,
|
|
715
|
+
stdout: '',
|
|
716
|
+
stderr: err instanceof Error ? err.message : String(err),
|
|
717
|
+
stdoutBytes: 0,
|
|
718
|
+
stderrBytes: 0,
|
|
719
|
+
truncated: false,
|
|
720
|
+
timedOut: false
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
//# sourceMappingURL=codex-cli-update.js.map
|