@rulemetric/cli 0.1.1 → 0.2.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 (259) hide show
  1. package/README.md +66 -0
  2. package/dist/base-command.d.ts.map +1 -1
  3. package/dist/base-command.js +15 -0
  4. package/dist/base-command.js.map +1 -1
  5. package/dist/commands/auth/login.d.ts.map +1 -1
  6. package/dist/commands/auth/login.js +7 -0
  7. package/dist/commands/auth/login.js.map +1 -1
  8. package/dist/commands/crons/list.d.ts +7 -0
  9. package/dist/commands/crons/list.d.ts.map +1 -0
  10. package/dist/commands/crons/list.js +24 -0
  11. package/dist/commands/crons/list.js.map +1 -0
  12. package/dist/commands/crons/run.d.ts +13 -0
  13. package/dist/commands/crons/run.d.ts.map +1 -0
  14. package/dist/commands/crons/run.js +48 -0
  15. package/dist/commands/crons/run.js.map +1 -0
  16. package/dist/commands/evals/listen.d.ts +28 -2
  17. package/dist/commands/evals/listen.d.ts.map +1 -1
  18. package/dist/commands/evals/listen.js +159 -25
  19. package/dist/commands/evals/listen.js.map +1 -1
  20. package/dist/commands/evals/listen.test.d.ts +2 -0
  21. package/dist/commands/evals/listen.test.d.ts.map +1 -0
  22. package/dist/commands/evals/listen.test.js +48 -0
  23. package/dist/commands/evals/listen.test.js.map +1 -0
  24. package/dist/commands/gateway/ensure.js +1 -1
  25. package/dist/commands/gateway/ensure.js.map +1 -1
  26. package/dist/commands/hooks/install.d.ts.map +1 -1
  27. package/dist/commands/hooks/install.js +40 -3
  28. package/dist/commands/hooks/install.js.map +1 -1
  29. package/dist/commands/hooks/run.js +1 -1
  30. package/dist/commands/hooks/run.js.map +1 -1
  31. package/dist/commands/hooks/uninstall.d.ts.map +1 -1
  32. package/dist/commands/hooks/uninstall.js +9 -1
  33. package/dist/commands/hooks/uninstall.js.map +1 -1
  34. package/dist/commands/org/current.d.ts +7 -0
  35. package/dist/commands/org/current.d.ts.map +1 -0
  36. package/dist/commands/org/current.js +70 -0
  37. package/dist/commands/org/current.js.map +1 -0
  38. package/dist/commands/org/list.d.ts +7 -0
  39. package/dist/commands/org/list.d.ts.map +1 -0
  40. package/dist/commands/org/list.js +37 -0
  41. package/dist/commands/org/list.js.map +1 -0
  42. package/dist/commands/org/switch.d.ts +13 -0
  43. package/dist/commands/org/switch.d.ts.map +1 -0
  44. package/dist/commands/org/switch.js +52 -0
  45. package/dist/commands/org/switch.js.map +1 -0
  46. package/dist/commands/proxy/restart.d.ts +11 -0
  47. package/dist/commands/proxy/restart.d.ts.map +1 -0
  48. package/dist/commands/proxy/restart.js +36 -0
  49. package/dist/commands/proxy/restart.js.map +1 -0
  50. package/dist/commands/proxy/start.d.ts.map +1 -1
  51. package/dist/commands/proxy/start.js +4 -0
  52. package/dist/commands/proxy/start.js.map +1 -1
  53. package/dist/commands/service/install.d.ts +16 -0
  54. package/dist/commands/service/install.d.ts.map +1 -1
  55. package/dist/commands/service/install.js +145 -6
  56. package/dist/commands/service/install.js.map +1 -1
  57. package/dist/commands/service/uninstall.d.ts.map +1 -1
  58. package/dist/commands/service/uninstall.js +28 -0
  59. package/dist/commands/service/uninstall.js.map +1 -1
  60. package/dist/commands/sessions/start.d.ts.map +1 -1
  61. package/dist/commands/sessions/start.js +5 -0
  62. package/dist/commands/sessions/start.js.map +1 -1
  63. package/dist/commands/signals/report.d.ts +10 -0
  64. package/dist/commands/signals/report.d.ts.map +1 -0
  65. package/dist/commands/signals/report.js +35 -0
  66. package/dist/commands/signals/report.js.map +1 -0
  67. package/dist/commands/signals/report.test.d.ts +2 -0
  68. package/dist/commands/signals/report.test.d.ts.map +1 -0
  69. package/dist/commands/signals/report.test.js +65 -0
  70. package/dist/commands/signals/report.test.js.map +1 -0
  71. package/dist/commands/skills/export-approved.d.ts +10 -0
  72. package/dist/commands/skills/export-approved.d.ts.map +1 -0
  73. package/dist/commands/skills/export-approved.js +31 -0
  74. package/dist/commands/skills/export-approved.js.map +1 -0
  75. package/dist/commands/skills/publish.d.ts +17 -0
  76. package/dist/commands/skills/publish.d.ts.map +1 -0
  77. package/dist/commands/skills/publish.js +80 -0
  78. package/dist/commands/skills/publish.js.map +1 -0
  79. package/dist/commands/skills/update.d.ts +3 -0
  80. package/dist/commands/skills/update.d.ts.map +1 -1
  81. package/dist/commands/skills/update.js +17 -6
  82. package/dist/commands/skills/update.js.map +1 -1
  83. package/dist/commands/suggestions/accept.d.ts +16 -0
  84. package/dist/commands/suggestions/accept.d.ts.map +1 -0
  85. package/dist/commands/suggestions/accept.js +61 -0
  86. package/dist/commands/suggestions/accept.js.map +1 -0
  87. package/dist/commands/suggestions/accept.test.d.ts +2 -0
  88. package/dist/commands/suggestions/accept.test.d.ts.map +1 -0
  89. package/dist/commands/suggestions/accept.test.js +52 -0
  90. package/dist/commands/suggestions/accept.test.js.map +1 -0
  91. package/dist/commands/suggestions/dismiss.d.ts +10 -0
  92. package/dist/commands/suggestions/dismiss.d.ts.map +1 -0
  93. package/dist/commands/suggestions/dismiss.js +26 -0
  94. package/dist/commands/suggestions/dismiss.js.map +1 -0
  95. package/dist/commands/suggestions/list.d.ts +10 -0
  96. package/dist/commands/suggestions/list.d.ts.map +1 -0
  97. package/dist/commands/suggestions/list.js +40 -0
  98. package/dist/commands/suggestions/list.js.map +1 -0
  99. package/dist/commands/suggestions/refresh.d.ts +7 -0
  100. package/dist/commands/suggestions/refresh.d.ts.map +1 -0
  101. package/dist/commands/suggestions/refresh.js +11 -0
  102. package/dist/commands/suggestions/refresh.js.map +1 -0
  103. package/dist/dashboard/Dashboard.d.ts.map +1 -1
  104. package/dist/dashboard/Dashboard.js +7 -1
  105. package/dist/dashboard/Dashboard.js.map +1 -1
  106. package/dist/lib/active-org-refresh.d.ts +22 -0
  107. package/dist/lib/active-org-refresh.d.ts.map +1 -0
  108. package/dist/lib/active-org-refresh.js +108 -0
  109. package/dist/lib/active-org-refresh.js.map +1 -0
  110. package/dist/lib/active-org-refresh.test.d.ts +2 -0
  111. package/dist/lib/active-org-refresh.test.d.ts.map +1 -0
  112. package/dist/lib/active-org-refresh.test.js +190 -0
  113. package/dist/lib/active-org-refresh.test.js.map +1 -0
  114. package/dist/lib/active-org.d.ts +43 -0
  115. package/dist/lib/active-org.d.ts.map +1 -0
  116. package/dist/lib/active-org.js +96 -0
  117. package/dist/lib/active-org.js.map +1 -0
  118. package/dist/lib/active-org.test.d.ts +2 -0
  119. package/dist/lib/active-org.test.d.ts.map +1 -0
  120. package/dist/lib/active-org.test.js +147 -0
  121. package/dist/lib/active-org.test.js.map +1 -0
  122. package/dist/lib/cron-registry.d.ts +47 -0
  123. package/dist/lib/cron-registry.d.ts.map +1 -0
  124. package/dist/lib/cron-registry.js +102 -0
  125. package/dist/lib/cron-registry.js.map +1 -0
  126. package/dist/lib/cron-registry.test.d.ts +2 -0
  127. package/dist/lib/cron-registry.test.d.ts.map +1 -0
  128. package/dist/lib/cron-registry.test.js +136 -0
  129. package/dist/lib/cron-registry.test.js.map +1 -0
  130. package/dist/lib/cron-runner.d.ts +16 -0
  131. package/dist/lib/cron-runner.d.ts.map +1 -0
  132. package/dist/lib/cron-runner.js +68 -0
  133. package/dist/lib/cron-runner.js.map +1 -0
  134. package/dist/lib/detect-languages.d.ts +7 -0
  135. package/dist/lib/detect-languages.d.ts.map +1 -0
  136. package/dist/lib/detect-languages.js +113 -0
  137. package/dist/lib/detect-languages.js.map +1 -0
  138. package/dist/lib/detect-languages.test.d.ts +2 -0
  139. package/dist/lib/detect-languages.test.d.ts.map +1 -0
  140. package/dist/lib/detect-languages.test.js +29 -0
  141. package/dist/lib/detect-languages.test.js.map +1 -0
  142. package/dist/lib/eval-executor.d.ts.map +1 -1
  143. package/dist/lib/eval-executor.js +5 -2
  144. package/dist/lib/eval-executor.js.map +1 -1
  145. package/dist/lib/gateway-entry.js +4 -1
  146. package/dist/lib/gateway-entry.js.map +1 -1
  147. package/dist/lib/gateway-lifecycle.d.ts +1 -1
  148. package/dist/lib/gateway-lifecycle.d.ts.map +1 -1
  149. package/dist/lib/gateway-lifecycle.js +13 -2
  150. package/dist/lib/gateway-lifecycle.js.map +1 -1
  151. package/dist/lib/gateway.d.ts +4 -5
  152. package/dist/lib/gateway.d.ts.map +1 -1
  153. package/dist/lib/gateway.js +68 -2
  154. package/dist/lib/gateway.js.map +1 -1
  155. package/dist/lib/graphile-worker-runner.d.ts +124 -0
  156. package/dist/lib/graphile-worker-runner.d.ts.map +1 -0
  157. package/dist/lib/graphile-worker-runner.js +530 -0
  158. package/dist/lib/graphile-worker-runner.js.map +1 -0
  159. package/dist/lib/graphile-worker-runner.test.d.ts +2 -0
  160. package/dist/lib/graphile-worker-runner.test.d.ts.map +1 -0
  161. package/dist/lib/graphile-worker-runner.test.js +153 -0
  162. package/dist/lib/graphile-worker-runner.test.js.map +1 -0
  163. package/dist/lib/handlers/cron-close-stale-sessions.d.ts +3 -0
  164. package/dist/lib/handlers/cron-close-stale-sessions.d.ts.map +1 -0
  165. package/dist/lib/handlers/cron-close-stale-sessions.js +8 -0
  166. package/dist/lib/handlers/cron-close-stale-sessions.js.map +1 -0
  167. package/dist/lib/handlers/cron-data-integrity-audit.d.ts +3 -0
  168. package/dist/lib/handlers/cron-data-integrity-audit.d.ts.map +1 -0
  169. package/dist/lib/handlers/cron-data-integrity-audit.js +16 -0
  170. package/dist/lib/handlers/cron-data-integrity-audit.js.map +1 -0
  171. package/dist/lib/handlers/cron-refresh-skills.d.ts +3 -0
  172. package/dist/lib/handlers/cron-refresh-skills.d.ts.map +1 -0
  173. package/dist/lib/handlers/cron-refresh-skills.js +91 -0
  174. package/dist/lib/handlers/cron-refresh-skills.js.map +1 -0
  175. package/dist/lib/handlers/cron-refresh-skills.test.d.ts +2 -0
  176. package/dist/lib/handlers/cron-refresh-skills.test.d.ts.map +1 -0
  177. package/dist/lib/handlers/cron-refresh-skills.test.js +114 -0
  178. package/dist/lib/handlers/cron-refresh-skills.test.js.map +1 -0
  179. package/dist/lib/handlers/cron-regenerate-insights.d.ts +3 -0
  180. package/dist/lib/handlers/cron-regenerate-insights.d.ts.map +1 -0
  181. package/dist/lib/handlers/cron-regenerate-insights.js +6 -0
  182. package/dist/lib/handlers/cron-regenerate-insights.js.map +1 -0
  183. package/dist/lib/handlers/cron-suggest-instructions.d.ts +3 -0
  184. package/dist/lib/handlers/cron-suggest-instructions.d.ts.map +1 -0
  185. package/dist/lib/handlers/cron-suggest-instructions.js +159 -0
  186. package/dist/lib/handlers/cron-suggest-instructions.js.map +1 -0
  187. package/dist/lib/handlers/cron-suggest-instructions.test.d.ts +2 -0
  188. package/dist/lib/handlers/cron-suggest-instructions.test.d.ts.map +1 -0
  189. package/dist/lib/handlers/cron-suggest-instructions.test.js +80 -0
  190. package/dist/lib/handlers/cron-suggest-instructions.test.js.map +1 -0
  191. package/dist/lib/handlers/process-conversation.d.ts +35 -0
  192. package/dist/lib/handlers/process-conversation.d.ts.map +1 -0
  193. package/dist/lib/handlers/process-conversation.js +35 -0
  194. package/dist/lib/handlers/process-conversation.js.map +1 -0
  195. package/dist/lib/handlers/process-eval.d.ts +32 -0
  196. package/dist/lib/handlers/process-eval.d.ts.map +1 -0
  197. package/dist/lib/handlers/process-eval.js +207 -0
  198. package/dist/lib/handlers/process-eval.js.map +1 -0
  199. package/dist/lib/handlers/process-insights.d.ts +39 -0
  200. package/dist/lib/handlers/process-insights.d.ts.map +1 -0
  201. package/dist/lib/handlers/process-insights.js +300 -0
  202. package/dist/lib/handlers/process-insights.js.map +1 -0
  203. package/dist/lib/handlers/process-launch.d.ts +29 -0
  204. package/dist/lib/handlers/process-launch.d.ts.map +1 -0
  205. package/dist/lib/handlers/process-launch.js +41 -0
  206. package/dist/lib/handlers/process-launch.js.map +1 -0
  207. package/dist/lib/handlers/process-session-goal.d.ts +29 -0
  208. package/dist/lib/handlers/process-session-goal.d.ts.map +1 -0
  209. package/dist/lib/handlers/process-session-goal.js +74 -0
  210. package/dist/lib/handlers/process-session-goal.js.map +1 -0
  211. package/dist/lib/handlers/process-session-goal.test.d.ts +2 -0
  212. package/dist/lib/handlers/process-session-goal.test.d.ts.map +1 -0
  213. package/dist/lib/handlers/process-session-goal.test.js +134 -0
  214. package/dist/lib/handlers/process-session-goal.test.js.map +1 -0
  215. package/dist/lib/hooks-config.d.ts +28 -4
  216. package/dist/lib/hooks-config.d.ts.map +1 -1
  217. package/dist/lib/hooks-config.js +138 -10
  218. package/dist/lib/hooks-config.js.map +1 -1
  219. package/dist/lib/llm-client.d.ts.map +1 -1
  220. package/dist/lib/llm-client.js +149 -16
  221. package/dist/lib/llm-client.js.map +1 -1
  222. package/dist/lib/mirror-writer-db.d.ts +12 -0
  223. package/dist/lib/mirror-writer-db.d.ts.map +1 -0
  224. package/dist/lib/mirror-writer-db.js +51 -0
  225. package/dist/lib/mirror-writer-db.js.map +1 -0
  226. package/dist/lib/mirror-writer.d.ts +35 -0
  227. package/dist/lib/mirror-writer.d.ts.map +1 -0
  228. package/dist/lib/mirror-writer.js +27 -0
  229. package/dist/lib/mirror-writer.js.map +1 -0
  230. package/dist/lib/mirror-writer.test.d.ts +2 -0
  231. package/dist/lib/mirror-writer.test.d.ts.map +1 -0
  232. package/dist/lib/mirror-writer.test.js +47 -0
  233. package/dist/lib/mirror-writer.test.js.map +1 -0
  234. package/dist/lib/permanent-job-error.d.ts +17 -0
  235. package/dist/lib/permanent-job-error.d.ts.map +1 -0
  236. package/dist/lib/permanent-job-error.js +23 -0
  237. package/dist/lib/permanent-job-error.js.map +1 -0
  238. package/dist/lib/permanent-job-error.test.d.ts +2 -0
  239. package/dist/lib/permanent-job-error.test.d.ts.map +1 -0
  240. package/dist/lib/permanent-job-error.test.js +29 -0
  241. package/dist/lib/permanent-job-error.test.js.map +1 -0
  242. package/dist/lib/version-check.d.ts +11 -0
  243. package/dist/lib/version-check.d.ts.map +1 -0
  244. package/dist/lib/version-check.js +106 -0
  245. package/dist/lib/version-check.js.map +1 -0
  246. package/oclif.manifest.json +611 -81
  247. package/package.json +21 -5
  248. package/dist/commands/insights/sync.d.ts +0 -17
  249. package/dist/commands/insights/sync.d.ts.map +0 -1
  250. package/dist/commands/insights/sync.js +0 -477
  251. package/dist/commands/insights/sync.js.map +0 -1
  252. package/dist/lib/eval-worker.d.ts +0 -12
  253. package/dist/lib/eval-worker.d.ts.map +0 -1
  254. package/dist/lib/eval-worker.js +0 -395
  255. package/dist/lib/eval-worker.js.map +0 -1
  256. package/dist/lib/insights-worker.d.ts +0 -10
  257. package/dist/lib/insights-worker.d.ts.map +0 -1
  258. package/dist/lib/insights-worker.js +0 -329
  259. package/dist/lib/insights-worker.js.map +0 -1
@@ -0,0 +1,108 @@
1
+ import { apiGet } from './api-client.js';
2
+ import { readActiveOrg, writeActiveOrg, clearActiveOrg, isStale, } from './active-org.js';
3
+ /**
4
+ * Module-level guard so we only refresh once per CLI invocation, even if
5
+ * `BaseCommand.init` runs multiple times across nested commands or tests.
6
+ */
7
+ let attempted = false;
8
+ /**
9
+ * Opportunistically reconcile the local active-org cache with the server.
10
+ *
11
+ * Runs in three cases:
12
+ * 1. Cache file is ABSENT (bootstrap) — populate from `/api/profile`. This
13
+ * branch is load-bearing: without it, a user who never explicitly ran
14
+ * `rulemetric org switch` would have hook captures with `org_id=NULL`
15
+ * forever, even when the server already has `profiles.active_org_id`
16
+ * set (e.g. switched via the web UI, or auto-set by the handle_new_user
17
+ * trigger from migration 00067). Caught 2026-05-25 after an audit found
18
+ * 244 hook sessions over 14 days all missing org_id.
19
+ * 2. Cache file exists but is stale (>5min) — re-sync from the server.
20
+ * 3. Env override is set OR cache is fresh — no-op, zero network.
21
+ *
22
+ * Failures are intentionally swallowed: this is a background nicety, not a
23
+ * gating step. The next API call the user actually invoked will surface
24
+ * any auth/network errors with clearer messaging than a refresh failure
25
+ * could. Capture-time fallback to `profiles.active_org_id` at
26
+ * `apps/api/src/routes/sessions.ts:48` is the final safety net.
27
+ */
28
+ /**
29
+ * Internal: fetch profile + (if needed) orgs, then write/clear the cache.
30
+ * Shared body for both `maybeRefreshActiveOrg` (guarded, used by every CLI
31
+ * command via BaseCommand) and `bootstrapActiveOrg` (unguarded, used by
32
+ * lifecycle commands that just wrote auth/install state and want the cache
33
+ * to reflect it before they exit).
34
+ */
35
+ async function syncActiveOrgFromServer() {
36
+ // Env override: caller has explicitly chosen the org. Don't write the
37
+ // file — env should be ephemeral.
38
+ if (process.env.RULEMETRIC_ORG_ID)
39
+ return;
40
+ const cached = readActiveOrg();
41
+ // Only skip when we have a fresh cache. Missing cache (cached === null)
42
+ // and stale cache both fall through to the refresh path below.
43
+ if (cached && !isStale(cached))
44
+ return;
45
+ try {
46
+ const profile = await apiGet('/api/profile');
47
+ if (!profile.activeOrgId) {
48
+ // Server has no active org — drop the local cache (if any).
49
+ clearActiveOrg();
50
+ return;
51
+ }
52
+ if (cached && profile.activeOrgId === cached.orgId) {
53
+ // Same id — just bump syncedAt so we don't re-fetch every command.
54
+ writeActiveOrg({
55
+ orgId: cached.orgId,
56
+ orgSlug: cached.orgSlug,
57
+ syncedAt: Date.now(),
58
+ });
59
+ return;
60
+ }
61
+ // Either bootstrapping fresh (no cache) OR the server changed the active
62
+ // org out from under us (e.g. via web UI). Look up the slug for the id
63
+ // and (re)write the cache.
64
+ const orgs = await apiGet('/api/orgs');
65
+ const match = orgs.find((o) => o.id === profile.activeOrgId);
66
+ writeActiveOrg({
67
+ orgId: profile.activeOrgId,
68
+ orgSlug: match?.slug,
69
+ syncedAt: Date.now(),
70
+ });
71
+ }
72
+ catch {
73
+ // Swallow — leave the old cache intact. The user's actual command will
74
+ // surface the real error if any.
75
+ }
76
+ }
77
+ export async function maybeRefreshActiveOrg() {
78
+ if (attempted)
79
+ return;
80
+ attempted = true;
81
+ await syncActiveOrgFromServer();
82
+ }
83
+ /**
84
+ * Explicit, guard-bypassing version of the refresh. Use ONLY from commands
85
+ * where the user just took an action that changes auth/install state and we
86
+ * want the cache to reflect it before the command exits:
87
+ *
88
+ * - After `auth login`: BaseCommand.init fired the guarded refresh BEFORE
89
+ * credentials existed and got a 401; we need to retry post-login.
90
+ * - After `hooks install` / `service install`: belt-and-suspenders so the
91
+ * first Claude Code session after install has a populated cache, even
92
+ * if BaseCommand's earlier refresh hit a transient error.
93
+ *
94
+ * Marks `attempted = true` after running so a redundant `maybeRefreshActiveOrg`
95
+ * call later in the same process is a no-op.
96
+ */
97
+ export async function bootstrapActiveOrg() {
98
+ attempted = true;
99
+ await syncActiveOrgFromServer();
100
+ }
101
+ /**
102
+ * Test hook — reset the once-per-process guard so each test can exercise
103
+ * the refresh logic.
104
+ */
105
+ export function _resetRefreshAttempt() {
106
+ attempted = false;
107
+ }
108
+ //# sourceMappingURL=active-org-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org-refresh.js","sourceRoot":"","sources":["../../src/lib/active-org-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,OAAO,GACR,MAAM,iBAAiB,CAAC;AAgBzB;;;GAGG;AACH,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;;;;GAMG;AACH,KAAK,UAAU,uBAAuB;IACpC,sEAAsE;IACtE,kCAAkC;IAClC,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAAE,OAAO;IAE1C,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,wEAAwE;IACxE,+DAA+D;IAC/D,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO;IAEvC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAkB,cAAc,CAAC,CAAC;QAE9D,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACzB,4DAA4D;YAC5D,cAAc,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QAED,IAAI,MAAM,IAAI,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;YACnD,mEAAmE;YACnE,cAAc,CAAC;gBACb,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;aACrB,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,yEAAyE;QACzE,uEAAuE;QACvE,2BAA2B;QAC3B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAgB,WAAW,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7D,cAAc,CAAC;YACb,KAAK,EAAE,OAAO,CAAC,WAAW;YAC1B,OAAO,EAAE,KAAK,EAAE,IAAI;YACpB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,SAAS;QAAE,OAAO;IACtB,SAAS,GAAG,IAAI,CAAC;IACjB,MAAM,uBAAuB,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,SAAS,GAAG,IAAI,CAAC;IACjB,MAAM,uBAAuB,EAAE,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,SAAS,GAAG,KAAK,CAAC;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=active-org-refresh.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org-refresh.test.d.ts","sourceRoot":"","sources":["../../src/lib/active-org-refresh.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,190 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ const { apiGet } = vi.hoisted(() => ({ apiGet: vi.fn() }));
6
+ vi.mock('./api-client.js', () => ({
7
+ apiGet,
8
+ apiPost: vi.fn(),
9
+ api: vi.fn(),
10
+ ApiError: class ApiError extends Error {
11
+ },
12
+ }));
13
+ // Import after the mock is hoisted.
14
+ import { maybeRefreshActiveOrg, _resetRefreshAttempt } from './active-org-refresh.js';
15
+ import { _internals, writeActiveOrg, readActiveOrg } from './active-org.js';
16
+ let tmpDir;
17
+ let savedConfigDir;
18
+ let savedOrgId;
19
+ beforeEach(() => {
20
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rulemetric-refresh-'));
21
+ savedConfigDir = process.env.RULEMETRIC_CONFIG_DIR;
22
+ savedOrgId = process.env.RULEMETRIC_ORG_ID;
23
+ process.env.RULEMETRIC_CONFIG_DIR = tmpDir;
24
+ delete process.env.RULEMETRIC_ORG_ID;
25
+ apiGet.mockReset();
26
+ _resetRefreshAttempt();
27
+ });
28
+ afterEach(() => {
29
+ fs.rmSync(tmpDir, { recursive: true, force: true });
30
+ if (savedConfigDir === undefined) {
31
+ delete process.env.RULEMETRIC_CONFIG_DIR;
32
+ }
33
+ else {
34
+ process.env.RULEMETRIC_CONFIG_DIR = savedConfigDir;
35
+ }
36
+ if (savedOrgId === undefined) {
37
+ delete process.env.RULEMETRIC_ORG_ID;
38
+ }
39
+ else {
40
+ process.env.RULEMETRIC_ORG_ID = savedOrgId;
41
+ }
42
+ });
43
+ describe('maybeRefreshActiveOrg — bootstrap path (regression for 2026-05-25 audit)', () => {
44
+ it('writes the cache file when none exists and server has an active org', async () => {
45
+ // Setup: no local cache file. Server reports an activeOrgId.
46
+ expect(readActiveOrg()).toBeNull();
47
+ apiGet
48
+ .mockResolvedValueOnce({
49
+ id: 'user-1',
50
+ email: 'nick@example.com',
51
+ displayName: null,
52
+ activeOrgId: 'bd37b027-f79b-4aea-8ccc-a27fa35ddf64',
53
+ })
54
+ .mockResolvedValueOnce([
55
+ { id: 'bd37b027-f79b-4aea-8ccc-a27fa35ddf64', name: 'Nick Personal', slug: 'nick-personal', role: 'owner' },
56
+ ]);
57
+ await maybeRefreshActiveOrg();
58
+ expect(apiGet).toHaveBeenNthCalledWith(1, '/api/profile');
59
+ expect(apiGet).toHaveBeenNthCalledWith(2, '/api/orgs');
60
+ const cached = readActiveOrg();
61
+ expect(cached?.orgId).toBe('bd37b027-f79b-4aea-8ccc-a27fa35ddf64');
62
+ expect(cached?.orgSlug).toBe('nick-personal');
63
+ expect(typeof cached?.syncedAt).toBe('number');
64
+ });
65
+ it('writes only orgId when /api/orgs has no matching entry (defensive)', async () => {
66
+ apiGet
67
+ .mockResolvedValueOnce({
68
+ id: 'user-1',
69
+ email: null,
70
+ displayName: null,
71
+ activeOrgId: 'bd37b027-f79b-4aea-8ccc-a27fa35ddf64',
72
+ })
73
+ .mockResolvedValueOnce([]); // orgs list empty — slug unknown
74
+ await maybeRefreshActiveOrg();
75
+ const cached = readActiveOrg();
76
+ expect(cached?.orgId).toBe('bd37b027-f79b-4aea-8ccc-a27fa35ddf64');
77
+ expect(cached?.orgSlug).toBeUndefined();
78
+ });
79
+ it('does NOT write a file when the server has no active org', async () => {
80
+ apiGet.mockResolvedValueOnce({
81
+ id: 'user-1',
82
+ email: null,
83
+ displayName: null,
84
+ activeOrgId: null,
85
+ });
86
+ await maybeRefreshActiveOrg();
87
+ expect(readActiveOrg()).toBeNull();
88
+ expect(fs.existsSync(_internals.getActiveOrgFile())).toBe(false);
89
+ // Only /api/profile was called — no follow-up /api/orgs.
90
+ expect(apiGet).toHaveBeenCalledTimes(1);
91
+ });
92
+ it('swallows API errors and leaves cache absent', async () => {
93
+ apiGet.mockRejectedValueOnce(new Error('network down'));
94
+ await expect(maybeRefreshActiveOrg()).resolves.toBeUndefined();
95
+ expect(readActiveOrg()).toBeNull();
96
+ });
97
+ });
98
+ describe('maybeRefreshActiveOrg — env override', () => {
99
+ it('does nothing when RULEMETRIC_ORG_ID is set, even if no cache exists', async () => {
100
+ process.env.RULEMETRIC_ORG_ID = '11111111-1111-1111-1111-111111111111';
101
+ await maybeRefreshActiveOrg();
102
+ expect(apiGet).not.toHaveBeenCalled();
103
+ // No file should be written for an env-driven session.
104
+ expect(fs.existsSync(_internals.getActiveOrgFile())).toBe(false);
105
+ });
106
+ });
107
+ describe('maybeRefreshActiveOrg — existing cache paths', () => {
108
+ it('skips refresh when cache is fresh', async () => {
109
+ writeActiveOrg({
110
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
111
+ orgSlug: 'acme',
112
+ syncedAt: Date.now(),
113
+ });
114
+ await maybeRefreshActiveOrg();
115
+ expect(apiGet).not.toHaveBeenCalled();
116
+ });
117
+ it('bumps syncedAt when server agrees with cached orgId', async () => {
118
+ const oldSynced = Date.now() - 10 * 60 * 1000; // 10 min ago — stale
119
+ writeActiveOrg({
120
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
121
+ orgSlug: 'acme',
122
+ syncedAt: oldSynced,
123
+ });
124
+ apiGet.mockResolvedValueOnce({
125
+ id: 'user-1',
126
+ email: null,
127
+ displayName: null,
128
+ activeOrgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
129
+ });
130
+ await maybeRefreshActiveOrg();
131
+ const cached = readActiveOrg();
132
+ expect(cached?.orgId).toBe('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa');
133
+ expect(cached?.orgSlug).toBe('acme'); // slug preserved from cache
134
+ expect(cached?.syncedAt).toBeGreaterThan(oldSynced);
135
+ // Only /api/profile was needed — no slug lookup since id didn't change.
136
+ expect(apiGet).toHaveBeenCalledTimes(1);
137
+ });
138
+ it('rewrites cache when server changed the active org', async () => {
139
+ writeActiveOrg({
140
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
141
+ orgSlug: 'old',
142
+ syncedAt: Date.now() - 10 * 60 * 1000,
143
+ });
144
+ apiGet
145
+ .mockResolvedValueOnce({
146
+ id: 'user-1',
147
+ email: null,
148
+ displayName: null,
149
+ activeOrgId: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
150
+ })
151
+ .mockResolvedValueOnce([
152
+ { id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', name: 'New Org', slug: 'new-org', role: 'admin' },
153
+ ]);
154
+ await maybeRefreshActiveOrg();
155
+ const cached = readActiveOrg();
156
+ expect(cached?.orgId).toBe('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb');
157
+ expect(cached?.orgSlug).toBe('new-org');
158
+ });
159
+ it('clears cache when server has no active org', async () => {
160
+ writeActiveOrg({
161
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
162
+ orgSlug: 'acme',
163
+ syncedAt: Date.now() - 10 * 60 * 1000,
164
+ });
165
+ apiGet.mockResolvedValueOnce({
166
+ id: 'user-1',
167
+ email: null,
168
+ displayName: null,
169
+ activeOrgId: null,
170
+ });
171
+ await maybeRefreshActiveOrg();
172
+ expect(readActiveOrg()).toBeNull();
173
+ expect(fs.existsSync(_internals.getActiveOrgFile())).toBe(false);
174
+ });
175
+ });
176
+ describe('maybeRefreshActiveOrg — once-per-process guard', () => {
177
+ it('only fires once even across multiple calls', async () => {
178
+ apiGet.mockResolvedValueOnce({
179
+ id: 'user-1',
180
+ email: null,
181
+ displayName: null,
182
+ activeOrgId: null,
183
+ });
184
+ await maybeRefreshActiveOrg();
185
+ await maybeRefreshActiveOrg();
186
+ await maybeRefreshActiveOrg();
187
+ expect(apiGet).toHaveBeenCalledTimes(1);
188
+ });
189
+ });
190
+ //# sourceMappingURL=active-org-refresh.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org-refresh.test.js","sourceRoot":"","sources":["../../src/lib/active-org-refresh.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Local cache of the user's currently selected org. Lives at
3
+ * `~/.config/rulemetric/active-org` (overridable via
4
+ * `RULEMETRIC_CONFIG_DIR`). The runtime contract is intentionally small —
5
+ * env beats file, and a missing file means "no active org".
6
+ */
7
+ export interface ActiveOrg {
8
+ orgId: string;
9
+ orgSlug?: string;
10
+ /** Unix milliseconds of last refresh from the API. Absent for env overrides. */
11
+ syncedAt?: number;
12
+ }
13
+ declare function getConfigDir(): string;
14
+ declare function getActiveOrgFile(): string;
15
+ /**
16
+ * Resolve the active org for the current process.
17
+ *
18
+ * Priority: env var (`RULEMETRIC_ORG_ID`) > local file > null. The env path
19
+ * always wins — it's the "I know what I want" override for CI and ad-hoc
20
+ * multi-tenant runs, even if the local file looks fresher.
21
+ */
22
+ export declare function readActiveOrg(): ActiveOrg | null;
23
+ /**
24
+ * Persist the active org to the local file. Atomic via tmp+rename so a
25
+ * mid-write crash never leaves a partial JSON file.
26
+ */
27
+ export declare function writeActiveOrg(active: ActiveOrg): void;
28
+ /**
29
+ * Remove the local active-org file. No-op if absent.
30
+ */
31
+ export declare function clearActiveOrg(): void;
32
+ /**
33
+ * Returns true when the cache is older than `maxAgeMs`. Cache entries
34
+ * without `syncedAt` (e.g. from env overrides) are never considered stale —
35
+ * the caller decided the value, not us.
36
+ */
37
+ export declare function isStale(active: ActiveOrg, maxAgeMs?: number): boolean;
38
+ export declare const _internals: {
39
+ getActiveOrgFile: typeof getActiveOrgFile;
40
+ getConfigDir: typeof getConfigDir;
41
+ };
42
+ export {};
43
+ //# sourceMappingURL=active-org.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org.d.ts","sourceRoot":"","sources":["../../src/lib/active-org.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,iBAAS,YAAY,IAAI,MAAM,CAK9B;AAED,iBAAS,gBAAgB,IAAI,MAAM,CAElC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,SAAS,GAAG,IAAI,CAuChD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAWtD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAMrC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,SAAkB,GAAG,OAAO,CAG9E;AAGD,eAAO,MAAM,UAAU;;;CAGtB,CAAC"}
@@ -0,0 +1,96 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ const FIVE_MINUTES_MS = 5 * 60 * 1000;
5
+ function getConfigDir() {
6
+ return (process.env.RULEMETRIC_CONFIG_DIR ||
7
+ path.join(os.homedir(), '.config', 'rulemetric'));
8
+ }
9
+ function getActiveOrgFile() {
10
+ return path.join(getConfigDir(), 'active-org');
11
+ }
12
+ /**
13
+ * Resolve the active org for the current process.
14
+ *
15
+ * Priority: env var (`RULEMETRIC_ORG_ID`) > local file > null. The env path
16
+ * always wins — it's the "I know what I want" override for CI and ad-hoc
17
+ * multi-tenant runs, even if the local file looks fresher.
18
+ */
19
+ export function readActiveOrg() {
20
+ const envOrgId = process.env.RULEMETRIC_ORG_ID;
21
+ if (envOrgId && envOrgId.trim()) {
22
+ return { orgId: envOrgId.trim() };
23
+ }
24
+ const file = getActiveOrgFile();
25
+ let raw;
26
+ try {
27
+ raw = fs.readFileSync(file, 'utf-8');
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(raw);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ if (!parsed ||
40
+ typeof parsed !== 'object' ||
41
+ !('orgId' in parsed) ||
42
+ typeof parsed.orgId !== 'string') {
43
+ return null;
44
+ }
45
+ const obj = parsed;
46
+ const result = { orgId: obj.orgId };
47
+ if (typeof obj.orgSlug === 'string') {
48
+ result.orgSlug = obj.orgSlug;
49
+ }
50
+ if (typeof obj.syncedAt === 'number') {
51
+ result.syncedAt = obj.syncedAt;
52
+ }
53
+ return result;
54
+ }
55
+ /**
56
+ * Persist the active org to the local file. Atomic via tmp+rename so a
57
+ * mid-write crash never leaves a partial JSON file.
58
+ */
59
+ export function writeActiveOrg(active) {
60
+ const dir = getConfigDir();
61
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
62
+ const file = getActiveOrgFile();
63
+ const tmpFile = `${file}.tmp`;
64
+ fs.writeFileSync(tmpFile, JSON.stringify(active, null, 2), {
65
+ encoding: 'utf-8',
66
+ mode: 0o600,
67
+ });
68
+ fs.renameSync(tmpFile, file);
69
+ }
70
+ /**
71
+ * Remove the local active-org file. No-op if absent.
72
+ */
73
+ export function clearActiveOrg() {
74
+ try {
75
+ fs.unlinkSync(getActiveOrgFile());
76
+ }
77
+ catch {
78
+ // file already gone
79
+ }
80
+ }
81
+ /**
82
+ * Returns true when the cache is older than `maxAgeMs`. Cache entries
83
+ * without `syncedAt` (e.g. from env overrides) are never considered stale —
84
+ * the caller decided the value, not us.
85
+ */
86
+ export function isStale(active, maxAgeMs = FIVE_MINUTES_MS) {
87
+ if (typeof active.syncedAt !== 'number')
88
+ return false;
89
+ return Date.now() - active.syncedAt > maxAgeMs;
90
+ }
91
+ // Exported for tests that need to know where the file lives.
92
+ export const _internals = {
93
+ getActiveOrgFile,
94
+ getConfigDir,
95
+ };
96
+ //# sourceMappingURL=active-org.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org.js","sourceRoot":"","sources":["../../src/lib/active-org.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=active-org.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org.test.d.ts","sourceRoot":"","sources":["../../src/lib/active-org.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,147 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { readActiveOrg, writeActiveOrg, clearActiveOrg, isStale, _internals, } from './active-org.js';
6
+ let tmpDir;
7
+ let savedConfigDir;
8
+ let savedOrgId;
9
+ beforeEach(() => {
10
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rulemetric-active-org-'));
11
+ savedConfigDir = process.env.RULEMETRIC_CONFIG_DIR;
12
+ savedOrgId = process.env.RULEMETRIC_ORG_ID;
13
+ process.env.RULEMETRIC_CONFIG_DIR = tmpDir;
14
+ delete process.env.RULEMETRIC_ORG_ID;
15
+ });
16
+ afterEach(() => {
17
+ fs.rmSync(tmpDir, { recursive: true, force: true });
18
+ if (savedConfigDir === undefined) {
19
+ delete process.env.RULEMETRIC_CONFIG_DIR;
20
+ }
21
+ else {
22
+ process.env.RULEMETRIC_CONFIG_DIR = savedConfigDir;
23
+ }
24
+ if (savedOrgId === undefined) {
25
+ delete process.env.RULEMETRIC_ORG_ID;
26
+ }
27
+ else {
28
+ process.env.RULEMETRIC_ORG_ID = savedOrgId;
29
+ }
30
+ });
31
+ describe('readActiveOrg', () => {
32
+ it('returns null when no file exists and env is unset', () => {
33
+ expect(readActiveOrg()).toBeNull();
34
+ });
35
+ it('returns env-sourced ActiveOrg when RULEMETRIC_ORG_ID is set', () => {
36
+ process.env.RULEMETRIC_ORG_ID = '11111111-1111-1111-1111-111111111111';
37
+ const active = readActiveOrg();
38
+ expect(active).toEqual({ orgId: '11111111-1111-1111-1111-111111111111' });
39
+ });
40
+ it('treats empty/whitespace env var as unset', () => {
41
+ process.env.RULEMETRIC_ORG_ID = ' ';
42
+ expect(readActiveOrg()).toBeNull();
43
+ });
44
+ it('returns file contents when file exists and env is unset', () => {
45
+ writeActiveOrg({
46
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
47
+ orgSlug: 'acme',
48
+ syncedAt: 1_700_000_000_000,
49
+ });
50
+ expect(readActiveOrg()).toEqual({
51
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
52
+ orgSlug: 'acme',
53
+ syncedAt: 1_700_000_000_000,
54
+ });
55
+ });
56
+ it('env wins over file (env is authoritative even if file looks fresher)', () => {
57
+ writeActiveOrg({
58
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
59
+ orgSlug: 'acme',
60
+ syncedAt: Date.now(),
61
+ });
62
+ process.env.RULEMETRIC_ORG_ID = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
63
+ const active = readActiveOrg();
64
+ expect(active?.orgId).toBe('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb');
65
+ expect(active?.orgSlug).toBeUndefined();
66
+ expect(active?.syncedAt).toBeUndefined();
67
+ });
68
+ it('returns null when file is corrupt JSON', () => {
69
+ fs.mkdirSync(tmpDir, { recursive: true });
70
+ fs.writeFileSync(_internals.getActiveOrgFile(), 'not json', 'utf-8');
71
+ expect(readActiveOrg()).toBeNull();
72
+ });
73
+ it('returns null when JSON lacks orgId', () => {
74
+ fs.mkdirSync(tmpDir, { recursive: true });
75
+ fs.writeFileSync(_internals.getActiveOrgFile(), JSON.stringify({ orgSlug: 'acme' }), 'utf-8');
76
+ expect(readActiveOrg()).toBeNull();
77
+ });
78
+ });
79
+ describe('writeActiveOrg', () => {
80
+ it('creates the parent directory if absent', () => {
81
+ const nested = path.join(tmpDir, 'nested', 'config');
82
+ process.env.RULEMETRIC_CONFIG_DIR = nested;
83
+ writeActiveOrg({
84
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
85
+ orgSlug: 'acme',
86
+ syncedAt: 1,
87
+ });
88
+ expect(fs.existsSync(path.join(nested, 'active-org'))).toBe(true);
89
+ });
90
+ it('round-trips through readActiveOrg', () => {
91
+ const value = {
92
+ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
93
+ orgSlug: 'acme',
94
+ syncedAt: 1_700_000_000_000,
95
+ };
96
+ writeActiveOrg(value);
97
+ expect(readActiveOrg()).toEqual(value);
98
+ });
99
+ it('writes file with restricted permissions (0o600)', () => {
100
+ writeActiveOrg({ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' });
101
+ const stat = fs.statSync(_internals.getActiveOrgFile());
102
+ expect(stat.mode & 0o777).toBe(0o600);
103
+ });
104
+ it('does not leave a tmp file on disk after success', () => {
105
+ writeActiveOrg({ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' });
106
+ const tmp = `${_internals.getActiveOrgFile()}.tmp`;
107
+ expect(fs.existsSync(tmp)).toBe(false);
108
+ });
109
+ it('overwrites existing file', () => {
110
+ writeActiveOrg({ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', orgSlug: 'acme' });
111
+ writeActiveOrg({ orgId: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', orgSlug: 'beta' });
112
+ expect(readActiveOrg()).toEqual({
113
+ orgId: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
114
+ orgSlug: 'beta',
115
+ });
116
+ });
117
+ });
118
+ describe('clearActiveOrg', () => {
119
+ it('removes the file', () => {
120
+ writeActiveOrg({ orgId: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' });
121
+ expect(fs.existsSync(_internals.getActiveOrgFile())).toBe(true);
122
+ clearActiveOrg();
123
+ expect(fs.existsSync(_internals.getActiveOrgFile())).toBe(false);
124
+ });
125
+ it('is a no-op when file is absent', () => {
126
+ expect(() => clearActiveOrg()).not.toThrow();
127
+ });
128
+ });
129
+ describe('isStale', () => {
130
+ it('returns true when syncedAt is older than maxAge', () => {
131
+ const stale = { orgId: 'x', syncedAt: Date.now() - 10 * 60 * 1000 };
132
+ expect(isStale(stale)).toBe(true);
133
+ });
134
+ it('returns false when syncedAt is recent', () => {
135
+ const fresh = { orgId: 'x', syncedAt: Date.now() - 30 * 1000 };
136
+ expect(isStale(fresh)).toBe(false);
137
+ });
138
+ it('returns false when syncedAt is missing (env override)', () => {
139
+ expect(isStale({ orgId: 'x' })).toBe(false);
140
+ });
141
+ it('respects custom maxAgeMs', () => {
142
+ const value = { orgId: 'x', syncedAt: Date.now() - 1000 };
143
+ expect(isStale(value, 500)).toBe(true);
144
+ expect(isStale(value, 2000)).toBe(false);
145
+ });
146
+ });
147
+ //# sourceMappingURL=active-org.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-org.test.js","sourceRoot":"","sources":["../../src/lib/active-org.test.ts"],"names":[],"mappings":"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"}