@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.
- package/README.md +66 -0
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +15 -0
- package/dist/base-command.js.map +1 -1
- package/dist/commands/auth/login.d.ts.map +1 -1
- package/dist/commands/auth/login.js +7 -0
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/crons/list.d.ts +7 -0
- package/dist/commands/crons/list.d.ts.map +1 -0
- package/dist/commands/crons/list.js +24 -0
- package/dist/commands/crons/list.js.map +1 -0
- package/dist/commands/crons/run.d.ts +13 -0
- package/dist/commands/crons/run.d.ts.map +1 -0
- package/dist/commands/crons/run.js +48 -0
- package/dist/commands/crons/run.js.map +1 -0
- package/dist/commands/evals/listen.d.ts +28 -2
- package/dist/commands/evals/listen.d.ts.map +1 -1
- package/dist/commands/evals/listen.js +159 -25
- package/dist/commands/evals/listen.js.map +1 -1
- package/dist/commands/evals/listen.test.d.ts +2 -0
- package/dist/commands/evals/listen.test.d.ts.map +1 -0
- package/dist/commands/evals/listen.test.js +48 -0
- package/dist/commands/evals/listen.test.js.map +1 -0
- package/dist/commands/gateway/ensure.js +1 -1
- package/dist/commands/gateway/ensure.js.map +1 -1
- package/dist/commands/hooks/install.d.ts.map +1 -1
- package/dist/commands/hooks/install.js +40 -3
- package/dist/commands/hooks/install.js.map +1 -1
- package/dist/commands/hooks/run.js +1 -1
- package/dist/commands/hooks/run.js.map +1 -1
- package/dist/commands/hooks/uninstall.d.ts.map +1 -1
- package/dist/commands/hooks/uninstall.js +9 -1
- package/dist/commands/hooks/uninstall.js.map +1 -1
- package/dist/commands/org/current.d.ts +7 -0
- package/dist/commands/org/current.d.ts.map +1 -0
- package/dist/commands/org/current.js +70 -0
- package/dist/commands/org/current.js.map +1 -0
- package/dist/commands/org/list.d.ts +7 -0
- package/dist/commands/org/list.d.ts.map +1 -0
- package/dist/commands/org/list.js +37 -0
- package/dist/commands/org/list.js.map +1 -0
- package/dist/commands/org/switch.d.ts +13 -0
- package/dist/commands/org/switch.d.ts.map +1 -0
- package/dist/commands/org/switch.js +52 -0
- package/dist/commands/org/switch.js.map +1 -0
- package/dist/commands/proxy/restart.d.ts +11 -0
- package/dist/commands/proxy/restart.d.ts.map +1 -0
- package/dist/commands/proxy/restart.js +36 -0
- package/dist/commands/proxy/restart.js.map +1 -0
- package/dist/commands/proxy/start.d.ts.map +1 -1
- package/dist/commands/proxy/start.js +4 -0
- package/dist/commands/proxy/start.js.map +1 -1
- package/dist/commands/service/install.d.ts +16 -0
- package/dist/commands/service/install.d.ts.map +1 -1
- package/dist/commands/service/install.js +145 -6
- package/dist/commands/service/install.js.map +1 -1
- package/dist/commands/service/uninstall.d.ts.map +1 -1
- package/dist/commands/service/uninstall.js +28 -0
- package/dist/commands/service/uninstall.js.map +1 -1
- package/dist/commands/sessions/start.d.ts.map +1 -1
- package/dist/commands/sessions/start.js +5 -0
- package/dist/commands/sessions/start.js.map +1 -1
- package/dist/commands/signals/report.d.ts +10 -0
- package/dist/commands/signals/report.d.ts.map +1 -0
- package/dist/commands/signals/report.js +35 -0
- package/dist/commands/signals/report.js.map +1 -0
- package/dist/commands/signals/report.test.d.ts +2 -0
- package/dist/commands/signals/report.test.d.ts.map +1 -0
- package/dist/commands/signals/report.test.js +65 -0
- package/dist/commands/signals/report.test.js.map +1 -0
- package/dist/commands/skills/export-approved.d.ts +10 -0
- package/dist/commands/skills/export-approved.d.ts.map +1 -0
- package/dist/commands/skills/export-approved.js +31 -0
- package/dist/commands/skills/export-approved.js.map +1 -0
- package/dist/commands/skills/publish.d.ts +17 -0
- package/dist/commands/skills/publish.d.ts.map +1 -0
- package/dist/commands/skills/publish.js +80 -0
- package/dist/commands/skills/publish.js.map +1 -0
- package/dist/commands/skills/update.d.ts +3 -0
- package/dist/commands/skills/update.d.ts.map +1 -1
- package/dist/commands/skills/update.js +17 -6
- package/dist/commands/skills/update.js.map +1 -1
- package/dist/commands/suggestions/accept.d.ts +16 -0
- package/dist/commands/suggestions/accept.d.ts.map +1 -0
- package/dist/commands/suggestions/accept.js +61 -0
- package/dist/commands/suggestions/accept.js.map +1 -0
- package/dist/commands/suggestions/accept.test.d.ts +2 -0
- package/dist/commands/suggestions/accept.test.d.ts.map +1 -0
- package/dist/commands/suggestions/accept.test.js +52 -0
- package/dist/commands/suggestions/accept.test.js.map +1 -0
- package/dist/commands/suggestions/dismiss.d.ts +10 -0
- package/dist/commands/suggestions/dismiss.d.ts.map +1 -0
- package/dist/commands/suggestions/dismiss.js +26 -0
- package/dist/commands/suggestions/dismiss.js.map +1 -0
- package/dist/commands/suggestions/list.d.ts +10 -0
- package/dist/commands/suggestions/list.d.ts.map +1 -0
- package/dist/commands/suggestions/list.js +40 -0
- package/dist/commands/suggestions/list.js.map +1 -0
- package/dist/commands/suggestions/refresh.d.ts +7 -0
- package/dist/commands/suggestions/refresh.d.ts.map +1 -0
- package/dist/commands/suggestions/refresh.js +11 -0
- package/dist/commands/suggestions/refresh.js.map +1 -0
- package/dist/dashboard/Dashboard.d.ts.map +1 -1
- package/dist/dashboard/Dashboard.js +7 -1
- package/dist/dashboard/Dashboard.js.map +1 -1
- package/dist/lib/active-org-refresh.d.ts +22 -0
- package/dist/lib/active-org-refresh.d.ts.map +1 -0
- package/dist/lib/active-org-refresh.js +108 -0
- package/dist/lib/active-org-refresh.js.map +1 -0
- package/dist/lib/active-org-refresh.test.d.ts +2 -0
- package/dist/lib/active-org-refresh.test.d.ts.map +1 -0
- package/dist/lib/active-org-refresh.test.js +190 -0
- package/dist/lib/active-org-refresh.test.js.map +1 -0
- package/dist/lib/active-org.d.ts +43 -0
- package/dist/lib/active-org.d.ts.map +1 -0
- package/dist/lib/active-org.js +96 -0
- package/dist/lib/active-org.js.map +1 -0
- package/dist/lib/active-org.test.d.ts +2 -0
- package/dist/lib/active-org.test.d.ts.map +1 -0
- package/dist/lib/active-org.test.js +147 -0
- package/dist/lib/active-org.test.js.map +1 -0
- package/dist/lib/cron-registry.d.ts +47 -0
- package/dist/lib/cron-registry.d.ts.map +1 -0
- package/dist/lib/cron-registry.js +102 -0
- package/dist/lib/cron-registry.js.map +1 -0
- package/dist/lib/cron-registry.test.d.ts +2 -0
- package/dist/lib/cron-registry.test.d.ts.map +1 -0
- package/dist/lib/cron-registry.test.js +136 -0
- package/dist/lib/cron-registry.test.js.map +1 -0
- package/dist/lib/cron-runner.d.ts +16 -0
- package/dist/lib/cron-runner.d.ts.map +1 -0
- package/dist/lib/cron-runner.js +68 -0
- package/dist/lib/cron-runner.js.map +1 -0
- package/dist/lib/detect-languages.d.ts +7 -0
- package/dist/lib/detect-languages.d.ts.map +1 -0
- package/dist/lib/detect-languages.js +113 -0
- package/dist/lib/detect-languages.js.map +1 -0
- package/dist/lib/detect-languages.test.d.ts +2 -0
- package/dist/lib/detect-languages.test.d.ts.map +1 -0
- package/dist/lib/detect-languages.test.js +29 -0
- package/dist/lib/detect-languages.test.js.map +1 -0
- package/dist/lib/eval-executor.d.ts.map +1 -1
- package/dist/lib/eval-executor.js +5 -2
- package/dist/lib/eval-executor.js.map +1 -1
- package/dist/lib/gateway-entry.js +4 -1
- package/dist/lib/gateway-entry.js.map +1 -1
- package/dist/lib/gateway-lifecycle.d.ts +1 -1
- package/dist/lib/gateway-lifecycle.d.ts.map +1 -1
- package/dist/lib/gateway-lifecycle.js +13 -2
- package/dist/lib/gateway-lifecycle.js.map +1 -1
- package/dist/lib/gateway.d.ts +4 -5
- package/dist/lib/gateway.d.ts.map +1 -1
- package/dist/lib/gateway.js +68 -2
- package/dist/lib/gateway.js.map +1 -1
- package/dist/lib/graphile-worker-runner.d.ts +124 -0
- package/dist/lib/graphile-worker-runner.d.ts.map +1 -0
- package/dist/lib/graphile-worker-runner.js +530 -0
- package/dist/lib/graphile-worker-runner.js.map +1 -0
- package/dist/lib/graphile-worker-runner.test.d.ts +2 -0
- package/dist/lib/graphile-worker-runner.test.d.ts.map +1 -0
- package/dist/lib/graphile-worker-runner.test.js +153 -0
- package/dist/lib/graphile-worker-runner.test.js.map +1 -0
- package/dist/lib/handlers/cron-close-stale-sessions.d.ts +3 -0
- package/dist/lib/handlers/cron-close-stale-sessions.d.ts.map +1 -0
- package/dist/lib/handlers/cron-close-stale-sessions.js +8 -0
- package/dist/lib/handlers/cron-close-stale-sessions.js.map +1 -0
- package/dist/lib/handlers/cron-data-integrity-audit.d.ts +3 -0
- package/dist/lib/handlers/cron-data-integrity-audit.d.ts.map +1 -0
- package/dist/lib/handlers/cron-data-integrity-audit.js +16 -0
- package/dist/lib/handlers/cron-data-integrity-audit.js.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.d.ts +3 -0
- package/dist/lib/handlers/cron-refresh-skills.d.ts.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.js +91 -0
- package/dist/lib/handlers/cron-refresh-skills.js.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.test.d.ts +2 -0
- package/dist/lib/handlers/cron-refresh-skills.test.d.ts.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.test.js +114 -0
- package/dist/lib/handlers/cron-refresh-skills.test.js.map +1 -0
- package/dist/lib/handlers/cron-regenerate-insights.d.ts +3 -0
- package/dist/lib/handlers/cron-regenerate-insights.d.ts.map +1 -0
- package/dist/lib/handlers/cron-regenerate-insights.js +6 -0
- package/dist/lib/handlers/cron-regenerate-insights.js.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.d.ts +3 -0
- package/dist/lib/handlers/cron-suggest-instructions.d.ts.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.js +159 -0
- package/dist/lib/handlers/cron-suggest-instructions.js.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.d.ts +2 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.d.ts.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.js +80 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.js.map +1 -0
- package/dist/lib/handlers/process-conversation.d.ts +35 -0
- package/dist/lib/handlers/process-conversation.d.ts.map +1 -0
- package/dist/lib/handlers/process-conversation.js +35 -0
- package/dist/lib/handlers/process-conversation.js.map +1 -0
- package/dist/lib/handlers/process-eval.d.ts +32 -0
- package/dist/lib/handlers/process-eval.d.ts.map +1 -0
- package/dist/lib/handlers/process-eval.js +207 -0
- package/dist/lib/handlers/process-eval.js.map +1 -0
- package/dist/lib/handlers/process-insights.d.ts +39 -0
- package/dist/lib/handlers/process-insights.d.ts.map +1 -0
- package/dist/lib/handlers/process-insights.js +300 -0
- package/dist/lib/handlers/process-insights.js.map +1 -0
- package/dist/lib/handlers/process-launch.d.ts +29 -0
- package/dist/lib/handlers/process-launch.d.ts.map +1 -0
- package/dist/lib/handlers/process-launch.js +41 -0
- package/dist/lib/handlers/process-launch.js.map +1 -0
- package/dist/lib/handlers/process-session-goal.d.ts +29 -0
- package/dist/lib/handlers/process-session-goal.d.ts.map +1 -0
- package/dist/lib/handlers/process-session-goal.js +74 -0
- package/dist/lib/handlers/process-session-goal.js.map +1 -0
- package/dist/lib/handlers/process-session-goal.test.d.ts +2 -0
- package/dist/lib/handlers/process-session-goal.test.d.ts.map +1 -0
- package/dist/lib/handlers/process-session-goal.test.js +134 -0
- package/dist/lib/handlers/process-session-goal.test.js.map +1 -0
- package/dist/lib/hooks-config.d.ts +28 -4
- package/dist/lib/hooks-config.d.ts.map +1 -1
- package/dist/lib/hooks-config.js +138 -10
- package/dist/lib/hooks-config.js.map +1 -1
- package/dist/lib/llm-client.d.ts.map +1 -1
- package/dist/lib/llm-client.js +149 -16
- package/dist/lib/llm-client.js.map +1 -1
- package/dist/lib/mirror-writer-db.d.ts +12 -0
- package/dist/lib/mirror-writer-db.d.ts.map +1 -0
- package/dist/lib/mirror-writer-db.js +51 -0
- package/dist/lib/mirror-writer-db.js.map +1 -0
- package/dist/lib/mirror-writer.d.ts +35 -0
- package/dist/lib/mirror-writer.d.ts.map +1 -0
- package/dist/lib/mirror-writer.js +27 -0
- package/dist/lib/mirror-writer.js.map +1 -0
- package/dist/lib/mirror-writer.test.d.ts +2 -0
- package/dist/lib/mirror-writer.test.d.ts.map +1 -0
- package/dist/lib/mirror-writer.test.js +47 -0
- package/dist/lib/mirror-writer.test.js.map +1 -0
- package/dist/lib/permanent-job-error.d.ts +17 -0
- package/dist/lib/permanent-job-error.d.ts.map +1 -0
- package/dist/lib/permanent-job-error.js +23 -0
- package/dist/lib/permanent-job-error.js.map +1 -0
- package/dist/lib/permanent-job-error.test.d.ts +2 -0
- package/dist/lib/permanent-job-error.test.d.ts.map +1 -0
- package/dist/lib/permanent-job-error.test.js +29 -0
- package/dist/lib/permanent-job-error.test.js.map +1 -0
- package/dist/lib/version-check.d.ts +11 -0
- package/dist/lib/version-check.d.ts.map +1 -0
- package/dist/lib/version-check.js +106 -0
- package/dist/lib/version-check.js.map +1 -0
- package/oclif.manifest.json +611 -81
- package/package.json +21 -5
- package/dist/commands/insights/sync.d.ts +0 -17
- package/dist/commands/insights/sync.d.ts.map +0 -1
- package/dist/commands/insights/sync.js +0 -477
- package/dist/commands/insights/sync.js.map +0 -1
- package/dist/lib/eval-worker.d.ts +0 -12
- package/dist/lib/eval-worker.d.ts.map +0 -1
- package/dist/lib/eval-worker.js +0 -395
- package/dist/lib/eval-worker.js.map +0 -1
- package/dist/lib/insights-worker.d.ts +0 -10
- package/dist/lib/insights-worker.d.ts.map +0 -1
- package/dist/lib/insights-worker.js +0 -329
- 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;
|
|
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"}
|
package/dist/base-command.js
CHANGED
|
@@ -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) {
|
package/dist/base-command.js.map
CHANGED
|
@@ -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;
|
|
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;
|
|
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;
|
|
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 @@
|
|
|
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":"
|
|
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
|
|
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:
|
|
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:
|
|
71
|
+
required: false,
|
|
72
|
+
hidden: true,
|
|
20
73
|
}),
|
|
21
74
|
};
|
|
22
75
|
async run() {
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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;
|
|
1
|
+
{"version":3,"file":"listen.js","sourceRoot":"","sources":["../../../src/commands/evals/listen.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|