sneakoscope 9.2.2 → 9.2.4

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 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.2** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
25
+ This README documents package **SKS 9.2.4** — 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
 
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "9.2.2"
262
+ version = "9.2.4"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "9.2.2"
3
+ version = "9.2.4"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -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', '--supervised'], ['--settings']);
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
- '--json', '--strict', '--require-ready', '--api-key-stdin', '--confirm'
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))) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "9.2.2",
3
+ "package_version": "9.2.4",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -0,0 +1,25 @@
1
+ export const CODEX_CURRENT_CORE_NATIVE_EXEC_ARGS = [
2
+ '--ignore-user-config',
3
+ '-c',
4
+ 'model_provider="openai"',
5
+ '-c',
6
+ 'forced_login_method="chatgpt"',
7
+ '-c',
8
+ 'mcp_servers={}'
9
+ ];
10
+ export const CODEX_CURRENT_CORE_NATIVE_LOOPBACK_ENV_KEYS = [
11
+ 'OPENAI_BASE_URL',
12
+ 'CHATGPT_BASE_URL',
13
+ 'OPENAI_API_BASE',
14
+ 'CODEX_API_BASE',
15
+ 'CODEX_BASE_URL'
16
+ ];
17
+ export function nativeCodexCurrentCoreProbeEnv(source = process.env) {
18
+ const env = { ...source };
19
+ for (const key of CODEX_CURRENT_CORE_NATIVE_LOOPBACK_ENV_KEYS)
20
+ delete env[key];
21
+ return env;
22
+ }
23
+ export function withNativeCodexCurrentCoreExecArgs(extraArgs = []) {
24
+ return [...CODEX_CURRENT_CORE_NATIVE_EXEC_ARGS, ...extraArgs];
25
+ }
@@ -5,6 +5,7 @@ import { ensureDir, runProcess, writeBinaryAtomic } from '../fsx.js';
5
5
  import { buildImageArtifactPathContract } from '../image/image-artifact-path-contract.js';
6
6
  import { codexCurrentCoreProbeTail, skippedCodexCurrentCoreProbe } from './codex-current-core-real-probes.js';
7
7
  import { prepareCodexAppServerRuntimeEnv } from './codex-app-server-runtime-env.js';
8
+ import { nativeCodexCurrentCoreProbeEnv, withNativeCodexCurrentCoreExecArgs } from './codex-current-core-native-exec.js';
8
9
  const ONE_BY_ONE_PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC', 'base64');
9
10
  export async function runCodexCurrentCoreImageReferencedPathRealProbe(input) {
10
11
  const started = Date.now();
@@ -54,9 +55,9 @@ export async function runCodexCurrentCoreImageReferencedPathRealProbe(input) {
54
55
  `Return compact JSON {"referenced_path":"${inputB.replace(/\\/g, '\\\\')}","saw_image":true}.`,
55
56
  'Do not edit files and do not reference any other image path.'
56
57
  ].join(' ');
57
- const extraArgs = ['-c', 'mcp_servers={}', '--image', inputB, '--skip-git-repo-check', '--ephemeral'];
58
+ const extraArgs = withNativeCodexCurrentCoreExecArgs(['--image', inputB, '--skip-git-repo-check', '--ephemeral']);
58
59
  const args = buildCodexExecArgs({ root: tempDir, prompt, outputFile, json: true, extraArgs });
59
- const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: input.env || process.env });
60
+ const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: nativeCodexCurrentCoreProbeEnv(input.env || process.env) });
60
61
  const result = await runCodexExec({
61
62
  root: tempDir,
62
63
  recoveryRoot: input.root,
@@ -97,6 +98,8 @@ export async function runCodexCurrentCoreImageReferencedPathRealProbe(input) {
97
98
  process_exited_successfully: processExitedSuccessfully,
98
99
  process_warning: processExitedSuccessfully ? null : 'Codex emitted the referenced path evidence before process timeout/nonzero exit.',
99
100
  output_file: outputFile,
101
+ native_codex_only: true,
102
+ ignored_user_config: true,
100
103
  desktop_bridge_launch_guard: result.desktop_bridge_launch_guard || null,
101
104
  contract_blockers: contract.blockers
102
105
  },
@@ -4,6 +4,7 @@ import { buildCodexExecArgs, findCodexBinary, runCodexExec } from '../codex-adap
4
4
  import { ensureDir, runProcess, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
5
5
  import { codexCurrentCoreProbeTail, skippedCodexCurrentCoreProbe } from './codex-current-core-real-probes.js';
6
6
  import { prepareCodexAppServerRuntimeEnv } from './codex-app-server-runtime-env.js';
7
+ import { nativeCodexCurrentCoreProbeEnv, withNativeCodexCurrentCoreExecArgs } from './codex-current-core-native-exec.js';
7
8
  export async function runCodexCurrentCoreWebSearchRealProbe(input) {
8
9
  const started = Date.now();
9
10
  if (!input.allowNetwork) {
@@ -18,15 +19,16 @@ export async function runCodexCurrentCoreWebSearchRealProbe(input) {
18
19
  await writeTextAtomic(path.join(tempDir, 'README.md'), 'Temporary current Codex web-search real probe workspace.\n');
19
20
  const outputFile = path.join(tempDir, 'last-message.txt');
20
21
  const prompt = 'In code mode, use standalone web search to find the title of https://example.com. Return JSON {"used_web_search":true,"answer":"...","sources":[...]}.';
21
- const args = buildCodexExecArgs({ root: tempDir, prompt, outputFile, json: true, extraArgs: ['-c', 'mcp_servers={}'] });
22
- const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: input.env || process.env });
22
+ const extraArgs = withNativeCodexCurrentCoreExecArgs();
23
+ const args = buildCodexExecArgs({ root: tempDir, prompt, outputFile, json: true, extraArgs });
24
+ const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: nativeCodexCurrentCoreProbeEnv(input.env || process.env) });
23
25
  const result = await runCodexExec({
24
26
  root: tempDir,
25
27
  recoveryRoot: input.root,
26
28
  prompt,
27
29
  outputFile,
28
30
  json: true,
29
- extraArgs: ['-c', 'mcp_servers={}'],
31
+ extraArgs,
30
32
  timeoutMs: input.timeoutMs || 120000,
31
33
  maxBufferBytes: 512 * 1024,
32
34
  stdoutFile: path.join(tempDir, 'codex.stdout.log'),
@@ -75,6 +77,8 @@ export async function runCodexCurrentCoreWebSearchRealProbe(input) {
75
77
  process_exited_successfully: processExitedSuccessfully,
76
78
  process_warning: processExitedSuccessfully ? null : 'Codex emitted web-search evidence before process timeout/nonzero exit.',
77
79
  output_file: outputFile,
80
+ native_codex_only: true,
81
+ ignored_user_config: true,
78
82
  desktop_bridge_launch_guard: result.desktop_bridge_launch_guard || null
79
83
  },
80
84
  blockers: ok ? [] : [
@@ -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
+ }
@@ -1,4 +1,6 @@
1
- export const PROVIDERS = ['codex-lb', 'openrouter'];
1
+ import { BRIDGE_OFFICIAL_ROUTE_ID, BRIDGE_PROVIDER_IDS } from '../bridge-contracts.js';
2
+ export const PROVIDERS = BRIDGE_PROVIDER_IDS;
3
+ export const ROUTE_TARGET_IDS = [...BRIDGE_PROVIDER_IDS, BRIDGE_OFFICIAL_ROUTE_ID];
2
4
  export const LEVELS = new Set(['shallow', 'transport', 'deep']);
3
5
  export const PROBE_STATES = new Set([
4
6
  'not_attempted', 'running', 'verified', 'degraded', 'blocked', 'failed',
@@ -1,6 +1,6 @@
1
1
  import { validateScope } from './capability.js';
2
2
  import { validateCatalogState } from './catalog.js';
3
- import { PROBE_STATES, PROVIDERS, booleanValue, enumValue, escapePath, exact, iso, literal, nonEmptyString, nullableInteger, nullableIso, nullableNumber, nullableString, object, stringArray } from './shared.js';
3
+ import { PROBE_STATES, PROVIDERS, ROUTE_TARGET_IDS, booleanValue, enumValue, escapePath, exact, iso, literal, nonEmptyString, nullableInteger, nullableIso, nullableNumber, nullableString, object, stringArray } from './shared.js';
4
4
  export function validateManagement(value, path, issues) {
5
5
  const row = object(value, path, issues);
6
6
  if (!row)
@@ -225,7 +225,7 @@ function validateRouteTarget(value, path, issues) {
225
225
  if (!row)
226
226
  return;
227
227
  exact(row, path, ['provider_id', 'upstream_model'], issues);
228
- enumValue(row.provider_id, new Set(PROVIDERS), `${path}.provider_id`, issues);
228
+ enumValue(row.provider_id, new Set(ROUTE_TARGET_IDS), `${path}.provider_id`, issues);
229
229
  nonEmptyString(row.upstream_model, `${path}.upstream_model`, issues);
230
230
  }
231
231
  export function validateReadiness(value, path, issues) {
@@ -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 command = await resolveLaunchCommand(options);
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', 'desktop_bridge_sks_executable_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, 'bridge', 'serve', '--settings', paths.settings_path, '--json', '--supervised'], stdoutPath: paths.stdout_log_path, stderrPath: paths.stderr_log_path });
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('--supervised');
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 {
@@ -697,9 +699,30 @@ function normalizeProviderRegistrySnapshot(value) {
697
699
  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
700
  function launchDomain(uid = typeof process.getuid === 'function' ? process.getuid() : 0) { return `gui/${uid}`; }
699
701
  function launchService(uid) { return `${launchDomain(uid)}/${DESKTOP_BRIDGE_LAUNCHD_LABEL}`; }
700
- async function resolveLaunchCommand(options) { if (options.executablePath)
701
- return await exists(path.resolve(options.executablePath)) ? { executable: path.resolve(options.executablePath), arguments: [...(options.executableArguments || [])] } : null; const entry = String(process.argv[1] || ''); if (entry && ['sks', 'sneakoscope'].includes(path.basename(entry).replace(/\.js$/i, '')) && await exists(entry))
702
- return { executable: path.resolve(process.execPath), arguments: [path.resolve(entry)] }; const sks = await which('sks').catch(() => null); return sks ? { executable: path.resolve(sks), arguments: [] } : null; }
702
+ async function resolveLaunchCommand(options, home) {
703
+ if (options.executablePath) {
704
+ return await exists(path.resolve(options.executablePath))
705
+ ? { command: { executable: path.resolve(options.executablePath), arguments: [...(options.executableArguments || [])] }, blocker: 'desktop_bridge_sks_executable_missing' }
706
+ : { command: null, blocker: 'desktop_bridge_sks_executable_missing' };
707
+ }
708
+ const candidates = [];
709
+ const entry = String(process.argv[1] || '');
710
+ if (entry && ['sks', 'sneakoscope'].includes(path.basename(entry).replace(/\.js$/i, '')) && await exists(entry)) {
711
+ candidates.push({ executable: path.resolve(process.execPath), arguments: [path.resolve(entry)] });
712
+ }
713
+ const sks = await which('sks').catch(() => null);
714
+ if (sks)
715
+ candidates.push({ executable: path.resolve(sks), arguments: [] });
716
+ let rejectedProtected = false;
717
+ for (const candidate of candidates) {
718
+ if (await launchTargetsProtectedFolder([candidate.executable, ...candidate.arguments], home)) {
719
+ rejectedProtected = true;
720
+ continue;
721
+ }
722
+ return { command: candidate, blocker: 'desktop_bridge_sks_executable_missing' };
723
+ }
724
+ return { command: null, blocker: rejectedProtected ? 'desktop_bridge_entry_macos_protected_folder' : 'desktop_bridge_sks_executable_missing' };
725
+ }
703
726
  function macosProtectedUserPath(target, home) {
704
727
  if (!target)
705
728
  return false;
@@ -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
- export async function runDesktopBridgeRestageStage() {
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
- if (process.platform !== 'darwin')
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 (process.env.NODE_TEST_CONTEXT !== undefined)
13
+ if (reachesRealLaunchd && (env.NODE_TEST_CONTEXT !== undefined || env.SKS_TEST_ISOLATION === '1')) {
9
14
  return skip('desktop_bridge_restage_skipped_under_tests');
10
- if (process.env.SKS_SKIP_BRIDGE_RESTAGE === '1')
15
+ }
16
+ if (env.SKS_SKIP_BRIDGE_RESTAGE === '1')
11
17
  return skip('desktop_bridge_restage_disabled');
12
- const home = os.homedir();
13
- const plist = path.join(home, 'Library', 'LaunchAgents', 'com.sneakoscope.desktop-bridge.plist');
14
- if (!(await exists(plist)))
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(path.join(home, '.codex', 'sks', 'desktop-bridge-state.json'), null);
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
- return skip('desktop_bridge_restage_no_running_bridge');
21
- try {
22
- process.kill(pid, 0);
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
- catch {
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 = typeof process.getuid === 'function' ? process.getuid() : null;
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'}:${PACKAGE_VERSION}`],
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,7 +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(/the \d+\.\d+\.\d+ to \d+\.\d+\.\d+ upgrade smoke/g, `the ${RELEASE_UPGRADE_BASELINE_VERSION} 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`)
366
366
  .replace(/under the \d+\.\d+\.\d+ release evidence root/g, `under the ${version} release evidence root`)
367
367
  .replace(/^Do not cut \d+\.\d+\.\d+ while/m, `Do not cut ${version} while`)
368
368
  .replace(/must agree on \d+\.\d+\.\d+\./g, `must agree on ${version}.`)
@@ -376,6 +376,13 @@ async function syncSourcePackageVersion(root, version) {
376
376
  {
377
377
  rel: 'docs/release-proof-truth.md',
378
378
  replace: (text) => text
379
+ .replace(/^# Release Proof Truth — \d+\.\d+\.\d+\s*$/m, `# Release Proof Truth — ${version}`)
380
+ .replace(/^(## Current assertion\n\n)\d+\.\d+\.\d+ is \*\*SOURCE TAG CONDITIONAL/m, `$1${version} is **SOURCE TAG CONDITIONAL`)
381
+ .replace(/^New \d+\.\d+\.\d+ claims:/m, `New ${version} claims:`)
382
+ .replace(/treated as \d+\.\d+\.\d+ evidence\./, `treated as ${version} evidence.`)
383
+ .replace(/All checked version authorities report \d+\.\d+\.\d+/, `All checked version authorities report ${version}`)
384
+ .replace(/The reported \d+\.\d+\.\d+ package is ready to publish/, `The reported ${version} package is ready to publish`)
385
+ .replace(/`release:version-truth` 15 surfaces at \d+\.\d+\.\d+/, `\`release:version-truth\` 15 surfaces at ${version}`)
379
386
  .replace(/^SKS \d+\.\d+\.\d+ release proof truth/m, `SKS ${version} release proof truth`)
380
387
  .replace(/^SKS \d+\.\d+\.\d+ must not claim/m, `SKS ${version} must not claim`)
381
388
  .replace(/; \d+\.\d+\.\d+ proof must additionally show/g, `; ${version} proof must additionally show`)
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = '9.2.2';
1
+ export const PACKAGE_VERSION = '9.2.4';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "9.2.2",
4
+ "version": "9.2.4",
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",
@@ -204,7 +204,10 @@
204
204
  "additionalProperties": false,
205
205
  "required": ["provider_id", "upstream_model"],
206
206
  "properties": {
207
- "provider_id": { "enum": ["codex-lb", "openrouter"] },
207
+ "provider_id": {
208
+ "enum": ["codex-lb", "openrouter", "openai"],
209
+ "description": "Registry providers plus the official ChatGPT identity route. openai is the canonical official id (OpenCodex OPENAI_CODEX_PROVIDER_ID), not a provider profile."
210
+ },
208
211
  "upstream_model": { "$ref": "#/$defs/nonEmptyString" }
209
212
  }
210
213
  },