sneakoscope 8.0.2 → 8.0.3
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 +47 -23
- package/config/skills-hash-ledger.v1.json +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +55 -13
- package/dist/bin/sks-dispatch.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -0
- package/dist/cli/command-registry.js +4 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +187 -0
- package/dist/cli/install-helpers-codex-lb-config.js +74 -8
- package/dist/cli/install-helpers.js +89 -44
- package/dist/commands/codex-lb.js +117 -19
- package/dist/commands/doctor.js +697 -37
- package/dist/config/skills-manifest.json +3 -2
- package/dist/core/agents/agent-runner-codex-exec.js +5 -3
- package/dist/core/codex/codex-config-guard.js +238 -52
- package/dist/core/codex-adapter.js +5 -3
- package/dist/core/codex-app/menubar/constants.js +10 -1
- package/dist/core/codex-app/menubar/global-install.js +288 -0
- package/dist/core/codex-app/menubar/installer.js +79 -14
- package/dist/core/codex-app/menubar/launch-agent.js +267 -45
- package/dist/core/codex-app/menubar/paths.js +3 -0
- package/dist/core/codex-app/menubar/rollback.js +55 -7
- package/dist/core/codex-app/menubar/status.js +75 -4
- package/dist/core/codex-control/codex-0139-image-path-real-probe.js +3 -1
- package/dist/core/codex-control/codex-0139-web-search-probe.js +3 -1
- package/dist/core/codex-control/codex-app-server-runtime-env.js +13 -4
- package/dist/core/codex-control/codex-app-server-v2-client.js +3 -3
- package/dist/core/codex-exec-output-schema.js +4 -1
- package/dist/core/codex-lb/codex-lb-env.js +63 -72
- package/dist/core/codex-lb/codex-lb-setup.js +15 -2
- package/dist/core/codex-lb/desktop-center-credentials.js +517 -58
- package/dist/core/codex-lb/desktop-controller.js +153 -24
- package/dist/core/codex-lb/routing-truth.js +327 -0
- package/dist/core/codex-native/core-skill-manifest.js +1 -1
- package/dist/core/commands/basic-cli.js +7 -1
- package/dist/core/commands/qa-loop-command.js +3 -1
- package/dist/core/commands/telegram-command.js +241 -0
- package/dist/core/config-adopt/config-adopt-command.js +35 -0
- package/dist/core/config-adopt/config-adopt.js +346 -0
- package/dist/core/config-adopt/index.js +3 -0
- package/dist/core/doctor/command-alias-cleanup.js +4 -1
- package/dist/core/doctor/current-project-guidance-nested.js +37 -6
- package/dist/core/doctor/current-project-guidance.js +11 -6
- package/dist/core/doctor/doctor-readiness-matrix.js +14 -0
- package/dist/core/doctor/retired-managed-residue-private.js +1 -1
- package/dist/core/doctor/sks-menubar-doctor.js +4 -0
- package/dist/core/feature-fixtures.js +8 -0
- package/dist/core/hooks-runtime/hook-context.js +9 -0
- package/dist/core/hooks-runtime/managed-skill-admission.js +54 -4
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +69 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +2 -2
- package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +3 -3
- package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +43 -5
- package/dist/core/hooks-runtime/subagent-skill-availability.js +64 -30
- package/dist/core/hooks-runtime/subagent-stop-hook.js +15 -2
- package/dist/core/hooks-runtime.js +148 -4
- package/dist/core/imagegen/imagegen-capability.js +46 -0
- package/dist/core/imagegen/require-imagegen.js +2 -2
- package/dist/core/init/skills.js +17 -3
- package/dist/core/install/installed-package-smoke.js +8 -2
- package/dist/core/naruto/hardware-capacity-probe.js +9 -3
- package/dist/core/naruto/naruto-concurrency-governor.js +68 -24
- package/dist/core/perf/import-graph-budget.js +0 -1
- package/dist/core/product-design-app-server.js +13 -1
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-pack-receipt.js +1 -2
- package/dist/core/routes/dollar-manifest-lite.js +1 -1
- package/dist/core/routes.js +1 -1
- package/dist/core/security/private-credential-file.js +209 -0
- package/dist/core/subagents/agent-catalog.js +17 -32
- package/dist/core/subagents/naruto-help-contract.js +5 -5
- package/dist/core/subagents/official-subagent-config.js +16 -6
- package/dist/core/subagents/official-subagent-preparation.js +34 -15
- package/dist/core/subagents/official-subagent-prompt.js +4 -4
- package/dist/core/subagents/official-subagent-runner.js +27 -4
- package/dist/core/subagents/thread-budget.js +19 -12
- package/dist/core/subagents/wave-lifecycle.js +18 -6
- package/dist/core/telegram/access.js +54 -0
- package/dist/core/telegram/client.js +144 -0
- package/dist/core/telegram/confirmation.js +37 -0
- package/dist/core/telegram/doctor.js +151 -0
- package/dist/core/telegram/index.js +9 -0
- package/dist/core/telegram/keychain.js +265 -0
- package/dist/core/telegram/liveness.js +109 -0
- package/dist/core/telegram/poller.js +90 -0
- package/dist/core/telegram/redaction.js +15 -0
- package/dist/core/telegram/types.js +1 -0
- package/dist/core/update/update-diagnostics.js +52 -0
- package/dist/core/update/update-migration-state.js +5 -1
- package/dist/core/update/update-operation.js +2 -1
- package/dist/core/update/update-stage-diagnostics.js +38 -0
- package/dist/core/update/update-status.js +2 -9
- package/dist/core/update-check.js +147 -31
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-launcher.js +23 -4
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +13 -0
- package/dist/native/sks-menubar/Sources/AppIdentity.swift +7 -0
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +3 -2
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +115 -0
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +17 -0
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +190 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +100 -59
- package/dist/native/sks-menubar/Sources/SingletonInstanceGuard.swift +328 -0
- package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +320 -0
- package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +303 -0
- package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +238 -0
- package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +56 -0
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
- package/dist/native/sks-menubar/Sources/TelegramTransport.swift +474 -0
- package/dist/native/sks-menubar/Sources/main.swift +15 -1
- package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +4 -3
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +129 -0
- package/dist/native/sks-menubar/Tests/SingletonInstanceGuardTests.swift +183 -0
- package/dist/native/sks-menubar/Tests/TelegramRuntimeSmokeTests.swift +267 -0
- package/dist/native/sks-menubar/Tests/TelegramRuntimeTests.swift +222 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -1
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +1 -1
- package/dist/scripts/current-command-surface-check.js +2 -4
- package/dist/scripts/official-subagent-workflow-check.js +1 -1
- package/dist/scripts/packlist-performance-check.js +1 -2
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { projectRoot, exists, formatBytes, nowIso, readText, writeJsonAtomic } from '../core/fsx.js';
|
|
4
5
|
import { flag } from '../cli/args.js';
|
|
5
6
|
import { printJson } from '../cli/output.js';
|
|
6
7
|
import { ui as cliUi } from '../cli/cli-theme.js';
|
|
@@ -10,6 +11,15 @@ import { codexAppIntegrationStatus } from '../core/codex-app.js';
|
|
|
10
11
|
import { codexLbMetrics, readCodexLbCircuit } from '../core/codex-lb-circuit.js';
|
|
11
12
|
import { codexLbStatus } from '../cli/install-helpers.js';
|
|
12
13
|
import { codexLbToolOutputRecoveryOverrideAcknowledged } from '../core/codex-lb/codex-lb-tool-output-recovery.js';
|
|
14
|
+
import { codexLbEnvPath, loadCodexLbEnv } from '../core/codex-lb/codex-lb-env.js';
|
|
15
|
+
import { codexLbLegacyKeychainMigrationStampPath, inspectDesktopCenterLaunchCredentials, inspectCodexLbLegacyKeychainMigration, repairCodexLbLegacyKeychainMigration, syncDesktopCenterLaunchCredentials } from '../core/codex-lb/desktop-center-credentials.js';
|
|
16
|
+
import { codexLbRoutingTruthIsActive, measureAndWriteCodexLbRoutingTruth, readCodexLbRoutingTruthReceipt } from '../core/codex-lb/routing-truth.js';
|
|
17
|
+
import { removeCodexLbOrphanManagedMarkers } from '../cli/install-helpers-codex-lb-config.js';
|
|
18
|
+
import { safeWriteCodexConfigToml } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
19
|
+
import { probeTelegram, telegramSelfHealAction } from '../core/telegram/doctor.js';
|
|
20
|
+
import { TelegramClient } from '../core/telegram/client.js';
|
|
21
|
+
import { resolveTelegramBotToken } from '../core/telegram/keychain.js';
|
|
22
|
+
import { telegramLivenessPath } from '../core/telegram/liveness.js';
|
|
13
23
|
import { normalizeInstallScope } from '../core/init.js';
|
|
14
24
|
import { inspectCodexConfigReadability } from '../core/codex/codex-config-readability.js';
|
|
15
25
|
import { inspectOAuthCallbackPortConflict, oauthCallbackDoctorGuidance } from '../core/codex/oauth-callback-port-diagnostic.js';
|
|
@@ -27,13 +37,18 @@ import { withSecretPreservationGuard } from '../core/config/config-migration-jou
|
|
|
27
37
|
import { reconcileDoctorSkills } from '../core/doctor/doctor-skill-reconcile.js';
|
|
28
38
|
import { buildSksMenuBarDoctorPostcheck } from '../core/doctor/sks-menubar-doctor.js';
|
|
29
39
|
import { isUpdateMigrationReceiptCurrent, projectUpdateMigrationReceiptPath, writeProjectUpdateMigrationReceipt } from '../core/update/update-migration-state.js';
|
|
30
|
-
import { inspectSksMenuBarStatus, installSksMenuBar, sksMenuBarRestartDeferred } from '../core/codex-app/menubar/index.js';
|
|
40
|
+
import { inspectSksMenuBarStatus, installSksMenuBar, sksMenuBarPaths, sksMenuBarRestartDeferred } from '../core/codex-app/menubar/index.js';
|
|
41
|
+
import { restartLaunchAgent } from '../core/codex-app/menubar/launch-agent.js';
|
|
31
42
|
import { sweepSksTempDirs } from '../core/retention.js';
|
|
32
43
|
import { detectImagegenCapability } from '../core/imagegen/imagegen-capability.js';
|
|
33
44
|
import { CURRENT_CODEX_RELEASE_MANIFEST } from '../core/codex-compat/codex-release-manifest.js';
|
|
34
45
|
import { formatHarnessConflictReport, scanHarnessConflicts } from '../core/harness-conflicts.js';
|
|
35
|
-
import { doctorArgWarnings, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs } from './doctor-profile.js';
|
|
36
|
-
export {
|
|
46
|
+
import { doctorArgWarnings as baseDoctorArgWarnings, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs } from './doctor-profile.js';
|
|
47
|
+
export { doctorMenuBarInstallPolicy, doctorProfileFromArgs } from './doctor-profile.js';
|
|
48
|
+
const CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG = '--retry-codex-lb-keychain-migration';
|
|
49
|
+
export function doctorArgWarnings(args = []) {
|
|
50
|
+
return baseDoctorArgWarnings(args.filter((arg) => arg !== CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG));
|
|
51
|
+
}
|
|
37
52
|
export function deferCommandAliasCleanupToMigrationReceipt(result) {
|
|
38
53
|
const observedBlockers = Array.isArray(result?.blockers)
|
|
39
54
|
? result.blockers.map(String).filter(Boolean)
|
|
@@ -53,6 +68,196 @@ export function deferCommandAliasCleanupToMigrationReceipt(result) {
|
|
|
53
68
|
]
|
|
54
69
|
};
|
|
55
70
|
}
|
|
71
|
+
export async function inspectDoctorCodexLbSecretResolution(input = {}, deps = {}) {
|
|
72
|
+
const processEnv = input.processEnv || process.env;
|
|
73
|
+
const home = path.resolve(input.home || processEnv.HOME || os.homedir());
|
|
74
|
+
const suppliedMigrationOptions = input.migrationOptions || {};
|
|
75
|
+
const migrationOptions = {
|
|
76
|
+
...suppliedMigrationOptions,
|
|
77
|
+
home,
|
|
78
|
+
env: suppliedMigrationOptions.env || processEnv,
|
|
79
|
+
processEnv: suppliedMigrationOptions.processEnv || processEnv,
|
|
80
|
+
...(input.baseUrl ? { baseUrl: input.baseUrl } : {}),
|
|
81
|
+
...(input.forceRetry === true ? { forceRetry: true } : {})
|
|
82
|
+
};
|
|
83
|
+
if (processEnv.SKS_TEST_FORBID_REAL_HOME === '1' && suppliedMigrationOptions.platform === undefined) {
|
|
84
|
+
migrationOptions.platform = 'linux';
|
|
85
|
+
}
|
|
86
|
+
const mode = input.fix === true ? 'repair' : 'inspect';
|
|
87
|
+
const reconcile = mode === 'repair'
|
|
88
|
+
? deps.repairImpl || repairCodexLbLegacyKeychainMigration
|
|
89
|
+
: deps.inspectImpl || inspectCodexLbLegacyKeychainMigration;
|
|
90
|
+
let migration;
|
|
91
|
+
try {
|
|
92
|
+
migration = await reconcile(migrationOptions);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
migration = {
|
|
96
|
+
schema: 'sks.codex-lb-legacy-keychain-reconciliation.v1',
|
|
97
|
+
ok: false,
|
|
98
|
+
status: `${mode}_failed`,
|
|
99
|
+
mode,
|
|
100
|
+
env_key_valid: false,
|
|
101
|
+
keychain_item_present: null,
|
|
102
|
+
prompt_risk: 'none',
|
|
103
|
+
attempted: false,
|
|
104
|
+
stamp_path: codexLbLegacyKeychainMigrationStampPath(home),
|
|
105
|
+
stamp_outcome: null,
|
|
106
|
+
keychain_deleted: false,
|
|
107
|
+
keychain_cleared: [],
|
|
108
|
+
blockers: [`codex_lb_legacy_keychain_${mode}_failed:${error instanceof Error ? error.message : String(error)}`]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const envPath = String(suppliedMigrationOptions.envPath || codexLbEnvPath(home));
|
|
112
|
+
let loaded = null;
|
|
113
|
+
let resolutionError = null;
|
|
114
|
+
try {
|
|
115
|
+
loaded = await (deps.loadEnvImpl || loadCodexLbEnv)({
|
|
116
|
+
home,
|
|
117
|
+
processEnv,
|
|
118
|
+
envPath,
|
|
119
|
+
...(suppliedMigrationOptions.metadataPath ? { metadataPath: suppliedMigrationOptions.metadataPath } : {})
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
resolutionError = error instanceof Error ? error.message : String(error);
|
|
124
|
+
}
|
|
125
|
+
const source = String(loaded?.source || 'missing');
|
|
126
|
+
const unsafeEnvFile = Array.isArray(loaded?.blockers)
|
|
127
|
+
&& loaded.blockers.some((blocker) => String(blocker).startsWith('codex_lb_env_file_'));
|
|
128
|
+
const resolutionPath = source === 'env-file' || unsafeEnvFile
|
|
129
|
+
? String(loaded?.env_paths?.[0] || envPath)
|
|
130
|
+
: null;
|
|
131
|
+
const operatorActions = [];
|
|
132
|
+
if (mode === 'inspect' && migration.prompt_risk === 'one_time_on_repair') {
|
|
133
|
+
operatorActions.push('Run `sks doctor --fix` to perform the one-time legacy Keychain transfer.');
|
|
134
|
+
}
|
|
135
|
+
if (mode === 'repair'
|
|
136
|
+
&& migration.ok === false
|
|
137
|
+
&& (migration.attempted === true || migration.status === 'already_attempted')) {
|
|
138
|
+
operatorActions.push(`Run \`sks doctor --fix ${CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG}\` to explicitly retry the one-time Keychain transfer.`);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: migration.ok !== false && resolutionError === null,
|
|
142
|
+
secret_resolution: {
|
|
143
|
+
source,
|
|
144
|
+
path: resolutionPath,
|
|
145
|
+
prompt_risk: migration.prompt_risk || 'none'
|
|
146
|
+
},
|
|
147
|
+
legacy_keychain_migration: {
|
|
148
|
+
...migration,
|
|
149
|
+
operator_actions: operatorActions,
|
|
150
|
+
...(resolutionError ? { resolution_error: resolutionError } : {})
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export async function inspectDoctorCodexLbDivergence(input = {}, deps = {}) {
|
|
155
|
+
const processEnv = input.processEnv || process.env;
|
|
156
|
+
const home = path.resolve(input.home || processEnv.HOME || os.homedir());
|
|
157
|
+
const status = input.providerStatus || {};
|
|
158
|
+
const configPath = String(status.config_path || path.join(home, '.codex', 'config.toml'));
|
|
159
|
+
const configText = await (deps.readTextImpl || readText)(configPath, '');
|
|
160
|
+
const orphan = removeCodexLbOrphanManagedMarkers(configText);
|
|
161
|
+
let orphanRepair = {
|
|
162
|
+
schema: 'sks.codex-lb-orphan-managed-marker-repair.v1',
|
|
163
|
+
attempted: false,
|
|
164
|
+
changed: false,
|
|
165
|
+
markers: orphan.orphan_markers,
|
|
166
|
+
receipt: null
|
|
167
|
+
};
|
|
168
|
+
if (input.fix === true && orphan.changed) {
|
|
169
|
+
const write = deps.writeConfigImpl || safeWriteCodexConfigToml;
|
|
170
|
+
const receipt = await write(configPath, configText, orphan.text, 'doctor-codex-lb-orphan-marker');
|
|
171
|
+
orphanRepair = {
|
|
172
|
+
...orphanRepair,
|
|
173
|
+
attempted: true,
|
|
174
|
+
changed: receipt?.ok !== false,
|
|
175
|
+
receipt
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const load = deps.loadEnvImpl || loadCodexLbEnv;
|
|
179
|
+
const canonical = await load({
|
|
180
|
+
home,
|
|
181
|
+
processEnv: {},
|
|
182
|
+
envPath: String(status.env_path || codexLbEnvPath(home))
|
|
183
|
+
}).catch(() => null);
|
|
184
|
+
const ambientKey = String(processEnv.CODEX_LB_API_KEY || '').trim();
|
|
185
|
+
const canonicalKey = String(canonical?.secret_api_key || '').trim();
|
|
186
|
+
const sha256 = (value) => value
|
|
187
|
+
? createHash('sha256').update(value).digest('hex')
|
|
188
|
+
: null;
|
|
189
|
+
const ambientHash = sha256(ambientKey);
|
|
190
|
+
const canonicalHash = sha256(canonicalKey);
|
|
191
|
+
const staleAmbient = Boolean(ambientHash && canonicalHash && ambientHash !== canonicalHash);
|
|
192
|
+
const bridgeActive = status.desktop_mode === 'desktop-native-bridge';
|
|
193
|
+
const definedButNotSelected = status.provider_configured === true
|
|
194
|
+
&& status.selected !== true
|
|
195
|
+
&& !bridgeActive
|
|
196
|
+
&& status.desktop_mode !== 'desktop-dual-auth-compat';
|
|
197
|
+
const mode = status.selected === true
|
|
198
|
+
? 'cli-provider'
|
|
199
|
+
: bridgeActive
|
|
200
|
+
? 'desktop-native-bridge'
|
|
201
|
+
: 'disabled';
|
|
202
|
+
const inspectLaunch = deps.inspectLaunchImpl || inspectDesktopCenterLaunchCredentials;
|
|
203
|
+
const syncLaunch = deps.syncLaunchImpl || syncDesktopCenterLaunchCredentials;
|
|
204
|
+
const launchOptions = {
|
|
205
|
+
mode,
|
|
206
|
+
home,
|
|
207
|
+
...(input.launchctlBin ? { launchctlBin: input.launchctlBin } : {}),
|
|
208
|
+
...(input.platform ? { platform: input.platform } : {}),
|
|
209
|
+
...(input.forceLaunchctl ? { force: true } : {}),
|
|
210
|
+
...(input.runProcessImpl ? { runProcessImpl: input.runProcessImpl } : {})
|
|
211
|
+
};
|
|
212
|
+
const launchBefore = await inspectLaunch(launchOptions);
|
|
213
|
+
const launchRepair = input.fix === true && launchBefore.ok === false
|
|
214
|
+
? await syncLaunch({ ...launchOptions, skipPurge: true })
|
|
215
|
+
: null;
|
|
216
|
+
const launchAfter = launchRepair ? await inspectLaunch(launchOptions) : launchBefore;
|
|
217
|
+
const warnings = [
|
|
218
|
+
...(definedButNotSelected
|
|
219
|
+
? ['codex_lb_defined_but_not_selected']
|
|
220
|
+
: []),
|
|
221
|
+
...(staleAmbient ? ['codex_lb_stale_ambient_key'] : []),
|
|
222
|
+
...(orphan.orphan_markers.length > 0
|
|
223
|
+
? [`codex_lb_orphan_managed_markers:${orphan.orphan_markers.join(',')}`]
|
|
224
|
+
: [])
|
|
225
|
+
];
|
|
226
|
+
const blockers = [
|
|
227
|
+
...(launchAfter.ok === false ? launchAfter.blockers || ['codex_lb_launchd_selection_state_mismatch'] : []),
|
|
228
|
+
...(input.fix === true && orphan.changed && orphanRepair.changed !== true
|
|
229
|
+
? ['codex_lb_orphan_marker_repair_failed']
|
|
230
|
+
: [])
|
|
231
|
+
];
|
|
232
|
+
return {
|
|
233
|
+
schema: 'sks.doctor-codex-lb-divergence.v1',
|
|
234
|
+
ok: blockers.length === 0,
|
|
235
|
+
warnings,
|
|
236
|
+
blockers,
|
|
237
|
+
operator_actions: [
|
|
238
|
+
...(definedButNotSelected
|
|
239
|
+
? ['Run `sks codex-lb use-cli` to select the stored provider.']
|
|
240
|
+
: []),
|
|
241
|
+
...(staleAmbient
|
|
242
|
+
? ['Remove or update the stale shell export: `unset CODEX_LB_API_KEY`; the canonical key remains in ~/.codex/sks-codex-lb.env.']
|
|
243
|
+
: []),
|
|
244
|
+
...(launchAfter.operator_actions || [])
|
|
245
|
+
],
|
|
246
|
+
ambient_key: {
|
|
247
|
+
present: Boolean(ambientKey),
|
|
248
|
+
sha256: ambientHash,
|
|
249
|
+
matches_canonical: ambientHash && canonicalHash ? ambientHash === canonicalHash : null
|
|
250
|
+
},
|
|
251
|
+
canonical_key: {
|
|
252
|
+
present: Boolean(canonicalKey),
|
|
253
|
+
sha256: canonicalHash,
|
|
254
|
+
path: String(status.env_path || codexLbEnvPath(home))
|
|
255
|
+
},
|
|
256
|
+
orphan_markers: orphan.orphan_markers,
|
|
257
|
+
orphan_marker_repair: orphanRepair,
|
|
258
|
+
launchd: { before: launchBefore, repair: launchRepair, after: launchAfter }
|
|
259
|
+
};
|
|
260
|
+
}
|
|
56
261
|
export async function run(_command, args = [], deps = {}) {
|
|
57
262
|
const root = await projectRoot();
|
|
58
263
|
if (flag(args, '--search')) {
|
|
@@ -102,9 +307,9 @@ export async function run(_command, args = [], deps = {}) {
|
|
|
102
307
|
: root;
|
|
103
308
|
return withSecretPreservationGuard(guardRoot, 'doctor-fix', async () => (globalOnly
|
|
104
309
|
? runDoctorGlobalOnlyFix(args, root, deps)
|
|
105
|
-
: runDoctor(args, root, doctorFix)));
|
|
310
|
+
: runDoctor(args, root, doctorFix, deps)));
|
|
106
311
|
}
|
|
107
|
-
return runDoctor(args, root, doctorFix);
|
|
312
|
+
return runDoctor(args, root, doctorFix, deps);
|
|
108
313
|
}
|
|
109
314
|
export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
110
315
|
const startedAtMs = Date.now();
|
|
@@ -122,7 +327,29 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
122
327
|
const doctorEnv = deps.env || (deps.home
|
|
123
328
|
? { ...process.env, HOME: home }
|
|
124
329
|
: process.env);
|
|
330
|
+
const statusOptions = {
|
|
331
|
+
home,
|
|
332
|
+
env: doctorEnv,
|
|
333
|
+
processEnv: doctorEnv
|
|
334
|
+
};
|
|
335
|
+
const preRepairProviderStatus = await codexLbStatusImpl({
|
|
336
|
+
...statusOptions,
|
|
337
|
+
probeToolOutputRecovery: false
|
|
338
|
+
}).catch(() => null);
|
|
339
|
+
const codexLbSecretProbeImpl = deps.codexLbSecretProbeImpl || inspectDoctorCodexLbSecretResolution;
|
|
340
|
+
const codexLbSecretProbe = await codexLbSecretProbeImpl({
|
|
341
|
+
home,
|
|
342
|
+
processEnv: doctorEnv,
|
|
343
|
+
fix: true,
|
|
344
|
+
forceRetry: flag(args, CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG),
|
|
345
|
+
baseUrl: preRepairProviderStatus?.base_url || preRepairProviderStatus?.provider_base_url || null,
|
|
346
|
+
migrationOptions: {
|
|
347
|
+
...(deps.codexLbMigrationOptions || {}),
|
|
348
|
+
...(!deps.codexLbMigrationOptions && deps.codexLbStatusImpl ? { platform: 'linux' } : {})
|
|
349
|
+
}
|
|
350
|
+
});
|
|
125
351
|
const providerStatus = await codexLbStatusImpl({
|
|
352
|
+
...statusOptions,
|
|
126
353
|
probeToolOutputRecovery: true,
|
|
127
354
|
allowUnverifiedToolOutputRecovery: codexLbToolOutputRecoveryOverrideAcknowledged({ args })
|
|
128
355
|
}).catch((err) => ({
|
|
@@ -188,6 +415,25 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
188
415
|
blockers: [err?.message || String(err)],
|
|
189
416
|
warnings: []
|
|
190
417
|
}));
|
|
418
|
+
const codexLbRoutingTruth = await codexLbRoutingTruthForStatus(providerStatus, { home, remeasure: true });
|
|
419
|
+
const codexLbRoutingReady = !doctorCodexLbRouteExpected(providerStatus)
|
|
420
|
+
|| codexLbRoutingTruthIsActive(codexLbRoutingTruth);
|
|
421
|
+
const codexLbDivergence = await inspectDoctorCodexLbDivergence({
|
|
422
|
+
home,
|
|
423
|
+
processEnv: doctorEnv,
|
|
424
|
+
providerStatus,
|
|
425
|
+
fix: true,
|
|
426
|
+
...(deps.codexLbLaunchctlBin ? { launchctlBin: deps.codexLbLaunchctlBin } : {}),
|
|
427
|
+
...(deps.codexLbLaunchPlatform ? { platform: deps.codexLbLaunchPlatform } : {}),
|
|
428
|
+
...(deps.codexLbLaunchRunProcessImpl ? { runProcessImpl: deps.codexLbLaunchRunProcessImpl } : {})
|
|
429
|
+
}, deps.codexLbDivergenceDeps || {});
|
|
430
|
+
const telegramRemote = await inspectTelegramRemote({
|
|
431
|
+
live: true,
|
|
432
|
+
fix: true,
|
|
433
|
+
root,
|
|
434
|
+
home,
|
|
435
|
+
env: doctorEnv
|
|
436
|
+
}, deps);
|
|
191
437
|
const recoveryReady = codexLbRecoveryStatusReady(providerStatus, true);
|
|
192
438
|
const globalSkillsReady = !globalSkills?.error
|
|
193
439
|
&& globalSkills?.ok !== false
|
|
@@ -202,7 +448,12 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
202
448
|
...(!globalFastModeReady ? [`global_fast_mode_repair_failed:${globalFastMode?.error || globalFastMode?.status || 'unknown'}`] : []),
|
|
203
449
|
...(!openRouterProviderReady ? (openRouterProvider?.blockers || ['openrouter_provider_repair_failed']) : []),
|
|
204
450
|
...(!menuBarReady ? (menuBar?.blockers || ['sks_menubar_repair_failed']) : []),
|
|
205
|
-
...(
|
|
451
|
+
...(codexLbSecretProbe.ok === false
|
|
452
|
+
? (codexLbSecretProbe.legacy_keychain_migration?.blockers || ['codex_lb_secret_resolution_failed'])
|
|
453
|
+
: []),
|
|
454
|
+
...(!recoveryReady ? (providerStatus?.tool_output_recovery?.blockers || ['codex_lb_tool_output_recovery_unverified']) : []),
|
|
455
|
+
...(!codexLbRoutingReady ? (codexLbRoutingTruth?.blockers || ['codex_lb_routing_truth_unverified']) : []),
|
|
456
|
+
...(codexLbDivergence.blockers || [])
|
|
206
457
|
].map(String).filter(Boolean))];
|
|
207
458
|
const ok = blockers.length === 0;
|
|
208
459
|
return {
|
|
@@ -231,14 +482,23 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
231
482
|
codex_app_fast_mode: globalFastMode,
|
|
232
483
|
openrouter_provider: openRouterProvider,
|
|
233
484
|
sks_menubar: menuBar,
|
|
485
|
+
telegram_remote: telegramRemote,
|
|
234
486
|
codex_lb: {
|
|
235
487
|
provider_status: providerStatus,
|
|
236
488
|
tool_output_recovery: providerStatus?.tool_output_recovery || null,
|
|
237
|
-
recovery_ok: recoveryReady
|
|
489
|
+
recovery_ok: recoveryReady,
|
|
490
|
+
routing_truth: codexLbRoutingTruth,
|
|
491
|
+
routing_ok: codexLbRoutingReady,
|
|
492
|
+
secret_resolution: codexLbSecretProbe.secret_resolution,
|
|
493
|
+
legacy_keychain_migration: codexLbSecretProbe.legacy_keychain_migration,
|
|
494
|
+
divergence: codexLbDivergence
|
|
238
495
|
},
|
|
239
496
|
blockers,
|
|
240
497
|
next_actions: [
|
|
241
498
|
...(recoveryReady ? [] : (providerStatus?.tool_output_recovery?.operator_actions || [])),
|
|
499
|
+
...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
|
|
500
|
+
...(codexLbDivergence.operator_actions || []),
|
|
501
|
+
...telegramDoctorOperatorActions(telegramRemote),
|
|
242
502
|
...(!openRouterProviderReady ? ['Run `sks codex-app set-openrouter-key` to repair the stored-key provider configuration without switching models.'] : []),
|
|
243
503
|
'Run `sks doctor --fix --json` from a specific project directory when project-scoped repair is required.'
|
|
244
504
|
]
|
|
@@ -261,6 +521,15 @@ async function runDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
261
521
|
console.log(`SKS Doctor global repair: ${result.ok ? 'ok' : 'blocked'}`);
|
|
262
522
|
console.log(`Global skills: ${result.skills.global?.error ? 'blocked' : 'reconciled'}`);
|
|
263
523
|
console.log(`SKS menu bar: ${result.sks_menubar?.status || (result.sks_menubar?.ok ? 'ok' : 'blocked')}`);
|
|
524
|
+
const telegramOutcome = result.telegram_remote?.self_heal_outcome;
|
|
525
|
+
const telegramAction = telegramOutcome?.attempted
|
|
526
|
+
? telegramOutcome.action
|
|
527
|
+
: result.telegram_remote?.self_heal_action || 'none';
|
|
528
|
+
console.log(`Telegram Remote: ${result.telegram_remote?.status || 'unknown'} (self-heal ${telegramAction}${telegramOutcome?.attempted ? `, ${telegramOutcome.recovered ? 'recovered' : 'still degraded'}` : ''})`);
|
|
529
|
+
const telegramCheckedLine = telegramDoctorCheckedLine(result.telegram_remote);
|
|
530
|
+
if (telegramCheckedLine)
|
|
531
|
+
console.log(telegramCheckedLine);
|
|
532
|
+
console.log(`codex-lb key: ${result.codex_lb.secret_resolution.source} (prompt risk ${result.codex_lb.secret_resolution.prompt_risk})`);
|
|
264
533
|
for (const blocker of result.blockers)
|
|
265
534
|
console.log(`- blocker: ${blocker}`);
|
|
266
535
|
for (const action of result.next_actions)
|
|
@@ -277,8 +546,203 @@ function codexLbRecoveryStatusReady(status, probeRequired = false) {
|
|
|
277
546
|
return false;
|
|
278
547
|
if (status.selected === false)
|
|
279
548
|
return true;
|
|
549
|
+
if (!probeRequired)
|
|
550
|
+
return true;
|
|
280
551
|
return status.selected === true && status.tool_output_recovery?.ok === true;
|
|
281
552
|
}
|
|
553
|
+
export async function codexLbRoutingTruthForStatus(status, deps = {}) {
|
|
554
|
+
if (!status)
|
|
555
|
+
return null;
|
|
556
|
+
const statusEnvPath = typeof status.env_path === 'string' && status.env_path
|
|
557
|
+
? status.env_path
|
|
558
|
+
: null;
|
|
559
|
+
const resolvedHome = deps.home
|
|
560
|
+
? path.resolve(deps.home)
|
|
561
|
+
: statusEnvPath
|
|
562
|
+
? path.dirname(path.dirname(statusEnvPath))
|
|
563
|
+
: undefined;
|
|
564
|
+
const envOptions = {
|
|
565
|
+
...(resolvedHome ? { home: resolvedHome } : {}),
|
|
566
|
+
...(statusEnvPath ? { envPath: statusEnvPath } : {})
|
|
567
|
+
};
|
|
568
|
+
const routeSelected = doctorCodexLbRouteExpected(status);
|
|
569
|
+
const mode = status.desktop_mode === 'desktop-native-bridge' ? 'bridge' : 'cli-provider';
|
|
570
|
+
const receiptOptions = {
|
|
571
|
+
...(resolvedHome ? { home: resolvedHome } : {}),
|
|
572
|
+
...(deps.receiptPath ? { receiptPath: deps.receiptPath } : {}),
|
|
573
|
+
expectedMode: mode,
|
|
574
|
+
expectedSelected: routeSelected
|
|
575
|
+
};
|
|
576
|
+
const receipt = await readCodexLbRoutingTruthReceipt(receiptOptions).catch(() => null);
|
|
577
|
+
if (deps.remeasure !== true && receipt?.fresh === true)
|
|
578
|
+
return receipt;
|
|
579
|
+
try {
|
|
580
|
+
const loaded = await loadCodexLbEnv({ ...envOptions, processEnv: {} });
|
|
581
|
+
const baseUrl = loaded.base_url || status.base_url || null;
|
|
582
|
+
const configuredHost = publicUrlHost(baseUrl);
|
|
583
|
+
const authTransport = mode === 'cli-provider'
|
|
584
|
+
? 'authorization-bearer'
|
|
585
|
+
: doctorBridgeRoutingTransport(status, receipt);
|
|
586
|
+
const contextReceipt = await readCodexLbRoutingTruthReceipt({
|
|
587
|
+
...receiptOptions,
|
|
588
|
+
expectedConfiguredHost: configuredHost,
|
|
589
|
+
expectedAuthTransport: authTransport
|
|
590
|
+
}).catch(() => null);
|
|
591
|
+
if (deps.remeasure !== true && contextReceipt?.fresh === true)
|
|
592
|
+
return contextReceipt;
|
|
593
|
+
return measureAndWriteCodexLbRoutingTruth({
|
|
594
|
+
mode,
|
|
595
|
+
selected: routeSelected,
|
|
596
|
+
baseUrl,
|
|
597
|
+
apiKey: loaded.secret_api_key,
|
|
598
|
+
authTransport,
|
|
599
|
+
measure: routeSelected,
|
|
600
|
+
...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {})
|
|
601
|
+
}, {
|
|
602
|
+
...envOptions,
|
|
603
|
+
...(deps.receiptPath ? { receiptPath: deps.receiptPath } : {})
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
return measureAndWriteCodexLbRoutingTruth({
|
|
608
|
+
mode,
|
|
609
|
+
selected: routeSelected,
|
|
610
|
+
baseUrl: status.base_url || null,
|
|
611
|
+
apiKey: null,
|
|
612
|
+
authTransport: mode === 'cli-provider'
|
|
613
|
+
? 'authorization-bearer'
|
|
614
|
+
: doctorBridgeRoutingTransport(status, receipt),
|
|
615
|
+
measure: false
|
|
616
|
+
}, {
|
|
617
|
+
...envOptions,
|
|
618
|
+
...(deps.receiptPath ? { receiptPath: deps.receiptPath } : {})
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function doctorCodexLbRouteExpected(status) {
|
|
623
|
+
return status?.selected === true || status?.desktop_mode === 'desktop-native-bridge';
|
|
624
|
+
}
|
|
625
|
+
function doctorBridgeRoutingTransport(status, receipt) {
|
|
626
|
+
if (receipt?.mode === 'bridge'
|
|
627
|
+
&& (receipt.auth_transport === 'authorization-bearer'
|
|
628
|
+
|| receipt.auth_transport === 'x-codex-lb-api-key')) {
|
|
629
|
+
return receipt.auth_transport;
|
|
630
|
+
}
|
|
631
|
+
return status?.gateway_auth_transport === 'authorization-bearer-compat'
|
|
632
|
+
? 'authorization-bearer'
|
|
633
|
+
: 'x-codex-lb-api-key';
|
|
634
|
+
}
|
|
635
|
+
function publicUrlHost(value) {
|
|
636
|
+
try {
|
|
637
|
+
return value ? new URL(String(value)).host : null;
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
export async function inspectTelegramRemote(input = {}, deps = {}) {
|
|
644
|
+
const env = input.env || process.env;
|
|
645
|
+
const home = path.resolve(input.home || env.HOME || os.homedir());
|
|
646
|
+
const receiptPath = input.receiptPath || telegramLivenessPath(home);
|
|
647
|
+
const probeImpl = deps.probeTelegramImpl || probeTelegram;
|
|
648
|
+
let tokenProvider = deps.telegramTokenProvider;
|
|
649
|
+
let client = deps.telegramClient;
|
|
650
|
+
if (input.live && (!tokenProvider || !client)) {
|
|
651
|
+
const resolveImpl = deps.resolveTelegramBotTokenImpl || resolveTelegramBotToken;
|
|
652
|
+
let resolvedToken = null;
|
|
653
|
+
tokenProvider ||= {
|
|
654
|
+
loadToken: () => {
|
|
655
|
+
resolvedToken ||= Promise.resolve(resolveImpl({ env })).then((result) => result.token);
|
|
656
|
+
return resolvedToken;
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
client ||= new TelegramClient({ tokenProvider });
|
|
660
|
+
}
|
|
661
|
+
const runProbe = () => {
|
|
662
|
+
if (!input.live)
|
|
663
|
+
return probeImpl({ receiptPath });
|
|
664
|
+
if (!client || !tokenProvider)
|
|
665
|
+
throw new Error('telegram_live_probe_dependencies_missing');
|
|
666
|
+
return probeImpl({ client, tokenProvider, receiptPath });
|
|
667
|
+
};
|
|
668
|
+
const before = await runProbe();
|
|
669
|
+
const action = telegramSelfHealAction(before);
|
|
670
|
+
if (!input.fix || action !== 'restart_poll' || !before.token_configured || before.paired_chat_count < 1) {
|
|
671
|
+
return {
|
|
672
|
+
...before,
|
|
673
|
+
self_heal_action: action,
|
|
674
|
+
self_heal_outcome: {
|
|
675
|
+
requested: input.fix === true,
|
|
676
|
+
attempted: false,
|
|
677
|
+
action,
|
|
678
|
+
reason: input.fix !== true
|
|
679
|
+
? 'report_only'
|
|
680
|
+
: action !== 'restart_poll'
|
|
681
|
+
? 'action_not_restart_poll'
|
|
682
|
+
: 'telegram_not_configured_or_paired'
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
const restartImpl = deps.restartLaunchAgentImpl || restartLaunchAgent;
|
|
687
|
+
const restart = await restartImpl(sksMenuBarPaths(home, input.root), env).catch((error) => ({
|
|
688
|
+
ok: false,
|
|
689
|
+
error: error instanceof Error ? error.message : String(error)
|
|
690
|
+
}));
|
|
691
|
+
const maximumReprobes = Math.max(1, Math.min(deps.telegramReprobeAttempts ?? 3, 5));
|
|
692
|
+
const reprobeDelayMs = Math.max(0, Math.min(deps.telegramReprobeDelayMs ?? 500, 5_000));
|
|
693
|
+
const sleepImpl = deps.telegramSleepImpl || ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
694
|
+
let reprobeAttempts = 1;
|
|
695
|
+
let after = await runProbe();
|
|
696
|
+
let afterAction = telegramSelfHealAction(after);
|
|
697
|
+
while (restart?.ok === true && afterAction === 'restart_poll' && reprobeAttempts < maximumReprobes) {
|
|
698
|
+
await sleepImpl(reprobeDelayMs);
|
|
699
|
+
after = await runProbe();
|
|
700
|
+
afterAction = telegramSelfHealAction(after);
|
|
701
|
+
reprobeAttempts += 1;
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
...after,
|
|
705
|
+
self_heal_action: afterAction,
|
|
706
|
+
self_heal_attempted_action: action,
|
|
707
|
+
self_heal_before: before,
|
|
708
|
+
self_heal_outcome: {
|
|
709
|
+
requested: true,
|
|
710
|
+
attempted: true,
|
|
711
|
+
action,
|
|
712
|
+
after_action: afterAction,
|
|
713
|
+
restart_ok: restart?.ok === true,
|
|
714
|
+
reprobe_ok: after.ok,
|
|
715
|
+
reprobe_attempts: reprobeAttempts,
|
|
716
|
+
recovered: restart?.ok === true && after.ok && afterAction === 'none',
|
|
717
|
+
error: restart?.error || null,
|
|
718
|
+
restart
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
export function telegramDoctorCheckedLine(probe, indent = '') {
|
|
723
|
+
if (!probe?.getme_checked_at)
|
|
724
|
+
return null;
|
|
725
|
+
return `${indent}checked: ${probe.getme_checked_at} (${probe.getme_latency_ms ?? 'unknown'} ms, ${probe.getme_check_kind || 'receipt'})`;
|
|
726
|
+
}
|
|
727
|
+
function telegramDoctorOperatorActions(probe) {
|
|
728
|
+
if (!probe || probe.status === 'not_configured')
|
|
729
|
+
return [];
|
|
730
|
+
if (probe.self_heal_action === 'restart_poll') {
|
|
731
|
+
if (probe.self_heal_outcome?.recovered === true)
|
|
732
|
+
return [];
|
|
733
|
+
return ['Run `sks menubar restart` to restart the resident Telegram long poller.'];
|
|
734
|
+
}
|
|
735
|
+
if (probe.self_heal_action === 'revalidate_token') {
|
|
736
|
+
return ['Run `sks telegram setup --token-stdin`, then `sks menubar restart`, to revalidate the bot identity.'];
|
|
737
|
+
}
|
|
738
|
+
if (probe.self_heal_action === 'operator_remove_webhook') {
|
|
739
|
+
return ['Remove the Telegram bot webhook through the Bot API, then run `sks menubar restart`; getUpdates and webhooks cannot be active together.'];
|
|
740
|
+
}
|
|
741
|
+
if (probe.self_heal_action === 'operator_repair_audit') {
|
|
742
|
+
return ['Restore owner-only write access to `~/.codex/sks-menubar/logs/telegram-audit.jsonl`, then run `sks menubar restart`.'];
|
|
743
|
+
}
|
|
744
|
+
return [];
|
|
745
|
+
}
|
|
282
746
|
async function runDoctorJsonFastPath(args = [], root) {
|
|
283
747
|
const startedAtMs = Date.now();
|
|
284
748
|
const reportFile = readOption(args, '--report-file', null);
|
|
@@ -289,7 +753,7 @@ async function runDoctorJsonFastPath(args = [], root) {
|
|
|
289
753
|
requireActualCodex: false,
|
|
290
754
|
codexBin: codexBin || undefined
|
|
291
755
|
};
|
|
292
|
-
const [codex, rust, codexConfig, sneakoscopeExists, oauthCallbackPortDiagnostic] = await Promise.all([
|
|
756
|
+
const [codex, rust, codexConfig, sneakoscopeExists, oauthCallbackPortDiagnostic, codexLbSecretProbe] = await Promise.all([
|
|
293
757
|
codexBin
|
|
294
758
|
? Promise.resolve({ bin: codexBin, version: 'fixture-or-explicit', available: true })
|
|
295
759
|
: getCodexInfo().catch(() => ({ bin: null, version: null, available: false })),
|
|
@@ -301,9 +765,11 @@ async function runDoctorJsonFastPath(args = [], root) {
|
|
|
301
765
|
blockers: [err?.message || String(err)]
|
|
302
766
|
})),
|
|
303
767
|
exists(`${root}/.sneakoscope`),
|
|
304
|
-
inspectOAuthCallbackPortConflict()
|
|
768
|
+
inspectOAuthCallbackPortConflict(),
|
|
769
|
+
inspectDoctorCodexLbSecretResolution({ processEnv: process.env, fix: false })
|
|
305
770
|
]);
|
|
306
771
|
const oauthCallbackOperatorActions = oauthCallbackDoctorGuidance(oauthCallbackPortDiagnostic);
|
|
772
|
+
const telegramRemote = await inspectTelegramRemote();
|
|
307
773
|
const ready = {
|
|
308
774
|
schema: 'sks.doctor-readiness-matrix.v2',
|
|
309
775
|
generated_at: nowIso(),
|
|
@@ -340,13 +806,20 @@ async function runDoctorJsonFastPath(args = [], root) {
|
|
|
340
806
|
deep_diagnostics_skipped: true,
|
|
341
807
|
deep_ok: null,
|
|
342
808
|
not_counted_as_full_doctor: true,
|
|
343
|
-
next_actions: [
|
|
809
|
+
next_actions: [
|
|
810
|
+
'Run sks doctor --full --json for deep diagnostics.',
|
|
811
|
+
...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
|
|
812
|
+
...telegramDoctorOperatorActions(telegramRemote)
|
|
813
|
+
],
|
|
344
814
|
root,
|
|
345
815
|
fast_path: true,
|
|
346
816
|
no_fix_write_policy: reportFile ? 'report_file_only' : 'no_writes_performed',
|
|
347
817
|
arg_warnings: doctorArgWarnings(args),
|
|
348
818
|
warnings: [...oauthCallbackPortDiagnostic.warnings],
|
|
349
|
-
operator_actions:
|
|
819
|
+
operator_actions: [
|
|
820
|
+
...oauthCallbackOperatorActions,
|
|
821
|
+
...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || [])
|
|
822
|
+
],
|
|
350
823
|
node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
|
|
351
824
|
codex,
|
|
352
825
|
oauth_callback_port_diagnostic: oauthCallbackPortDiagnostic,
|
|
@@ -371,6 +844,7 @@ async function runDoctorJsonFastPath(args = [], root) {
|
|
|
371
844
|
blockers: [],
|
|
372
845
|
warnings: ['menubar_install_deferred_to_fix_or_full_doctor']
|
|
373
846
|
},
|
|
847
|
+
telegram_remote: telegramRemote,
|
|
374
848
|
provider_context: {
|
|
375
849
|
schema: 'sks.provider-context.v1',
|
|
376
850
|
generated_at: nowIso(),
|
|
@@ -384,7 +858,13 @@ async function runDoctorJsonFastPath(args = [], root) {
|
|
|
384
858
|
warnings: ['provider_context_optional_diagnostic_skipped'],
|
|
385
859
|
signals: {}
|
|
386
860
|
},
|
|
387
|
-
codex_lb:
|
|
861
|
+
codex_lb: {
|
|
862
|
+
...codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({}))),
|
|
863
|
+
routing_truth: null,
|
|
864
|
+
routing_measurement_deferred: true,
|
|
865
|
+
secret_resolution: codexLbSecretProbe.secret_resolution,
|
|
866
|
+
legacy_keychain_migration: codexLbSecretProbe.legacy_keychain_migration
|
|
867
|
+
},
|
|
388
868
|
codex_doctor: null,
|
|
389
869
|
pre_repair_codex_doctor: null,
|
|
390
870
|
post_repair_codex_doctor: null,
|
|
@@ -452,7 +932,7 @@ async function runDoctorJsonFastPath(args = [], root) {
|
|
|
452
932
|
process.exitCode = 1;
|
|
453
933
|
return result;
|
|
454
934
|
}
|
|
455
|
-
async function runDoctor(args = [], root, doctorFix) {
|
|
935
|
+
async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
456
936
|
const startedAtMs = Date.now();
|
|
457
937
|
const oauthCallbackPortDiagnostic = await inspectOAuthCallbackPortConflict();
|
|
458
938
|
const oauthCallbackOperatorActions = oauthCallbackDoctorGuidance(oauthCallbackPortDiagnostic);
|
|
@@ -630,29 +1110,54 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
630
1110
|
? await codexAppIntegrationStatus({ codex }).catch((err) => ({ ok: false, error: err.message }))
|
|
631
1111
|
: { ok: false, skipped: true, warnings: ['codex_app_optional_diagnostic_skipped'] };
|
|
632
1112
|
const codexLbCircuit = codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({})));
|
|
633
|
-
const
|
|
634
|
-
? await codexLbStatus({
|
|
635
|
-
probeToolOutputRecovery: true,
|
|
636
|
-
allowUnverifiedToolOutputRecovery: codexLbToolOutputRecoveryOverrideAcknowledged({ args })
|
|
637
|
-
}).catch((err) => ({
|
|
638
|
-
selected: null,
|
|
639
|
-
provider_ready: false,
|
|
640
|
-
recovery_probe_failed: true,
|
|
641
|
-
tool_output_recovery: {
|
|
642
|
-
ok: false,
|
|
643
|
-
status: 'probe_failed',
|
|
644
|
-
blockers: ['codex_lb_tool_output_recovery_status_probe_failed'],
|
|
645
|
-
operator_actions: []
|
|
646
|
-
},
|
|
647
|
-
error: err?.message || String(err)
|
|
648
|
-
}))
|
|
1113
|
+
const codexLbPreRepairStatus = doctorFix
|
|
1114
|
+
? await codexLbStatus({ probeToolOutputRecovery: false }).catch(() => null)
|
|
649
1115
|
: null;
|
|
1116
|
+
const codexLbSecretProbe = await inspectDoctorCodexLbSecretResolution({
|
|
1117
|
+
processEnv: process.env,
|
|
1118
|
+
fix: doctorFix,
|
|
1119
|
+
forceRetry: flag(args, CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG),
|
|
1120
|
+
baseUrl: codexLbPreRepairStatus?.base_url || codexLbPreRepairStatus?.provider_base_url || null
|
|
1121
|
+
});
|
|
1122
|
+
const codexLbProviderStatus = await codexLbStatus({
|
|
1123
|
+
probeToolOutputRecovery: deepDiagnostics || doctorFix,
|
|
1124
|
+
allowUnverifiedToolOutputRecovery: codexLbToolOutputRecoveryOverrideAcknowledged({ args })
|
|
1125
|
+
}).catch((err) => ({
|
|
1126
|
+
selected: null,
|
|
1127
|
+
provider_ready: false,
|
|
1128
|
+
recovery_probe_failed: deepDiagnostics || doctorFix,
|
|
1129
|
+
tool_output_recovery: {
|
|
1130
|
+
ok: false,
|
|
1131
|
+
status: deepDiagnostics || doctorFix ? 'probe_failed' : 'not_checked',
|
|
1132
|
+
blockers: deepDiagnostics || doctorFix ? ['codex_lb_tool_output_recovery_status_probe_failed'] : [],
|
|
1133
|
+
operator_actions: []
|
|
1134
|
+
},
|
|
1135
|
+
error: err?.message || String(err)
|
|
1136
|
+
}));
|
|
650
1137
|
const codexLbRecoveryReady = codexLbRecoveryStatusReady(codexLbProviderStatus, deepDiagnostics || doctorFix);
|
|
1138
|
+
const codexLbRoutingTruth = await codexLbRoutingTruthForStatus(codexLbProviderStatus, {
|
|
1139
|
+
remeasure: deepDiagnostics || doctorFix
|
|
1140
|
+
});
|
|
1141
|
+
const codexLbRoutingReady = !doctorCodexLbRouteExpected(codexLbProviderStatus)
|
|
1142
|
+
|| codexLbRoutingTruthIsActive(codexLbRoutingTruth);
|
|
1143
|
+
const codexLbDivergence = await inspectDoctorCodexLbDivergence({
|
|
1144
|
+
processEnv: process.env,
|
|
1145
|
+
providerStatus: codexLbProviderStatus,
|
|
1146
|
+
fix: doctorFix,
|
|
1147
|
+
...(deps.codexLbLaunchctlBin ? { launchctlBin: deps.codexLbLaunchctlBin } : {}),
|
|
1148
|
+
...(deps.codexLbLaunchPlatform ? { platform: deps.codexLbLaunchPlatform } : {}),
|
|
1149
|
+
...(deps.codexLbLaunchRunProcessImpl ? { runProcessImpl: deps.codexLbLaunchRunProcessImpl } : {})
|
|
1150
|
+
}, deps.codexLbDivergenceDeps || {});
|
|
651
1151
|
const codexLb = {
|
|
652
1152
|
...codexLbCircuit,
|
|
653
1153
|
provider_status: codexLbProviderStatus,
|
|
654
1154
|
tool_output_recovery: codexLbProviderStatus?.tool_output_recovery || null,
|
|
655
|
-
recovery_ok: codexLbRecoveryReady
|
|
1155
|
+
recovery_ok: codexLbRecoveryReady,
|
|
1156
|
+
routing_truth: codexLbRoutingTruth,
|
|
1157
|
+
routing_ok: codexLbRoutingReady,
|
|
1158
|
+
secret_resolution: codexLbSecretProbe.secret_resolution,
|
|
1159
|
+
legacy_keychain_migration: codexLbSecretProbe.legacy_keychain_migration,
|
|
1160
|
+
divergence: codexLbDivergence
|
|
656
1161
|
};
|
|
657
1162
|
const providerContext = deepDiagnostics
|
|
658
1163
|
? await resolveProviderContext({ root, route: '$Doctor', serviceTier: process.env.SKS_SERVICE_TIER || 'fast' }).catch((err) => ({
|
|
@@ -756,6 +1261,12 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
756
1261
|
blockers: [err?.message || String(err)],
|
|
757
1262
|
warnings: []
|
|
758
1263
|
}));
|
|
1264
|
+
const telegramRemote = await inspectTelegramRemote({
|
|
1265
|
+
live: deepDiagnostics || doctorFix,
|
|
1266
|
+
fix: doctorFix,
|
|
1267
|
+
root,
|
|
1268
|
+
env: process.env
|
|
1269
|
+
}, deps);
|
|
759
1270
|
const zellijRepair = shouldRunZellijRepair
|
|
760
1271
|
? await runDoctorZellijRepair({ root, args, doctorFix }).catch((err) => ({
|
|
761
1272
|
schema: 'sks.zellij-self-heal.v1',
|
|
@@ -968,14 +1479,34 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
968
1479
|
rollback_evidence: sksMenuBar?.launch_agent_path || sksMenuBar?.report_path || 'sks_menubar_optional_no_core_mutation'
|
|
969
1480
|
}),
|
|
970
1481
|
postcheck: async () => {
|
|
971
|
-
const
|
|
1482
|
+
const inspectMenuBarStatusImpl = deps.inspectSksMenuBarStatusImpl || inspectSksMenuBarStatus;
|
|
1483
|
+
const status = await inspectMenuBarStatusImpl({ root }).catch((err) => ({
|
|
972
1484
|
ok: false,
|
|
973
1485
|
launchd: { ok: false, state: null, pid: null, error: err?.message || String(err) },
|
|
974
1486
|
action_target: { ok: false, smoke_code: null, smoke_output: null },
|
|
975
1487
|
blockers: [err?.message || String(err)],
|
|
976
1488
|
warnings: []
|
|
977
1489
|
}));
|
|
978
|
-
|
|
1490
|
+
const launchdRepair = await rebootstrapSksMenuBarLaunchdForDoctorFix({
|
|
1491
|
+
fix: doctorFix,
|
|
1492
|
+
root,
|
|
1493
|
+
env: process.env,
|
|
1494
|
+
status
|
|
1495
|
+
}, {
|
|
1496
|
+
inspectSksMenuBarStatusImpl: inspectMenuBarStatusImpl,
|
|
1497
|
+
restartLaunchAgentImpl: deps.restartLaunchAgentImpl
|
|
1498
|
+
});
|
|
1499
|
+
const postcheck = buildSksMenuBarDoctorPostcheck(launchdRepair.status);
|
|
1500
|
+
return launchdRepair.attempted
|
|
1501
|
+
? {
|
|
1502
|
+
...postcheck,
|
|
1503
|
+
repaired: launchdRepair.ok,
|
|
1504
|
+
warnings: [
|
|
1505
|
+
...(postcheck.warnings || []),
|
|
1506
|
+
launchdRepair.ok ? 'launchd_rebootstrap_recovered' : 'launchd_rebootstrap_failed'
|
|
1507
|
+
]
|
|
1508
|
+
}
|
|
1509
|
+
: postcheck;
|
|
979
1510
|
}
|
|
980
1511
|
},
|
|
981
1512
|
{
|
|
@@ -1284,6 +1815,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1284
1815
|
repair: configRepair,
|
|
1285
1816
|
codex_app_ui: codexAppUi,
|
|
1286
1817
|
sks_menubar: sksMenuBar,
|
|
1818
|
+
telegram_remote: telegramRemote,
|
|
1287
1819
|
codex_0138_doctor: codex0138Doctor,
|
|
1288
1820
|
codex_plugin_inventory: pluginInventory?.report || null,
|
|
1289
1821
|
codex_plugin_app_template_policy: pluginPolicy,
|
|
@@ -1295,7 +1827,9 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1295
1827
|
...(zellijRepair && !zellijRepair.ok && zellijRepair.command ? [`Run: ${zellijRepair.command}`] : []),
|
|
1296
1828
|
...(codexStartupRepair.manual_actions || []),
|
|
1297
1829
|
...(codexConfigSyntaxRepair?.manual_actions || []),
|
|
1298
|
-
...(pluginPolicy?.doctor_warnings || [])
|
|
1830
|
+
...(pluginPolicy?.doctor_warnings || []),
|
|
1831
|
+
...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
|
|
1832
|
+
...telegramDoctorOperatorActions(telegramRemote)
|
|
1299
1833
|
]
|
|
1300
1834
|
};
|
|
1301
1835
|
let ready = await writeDoctorReadinessMatrix(root, doctorReadinessInput);
|
|
@@ -1316,6 +1850,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1316
1850
|
&& readinessBlockers.every(isMigrationUserOwnedProjectConfigBlocker);
|
|
1317
1851
|
const receiptBlockers = preservedUserOwnedConfig ? [] : readinessBlockers;
|
|
1318
1852
|
const migrationWarnings = [
|
|
1853
|
+
...(commandAliasCleanup?.warnings || []),
|
|
1319
1854
|
...(doctorNativeCapabilityRepair?.optional_warnings || []),
|
|
1320
1855
|
...(doctorFixPostcheck?.optional_warnings || []),
|
|
1321
1856
|
...(preservedUserOwnedConfig
|
|
@@ -1371,7 +1906,10 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1371
1906
|
&& agentRoleConfigRepair.ok !== false
|
|
1372
1907
|
&& openRouterProviderRepair.ok !== false
|
|
1373
1908
|
&& (officialSubagentConfig.blockers || []).length === 0
|
|
1374
|
-
&&
|
|
1909
|
+
&& codexLbSecretProbe.ok !== false
|
|
1910
|
+
&& codexLbDivergence.ok !== false
|
|
1911
|
+
&& codexLbRecoveryReady
|
|
1912
|
+
&& codexLbRoutingReady;
|
|
1375
1913
|
const result = {
|
|
1376
1914
|
schema: 'sks.doctor-status.v3',
|
|
1377
1915
|
elapsed_ms: Date.now() - startedAtMs,
|
|
@@ -1386,8 +1924,12 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1386
1924
|
not_counted_as_full_doctor: !deepDiagnostics,
|
|
1387
1925
|
root,
|
|
1388
1926
|
arg_warnings: argWarnings,
|
|
1389
|
-
warnings: [...oauthCallbackPortDiagnostic.warnings],
|
|
1390
|
-
operator_actions:
|
|
1927
|
+
warnings: [...oauthCallbackPortDiagnostic.warnings, ...(codexLbDivergence.warnings || [])],
|
|
1928
|
+
operator_actions: [
|
|
1929
|
+
...oauthCallbackOperatorActions,
|
|
1930
|
+
...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
|
|
1931
|
+
...(codexLbDivergence.operator_actions || [])
|
|
1932
|
+
],
|
|
1391
1933
|
node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
|
|
1392
1934
|
codex,
|
|
1393
1935
|
oauth_callback_port_diagnostic: oauthCallbackPortDiagnostic,
|
|
@@ -1397,6 +1939,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1397
1939
|
codex_app_ui: codexAppUi,
|
|
1398
1940
|
openrouter_provider: openRouterProviderRepair,
|
|
1399
1941
|
sks_menubar: sksMenuBar,
|
|
1942
|
+
telegram_remote: telegramRemote,
|
|
1400
1943
|
provider_context: providerContext,
|
|
1401
1944
|
codex_lb: codexLb,
|
|
1402
1945
|
codex_doctor: authoritativeCodexDoctor,
|
|
@@ -1607,6 +2150,8 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1607
2150
|
console.log(` next action: ${codexAppUi.next_action}`);
|
|
1608
2151
|
console.log('SKS Menu Bar:');
|
|
1609
2152
|
console.log(` status: ${sksMenuBar.status || (sksMenuBar.ok ? 'ok' : 'blocked')}`);
|
|
2153
|
+
for (const line of sksMenuBarRunningVersionConsoleLines(sksMenuBar))
|
|
2154
|
+
console.log(line);
|
|
1610
2155
|
const menubarPhase = doctorFixTransaction?.phases?.find((phase) => phase?.id === 'sks_menubar');
|
|
1611
2156
|
if (menubarPhase) {
|
|
1612
2157
|
const menubarSummary = menubarPhase.ok
|
|
@@ -1622,6 +2167,21 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1622
2167
|
console.log(` blockers: ${sksMenuBar.blockers.join(', ')}`);
|
|
1623
2168
|
if (Array.isArray(sksMenuBar.warnings) && sksMenuBar.warnings.length)
|
|
1624
2169
|
console.log(` warnings: ${sksMenuBar.warnings.join(', ')}`);
|
|
2170
|
+
console.log('Telegram Remote:');
|
|
2171
|
+
console.log(` status: ${telegramRemote.status || 'unknown'}`);
|
|
2172
|
+
console.log(` getMe: ${telegramRemote.bot_identity_valid ? 'verified' : telegramRemote.token_configured ? 'invalid' : 'not configured'}`);
|
|
2173
|
+
console.log(` audit: ${telegramRemote.audit_healthy ? 'healthy' : 'unavailable'}`);
|
|
2174
|
+
const telegramCheckedLine = telegramDoctorCheckedLine(telegramRemote, ' ');
|
|
2175
|
+
if (telegramCheckedLine)
|
|
2176
|
+
console.log(telegramCheckedLine);
|
|
2177
|
+
console.log(` long poll: ${telegramRemote.poller?.running ? 'running' : 'stopped'}`);
|
|
2178
|
+
const telegramSelfHealOutcome = telegramRemote.self_heal_outcome;
|
|
2179
|
+
const telegramDisplayedAction = telegramSelfHealOutcome?.attempted
|
|
2180
|
+
? telegramSelfHealOutcome.action
|
|
2181
|
+
: telegramRemote.self_heal_action;
|
|
2182
|
+
if (telegramDisplayedAction && telegramDisplayedAction !== 'none') {
|
|
2183
|
+
console.log(` self-heal: ${telegramDisplayedAction}${telegramSelfHealOutcome?.attempted ? ` (${telegramSelfHealOutcome.recovered ? 'recovered' : 'attempted, still degraded'})` : ''}`);
|
|
2184
|
+
}
|
|
1625
2185
|
console.log(`Provider: ${providerContext.provider || 'unknown'} ${providerContext.service_tier || ''} (${providerContext.source || 'unknown'}, ${providerContext.confidence || 'low'})`);
|
|
1626
2186
|
const imagegenReady = imagegen.auth_readiness;
|
|
1627
2187
|
if (imagegenReady) {
|
|
@@ -1674,6 +2234,17 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1674
2234
|
if (codex0138Doctor?.fixed?.length)
|
|
1675
2235
|
console.log(` doctor --fix repaired: ${codex0138Doctor.fixed.join(', ')}`);
|
|
1676
2236
|
console.log(`codex-lb: ${codexLb.ok ? 'ok' : `warning ${codexLb.circuit?.state || 'unknown'}`}`);
|
|
2237
|
+
console.log(` key source: ${codexLb.secret_resolution.source}${codexLb.secret_resolution.path ? ` (${codexLb.secret_resolution.path})` : ''}; prompt risk: ${codexLb.secret_resolution.prompt_risk}`);
|
|
2238
|
+
for (const action of codexLb.legacy_keychain_migration?.operator_actions || [])
|
|
2239
|
+
console.log(` action: ${action}`);
|
|
2240
|
+
for (const warning of codexLb.divergence?.warnings || [])
|
|
2241
|
+
console.log(` warning: ${warning}`);
|
|
2242
|
+
for (const action of codexLb.divergence?.operator_actions || [])
|
|
2243
|
+
console.log(` action: ${action}`);
|
|
2244
|
+
if (codexLb.routing_truth) {
|
|
2245
|
+
const routingTruth = codexLb.routing_truth;
|
|
2246
|
+
console.log(` routing truth: ${codexLb.routing_ok ? 'verified' : 'blocked'} (${routingTruth.actual_host || routingTruth.configured_host || 'host unavailable'}, ${routingTruth.auth_outcome}, ${routingTruth.latency_ms ?? 'unmeasured'} ms at ${routingTruth.checked_at})`);
|
|
2247
|
+
}
|
|
1677
2248
|
if (codexLb.tool_output_recovery) {
|
|
1678
2249
|
const recovery = codexLb.tool_output_recovery;
|
|
1679
2250
|
console.log(` interrupted tool-output recovery: ${recovery.ok ? 'ready' : 'blocked'} (${recovery.observed_version || recovery.status}; minimum ${recovery.minimum_version})`);
|
|
@@ -2011,6 +2582,95 @@ export function formatCodexDoctorConsoleStatus(report) {
|
|
|
2011
2582
|
return 'unavailable';
|
|
2012
2583
|
return report.disposition || (report.exit_code === 0 ? 'pass' : 'warn');
|
|
2013
2584
|
}
|
|
2585
|
+
export function sksMenuBarRunningVersionConsoleLines(status) {
|
|
2586
|
+
const runningVersion = status?.running_process?.package_version
|
|
2587
|
+
|| status?.menubar_version_probe?.running_version
|
|
2588
|
+
|| null;
|
|
2589
|
+
const runningPid = status?.running_process?.pid ?? status?.menubar_version_probe?.pid ?? null;
|
|
2590
|
+
if (typeof runningVersion !== 'string' || !runningVersion || !Number.isInteger(runningPid) || runningPid <= 0) {
|
|
2591
|
+
return [];
|
|
2592
|
+
}
|
|
2593
|
+
const expectedVersion = status?.menubar_version_probe?.expected_version
|
|
2594
|
+
|| status?.action_target?.expected_version
|
|
2595
|
+
|| status?.package_version
|
|
2596
|
+
|| null;
|
|
2597
|
+
const installedVersion = status?.installed_version || status?.build_stamp?.package_version || null;
|
|
2598
|
+
return [
|
|
2599
|
+
` RUNNING: version ${runningVersion} (PID ${runningPid})`,
|
|
2600
|
+
...(typeof expectedVersion === 'string' && expectedVersion && expectedVersion !== runningVersion
|
|
2601
|
+
? [` expected: version ${expectedVersion}`]
|
|
2602
|
+
: []),
|
|
2603
|
+
...(typeof installedVersion === 'string' && installedVersion && installedVersion !== runningVersion
|
|
2604
|
+
? [` installed: version ${installedVersion}`]
|
|
2605
|
+
: [])
|
|
2606
|
+
];
|
|
2607
|
+
}
|
|
2608
|
+
export async function rebootstrapSksMenuBarLaunchdForDoctorFix(input, deps = {}) {
|
|
2609
|
+
const env = input.env || process.env;
|
|
2610
|
+
const status = input.status;
|
|
2611
|
+
const expectedVersion = status?.menubar_version_probe?.expected_version
|
|
2612
|
+
|| status?.action_target?.expected_version
|
|
2613
|
+
|| status?.installed_version
|
|
2614
|
+
|| null;
|
|
2615
|
+
const warningOnlyVerifiedProcess = input.fix
|
|
2616
|
+
&& !sksMenuBarRestartDeferred(env)
|
|
2617
|
+
&& status?.launchd?.checked === true
|
|
2618
|
+
&& status?.launchd?.ok === false
|
|
2619
|
+
&& Array.isArray(status?.warnings)
|
|
2620
|
+
&& status.warnings.includes('launchd_not_running_process_active')
|
|
2621
|
+
&& !(Array.isArray(status?.blockers) && status.blockers.includes('launchd_not_running'))
|
|
2622
|
+
&& status?.running_process?.ok === true
|
|
2623
|
+
&& typeof expectedVersion === 'string'
|
|
2624
|
+
&& status.running_process.package_version === expectedVersion;
|
|
2625
|
+
if (!warningOnlyVerifiedProcess) {
|
|
2626
|
+
return { attempted: false, ok: true, status, restart: null };
|
|
2627
|
+
}
|
|
2628
|
+
const paths = sksMenuBarPaths(input.home || env.HOME, input.root);
|
|
2629
|
+
const restartImpl = deps.restartLaunchAgentImpl || restartLaunchAgent;
|
|
2630
|
+
const inspectImpl = deps.inspectSksMenuBarStatusImpl || inspectSksMenuBarStatus;
|
|
2631
|
+
const restart = await restartImpl(paths, env).catch((error) => ({
|
|
2632
|
+
ok: false,
|
|
2633
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2634
|
+
}));
|
|
2635
|
+
if (restart?.ok !== true) {
|
|
2636
|
+
const detail = restart?.error ? `:${restart.error}` : '';
|
|
2637
|
+
return {
|
|
2638
|
+
attempted: true,
|
|
2639
|
+
ok: false,
|
|
2640
|
+
restart,
|
|
2641
|
+
status: {
|
|
2642
|
+
...status,
|
|
2643
|
+
ok: false,
|
|
2644
|
+
blockers: [...new Set([...(status?.blockers || []), `launchd_rebootstrap_failed${detail}`])]
|
|
2645
|
+
}
|
|
2646
|
+
};
|
|
2647
|
+
}
|
|
2648
|
+
const refreshed = await inspectImpl({
|
|
2649
|
+
root: input.root,
|
|
2650
|
+
...(input.home ? { home: input.home } : {}),
|
|
2651
|
+
...(input.env ? { env: input.env } : {})
|
|
2652
|
+
}).catch((error) => ({
|
|
2653
|
+
...status,
|
|
2654
|
+
ok: false,
|
|
2655
|
+
blockers: [...new Set([
|
|
2656
|
+
...(status?.blockers || []),
|
|
2657
|
+
`launchd_rebootstrap_status_failed:${error instanceof Error ? error.message : String(error)}`
|
|
2658
|
+
])]
|
|
2659
|
+
}));
|
|
2660
|
+
if (refreshed?.launchd?.checked !== true || refreshed?.launchd?.ok !== true) {
|
|
2661
|
+
return {
|
|
2662
|
+
attempted: true,
|
|
2663
|
+
ok: false,
|
|
2664
|
+
restart,
|
|
2665
|
+
status: {
|
|
2666
|
+
...refreshed,
|
|
2667
|
+
ok: false,
|
|
2668
|
+
blockers: [...new Set([...(refreshed?.blockers || []), 'launchd_rebootstrap_not_confirmed'])]
|
|
2669
|
+
}
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
return { attempted: true, ok: true, status: refreshed, restart };
|
|
2673
|
+
}
|
|
2014
2674
|
function mergeObservedCodexStartupWarnings(startupRepair, codexDoctor) {
|
|
2015
2675
|
const text = `${codexDoctor?.stdout_tail || ''}\n${codexDoctor?.stderr_tail || ''}`;
|
|
2016
2676
|
const manual = new Set(Array.isArray(startupRepair?.manual_actions) ? startupRepair.manual_actions : []);
|