@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
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @rulemetric/cli
2
+
3
+ The RuleMetric CLI. Manages instructions, sessions, evals, and (as of the
4
+ user-management release) organizations.
5
+
6
+ ## Auth
7
+
8
+ ```bash
9
+ rulemetric auth login # interactive — writes ~/.config/rulemetric/env
10
+ rulemetric auth create-key # generate a long-lived API key
11
+ rulemetric auth whoami # print the current user
12
+ ```
13
+
14
+ Credentials are read from (first match wins):
15
+ 1. `RULEMETRIC_API_URL` + `RULEMETRIC_API_KEY` (or `RULEMETRIC_ACCESS_TOKEN`)
16
+ 2. `~/.config/rulemetric/env`
17
+ 3. `~/.config/rulemetric/auth.json` (JSON fallback, requires `jq`)
18
+ 4. `.env.local` in the project root (development)
19
+
20
+ ## Org commands
21
+
22
+ The org commands let you list and switch the "active org" used by every
23
+ subsequent CLI invocation. Sessions captured by hooks/proxy/MCP while an
24
+ active org is set will stamp `sessions.org_id` to that org.
25
+
26
+ ```bash
27
+ rulemetric org list # list orgs you belong to; star marks the active one
28
+ rulemetric org current # print the active org's slug, name, and role
29
+ rulemetric org switch <slug> # set the active org (cached locally)
30
+ rulemetric org switch --clear # clear the active org (back to personal mode)
31
+ ```
32
+
33
+ **Active-org resolution order:**
34
+ 1. `RULEMETRIC_ORG_ID` environment variable (process-scoped override)
35
+ 2. Local cache at `~/.config/rulemetric/active-org.json` (written by
36
+ `rulemetric org switch`)
37
+ 3. None (sessions captured without an org_id)
38
+
39
+ A background prefetch refreshes the cache on a TTL so a slug change in the
40
+ web UI is picked up by the next CLI run without an explicit `org switch`.
41
+
42
+ **Creating an org:** Currently web-only — visit `/orgs/new` in the
43
+ dashboard. The CLI will adopt the org once you `org switch <slug>` (or set
44
+ the active org in the web UI, which the CLI cache picks up).
45
+
46
+ ## Other top-level commands
47
+
48
+ - `rulemetric instructions ...` — list, get, create, edit, push, pull
49
+ - `rulemetric convert ...` — format conversion (25+ formats)
50
+ - `rulemetric import ...` — bulk import from a project tree
51
+ - `rulemetric sessions ...` — list/get/import sessions
52
+ - `rulemetric evals ...` — eval target/case/run/optimize commands
53
+ - `rulemetric insights sync|generate` — push local session-meta + facets up
54
+ - `rulemetric hooks install|uninstall|run <name>` — manage tool hook scripts
55
+ - `rulemetric proxy ...` — start/stop the mitmproxy capture layer
56
+ - `rulemetric service install|uninstall` — launchd services for API + worker
57
+
58
+ ## Tests
59
+
60
+ ```bash
61
+ pnpm --filter @rulemetric/cli test:unit # unit tests (no creds needed)
62
+ pnpm --filter @rulemetric/cli test:e2e # e2e (needs Supabase creds)
63
+ ```
64
+
65
+ E2E tests start a real Hono API on port 3001 and create a temp Supabase
66
+ user; see `apps/cli/e2e/global-setup.ts`.
@@ -1 +1 @@
1
- {"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAI7C,8BAAsB,WAAY,SAAQ,OAAO;IAC/C,OAAgB,SAAS;;MAMvB;cAEuB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9C,SAAS,CAAC,WAAW,IAAI,IAAI;cAMJ,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAelF"}
1
+ {"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAM7C,8BAAsB,WAAY,SAAQ,OAAO;IAC/C,OAAgB,SAAS;;MAMvB;cAEuB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB9C,SAAS,CAAC,WAAW,IAAI,IAAI;cASJ,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAelF"}
@@ -1,6 +1,8 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
2
  import { isAuthenticated } from './lib/auth.js';
3
3
  import { ApiError } from './lib/api-client.js';
4
+ import { maybeRefreshActiveOrg } from './lib/active-org-refresh.js';
5
+ import { getUpgradeBanner, refreshVersionCacheInBackground } from './lib/version-check.js';
4
6
  export class BaseCommand extends Command {
5
7
  static baseFlags = {
6
8
  'api-url': Flags.string({
@@ -15,11 +17,24 @@ export class BaseCommand extends Command {
15
17
  if (flags['api-url']) {
16
18
  process.env.RULEMETRIC_API_URL = flags['api-url'];
17
19
  }
20
+ // Print the upgrade banner from the cache (populated by the previous
21
+ // command). To stderr so it doesn't pollute scriptable stdout. Then
22
+ // kick off a background refresh for the *next* invocation. Suppressed
23
+ // on commands meant to be machine-readable (`hooks run`, etc.) since
24
+ // a banner there would corrupt JSON output piped to other tools.
25
+ const banner = getUpgradeBanner(this.config.version);
26
+ if (banner && !this.argv.includes('--quiet') && !process.env.RULEMETRIC_QUIET) {
27
+ process.stderr.write(`\n${banner}\n\n`);
28
+ }
29
+ refreshVersionCacheInBackground();
18
30
  }
19
31
  requireAuth() {
20
32
  if (!isAuthenticated()) {
21
33
  this.error('Not logged in. Run `rulemetric auth login` to authenticate.');
22
34
  }
35
+ // Fire-and-forget refresh of the local active-org cache. Doesn't block
36
+ // the command — its only side effect is rewriting the cache file.
37
+ void maybeRefreshActiveOrg();
23
38
  }
24
39
  async catch(err) {
25
40
  if (err instanceof ApiError) {
@@ -1 +1 @@
1
- {"version":3,"file":"base-command.js","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,MAAM,OAAgB,WAAY,SAAQ,OAAO;IAC/C,MAAM,CAAU,SAAS,GAAG;QAC1B,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC;YACtB,WAAW,EAAE,uBAAuB;YACpC,GAAG,EAAE,oBAAoB;YACzB,MAAM,EAAE,IAAI;SACb,CAAC;KACH,CAAC;IAEiB,KAAK,CAAC,IAAI;QAC3B,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAiC,CAAC,CAAC;QAC3E,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAES,WAAW;QACnB,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAEkB,KAAK,CAAC,KAAK,CAAC,GAAkC;QAC/D,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;YACjF,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACnF,IAAI,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,GAAG,CAAC;IACZ,CAAC"}
1
+ {"version":3,"file":"base-command.js","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAE3F,MAAM,OAAgB,WAAY,SAAQ,OAAO;IAC/C,MAAM,CAAU,SAAS,GAAG;QAC1B,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC;YACtB,WAAW,EAAE,uBAAuB;YACpC,GAAG,EAAE,oBAAoB;YACzB,MAAM,EAAE,IAAI;SACb,CAAC;KACH,CAAC;IAEiB,KAAK,CAAC,IAAI;QAC3B,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAiC,CAAC,CAAC;QAC3E,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,qEAAqE;QACrE,oEAAoE;QACpE,sEAAsE;QACtE,qEAAqE;QACrE,iEAAiE;QACjE,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrD,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC9E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,MAAM,CAAC,CAAC;QAC1C,CAAC;QACD,+BAA+B,EAAE,CAAC;IACpC,CAAC;IAES,WAAW;QACnB,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QAC5E,CAAC;QACD,uEAAuE;QACvE,kEAAkE;QAClE,KAAK,qBAAqB,EAAE,CAAC;IAC/B,CAAC;IAEkB,KAAK,CAAC,KAAK,CAAC,GAAkC;QAC/D,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;YACjF,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACnF,IAAI,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,GAAG,CAAC;IACZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAWpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,WAAW,SAA0B;IAErD,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;MAGnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAiC3B"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAYpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,WAAW,SAA0B;IAErD,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;MAGnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAwC3B"}
@@ -2,6 +2,7 @@ import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base-command.js';
3
3
  import { saveToken, saveEnvFile } from '../../lib/auth.js';
4
4
  import { apiPost } from '../../lib/api-client.js';
5
+ import { bootstrapActiveOrg } from '../../lib/active-org-refresh.js';
5
6
  export default class AuthLogin extends BaseCommand {
6
7
  static description = 'Log in to RuleMetric';
7
8
  static examples = [
@@ -34,6 +35,12 @@ export default class AuthLogin extends BaseCommand {
34
35
  email: data.email,
35
36
  });
36
37
  saveEnvFile(data.accessToken, process.env.RULEMETRIC_API_URL);
38
+ // Bootstrap the active-org cache now that auth is available. BaseCommand
39
+ // fired the guarded refresh BEFORE login completed (no creds → 401 →
40
+ // swallowed); without this explicit retry, the cache stays empty until
41
+ // the user runs another CLI command, and any Claude Code session that
42
+ // fires in the meantime lands with org_id = NULL.
43
+ await bootstrapActiveOrg();
37
44
  this.log(`Logged in as ${data.email}`);
38
45
  }
39
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AASlD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,MAAM,CAAU,WAAW,GAAG,sBAAsB,CAAC;IAErD,MAAM,CAAU,QAAQ,GAAG;QACzB,8BAA8B;QAC9B,kDAAkD;KACnD,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;QAChE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;KACzF,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAE9C,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACxB,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAE9B,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YAE9E,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7C,CAAC;YAED,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,GAAG,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAgB,aAAa,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAE9E,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAErF,SAAS,CAAC;YACR,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS;YACT,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QAEH,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAE9D,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACzC,CAAC"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AASrE,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,MAAM,CAAU,WAAW,GAAG,sBAAsB,CAAC;IAErD,MAAM,CAAU,QAAQ,GAAG;QACzB,8BAA8B;QAC9B,kDAAkD;KACnD,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;QAChE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;KACzF,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAE9C,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACxB,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAE9B,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YAE9E,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7C,CAAC;YAED,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,GAAG,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAgB,aAAa,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAE9E,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAErF,SAAS,CAAC;YACR,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS;YACT,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QAEH,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAE9D,yEAAyE;QACzE,qEAAqE;QACrE,uEAAuE;QACvE,sEAAsE;QACtE,kDAAkD;QAClD,MAAM,kBAAkB,EAAE,CAAC;QAE3B,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACzC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class CronsList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
7
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/crons/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAC9C,OAAgB,WAAW,SAAwD;IAEnF,OAAgB,QAAQ,WAEtB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAc7B"}
@@ -0,0 +1,24 @@
1
+ import { printTable } from '@oclif/table';
2
+ import { BaseCommand } from '../../base-command.js';
3
+ import { CRON_REGISTRY } from '../../lib/cron-registry.js';
4
+ export default class CronsList extends BaseCommand {
5
+ static description = 'List all registered cron tasks and their schedules';
6
+ static examples = [
7
+ '<%= config.bin %> crons list',
8
+ ];
9
+ async run() {
10
+ await this.parse(CronsList);
11
+ printTable({
12
+ data: CRON_REGISTRY.map((c) => ({
13
+ Name: c.name,
14
+ Schedule: c.schedule,
15
+ Description: c.description,
16
+ })),
17
+ columns: ['Name', 'Schedule', 'Description'],
18
+ });
19
+ this.log('');
20
+ this.log(`${CRON_REGISTRY.length} cron task(s) registered. Each runs on the user's local worker.`);
21
+ this.log('Run any of them on demand with: rulemetric crons run <name>');
22
+ }
23
+ }
24
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/crons/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAC9C,MAAM,CAAU,WAAW,GAAG,oDAAoD,CAAC;IAEnF,MAAM,CAAU,QAAQ,GAAG;QACvB,8BAA8B;KACjC,CAAC;IAEF,KAAK,CAAC,GAAG;QACL,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC5B,UAAU,CAAC;YACP,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5B,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,WAAW,EAAE,CAAC,CAAC,WAAW;aAC7B,CAAC,CAAC;YACH,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,MAAM,iEAAiE,CAAC,CAAC;QACnG,IAAI,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;IAC5E,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../base-command.js';
2
+ export default class CronsRun extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/crons/run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC7C,OAAgB,WAAW,SAAyD;IAEpF,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,IAAI;;MAKlB;IAEF,OAAgB,KAAK;;MAKnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2B7B"}
@@ -0,0 +1,48 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base-command.js';
3
+ import { CRON_REGISTRY } from '../../lib/cron-registry.js';
4
+ import { runCronByName } from '../../lib/cron-runner.js';
5
+ export default class CronsRun extends BaseCommand {
6
+ static description = 'Run a registered cron task synchronously, right now';
7
+ static examples = [
8
+ '<%= config.bin %> crons run cron_refresh_skills',
9
+ '<%= config.bin %> crons run cron_suggest_instructions --quiet',
10
+ ];
11
+ static args = {
12
+ name: Args.string({
13
+ description: 'Cron task name (run `rulemetric crons list` to see all)',
14
+ required: true,
15
+ }),
16
+ };
17
+ static flags = {
18
+ quiet: Flags.boolean({
19
+ char: 'q',
20
+ description: 'Suppress per-line log output; print summary only',
21
+ }),
22
+ };
23
+ async run() {
24
+ const { args, flags } = await this.parse(CronsRun);
25
+ this.requireAuth();
26
+ const def = CRON_REGISTRY.find((c) => c.name === args.name);
27
+ if (!def) {
28
+ this.error(`Unknown cron: ${args.name}\nRun \`rulemetric crons list\` to see available tasks.`);
29
+ }
30
+ this.log(`▶ Running ${def.name} …`);
31
+ const startedAt = Date.now();
32
+ const result = await runCronByName(args.name);
33
+ if (!flags.quiet) {
34
+ for (const line of result.logs) {
35
+ const prefix = line.level === 'error' || line.level === 'warn' ? `[${line.level}]` : '';
36
+ this.log(` ${prefix} ${line.message}`);
37
+ }
38
+ }
39
+ const seconds = ((result.durationMs ?? Date.now() - startedAt) / 1000).toFixed(1);
40
+ if (result.ok) {
41
+ this.log(`✓ ${def.name} completed in ${seconds}s (${result.logs.length} log line${result.logs.length === 1 ? '' : 's'})`);
42
+ }
43
+ else {
44
+ this.error(`✗ ${def.name} failed after ${seconds}s: ${result.error}`);
45
+ }
46
+ }
47
+ }
48
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/crons/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC7C,MAAM,CAAU,WAAW,GAAG,qDAAqD,CAAC;IAEpF,MAAM,CAAU,QAAQ,GAAG;QACvB,iDAAiD;QACjD,+DAA+D;KAClE,CAAC;IAEF,MAAM,CAAU,IAAI,GAAG;QACnB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;YACd,WAAW,EAAE,yDAAyD;YACtE,QAAQ,EAAE,IAAI;SACjB,CAAC;KACL,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACpB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;YACjB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,kDAAkD;SAClE,CAAC;KACL,CAAC;IAEF,KAAK,CAAC,GAAG;QACL,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,IAAI,CAAC,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,yDAAyD,CAAC,CAAC;QACpG,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE9C,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACf,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxF,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5C,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClF,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,iBAAiB,OAAO,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,YAAY,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAC9H,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,iBAAiB,OAAO,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1E,CAAC;IACL,CAAC"}
@@ -1,10 +1,36 @@
1
1
  import { BaseCommand } from '../../base-command.js';
2
+ /**
3
+ * Rewrite a Supabase Supavisor TRANSACTION-mode pooler URL (port 6543) to the
4
+ * SESSION-mode pooler URL (port 5432) on the same host.
5
+ *
6
+ * in: postgresql://postgres.PROJECTREF:PASS@aws-N-REGION.pooler.supabase.com:6543/postgres
7
+ * out: postgresql://postgres.PROJECTREF:PASS@aws-N-REGION.pooler.supabase.com:5432/postgres
8
+ *
9
+ * graphile-worker relies on LISTEN/NOTIFY. Through transaction-mode pooling
10
+ * each pooled checkout can land on a different backend, so LISTEN registrations
11
+ * and the NOTIFYs meant to wake them are decoupled; the worker then reconnects
12
+ * in a tight loop and those failed LISTEN reconnects pile up as half-open
13
+ * pooler sessions (see docs/incidents/2026-05-18-supavisor-cascade.md).
14
+ * Session mode pins one backend for the lifetime of the client connection, so
15
+ * LISTEN works.
16
+ *
17
+ * We deliberately target the session POOLER, not the true direct connection
18
+ * (`db.<ref>.supabase.co:5432`): that host is IPv6-only on Supabase, so a
19
+ * worker on an IPv4-only network — `getaddrinfo` returns no address — could
20
+ * not connect at all. The session pooler shares the pooler host, which
21
+ * resolves on IPv4.
22
+ *
23
+ * Returns the input untouched when it isn't a transaction-mode Supavisor URL:
24
+ * an already-session-mode URL, a direct connection, or any self-hosted / local
25
+ * Postgres all work for graphile-worker as-is.
26
+ */
27
+ export declare function deriveSessionPoolerUrl(input: string | undefined): string | undefined;
2
28
  export default class EvalListenCommand extends BaseCommand {
3
29
  static description: string;
4
30
  static examples: string[];
5
31
  static flags: {
6
- 'supabase-url': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
- 'supabase-anon-key': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
32
+ 'supabase-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
33
+ 'supabase-anon-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
34
  };
9
35
  run(): Promise<void>;
10
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"listen.d.ts","sourceRoot":"","sources":["../../../src/commands/evals/listen.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,WAAW;IACxD,OAAgB,WAAW,SAAmE;IAE9F,OAAgB,QAAQ,WAEtB;IAEF,OAAgB,KAAK;;;MAWnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAqC3B"}
1
+ {"version":3,"file":"listen.d.ts","sourceRoot":"","sources":["../../../src/commands/evals/listen.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKpD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAepF;AAED,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,WAAW;IACxD,OAAgB,WAAW,SAAmE;IAE9F,OAAgB,QAAQ,WAEtB;IAEF,OAAgB,KAAK;;;MAiBnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA4H3B"}
@@ -1,53 +1,187 @@
1
1
  import { Flags } from '@oclif/core';
2
+ import { initSentry, flushSentry, initTelemetry, shutdownTelemetry } from '@rulemetric/telemetry';
2
3
  import { BaseCommand } from '../../base-command.js';
3
- import { startWorker, createWorkerId, getUserIdFromJwt } from '../../lib/eval-worker.js';
4
+ import { startWorker } from '../../lib/graphile-worker-runner.js';
4
5
  import { loadToken } from '../../lib/auth.js';
6
+ import { apiGet } from '../../lib/api-client.js';
7
+ /**
8
+ * Rewrite a Supabase Supavisor TRANSACTION-mode pooler URL (port 6543) to the
9
+ * SESSION-mode pooler URL (port 5432) on the same host.
10
+ *
11
+ * in: postgresql://postgres.PROJECTREF:PASS@aws-N-REGION.pooler.supabase.com:6543/postgres
12
+ * out: postgresql://postgres.PROJECTREF:PASS@aws-N-REGION.pooler.supabase.com:5432/postgres
13
+ *
14
+ * graphile-worker relies on LISTEN/NOTIFY. Through transaction-mode pooling
15
+ * each pooled checkout can land on a different backend, so LISTEN registrations
16
+ * and the NOTIFYs meant to wake them are decoupled; the worker then reconnects
17
+ * in a tight loop and those failed LISTEN reconnects pile up as half-open
18
+ * pooler sessions (see docs/incidents/2026-05-18-supavisor-cascade.md).
19
+ * Session mode pins one backend for the lifetime of the client connection, so
20
+ * LISTEN works.
21
+ *
22
+ * We deliberately target the session POOLER, not the true direct connection
23
+ * (`db.<ref>.supabase.co:5432`): that host is IPv6-only on Supabase, so a
24
+ * worker on an IPv4-only network — `getaddrinfo` returns no address — could
25
+ * not connect at all. The session pooler shares the pooler host, which
26
+ * resolves on IPv4.
27
+ *
28
+ * Returns the input untouched when it isn't a transaction-mode Supavisor URL:
29
+ * an already-session-mode URL, a direct connection, or any self-hosted / local
30
+ * Postgres all work for graphile-worker as-is.
31
+ */
32
+ export function deriveSessionPoolerUrl(input) {
33
+ if (!input)
34
+ return undefined;
35
+ let url;
36
+ try {
37
+ url = new URL(input);
38
+ }
39
+ catch {
40
+ return input;
41
+ }
42
+ // Only Supavisor pooler hosts have a transaction-vs-session port split.
43
+ if (!/\.pooler\.supabase\.com$/i.test(url.hostname))
44
+ return input;
45
+ // 6543 = transaction mode (breaks LISTEN); 5432 = session mode. Anything
46
+ // not on 6543 is left alone — credentials are identical across both modes.
47
+ if (url.port !== '6543')
48
+ return input;
49
+ url.port = '5432';
50
+ return url.toString();
51
+ }
5
52
  export default class EvalListenCommand extends BaseCommand {
6
53
  static description = 'Listen for eval jobs from the web UI and execute them locally';
7
54
  static examples = [
8
55
  '<%= config.bin %> evals listen',
9
56
  ];
10
57
  static flags = {
58
+ // Legacy flags from the pre-graphile-worker queue. The graphile-worker
59
+ // runner connects directly to Postgres via DATABASE_URL, so these are
60
+ // no longer needed — but we keep them as silent no-ops so existing
61
+ // launchd plists that pass them keep working.
11
62
  'supabase-url': Flags.string({
12
- description: 'Supabase project URL',
63
+ description: '[deprecated, ignored] Supabase project URL (no longer used)',
13
64
  env: 'SUPABASE_URL',
14
- required: true,
65
+ required: false,
66
+ hidden: true,
15
67
  }),
16
68
  'supabase-anon-key': Flags.string({
17
- description: 'Supabase anon key',
69
+ description: '[deprecated, ignored] Supabase anon key (no longer used)',
18
70
  env: 'SUPABASE_ANON_KEY',
19
- required: true,
71
+ required: false,
72
+ hidden: true,
20
73
  }),
21
74
  };
22
75
  async run() {
23
- const { flags } = await this.parse(EvalListenCommand);
76
+ await this.parse(EvalListenCommand);
24
77
  this.requireAuth();
78
+ process.env.PG_APPLICATION_NAME ||= 'rulemetric-worker';
79
+ // Sentry first — captures any uncaughtException / unhandledRejection that
80
+ // fires during the rest of init (queue derivation, profile fetch, runner
81
+ // boot). Silent no-op when SENTRY_DSN is unset so dev laptops aren't
82
+ // affected. The graphile-worker-runner additionally hooks `job:failed`
83
+ // events to forward task-level errors with the mirrorId/taskName tagged.
84
+ const sentryEnabled = initSentry({ serviceName: 'rulemetric-worker' });
85
+ if (sentryEnabled)
86
+ this.log('[sentry] error capture enabled');
87
+ // OpenTelemetry — defaults to ConsoleSpanExporter (writes spans to stdout
88
+ // as JSON, captured by launchd to worker-stdout.log) when no OTLP endpoint
89
+ // is set. The cron envelope wraps each cron firing in a span, so the
90
+ // worker-side trace surface mirrors the API: search for `cron <name>` in
91
+ // worker-stdout.log to see span duration, status, and any recorded
92
+ // exception. To ship spans to a backend, set OTEL_EXPORTER_OTLP_ENDPOINT
93
+ // in the worker's launchd EnvironmentVariables (same convention as the
94
+ // API on Fly).
95
+ const otelMode = initTelemetry({ serviceName: 'rulemetric-worker' });
96
+ if (otelMode === 'otlp') {
97
+ this.log(`[otel] traces exporting to ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}`);
98
+ }
99
+ else if (otelMode === 'console') {
100
+ this.log('[otel] cron spans printing to stdout (set OTEL_EXPORTER_OTLP_ENDPOINT to ship to a backend)');
101
+ }
102
+ else {
103
+ this.log('[otel] disabled (TELEMETRY_ENABLED=false)');
104
+ }
105
+ // Token isn't passed into the runner — api-client.ts loads it from
106
+ // ~/.config/rulemetric/env on every request. We still call loadToken()
107
+ // here as an early sanity check so the worker fails fast at startup
108
+ // if the user hasn't run `rulemetric auth login`.
25
109
  const token = loadToken();
26
- const accessToken = token?.accessToken ?? flags['supabase-anon-key'];
27
- const userId = token ? getUserIdFromJwt(token.accessToken) : null;
28
- if (!userId) {
29
- this.error('Could not determine user ID. Run `rulemetric auth login`.');
110
+ if (!token) {
111
+ this.error('No auth token found. Run `rulemetric auth login` first.');
112
+ }
113
+ // graphile-worker uses LISTEN/NOTIFY, which is incompatible with
114
+ // transaction-mode pooling (Supavisor :6543). Each failed LISTEN reconnect
115
+ // burns a pooler slot, and when the pooler saturates the retry storm
116
+ // (graphile-worker reconnects every 102 ms) perpetuates it indefinitely —
117
+ // we observed Supavisor pinned at 200/200 client connections for 24+ hours
118
+ // (docs/incidents/2026-05-18-supavisor-cascade.md).
119
+ //
120
+ // Resolution: run graphile-worker against the SESSION-mode pooler (:5432).
121
+ // Session mode pins a backend per connection so LISTEN works, and — unlike
122
+ // the true direct host (db.<ref>.supabase.co, IPv6-only) — it stays
123
+ // reachable on the IPv4-only networks most worker laptops run on.
124
+ // Precedence: WORKER_DATABASE_URL > auto-derived session URL > error.
125
+ const connectionString = process.env.WORKER_DATABASE_URL ||
126
+ deriveSessionPoolerUrl(process.env.DATABASE_URL);
127
+ if (!connectionString) {
128
+ this.error('Could not resolve a Postgres URL for the worker. Set WORKER_DATABASE_URL or DATABASE_URL (see .env.example).');
129
+ }
130
+ // Warn only on transaction-mode pooling — the cascade trigger. A
131
+ // session-mode pooler URL (:5432) or a direct connection is the goal,
132
+ // so neither should warn.
133
+ if (/:6543\b/.test(connectionString)) {
134
+ this.warn('Worker is connecting through the Supavisor TRANSACTION-mode pooler (:6543). graphile-worker LISTEN/NOTIFY is unstable there — set WORKER_DATABASE_URL to a session-mode (:5432) or direct URL.');
135
+ }
136
+ // Resolve the worker's userId — required so registered task names match
137
+ // the per-user suffix the API uses when enqueueing. Without this, the
138
+ // worker would register `process_eval__undefined` and claim nothing.
139
+ let userId;
140
+ try {
141
+ const profile = await apiGet('/api/profile');
142
+ userId = profile.id;
143
+ }
144
+ catch (err) {
145
+ this.error(`Failed to resolve worker user (GET /api/profile): ${err instanceof Error ? err.message : String(err)}`);
30
146
  }
31
- this.log(`Starting eval worker (${createWorkerId()})...`);
147
+ this.log('Starting eval worker (graphile-worker)...');
32
148
  this.log('Press Ctrl+C to stop.\n');
33
149
  const cleanup = await startWorker({
34
- supabaseUrl: flags['supabase-url'],
35
- supabaseAnonKey: flags['supabase-anon-key'],
36
- accessToken,
150
+ connectionString,
37
151
  userId,
38
152
  log: (msg) => this.log(msg),
39
153
  });
40
- // Keep alive until SIGINT
41
- await new Promise((resolve) => {
42
- process.on('SIGINT', () => {
43
- this.log('\nShutting down worker...');
44
- cleanup();
45
- resolve();
46
- });
47
- process.on('SIGTERM', () => {
48
- cleanup();
49
- resolve();
50
- });
154
+ // Keep alive until SIGINT/SIGTERM. cleanup() awaits runner.stop() which
155
+ // drains in-flight jobs — but insights/cron jobs spawn `claude`
156
+ // subprocesses that don't respond to abort signals, so the graceful path
157
+ // can hang for minutes or hours. We race cleanup against a 10s timeout
158
+ // and then process.exit() unconditionally; any in-flight child processes
159
+ // get orphaned to launchd/init which is fine — they finish on their own.
160
+ // Diagnosed 2026-05-21 after observing 23h-old worker processes that
161
+ // survived multiple `launchctl unload` cycles because runner.stop()
162
+ // never resolved.
163
+ const SHUTDOWN_GRACE_MS = 10_000;
164
+ const shutdown = async (signal) => {
165
+ this.log(`\n[${signal}] shutting down worker (max ${SHUTDOWN_GRACE_MS}ms graceful)...`);
166
+ await Promise.race([
167
+ cleanup().catch((err) => {
168
+ this.log(`cleanup error (continuing): ${err instanceof Error ? err.message : String(err)}`);
169
+ }),
170
+ new Promise((r) => setTimeout(r, SHUTDOWN_GRACE_MS)),
171
+ ]);
172
+ // Flush Sentry + OTel with tight ceilings so a wedged transport doesn't
173
+ // re-extend the shutdown beyond launchd's expectations. The BatchSpanProcessor
174
+ // can hold up to ~30s of in-flight spans by default; shutdownTelemetry()
175
+ // forces a final flush before resolving.
176
+ await Promise.allSettled([
177
+ flushSentry(2_000),
178
+ shutdownTelemetry(),
179
+ ]);
180
+ process.exit(0);
181
+ };
182
+ await new Promise(() => {
183
+ process.on('SIGINT', () => { void shutdown('SIGINT'); });
184
+ process.on('SIGTERM', () => { void shutdown('SIGTERM'); });
51
185
  });
52
186
  }
53
187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"listen.js","sourceRoot":"","sources":["../../../src/commands/evals/listen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,WAAW;IACxD,MAAM,CAAU,WAAW,GAAG,+DAA+D,CAAC;IAE9F,MAAM,CAAU,QAAQ,GAAG;QACzB,gCAAgC;KACjC,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC;YAC3B,WAAW,EAAE,sBAAsB;YACnC,GAAG,EAAE,cAAc;YACnB,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,mBAAmB,EAAE,KAAK,CAAC,MAAM,CAAC;YAChC,WAAW,EAAE,mBAAmB;YAChC,GAAG,EAAE,mBAAmB;YACxB,QAAQ,EAAE,IAAI;SACf,CAAC;KACH,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAE1B,MAAM,WAAW,GAAG,KAAK,EAAE,WAAW,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAElE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC1E,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,yBAAyB,cAAc,EAAE,MAAM,CAAC,CAAC;QAC1D,IAAI,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAEpC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC;YAChC,WAAW,EAAE,KAAK,CAAC,cAAc,CAAC;YAClC,eAAe,EAAE,KAAK,CAAC,mBAAmB,CAAC;YAC3C,WAAW;YACX,MAAM;YACN,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;SAC5B,CAAC,CAAC;QAEH,0BAA0B;QAC1B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACxB,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;gBACtC,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACzB,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC"}
1
+ {"version":3,"file":"listen.js","sourceRoot":"","sources":["../../../src/commands/evals/listen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=listen.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.test.d.ts","sourceRoot":"","sources":["../../../src/commands/evals/listen.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deriveSessionPoolerUrl } from './listen.js';
3
+ describe('deriveSessionPoolerUrl', () => {
4
+ it('rewrites a transaction-mode pooler URL (:6543) to session mode (:5432)', () => {
5
+ const txn = 'postgresql://postgres.pkilumvmitlghljtqeud:secret@aws-1-us-east-1.pooler.supabase.com:6543/postgres';
6
+ expect(deriveSessionPoolerUrl(txn)).toBe('postgresql://postgres.pkilumvmitlghljtqeud:secret@aws-1-us-east-1.pooler.supabase.com:5432/postgres');
7
+ });
8
+ it('changes only the port — host, username, password and database are preserved', () => {
9
+ const txn = 'postgresql://postgres.abc123:pw@aws-0-eu-central-1.pooler.supabase.com:6543/postgres';
10
+ const out = deriveSessionPoolerUrl(txn);
11
+ expect(out).toBeDefined();
12
+ const u = new URL(out);
13
+ expect(u.port).toBe('5432');
14
+ expect(u.hostname).toBe('aws-0-eu-central-1.pooler.supabase.com');
15
+ expect(u.username).toBe('postgres.abc123');
16
+ expect(u.password).toBe('pw');
17
+ expect(u.pathname).toBe('/postgres');
18
+ });
19
+ it('leaves an already session-mode pooler URL (:5432) untouched', () => {
20
+ const session = 'postgresql://postgres.abc123:pw@aws-0-us-east-1.pooler.supabase.com:5432/postgres';
21
+ expect(deriveSessionPoolerUrl(session)).toBe(session);
22
+ });
23
+ it('leaves a true direct connection (db.<ref>.supabase.co) untouched', () => {
24
+ // The direct host is IPv6-only on Supabase, so we never auto-target it —
25
+ // a worker on an IPv4-only network could not resolve it. If a caller
26
+ // explicitly supplies a direct URL we pass it through unchanged.
27
+ const direct = 'postgresql://postgres:pw@db.abc123.supabase.co:5432/postgres';
28
+ expect(deriveSessionPoolerUrl(direct)).toBe(direct);
29
+ });
30
+ it('leaves non-Supabase URLs untouched (self-hosted / local pg)', () => {
31
+ const local = 'postgresql://user:pw@localhost:6543/mydb';
32
+ expect(deriveSessionPoolerUrl(local)).toBe(local);
33
+ });
34
+ it('rewrites regardless of the role name in the username', () => {
35
+ // The port is the only thing that distinguishes transaction mode from
36
+ // session mode — the pooler uses the same credentials for both, so a
37
+ // non-standard role is still safe to carry across.
38
+ const odd = 'postgresql://service_role.abc123:pw@aws-0-us-east-1.pooler.supabase.com:6543/postgres';
39
+ expect(deriveSessionPoolerUrl(odd)).toBe('postgresql://service_role.abc123:pw@aws-0-us-east-1.pooler.supabase.com:5432/postgres');
40
+ });
41
+ it('returns undefined for undefined input', () => {
42
+ expect(deriveSessionPoolerUrl(undefined)).toBeUndefined();
43
+ });
44
+ it('returns input unchanged for malformed URLs', () => {
45
+ expect(deriveSessionPoolerUrl('not-a-url')).toBe('not-a-url');
46
+ });
47
+ });
48
+ //# sourceMappingURL=listen.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.test.js","sourceRoot":"","sources":["../../../src/commands/evals/listen.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,GAAG,GACP,qGAAqG,CAAC;QACxG,MAAM,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CACtC,qGAAqG,CACtG,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,GAAG,GACP,sFAAsF,CAAC;QACzF,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAa,CAAC,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,OAAO,GACX,mFAAmF,CAAC;QACtF,MAAM,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,yEAAyE;QACzE,qEAAqE;QACrE,iEAAiE;QACjE,MAAM,MAAM,GAAG,8DAA8D,CAAC;QAC9E,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,KAAK,GAAG,0CAA0C,CAAC;QACzD,MAAM,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,sEAAsE;QACtE,qEAAqE;QACrE,mDAAmD;QACnD,MAAM,GAAG,GACP,uFAAuF,CAAC;QAC1F,MAAM,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CACtC,uFAAuF,CACxF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -9,7 +9,7 @@ export default class GatewayEnsure extends BaseCommand {
9
9
  return; // Already running, silent success
10
10
  }
11
11
  try {
12
- spawnGateway();
12
+ spawnGateway(this.config.version);
13
13
  }
14
14
  catch (err) {
15
15
  // Don't crash the hook — just warn