@bytesbrains/cruise 0.12.1 → 0.13.0

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 (36) hide show
  1. package/dist/{budget-FIRNSN67.mjs → budget-IIC4NJUU.mjs} +6 -5
  2. package/dist/{chunk-FGLMW5IY.mjs → chunk-2Z62Z53P.mjs} +2 -2
  3. package/dist/chunk-5AFYJTLS.mjs +62 -0
  4. package/dist/{chunk-WS6NFRIU.mjs → chunk-5LRJO4Z3.mjs} +1 -1
  5. package/dist/chunk-7ZUKADIS.mjs +131 -0
  6. package/dist/{chunk-LALZUKEC.mjs → chunk-A4DIVNYV.mjs} +1 -0
  7. package/dist/chunk-CDBZLSL7.mjs +65 -0
  8. package/dist/chunk-D3K25SBO.mjs +69 -0
  9. package/dist/chunk-NR4K7AUA.mjs +17 -0
  10. package/dist/{chunk-GUZNI3NS.mjs → chunk-PNJVYZ3Y.mjs} +3 -56
  11. package/dist/chunk-TJR2U73N.mjs +12 -0
  12. package/dist/{chunk-THEXUHQC.mjs → chunk-UBM65ZNR.mjs} +1 -0
  13. package/dist/{chunk-JZPUTXM7.mjs → chunk-Y2I46CTD.mjs} +113 -1
  14. package/dist/{compress-3DWG35BY.mjs → compress-QAEEBNVF.mjs} +1 -2
  15. package/dist/{conformance-NPFDYCQL.mjs → conformance-H2DRKMVX.mjs} +4 -5
  16. package/dist/cruise.mjs +132 -32
  17. package/dist/demo-seed-PRVDIWCM.mjs +59 -0
  18. package/dist/doctor-LVMXEX35.mjs +802 -0
  19. package/dist/{fallback-P5KK55M4.mjs → fallback-HRVI5XCK.mjs} +7 -6
  20. package/dist/journey-JXQ3VYCT.mjs +136 -0
  21. package/dist/{keys-FWPQPMTW.mjs → keys-S5QOOVMS.mjs} +3 -4
  22. package/dist/{ledger-XBGWXY6C.mjs → ledger-DWOMA7WD.mjs} +2 -3
  23. package/dist/{member-ZBV4YG2M.mjs → member-R7HUJK45.mjs} +1 -2
  24. package/dist/metrics-LCO4YNPW.mjs +144 -0
  25. package/dist/{report-VRYJ3W5L.mjs → report-CLNJPK6Z.mjs} +2 -3
  26. package/dist/secrets-check-6PWXXJM7.mjs +65 -0
  27. package/dist/{service-key-N2RZ7FMZ.mjs → service-key-E6SGTLWA.mjs} +1 -2
  28. package/dist/{smoke-ZNYX6RO5.mjs → smoke-DFNUPTZ7.mjs} +1 -2
  29. package/dist/staging-seed-647DRUW5.mjs +41 -0
  30. package/dist/{sweep-SZX7OIDK.mjs → sweep-5O32ZU4T.mjs} +2 -3
  31. package/dist/{tenant-4YDGI23Q.mjs → tenant-DRN6BAOI.mjs} +13 -9
  32. package/dist/{tier-4L3SPLFP.mjs → tier-JSY5OU65.mjs} +1 -2
  33. package/package.json +2 -2
  34. package/dist/chunk-7NQHBXVZ.mjs +0 -104
  35. package/dist/chunk-ICJVJSTC.mjs +0 -42
  36. package/dist/chunk-N3YKWKOA.mjs +0 -9
package/dist/cruise.mjs CHANGED
@@ -6,53 +6,72 @@ import {
6
6
  } from "./chunk-2FB2T3UI.mjs";
7
7
  import {
8
8
  provisionTenant
9
- } from "./chunk-GUZNI3NS.mjs";
10
- import "./chunk-N3YKWKOA.mjs";
9
+ } from "./chunk-PNJVYZ3Y.mjs";
10
+ import "./chunk-5AFYJTLS.mjs";
11
+ import "./chunk-D3K25SBO.mjs";
12
+ import "./chunk-CDBZLSL7.mjs";
11
13
  import "./chunk-2NVDAK5J.mjs";
12
- import "./chunk-FGLMW5IY.mjs";
14
+ import "./chunk-2Z62Z53P.mjs";
13
15
  import "./chunk-4447IUTF.mjs";
14
16
  import "./chunk-XOH3XKM3.mjs";
15
17
  import "./chunk-42ERHAJD.mjs";
16
18
  import "./chunk-BNP63KB2.mjs";
17
- import "./chunk-ICJVJSTC.mjs";
18
19
  import "./chunk-UNXM6FSM.mjs";
19
20
  import "./chunk-SYTYOTO4.mjs";
20
21
  import "./chunk-O7TNRKSA.mjs";
22
+ import "./chunk-O7TNRKSA.mjs";
23
+ import "./chunk-7ZUKADIS.mjs";
21
24
  import "./chunk-T3ICD43U.mjs";
22
25
  import "./chunk-RT4R2W6H.mjs";
23
- import "./chunk-THEXUHQC.mjs";
24
- import "./chunk-LALZUKEC.mjs";
26
+ import "./chunk-NR4K7AUA.mjs";
27
+ import "./chunk-UBM65ZNR.mjs";
28
+ import "./chunk-A4DIVNYV.mjs";
25
29
  import "./chunk-5TVD3ZAI.mjs";
26
30
  import {
27
31
  HttpD1,
28
- LocalD1
29
- } from "./chunk-JZPUTXM7.mjs";
32
+ LocalD1,
33
+ connect,
34
+ loadWranglerConfig,
35
+ resolveScope,
36
+ wranglerConfigPath
37
+ } from "./chunk-Y2I46CTD.mjs";
38
+
39
+ // packages/cli/src/main.ts
40
+ import path8 from "node:path";
30
41
 
31
42
  // selfhost/commands.ts
32
43
  var COMMANDS = {
33
- "key:issue": { load: () => import("./keys-FWPQPMTW.mjs"), args: ["issue"] },
34
- "key:revoke": { load: () => import("./keys-FWPQPMTW.mjs"), args: ["revoke"] },
35
- "key:list": { load: () => import("./keys-FWPQPMTW.mjs"), args: ["list"] },
36
- "budget:show": { load: () => import("./budget-FIRNSN67.mjs"), args: ["show"] },
37
- "budget:set": { load: () => import("./budget-FIRNSN67.mjs"), args: ["set"] },
38
- "budget:align": { load: () => import("./budget-FIRNSN67.mjs"), args: ["align"] },
39
- "tenant:provision": { load: () => import("./tenant-4YDGI23Q.mjs"), args: ["provision"] },
40
- "tenant:list": { load: () => import("./tenant-4YDGI23Q.mjs"), args: ["list"] },
41
- "member:grant": { load: () => import("./member-ZBV4YG2M.mjs"), args: ["grant"] },
42
- "member:revoke": { load: () => import("./member-ZBV4YG2M.mjs"), args: ["revoke"] },
43
- "member:list": { load: () => import("./member-ZBV4YG2M.mjs"), args: ["list"] },
44
- "service-key:issue": { load: () => import("./service-key-N2RZ7FMZ.mjs"), args: ["issue"] },
45
- "fallback:show": { load: () => import("./fallback-P5KK55M4.mjs"), args: ["show"] },
46
- "fallback:set": { load: () => import("./fallback-P5KK55M4.mjs"), args: ["set"] },
47
- "tier:show": { load: () => import("./tier-4L3SPLFP.mjs"), args: ["show"] },
48
- "tier:set": { load: () => import("./tier-4L3SPLFP.mjs"), args: ["set"] },
49
- "compress:show": { load: () => import("./compress-3DWG35BY.mjs"), args: ["show"] },
50
- "compress:set": { load: () => import("./compress-3DWG35BY.mjs"), args: ["set"] },
51
- ledger: { load: () => import("./ledger-XBGWXY6C.mjs"), args: [] },
52
- report: { load: () => import("./report-VRYJ3W5L.mjs"), args: [] },
53
- conformance: { load: () => import("./conformance-NPFDYCQL.mjs"), args: [] },
54
- sweep: { load: () => import("./sweep-SZX7OIDK.mjs"), args: [] },
55
- smoke: { load: () => import("./smoke-ZNYX6RO5.mjs"), args: [] }
44
+ "key:issue": { load: () => import("./keys-S5QOOVMS.mjs"), args: ["issue"] },
45
+ "key:revoke": { load: () => import("./keys-S5QOOVMS.mjs"), args: ["revoke"] },
46
+ "key:list": { load: () => import("./keys-S5QOOVMS.mjs"), args: ["list"] },
47
+ "budget:show": { load: () => import("./budget-IIC4NJUU.mjs"), args: ["show"] },
48
+ "budget:set": { load: () => import("./budget-IIC4NJUU.mjs"), args: ["set"] },
49
+ "budget:align": { load: () => import("./budget-IIC4NJUU.mjs"), args: ["align"] },
50
+ "tenant:provision": { load: () => import("./tenant-DRN6BAOI.mjs"), args: ["provision"] },
51
+ "tenant:list": { load: () => import("./tenant-DRN6BAOI.mjs"), args: ["list"] },
52
+ "member:grant": { load: () => import("./member-R7HUJK45.mjs"), args: ["grant"] },
53
+ "member:revoke": { load: () => import("./member-R7HUJK45.mjs"), args: ["revoke"] },
54
+ "member:list": { load: () => import("./member-R7HUJK45.mjs"), args: ["list"] },
55
+ "service-key:issue": { load: () => import("./service-key-E6SGTLWA.mjs"), args: ["issue"] },
56
+ "fallback:show": { load: () => import("./fallback-HRVI5XCK.mjs"), args: ["show"] },
57
+ "fallback:set": { load: () => import("./fallback-HRVI5XCK.mjs"), args: ["set"] },
58
+ "tier:show": { load: () => import("./tier-JSY5OU65.mjs"), args: ["show"] },
59
+ "tier:set": { load: () => import("./tier-JSY5OU65.mjs"), args: ["set"] },
60
+ "compress:show": { load: () => import("./compress-QAEEBNVF.mjs"), args: ["show"] },
61
+ "compress:set": { load: () => import("./compress-QAEEBNVF.mjs"), args: ["set"] },
62
+ ledger: { load: () => import("./ledger-DWOMA7WD.mjs"), args: [] },
63
+ report: { load: () => import("./report-CLNJPK6Z.mjs"), args: [] },
64
+ conformance: { load: () => import("./conformance-H2DRKMVX.mjs"), args: [] },
65
+ sweep: { load: () => import("./sweep-5O32ZU4T.mjs"), args: [] },
66
+ smoke: { load: () => import("./smoke-DFNUPTZ7.mjs"), args: [] }
67
+ };
68
+ var CLOUDFLARE_COMMANDS = {
69
+ doctor: { load: () => import("./doctor-LVMXEX35.mjs"), args: [] },
70
+ metrics: { load: () => import("./metrics-LCO4YNPW.mjs"), args: [] },
71
+ journey: { load: () => import("./journey-JXQ3VYCT.mjs"), args: [] },
72
+ "secrets:check": { load: () => import("./secrets-check-6PWXXJM7.mjs"), args: [] },
73
+ "demo:seed": { load: () => import("./demo-seed-PRVDIWCM.mjs"), args: [] },
74
+ "staging:seed": { load: () => import("./staging-seed-647DRUW5.mjs"), args: [] }
56
75
  };
57
76
 
58
77
  // selfhost/measure.ts
@@ -621,6 +640,58 @@ async function inContainer(argv) {
621
640
  process.exitCode = code;
622
641
  }
623
642
 
643
+ // packages/cli/src/release.ts
644
+ import { spawn as spawn4 } from "node:child_process";
645
+ import { readdirSync } from "node:fs";
646
+ import path7 from "node:path";
647
+ function wrangler2(args) {
648
+ return new Promise((resolve, reject) => {
649
+ const child = spawn4("npx", ["--yes", `wrangler@${WRANGLER_VERSION}`, ...args, "-c", wranglerConfigPath()], {
650
+ stdio: "inherit",
651
+ // CI=true answers wrangler's confirmation prompt on a remote migration apply.
652
+ env: { ...process.env, CI: "true", WRANGLER_SEND_METRICS: "false" }
653
+ });
654
+ child.on("error", reject);
655
+ child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`wrangler ${args.join(" ")} exited ${code}`)));
656
+ });
657
+ }
658
+ function database(environment) {
659
+ const scope = resolveScope(loadWranglerConfig(), environment);
660
+ return scope.d1_databases?.[0] ?? null;
661
+ }
662
+ function shippedMigrations(entry, configPath) {
663
+ const dir = path7.resolve(path7.dirname(configPath), entry.migrations_dir ?? "migrations");
664
+ return readdirSync(dir).filter((file) => file.endsWith(".sql")).sort();
665
+ }
666
+ function pendingMigrations(shipped, applied) {
667
+ const done = new Set(applied);
668
+ return shipped.filter((name) => !done.has(name));
669
+ }
670
+ async function migrate(environment) {
671
+ const entry = database(environment);
672
+ if (!entry?.database_name) throw new Error(`--env ${environment} has no D1 database in ${wranglerConfigPath()}`);
673
+ await wrangler2(["d1", "migrations", "apply", entry.database_name, "--remote", "--env", environment]);
674
+ }
675
+ async function deploy2(environment) {
676
+ const entry = database(environment);
677
+ if (entry) {
678
+ const token2 = process.env.CLOUDFLARE_API_TOKEN;
679
+ if (!token2) throw new Error("Set CLOUDFLARE_API_TOKEN: deploy checks the database's migrations first.");
680
+ const accountId = loadWranglerConfig().account_id;
681
+ if (!accountId || !entry.database_id) throw new Error(`--env ${environment} needs account_id and database_id`);
682
+ const db = new HttpD1(accountId, entry.database_id, token2);
683
+ const table = entry.migrations_table ?? "d1_migrations";
684
+ const { results } = await db.query(`SELECT name FROM ${table}`, []);
685
+ const pending = pendingMigrations(shippedMigrations(entry, wranglerConfigPath()), results.map((r) => r.name));
686
+ if (pending.length > 0) {
687
+ throw new Error(
688
+ `${environment} has not applied ${pending.length} migration(s) this Worker reads: ${pending.join(", ")}. Run \`cruise migrate --config ${path7.basename(wranglerConfigPath())} --env ${environment}\` first.`
689
+ );
690
+ }
691
+ }
692
+ await wrangler2(["deploy", "--env", environment]);
693
+ }
694
+
624
695
  // packages/cli/src/main.ts
625
696
  var HELP = `cruise ${VERSION} \u2014 one OpenAI-compatible endpoint in front of every model provider
626
697
 
@@ -636,6 +707,12 @@ Your own Cloudflare account (needs CLOUDFLARE_API_TOKEN):
636
707
  cruise cloudflare measure [--name cruise]
637
708
  cruise cloudflare <admin command> [--name cruise]
638
709
 
710
+ A deployment described by your own wrangler.jsonc (needs CLOUDFLARE_API_TOKEN):
711
+ cruise migrate --config wrangler.jsonc --env <env> apply the pinned Worker's migrations
712
+ cruise deploy --config wrangler.jsonc --env <env> deploy it; refused while a migration is pending
713
+ cruise measure --config wrangler.jsonc --env <env> --base https://\u2026
714
+ cruise <command> --config wrangler.jsonc --env <env> any admin command, and ${Object.keys(CLOUDFLARE_COMMANDS).join(", ")}
715
+
639
716
  Admin commands: ${Object.keys(COMMANDS).join(", ")}
640
717
  Manual: https://gitlab.com/bytesbrains-oss/cruise/-/blob/main/docs/self-hosting.md`;
641
718
  function takeFlag(argv, name) {
@@ -644,7 +721,7 @@ function takeFlag(argv, name) {
644
721
  return { value: argv[i + 1], rest: [...argv.slice(0, i), ...argv.slice(i + 2)] };
645
722
  }
646
723
  async function runScript(name, rest) {
647
- const command = COMMANDS[name];
724
+ const command = COMMANDS[name] ?? CLOUDFLARE_COMMANDS[name];
648
725
  if (!command) throw new Error(`unknown command: ${name}
649
726
 
650
727
  ${HELP}`);
@@ -667,7 +744,30 @@ async function cloudflare(argv) {
667
744
  }
668
745
  return runScript(sub, ["--env", "cloudflare", ...args]);
669
746
  }
747
+ function flagValue(argv, name) {
748
+ const i = argv.indexOf(`--${name}`);
749
+ return i >= 0 ? argv[i + 1] : void 0;
750
+ }
751
+ async function configured(argv) {
752
+ const [name, ...rest] = argv;
753
+ const environment = flagValue(rest, "env");
754
+ if (!name || !environment) throw new Error(`--config needs a command and --env <environment>
755
+
756
+ ${HELP}`);
757
+ if (name === "migrate") return migrate(environment);
758
+ if (name === "deploy") return deploy2(environment);
759
+ if (name === "measure") {
760
+ const base = flagValue(rest, "base");
761
+ if (!base) throw new Error("measure --config needs --base https://\u2026 : the deployment's own URL");
762
+ const others = rest.filter((arg, i) => !(arg === "--env" || rest[i - 1] === "--env"));
763
+ return measure(others, { db: connect(environment), environment, base });
764
+ }
765
+ return runScript(name, rest);
766
+ }
670
767
  async function main(argv) {
768
+ const { value: config, rest: withoutConfig } = takeFlag(argv, "config");
769
+ if (config) process.env.CRUISE_WRANGLER_CONFIG = path8.resolve(config);
770
+ if (process.env.CRUISE_WRANGLER_CONFIG) return configured(withoutConfig);
671
771
  const [name, ...rest] = argv;
672
772
  if (!name || name === "help" || name === "--help" || name === "-h") {
673
773
  console.log(HELP);
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ // Built by packages/cli/build.ts.
3
+ import {
4
+ DEMO_EMULATOR_BASE,
5
+ DEMO_ENVIRONMENT,
6
+ seedDemoCatalogue
7
+ } from "./chunk-D3K25SBO.mjs";
8
+ import {
9
+ productionRoutable,
10
+ requireSource
11
+ } from "./chunk-CDBZLSL7.mjs";
12
+ import "./chunk-A4DIVNYV.mjs";
13
+ import {
14
+ connect,
15
+ environmentOf,
16
+ parseArgs,
17
+ run
18
+ } from "./chunk-Y2I46CTD.mjs";
19
+
20
+ // scripts/demo-seed.ts
21
+ var SOURCE = "production";
22
+ run(async () => {
23
+ const args = parseArgs(process.argv.slice(2));
24
+ const environment = environmentOf(args);
25
+ if (environment !== DEMO_ENVIRONMENT) {
26
+ throw new Error(
27
+ `demo:seed refuses --env ${environment}. It zeroes every price and repoints every upstream at the emulator, which is only ever right on the demo. Pass --env ${DEMO_ENVIRONMENT}.`
28
+ );
29
+ }
30
+ const emulator = typeof args.emulator === "string" ? args.emulator : DEMO_EMULATOR_BASE;
31
+ const measured = requireSource(await productionRoutable(connect(SOURCE)), SOURCE);
32
+ console.log(`Cruise \u2014 demo catalogue, from ${SOURCE}
33
+ `);
34
+ console.log(` emulator ${emulator}`);
35
+ console.log(` routable ${measured.length} model(s) in ${SOURCE}`);
36
+ if (args["dry-run"] === true) {
37
+ for (const m of measured) console.log(` ${m.modality.padEnd(10)} ${m.id}`);
38
+ console.log("\n --dry-run: nothing written.");
39
+ return;
40
+ }
41
+ const summary = await seedDemoCatalogue(connect(environment), measured, emulator);
42
+ console.log(`
43
+ upstreams ${summary.upstreams} repointed at the emulator`);
44
+ console.log(` prices ${summary.pricedRows} token row(s), ${summary.componentRows} component row(s) zeroed`);
45
+ console.log(` budgets ${summary.budgets} row(s) at reserve_micros = 0`);
46
+ console.log(` measured ${summary.routable.length} model(s) now routable on the demo`);
47
+ if (summary.absent.length > 0) {
48
+ console.log(`
49
+ ${summary.absent.length} model(s) ${SOURCE} serves are NOT IN THIS CATALOGUE \u2014 run migrate:demo:`);
50
+ for (const id of summary.absent) console.log(` ${id}`);
51
+ }
52
+ if (summary.unservable.length > 0) {
53
+ console.log(`
54
+ ${summary.unservable.length} model(s) left unmeasured \u2014 the emulator has no route for them:`);
55
+ for (const id of summary.unservable) console.log(` ${id}`);
56
+ }
57
+ console.log(`
58
+ Run \`npm run doctor -- --env ${DEMO_ENVIRONMENT}\` to check the mirror.`);
59
+ });