oc-codex-multi-auth 6.0.0 → 6.1.1

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 (284) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +56 -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 +112 -2525
  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.d.ts +1 -0
  93. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  94. package/dist/lib/request/fetch-helpers.js +58 -10
  95. package/dist/lib/request/fetch-helpers.js.map +1 -1
  96. package/dist/lib/request/response-handler.d.ts.map +1 -1
  97. package/dist/lib/request/response-handler.js +8 -2
  98. package/dist/lib/request/response-handler.js.map +1 -1
  99. package/dist/lib/runtime.d.ts +101 -0
  100. package/dist/lib/runtime.d.ts.map +1 -0
  101. package/dist/lib/runtime.js +77 -0
  102. package/dist/lib/runtime.js.map +1 -0
  103. package/dist/lib/schemas.d.ts +164 -1
  104. package/dist/lib/schemas.d.ts.map +1 -1
  105. package/dist/lib/schemas.js +251 -0
  106. package/dist/lib/schemas.js.map +1 -1
  107. package/dist/lib/storage/atomic-write.d.ts +31 -0
  108. package/dist/lib/storage/atomic-write.d.ts.map +1 -0
  109. package/dist/lib/storage/atomic-write.js +75 -0
  110. package/dist/lib/storage/atomic-write.js.map +1 -0
  111. package/dist/lib/storage/backup.d.ts +13 -0
  112. package/dist/lib/storage/backup.d.ts.map +1 -0
  113. package/dist/lib/storage/backup.js +59 -0
  114. package/dist/lib/storage/backup.js.map +1 -0
  115. package/dist/lib/storage/errors.d.ts +17 -0
  116. package/dist/lib/storage/errors.d.ts.map +1 -0
  117. package/dist/lib/storage/errors.js +38 -0
  118. package/dist/lib/storage/errors.js.map +1 -0
  119. package/dist/lib/storage/export-import.d.ts +84 -0
  120. package/dist/lib/storage/export-import.d.ts.map +1 -0
  121. package/dist/lib/storage/export-import.js +222 -0
  122. package/dist/lib/storage/export-import.js.map +1 -0
  123. package/dist/lib/storage/flagged.d.ts +29 -0
  124. package/dist/lib/storage/flagged.d.ts.map +1 -0
  125. package/dist/lib/storage/flagged.js +207 -0
  126. package/dist/lib/storage/flagged.js.map +1 -0
  127. package/dist/lib/storage/identity.d.ts +72 -0
  128. package/dist/lib/storage/identity.d.ts.map +1 -0
  129. package/dist/lib/storage/identity.js +230 -0
  130. package/dist/lib/storage/identity.js.map +1 -0
  131. package/dist/lib/storage/keychain.d.ts +146 -0
  132. package/dist/lib/storage/keychain.d.ts.map +1 -0
  133. package/dist/lib/storage/keychain.js +278 -0
  134. package/dist/lib/storage/keychain.js.map +1 -0
  135. package/dist/lib/storage/load-save.d.ts +61 -0
  136. package/dist/lib/storage/load-save.d.ts.map +1 -0
  137. package/dist/lib/storage/load-save.js +606 -0
  138. package/dist/lib/storage/load-save.js.map +1 -0
  139. package/dist/lib/storage/migrations.d.ts +35 -0
  140. package/dist/lib/storage/migrations.d.ts.map +1 -1
  141. package/dist/lib/storage/migrations.js +37 -0
  142. package/dist/lib/storage/migrations.js.map +1 -1
  143. package/dist/lib/storage/normalize.d.ts +22 -0
  144. package/dist/lib/storage/normalize.d.ts.map +1 -0
  145. package/dist/lib/storage/normalize.js +114 -0
  146. package/dist/lib/storage/normalize.js.map +1 -0
  147. package/dist/lib/storage/paths.d.ts.map +1 -1
  148. package/dist/lib/storage/paths.js +2 -1
  149. package/dist/lib/storage/paths.js.map +1 -1
  150. package/dist/lib/storage/state.d.ts +46 -0
  151. package/dist/lib/storage/state.d.ts.map +1 -0
  152. package/dist/lib/storage/state.js +100 -0
  153. package/dist/lib/storage/state.js.map +1 -0
  154. package/dist/lib/storage/worktree-lock.d.ts +79 -0
  155. package/dist/lib/storage/worktree-lock.d.ts.map +1 -0
  156. package/dist/lib/storage/worktree-lock.js +265 -0
  157. package/dist/lib/storage/worktree-lock.js.map +1 -0
  158. package/dist/lib/storage.d.ts +21 -155
  159. package/dist/lib/storage.d.ts.map +1 -1
  160. package/dist/lib/storage.js +24 -1167
  161. package/dist/lib/storage.js.map +1 -1
  162. package/dist/lib/tools/_shared.d.ts +16 -0
  163. package/dist/lib/tools/_shared.d.ts.map +1 -0
  164. package/dist/lib/tools/_shared.js +21 -0
  165. package/dist/lib/tools/_shared.js.map +1 -0
  166. package/dist/lib/tools/codex-dashboard.d.ts +8 -0
  167. package/dist/lib/tools/codex-dashboard.d.ts.map +1 -0
  168. package/dist/lib/tools/codex-dashboard.js +176 -0
  169. package/dist/lib/tools/codex-dashboard.js.map +1 -0
  170. package/dist/lib/tools/codex-diag.d.ts +19 -0
  171. package/dist/lib/tools/codex-diag.d.ts.map +1 -0
  172. package/dist/lib/tools/codex-diag.js +139 -0
  173. package/dist/lib/tools/codex-diag.js.map +1 -0
  174. package/dist/lib/tools/codex-diff.d.ts +44 -0
  175. package/dist/lib/tools/codex-diff.d.ts.map +1 -0
  176. package/dist/lib/tools/codex-diff.js +284 -0
  177. package/dist/lib/tools/codex-diff.js.map +1 -0
  178. package/dist/lib/tools/codex-doctor.d.ts +8 -0
  179. package/dist/lib/tools/codex-doctor.d.ts.map +1 -0
  180. package/dist/lib/tools/codex-doctor.js +284 -0
  181. package/dist/lib/tools/codex-doctor.js.map +1 -0
  182. package/dist/lib/tools/codex-export.d.ts +8 -0
  183. package/dist/lib/tools/codex-export.d.ts.map +1 -0
  184. package/dist/lib/tools/codex-export.js +63 -0
  185. package/dist/lib/tools/codex-export.js.map +1 -0
  186. package/dist/lib/tools/codex-health.d.ts +8 -0
  187. package/dist/lib/tools/codex-health.d.ts.map +1 -0
  188. package/dist/lib/tools/codex-health.js +124 -0
  189. package/dist/lib/tools/codex-health.js.map +1 -0
  190. package/dist/lib/tools/codex-help.d.ts +8 -0
  191. package/dist/lib/tools/codex-help.d.ts.map +1 -0
  192. package/dist/lib/tools/codex-help.js +127 -0
  193. package/dist/lib/tools/codex-help.js.map +1 -0
  194. package/dist/lib/tools/codex-import.d.ts +8 -0
  195. package/dist/lib/tools/codex-import.d.ts.map +1 -0
  196. package/dist/lib/tools/codex-import.js +97 -0
  197. package/dist/lib/tools/codex-import.js.map +1 -0
  198. package/dist/lib/tools/codex-keychain.d.ts +36 -0
  199. package/dist/lib/tools/codex-keychain.d.ts.map +1 -0
  200. package/dist/lib/tools/codex-keychain.js +289 -0
  201. package/dist/lib/tools/codex-keychain.js.map +1 -0
  202. package/dist/lib/tools/codex-label.d.ts +8 -0
  203. package/dist/lib/tools/codex-label.d.ts.map +1 -0
  204. package/dist/lib/tools/codex-label.js +143 -0
  205. package/dist/lib/tools/codex-label.js.map +1 -0
  206. package/dist/lib/tools/codex-limits.d.ts +8 -0
  207. package/dist/lib/tools/codex-limits.d.ts.map +1 -0
  208. package/dist/lib/tools/codex-limits.js +497 -0
  209. package/dist/lib/tools/codex-limits.js.map +1 -0
  210. package/dist/lib/tools/codex-list.d.ts +8 -0
  211. package/dist/lib/tools/codex-list.d.ts.map +1 -0
  212. package/dist/lib/tools/codex-list.js +260 -0
  213. package/dist/lib/tools/codex-list.js.map +1 -0
  214. package/dist/lib/tools/codex-metrics.d.ts +8 -0
  215. package/dist/lib/tools/codex-metrics.d.ts.map +1 -0
  216. package/dist/lib/tools/codex-metrics.js +180 -0
  217. package/dist/lib/tools/codex-metrics.js.map +1 -0
  218. package/dist/lib/tools/codex-next.d.ts +8 -0
  219. package/dist/lib/tools/codex-next.d.ts.map +1 -0
  220. package/dist/lib/tools/codex-next.js +54 -0
  221. package/dist/lib/tools/codex-next.js.map +1 -0
  222. package/dist/lib/tools/codex-note.d.ts +8 -0
  223. package/dist/lib/tools/codex-note.d.ts.map +1 -0
  224. package/dist/lib/tools/codex-note.js +79 -0
  225. package/dist/lib/tools/codex-note.js.map +1 -0
  226. package/dist/lib/tools/codex-refresh.d.ts +8 -0
  227. package/dist/lib/tools/codex-refresh.d.ts.map +1 -0
  228. package/dist/lib/tools/codex-refresh.js +78 -0
  229. package/dist/lib/tools/codex-refresh.js.map +1 -0
  230. package/dist/lib/tools/codex-remove.d.ts +8 -0
  231. package/dist/lib/tools/codex-remove.d.ts.map +1 -0
  232. package/dist/lib/tools/codex-remove.js +184 -0
  233. package/dist/lib/tools/codex-remove.js.map +1 -0
  234. package/dist/lib/tools/codex-setup.d.ts +8 -0
  235. package/dist/lib/tools/codex-setup.d.ts.map +1 -0
  236. package/dist/lib/tools/codex-setup.js +26 -0
  237. package/dist/lib/tools/codex-setup.js.map +1 -0
  238. package/dist/lib/tools/codex-status.d.ts +8 -0
  239. package/dist/lib/tools/codex-status.d.ts.map +1 -0
  240. package/dist/lib/tools/codex-status.js +263 -0
  241. package/dist/lib/tools/codex-status.js.map +1 -0
  242. package/dist/lib/tools/codex-switch.d.ts +8 -0
  243. package/dist/lib/tools/codex-switch.d.ts.map +1 -0
  244. package/dist/lib/tools/codex-switch.js +122 -0
  245. package/dist/lib/tools/codex-switch.js.map +1 -0
  246. package/dist/lib/tools/codex-tag.d.ts +8 -0
  247. package/dist/lib/tools/codex-tag.d.ts.map +1 -0
  248. package/dist/lib/tools/codex-tag.js +101 -0
  249. package/dist/lib/tools/codex-tag.js.map +1 -0
  250. package/dist/lib/tools/index.d.ts +143 -0
  251. package/dist/lib/tools/index.d.ts.map +1 -0
  252. package/dist/lib/tools/index.js +67 -0
  253. package/dist/lib/tools/index.js.map +1 -0
  254. package/dist/lib/ui/format.d.ts.map +1 -1
  255. package/dist/lib/ui/format.js +5 -0
  256. package/dist/lib/ui/format.js.map +1 -1
  257. package/dist/lib/ui/runtime.d.ts +10 -0
  258. package/dist/lib/ui/runtime.d.ts.map +1 -1
  259. package/dist/lib/ui/runtime.js +16 -11
  260. package/dist/lib/ui/runtime.js.map +1 -1
  261. package/dist/lib/ui/select.d.ts.map +1 -1
  262. package/dist/lib/ui/select.js +12 -3
  263. package/dist/lib/ui/select.js.map +1 -1
  264. package/dist/lib/ui/theme.d.ts +19 -0
  265. package/dist/lib/ui/theme.d.ts.map +1 -1
  266. package/dist/lib/ui/theme.js +27 -0
  267. package/dist/lib/ui/theme.js.map +1 -1
  268. package/package.json +24 -16
  269. package/scripts/copy-oauth-success.js +16 -6
  270. package/scripts/install-oc-codex-multi-auth-core.js +462 -381
  271. package/scripts/install-oc-codex-multi-auth.js +15 -15
  272. package/scripts/test-all-models.sh +1 -1
  273. package/dist/lib/audit.d.ts +0 -45
  274. package/dist/lib/audit.d.ts.map +0 -1
  275. package/dist/lib/audit.js +0 -154
  276. package/dist/lib/audit.js.map +0 -1
  277. package/dist/lib/auth-rate-limit.d.ts +0 -20
  278. package/dist/lib/auth-rate-limit.d.ts.map +0 -1
  279. package/dist/lib/auth-rate-limit.js +0 -91
  280. package/dist/lib/auth-rate-limit.js.map +0 -1
  281. package/dist/lib/runtime-contracts.d.ts +0 -16
  282. package/dist/lib/runtime-contracts.d.ts.map +0 -1
  283. package/dist/lib/runtime-contracts.js +0 -24
  284. package/dist/lib/runtime-contracts.js.map +0 -1
@@ -0,0 +1,289 @@
1
+ /**
2
+ * `codex-keychain` tool — inspect and manage the opt-in OS-keychain backend.
3
+ *
4
+ * Phase 4 F1. Companion to `lib/storage/keychain.ts`. The tool exposes three
5
+ * subcommands chosen for operator-level control over the credential surface:
6
+ *
7
+ * - `status`: report which backend is active and whether the OS keychain
8
+ * is reachable. Never mutates state; safe to run under any config.
9
+ * - `migrate`: explicitly move the current on-disk JSON accounts file into
10
+ * the OS keychain, rename the JSON as
11
+ * `<path>.migrated-to-keychain.<ts>` for rollback, and leave the
12
+ * authoritative copy in the keychain. Idempotent: running again when
13
+ * the keychain already holds a fresher copy is a no-op.
14
+ * - `rollback`: restore the most recent `.migrated-to-keychain.<ts>`
15
+ * backup next to the accounts file and delete the keychain entry so
16
+ * subsequent loads read from disk again. The inverse of `migrate`.
17
+ *
18
+ * Runs under the same storage lock as `saveAccounts`/`loadAccounts` so the
19
+ * mutation cannot interleave with an in-flight rotation save.
20
+ *
21
+ * Security notes:
22
+ * - No secret value ever reaches the tool output. Success messages show
23
+ * account counts and file paths, never token material.
24
+ * - Failures fall back to JSON at the storage layer — this tool never
25
+ * hides that from the operator.
26
+ */
27
+ import { promises as fs } from "node:fs";
28
+ import { basename, dirname, join } from "node:path";
29
+ import { tool } from "@opencode-ai/plugin/tool";
30
+ import { clearAccounts, loadAccounts, saveAccounts, } from "../storage.js";
31
+ import { deleteFromKeychain, isKeychainOptInEnabled, keychainIsAvailable, readFromKeychain, } from "../storage/keychain.js";
32
+ import { getCurrentProjectStorageKey, getStoragePath } from "../storage/state.js";
33
+ import { normalizeAccountStorage } from "../storage/normalize.js";
34
+ import { formatUiHeader, formatUiItem, formatUiKeyValue, } from "../ui/format.js";
35
+ function normalizeSubcommand(raw) {
36
+ const v = (raw ?? "").trim().toLowerCase();
37
+ if (v === "migrate" || v === "rollback")
38
+ return v;
39
+ return "status";
40
+ }
41
+ /**
42
+ * List `<path>.migrated-to-keychain.<ts>` siblings of the current storage
43
+ * path, sorted most-recent first by `fs.stat().mtimeMs` (F1 post-merge
44
+ * MEDIUM finding). The previous implementation sorted the filenames
45
+ * lexicographically, which happens to produce the correct order when all
46
+ * filenames share the fixed-width ISO-8601 suffix
47
+ * (`YYYY-MM-DDTHH-MM-SS-mmmZ`) emitted by `migrateOnDiskJsonToKeychainBackup`
48
+ * in `lib/storage/load-save.ts`. Any format drift (locale epoch, test
49
+ * fixture with non-ISO suffix, future migration-suffix change) silently
50
+ * picks the alphabetically-last entry instead of the most-recent. Sorting
51
+ * by `mtimeMs` is format-independent and matches "most recent backup"
52
+ * exactly. The filename tiebreaker (rare: identical mtime) preserves the
53
+ * previous descending-lex behaviour so the function stays deterministic.
54
+ *
55
+ * Exported as `_findMigrationBackupsForTests` below so the sort order can
56
+ * be asserted without stubbing the tool closure.
57
+ */
58
+ async function findMigrationBackups(storagePath) {
59
+ const dir = dirname(storagePath);
60
+ const base = basename(storagePath);
61
+ const prefix = `${base}.migrated-to-keychain.`;
62
+ let entries;
63
+ try {
64
+ entries = await fs.readdir(dir);
65
+ }
66
+ catch {
67
+ return [];
68
+ }
69
+ const matches = entries.filter((name) => name.startsWith(prefix));
70
+ const withMtime = await Promise.all(matches.map(async (name) => {
71
+ const full = join(dir, name);
72
+ let mtimeMs = Number.NEGATIVE_INFINITY;
73
+ try {
74
+ const st = await fs.stat(full);
75
+ mtimeMs = st.mtimeMs;
76
+ }
77
+ catch {
78
+ // Stat failure: keep -Infinity so the entry sorts last.
79
+ // This protects against a backup that disappeared between
80
+ // readdir and stat (race) without crashing the tool.
81
+ }
82
+ return { full, name, mtimeMs };
83
+ }));
84
+ withMtime.sort((a, b) => {
85
+ if (b.mtimeMs !== a.mtimeMs)
86
+ return b.mtimeMs - a.mtimeMs;
87
+ return a.name < b.name ? 1 : a.name > b.name ? -1 : 0;
88
+ });
89
+ return withMtime.map((entry) => entry.full);
90
+ }
91
+ /**
92
+ * Test-only export for `findMigrationBackups`. Kept separate from the
93
+ * tool factory so the sort semantics can be asserted directly against a
94
+ * temp directory in `test/tools-codex-keychain.test.ts`.
95
+ */
96
+ export async function _findMigrationBackupsForTests(storagePath) {
97
+ return findMigrationBackups(storagePath);
98
+ }
99
+ export function createCodexKeychainTool(ctx) {
100
+ // ctx.resolveUiRuntime is the only helper we need: it threads the v2
101
+ // TUI / color-profile state from plugin config + CODEX_TUI_* env into
102
+ // every format helper call, so output is consistent with the rest of
103
+ // the codex-* tools without leaking the UI-runtime plumbing in here.
104
+ const { resolveUiRuntime } = ctx;
105
+ return tool({
106
+ description: "Inspect and manage the opt-in OS-keychain credential backend. Subcommands: status (default), migrate, rollback. Rollback requires confirm=true when a current accounts JSON file exists alongside the backup (safety gate).",
107
+ args: {
108
+ command: tool.schema
109
+ .string()
110
+ .optional()
111
+ .describe('Subcommand: "status" (default), "migrate", or "rollback".'),
112
+ confirm: tool.schema
113
+ .boolean()
114
+ .optional()
115
+ .describe("rollback only: pass true to archive any current accounts JSON as `.pre-rollback.<ts>` before restoring the backup. Without confirm=true, rollback refuses if a current file exists."),
116
+ },
117
+ async execute({ command, confirm, }) {
118
+ const ui = resolveUiRuntime();
119
+ const sub = normalizeSubcommand(command);
120
+ const optIn = isKeychainOptInEnabled();
121
+ const projectKey = getCurrentProjectStorageKey();
122
+ const storagePath = (() => {
123
+ try {
124
+ return getStoragePath();
125
+ }
126
+ catch {
127
+ return "<unresolved>";
128
+ }
129
+ })();
130
+ if (sub === "status") {
131
+ // When opt-in is off, skip the probe entirely (F1 post-merge
132
+ // LOW finding). Probing under an unset `CODEX_KEYCHAIN`
133
+ // violates the "unset -> no keychain code path" invariant
134
+ // and can trigger a first-run macOS "allow/always allow"
135
+ // prompt for users who run `codex-keychain status` without
136
+ // opting in. `keychainIsAvailable` already short-circuits
137
+ // on the opt-in check; we surface that explicitly here so
138
+ // the status line reads "not checked; CODEX_KEYCHAIN unset"
139
+ // instead of the misleading "keychain unavailable".
140
+ const available = optIn ? await keychainIsAvailable() : false;
141
+ const keychainHasEntry = optIn
142
+ ? (await readFromKeychain(projectKey)) !== null
143
+ : false;
144
+ const activeBackend = !optIn
145
+ ? "JSON (keychain disabled; CODEX_KEYCHAIN unset)"
146
+ : available && keychainHasEntry
147
+ ? "keychain"
148
+ : available
149
+ ? "keychain (empty, JSON fallback)"
150
+ : "JSON (keychain unavailable)";
151
+ const lines = [];
152
+ lines.push(...formatUiHeader(ui, "Codex keychain status"));
153
+ lines.push("");
154
+ lines.push(formatUiKeyValue(ui, "Active backend", activeBackend));
155
+ lines.push(formatUiKeyValue(ui, "CODEX_KEYCHAIN", optIn ? "1 (enabled)" : "unset/disabled"));
156
+ lines.push(formatUiKeyValue(ui, "Keychain reachable", optIn ? (available ? "yes" : "no") : "not checked (opt-in off)"));
157
+ lines.push(formatUiKeyValue(ui, "Project scope", projectKey ?? "global"));
158
+ lines.push(formatUiKeyValue(ui, "On-disk path", storagePath));
159
+ if (!optIn) {
160
+ lines.push("");
161
+ lines.push(formatUiItem(ui, "Set CODEX_KEYCHAIN=1 to enable the OS-keychain backend. Migration runs on the next save."));
162
+ }
163
+ return lines.join("\n");
164
+ }
165
+ if (sub === "migrate") {
166
+ if (!optIn) {
167
+ return "codex-keychain migrate: refusing to migrate because CODEX_KEYCHAIN is not set to 1. Enable the opt-in first, then re-run this command.";
168
+ }
169
+ const storage = await loadAccounts();
170
+ if (!storage) {
171
+ return "codex-keychain migrate: no accounts found to migrate. Nothing to do.";
172
+ }
173
+ // Re-saving under the opt-in path triggers the same keychain
174
+ // write + JSON-backup flow used by every rotation save, so we
175
+ // avoid duplicating the migration logic here.
176
+ await saveAccounts(storage);
177
+ return [
178
+ ...formatUiHeader(ui, "Codex keychain migrate"),
179
+ "",
180
+ formatUiItem(ui, `Migrated ${storage.accounts.length} account(s) to the OS keychain.`),
181
+ formatUiKeyValue(ui, "Project scope", projectKey ?? "global"),
182
+ formatUiItem(ui, "On-disk JSON (if any) was renamed with a .migrated-to-keychain.<timestamp> suffix. Use `codex-keychain rollback` to restore it."),
183
+ ].join("\n");
184
+ }
185
+ // rollback
186
+ const backups = await findMigrationBackups(storagePath);
187
+ const mostRecent = backups[0];
188
+ if (!mostRecent) {
189
+ return `codex-keychain rollback: no .migrated-to-keychain.<ts> backup found next to ${storagePath}. Nothing to restore.`;
190
+ }
191
+ // Verify the backup parses as a V3 storage blob before we trust
192
+ // it. A corrupt backup must not be promoted to the active file.
193
+ let accountCount = 0;
194
+ try {
195
+ const raw = await fs.readFile(mostRecent, "utf-8");
196
+ const parsed = JSON.parse(raw);
197
+ const normalized = normalizeAccountStorage(parsed, mostRecent);
198
+ if (!normalized) {
199
+ return `codex-keychain rollback: backup at ${mostRecent} did not parse as V3 account storage. Aborted.`;
200
+ }
201
+ accountCount = normalized.accounts.length;
202
+ }
203
+ catch (err) {
204
+ return `codex-keychain rollback: failed to read backup at ${mostRecent}: ${err.message}`;
205
+ }
206
+ // Wipe the keychain entry + any current on-disk file, then rename
207
+ // the backup back to the canonical storage path so the next load
208
+ // reads from disk again. clearAccounts handles both sides.
209
+ await clearAccounts();
210
+ // Silent-clobber guard (F1 post-merge MEDIUM finding). On POSIX,
211
+ // `fs.rename(backup, storagePath)` silently overwrites an
212
+ // existing destination. If `clearAccounts` failed to unlink the
213
+ // current JSON (EACCES/EBUSY) or a race write landed a new file
214
+ // between clearAccounts and rename, the user's current accounts
215
+ // would be silently discarded with no way to recover them.
216
+ // Require explicit `confirm=true` before overwriting; without
217
+ // it, refuse and surface the offending path.
218
+ let currentExists = false;
219
+ try {
220
+ await fs.access(storagePath);
221
+ currentExists = true;
222
+ }
223
+ catch {
224
+ /* canonical path is clear; safe to rename */
225
+ }
226
+ let preRollbackArchive = null;
227
+ if (currentExists) {
228
+ if (!confirm) {
229
+ return [
230
+ `codex-keychain rollback: refusing to overwrite existing accounts file at ${storagePath}.`,
231
+ `Backup at ${mostRecent} was not restored.`,
232
+ "Pass confirm=true to archive the current file as .pre-rollback.<timestamp> and proceed, or move the current file aside and re-run.",
233
+ ].join("\n");
234
+ }
235
+ const archiveSuffix = new Date()
236
+ .toISOString()
237
+ .replace(/[:.]/g, "-");
238
+ preRollbackArchive = `${storagePath}.pre-rollback.${archiveSuffix}`;
239
+ try {
240
+ await fs.rename(storagePath, preRollbackArchive);
241
+ }
242
+ catch (err) {
243
+ return `codex-keychain rollback: failed to archive current accounts file at ${storagePath} -> ${preRollbackArchive}: ${err.message}. Backup at ${mostRecent} was not restored.`;
244
+ }
245
+ }
246
+ try {
247
+ await fs.rename(mostRecent, storagePath);
248
+ }
249
+ catch (err) {
250
+ return `codex-keychain rollback: failed to rename ${mostRecent} -> ${storagePath}: ${err.message}`;
251
+ }
252
+ // Re-apply 0o600 after rollback rename (F1 post-merge LOW
253
+ // finding). Mirror the chmod applied on the backup-write path
254
+ // so a restored file can never end up group/world-readable even
255
+ // if the backup's mode drifted between migration and rollback.
256
+ // Windows ignores POSIX mode bits, so skip there.
257
+ if (process.platform !== "win32") {
258
+ try {
259
+ await fs.chmod(storagePath, 0o600);
260
+ }
261
+ catch {
262
+ /* non-fatal: file is restored, mode drift is a hardening
263
+ * nicety. Swallow to avoid failing the rollback on a
264
+ * permission-sensitive mount. */
265
+ }
266
+ }
267
+ // Best-effort keychain delete in case opt-in is still on; the
268
+ // storage layer will honour that on subsequent saves.
269
+ try {
270
+ await deleteFromKeychain(projectKey);
271
+ }
272
+ catch {
273
+ /* already deleted by clearAccounts */
274
+ }
275
+ const lines = [
276
+ ...formatUiHeader(ui, "Codex keychain rollback"),
277
+ "",
278
+ formatUiItem(ui, `Restored ${accountCount} account(s) from backup ${mostRecent}.`),
279
+ formatUiKeyValue(ui, "Active file", storagePath),
280
+ ];
281
+ if (preRollbackArchive) {
282
+ lines.push(formatUiKeyValue(ui, "Previous file archived at", preRollbackArchive));
283
+ }
284
+ lines.push(formatUiItem(ui, "To stop using the keychain backend, unset CODEX_KEYCHAIN (or set it to any value other than \"1\") before the next save."));
285
+ return lines.join("\n");
286
+ },
287
+ });
288
+ }
289
+ //# sourceMappingURL=codex-keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-keychain.js","sourceRoot":"","sources":["../../../lib/tools/codex-keychain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `codex-label` tool — set or clear per-account display label.
3
+ * Extracted from `index.ts` per RC-1 Phase 2.
4
+ */
5
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
6
+ import type { ToolContext } from "./index.js";
7
+ export declare function createCodexLabelTool(ctx: ToolContext): ToolDefinition;
8
+ //# sourceMappingURL=codex-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-label.d.ts","sourceRoot":"","sources":["../../../lib/tools/codex-label.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AASrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,CA6LrE"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * `codex-label` tool — set or clear per-account display label.
3
+ * Extracted from `index.ts` per RC-1 Phase 2.
4
+ */
5
+ import { tool } from "@opencode-ai/plugin/tool";
6
+ import { loadAccounts, saveAccounts } from "../storage.js";
7
+ import { AccountManager } from "../accounts.js";
8
+ import { logWarn } from "../logger.js";
9
+ import { formatUiHeader, formatUiItem, formatUiKeyValue, } from "../ui/format.js";
10
+ export function createCodexLabelTool(ctx) {
11
+ const { resolveUiRuntime, promptAccountIndexSelection, supportsInteractiveMenus, formatCommandAccountLabel, getStatusMarker, cachedAccountManagerRef, accountManagerPromiseRef, } = ctx;
12
+ return tool({
13
+ description: "Set or clear a beginner-friendly display label for an account (interactive picker when index is omitted).",
14
+ args: {
15
+ index: tool.schema
16
+ .number()
17
+ .optional()
18
+ .describe("Account number to update (1-based, e.g., 1 for first account)"),
19
+ label: tool.schema
20
+ .string()
21
+ .describe("Display label. Use an empty string to clear (e.g., Work, Personal, Team A)"),
22
+ },
23
+ async execute({ index, label }) {
24
+ const ui = resolveUiRuntime();
25
+ const storage = await loadAccounts();
26
+ if (!storage || storage.accounts.length === 0) {
27
+ if (ui.v2Enabled) {
28
+ return [
29
+ ...formatUiHeader(ui, "Set account label"),
30
+ "",
31
+ formatUiItem(ui, "No accounts configured.", "warning"),
32
+ formatUiItem(ui, "Run: opencode auth login", "accent"),
33
+ ].join("\n");
34
+ }
35
+ return "No Codex accounts configured. Run: opencode auth login";
36
+ }
37
+ let resolvedIndex = index;
38
+ if (resolvedIndex === undefined) {
39
+ const selectedIndex = await promptAccountIndexSelection(ui, storage, "Set account label");
40
+ if (selectedIndex === null) {
41
+ if (supportsInteractiveMenus()) {
42
+ if (ui.v2Enabled) {
43
+ return [
44
+ ...formatUiHeader(ui, "Set account label"),
45
+ "",
46
+ formatUiItem(ui, "No account selected.", "warning"),
47
+ formatUiItem(ui, 'Run again and pick an account, or pass codex-label index=2 label="Work".', "muted"),
48
+ ].join("\n");
49
+ }
50
+ return "No account selected.";
51
+ }
52
+ if (ui.v2Enabled) {
53
+ return [
54
+ ...formatUiHeader(ui, "Set account label"),
55
+ "",
56
+ formatUiItem(ui, "Missing account number.", "warning"),
57
+ formatUiItem(ui, 'Use: codex-label index=2 label="Work"', "accent"),
58
+ ].join("\n");
59
+ }
60
+ return 'Missing account number. Use: codex-label index=2 label="Work"';
61
+ }
62
+ resolvedIndex = selectedIndex + 1;
63
+ }
64
+ const targetIndex = Math.floor((resolvedIndex ?? 0) - 1);
65
+ if (!Number.isFinite(targetIndex) ||
66
+ targetIndex < 0 ||
67
+ targetIndex >= storage.accounts.length) {
68
+ if (ui.v2Enabled) {
69
+ return [
70
+ ...formatUiHeader(ui, "Set account label"),
71
+ "",
72
+ formatUiItem(ui, `Invalid account number: ${resolvedIndex}`, "danger"),
73
+ formatUiKeyValue(ui, "Valid range", `1-${storage.accounts.length}`, "muted"),
74
+ ].join("\n");
75
+ }
76
+ return `Invalid account number: ${resolvedIndex}\n\nValid range: 1-${storage.accounts.length}`;
77
+ }
78
+ const normalizedLabel = (label ?? "").trim().replace(/\s+/g, " ");
79
+ if (normalizedLabel.length > 60) {
80
+ if (ui.v2Enabled) {
81
+ return [
82
+ ...formatUiHeader(ui, "Set account label"),
83
+ "",
84
+ formatUiItem(ui, "Label is too long (max 60 characters).", "danger"),
85
+ ].join("\n");
86
+ }
87
+ return "Label is too long (max 60 characters).";
88
+ }
89
+ const account = storage.accounts[targetIndex];
90
+ if (!account) {
91
+ return `Account ${resolvedIndex} not found.`;
92
+ }
93
+ const previousLabel = account.accountLabel?.trim() ?? "";
94
+ if (normalizedLabel.length === 0) {
95
+ delete account.accountLabel;
96
+ }
97
+ else {
98
+ account.accountLabel = normalizedLabel;
99
+ }
100
+ try {
101
+ await saveAccounts(storage);
102
+ }
103
+ catch (saveError) {
104
+ logWarn("Failed to save account label update", {
105
+ error: String(saveError),
106
+ });
107
+ if (ui.v2Enabled) {
108
+ return [
109
+ ...formatUiHeader(ui, "Set account label"),
110
+ "",
111
+ formatUiItem(ui, "Label updated in memory but failed to persist.", "danger"),
112
+ ].join("\n");
113
+ }
114
+ return "Label updated in memory but failed to persist. Changes may be lost on restart.";
115
+ }
116
+ if (cachedAccountManagerRef.current) {
117
+ const reloadedManager = await AccountManager.loadFromDisk();
118
+ cachedAccountManagerRef.current = reloadedManager;
119
+ accountManagerPromiseRef.current = Promise.resolve(reloadedManager);
120
+ }
121
+ const accountLabel = formatCommandAccountLabel(account, targetIndex);
122
+ if (ui.v2Enabled) {
123
+ const statusText = normalizedLabel.length === 0
124
+ ? `Cleared label for ${accountLabel}`
125
+ : `Set label for ${accountLabel} to "${normalizedLabel}"`;
126
+ const previousText = previousLabel.length > 0
127
+ ? formatUiKeyValue(ui, "Previous label", previousLabel, "muted")
128
+ : formatUiKeyValue(ui, "Previous label", "none", "muted");
129
+ return [
130
+ ...formatUiHeader(ui, "Set account label"),
131
+ "",
132
+ formatUiItem(ui, `${getStatusMarker(ui, "ok")} ${statusText}`, "success"),
133
+ previousText,
134
+ ].join("\n");
135
+ }
136
+ if (normalizedLabel.length === 0) {
137
+ return `Cleared label for ${accountLabel}`;
138
+ }
139
+ return `Set label for ${accountLabel} to "${normalizedLabel}"`;
140
+ },
141
+ });
142
+ }
143
+ //# sourceMappingURL=codex-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-label.js","sourceRoot":"","sources":["../../../lib/tools/codex-label.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `codex-limits` tool — show Codex usage limits per account.
3
+ * Extracted from `index.ts` per RC-1 Phase 2.
4
+ */
5
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
6
+ import type { ToolContext } from "./index.js";
7
+ export declare function createCodexLimitsTool(ctx: ToolContext): ToolDefinition;
8
+ //# sourceMappingURL=codex-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-limits.d.ts","sourceRoot":"","sources":["../../../lib/tools/codex-limits.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAoBrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,CAwrBtE"}