sneakoscope 6.4.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +26 -34
  2. package/config/codex-releases/{rust-v0.144.1.json → rust-v0.144.5.json} +5 -5
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/bin/install.js +1 -1
  6. package/dist/cli/command-manifest-lite.js +2 -3
  7. package/dist/cli/command-registry.js +6 -4
  8. package/dist/cli/help-fast.js +0 -1
  9. package/dist/cli/router.js +24 -3
  10. package/dist/commands/zellij.js +18 -29
  11. package/dist/config/skills-manifest.json +62 -62
  12. package/dist/core/agent-bridge/agent-manifest.js +22 -1
  13. package/dist/core/agents/agent-codex-cockpit.js +1 -81
  14. package/dist/core/agents/agent-effort-policy.js +13 -7
  15. package/dist/core/agents/agent-orchestrator.js +0 -6
  16. package/dist/core/agents/agent-output-validator.js +2 -1
  17. package/dist/core/agents/agent-patch-queue-store.js +0 -8
  18. package/dist/core/agents/agent-proof-evidence.js +0 -6
  19. package/dist/core/agents/agent-worker-pipeline.js +12 -6
  20. package/dist/core/agents/native-cli-worker-runtime.js +1 -16
  21. package/dist/core/agents/native-worker-backend-router.js +1 -1
  22. package/dist/core/bench.js +1 -1
  23. package/dist/core/codex/official-goal-mode.js +4 -2
  24. package/dist/core/codex-app/codex-app-fast-ui-repair.js +11 -17
  25. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +34 -6
  26. package/dist/core/codex-app/menubar/constants.js +0 -1
  27. package/dist/core/codex-app.js +1 -1
  28. package/dist/core/codex-compat/codex-release-manifest.js +5 -5
  29. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +1 -1
  30. package/dist/core/codex-control/codex-0139-web-search-probe.js +1 -1
  31. package/dist/core/codex-control/codex-control-proof.js +1 -0
  32. package/dist/core/codex-control/codex-fake-sdk-adapter.js +1 -1
  33. package/dist/core/codex-control/codex-lb-launch-recovery.js +7 -3
  34. package/dist/core/codex-control/codex-sdk-adapter.js +8 -7
  35. package/dist/core/codex-control/codex-sdk-config-policy.js +4 -24
  36. package/dist/core/codex-control/codex-sdk-env-policy.js +396 -11
  37. package/dist/core/codex-control/codex-task-runner.js +112 -57
  38. package/dist/core/codex-control/gpt-final-arbiter.js +1 -2
  39. package/dist/core/codex-control/python-codex-sdk-adapter.js +38 -15
  40. package/dist/core/codex-lb/codex-lb-tool-catalog.js +3 -3
  41. package/dist/core/codex-native/core-skill-manifest.js +4 -7
  42. package/dist/core/codex-runtime/resolve-codex-runtime.js +199 -0
  43. package/dist/core/commands/agent-bridge-command.js +46 -15
  44. package/dist/core/commands/basic-cli.js +1 -1
  45. package/dist/core/commands/command-utils.js +2 -2
  46. package/dist/core/commands/eval-command.js +19 -2
  47. package/dist/core/commands/goal-command.js +37 -86
  48. package/dist/core/commands/mad-sks-command.js +0 -2
  49. package/dist/core/commands/naruto-command.js +236 -176
  50. package/dist/core/doctor/current-project-guidance.js +1 -1
  51. package/dist/core/doctor/doctor-dirty-planner.js +1 -1
  52. package/dist/core/doctor/retired-managed-residue-missions.js +26 -11
  53. package/dist/core/doctor/retired-managed-residue-private.js +1 -1
  54. package/dist/core/doctor/retired-managed-residue-runtime.js +12 -2
  55. package/dist/core/doctor/retired-managed-residue-state.js +7 -7
  56. package/dist/core/evaluation.js +83 -21
  57. package/dist/core/feature-fixtures.js +4 -7
  58. package/dist/core/feature-registry.js +0 -1
  59. package/dist/core/fsx.js +169 -14
  60. package/dist/core/goal-workflow.js +71 -140
  61. package/dist/core/hooks-runtime/hook-io.js +2 -2
  62. package/dist/core/hooks-runtime.js +228 -50
  63. package/dist/core/init/skills.js +7 -6
  64. package/dist/core/init.js +52 -33
  65. package/dist/core/install/installed-package-smoke.js +1 -1
  66. package/dist/core/lean-engineering-policy.js +25 -23
  67. package/dist/core/locks/file-lock.js +74 -21
  68. package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
  69. package/dist/core/mcp-config/health-check.js +7 -2
  70. package/dist/core/mission.js +99 -3
  71. package/dist/core/ops/upgrade-migration-fixtures.js +30 -2
  72. package/dist/core/pipeline-internals/runtime-core.js +67 -95
  73. package/dist/core/pipeline-internals/runtime-gates.js +25 -6
  74. package/dist/core/proof/fake-real-proof-policy.js +32 -9
  75. package/dist/core/proof/route-proof-gate.js +49 -1
  76. package/dist/core/provider/model-router.js +2 -0
  77. package/dist/core/questions.js +31 -5
  78. package/dist/core/recallpulse.js +2 -2
  79. package/dist/core/release/canonical-test-proof.js +135 -0
  80. package/dist/core/release/release-closure-contract.js +1 -0
  81. package/dist/core/release/release-closure-validation.js +25 -12
  82. package/dist/core/release/release-closure.js +34 -5
  83. package/dist/core/release/release-gate-contract.js +4 -3
  84. package/dist/core/release/release-gate-dag.js +4 -2
  85. package/dist/core/release/release-pack-content-scanner.js +7 -6
  86. package/dist/core/release/release-pack-receipt.js +33 -12
  87. package/dist/core/release/release-real-contract.js +41 -2
  88. package/dist/core/retention.js +95 -16
  89. package/dist/core/routes/constants.js +1 -1
  90. package/dist/core/routes/dollar-manifest-lite.js +1 -1
  91. package/dist/core/routes.js +18 -20
  92. package/dist/core/safety/command-contract/contracts.js +33 -0
  93. package/dist/core/safety/command-contract/types.js +1 -1
  94. package/dist/core/safety/ssot-guard.js +1 -54
  95. package/dist/core/stop-gate/stop-gate-check.js +0 -14
  96. package/dist/core/stop-gate/stop-gate-writer.js +0 -4
  97. package/dist/core/strategy/adhd-orchestrating-gate.js +2 -2
  98. package/dist/core/subagents/agent-catalog.js +28 -5
  99. package/dist/core/subagents/model-policy.js +52 -11
  100. package/dist/core/subagents/naruto-help-contract.js +2 -1
  101. package/dist/core/subagents/naruto-proof-projection.js +449 -0
  102. package/dist/core/subagents/official-subagent-preparation.js +641 -51
  103. package/dist/core/subagents/official-subagent-prompt.js +16 -6
  104. package/dist/core/subagents/official-subagent-runner.js +132 -12
  105. package/dist/core/subagents/subagent-evidence.js +44 -8
  106. package/dist/core/subagents/wave-lifecycle.js +238 -0
  107. package/dist/core/update-check.js +13 -4
  108. package/dist/core/verification/machine-feedback.js +39 -19
  109. package/dist/core/version.js +1 -1
  110. package/dist/core/zellij/zellij-lane-renderer.js +19 -19
  111. package/dist/core/zellij/zellij-official-subagent-activity.js +2 -0
  112. package/dist/core/zellij/zellij-right-column-manager.js +6 -55
  113. package/dist/core/zellij/zellij-slot-column-anchor.js +4 -5
  114. package/dist/core/zellij/zellij-slot-pane-renderer.js +1 -1
  115. package/dist/core/zellij/zellij-ui-mode.js +0 -7
  116. package/dist/core/zellij/zellij-worker-pane-manager.js +3 -11
  117. package/dist/native/sks-menubar/Sources/AppDelegate.swift +0 -1
  118. package/dist/native/sks-menubar/Sources/NotificationCoordinator.swift +1 -3
  119. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +194 -24
  120. package/dist/native/sks-menubar/Sources/ProcessClient.swift +49 -4
  121. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +38 -10
  122. package/dist/native/sks-menubar/Sources/StatusItemController.swift +73 -11
  123. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +27 -2
  124. package/dist/scripts/architecture-guard-check.js +1 -9
  125. package/dist/scripts/canonical-test-runner.js +51 -8
  126. package/dist/scripts/check-architecture.js +11 -1
  127. package/dist/scripts/cli-output-consistency-check.js +0 -2
  128. package/dist/scripts/codex-0144-app-server-v2-check.js +4 -4
  129. package/dist/scripts/codex-0144-binary-identity-check.js +10 -4
  130. package/dist/scripts/codex-0144-capability-check.js +5 -5
  131. package/dist/scripts/codex-0144-policy-check.js +4 -0
  132. package/dist/scripts/codex-control-all-pipelines-check.js +2 -1
  133. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +3 -3
  134. package/dist/scripts/codex-sdk-all-pipelines-check.js +2 -1
  135. package/dist/scripts/codex-sdk-real-smoke-check.js +20 -2
  136. package/dist/scripts/current-command-surface-check.js +5 -3
  137. package/dist/scripts/current-surface-update-e2e-check.js +2 -2
  138. package/dist/scripts/docs-truthfulness-check.js +8 -6
  139. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +3 -2
  140. package/dist/scripts/gate-policy-audit-check.js +7 -1
  141. package/dist/scripts/harness-benchmark-check.js +4 -4
  142. package/dist/scripts/hook-latency-quantum-check.js +2 -2
  143. package/dist/scripts/legacy-gate-purge-check.js +4 -2
  144. package/dist/scripts/loop-directive-check-lib.js +14 -13
  145. package/dist/scripts/naruto-codex-e2e-check.js +2 -2
  146. package/dist/scripts/naruto-ssot-gate-aliases-check.js +9 -0
  147. package/dist/scripts/naruto-ssot-pipeline-default-check.js +10 -0
  148. package/dist/scripts/naruto-ssot-route-normalization-check.js +8 -0
  149. package/dist/scripts/naruto-ssot-routing-check.js +12 -0
  150. package/dist/scripts/product-design-plugin-routing-check.js +4 -9
  151. package/dist/scripts/release-affected-selector-check.js +7 -1
  152. package/dist/scripts/release-check-stamp.js +151 -18
  153. package/dist/scripts/release-dynamic-presets-check.js +5 -2
  154. package/dist/scripts/release-metadata-1-19-check.js +4 -0
  155. package/dist/scripts/release-real-check.js +21 -3
  156. package/dist/scripts/sks-menubar-install-check.js +5 -5
  157. package/dist/scripts/upgrade-migration-matrix-check.js +278 -5
  158. package/dist/scripts/zellij-developer-controls-check.js +3 -5
  159. package/dist/scripts/zellij-initial-main-only-blackbox.js +3 -8
  160. package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +1 -2
  161. package/dist/scripts/zellij-right-column-headless-overflow-check.js +1 -1
  162. package/dist/scripts/zellij-right-column-manager-check.js +3 -4
  163. package/dist/scripts/zellij-slot-only-ui-check.js +2 -2
  164. package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +1 -2
  165. package/docs/demo.tape +1 -1
  166. package/infra-harness-gates.json +0 -27
  167. package/package.json +11 -6
  168. package/release-gates.v2.json +150 -22
  169. package/schemas/codex/agent-result.schema.json +4 -0
  170. package/schemas/codex/app-server-0.144/codex_app_server_protocol.v2.schemas.json +1 -18812
  171. package/schemas/codex/codex-0139-real-probe-result.schema.json +2 -2
  172. package/schemas/zellij/zellij-right-column-state.schema.json +0 -1
  173. package/dist/core/commands/ui-command.js +0 -198
  174. package/dist/core/loops/goal-to-loop-compat.js +0 -22
  175. package/dist/core/naruto/naruto-allocation-policy.js +0 -98
  176. package/dist/core/naruto/naruto-finalizer.js +0 -22
  177. package/dist/core/naruto/naruto-gpt-final-pack.js +0 -50
  178. package/dist/core/naruto/naruto-real-worker-child.js +0 -209
  179. package/dist/core/naruto/naruto-real-worker-runtime.js +0 -178
  180. package/dist/core/naruto/naruto-rebalance-policy.js +0 -47
  181. package/dist/core/naruto/naruto-verification-pool.js +0 -17
  182. package/dist/core/ui/dashboard-html.js +0 -110
  183. package/dist/core/zellij/zellij-dashboard-pane.js +0 -70
  184. package/dist/core/zellij/zellij-dashboard-renderer.js +0 -59
  185. package/dist/core/zellij/zellij-naruto-dashboard.js +0 -44
  186. package/dist/scripts/zellij-dashboard-pane-check.js +0 -69
  187. package/dist/scripts/zellij-dashboard-watch.js +0 -40
@@ -1,3 +1,7 @@
1
+ import { constants as fsConstants } from 'node:fs';
2
+ import fsp from 'node:fs/promises';
3
+ import { createHash, randomBytes } from 'node:crypto';
4
+ import os from 'node:os';
1
5
  import path from 'node:path';
2
6
  const SECRET_RE = /(?:key|token|secret|password|credential|auth|cookie)/i;
3
7
  const BASE_ALLOWED_ENV = new Set([
@@ -40,14 +44,7 @@ export function buildCodexSdkEnv(input) {
40
44
  if (BASE_ALLOWED_ENV.has(key) || key.startsWith('LC_'))
41
45
  env[key] = String(value);
42
46
  }
43
- const codexLbApiKey = String(process.env.CODEX_LB_API_KEY || '').trim();
44
- const codexLbBaseUrl = String(process.env.CODEX_LB_BASE_URL || '').trim();
45
- const codexLbEnvInjected = process.env.SKS_CODEX_LB_AUTOBYPASS !== '1' && Boolean(codexLbApiKey && codexLbBaseUrl);
46
- if (codexLbEnvInjected) {
47
- env.CODEX_LB_API_KEY = codexLbApiKey;
48
- env.CODEX_LB_BASE_URL = codexLbBaseUrl;
49
- }
50
- env.SKS_CODEX_CONTROL_PLANE_CONFIG_ISOLATED = '1';
47
+ env.SKS_CODEX_CONTROL_PLANE_NATIVE = '1';
51
48
  const inheritedKeys = Object.keys(env).filter((key) => !key.startsWith('SKS_') && key !== 'HOME' && key !== 'CODEX_HOME').sort();
52
49
  const blockedHostKeys = Object.keys(process.env).filter((key) => !(key in env)).sort();
53
50
  return {
@@ -59,10 +56,15 @@ export function buildCodexSdkEnv(input) {
59
56
  blocked_host_env_key_count: blockedHostKeys.length,
60
57
  blocked_sensitive_host_env_key_count: blockedHostKeys.filter((key) => SECRET_RE.test(key)).length,
61
58
  redacted_sensitive_keys: Object.keys(env).filter((key) => SECRET_RE.test(key)).sort(),
62
- codex_lb_env_injected: codexLbEnvInjected,
63
- codex_lb_env_source: codexLbEnvInjected ? 'process.env' : null,
64
- codex_lb_api_key_redacted: codexLbEnvInjected,
59
+ native_codex_only: true,
60
+ model_provider: 'openai',
61
+ codex_lb_env_injected: false,
62
+ codex_lb_env_source: null,
63
+ codex_lb_api_key_redacted: false,
65
64
  codex_home_isolated: true,
65
+ home_isolated: true,
66
+ native_codex_auth_bridge_required: true,
67
+ native_codex_auth_source: 'host_codex_home/auth.json',
66
68
  codex_home: env.CODEX_HOME,
67
69
  home: env.HOME
68
70
  }
@@ -71,3 +73,386 @@ export function buildCodexSdkEnv(input) {
71
73
  export function redactCodexSdkEnv(env) {
72
74
  return Object.fromEntries(Object.entries(env).map(([key, value]) => [key, SECRET_RE.test(key) ? '<redacted>' : value]));
73
75
  }
76
+ export async function prepareNativeCodexAuthBridge(env, opts = {}) {
77
+ const required = opts.required !== false;
78
+ if (!required) {
79
+ return {
80
+ ok: true,
81
+ blockers: [],
82
+ proof: {
83
+ schema: 'sks.codex-native-auth-bridge.v1',
84
+ ok: true,
85
+ status: 'skipped_fixture_adapter',
86
+ method: null,
87
+ cleanup_required: false
88
+ },
89
+ cleanup: async () => ({ ok: true, status: 'cleaned', outcome: 'not_required', cleanup_required: false, blockers: [] })
90
+ };
91
+ }
92
+ const nativeHome = String(process.env.HOME || os.homedir());
93
+ const sourceHome = path.resolve(String(process.env.CODEX_HOME || path.join(nativeHome, '.codex')));
94
+ const sourceAuthCandidate = path.join(sourceHome, 'auth.json');
95
+ const originalHome = String(env.HOME || '');
96
+ const originalCodexHome = String(env.CODEX_HOME || '');
97
+ let tempRoot = null;
98
+ let tempRootIdentity = null;
99
+ let sourceAuthIdentity = null;
100
+ let sourceAuthFingerprint = '';
101
+ let originalTokenFingerprint = '';
102
+ let originalApiKeyFields = {};
103
+ let destinationAuth = null;
104
+ let destinationAuthIdentity = null;
105
+ let bridgeProof = null;
106
+ let cleanupComplete = false;
107
+ const removeOwnedTempRoot = async () => {
108
+ if (!tempRoot || cleanupComplete)
109
+ return;
110
+ let current;
111
+ try {
112
+ current = await fsp.lstat(tempRoot);
113
+ }
114
+ catch (error) {
115
+ if (error?.code === 'ENOENT') {
116
+ cleanupComplete = true;
117
+ return;
118
+ }
119
+ throw error;
120
+ }
121
+ if (!tempRootIdentity || !current.isDirectory() || current.isSymbolicLink()
122
+ || current.dev !== tempRootIdentity.dev || current.ino !== tempRootIdentity.ino) {
123
+ throw new Error('native_codex_auth_temp_root_identity_changed');
124
+ }
125
+ await fsp.rm(tempRoot, { recursive: true, force: false });
126
+ try {
127
+ await fsp.lstat(tempRoot);
128
+ throw new Error('native_codex_auth_temp_root_cleanup_unverified');
129
+ }
130
+ catch (error) {
131
+ if (error?.code !== 'ENOENT')
132
+ throw error;
133
+ }
134
+ cleanupComplete = true;
135
+ };
136
+ const cleanupOwnedTempRoot = async () => {
137
+ if (!tempRoot || cleanupComplete) {
138
+ return { ok: true, status: 'cleaned', outcome: 'already_cleaned', cleanup_required: false, blockers: [] };
139
+ }
140
+ try {
141
+ if (!destinationAuth || !destinationAuthIdentity || !sourceAuthIdentity || !sourceAuthFingerprint) {
142
+ throw new Error('native_codex_auth_cleanup_state_incomplete');
143
+ }
144
+ const tempAuthText = await readValidatedAuthFile(destinationAuth, {
145
+ expectedIdentity: destinationAuthIdentity,
146
+ requirePrivateMode: true,
147
+ requireSingleLink: true,
148
+ errorPrefix: 'native_codex_auth_destination'
149
+ });
150
+ const tempAuth = parseStrictChatGptAuth(tempAuthText, { forbidTopLevelApiKeys: true });
151
+ const refreshedTokenFingerprint = fingerprintJson(tempAuth.tokens);
152
+ if (refreshedTokenFingerprint === originalTokenFingerprint) {
153
+ await removeOwnedTempRoot();
154
+ const result = { ok: true, status: 'cleaned', outcome: 'unchanged', cleanup_required: false, blockers: [] };
155
+ applyCleanupProof(bridgeProof, result);
156
+ return result;
157
+ }
158
+ await assertSourceAuthUnchanged(sourceAuthCandidate, sourceAuthIdentity, sourceAuthFingerprint);
159
+ const hostAuth = { ...tempAuth, ...originalApiKeyFields };
160
+ const hostAuthText = `${JSON.stringify(hostAuth, null, 2)}\n`;
161
+ await writeAuthFileAtomic(sourceAuthCandidate, hostAuthText, async () => {
162
+ await assertSourceAuthUnchanged(sourceAuthCandidate, sourceAuthIdentity, sourceAuthFingerprint);
163
+ });
164
+ await removeOwnedTempRoot();
165
+ const result = { ok: true, status: 'cleaned', outcome: 'refreshed_persisted', cleanup_required: false, blockers: [] };
166
+ applyCleanupProof(bridgeProof, result);
167
+ return result;
168
+ }
169
+ catch (error) {
170
+ const reason = String(error?.message || error || 'native_codex_auth_cleanup_failed');
171
+ const outcome = reason === 'native_codex_auth_source_conflict' ? 'source_conflict' : 'failed';
172
+ const blocker = `native_codex_auth_cleanup_failed:${reason}`;
173
+ const result = { ok: false, status: 'blocked', outcome, cleanup_required: true, blockers: [blocker] };
174
+ applyCleanupProof(bridgeProof, result, tempRoot);
175
+ return result;
176
+ }
177
+ };
178
+ try {
179
+ const sourcePathStat = await fsp.lstat(sourceAuthCandidate);
180
+ if (sourcePathStat.isSymbolicLink())
181
+ throw new Error('native_codex_auth_source_symlink_forbidden');
182
+ if (!sourcePathStat.isFile())
183
+ throw new Error('native_codex_auth_source_not_file');
184
+ const noFollow = process.platform === 'win32' ? 0 : fsConstants.O_NOFOLLOW;
185
+ let sanitizedAuthText = '';
186
+ const sourceText = await readValidatedAuthFile(sourceAuthCandidate, {
187
+ expectedIdentity: { dev: sourcePathStat.dev, ino: sourcePathStat.ino },
188
+ requirePrivateMode: true,
189
+ errorPrefix: 'native_codex_auth_source'
190
+ });
191
+ const sourceAuth = parseStrictChatGptAuth(sourceText);
192
+ sourceAuthIdentity = { dev: sourcePathStat.dev, ino: sourcePathStat.ino };
193
+ sourceAuthFingerprint = fingerprintText(sourceText);
194
+ originalTokenFingerprint = fingerprintJson(sourceAuth.tokens);
195
+ originalApiKeyFields = Object.fromEntries(Object.entries(sourceAuth).filter(([key]) => isTopLevelApiKeyField(key)));
196
+ sanitizedAuthText = `${JSON.stringify(removeTopLevelApiKeyFields(sourceAuth), null, 2)}\n`;
197
+ const tempBase = await fsp.realpath(os.tmpdir());
198
+ tempRoot = await fsp.mkdtemp(path.join(tempBase, 'sks-native-codex-'));
199
+ const rootStat = await fsp.lstat(tempRoot);
200
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
201
+ throw new Error('native_codex_auth_temp_root_invalid');
202
+ }
203
+ tempRootIdentity = { dev: rootStat.dev, ino: rootStat.ino };
204
+ await fsp.chmod(tempRoot, 0o700);
205
+ const securedRootStat = await fsp.lstat(tempRoot);
206
+ if (securedRootStat.dev !== tempRootIdentity.dev || securedRootStat.ino !== tempRootIdentity.ino
207
+ || (process.platform !== 'win32' && (securedRootStat.mode & 0o777) !== 0o700)) {
208
+ throw new Error('native_codex_auth_temp_root_permissions_invalid');
209
+ }
210
+ if (isPathInside(process.cwd(), tempRoot) || isPathInside(path.dirname(originalCodexHome), tempRoot)) {
211
+ throw new Error('native_codex_auth_temp_root_inside_workspace');
212
+ }
213
+ const destinationHome = path.join(tempRoot, 'home');
214
+ const destinationCodexHome = path.join(tempRoot, 'codex');
215
+ destinationAuth = path.join(destinationCodexHome, 'auth.json');
216
+ await fsp.mkdir(destinationHome, { mode: 0o700 });
217
+ await fsp.mkdir(destinationCodexHome, { mode: 0o700 });
218
+ await fsp.chmod(destinationHome, 0o700);
219
+ await fsp.chmod(destinationCodexHome, 0o700);
220
+ const destinationHandle = await fsp.open(destinationAuth, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollow, 0o600);
221
+ let destinationIdentity = null;
222
+ try {
223
+ await destinationHandle.writeFile(sanitizedAuthText, 'utf8');
224
+ await destinationHandle.chmod(0o600);
225
+ const destinationStat = await destinationHandle.stat();
226
+ if (!destinationStat.isFile() || destinationStat.nlink !== 1
227
+ || (process.platform !== 'win32' && (destinationStat.mode & 0o777) !== 0o600)) {
228
+ throw new Error('native_codex_auth_destination_validation_failed');
229
+ }
230
+ destinationIdentity = { dev: destinationStat.dev, ino: destinationStat.ino };
231
+ }
232
+ finally {
233
+ await destinationHandle.close();
234
+ }
235
+ const destinationPathStat = await fsp.lstat(destinationAuth);
236
+ if (!destinationIdentity || !destinationPathStat.isFile() || destinationPathStat.isSymbolicLink()
237
+ || destinationPathStat.dev !== destinationIdentity.dev || destinationPathStat.ino !== destinationIdentity.ino) {
238
+ throw new Error('native_codex_auth_destination_not_regular_file');
239
+ }
240
+ destinationAuthIdentity = destinationIdentity;
241
+ env.HOME = destinationHome;
242
+ env.CODEX_HOME = destinationCodexHome;
243
+ bridgeProof = {
244
+ schema: 'sks.codex-native-auth-bridge.v1',
245
+ ok: true,
246
+ status: 'ready',
247
+ method: 'exclusive_copy',
248
+ auth_mode: 'chatgpt_oauth',
249
+ source: 'host_codex_home/auth.json',
250
+ destination: 'private_temp_codex_home/auth.json',
251
+ temp_root_location: 'os_tmpdir',
252
+ temp_root_mode: '0700',
253
+ auth_file_mode: '0600',
254
+ source_symlinks_allowed: false,
255
+ source_identity_captured: true,
256
+ source_content_fingerprint_captured: true,
257
+ api_key_fields_removed: true,
258
+ refresh_writeback: 'strict_chatgpt_tokens_compare_and_swap',
259
+ cleanup_required: true
260
+ };
261
+ return {
262
+ ok: true,
263
+ blockers: [],
264
+ proof: bridgeProof,
265
+ cleanup: cleanupOwnedTempRoot
266
+ };
267
+ }
268
+ catch (error) {
269
+ let cleanupError = null;
270
+ try {
271
+ await removeOwnedTempRoot();
272
+ }
273
+ catch (cleanupFailure) {
274
+ cleanupError = cleanupFailure;
275
+ }
276
+ env.HOME = originalHome;
277
+ env.CODEX_HOME = originalCodexHome;
278
+ const reason = String(error?.message || error || 'native_codex_auth_bridge_failed');
279
+ const cleanupReason = cleanupError ? `:cleanup_failed:${String(cleanupError?.message || cleanupError)}` : '';
280
+ const blocker = `native_codex_auth_bridge_failed:${reason}${cleanupReason}`;
281
+ return {
282
+ ok: false,
283
+ blockers: [blocker],
284
+ proof: {
285
+ schema: 'sks.codex-native-auth-bridge.v1',
286
+ ok: false,
287
+ status: 'blocked',
288
+ method: null,
289
+ source: 'host_codex_home/auth.json',
290
+ destination: 'private_temp_codex_home/auth.json',
291
+ cleanup_required: Boolean(tempRoot && !cleanupComplete),
292
+ blockers: [blocker]
293
+ },
294
+ cleanup: async () => {
295
+ try {
296
+ await removeOwnedTempRoot();
297
+ return { ok: true, status: 'cleaned', outcome: 'setup_failure_cleaned', cleanup_required: false, blockers: [] };
298
+ }
299
+ catch (cleanupFailure) {
300
+ const blocker = `native_codex_auth_cleanup_failed:${String(cleanupFailure?.message || cleanupFailure)}`;
301
+ return { ok: false, status: 'blocked', outcome: 'failed', cleanup_required: true, blockers: [blocker] };
302
+ }
303
+ }
304
+ };
305
+ }
306
+ }
307
+ function parseStrictChatGptAuth(sourceText, opts = {}) {
308
+ let parsed;
309
+ try {
310
+ parsed = JSON.parse(sourceText);
311
+ }
312
+ catch {
313
+ throw new Error('native_codex_auth_source_invalid_json');
314
+ }
315
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
316
+ throw new Error('native_codex_auth_source_unknown_shape');
317
+ }
318
+ const authMode = String(parsed.auth_mode || '').trim().toLowerCase();
319
+ if (authMode !== 'chatgpt') {
320
+ throw new Error(authMode.includes('api') ? 'native_codex_auth_api_key_forbidden' : 'native_codex_auth_mode_unsupported');
321
+ }
322
+ const tokens = parsed.tokens;
323
+ if (!tokens || typeof tokens !== 'object' || Array.isArray(tokens)) {
324
+ throw new Error('native_codex_auth_oauth_tokens_missing');
325
+ }
326
+ const accessToken = typeof tokens?.access_token === 'string' ? tokens.access_token.trim() : '';
327
+ const refreshToken = typeof tokens?.refresh_token === 'string' ? tokens.refresh_token.trim() : '';
328
+ const idToken = typeof tokens?.id_token === 'string' ? tokens.id_token.trim() : '';
329
+ if (!accessToken || (!refreshToken && !idToken)) {
330
+ throw new Error('native_codex_auth_oauth_tokens_missing');
331
+ }
332
+ if (opts.forbidTopLevelApiKeys && Object.keys(parsed).some((key) => isTopLevelApiKeyField(key))) {
333
+ throw new Error('native_codex_auth_destination_api_key_field_forbidden');
334
+ }
335
+ return parsed;
336
+ }
337
+ function removeTopLevelApiKeyFields(auth) {
338
+ return Object.fromEntries(Object.entries(auth).filter(([key]) => !isTopLevelApiKeyField(key)));
339
+ }
340
+ function isTopLevelApiKeyField(key) {
341
+ const normalized = key.replace(/[^a-z0-9]/gi, '').toLowerCase();
342
+ return normalized === 'key' || normalized === 'apikey' || normalized === 'openaiapikey';
343
+ }
344
+ async function readValidatedAuthFile(filePath, opts) {
345
+ const noFollow = process.platform === 'win32' ? 0 : fsConstants.O_NOFOLLOW;
346
+ const pathStat = await fsp.lstat(filePath);
347
+ if (pathStat.isSymbolicLink())
348
+ throw new Error(`${opts.errorPrefix}_symlink_forbidden`);
349
+ if (!pathStat.isFile())
350
+ throw new Error(`${opts.errorPrefix}_not_file`);
351
+ const handle = await fsp.open(filePath, fsConstants.O_RDONLY | noFollow);
352
+ try {
353
+ const stat = await handle.stat();
354
+ if (!stat.isFile() || stat.dev !== pathStat.dev || stat.ino !== pathStat.ino
355
+ || (opts.expectedIdentity && (stat.dev !== opts.expectedIdentity.dev || stat.ino !== opts.expectedIdentity.ino))) {
356
+ throw new Error(`${opts.errorPrefix}_identity_changed`);
357
+ }
358
+ if (opts.requireSingleLink && stat.nlink !== 1)
359
+ throw new Error(`${opts.errorPrefix}_link_count_invalid`);
360
+ if (opts.requirePrivateMode && process.platform !== 'win32' && (stat.mode & 0o077) !== 0) {
361
+ throw new Error(`${opts.errorPrefix}_permissions_too_open`);
362
+ }
363
+ return await handle.readFile('utf8');
364
+ }
365
+ finally {
366
+ await handle.close();
367
+ }
368
+ }
369
+ async function assertSourceAuthUnchanged(sourcePath, expectedIdentity, expectedFingerprint) {
370
+ try {
371
+ const currentText = await readValidatedAuthFile(sourcePath, {
372
+ expectedIdentity,
373
+ requirePrivateMode: true,
374
+ errorPrefix: 'native_codex_auth_source'
375
+ });
376
+ if (fingerprintText(currentText) !== expectedFingerprint)
377
+ throw new Error('native_codex_auth_source_conflict');
378
+ }
379
+ catch (error) {
380
+ if (String(error?.message || error) === 'native_codex_auth_source_conflict')
381
+ throw error;
382
+ throw new Error('native_codex_auth_source_conflict');
383
+ }
384
+ }
385
+ async function writeAuthFileAtomic(sourcePath, contents, revalidateSource) {
386
+ const noFollow = process.platform === 'win32' ? 0 : fsConstants.O_NOFOLLOW;
387
+ const temporaryPath = path.join(path.dirname(sourcePath), `.auth.json.sks-refresh-${process.pid}-${randomBytes(8).toString('hex')}`);
388
+ let temporaryCreated = false;
389
+ try {
390
+ const handle = await fsp.open(temporaryPath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollow, 0o600);
391
+ temporaryCreated = true;
392
+ try {
393
+ await handle.writeFile(contents, 'utf8');
394
+ await handle.chmod(0o600);
395
+ await handle.sync();
396
+ const stat = await handle.stat();
397
+ if (!stat.isFile() || stat.nlink !== 1 || (process.platform !== 'win32' && (stat.mode & 0o777) !== 0o600)) {
398
+ throw new Error('native_codex_auth_writeback_temp_validation_failed');
399
+ }
400
+ }
401
+ finally {
402
+ await handle.close();
403
+ }
404
+ await revalidateSource();
405
+ await fsp.rename(temporaryPath, sourcePath);
406
+ temporaryCreated = false;
407
+ const persistedText = await readValidatedAuthFile(sourcePath, {
408
+ requirePrivateMode: true,
409
+ requireSingleLink: true,
410
+ errorPrefix: 'native_codex_auth_writeback'
411
+ });
412
+ if (fingerprintText(persistedText) !== fingerprintText(contents)) {
413
+ throw new Error('native_codex_auth_writeback_verification_failed');
414
+ }
415
+ }
416
+ finally {
417
+ if (temporaryCreated)
418
+ await fsp.rm(temporaryPath, { force: true });
419
+ }
420
+ }
421
+ function fingerprintText(value) {
422
+ return createHash('sha256').update(value).digest('hex');
423
+ }
424
+ function fingerprintJson(value) {
425
+ return fingerprintText(stableJson(value));
426
+ }
427
+ function stableJson(value) {
428
+ if (Array.isArray(value))
429
+ return `[${value.map(stableJson).join(',')}]`;
430
+ if (value && typeof value === 'object') {
431
+ return `{${Object.entries(value)
432
+ .sort(([left], [right]) => left.localeCompare(right))
433
+ .map(([key, child]) => `${JSON.stringify(key)}:${stableJson(child)}`)
434
+ .join(',')}}`;
435
+ }
436
+ return JSON.stringify(value);
437
+ }
438
+ function applyCleanupProof(proof, result, retainedTempRoot) {
439
+ if (!proof)
440
+ return;
441
+ proof.cleanup_required = result.cleanup_required;
442
+ proof.cleanup_status = result.status;
443
+ proof.cleanup_outcome = result.outcome;
444
+ proof.refreshed_auth_persisted = result.outcome === 'refreshed_persisted';
445
+ proof.recovery_temp_root_retained = result.cleanup_required;
446
+ if (retainedTempRoot)
447
+ proof.recovery_temp_root = retainedTempRoot;
448
+ if (result.blockers.length)
449
+ proof.cleanup_blockers = result.blockers;
450
+ else
451
+ delete proof.cleanup_blockers;
452
+ }
453
+ function isPathInside(parent, child) {
454
+ if (!parent)
455
+ return false;
456
+ const relative = path.relative(path.resolve(parent), path.resolve(child));
457
+ return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
458
+ }