codexgpt 1.0.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 (654) hide show
  1. package/AGENTS.example.md +20 -0
  2. package/CHANGELOG.md +342 -0
  3. package/CHATGPT_PROMPT.md +12 -0
  4. package/CLOUDFLARED_VERIFIED_INSTALL.md +94 -0
  5. package/CODEX_PROMPT.md +12 -0
  6. package/CONTRIBUTING.md +62 -0
  7. package/DOMAIN_SETUP.md +275 -0
  8. package/FAQ.md +366 -0
  9. package/FAQ_ZH.md +329 -0
  10. package/LICENSE +21 -0
  11. package/PUBLIC_LAUNCH_CHECKLIST.md +111 -0
  12. package/README.md +490 -0
  13. package/README_ZH.md +747 -0
  14. package/SECURITY.md +295 -0
  15. package/config.example.env +103 -0
  16. package/dist/access/fullAccessLease.js +148 -0
  17. package/dist/access/fullAccessLease.js.map +1 -0
  18. package/dist/access/protectedRoots.js +122 -0
  19. package/dist/access/protectedRoots.js.map +1 -0
  20. package/dist/access/rootAdmission.js +347 -0
  21. package/dist/access/rootAdmission.js.map +1 -0
  22. package/dist/analysis/cache.js +27 -0
  23. package/dist/analysis/cache.js.map +1 -0
  24. package/dist/analysis/classify.js +102 -0
  25. package/dist/analysis/classify.js.map +1 -0
  26. package/dist/analysis/extract.js +139 -0
  27. package/dist/analysis/extract.js.map +1 -0
  28. package/dist/analysis/graph.js +22 -0
  29. package/dist/analysis/graph.js.map +1 -0
  30. package/dist/analysis/impact.js +167 -0
  31. package/dist/analysis/impact.js.map +1 -0
  32. package/dist/analysis/index.js +215 -0
  33. package/dist/analysis/index.js.map +1 -0
  34. package/dist/analysis/inventory.js +51 -0
  35. package/dist/analysis/inventory.js.map +1 -0
  36. package/dist/analysis/providers.js +24 -0
  37. package/dist/analysis/providers.js.map +1 -0
  38. package/dist/analysis/rank.js +27 -0
  39. package/dist/analysis/rank.js.map +1 -0
  40. package/dist/analysis/types.js +8 -0
  41. package/dist/analysis/types.js.map +1 -0
  42. package/dist/audit/canonicalJson.js +79 -0
  43. package/dist/audit/canonicalJson.js.map +1 -0
  44. package/dist/audit/diagnostics.js +128 -0
  45. package/dist/audit/diagnostics.js.map +1 -0
  46. package/dist/audit/index.js +12 -0
  47. package/dist/audit/index.js.map +1 -0
  48. package/dist/audit/lifecycleV3.js +53 -0
  49. package/dist/audit/lifecycleV3.js.map +1 -0
  50. package/dist/audit/lifecycleV4.js +70 -0
  51. package/dist/audit/lifecycleV4.js.map +1 -0
  52. package/dist/audit/lock.js +195 -0
  53. package/dist/audit/lock.js.map +1 -0
  54. package/dist/audit/queryTool.js +120 -0
  55. package/dist/audit/queryTool.js.map +1 -0
  56. package/dist/audit/runtime.js +48 -0
  57. package/dist/audit/runtime.js.map +1 -0
  58. package/dist/audit/schemas.js +640 -0
  59. package/dist/audit/schemas.js.map +1 -0
  60. package/dist/audit/store.js +1309 -0
  61. package/dist/audit/store.js.map +1 -0
  62. package/dist/audit/transactionParticipant.js +45 -0
  63. package/dist/audit/transactionParticipant.js.map +1 -0
  64. package/dist/audit/types.js +11 -0
  65. package/dist/audit/types.js.map +1 -0
  66. package/dist/auth/audit.js +44 -0
  67. package/dist/auth/audit.js.map +1 -0
  68. package/dist/auth/authorizationStore.js +333 -0
  69. package/dist/auth/authorizationStore.js.map +1 -0
  70. package/dist/auth/challenges.js +25 -0
  71. package/dist/auth/challenges.js.map +1 -0
  72. package/dist/auth/clientStore.js +469 -0
  73. package/dist/auth/clientStore.js.map +1 -0
  74. package/dist/auth/cloudflareConfig.js +112 -0
  75. package/dist/auth/cloudflareConfig.js.map +1 -0
  76. package/dist/auth/configuration.js +167 -0
  77. package/dist/auth/configuration.js.map +1 -0
  78. package/dist/auth/credentialStore.js +65 -0
  79. package/dist/auth/credentialStore.js.map +1 -0
  80. package/dist/auth/deploymentLock.js +220 -0
  81. package/dist/auth/deploymentLock.js.map +1 -0
  82. package/dist/auth/deploymentRegistry.js +149 -0
  83. package/dist/auth/deploymentRegistry.js.map +1 -0
  84. package/dist/auth/errors.js +26 -0
  85. package/dist/auth/errors.js.map +1 -0
  86. package/dist/auth/grantStore.js +307 -0
  87. package/dist/auth/grantStore.js.map +1 -0
  88. package/dist/auth/index.js +39 -0
  89. package/dist/auth/index.js.map +1 -0
  90. package/dist/auth/keyManager.js +148 -0
  91. package/dist/auth/keyManager.js.map +1 -0
  92. package/dist/auth/localAdminSession.js +173 -0
  93. package/dist/auth/localAdminSession.js.map +1 -0
  94. package/dist/auth/metadata.js +69 -0
  95. package/dist/auth/metadata.js.map +1 -0
  96. package/dist/auth/oauthProvider.js +589 -0
  97. package/dist/auth/oauthProvider.js.map +1 -0
  98. package/dist/auth/ownerApproval.js +51 -0
  99. package/dist/auth/ownerApproval.js.map +1 -0
  100. package/dist/auth/policyIdentity.js +189 -0
  101. package/dist/auth/policyIdentity.js.map +1 -0
  102. package/dist/auth/rateLimits.js +66 -0
  103. package/dist/auth/rateLimits.js.map +1 -0
  104. package/dist/auth/recovery.js +275 -0
  105. package/dist/auth/recovery.js.map +1 -0
  106. package/dist/auth/requestContext.js +34 -0
  107. package/dist/auth/requestContext.js.map +1 -0
  108. package/dist/auth/resourceMiddleware.js +147 -0
  109. package/dist/auth/resourceMiddleware.js.map +1 -0
  110. package/dist/auth/runtimeStatus.js +51 -0
  111. package/dist/auth/runtimeStatus.js.map +1 -0
  112. package/dist/auth/schemas.js +84 -0
  113. package/dist/auth/schemas.js.map +1 -0
  114. package/dist/auth/stateStore.js +468 -0
  115. package/dist/auth/stateStore.js.map +1 -0
  116. package/dist/auth/tokenService.js +631 -0
  117. package/dist/auth/tokenService.js.map +1 -0
  118. package/dist/auth/toolSecurity.js +108 -0
  119. package/dist/auth/toolSecurity.js.map +1 -0
  120. package/dist/auth/types.js +6 -0
  121. package/dist/auth/types.js.map +1 -0
  122. package/dist/auth/windowsDpapi.js +193 -0
  123. package/dist/auth/windowsDpapi.js.map +1 -0
  124. package/dist/bashOps.js +292 -0
  125. package/dist/bashOps.js.map +1 -0
  126. package/dist/capabilitiesOps.js +412 -0
  127. package/dist/capabilitiesOps.js.map +1 -0
  128. package/dist/changesets/crypto.js +92 -0
  129. package/dist/changesets/crypto.js.map +1 -0
  130. package/dist/changesets/index.js +9 -0
  131. package/dist/changesets/index.js.map +1 -0
  132. package/dist/changesets/moveStore.js +237 -0
  133. package/dist/changesets/moveStore.js.map +1 -0
  134. package/dist/changesets/moveUndo.js +386 -0
  135. package/dist/changesets/moveUndo.js.map +1 -0
  136. package/dist/changesets/schemas.js +234 -0
  137. package/dist/changesets/schemas.js.map +1 -0
  138. package/dist/changesets/store.js +573 -0
  139. package/dist/changesets/store.js.map +1 -0
  140. package/dist/changesets/types.js +9 -0
  141. package/dist/changesets/types.js.map +1 -0
  142. package/dist/changesets/undo.js +525 -0
  143. package/dist/changesets/undo.js.map +1 -0
  144. package/dist/changesets/unifiedUndo.js +31 -0
  145. package/dist/changesets/unifiedUndo.js.map +1 -0
  146. package/dist/cliEnvironment.js +85 -0
  147. package/dist/cliEnvironment.js.map +1 -0
  148. package/dist/codexSessions.js +733 -0
  149. package/dist/codexSessions.js.map +1 -0
  150. package/dist/codexgptSupertool.js +216 -0
  151. package/dist/codexgptSupertool.js.map +1 -0
  152. package/dist/codexproSupertool.js +204 -0
  153. package/dist/codexproSupertool.js.map +1 -0
  154. package/dist/config.js +733 -0
  155. package/dist/config.js.map +1 -0
  156. package/dist/control/localApprovalClient.js +287 -0
  157. package/dist/control/localApprovalClient.js.map +1 -0
  158. package/dist/control/localApprovalServer.js +466 -0
  159. package/dist/control/localApprovalServer.js.map +1 -0
  160. package/dist/control/runtime.js +178 -0
  161. package/dist/control/runtime.js.map +1 -0
  162. package/dist/control/schemas.js +184 -0
  163. package/dist/control/schemas.js.map +1 -0
  164. package/dist/control/windowsLocalControl.js +348 -0
  165. package/dist/control/windowsLocalControl.js.map +1 -0
  166. package/dist/fsOps.js +541 -0
  167. package/dist/fsOps.js.map +1 -0
  168. package/dist/git/admission.js +26 -0
  169. package/dist/git/admission.js.map +1 -0
  170. package/dist/git/branchService.js +51 -0
  171. package/dist/git/branchService.js.map +1 -0
  172. package/dist/git/capabilities.js +143 -0
  173. package/dist/git/capabilities.js.map +1 -0
  174. package/dist/git/commitService.js +344 -0
  175. package/dist/git/commitService.js.map +1 -0
  176. package/dist/git/durableState.js +151 -0
  177. package/dist/git/durableState.js.map +1 -0
  178. package/dist/git/execution.js +451 -0
  179. package/dist/git/execution.js.map +1 -0
  180. package/dist/git/fileTransaction.js +50 -0
  181. package/dist/git/fileTransaction.js.map +1 -0
  182. package/dist/git/indexService.js +421 -0
  183. package/dist/git/indexService.js.map +1 -0
  184. package/dist/git/integrations.js +653 -0
  185. package/dist/git/integrations.js.map +1 -0
  186. package/dist/git/locks.js +287 -0
  187. package/dist/git/locks.js.map +1 -0
  188. package/dist/git/mutationContext.js +92 -0
  189. package/dist/git/mutationContext.js.map +1 -0
  190. package/dist/git/mutationJournal.js +91 -0
  191. package/dist/git/mutationJournal.js.map +1 -0
  192. package/dist/git/mutationService.js +352 -0
  193. package/dist/git/mutationService.js.map +1 -0
  194. package/dist/git/normalization.js +57 -0
  195. package/dist/git/normalization.js.map +1 -0
  196. package/dist/git/objectQuarantine.js +243 -0
  197. package/dist/git/objectQuarantine.js.map +1 -0
  198. package/dist/git/opaqueRecordStore.js +187 -0
  199. package/dist/git/opaqueRecordStore.js.map +1 -0
  200. package/dist/git/operationStore.js +287 -0
  201. package/dist/git/operationStore.js.map +1 -0
  202. package/dist/git/parsers.js +311 -0
  203. package/dist/git/parsers.js.map +1 -0
  204. package/dist/git/privateIndex.js +79 -0
  205. package/dist/git/privateIndex.js.map +1 -0
  206. package/dist/git/productionBootstrap.js +39 -0
  207. package/dist/git/productionBootstrap.js.map +1 -0
  208. package/dist/git/readService.js +846 -0
  209. package/dist/git/readService.js.map +1 -0
  210. package/dist/git/recovery.js +417 -0
  211. package/dist/git/recovery.js.map +1 -0
  212. package/dist/git/repositoryIdentity.js +611 -0
  213. package/dist/git/repositoryIdentity.js.map +1 -0
  214. package/dist/git/repositoryStore.js +237 -0
  215. package/dist/git/repositoryStore.js.map +1 -0
  216. package/dist/git/resources.js +113 -0
  217. package/dist/git/resources.js.map +1 -0
  218. package/dist/git/restoreService.js +178 -0
  219. package/dist/git/restoreService.js.map +1 -0
  220. package/dist/git/reviewToken.js +86 -0
  221. package/dist/git/reviewToken.js.map +1 -0
  222. package/dist/git/stashService.js +566 -0
  223. package/dist/git/stashService.js.map +1 -0
  224. package/dist/git/stateToken.js +139 -0
  225. package/dist/git/stateToken.js.map +1 -0
  226. package/dist/gitOps.js +147 -0
  227. package/dist/gitOps.js.map +1 -0
  228. package/dist/guard.js +608 -0
  229. package/dist/guard.js.map +1 -0
  230. package/dist/guidance/diagnostics.js +14 -0
  231. package/dist/guidance/diagnostics.js.map +1 -0
  232. package/dist/guidance/instructions.js +167 -0
  233. package/dist/guidance/instructions.js.map +1 -0
  234. package/dist/guidance/mode.js +18 -0
  235. package/dist/guidance/mode.js.map +1 -0
  236. package/dist/guidance/openaiSkillMetadata.js +29 -0
  237. package/dist/guidance/openaiSkillMetadata.js.map +1 -0
  238. package/dist/guidance/safeTextReader.js +171 -0
  239. package/dist/guidance/safeTextReader.js.map +1 -0
  240. package/dist/guidance/skillCatalog.js +70 -0
  241. package/dist/guidance/skillCatalog.js.map +1 -0
  242. package/dist/guidance/skillDiscovery.js +353 -0
  243. package/dist/guidance/skillDiscovery.js.map +1 -0
  244. package/dist/guidance/skillMetadata.js +115 -0
  245. package/dist/guidance/skillMetadata.js.map +1 -0
  246. package/dist/guidance/skillResources.js +146 -0
  247. package/dist/guidance/skillResources.js.map +1 -0
  248. package/dist/handoffOps.js +449 -0
  249. package/dist/handoffOps.js.map +1 -0
  250. package/dist/http/localAdminApp.js +203 -0
  251. package/dist/http/localAdminApp.js.map +1 -0
  252. package/dist/http/oauthMcpRuntime.js +294 -0
  253. package/dist/http/oauthMcpRuntime.js.map +1 -0
  254. package/dist/http/publicApp.js +350 -0
  255. package/dist/http/publicApp.js.map +1 -0
  256. package/dist/http/securityHeaders.js +11 -0
  257. package/dist/http/securityHeaders.js.map +1 -0
  258. package/dist/http.js +2076 -0
  259. package/dist/http.js.map +1 -0
  260. package/dist/moves/engine.js +1079 -0
  261. package/dist/moves/engine.js.map +1 -0
  262. package/dist/moves/index.js +6 -0
  263. package/dist/moves/index.js.map +1 -0
  264. package/dist/moves/planner.js +270 -0
  265. package/dist/moves/planner.js.map +1 -0
  266. package/dist/moves/recovery.js +557 -0
  267. package/dist/moves/recovery.js.map +1 -0
  268. package/dist/moves/service.js +300 -0
  269. package/dist/moves/service.js.map +1 -0
  270. package/dist/moves/types.js +11 -0
  271. package/dist/moves/types.js.map +1 -0
  272. package/dist/mutations/index.js +4 -0
  273. package/dist/mutations/index.js.map +1 -0
  274. package/dist/mutations/lifecycle.js +165 -0
  275. package/dist/mutations/lifecycle.js.map +1 -0
  276. package/dist/mutations/localService.js +163 -0
  277. package/dist/mutations/localService.js.map +1 -0
  278. package/dist/mutations/runtime.js +322 -0
  279. package/dist/mutations/runtime.js.map +1 -0
  280. package/dist/mutations/types.js +2 -0
  281. package/dist/mutations/types.js.map +1 -0
  282. package/dist/mutations/writers.js +360 -0
  283. package/dist/mutations/writers.js.map +1 -0
  284. package/dist/patchOps.js +420 -0
  285. package/dist/patchOps.js.map +1 -0
  286. package/dist/policy/approval.js +306 -0
  287. package/dist/policy/approval.js.map +1 -0
  288. package/dist/policy/audit.js +88 -0
  289. package/dist/policy/audit.js.map +1 -0
  290. package/dist/policy/authorizationFacts.js +187 -0
  291. package/dist/policy/authorizationFacts.js.map +1 -0
  292. package/dist/policy/compat.js +104 -0
  293. package/dist/policy/compat.js.map +1 -0
  294. package/dist/policy/context.js +31 -0
  295. package/dist/policy/context.js.map +1 -0
  296. package/dist/policy/enforcement.js +46 -0
  297. package/dist/policy/enforcement.js.map +1 -0
  298. package/dist/policy/evaluator.js +338 -0
  299. package/dist/policy/evaluator.js.map +1 -0
  300. package/dist/policy/executionCapabilities.js +80 -0
  301. package/dist/policy/executionCapabilities.js.map +1 -0
  302. package/dist/policy/executionResources.js +192 -0
  303. package/dist/policy/executionResources.js.map +1 -0
  304. package/dist/policy/fullAccessResources.js +23 -0
  305. package/dist/policy/fullAccessResources.js.map +1 -0
  306. package/dist/policy/hardPolicy.js +50 -0
  307. package/dist/policy/hardPolicy.js.map +1 -0
  308. package/dist/policy/identity.js +142 -0
  309. package/dist/policy/identity.js.map +1 -0
  310. package/dist/policy/integration.js +331 -0
  311. package/dist/policy/integration.js.map +1 -0
  312. package/dist/policy/pendingApprovals.js +301 -0
  313. package/dist/policy/pendingApprovals.js.map +1 -0
  314. package/dist/policy/profileStore.js +367 -0
  315. package/dist/policy/profileStore.js.map +1 -0
  316. package/dist/policy/resources.js +249 -0
  317. package/dist/policy/resources.js.map +1 -0
  318. package/dist/policy/runtime.js +713 -0
  319. package/dist/policy/runtime.js.map +1 -0
  320. package/dist/policy/schemas.js +625 -0
  321. package/dist/policy/schemas.js.map +1 -0
  322. package/dist/policy/toolPolicy.js +141 -0
  323. package/dist/policy/toolPolicy.js.map +1 -0
  324. package/dist/policy/transport.js +10 -0
  325. package/dist/policy/transport.js.map +1 -0
  326. package/dist/policy/types.js +31 -0
  327. package/dist/policy/types.js.map +1 -0
  328. package/dist/proContext.js +675 -0
  329. package/dist/proContext.js.map +1 -0
  330. package/dist/process/authority.js +18 -0
  331. package/dist/process/authority.js.map +1 -0
  332. package/dist/process/backendDiscovery.js +83 -0
  333. package/dist/process/backendDiscovery.js.map +1 -0
  334. package/dist/process/commandCompiler.js +71 -0
  335. package/dist/process/commandCompiler.js.map +1 -0
  336. package/dist/process/outputCursor.js +67 -0
  337. package/dist/process/outputCursor.js.map +1 -0
  338. package/dist/process/outputQuota.js +94 -0
  339. package/dist/process/outputQuota.js.map +1 -0
  340. package/dist/process/outputRing.js +138 -0
  341. package/dist/process/outputRing.js.map +1 -0
  342. package/dist/process/processAuditCoordinator.js +52 -0
  343. package/dist/process/processAuditCoordinator.js.map +1 -0
  344. package/dist/process/processManager.js +311 -0
  345. package/dist/process/processManager.js.map +1 -0
  346. package/dist/process/runCommand.js +332 -0
  347. package/dist/process/runCommand.js.map +1 -0
  348. package/dist/process/streamingRedactor.js +105 -0
  349. package/dist/process/streamingRedactor.js.map +1 -0
  350. package/dist/process/types.js +2 -0
  351. package/dist/process/types.js.map +1 -0
  352. package/dist/process/windowsHostClient.js +604 -0
  353. package/dist/process/windowsHostClient.js.map +1 -0
  354. package/dist/process/windowsHostProtocol.js +270 -0
  355. package/dist/process/windowsHostProtocol.js.map +1 -0
  356. package/dist/process/windowsPersistentBackend.js +263 -0
  357. package/dist/process/windowsPersistentBackend.js.map +1 -0
  358. package/dist/productionRuntime.js +907 -0
  359. package/dist/productionRuntime.js.map +1 -0
  360. package/dist/profileStore.js +231 -0
  361. package/dist/profileStore.js.map +1 -0
  362. package/dist/redact.js +124 -0
  363. package/dist/redact.js.map +1 -0
  364. package/dist/searchOps.js +186 -0
  365. package/dist/searchOps.js.map +1 -0
  366. package/dist/selfTestOps.js +786 -0
  367. package/dist/selfTestOps.js.map +1 -0
  368. package/dist/semantic/budgets.js +12 -0
  369. package/dist/semantic/budgets.js.map +1 -0
  370. package/dist/semantic/builtin/typescriptAssets.js +68 -0
  371. package/dist/semantic/builtin/typescriptAssets.js.map +1 -0
  372. package/dist/semantic/builtin/typescriptProvider.js +211 -0
  373. package/dist/semantic/builtin/typescriptProvider.js.map +1 -0
  374. package/dist/semantic/builtin/typescriptWorker.js +365 -0
  375. package/dist/semantic/builtin/typescriptWorker.js.map +1 -0
  376. package/dist/semantic/diffPreview.js +36 -0
  377. package/dist/semantic/diffPreview.js.map +1 -0
  378. package/dist/semantic/index.js +11 -0
  379. package/dist/semantic/index.js.map +1 -0
  380. package/dist/semantic/manager.js +522 -0
  381. package/dist/semantic/manager.js.map +1 -0
  382. package/dist/semantic/positions.js +86 -0
  383. package/dist/semantic/positions.js.map +1 -0
  384. package/dist/semantic/previewStore.js +276 -0
  385. package/dist/semantic/previewStore.js.map +1 -0
  386. package/dist/semantic/projectResolver.js +309 -0
  387. package/dist/semantic/projectResolver.js.map +1 -0
  388. package/dist/semantic/sourceSnapshot.js +54 -0
  389. package/dist/semantic/sourceSnapshot.js.map +1 -0
  390. package/dist/semantic/status.js +46 -0
  391. package/dist/semantic/status.js.map +1 -0
  392. package/dist/semantic/types.js +2 -0
  393. package/dist/semantic/types.js.map +1 -0
  394. package/dist/server.js +8676 -0
  395. package/dist/server.js.map +1 -0
  396. package/dist/stdio.js +64 -0
  397. package/dist/stdio.js.map +1 -0
  398. package/dist/toolCardWidget.js +2000 -0
  399. package/dist/toolCardWidget.js.map +1 -0
  400. package/dist/tools/contracts/catalog.js +111 -0
  401. package/dist/tools/contracts/catalog.js.map +1 -0
  402. package/dist/tools/contracts/index.js +8 -0
  403. package/dist/tools/contracts/index.js.map +1 -0
  404. package/dist/tools/contracts/registration.js +27 -0
  405. package/dist/tools/contracts/registration.js.map +1 -0
  406. package/dist/tools/contracts/schemas.js +11 -0
  407. package/dist/tools/contracts/schemas.js.map +1 -0
  408. package/dist/tools/contracts/types.js +2 -0
  409. package/dist/tools/contracts/types.js.map +1 -0
  410. package/dist/tools/contracts/v3.js +76 -0
  411. package/dist/tools/contracts/v3.js.map +1 -0
  412. package/dist/tools/contracts/v4.js +39 -0
  413. package/dist/tools/contracts/v4.js.map +1 -0
  414. package/dist/tools/contracts/v5.js +20 -0
  415. package/dist/tools/contracts/v5.js.map +1 -0
  416. package/dist/tools/phase3dServer.js +350 -0
  417. package/dist/tools/phase3dServer.js.map +1 -0
  418. package/dist/tools/schemas/applyPatch.js +388 -0
  419. package/dist/tools/schemas/applyPatch.js.map +1 -0
  420. package/dist/tools/schemas/bash.js +202 -0
  421. package/dist/tools/schemas/bash.js.map +1 -0
  422. package/dist/tools/schemas/closeWorkspace.js +105 -0
  423. package/dist/tools/schemas/closeWorkspace.js.map +1 -0
  424. package/dist/tools/schemas/codexContext.js +402 -0
  425. package/dist/tools/schemas/codexContext.js.map +1 -0
  426. package/dist/tools/schemas/codexSessions.js +328 -0
  427. package/dist/tools/schemas/codexSessions.js.map +1 -0
  428. package/dist/tools/schemas/codexgpt.js +1068 -0
  429. package/dist/tools/schemas/codexgpt.js.map +1 -0
  430. package/dist/tools/schemas/codexgptInventory.js +359 -0
  431. package/dist/tools/schemas/codexgptInventory.js.map +1 -0
  432. package/dist/tools/schemas/codexgptSelfTest.js +563 -0
  433. package/dist/tools/schemas/codexgptSelfTest.js.map +1 -0
  434. package/dist/tools/schemas/codexpro.js +917 -0
  435. package/dist/tools/schemas/codexpro.js.map +1 -0
  436. package/dist/tools/schemas/codexproInventory.js +352 -0
  437. package/dist/tools/schemas/codexproInventory.js.map +1 -0
  438. package/dist/tools/schemas/codexproSelfTest.js +546 -0
  439. package/dist/tools/schemas/codexproSelfTest.js.map +1 -0
  440. package/dist/tools/schemas/common.js +21 -0
  441. package/dist/tools/schemas/common.js.map +1 -0
  442. package/dist/tools/schemas/createTaskWorktree.js +51 -0
  443. package/dist/tools/schemas/createTaskWorktree.js.map +1 -0
  444. package/dist/tools/schemas/edit.js +321 -0
  445. package/dist/tools/schemas/edit.js.map +1 -0
  446. package/dist/tools/schemas/execution.js +330 -0
  447. package/dist/tools/schemas/execution.js.map +1 -0
  448. package/dist/tools/schemas/exportProContext.js +370 -0
  449. package/dist/tools/schemas/exportProContext.js.map +1 -0
  450. package/dist/tools/schemas/getTaskWorktree.js +19 -0
  451. package/dist/tools/schemas/getTaskWorktree.js.map +1 -0
  452. package/dist/tools/schemas/gitBranch.js +42 -0
  453. package/dist/tools/schemas/gitBranch.js.map +1 -0
  454. package/dist/tools/schemas/gitCommit.js +40 -0
  455. package/dist/tools/schemas/gitCommit.js.map +1 -0
  456. package/dist/tools/schemas/gitCreateBranch.js +22 -0
  457. package/dist/tools/schemas/gitCreateBranch.js.map +1 -0
  458. package/dist/tools/schemas/gitDiff.js +222 -0
  459. package/dist/tools/schemas/gitDiff.js.map +1 -0
  460. package/dist/tools/schemas/gitLog.js +41 -0
  461. package/dist/tools/schemas/gitLog.js.map +1 -0
  462. package/dist/tools/schemas/gitRestore.js +47 -0
  463. package/dist/tools/schemas/gitRestore.js.map +1 -0
  464. package/dist/tools/schemas/gitStage.js +33 -0
  465. package/dist/tools/schemas/gitStage.js.map +1 -0
  466. package/dist/tools/schemas/gitStash.js +102 -0
  467. package/dist/tools/schemas/gitStash.js.map +1 -0
  468. package/dist/tools/schemas/gitStatus.js +226 -0
  469. package/dist/tools/schemas/gitStatus.js.map +1 -0
  470. package/dist/tools/schemas/gitV4Common.js +205 -0
  471. package/dist/tools/schemas/gitV4Common.js.map +1 -0
  472. package/dist/tools/schemas/guidance.js +40 -0
  473. package/dist/tools/schemas/guidance.js.map +1 -0
  474. package/dist/tools/schemas/handoffToAgent.js +276 -0
  475. package/dist/tools/schemas/handoffToAgent.js.map +1 -0
  476. package/dist/tools/schemas/handoffToCodex.js +137 -0
  477. package/dist/tools/schemas/handoffToCodex.js.map +1 -0
  478. package/dist/tools/schemas/inspectWorkspace.js +379 -0
  479. package/dist/tools/schemas/inspectWorkspace.js.map +1 -0
  480. package/dist/tools/schemas/listTaskWorktrees.js +21 -0
  481. package/dist/tools/schemas/listTaskWorktrees.js.map +1 -0
  482. package/dist/tools/schemas/listWorkspaces.js +134 -0
  483. package/dist/tools/schemas/listWorkspaces.js.map +1 -0
  484. package/dist/tools/schemas/loadSkill.js +619 -0
  485. package/dist/tools/schemas/loadSkill.js.map +1 -0
  486. package/dist/tools/schemas/mergeTaskWorktree.js +105 -0
  487. package/dist/tools/schemas/mergeTaskWorktree.js.map +1 -0
  488. package/dist/tools/schemas/movePaths.js +175 -0
  489. package/dist/tools/schemas/movePaths.js.map +1 -0
  490. package/dist/tools/schemas/openCurrentWorkspace.js +174 -0
  491. package/dist/tools/schemas/openCurrentWorkspace.js.map +1 -0
  492. package/dist/tools/schemas/openFullAccessWorkspace.js +81 -0
  493. package/dist/tools/schemas/openFullAccessWorkspace.js.map +1 -0
  494. package/dist/tools/schemas/openWorkspace.js +193 -0
  495. package/dist/tools/schemas/openWorkspace.js.map +1 -0
  496. package/dist/tools/schemas/queryAuditEvents.js +193 -0
  497. package/dist/tools/schemas/queryAuditEvents.js.map +1 -0
  498. package/dist/tools/schemas/read.js +199 -0
  499. package/dist/tools/schemas/read.js.map +1 -0
  500. package/dist/tools/schemas/readCodexSession.js +353 -0
  501. package/dist/tools/schemas/readCodexSession.js.map +1 -0
  502. package/dist/tools/schemas/readHandoff.js +446 -0
  503. package/dist/tools/schemas/readHandoff.js.map +1 -0
  504. package/dist/tools/schemas/removeTaskWorktree.js +40 -0
  505. package/dist/tools/schemas/removeTaskWorktree.js.map +1 -0
  506. package/dist/tools/schemas/search.js +235 -0
  507. package/dist/tools/schemas/search.js.map +1 -0
  508. package/dist/tools/schemas/semantic.js +195 -0
  509. package/dist/tools/schemas/semantic.js.map +1 -0
  510. package/dist/tools/schemas/serverConfig.js +157 -0
  511. package/dist/tools/schemas/serverConfig.js.map +1 -0
  512. package/dist/tools/schemas/showChanges.js +283 -0
  513. package/dist/tools/schemas/showChanges.js.map +1 -0
  514. package/dist/tools/schemas/transactionResult.js +11 -0
  515. package/dist/tools/schemas/transactionResult.js.map +1 -0
  516. package/dist/tools/schemas/tree.js +136 -0
  517. package/dist/tools/schemas/tree.js.map +1 -0
  518. package/dist/tools/schemas/undoChangeSet.js +152 -0
  519. package/dist/tools/schemas/undoChangeSet.js.map +1 -0
  520. package/dist/tools/schemas/waitForHandoff.js +462 -0
  521. package/dist/tools/schemas/waitForHandoff.js.map +1 -0
  522. package/dist/tools/schemas/workspaceSnapshot.js +145 -0
  523. package/dist/tools/schemas/workspaceSnapshot.js.map +1 -0
  524. package/dist/tools/schemas/write.js +285 -0
  525. package/dist/tools/schemas/write.js.map +1 -0
  526. package/dist/transactions/atomicFs.js +438 -0
  527. package/dist/transactions/atomicFs.js.map +1 -0
  528. package/dist/transactions/atomicStateFile.js +190 -0
  529. package/dist/transactions/atomicStateFile.js.map +1 -0
  530. package/dist/transactions/engine.js +574 -0
  531. package/dist/transactions/engine.js.map +1 -0
  532. package/dist/transactions/index.js +12 -0
  533. package/dist/transactions/index.js.map +1 -0
  534. package/dist/transactions/installation.js +158 -0
  535. package/dist/transactions/installation.js.map +1 -0
  536. package/dist/transactions/manifestV2Store.js +96 -0
  537. package/dist/transactions/manifestV2Store.js.map +1 -0
  538. package/dist/transactions/participantRecovery.js +98 -0
  539. package/dist/transactions/participantRecovery.js.map +1 -0
  540. package/dist/transactions/recovery.js +513 -0
  541. package/dist/transactions/recovery.js.map +1 -0
  542. package/dist/transactions/schemas.js +365 -0
  543. package/dist/transactions/schemas.js.map +1 -0
  544. package/dist/transactions/stateRoot.js +63 -0
  545. package/dist/transactions/stateRoot.js.map +1 -0
  546. package/dist/transactions/types.js +11 -0
  547. package/dist/transactions/types.js.map +1 -0
  548. package/dist/transactions/workspaceLock.js +201 -0
  549. package/dist/transactions/workspaceLock.js.map +1 -0
  550. package/dist/workspaceOps.js +924 -0
  551. package/dist/workspaceOps.js.map +1 -0
  552. package/dist/worktrees/candidateWorkspace.js +566 -0
  553. package/dist/worktrees/candidateWorkspace.js.map +1 -0
  554. package/dist/worktrees/manager.js +258 -0
  555. package/dist/worktrees/manager.js.map +1 -0
  556. package/dist/worktrees/materializer.js +60 -0
  557. package/dist/worktrees/materializer.js.map +1 -0
  558. package/dist/worktrees/mergeExecute.js +498 -0
  559. package/dist/worktrees/mergeExecute.js.map +1 -0
  560. package/dist/worktrees/mergePlanStore.js +247 -0
  561. package/dist/worktrees/mergePlanStore.js.map +1 -0
  562. package/dist/worktrees/mergePrepare.js +709 -0
  563. package/dist/worktrees/mergePrepare.js.map +1 -0
  564. package/dist/worktrees/recovery.js +294 -0
  565. package/dist/worktrees/recovery.js.map +1 -0
  566. package/dist/worktrees/remove.js +296 -0
  567. package/dist/worktrees/remove.js.map +1 -0
  568. package/dist/worktrees/remover.js +57 -0
  569. package/dist/worktrees/remover.js.map +1 -0
  570. package/dist/worktrees/root.js +47 -0
  571. package/dist/worktrees/root.js.map +1 -0
  572. package/dist/worktrees/service.js +245 -0
  573. package/dist/worktrees/service.js.map +1 -0
  574. package/dist/worktrees/store.js +156 -0
  575. package/dist/worktrees/store.js.map +1 -0
  576. package/dist/worktrees/treeManifest.js +106 -0
  577. package/dist/worktrees/treeManifest.js.map +1 -0
  578. package/dist/worktrees/verificationReceipts.js +215 -0
  579. package/dist/worktrees/verificationReceipts.js.map +1 -0
  580. package/dist/worktrees/verificationTerminal.js +23 -0
  581. package/dist/worktrees/verificationTerminal.js.map +1 -0
  582. package/dist/worktrees/workspaceAuthority.js +64 -0
  583. package/dist/worktrees/workspaceAuthority.js.map +1 -0
  584. package/docs/favicon.svg +5 -0
  585. package/docs/index.html +624 -0
  586. package/docs/og.svg +52 -0
  587. package/docs/script.js +33 -0
  588. package/docs/star.svg +11 -0
  589. package/docs/styles.css +1571 -0
  590. package/docs/zh.html +428 -0
  591. package/package.json +121 -0
  592. package/scripts/analysis-cli-smoke.mjs +81 -0
  593. package/scripts/analysis-smoke.mjs +179 -0
  594. package/scripts/atomic-file.mjs +137 -0
  595. package/scripts/ci-change-classifier.mjs +59 -0
  596. package/scripts/ci-failure-summary.mjs +92 -0
  597. package/scripts/cloudflared-installer.mjs +215 -0
  598. package/scripts/cloudflared-release.mjs +63 -0
  599. package/scripts/codexgpt-entry.mjs +235 -0
  600. package/scripts/codexgpt.mjs +4987 -0
  601. package/scripts/connector-auth-output-shim.cjs +66 -0
  602. package/scripts/doctor-smoke.mjs +90 -0
  603. package/scripts/doctor.mjs +165 -0
  604. package/scripts/exact-head-ci.mjs +126 -0
  605. package/scripts/execute-handoff-smoke-platform-compat.mjs +38 -0
  606. package/scripts/execute-handoff-smoke.mjs +1117 -0
  607. package/scripts/g8-u-live-probe.mjs +208 -0
  608. package/scripts/http-smoke-compat.mjs +151 -0
  609. package/scripts/http-smoke.mjs +776 -0
  610. package/scripts/long-task-runner.mjs +1037 -0
  611. package/scripts/oauth-admin.mjs +1608 -0
  612. package/scripts/output-bounds.mjs +29 -0
  613. package/scripts/owned-temp-root.d.mts +50 -0
  614. package/scripts/owned-temp-root.mjs +567 -0
  615. package/scripts/phase-4a-smoke.mjs +36 -0
  616. package/scripts/policy-windows-spike.mjs +135 -0
  617. package/scripts/pro-apply.mjs +152 -0
  618. package/scripts/pro-bundle.mjs +121 -0
  619. package/scripts/pro-smoke.mjs +97 -0
  620. package/scripts/process-identity.mjs +67 -0
  621. package/scripts/repository-policy.mjs +166 -0
  622. package/scripts/run-and-summarize.mjs +134 -0
  623. package/scripts/run-smoke.mjs +52 -0
  624. package/scripts/run-with-cleanup.mjs +98 -0
  625. package/scripts/settings-smoke-platform-compat.mjs +85 -0
  626. package/scripts/settings-smoke.mjs +769 -0
  627. package/scripts/smoke-platform-compat.mjs +302 -0
  628. package/scripts/smoke.mjs +1180 -0
  629. package/scripts/stress-contract-compat.mjs +94 -0
  630. package/scripts/stress.mjs +882 -0
  631. package/scripts/test-domains.mjs +247 -0
  632. package/scripts/test-execution-profile-manifest.mjs +273 -0
  633. package/scripts/test-execution-profiles.mjs +147 -0
  634. package/scripts/test-performance-reporter.mjs +291 -0
  635. package/scripts/toolchain-manager.mjs +266 -0
  636. package/scripts/toolchains.json +19 -0
  637. package/scripts/windows-conpty-probe-child.mjs +82 -0
  638. package/scripts/windows-conpty-worker.ps1 +24 -0
  639. package/scripts/windows-credential-host-manifest.json +16 -0
  640. package/scripts/windows-credential-host-protocol-v1.json +16 -0
  641. package/scripts/windows-credential-host.cs +256 -0
  642. package/scripts/windows-credential-host.ps1 +26 -0
  643. package/scripts/windows-local-control-manifest.json +23 -0
  644. package/scripts/windows-local-control-spike.cs +738 -0
  645. package/scripts/windows-local-control-spike.mjs +415 -0
  646. package/scripts/windows-local-control-spike.ps1 +35 -0
  647. package/scripts/windows-local-control.cs +738 -0
  648. package/scripts/windows-local-control.ps1 +35 -0
  649. package/scripts/windows-native-api-inventory-v1.json +90 -0
  650. package/scripts/windows-process-host-manifest.json +20 -0
  651. package/scripts/windows-process-host-protocol-v1.json +59 -0
  652. package/scripts/windows-process-host.cs +2385 -0
  653. package/scripts/windows-process-host.ps1 +59 -0
  654. package/scripts/windows-realpath-shim.cjs +78 -0
@@ -0,0 +1,1608 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn, spawnSync } from "node:child_process";
4
+ import { createHash } from "node:crypto";
5
+ import { Resolver as DnsResolver } from "node:dns/promises";
6
+ import fs from "node:fs";
7
+ import http from "node:http";
8
+ import https from "node:https";
9
+ import net from "node:net";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import { createInterface } from "node:readline/promises";
13
+ import { fileURLToPath, pathToFileURL } from "node:url";
14
+ import {
15
+ installVerifiedCloudflared,
16
+ managedCloudflaredInstallPath,
17
+ verifiedInstalledCloudflaredVersion
18
+ } from "./cloudflared-installer.mjs";
19
+
20
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
21
+ const projectRoot = path.resolve(scriptDir, "..");
22
+ const DEFAULT_PUBLIC_PORT = 8787;
23
+ const DEFAULT_LOCAL_ADMIN_PORT = 8788;
24
+ const SETUP_JOURNAL_SCHEMA = 1;
25
+ const SAFE_BACKUP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/;
26
+
27
+ export function authUsage() {
28
+ return `CodexGPT OAuth administration
29
+
30
+ Usage:
31
+ codexgpt auth setup --root <workspace> --hostname <host> --tunnel-name <name>
32
+ codexgpt auth status --root <workspace> [--json]
33
+ codexgpt auth pending --root <workspace>
34
+ codexgpt auth open --root <workspace>
35
+ codexgpt auth approve <correlation-code> --root <workspace>
36
+ codexgpt auth deny <correlation-code> --root <workspace>
37
+ codexgpt auth clients --root <workspace>
38
+ codexgpt auth client remove <client-id> --root <workspace>
39
+ codexgpt auth prune --unapproved --root <workspace>
40
+ codexgpt auth revoke <grant-id> --root <workspace>
41
+ codexgpt auth revoke --all --root <workspace>
42
+ codexgpt auth rotate-signing-key [--revoke-all] --root <workspace>
43
+ codexgpt auth rollback --root <workspace>
44
+ codexgpt auth recover inspect --root <workspace>
45
+ codexgpt auth recover restore <backup-id> --root <workspace>
46
+ codexgpt auth recover unlock <exact-owner-id> --root <workspace>
47
+ codexgpt auth reinitialize --revoke-all --root <workspace>
48
+ codexgpt auth rebind --from-root <old> --root <new> --hostname <host> --revoke-all
49
+
50
+ Setup controls:
51
+ --no-tunnel-changes Perform local checks and print exact remaining Cloudflare commands.
52
+ --provision-tunnel Explicitly allow bounded named-tunnel and DNS provisioning.
53
+ --no-start Stop the verified candidate after setup commits.
54
+ --public-port <port> Public loopback listener. Default: 8787.
55
+ --local-admin-port <port> Local-only admin listener. Default: 8788.
56
+ --cloudflare-config <path> Dedicated generated ingress config path.
57
+ --legacy-hostname <host> One-time pre-route migration input for auth rollback.
58
+ --legacy-tunnel-name <name> One-time retained Legacy named-tunnel input.
59
+ --legacy-public-port <port> One-time retained Legacy loopback port input.
60
+ --confirm-revoke-all Required for noninteractive owner-wide grant revocation.
61
+ --confirm-forced-relink Required for noninteractive backup restore.
62
+ --confirm-dead-owner Required for noninteractive stale-lock recovery.
63
+ --confirm-reinitialize Required for noninteractive deployment reinitialization.
64
+ `;
65
+ }
66
+
67
+ function authError(code, message, repairCommand = "") {
68
+ const error = Object.assign(new Error(`${code}: ${message}`), { code });
69
+ if (repairCommand) error.repairCommand = repairCommand;
70
+ return error;
71
+ }
72
+
73
+ function expandHome(input) {
74
+ if (input === "~") return os.homedir();
75
+ if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(os.homedir(), input.slice(2));
76
+ return input;
77
+ }
78
+
79
+ function CodexGPTHome(env = process.env) {
80
+ const configured = env.CODEXGPT_HOME?.trim();
81
+ return configured ? path.resolve(expandHome(configured)) : path.join(os.homedir(), ".codexgpt");
82
+ }
83
+
84
+ function commandArgument(value) {
85
+ const text = String(value);
86
+ return /^[A-Za-z0-9._:/=-]+$/.test(text) ? text : `"${text.replaceAll('"', '""')}"`;
87
+ }
88
+
89
+ function supportedCommand(args) {
90
+ const installedPackage = projectRoot.split(path.sep).some((part) => part.toLocaleLowerCase("en-US") === "node_modules");
91
+ const prefix = installedPackage
92
+ ? "codexgpt"
93
+ : `node ${commandArgument(path.join(projectRoot, "scripts", "codexgpt-entry.mjs"))}`;
94
+ return `${prefix} ${args.map(commandArgument).join(" ")}`.trim();
95
+ }
96
+
97
+ function optionValue(argv, name) { const inline = argv.find((value) => value.startsWith(`--${name}=`));
98
+ if (inline) return inline.slice(name.length + 3);
99
+ const index = argv.indexOf(`--${name}`);
100
+ if (index >= 0 && argv[index + 1] && !argv[index + 1].startsWith("--")) return argv[index + 1];
101
+ return "";
102
+ }
103
+
104
+ function hasFlag(argv, name) {
105
+ return argv.includes(`--${name}`);
106
+ }
107
+
108
+ function exactPort(value, label) {
109
+ const parsed = Number(value);
110
+ if (!/^\d+$/.test(String(value)) || !Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
111
+ throw authError("OAUTH_DEPLOYMENT_INVALID", `${label} must be an integer from 1 to 65535.`);
112
+ }
113
+ return parsed;
114
+ }
115
+
116
+ export function parseAuthInvocation(argv) {
117
+ const command = argv[0] ?? "help";
118
+ let operation = command;
119
+ let target = "";
120
+ if (command === "client") {
121
+ operation = `client.${argv[1] ?? ""}`;
122
+ target = argv[2] ?? "";
123
+ } else if (command === "recover") {
124
+ operation = `recover.${argv[1] ?? ""}`;
125
+ target = argv[2] ?? "";
126
+ } else {
127
+ target = argv[1] && !argv[1].startsWith("--") ? argv[1] : "";
128
+ }
129
+ return Object.freeze({ command, operation, target, argv: [...argv] });
130
+ }
131
+
132
+ function canonicalDirectory(input) {
133
+ const resolved = path.resolve(expandHome(input));
134
+ let canonical;
135
+ try {
136
+ canonical = fs.realpathSync.native(resolved);
137
+ } catch {
138
+ throw authError("OAUTH_ROOT_REQUIRED", `Workspace root does not exist: ${resolved}`);
139
+ }
140
+ if (!fs.statSync(canonical).isDirectory()) throw authError("OAUTH_ROOT_REQUIRED", `Workspace root is not a directory: ${canonical}`);
141
+ return canonical;
142
+ }
143
+
144
+ function profileIdForRoot(root) {
145
+ return createHash("sha256").update(root).digest("hex").slice(0, 24);
146
+ }
147
+
148
+ async function runtimeModules() {
149
+ const dist = (...parts) => pathToFileURL(path.join(projectRoot, "dist", ...parts)).href;
150
+ const [profile, stateRoot, auth, control, audit, locks, config] = await Promise.all([
151
+ import(dist("profileStore.js")),
152
+ import(dist("transactions", "stateRoot.js")),
153
+ import(dist("auth", "index.js")),
154
+ import(dist("control", "localApprovalClient.js")),
155
+ import(dist("audit", "store.js")),
156
+ import(dist("transactions", "workspaceLock.js")),
157
+ import(dist("config.js"))
158
+ ]);
159
+ return { profile, stateRoot, auth, control, audit, locks, config };
160
+ }
161
+
162
+ function resolveRoot(argv, profileModule, options = {}) {
163
+ const explicit = optionValue(argv, options.name ?? "root");
164
+ if (explicit) return canonicalDirectory(explicit);
165
+ const cwd = canonicalDirectory(process.cwd());
166
+ const profile = profileModule.readWorkspaceProfile(cwd);
167
+ if (profile.root === cwd) return cwd;
168
+ throw authError(
169
+ "OAUTH_ROOT_REQUIRED",
170
+ "OAuth administration requires --root unless the current directory has one exact matching profile.",
171
+ supportedCommand(["auth", argv[0] ?? "status", "--root", cwd])
172
+ );
173
+ }
174
+
175
+ function oauthStateRoots(modules) {
176
+ const transactionStateRoot = modules.stateRoot.resolveTransactionStateRoot();
177
+ return { transactionStateRoot, oauthStateRoot: path.join(transactionStateRoot, "oauth") };
178
+ }
179
+
180
+ function stableProfilePayload(profile, overrides = {}) {
181
+ const { profilePath, version, updatedAt, ...rest } = profile ?? {};
182
+ return { ...rest, ...overrides };
183
+ }
184
+
185
+ function writeJsonAtomic(filePath, value) {
186
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
187
+ const temporary = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.tmp`);
188
+ let descriptor;
189
+ try {
190
+ descriptor = fs.openSync(temporary, "wx", 0o600);
191
+ fs.writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}\n`, "utf8");
192
+ fs.fsyncSync(descriptor);
193
+ fs.closeSync(descriptor);
194
+ descriptor = undefined;
195
+ fs.renameSync(temporary, filePath);
196
+ try { fs.chmodSync(filePath, 0o600); } catch {}
197
+ } finally {
198
+ if (descriptor !== undefined) fs.closeSync(descriptor);
199
+ fs.rmSync(temporary, { force: true });
200
+ }
201
+ }
202
+
203
+ function writeTextAtomic(filePath, text) {
204
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
205
+ const temporary = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.tmp`);
206
+ let descriptor;
207
+ try {
208
+ descriptor = fs.openSync(temporary, "wx", 0o600);
209
+ fs.writeFileSync(descriptor, text, "utf8");
210
+ fs.fsyncSync(descriptor);
211
+ fs.closeSync(descriptor);
212
+ descriptor = undefined;
213
+ fs.renameSync(temporary, filePath);
214
+ try { fs.chmodSync(filePath, 0o600); } catch {}
215
+ } finally {
216
+ if (descriptor !== undefined) fs.closeSync(descriptor);
217
+ fs.rmSync(temporary, { force: true });
218
+ }
219
+ }
220
+
221
+ export function setupJournalPath(root) {
222
+ return path.join(CodexGPTHome(), "auth-setup", `${profileIdForRoot(root)}.json`);
223
+ }
224
+
225
+ export function readSetupJournal(root) {
226
+ try {
227
+ const parsed = JSON.parse(fs.readFileSync(setupJournalPath(root), "utf8"));
228
+ if (
229
+ parsed?.schemaVersion !== SETUP_JOURNAL_SCHEMA ||
230
+ parsed.profileId !== profileIdForRoot(root) ||
231
+ parsed.canonicalRoot !== root
232
+ ) return null;
233
+ return parsed;
234
+ } catch {
235
+ return null;
236
+ }
237
+ }
238
+
239
+ export function updateSetupJournal(root, values) {
240
+ const filePath = setupJournalPath(root);
241
+ const previous = readSetupJournal(root) ?? {};
242
+ const now = new Date().toISOString();
243
+ const existingHistory = Array.isArray(previous.history)
244
+ ? previous.history.filter((entry) => entry && typeof entry.phase === "string" && typeof entry.at === "string")
245
+ : [];
246
+ const nextHistory = typeof values.phase === "string" && existingHistory.at(-1)?.phase !== values.phase
247
+ ? [...existingHistory, { phase: values.phase, at: now }].slice(-64)
248
+ : existingHistory.slice(-64);
249
+ const journal = {
250
+ ...previous,
251
+ ...values,
252
+ schemaVersion: SETUP_JOURNAL_SCHEMA,
253
+ profileId: profileIdForRoot(root),
254
+ canonicalRoot: root,
255
+ createdAt: previous.createdAt ?? now,
256
+ updatedAt: now,
257
+ history: nextHistory
258
+ };
259
+ writeJsonAtomic(filePath, journal);
260
+ return journal;
261
+ }
262
+
263
+ function managedCloudflaredPath() {
264
+ return managedCloudflaredInstallPath();
265
+ }
266
+
267
+ function defaultCloudflareConfigPath(root) {
268
+ return path.join(CodexGPTHome(), "oauth", "tunnels", profileIdForRoot(root), "config.yml");
269
+ }
270
+
271
+ function dedicatedTunnelName(tunnelName) {
272
+ const suffix = "-dedicated";
273
+ if (tunnelName.endsWith(suffix)) return tunnelName;
274
+ return `${tunnelName.slice(0, 128 - suffix.length)}${suffix}`;
275
+ }
276
+
277
+ function sameNativePath(left, right) {
278
+ const a = path.resolve(left);
279
+ const b = path.resolve(right);
280
+ return process.platform === "win32"
281
+ ? a.toLocaleLowerCase("en-US") === b.toLocaleLowerCase("en-US")
282
+ : a === b;
283
+ }
284
+
285
+ function dedicatedTunnelConfigPath(root, configPath) {
286
+ const defaultPath = path.resolve(defaultCloudflareConfigPath(root));
287
+ const directory = path.dirname(defaultPath);
288
+ const candidates = [defaultPath];
289
+ for (let index = 1; index <= 100; index += 1) {
290
+ candidates.push(path.join(directory, index === 1 ? "config.dedicated.yml" : `config.dedicated-${index}.yml`));
291
+ }
292
+ const selected = candidates.find((candidate) => !sameNativePath(candidate, configPath) && !fs.existsSync(candidate));
293
+ if (!selected) {
294
+ throw authError("AUTH_TUNNEL_CONFIG_PATH_EXHAUSTED", "No unused dedicated Cloudflare config path is available.");
295
+ }
296
+ return selected;
297
+ }
298
+
299
+ function dedicatedTunnelCommand(input) {
300
+ return supportedCommand([
301
+ "auth", "setup",
302
+ "--root", input.root,
303
+ "--hostname", input.hostname,
304
+ "--tunnel-name", dedicatedTunnelName(input.tunnelName),
305
+ "--cloudflare-config", dedicatedTunnelConfigPath(input.root, input.configPath)
306
+ ]);
307
+ }
308
+
309
+ function tunnelOwnerMarkerPath(configPath) {
310
+ return `${configPath}.owner.json`;
311
+ }
312
+
313
+ export function writeTunnelOwnerMarker(configPath, input) {
314
+ writeJsonAtomic(tunnelOwnerMarkerPath(configPath), {
315
+ schemaVersion: 1,
316
+ owner: "codexgpt",
317
+ profileId: input.profileId,
318
+ bindingId: input.bindingId,
319
+ tunnelId: input.tunnelId,
320
+ tunnelName: input.tunnelName,
321
+ hostname: input.hostname,
322
+ createdAt: new Date().toISOString()
323
+ });
324
+ }
325
+
326
+ export function validateTunnelOwnerMarker(configPath, input) {
327
+ let marker;
328
+ try { marker = JSON.parse(fs.readFileSync(tunnelOwnerMarkerPath(configPath), "utf8")); }
329
+ catch { throw authError("AUTH_TUNNEL_OWNERSHIP_UNPROVEN", "Dedicated tunnel config lacks a CodexGPT ownership marker."); }
330
+ if (
331
+ marker?.schemaVersion !== 1 || marker.owner !== "codexgpt" ||
332
+ marker.profileId !== input.profileId || marker.bindingId !== input.bindingId ||
333
+ marker.tunnelId !== input.tunnelId || marker.tunnelName !== input.tunnelName ||
334
+ marker.hostname !== input.hostname
335
+ ) {
336
+ throw authError("AUTH_TUNNEL_OWNERSHIP_UNPROVEN", "Dedicated tunnel ownership marker does not match this workspace and deployment.");
337
+ }
338
+ return marker;
339
+ }
340
+
341
+ export function preflightExistingTunnelConfig(input) {
342
+ if (!fs.existsSync(input.configPath)) return null;
343
+ const repairCommand = dedicatedTunnelCommand(input);
344
+ const text = fs.readFileSync(input.configPath, "utf8");
345
+ let validation;
346
+ try {
347
+ validation = input.validateDedicatedTunnelConfig(text, {
348
+ hostname: input.hostname,
349
+ publicPort: input.publicPort,
350
+ localAdminPort: input.localAdminPort
351
+ });
352
+ } catch (error) {
353
+ if (["AUTH_TUNNEL_SHARED_CONFIG", "AUTH_TUNNEL_ADMIN_EXPOSED", "AUTH_TUNNEL_INGRESS_INVALID"].includes(error?.code)) {
354
+ throw authError(
355
+ error.code,
356
+ "Existing Cloudflare config is shared or unsafe and will not be modified.",
357
+ repairCommand
358
+ );
359
+ }
360
+ throw error;
361
+ }
362
+
363
+ let marker;
364
+ try { marker = JSON.parse(fs.readFileSync(tunnelOwnerMarkerPath(input.configPath), "utf8")); }
365
+ catch {
366
+ throw authError(
367
+ "AUTH_TUNNEL_OWNERSHIP_UNPROVEN",
368
+ "Existing Cloudflare config is not owned by this CodexGPT deployment and will not be modified.",
369
+ repairCommand
370
+ );
371
+ }
372
+ if (
373
+ marker?.schemaVersion !== 1 || marker.owner !== "codexgpt" ||
374
+ marker.profileId !== input.profileId ||
375
+ !/^binding_[a-f0-9]{32}$/.test(String(marker.bindingId ?? "")) ||
376
+ marker.tunnelId !== validation.tunnelId || marker.tunnelName !== input.tunnelName ||
377
+ marker.hostname !== input.hostname
378
+ ) {
379
+ throw authError(
380
+ "AUTH_TUNNEL_OWNERSHIP_UNPROVEN",
381
+ "Existing Cloudflare ownership marker does not match this workspace and tunnel and will not be modified.",
382
+ repairCommand
383
+ );
384
+ }
385
+ return { text, validation, marker };
386
+ }
387
+
388
+ function powershellPath() {
389
+ const drive = path.parse(process.execPath).root;
390
+ return path.join(drive, "Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
391
+ }
392
+
393
+ function persistedUserAuthMode() {
394
+ if (process.platform !== "win32") return "";
395
+ const result = spawnSync(powershellPath(), [
396
+ "-NoLogo", "-NoProfile", "-NonInteractive", "-Command",
397
+ "[Console]::Out.Write([Environment]::GetEnvironmentVariable('CODEXGPT_AUTH_MODE','User'))"
398
+ ], { encoding: "utf8", windowsHide: true, timeout: 5_000 });
399
+ return result.status === 0 ? String(result.stdout ?? "").trim() : "";
400
+ }
401
+
402
+ function windowsProcessCreationTime(pid) {
403
+ if (process.platform !== "win32" || !Number.isSafeInteger(pid) || pid <= 0) return "";
404
+ const command = `$p=Get-Process -Id ${pid} -ErrorAction Stop;[Console]::Out.Write($p.StartTime.ToUniversalTime().ToString('O'))`;
405
+ const result = spawnSync(powershellPath(), [
406
+ "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command
407
+ ], { encoding: "utf8", windowsHide: true, timeout: 5_000 });
408
+ return result.status === 0 ? String(result.stdout ?? "").trim() : "";
409
+ }
410
+
411
+ function currentOAuthRuntime(modules, oauthStateRoot, profileId) {
412
+ const runtime = modules.auth.readOAuthRuntimeStatus(oauthStateRoot, profileId);
413
+ if (!runtime) return null;
414
+ const liveCreationTime = windowsProcessCreationTime(runtime.pid);
415
+ if (liveCreationTime && liveCreationTime === runtime.processCreationTime) return runtime;
416
+ modules.auth.removeOAuthRuntimeStatus(oauthStateRoot, profileId, {
417
+ pid: runtime.pid,
418
+ serverId: runtime.serverId
419
+ });
420
+ return null;
421
+ }
422
+
423
+ function checkPortFree(port) {
424
+ return new Promise((resolve, reject) => {
425
+ const server = net.createServer();
426
+ server.once("error", (error) => reject(authError("OAUTH_PORT_IN_USE", `Local port ${port} is already in use.`, "Stop the owning process or select a different port.")));
427
+ server.listen(port, "127.0.0.1", () => server.close((error) => error ? reject(error) : resolve()));
428
+ });
429
+ }
430
+
431
+ function safeRuntimeView(runtime) {
432
+ if (!runtime) return null;
433
+ return {
434
+ running: true,
435
+ pid: runtime.pid,
436
+ startedAt: runtime.startedAt,
437
+ localAdminOrigin: runtime.localAdminOrigin,
438
+ bindingId: runtime.bindingId,
439
+ incarnationId: runtime.incarnationId
440
+ };
441
+ }
442
+
443
+ async function onlineContext(root, modules) {
444
+ const profile = modules.profile.readWorkspaceProfile(root);
445
+ const profileId = modules.profile.profileIdForRoot(root);
446
+ const { transactionStateRoot, oauthStateRoot } = oauthStateRoots(modules);
447
+ const runtime = currentOAuthRuntime(modules, oauthStateRoot, profileId);
448
+ if (!runtime || runtime.canonicalRoot !== root) {
449
+ throw authError(
450
+ "OAUTH_SERVICE_NOT_RUNNING",
451
+ "The OAuth service is not running for this workspace.",
452
+ supportedCommand(["start", "--root", root])
453
+ );
454
+ }
455
+ const client = new modules.control.LocalApprovalClient({ stateBaseRoot: transactionStateRoot });
456
+ return { profile, profileId, transactionStateRoot, oauthStateRoot, runtime, client };
457
+ }
458
+
459
+ async function withOfflineAuth(root, modules, action) {
460
+ const profile = modules.profile.readWorkspaceProfile(root);
461
+ const profileId = modules.profile.profileIdForRoot(root);
462
+ const { transactionStateRoot, oauthStateRoot } = oauthStateRoots(modules);
463
+ const runtime = currentOAuthRuntime(modules, oauthStateRoot, profileId);
464
+ if (runtime) throw authError("OAUTH_STATE_BUSY", "Stop the OAuth service before running this offline recovery operation.");
465
+ const auditRegistry = new modules.locks.ProcessInstanceRegistry(transactionStateRoot);
466
+ const auditStore = modules.audit.PersistentAuditStore.open({
467
+ stateRoot: transactionStateRoot,
468
+ registry: auditRegistry,
469
+ retention: { maxAgeDays: 30, maxClosedBytes: 512 * 1024 * 1024 }
470
+ });
471
+ const credentialStore = modules.auth.createProductionCredentialStore();
472
+ const authInstance = new modules.auth.AuthProcessInstanceRegistry(oauthStateRoot);
473
+ try {
474
+ await credentialStore.probe();
475
+ const store = new modules.auth.AuthStateStore(oauthStateRoot, credentialStore, {
476
+ audit: new modules.auth.PersistentAuthStateAuditAppender(auditStore)
477
+ });
478
+ const locks = new modules.auth.AuthStateLock(oauthStateRoot, authInstance);
479
+ const registry = new modules.auth.DeploymentRegistry(store);
480
+ const coordinator = new modules.auth.AuthDeploymentCoordinator(
481
+ store,
482
+ new modules.auth.AuthKeyManager(credentialStore),
483
+ registry,
484
+ locks
485
+ );
486
+ return await action({ profile, profileId, transactionStateRoot, oauthStateRoot, store, locks, registry, coordinator });
487
+ } finally {
488
+ authInstance.dispose();
489
+ auditStore.dispose();
490
+ auditRegistry.dispose();
491
+ }
492
+ }
493
+
494
+ function deploymentConfiguration(root, profile, modules, overrides = {}) {
495
+ const profileId = modules.profile.profileIdForRoot(root);
496
+ const oauthRoute = profile.authRoutes?.oauth ?? {};
497
+ return modules.auth.resolveOAuthDeploymentConfiguration({
498
+ canonicalRoot: root,
499
+ profileId,
500
+ hostname: overrides.hostname ?? oauthRoute.hostname ?? profile.hostname ?? "",
501
+ issuer: overrides.issuer ?? profile.oauthIssuer,
502
+ resource: overrides.resource ?? profile.oauthResource,
503
+ tunnel: overrides.tunnel ?? oauthRoute.tunnel ?? profile.tunnel ?? "",
504
+ tunnelName: overrides.tunnelName ?? oauthRoute.tunnelName ?? profile.tunnelName ?? "",
505
+ tunnelOwner: overrides.tunnelOwner ?? oauthRoute.tunnelOwner ?? profile.tunnelOwner ?? "",
506
+ publicHost: "127.0.0.1",
507
+ publicPort: Number(overrides.publicPort ?? oauthRoute.port ?? profile.port ?? DEFAULT_PUBLIC_PORT),
508
+ localAdminHost: "127.0.0.1",
509
+ localAdminPort: Number(overrides.localAdminPort ?? oauthRoute.localAdminPort ?? profile.localAdminPort ?? DEFAULT_LOCAL_ADMIN_PORT)
510
+ });
511
+ }
512
+
513
+ function printRows(rows) {
514
+ for (const [label, value] of rows) console.log(`${String(label).padEnd(14)} ${value}`);
515
+ }
516
+
517
+ function safeLists(authorizations, clients, grants) {
518
+ return {
519
+ authorizations: authorizations.oauthAuthorizations ?? [],
520
+ clients: clients.oauthClients ?? [],
521
+ grants: grants.oauthGrants ?? []
522
+ };
523
+ }
524
+
525
+ async function commandStatus(root, modules, json = false) {
526
+ const profile = modules.profile.readWorkspaceProfile(root);
527
+ const resolution = modules.auth.resolveHttpAuthMode({
528
+ currentProcess: process.env.CODEXGPT_AUTH_MODE,
529
+ persistedUser: persistedUserAuthMode() || undefined,
530
+ profile: profile.authMode
531
+ });
532
+ const { oauthStateRoot } = oauthStateRoots(modules);
533
+ const runtime = currentOAuthRuntime(modules, oauthStateRoot, modules.profile.profileIdForRoot(root));
534
+ let live = null;
535
+ if (runtime) {
536
+ try {
537
+ const context = await onlineContext(root, modules);
538
+ const [authorizations, clients, grants] = await Promise.all([
539
+ context.client.listOAuthAuthorizations(runtime.serverId),
540
+ context.client.listOAuthClients(runtime.serverId),
541
+ context.client.listOAuthGrants(runtime.serverId)
542
+ ]);
543
+ live = safeLists(authorizations, clients, grants);
544
+ } catch {
545
+ live = null;
546
+ }
547
+ }
548
+ const result = {
549
+ root,
550
+ mode: resolution.mode,
551
+ modeSource: resolution.source,
552
+ configured: profile.authMode === "oauth",
553
+ hostname: profile.hostname ?? null,
554
+ resource: profile.oauthResource ?? null,
555
+ tunnel: profile.tunnel ?? null,
556
+ tunnelName: profile.tunnelName ?? null,
557
+ runtime: safeRuntimeView(runtime),
558
+ live
559
+ };
560
+ if (json) console.log(JSON.stringify(result, null, 2));
561
+ else printRows([
562
+ ["Workspace", root],
563
+ ["Mode", `${result.mode} (${result.modeSource})`],
564
+ ["Hostname", result.hostname ?? "not configured"],
565
+ ["Tunnel", result.tunnelName ? `${result.tunnel} / ${result.tunnelName}` : "not configured"],
566
+ ["Runtime", runtime ? `running pid=${runtime.pid}` : "stopped"],
567
+ ["Pending", live ? live.authorizations.filter((entry) => entry.status === "pending").length : "unavailable"],
568
+ ["Clients", live ? live.clients.filter((entry) => entry.status !== "revoked").length : "unavailable"],
569
+ ["Active grants", live ? live.grants.filter((entry) => entry.status === "active").length : "unavailable"]
570
+ ]);
571
+ return result;
572
+ }
573
+
574
+ async function listOnline(root, modules, kind) {
575
+ const context = await onlineContext(root, modules);
576
+ const response = kind === "pending"
577
+ ? await context.client.listOAuthAuthorizations(context.runtime.serverId)
578
+ : kind === "clients"
579
+ ? await context.client.listOAuthClients(context.runtime.serverId)
580
+ : await context.client.listOAuthGrants(context.runtime.serverId);
581
+ const entries = kind === "pending" ? response.oauthAuthorizations ?? [] : kind === "clients" ? response.oauthClients ?? [] : response.oauthGrants ?? [];
582
+ if (entries.length === 0) {
583
+ console.log(`No OAuth ${kind}.`);
584
+ return entries;
585
+ }
586
+ for (const entry of entries) {
587
+ if (kind === "pending") {
588
+ console.log(`${entry.correlationCode} ${entry.status} ${entry.clientLabel} ${entry.scopes.join(",")} expires=${entry.expiresAt}`);
589
+ } else if (kind === "clients") {
590
+ console.log(`${entry.clientId} ${entry.status} ${entry.label} ${entry.redirectHost}${entry.redirectPath}`);
591
+ } else {
592
+ console.log(`${entry.grantId} ${entry.status} ${entry.scopes.join(",")} expires=${entry.absoluteExpiresAt}`);
593
+ }
594
+ }
595
+ return entries;
596
+ }
597
+
598
+ async function resolvePending(context, target) {
599
+ const response = await context.client.listOAuthAuthorizations(context.runtime.serverId);
600
+ const normalized = target.toUpperCase();
601
+ const matches = (response.oauthAuthorizations ?? []).filter((entry) =>
602
+ entry.status === "pending" && (entry.pendingId === target || entry.correlationCode === normalized)
603
+ );
604
+ if (matches.length !== 1) throw authError("OAUTH_AUTHORIZATION_NOT_FOUND", "The pending authorization was not found or was ambiguous.");
605
+ return matches[0];
606
+ }
607
+
608
+ function openBrowser(url) {
609
+ if (!/^http:\/\/127\.0\.0\.1:\d{1,5}\/#bootstrap=[A-Za-z0-9_-]{43}$/.test(url)) {
610
+ throw authError("AUTH_ADMIN_BOOTSTRAP_INVALID", "Refusing to open an invalid local admin bootstrap URL.");
611
+ }
612
+ const windows = process.platform === "win32";
613
+ const executable = windows ? powershellPath() : "xdg-open";
614
+ const args = windows
615
+ ? ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "Start-Process -FilePath $env:CODEXGPT_ADMIN_BOOTSTRAP"]
616
+ : [url];
617
+ const result = spawnSync(executable, args, {
618
+ env: windows ? { ...process.env, CODEXGPT_ADMIN_BOOTSTRAP: url } : process.env,
619
+ stdio: "ignore",
620
+ windowsHide: true,
621
+ timeout: 10_000
622
+ });
623
+ if (result.error || result.status !== 0) {
624
+ throw authError("AUTH_ADMIN_BROWSER_OPEN_FAILED", "The local OAuth administration page could not be opened in the current Windows session.");
625
+ }
626
+ }
627
+
628
+ export function cloudflaredSuccessOutput(result) {
629
+ return String(result?.stdout ?? "");
630
+ }
631
+
632
+ function runCloudflared(binary, args, options = {}) {
633
+ const result = spawnSync(binary, args, {
634
+ cwd: options.cwd ?? process.cwd(),
635
+ env: options.env ?? process.env,
636
+ encoding: "utf8",
637
+ windowsHide: true,
638
+ timeout: options.timeout ?? 60_000,
639
+ maxBuffer: 1024 * 1024
640
+ });
641
+ if (result.error || result.status !== 0) {
642
+ const detail = `${result.stderr ?? ""}${result.stdout ?? ""}`.trim();
643
+ throw authError("AUTH_TUNNEL_COMMAND_FAILED", detail || result.error?.message || `cloudflared exited ${result.status}`);
644
+ }
645
+ return cloudflaredSuccessOutput(result);
646
+ }
647
+
648
+ function cloudflareLoginMaterial() {
649
+ return path.join(os.homedir(), ".cloudflared", "cert.pem");
650
+ }
651
+
652
+ function listCloudflareTunnels(binary) {
653
+ const output = runCloudflared(binary, ["tunnel", "--no-autoupdate", "list", "--output", "json"]);
654
+ let parsed;
655
+ try { parsed = JSON.parse(output); } catch { throw authError("AUTH_TUNNEL_LIST_INVALID", "cloudflared returned invalid tunnel inventory JSON."); }
656
+ return Array.isArray(parsed) ? parsed : [];
657
+ }
658
+
659
+ function tunnelIdentity(entry) {
660
+ const id = String(entry?.id ?? entry?.uuid ?? "").toLowerCase();
661
+ const name = String(entry?.name ?? "");
662
+ return { id, name };
663
+ }
664
+
665
+ export function selectOwnedTunnel(inventory, tunnelName, expectedTunnelId = "") {
666
+ const matches = inventory.map(tunnelIdentity).filter((entry) => entry.name === tunnelName);
667
+ if (matches.length > 1) {
668
+ throw authError("AUTH_TUNNEL_NAME_AMBIGUOUS", "Multiple Cloudflare tunnels have the requested name.");
669
+ }
670
+ if (matches.length === 0) return null;
671
+ if (matches[0].id !== expectedTunnelId) {
672
+ throw authError(
673
+ "AUTH_TUNNEL_NAME_CONFLICT",
674
+ `Cloudflare tunnel name '${tunnelName}' already exists without exact ownership evidence. Choose a new dedicated tunnel name.`
675
+ );
676
+ }
677
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(matches[0].id)) {
678
+ throw authError("AUTH_TUNNEL_CREATE_UNVERIFIED", "Cloudflare tunnel id is invalid.");
679
+ }
680
+ return matches[0];
681
+ }
682
+
683
+ function ensureTunnel(binary, tunnelName, expectedTunnelId = "") {
684
+ if (!fs.existsSync(cloudflareLoginMaterial())) {
685
+ throw authError("AUTH_TUNNEL_LOGIN_REQUIRED", "Cloudflare owner login material is missing.");
686
+ }
687
+ let tunnels = listCloudflareTunnels(binary);
688
+ let selected = selectOwnedTunnel(tunnels, tunnelName, expectedTunnelId);
689
+ let created = false;
690
+ if (!selected) {
691
+ runCloudflared(binary, ["tunnel", "--no-autoupdate", "create", tunnelName]);
692
+ created = true;
693
+ tunnels = listCloudflareTunnels(binary);
694
+ const createdMatches = tunnels.map(tunnelIdentity).filter((entry) => entry.name === tunnelName);
695
+ if (createdMatches.length !== 1 || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(createdMatches[0].id)) {
696
+ throw authError("AUTH_TUNNEL_CREATE_UNVERIFIED", "The created Cloudflare tunnel could not be identified exactly.");
697
+ }
698
+ selected = createdMatches[0];
699
+ }
700
+ return { tunnelId: selected.id, created };
701
+ }
702
+
703
+ export function tunnelDnsRouteArgs(tunnelId, hostname) {
704
+ const normalizedTunnelId = String(tunnelId ?? "").toLowerCase();
705
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(normalizedTunnelId)) {
706
+ throw authError("AUTH_TUNNEL_CREATE_UNVERIFIED", "Cloudflare tunnel id is invalid.");
707
+ }
708
+ return ["tunnel", "--no-autoupdate", "route", "dns", normalizedTunnelId, hostname];
709
+ }
710
+
711
+ function routeTunnelDns(binary, tunnelId, hostname) {
712
+ runCloudflared(binary, tunnelDnsRouteArgs(tunnelId, hostname));
713
+ }
714
+
715
+ function printTunnelPlan(root, binary, tunnelName, hostname, configPath, publicPort) {
716
+ console.log("Cloudflare changes not performed.");
717
+ console.log(`Resume command: ${supportedCommand(["auth", "setup", "--root", root, "--hostname", hostname, "--tunnel-name", tunnelName, "--provision-tunnel"])}`);
718
+ console.log("Remaining commands:");
719
+ console.log(` "${binary}" tunnel login`);
720
+ console.log(` "${binary}" tunnel create ${tunnelName}`);
721
+ console.log(` "${binary}" tunnel route dns ${tunnelName} ${hostname}`);
722
+ console.log(` Write dedicated ingress to: ${configPath}`);
723
+ console.log(` Required ingress: ${hostname} -> http://127.0.0.1:${publicPort}; final http_status:404`);
724
+ }
725
+
726
+ async function confirmProvision(tunnelName, hostname) {
727
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
728
+ try {
729
+ const answer = await rl.question(`Create/reuse Cloudflare tunnel '${tunnelName}' and route '${hostname}'? Type YES: `);
730
+ return answer === "YES";
731
+ } finally {
732
+ rl.close();
733
+ }
734
+ }
735
+
736
+ async function requireDestructiveConfirmation(argv, flag, phrase, effect) {
737
+ if (hasFlag(argv, flag)) return;
738
+ if (!process.stdin.isTTY) {
739
+ throw authError(
740
+ "AUTH_CONFIRMATION_REQUIRED",
741
+ `${effect} Noninteractive execution requires --${flag}.`,
742
+ `Re-run the same command with --${flag}`
743
+ );
744
+ }
745
+ console.log(effect);
746
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
747
+ try {
748
+ const answer = await rl.question(`Type ${phrase} to continue: `);
749
+ if (answer !== phrase) throw authError("AUTH_CONFIRMATION_DECLINED", "Destructive OAuth operation was not confirmed.");
750
+ } finally {
751
+ rl.close();
752
+ }
753
+ }
754
+
755
+ function stopProcessTree(child) {
756
+ if (!child || child.exitCode !== null || child.signalCode !== null) return;
757
+ if (process.platform === "win32" && child.pid) {
758
+ spawnSync("taskkill.exe", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, timeout: 15_000 });
759
+ } else child.kill("SIGTERM");
760
+ }
761
+
762
+ function createPublicDnsResolver() {
763
+ const resolver = new DnsResolver();
764
+ resolver.setServers(["1.1.1.1", "1.0.0.1"]);
765
+ return resolver;
766
+ }
767
+
768
+ export async function resolvePublicProbeAddress(hostname, resolver = createPublicDnsResolver()) {
769
+ try {
770
+ const addresses = await resolver.resolve4(hostname);
771
+ const address = addresses.find((value) => net.isIPv4(value));
772
+ if (address) return { address, family: 4 };
773
+ } catch {}
774
+ try {
775
+ const addresses = await resolver.resolve6(hostname);
776
+ const address = addresses.find((value) => net.isIPv6(value));
777
+ if (address) return { address, family: 6 };
778
+ } catch {}
779
+ throw authError("AUTH_EXTERNAL_DNS_UNAVAILABLE", `Public DNS did not resolve ${hostname}.`);
780
+ }
781
+
782
+ export function createFixedAddressLookup(resolved) {
783
+ const address = String(resolved?.address ?? "");
784
+ const family = Number(resolved?.family);
785
+ if (net.isIP(address) !== family || (family !== 4 && family !== 6)) {
786
+ throw authError("AUTH_EXTERNAL_DNS_INVALID", "Public DNS returned an invalid address.");
787
+ }
788
+ return (_hostname, lookupOptions, callback) => {
789
+ const done = typeof lookupOptions === "function" ? lookupOptions : callback;
790
+ const all = typeof lookupOptions === "object" && lookupOptions?.all === true;
791
+ queueMicrotask(() => {
792
+ if (all) done(null, [{ address, family }]);
793
+ else done(null, address, family);
794
+ });
795
+ };
796
+ }
797
+
798
+ function requestJson(url, options = {}) {
799
+ const parsed = new URL(url);
800
+ const transport = parsed.protocol === "https:" ? https : parsed.protocol === "http:" ? http : null;
801
+ if (!transport) return Promise.reject(authError("AUTH_DOCTOR_URL_INVALID", "Doctor probe URL must use HTTP or HTTPS."));
802
+ const maximumBytes = options.maximumBytes ?? 64 * 1024;
803
+ return new Promise((resolve, reject) => {
804
+ const request = transport.get({
805
+ protocol: parsed.protocol,
806
+ hostname: parsed.hostname,
807
+ port: parsed.port || undefined,
808
+ path: `${parsed.pathname}${parsed.search}`,
809
+ timeout: options.timeoutMs ?? 5_000,
810
+ lookup: options.lookup,
811
+ headers: { accept: "application/json", host: options.hostHeader ?? parsed.host }
812
+ }, (response) => {
813
+ if (response.statusCode !== 200) {
814
+ response.resume();
815
+ reject(authError("AUTH_DOCTOR_HTTP_FAILED", `${url} returned HTTP ${response.statusCode}.`));
816
+ return;
817
+ }
818
+ const chunks = [];
819
+ let bytes = 0;
820
+ response.on("data", (chunk) => {
821
+ bytes += chunk.length;
822
+ if (bytes > maximumBytes) request.destroy(authError("AUTH_DOCTOR_RESPONSE_TOO_LARGE", `${url} exceeded the response limit.`));
823
+ else chunks.push(chunk);
824
+ });
825
+ response.on("end", () => {
826
+ try { resolve(JSON.parse(Buffer.concat(chunks).toString("utf8"))); }
827
+ catch { reject(authError("AUTH_DOCTOR_RESPONSE_INVALID", `${url} returned invalid JSON.`)); }
828
+ });
829
+ });
830
+ request.on("timeout", () => request.destroy(authError("AUTH_DOCTOR_TIMEOUT", `${url} timed out.`)));
831
+ request.on("error", reject);
832
+ });
833
+ }
834
+
835
+ async function verifyPublicOAuthSurface(hostname, expectedState) {
836
+ const base = `https://${hostname}`;
837
+ const resolved = await resolvePublicProbeAddress(hostname);
838
+ const lookup = createFixedAddressLookup(resolved);
839
+ const [protectedResource, authorizationServer, jwks, health] = await Promise.all([
840
+ requestJson(`${base}/.well-known/oauth-protected-resource/mcp`, { lookup }),
841
+ requestJson(`${base}/.well-known/oauth-authorization-server`, { lookup }),
842
+ requestJson(`${base}/jwks`, { lookup }),
843
+ requestJson(`${base}/healthz`, { lookup })
844
+ ]);
845
+ const expectedResource = `${base}/mcp`;
846
+ if (
847
+ protectedResource?.resource !== expectedResource ||
848
+ !Array.isArray(protectedResource?.authorization_servers) ||
849
+ protectedResource.authorization_servers.length !== 1 ||
850
+ protectedResource.authorization_servers[0] !== base ||
851
+ !Array.isArray(protectedResource?.scopes_supported) ||
852
+ !protectedResource.scopes_supported.includes("codexgpt:read")
853
+ ) {
854
+ throw authError("AUTH_EXTERNAL_METADATA_MISMATCH", "Protected-resource metadata does not match the configured issuer/resource.");
855
+ }
856
+ const expectedEndpoints = {
857
+ issuer: base,
858
+ authorization_endpoint: `${base}/authorize`,
859
+ token_endpoint: `${base}/token`,
860
+ registration_endpoint: `${base}/register`,
861
+ revocation_endpoint: `${base}/revoke`,
862
+ jwks_uri: `${base}/jwks`
863
+ };
864
+ for (const [field, expected] of Object.entries(expectedEndpoints)) {
865
+ if (authorizationServer?.[field] !== expected) {
866
+ throw authError("AUTH_EXTERNAL_METADATA_MISMATCH", `Authorization-server metadata field ${field} is inconsistent.`);
867
+ }
868
+ }
869
+ if (!Array.isArray(jwks?.keys) || jwks.keys.length < 1 || jwks.keys.some((key) => key?.kty !== "EC" || key?.crv !== "P-256" || key?.alg !== "ES256" || key?.use !== "sig")) {
870
+ throw authError("AUTH_EXTERNAL_JWKS_INVALID", "Public JWKS is missing or contains an unsupported key.");
871
+ }
872
+ if (expectedState && !jwks.keys.some((key) => key.kid === expectedState.activePublicJwk.kid)) {
873
+ throw authError("AUTH_EXTERNAL_JWKS_MISMATCH", "Public JWKS does not contain the active local signing key.");
874
+ }
875
+ if (health?.ok !== true || health?.authMode !== "oauth" || health?.mcpAvailable !== true) {
876
+ throw authError("AUTH_EXTERNAL_HEALTH_INVALID", "Public OAuth health does not report authenticated MCP availability.");
877
+ }
878
+ return { protectedResource, authorizationServer, jwks, health };
879
+ }
880
+
881
+ function publicMetadataProbe(hostname, expectedState, timeoutMs = 45_000) {
882
+ const deadline = Date.now() + timeoutMs;
883
+ return new Promise((resolve, reject) => {
884
+ const attempt = async () => {
885
+ if (Date.now() >= deadline) {
886
+ reject(authError("AUTH_EXTERNAL_PROBE_FAILED", `Public OAuth surface did not become healthy at https://${hostname}.`));
887
+ return;
888
+ }
889
+ try {
890
+ resolve(await verifyPublicOAuthSurface(hostname, expectedState));
891
+ } catch {
892
+ setTimeout(attempt, 500);
893
+ }
894
+ };
895
+ void attempt();
896
+ });
897
+ }
898
+
899
+ async function commandSetup(root, invocation, modules) {
900
+ if (process.platform !== "win32") throw authError("OAUTH_DEPLOYMENT_INVALID", "Phase 8 Core OAuth setup requires native Windows.");
901
+ const argv = invocation.argv;
902
+ const existing = modules.profile.readWorkspaceProfile(root);
903
+ const savedOAuthRoute = existing.authRoutes?.oauth ?? (existing.authMode === "oauth"
904
+ ? modules.profile.workspaceAuthRouteFromProfile(existing)
905
+ : {});
906
+ const hostname = modules.auth.normalizeOAuthHostname(optionValue(argv, "hostname") || savedOAuthRoute.hostname || existing.hostname || "");
907
+ const tunnelName = optionValue(argv, "tunnel-name") || savedOAuthRoute.tunnelName || existing.tunnelName || "";
908
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(tunnelName)) throw authError("OAUTH_DEPLOYMENT_INVALID", "--tunnel-name is required and invalid.");
909
+ const publicPort = exactPort(optionValue(argv, "public-port") || savedOAuthRoute.port || existing.port || DEFAULT_PUBLIC_PORT, "Public port");
910
+ const localAdminPort = exactPort(optionValue(argv, "local-admin-port") || savedOAuthRoute.localAdminPort || existing.localAdminPort || DEFAULT_LOCAL_ADMIN_PORT, "Local-admin port");
911
+ if (publicPort === localAdminPort) throw authError("OAUTH_DEPLOYMENT_INVALID", "Public and local-admin ports must be distinct.");
912
+ const profileId = modules.profile.profileIdForRoot(root);
913
+ const configPath = path.resolve(optionValue(argv, "cloudflare-config") || savedOAuthRoute.cloudflareConfig || existing.cloudflareConfig || defaultCloudflareConfigPath(root));
914
+ const oauthRoute = {
915
+ tunnel: "cloudflare-named",
916
+ hostname,
917
+ tunnelName,
918
+ tunnelOwner: "codexgpt",
919
+ port: String(publicPort),
920
+ localAdminPort: String(localAdminPort),
921
+ cloudflareConfig: configPath,
922
+ ...(savedOAuthRoute.cloudflareTokenFile ? { cloudflareTokenFile: savedOAuthRoute.cloudflareTokenFile } : {}),
923
+ ...(savedOAuthRoute.noInstallCloudflared ? { noInstallCloudflared: true } : {})
924
+ };
925
+ const retainedLegacyRoute = existing.authRoutes?.legacy ?? (existing.authMode !== "oauth"
926
+ ? modules.profile.workspaceAuthRouteFromProfile(existing)
927
+ : undefined);
928
+ const authRoutes = {
929
+ ...existing.authRoutes,
930
+ ...(retainedLegacyRoute ? { legacy: retainedLegacyRoute } : {}),
931
+ oauth: oauthRoute
932
+ };
933
+ const configuration = modules.auth.resolveOAuthDeploymentConfiguration({
934
+ canonicalRoot: root,
935
+ profileId: modules.profile.profileIdForRoot(root),
936
+ hostname,
937
+ tunnel: "cloudflare-named",
938
+ tunnelName,
939
+ tunnelOwner: "codexgpt",
940
+ publicHost: "127.0.0.1",
941
+ publicPort,
942
+ localAdminHost: "127.0.0.1",
943
+ localAdminPort
944
+ });
945
+
946
+ const existingTunnel = preflightExistingTunnelConfig({
947
+ root,
948
+ profileId,
949
+ configPath,
950
+ hostname,
951
+ tunnelName,
952
+ publicPort,
953
+ localAdminPort,
954
+ validateDedicatedTunnelConfig: modules.auth.validateDedicatedTunnelConfig
955
+ });
956
+
957
+ const { oauthStateRoot } = oauthStateRoots(modules);
958
+ const running = currentOAuthRuntime(modules, oauthStateRoot, profileId);
959
+ if (running) {
960
+ if (running.canonicalRoot !== root || running.localAdminOrigin !== `http://127.0.0.1:${localAdminPort}`) {
961
+ throw authError("OAUTH_STATE_CONFLICT", "The running OAuth instance does not match the requested workspace/listener configuration.");
962
+ }
963
+ if (!existingTunnel) {
964
+ throw authError("AUTH_TUNNEL_CONFIG_INVALID", "The running OAuth instance has no saved dedicated tunnel config.");
965
+ }
966
+ validateTunnelOwnerMarker(configPath, {
967
+ profileId,
968
+ bindingId: running.bindingId,
969
+ tunnelId: existingTunnel.validation.tunnelId,
970
+ tunnelName,
971
+ hostname
972
+ });
973
+ await verifyPublicOAuthSurface(hostname, null);
974
+ modules.profile.saveWorkspaceProfile(root, modules.profile.applyWorkspaceAuthRoute(stableProfilePayload(existing, {
975
+ authMode: "oauth",
976
+ authRoutes,
977
+ oauthIssuer: configuration.issuer,
978
+ oauthResource: configuration.resource
979
+ }), oauthRoute));
980
+ updateSetupJournal(root, { phase: "mode-committed", bindingId: running.bindingId, incarnationId: running.incarnationId });
981
+ updateSetupJournal(root, { phase: "foreground-running", candidatePid: running.pid });
982
+ console.log("OAuth already ready");
983
+ console.log(`Server URL https://${hostname}/mcp`);
984
+ console.log("Admin local only");
985
+ console.log(`Status ${supportedCommand(["auth", "status", "--root", root])}`);
986
+ return;
987
+ }
988
+
989
+ await checkPortFree(publicPort);
990
+ await checkPortFree(localAdminPort);
991
+ updateSetupJournal(root, { phase: "preflight", hostname, tunnelName, publicPort, localAdminPort });
992
+ const cloudflared = await installVerifiedCloudflared({ ensureOnly: true });
993
+ const initialized = await withOfflineAuth(root, modules, async ({ coordinator }) => coordinator.initialize(configuration));
994
+ updateSetupJournal(root, {
995
+ phase: "candidate-local-state",
996
+ bindingId: initialized.state.bindingId,
997
+ incarnationId: initialized.state.incarnationId
998
+ });
999
+
1000
+ const candidateProfile = modules.profile.applyWorkspaceAuthRoute(stableProfilePayload(existing, {
1001
+ root,
1002
+ authRoutes,
1003
+ oauthIssuer: configuration.issuer,
1004
+ oauthResource: configuration.resource,
1005
+ oauthCredentialProvider: "windows-dpapi-current-user",
1006
+ oauthStateRef: `state_${initialized.state.bindingId.slice("binding_".length)}`,
1007
+ authMode: existing.authMode ?? "legacy"
1008
+ }), oauthRoute);
1009
+
1010
+ let tunnelId = "";
1011
+ if (existingTunnel) {
1012
+ tunnelId = existingTunnel.validation.tunnelId;
1013
+ validateTunnelOwnerMarker(configPath, {
1014
+ profileId: modules.profile.profileIdForRoot(root),
1015
+ bindingId: initialized.state.bindingId,
1016
+ tunnelId,
1017
+ tunnelName,
1018
+ hostname
1019
+ });
1020
+ updateSetupJournal(root, { phase: "tunnel-created", tunnelId, tunnelCreated: false });
1021
+ updateSetupJournal(root, { phase: "dns-routed", tunnelId, dnsRouteVerifiedByExternalProbe: true });
1022
+ updateSetupJournal(root, { phase: "ingress-written", tunnelId, configDigest: existingTunnel.validation.digest });
1023
+ } else {
1024
+ const noTunnelChanges = hasFlag(argv, "no-tunnel-changes");
1025
+ let provision = hasFlag(argv, "provision-tunnel");
1026
+ if (!provision && process.stdin.isTTY && !noTunnelChanges) provision = await confirmProvision(tunnelName, hostname);
1027
+ if (!provision) {
1028
+ updateSetupJournal(root, { phase: "login-required" });
1029
+ printTunnelPlan(root, cloudflared, tunnelName, hostname, configPath, publicPort);
1030
+ throw authError("AUTH_TUNNEL_PROVISIONING_REQUIRED", "Re-run with --provision-tunnel after reviewing the printed Cloudflare changes.");
1031
+ }
1032
+ if (!fs.existsSync(cloudflareLoginMaterial())) {
1033
+ updateSetupJournal(root, { phase: "login-required" });
1034
+ const resume = supportedCommand(["auth", "setup", "--root", root, "--hostname", hostname, "--tunnel-name", tunnelName, "--provision-tunnel"]);
1035
+ if (!process.stdin.isTTY) {
1036
+ throw authError("AUTH_TUNNEL_LOGIN_REQUIRED", "Cloudflare owner login material is missing.", `${cloudflared} tunnel login; ${resume}`);
1037
+ }
1038
+ runCloudflared(cloudflared, ["tunnel", "--no-autoupdate", "login"], { timeout: 300_000 });
1039
+ if (!fs.existsSync(cloudflareLoginMaterial())) {
1040
+ throw authError("AUTH_TUNNEL_LOGIN_REQUIRED", "Cloudflare login completed without producing owner login material.", resume);
1041
+ }
1042
+ }
1043
+ const priorJournal = readSetupJournal(root);
1044
+ const resumableTunnelId =
1045
+ priorJournal?.hostname === hostname && priorJournal?.tunnelName === tunnelName &&
1046
+ typeof priorJournal?.tunnelId === "string"
1047
+ ? priorJournal.tunnelId
1048
+ : "";
1049
+ const ensured = ensureTunnel(cloudflared, tunnelName, resumableTunnelId);
1050
+ tunnelId = ensured.tunnelId;
1051
+ updateSetupJournal(root, { phase: "tunnel-created", tunnelId, tunnelCreated: ensured.created });
1052
+ routeTunnelDns(cloudflared, tunnelId, hostname);
1053
+ updateSetupJournal(root, { phase: "dns-routed", tunnelId });
1054
+ const credentialsFile = path.join(os.homedir(), ".cloudflared", `${tunnelId}.json`);
1055
+ if (!fs.existsSync(credentialsFile)) throw authError("AUTH_TUNNEL_CREDENTIALS_INVALID", `Tunnel credential file was not found: ${credentialsFile}`);
1056
+ const configText = modules.auth.createDedicatedTunnelConfig({ tunnelId, credentialsFile, hostname, publicPort });
1057
+ writeTextAtomic(configPath, configText);
1058
+ const validation = modules.auth.validateDedicatedTunnelConfig(configText, { tunnelId, hostname, publicPort, localAdminPort });
1059
+ writeTunnelOwnerMarker(configPath, {
1060
+ profileId: modules.profile.profileIdForRoot(root),
1061
+ bindingId: initialized.state.bindingId,
1062
+ tunnelId,
1063
+ tunnelName,
1064
+ hostname
1065
+ });
1066
+ updateSetupJournal(root, { phase: "ingress-written", tunnelId, configDigest: validation.digest });
1067
+ }
1068
+
1069
+ modules.profile.saveWorkspaceProfile(root, candidateProfile);
1070
+
1071
+ const childArgs = [
1072
+ path.join(scriptDir, "codexgpt.mjs"), "start",
1073
+ "--root", root,
1074
+ "--tunnel", "cloudflare-named",
1075
+ "--hostname", hostname,
1076
+ "--tunnel-name", tunnelName,
1077
+ "--cloudflare-config", configPath,
1078
+ "--cloudflared", cloudflared,
1079
+ "--no-install-cloudflared",
1080
+ "--no-copy-url"
1081
+ ];
1082
+ const candidate = spawn(process.execPath, childArgs, {
1083
+ cwd: root,
1084
+ env: { ...process.env, CODEXGPT_AUTH_MODE: "oauth", CODEXGPT_ALLOW_QUERY_TOKEN: "0" },
1085
+ stdio: "inherit",
1086
+ windowsHide: true
1087
+ });
1088
+ updateSetupJournal(root, { phase: "candidate-listener-started", candidatePid: candidate.pid ?? null });
1089
+ let exited = false;
1090
+ candidate.once("exit", () => { exited = true; });
1091
+ try {
1092
+ await Promise.race([
1093
+ publicMetadataProbe(hostname, initialized.state),
1094
+ new Promise((_, reject) => candidate.once("exit", (code) => reject(authError("AUTH_CANDIDATE_EXITED", `Candidate OAuth service exited with code ${code}.`))))
1095
+ ]);
1096
+ updateSetupJournal(root, { phase: "external-probe" });
1097
+ modules.profile.saveWorkspaceProfile(root, stableProfilePayload(candidateProfile, { authMode: "oauth" }));
1098
+ updateSetupJournal(root, { phase: "mode-committed" });
1099
+ console.log("OAuth ready");
1100
+ console.log(`Server URL https://${hostname}/mcp`);
1101
+ console.log("Admin local only");
1102
+ console.log("Next in the OAuth App, choose Scan Tools once or recreate the App, then approve the link on this PC");
1103
+ console.log(`Rollback use the retained Legacy App after: ${supportedCommand(["auth", "rollback", "--root", root])}`);
1104
+ if (hasFlag(argv, "no-start") || !process.stdin.isTTY) {
1105
+ stopProcessTree(candidate);
1106
+ updateSetupJournal(root, { phase: "configured-no-start", candidatePid: null });
1107
+ console.log(`Start ${supportedCommand(["start", "--root", root])}`);
1108
+ return;
1109
+ }
1110
+ updateSetupJournal(root, { phase: "foreground-running" });
1111
+ await new Promise((resolve, reject) => candidate.once("exit", (code) => code === 0 ? resolve() : reject(authError("AUTH_SERVICE_EXITED", `OAuth service exited with code ${code}.`))));
1112
+ } catch (error) {
1113
+ if (!exited) stopProcessTree(candidate);
1114
+ throw error;
1115
+ }
1116
+ }
1117
+
1118
+ async function commandRollback(root, invocation, modules) {
1119
+ const profile = modules.profile.readWorkspaceProfile(root);
1120
+ const { oauthStateRoot } = oauthStateRoots(modules);
1121
+ const runtime = currentOAuthRuntime(modules, oauthStateRoot, modules.profile.profileIdForRoot(root));
1122
+ const currentProcess = process.env.CODEXGPT_AUTH_MODE?.trim();
1123
+ const persistedUser = persistedUserAuthMode();
1124
+ const resolution = modules.auth.resolveHttpAuthMode({ currentProcess: currentProcess || undefined, persistedUser: persistedUser || undefined, profile: profile.authMode });
1125
+ if (resolution.source === "current-process") {
1126
+ throw authError("AUTH_MODE_ENV_OVERRIDE", "Current-process CODEXGPT_AUTH_MODE overrides the workspace profile.", "Remove-Item Env:CODEXGPT_AUTH_MODE");
1127
+ }
1128
+ if (resolution.source === "persisted-user") {
1129
+ throw authError("AUTH_MODE_ENV_OVERRIDE", "Persisted user CODEXGPT_AUTH_MODE overrides the workspace profile.", "[Environment]::SetEnvironmentVariable('CODEXGPT_AUTH_MODE',$null,'User')");
1130
+ }
1131
+ const oauthRoute = profile.authRoutes?.oauth ?? modules.profile.workspaceAuthRouteFromProfile(profile);
1132
+ let legacyRoute = profile.authRoutes?.legacy;
1133
+ if (!legacyRoute) {
1134
+ const hostnameInput = optionValue(invocation.argv, "legacy-hostname");
1135
+ const tunnelNameInput = optionValue(invocation.argv, "legacy-tunnel-name");
1136
+ const publicPortInput = optionValue(invocation.argv, "legacy-public-port");
1137
+ const anyMigrationInput = Boolean(hostnameInput || tunnelNameInput || publicPortInput);
1138
+ if (!hostnameInput || !tunnelNameInput || !publicPortInput) {
1139
+ const migrationCommand = supportedCommand([
1140
+ "auth", "rollback", "--root", root,
1141
+ "--legacy-hostname", "<retained-legacy-hostname>",
1142
+ "--legacy-tunnel-name", "<retained-legacy-tunnel-name>",
1143
+ "--legacy-public-port", "<retained-legacy-port>"
1144
+ ]);
1145
+ throw authError(
1146
+ "AUTH_LEGACY_ROUTE_MISSING",
1147
+ anyMigrationInput
1148
+ ? "The one-time retained Legacy route migration requires hostname, tunnel name, and public port together."
1149
+ : "The pre-migration Legacy App route is not recorded, so rollback cannot choose a safe endpoint.",
1150
+ migrationCommand
1151
+ );
1152
+ }
1153
+ const hostname = modules.auth.normalizeOAuthHostname(hostnameInput);
1154
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(tunnelNameInput)) {
1155
+ throw authError("OAUTH_DEPLOYMENT_INVALID", "--legacy-tunnel-name is invalid.");
1156
+ }
1157
+ const publicPort = exactPort(publicPortInput, "Legacy public port");
1158
+ legacyRoute = {
1159
+ tunnel: "cloudflare-named",
1160
+ hostname,
1161
+ tunnelName: tunnelNameInput,
1162
+ port: String(publicPort),
1163
+ ...(profile.noInstallCloudflared ? { noInstallCloudflared: true } : {})
1164
+ };
1165
+ console.log("Recorded retained Legacy route from explicit safe routing inputs; no credential was copied into routes or displayed.");
1166
+ }
1167
+ const switched = modules.profile.applyWorkspaceAuthRoute(stableProfilePayload(profile, {
1168
+ authMode: "legacy",
1169
+ authRoutes: { ...profile.authRoutes, legacy: legacyRoute, oauth: oauthRoute }
1170
+ }), legacyRoute);
1171
+ modules.profile.saveWorkspaceProfile(root, switched);
1172
+ console.log("Authentication mode set to legacy. OAuth state, keys, grants, clients, audit, and Cloudflare routes were preserved.");
1173
+ if (runtime) {
1174
+ console.log(`Current run still OAuth in pid ${runtime.pid}; press q in its terminal to stop it`);
1175
+ }
1176
+ console.log(`Restart ${supportedCommand(["start", "--root", root])}`);
1177
+ console.log("ChatGPT use the separately retained Legacy App after the restart");
1178
+ }
1179
+
1180
+ function backupFilesForState(store, oauthStateRoot, state, auth) {
1181
+ return store.listBindingBackups(state.bindingId)
1182
+ .filter((entry) => SAFE_BACKUP_ID.test(entry.backupId))
1183
+ .map((entry) => {
1184
+ const filePath = auth.deploymentBackupFile(
1185
+ oauthStateRoot,
1186
+ state.bindingId,
1187
+ entry.incarnationId,
1188
+ entry.backupId
1189
+ );
1190
+ return {
1191
+ id: entry.backupId,
1192
+ incarnationId: entry.incarnationId,
1193
+ path: filePath,
1194
+ bytes: fs.statSync(filePath).size
1195
+ };
1196
+ });
1197
+ }
1198
+
1199
+ async function commandRecover(root, invocation, modules) {
1200
+ if (invocation.operation === "recover.restore") {
1201
+ await requireDestructiveConfirmation(
1202
+ invocation.argv,
1203
+ "confirm-forced-relink",
1204
+ "FORCE RELINK",
1205
+ "Backup restore creates a new signing key, pepper, epoch, and incarnation; every prior token remains invalid and ChatGPT must relink."
1206
+ );
1207
+ }
1208
+ if (invocation.operation === "recover.unlock") {
1209
+ await requireDestructiveConfirmation(
1210
+ invocation.argv,
1211
+ "confirm-dead-owner",
1212
+ "RECOVER DEAD OWNER",
1213
+ "Dead-owner recovery quarantines only the exact verified stale lock and does not stop a live process."
1214
+ );
1215
+ }
1216
+ return await withOfflineAuth(root, modules, async (context) => {
1217
+ const configuration = deploymentConfiguration(root, context.profile, modules);
1218
+ const state = context.registry.readCurrentState(configuration.identityKey);
1219
+ if (invocation.operation === "recover.inspect") {
1220
+ const backups = backupFilesForState(context.store, context.oauthStateRoot, state, modules.auth);
1221
+ console.log(JSON.stringify({
1222
+ root,
1223
+ bindingId: state.bindingId,
1224
+ incarnationId: state.incarnationId,
1225
+ generation: state.generation,
1226
+ recoveryRequired: state.recoveryRequired,
1227
+ backups: backups.map(({ id, incarnationId, bytes }) => ({ id, incarnationId, bytes }))
1228
+ }, null, 2));
1229
+ return;
1230
+ }
1231
+ if (invocation.operation === "recover.restore") {
1232
+ const backupId = invocation.target;
1233
+ if (!SAFE_BACKUP_ID.test(backupId)) throw authError("OAUTH_BACKUP_INVALID", "Backup id is invalid.");
1234
+ const backup = backupFilesForState(context.store, context.oauthStateRoot, state, modules.auth).find((entry) => entry.id === backupId);
1235
+ if (!backup) throw authError("OAUTH_BACKUP_NOT_FOUND", "Verified OAuth backup was not found.");
1236
+ const parsed = context.store.readDeploymentBackup(state.bindingId, backup.incarnationId, backup.id);
1237
+ const restored = await context.coordinator.restoreAsSecurityReset(configuration.identityKey, parsed);
1238
+ console.log(`OAuth state restored as security reset. New incarnation: ${restored.incarnationId}`);
1239
+ console.log("All restored grants are inactive; relink ChatGPT before use.");
1240
+ return;
1241
+ }
1242
+ if (invocation.operation === "recover.unlock") {
1243
+ const exactOwnerId = invocation.target;
1244
+ if (!/^authrun_[a-f0-9]{32}$/.test(exactOwnerId)) throw authError("OAUTH_LOCK_OWNER_INVALID", "Exact owner id must be an authrun identifier.");
1245
+ const lockRoot = path.join(context.oauthStateRoot, "runtime", "locks");
1246
+ const candidates = fs.existsSync(lockRoot) ? fs.readdirSync(lockRoot).filter((name) => name.endsWith(".lock")) : [];
1247
+ let matched = "";
1248
+ for (const name of candidates) {
1249
+ try {
1250
+ const owner = JSON.parse(fs.readFileSync(path.join(lockRoot, name, "owner.json"), "utf8"));
1251
+ if (owner.runId === exactOwnerId) { matched = owner.lockName; break; }
1252
+ } catch {}
1253
+ }
1254
+ if (!matched) throw authError("OAUTH_LOCK_OWNER_NOT_FOUND", "Exact dead lock owner was not found.");
1255
+ const handle = context.locks.acquire(matched);
1256
+ handle.release();
1257
+ console.log(`Recovered dead OAuth lock owner ${exactOwnerId}.`);
1258
+ return;
1259
+ }
1260
+ throw authError("OAUTH_COMMAND_INVALID", "Unknown recover operation.");
1261
+ });
1262
+ }
1263
+
1264
+ async function commandReinitialize(root, invocation, modules) {
1265
+ if (!hasFlag(invocation.argv, "revoke-all")) throw authError("OAUTH_REVOKE_ALL_REQUIRED", "reinitialize requires --revoke-all.");
1266
+ await requireDestructiveConfirmation(
1267
+ invocation.argv,
1268
+ "confirm-reinitialize",
1269
+ "REINITIALIZE OAUTH",
1270
+ "Reinitialization rotates deployment authority and revokes every prior token and grant while preserving the stable binding and tunnel."
1271
+ );
1272
+ await withOfflineAuth(root, modules, async (context) => {
1273
+ const configuration = deploymentConfiguration(root, context.profile, modules);
1274
+ const current = context.registry.readCurrentState(configuration.identityKey);
1275
+ const reset = await context.coordinator.restoreAsSecurityReset(configuration.identityKey, current);
1276
+ console.log(`OAuth deployment reinitialized. New incarnation: ${reset.incarnationId}`);
1277
+ console.log("All prior access and refresh tokens are invalid; relink ChatGPT.");
1278
+ });
1279
+ }
1280
+
1281
+ async function commandRebind(root, invocation, modules) {
1282
+ if (!hasFlag(invocation.argv, "revoke-all")) throw authError("OAUTH_REVOKE_ALL_REQUIRED", "rebind requires --revoke-all.");
1283
+ const fromRootRaw = optionValue(invocation.argv, "from-root");
1284
+ if (!fromRootRaw) throw authError("OAUTH_ROOT_REQUIRED", "rebind requires --from-root.");
1285
+ const fromRoot = canonicalDirectory(fromRootRaw);
1286
+ if (fromRoot === root) {
1287
+ throw authError("OAUTH_STATE_CONFLICT", "rebind requires distinct source and target workspace roots.");
1288
+ }
1289
+ const fromProfile = modules.profile.readWorkspaceProfile(fromRoot);
1290
+ const hostname = modules.auth.normalizeOAuthHostname(optionValue(invocation.argv, "hostname") || fromProfile.hostname || "");
1291
+ if (hostname !== fromProfile.hostname) throw authError("OAUTH_STATE_CONFLICT", "Core rebind preserves the existing hostname/issuer/resource.");
1292
+ const targetProfile = modules.profile.readWorkspaceProfile(root);
1293
+ if (
1294
+ targetProfile.authMode === "oauth" || targetProfile.oauthStateRef ||
1295
+ targetProfile.oauthIssuer || targetProfile.oauthResource
1296
+ ) {
1297
+ throw authError("OAUTH_STATE_CONFLICT", "Target workspace already has OAuth deployment selectors.");
1298
+ }
1299
+ const { oauthStateRoot } = oauthStateRoots(modules);
1300
+ if (currentOAuthRuntime(modules, oauthStateRoot, modules.profile.profileIdForRoot(root))) {
1301
+ throw authError("OAUTH_STATE_BUSY", "Stop the target workspace runtime before rebind.");
1302
+ }
1303
+ await withOfflineAuth(fromRoot, modules, async (context) => {
1304
+ const oldConfiguration = deploymentConfiguration(fromRoot, fromProfile, modules);
1305
+ const current = context.registry.readCurrentState(oldConfiguration.identityKey);
1306
+ if (!fromProfile.cloudflareConfig) {
1307
+ throw authError("AUTH_TUNNEL_OWNERSHIP_UNPROVEN", "Rebind requires the existing dedicated tunnel config and owner marker.");
1308
+ }
1309
+ const tunnelValidation = modules.auth.validateDedicatedTunnelConfig(
1310
+ fs.readFileSync(fromProfile.cloudflareConfig, "utf8"),
1311
+ {
1312
+ hostname: oldConfiguration.hostname,
1313
+ publicPort: oldConfiguration.listeners.publicPort,
1314
+ localAdminPort: oldConfiguration.listeners.localAdminPort
1315
+ }
1316
+ );
1317
+ validateTunnelOwnerMarker(fromProfile.cloudflareConfig, {
1318
+ profileId: current.profileId,
1319
+ bindingId: current.bindingId,
1320
+ tunnelId: tunnelValidation.tunnelId,
1321
+ tunnelName: fromProfile.tunnelName,
1322
+ hostname
1323
+ });
1324
+ const newConfiguration = modules.auth.resolveOAuthDeploymentConfiguration({
1325
+ canonicalRoot: root,
1326
+ profileId: modules.profile.profileIdForRoot(root),
1327
+ hostname,
1328
+ tunnel: "cloudflare-named",
1329
+ tunnelName: fromProfile.tunnelName ?? "",
1330
+ tunnelOwner: "codexgpt",
1331
+ publicHost: "127.0.0.1",
1332
+ publicPort: Number(fromProfile.port ?? DEFAULT_PUBLIC_PORT),
1333
+ localAdminHost: "127.0.0.1",
1334
+ localAdminPort: Number(fromProfile.localAdminPort ?? DEFAULT_LOCAL_ADMIN_PORT)
1335
+ });
1336
+ const rebound = await context.coordinator.rebindAsSecurityReset(oldConfiguration.identityKey, newConfiguration);
1337
+ writeTunnelOwnerMarker(fromProfile.cloudflareConfig, {
1338
+ profileId: rebound.profileId,
1339
+ bindingId: rebound.bindingId,
1340
+ tunnelId: tunnelValidation.tunnelId,
1341
+ tunnelName: fromProfile.tunnelName,
1342
+ hostname
1343
+ });
1344
+ modules.profile.saveWorkspaceProfile(root, stableProfilePayload(targetProfile, {
1345
+ ...stableProfilePayload(fromProfile),
1346
+ root,
1347
+ authMode: "oauth",
1348
+ oauthStateRef: `state_${rebound.bindingId.slice("binding_".length)}`
1349
+ }));
1350
+ modules.profile.saveWorkspaceProfile(fromRoot, stableProfilePayload(fromProfile, { authMode: "legacy" }));
1351
+ console.log(`OAuth hostname rebound to ${root}. New incarnation: ${rebound.incarnationId}`);
1352
+ console.log("All prior grants and tokens were revoked. Cloudflare routes were not changed.");
1353
+ });
1354
+ }
1355
+
1356
+ async function commandDoctor(root, modules) {
1357
+ const profile = modules.profile.readWorkspaceProfile(root);
1358
+ const checks = [];
1359
+ const record = (status, name, detail) => checks.push({
1360
+ status: status === true ? "ok" : status === false ? "fail" : status,
1361
+ name,
1362
+ detail
1363
+ });
1364
+ record(process.platform === "win32", "Native Windows", process.platform);
1365
+
1366
+ let configuration = null;
1367
+ try {
1368
+ configuration = deploymentConfiguration(root, profile, modules);
1369
+ record(true, "OAuth profile", "complete reviewed selector set");
1370
+ } catch (error) {
1371
+ record(false, "OAuth profile", error.message);
1372
+ }
1373
+
1374
+ const { oauthStateRoot } = oauthStateRoots(modules);
1375
+ let credentialStore = null;
1376
+ try {
1377
+ credentialStore = modules.auth.createProductionCredentialStore();
1378
+ await credentialStore.probe();
1379
+ record(true, "DPAPI CurrentUser", "available");
1380
+ } catch (error) {
1381
+ record(false, "DPAPI CurrentUser", error.message);
1382
+ }
1383
+
1384
+ let currentState = null;
1385
+ if (configuration && credentialStore) {
1386
+ try {
1387
+ const store = new modules.auth.AuthStateStore(oauthStateRoot, credentialStore, {
1388
+ audit: { append() {} }
1389
+ });
1390
+ const registry = new modules.auth.DeploymentRegistry(store);
1391
+ const owner = store.readOwner();
1392
+ const entry = registry.resolve(configuration.identityKey);
1393
+ if (!entry) throw authError("OAUTH_STATE_CONFLICT", "Configured issuer/resource is not registered.");
1394
+ currentState = registry.readCurrentState(configuration.identityKey);
1395
+ if (
1396
+ currentState.ownerRef !== owner.ownerRef ||
1397
+ currentState.canonicalRoot !== root ||
1398
+ currentState.profileId !== modules.profile.profileIdForRoot(root) ||
1399
+ currentState.recoveryRequired
1400
+ ) {
1401
+ throw authError("OAUTH_STATE_RECOVERY_REQUIRED", "OAuth registry, owner, profile, or deployment state is inconsistent.");
1402
+ }
1403
+ record(true, "OAuth state", `binding ${currentState.bindingId}; generation ${currentState.generation}`);
1404
+ } catch (error) {
1405
+ record(false, "OAuth state", error.message);
1406
+ }
1407
+ } else {
1408
+ record(false, "OAuth state", "profile or DPAPI prerequisite unavailable");
1409
+ }
1410
+
1411
+ try {
1412
+ const binary = managedCloudflaredPath();
1413
+ const version = verifiedInstalledCloudflaredVersion(binary);
1414
+ record(Boolean(version), "Managed cloudflared", version || "unavailable or SHA-256/version verification failed");
1415
+ } catch (error) {
1416
+ record(false, "Managed cloudflared", error.message);
1417
+ }
1418
+
1419
+ let tunnelValidation = null;
1420
+ if (profile.cloudflareConfig && configuration) {
1421
+ try {
1422
+ tunnelValidation = modules.auth.validateDedicatedTunnelConfig(fs.readFileSync(profile.cloudflareConfig, "utf8"), {
1423
+ hostname: configuration.hostname,
1424
+ publicPort: configuration.listeners.publicPort,
1425
+ localAdminPort: configuration.listeners.localAdminPort
1426
+ });
1427
+ record(true, "Tunnel ingress", "dedicated public listener plus final 404");
1428
+ } catch (error) {
1429
+ record(false, "Tunnel ingress", error.message);
1430
+ }
1431
+ } else {
1432
+ record(false, "Tunnel ingress", "not configured");
1433
+ }
1434
+ if (tunnelValidation && currentState) {
1435
+ try {
1436
+ validateTunnelOwnerMarker(profile.cloudflareConfig, {
1437
+ profileId: currentState.profileId,
1438
+ bindingId: currentState.bindingId,
1439
+ tunnelId: tunnelValidation.tunnelId,
1440
+ tunnelName: profile.tunnelName,
1441
+ hostname: profile.hostname
1442
+ });
1443
+ record(true, "Tunnel ownership", "owner marker matches profile and stable binding");
1444
+ } catch (error) {
1445
+ record(false, "Tunnel ownership", error.message);
1446
+ }
1447
+ } else {
1448
+ record(false, "Tunnel ownership", "ingress or local state unavailable");
1449
+ }
1450
+
1451
+ let runtime = null;
1452
+ try {
1453
+ runtime = currentOAuthRuntime(modules, oauthStateRoot, modules.profile.profileIdForRoot(root));
1454
+ record(runtime ? "ok" : "warn", "OAuth runtime", runtime ? `pid ${runtime.pid}; exact creation time verified` : "stopped");
1455
+ } catch (error) {
1456
+ record(false, "OAuth runtime", error.message);
1457
+ }
1458
+
1459
+ if (runtime) {
1460
+ try {
1461
+ const localHealth = await requestJson(`${runtime.localAdminOrigin}/healthz`, {
1462
+ hostHeader: new URL(runtime.localAdminOrigin).host
1463
+ });
1464
+ if (localHealth?.ok !== true || localHealth?.ownerChannel !== "local-control-cli" || localHealth?.ownerChannelAvailable !== true) {
1465
+ throw authError("AUTH_ADMIN_HEALTH_INVALID", "Local admin listener is not attached to the current-user owner channel.");
1466
+ }
1467
+ record(true, "Local admin", `${runtime.localAdminOrigin}; owner channel available`);
1468
+ } catch (error) {
1469
+ record(false, "Local admin", error.message);
1470
+ }
1471
+
1472
+ try {
1473
+ const context = await onlineContext(root, modules);
1474
+ await Promise.all([
1475
+ context.client.listOAuthAuthorizations(runtime.serverId),
1476
+ context.client.listOAuthClients(runtime.serverId),
1477
+ context.client.listOAuthGrants(runtime.serverId)
1478
+ ]);
1479
+ record(true, "Owner control", "current-user named pipe accepted safe list operations");
1480
+ } catch (error) {
1481
+ record(false, "Owner control", error.message);
1482
+ }
1483
+
1484
+ if (configuration) {
1485
+ try {
1486
+ await verifyPublicOAuthSurface(configuration.hostname, currentState);
1487
+ record(true, "Public OAuth", "resource metadata, issuer endpoints, JWKS, and MCP health are consistent");
1488
+ } catch (error) {
1489
+ record(false, "Public OAuth", error.message);
1490
+ }
1491
+ } else {
1492
+ record(false, "Public OAuth", "profile unavailable");
1493
+ }
1494
+ } else {
1495
+ record("warn", "Local admin", "runtime stopped; probe not applicable");
1496
+ record("warn", "Owner control", "runtime stopped; probe not applicable");
1497
+ record("warn", "Public OAuth", "runtime stopped; probe not applicable");
1498
+ }
1499
+
1500
+ for (const check of checks) {
1501
+ const marker = check.status === "ok" ? "OK " : check.status === "warn" ? "WARN" : "FAIL";
1502
+ console.log(`${marker} ${check.name.padEnd(22)} ${check.detail}`);
1503
+ }
1504
+ if (checks.some((check) => check.status === "fail")) process.exitCode = 1;
1505
+ return checks;
1506
+ }
1507
+
1508
+ export async function runAuth(argv = process.argv.slice(2)) {
1509
+ if (!fs.existsSync(path.join(projectRoot, "dist", "auth", "index.js"))) {
1510
+ throw authError("OAUTH_BUILD_REQUIRED", "Missing built OAuth modules. Run npm run build.");
1511
+ }
1512
+ const invocation = parseAuthInvocation(argv);
1513
+ if (invocation.operation === "help" || hasFlag(argv, "help")) {
1514
+ console.log(authUsage());
1515
+ return;
1516
+ }
1517
+ const modules = await runtimeModules();
1518
+ const root = resolveRoot(argv, modules.profile);
1519
+ switch (invocation.operation) {
1520
+ case "setup": return await commandSetup(root, invocation, modules);
1521
+ case "status": return await commandStatus(root, modules, hasFlag(argv, "json"));
1522
+ case "doctor": return await commandDoctor(root, modules);
1523
+ case "pending": return await listOnline(root, modules, "pending");
1524
+ case "clients": return await listOnline(root, modules, "clients");
1525
+ case "open": {
1526
+ const context = await onlineContext(root, modules);
1527
+ const response = await context.client.issueOAuthAdminBootstrap(context.runtime.serverId);
1528
+ if (!response.ok || !response.oauthAdminBootstrapUrl) throw authError(response.code, "Local admin bootstrap could not be issued.");
1529
+ openBrowser(response.oauthAdminBootstrapUrl);
1530
+ console.log("Opened the local OAuth administration page. The one-time bootstrap was not printed.");
1531
+ return;
1532
+ }
1533
+ case "approve":
1534
+ case "deny": {
1535
+ if (!invocation.target) throw authError("OAUTH_AUTHORIZATION_REQUIRED", `${invocation.operation} requires a correlation code.`);
1536
+ const context = await onlineContext(root, modules);
1537
+ const pending = await resolvePending(context, invocation.target);
1538
+ const response = invocation.operation === "approve"
1539
+ ? await context.client.approveOAuthAuthorization(context.runtime.serverId, pending.pendingId)
1540
+ : await context.client.denyOAuthAuthorization(context.runtime.serverId, pending.pendingId);
1541
+ if (!response.ok) throw authError(response.code, "OAuth authorization operation failed.");
1542
+ console.log(`${pending.correlationCode} ${invocation.operation === "approve" ? "approved" : "denied"}.`);
1543
+ return;
1544
+ }
1545
+ case "client.remove": {
1546
+ if (!invocation.target) throw authError("OAUTH_CLIENT_REQUIRED", "client remove requires a client id.");
1547
+ const context = await onlineContext(root, modules);
1548
+ const response = await context.client.revokeOAuthClient(context.runtime.serverId, invocation.target);
1549
+ if (!response.ok) throw authError(response.code, "OAuth client was not found.");
1550
+ console.log(response.code);
1551
+ return;
1552
+ }
1553
+ case "prune": {
1554
+ if (!hasFlag(argv, "unapproved")) throw authError("OAUTH_PRUNE_TARGET_REQUIRED", "prune requires --unapproved.");
1555
+ const context = await onlineContext(root, modules);
1556
+ const response = await context.client.pruneUnapprovedOAuthClients(context.runtime.serverId);
1557
+ if (!response.ok) throw authError(response.code, "OAuth unapproved-client pruning failed.");
1558
+ console.log(response.code);
1559
+ return;
1560
+ }
1561
+ case "revoke": {
1562
+ const revokeAll = hasFlag(argv, "all");
1563
+ if (revokeAll) {
1564
+ await requireDestructiveConfirmation(
1565
+ argv,
1566
+ "confirm-revoke-all",
1567
+ "REVOKE ALL GRANTS",
1568
+ "Owner-wide revocation invalidates every active OAuth grant for this workspace but does not remove registered clients or Cloudflare state."
1569
+ );
1570
+ } else if (!invocation.target) {
1571
+ throw authError("OAUTH_GRANT_REQUIRED", "revoke requires a grant id or --all.");
1572
+ }
1573
+ const context = await onlineContext(root, modules);
1574
+ const response = revokeAll
1575
+ ? await context.client.revokeOAuthOwnerGrants(context.runtime.serverId)
1576
+ : await context.client.revokeOAuthGrant(context.runtime.serverId, invocation.target);
1577
+ if (!response.ok) throw authError(response.code, "OAuth grant revocation failed.");
1578
+ console.log(response.code);
1579
+ return;
1580
+ }
1581
+ case "rotate-signing-key": {
1582
+ const context = await onlineContext(root, modules);
1583
+ const response = await context.client.rotateOAuthSigningKey(context.runtime.serverId, hasFlag(argv, "revoke-all"));
1584
+ if (!response.ok) throw authError(response.code, "OAuth signing-key rotation failed.");
1585
+ console.log(response.code);
1586
+ return;
1587
+ }
1588
+ case "rollback": return await commandRollback(root, invocation, modules);
1589
+ case "recover.inspect":
1590
+ case "recover.restore":
1591
+ case "recover.unlock": return await commandRecover(root, invocation, modules);
1592
+ case "reinitialize": return await commandReinitialize(root, invocation, modules);
1593
+ case "rebind": return await commandRebind(root, invocation, modules);
1594
+ default: throw authError("OAUTH_COMMAND_INVALID", `Unknown auth command: ${invocation.operation}\n\n${authUsage()}`);
1595
+ }
1596
+ }
1597
+
1598
+ export function isMainInvocation(metaUrl, argvPath = process.argv[1]) {
1599
+ return Boolean(argvPath && metaUrl === pathToFileURL(path.resolve(argvPath)).href);
1600
+ }
1601
+
1602
+ if (isMainInvocation(import.meta.url)) {
1603
+ runAuth().catch((error) => {
1604
+ console.error(error instanceof Error ? error.message : String(error));
1605
+ if (error?.repairCommand) console.error(`Repair: ${error.repairCommand}`);
1606
+ process.exitCode = 1;
1607
+ });
1608
+ }