quilt-sdk 0.0.6 → 0.0.8

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 (43) hide show
  1. package/README.md +50 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +606 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/core/client.d.ts +2 -2
  7. package/dist/core/client.d.ts.map +1 -1
  8. package/dist/core/client.js +20 -20
  9. package/dist/core/client.js.map +1 -1
  10. package/dist/core/http.js +2 -2
  11. package/dist/generated/platform-contract.d.ts +9329 -1570
  12. package/dist/generated/platform-contract.d.ts.map +1 -1
  13. package/dist/generated/platform-contract.js +350 -29
  14. package/dist/generated/platform-contract.js.map +1 -1
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +3 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/modules/agent.d.ts +5 -33
  20. package/dist/modules/agent.d.ts.map +1 -1
  21. package/dist/modules/agent.js.map +1 -1
  22. package/dist/modules/containers.d.ts +16 -35
  23. package/dist/modules/containers.d.ts.map +1 -1
  24. package/dist/modules/containers.js +7 -7
  25. package/dist/modules/containers.js.map +1 -1
  26. package/dist/modules/functions.d.ts +4 -32
  27. package/dist/modules/functions.d.ts.map +1 -1
  28. package/dist/modules/functions.js.map +1 -1
  29. package/dist/modules/images.d.ts +24 -0
  30. package/dist/modules/images.d.ts.map +1 -0
  31. package/dist/modules/images.js +42 -0
  32. package/dist/modules/images.js.map +1 -0
  33. package/dist/modules/platform.d.ts +10 -13
  34. package/dist/modules/platform.d.ts.map +1 -1
  35. package/dist/modules/platform.js +7 -7
  36. package/dist/modules/platform.js.map +1 -1
  37. package/dist/realtime/events.js +1 -1
  38. package/dist/realtime/terminal.js +1 -1
  39. package/package.json +12 -8
  40. package/dist/modules/master.d.ts +0 -10
  41. package/dist/modules/master.d.ts.map +0 -1
  42. package/dist/modules/master.js +0 -19
  43. package/dist/modules/master.js.map +0 -1
package/README.md CHANGED
@@ -10,12 +10,21 @@ Type-safe TypeScript SDK for Quilt production APIs.
10
10
  npm install quilt-sdk
11
11
  ```
12
12
 
13
+ Local package install exposes the SDK CLI through `npx quilt` and `npx quilt-sdk`.
14
+
13
15
  or
14
16
 
15
17
  ```bash
16
18
  bun add quilt-sdk
17
19
  ```
18
20
 
21
+ Global install exposes the same CLI directly:
22
+
23
+ ```bash
24
+ npm install -g quilt-sdk
25
+ quilt health
26
+ ```
27
+
19
28
  ## Requirements
20
29
 
21
30
  - Node `>=20.10.0`
@@ -65,6 +74,7 @@ Primary SDK surfaces:
65
74
  - `client.system` for health, info, and activity
66
75
  - `client.containers` for container lifecycle, exec, logs, metrics, snapshots, network, and GUI URLs
67
76
  - `client.platform` for cross-cutting routes such as operations, env maps, archives, jobs, ICC, OCI, and helper control flows
77
+ - `client.images` for OCI pull, inspect, history, remove, build-context upload, and OCI image builds
68
78
  - `client.volumes` for volume lifecycle and file browsing
69
79
  - `client.clusters` for cluster, node, workload, placement, and join-token control-plane flows
70
80
  - `client.agent` for join-token and node-token authenticated agent calls
@@ -108,6 +118,41 @@ const accepted = await client.containers.create(
108
118
  );
109
119
  ```
110
120
 
121
+ Build a local Docker context into Quilt's OCI store:
122
+
123
+ ```ts
124
+ import { readFile } from "node:fs/promises";
125
+ import { QuiltClient } from "quilt-sdk";
126
+
127
+ const client = QuiltClient.connect({
128
+ baseUrl: process.env.QUILT_BASE_URL ?? "https://backend.quilt.sh",
129
+ apiKey: process.env.QUILT_API_KEY,
130
+ });
131
+
132
+ const upload = await client.images.uploadBuildContext(
133
+ (await readFile("./context.tar.gz")).toString("base64"),
134
+ );
135
+
136
+ const accepted = await client.images.build({
137
+ context_id: upload.context_id,
138
+ image_reference: "docker.io/acme/my-app:latest",
139
+ dockerfile_path: "Dockerfile",
140
+ });
141
+ ```
142
+
143
+ ## CLI
144
+
145
+ The npm package now ships a CLI backed by the same SDK client.
146
+
147
+ Examples:
148
+
149
+ ```bash
150
+ npx quilt --api-key "$QUILT_API_KEY" health
151
+ npx quilt --api-key "$QUILT_API_KEY" oci pull --reference docker.io/library/alpine:3.20
152
+ npx quilt --api-key "$QUILT_API_KEY" build --context . --dockerfile Dockerfile --tag docker.io/acme/my-app:latest
153
+ npx quilt --api-key "$QUILT_API_KEY" container create --image docker.io/acme/my-app:latest --oci --wait -- sleep 60
154
+ ```
155
+
111
156
  ## Public Types
112
157
 
113
158
  The SDK ships declarations automatically through the package export.
@@ -138,6 +183,11 @@ Use the SDK in the same execution style the backend expects:
138
183
  - invoke a shell explicitly when shell parsing is required
139
184
  - prefer typed module methods over `client.raw(...)`
140
185
 
186
+ Recent contract-alignment details now reflected in the SDK:
187
+
188
+ - `client.containers.create()` and `createBatch()` accept the full backend request shape, including `gpu_count` and `gpu_ids`
189
+ - snapshot responses include `source_container_name` when the backend captured it at snapshot creation time
190
+
141
191
  ## Realtime
142
192
 
143
193
  ### SSE
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,606 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+ import { createInterface } from "node:readline/promises";
8
+ import { Writable } from "node:stream";
9
+ import { fileURLToPath } from "node:url";
10
+ import { create as createTarball } from "tar";
11
+ import { QuiltApiError, QuiltClient } from "./index.js";
12
+ class MutedOutput extends Writable {
13
+ delegate;
14
+ muted = false;
15
+ constructor(delegate) {
16
+ super();
17
+ this.delegate = delegate;
18
+ }
19
+ _write(chunk, encoding, callback) {
20
+ if (!this.muted) {
21
+ this.delegate.write(chunk, encoding);
22
+ }
23
+ callback();
24
+ }
25
+ }
26
+ async function main() {
27
+ const argv = process.argv.slice(2);
28
+ loadLocalEnv(argv);
29
+ const global = parseGlobalOptions(argv);
30
+ const auth = await ensureAuth(global.auth);
31
+ const client = QuiltClient.connect({
32
+ baseUrl: global.baseUrl,
33
+ auth,
34
+ });
35
+ const ctx = {
36
+ client,
37
+ json: global.json,
38
+ };
39
+ const commands = buildCommands();
40
+ const key = commandKey(global.rest);
41
+ const handler = commands.get(key);
42
+ if (!handler) {
43
+ printUsage();
44
+ process.exitCode = 1;
45
+ return;
46
+ }
47
+ try {
48
+ await handler(ctx, global.rest.slice(key.split(" ").length));
49
+ }
50
+ catch (error) {
51
+ renderError(error);
52
+ process.exitCode = 1;
53
+ }
54
+ }
55
+ function buildCommands() {
56
+ return new Map([
57
+ ["health", healthCommand],
58
+ ["build", buildCommand],
59
+ ["container create", containerCreateCommand],
60
+ ["container list", containerListCommand],
61
+ ["container get", containerGetCommand],
62
+ ["container logs", containerLogsCommand],
63
+ ["container remove", containerRemoveCommand],
64
+ ["container exec", containerExecCommand],
65
+ ["oci pull", ociPullCommand],
66
+ ["oci list", ociListCommand],
67
+ ["oci inspect", ociInspectCommand],
68
+ ["oci history", ociHistoryCommand],
69
+ ["operation get", operationGetCommand],
70
+ ["operation wait", operationWaitCommand],
71
+ ]);
72
+ }
73
+ function commandKey(args) {
74
+ if (args.length >= 2) {
75
+ const two = `${args[0]} ${args[1]}`;
76
+ if (two === "container create" ||
77
+ two === "container list" ||
78
+ two === "container get" ||
79
+ two === "container logs" ||
80
+ two === "container remove" ||
81
+ two === "container exec" ||
82
+ two === "oci pull" ||
83
+ two === "oci list" ||
84
+ two === "oci inspect" ||
85
+ two === "oci history" ||
86
+ two === "operation get" ||
87
+ two === "operation wait") {
88
+ return two;
89
+ }
90
+ }
91
+ return args[0] ?? "";
92
+ }
93
+ function parseGlobalOptions(argv) {
94
+ let json = false;
95
+ let baseUrl = process.env["QUILT_API_URL"] ?? process.env["QUILT_BASE_URL"] ?? "https://backend.quilt.sh";
96
+ let apiKey = process.env["QUILT_API_KEY"];
97
+ let token = process.env["QUILT_TOKEN"] ?? process.env["QUILT_JWT"];
98
+ const rest = [];
99
+ for (let index = 0; index < argv.length; index += 1) {
100
+ const value = argv[index];
101
+ if (value === "--json") {
102
+ json = true;
103
+ continue;
104
+ }
105
+ if (value === "--base-url") {
106
+ baseUrl = requiredValue(argv, ++index, "--base-url");
107
+ continue;
108
+ }
109
+ if (value === "--api-key") {
110
+ apiKey = requiredValue(argv, ++index, "--api-key");
111
+ continue;
112
+ }
113
+ if (value === "--token") {
114
+ token = requiredValue(argv, ++index, "--token");
115
+ continue;
116
+ }
117
+ rest.push(...argv.slice(index));
118
+ break;
119
+ }
120
+ const auth = apiKey
121
+ ? { type: "apiKey", apiKey }
122
+ : token
123
+ ? { type: "bearer", token }
124
+ : { type: "none" };
125
+ return { baseUrl, auth, json, rest };
126
+ }
127
+ function loadLocalEnv(argv) {
128
+ const cwd = process.cwd();
129
+ const preparse = {
130
+ noEnv: argv.includes("--no-env"),
131
+ envFile: firstValue(argv, "--env-file"),
132
+ };
133
+ if (preparse.noEnv) {
134
+ return;
135
+ }
136
+ for (const fileName of [".env", ".env.local"]) {
137
+ loadEnvFile(path.join(cwd, fileName));
138
+ }
139
+ if (preparse.envFile) {
140
+ loadEnvFile(path.resolve(cwd, preparse.envFile));
141
+ }
142
+ }
143
+ function loadEnvFile(filePath) {
144
+ if (!existsSync(filePath)) {
145
+ return;
146
+ }
147
+ const content = readFileSync(filePath, "utf8");
148
+ for (const [key, value] of Object.entries(parseDotEnv(content))) {
149
+ if (process.env[key] === undefined) {
150
+ process.env[key] = value;
151
+ }
152
+ }
153
+ }
154
+ function parseDotEnv(content) {
155
+ const parsed = {};
156
+ for (const rawLine of content.split(/\r?\n/u)) {
157
+ const line = rawLine.trim();
158
+ if (!line || line.startsWith("#")) {
159
+ continue;
160
+ }
161
+ const exportLine = line.startsWith("export ") ? line.slice(7).trim() : line;
162
+ const separator = exportLine.indexOf("=");
163
+ if (separator === -1) {
164
+ continue;
165
+ }
166
+ const key = exportLine.slice(0, separator).trim();
167
+ let value = exportLine.slice(separator + 1).trim();
168
+ if ((value.startsWith('"') && value.endsWith('"')) ||
169
+ (value.startsWith("'") && value.endsWith("'"))) {
170
+ value = value.slice(1, -1);
171
+ }
172
+ parsed[key] = value;
173
+ }
174
+ return parsed;
175
+ }
176
+ async function ensureAuth(auth) {
177
+ if (auth.type !== "none") {
178
+ return auth;
179
+ }
180
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
181
+ return auth;
182
+ }
183
+ const output = new MutedOutput(process.stderr);
184
+ const rl = createInterface({
185
+ input: process.stdin,
186
+ output,
187
+ terminal: true,
188
+ });
189
+ try {
190
+ process.stderr.write("Enter your API key: ");
191
+ output.muted = true;
192
+ const answer = (await rl.question("")).trim();
193
+ process.stderr.write("\n");
194
+ return answer.length > 0 ? { type: "apiKey", apiKey: answer } : auth;
195
+ }
196
+ finally {
197
+ output.muted = false;
198
+ rl.close();
199
+ }
200
+ }
201
+ async function healthCommand(ctx) {
202
+ const response = await ctx.client.system.health();
203
+ printResult(ctx, response);
204
+ }
205
+ async function buildCommand(ctx, args) {
206
+ const parsed = parseOptions(args, {
207
+ "--context": ".",
208
+ "--dockerfile": "Dockerfile",
209
+ "--tag": "",
210
+ "--target": "",
211
+ "--build-arg": [],
212
+ "--no-wait": false,
213
+ });
214
+ const tag = stringOption(parsed, "--tag");
215
+ if (!tag) {
216
+ throw new Error("build requires --tag <image-reference>");
217
+ }
218
+ const contextDir = path.resolve(stringOption(parsed, "--context") ?? ".");
219
+ const archive = await createContextArchive(contextDir);
220
+ const upload = await ctx.client.images.uploadBuildContext(archive.toString("base64"));
221
+ if (!upload) {
222
+ throw new Error("build context upload returned no response body");
223
+ }
224
+ const targetStage = optionalString(parsed, "--target");
225
+ const buildBody = {
226
+ context_id: upload.context_id,
227
+ image_reference: tag,
228
+ dockerfile_path: stringOption(parsed, "--dockerfile") ?? "Dockerfile",
229
+ build_args: buildArgMap(arrayOption(parsed, "--build-arg")),
230
+ ...(targetStage ? { target_stage: targetStage } : {}),
231
+ };
232
+ const accepted = await ctx.client.images.build(buildBody);
233
+ if (booleanOption(parsed, "--no-wait")) {
234
+ printResult(ctx, {
235
+ context_id: upload.context_id,
236
+ size_bytes: upload.size_bytes,
237
+ operation_id: accepted.operation_id,
238
+ status_url: accepted.status_url,
239
+ });
240
+ return;
241
+ }
242
+ const result = await waitForOperation(ctx.client, accepted);
243
+ printResult(ctx, result);
244
+ }
245
+ async function containerCreateCommand(ctx, args) {
246
+ const parsed = parseOptions(args, {
247
+ "--name": "",
248
+ "--image": "prod",
249
+ "--oci": false,
250
+ "--env": [],
251
+ "--memory": "",
252
+ "--cpu": "",
253
+ "--volume": [],
254
+ "--workdir": "",
255
+ "--wait": false,
256
+ "--": [],
257
+ });
258
+ const image = stringOption(parsed, "--image") ?? "prod";
259
+ const name = optionalString(parsed, "--name");
260
+ const oci = booleanOption(parsed, "--oci") || !["prod", "prod-gui"].includes(image);
261
+ const command = arrayOption(parsed, "--");
262
+ const memoryLimitMb = numberOption(parsed, "--memory");
263
+ const cpuLimitPercent = numberOption(parsed, "--cpu");
264
+ const workdir = optionalString(parsed, "--workdir");
265
+ const environment = keyValueMap(arrayOption(parsed, "--env"));
266
+ const volumes = arrayOption(parsed, "--volume");
267
+ const containerBody = {
268
+ ...(name ? { name } : {}),
269
+ image,
270
+ ...(oci ? { oci: true } : {}),
271
+ ...(Object.keys(environment).length > 0 ? { environment } : {}),
272
+ ...(memoryLimitMb !== undefined ? { memory_limit_mb: memoryLimitMb } : {}),
273
+ ...(cpuLimitPercent !== undefined ? { cpu_limit_percent: cpuLimitPercent } : {}),
274
+ ...(volumes.length > 0 ? { volumes } : {}),
275
+ ...(workdir ? { working_directory: workdir } : {}),
276
+ ...(command.length > 0 ? { command } : {}),
277
+ };
278
+ const accepted = await ctx.client.containers.create(containerBody, "async");
279
+ if (!booleanOption(parsed, "--wait")) {
280
+ printResult(ctx, accepted);
281
+ return;
282
+ }
283
+ const result = await waitForOperation(ctx.client, asAcceptedOperation(accepted));
284
+ printResult(ctx, result);
285
+ }
286
+ async function containerListCommand(ctx, args) {
287
+ const parsed = parseOptions(args, { "--state": "" });
288
+ const state = optionalString(parsed, "--state");
289
+ const response = await ctx.client.containers.list(state ? { state } : undefined);
290
+ printResult(ctx, response);
291
+ }
292
+ async function containerGetCommand(ctx, args) {
293
+ const identifier = args[0];
294
+ if (!identifier) {
295
+ throw new Error("container get requires <id-or-name>");
296
+ }
297
+ printResult(ctx, await ctx.client.containers.get(identifier));
298
+ }
299
+ async function containerLogsCommand(ctx, args) {
300
+ const identifier = args[0];
301
+ if (!identifier) {
302
+ throw new Error("container logs requires <id-or-name>");
303
+ }
304
+ const parsed = parseOptions(args.slice(1), { "--limit": "" });
305
+ const limit = numberOption(parsed, "--limit");
306
+ printResult(ctx, await ctx.client.containers.logs(identifier, limit ? { limit } : undefined));
307
+ }
308
+ async function containerRemoveCommand(ctx, args) {
309
+ const identifier = args[0];
310
+ if (!identifier) {
311
+ throw new Error("container remove requires <id-or-name>");
312
+ }
313
+ const parsed = parseOptions(args.slice(1), { "--wait": false });
314
+ const accepted = await ctx.client.containers.remove(identifier, "async");
315
+ if (!booleanOption(parsed, "--wait")) {
316
+ printResult(ctx, accepted);
317
+ return;
318
+ }
319
+ printResult(ctx, await waitForOperation(ctx.client, asAcceptedOperation(accepted)));
320
+ }
321
+ async function containerExecCommand(ctx, args) {
322
+ const identifier = args[0];
323
+ if (!identifier) {
324
+ throw new Error("container exec requires <id-or-name>");
325
+ }
326
+ const parsed = parseOptions(args.slice(1), {
327
+ "--workdir": "",
328
+ "--capture-output": false,
329
+ "--detach": false,
330
+ "--timeout-ms": "",
331
+ "--": [],
332
+ });
333
+ const command = arrayOption(parsed, "--");
334
+ if (command.length === 0) {
335
+ throw new Error("container exec requires a command after --");
336
+ }
337
+ const workdir = optionalString(parsed, "--workdir");
338
+ const timeoutMs = numberOption(parsed, "--timeout-ms");
339
+ printResult(ctx, await ctx.client.containers.exec(identifier, {
340
+ command,
341
+ ...(workdir ? { workdir } : {}),
342
+ ...(booleanOption(parsed, "--capture-output") ? { capture_output: true } : {}),
343
+ ...(booleanOption(parsed, "--detach") ? { detach: true } : {}),
344
+ ...(timeoutMs !== undefined ? { timeout_ms: timeoutMs } : {}),
345
+ }));
346
+ }
347
+ async function ociPullCommand(ctx, args) {
348
+ const parsed = parseOptions(args, {
349
+ "--reference": "",
350
+ "--force": false,
351
+ "--platform": "",
352
+ "--username": "",
353
+ "--password": "",
354
+ });
355
+ const reference = stringOption(parsed, "--reference");
356
+ if (!reference) {
357
+ throw new Error("oci pull requires --reference <oci-reference>");
358
+ }
359
+ const pullBody = {
360
+ reference,
361
+ force: booleanOption(parsed, "--force"),
362
+ };
363
+ const platform = optionalString(parsed, "--platform");
364
+ if (platform) {
365
+ pullBody.platform = platform;
366
+ }
367
+ const username = optionalString(parsed, "--username");
368
+ if (username) {
369
+ pullBody.registry_username = username;
370
+ }
371
+ const password = optionalString(parsed, "--password");
372
+ if (password) {
373
+ pullBody.registry_password = password;
374
+ }
375
+ printResult(ctx, await ctx.client.images.pull(pullBody));
376
+ }
377
+ async function ociListCommand(ctx, args) {
378
+ const parsed = parseOptions(args, { "--filter": "", "--include-digests": false });
379
+ const listQuery = {};
380
+ const filter = optionalString(parsed, "--filter");
381
+ if (filter) {
382
+ listQuery.filter = filter;
383
+ }
384
+ if (booleanOption(parsed, "--include-digests")) {
385
+ listQuery.include_digests = true;
386
+ }
387
+ printResult(ctx, await ctx.client.images.list(listQuery));
388
+ }
389
+ async function ociInspectCommand(ctx, args) {
390
+ const reference = firstValue(args, "--reference");
391
+ if (!reference) {
392
+ throw new Error("oci inspect requires --reference <oci-reference>");
393
+ }
394
+ printResult(ctx, await ctx.client.images.inspect(reference));
395
+ }
396
+ async function ociHistoryCommand(ctx, args) {
397
+ const reference = firstValue(args, "--reference");
398
+ if (!reference) {
399
+ throw new Error("oci history requires --reference <oci-reference>");
400
+ }
401
+ printResult(ctx, await ctx.client.images.history(reference));
402
+ }
403
+ async function operationGetCommand(ctx, args) {
404
+ const operationId = args[0];
405
+ if (!operationId) {
406
+ throw new Error("operation get requires <operation-id>");
407
+ }
408
+ printResult(ctx, await ctx.client.platform.getOperationStatus(operationId));
409
+ }
410
+ async function operationWaitCommand(ctx, args) {
411
+ const operationId = args[0];
412
+ if (!operationId) {
413
+ throw new Error("operation wait requires <operation-id>");
414
+ }
415
+ const parsed = parseOptions(args.slice(1), { "--timeout-ms": "", "--interval-ms": "" });
416
+ printResult(ctx, await ctx.client.awaitOperation(operationId, {
417
+ timeoutMs: numberOption(parsed, "--timeout-ms") ?? 300_000,
418
+ intervalMs: numberOption(parsed, "--interval-ms") ?? 1_000,
419
+ }));
420
+ }
421
+ async function createContextArchive(contextDir) {
422
+ const dockerignorePath = path.join(contextDir, ".dockerignore");
423
+ const ignore = await readDockerignore(dockerignorePath);
424
+ const tempDir = await mkdtemp(path.join(tmpdir(), "quilt-sdk-build-"));
425
+ const archivePath = path.join(tempDir, "context.tar.gz");
426
+ try {
427
+ await createTarball({
428
+ file: archivePath,
429
+ gzip: true,
430
+ cwd: contextDir,
431
+ portable: true,
432
+ filter: (entryPath) => shouldIncludeEntry(entryPath, ignore),
433
+ }, ["."]);
434
+ return await readFile(archivePath);
435
+ }
436
+ finally {
437
+ await rm(tempDir, { recursive: true, force: true });
438
+ }
439
+ }
440
+ async function readDockerignore(filePath) {
441
+ try {
442
+ const raw = await readFile(filePath, "utf8");
443
+ return raw
444
+ .split(/\r?\n/u)
445
+ .map((line) => line.trim())
446
+ .filter((line) => line.length > 0 && !line.startsWith("#"));
447
+ }
448
+ catch {
449
+ return [];
450
+ }
451
+ }
452
+ function shouldIncludeEntry(entryPath, ignore) {
453
+ const normalized = entryPath.replaceAll("\\", "/").replace(/^\.\/?/u, "");
454
+ if (normalized.length === 0) {
455
+ return true;
456
+ }
457
+ for (const pattern of ignore) {
458
+ const candidate = pattern.replaceAll("\\", "/").replace(/^\.\/?/u, "");
459
+ if (normalized === candidate || normalized.startsWith(`${candidate}/`)) {
460
+ return false;
461
+ }
462
+ }
463
+ return true;
464
+ }
465
+ async function waitForOperation(client, accepted) {
466
+ return await client.awaitOperation(accepted.operation_id, { timeoutMs: 600_000 });
467
+ }
468
+ function parseOptions(args, defaults) {
469
+ const values = new Map();
470
+ for (const [key, value] of Object.entries(defaults)) {
471
+ values.set(key, Array.isArray(value) ? [...value] : value);
472
+ }
473
+ for (let index = 0; index < args.length; index += 1) {
474
+ const token = args[index];
475
+ if (token === undefined) {
476
+ break;
477
+ }
478
+ if (token === "--") {
479
+ values.set("--", args.slice(index + 1));
480
+ break;
481
+ }
482
+ if (!token.startsWith("--")) {
483
+ const positional = values.get("_") ?? [];
484
+ positional.push(token);
485
+ values.set("_", positional);
486
+ continue;
487
+ }
488
+ const current = values.get(token);
489
+ if (typeof current === "boolean") {
490
+ values.set(token, true);
491
+ continue;
492
+ }
493
+ if (Array.isArray(current)) {
494
+ const next = requiredValue(args, ++index, token);
495
+ values.set(token, [...current, next]);
496
+ continue;
497
+ }
498
+ values.set(token, requiredValue(args, ++index, token));
499
+ }
500
+ return values;
501
+ }
502
+ function requiredValue(args, index, flag) {
503
+ const value = args[index];
504
+ if (!value) {
505
+ throw new Error(`${flag} requires a value`);
506
+ }
507
+ return value;
508
+ }
509
+ function asAcceptedOperation(value) {
510
+ if (typeof value === "object" &&
511
+ value !== null &&
512
+ "operation_id" in value &&
513
+ "status_url" in value) {
514
+ return value;
515
+ }
516
+ throw new Error("Expected an operation response from the API");
517
+ }
518
+ function stringOption(values, key) {
519
+ const value = values.get(key);
520
+ return typeof value === "string" ? value : undefined;
521
+ }
522
+ function optionalString(values, key) {
523
+ const value = stringOption(values, key);
524
+ return value && value.length > 0 ? value : undefined;
525
+ }
526
+ function booleanOption(values, key) {
527
+ return values.get(key) === true;
528
+ }
529
+ function arrayOption(values, key) {
530
+ const value = values.get(key);
531
+ return Array.isArray(value) ? value : [];
532
+ }
533
+ function numberOption(values, key) {
534
+ const value = optionalString(values, key);
535
+ return value ? Number(value) : undefined;
536
+ }
537
+ function keyValueMap(values) {
538
+ const entries = values.map((entry) => {
539
+ const [key, value] = splitPair(entry);
540
+ return [key, value];
541
+ });
542
+ return Object.fromEntries(entries);
543
+ }
544
+ function buildArgMap(values) {
545
+ return keyValueMap(values);
546
+ }
547
+ function splitPair(entry) {
548
+ const separator = entry.indexOf("=");
549
+ if (separator === -1) {
550
+ throw new Error(`Expected KEY=VALUE, got '${entry}'`);
551
+ }
552
+ return [entry.slice(0, separator), entry.slice(separator + 1)];
553
+ }
554
+ function firstValue(args, flag) {
555
+ const index = args.indexOf(flag);
556
+ return index === -1 ? undefined : args[index + 1];
557
+ }
558
+ function printResult(ctx, value) {
559
+ if (ctx.json) {
560
+ console.log(JSON.stringify(value, null, 2));
561
+ return;
562
+ }
563
+ if (typeof value === "string") {
564
+ console.log(value);
565
+ return;
566
+ }
567
+ console.log(JSON.stringify(value, null, 2));
568
+ }
569
+ function renderError(error) {
570
+ if (error instanceof QuiltApiError) {
571
+ console.error(error.message);
572
+ if (error.body) {
573
+ console.error(JSON.stringify(error.body, null, 2));
574
+ }
575
+ return;
576
+ }
577
+ if (error instanceof Error) {
578
+ console.error(error.message);
579
+ return;
580
+ }
581
+ console.error(String(error));
582
+ }
583
+ function printUsage() {
584
+ const scriptName = path.basename(fileURLToPath(import.meta.url), path.extname(fileURLToPath(import.meta.url)));
585
+ console.log([
586
+ `Usage: ${scriptName} [--base-url URL] [--api-key KEY|--token JWT] [--json] <command>`,
587
+ "",
588
+ "Commands:",
589
+ " health",
590
+ " build --tag <oci-ref> [--context dir] [--dockerfile Dockerfile] [--build-arg KEY=VALUE] [--target stage] [--no-wait]",
591
+ " container create [--name name] [--image ref-or-alias] [--oci] [--env KEY=VALUE] [--memory MB] [--cpu PERCENT] [--volume spec] [--workdir dir] [--wait] -- <command...>",
592
+ " container list [--state state]",
593
+ " container get <id-or-name>",
594
+ " container logs <id-or-name> [--limit N]",
595
+ " container remove <id-or-name> [--wait]",
596
+ " container exec <id-or-name> [--workdir dir] [--capture-output] [--detach] [--timeout-ms N] -- <command...>",
597
+ " oci pull --reference <oci-ref> [--force] [--platform platform] [--username user] [--password pass]",
598
+ " oci list [--filter value] [--include-digests]",
599
+ " oci inspect --reference <oci-ref>",
600
+ " oci history --reference <oci-ref>",
601
+ " operation get <operation-id>",
602
+ " operation wait <operation-id> [--timeout-ms N] [--interval-ms N]",
603
+ ].join("\n"));
604
+ }
605
+ void main();
606
+ //# sourceMappingURL=cli.js.map