sneakoscope 8.0.0 → 8.0.2

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.
Files changed (110) hide show
  1. package/README.md +13 -2
  2. package/config/skills-hash-ledger.v1.json +63 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/bin/fast-inline.js +14 -3
  6. package/dist/bin/sks-dispatch.js +1 -1
  7. package/dist/cli/codex-lb-setup-warning-output.js +17 -0
  8. package/dist/cli/command-manifest-lite.js +2 -13
  9. package/dist/cli/install-helpers-codex-lb-config.js +475 -8
  10. package/dist/cli/install-helpers-install-support.js +1 -1
  11. package/dist/cli/install-helpers.js +764 -68
  12. package/dist/cli/router.js +5 -2
  13. package/dist/commands/codex-lb.js +77 -47
  14. package/dist/commands/doctor-profile.js +1 -0
  15. package/dist/commands/doctor.js +165 -58
  16. package/dist/config/skills-manifest.json +193 -97
  17. package/dist/core/codex/codex-config-guard.js +70 -16
  18. package/dist/core/codex/oauth-callback-port-diagnostic.js +121 -0
  19. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -2
  20. package/dist/core/codex-app/menubar/constants.js +3 -0
  21. package/dist/core/codex-app/menubar/installer.js +32 -9
  22. package/dist/core/codex-app/menubar/launch-agent.js +58 -6
  23. package/dist/core/codex-app/menubar/migration.js +17 -6
  24. package/dist/core/codex-app/menubar/status.js +15 -3
  25. package/dist/core/codex-app.js +15 -20
  26. package/dist/core/codex-control/codex-lb-launch-recovery.js +26 -0
  27. package/dist/core/codex-lb/capability-runner.js +1 -0
  28. package/dist/core/codex-lb/codex-lb-env.js +52 -41
  29. package/dist/core/codex-lb/codex-lb-setup.js +85 -8
  30. package/dist/core/codex-lb/desktop-center-credentials.js +474 -39
  31. package/dist/core/codex-lb/desktop-controller.js +118 -4
  32. package/dist/core/codex-lb/legacy-migration.js +6 -3
  33. package/dist/core/codex-native/codex-native-feature-broker.js +5 -1
  34. package/dist/core/codex-native/core-skill-manifest.js +1 -1
  35. package/dist/core/codex-native/managed-skill-generation-lock.js +13 -0
  36. package/dist/core/codex-native/sks-skill-paths.js +97 -10
  37. package/dist/core/codex-runtime/codex-desktop-config-policy.js +1 -0
  38. package/dist/core/commands/basic-cli.js +54 -4
  39. package/dist/core/commands/mad-sks-command.js +1 -1
  40. package/dist/core/commands/menubar-command.js +24 -8
  41. package/dist/core/commands/naruto-command.js +42 -217
  42. package/dist/core/doctor/codex-config-syntax-repair.js +368 -0
  43. package/dist/core/doctor/doctor-native-capability-repair.js +12 -5
  44. package/dist/core/doctor/doctor-readiness-matrix.js +51 -4
  45. package/dist/core/doctor/skill-legacy-surface.js +0 -1
  46. package/dist/core/doctor/sks-menubar-doctor.js +21 -0
  47. package/dist/core/fsx.js +7 -14
  48. package/dist/core/hooks-runtime/hook-context.js +30 -16
  49. package/dist/core/hooks-runtime/managed-skill-admission.js +17 -0
  50. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +891 -30
  51. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +99 -6
  52. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +3 -1
  53. package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +1 -1
  54. package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +4 -4
  55. package/dist/core/hooks-runtime/subagent-skill-availability.js +9 -7
  56. package/dist/core/hooks-runtime/subagent-stop-hook.js +19 -0
  57. package/dist/core/hooks-runtime.js +41 -14
  58. package/dist/core/init/skills.js +270 -11
  59. package/dist/core/mission.js +20 -0
  60. package/dist/core/naruto/hardware-capacity-probe.js +3 -1
  61. package/dist/core/naruto/naruto-concurrency-governor.js +9 -3
  62. package/dist/core/pipeline-internals/runtime-core.js +68 -9
  63. package/dist/core/pipeline-internals/runtime-gates.js +10 -2
  64. package/dist/core/preflight/parallel-preflight-engine.js +18 -4
  65. package/dist/core/release/package-dist-snapshot.js +50 -8
  66. package/dist/core/routes.js +1 -1
  67. package/dist/core/safety/command-contract/contracts.js +10 -13
  68. package/dist/core/safety/command-contract/json-schema.js +38 -1
  69. package/dist/core/subagents/agent-catalog.js +37 -13
  70. package/dist/core/subagents/model-policy.js +14 -9
  71. package/dist/core/subagents/naruto-command-args.js +237 -0
  72. package/dist/core/subagents/naruto-command-input-contract.js +123 -0
  73. package/dist/core/subagents/naruto-help-contract.js +35 -2
  74. package/dist/core/subagents/naruto-launch-diagnostics.js +35 -0
  75. package/dist/core/subagents/official-subagent-config.js +30 -3
  76. package/dist/core/subagents/official-subagent-preparation.js +30 -5
  77. package/dist/core/subagents/official-subagent-prompt.js +4 -3
  78. package/dist/core/subagents/official-subagent-runner.js +12 -0
  79. package/dist/core/subagents/subagent-evidence.js +10 -0
  80. package/dist/core/subagents/thread-budget.js +1 -1
  81. package/dist/core/subagents/wave-lifecycle.js +41 -11
  82. package/dist/core/subagents/wave-parent-guidance.js +10 -3
  83. package/dist/core/update/update-migration-state.js +694 -63
  84. package/dist/core/update/update-operation.js +498 -32
  85. package/dist/core/update-check.js +990 -509
  86. package/dist/core/version.js +1 -1
  87. package/dist/native/sks-menubar/Sources/AppDelegate.swift +1 -0
  88. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -1
  89. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +1 -1
  90. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +127 -12
  91. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +7 -2
  92. package/dist/native/sks-menubar/Sources/ProcessClient.swift +287 -84
  93. package/dist/native/sks-menubar/Sources/ProcessExecutionState.swift +45 -0
  94. package/dist/native/sks-menubar/Sources/ProcessIdentityGuard.swift +196 -0
  95. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +169 -0
  96. package/dist/native/sks-menubar/Sources/StatusItemController.swift +23 -5
  97. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +113 -24
  98. package/dist/native/sks-menubar/Sources/main.swift +12 -0
  99. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +14 -0
  100. package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +174 -0
  101. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +4 -0
  102. package/dist/scripts/current-surface-update-e2e-check.js +24 -5
  103. package/dist/scripts/lib/ensure-dist-fresh.js +12 -8
  104. package/dist/scripts/mutation-callsite-analysis.js +381 -0
  105. package/dist/scripts/mutation-callsite-coverage-check.js +127 -62
  106. package/dist/scripts/official-subagent-workflow-check.js +1 -1
  107. package/dist/scripts/skills-manifest-continuity-check.js +19 -1
  108. package/package.json +5 -3
  109. package/runtime-required-scripts.json +5 -0
  110. package/scripts/build-clean-atomic.mjs +323 -0
@@ -1,6 +1,9 @@
1
1
  import path from 'node:path';
2
2
  import os from 'node:os';
3
- import { ensureDir, readText } from '../core/fsx.js';
3
+ import { constants as fsConstants } from 'node:fs';
4
+ import fsp from 'node:fs/promises';
5
+ import { randomBytes } from 'node:crypto';
6
+ import { assertTestHomeWriteAllowed, ensureDir, readText, writeBinaryAtomic } from '../core/fsx.js';
4
7
  import { GLM_CODEX_CONFIG_PROVIDER_ID, GLM_52_OPENROUTER_MODEL, OPENROUTER_AUTH_COMMAND, OPENROUTER_AUTH_REFRESH_INTERVAL_MS, OPENROUTER_AUTH_TIMEOUT_MS, openRouterAuthCommandArgs, OPENROUTER_DEFAULT_PROFILE_ID, RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS } from '../core/codex-app/openrouter-provider.js';
5
8
  import { openRouterSecretPaths, resolveOpenRouterApiKey } from '../core/providers/openrouter/openrouter-secret-store.js';
6
9
  import { reconcileRetiredSksConfigText } from '../core/auto-review.js';
@@ -290,17 +293,481 @@ export function detectCodexLbSetupDrift(state = {}) {
290
293
  drift.push('shell_profile_changed_despite_skip');
291
294
  return drift;
292
295
  }
293
- export async function captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile } = {}) {
296
+ export async function captureCodexLbSetupWriteState({ home, configPath, envPath, metadataPath, shellProfile } = {}) {
294
297
  const profileFiles = profileFilesForDrift(home, shellProfile);
298
+ const paths = [configPath, envPath, metadataPath, ...profileFiles].filter(Boolean);
299
+ const files = await Promise.all(paths.map(captureSetupFile));
300
+ const hashesByPath = new Map(await Promise.all(files.map(async (file) => [file.path, await capturedSetupFileHash(file)])));
301
+ const hashForPath = (file) => file ? hashesByPath.get(file) || 'missing' : 'missing';
295
302
  return {
296
- configHash: await fileHashOrMissing(configPath),
297
- envHash: await fileHashOrMissing(envPath),
298
- profileHash: (await Promise.all(profileFiles.map((file) => fileHashOrMissing(file)))).join('|')
303
+ configHash: hashForPath(configPath),
304
+ envHash: hashForPath(envPath),
305
+ metadataHash: hashForPath(metadataPath),
306
+ profileHash: profileFiles.map(hashForPath).join('|'),
307
+ files,
308
+ stateHash: await sha256Text(JSON.stringify(files))
299
309
  };
300
310
  }
301
- async function fileHashOrMissing(file) {
302
- const text = await readText(file, null).catch(() => null);
303
- return text === null ? 'missing' : await sha256Text(String(text));
311
+ export async function writeCodexLbSetupFileIfUnchanged(input) {
312
+ const file = path.resolve(input.file);
313
+ if (path.resolve(String(input.expected.path || '')) !== file) {
314
+ return {
315
+ ok: false,
316
+ status: 'write_failed',
317
+ installed: false,
318
+ expected_after: {
319
+ path: file,
320
+ existed: true,
321
+ kind: 'regular',
322
+ bytes_base64: Buffer.from(input.text).toString('base64'),
323
+ mode: Number(input.mode) & 0o777
324
+ },
325
+ error: 'setup_snapshot_path_mismatch'
326
+ };
327
+ }
328
+ const expected = { ...input.expected, path: file };
329
+ const mode = Number(input.mode) & 0o777;
330
+ const expectedAfter = {
331
+ path: file,
332
+ existed: true,
333
+ kind: 'regular',
334
+ bytes_base64: Buffer.from(input.text).toString('base64'),
335
+ mode
336
+ };
337
+ if (expected.existed === true && expected.kind !== 'regular') {
338
+ return {
339
+ ok: false,
340
+ status: 'unsafe_setup_write_target',
341
+ installed: false,
342
+ expected_after: expectedAfter
343
+ };
344
+ }
345
+ assertTestHomeWriteAllowed(file);
346
+ await ensureDir(path.dirname(file));
347
+ const token = `${Date.now().toString(36)}-${process.pid}-${randomBytes(6).toString('hex')}`;
348
+ const claimedPath = `${file}.sks-setup-claimed-${token}`;
349
+ const candidatePath = `${file}.sks-setup-candidate-${token}`;
350
+ let claimed = false;
351
+ let installed = false;
352
+ try {
353
+ await writeBinaryAtomic(candidatePath, Buffer.from(input.text), { mode });
354
+ const observed = await captureSetupFile(file);
355
+ if (!setupFileStatesEqual(observed, expected)) {
356
+ return {
357
+ ok: false,
358
+ status: 'concurrent_change_detected',
359
+ installed: false,
360
+ expected_after: expectedAfter
361
+ };
362
+ }
363
+ if (setupFileStatesEqual(expected, expectedAfter)) {
364
+ return {
365
+ ok: true,
366
+ status: 'present',
367
+ installed: false,
368
+ expected_after: expectedAfter
369
+ };
370
+ }
371
+ if (expected.existed === true) {
372
+ try {
373
+ await fsp.rename(file, claimedPath);
374
+ claimed = true;
375
+ }
376
+ catch {
377
+ return {
378
+ ok: false,
379
+ status: 'concurrent_change_detected',
380
+ installed: false,
381
+ expected_after: expectedAfter
382
+ };
383
+ }
384
+ const claimedState = await captureSetupFile(claimedPath);
385
+ if (!setupFileStatesEqual(claimedState, { ...expected, path: claimedPath })) {
386
+ const recovered = await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState);
387
+ claimed = !recovered;
388
+ const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
389
+ return {
390
+ ok: false,
391
+ status: hardened ? 'concurrent_change_detected' : 'write_failed',
392
+ installed: false,
393
+ expected_after: expectedAfter,
394
+ ...(claimed ? { recovery_path: claimedPath } : {}),
395
+ ...(hardened ? {} : { error: 'setup_recovery_mode_hardening_failed' })
396
+ };
397
+ }
398
+ }
399
+ await input.beforeReplacement?.({ path: file });
400
+ try {
401
+ await fsp.link(candidatePath, file);
402
+ installed = true;
403
+ }
404
+ catch {
405
+ if (claimed) {
406
+ const claimedState = await captureSetupFile(claimedPath).catch(() => null);
407
+ const recovered = claimedState
408
+ ? await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState)
409
+ : false;
410
+ claimed = !recovered;
411
+ }
412
+ const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
413
+ return {
414
+ ok: false,
415
+ status: hardened ? 'concurrent_change_detected' : 'write_failed',
416
+ installed: false,
417
+ expected_after: expectedAfter,
418
+ ...(claimed ? { recovery_path: claimedPath } : {}),
419
+ ...(hardened ? {} : { error: 'setup_recovery_mode_hardening_failed' })
420
+ };
421
+ }
422
+ const committed = await captureSetupFile(file);
423
+ if (!setupFileStatesEqual(committed, expectedAfter)) {
424
+ const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
425
+ return {
426
+ ok: false,
427
+ status: hardened ? 'concurrent_change_detected' : 'write_failed',
428
+ installed: true,
429
+ expected_after: expectedAfter,
430
+ ...(claimed ? { recovery_path: claimedPath } : {}),
431
+ ...(hardened ? {} : { error: 'setup_recovery_mode_hardening_failed' })
432
+ };
433
+ }
434
+ if (claimed && !await hardenCodexLbSetupRecoveryPath(claimedPath)) {
435
+ return {
436
+ ok: false,
437
+ status: 'write_failed',
438
+ installed: true,
439
+ expected_after: expectedAfter,
440
+ recovery_path: claimedPath,
441
+ error: 'setup_recovery_mode_hardening_failed'
442
+ };
443
+ }
444
+ return {
445
+ ok: true,
446
+ status: 'written',
447
+ installed: true,
448
+ expected_after: expectedAfter,
449
+ ...(claimed ? { recovery_path: claimedPath } : {})
450
+ };
451
+ }
452
+ catch (error) {
453
+ if (claimed && !installed) {
454
+ const claimedState = await captureSetupFile(claimedPath).catch(() => null);
455
+ const recovered = claimedState
456
+ ? await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState)
457
+ : false;
458
+ claimed = !recovered;
459
+ }
460
+ const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
461
+ return {
462
+ ok: false,
463
+ status: 'write_failed',
464
+ installed,
465
+ expected_after: expectedAfter,
466
+ ...(claimed ? { recovery_path: claimedPath } : {}),
467
+ error: [
468
+ error instanceof Error ? error.message : String(error),
469
+ ...(hardened ? [] : ['setup_recovery_mode_hardening_failed'])
470
+ ].join(':')
471
+ };
472
+ }
473
+ finally {
474
+ await fsp.rm(candidatePath, { force: true }).catch(() => undefined);
475
+ }
476
+ }
477
+ export async function hardenCodexLbSetupRecoveryPath(file) {
478
+ let handle = null;
479
+ try {
480
+ handle = await fsp.open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
481
+ const before = await handle.stat();
482
+ if (!before.isFile())
483
+ return false;
484
+ await handle.chmod(0o600);
485
+ const hardened = await handle.stat();
486
+ const pathStat = await fsp.lstat(file);
487
+ return hardened.isFile()
488
+ && (hardened.mode & 0o777) === 0o600
489
+ && pathStat.isFile()
490
+ && !pathStat.isSymbolicLink()
491
+ && pathStat.dev === hardened.dev
492
+ && pathStat.ino === hardened.ino;
493
+ }
494
+ catch {
495
+ return false;
496
+ }
497
+ finally {
498
+ await handle?.close().catch(() => undefined);
499
+ }
500
+ }
501
+ export async function removeCodexLbSetupRecoveryPath(file) {
502
+ if (!path.basename(file).includes('.sks-setup-claimed-'))
503
+ return false;
504
+ let handle = null;
505
+ try {
506
+ handle = await fsp.open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
507
+ const opened = await handle.stat();
508
+ const pathStat = await fsp.lstat(file);
509
+ if (!opened.isFile()
510
+ || !pathStat.isFile()
511
+ || pathStat.isSymbolicLink()
512
+ || pathStat.dev !== opened.dev
513
+ || pathStat.ino !== opened.ino)
514
+ return false;
515
+ await fsp.unlink(file);
516
+ return await fsp.lstat(file).then(() => false, (error) => error.code === 'ENOENT');
517
+ }
518
+ catch {
519
+ return false;
520
+ }
521
+ finally {
522
+ await handle?.close().catch(() => undefined);
523
+ }
524
+ }
525
+ export async function restoreCodexLbSetupWriteState(state, expectedCurrentState, opts = {}) {
526
+ const restored = [];
527
+ const removed = [];
528
+ const blockers = [];
529
+ const recovery = [];
530
+ const expectedByPath = new Map((Array.isArray(expectedCurrentState?.files) ? expectedCurrentState.files : [])
531
+ .map((entry) => [String(entry?.path || ''), entry]));
532
+ for (const entry of Array.isArray(state?.files) ? [...state.files].reverse() : []) {
533
+ const file = String(entry?.path || '');
534
+ if (!file)
535
+ continue;
536
+ try {
537
+ const expected = expectedByPath.get(file);
538
+ if (!expected) {
539
+ blockers.push(`setup_rollback_missing_expected_state:${file}`);
540
+ continue;
541
+ }
542
+ if (expected && setupFileStatesEqual(entry, expected))
543
+ continue;
544
+ const commit = await commitSetupRollbackIfUnchanged({
545
+ file,
546
+ before: entry,
547
+ expected,
548
+ ...(opts.beforeReplacement ? { beforeReplacement: opts.beforeReplacement } : {})
549
+ });
550
+ if (commit.recoveryPath)
551
+ recovery.push(commit.recoveryPath);
552
+ if (!commit.ok) {
553
+ blockers.push(`${commit.failed ? 'setup_rollback_failed' : 'setup_rollback_conflict'}:${file}`);
554
+ continue;
555
+ }
556
+ if (commit.restored)
557
+ restored.push(file);
558
+ if (commit.removed)
559
+ removed.push(file);
560
+ }
561
+ catch {
562
+ blockers.push(`setup_rollback_failed:${file}`);
563
+ }
564
+ }
565
+ return { ok: blockers.length === 0, restored, removed, blockers, recovery };
566
+ }
567
+ function setupFileStatesEqual(left, right) {
568
+ return left?.existed === right?.existed
569
+ && String(left?.kind || (left?.existed ? 'regular' : 'missing')) === String(right?.kind || (right?.existed ? 'regular' : 'missing'))
570
+ && String(left?.bytes_base64 || '') === String(right?.bytes_base64 || '')
571
+ && (left?.existed !== true || Number(left?.mode) === Number(right?.mode));
572
+ }
573
+ async function captureSetupFile(file) {
574
+ let pathStat;
575
+ try {
576
+ pathStat = await fsp.lstat(file);
577
+ }
578
+ catch (error) {
579
+ if (error?.code === 'ENOENT') {
580
+ return { path: file, existed: false, kind: 'missing', bytes_base64: '', mode: null };
581
+ }
582
+ throw error;
583
+ }
584
+ if (pathStat.isSymbolicLink()) {
585
+ return { path: file, existed: true, kind: 'symlink', bytes_base64: '', mode: pathStat.mode & 0o777 };
586
+ }
587
+ if (!pathStat.isFile()) {
588
+ return { path: file, existed: true, kind: 'non_regular', bytes_base64: '', mode: pathStat.mode & 0o777 };
589
+ }
590
+ let handle = null;
591
+ try {
592
+ handle = await fsp.open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
593
+ const stat = await handle.stat();
594
+ if (!stat.isFile()) {
595
+ return { path: file, existed: true, kind: 'non_regular', bytes_base64: '', mode: stat.mode & 0o777 };
596
+ }
597
+ const bytes = await handle.readFile();
598
+ return {
599
+ path: file,
600
+ existed: true,
601
+ kind: 'regular',
602
+ bytes_base64: bytes.toString('base64'),
603
+ mode: stat.mode & 0o777
604
+ };
605
+ }
606
+ catch (error) {
607
+ if (error?.code === 'ENOENT') {
608
+ return { path: file, existed: false, kind: 'missing', bytes_base64: '', mode: null };
609
+ }
610
+ if (error?.code === 'ELOOP') {
611
+ const stat = await fsp.lstat(file).catch(() => null);
612
+ return {
613
+ path: file,
614
+ existed: true,
615
+ kind: 'symlink',
616
+ bytes_base64: '',
617
+ mode: stat ? stat.mode & 0o777 : null
618
+ };
619
+ }
620
+ throw error;
621
+ }
622
+ finally {
623
+ await handle?.close().catch(() => undefined);
624
+ }
625
+ }
626
+ async function commitSetupRollbackIfUnchanged(input) {
627
+ const { file, before, expected } = input;
628
+ await ensureDir(path.dirname(file));
629
+ const token = `${Date.now().toString(36)}-${process.pid}-${randomBytes(6).toString('hex')}`;
630
+ const claimedPath = `${file}.sks-rollback-claimed-${token}`;
631
+ const candidatePath = `${file}.sks-rollback-candidate-${token}`;
632
+ let claimed = false;
633
+ try {
634
+ if (expected.existed === true) {
635
+ const currentStat = await fsp.lstat(file).catch((error) => {
636
+ if (error.code === 'ENOENT')
637
+ return null;
638
+ throw error;
639
+ });
640
+ if (!currentStat || currentStat.isSymbolicLink() || !currentStat.isFile()) {
641
+ return { ok: false, restored: false, removed: false };
642
+ }
643
+ try {
644
+ await fsp.rename(file, claimedPath);
645
+ claimed = true;
646
+ }
647
+ catch {
648
+ return { ok: false, restored: false, removed: false };
649
+ }
650
+ const claimedState = await captureSetupFile(claimedPath);
651
+ if (!setupFileStatesEqual(claimedState, { ...expected, path: claimedPath })) {
652
+ const recovered = await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState);
653
+ claimed = !recovered;
654
+ return {
655
+ ok: false,
656
+ restored: false,
657
+ removed: false,
658
+ ...(claimed ? { recoveryPath: claimedPath } : {})
659
+ };
660
+ }
661
+ }
662
+ else {
663
+ const current = await captureSetupFile(file);
664
+ if (!setupFileStatesEqual(current, expected)) {
665
+ return { ok: false, restored: false, removed: false };
666
+ }
667
+ }
668
+ await input.beforeReplacement?.({ path: file });
669
+ if (before.existed === true) {
670
+ if (String(before.kind || 'regular') !== 'regular') {
671
+ if (claimed) {
672
+ const claimedState = await captureSetupFile(claimedPath);
673
+ const recovered = await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState);
674
+ claimed = !recovered;
675
+ }
676
+ return {
677
+ ok: false,
678
+ restored: false,
679
+ removed: false,
680
+ failed: true,
681
+ ...(claimed ? { recoveryPath: claimedPath } : {})
682
+ };
683
+ }
684
+ const bytes = Buffer.from(String(before.bytes_base64 || ''), 'base64');
685
+ await writeBinaryAtomic(candidatePath, bytes, { mode: Number(before.mode || 0o600) });
686
+ try {
687
+ await fsp.link(candidatePath, file);
688
+ }
689
+ catch {
690
+ return {
691
+ ok: false,
692
+ restored: false,
693
+ removed: false,
694
+ ...(claimed ? { recoveryPath: claimedPath } : {})
695
+ };
696
+ }
697
+ const committed = await captureSetupFile(file);
698
+ if (!setupFileStatesEqual(committed, before)) {
699
+ return {
700
+ ok: false,
701
+ restored: false,
702
+ removed: false,
703
+ ...(claimed ? { recoveryPath: claimedPath } : {})
704
+ };
705
+ }
706
+ return {
707
+ ok: true,
708
+ restored: true,
709
+ removed: false,
710
+ ...(claimed ? { recoveryPath: claimedPath } : {})
711
+ };
712
+ }
713
+ const committed = await captureSetupFile(file);
714
+ if (!setupFileStatesEqual(committed, before)) {
715
+ return {
716
+ ok: false,
717
+ restored: false,
718
+ removed: false,
719
+ ...(claimed ? { recoveryPath: claimedPath } : {})
720
+ };
721
+ }
722
+ return {
723
+ ok: true,
724
+ restored: false,
725
+ removed: true,
726
+ ...(claimed ? { recoveryPath: claimedPath } : {})
727
+ };
728
+ }
729
+ catch {
730
+ if (claimed) {
731
+ const claimedState = await captureSetupFile(claimedPath).catch(() => null);
732
+ const recovered = claimedState
733
+ ? await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState)
734
+ : false;
735
+ claimed = !recovered;
736
+ }
737
+ return {
738
+ ok: false,
739
+ restored: false,
740
+ removed: false,
741
+ failed: true,
742
+ ...(claimed ? { recoveryPath: claimedPath } : {})
743
+ };
744
+ }
745
+ finally {
746
+ await fsp.rm(candidatePath, { force: true }).catch(() => undefined);
747
+ }
748
+ }
749
+ async function restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState) {
750
+ try {
751
+ if (claimedState?.kind === 'regular') {
752
+ await fsp.link(claimedPath, file);
753
+ }
754
+ else if (claimedState?.kind === 'symlink') {
755
+ await fsp.symlink(await fsp.readlink(claimedPath), file);
756
+ }
757
+ else {
758
+ return false;
759
+ }
760
+ await fsp.unlink(claimedPath);
761
+ return true;
762
+ }
763
+ catch {
764
+ return false;
765
+ }
766
+ }
767
+ async function capturedSetupFileHash(file) {
768
+ if (file.kind !== 'regular')
769
+ return 'missing';
770
+ return await sha256Text(Buffer.from(file.bytes_base64, 'base64').toString('utf8'));
304
771
  }
305
772
  function profileFilesForDrift(home, shellProfile) {
306
773
  const targets = {
@@ -15,7 +15,7 @@ import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
15
15
  import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
16
16
  import { cleanupMacLaunchSecretEnvironment } from '../core/codex-app/menubar/index.js';
17
17
  import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
18
- import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
18
+ import { loadCodexLbEnv, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
19
19
  import { codexLbToolCatalogPath, ensureCodexLbToolCatalog } from '../core/codex-lb/codex-lb-tool-catalog.js';
20
20
  import { codexLbToolOutputRecoveryNotChecked, codexLbToolOutputRecoveryNotSelected, codexLbToolOutputRecoveryOverrideAcknowledged, probeCodexLbToolOutputRecovery } from '../core/codex-lb/codex-lb-tool-output-recovery.js';
21
21
  import { GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES, GLM_52_OPENROUTER_MODEL } from '../core/codex-app/openrouter-provider.js';