@codemarc/blt 1.6.5 → 1.8.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 (81) hide show
  1. package/README.md +192 -0
  2. package/dist/blt +4 -0
  3. package/dist/blt.d.ts.map +1 -1
  4. package/dist/blt.js.map +1 -1
  5. package/dist/commands/data/apply.d.ts +6 -0
  6. package/dist/commands/data/apply.d.ts.map +1 -0
  7. package/dist/commands/data/apply.js +363 -0
  8. package/dist/commands/data/apply.js.map +1 -0
  9. package/dist/commands/data/helpers.d.ts +15 -0
  10. package/dist/commands/data/helpers.d.ts.map +1 -0
  11. package/dist/commands/data/helpers.js +53 -0
  12. package/dist/commands/data/helpers.js.map +1 -0
  13. package/dist/commands/data/pull.d.ts +11 -0
  14. package/dist/commands/data/pull.d.ts.map +1 -0
  15. package/dist/commands/data/pull.js +101 -0
  16. package/dist/commands/data/pull.js.map +1 -0
  17. package/dist/commands/data/remove.d.ts +7 -0
  18. package/dist/commands/data/remove.d.ts.map +1 -0
  19. package/dist/commands/data/remove.js +29 -0
  20. package/dist/commands/data/remove.js.map +1 -0
  21. package/dist/commands/data.d.ts +3 -0
  22. package/dist/commands/data.d.ts.map +1 -0
  23. package/dist/commands/data.js +115 -0
  24. package/dist/commands/data.js.map +1 -0
  25. package/dist/commands/spin/dns.d.ts +7 -0
  26. package/dist/commands/spin/dns.d.ts.map +1 -0
  27. package/dist/commands/spin/dns.js +42 -0
  28. package/dist/commands/spin/dns.js.map +1 -0
  29. package/dist/commands/spin/down.d.ts +3 -0
  30. package/dist/commands/spin/down.d.ts.map +1 -0
  31. package/dist/commands/spin/down.js +17 -0
  32. package/dist/commands/spin/down.js.map +1 -0
  33. package/dist/commands/spin/helpers.d.ts +7 -0
  34. package/dist/commands/spin/helpers.d.ts.map +1 -0
  35. package/dist/commands/spin/helpers.js +38 -0
  36. package/dist/commands/spin/helpers.js.map +1 -0
  37. package/dist/commands/spin/list.d.ts +3 -0
  38. package/dist/commands/spin/list.d.ts.map +1 -0
  39. package/dist/commands/spin/list.js +20 -0
  40. package/dist/commands/spin/list.js.map +1 -0
  41. package/dist/commands/spin/setup.d.ts +3 -0
  42. package/dist/commands/spin/setup.d.ts.map +1 -0
  43. package/dist/commands/spin/setup.js +32 -0
  44. package/dist/commands/spin/setup.js.map +1 -0
  45. package/dist/commands/spin/ssh.d.ts +3 -0
  46. package/dist/commands/spin/ssh.d.ts.map +1 -0
  47. package/dist/commands/spin/ssh.js +14 -0
  48. package/dist/commands/spin/ssh.js.map +1 -0
  49. package/dist/commands/spin/status.d.ts +3 -0
  50. package/dist/commands/spin/status.d.ts.map +1 -0
  51. package/dist/commands/spin/status.js +21 -0
  52. package/dist/commands/spin/status.js.map +1 -0
  53. package/dist/commands/spin/up.d.ts +11 -0
  54. package/dist/commands/spin/up.d.ts.map +1 -0
  55. package/dist/commands/spin/up.js +34 -0
  56. package/dist/commands/spin/up.js.map +1 -0
  57. package/dist/commands/spin.d.ts +3 -0
  58. package/dist/commands/spin.d.ts.map +1 -0
  59. package/dist/commands/spin.js +166 -0
  60. package/dist/commands/spin.js.map +1 -0
  61. package/dist/lib/data-pull-extra-tables.d.ts +26 -0
  62. package/dist/lib/data-pull-extra-tables.d.ts.map +1 -0
  63. package/dist/lib/data-pull-extra-tables.js +212 -0
  64. package/dist/lib/data-pull-extra-tables.js.map +1 -0
  65. package/dist/lib/data-pull-manifest.d.ts +97 -0
  66. package/dist/lib/data-pull-manifest.d.ts.map +1 -0
  67. package/dist/lib/data-pull-manifest.js +603 -0
  68. package/dist/lib/data-pull-manifest.js.map +1 -0
  69. package/dist/lib/database-runner.d.ts +28 -2
  70. package/dist/lib/database-runner.d.ts.map +1 -1
  71. package/dist/lib/database-runner.js +112 -49
  72. package/dist/lib/database-runner.js.map +1 -1
  73. package/dist/lib/digitalocean.d.ts +60 -0
  74. package/dist/lib/digitalocean.d.ts.map +1 -0
  75. package/dist/lib/digitalocean.js +108 -0
  76. package/dist/lib/digitalocean.js.map +1 -0
  77. package/dist/lib/yaml-converter.d.ts +25 -1
  78. package/dist/lib/yaml-converter.d.ts.map +1 -1
  79. package/dist/lib/yaml-converter.js +98 -15
  80. package/dist/lib/yaml-converter.js.map +1 -1
  81. package/package.json +2 -1
@@ -0,0 +1,3 @@
1
+ import type { Logger } from "@caporal/core";
2
+ export declare function spinSsh(nameOrId: string, user: string, logger: Logger): Promise<void>;
3
+ //# sourceMappingURL=ssh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc3F"}
@@ -0,0 +1,14 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { getDroplet } from "../../lib/digitalocean";
3
+ import { getPublicIp } from "./helpers";
4
+ export async function spinSsh(nameOrId, user, logger) {
5
+ const droplet = await getDroplet(nameOrId);
6
+ const ip = getPublicIp(droplet);
7
+ if (!ip) {
8
+ throw new Error(`Droplet "${droplet.name}" has no public IP yet (status: ${droplet.status})`);
9
+ }
10
+ logger.info(`Connecting to ${user}@${ip} (${droplet.name})…`);
11
+ const result = spawnSync("ssh", ["-o", "StrictHostKeyChecking=accept-new", `${user}@${ip}`], { stdio: "inherit" });
12
+ process.exit(result.status ?? 0);
13
+ }
14
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../../src/commands/spin/ssh.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB,EAAE,IAAY,EAAE,MAAc;IAC3E,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,CAAC,IAAI,mCAAmC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/F,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,SAAS,CACvB,KAAK,EACL,CAAC,IAAI,EAAE,kCAAkC,EAAE,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC,EAC3D,EAAE,KAAK,EAAE,SAAS,EAAE,CACpB,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Logger } from "@caporal/core";
2
+ export declare function spinStatus(nameOrId: string, _logger: Logger): Promise<void>;
3
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBjF"}
@@ -0,0 +1,21 @@
1
+ import { getDroplet } from "../../lib/digitalocean";
2
+ import { formatTable, getPublicIp } from "./helpers";
3
+ export async function spinStatus(nameOrId, _logger) {
4
+ const d = await getDroplet(nameOrId);
5
+ const ip = getPublicIp(d) ?? "pending";
6
+ console.log(formatTable([
7
+ ["Name", d.name],
8
+ ["ID", String(d.id)],
9
+ ["Status", d.status],
10
+ ["IP", ip],
11
+ ["Region", `${d.region.slug} (${d.region.name})`],
12
+ ["Size", d.size_slug],
13
+ ["Image", `${d.image.slug} (${d.image.name})`],
14
+ ["vCPUs", String(d.vcpus)],
15
+ ["Memory", `${d.memory} MB`],
16
+ ["Disk", `${d.disk} GB`],
17
+ ["Tags", d.tags.join(", ") || "none"],
18
+ ["Created", d.created_at],
19
+ ]));
20
+ }
21
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/spin/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,OAAe;IACjE,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACvB,CAAC,MAAM,EAAK,CAAC,CAAC,IAAI,CAAC;QACnB,CAAC,IAAI,EAAO,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC,QAAQ,EAAG,CAAC,CAAC,MAAM,CAAC;QACrB,CAAC,IAAI,EAAO,EAAE,CAAC;QACf,CAAC,QAAQ,EAAG,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;QAClD,CAAC,MAAM,EAAK,CAAC,CAAC,SAAS,CAAC;QACxB,CAAC,OAAO,EAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;QAChD,CAAC,OAAO,EAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,QAAQ,EAAG,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,MAAM,EAAK,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC;QAC3B,CAAC,MAAM,EAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;QACxC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC;KACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Logger } from "@caporal/core";
2
+ export type SpinUpOpts = {
3
+ name: string;
4
+ size: string;
5
+ region: string;
6
+ image: string;
7
+ sshKeys: string[];
8
+ tag: string;
9
+ };
10
+ export declare function spinUp(opts: SpinUpOpts, logger: Logger): Promise<void>;
11
+ //# sourceMappingURL=up.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"up.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/up.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC5E"}
@@ -0,0 +1,34 @@
1
+ import { createDroplet, listSshKeys } from "../../lib/digitalocean";
2
+ import { getPublicIp, waitForActive } from "./helpers";
3
+ export async function spinUp(opts, logger) {
4
+ if (opts.sshKeys.length === 0) {
5
+ const keys = await listSshKeys();
6
+ const bltKeys = keys.filter((k) => k.name.toLowerCase().includes("blt"));
7
+ if (bltKeys.length > 0) {
8
+ opts.sshKeys = bltKeys.map((k) => String(k.id));
9
+ logger.info(`Auto-selected SSH keys: ${bltKeys.map((k) => k.name).join(", ")}`);
10
+ }
11
+ else if (keys.length > 0) {
12
+ opts.sshKeys = keys.map((k) => String(k.id));
13
+ logger.info(`No 'blt' SSH keys found — using all account keys: ${keys.map((k) => k.name).join(", ")}`);
14
+ }
15
+ else {
16
+ logger.warn("No SSH keys found in DO account. Droplet will use password auth (check email for root password).");
17
+ }
18
+ }
19
+ logger.info(`Creating droplet "${opts.name}" (${opts.size}) in ${opts.region}…`);
20
+ const droplet = await createDroplet({
21
+ name: opts.name,
22
+ size: opts.size,
23
+ region: opts.region,
24
+ image: opts.image,
25
+ sshKeys: opts.sshKeys,
26
+ tags: [opts.tag],
27
+ });
28
+ logger.info(`Droplet created — id: ${droplet.id}, status: ${droplet.status}`);
29
+ logger.info("Waiting for droplet to become active…");
30
+ const ready = await waitForActive(String(droplet.id), logger);
31
+ const ip = getPublicIp(ready) ?? "unknown";
32
+ logger.info(`Droplet ready — id: ${ready.id}, ip: ${ip}`);
33
+ }
34
+ //# sourceMappingURL=up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"up.js","sourceRoot":"","sources":["../../../src/commands/spin/up.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAWvD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAgB,EAAE,MAAc;IAC5D,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAC3C,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,IAAI,CACV,2BAA2B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClE,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CACV,qDAAqD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,MAAM,CAAC,IAAI,CACV,kGAAkG,CAClG,CAAC;QACH,CAAC;IACF,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,QAAQ,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACjF,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC;QACnC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;KAChB,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,EAAE,aAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE9E,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC;IAC3C,MAAM,CAAC,IAAI,CAAC,uBAAuB,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Program } from "@caporal/core";
2
+ export default function spinCommand(program: Program): void;
3
+ //# sourceMappingURL=spin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spin.d.ts","sourceRoot":"","sources":["../../src/commands/spin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,eAAe,CAAC;AAwB5E,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,OAAO,EAAE,OAAO,QAuNnD"}
@@ -0,0 +1,166 @@
1
+ import { spinUp } from "./spin/up";
2
+ import { spinDown } from "./spin/down";
3
+ import { spinList } from "./spin/list";
4
+ import { spinStatus } from "./spin/status";
5
+ import { spinSsh } from "./spin/ssh";
6
+ import { spinDns } from "./spin/dns";
7
+ import { spinSetup } from "./spin/setup";
8
+ const spinHelp = `
9
+ Manage DigitalOcean droplets for BLT test environments
10
+
11
+ Usage:
12
+ blt spin up --name <name> [--size slug] [--region slug] [--image slug] [--ssh-keys ids] [--tag tag]
13
+ blt spin down <name> [--force]
14
+ blt spin list [--tag tag]
15
+ blt spin status <name>
16
+ blt spin ssh <name> [--user user]
17
+ blt spin dns <action> --domain <domain> [--name sub] [--ip addr] [--record-id id]
18
+ blt spin setup <name>
19
+
20
+ Requires DIGITALOCEAN_ACCESS_TOKEN env var.
21
+ `;
22
+ export default function spinCommand(program) {
23
+ program
24
+ .command("spin", "Manage DigitalOcean droplets for BLT environments")
25
+ .help(spinHelp)
26
+ .action(() => {
27
+ console.log(spinHelp);
28
+ });
29
+ // -- spin up ---------------------------------------------------------------
30
+ program
31
+ .command("spin up", "Create a new droplet")
32
+ .hide()
33
+ .option("--name <name>", "Droplet name", { required: true })
34
+ .option("--size <size>", "Droplet size slug", {
35
+ default: "s-1vcpu-512mb-10gb",
36
+ })
37
+ .option("--region <region>", "Region slug", { default: "nyc3" })
38
+ .option("--image <image>", "OS image slug", {
39
+ default: "ubuntu-24-04-x64",
40
+ })
41
+ .option("--ssh-keys <keys>", "Comma-separated SSH key IDs or fingerprints")
42
+ .option("--tag <tag>", "Tag for the droplet", { default: "blt" })
43
+ .action(async ({ options, logger, }) => {
44
+ try {
45
+ const sshKeysRaw = options.sshKeys;
46
+ const sshKeys = sshKeysRaw
47
+ ? String(sshKeysRaw).split(",").map((k) => k.trim()).filter(Boolean)
48
+ : [];
49
+ await spinUp({
50
+ name: String(options.name),
51
+ size: String(options.size),
52
+ region: String(options.region),
53
+ image: String(options.image),
54
+ tag: String(options.tag),
55
+ sshKeys,
56
+ }, logger);
57
+ }
58
+ catch (error) {
59
+ const message = error instanceof Error ? error.message : String(error);
60
+ logger.error(`spin up failed: ${message}`);
61
+ process.exit(1);
62
+ }
63
+ });
64
+ // -- spin down -------------------------------------------------------------
65
+ program
66
+ .command("spin down", "Destroy a droplet")
67
+ .hide()
68
+ .argument("<name>", "Droplet name or ID")
69
+ .option("--force", "Skip confirmation", { default: false })
70
+ .action(async ({ args, options, logger, }) => {
71
+ try {
72
+ await spinDown(args.name, !!options.force, logger);
73
+ }
74
+ catch (error) {
75
+ const message = error instanceof Error ? error.message : String(error);
76
+ logger.error(`spin down failed: ${message}`);
77
+ process.exit(1);
78
+ }
79
+ });
80
+ // -- spin list -------------------------------------------------------------
81
+ program
82
+ .command("spin list", "List BLT droplets")
83
+ .hide()
84
+ .option("--tag <tag>", "Filter by tag", { default: "blt" })
85
+ .action(async ({ options, logger, }) => {
86
+ try {
87
+ await spinList(options.tag, logger);
88
+ }
89
+ catch (error) {
90
+ const message = error instanceof Error ? error.message : String(error);
91
+ logger.error(`spin list failed: ${message}`);
92
+ process.exit(1);
93
+ }
94
+ });
95
+ // -- spin status -----------------------------------------------------------
96
+ program
97
+ .command("spin status", "Check droplet status")
98
+ .hide()
99
+ .argument("<name>", "Droplet name or ID")
100
+ .action(async ({ args, logger, }) => {
101
+ try {
102
+ await spinStatus(args.name, logger);
103
+ }
104
+ catch (error) {
105
+ const message = error instanceof Error ? error.message : String(error);
106
+ logger.error(`spin status failed: ${message}`);
107
+ process.exit(1);
108
+ }
109
+ });
110
+ // -- spin ssh --------------------------------------------------------------
111
+ program
112
+ .command("spin ssh", "SSH into a droplet")
113
+ .hide()
114
+ .argument("<name>", "Droplet name or ID")
115
+ .option("--user <user>", "SSH user", { default: "blt" })
116
+ .action(async ({ args, options, logger, }) => {
117
+ try {
118
+ await spinSsh(args.name, options.user, logger);
119
+ }
120
+ catch (error) {
121
+ const message = error instanceof Error ? error.message : String(error);
122
+ logger.error(`spin ssh failed: ${message}`);
123
+ process.exit(1);
124
+ }
125
+ });
126
+ // -- spin dns --------------------------------------------------------------
127
+ program
128
+ .command("spin dns", "Manage DNS records for a domain")
129
+ .hide()
130
+ .argument("<action>", "Action: create, list, delete")
131
+ .option("--domain <domain>", "Domain name", { default: "bltcore.com" })
132
+ .option("--name <name>", "Record name (subdomain)")
133
+ .option("--ip <ip>", "IP address for A record")
134
+ .option("--record-id <id>", "Record ID (for delete)")
135
+ .action(async ({ args, options, logger, }) => {
136
+ try {
137
+ await spinDns(args.action, options.domain, {
138
+ name: options.name,
139
+ ip: options.ip,
140
+ recordId: options.recordId,
141
+ }, logger);
142
+ }
143
+ catch (error) {
144
+ const message = error instanceof Error ? error.message : String(error);
145
+ logger.error(`spin dns failed: ${message}`);
146
+ process.exit(1);
147
+ }
148
+ });
149
+ // -- spin setup ------------------------------------------------------------
150
+ program
151
+ .command("spin setup", "Bootstrap a fresh droplet (Docker, users, dirs)")
152
+ .hide()
153
+ .argument("<name>", "Droplet name or ID")
154
+ .option("--user <user>", "SSH user for bootstrap", { default: "root" })
155
+ .action(async ({ args, options, logger, }) => {
156
+ try {
157
+ await spinSetup(args.name, options.user, logger);
158
+ }
159
+ catch (error) {
160
+ const message = error instanceof Error ? error.message : String(error);
161
+ logger.error(`spin setup failed: ${message}`);
162
+ process.exit(1);
163
+ }
164
+ });
165
+ }
166
+ //# sourceMappingURL=spin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spin.js","sourceRoot":"","sources":["../../src/commands/spin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Pull remaining `public` base tables not covered by the curated snapshot manifest
3
+ * (everything in `schema/public/sql` should exist as a table in a live DB — we discover via catalog).
4
+ */
5
+ import type { Client } from "pg";
6
+ import type { PullContext, PullPartition } from "./data-pull-manifest";
7
+ export declare function listPublicBaseTables(client: Client): Promise<string[]>;
8
+ /** Insert-safe order: parents before children (FK targets first). */
9
+ export declare function topoSortTables(client: Client, tables: string[]): Promise<string[]>;
10
+ /** Column `udt_name` map for snapshot apply (json/jsonb-aware literals). */
11
+ export declare function fetchPublicTableColumnUdts(client: Client, table: string): Promise<Record<string, string>>;
12
+ /** Columns that are `GENERATED ALWAYS AS IDENTITY` — inserts need `OVERRIDING SYSTEM VALUE` when values are supplied. */
13
+ export declare function fetchPublicTableIdentityAlwaysColumns(client: Client, table: string): Promise<string[]>;
14
+ /** Columns that are `STORED` generated (`GENERATED ALWAYS AS (...)`); inserts must omit them (values are computed). */
15
+ export declare function fetchPublicTableGeneratedStoredColumnNames(client: Client, table: string): Promise<string[]>;
16
+ export declare function pullExtraPublicTablePartition(client: Client, table: string): Promise<PullPartition>;
17
+ /** Full-row pull for curated manifest assets that need stable PKs (e.g. `items.id` for `order_items` FKs). */
18
+ export declare function dumpPublicTableForCuratedPull(client: Client, table: string): Promise<{
19
+ rows: {
20
+ row: Record<string, unknown>;
21
+ }[];
22
+ columnUdts: Record<string, string>;
23
+ primaryKey: string[];
24
+ }>;
25
+ export declare function discoverAndSortExtraPublicTables(client: Client, ctx: PullContext): Promise<string[]>;
26
+ //# sourceMappingURL=data-pull-extra-tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-pull-extra-tables.d.ts","sourceRoot":"","sources":["../../src/lib/data-pull-extra-tables.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA2BvE,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAa5E;AAmBD,qEAAqE;AACrE,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiCxF;AAeD,4EAA4E;AAC5E,wBAAsB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAG/G;AAED,yHAAyH;AACzH,wBAAsB,qCAAqC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW5G;AAED,uHAAuH;AACvH,wBAAsB,0CAA0C,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAejH;AA2CD,wBAAsB,6BAA6B,CAClD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,aAAa,CAAC,CAoCxB;AAED,8GAA8G;AAC9G,wBAAsB,6BAA6B,CAClD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAAC;IACV,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC,CAOD;AAED,wBAAsB,gCAAgC,CACrD,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,WAAW,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAOnB"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Pull remaining `public` base tables not covered by the curated snapshot manifest
3
+ * (everything in `schema/public/sql` should exist as a table in a live DB — we discover via catalog).
4
+ */
5
+ import { MANIFEST_COVERED_PUBLIC_TABLES } from "./data-pull-manifest";
6
+ /** Never auto-pull these (noise, huge, or not data). */
7
+ const EXTRA_TABLE_BLOCKLIST = new Set([
8
+ "spatial_ref_sys",
9
+ "geometry_columns",
10
+ "geography_columns",
11
+ "raster_columns",
12
+ "raster_overviews",
13
+ ]);
14
+ function slugTableForFilename(table) {
15
+ return table.replace(/[^a-zA-Z0-9_-]/g, "_");
16
+ }
17
+ /** Tables handled by FULL_PULL_SEQUENCE (same physical table as curated assets). */
18
+ function coveredPublicTablesForPull(ctx) {
19
+ const s = new Set(MANIFEST_COVERED_PUBLIC_TABLES);
20
+ /** User-shaped assets map to profiles / profile_roles; avoid duplicate apply. */
21
+ if (ctx.includeUsers) {
22
+ s.add("profiles");
23
+ s.add("profile_roles");
24
+ }
25
+ return s;
26
+ }
27
+ export async function listPublicBaseTables(client) {
28
+ const { rows } = await client.query(`SELECT c.relname AS t
29
+ FROM pg_class c
30
+ JOIN pg_namespace n ON n.oid = c.relnamespace
31
+ WHERE n.nspname = 'public'
32
+ AND (
33
+ (c.relkind = 'r' AND NOT c.relispartition)
34
+ OR c.relkind = 'p'
35
+ )
36
+ ORDER BY c.relname ASC`);
37
+ return rows.map((r) => r.t);
38
+ }
39
+ async function publicFkEdgesAmong(client, tableSet) {
40
+ const { rows } = await client.query(`SELECT cl.relname AS child, pf.relname AS parent
41
+ FROM pg_constraint con
42
+ JOIN pg_class cl ON cl.oid = con.conrelid
43
+ JOIN pg_namespace nl ON nl.oid = cl.relnamespace
44
+ JOIN pg_class pf ON pf.oid = con.confrelid
45
+ JOIN pg_namespace nf ON nf.oid = pf.relnamespace
46
+ WHERE con.contype = 'f'
47
+ AND nl.nspname = 'public'
48
+ AND nf.nspname = 'public'`);
49
+ return rows.filter((e) => tableSet.has(e.child) && tableSet.has(e.parent));
50
+ }
51
+ /** Insert-safe order: parents before children (FK targets first). */
52
+ export async function topoSortTables(client, tables) {
53
+ const set = new Set(tables);
54
+ if (tables.length <= 1)
55
+ return [...tables];
56
+ const edges = await publicFkEdgesAmong(client, set);
57
+ const inDeg = new Map();
58
+ for (const t of tables)
59
+ inDeg.set(t, 0);
60
+ for (const e of edges) {
61
+ if (set.has(e.child))
62
+ inDeg.set(e.child, (inDeg.get(e.child) ?? 0) + 1);
63
+ }
64
+ const q = tables.filter((t) => (inDeg.get(t) ?? 0) === 0).sort((a, b) => a.localeCompare(b));
65
+ const out = [];
66
+ while (q.length > 0) {
67
+ const t = q.shift();
68
+ if (t === undefined)
69
+ break;
70
+ out.push(t);
71
+ for (const e of edges) {
72
+ if (e.parent === t && set.has(e.child)) {
73
+ const d = (inDeg.get(e.child) ?? 1) - 1;
74
+ inDeg.set(e.child, d);
75
+ if (d === 0) {
76
+ q.push(e.child);
77
+ q.sort((a, b) => a.localeCompare(b));
78
+ }
79
+ }
80
+ }
81
+ }
82
+ if (out.length !== tables.length) {
83
+ const seen = new Set(out);
84
+ for (const t of tables.sort((a, b) => a.localeCompare(b))) {
85
+ if (!seen.has(t))
86
+ out.push(t);
87
+ }
88
+ }
89
+ return out;
90
+ }
91
+ async function listTableColumns(client, table) {
92
+ const { rows } = await client.query(`SELECT column_name, udt_name
93
+ FROM information_schema.columns
94
+ WHERE table_schema = 'public' AND table_name = $1
95
+ ORDER BY ordinal_position ASC`, [table]);
96
+ return rows.map((r) => ({ name: r.column_name, udt: r.udt_name }));
97
+ }
98
+ /** Column `udt_name` map for snapshot apply (json/jsonb-aware literals). */
99
+ export async function fetchPublicTableColumnUdts(client, table) {
100
+ const cols = await listTableColumns(client, table);
101
+ return Object.fromEntries(cols.map((c) => [c.name, c.udt]));
102
+ }
103
+ /** Columns that are `GENERATED ALWAYS AS IDENTITY` — inserts need `OVERRIDING SYSTEM VALUE` when values are supplied. */
104
+ export async function fetchPublicTableIdentityAlwaysColumns(client, table) {
105
+ const { rows } = await client.query(`SELECT column_name
106
+ FROM information_schema.columns
107
+ WHERE table_schema = 'public'
108
+ AND table_name = $1
109
+ AND is_identity = 'YES'
110
+ AND identity_generation = 'ALWAYS'`, [table]);
111
+ return rows.map((r) => r.column_name);
112
+ }
113
+ /** Columns that are `STORED` generated (`GENERATED ALWAYS AS (...)`); inserts must omit them (values are computed). */
114
+ export async function fetchPublicTableGeneratedStoredColumnNames(client, table) {
115
+ const { rows } = await client.query(`SELECT a.attname AS column_name
116
+ FROM pg_attribute a
117
+ JOIN pg_class c ON c.oid = a.attrelid
118
+ JOIN pg_namespace n ON n.oid = c.relnamespace
119
+ WHERE n.nspname = 'public'
120
+ AND c.relname = $1
121
+ AND a.attnum > 0
122
+ AND NOT a.attisdropped
123
+ AND a.attgenerated = 's'
124
+ ORDER BY a.attnum`, [table]);
125
+ return rows.map((r) => r.column_name);
126
+ }
127
+ async function primaryKeyColumns(client, table) {
128
+ const { rows } = await client.query(`SELECT a.attname
129
+ FROM pg_index i
130
+ JOIN pg_class c ON c.oid = i.indrelid
131
+ JOIN pg_namespace n ON n.oid = c.relnamespace
132
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY (i.indkey)
133
+ WHERE n.nspname = 'public' AND c.relname = $1 AND i.indisprimary
134
+ ORDER BY array_position(i.indkey, a.attnum)`, [table]);
135
+ return rows.map((r) => r.attname);
136
+ }
137
+ function buildSelectList(cols) {
138
+ const parts = [];
139
+ for (const c of cols) {
140
+ const q = `"${c.name.replace(/"/g, '""')}"`;
141
+ if (c.udt === "bytea") {
142
+ parts.push(`encode(${q}, 'base64') AS ${q}`);
143
+ }
144
+ else {
145
+ parts.push(q);
146
+ }
147
+ }
148
+ return parts.join(", ");
149
+ }
150
+ function normalizeCell(column, value, udtByCol) {
151
+ if (value === null || value === undefined)
152
+ return value;
153
+ if (typeof value === "bigint")
154
+ return value.toString();
155
+ if (value instanceof Date)
156
+ return value.toISOString();
157
+ if (Buffer.isBuffer(value)) {
158
+ return value.toString("base64");
159
+ }
160
+ /** After SELECT encode(..., 'base64') for bytea, driver returns string. */
161
+ if (udtByCol.get(column) === "bytea" && typeof value === "string") {
162
+ return { __blt_bytea_b64: value };
163
+ }
164
+ return value;
165
+ }
166
+ export async function pullExtraPublicTablePartition(client, table) {
167
+ const cols = await listTableColumns(client, table);
168
+ if (cols.length === 0) {
169
+ return {
170
+ assetId: `10-900-public-${slugTableForFilename(table)}`,
171
+ snapshotStem: `10-900-public-${slugTableForFilename(table)}`,
172
+ rows: [],
173
+ };
174
+ }
175
+ const pk = await primaryKeyColumns(client, table);
176
+ const udtByCol = new Map(cols.map((c) => [c.name, c.udt]));
177
+ const selectList = buildSelectList(cols);
178
+ const orderBy = pk.length > 0 ? pk.map((c) => `"${c.replace(/"/g, '""')}" ASC`).join(", ") : `"${cols[0].name.replace(/"/g, '""')}" ASC`;
179
+ const { rows: data } = await client.query(`SELECT ${selectList} FROM public."${table.replace(/"/g, '""')}" ORDER BY ${orderBy}`);
180
+ const rrows = data.map((r) => {
181
+ const row = {};
182
+ for (const c of cols) {
183
+ row[c.name] = normalizeCell(c.name, r[c.name], udtByCol);
184
+ }
185
+ return { row };
186
+ });
187
+ const stem = `10-900-public-${slugTableForFilename(table)}`;
188
+ const columnUdts = Object.fromEntries(cols.map((c) => [c.name, c.udt]));
189
+ return {
190
+ assetId: stem,
191
+ snapshotStem: stem,
192
+ rows: rrows,
193
+ primaryKey: pk.length > 0 ? pk : undefined,
194
+ columnUdts,
195
+ };
196
+ }
197
+ /** Full-row pull for curated manifest assets that need stable PKs (e.g. `items.id` for `order_items` FKs). */
198
+ export async function dumpPublicTableForCuratedPull(client, table) {
199
+ const part = await pullExtraPublicTablePartition(client, table);
200
+ return {
201
+ rows: part.rows,
202
+ columnUdts: part.columnUdts ?? {},
203
+ primaryKey: part.primaryKey ?? [],
204
+ };
205
+ }
206
+ export async function discoverAndSortExtraPublicTables(client, ctx) {
207
+ const covered = coveredPublicTablesForPull(ctx);
208
+ const all = await listPublicBaseTables(client);
209
+ const candidates = all.filter((t) => !covered.has(t) && !EXTRA_TABLE_BLOCKLIST.has(t) && !t.startsWith("pg_"));
210
+ return topoSortTables(client, candidates);
211
+ }
212
+ //# sourceMappingURL=data-pull-extra-tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-pull-extra-tables.js","sourceRoot":"","sources":["../../src/lib/data-pull-extra-tables.ts"],"names":[],"mappings":"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"}