@databricks-solutions/lakebase-scm-utils 0.3.9 → 0.3.10

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 (56) hide show
  1. package/dist/scripts/index.cjs +59 -24
  2. package/dist/scripts/index.js +59 -24
  3. package/dist/scripts/lakebase/branch.cli.cjs +55 -10
  4. package/dist/scripts/lakebase/branch.cli.js +55 -10
  5. package/dist/scripts/lakebase/ci-resolve-branch.cli.cjs +59 -24
  6. package/dist/scripts/lakebase/ci-resolve-branch.cli.js +59 -24
  7. package/dist/scripts/lakebase/collapse-heads.cli.cjs +16 -14
  8. package/dist/scripts/lakebase/collapse-heads.cli.js +16 -14
  9. package/dist/scripts/lakebase/cut-backup.cli.cjs +43 -10
  10. package/dist/scripts/lakebase/cut-backup.cli.js +43 -10
  11. package/dist/scripts/lakebase/cut-tier.cli.cjs +43 -10
  12. package/dist/scripts/lakebase/cut-tier.cli.js +43 -10
  13. package/dist/scripts/lakebase/detect-language.cli.cjs +16 -14
  14. package/dist/scripts/lakebase/detect-language.cli.js +16 -14
  15. package/dist/scripts/lakebase/doctor.cli.cjs +16 -14
  16. package/dist/scripts/lakebase/doctor.cli.js +16 -14
  17. package/dist/scripts/lakebase/get-connection.cli.cjs +16 -14
  18. package/dist/scripts/lakebase/get-connection.cli.js +16 -14
  19. package/dist/scripts/lakebase/index.cjs +59 -24
  20. package/dist/scripts/lakebase/index.d.cts +23 -0
  21. package/dist/scripts/lakebase/index.d.ts +23 -0
  22. package/dist/scripts/lakebase/index.js +59 -24
  23. package/dist/scripts/lakebase/infra-runner.cli.cjs +16 -14
  24. package/dist/scripts/lakebase/infra-runner.cli.js +16 -14
  25. package/dist/scripts/lakebase/new-migration.cli.cjs +16 -14
  26. package/dist/scripts/lakebase/new-migration.cli.js +16 -14
  27. package/dist/scripts/lakebase/schema-diff.cli.cjs +16 -14
  28. package/dist/scripts/lakebase/schema-diff.cli.js +16 -14
  29. package/dist/scripts/lakebase/schema-migrate.cli.cjs +16 -14
  30. package/dist/scripts/lakebase/schema-migrate.cli.js +16 -14
  31. package/dist/scripts/lakebase/scm-claim-feature.cli.cjs +59 -24
  32. package/dist/scripts/lakebase/scm-claim-feature.cli.js +59 -24
  33. package/dist/scripts/lakebase/scm-doctor.cli.cjs +59 -24
  34. package/dist/scripts/lakebase/scm-doctor.cli.js +59 -24
  35. package/dist/scripts/lakebase/scm-merge.cli.cjs +16 -14
  36. package/dist/scripts/lakebase/scm-merge.cli.js +16 -14
  37. package/dist/scripts/lakebase/scm-reconcile-tier.cli.cjs +16 -14
  38. package/dist/scripts/lakebase/scm-reconcile-tier.cli.js +16 -14
  39. package/dist/scripts/lakebase/scm-recover-orphans.cli.cjs +43 -10
  40. package/dist/scripts/lakebase/scm-recover-orphans.cli.js +43 -10
  41. package/dist/scripts/lakebase/snapshot.cli.cjs +418 -0
  42. package/dist/scripts/lakebase/snapshot.cli.d.cts +1 -0
  43. package/dist/scripts/lakebase/snapshot.cli.d.ts +1 -0
  44. package/dist/scripts/lakebase/snapshot.cli.js +395 -0
  45. package/dist/scripts/util/index.cjs +2 -2
  46. package/dist/scripts/util/index.js +2 -2
  47. package/package.json +2 -1
  48. package/scripts/lakebase/branch-create.ts +93 -14
  49. package/scripts/lakebase/branch.cli.ts +15 -0
  50. package/scripts/lakebase/get-connection.ts +29 -12
  51. package/scripts/lakebase/snapshot.cli.ts +117 -0
  52. package/scripts/lakebase/snapshot.ts +85 -0
  53. package/templates/project/common/scripts/run-tests.sh +43 -2
  54. package/templates/project/python/tests/conftest.py +22 -0
  55. package/templates/project/common/tests/e2e/__pycache__/conftest.cpython-314.pyc +0 -0
  56. package/templates/project/python/tests/__pycache__/conftest.cpython-314.pyc +0 -0
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // scripts/lakebase/databricks-cli.ts
27
+ var import_node_child_process2 = require("child_process");
28
+ var import_node_util = require("util");
29
+ var import_node_path = require("path");
30
+
31
+ // scripts/lakebase/kit-config.ts
32
+ function intFromEnv(name, fallback) {
33
+ const raw = process.env[name];
34
+ if (!raw) return fallback;
35
+ const parsed = Number.parseInt(raw, 10);
36
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
37
+ return parsed;
38
+ }
39
+ var DAY_MS = 24 * 60 * 60 * 1e3;
40
+ var KIT_TIMEOUTS = {
41
+ cliDefault: intFromEnv("LAKEBASE_KIT_TIMEOUT_CLI_DEFAULT_MS", 3e4),
42
+ cliCreateProject: intFromEnv("LAKEBASE_KIT_TIMEOUT_CLI_CREATE_PROJECT_MS", 18e4),
43
+ cliCreateBranch: intFromEnv("LAKEBASE_KIT_TIMEOUT_CLI_CREATE_BRANCH_MS", 6e4),
44
+ cliCreateEndpoint: intFromEnv("LAKEBASE_KIT_TIMEOUT_CLI_CREATE_ENDPOINT_MS", 6e4),
45
+ readyWait: intFromEnv("LAKEBASE_KIT_TIMEOUT_READY_WAIT_MS", 12e4),
46
+ readyPoll: intFromEnv("LAKEBASE_KIT_TIMEOUT_READY_POLL_MS", 5e3),
47
+ pgConnect: intFromEnv("LAKEBASE_KIT_TIMEOUT_PG_CONNECT_MS", 1e4),
48
+ pgStatement: intFromEnv("LAKEBASE_KIT_TIMEOUT_PG_STATEMENT_MS", 15e3),
49
+ gitDefault: intFromEnv("LAKEBASE_KIT_TIMEOUT_GIT_DEFAULT_MS", 5e3),
50
+ gitCheckout: intFromEnv("LAKEBASE_KIT_TIMEOUT_GIT_CHECKOUT_MS", 1e4),
51
+ gitNetwork: intFromEnv("LAKEBASE_KIT_TIMEOUT_GIT_NETWORK_MS", 15e3),
52
+ gitPush: intFromEnv("LAKEBASE_KIT_TIMEOUT_GIT_PUSH_MS", 3e4),
53
+ cliLong: intFromEnv("LAKEBASE_KIT_TIMEOUT_CLI_LONG_MS", 6e4),
54
+ cmdShort: intFromEnv("LAKEBASE_KIT_TIMEOUT_CMD_SHORT_MS", 5e3),
55
+ initializrCacheTtl: intFromEnv("LAKEBASE_KIT_INITIALIZR_CACHE_TTL_MS", 10 * 60 * 1e3),
56
+ featureBranchTtlMs: intFromEnv("LAKEBASE_KIT_FEATURE_BRANCH_TTL_MS", 30 * DAY_MS),
57
+ testBranchTtlMs: intFromEnv("LAKEBASE_KIT_TEST_BRANCH_TTL_MS", 14 * DAY_MS),
58
+ uatBranchTtlMs: intFromEnv("LAKEBASE_KIT_UAT_BRANCH_TTL_MS", 14 * DAY_MS),
59
+ perfBranchTtlMs: intFromEnv("LAKEBASE_KIT_PERF_BRANCH_TTL_MS", 7 * DAY_MS)
60
+ };
61
+ function urlFromEnv(name, fallback) {
62
+ const raw = process.env[name];
63
+ if (!raw) return fallback;
64
+ return raw.replace(/\/+$/, "");
65
+ }
66
+ var KIT_REGISTRIES = {
67
+ mavenCentral: urlFromEnv("LAKEBASE_KIT_REGISTRY_MAVEN_CENTRAL", "https://repo1.maven.org/maven2"),
68
+ springInitializr: urlFromEnv("LAKEBASE_KIT_REGISTRY_SPRING_INITIALIZR", "https://start.spring.io")
69
+ };
70
+
71
+ // scripts/lakebase/databricks-profile.ts
72
+ var fs = __toESM(require("fs"), 1);
73
+ var import_node_child_process = require("child_process");
74
+
75
+ // scripts/util/exec.ts
76
+ var cp = __toESM(require("child_process"), 1);
77
+
78
+ // scripts/lakebase/databricks-profile.ts
79
+ function normalizeHost(host) {
80
+ return host.trim().replace(/\/+$/, "").toLowerCase();
81
+ }
82
+ function selectProfileForHost(profilesJson, host) {
83
+ const target = normalizeHost(host);
84
+ if (!target) return void 0;
85
+ const start = profilesJson.indexOf("{");
86
+ if (start < 0) return void 0;
87
+ let parsed;
88
+ try {
89
+ parsed = JSON.parse(profilesJson.slice(start));
90
+ } catch {
91
+ return void 0;
92
+ }
93
+ const profiles = parsed.profiles;
94
+ if (!Array.isArray(profiles)) return void 0;
95
+ const names = profiles.filter((p) => {
96
+ if (!p || typeof p !== "object") return false;
97
+ const rec = p;
98
+ return typeof rec.name === "string" && typeof rec.host === "string" && rec.valid === true && normalizeHost(rec.host) === target;
99
+ }).map((p) => p.name);
100
+ const distinct = Array.from(new Set(names));
101
+ return distinct.length === 1 ? distinct[0] : void 0;
102
+ }
103
+ function resolveProfileForHostSync(host, timeoutMs = KIT_TIMEOUTS.cliDefault) {
104
+ if (!normalizeHost(host)) return void 0;
105
+ let out2;
106
+ try {
107
+ out2 = (0, import_node_child_process.execFileSync)("databricks", ["auth", "profiles", "-o", "json"], {
108
+ encoding: "utf8",
109
+ stdio: ["ignore", "pipe", "pipe"],
110
+ timeout: timeoutMs
111
+ });
112
+ } catch {
113
+ return void 0;
114
+ }
115
+ return selectProfileForHost(out2, host);
116
+ }
117
+
118
+ // scripts/lakebase/env-file.ts
119
+ var fs2 = __toESM(require("fs"), 1);
120
+ var path = __toESM(require("path"), 1);
121
+ function readEnvVar(envPath, key) {
122
+ if (!fs2.existsSync(envPath)) return void 0;
123
+ let value;
124
+ for (const line of fs2.readFileSync(envPath, "utf-8").split("\n")) {
125
+ const trimmed = line.trimStart();
126
+ if (trimmed.startsWith("#") || !trimmed.startsWith(`${key}=`)) continue;
127
+ value = trimmed.slice(key.length + 1).trim().replace(/^["']|["']$/g, "");
128
+ }
129
+ return value && value.length > 0 ? value : void 0;
130
+ }
131
+
132
+ // scripts/lakebase/databricks-cli.ts
133
+ var execFileP = (0, import_node_util.promisify)(import_node_child_process2.execFile);
134
+ var DatabricksCliError = class extends Error {
135
+ constructor(message, profile, stderr) {
136
+ super(message);
137
+ this.profile = profile;
138
+ this.stderr = stderr;
139
+ this.name = "DatabricksCliError";
140
+ }
141
+ profile;
142
+ stderr;
143
+ };
144
+ var DatabricksAuthError = class extends DatabricksCliError {
145
+ constructor(profile, detail) {
146
+ const login = `databricks auth login${profile ? ` --profile ${profile}` : ""}`;
147
+ super(
148
+ `Databricks authentication failed${profile ? ` for profile "${profile}"` : ""}: the cached token is missing or expired. Re-authenticate, then re-run:
149
+ ${login}
150
+ ${detail}`,
151
+ profile,
152
+ detail
153
+ );
154
+ this.name = "DatabricksAuthError";
155
+ }
156
+ };
157
+ var profileByHost = /* @__PURE__ */ new Map();
158
+ var profileByEnvFile = /* @__PURE__ */ new Map();
159
+ var hostByEnvFile = /* @__PURE__ */ new Map();
160
+ function envFileHost(cwd) {
161
+ if (hostByEnvFile.has(cwd)) return hostByEnvFile.get(cwd);
162
+ const v = readEnvVar((0, import_node_path.join)(cwd, ".env"), "DATABRICKS_HOST");
163
+ hostByEnvFile.set(cwd, v);
164
+ return v;
165
+ }
166
+ function effectiveHost(opts) {
167
+ const base = opts.env ?? process.env;
168
+ const cwd = opts.cwd ?? process.cwd();
169
+ const h = opts.host ?? base.DATABRICKS_HOST ?? envFileHost(cwd);
170
+ return h?.trim() || void 0;
171
+ }
172
+ function isAuthFailure(text) {
173
+ return /refresh token is invalid|auth login|could not be retrieved because|not authenticated|no valid.*(credential|token)|invalid.*(access token|credential)|\b401\b|unauthorized/i.test(
174
+ text
175
+ );
176
+ }
177
+ function resolveProfile(opts) {
178
+ const base = opts.env ?? process.env;
179
+ if (opts.profile) return opts.profile;
180
+ const envProfile = base.DATABRICKS_CONFIG_PROFILE?.trim();
181
+ if (envProfile) return envProfile;
182
+ const cwd = opts.cwd ?? process.cwd();
183
+ let fromEnvFile;
184
+ if (profileByEnvFile.has(cwd)) {
185
+ fromEnvFile = profileByEnvFile.get(cwd);
186
+ } else {
187
+ fromEnvFile = readEnvVar((0, import_node_path.join)(cwd, ".env"), "DATABRICKS_CONFIG_PROFILE");
188
+ profileByEnvFile.set(cwd, fromEnvFile);
189
+ }
190
+ if (fromEnvFile) return fromEnvFile;
191
+ const host = effectiveHost(opts);
192
+ if (!host) return void 0;
193
+ if (profileByHost.has(host)) return profileByHost.get(host);
194
+ const resolved = resolveProfileForHostSync(host, opts.timeout);
195
+ profileByHost.set(host, resolved);
196
+ return resolved;
197
+ }
198
+ function buildInvocation(args, opts) {
199
+ const base = opts.env ?? process.env;
200
+ const trimmedHost = effectiveHost(opts)?.replace(/\/+$/, "");
201
+ const env = { ...base };
202
+ if (trimmedHost) env.DATABRICKS_HOST = trimmedHost;
203
+ delete env.DATABRICKS_WORKSPACE_ID;
204
+ const profile = resolveProfile(opts);
205
+ const argv = profile && !opts.noProfile && !args.includes("--profile") ? [...args, "--profile", profile] : args;
206
+ return { argv, env, profile };
207
+ }
208
+ function classifyDatabricksError(err, argv, profile) {
209
+ const e = err;
210
+ const asText = (v) => typeof v === "string" ? v : Buffer.isBuffer(v) ? v.toString("utf8") : "";
211
+ const stderr = asText(e.stderr).trim();
212
+ const stdout = asText(e.stdout).trim();
213
+ const haystack = `${e.message ?? ""}
214
+ ${stderr}
215
+ ${stdout}`;
216
+ if (isAuthFailure(haystack)) {
217
+ return new DatabricksAuthError(profile, stderr || stdout || (e.message ?? ""));
218
+ }
219
+ const killed = e.killed === true;
220
+ const signal = e.signal ?? void 0;
221
+ const detail = stderr ? `
222
+ stderr: ${stderr}` : stdout ? `
223
+ stdout: ${stdout}` : killed || signal ? `
224
+ (no output; the CLI was killed${signal ? ` by ${signal}` : ""}, likely a TIMEOUT; raise the budget via the matching LAKEBASE_KIT_TIMEOUT_* env var)` : e.code !== void 0 ? `
225
+ (no stderr/stdout; exit ${e.code})` : "";
226
+ return new DatabricksCliError(
227
+ `databricks ${argv.join(" ")} failed: ${e.message}${detail}`,
228
+ profile,
229
+ stderr || stdout
230
+ );
231
+ }
232
+ async function runDatabricks(args, opts = {}) {
233
+ const { argv, env, profile } = buildInvocation(args, opts);
234
+ const timeout = opts.timeout ?? KIT_TIMEOUTS.cliDefault;
235
+ try {
236
+ if (opts.input !== void 0) {
237
+ return await execDatabricksWithStdin(argv, opts.input, env, timeout);
238
+ }
239
+ const { stdout } = await execFileP("databricks", argv, { env, timeout });
240
+ return stdout.toString();
241
+ } catch (err) {
242
+ throw classifyDatabricksError(err, argv, profile);
243
+ }
244
+ }
245
+ function execDatabricksWithStdin(argv, input, env, timeout) {
246
+ return new Promise((resolve, reject) => {
247
+ const child = (0, import_node_child_process2.execFile)("databricks", argv, { env, timeout }, (err, stdout, stderr) => {
248
+ if (err) {
249
+ err.stdout = String(stdout ?? "");
250
+ err.stderr = String(stderr ?? "");
251
+ reject(err);
252
+ return;
253
+ }
254
+ resolve(String(stdout ?? ""));
255
+ });
256
+ child.stdin?.on("error", () => {
257
+ });
258
+ child.stdin?.end(input);
259
+ });
260
+ }
261
+
262
+ // scripts/lakebase/branch-utils.ts
263
+ function projectPath(instance) {
264
+ return `projects/${instance}`;
265
+ }
266
+
267
+ // scripts/lakebase/snapshot.ts
268
+ function branchResource(instance, branch) {
269
+ return branch.includes("/") ? branch : `${projectPath(instance)}/branches/${branch}`;
270
+ }
271
+ function snapshotResource(instance, snapshot) {
272
+ return snapshot.includes("/") ? snapshot : `${projectPath(instance)}/snapshots/${snapshot}`;
273
+ }
274
+ function buildSnapshotSpec(args) {
275
+ if (args.sourceBranchTime && args.sourceBranchLsn) {
276
+ throw new Error("sourceBranchTime and sourceBranchLsn are mutually exclusive \u2014 pass at most one point-in-time.");
277
+ }
278
+ if (args.ttl && args.noExpiry === true) {
279
+ throw new Error(`Cannot set both ttl ("${args.ttl}") and noExpiry: true \u2014 they are mutually exclusive.`);
280
+ }
281
+ const spec = { source_branch: branchResource(args.instance, args.sourceBranch) };
282
+ if (args.sourceBranchTime) spec.source_branch_time = args.sourceBranchTime;
283
+ else if (args.sourceBranchLsn) spec.source_branch_lsn = args.sourceBranchLsn;
284
+ if (args.ttl) spec.ttl = args.ttl;
285
+ else if (args.noExpiry ?? true) spec.no_expiry = true;
286
+ return spec;
287
+ }
288
+ async function createSnapshot(args) {
289
+ const spec = buildSnapshotSpec(args);
290
+ return runDatabricks(
291
+ ["postgres", "create-snapshot", projectPath(args.instance), args.snapshot, "--json", JSON.stringify({ spec })],
292
+ { host: args.host, timeout: KIT_TIMEOUTS.cliCreateBranch }
293
+ );
294
+ }
295
+ async function deleteSnapshot(args) {
296
+ return runDatabricks(["postgres", "delete-snapshot", snapshotResource(args.instance, args.snapshot)], {
297
+ host: args.host
298
+ });
299
+ }
300
+ async function listSnapshots(args) {
301
+ return runDatabricks(["postgres", "list-snapshots", projectPath(args.instance), "-o", "json"], { host: args.host });
302
+ }
303
+
304
+ // scripts/lakebase/snapshot.cli.ts
305
+ function parseArgs(argv) {
306
+ const out2 = {};
307
+ let i = 0;
308
+ if (argv[0] && !argv[0].startsWith("-")) {
309
+ out2.subcommand = argv[0];
310
+ i = 1;
311
+ }
312
+ for (; i < argv.length; i++) {
313
+ switch (argv[i]) {
314
+ case "--instance":
315
+ out2.instance = argv[++i];
316
+ break;
317
+ case "--snapshot":
318
+ out2.snapshot = argv[++i];
319
+ break;
320
+ case "--branch":
321
+ out2.branch = argv[++i];
322
+ break;
323
+ case "--at-time":
324
+ out2.atTime = argv[++i];
325
+ break;
326
+ case "--at-lsn":
327
+ out2.atLsn = argv[++i];
328
+ break;
329
+ case "--ttl":
330
+ out2.ttl = argv[++i];
331
+ break;
332
+ case "--no-expiry":
333
+ out2.noExpiry = true;
334
+ break;
335
+ case "--host":
336
+ out2.host = argv[++i];
337
+ break;
338
+ case "--pretty":
339
+ out2.pretty = true;
340
+ break;
341
+ case "--help":
342
+ case "-h":
343
+ out2.help = true;
344
+ break;
345
+ }
346
+ }
347
+ return out2;
348
+ }
349
+ var USAGE = `usage: lakebase-snapshot <create|delete|list> --instance <project> [flags]
350
+ create --snapshot <id> --branch <src> [--at-time <iso> | --at-lsn <lsn>] [--ttl <s> | --no-expiry]
351
+ delete --snapshot <id>
352
+ list
353
+ Then branch from it: lakebase-branch create --branch <v> --from-snapshot <id> --instance <project>`;
354
+ function out(result, pretty) {
355
+ process.stdout.write(JSON.stringify(result, null, pretty ? 2 : void 0) + "\n");
356
+ }
357
+ function need(a, keys) {
358
+ const missing = keys.filter((k) => a[k] === void 0 || a[k] === "");
359
+ if (missing.length) {
360
+ process.stderr.write(`missing ${missing.map((k) => "--" + String(k)).join(", ")}
361
+ ${USAGE}
362
+ `);
363
+ return false;
364
+ }
365
+ return true;
366
+ }
367
+ async function main() {
368
+ const a = parseArgs(process.argv.slice(2));
369
+ if (a.help || !a.subcommand) {
370
+ process.stderr.write(USAGE + "\n");
371
+ return a.help ? 0 : 2;
372
+ }
373
+ switch (a.subcommand) {
374
+ case "create": {
375
+ if (!need(a, ["instance", "snapshot", "branch"])) return 2;
376
+ const raw = await createSnapshot({
377
+ instance: a.instance,
378
+ snapshot: a.snapshot,
379
+ sourceBranch: a.branch,
380
+ sourceBranchTime: a.atTime,
381
+ sourceBranchLsn: a.atLsn,
382
+ ttl: a.ttl,
383
+ noExpiry: a.noExpiry,
384
+ host: a.host
385
+ });
386
+ out({ created: a.snapshot, raw: raw.trim() || void 0 }, !!a.pretty);
387
+ return 0;
388
+ }
389
+ case "delete": {
390
+ if (!need(a, ["instance", "snapshot"])) return 2;
391
+ await deleteSnapshot({ instance: a.instance, snapshot: a.snapshot, host: a.host });
392
+ out({ deleted: a.snapshot }, !!a.pretty);
393
+ return 0;
394
+ }
395
+ case "list": {
396
+ if (!need(a, ["instance"])) return 2;
397
+ const raw = await listSnapshots({ instance: a.instance, host: a.host });
398
+ try {
399
+ out(JSON.parse(raw), !!a.pretty);
400
+ } catch {
401
+ process.stdout.write(raw);
402
+ }
403
+ return 0;
404
+ }
405
+ default:
406
+ process.stderr.write(`unknown subcommand '${a.subcommand}'
407
+ ${USAGE}
408
+ `);
409
+ return 2;
410
+ }
411
+ }
412
+ main().then(
413
+ (code) => process.exit(code),
414
+ (err) => {
415
+ process.stderr.write(String(err instanceof Error ? err.message : err) + "\n");
416
+ process.exit(1);
417
+ }
418
+ );
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node