sneakoscope 9.2.3 → 9.2.5
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 +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/commands/bridge.js +8 -5
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/codex/codex-feature-flags.js +2 -2
- package/dist/core/codex-control/codex-current-collab-agent-real-probe.js +5 -2
- package/dist/core/codex-lb/bridge-cli-contract.js +35 -0
- package/dist/core/codex-lb/desktop-service.js +38 -17
- package/dist/core/codex-lb/provider-route-policy.js +26 -5
- package/dist/core/doctor/legacy-runtime-data-gc.js +185 -0
- package/dist/core/init/legacy-generation-convergence.js +30 -5
- package/dist/core/update/update-migration-state/desktop-bridge-restage.js +52 -25
- package/dist/core/version-manager.js +1 -2
- package/dist/core/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, mult
|
|
|
22
22
|
Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
|
|
23
23
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
24
24
|
|
|
25
|
-
This README documents package **SKS 9.2.
|
|
25
|
+
This README documents package **SKS 9.2.5** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
|
|
26
26
|
|
|
27
27
|
Use the official latest stable SKS and Codex CLI releases. The Codex compatibility SSOT is always the **current latest stable** host; capability probes measure what that host can actually do. Product docs do not crown a fixed `0.x.y` string as SSOT (release pins and schema directories are measured artifacts for the current package, not a permanent product version claim). Menu Bar / Center induce updates to the latest stable build. Run `sks update-check` for what is installed and read the capability report for what is supported. Install SSOT is npm `sneakoscope@latest`; PATH `sks` and Menu Bar stamped generation must match that version or gates fail. It resolves managed SKS skills from the authoritative global install, preserves a runnable Naruto child slot when `max_threads=2`, and keeps Menu Bar repair transactional so stamped generations remain verifiable. Naruto uses stable opt-in multi-agent V2 when the host exposes it (Codex official multi-agent wrap-only; SKS does not reimplement a parallel runtime). Local code search is mode-separated (`sks search files|text|structure|symbol|context`); `context` is answered by the compiled TriWiki Context Graph (`context-graph.json` is exhaustive authority; `context-pack.json` and managed `AGENTS.md` are bounded projections) — see [docs/architecture/context-graph.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/architecture/context-graph.md) and [docs/PRODUCT-CONTRACT.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/PRODUCT-CONTRACT.md). See [CHANGELOG.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/CHANGELOG.md).
|
|
28
28
|
|
package/dist/commands/bridge.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { readStdin } from '../core/fsx.js';
|
|
3
|
+
import { BRIDGE_CLI_BOOLEAN_OPTIONS, BRIDGE_CLI_VALUE_OPTIONS, DESKTOP_BRIDGE_SUPERVISED_FLAG, isBridgeCliBooleanOption, isBridgeCliValueOption } from '../core/codex-lb/bridge-cli-contract.js';
|
|
3
4
|
import { serveDesktopBridge } from '../core/codex-lb/desktop-service.js';
|
|
4
5
|
import { BridgeCliError, errorOutput, mergeMetadata, ordinaryOutput, sanitizeBridgeValue, textSummary, verificationOutput } from './bridge-command-output.js';
|
|
5
6
|
const CONTROLLER_FACADE_EXPORT = 'executeDesktopBridgeCommand';
|
|
@@ -117,7 +118,7 @@ async function parseInvocation(args, io) {
|
|
|
117
118
|
return { ...base, request: { operation: 'status' }, label: 'Desktop Bridge status' };
|
|
118
119
|
}
|
|
119
120
|
if (area === 'serve' && action === undefined) {
|
|
120
|
-
allowOnly(parsed, ['--json',
|
|
121
|
+
allowOnly(parsed, ['--json', DESKTOP_BRIDGE_SUPERVISED_FLAG], ['--settings']);
|
|
121
122
|
const settingsPath = parsed.values.get('--settings') || '';
|
|
122
123
|
if (!path.isAbsolute(settingsPath)) {
|
|
123
124
|
throw new BridgeCliError('desktop_bridge_settings_path_must_be_absolute');
|
|
@@ -280,10 +281,8 @@ function parseArgs(args) {
|
|
|
280
281
|
const positionals = [];
|
|
281
282
|
const flags = new Set();
|
|
282
283
|
const values = new Map();
|
|
283
|
-
const booleanOptions = new Set(
|
|
284
|
-
|
|
285
|
-
]);
|
|
286
|
-
const valueOptions = new Set(['--level', '--host', '--settings', '--set']);
|
|
284
|
+
const booleanOptions = new Set(BRIDGE_CLI_BOOLEAN_OPTIONS);
|
|
285
|
+
const valueOptions = new Set(BRIDGE_CLI_VALUE_OPTIONS);
|
|
287
286
|
for (let index = 0; index < args.length; index += 1) {
|
|
288
287
|
const value = String(args[index] || '');
|
|
289
288
|
if (!value.startsWith('--')) {
|
|
@@ -323,6 +322,10 @@ function rejectSecretArgv(args) {
|
|
|
323
322
|
}
|
|
324
323
|
}
|
|
325
324
|
function allowOnly(parsed, allowedFlags, allowedValues) {
|
|
325
|
+
if (allowedFlags.some((entry) => !isBridgeCliBooleanOption(entry))
|
|
326
|
+
|| allowedValues.some((entry) => !isBridgeCliValueOption(entry))) {
|
|
327
|
+
throw new BridgeCliError('bridge_command_option_table_desynchronized');
|
|
328
|
+
}
|
|
326
329
|
const flags = new Set(allowedFlags);
|
|
327
330
|
const values = new Set(allowedValues);
|
|
328
331
|
if ([...parsed.flags].some((entry) => !flags.has(entry))) {
|
|
@@ -2,9 +2,9 @@ export const MANAGED_CODEX_FEATURE_FLAGS = Object.freeze(['hooks', 'fast_mode',
|
|
|
2
2
|
export const REMOVED_CODEX_FEATURE_FLAGS = Object.freeze([
|
|
3
3
|
'fast_mode_ui',
|
|
4
4
|
'codex_hooks',
|
|
5
|
+
'multi_agent_mode',
|
|
5
6
|
'remote_control',
|
|
6
7
|
'codex_git_commit',
|
|
7
8
|
'plugin_hooks',
|
|
8
|
-
'js_repl'
|
|
9
|
-
'multi_agent_mode'
|
|
9
|
+
'js_repl'
|
|
10
10
|
]);
|
|
@@ -3,7 +3,10 @@ import path from 'node:path';
|
|
|
3
3
|
import { findCodexBinary } from '../codex-adapter.js';
|
|
4
4
|
import { ensureDir, runProcess } from '../fsx.js';
|
|
5
5
|
import { codexCurrentCoreProbeTail, skippedCodexCurrentCoreProbe } from './codex-current-core-real-probes.js';
|
|
6
|
-
const CURRENT_COLLAB_TOOLS = [
|
|
6
|
+
const CURRENT_COLLAB_TOOLS = [
|
|
7
|
+
'spawnAgent', 'sendInput', 'resumeAgent', 'wait', 'closeAgent',
|
|
8
|
+
'sendMessage', 'followupTask', 'interruptAgent', 'listAgents'
|
|
9
|
+
];
|
|
7
10
|
export async function runCodexCurrentCollabAgentToolSchemaRealProbe(input) {
|
|
8
11
|
const started = Date.now();
|
|
9
12
|
const codexBin = input.codexBin || await findCodexBinary();
|
|
@@ -23,7 +26,7 @@ export async function runCodexCurrentCollabAgentToolSchemaRealProbe(input) {
|
|
|
23
26
|
? schema.definitions.CollabAgentTool.enum.map(String)
|
|
24
27
|
: [];
|
|
25
28
|
const currentNamesPresent = CURRENT_COLLAB_TOOLS.every((name) => tools.includes(name));
|
|
26
|
-
const legacyInterruptAbsent = !tools.includes('
|
|
29
|
+
const legacyInterruptAbsent = !tools.includes('interrupt_agent');
|
|
27
30
|
const collabItemPresent = JSON.stringify(schema?.definitions?.ThreadItem || {}).includes('collabAgentToolCall')
|
|
28
31
|
|| JSON.stringify(schema || {}).includes('collabAgentToolCall');
|
|
29
32
|
const processExitedSuccessfully = result.code === 0;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DesktopBridgeError } from './desktop-bridge/types.js';
|
|
2
|
+
export const DESKTOP_BRIDGE_SUPERVISED_FLAG = '--supervised';
|
|
3
|
+
export const BRIDGE_CLI_BOOLEAN_OPTIONS = [
|
|
4
|
+
'--json',
|
|
5
|
+
'--strict',
|
|
6
|
+
'--require-ready',
|
|
7
|
+
'--api-key-stdin',
|
|
8
|
+
'--confirm',
|
|
9
|
+
DESKTOP_BRIDGE_SUPERVISED_FLAG
|
|
10
|
+
];
|
|
11
|
+
export const BRIDGE_CLI_VALUE_OPTIONS = ['--level', '--host', '--settings', '--set'];
|
|
12
|
+
export function isBridgeCliBooleanOption(value) {
|
|
13
|
+
return BRIDGE_CLI_BOOLEAN_OPTIONS.includes(value);
|
|
14
|
+
}
|
|
15
|
+
export function isBridgeCliValueOption(value) {
|
|
16
|
+
return BRIDGE_CLI_VALUE_OPTIONS.includes(value);
|
|
17
|
+
}
|
|
18
|
+
export function isBridgeCliOption(value) {
|
|
19
|
+
return isBridgeCliBooleanOption(value) || isBridgeCliValueOption(value);
|
|
20
|
+
}
|
|
21
|
+
export function desktopBridgeServeArguments(settingsPath) {
|
|
22
|
+
const argv = [
|
|
23
|
+
'bridge',
|
|
24
|
+
'serve',
|
|
25
|
+
'--settings',
|
|
26
|
+
settingsPath,
|
|
27
|
+
'--json',
|
|
28
|
+
DESKTOP_BRIDGE_SUPERVISED_FLAG
|
|
29
|
+
];
|
|
30
|
+
const unregistered = argv.filter((token) => token.startsWith('--') && !isBridgeCliOption(token));
|
|
31
|
+
if (unregistered.length) {
|
|
32
|
+
throw new DesktopBridgeError(`bridge_serve_option_unregistered:${unregistered.join(',')}`);
|
|
33
|
+
}
|
|
34
|
+
return argv;
|
|
35
|
+
}
|
|
@@ -8,6 +8,7 @@ import { withFileLock } from '../locks/file-lock.js';
|
|
|
8
8
|
import { loadCodexLbEnv } from './codex-lb-env.js';
|
|
9
9
|
import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
|
|
10
10
|
import { cleanupRetiredDesktopBridgeRuntime, prepareRetiredDesktopBridgeRuntime, } from './desktop-bridge-migration/retired-runtime-cleanup.js';
|
|
11
|
+
import { DESKTOP_BRIDGE_SUPERVISED_FLAG, desktopBridgeServeArguments } from './bridge-cli-contract.js';
|
|
11
12
|
import { canonicalizeBridgeModelId, normalizeBridgeUpstreamModelId, sha256Stable } from './route-index.js';
|
|
12
13
|
import { captureCodexAuthSnapshot } from './desktop-auth-invariant.js';
|
|
13
14
|
import { applyOfficialModelPassthrough, bridgeRoutePolicyPath, writeBridgeRoutingPolicy } from './provider-route-policy.js';
|
|
@@ -352,12 +353,13 @@ export async function installAndStartDesktopBridgeService(options = {}) {
|
|
|
352
353
|
catch (error) {
|
|
353
354
|
return failedStatus(paths, settings, launchService(options.uid), 'credentials_unavailable', safeServiceError(error));
|
|
354
355
|
}
|
|
355
|
-
const
|
|
356
|
+
const launch = await resolveLaunchCommand(options, home);
|
|
357
|
+
const command = launch.command;
|
|
356
358
|
if (!command)
|
|
357
|
-
return failedStatus(paths, settings, launchService(options.uid), 'settings_missing',
|
|
359
|
+
return failedStatus(paths, settings, launchService(options.uid), 'settings_missing', launch.blocker);
|
|
358
360
|
await prepareDesktopBridgeServicePaths(paths);
|
|
359
361
|
await writeDesktopBridgeServiceSettings(paths.settings_path, { ...settings, provider_registry: runtime.config.providerRegistry, route_policy: runtime.config.routePolicy });
|
|
360
|
-
await writeDesktopBridgeLaunchdPlist(paths.launch_agent_path, { executablePath: command.executable, arguments: [...command.arguments,
|
|
362
|
+
await writeDesktopBridgeLaunchdPlist(paths.launch_agent_path, { executablePath: command.executable, arguments: [...command.arguments, ...desktopBridgeServeArguments(paths.settings_path)], stdoutPath: paths.stdout_log_path, stderrPath: paths.stderr_log_path });
|
|
361
363
|
const run = options.run || runProcess;
|
|
362
364
|
const ctl = options.launchctl || '/bin/launchctl';
|
|
363
365
|
const service = launchService(options.uid);
|
|
@@ -426,8 +428,8 @@ export async function stopDesktopBridgeService(options = {}) {
|
|
|
426
428
|
const status = await desktopBridgeServiceStatus({ ...options, home });
|
|
427
429
|
return { ...status, ok: !status.running, blockers: status.running ? ['desktop_bridge_process_still_running'] : [] };
|
|
428
430
|
}
|
|
429
|
-
function desktopBridgeIsSupervised(env = process.env, argv = process.argv) {
|
|
430
|
-
return env.XPC_SERVICE_NAME === DESKTOP_BRIDGE_LAUNCHD_LABEL || argv.includes(
|
|
431
|
+
export function desktopBridgeIsSupervised(env = process.env, argv = process.argv) {
|
|
432
|
+
return env.XPC_SERVICE_NAME === DESKTOP_BRIDGE_LAUNCHD_LABEL || argv.includes(DESKTOP_BRIDGE_SUPERVISED_FLAG);
|
|
431
433
|
}
|
|
432
434
|
async function installedPackageVersion() {
|
|
433
435
|
try {
|
|
@@ -463,17 +465,15 @@ async function autoApplyOfficialModelsAtServe(runtime, home, options) {
|
|
|
463
465
|
home,
|
|
464
466
|
codexLbRegistered: desktopBridgeCodexLbRegistered(settings.provider_registry.providers),
|
|
465
467
|
});
|
|
466
|
-
|
|
468
|
+
const converged = applyOfficialModelPassthrough(settings.route_policy, { mode });
|
|
469
|
+
if (converged.policy_generation === settings.route_policy.policy_generation)
|
|
467
470
|
return;
|
|
468
|
-
const
|
|
469
|
-
if (flipped.policy_generation === settings.route_policy.policy_generation)
|
|
470
|
-
return;
|
|
471
|
-
const nextSettings = { ...settings, route_policy: flipped };
|
|
471
|
+
const nextSettings = { ...settings, route_policy: converged };
|
|
472
472
|
await writeDesktopBridgeServiceSettings(options.settingsPath || desktopBridgeServicePaths(home).settings_path, nextSettings);
|
|
473
|
-
await writeBridgeRoutingPolicy(bridgeRoutePolicyPath(path.join(path.resolve(home), '.codex')),
|
|
474
|
-
settings.route_policy =
|
|
475
|
-
runtime.config.routePolicy =
|
|
476
|
-
process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.official_models_auto_applied', at: new Date().toISOString(), sks_version: PACKAGE_VERSION, mode
|
|
473
|
+
await writeBridgeRoutingPolicy(bridgeRoutePolicyPath(path.join(path.resolve(home), '.codex')), converged).catch(() => undefined);
|
|
474
|
+
settings.route_policy = converged;
|
|
475
|
+
runtime.config.routePolicy = converged;
|
|
476
|
+
process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.official_models_auto_applied', at: new Date().toISOString(), sks_version: PACKAGE_VERSION, mode, policy_generation: converged.policy_generation, secret_fields_redacted: true })}\n`);
|
|
477
477
|
}
|
|
478
478
|
export async function serveDesktopBridge(options = {}) {
|
|
479
479
|
let handle = null;
|
|
@@ -697,9 +697,30 @@ function normalizeProviderRegistrySnapshot(value) {
|
|
|
697
697
|
function overridePaths(base, options) { return { settings_path: options.settingsPath || base.settings_path, state_path: options.statePath || base.state_path, client_capability_path: options.clientCapabilityPath || base.client_capability_path, launch_agent_path: options.launchAgentPath || base.launch_agent_path, stdout_log_path: options.stdoutLogPath || base.stdout_log_path, stderr_log_path: options.stderrLogPath || base.stderr_log_path }; }
|
|
698
698
|
function launchDomain(uid = typeof process.getuid === 'function' ? process.getuid() : 0) { return `gui/${uid}`; }
|
|
699
699
|
function launchService(uid) { return `${launchDomain(uid)}/${DESKTOP_BRIDGE_LAUNCHD_LABEL}`; }
|
|
700
|
-
async function resolveLaunchCommand(options) {
|
|
701
|
-
|
|
702
|
-
|
|
700
|
+
async function resolveLaunchCommand(options, home) {
|
|
701
|
+
if (options.executablePath) {
|
|
702
|
+
return await exists(path.resolve(options.executablePath))
|
|
703
|
+
? { command: { executable: path.resolve(options.executablePath), arguments: [...(options.executableArguments || [])] }, blocker: 'desktop_bridge_sks_executable_missing' }
|
|
704
|
+
: { command: null, blocker: 'desktop_bridge_sks_executable_missing' };
|
|
705
|
+
}
|
|
706
|
+
const candidates = [];
|
|
707
|
+
const entry = String(process.argv[1] || '');
|
|
708
|
+
if (entry && ['sks', 'sneakoscope'].includes(path.basename(entry).replace(/\.js$/i, '')) && await exists(entry)) {
|
|
709
|
+
candidates.push({ executable: path.resolve(process.execPath), arguments: [path.resolve(entry)] });
|
|
710
|
+
}
|
|
711
|
+
const sks = await which('sks').catch(() => null);
|
|
712
|
+
if (sks)
|
|
713
|
+
candidates.push({ executable: path.resolve(sks), arguments: [] });
|
|
714
|
+
let rejectedProtected = false;
|
|
715
|
+
for (const candidate of candidates) {
|
|
716
|
+
if (await launchTargetsProtectedFolder([candidate.executable, ...candidate.arguments], home)) {
|
|
717
|
+
rejectedProtected = true;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
return { command: candidate, blocker: 'desktop_bridge_sks_executable_missing' };
|
|
721
|
+
}
|
|
722
|
+
return { command: null, blocker: rejectedProtected ? 'desktop_bridge_entry_macos_protected_folder' : 'desktop_bridge_sks_executable_missing' };
|
|
723
|
+
}
|
|
703
724
|
function macosProtectedUserPath(target, home) {
|
|
704
725
|
if (!target)
|
|
705
726
|
return false;
|
|
@@ -118,14 +118,18 @@ function isRouteTargetId(value) {
|
|
|
118
118
|
}
|
|
119
119
|
export const OFFICIAL_MODEL_ID_PATTERN = /^(?:gpt-[0-9]|o[0-9]|codex-mini)/;
|
|
120
120
|
export function applyOfficialModelPassthrough(policy, input = { mode: 'passthrough' }) {
|
|
121
|
-
if (input.mode === 'gateway')
|
|
122
|
-
return policy;
|
|
123
121
|
const routes = {};
|
|
122
|
+
let changed = false;
|
|
124
123
|
for (const [model, target] of Object.entries(policy.model_routes)) {
|
|
125
|
-
|
|
126
|
-
?
|
|
127
|
-
: target;
|
|
124
|
+
const next = input.mode === 'passthrough'
|
|
125
|
+
? passthroughRouteTarget(model, target)
|
|
126
|
+
: gatewayRouteTarget(model, target, policy);
|
|
127
|
+
routes[model] = next;
|
|
128
|
+
if (next !== target)
|
|
129
|
+
changed = true;
|
|
128
130
|
}
|
|
131
|
+
if (!changed)
|
|
132
|
+
return policy;
|
|
129
133
|
const semantic = {
|
|
130
134
|
default_provider_id: policy.default_provider_id,
|
|
131
135
|
fallback: 'none',
|
|
@@ -139,6 +143,23 @@ export function applyOfficialModelPassthrough(policy, input = { mode: 'passthrou
|
|
|
139
143
|
changed_at: input.changedAt || new Date().toISOString()
|
|
140
144
|
};
|
|
141
145
|
}
|
|
146
|
+
function passthroughRouteTarget(model, target) {
|
|
147
|
+
if (!OFFICIAL_MODEL_ID_PATTERN.test(model) || model.includes(':'))
|
|
148
|
+
return target;
|
|
149
|
+
if (target.provider_id === BRIDGE_OFFICIAL_ROUTE_ID && target.upstream_model === model)
|
|
150
|
+
return target;
|
|
151
|
+
return { provider_id: BRIDGE_OFFICIAL_ROUTE_ID, upstream_model: model };
|
|
152
|
+
}
|
|
153
|
+
function gatewayRouteTarget(model, target, policy) {
|
|
154
|
+
if (target.provider_id !== BRIDGE_OFFICIAL_ROUTE_ID)
|
|
155
|
+
return target;
|
|
156
|
+
if (!OFFICIAL_MODEL_ID_PATTERN.test(model) || model.includes(':'))
|
|
157
|
+
return target;
|
|
158
|
+
const twin = policy.model_routes[`codex-lb:${model}`];
|
|
159
|
+
if (!twin || twin.provider_id !== 'codex-lb')
|
|
160
|
+
return target;
|
|
161
|
+
return { provider_id: twin.provider_id, upstream_model: twin.upstream_model };
|
|
162
|
+
}
|
|
142
163
|
function sameTarget(left, right) {
|
|
143
164
|
return left.provider_id === right.provider_id && left.upstream_model === right.upstream_model;
|
|
144
165
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import { readJson } from '../fsx.js';
|
|
4
|
+
import { inspectConfinedPath, publicPathError, removeManagedPathVerified } from '../managed-path-safety.js';
|
|
5
|
+
export const LEGACY_RUNTIME_DATA_GC_SCHEMA = 'sks.legacy-runtime-data-gc.v1';
|
|
6
|
+
const CONFIG_BACKUP_NAME = /^config\.toml\.(?:backup-[A-Za-z0-9_.:-]+|bak(?:[-.][A-Za-z0-9_.-]+)?|[A-Za-z0-9_.-]+\.bak)$/;
|
|
7
|
+
const BRIDGE_GENERATION_DIR = /^[0-9a-f]{64}\.[0-9a-f]{64}\.[0-9a-f]{64}$/;
|
|
8
|
+
const RETIRED_VERSION_CACHE = /^codex-0\d{2,3}-(?:capability|doctor)\.json$/;
|
|
9
|
+
const RETIRED_CHROME_HOSTS_V1 = 'chrome-native-hosts.json';
|
|
10
|
+
const CHROME_HOSTS_V2 = 'chrome-native-hosts-v2.json';
|
|
11
|
+
export const LEGACY_CONFIG_BACKUP_KEEP_COUNT = 3;
|
|
12
|
+
export const LEGACY_BRIDGE_GENERATION_KEEP_COUNT = 1;
|
|
13
|
+
export async function reconcileLegacyRuntimeData(input) {
|
|
14
|
+
const codexHome = path.resolve(input.codexHome);
|
|
15
|
+
const configBackups = await reconcileConfigBackups(codexHome, input.fix);
|
|
16
|
+
const bridgeGenerations = await reconcileBridgeGenerations(codexHome, input.fix);
|
|
17
|
+
const retiredVersionCaches = await reconcileRetiredVersionCaches(input.stateRoots, input.fix);
|
|
18
|
+
const retiredSingletons = await reconcileRetiredSingletons(codexHome, input.fix);
|
|
19
|
+
const categories = [configBackups, bridgeGenerations, retiredVersionCaches, retiredSingletons];
|
|
20
|
+
const remainingCount = categories.reduce((total, category) => total + category.remaining, 0);
|
|
21
|
+
const errorCount = categories.reduce((total, category) => total + category.errors.length, 0);
|
|
22
|
+
return {
|
|
23
|
+
schema: LEGACY_RUNTIME_DATA_GC_SCHEMA,
|
|
24
|
+
ok: remainingCount === 0 && errorCount === 0,
|
|
25
|
+
fix: input.fix,
|
|
26
|
+
config_backups: configBackups,
|
|
27
|
+
bridge_generations: bridgeGenerations,
|
|
28
|
+
retired_version_caches: retiredVersionCaches,
|
|
29
|
+
retired_singletons: retiredSingletons,
|
|
30
|
+
remaining_count: remainingCount,
|
|
31
|
+
error_count: errorCount
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function emptyCategory() {
|
|
35
|
+
return { detected: 0, removed: 0, kept: 0, remaining: 0, errors: [] };
|
|
36
|
+
}
|
|
37
|
+
async function reconcileConfigBackups(codexHome, fix) {
|
|
38
|
+
const report = emptyCategory();
|
|
39
|
+
const names = await readdirOrNull(codexHome);
|
|
40
|
+
if (!names)
|
|
41
|
+
return report;
|
|
42
|
+
const candidates = [];
|
|
43
|
+
for (const name of names) {
|
|
44
|
+
if (!CONFIG_BACKUP_NAME.test(name))
|
|
45
|
+
continue;
|
|
46
|
+
const inspected = await inspectOwnedRegularFile(codexHome, path.join(codexHome, name), report);
|
|
47
|
+
if (!inspected)
|
|
48
|
+
continue;
|
|
49
|
+
report.detected += 1;
|
|
50
|
+
candidates.push({ file: inspected.path, mtimeMs: inspected.stat?.mtimeMs || 0 });
|
|
51
|
+
}
|
|
52
|
+
candidates.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
53
|
+
report.kept = Math.min(candidates.length, LEGACY_CONFIG_BACKUP_KEEP_COUNT);
|
|
54
|
+
for (const candidate of candidates.slice(LEGACY_CONFIG_BACKUP_KEEP_COUNT)) {
|
|
55
|
+
await removeOrCount(codexHome, candidate.file, fix, report);
|
|
56
|
+
}
|
|
57
|
+
return report;
|
|
58
|
+
}
|
|
59
|
+
async function reconcileBridgeGenerations(codexHome, fix) {
|
|
60
|
+
const report = emptyCategory();
|
|
61
|
+
const runtimeRoot = path.join(codexHome, 'sks');
|
|
62
|
+
const generationsRoot = path.join(runtimeRoot, '.sks-bridge-generations');
|
|
63
|
+
const names = await readdirOrNull(generationsRoot);
|
|
64
|
+
if (!names)
|
|
65
|
+
return report;
|
|
66
|
+
const pointer = await readJson(path.join(runtimeRoot, 'sks-bridge-active-generation.json'), null);
|
|
67
|
+
const activeName = typeof pointer?.bundle_directory === 'string'
|
|
68
|
+
? path.basename(pointer.bundle_directory)
|
|
69
|
+
: null;
|
|
70
|
+
const candidates = [];
|
|
71
|
+
for (const name of names) {
|
|
72
|
+
if (!BRIDGE_GENERATION_DIR.test(name))
|
|
73
|
+
continue;
|
|
74
|
+
const target = path.join(generationsRoot, name);
|
|
75
|
+
let inspected;
|
|
76
|
+
try {
|
|
77
|
+
inspected = await inspectConfinedPath(generationsRoot, target);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
report.errors.push(publicPathError(error, target));
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (!inspected.exists || inspected.leafSymlink || !inspected.stat?.isDirectory() || !ownedByCurrentUid(inspected))
|
|
84
|
+
continue;
|
|
85
|
+
report.detected += 1;
|
|
86
|
+
if (name === activeName) {
|
|
87
|
+
report.kept += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
candidates.push({ file: target, name, mtimeMs: inspected.stat.mtimeMs });
|
|
91
|
+
}
|
|
92
|
+
if (!activeName || !BRIDGE_GENERATION_DIR.test(activeName)) {
|
|
93
|
+
report.kept += candidates.length;
|
|
94
|
+
return report;
|
|
95
|
+
}
|
|
96
|
+
candidates.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
97
|
+
report.kept += Math.min(candidates.length, LEGACY_BRIDGE_GENERATION_KEEP_COUNT);
|
|
98
|
+
for (const candidate of candidates.slice(LEGACY_BRIDGE_GENERATION_KEEP_COUNT)) {
|
|
99
|
+
await removeOrCount(generationsRoot, candidate.file, fix, report);
|
|
100
|
+
}
|
|
101
|
+
return report;
|
|
102
|
+
}
|
|
103
|
+
async function reconcileRetiredVersionCaches(stateRoots, fix) {
|
|
104
|
+
const report = emptyCategory();
|
|
105
|
+
for (const root of [...new Set(stateRoots.map((value) => path.resolve(value)))]) {
|
|
106
|
+
const names = await readdirOrNull(root);
|
|
107
|
+
if (!names)
|
|
108
|
+
continue;
|
|
109
|
+
for (const name of names) {
|
|
110
|
+
if (!RETIRED_VERSION_CACHE.test(name))
|
|
111
|
+
continue;
|
|
112
|
+
const inspected = await inspectOwnedRegularFile(root, path.join(root, name), report);
|
|
113
|
+
if (!inspected)
|
|
114
|
+
continue;
|
|
115
|
+
report.detected += 1;
|
|
116
|
+
await removeOrCount(root, inspected.path, fix, report);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return report;
|
|
120
|
+
}
|
|
121
|
+
async function reconcileRetiredSingletons(codexHome, fix) {
|
|
122
|
+
const report = emptyCategory();
|
|
123
|
+
const v1 = path.join(codexHome, RETIRED_CHROME_HOSTS_V1);
|
|
124
|
+
const v2 = path.join(codexHome, CHROME_HOSTS_V2);
|
|
125
|
+
const v1Inspected = await inspectOwnedRegularFile(codexHome, v1, report, { quiet: true });
|
|
126
|
+
if (!v1Inspected)
|
|
127
|
+
return report;
|
|
128
|
+
const v2Inspected = await inspectOwnedRegularFile(codexHome, v2, report, { quiet: true });
|
|
129
|
+
report.detected += 1;
|
|
130
|
+
if (!v2Inspected) {
|
|
131
|
+
report.kept += 1;
|
|
132
|
+
return report;
|
|
133
|
+
}
|
|
134
|
+
await removeOrCount(codexHome, v1, fix, report);
|
|
135
|
+
return report;
|
|
136
|
+
}
|
|
137
|
+
async function removeOrCount(boundary, file, fix, report) {
|
|
138
|
+
if (!fix) {
|
|
139
|
+
report.remaining += 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await removeManagedPathVerified(boundary, file);
|
|
144
|
+
report.removed += 1;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
report.errors.push(publicPathError(error, file));
|
|
148
|
+
report.remaining += 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function inspectOwnedRegularFile(boundary, file, report, options = {}) {
|
|
152
|
+
let inspected;
|
|
153
|
+
try {
|
|
154
|
+
inspected = await inspectConfinedPath(boundary, file);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (!options.quiet)
|
|
158
|
+
report.errors.push(publicPathError(error, file));
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (!inspected.exists || inspected.leafSymlink || !inspected.stat?.isFile() || !ownedByCurrentUid(inspected))
|
|
162
|
+
return null;
|
|
163
|
+
return inspected;
|
|
164
|
+
}
|
|
165
|
+
function ownedByCurrentUid(inspected) {
|
|
166
|
+
if (typeof process.getuid !== 'function')
|
|
167
|
+
return true;
|
|
168
|
+
return inspected.stat?.uid === process.getuid();
|
|
169
|
+
}
|
|
170
|
+
async function readdirOrNull(directory) {
|
|
171
|
+
try {
|
|
172
|
+
const stat = await fsp.lstat(directory);
|
|
173
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
return (await fsp.readdir(directory)).sort();
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -6,6 +6,7 @@ import { hasExplicitSksManagedCodexConfigMarker, writeCodexConfigGuarded } from
|
|
|
6
6
|
import { validateCodexConfigRoundTrip } from '../codex/codex-config-toml.js';
|
|
7
7
|
import { collectNestedProjectRoots } from '../doctor/current-project-guidance-nested.js';
|
|
8
8
|
import { reconcileRetiredManagedResidue } from '../doctor/retired-managed-residue.js';
|
|
9
|
+
import { reconcileLegacyRuntimeData } from '../doctor/legacy-runtime-data-gc.js';
|
|
9
10
|
import { readJson, readText } from '../fsx.js';
|
|
10
11
|
import { removeMcpServerBlock, mcpServerBlockWithChildren } from '../mcp/mcp-config-preservation.js';
|
|
11
12
|
import { reconcileRetiredSksConfigText } from '../auto-review.js';
|
|
@@ -54,6 +55,11 @@ export async function reconcileLegacyManagedGeneration(input) {
|
|
|
54
55
|
for (const runtimeRoot of runtimeRoots) {
|
|
55
56
|
retiredRuntimeScopes.push(await reconcileRuntimeScope(runtimeRoot, input.fix));
|
|
56
57
|
}
|
|
58
|
+
const runtimeDataGc = await reconcileLegacyRuntimeData({
|
|
59
|
+
codexHome,
|
|
60
|
+
stateRoots: runtimeRoots.map((runtimeRoot) => path.join(runtimeRoot, '.sneakoscope')),
|
|
61
|
+
fix: input.fix
|
|
62
|
+
});
|
|
57
63
|
const managedConfigs = await reconcileManagedConfigs({
|
|
58
64
|
projectRoots,
|
|
59
65
|
home,
|
|
@@ -68,6 +74,7 @@ export async function reconcileLegacyManagedGeneration(input) {
|
|
|
68
74
|
ok: skillsOk
|
|
69
75
|
&& retiredAgentRoles.ok
|
|
70
76
|
&& runtimeOk
|
|
77
|
+
&& runtimeDataGc.ok
|
|
71
78
|
&& managedConfigs.ok
|
|
72
79
|
&& blockers.length === 0
|
|
73
80
|
&& nested.errorCount === 0,
|
|
@@ -79,6 +86,7 @@ export async function reconcileLegacyManagedGeneration(input) {
|
|
|
79
86
|
project_skills: projectSkills,
|
|
80
87
|
retired_agent_roles: retiredAgentRoles,
|
|
81
88
|
retired_runtime_scopes: retiredRuntimeScopes,
|
|
89
|
+
runtime_data_gc: runtimeDataGc,
|
|
82
90
|
managed_configs: managedConfigs,
|
|
83
91
|
blockers,
|
|
84
92
|
warnings
|
|
@@ -133,6 +141,7 @@ async function reconcileManagedConfigs(input) {
|
|
|
133
141
|
rewritten_count: 0,
|
|
134
142
|
retired_mcp_block_count: 0,
|
|
135
143
|
retired_config_entry_count: 0,
|
|
144
|
+
compacted_marker_line_count: 0,
|
|
136
145
|
preserved_user_config_count: 0,
|
|
137
146
|
remaining_count: 0,
|
|
138
147
|
error_count: 0,
|
|
@@ -179,7 +188,8 @@ async function reconcileManagedConfigTarget(target, fix, report) {
|
|
|
179
188
|
const before = await readText(target.configPath, '');
|
|
180
189
|
const ownership = await managedConfigOwnershipProof(target.ownerRoot, target.configPath, before);
|
|
181
190
|
const retiredConfig = reconcileRetiredSksConfigText(before);
|
|
182
|
-
|
|
191
|
+
const retiredApplied = !(retiredConfig.user_authored_conflict && !ownership);
|
|
192
|
+
let next = retiredApplied ? retiredConfig.text : before;
|
|
183
193
|
let retiredMcpCount = 0;
|
|
184
194
|
if (ownership) {
|
|
185
195
|
for (const server of RETIRED_SKS_MCP_SERVERS) {
|
|
@@ -189,8 +199,11 @@ async function reconcileManagedConfigTarget(target, fix, report) {
|
|
|
189
199
|
retiredMcpCount += 1;
|
|
190
200
|
}
|
|
191
201
|
}
|
|
192
|
-
const
|
|
193
|
-
const
|
|
202
|
+
const compacted = compactSksMovedConfigMarkers(next);
|
|
203
|
+
const markerCount = compacted.removed_count;
|
|
204
|
+
next = compacted.text;
|
|
205
|
+
const retiredConfigCount = retiredApplied && retiredConfig.detected_count > 0 ? retiredConfig.detected_count : 0;
|
|
206
|
+
const detectedCount = retiredConfigCount + retiredMcpCount + markerCount;
|
|
194
207
|
if (retiredConfig.user_authored_conflict && !ownership) {
|
|
195
208
|
report.preserved_user_config_count += 1;
|
|
196
209
|
report.preserved_user_configs.push(target.configPath);
|
|
@@ -200,6 +213,7 @@ async function reconcileManagedConfigTarget(target, fix, report) {
|
|
|
200
213
|
report.detected_count += detectedCount;
|
|
201
214
|
report.retired_config_entry_count += retiredConfigCount;
|
|
202
215
|
report.retired_mcp_block_count += retiredMcpCount;
|
|
216
|
+
report.compacted_marker_line_count += markerCount;
|
|
203
217
|
if (!fix) {
|
|
204
218
|
report.remaining_count += detectedCount;
|
|
205
219
|
return;
|
|
@@ -211,8 +225,9 @@ async function reconcileManagedConfigTarget(target, fix, report) {
|
|
|
211
225
|
}
|
|
212
226
|
try {
|
|
213
227
|
const normalized = normalizeConfigText(next);
|
|
214
|
-
const exactRetiredConfigAuthorized = retiredConfig.detected_count > 0
|
|
215
|
-
&& retiredConfig.user_authored_conflict !== true
|
|
228
|
+
const exactRetiredConfigAuthorized = (retiredConfig.detected_count > 0
|
|
229
|
+
&& retiredConfig.user_authored_conflict !== true)
|
|
230
|
+
|| markerCount > 0;
|
|
216
231
|
const guarded = await writeCodexConfigGuarded({
|
|
217
232
|
root: target.ownerRoot,
|
|
218
233
|
configPath: target.configPath,
|
|
@@ -254,6 +269,16 @@ function normalizeConfigText(text) {
|
|
|
254
269
|
const value = String(text || '').trimEnd().replace(/\n{3,}/g, '\n\n');
|
|
255
270
|
return value ? `${value}\n` : '';
|
|
256
271
|
}
|
|
272
|
+
const SKS_MOVED_MARKER_LINE = /^\s*#\s*SKS moved machine-local Codex config\b/i;
|
|
273
|
+
function compactSksMovedConfigMarkers(text) {
|
|
274
|
+
const lines = String(text || '').split('\n');
|
|
275
|
+
const markerIndexes = lines.flatMap((line, index) => (SKS_MOVED_MARKER_LINE.test(line) ? [index] : []));
|
|
276
|
+
if (markerIndexes.length < 2)
|
|
277
|
+
return { text, removed_count: 0 };
|
|
278
|
+
const keep = markerIndexes[markerIndexes.length - 1];
|
|
279
|
+
const kept = lines.filter((line, index) => !SKS_MOVED_MARKER_LINE.test(line) || index === keep);
|
|
280
|
+
return { text: kept.join('\n'), removed_count: markerIndexes.length - 1 };
|
|
281
|
+
}
|
|
257
282
|
function recordConfigError(report, file, error) {
|
|
258
283
|
report.error_count += 1;
|
|
259
284
|
report.errors.push(`${file}:${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1,46 +1,73 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { exists, PACKAGE_VERSION, readJson, runProcess } from '../../fsx.js';
|
|
4
|
-
|
|
4
|
+
import { bootstrapExistingDesktopBridgeService, desktopBridgeServicePaths } from '../../codex-lb/desktop-service.js';
|
|
5
|
+
export async function desktopBridgeRestage(options = {}) {
|
|
5
6
|
const skip = (reason) => ({ ok: true, status: 'ok', actions: [reason], blockers: [], warnings: [] });
|
|
6
|
-
|
|
7
|
+
const warn = (warnings) => ({ ok: true, status: 'ok', actions: [], blockers: [], warnings });
|
|
8
|
+
const env = options.env || process.env;
|
|
9
|
+
const version = options.packageVersion || PACKAGE_VERSION;
|
|
10
|
+
const reachesRealLaunchd = !(options.run && options.bootstrapService);
|
|
11
|
+
if ((options.platform || process.platform) !== 'darwin')
|
|
7
12
|
return skip('desktop_bridge_restage_not_macos');
|
|
8
|
-
if (
|
|
13
|
+
if (reachesRealLaunchd && (env.NODE_TEST_CONTEXT !== undefined || env.SKS_TEST_ISOLATION === '1')) {
|
|
9
14
|
return skip('desktop_bridge_restage_skipped_under_tests');
|
|
10
|
-
|
|
15
|
+
}
|
|
16
|
+
if (env.SKS_SKIP_BRIDGE_RESTAGE === '1')
|
|
11
17
|
return skip('desktop_bridge_restage_disabled');
|
|
12
|
-
const home = os.homedir();
|
|
13
|
-
const
|
|
14
|
-
if (!(await exists(
|
|
18
|
+
const home = options.home || path.resolve(env.HOME || os.homedir());
|
|
19
|
+
const paths = desktopBridgeServicePaths(home);
|
|
20
|
+
if (!(await exists(paths.launch_agent_path)))
|
|
15
21
|
return skip('desktop_bridge_restage_no_launch_agent');
|
|
16
|
-
const state = await readJson(
|
|
22
|
+
const state = await readJson(paths.state_path, null);
|
|
17
23
|
const runningVersion = typeof state?.sks_version === 'string' ? state.sks_version : null;
|
|
18
24
|
const pid = typeof state?.pid === 'number' && Number.isInteger(state.pid) && state.pid > 1 ? state.pid : null;
|
|
19
|
-
if (!pid)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
if (pid === null || !(options.processAlive || processAlive)(pid)) {
|
|
26
|
+
if (!(await exists(paths.settings_path)))
|
|
27
|
+
return skip('desktop_bridge_restage_no_managed_bridge');
|
|
28
|
+
const bootstrap = await (options.bootstrapService || bootstrapExistingDesktopBridgeService)({ home })
|
|
29
|
+
.catch(() => null);
|
|
30
|
+
if (bootstrap?.running) {
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
status: 'ok',
|
|
34
|
+
actions: [`desktop_bridge_bootstrapped:${version}`],
|
|
35
|
+
blockers: [],
|
|
36
|
+
warnings: bootstrap.blockers.map((blocker) => `desktop_bridge_restage_bootstrap_incomplete:${blocker}`)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return warn([
|
|
40
|
+
...(bootstrap?.blockers || ['desktop_bridge_restage_bootstrap_failed'])
|
|
41
|
+
.map((blocker) => `desktop_bridge_restage_bootstrap_incomplete:${blocker}`),
|
|
42
|
+
'Desktop Bridge is installed but not running: run `sks bridge repair` from your home directory'
|
|
43
|
+
]);
|
|
23
44
|
}
|
|
24
|
-
|
|
25
|
-
return skip('desktop_bridge_restage_no_running_bridge');
|
|
26
|
-
}
|
|
27
|
-
if (runningVersion === PACKAGE_VERSION)
|
|
45
|
+
if (runningVersion === version)
|
|
28
46
|
return skip('desktop_bridge_restage_already_current');
|
|
29
|
-
const uid =
|
|
47
|
+
const uid = options.uid === undefined
|
|
48
|
+
? (typeof process.getuid === 'function' ? process.getuid() : null)
|
|
49
|
+
: options.uid;
|
|
30
50
|
if (uid === null)
|
|
31
51
|
return skip('desktop_bridge_restage_no_uid');
|
|
32
|
-
const kick = await runProcess('/bin/launchctl', ['kickstart', '-k', `gui/${uid}/com.sneakoscope.desktop-bridge`], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch(() => null);
|
|
52
|
+
const kick = await (options.run || runProcess)('/bin/launchctl', ['kickstart', '-k', `gui/${uid}/com.sneakoscope.desktop-bridge`], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch(() => null);
|
|
33
53
|
if (!kick || kick.code !== 0) {
|
|
34
|
-
return {
|
|
35
|
-
ok: true, status: 'ok',
|
|
36
|
-
actions: [],
|
|
37
|
-
blockers: [],
|
|
38
|
-
warnings: [`desktop_bridge_restage_kickstart_failed:${runningVersion || 'pre-8.6.2'}`]
|
|
39
|
-
};
|
|
54
|
+
return warn([`desktop_bridge_restage_kickstart_failed:${runningVersion || 'pre-8.6.2'}`]);
|
|
40
55
|
}
|
|
41
56
|
return {
|
|
42
57
|
ok: true, status: 'ok',
|
|
43
|
-
actions: [`desktop_bridge_restarted:${runningVersion || 'pre-8.6.2'}:${
|
|
58
|
+
actions: [`desktop_bridge_restarted:${runningVersion || 'pre-8.6.2'}:${version}`],
|
|
44
59
|
blockers: [], warnings: []
|
|
45
60
|
};
|
|
46
61
|
}
|
|
62
|
+
export function runDesktopBridgeRestageStage() {
|
|
63
|
+
return desktopBridgeRestage();
|
|
64
|
+
}
|
|
65
|
+
function processAlive(pid) {
|
|
66
|
+
try {
|
|
67
|
+
process.kill(pid, 0);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -362,8 +362,7 @@ async function syncSourcePackageVersion(root, version) {
|
|
|
362
362
|
.replace(/not represented as current \d+\.\d+\.\d+ completion proof\./g, `not represented as current ${version} completion proof.`)
|
|
363
363
|
.replace(/^For \d+\.\d+\.\d+, a selected codex-lb/m, `For ${version}, a selected codex-lb`)
|
|
364
364
|
.replace(/^The \d+\.\d+\.\d+ SKS menu bar/m, `The ${version} SKS menu bar`)
|
|
365
|
-
.replace(
|
|
366
|
-
.replace(/to \d+\.\d+\.\d+ upgrade smoke/g, `to ${version} upgrade smoke`)
|
|
365
|
+
.replace(/\bthe (isolated )?\d+\.\d+\.\d+ to \d+\.\d+\.\d+ upgrade smoke/, (_match, isolated) => `the ${isolated || ''}${RELEASE_UPGRADE_BASELINE_VERSION} to ${version} upgrade smoke`)
|
|
367
366
|
.replace(/under the \d+\.\d+\.\d+ release evidence root/g, `under the ${version} release evidence root`)
|
|
368
367
|
.replace(/^Do not cut \d+\.\d+\.\d+ while/m, `Do not cut ${version} while`)
|
|
369
368
|
.replace(/must agree on \d+\.\d+\.\d+\./g, `must agree on ${version}.`)
|
package/dist/core/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '9.2.
|
|
1
|
+
export const PACKAGE_VERSION = '9.2.5';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "9.2.
|
|
4
|
+
"version": "9.2.5",
|
|
5
5
|
"description": "Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
"dependencies": {
|
|
201
201
|
"@modelcontextprotocol/client": "2.0.0",
|
|
202
202
|
"@modelcontextprotocol/server": "2.0.0",
|
|
203
|
-
"@openai/codex-sdk": "0.
|
|
203
|
+
"@openai/codex-sdk": "0.150.1",
|
|
204
204
|
"smol-toml": "^1.7.0",
|
|
205
205
|
"typescript": "^5.9.3"
|
|
206
206
|
},
|