oh-my-codex 0.18.2 → 0.18.4

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 (233) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +1 -0
  4. package/dist/agents/__tests__/definitions.test.js +9 -0
  5. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  6. package/dist/agents/__tests__/native-config.test.js +1 -0
  7. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  8. package/dist/agents/definitions.d.ts.map +1 -1
  9. package/dist/agents/definitions.js +10 -0
  10. package/dist/agents/definitions.js.map +1 -1
  11. package/dist/auth/__tests__/config-sessions.test.d.ts +2 -0
  12. package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
  13. package/dist/auth/__tests__/config-sessions.test.js +48 -0
  14. package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
  15. package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
  16. package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
  17. package/dist/auth/__tests__/quota-rotation.test.js +33 -0
  18. package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
  19. package/dist/auth/__tests__/redact.test.d.ts +2 -0
  20. package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/redact.test.js +20 -0
  22. package/dist/auth/__tests__/redact.test.js.map +1 -0
  23. package/dist/auth/__tests__/storage.test.d.ts +2 -0
  24. package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
  25. package/dist/auth/__tests__/storage.test.js +108 -0
  26. package/dist/auth/__tests__/storage.test.js.map +1 -0
  27. package/dist/auth/config.d.ts +9 -0
  28. package/dist/auth/config.d.ts.map +1 -0
  29. package/dist/auth/config.js +77 -0
  30. package/dist/auth/config.js.map +1 -0
  31. package/dist/auth/hotswap.d.ts +36 -0
  32. package/dist/auth/hotswap.d.ts.map +1 -0
  33. package/dist/auth/hotswap.js +159 -0
  34. package/dist/auth/hotswap.js.map +1 -0
  35. package/dist/auth/index.d.ts +8 -0
  36. package/dist/auth/index.d.ts.map +1 -0
  37. package/dist/auth/index.js +8 -0
  38. package/dist/auth/index.js.map +1 -0
  39. package/dist/auth/paths.d.ts +12 -0
  40. package/dist/auth/paths.d.ts.map +1 -0
  41. package/dist/auth/paths.js +78 -0
  42. package/dist/auth/paths.js.map +1 -0
  43. package/dist/auth/quota-detector.d.ts +10 -0
  44. package/dist/auth/quota-detector.d.ts.map +1 -0
  45. package/dist/auth/quota-detector.js +40 -0
  46. package/dist/auth/quota-detector.js.map +1 -0
  47. package/dist/auth/redact.d.ts +2 -0
  48. package/dist/auth/redact.d.ts.map +1 -0
  49. package/dist/auth/redact.js +26 -0
  50. package/dist/auth/redact.js.map +1 -0
  51. package/dist/auth/rotation.d.ts +9 -0
  52. package/dist/auth/rotation.d.ts.map +1 -0
  53. package/dist/auth/rotation.js +26 -0
  54. package/dist/auth/rotation.js.map +1 -0
  55. package/dist/auth/sessions.d.ts +15 -0
  56. package/dist/auth/sessions.d.ts.map +1 -0
  57. package/dist/auth/sessions.js +62 -0
  58. package/dist/auth/sessions.js.map +1 -0
  59. package/dist/auth/storage.d.ts +27 -0
  60. package/dist/auth/storage.d.ts.map +1 -0
  61. package/dist/auth/storage.js +111 -0
  62. package/dist/auth/storage.js.map +1 -0
  63. package/dist/cli/__tests__/auth.test.d.ts +2 -0
  64. package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
  65. package/dist/cli/__tests__/auth.test.js +168 -0
  66. package/dist/cli/__tests__/auth.test.js.map +1 -0
  67. package/dist/cli/__tests__/doctor-warning-copy.test.js +88 -3
  68. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  69. package/dist/cli/__tests__/explore.test.js +28 -7
  70. package/dist/cli/__tests__/explore.test.js.map +1 -1
  71. package/dist/cli/__tests__/index.test.js +70 -2
  72. package/dist/cli/__tests__/index.test.js.map +1 -1
  73. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  74. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  75. package/dist/cli/__tests__/setup-agents-overwrite.test.js +30 -1
  76. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-install-mode.test.js +103 -17
  78. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  80. package/dist/cli/__tests__/sparkshell-cli.test.js +2 -2
  81. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  82. package/dist/cli/auth.d.ts +4 -0
  83. package/dist/cli/auth.d.ts.map +1 -0
  84. package/dist/cli/auth.js +89 -0
  85. package/dist/cli/auth.js.map +1 -0
  86. package/dist/cli/doctor.d.ts.map +1 -1
  87. package/dist/cli/doctor.js +128 -19
  88. package/dist/cli/doctor.js.map +1 -1
  89. package/dist/cli/explore.d.ts +1 -0
  90. package/dist/cli/explore.d.ts.map +1 -1
  91. package/dist/cli/explore.js +18 -0
  92. package/dist/cli/explore.js.map +1 -1
  93. package/dist/cli/index.d.ts +20 -2
  94. package/dist/cli/index.d.ts.map +1 -1
  95. package/dist/cli/index.js +114 -10
  96. package/dist/cli/index.js.map +1 -1
  97. package/dist/cli/question.d.ts.map +1 -1
  98. package/dist/cli/question.js +5 -1
  99. package/dist/cli/question.js.map +1 -1
  100. package/dist/cli/setup.d.ts.map +1 -1
  101. package/dist/cli/setup.js +29 -57
  102. package/dist/cli/setup.js.map +1 -1
  103. package/dist/config/__tests__/deep-interview.test.d.ts +2 -0
  104. package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
  105. package/dist/config/__tests__/deep-interview.test.js +239 -0
  106. package/dist/config/__tests__/deep-interview.test.js.map +1 -0
  107. package/dist/config/__tests__/generator-idempotent.test.js +128 -5
  108. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  109. package/dist/config/deep-interview.d.ts +22 -0
  110. package/dist/config/deep-interview.d.ts.map +1 -0
  111. package/dist/config/deep-interview.js +151 -0
  112. package/dist/config/deep-interview.js.map +1 -0
  113. package/dist/config/generator.d.ts +13 -4
  114. package/dist/config/generator.d.ts.map +1 -1
  115. package/dist/config/generator.js +154 -40
  116. package/dist/config/generator.js.map +1 -1
  117. package/dist/hooks/__tests__/agents-overlay.test.js +9 -7
  118. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  119. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -1
  120. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -0
  122. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  123. package/dist/hooks/__tests__/explore-routing.test.js +10 -12
  124. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  125. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +13 -15
  126. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  127. package/dist/hooks/__tests__/keyword-detector.test.js +301 -0
  128. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +33 -0
  130. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  131. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +60 -0
  132. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  133. package/dist/hooks/deep-interview-config-instruction.d.ts +3 -0
  134. package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
  135. package/dist/hooks/deep-interview-config-instruction.js +47 -0
  136. package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
  137. package/dist/hooks/explore-routing.d.ts.map +1 -1
  138. package/dist/hooks/explore-routing.js +8 -13
  139. package/dist/hooks/explore-routing.js.map +1 -1
  140. package/dist/hooks/keyword-detector.d.ts +5 -0
  141. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  142. package/dist/hooks/keyword-detector.js +52 -8
  143. package/dist/hooks/keyword-detector.js.map +1 -1
  144. package/dist/hud/__tests__/hud-tmux-injection.test.js +19 -14
  145. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  146. package/dist/hud/__tests__/reconcile.test.js +117 -9
  147. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  148. package/dist/hud/__tests__/tmux.test.js +103 -1
  149. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  150. package/dist/hud/index.d.ts +1 -1
  151. package/dist/hud/index.d.ts.map +1 -1
  152. package/dist/hud/index.js +24 -2
  153. package/dist/hud/index.js.map +1 -1
  154. package/dist/hud/reconcile.d.ts +1 -1
  155. package/dist/hud/reconcile.d.ts.map +1 -1
  156. package/dist/hud/reconcile.js +23 -0
  157. package/dist/hud/reconcile.js.map +1 -1
  158. package/dist/hud/tmux.d.ts +7 -0
  159. package/dist/hud/tmux.d.ts.map +1 -1
  160. package/dist/hud/tmux.js +46 -9
  161. package/dist/hud/tmux.js.map +1 -1
  162. package/dist/question/__tests__/deep-interview.test.js +80 -7
  163. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  164. package/dist/question/__tests__/policy.test.js +83 -9
  165. package/dist/question/__tests__/policy.test.js.map +1 -1
  166. package/dist/question/autopilot-wait.d.ts +10 -0
  167. package/dist/question/autopilot-wait.d.ts.map +1 -0
  168. package/dist/question/autopilot-wait.js +134 -0
  169. package/dist/question/autopilot-wait.js.map +1 -0
  170. package/dist/question/deep-interview.d.ts +2 -0
  171. package/dist/question/deep-interview.d.ts.map +1 -1
  172. package/dist/question/deep-interview.js +4 -0
  173. package/dist/question/deep-interview.js.map +1 -1
  174. package/dist/question/policy.d.ts +1 -0
  175. package/dist/question/policy.d.ts.map +1 -1
  176. package/dist/question/policy.js +19 -0
  177. package/dist/question/policy.js.map +1 -1
  178. package/dist/scripts/__tests__/codex-native-hook.test.js +718 -0
  179. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  180. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  181. package/dist/scripts/codex-native-hook.js +69 -5
  182. package/dist/scripts/codex-native-hook.js.map +1 -1
  183. package/dist/scripts/notify-hook.js +13 -0
  184. package/dist/scripts/notify-hook.js.map +1 -1
  185. package/dist/state/__tests__/planning-gate.test.d.ts +2 -0
  186. package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
  187. package/dist/state/__tests__/planning-gate.test.js +219 -0
  188. package/dist/state/__tests__/planning-gate.test.js.map +1 -0
  189. package/dist/state/workflow-transition.d.ts +23 -0
  190. package/dist/state/workflow-transition.d.ts.map +1 -1
  191. package/dist/state/workflow-transition.js +63 -0
  192. package/dist/state/workflow-transition.js.map +1 -1
  193. package/dist/subagents/__tests__/tracker.test.js +69 -0
  194. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  195. package/dist/subagents/tracker.d.ts +5 -0
  196. package/dist/subagents/tracker.d.ts.map +1 -1
  197. package/dist/subagents/tracker.js +16 -0
  198. package/dist/subagents/tracker.js.map +1 -1
  199. package/dist/team/__tests__/tmux-session.test.js +86 -0
  200. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  201. package/dist/team/tmux-session.d.ts.map +1 -1
  202. package/dist/team/tmux-session.js +7 -0
  203. package/dist/team/tmux-session.js.map +1 -1
  204. package/dist/ultragoal/__tests__/artifacts.test.js +126 -0
  205. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  206. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  207. package/dist/ultragoal/artifacts.js +126 -8
  208. package/dist/ultragoal/artifacts.js.map +1 -1
  209. package/package.json +1 -1
  210. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  211. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  212. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +11 -1
  213. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +4 -4
  214. package/plugins/oh-my-codex/skills/plan/SKILL.md +5 -5
  215. package/plugins/oh-my-codex/skills/ralph/SKILL.md +1 -1
  216. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +10 -6
  217. package/prompts/executor.md +1 -1
  218. package/prompts/explore-harness.md +2 -2
  219. package/prompts/explore.md +1 -1
  220. package/prompts/planner.md +1 -1
  221. package/prompts/scholastic.md +11 -0
  222. package/prompts/sisyphus-lite.md +1 -1
  223. package/skills/autopilot/SKILL.md +2 -2
  224. package/skills/deep-interview/SKILL.md +11 -1
  225. package/skills/omx-setup/SKILL.md +4 -4
  226. package/skills/plan/SKILL.md +5 -5
  227. package/skills/ralph/SKILL.md +1 -1
  228. package/skills/ralplan/SKILL.md +10 -6
  229. package/src/scripts/__tests__/codex-native-hook.test.ts +853 -0
  230. package/src/scripts/codex-native-hook.ts +73 -3
  231. package/src/scripts/notify-hook.ts +15 -0
  232. package/templates/AGENTS.md +3 -3
  233. package/templates/catalog-manifest.json +5 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAgB,aAAa,IAAI,YAAY,CAE5C;AAED,wBAAsB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAa3E;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5F;AAeD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,GAAG,OAAa,GACf,OAAO,CAAC,cAAc,CAAC,CAWzB;AAED,wBAAsB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGxE;AAED,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,GAAG,OAAa,GACf,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,EACb,GAAG,OAAa,GACf,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpF"}
@@ -0,0 +1,111 @@
1
+ import { randomBytes } from "crypto";
2
+ import { dirname } from "path";
3
+ import { readFile, rename, rm, writeFile, chmod } from "fs/promises";
4
+ import { existsSync } from "fs";
5
+ import { AUTH_FILE_MODE, assertNoSymlink, assertReadableFile, ensurePrivateDir, resolveAuthMetadataPath, resolveOmxAuthDir, resolveSlotPath, validateSlotName, } from "./paths.js";
6
+ export async function atomicWriteFile(targetPath, data, options = {}) {
7
+ const dir = dirname(targetPath);
8
+ await ensurePrivateDir(dir);
9
+ await assertNoSymlink(targetPath, "auth target");
10
+ const tempPath = `${targetPath}.tmp-${process.pid}-${Date.now()}-${randomBytes(4).toString("hex")}`;
11
+ try {
12
+ await writeFile(tempPath, data, { mode: options.mode ?? AUTH_FILE_MODE });
13
+ if (process.platform !== "win32")
14
+ await chmod(tempPath, options.mode ?? AUTH_FILE_MODE).catch(() => undefined);
15
+ await options.beforeRename?.(tempPath);
16
+ await rename(tempPath, targetPath);
17
+ if (process.platform !== "win32")
18
+ await chmod(targetPath, options.mode ?? AUTH_FILE_MODE).catch(() => undefined);
19
+ }
20
+ catch (err) {
21
+ await rm(tempPath, { force: true }).catch(() => undefined);
22
+ throw err;
23
+ }
24
+ }
25
+ export function emptyMetadata() {
26
+ return { version: 1, slots: [] };
27
+ }
28
+ export async function readAuthMetadata(home) {
29
+ const path = resolveAuthMetadataPath(home);
30
+ if (!existsSync(path))
31
+ return emptyMetadata();
32
+ const parsed = JSON.parse(await readFile(path, "utf-8"));
33
+ return {
34
+ version: 1,
35
+ currentSlot: typeof parsed.currentSlot === "string" ? parsed.currentSlot : undefined,
36
+ slots: Array.isArray(parsed.slots)
37
+ ? parsed.slots
38
+ .filter((slot) => Boolean(slot && typeof slot.slot === "string"))
39
+ .map((slot) => ({ ...slot, slot: validateSlotName(slot.slot) }))
40
+ : [],
41
+ };
42
+ }
43
+ export async function writeAuthMetadata(metadata, home) {
44
+ const authDir = resolveOmxAuthDir(home);
45
+ await ensurePrivateDir(authDir);
46
+ await atomicWriteFile(resolveAuthMetadataPath(home), `${JSON.stringify(metadata, null, 2)}\n`, {
47
+ mode: AUTH_FILE_MODE,
48
+ });
49
+ }
50
+ function upsertSlotRecord(metadata, slot, nowIso) {
51
+ const safeSlot = validateSlotName(slot);
52
+ const existing = metadata.slots.find((record) => record.slot === safeSlot);
53
+ if (existing) {
54
+ existing.updatedAt = nowIso;
55
+ return existing;
56
+ }
57
+ const record = { slot: safeSlot, createdAt: nowIso, updatedAt: nowIso };
58
+ metadata.slots.push(record);
59
+ metadata.slots.sort((a, b) => a.slot.localeCompare(b.slot));
60
+ return record;
61
+ }
62
+ export async function addSlotFromAuthFile(slot, liveAuthPath, home, now = new Date()) {
63
+ const safeSlot = validateSlotName(slot);
64
+ await assertReadableFile(liveAuthPath, "live Codex auth.json");
65
+ await ensurePrivateDir(resolveOmxAuthDir(home));
66
+ const data = await readFile(liveAuthPath);
67
+ const target = resolveSlotPath(safeSlot, home);
68
+ await atomicWriteFile(target, data, { mode: AUTH_FILE_MODE });
69
+ const metadata = await readAuthMetadata(home);
70
+ const record = upsertSlotRecord(metadata, safeSlot, now.toISOString());
71
+ await writeAuthMetadata(metadata, home);
72
+ return record;
73
+ }
74
+ export async function listSlots(home) {
75
+ const metadata = await readAuthMetadata(home);
76
+ return metadata.slots.filter((slot) => existsSync(resolveSlotPath(slot.slot, home)));
77
+ }
78
+ export async function useSlot(slot, liveAuthPath, home, now = new Date()) {
79
+ const safeSlot = validateSlotName(slot);
80
+ const slotPath = resolveSlotPath(safeSlot, home);
81
+ await assertReadableFile(slotPath, `auth slot ${safeSlot}`);
82
+ await ensurePrivateDir(dirname(liveAuthPath));
83
+ await assertNoSymlink(liveAuthPath, "live Codex auth.json");
84
+ const data = await readFile(slotPath);
85
+ await atomicWriteFile(liveAuthPath, data, { mode: AUTH_FILE_MODE });
86
+ const metadata = await readAuthMetadata(home);
87
+ const record = upsertSlotRecord(metadata, safeSlot, now.toISOString());
88
+ record.lastUsedAt = now.toISOString();
89
+ delete record.exhaustedAt;
90
+ metadata.currentSlot = safeSlot;
91
+ await writeAuthMetadata(metadata, home);
92
+ return record;
93
+ }
94
+ export async function markSlotQuota(slot, home, now = new Date()) {
95
+ const safeSlot = validateSlotName(slot);
96
+ const metadata = await readAuthMetadata(home);
97
+ const record = upsertSlotRecord(metadata, safeSlot, now.toISOString());
98
+ record.lastQuotaAt = now.toISOString();
99
+ record.exhaustedAt = now.toISOString();
100
+ await writeAuthMetadata(metadata, home);
101
+ return metadata;
102
+ }
103
+ export async function clearSlotExhaustion(slot, home) {
104
+ const metadata = await readAuthMetadata(home);
105
+ const record = metadata.slots.find((entry) => entry.slot === validateSlotName(slot));
106
+ if (record) {
107
+ delete record.exhaustedAt;
108
+ await writeAuthMetadata(metadata, home);
109
+ }
110
+ }
111
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../../src/cli/__tests__/auth.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,168 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
4
+ import { spawnSync } from "node:child_process";
5
+ import { tmpdir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ function omxBin() {
9
+ const testDir = dirname(fileURLToPath(import.meta.url));
10
+ return join(testDir, "..", "..", "..", "dist", "cli", "omx.js");
11
+ }
12
+ function runOmx(cwd, argv, env = {}) {
13
+ const result = spawnSync(process.execPath, [omxBin(), ...argv], {
14
+ cwd,
15
+ encoding: "utf-8",
16
+ env: {
17
+ ...process.env,
18
+ HOME: env.HOME,
19
+ CODEX_HOME: env.CODEX_HOME ?? "",
20
+ NODE_OPTIONS: "",
21
+ OMX_AUTO_UPDATE: "0",
22
+ OMX_NOTIFY_FALLBACK: "0",
23
+ OMX_HOOK_DERIVED_SIGNALS: "0",
24
+ ...env,
25
+ },
26
+ });
27
+ return { status: result.status, stdout: result.stdout || "", stderr: result.stderr || "", error: result.error?.message || "" };
28
+ }
29
+ async function writeFakeCodex(binDir, script) {
30
+ await mkdir(binDir, { recursive: true });
31
+ const path = join(binDir, "codex");
32
+ await writeFile(path, script);
33
+ await chmod(path, 0o755);
34
+ return path;
35
+ }
36
+ describe("omx auth CLI", () => {
37
+ it("shows nested help and top-level hotswap help", async () => {
38
+ const wd = await mkdtemp(join(tmpdir(), "omx-auth-help-"));
39
+ try {
40
+ const help = runOmx(wd, ["auth", "--help"], { HOME: wd });
41
+ assert.equal(help.status, 0, help.stderr);
42
+ assert.match(help.stdout, /omx auth add <slot>/);
43
+ const top = runOmx(wd, ["--help"], { HOME: wd });
44
+ assert.match(top.stdout, /--hotswap/);
45
+ assert.match(top.stdout, /omx auth/);
46
+ }
47
+ finally {
48
+ await rm(wd, { recursive: true, force: true });
49
+ }
50
+ });
51
+ it("adds, lists, and uses slots through the compiled CLI without leaking tokens", async () => {
52
+ const wd = await mkdtemp(join(tmpdir(), "omx-auth-cli-"));
53
+ try {
54
+ const home = join(wd, "home");
55
+ const codexHome = join(home, ".codex");
56
+ const bin = join(wd, "bin");
57
+ await mkdir(codexHome, { recursive: true });
58
+ await writeFakeCodex(bin, `#!/bin/sh\nif [ "$1" = "login" ]; then mkdir -p "$CODEX_HOME"; printf '{"access_token":"sentinel-secret"}\\n' > "$CODEX_HOME/auth.json"; exit 0; fi\necho unexpected "$@" >&2\nexit 2\n`);
59
+ const env = { HOME: home, CODEX_HOME: codexHome, PATH: `${bin}:/usr/bin:/bin` };
60
+ const add = runOmx(wd, ["auth", "add", "work"], env);
61
+ assert.equal(add.status, 0, add.stderr);
62
+ assert.doesNotMatch(add.stdout + add.stderr, /sentinel-secret/);
63
+ const list = runOmx(wd, ["auth", "list", "--json"], env);
64
+ assert.equal(list.status, 0, list.stderr);
65
+ assert.match(list.stdout, /"slot": "work"/);
66
+ await writeFile(join(codexHome, "auth.json"), '{"access_token":"other"}\n');
67
+ const use = runOmx(wd, ["auth", "use", "work"], env);
68
+ assert.equal(use.status, 0, use.stderr);
69
+ assert.doesNotMatch(use.stdout + use.stderr, /sentinel-secret/);
70
+ assert.equal(await readFile(join(codexHome, "auth.json"), "utf-8"), '{"access_token":"sentinel-secret"}\n');
71
+ }
72
+ finally {
73
+ await rm(wd, { recursive: true, force: true });
74
+ }
75
+ });
76
+ it("adds project-scope slots from the same CODEX_HOME used by launch", async () => {
77
+ const wd = await mkdtemp(join(tmpdir(), "omx-auth-project-add-"));
78
+ try {
79
+ const home = join(wd, "home");
80
+ const bin = join(wd, "bin");
81
+ await mkdir(join(wd, ".omx"), { recursive: true });
82
+ await writeFile(join(wd, ".omx", "setup-scope.json"), '{"scope":"project"}\n');
83
+ await writeFakeCodex(bin, `#!/bin/sh
84
+ if [ "$1" = "login" ]; then case "$CODEX_HOME" in ${JSON.stringify(`${wd}/.codex`)}) mkdir -p "$CODEX_HOME"; printf '{"access_token":"project-secret"}\n' > "$CODEX_HOME/auth.json"; exit 0;; *) echo "wrong CODEX_HOME=$CODEX_HOME" >&2; exit 4;; esac; fi
85
+ echo unexpected "$@" >&2
86
+ exit 2
87
+ `);
88
+ const env = { HOME: home, PATH: `${bin}:/usr/bin:/bin` };
89
+ const add = runOmx(wd, ["auth", "add", "project"], env);
90
+ assert.equal(add.status, 0, add.stderr);
91
+ assert.doesNotMatch(add.stdout + add.stderr, /project-secret/);
92
+ assert.equal(await readFile(join(home, ".omx", "auth", "project.json"), "utf-8"), '{"access_token":"project-secret"}\n');
93
+ }
94
+ finally {
95
+ await rm(wd, { recursive: true, force: true });
96
+ }
97
+ });
98
+ it("fails soft when no slots are configured", async () => {
99
+ const wd = await mkdtemp(join(tmpdir(), "omx-auth-noslots-"));
100
+ try {
101
+ const result = runOmx(wd, ["--hotswap", "--direct"], { HOME: join(wd, "home"), PATH: `/usr/bin:/bin` });
102
+ assert.equal(result.status, 1);
103
+ assert.match(result.stderr, /no slots configured/);
104
+ }
105
+ finally {
106
+ await rm(wd, { recursive: true, force: true });
107
+ }
108
+ });
109
+ it("hotswaps on 429 and resumes the latest rollout with the next slot", async () => {
110
+ const wd = await mkdtemp(join(tmpdir(), "omx-auth-hotswap-"));
111
+ try {
112
+ const home = join(wd, "home");
113
+ const codexHome = join(home, ".codex");
114
+ const authDir = join(home, ".omx", "auth");
115
+ const bin = join(wd, "bin");
116
+ const countFile = join(wd, "count");
117
+ const argvFile = join(wd, "argv.log");
118
+ await mkdir(authDir, { recursive: true });
119
+ await mkdir(codexHome, { recursive: true });
120
+ await writeFile(join(authDir, "first.json"), '{"access_token":"first-secret"}\n');
121
+ await writeFile(join(authDir, "second.json"), '{"access_token":"second-secret"}\n');
122
+ await writeFile(join(authDir, "slots.json"), JSON.stringify({ version: 1, currentSlot: "first", slots: [
123
+ { slot: "first", createdAt: "now", updatedAt: "now" },
124
+ { slot: "second", createdAt: "now", updatedAt: "now" }
125
+ ] }, null, 2));
126
+ await writeFakeCodex(bin, `#!/bin/sh\ncount=0\n[ -f ${JSON.stringify(countFile)} ] && count=$(cat ${JSON.stringify(countFile)})\ncount=$((count+1))\nprintf '%s' "$count" > ${JSON.stringify(countFile)}\nprintf '%s\\n' "$*" >> ${JSON.stringify(argvFile)}\nif [ "$count" -eq 1 ]; then mkdir -p "$CODEX_HOME/sessions/2026/05/24"; printf '{}\\n' > "$CODEX_HOME/sessions/2026/05/24/rollout-session-123.jsonl"; echo 'HTTP 429 quota exceeded access_token=stderr-secret Bearer abc.def' >&2; exit 1; fi\ncase "$*" in *"resume session-123"*--model*"gpt-review"*) exit 0;; *) echo 'missing resume args or model flag' >&2; exit 3;; esac\n`);
127
+ const env = { HOME: home, CODEX_HOME: codexHome, PATH: `${bin}:/usr/bin:/bin` };
128
+ const result = runOmx(wd, ["--hotswap", "--direct", "--model", "gpt-review"], env);
129
+ assert.equal(result.status, 0, result.stderr + result.stdout);
130
+ assert.match(result.stderr, /HTTP 429 quota exceeded/);
131
+ const argvLog = await readFile(argvFile, "utf-8");
132
+ assert.match(argvLog, /resume session-123/);
133
+ assert.match(argvLog, /--model gpt-review/);
134
+ assert.equal(await readFile(join(codexHome, "auth.json"), "utf-8"), '{"access_token":"second-secret"}\n');
135
+ assert.doesNotMatch(result.stderr + result.stdout, /first-secret|second-secret|stderr-secret|abc\.def/);
136
+ }
137
+ finally {
138
+ await rm(wd, { recursive: true, force: true });
139
+ }
140
+ });
141
+ it("emits one clean error when all hotswap slots are exhausted", async () => {
142
+ const wd = await mkdtemp(join(tmpdir(), "omx-auth-exhausted-"));
143
+ try {
144
+ const home = join(wd, "home");
145
+ const codexHome = join(home, ".codex");
146
+ const authDir = join(home, ".omx", "auth");
147
+ const bin = join(wd, "bin");
148
+ await mkdir(authDir, { recursive: true });
149
+ await mkdir(codexHome, { recursive: true });
150
+ await writeFile(join(authDir, "first.json"), '{"access_token":"first-secret"}\n');
151
+ await writeFile(join(authDir, "second.json"), '{"access_token":"second-secret"}\n');
152
+ await writeFile(join(authDir, "slots.json"), JSON.stringify({ version: 1, currentSlot: "first", slots: [
153
+ { slot: "first", createdAt: "now", updatedAt: "now" },
154
+ { slot: "second", createdAt: "now", updatedAt: "now" }
155
+ ] }, null, 2));
156
+ await writeFakeCodex(bin, `#!/bin/sh\nmkdir -p "$CODEX_HOME/sessions/2026/05/24"\nprintf '{}\\n' > "$CODEX_HOME/sessions/2026/05/24/rollout-session-429.jsonl"\necho 'HTTP 429 quota exceeded' >&2\nexit 1\n`);
157
+ const result = runOmx(wd, ["--hotswap", "--direct"], { HOME: home, CODEX_HOME: codexHome, PATH: `${bin}:/usr/bin:/bin` });
158
+ assert.equal(result.status, 1);
159
+ const matches = result.stderr.match(/all slots exhausted: first, second/g) ?? [];
160
+ assert.equal(matches.length, 1, result.stderr);
161
+ assert.doesNotMatch(result.stderr + result.stdout, /first-secret|second-secret/);
162
+ }
163
+ finally {
164
+ await rm(wd, { recursive: true, force: true });
165
+ }
166
+ });
167
+ });
168
+ //# sourceMappingURL=auth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../../../src/cli/__tests__/auth.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,SAAS,MAAM;IACb,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,MAAM,CAAC,GAAW,EAAE,IAAc,EAAE,MAA8B,EAAE;IAC3E,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE;QAC9D,GAAG;QACH,QAAQ,EAAE,OAAO;QACjB,GAAG,EAAE;YACH,GAAG,OAAO,CAAC,GAAG;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE;YAChC,YAAY,EAAE,EAAE;YAChB,eAAe,EAAE,GAAG;YACpB,mBAAmB,EAAE,GAAG;YACxB,wBAAwB,EAAE,GAAG;YAC7B,GAAG,GAAG;SACP;KACF,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;AACjI,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,MAAc;IAC1D,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;YACjD,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACjD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACvC,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5B,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,MAAM,cAAc,CAAC,GAAG,EAAE,yLAAyL,CAAC,CAAC;YACrN,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,gBAAgB,EAAE,CAAC;YAChF,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;YACrD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;YAChE,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;YACzD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;YAC5C,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,4BAA4B,CAAC,CAAC;YAC5E,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;YACrD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;YAChE,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,EAAE,sCAAsC,CAAC,CAAC;QAC9G,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAGH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5B,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,uBAAuB,CAAC,CAAC;YAC/E,MAAM,cAAc,CAAC,GAAG,EAAE;oDACoB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC;;;CAGjF,CAAC,CAAC;YACG,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,GAAG,gBAAgB,EAAE,CAAC;YACzD,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;YACxD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;YAC/D,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,EAAE,qCAAqC,CAAC,CAAC;QAC3H,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;YACxG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;QACrD,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YACtC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,mCAAmC,CAAC,CAAC;YAClF,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,oCAAoC,CAAC,CAAC;YACpF,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;oBACrG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;oBACrD,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;iBACvD,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACf,MAAM,cAAc,CAAC,GAAG,EAAE,4BAA4B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,qBAAqB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,iDAAiD,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,uXAAuX,CAAC,CAAC;YACpnB,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,gBAAgB,EAAE,CAAC;YAChF,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC;YACnF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,EAAE,oCAAoC,CAAC,CAAC;YAC1G,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,mDAAmD,CAAC,CAAC;QAC1G,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5B,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,mCAAmC,CAAC,CAAC;YAClF,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,oCAAoC,CAAC,CAAC;YACpF,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;oBACrG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;oBACrD,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;iBACvD,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACf,MAAM,cAAc,CAAC,GAAG,EAAE,mLAAmL,CAAC,CAAC;YAC/M,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,gBAAgB,EAAE,CAAC,CAAC;YAC1H,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,IAAI,EAAE,CAAC;YACjF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC;QACnF,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -180,7 +180,7 @@ command = "node"
180
180
  await rm(wd, { recursive: true, force: true });
181
181
  }
182
182
  });
183
- it("treats plugin-mode setup omissions as expected and verifies marketplace registration", async () => {
183
+ it("recognizes setup-installed native reviewer roles separately from healthy plugin skills and hooks", async () => {
184
184
  const wd = await mkdtemp(join(tmpdir(), "omx-doctor-plugin-mode-"));
185
185
  try {
186
186
  const home = join(wd, "home");
@@ -204,6 +204,8 @@ command = "node"
204
204
  assert.match(res.stdout, /Resolved setup MCP mode: none/);
205
205
  assert.match(res.stdout, /Prompts: plugin mode intentionally omits setup-owned prompts; Codex plugin discovery supplies workflow surfaces/);
206
206
  assert.match(res.stdout, /Skills: plugin marketplace oh-my-codex-local registered; OMX skills are supplied by/);
207
+ assert.match(res.stdout, /\[OK\] Native reviewer roles: required RALPLAN\/Autopilot native reviewer roles are available \(architect, critic\); advisory scholastic role is also available/);
208
+ assert.doesNotMatch(res.stdout, /role-specific subagent calls may degrade/);
207
209
  assert.match(res.stdout, /MCP Servers: CLI-first plugin mode: first-party MCP compatibility explicitly disabled/);
208
210
  assert.doesNotMatch(res.stdout, /Prompts: prompts directory not found/);
209
211
  assert.doesNotMatch(res.stdout, /Skills: skills directory not found/);
@@ -214,6 +216,37 @@ command = "node"
214
216
  await rm(wd, { recursive: true, force: true });
215
217
  }
216
218
  });
219
+ it("accepts plugin mode when required native reviewer roles are available from agent files and config", async () => {
220
+ const wd = await mkdtemp(join(tmpdir(), "omx-doctor-plugin-native-roles-ok-"));
221
+ try {
222
+ const home = join(wd, "home");
223
+ const codexDir = join(home, ".codex");
224
+ await mkdir(codexDir, { recursive: true });
225
+ const setupRes = runOmx(wd, ["setup", "--scope", "user", "--plugin", "--force"], {
226
+ HOME: home,
227
+ CODEX_HOME: codexDir,
228
+ });
229
+ if (shouldSkipForSpawnPermissions(setupRes.error))
230
+ return;
231
+ assert.equal(setupRes.status, 0, setupRes.stderr || setupRes.stdout);
232
+ await mkdir(join(codexDir, "agents"), { recursive: true });
233
+ await writeFile(join(codexDir, "agents", "architect.toml"), 'name = "architect"\ndescription = "Architect reviewer"\n');
234
+ await writeFile(join(codexDir, "config.toml"), `${await readFile(join(codexDir, "config.toml"), "utf-8")}\n[agents.critic]\ndescription = "Critic reviewer"\n\n[agents.scholastic]\ndescription = "Scholastic advisory reviewer"\n`);
235
+ const res = runOmx(wd, ["doctor"], {
236
+ HOME: home,
237
+ CODEX_HOME: codexDir,
238
+ });
239
+ if (shouldSkipForSpawnPermissions(res.error))
240
+ return;
241
+ assert.equal(res.status, 0, res.stderr || res.stdout);
242
+ assert.match(res.stdout, /\[OK\] Native reviewer roles: required RALPLAN\/Autopilot native reviewer roles are available \(architect, critic\); advisory scholastic role is also available/);
243
+ assert.match(res.stdout, /Skills: plugin marketplace oh-my-codex-local registered; OMX skills are supplied by/);
244
+ assert.doesNotMatch(res.stdout, /role-specific subagent calls may degrade/);
245
+ }
246
+ finally {
247
+ await rm(wd, { recursive: true, force: true });
248
+ }
249
+ });
217
250
  it("warns when plugin cache manifest version is stale even when skills match", async () => {
218
251
  const wd = await mkdtemp(join(tmpdir(), "omx-doctor-plugin-cache-stale-version-"));
219
252
  try {
@@ -460,7 +493,7 @@ enabled = true
460
493
  }
461
494
  });
462
495
  });
463
- it("warns when explore routing is explicitly disabled in config.toml", async () => {
496
+ it("passes when deprecated explore routing is explicitly disabled by environment/config", async () => {
464
497
  const wd = await mkdtemp(join(tmpdir(), "omx-doctor-explore-routing-"));
465
498
  try {
466
499
  const home = join(wd, "home");
@@ -473,11 +506,12 @@ USE_OMX_EXPLORE_CMD = "off"
473
506
  const res = runOmx(wd, ["doctor"], {
474
507
  HOME: home,
475
508
  CODEX_HOME: join(home, ".codex"),
509
+ USE_OMX_EXPLORE_CMD: "off",
476
510
  });
477
511
  if (shouldSkipForSpawnPermissions(res.error))
478
512
  return;
479
513
  assert.equal(res.status, 0, res.stderr || res.stdout);
480
- assert.match(res.stdout, /Explore routing: disabled in config\.toml; set USE_OMX_EXPLORE_CMD = "1" under \[shell_environment_policy\.set\] to restore default explore-first routing/);
514
+ assert.match(res.stdout, /Explore routing: deprecated compatibility routing disabled by environment override \(recommended\)/);
481
515
  }
482
516
  finally {
483
517
  await rm(wd, { recursive: true, force: true });
@@ -619,6 +653,57 @@ OMX_LORE_COMMIT_GUARD = "truee"
619
653
  await rm(wd, { recursive: true, force: true });
620
654
  }
621
655
  });
656
+ it("accepts plugin-scoped native hooks when hooks.json contains user-owned hooks", async () => {
657
+ const wd = await mkdtemp(join(tmpdir(), "omx-doctor-plugin-scoped-hooks-user-"));
658
+ try {
659
+ const home = join(wd, "home");
660
+ const codexDir = join(home, ".codex");
661
+ await mkdir(join(wd, ".omx"), { recursive: true });
662
+ await mkdir(codexDir, { recursive: true });
663
+ const cacheDir = await installPluginCacheFixture(codexDir);
664
+ await writeFile(join(wd, ".omx", "setup-scope.json"), `${JSON.stringify({ scope: "user", installMode: "plugin", mcpMode: "none" }, null, 2)}\n`);
665
+ await writeFile(join(codexDir, "config.toml"), [
666
+ "plugin_hooks = true",
667
+ "goals = true",
668
+ "",
669
+ "[marketplaces.oh-my-codex-local]",
670
+ 'source_type = "local"',
671
+ `source = ${JSON.stringify(repoRoot())}`,
672
+ "",
673
+ '[plugins."oh-my-codex@oh-my-codex-local"]',
674
+ "enabled = true",
675
+ "",
676
+ ].join("\n"));
677
+ await writeFile(join(codexDir, "hooks.json"), JSON.stringify({
678
+ hooks: {
679
+ Stop: [
680
+ {
681
+ hooks: [
682
+ {
683
+ type: "command",
684
+ command: "/usr/bin/python3 /tmp/user-notify.py",
685
+ timeout: 5,
686
+ },
687
+ ],
688
+ },
689
+ ],
690
+ },
691
+ }, null, 2));
692
+ const res = runOmx(wd, ["doctor"], {
693
+ HOME: home,
694
+ CODEX_HOME: codexDir,
695
+ });
696
+ if (shouldSkipForSpawnPermissions(res.error))
697
+ return;
698
+ assert.equal(res.status, 0, res.stderr || res.stdout);
699
+ assert.match(res.stdout, new RegExp(`\\[OK\\] Native hooks: plugin-scoped hooks are enabled; existing hooks\\.json at .*\\.codex[\\/]+hooks\\.json is treated as user-owned because plugin-scoped hooks are enabled, and plugin cache native hook coverage smoke passed via ${join(cacheDir, "hooks", "hooks.json").replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`));
700
+ assert.doesNotMatch(res.stdout, /hooks\.json is missing OMX-managed coverage/);
701
+ assert.doesNotMatch(res.stdout, /run "omx setup --force" to restore native hooks/);
702
+ }
703
+ finally {
704
+ await rm(wd, { recursive: true, force: true });
705
+ }
706
+ });
622
707
  it("warns when hooks.json is missing OMX-managed native hook coverage", async () => {
623
708
  const wd = await mkdtemp(join(tmpdir(), "omx-doctor-hooks-coverage-"));
624
709
  try {