oc-codex-multi-auth 6.0.0 → 6.1.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 (270) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +58 -18
  3. package/assets/readme-hero.svg +10 -10
  4. package/config/README.md +1 -1
  5. package/config/minimal-opencode.json +1 -1
  6. package/config/opencode-legacy.json +1 -1
  7. package/config/opencode-modern.json +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +98 -2520
  11. package/dist/index.js.map +1 -1
  12. package/dist/lib/accounts/persistence.d.ts +34 -0
  13. package/dist/lib/accounts/persistence.d.ts.map +1 -0
  14. package/dist/lib/accounts/persistence.js +153 -0
  15. package/dist/lib/accounts/persistence.js.map +1 -0
  16. package/dist/lib/accounts/recovery.d.ts +59 -0
  17. package/dist/lib/accounts/recovery.d.ts.map +1 -0
  18. package/dist/lib/accounts/recovery.js +229 -0
  19. package/dist/lib/accounts/recovery.js.map +1 -0
  20. package/dist/lib/accounts/rotation.d.ts +40 -0
  21. package/dist/lib/accounts/rotation.d.ts.map +1 -0
  22. package/dist/lib/accounts/rotation.js +204 -0
  23. package/dist/lib/accounts/rotation.js.map +1 -0
  24. package/dist/lib/accounts/state.d.ts +92 -0
  25. package/dist/lib/accounts/state.d.ts.map +1 -0
  26. package/dist/lib/accounts/state.js +366 -0
  27. package/dist/lib/accounts/state.js.map +1 -0
  28. package/dist/lib/accounts.d.ts +48 -91
  29. package/dist/lib/accounts.d.ts.map +1 -1
  30. package/dist/lib/accounts.js +121 -699
  31. package/dist/lib/accounts.js.map +1 -1
  32. package/dist/lib/auth/auth.d.ts +1 -1
  33. package/dist/lib/auth/auth.d.ts.map +1 -1
  34. package/dist/lib/auth/auth.js +5 -2
  35. package/dist/lib/auth/auth.js.map +1 -1
  36. package/dist/lib/auth/login-runner.d.ts +32 -0
  37. package/dist/lib/auth/login-runner.d.ts.map +1 -1
  38. package/dist/lib/auth/login-runner.js +79 -64
  39. package/dist/lib/auth/login-runner.js.map +1 -1
  40. package/dist/lib/auth/server.d.ts.map +1 -1
  41. package/dist/lib/auth/server.js +3 -8
  42. package/dist/lib/auth/server.js.map +1 -1
  43. package/dist/lib/circuit-breaker.d.ts +53 -2
  44. package/dist/lib/circuit-breaker.d.ts.map +1 -1
  45. package/dist/lib/circuit-breaker.js +65 -6
  46. package/dist/lib/circuit-breaker.js.map +1 -1
  47. package/dist/lib/config.d.ts.map +1 -1
  48. package/dist/lib/config.js +77 -32
  49. package/dist/lib/config.js.map +1 -1
  50. package/dist/lib/error-sentinels.d.ts +20 -0
  51. package/dist/lib/error-sentinels.d.ts.map +1 -0
  52. package/dist/lib/error-sentinels.js +28 -0
  53. package/dist/lib/error-sentinels.js.map +1 -0
  54. package/dist/lib/errors.d.ts +100 -1
  55. package/dist/lib/errors.d.ts.map +1 -1
  56. package/dist/lib/errors.js +116 -1
  57. package/dist/lib/errors.js.map +1 -1
  58. package/dist/lib/logger.d.ts +14 -1
  59. package/dist/lib/logger.d.ts.map +1 -1
  60. package/dist/lib/logger.js +65 -5
  61. package/dist/lib/logger.js.map +1 -1
  62. package/dist/lib/oauth-constants.d.ts +18 -0
  63. package/dist/lib/oauth-constants.d.ts.map +1 -0
  64. package/dist/lib/oauth-constants.js +18 -0
  65. package/dist/lib/oauth-constants.js.map +1 -0
  66. package/dist/lib/oauth-success.d.ts +2 -0
  67. package/dist/lib/oauth-success.d.ts.map +1 -0
  68. package/dist/lib/oauth-success.html +711 -711
  69. package/dist/lib/oauth-success.js +714 -0
  70. package/dist/lib/oauth-success.js.map +1 -0
  71. package/dist/lib/prompts/codex.d.ts.map +1 -1
  72. package/dist/lib/prompts/codex.js +12 -3
  73. package/dist/lib/prompts/codex.js.map +1 -1
  74. package/dist/lib/prompts/opencode-codex.d.ts.map +1 -1
  75. package/dist/lib/prompts/opencode-codex.js +3 -2
  76. package/dist/lib/prompts/opencode-codex.js.map +1 -1
  77. package/dist/lib/recovery/hook.d.ts +43 -0
  78. package/dist/lib/recovery/hook.d.ts.map +1 -0
  79. package/dist/lib/recovery/hook.js +325 -0
  80. package/dist/lib/recovery/hook.js.map +1 -0
  81. package/dist/lib/recovery/index.d.ts +1 -0
  82. package/dist/lib/recovery/index.d.ts.map +1 -1
  83. package/dist/lib/recovery/index.js +1 -0
  84. package/dist/lib/recovery/index.js.map +1 -1
  85. package/dist/lib/recovery/storage.d.ts.map +1 -1
  86. package/dist/lib/recovery/storage.js +5 -1
  87. package/dist/lib/recovery/storage.js.map +1 -1
  88. package/dist/lib/recovery.d.ts +15 -30
  89. package/dist/lib/recovery.d.ts.map +1 -1
  90. package/dist/lib/recovery.js +14 -312
  91. package/dist/lib/recovery.js.map +1 -1
  92. package/dist/lib/request/fetch-helpers.js +1 -1
  93. package/dist/lib/request/fetch-helpers.js.map +1 -1
  94. package/dist/lib/request/response-handler.d.ts.map +1 -1
  95. package/dist/lib/request/response-handler.js +8 -2
  96. package/dist/lib/request/response-handler.js.map +1 -1
  97. package/dist/lib/runtime.d.ts +101 -0
  98. package/dist/lib/runtime.d.ts.map +1 -0
  99. package/dist/lib/runtime.js +77 -0
  100. package/dist/lib/runtime.js.map +1 -0
  101. package/dist/lib/schemas.d.ts +164 -1
  102. package/dist/lib/schemas.d.ts.map +1 -1
  103. package/dist/lib/schemas.js +251 -0
  104. package/dist/lib/schemas.js.map +1 -1
  105. package/dist/lib/storage/atomic-write.d.ts +31 -0
  106. package/dist/lib/storage/atomic-write.d.ts.map +1 -0
  107. package/dist/lib/storage/atomic-write.js +75 -0
  108. package/dist/lib/storage/atomic-write.js.map +1 -0
  109. package/dist/lib/storage/backup.d.ts +13 -0
  110. package/dist/lib/storage/backup.d.ts.map +1 -0
  111. package/dist/lib/storage/backup.js +59 -0
  112. package/dist/lib/storage/backup.js.map +1 -0
  113. package/dist/lib/storage/errors.d.ts +17 -0
  114. package/dist/lib/storage/errors.d.ts.map +1 -0
  115. package/dist/lib/storage/errors.js +38 -0
  116. package/dist/lib/storage/errors.js.map +1 -0
  117. package/dist/lib/storage/export-import.d.ts +84 -0
  118. package/dist/lib/storage/export-import.d.ts.map +1 -0
  119. package/dist/lib/storage/export-import.js +222 -0
  120. package/dist/lib/storage/export-import.js.map +1 -0
  121. package/dist/lib/storage/flagged.d.ts +29 -0
  122. package/dist/lib/storage/flagged.d.ts.map +1 -0
  123. package/dist/lib/storage/flagged.js +207 -0
  124. package/dist/lib/storage/flagged.js.map +1 -0
  125. package/dist/lib/storage/identity.d.ts +72 -0
  126. package/dist/lib/storage/identity.d.ts.map +1 -0
  127. package/dist/lib/storage/identity.js +230 -0
  128. package/dist/lib/storage/identity.js.map +1 -0
  129. package/dist/lib/storage/keychain.d.ts +146 -0
  130. package/dist/lib/storage/keychain.d.ts.map +1 -0
  131. package/dist/lib/storage/keychain.js +278 -0
  132. package/dist/lib/storage/keychain.js.map +1 -0
  133. package/dist/lib/storage/load-save.d.ts +61 -0
  134. package/dist/lib/storage/load-save.d.ts.map +1 -0
  135. package/dist/lib/storage/load-save.js +606 -0
  136. package/dist/lib/storage/load-save.js.map +1 -0
  137. package/dist/lib/storage/migrations.d.ts +35 -0
  138. package/dist/lib/storage/migrations.d.ts.map +1 -1
  139. package/dist/lib/storage/migrations.js +37 -0
  140. package/dist/lib/storage/migrations.js.map +1 -1
  141. package/dist/lib/storage/normalize.d.ts +22 -0
  142. package/dist/lib/storage/normalize.d.ts.map +1 -0
  143. package/dist/lib/storage/normalize.js +114 -0
  144. package/dist/lib/storage/normalize.js.map +1 -0
  145. package/dist/lib/storage/paths.d.ts.map +1 -1
  146. package/dist/lib/storage/paths.js +2 -1
  147. package/dist/lib/storage/paths.js.map +1 -1
  148. package/dist/lib/storage/state.d.ts +46 -0
  149. package/dist/lib/storage/state.d.ts.map +1 -0
  150. package/dist/lib/storage/state.js +100 -0
  151. package/dist/lib/storage/state.js.map +1 -0
  152. package/dist/lib/storage/worktree-lock.d.ts +79 -0
  153. package/dist/lib/storage/worktree-lock.d.ts.map +1 -0
  154. package/dist/lib/storage/worktree-lock.js +265 -0
  155. package/dist/lib/storage/worktree-lock.js.map +1 -0
  156. package/dist/lib/storage.d.ts +21 -155
  157. package/dist/lib/storage.d.ts.map +1 -1
  158. package/dist/lib/storage.js +24 -1167
  159. package/dist/lib/storage.js.map +1 -1
  160. package/dist/lib/tools/_shared.d.ts +16 -0
  161. package/dist/lib/tools/_shared.d.ts.map +1 -0
  162. package/dist/lib/tools/_shared.js +21 -0
  163. package/dist/lib/tools/_shared.js.map +1 -0
  164. package/dist/lib/tools/codex-dashboard.d.ts +8 -0
  165. package/dist/lib/tools/codex-dashboard.d.ts.map +1 -0
  166. package/dist/lib/tools/codex-dashboard.js +176 -0
  167. package/dist/lib/tools/codex-dashboard.js.map +1 -0
  168. package/dist/lib/tools/codex-diag.d.ts +19 -0
  169. package/dist/lib/tools/codex-diag.d.ts.map +1 -0
  170. package/dist/lib/tools/codex-diag.js +139 -0
  171. package/dist/lib/tools/codex-diag.js.map +1 -0
  172. package/dist/lib/tools/codex-diff.d.ts +44 -0
  173. package/dist/lib/tools/codex-diff.d.ts.map +1 -0
  174. package/dist/lib/tools/codex-diff.js +284 -0
  175. package/dist/lib/tools/codex-diff.js.map +1 -0
  176. package/dist/lib/tools/codex-doctor.d.ts +8 -0
  177. package/dist/lib/tools/codex-doctor.d.ts.map +1 -0
  178. package/dist/lib/tools/codex-doctor.js +284 -0
  179. package/dist/lib/tools/codex-doctor.js.map +1 -0
  180. package/dist/lib/tools/codex-export.d.ts +8 -0
  181. package/dist/lib/tools/codex-export.d.ts.map +1 -0
  182. package/dist/lib/tools/codex-export.js +63 -0
  183. package/dist/lib/tools/codex-export.js.map +1 -0
  184. package/dist/lib/tools/codex-health.d.ts +8 -0
  185. package/dist/lib/tools/codex-health.d.ts.map +1 -0
  186. package/dist/lib/tools/codex-health.js +124 -0
  187. package/dist/lib/tools/codex-health.js.map +1 -0
  188. package/dist/lib/tools/codex-help.d.ts +8 -0
  189. package/dist/lib/tools/codex-help.d.ts.map +1 -0
  190. package/dist/lib/tools/codex-help.js +127 -0
  191. package/dist/lib/tools/codex-help.js.map +1 -0
  192. package/dist/lib/tools/codex-import.d.ts +8 -0
  193. package/dist/lib/tools/codex-import.d.ts.map +1 -0
  194. package/dist/lib/tools/codex-import.js +97 -0
  195. package/dist/lib/tools/codex-import.js.map +1 -0
  196. package/dist/lib/tools/codex-keychain.d.ts +36 -0
  197. package/dist/lib/tools/codex-keychain.d.ts.map +1 -0
  198. package/dist/lib/tools/codex-keychain.js +289 -0
  199. package/dist/lib/tools/codex-keychain.js.map +1 -0
  200. package/dist/lib/tools/codex-label.d.ts +8 -0
  201. package/dist/lib/tools/codex-label.d.ts.map +1 -0
  202. package/dist/lib/tools/codex-label.js +143 -0
  203. package/dist/lib/tools/codex-label.js.map +1 -0
  204. package/dist/lib/tools/codex-limits.d.ts +8 -0
  205. package/dist/lib/tools/codex-limits.d.ts.map +1 -0
  206. package/dist/lib/tools/codex-limits.js +497 -0
  207. package/dist/lib/tools/codex-limits.js.map +1 -0
  208. package/dist/lib/tools/codex-list.d.ts +8 -0
  209. package/dist/lib/tools/codex-list.d.ts.map +1 -0
  210. package/dist/lib/tools/codex-list.js +260 -0
  211. package/dist/lib/tools/codex-list.js.map +1 -0
  212. package/dist/lib/tools/codex-metrics.d.ts +8 -0
  213. package/dist/lib/tools/codex-metrics.d.ts.map +1 -0
  214. package/dist/lib/tools/codex-metrics.js +180 -0
  215. package/dist/lib/tools/codex-metrics.js.map +1 -0
  216. package/dist/lib/tools/codex-next.d.ts +8 -0
  217. package/dist/lib/tools/codex-next.d.ts.map +1 -0
  218. package/dist/lib/tools/codex-next.js +54 -0
  219. package/dist/lib/tools/codex-next.js.map +1 -0
  220. package/dist/lib/tools/codex-note.d.ts +8 -0
  221. package/dist/lib/tools/codex-note.d.ts.map +1 -0
  222. package/dist/lib/tools/codex-note.js +79 -0
  223. package/dist/lib/tools/codex-note.js.map +1 -0
  224. package/dist/lib/tools/codex-refresh.d.ts +8 -0
  225. package/dist/lib/tools/codex-refresh.d.ts.map +1 -0
  226. package/dist/lib/tools/codex-refresh.js +78 -0
  227. package/dist/lib/tools/codex-refresh.js.map +1 -0
  228. package/dist/lib/tools/codex-remove.d.ts +8 -0
  229. package/dist/lib/tools/codex-remove.d.ts.map +1 -0
  230. package/dist/lib/tools/codex-remove.js +184 -0
  231. package/dist/lib/tools/codex-remove.js.map +1 -0
  232. package/dist/lib/tools/codex-setup.d.ts +8 -0
  233. package/dist/lib/tools/codex-setup.d.ts.map +1 -0
  234. package/dist/lib/tools/codex-setup.js +26 -0
  235. package/dist/lib/tools/codex-setup.js.map +1 -0
  236. package/dist/lib/tools/codex-status.d.ts +8 -0
  237. package/dist/lib/tools/codex-status.d.ts.map +1 -0
  238. package/dist/lib/tools/codex-status.js +263 -0
  239. package/dist/lib/tools/codex-status.js.map +1 -0
  240. package/dist/lib/tools/codex-switch.d.ts +8 -0
  241. package/dist/lib/tools/codex-switch.d.ts.map +1 -0
  242. package/dist/lib/tools/codex-switch.js +122 -0
  243. package/dist/lib/tools/codex-switch.js.map +1 -0
  244. package/dist/lib/tools/codex-tag.d.ts +8 -0
  245. package/dist/lib/tools/codex-tag.d.ts.map +1 -0
  246. package/dist/lib/tools/codex-tag.js +101 -0
  247. package/dist/lib/tools/codex-tag.js.map +1 -0
  248. package/dist/lib/tools/index.d.ts +143 -0
  249. package/dist/lib/tools/index.d.ts.map +1 -0
  250. package/dist/lib/tools/index.js +67 -0
  251. package/dist/lib/tools/index.js.map +1 -0
  252. package/dist/lib/ui/format.d.ts.map +1 -1
  253. package/dist/lib/ui/format.js +5 -0
  254. package/dist/lib/ui/format.js.map +1 -1
  255. package/dist/lib/ui/runtime.d.ts +10 -0
  256. package/dist/lib/ui/runtime.d.ts.map +1 -1
  257. package/dist/lib/ui/runtime.js +16 -11
  258. package/dist/lib/ui/runtime.js.map +1 -1
  259. package/dist/lib/ui/select.d.ts.map +1 -1
  260. package/dist/lib/ui/select.js +12 -3
  261. package/dist/lib/ui/select.js.map +1 -1
  262. package/dist/lib/ui/theme.d.ts +19 -0
  263. package/dist/lib/ui/theme.d.ts.map +1 -1
  264. package/dist/lib/ui/theme.js +27 -0
  265. package/dist/lib/ui/theme.js.map +1 -1
  266. package/package.json +24 -16
  267. package/scripts/copy-oauth-success.js +16 -6
  268. package/scripts/install-oc-codex-multi-auth-core.js +462 -381
  269. package/scripts/install-oc-codex-multi-auth.js +15 -15
  270. package/scripts/test-all-models.sh +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../lib/storage/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAKF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAaT;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC,GAChF,MAAM,EAAE,CAkBV;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC,GAChF,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CASpF;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC,EAAE,EACpF,YAAY,EAAE,MAAM,EAAE,GACrB,MAAM,CASR;AAgFD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACtJ,QAAQ,EAAE,CAAC,EAAE,GACZ,CAAC,EAAE,CAEL;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACvJ,QAAQ,EAAE,CAAC,EAAE,GACZ,CAAC,EAAE,CAiEL;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE5G"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Account identity + dedup helpers.
3
+ *
4
+ * Split out of `lib/storage.ts` in RC-2. This module is pure and synchronous:
5
+ * it owns the identity-key hierarchy (`organizationId` -> `accountId` ->
6
+ * `refreshToken`) and the dedup + merge rules used by `normalizeAccountStorage`
7
+ * and the import transaction.
8
+ *
9
+ * The two public exports that were already re-exported from `lib/storage.ts`
10
+ * (`deduplicateAccounts`, `deduplicateAccountsByEmail`) keep their original
11
+ * shape so external callers can import them via either the barrel or this
12
+ * submodule.
13
+ */
14
+ const normalizeWorkspaceIdentityPart = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
15
+ export function getWorkspaceIdentityKey(account) {
16
+ const organizationId = normalizeWorkspaceIdentityPart(account.organizationId);
17
+ const accountId = normalizeWorkspaceIdentityPart(account.accountId);
18
+ const refreshToken = normalizeWorkspaceIdentityPart(account.refreshToken) ?? "";
19
+ if (organizationId) {
20
+ return accountId
21
+ ? `organizationId:${organizationId}|accountId:${accountId}`
22
+ : `organizationId:${organizationId}`;
23
+ }
24
+ if (accountId) {
25
+ return `accountId:${accountId}`;
26
+ }
27
+ return `refreshToken:${refreshToken}`;
28
+ }
29
+ export function isRecord(value) {
30
+ return !!value && typeof value === "object" && !Array.isArray(value);
31
+ }
32
+ export function clampIndex(index, length) {
33
+ if (length <= 0)
34
+ return 0;
35
+ return Math.max(0, Math.min(index, length - 1));
36
+ }
37
+ export function toAccountIdentityKeys(account) {
38
+ const keys = [];
39
+ const organizationId = typeof account.organizationId === "string" ? account.organizationId.trim() : "";
40
+ if (organizationId) {
41
+ keys.push(`organizationId:${organizationId}`);
42
+ }
43
+ const accountId = typeof account.accountId === "string" ? account.accountId.trim() : "";
44
+ if (accountId) {
45
+ keys.push(`accountId:${accountId}`);
46
+ }
47
+ const refreshToken = typeof account.refreshToken === "string" ? account.refreshToken.trim() : "";
48
+ if (refreshToken) {
49
+ keys.push(`refreshToken:${refreshToken}`);
50
+ }
51
+ return keys;
52
+ }
53
+ export function toAccountIdentityKey(account) {
54
+ return toAccountIdentityKeys(account)[0];
55
+ }
56
+ export function extractActiveKeys(accounts, activeIndex) {
57
+ const candidate = accounts[activeIndex];
58
+ if (!isRecord(candidate))
59
+ return [];
60
+ return toAccountIdentityKeys({
61
+ organizationId: typeof candidate.organizationId === "string" ? candidate.organizationId : undefined,
62
+ accountId: typeof candidate.accountId === "string" ? candidate.accountId : undefined,
63
+ refreshToken: typeof candidate.refreshToken === "string" ? candidate.refreshToken : "",
64
+ });
65
+ }
66
+ export function findAccountIndexByIdentityKeys(accounts, identityKeys) {
67
+ if (identityKeys.length === 0)
68
+ return -1;
69
+ for (const identityKey of identityKeys) {
70
+ const idx = accounts.findIndex((account) => toAccountIdentityKeys(account).includes(identityKey));
71
+ if (idx >= 0) {
72
+ return idx;
73
+ }
74
+ }
75
+ return -1;
76
+ }
77
+ function selectNewestAccount(current, candidate) {
78
+ if (!current)
79
+ return candidate;
80
+ const currentLastUsed = current.lastUsed || 0;
81
+ const candidateLastUsed = candidate.lastUsed || 0;
82
+ if (candidateLastUsed > currentLastUsed)
83
+ return candidate;
84
+ if (candidateLastUsed < currentLastUsed)
85
+ return current;
86
+ const currentAddedAt = current.addedAt || 0;
87
+ const candidateAddedAt = candidate.addedAt || 0;
88
+ return candidateAddedAt >= currentAddedAt ? candidate : current;
89
+ }
90
+ function pickNewestAccountIndex(accounts, existingIndex, candidateIndex) {
91
+ const existing = accounts[existingIndex];
92
+ const candidate = accounts[candidateIndex];
93
+ if (!existing)
94
+ return candidateIndex;
95
+ if (!candidate)
96
+ return existingIndex;
97
+ const newest = selectNewestAccount(existing, candidate);
98
+ return newest === candidate ? candidateIndex : existingIndex;
99
+ }
100
+ function mergeAccountRecords(target, source) {
101
+ const newest = selectNewestAccount(target, source);
102
+ const older = newest === target ? source : target;
103
+ return {
104
+ ...older,
105
+ ...newest,
106
+ organizationId: target.organizationId ?? source.organizationId,
107
+ accountId: target.accountId ?? source.accountId,
108
+ accountIdSource: target.accountIdSource ?? source.accountIdSource,
109
+ accountLabel: target.accountLabel ?? source.accountLabel,
110
+ email: target.email ?? source.email,
111
+ };
112
+ }
113
+ function deduplicateAccountsByKey(accounts) {
114
+ const working = [...accounts];
115
+ const keyToIndex = new Map();
116
+ const indicesToRemove = new Set();
117
+ for (let i = 0; i < working.length; i += 1) {
118
+ const account = working[i];
119
+ if (!account)
120
+ continue;
121
+ const key = toAccountIdentityKey(account);
122
+ if (!key)
123
+ continue;
124
+ const existingIndex = keyToIndex.get(key);
125
+ if (existingIndex === undefined) {
126
+ keyToIndex.set(key, i);
127
+ continue;
128
+ }
129
+ const newestIndex = pickNewestAccountIndex(working, existingIndex, i);
130
+ const obsoleteIndex = newestIndex === existingIndex ? i : existingIndex;
131
+ const target = working[newestIndex];
132
+ const source = working[obsoleteIndex];
133
+ if (target && source) {
134
+ working[newestIndex] = mergeAccountRecords(target, source);
135
+ }
136
+ indicesToRemove.add(obsoleteIndex);
137
+ keyToIndex.set(key, newestIndex);
138
+ }
139
+ const result = [];
140
+ for (let i = 0; i < working.length; i += 1) {
141
+ if (indicesToRemove.has(i))
142
+ continue;
143
+ const account = working[i];
144
+ if (account)
145
+ result.push(account);
146
+ }
147
+ return result;
148
+ }
149
+ /**
150
+ * Removes duplicate accounts, keeping the most recently used entry for each unique key.
151
+ * Deduplication identity hierarchy: organizationId -> accountId -> refreshToken.
152
+ * @param accounts - Array of accounts to deduplicate
153
+ * @returns New array with duplicates removed
154
+ */
155
+ export function deduplicateAccounts(accounts) {
156
+ return deduplicateAccountsByKey(accounts);
157
+ }
158
+ /**
159
+ * Removes duplicate legacy accounts by email, keeping the most recently used entry.
160
+ * Accounts with organizationId/accountId are never merged by email to avoid collapsing workspace variants.
161
+ * Accounts without email are always preserved.
162
+ * @param accounts - Array of accounts to deduplicate
163
+ * @returns New array with email duplicates removed
164
+ */
165
+ export function deduplicateAccountsByEmail(accounts) {
166
+ const emailToNewestIndex = new Map();
167
+ const indicesToKeep = new Set();
168
+ for (let i = 0; i < accounts.length; i += 1) {
169
+ const account = accounts[i];
170
+ if (!account)
171
+ continue;
172
+ const organizationId = account.organizationId?.trim();
173
+ if (organizationId) {
174
+ indicesToKeep.add(i);
175
+ continue;
176
+ }
177
+ const accountId = account.accountId?.trim();
178
+ if (accountId) {
179
+ indicesToKeep.add(i);
180
+ continue;
181
+ }
182
+ const email = account.email?.trim();
183
+ if (!email) {
184
+ indicesToKeep.add(i);
185
+ continue;
186
+ }
187
+ const existingIndex = emailToNewestIndex.get(email);
188
+ if (existingIndex === undefined) {
189
+ emailToNewestIndex.set(email, i);
190
+ continue;
191
+ }
192
+ const existing = accounts[existingIndex];
193
+ // istanbul ignore next -- defensive code: existingIndex always refers to valid account
194
+ if (!existing) {
195
+ emailToNewestIndex.set(email, i);
196
+ continue;
197
+ }
198
+ const existingLastUsed = existing.lastUsed || 0;
199
+ const candidateLastUsed = account.lastUsed || 0;
200
+ const existingAddedAt = existing.addedAt || 0;
201
+ const candidateAddedAt = account.addedAt || 0;
202
+ const isNewer = candidateLastUsed > existingLastUsed ||
203
+ (candidateLastUsed === existingLastUsed && candidateAddedAt > existingAddedAt);
204
+ if (isNewer) {
205
+ emailToNewestIndex.set(email, i);
206
+ }
207
+ }
208
+ for (const idx of emailToNewestIndex.values()) {
209
+ indicesToKeep.add(idx);
210
+ }
211
+ const result = [];
212
+ for (let i = 0; i < accounts.length; i += 1) {
213
+ if (indicesToKeep.has(i)) {
214
+ const account = accounts[i];
215
+ if (account)
216
+ result.push(account);
217
+ }
218
+ }
219
+ return result;
220
+ }
221
+ /**
222
+ * Applies storage deduplication semantics used by normalize/import paths.
223
+ * 1) Dedupe only exact identity duplicates (organizationId -> accountId -> refreshToken),
224
+ * preserving distinct workspace variants that share a refresh token.
225
+ * 2) Then apply legacy email dedupe only for entries that still do not have organizationId/accountId.
226
+ */
227
+ export function deduplicateAccountsForStorage(accounts) {
228
+ return deduplicateAccountsByEmail(deduplicateAccountsByKey(accounts));
229
+ }
230
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../lib/storage/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Opt-in OS-keychain credential backend.
3
+ *
4
+ * Phase 4 F1. Addresses the audit finding (docs/audits/09-security-trust.md)
5
+ * that per-project accounts are stored as plaintext V3 JSON on disk. This
6
+ * module introduces an alternate storage backend that persists the SAME
7
+ * V3 JSON blob as a secret value in the OS keychain:
8
+ *
9
+ * - macOS: Keychain
10
+ * - Windows: Credential Manager
11
+ * - Linux: Secret Service / libsecret
12
+ *
13
+ * **Default behavior is unchanged.** The backend only activates when
14
+ * `CODEX_KEYCHAIN=1` is set in the environment. Any other value (unset,
15
+ * `"0"`, `"false"`, `""`, `"yes"`, ...) leaves the existing JSON path in
16
+ * full control. This is deliberate: credential storage is the highest-trust
17
+ * surface in the plugin and every new failure mode is a login regression.
18
+ *
19
+ * Data model:
20
+ * - Service name: `oc-codex-multi-auth` (fixed)
21
+ * - Account key: `accounts:<project-storage-key>` where the project key
22
+ * is the `projectName-sha256hash12` string already produced by
23
+ * `lib/storage/paths.ts#getProjectStorageKey`. For the global file we
24
+ * use the literal key `accounts:global`.
25
+ * - Secret payload: the exact JSON string that would otherwise be written
26
+ * to disk (UTF-8, pretty-printed to match the file contents; the OS
27
+ * keychain APIs used here store arbitrary UTF-8 so no base64 encoding
28
+ * is required — documented here for reviewers).
29
+ *
30
+ * Fallback contract:
31
+ * - Every entry point returns a soft failure (`null` on read,
32
+ * `{ok: false}` on write) instead of throwing when the native module
33
+ * cannot load or a keychain call fails. Callers MUST fall back to the
34
+ * JSON backend on failure; they must never silently lose credentials.
35
+ * - All errors are logged with `log.warn` / `log.error`. Secret values
36
+ * never appear in log text — only the service name, account key, and
37
+ * the native error message.
38
+ *
39
+ * Testing:
40
+ * - The native module is loaded lazily so unit tests can substitute
41
+ * `_setBackendForTests` to avoid hitting the real OS keychain.
42
+ * Integration tests that DO want to hit the real keychain can set
43
+ * `CODEX_KEYCHAIN=1` and let `loadBackend` resolve the module.
44
+ */
45
+ /**
46
+ * The keychain service identifier this plugin owns. Chosen to match the npm
47
+ * package name so a human inspecting Keychain Access / Credential Manager
48
+ * can easily identify which program owns a stored credential.
49
+ */
50
+ export declare const KEYCHAIN_SERVICE_NAME = "oc-codex-multi-auth";
51
+ /**
52
+ * Account key used when the plugin is operating without a per-project
53
+ * storage path (i.e. the global accounts file). Kept distinct from any
54
+ * per-project key so the two storage scopes never collide in the OS
55
+ * keychain's (service, account) index.
56
+ */
57
+ export declare const GLOBAL_KEYCHAIN_ACCOUNT_KEY = "accounts:global";
58
+ /**
59
+ * Reserved account key used exclusively by the availability probe. Kept
60
+ * double-underscored and service-suffixed so a future refactor that drops
61
+ * the `accounts:` prefix on real entries (or adds a different prefix) still
62
+ * cannot collide with this probe (F1 post-merge LOW finding).
63
+ */
64
+ export declare const KEYCHAIN_PROBE_ACCOUNT_KEY = "__probe__@oc-codex-multi-auth";
65
+ /**
66
+ * Minimal abstraction over the native `@napi-rs/keyring` `Entry` API.
67
+ * Declared as an interface so tests can inject a deterministic in-memory
68
+ * backend without touching the real OS keychain.
69
+ */
70
+ export interface KeychainBackend {
71
+ get(service: string, account: string): Promise<string | null>;
72
+ set(service: string, account: string, secret: string): Promise<void>;
73
+ delete(service: string, account: string): Promise<boolean>;
74
+ isAvailable(): Promise<boolean>;
75
+ }
76
+ /**
77
+ * Build the keychain account key for a given project storage key. When the
78
+ * plugin is running against the global accounts file, pass `null` to use
79
+ * the reserved global account key.
80
+ */
81
+ export declare function buildKeychainAccountKey(projectStorageKey: string | null): string;
82
+ /**
83
+ * Reads the V3 JSON blob from the OS keychain for the given project storage
84
+ * key. Returns `null` when:
85
+ * - the native module is unavailable (not installed, missing prebuilt)
86
+ * - no entry exists yet
87
+ * - any keychain error occurs
88
+ *
89
+ * The caller MUST treat `null` as "fall back to JSON". Callers must never
90
+ * interpret `null` as "no credentials" unconditionally — it may mean
91
+ * "keychain locked / permission denied" and the JSON file still holds the
92
+ * authoritative copy.
93
+ */
94
+ export declare function readFromKeychain(projectStorageKey: string | null): Promise<string | null>;
95
+ export interface KeychainWriteResult {
96
+ ok: boolean;
97
+ /** Populated on failure. Never contains secret material. */
98
+ error?: string;
99
+ }
100
+ /**
101
+ * Persist the V3 JSON blob to the OS keychain. Returns `{ok:false}` on
102
+ * failure so callers can fall back to JSON. Never throws.
103
+ */
104
+ export declare function writeToKeychain(projectStorageKey: string | null, jsonBlob: string): Promise<KeychainWriteResult>;
105
+ /**
106
+ * Remove the plugin's keychain entry for the given project key. Returns
107
+ * true when the entry existed and was deleted, false otherwise (including
108
+ * "not present"). Never throws.
109
+ */
110
+ export declare function deleteFromKeychain(projectStorageKey: string | null): Promise<boolean>;
111
+ /**
112
+ * Probe the backend end-to-end (write + read + delete a throwaway entry)
113
+ * to confirm the OS keychain is reachable and unlocked. Used by the
114
+ * `codex-keychain status` tool to give the operator a clear yes/no signal
115
+ * instead of waiting until the next real save.
116
+ *
117
+ * Gated on the opt-in flag (F1 post-merge LOW finding): with
118
+ * `CODEX_KEYCHAIN` unset the probe is a no-op that returns `false` without
119
+ * touching the OS keychain. This preserves the "unset -> zero keychain code
120
+ * path" invariant the feature advertises and avoids the first-run macOS
121
+ * "allow/always allow" prompt that `entry.setPassword` can otherwise
122
+ * trigger for users who run `codex-keychain status` without opting in.
123
+ *
124
+ * Pass `env` to override the opt-in lookup in tests.
125
+ */
126
+ export declare function keychainIsAvailable(env?: NodeJS.ProcessEnv): Promise<boolean>;
127
+ /**
128
+ * Parse the `CODEX_KEYCHAIN` environment variable. Only the literal string
129
+ * `"1"` enables the opt-in. Anything else (unset, `"0"`, `"false"`, `""`,
130
+ * `"yes"`, ...) leaves the JSON backend in full control. This mirrors
131
+ * `EnvBooleanSchema`'s contract and is documented in README.md.
132
+ */
133
+ export declare function isKeychainOptInEnabled(env?: NodeJS.ProcessEnv): boolean;
134
+ /**
135
+ * Test-only hook: inject a deterministic backend (typically an in-memory
136
+ * Map-backed stub) and mark the cache as resolved so `getBackend` returns
137
+ * the injected backend without attempting to load the native module.
138
+ *
139
+ * Passing `null` clears the cache so a subsequent call re-runs the lazy
140
+ * loader — useful when a test needs to verify the unavailable-backend
141
+ * branch.
142
+ */
143
+ export declare function _setBackendForTests(backend: KeychainBackend | null): void;
144
+ /** Test-only reset to the initial "not yet loaded" state. */
145
+ export declare function _resetBackendForTests(): void;
146
+ //# sourceMappingURL=keychain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../../lib/storage/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAMH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,oBAAoB,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kCAAuC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9D,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAChC;AAiHD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB;AAED,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAkB9B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACvC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC9B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAGzE;AAED,6DAA6D;AAC7D,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C"}