gigadrive 2.7.4 → 2.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.
package/dist/index.cjs CHANGED
@@ -24,15 +24,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_cli16 = require("@effect/cli");
27
+ var import_cli17 = require("@effect/cli");
28
28
  var import_platform_node = require("@effect/platform-node");
29
29
  var import_network_config2 = require("@gigadrive/network-config");
30
- var import_effect30 = require("effect");
30
+ var import_effect31 = require("effect");
31
31
 
32
32
  // package.json
33
33
  var package_default = {
34
34
  name: "gigadrive",
35
- version: "2.7.4",
35
+ version: "2.8.0",
36
36
  repository: {
37
37
  type: "git",
38
38
  url: "https://github.com/Gigadrive/sdk.git",
@@ -355,6 +355,24 @@ var DeploymentFailedError = class extends import_effect.Schema.TaggedError()("De
355
355
  message: import_effect.Schema.String
356
356
  }) {
357
357
  };
358
+ var InvalidMicroVmOptionsError = class extends import_effect.Schema.TaggedError()(
359
+ "InvalidMicroVmOptionsError",
360
+ { message: import_effect.Schema.String }
361
+ ) {
362
+ };
363
+ var InvalidCopyTargetError = class extends import_effect.Schema.TaggedError()("InvalidCopyTargetError", {
364
+ message: import_effect.Schema.String
365
+ }) {
366
+ };
367
+ var LocalFileError = class extends import_effect.Schema.TaggedError()("LocalFileError", {
368
+ message: import_effect.Schema.String,
369
+ path: import_effect.Schema.String
370
+ }) {
371
+ };
372
+ var TerminalRequiredError = class extends import_effect.Schema.TaggedError()("TerminalRequiredError", {
373
+ message: import_effect.Schema.String
374
+ }) {
375
+ };
358
376
 
359
377
  // src/services/api-client.ts
360
378
  var import_sdk = require("@gigadrive/sdk");
@@ -559,6 +577,13 @@ var CLI_SCOPES = [
559
577
  "network:deployments:trigger",
560
578
  "network:ai_gateway:chat",
561
579
  "network:ai_gateway:models",
580
+ "network:microvms:read",
581
+ "network:microvms:write",
582
+ "network:microvms:exec",
583
+ "network:microvms:delete",
584
+ "network:microvm_images:read",
585
+ "network:microvm_images:write",
586
+ "network:microvm_images:delete",
562
587
  // Provisioning local-dev API credentials
563
588
  "platform:api_keys:read",
564
589
  "platform:api_keys:write",
@@ -2953,23 +2978,889 @@ var logoutCommand = import_cli11.Command.make(
2953
2978
  )
2954
2979
  );
2955
2980
 
2981
+ // src/commands/microvms/index.ts
2982
+ var import_cli12 = require("@effect/cli");
2983
+ var import_platform8 = require("@effect/platform");
2984
+ var import_sdk2 = require("@gigadrive/sdk");
2985
+ var import_effect24 = require("effect");
2986
+
2987
+ // src/lib/microvm.ts
2988
+ var REMOTE_PREFIX = "vm:";
2989
+ var parseCopyTarget = (value) => {
2990
+ if (!value.startsWith(REMOTE_PREFIX)) {
2991
+ return value.length === 0 ? "The path is empty." : { kind: "local", path: value };
2992
+ }
2993
+ const rest = value.slice(REMOTE_PREFIX.length);
2994
+ const separator = rest.indexOf(":");
2995
+ if (separator <= 0) return `"${value}" is not in the form vm:<id>:<path>.`;
2996
+ const microvmId = rest.slice(0, separator);
2997
+ const path2 = rest.slice(separator + 1);
2998
+ return { kind: "remote", microvmId, path: path2.length === 0 ? "." : path2 };
2999
+ };
3000
+ var posixBasename = (path2) => {
3001
+ const trimmed = path2.replace(/\/+$/, "");
3002
+ const index = trimmed.lastIndexOf("/");
3003
+ return index === -1 ? trimmed : trimmed.slice(index + 1);
3004
+ };
3005
+ var posixJoin = (...segments) => segments.filter((segment) => segment.length > 0).join("/").replace(/\/{2,}/g, "/");
3006
+ var relativeTo = (root, entry) => {
3007
+ const base = root.replace(/\/+$/, "");
3008
+ return entry.startsWith(`${base}/`) ? entry.slice(base.length + 1) : posixBasename(entry);
3009
+ };
3010
+ var parseKeyValues = (entries) => {
3011
+ const value = {};
3012
+ for (const entry of entries) {
3013
+ const index = entry.indexOf("=");
3014
+ if (index <= 0) return { ok: false, entry };
3015
+ value[entry.slice(0, index)] = entry.slice(index + 1);
3016
+ }
3017
+ return { ok: true, value };
3018
+ };
3019
+ var SIGNAL_NUMBERS = {
3020
+ SIGHUP: 1,
3021
+ SIGINT: 2,
3022
+ SIGQUIT: 3,
3023
+ SIGABRT: 6,
3024
+ SIGKILL: 9,
3025
+ SIGUSR1: 10,
3026
+ SIGSEGV: 11,
3027
+ SIGUSR2: 12,
3028
+ SIGPIPE: 13,
3029
+ SIGTERM: 15
3030
+ };
3031
+ var exitStatusOf = (command) => {
3032
+ if (command.exitCode !== null) return command.exitCode;
3033
+ if (command.signal) return 128 + (SIGNAL_NUMBERS[command.signal] ?? 0);
3034
+ return 1;
3035
+ };
3036
+ var formatMemory = (mb) => mb % 1024 === 0 ? `${mb / 1024} GiB` : `${mb} MiB`;
3037
+ var relativeTime = (iso, now = Date.now()) => {
3038
+ if (!iso) return "-";
3039
+ const delta = Date.parse(iso) - now;
3040
+ if (!Number.isFinite(delta)) return iso;
3041
+ const abs = Math.abs(delta);
3042
+ const text = abs < 6e4 ? `${Math.round(abs / 1e3)}s` : abs < 36e5 ? `${Math.round(abs / 6e4)}m` : abs < 864e5 ? `${Math.round(abs / 36e5)}h` : `${Math.round(abs / 864e5)}d`;
3043
+ return delta >= 0 ? `in ${text}` : `${text} ago`;
3044
+ };
3045
+ var formatTable = (rows) => {
3046
+ const widths = [];
3047
+ for (const row of rows) {
3048
+ row.forEach((cell, index) => {
3049
+ widths[index] = Math.max(widths[index] ?? 0, cell.length);
3050
+ });
3051
+ }
3052
+ return rows.map(
3053
+ (row) => row.map((cell, index) => index === row.length - 1 ? cell : cell.padEnd(widths[index] ?? 0)).join(" ").trimEnd()
3054
+ );
3055
+ };
3056
+ var formatMicroVmTable = (vms, now = Date.now()) => formatTable([
3057
+ ["ID", "STATUS", "IMAGE", "SIZE", "PORTS", "CREATED", "EXPIRES"],
3058
+ ...vms.map((vm) => [
3059
+ vm.id,
3060
+ vm.status,
3061
+ vm.image.name,
3062
+ `${vm.vcpus} vCPU, ${formatMemory(vm.memoryMb)}`,
3063
+ vm.ports.map((entry) => entry.port).join(",") || "-",
3064
+ relativeTime(vm.createdAt, now),
3065
+ vm.status === "running" || vm.status === "starting" ? relativeTime(vm.expiresAt, now) : "-"
3066
+ ])
3067
+ ]);
3068
+ var describeMicroVm = (vm, now = Date.now()) => {
3069
+ const lines = [
3070
+ `ID: ${vm.id}`,
3071
+ `Status: ${vm.status}${vm.stopReason ? ` (${vm.stopReason})` : ""}`,
3072
+ `Image: ${vm.image.name}`,
3073
+ `Size: ${vm.vcpus} vCPU, ${formatMemory(vm.memoryMb)} memory, ${formatMemory(vm.diskMb)} disk`,
3074
+ `Region: ${vm.region}`,
3075
+ `Network: ${vm.network.egress === "deny" ? "outbound traffic blocked" : "internet access"}`,
3076
+ `Application: ${vm.applicationId}`,
3077
+ `Created: ${vm.createdAt}`
3078
+ ];
3079
+ if (vm.expiresAt && (vm.status === "running" || vm.status === "starting")) {
3080
+ lines.push(`Expires: ${vm.expiresAt} (${relativeTime(vm.expiresAt, now)})`);
3081
+ }
3082
+ if (vm.stoppedAt) lines.push(`Stopped: ${vm.stoppedAt}`);
3083
+ const metadata = Object.entries(vm.metadata);
3084
+ if (metadata.length > 0) lines.push(`Metadata: ${metadata.map(([key, value]) => `${key}=${value}`).join(", ")}`);
3085
+ if (vm.ports.length > 0) {
3086
+ lines.push("Ports:");
3087
+ for (const entry of vm.ports) lines.push(` ${entry.port} ${entry.url}`);
3088
+ }
3089
+ return lines;
3090
+ };
3091
+ var imageSource = (image) => {
3092
+ if (image.builtin) return "built-in";
3093
+ const source = image.source;
3094
+ if (!source) return "-";
3095
+ return source.type === "dockerfile" ? "Dockerfile" : source.ref;
3096
+ };
3097
+ var formatImageTable = (images) => formatTable([
3098
+ ["NAME", "STATUS", "SOURCE", "ID"],
3099
+ ...images.map((image) => [image.name, image.status, imageSource(image), image.id])
3100
+ ]);
3101
+ var formatBytes = (bytes) => {
3102
+ if (bytes < 1024) return `${bytes} B`;
3103
+ const units = ["KiB", "MiB", "GiB", "TiB"];
3104
+ let value = bytes / 1024;
3105
+ let unit = 0;
3106
+ while (value >= 1024 && unit < units.length - 1) {
3107
+ value /= 1024;
3108
+ unit += 1;
3109
+ }
3110
+ return `${value.toFixed(1)} ${units[unit]}`;
3111
+ };
3112
+ var concatBytes = (chunks) => {
3113
+ if (chunks.length === 1) return chunks[0];
3114
+ const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
3115
+ const result = new Uint8Array(total);
3116
+ let offset = 0;
3117
+ for (const chunk of chunks) {
3118
+ result.set(chunk, offset);
3119
+ offset += chunk.length;
3120
+ }
3121
+ return result;
3122
+ };
3123
+ var StdinBatcher = class {
3124
+ constructor(send, options = {}) {
3125
+ this.send = send;
3126
+ this.options = options;
3127
+ this.delayMs = options.delayMs ?? 8;
3128
+ this.maxBytes = options.maxBytes ?? 64 * 1024;
3129
+ }
3130
+ send;
3131
+ options;
3132
+ pending = [];
3133
+ pendingBytes = 0;
3134
+ timer;
3135
+ inFlight;
3136
+ closed = false;
3137
+ delayMs;
3138
+ maxBytes;
3139
+ /** Queues a chunk. Ignored after {@link close}. */
3140
+ push(chunk) {
3141
+ if (this.closed || chunk.length === 0) return;
3142
+ this.pending.push(chunk);
3143
+ this.pendingBytes += chunk.length;
3144
+ if (this.pendingBytes >= this.maxBytes) this.schedule(0);
3145
+ else if (this.timer === void 0) this.schedule(this.delayMs);
3146
+ }
3147
+ /** Sends everything queued so far, after any request already in flight. */
3148
+ async flush() {
3149
+ while (this.inFlight) await this.inFlight;
3150
+ if (this.pending.length === 0) return;
3151
+ const data = concatBytes(this.pending);
3152
+ this.pending = [];
3153
+ this.pendingBytes = 0;
3154
+ this.inFlight = this.send(data).catch((error) => this.options.onError?.(error)).finally(() => {
3155
+ this.inFlight = void 0;
3156
+ });
3157
+ await this.inFlight;
3158
+ }
3159
+ /** Stops accepting chunks and sends what is left. */
3160
+ async close() {
3161
+ this.closed = true;
3162
+ if (this.timer !== void 0) clearTimeout(this.timer);
3163
+ this.timer = void 0;
3164
+ await this.flush();
3165
+ }
3166
+ schedule(delay) {
3167
+ if (this.timer !== void 0) clearTimeout(this.timer);
3168
+ this.timer = setTimeout(() => {
3169
+ this.timer = void 0;
3170
+ void this.flush();
3171
+ }, delay);
3172
+ }
3173
+ };
3174
+
3175
+ // src/lib/microvm-session.ts
3176
+ async function attachCommand(command, io, options) {
3177
+ const batcher = new StdinBatcher((data) => command.write(data), {
3178
+ // Input after the command exited is expected to fail; nothing to report.
3179
+ onError: () => void 0
3180
+ });
3181
+ const cleanups = [];
3182
+ if (options.forwardInput) {
3183
+ cleanups.push(
3184
+ io.onInput(
3185
+ (chunk) => batcher.push(chunk),
3186
+ () => {
3187
+ if (!options.endInputOnEof) return;
3188
+ void batcher.close().then(() => command.end()).catch(() => void 0);
3189
+ }
3190
+ )
3191
+ );
3192
+ }
3193
+ if (options.forwardResize) {
3194
+ cleanups.push(
3195
+ io.onResize(() => {
3196
+ const size = io.size();
3197
+ if (size) void command.resize(size.cols, size.rows).catch(() => void 0);
3198
+ })
3199
+ );
3200
+ }
3201
+ try {
3202
+ for await (const chunk of command.logs()) io.write(chunk.data, chunk.stream);
3203
+ } finally {
3204
+ for (const cleanup of cleanups) cleanup();
3205
+ await batcher.close();
3206
+ }
3207
+ return command.status === "exited" ? command.info : await command.wait();
3208
+ }
3209
+ var nodeSessionIo = () => ({
3210
+ onInput: (listener, onEnd) => {
3211
+ const encoder = new TextEncoder();
3212
+ const onData = (chunk) => listener(typeof chunk === "string" ? encoder.encode(chunk) : chunk);
3213
+ process.stdin.on("data", onData);
3214
+ process.stdin.on("end", onEnd);
3215
+ process.stdin.resume();
3216
+ return () => {
3217
+ process.stdin.off("data", onData);
3218
+ process.stdin.off("end", onEnd);
3219
+ process.stdin.pause();
3220
+ };
3221
+ },
3222
+ write: (data, stream) => {
3223
+ (stream === "stderr" ? process.stderr : process.stdout).write(data);
3224
+ },
3225
+ size: () => process.stdout.isTTY ? { cols: process.stdout.columns, rows: process.stdout.rows } : void 0,
3226
+ onResize: (listener) => {
3227
+ process.stdout.on("resize", listener);
3228
+ return () => {
3229
+ process.stdout.off("resize", listener);
3230
+ };
3231
+ }
3232
+ });
3233
+
3234
+ // src/commands/microvms/index.ts
3235
+ var CONSOLE_URL2 = "https://console.gigadrive.de";
3236
+ var MICROVM_STATUSES = [
3237
+ "pending",
3238
+ "starting",
3239
+ "running",
3240
+ "stopping",
3241
+ "stopped",
3242
+ "failed"
3243
+ ];
3244
+ var DEFAULT_SHELL = ["/bin/sh", "-c", "if command -v bash >/dev/null 2>&1; then exec bash -l; else exec sh -l; fi"];
3245
+ var appOption6 = import_cli12.Options.text("app").pipe(
3246
+ import_cli12.Options.withAlias("a"),
3247
+ import_cli12.Options.withDescription("Application ID (defaults to the linked application)"),
3248
+ import_cli12.Options.optional
3249
+ );
3250
+ var jsonOption2 = import_cli12.Options.boolean("json").pipe(import_cli12.Options.withDescription("Print the result as JSON"));
3251
+ var microvmIdArg = import_cli12.Args.text({ name: "microvm-id" }).pipe(import_cli12.Args.withDescription("The microVM ID"));
3252
+ var envOption = import_cli12.Options.text("env").pipe(
3253
+ import_cli12.Options.withAlias("e"),
3254
+ import_cli12.Options.withDescription("Environment variable as KEY=VALUE (repeatable)"),
3255
+ import_cli12.Options.repeated
3256
+ );
3257
+ var userOption = import_cli12.Options.text("user").pipe(
3258
+ import_cli12.Options.withAlias("u"),
3259
+ import_cli12.Options.withDescription("Run as this user (default: the image user)"),
3260
+ import_cli12.Options.optional
3261
+ );
3262
+ var cwdOption = import_cli12.Options.text("cwd").pipe(
3263
+ import_cli12.Options.withAlias("w"),
3264
+ import_cli12.Options.withDescription("Working directory inside the microVM"),
3265
+ import_cli12.Options.optional
3266
+ );
3267
+ var errorMessage2 = (action, error) => {
3268
+ switch (error._tag) {
3269
+ case "NotAuthenticatedError":
3270
+ return 'You are not logged in. Run "gigadrive login" to authenticate.';
3271
+ case "ApiRequestError":
3272
+ if (error.code === "microvm_not_running") {
3273
+ return `${error.message}
3274
+ The microVM has stopped. Create a new one with "gigadrive microvms create".`;
3275
+ }
3276
+ if (error.code === "quota_exceeded" || error.code === "plan_limit_exceeded") {
3277
+ return `${error.message}
3278
+ Stop microVMs you no longer need, or raise your plan's limits at ${CONSOLE_URL2}.`;
3279
+ }
3280
+ if (error.code === "microvm_busy") {
3281
+ return `${error.message}
3282
+ The microVM is busy. Wait a moment and try again.`;
3283
+ }
3284
+ if (error.code === "microvm_unreachable") {
3285
+ return `${error.message}
3286
+ The microVM could not be reached. Try again; if it keeps failing, check "gigadrive microvms get <id>".`;
3287
+ }
3288
+ if (error.code === "image_not_ready") {
3289
+ return `${error.message}
3290
+ The image is not built yet. Check it with "gigadrive microvms images ls".`;
3291
+ }
3292
+ return `Failed to ${action}: ${error.message}`;
3293
+ default:
3294
+ return error.message;
3295
+ }
3296
+ };
3297
+ var reportErrors2 = (action) => (self) => import_effect24.Effect.tapError(self, (error) => import_effect24.Console.error(errorMessage2(action, error)));
3298
+ var parseDuration = (flag, value) => import_effect24.Effect.try({
3299
+ try: () => {
3300
+ if (/^\d+(\.\d+)?$/.test(value.trim())) throw new RangeError("missing unit");
3301
+ return (0, import_sdk2.durationToMs)(value);
3302
+ },
3303
+ catch: () => new InvalidMicroVmOptionsError({
3304
+ message: `${flag} must be a duration with a unit, such as 90s, 10m or 1h (got "${value}").`
3305
+ })
3306
+ });
3307
+ var optionalDuration = (flag, value) => import_effect24.Option.match(value, {
3308
+ onNone: () => import_effect24.Effect.succeed(void 0),
3309
+ onSome: (text) => parseDuration(flag, text)
3310
+ });
3311
+ var parseLifetime = (flag, value) => parseDuration(flag, value).pipe(
3312
+ import_effect24.Effect.filterOrFail(
3313
+ (ms) => ms >= 1 && ms <= import_sdk2.MICROVM_MAX_TIMEOUT_MS,
3314
+ () => new InvalidMicroVmOptionsError({ message: `${flag} can be at most 24h (got "${value}").` })
3315
+ )
3316
+ );
3317
+ var keyValues = (flag, entries) => {
3318
+ const parsed = parseKeyValues(entries);
3319
+ return parsed.ok ? import_effect24.Effect.succeed(entries.length > 0 ? parsed.value : void 0) : import_effect24.Effect.fail(new InvalidMicroVmOptionsError({ message: `${flag} expects KEY=VALUE, got "${parsed.entry}".` }));
3320
+ };
3321
+ var resolveApplicationId = (app) => import_effect24.Option.match(app, {
3322
+ onSome: (id) => import_effect24.Effect.succeed(id),
3323
+ onNone: () => import_effect24.Effect.gen(function* () {
3324
+ const projectLink = yield* ProjectLinkService;
3325
+ const link = yield* projectLink.load(process.cwd());
3326
+ return import_effect24.Option.match(link, { onNone: () => void 0, onSome: (value) => value.applicationId });
3327
+ })
3328
+ });
3329
+ var withClient = (run) => import_effect24.Effect.gen(function* () {
3330
+ const apiClient = yield* ApiClientService;
3331
+ const client = yield* apiClient.getClient;
3332
+ return yield* import_effect24.Effect.tryPromise({ try: () => run(client), catch: toApiRequestError });
3333
+ });
3334
+ var signalOnInterrupt = (signal) => (command, exit) => import_effect24.Exit.isInterrupted(exit) ? import_effect24.Effect.tryPromise(() => command.kill(signal)).pipe(import_effect24.Effect.timeout("2 seconds"), import_effect24.Effect.ignore) : import_effect24.Effect.void;
3335
+ var createInput = (flags) => import_effect24.Effect.gen(function* () {
3336
+ const env = yield* keyValues("--env", flags.env);
3337
+ const metadata = yield* keyValues("--metadata", flags.metadata);
3338
+ const timeoutMs = yield* import_effect24.Option.match(flags.timeout, {
3339
+ onNone: () => import_effect24.Effect.succeed(void 0),
3340
+ onSome: (text) => parseLifetime("--timeout", text)
3341
+ });
3342
+ const badPort = flags.ports.find((port) => !Number.isInteger(port) || port < 1 || port > 65535);
3343
+ if (badPort !== void 0) {
3344
+ return yield* new InvalidMicroVmOptionsError({ message: `--port must be between 1 and 65535 (got ${badPort}).` });
3345
+ }
3346
+ const applicationId = yield* resolveApplicationId(flags.app);
3347
+ const input = {
3348
+ ...applicationId ? { applicationId } : {},
3349
+ ...import_effect24.Option.match(flags.image, { onNone: () => ({}), onSome: (image) => ({ image }) }),
3350
+ ...import_effect24.Option.match(flags.vcpus, {
3351
+ onNone: () => ({}),
3352
+ onSome: (vcpus) => ({ vcpus: Number(vcpus) })
3353
+ }),
3354
+ ...import_effect24.Option.match(flags.memory, { onNone: () => ({}), onSome: (memoryMb) => ({ memoryMb }) }),
3355
+ ...import_effect24.Option.match(flags.disk, { onNone: () => ({}), onSome: (diskMb) => ({ diskMb }) }),
3356
+ ...timeoutMs !== void 0 ? { timeoutMs } : {},
3357
+ ...flags.ports.length > 0 ? { ports: [...flags.ports] } : {},
3358
+ ...env ? { env } : {},
3359
+ ...metadata ? { metadata } : {},
3360
+ ...flags.denyEgress ? { network: { egress: "deny" } } : {},
3361
+ ...import_effect24.Option.match(flags.region, { onNone: () => ({}), onSome: (region) => ({ region }) })
3362
+ };
3363
+ return input;
3364
+ });
3365
+ var createMicroVm = (flags) => import_effect24.Effect.gen(function* () {
3366
+ const apiClient = yield* ApiClientService;
3367
+ const input = yield* createInput(flags);
3368
+ if (!flags.json) yield* import_effect24.Console.error(`Creating a microVM from ${input.image ?? "node22"}...`);
3369
+ const vm = yield* apiClient.request((client) => client.microvms.create(input));
3370
+ if (flags.json) {
3371
+ yield* import_effect24.Console.log(JSON.stringify(vm, null, 2));
3372
+ return vm;
3373
+ }
3374
+ for (const line of describeMicroVm(vm)) yield* import_effect24.Console.log(line);
3375
+ yield* import_effect24.Console.log(`
3376
+ Run a command: gigadrive microvms exec ${vm.id} -- <command>`);
3377
+ yield* import_effect24.Console.log(`Open a shell: gigadrive microvms shell ${vm.id}`);
3378
+ return vm;
3379
+ });
3380
+ var createCommand = import_cli12.Command.make(
3381
+ "create",
3382
+ {
3383
+ app: appOption6,
3384
+ image: import_cli12.Options.text("image").pipe(
3385
+ import_cli12.Options.withAlias("i"),
3386
+ import_cli12.Options.withDescription(
3387
+ "Image name or ID: node22, node24, python3.13, python3.12, bun1, debian12, ubuntu24.04, or a custom image (default: node22)"
3388
+ ),
3389
+ import_cli12.Options.optional
3390
+ ),
3391
+ vcpus: import_cli12.Options.choice("vcpus", ["1", "2", "4"]).pipe(
3392
+ import_cli12.Options.withDescription("Number of vCPUs (default: 1)"),
3393
+ import_cli12.Options.optional
3394
+ ),
3395
+ memory: import_cli12.Options.integer("memory").pipe(import_cli12.Options.withDescription("Memory in MiB (default: 2048)"), import_cli12.Options.optional),
3396
+ disk: import_cli12.Options.integer("disk").pipe(import_cli12.Options.withDescription("Disk in MiB (default: 10240)"), import_cli12.Options.optional),
3397
+ timeout: import_cli12.Options.text("timeout").pipe(
3398
+ import_cli12.Options.withAlias("t"),
3399
+ import_cli12.Options.withDescription("Lifetime before it stops automatically, such as 30m or 2h (default: 5m)"),
3400
+ import_cli12.Options.optional
3401
+ ),
3402
+ ports: import_cli12.Options.integer("port").pipe(
3403
+ import_cli12.Options.withAlias("p"),
3404
+ import_cli12.Options.withDescription("Port to expose on a public preview URL (repeatable, up to 8)"),
3405
+ import_cli12.Options.repeated
3406
+ ),
3407
+ env: envOption,
3408
+ metadata: import_cli12.Options.text("metadata").pipe(
3409
+ import_cli12.Options.withAlias("m"),
3410
+ import_cli12.Options.withDescription("Metadata as KEY=VALUE (repeatable)"),
3411
+ import_cli12.Options.repeated
3412
+ ),
3413
+ denyEgress: import_cli12.Options.boolean("deny-egress").pipe(import_cli12.Options.withDescription("Block all outbound network traffic")),
3414
+ region: import_cli12.Options.text("region").pipe(import_cli12.Options.withDescription("Region to place the microVM in"), import_cli12.Options.optional),
3415
+ json: jsonOption2
3416
+ },
3417
+ (flags) => createMicroVm(flags).pipe(import_effect24.Effect.asVoid, reportErrors2("create the microVM"))
3418
+ ).pipe(import_cli12.Command.withDescription("Create a microVM and wait until it runs"));
3419
+ var listMicroVms = (flags) => import_effect24.Effect.gen(function* () {
3420
+ const apiClient = yield* ApiClientService;
3421
+ const applicationId = yield* resolveApplicationId(flags.app);
3422
+ const status = import_effect24.Option.getOrUndefined(flags.status);
3423
+ const showAll = flags.all || status !== void 0;
3424
+ const vms = yield* apiClient.request(async (client) => {
3425
+ const items = [];
3426
+ for await (const vm of client.microvms.listAll({ applicationId, status, limit: Math.min(flags.limit, 100) })) {
3427
+ if (!showAll && (vm.status === "stopped" || vm.status === "failed")) continue;
3428
+ items.push(vm);
3429
+ if (items.length >= flags.limit) break;
3430
+ }
3431
+ return items;
3432
+ });
3433
+ if (flags.json) return yield* import_effect24.Console.log(JSON.stringify(vms, null, 2));
3434
+ if (vms.length === 0) {
3435
+ return yield* import_effect24.Console.log(
3436
+ showAll ? "No microVMs found." : 'No running microVMs. Create one with "gigadrive microvms create".'
3437
+ );
3438
+ }
3439
+ for (const line of formatMicroVmTable(vms)) yield* import_effect24.Console.log(line);
3440
+ });
3441
+ var listCommand2 = import_cli12.Command.make(
3442
+ "ls",
3443
+ {
3444
+ app: appOption6,
3445
+ status: import_cli12.Options.choice("status", MICROVM_STATUSES).pipe(
3446
+ import_cli12.Options.withAlias("s"),
3447
+ import_cli12.Options.withDescription("Only microVMs in this status"),
3448
+ import_cli12.Options.optional
3449
+ ),
3450
+ all: import_cli12.Options.boolean("all").pipe(import_cli12.Options.withDescription("Include stopped and failed microVMs")),
3451
+ limit: import_cli12.Options.integer("limit").pipe(
3452
+ import_cli12.Options.withDescription("Show at most this many (default: 50)"),
3453
+ import_cli12.Options.withDefault(50)
3454
+ ),
3455
+ json: jsonOption2
3456
+ },
3457
+ (flags) => listMicroVms(flags).pipe(reportErrors2("list microVMs"))
3458
+ ).pipe(import_cli12.Command.withDescription("List microVMs"));
3459
+ var getCommand = import_cli12.Command.make(
3460
+ "get",
3461
+ { microvmId: microvmIdArg, json: jsonOption2 },
3462
+ ({ microvmId, json }) => import_effect24.Effect.gen(function* () {
3463
+ const apiClient = yield* ApiClientService;
3464
+ const vm = yield* apiClient.request((client) => client.microvms.get(microvmId));
3465
+ if (json) return yield* import_effect24.Console.log(JSON.stringify(vm, null, 2));
3466
+ for (const line of describeMicroVm(vm)) yield* import_effect24.Console.log(line);
3467
+ }).pipe(reportErrors2("get the microVM"))
3468
+ ).pipe(import_cli12.Command.withDescription("Show a microVM"));
3469
+ var execInMicroVm = (flags, io = nodeSessionIo()) => import_effect24.Effect.gen(function* () {
3470
+ const env = yield* keyValues("--env", flags.env);
3471
+ const timeout = yield* optionalDuration("--timeout", flags.timeout);
3472
+ const [cmd, ...args] = flags.command;
3473
+ const options = {
3474
+ cwd: import_effect24.Option.getOrUndefined(flags.cwd),
3475
+ env,
3476
+ user: import_effect24.Option.getOrUndefined(flags.user),
3477
+ sudo: flags.sudo,
3478
+ timeout,
3479
+ stdin: flags.interactive
3480
+ };
3481
+ const info = yield* import_effect24.Effect.acquireUseRelease(
3482
+ withClient(async (client) => {
3483
+ const vm = await import_sdk2.MicroVM.get(flags.microvmId, { client });
3484
+ return args.length > 0 ? vm.spawn(cmd, args, options) : vm.spawn(cmd, options);
3485
+ }),
3486
+ (command) => import_effect24.Effect.tryPromise({
3487
+ try: () => attachCommand(command, io, {
3488
+ forwardInput: flags.interactive,
3489
+ endInputOnEof: true,
3490
+ forwardResize: false
3491
+ }),
3492
+ catch: toApiRequestError
3493
+ }),
3494
+ signalOnInterrupt("SIGINT")
3495
+ );
3496
+ return exitStatusOf(info);
3497
+ });
3498
+ var setExitCode = (status) => import_effect24.Effect.sync(() => {
3499
+ process.exitCode = status;
3500
+ });
3501
+ var execCommand = import_cli12.Command.make(
3502
+ "exec",
3503
+ {
3504
+ microvmId: microvmIdArg,
3505
+ command: import_cli12.Args.text({ name: "command" }).pipe(
3506
+ import_cli12.Args.withDescription('The command and its arguments, after "--". One quoted argument runs through /bin/sh -c'),
3507
+ import_cli12.Args.atLeast(1)
3508
+ ),
3509
+ cwd: cwdOption,
3510
+ env: envOption,
3511
+ user: userOption,
3512
+ sudo: import_cli12.Options.boolean("sudo").pipe(import_cli12.Options.withDescription("Run as root")),
3513
+ timeout: import_cli12.Options.text("timeout").pipe(
3514
+ import_cli12.Options.withAlias("t"),
3515
+ import_cli12.Options.withDescription("Kill the command after this long, such as 30s or 10m"),
3516
+ import_cli12.Options.optional
3517
+ ),
3518
+ interactive: import_cli12.Options.boolean("interactive").pipe(
3519
+ import_cli12.Options.withAlias("i"),
3520
+ import_cli12.Options.withDescription("Forward local stdin to the command")
3521
+ )
3522
+ },
3523
+ (flags) => execInMicroVm(flags).pipe(import_effect24.Effect.flatMap(setExitCode), reportErrors2("run the command"))
3524
+ ).pipe(import_cli12.Command.withDescription("Run a command in a microVM and exit with its exit code: exec <id> -- <command...>"));
3525
+ var openShell = (flags, io = nodeSessionIo()) => import_effect24.Effect.gen(function* () {
3526
+ if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
3527
+ return yield* new TerminalRequiredError({
3528
+ message: 'microvms shell needs an interactive terminal. Use "gigadrive microvms exec <id> -- <command>" in scripts.'
3529
+ });
3530
+ }
3531
+ const term = yield* import_effect24.Config.string("TERM").pipe(import_effect24.Config.withDefault("xterm-256color"));
3532
+ const size = io.size() ?? { cols: 80, rows: 24 };
3533
+ const [cmd, ...args] = import_effect24.Option.match(flags.shell, { onNone: () => DEFAULT_SHELL, onSome: (shell) => [shell] });
3534
+ const options = {
3535
+ pty: { cols: size.cols, rows: size.rows },
3536
+ env: { TERM: term },
3537
+ cwd: import_effect24.Option.getOrUndefined(flags.cwd),
3538
+ user: import_effect24.Option.getOrUndefined(flags.user)
3539
+ };
3540
+ const info = yield* import_effect24.Effect.acquireUseRelease(
3541
+ withClient(async (client) => {
3542
+ const vm = await import_sdk2.MicroVM.get(flags.microvmId, { client });
3543
+ return args.length > 0 ? vm.spawn(cmd, args, options) : vm.spawn(cmd, options);
3544
+ }),
3545
+ (command) => import_effect24.Effect.acquireUseRelease(
3546
+ import_effect24.Effect.sync(() => process.stdin.setRawMode(true)),
3547
+ () => import_effect24.Effect.tryPromise({
3548
+ try: () => attachCommand(command, io, { forwardInput: true, endInputOnEof: false, forwardResize: true }),
3549
+ catch: toApiRequestError
3550
+ }),
3551
+ () => import_effect24.Effect.sync(() => process.stdin.setRawMode(false))
3552
+ ),
3553
+ signalOnInterrupt("SIGHUP")
3554
+ );
3555
+ return exitStatusOf(info);
3556
+ });
3557
+ var shellCommand = import_cli12.Command.make(
3558
+ "shell",
3559
+ {
3560
+ microvmId: microvmIdArg,
3561
+ shell: import_cli12.Options.text("shell").pipe(
3562
+ import_cli12.Options.withDescription("Program to run instead of the login shell, such as python3"),
3563
+ import_cli12.Options.optional
3564
+ ),
3565
+ cwd: cwdOption,
3566
+ user: userOption
3567
+ },
3568
+ (flags) => openShell(flags).pipe(import_effect24.Effect.flatMap(setExitCode), reportErrors2("open a shell"))
3569
+ ).pipe(import_cli12.Command.withDescription("Open an interactive shell in a microVM"));
3570
+ var localError = (path2, action) => () => new LocalFileError({ message: `Failed to ${action} ${path2}`, path: path2 });
3571
+ var parseTarget = (value) => {
3572
+ const target = parseCopyTarget(value);
3573
+ return typeof target === "string" ? import_effect24.Effect.fail(new InvalidCopyTargetError({ message: target })) : import_effect24.Effect.succeed(target);
3574
+ };
3575
+ var remoteStat = (client, microvmId, path2) => import_effect24.Effect.tryPromise({ try: () => client.microvms.files.stat(microvmId, path2), catch: toApiRequestError }).pipe(
3576
+ import_effect24.Effect.map(import_effect24.Option.some),
3577
+ import_effect24.Effect.catchIf(
3578
+ (error) => error.statusCode === 404,
3579
+ () => import_effect24.Effect.succeed(import_effect24.Option.none())
3580
+ )
3581
+ );
3582
+ var upload = (client, source, target) => import_effect24.Effect.gen(function* () {
3583
+ const fs = yield* import_platform8.FileSystem.FileSystem;
3584
+ const path2 = yield* import_platform8.Path.Path;
3585
+ const info = yield* fs.stat(source).pipe(import_effect24.Effect.mapError(localError(source, "read")));
3586
+ const existing = yield* remoteStat(client, target.microvmId, target.path);
3587
+ const intoDirectory = target.path.endsWith("/") || import_effect24.Option.exists(existing, (entry) => entry.type === "directory");
3588
+ const destination = intoDirectory ? posixJoin(target.path, path2.basename(source)) : target.path;
3589
+ const files = [];
3590
+ if (info.type === "Directory") {
3591
+ const entries = yield* fs.readDirectory(source, { recursive: true }).pipe(import_effect24.Effect.mapError(localError(source, "list")));
3592
+ for (const entry of entries) {
3593
+ const local = path2.join(source, entry);
3594
+ const stat = yield* fs.stat(local).pipe(import_effect24.Effect.mapError(localError(local, "read")));
3595
+ if (stat.type === "File")
3596
+ files.push({ local, remote: posixJoin(destination, entry.split(path2.sep).join("/")) });
3597
+ }
3598
+ } else {
3599
+ files.push({ local: source, remote: destination });
3600
+ }
3601
+ let bytes = 0;
3602
+ for (const file of files) {
3603
+ const content = yield* fs.readFile(file.local).pipe(import_effect24.Effect.mapError(localError(file.local, "read")));
3604
+ const result = yield* import_effect24.Effect.tryPromise({
3605
+ try: () => client.microvms.files.write(target.microvmId, file.remote, content),
3606
+ catch: toApiRequestError
3607
+ });
3608
+ bytes += result.bytes;
3609
+ }
3610
+ return { files: files.length, bytes };
3611
+ });
3612
+ var download = (client, source, target) => import_effect24.Effect.gen(function* () {
3613
+ const fs = yield* import_platform8.FileSystem.FileSystem;
3614
+ const path2 = yield* import_platform8.Path.Path;
3615
+ const entry = yield* import_effect24.Effect.tryPromise({
3616
+ try: () => client.microvms.files.stat(source.microvmId, source.path),
3617
+ catch: toApiRequestError
3618
+ });
3619
+ const targetExists = yield* fs.exists(target).pipe(import_effect24.Effect.mapError(localError(target, "check")));
3620
+ const targetIsDirectory = targetExists ? (yield* fs.stat(target).pipe(import_effect24.Effect.mapError(localError(target, "read")))).type === "Directory" : false;
3621
+ const intoDirectory = targetIsDirectory || target.endsWith(path2.sep) || target.endsWith("/");
3622
+ const destination = intoDirectory ? path2.join(target, posixBasename(entry.path)) : target;
3623
+ const files = [];
3624
+ if (entry.type === "directory") {
3625
+ const listing = yield* import_effect24.Effect.tryPromise({
3626
+ try: () => client.microvms.files.list(source.microvmId, entry.path, { recursive: true }),
3627
+ catch: toApiRequestError
3628
+ });
3629
+ if (listing.truncated) {
3630
+ yield* import_effect24.Console.error(
3631
+ `${entry.path} has too many files to list at once; only part of it is copied. Copy its subdirectories separately.`
3632
+ );
3633
+ }
3634
+ for (const child of listing.entries) {
3635
+ if (child.type !== "file") continue;
3636
+ const segments = relativeTo(entry.path, child.path).split("/");
3637
+ if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
3638
+ yield* import_effect24.Console.error(`Skipped ${child.path}: its path leaves ${entry.path}.`);
3639
+ continue;
3640
+ }
3641
+ files.push({ remote: child.path, local: path2.join(destination, ...segments) });
3642
+ }
3643
+ yield* fs.makeDirectory(destination, { recursive: true }).pipe(import_effect24.Effect.mapError(localError(destination, "create")));
3644
+ } else {
3645
+ files.push({ remote: entry.path, local: destination });
3646
+ }
3647
+ let bytes = 0;
3648
+ for (const file of files) {
3649
+ const content = yield* import_effect24.Effect.tryPromise({
3650
+ try: () => client.microvms.files.read(source.microvmId, file.remote),
3651
+ catch: toApiRequestError
3652
+ });
3653
+ yield* fs.makeDirectory(path2.dirname(file.local), { recursive: true }).pipe(import_effect24.Effect.mapError(localError(file.local, "create the directory of")));
3654
+ yield* fs.writeFile(file.local, content).pipe(import_effect24.Effect.mapError(localError(file.local, "write")));
3655
+ bytes += content.length;
3656
+ }
3657
+ return { files: files.length, bytes };
3658
+ });
3659
+ var copyFiles = (sourceSpec, targetSpec) => import_effect24.Effect.gen(function* () {
3660
+ const source = yield* parseTarget(sourceSpec);
3661
+ const target = yield* parseTarget(targetSpec);
3662
+ const apiClient = yield* ApiClientService;
3663
+ const client = yield* apiClient.getClient;
3664
+ if (source.kind === "local" && target.kind === "remote") return yield* upload(client, source.path, target);
3665
+ if (source.kind === "remote" && target.kind === "local") return yield* download(client, source, target.path);
3666
+ return yield* new InvalidCopyTargetError({
3667
+ message: 'One side must be local and the other vm:<id>:<path>, e.g. "gigadrive microvms cp ./app vm:<id>:/app".'
3668
+ });
3669
+ });
3670
+ var copyCommand = import_cli12.Command.make(
3671
+ "cp",
3672
+ {
3673
+ source: import_cli12.Args.text({ name: "source" }).pipe(import_cli12.Args.withDescription("Local path or vm:<id>:<path>")),
3674
+ target: import_cli12.Args.text({ name: "target" }).pipe(import_cli12.Args.withDescription("Local path or vm:<id>:<path>"))
3675
+ },
3676
+ ({ source, target }) => copyFiles(source, target).pipe(
3677
+ import_effect24.Effect.flatMap(
3678
+ (result) => import_effect24.Console.log(`Copied ${result.files} file${result.files === 1 ? "" : "s"} (${formatBytes(result.bytes)}).`)
3679
+ ),
3680
+ reportErrors2("copy")
3681
+ )
3682
+ ).pipe(import_cli12.Command.withDescription("Copy files between this machine and a microVM (vm:<id>:<path>)"));
3683
+ var microvmIdsArg = import_cli12.Args.text({ name: "microvm-id" }).pipe(
3684
+ import_cli12.Args.withDescription("One or more microVM IDs"),
3685
+ import_cli12.Args.atLeast(1)
3686
+ );
3687
+ var stopCommand = import_cli12.Command.make(
3688
+ "stop",
3689
+ { microvmIds: microvmIdsArg },
3690
+ ({ microvmIds }) => import_effect24.Effect.forEach(
3691
+ microvmIds,
3692
+ (microvmId) => import_effect24.Effect.gen(function* () {
3693
+ const apiClient = yield* ApiClientService;
3694
+ yield* apiClient.request((client) => client.microvms.stop(microvmId));
3695
+ yield* import_effect24.Console.log(`Stopped ${microvmId}.`);
3696
+ }),
3697
+ { discard: true }
3698
+ ).pipe(reportErrors2("stop the microVM"))
3699
+ ).pipe(import_cli12.Command.withDescription("Stop microVMs; their disk and memory are discarded"));
3700
+ var removeCommand2 = import_cli12.Command.make(
3701
+ "rm",
3702
+ { microvmIds: microvmIdsArg },
3703
+ ({ microvmIds }) => import_effect24.Effect.forEach(
3704
+ microvmIds,
3705
+ (microvmId) => import_effect24.Effect.gen(function* () {
3706
+ const apiClient = yield* ApiClientService;
3707
+ yield* apiClient.request((client) => client.microvms.delete(microvmId));
3708
+ yield* import_effect24.Console.log(`Deleted ${microvmId}.`);
3709
+ }),
3710
+ { discard: true }
3711
+ ).pipe(reportErrors2("delete the microVM"))
3712
+ ).pipe(import_cli12.Command.withDescription("Delete microVMs, stopping them first"));
3713
+ var extendMicroVm = (microvmId, duration) => import_effect24.Effect.gen(function* () {
3714
+ const timeoutMs = yield* parseLifetime("The duration", duration);
3715
+ const apiClient = yield* ApiClientService;
3716
+ const vm = yield* apiClient.request((client) => client.microvms.extendTimeout(microvmId, timeoutMs));
3717
+ yield* import_effect24.Console.log(`${vm.id} now stops at ${vm.expiresAt ?? "an unknown time"}.`);
3718
+ return vm;
3719
+ });
3720
+ var extendCommand = import_cli12.Command.make(
3721
+ "extend",
3722
+ {
3723
+ microvmId: microvmIdArg,
3724
+ duration: import_cli12.Args.text({ name: "duration" }).pipe(
3725
+ import_cli12.Args.withDescription("New remaining lifetime from now, such as 30m")
3726
+ )
3727
+ },
3728
+ ({ microvmId, duration }) => extendMicroVm(microvmId, duration).pipe(import_effect24.Effect.asVoid, reportErrors2("extend the microVM"))
3729
+ ).pipe(import_cli12.Command.withDescription("Set how long a microVM keeps running, counted from now"));
3730
+ var imagesListCommand = import_cli12.Command.make(
3731
+ "ls",
3732
+ { app: appOption6, json: jsonOption2 },
3733
+ ({ app, json }) => import_effect24.Effect.gen(function* () {
3734
+ const apiClient = yield* ApiClientService;
3735
+ const applicationId = yield* resolveApplicationId(app);
3736
+ const images = yield* apiClient.request((client) => client.microvms.images.list({ applicationId }));
3737
+ if (json) return yield* import_effect24.Console.log(JSON.stringify(images, null, 2));
3738
+ for (const line of formatImageTable(images)) yield* import_effect24.Console.log(line);
3739
+ }).pipe(reportErrors2("list images"))
3740
+ ).pipe(import_cli12.Command.withDescription("List built-in and custom images"));
3741
+ var createImage = (flags) => import_effect24.Effect.gen(function* () {
3742
+ const fs = yield* import_platform8.FileSystem.FileSystem;
3743
+ const apiClient = yield* ApiClientService;
3744
+ if (import_effect24.Option.isSome(flags.from) === import_effect24.Option.isSome(flags.dockerfile)) {
3745
+ return yield* new InvalidMicroVmOptionsError({
3746
+ message: "Pass exactly one of --from <registry reference> or --dockerfile <path>."
3747
+ });
3748
+ }
3749
+ const from = import_effect24.Option.isSome(flags.from) ? flags.from.value : {
3750
+ dockerfile: yield* fs.readFileString(import_effect24.Option.getOrElse(flags.dockerfile, () => "Dockerfile")).pipe(
3751
+ import_effect24.Effect.mapError(
3752
+ localError(
3753
+ import_effect24.Option.getOrElse(flags.dockerfile, () => "Dockerfile"),
3754
+ "read"
3755
+ )
3756
+ )
3757
+ )
3758
+ };
3759
+ const applicationId = yield* resolveApplicationId(flags.app);
3760
+ let image = yield* apiClient.request(
3761
+ (client) => client.microvms.images.create({ name: flags.name, from, ...applicationId ? { applicationId } : {} })
3762
+ );
3763
+ if (flags.wait) {
3764
+ if (!flags.json) yield* import_effect24.Console.error(`Building ${image.name}...`);
3765
+ image = yield* apiClient.request(
3766
+ (client) => client.microvms.images.waitUntilReady(image.id, {
3767
+ onStatus: (current) => {
3768
+ if (!flags.json) process.stderr.write(` ${current.status}
3769
+ `);
3770
+ }
3771
+ })
3772
+ );
3773
+ }
3774
+ if (flags.json) {
3775
+ yield* import_effect24.Console.log(JSON.stringify(image, null, 2));
3776
+ } else if (image.status === "ready") {
3777
+ yield* import_effect24.Console.log(`${image.name} is ready. Use it with: gigadrive microvms create --image ${image.name}`);
3778
+ } else {
3779
+ yield* import_effect24.Console.log(
3780
+ `${image.name} is ${image.status} (${image.id}). Check it with "gigadrive microvms images ls".`
3781
+ );
3782
+ }
3783
+ return image;
3784
+ });
3785
+ var imagesCreateCommand = import_cli12.Command.make(
3786
+ "create",
3787
+ {
3788
+ name: import_cli12.Args.text({ name: "name" }).pipe(
3789
+ import_cli12.Args.withDescription('Image name: lowercase letters, digits, ".", "_", "-"')
3790
+ ),
3791
+ app: appOption6,
3792
+ from: import_cli12.Options.text("from").pipe(
3793
+ import_cli12.Options.withDescription("Public linux/amd64 registry image, such as python:3.13 or ghcr.io/acme/tool:1"),
3794
+ import_cli12.Options.optional
3795
+ ),
3796
+ dockerfile: import_cli12.Options.text("dockerfile").pipe(
3797
+ import_cli12.Options.withAlias("f"),
3798
+ import_cli12.Options.withDescription("Build from this Dockerfile instead"),
3799
+ import_cli12.Options.optional
3800
+ ),
3801
+ wait: import_cli12.Options.boolean("wait").pipe(import_cli12.Options.withDescription("Wait until the build finished")),
3802
+ json: jsonOption2
3803
+ },
3804
+ (flags) => createImage(flags).pipe(import_effect24.Effect.asVoid, reportErrors2("create the image"))
3805
+ ).pipe(import_cli12.Command.withDescription("Build a custom image from a registry image or a Dockerfile"));
3806
+ var removeImage = (idOrName, app) => import_effect24.Effect.gen(function* () {
3807
+ const apiClient = yield* ApiClientService;
3808
+ const applicationId = yield* resolveApplicationId(app);
3809
+ const images = yield* apiClient.request((client) => client.microvms.images.list({ applicationId }));
3810
+ const image = images.find((candidate) => candidate.id === idOrName) ?? images.find((candidate) => !candidate.builtin && candidate.name === idOrName) ?? { id: idOrName, name: idOrName };
3811
+ yield* apiClient.request((client) => client.microvms.images.delete(image.id));
3812
+ yield* import_effect24.Console.log(`Deleted image ${image.name}.`);
3813
+ });
3814
+ var imagesRemoveCommand = import_cli12.Command.make(
3815
+ "rm",
3816
+ {
3817
+ image: import_cli12.Args.text({ name: "image" }).pipe(import_cli12.Args.withDescription("Image ID or name")),
3818
+ app: appOption6
3819
+ },
3820
+ ({ image, app }) => removeImage(image, app).pipe(reportErrors2("delete the image"))
3821
+ ).pipe(import_cli12.Command.withDescription("Delete a custom image"));
3822
+ var imagesCommand = import_cli12.Command.make("images", {}, () => import_effect24.Effect.void).pipe(
3823
+ import_cli12.Command.withDescription("Built-in and custom microVM images"),
3824
+ import_cli12.Command.withSubcommands([imagesListCommand, imagesCreateCommand, imagesRemoveCommand])
3825
+ );
3826
+ var SUBCOMMANDS = [
3827
+ createCommand,
3828
+ listCommand2,
3829
+ getCommand,
3830
+ execCommand,
3831
+ shellCommand,
3832
+ copyCommand,
3833
+ stopCommand,
3834
+ removeCommand2,
3835
+ extendCommand,
3836
+ imagesCommand
3837
+ ];
3838
+ var microvmsCommand = import_cli12.Command.make("microvms", {}, () => import_effect24.Effect.void).pipe(
3839
+ import_cli12.Command.withDescription("Isolated Linux machines for running code: create, exec, shell, cp, stop"),
3840
+ import_cli12.Command.withSubcommands(SUBCOMMANDS)
3841
+ );
3842
+ var vmCommand = import_cli12.Command.make("vm", {}, () => import_effect24.Effect.void).pipe(
3843
+ import_cli12.Command.withDescription('Alias of "gigadrive microvms"'),
3844
+ import_cli12.Command.withSubcommands(SUBCOMMANDS)
3845
+ );
3846
+
2956
3847
  // src/commands/platform/index.ts
2957
- var import_cli13 = require("@effect/cli");
2958
- var import_effect27 = require("effect");
3848
+ var import_cli14 = require("@effect/cli");
3849
+ var import_effect28 = require("effect");
2959
3850
 
2960
3851
  // src/commands/platform/deploy/index.ts
2961
- var import_cli12 = require("@effect/cli");
2962
- var import_platform9 = require("@effect/platform");
3852
+ var import_cli13 = require("@effect/cli");
3853
+ var import_platform10 = require("@effect/platform");
2963
3854
  var import_commons = require("@gigadrive/commons");
2964
- var import_effect26 = require("effect");
3855
+ var import_effect27 = require("effect");
2965
3856
  var os4 = __toESM(require("os"), 1);
2966
3857
  var path = __toESM(require("path"), 1);
2967
3858
 
2968
3859
  // src/services/archive.ts
2969
- var import_platform8 = require("@effect/platform");
3860
+ var import_platform9 = require("@effect/platform");
2970
3861
  var import_build_utils3 = require("@gigadrive/build-utils");
2971
3862
  var import_archiver = __toESM(require("archiver"), 1);
2972
- var import_effect24 = require("effect");
3863
+ var import_effect25 = require("effect");
2973
3864
  var import_ignore = __toESM(require("ignore"), 1);
2974
3865
  var nodeFs = __toESM(require("fs"), 1);
2975
3866
  var nodeFsPromises = __toESM(require("fs/promises"), 1);
@@ -2994,15 +3885,15 @@ var CONTAINER_BUILD_FILE_PATTERNS = [
2994
3885
  ];
2995
3886
  var DOCKER_BUILD_IGNORE_FILE = ".dockerignore";
2996
3887
  var readIgnoreFile = (fs, ignorePath) => fs.readFileString(ignorePath, "utf8").pipe(
2997
- import_effect24.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
2998
- import_effect24.Effect.catchAll(() => import_effect24.Effect.succeed([]))
3888
+ import_effect25.Effect.map((content) => content.split("\n").filter((line) => line.trim() !== "")),
3889
+ import_effect25.Effect.catchAll(() => import_effect25.Effect.succeed([]))
2999
3890
  );
3000
- var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules, ignoreFileNames) => import_effect24.Effect.gen(function* () {
3001
- const items = yield* fs.readDirectory(dir).pipe(import_effect24.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
3891
+ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules, ignoreFileNames) => import_effect25.Effect.gen(function* () {
3892
+ const items = yield* fs.readDirectory(dir).pipe(import_effect25.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${dir}` })));
3002
3893
  for (const item of items) {
3003
3894
  const fullPath = pathService.join(dir, item);
3004
3895
  const relativePath = pathService.relative(baseDir, fullPath);
3005
- const stat = yield* import_effect24.Effect.tryPromise({
3896
+ const stat = yield* import_effect25.Effect.tryPromise({
3006
3897
  try: () => nodeFsPromises.lstat(fullPath),
3007
3898
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
3008
3899
  });
@@ -3022,8 +3913,8 @@ var collectIgnorePatterns = (fs, pathService, dir, baseDir, ignoreRules, ignoreF
3022
3913
  yield* collectIgnorePatterns(fs, pathService, fullPath, baseDir, ignoreRules, ignoreFileNames);
3023
3914
  }
3024
3915
  }
3025
- }).pipe(import_effect24.Effect.catchAll(() => import_effect24.Effect.void));
3026
- var initializeIgnoreRules = (fs, pathService, baseDir, useIgnoreFiles, useManagedIgnore, buildsContainerImages) => import_effect24.Effect.gen(function* () {
3916
+ }).pipe(import_effect25.Effect.catchAll(() => import_effect25.Effect.void));
3917
+ var initializeIgnoreRules = (fs, pathService, baseDir, useIgnoreFiles, useManagedIgnore, buildsContainerImages) => import_effect25.Effect.gen(function* () {
3027
3918
  const ignoreRules = (0, import_ignore.default)();
3028
3919
  const ignoreFileNames = buildsContainerImages ? IGNORE_FILE_NAMES.filter((name) => name !== DOCKER_BUILD_IGNORE_FILE) : IGNORE_FILE_NAMES;
3029
3920
  if (useManagedIgnore) {
@@ -3044,14 +3935,14 @@ var initializeIgnoreRules = (fs, pathService, baseDir, useIgnoreFiles, useManage
3044
3935
  }
3045
3936
  return ignoreRules;
3046
3937
  });
3047
- var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect24.Effect.gen(function* () {
3938
+ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect25.Effect.gen(function* () {
3048
3939
  const filesToInclude = [];
3049
- const walkDir = (currentDir) => import_effect24.Effect.gen(function* () {
3050
- const items = yield* fs.readDirectory(currentDir).pipe(import_effect24.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
3940
+ const walkDir = (currentDir) => import_effect25.Effect.gen(function* () {
3941
+ const items = yield* fs.readDirectory(currentDir).pipe(import_effect25.Effect.mapError(() => new ArchiveCreateError({ message: `Failed to read directory: ${currentDir}` })));
3051
3942
  for (const item of items) {
3052
3943
  const fullPath = pathService.join(currentDir, item);
3053
3944
  const relativePath = pathService.relative(dir, fullPath);
3054
- const stat = yield* import_effect24.Effect.tryPromise({
3945
+ const stat = yield* import_effect25.Effect.tryPromise({
3055
3946
  try: () => nodeFsPromises.lstat(fullPath),
3056
3947
  catch: () => new ArchiveCreateError({ message: `Failed to stat: ${fullPath}` })
3057
3948
  });
@@ -3065,19 +3956,19 @@ var getFilesToInclude = (fs, pathService, dir, ignoreRules) => import_effect24.E
3065
3956
  }
3066
3957
  }
3067
3958
  }
3068
- }).pipe(import_effect24.Effect.catchAll(() => import_effect24.Effect.void));
3959
+ }).pipe(import_effect25.Effect.catchAll(() => import_effect25.Effect.void));
3069
3960
  yield* walkDir(dir);
3070
3961
  return filesToInclude;
3071
3962
  });
3072
- var ArchiveService = class extends import_effect24.Effect.Service()("ArchiveService", {
3963
+ var ArchiveService = class extends import_effect25.Effect.Service()("ArchiveService", {
3073
3964
  accessors: true,
3074
- effect: import_effect24.Effect.gen(function* () {
3075
- const fs = yield* import_platform8.FileSystem.FileSystem;
3076
- const pathService = yield* import_platform8.Path.Path;
3077
- const createZipArchive = import_effect24.Effect.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
3078
- yield* import_effect24.Effect.annotateCurrentSpan("inputDir", inputDir);
3079
- yield* import_effect24.Effect.annotateCurrentSpan("outputFile", outputFile);
3080
- yield* import_effect24.Effect.log("Creating zip archive", { inputDir, outputFile });
3965
+ effect: import_effect25.Effect.gen(function* () {
3966
+ const fs = yield* import_platform9.FileSystem.FileSystem;
3967
+ const pathService = yield* import_platform9.Path.Path;
3968
+ const createZipArchive = import_effect25.Effect.fn("ArchiveService.createZipArchive")(function* (inputDir, outputFile, options = {}) {
3969
+ yield* import_effect25.Effect.annotateCurrentSpan("inputDir", inputDir);
3970
+ yield* import_effect25.Effect.annotateCurrentSpan("outputFile", outputFile);
3971
+ yield* import_effect25.Effect.log("Creating zip archive", { inputDir, outputFile });
3081
3972
  const ignoreRules = yield* initializeIgnoreRules(
3082
3973
  fs,
3083
3974
  pathService,
@@ -3086,7 +3977,7 @@ var ArchiveService = class extends import_effect24.Effect.Service()("ArchiveServ
3086
3977
  options.useManagedIgnore !== false,
3087
3978
  options.buildsContainerImages === true
3088
3979
  );
3089
- const filesToInclude = options.whitelist ? yield* import_effect24.Effect.tryPromise({
3980
+ const filesToInclude = options.whitelist ? yield* import_effect25.Effect.tryPromise({
3090
3981
  try: () => Promise.all(
3091
3982
  options.whitelist.map((file) => (0, import_build_utils3.getFilesForPattern)(file, inputDir, options.excludeFiles ?? []))
3092
3983
  ),
@@ -3094,25 +3985,25 @@ var ArchiveService = class extends import_effect24.Effect.Service()("ArchiveServ
3094
3985
  message: "Failed to resolve whitelist patterns",
3095
3986
  cause: error instanceof Error ? error.message : String(error)
3096
3987
  })
3097
- }) : yield* import_effect24.Effect.gen(function* () {
3988
+ }) : yield* import_effect25.Effect.gen(function* () {
3098
3989
  if (options.excludeFiles && options.excludeFiles.length > 0) {
3099
3990
  ignoreRules.add(options.excludeFiles);
3100
3991
  }
3101
3992
  return yield* getFilesToInclude(fs, pathService, inputDir, ignoreRules).pipe(
3102
- import_effect24.Effect.map((files) => [files])
3993
+ import_effect25.Effect.map((files) => [files])
3103
3994
  );
3104
3995
  });
3105
- const archiveExists = yield* fs.exists(outputFile).pipe(import_effect24.Effect.catchAll(() => import_effect24.Effect.succeed(false)));
3996
+ const archiveExists = yield* fs.exists(outputFile).pipe(import_effect25.Effect.catchAll(() => import_effect25.Effect.succeed(false)));
3106
3997
  if (archiveExists) {
3107
3998
  yield* fs.remove(outputFile).pipe(
3108
- import_effect24.Effect.mapError(
3999
+ import_effect25.Effect.mapError(
3109
4000
  (error) => new ArchiveCreateError({
3110
4001
  message: `Failed to remove existing archive: ${String(error)}`
3111
4002
  })
3112
4003
  )
3113
4004
  );
3114
4005
  }
3115
- yield* import_effect24.Effect.tryPromise({
4006
+ yield* import_effect25.Effect.tryPromise({
3116
4007
  try: () => new Promise((resolve, reject) => {
3117
4008
  const output = nodeFs.createWriteStream(outputFile);
3118
4009
  const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
@@ -3131,14 +4022,14 @@ var ArchiveService = class extends import_effect24.Effect.Service()("ArchiveServ
3131
4022
  })
3132
4023
  });
3133
4024
  const fileInfo = yield* fs.stat(outputFile).pipe(
3134
- import_effect24.Effect.mapError(
4025
+ import_effect25.Effect.mapError(
3135
4026
  (error) => new ArchiveCreateError({
3136
4027
  message: `Failed to stat archive: ${String(error)}`
3137
4028
  })
3138
4029
  )
3139
4030
  );
3140
4031
  const size = Number(fileInfo.size);
3141
- yield* import_effect24.Effect.log("Archive created", { size });
4032
+ yield* import_effect25.Effect.log("Archive created", { size });
3142
4033
  return { path: outputFile, size };
3143
4034
  });
3144
4035
  return { createZipArchive };
@@ -3147,32 +4038,32 @@ var ArchiveService = class extends import_effect24.Effect.Service()("ArchiveServ
3147
4038
  };
3148
4039
 
3149
4040
  // src/services/deployment-api.ts
3150
- var import_effect25 = require("effect");
3151
- var DeploymentApiService = class extends import_effect25.Effect.Service()("DeploymentApiService", {
4041
+ var import_effect26 = require("effect");
4042
+ var DeploymentApiService = class extends import_effect26.Effect.Service()("DeploymentApiService", {
3152
4043
  accessors: true,
3153
4044
  dependencies: [ApiClientService.Default],
3154
- effect: import_effect25.Effect.gen(function* () {
4045
+ effect: import_effect26.Effect.gen(function* () {
3155
4046
  const apiClient = yield* ApiClientService;
3156
4047
  const getClient = apiClient.getClient.pipe(
3157
- import_effect25.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
4048
+ import_effect26.Effect.mapError((err) => new DeploymentAuthError({ message: `Authentication failed: ${err.message}` }))
3158
4049
  );
3159
- const createDeployment = import_effect25.Effect.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
3160
- yield* import_effect25.Effect.annotateCurrentSpan("applicationId", applicationId);
3161
- yield* import_effect25.Effect.log("Creating deployment", { applicationId });
4050
+ const createDeployment = import_effect26.Effect.fn("DeploymentApiService.createDeployment")(function* (applicationId) {
4051
+ yield* import_effect26.Effect.annotateCurrentSpan("applicationId", applicationId);
4052
+ yield* import_effect26.Effect.log("Creating deployment", { applicationId });
3162
4053
  const client = yield* getClient;
3163
- const deployment = yield* import_effect25.Effect.tryPromise({
4054
+ const deployment = yield* import_effect26.Effect.tryPromise({
3164
4055
  try: () => client.deployments.create({ applicationId }),
3165
4056
  catch: (error) => new DeploymentCreateError({
3166
4057
  message: `Failed to create deployment: ${error instanceof Error ? error.message : String(error)}`
3167
4058
  })
3168
4059
  });
3169
- yield* import_effect25.Effect.log("Deployment created", { deploymentId: deployment.id });
4060
+ yield* import_effect26.Effect.log("Deployment created", { deploymentId: deployment.id });
3170
4061
  return deployment.id;
3171
4062
  });
3172
- const startMultipartUpload = import_effect25.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
3173
- yield* import_effect25.Effect.annotateCurrentSpan("deploymentId", deploymentId);
4063
+ const startMultipartUpload = import_effect26.Effect.fn("DeploymentApiService.startMultipartUpload")(function* (deploymentId) {
4064
+ yield* import_effect26.Effect.annotateCurrentSpan("deploymentId", deploymentId);
3174
4065
  const client = yield* getClient;
3175
- const result = yield* import_effect25.Effect.tryPromise({
4066
+ const result = yield* import_effect26.Effect.tryPromise({
3176
4067
  try: () => client.deployments.startUpload(deploymentId),
3177
4068
  catch: (error) => new UploadStartError({
3178
4069
  message: `Failed to start multipart upload: ${error instanceof Error ? error.message : String(error)}`
@@ -3180,9 +4071,9 @@ var DeploymentApiService = class extends import_effect25.Effect.Service()("Deplo
3180
4071
  });
3181
4072
  return result.uploadId;
3182
4073
  });
3183
- const getPresignedUrl = import_effect25.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
4074
+ const getPresignedUrl = import_effect26.Effect.fn("DeploymentApiService.getPresignedUrl")(function* (deploymentId, uploadId, partNumber) {
3184
4075
  const client = yield* getClient;
3185
- const result = yield* import_effect25.Effect.tryPromise({
4076
+ const result = yield* import_effect26.Effect.tryPromise({
3186
4077
  try: () => client.deployments.getPresignedUrl(deploymentId, uploadId, partNumber),
3187
4078
  catch: (error) => new PresignedUrlError({
3188
4079
  message: `Failed to get presigned URL: ${error instanceof Error ? error.message : String(error)}`,
@@ -3191,11 +4082,11 @@ var DeploymentApiService = class extends import_effect25.Effect.Service()("Deplo
3191
4082
  });
3192
4083
  return result.url;
3193
4084
  });
3194
- const uploadPart = import_effect25.Effect.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
3195
- yield* import_effect25.Effect.annotateCurrentSpan("partNumber", partNumber);
3196
- yield* import_effect25.Effect.annotateCurrentSpan("size", data.length);
4085
+ const uploadPart = import_effect26.Effect.fn("DeploymentApiService.uploadPart")(function* (presignedUrl, data, partNumber) {
4086
+ yield* import_effect26.Effect.annotateCurrentSpan("partNumber", partNumber);
4087
+ yield* import_effect26.Effect.annotateCurrentSpan("size", data.length);
3197
4088
  const client = yield* getClient;
3198
- const result = yield* import_effect25.Effect.tryPromise({
4089
+ const result = yield* import_effect26.Effect.tryPromise({
3199
4090
  try: () => client.deployments.uploadPart(presignedUrl, data, partNumber),
3200
4091
  catch: (error) => new UploadPartError({
3201
4092
  message: `Failed to upload part ${partNumber}: ${error instanceof Error ? error.message : String(error)}`,
@@ -3204,21 +4095,21 @@ var DeploymentApiService = class extends import_effect25.Effect.Service()("Deplo
3204
4095
  });
3205
4096
  return result;
3206
4097
  });
3207
- const completeUpload = import_effect25.Effect.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
3208
- yield* import_effect25.Effect.annotateCurrentSpan("deploymentId", deploymentId);
3209
- yield* import_effect25.Effect.annotateCurrentSpan("partsCount", parts.length);
4098
+ const completeUpload = import_effect26.Effect.fn("DeploymentApiService.completeUpload")(function* (deploymentId, uploadId, parts) {
4099
+ yield* import_effect26.Effect.annotateCurrentSpan("deploymentId", deploymentId);
4100
+ yield* import_effect26.Effect.annotateCurrentSpan("partsCount", parts.length);
3210
4101
  const client = yield* getClient;
3211
- yield* import_effect25.Effect.tryPromise({
4102
+ yield* import_effect26.Effect.tryPromise({
3212
4103
  try: () => client.deployments.completeUpload(deploymentId, uploadId, parts),
3213
4104
  catch: (error) => new UploadCompleteError({
3214
4105
  message: `Failed to complete upload: ${error instanceof Error ? error.message : String(error)}`
3215
4106
  })
3216
4107
  });
3217
- yield* import_effect25.Effect.log("Upload completed successfully");
4108
+ yield* import_effect26.Effect.log("Upload completed successfully");
3218
4109
  });
3219
- const getDeploymentStatus = import_effect25.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
4110
+ const getDeploymentStatus = import_effect26.Effect.fn("DeploymentApiService.getDeploymentStatus")(function* (deploymentId) {
3220
4111
  const client = yield* getClient;
3221
- const deployment = yield* import_effect25.Effect.tryPromise({
4112
+ const deployment = yield* import_effect26.Effect.tryPromise({
3222
4113
  try: () => client.deployments.get(deploymentId),
3223
4114
  catch: (error) => new DeploymentStatusError({
3224
4115
  message: `Failed to get deployment status: ${error instanceof Error ? error.message : String(error)}`
@@ -3226,9 +4117,9 @@ var DeploymentApiService = class extends import_effect25.Effect.Service()("Deplo
3226
4117
  });
3227
4118
  return deployment.status;
3228
4119
  });
3229
- const getLogs = import_effect25.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
4120
+ const getLogs = import_effect26.Effect.fn("DeploymentApiService.getLogs")(function* (deploymentId, options) {
3230
4121
  const client = yield* getClient;
3231
- const result = yield* import_effect25.Effect.tryPromise({
4122
+ const result = yield* import_effect26.Effect.tryPromise({
3232
4123
  try: () => client.deployments.getLogs(deploymentId, options),
3233
4124
  catch: (error) => new DeploymentLogsFetchError({
3234
4125
  message: `Failed to get logs: ${error instanceof Error ? error.message : String(error)}`
@@ -3236,9 +4127,9 @@ var DeploymentApiService = class extends import_effect25.Effect.Service()("Deplo
3236
4127
  });
3237
4128
  return result;
3238
4129
  });
3239
- const getHostnames = import_effect25.Effect.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
4130
+ const getHostnames = import_effect26.Effect.fn("DeploymentApiService.getHostnames")(function* (deploymentId) {
3240
4131
  const client = yield* getClient;
3241
- const result = yield* import_effect25.Effect.tryPromise({
4132
+ const result = yield* import_effect26.Effect.tryPromise({
3242
4133
  try: () => client.deployments.getHostnames(deploymentId),
3243
4134
  catch: (error) => new DeploymentHostnamesFetchError({
3244
4135
  message: `Failed to get hostnames: ${error instanceof Error ? error.message : String(error)}`
@@ -3261,39 +4152,39 @@ var DeploymentApiService = class extends import_effect25.Effect.Service()("Deplo
3261
4152
  };
3262
4153
 
3263
4154
  // src/commands/platform/deploy/index.ts
3264
- var appOption6 = import_cli12.Options.text("app").pipe(
3265
- import_cli12.Options.withAlias("a"),
3266
- import_cli12.Options.withDescription("Application ID to deploy to (overrides the linked application)"),
3267
- import_cli12.Options.optional
4155
+ var appOption7 = import_cli13.Options.text("app").pipe(
4156
+ import_cli13.Options.withAlias("a"),
4157
+ import_cli13.Options.withDescription("Application ID to deploy to (overrides the linked application)"),
4158
+ import_cli13.Options.optional
3268
4159
  );
3269
- var orgOption6 = import_cli12.Options.text("org").pipe(
3270
- import_cli12.Options.withAlias("o"),
3271
- import_cli12.Options.withDescription("Organization ID to use when automatically creating an application"),
3272
- import_cli12.Options.optional
4160
+ var orgOption6 = import_cli13.Options.text("org").pipe(
4161
+ import_cli13.Options.withAlias("o"),
4162
+ import_cli13.Options.withDescription("Organization ID to use when automatically creating an application"),
4163
+ import_cli13.Options.optional
3273
4164
  );
3274
- var nameOption2 = import_cli12.Options.text("name").pipe(
3275
- import_cli12.Options.withAlias("n"),
3276
- import_cli12.Options.withDescription("Application name to use when automatically creating an application"),
3277
- import_cli12.Options.optional
4165
+ var nameOption2 = import_cli13.Options.text("name").pipe(
4166
+ import_cli13.Options.withAlias("n"),
4167
+ import_cli13.Options.withDescription("Application name to use when automatically creating an application"),
4168
+ import_cli13.Options.optional
3278
4169
  );
3279
- var createAndLinkDeploymentApplication = (input) => import_effect26.Effect.gen(function* () {
4170
+ var createAndLinkDeploymentApplication = (input) => import_effect27.Effect.gen(function* () {
3280
4171
  const projectLink = yield* ProjectLinkService;
3281
4172
  const apiClient = yield* ApiClientService;
3282
4173
  const { items: organizations } = yield* apiClient.request((client) => client.organizations.list());
3283
- const availableOrganizations = import_effect26.Option.match(input.org, {
4174
+ const availableOrganizations = import_effect27.Option.match(input.org, {
3284
4175
  onNone: () => organizations,
3285
4176
  onSome: (organizationId) => organizations.filter((organization2) => organization2.id === organizationId)
3286
4177
  });
3287
4178
  if (availableOrganizations.length === 0) {
3288
4179
  return yield* new NoOrganizationsFoundError({
3289
- message: import_effect26.Option.match(input.org, {
4180
+ message: import_effect27.Option.match(input.org, {
3290
4181
  onNone: () => 'No organization is available for this deployment. Create an organization, then run "gigadrive deploy" again.',
3291
4182
  onSome: (organizationId) => `Organization "${organizationId}" was not found among your organizations.`
3292
4183
  })
3293
4184
  });
3294
4185
  }
3295
- const organization = availableOrganizations.length === 1 ? availableOrganizations[0] : yield* import_cli12.Prompt.run(
3296
- import_cli12.Prompt.select({
4186
+ const organization = availableOrganizations.length === 1 ? availableOrganizations[0] : yield* import_cli13.Prompt.run(
4187
+ import_cli13.Prompt.select({
3297
4188
  message: "Select an organization for this application",
3298
4189
  choices: availableOrganizations.map((candidate) => ({
3299
4190
  title: candidate.name,
@@ -3302,7 +4193,7 @@ var createAndLinkDeploymentApplication = (input) => import_effect26.Effect.gen(f
3302
4193
  })
3303
4194
  );
3304
4195
  const inferredName = path.basename(input.cwd) || "Application";
3305
- const applicationName = import_effect26.Option.match(input.name, {
4196
+ const applicationName = import_effect27.Option.match(input.name, {
3306
4197
  onNone: () => inferredName,
3307
4198
  onSome: (name) => name
3308
4199
  }).trim();
@@ -3316,37 +4207,37 @@ var createAndLinkDeploymentApplication = (input) => import_effect26.Effect.gen(f
3316
4207
  applicationId: application.id,
3317
4208
  organizationId: organization.id
3318
4209
  });
3319
- yield* import_effect26.Console.log(`Created and linked application ${application.name} (${application.id}).`);
4210
+ yield* import_effect27.Console.log(`Created and linked application ${application.name} (${application.id}).`);
3320
4211
  return application.id;
3321
4212
  });
3322
- var resolveLinkedDeploymentApplication = (input) => import_effect26.Effect.gen(function* () {
4213
+ var resolveLinkedDeploymentApplication = (input) => import_effect27.Effect.gen(function* () {
3323
4214
  const projectLink = yield* ProjectLinkService;
3324
4215
  const existingLink = yield* projectLink.load(input.cwd);
3325
- return yield* import_effect26.Option.match(existingLink, {
4216
+ return yield* import_effect27.Option.match(existingLink, {
3326
4217
  onNone: () => createAndLinkDeploymentApplication(input),
3327
- onSome: (link) => import_effect26.Effect.succeed(link.applicationId)
4218
+ onSome: (link) => import_effect27.Effect.succeed(link.applicationId)
3328
4219
  });
3329
4220
  });
3330
- var resolveDeploymentApplication = (input) => import_effect26.Option.match(input.app, {
4221
+ var resolveDeploymentApplication = (input) => import_effect27.Option.match(input.app, {
3331
4222
  onNone: () => resolveLinkedDeploymentApplication(input),
3332
- onSome: import_effect26.Effect.succeed
4223
+ onSome: import_effect27.Effect.succeed
3333
4224
  });
3334
- var deployCommand = import_cli12.Command.make(
4225
+ var deployCommand = import_cli13.Command.make(
3335
4226
  "deploy",
3336
- { app: appOption6, org: orgOption6, name: nameOption2 },
3337
- ({ app, org, name }) => import_effect26.Effect.gen(function* () {
4227
+ { app: appOption7, org: orgOption6, name: nameOption2 },
4228
+ ({ app, org, name }) => import_effect27.Effect.gen(function* () {
3338
4229
  const projectConfig = yield* ProjectConfigService;
3339
4230
  const deploymentApi = yield* DeploymentApiService;
3340
4231
  const archiveService = yield* ArchiveService;
3341
- const pathService = yield* import_platform9.Path.Path;
4232
+ const pathService = yield* import_platform10.Path.Path;
3342
4233
  const cwd = process.cwd();
3343
- yield* import_effect26.Effect.log("Starting deploy command");
4234
+ yield* import_effect27.Effect.log("Starting deploy command");
3344
4235
  const { config } = yield* projectConfig.resolve(cwd);
3345
4236
  const applicationId = yield* resolveDeploymentApplication({ app, org, name, cwd });
3346
- yield* import_effect26.Console.log("Creating deployment...");
4237
+ yield* import_effect27.Console.log("Creating deployment...");
3347
4238
  const deploymentId = yield* deploymentApi.createDeployment(applicationId);
3348
- yield* import_effect26.Console.log(`Deployment ID: ${deploymentId}`);
3349
- yield* import_effect26.Console.log("Creating archive...");
4239
+ yield* import_effect27.Console.log(`Deployment ID: ${deploymentId}`);
4240
+ yield* import_effect27.Console.log("Creating archive...");
3350
4241
  const archivePath = pathService.join(os4.tmpdir(), `project-${Date.now()}.zip`);
3351
4242
  const archive = yield* archiveService.createZipArchive(config.userArchive?.rootOverwrite || cwd, archivePath, {
3352
4243
  whitelist: config.userArchive?.fileWhitelist,
@@ -3356,68 +4247,68 @@ var deployCommand = import_cli12.Command.make(
3356
4247
  ...config.sidecars ?? []
3357
4248
  ].some((container) => container.source.type === "dockerfile")
3358
4249
  });
3359
- yield* import_effect26.Console.log(`Archive created (${(0, import_commons.formatFileSize)(archive.size)})`);
3360
- yield* import_effect26.Console.log("Uploading archive...");
4250
+ yield* import_effect27.Console.log(`Archive created (${(0, import_commons.formatFileSize)(archive.size)})`);
4251
+ yield* import_effect27.Console.log("Uploading archive...");
3361
4252
  yield* uploadArchive(deploymentId, archivePath, archive.size);
3362
- yield* import_effect26.Console.log("Upload complete.");
3363
- yield* import_effect26.Console.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
4253
+ yield* import_effect27.Console.log("Upload complete.");
4254
+ yield* import_effect27.Console.log("The deployment pipeline is now being provisioned. This may take a few seconds.");
3364
4255
  yield* pollDeployment(deploymentId);
3365
4256
  }).pipe(
3366
- import_effect26.Effect.catchTags({
3367
- QuitException: () => import_effect26.Console.log("Cancelled."),
3368
- NotAuthenticatedError: (err) => import_effect26.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
3369
- import_effect26.Effect.andThen(import_effect26.Effect.fail(err))
4257
+ import_effect27.Effect.catchTags({
4258
+ QuitException: () => import_effect27.Console.log("Cancelled."),
4259
+ NotAuthenticatedError: (err) => import_effect27.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
4260
+ import_effect27.Effect.andThen(import_effect27.Effect.fail(err))
3370
4261
  ),
3371
- ApiRequestError: (err) => import_effect26.Console.error(`Application setup failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3372
- NoOrganizationsFoundError: (err) => import_effect26.Console.error(err.message).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3373
- ProjectLinkReadError: (err) => import_effect26.Console.error(err.message).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3374
- ProjectLinkWriteError: (err) => import_effect26.Console.error(err.message).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3375
- ConfigNotFoundError: (err) => import_effect26.Console.error(err.message).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3376
- ConfigParseError: (err) => import_effect26.Console.error(`Config parse error: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3377
- ConfigValidationError: (err) => import_effect26.Effect.gen(function* () {
4262
+ ApiRequestError: (err) => import_effect27.Console.error(`Application setup failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4263
+ NoOrganizationsFoundError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4264
+ ProjectLinkReadError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4265
+ ProjectLinkWriteError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4266
+ ConfigNotFoundError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4267
+ ConfigParseError: (err) => import_effect27.Console.error(`Config parse error: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4268
+ ConfigValidationError: (err) => import_effect27.Effect.gen(function* () {
3378
4269
  for (const e of err.errors) {
3379
- yield* import_effect26.Console.error(e);
4270
+ yield* import_effect27.Console.error(e);
3380
4271
  }
3381
- yield* import_effect26.Console.error(err.message);
3382
- }).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3383
- DeploymentCreateError: (err) => import_effect26.Console.error(`Deployment creation failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3384
- ArchiveCreateError: (err) => import_effect26.Console.error(`Archive creation failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3385
- UploadStartError: (err) => import_effect26.Console.error(`Upload failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3386
- UploadPartError: (err) => import_effect26.Console.error(`Upload failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3387
- UploadCompleteError: (err) => import_effect26.Console.error(`Upload failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3388
- PresignedUrlError: (err) => import_effect26.Console.error(`Upload failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3389
- DeploymentAuthError: (err) => import_effect26.Console.error(`Authentication failed: ${err.message}`).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err))),
3390
- DeploymentFailedError: (err) => import_effect26.Console.error(err.message).pipe(import_effect26.Effect.andThen(import_effect26.Effect.fail(err)))
4272
+ yield* import_effect27.Console.error(err.message);
4273
+ }).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4274
+ DeploymentCreateError: (err) => import_effect27.Console.error(`Deployment creation failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4275
+ ArchiveCreateError: (err) => import_effect27.Console.error(`Archive creation failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4276
+ UploadStartError: (err) => import_effect27.Console.error(`Upload failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4277
+ UploadPartError: (err) => import_effect27.Console.error(`Upload failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4278
+ UploadCompleteError: (err) => import_effect27.Console.error(`Upload failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4279
+ PresignedUrlError: (err) => import_effect27.Console.error(`Upload failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4280
+ DeploymentAuthError: (err) => import_effect27.Console.error(`Authentication failed: ${err.message}`).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err))),
4281
+ DeploymentFailedError: (err) => import_effect27.Console.error(err.message).pipe(import_effect27.Effect.andThen(import_effect27.Effect.fail(err)))
3391
4282
  })
3392
4283
  )
3393
4284
  );
3394
- var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect26.Effect.gen(function* () {
4285
+ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect27.Effect.gen(function* () {
3395
4286
  const deploymentApi = yield* DeploymentApiService;
3396
- const fs = yield* import_platform9.FileSystem.FileSystem;
4287
+ const fs = yield* import_platform10.FileSystem.FileSystem;
3397
4288
  const uploadId = yield* deploymentApi.startMultipartUpload(deploymentId);
3398
4289
  const FILE_CHUNK_SIZE = 10 * 1024 * 1024;
3399
4290
  const numChunks = Math.ceil(fileSize / FILE_CHUNK_SIZE);
3400
- yield* import_effect26.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
3401
- const parts = yield* import_effect26.Effect.all(
4291
+ yield* import_effect27.Effect.log("Starting multipart upload", { uploadId, numChunks, fileSize });
4292
+ const parts = yield* import_effect27.Effect.all(
3402
4293
  Array.from({ length: numChunks }, (_, i) => {
3403
4294
  const partNumber = i + 1;
3404
4295
  const start = i * FILE_CHUNK_SIZE;
3405
4296
  const bytesToRead = partNumber < numChunks ? FILE_CHUNK_SIZE : fileSize - start;
3406
- return import_effect26.Effect.gen(function* () {
4297
+ return import_effect27.Effect.gen(function* () {
3407
4298
  const presignedUrl = yield* deploymentApi.getPresignedUrl(deploymentId, uploadId, partNumber);
3408
- const chunkData = yield* import_effect26.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
3409
- import_effect26.Effect.map((chunks) => {
4299
+ const chunkData = yield* import_effect27.Stream.runCollect(fs.stream(archivePath, { offset: start, bytesToRead })).pipe(
4300
+ import_effect27.Effect.map((chunks) => {
3410
4301
  const arrays = Array.from(chunks);
3411
4302
  return Buffer.concat(arrays);
3412
4303
  }),
3413
- import_effect26.Effect.mapError(
4304
+ import_effect27.Effect.mapError(
3414
4305
  (error) => new UploadPartError({
3415
4306
  message: `Failed to read chunk ${partNumber}: ${String(error)}`,
3416
4307
  partNumber
3417
4308
  })
3418
4309
  )
3419
4310
  );
3420
- yield* import_effect26.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
4311
+ yield* import_effect27.Effect.log(`Uploading part ${partNumber}/${numChunks}`, { size: chunkData.length });
3421
4312
  return yield* deploymentApi.uploadPart(presignedUrl, chunkData, partNumber);
3422
4313
  });
3423
4314
  }),
@@ -3425,20 +4316,20 @@ var uploadArchive = (deploymentId, archivePath, fileSize) => import_effect26.Eff
3425
4316
  );
3426
4317
  yield* deploymentApi.completeUpload(deploymentId, uploadId, parts);
3427
4318
  });
3428
- var pollDeployment = (deploymentId) => import_effect26.Effect.gen(function* () {
4319
+ var pollDeployment = (deploymentId) => import_effect27.Effect.gen(function* () {
3429
4320
  const deploymentApi = yield* DeploymentApiService;
3430
4321
  let lastStatus = "PENDING";
3431
4322
  let logOffset = 0;
3432
- yield* import_effect26.Console.log("Status: Pending");
3433
- yield* import_effect26.Effect.repeat(
3434
- import_effect26.Effect.gen(function* () {
3435
- const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(import_effect26.Effect.catchTag("DeploymentStatusError", () => import_effect26.Effect.succeed(lastStatus)));
4323
+ yield* import_effect27.Console.log("Status: Pending");
4324
+ yield* import_effect27.Effect.repeat(
4325
+ import_effect27.Effect.gen(function* () {
4326
+ const newStatus = yield* deploymentApi.getDeploymentStatus(deploymentId).pipe(import_effect27.Effect.catchTag("DeploymentStatusError", () => import_effect27.Effect.succeed(lastStatus)));
3436
4327
  if (newStatus !== lastStatus) {
3437
4328
  lastStatus = newStatus;
3438
4329
  const label = newStatus.charAt(0) + newStatus.slice(1).toLowerCase();
3439
- yield* import_effect26.Console.log(`Status: ${label}`);
4330
+ yield* import_effect27.Console.log(`Status: ${label}`);
3440
4331
  }
3441
- const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect26.Effect.catchTag("DeploymentLogsFetchError", () => import_effect26.Effect.succeed(null)));
4332
+ const logsPage = yield* deploymentApi.getLogs(deploymentId, { offset: logOffset, limit: 100 }).pipe(import_effect27.Effect.catchTag("DeploymentLogsFetchError", () => import_effect27.Effect.succeed(null)));
3442
4333
  if (logsPage) {
3443
4334
  for (const logEntry of logsPage.items) {
3444
4335
  logOffset++;
@@ -3446,86 +4337,86 @@ var pollDeployment = (deploymentId) => import_effect26.Effect.gen(function* () {
3446
4337
  }
3447
4338
  }
3448
4339
  if (newStatus === "ACTIVE") {
3449
- const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(import_effect26.Effect.catchTag("DeploymentHostnamesFetchError", () => import_effect26.Effect.succeed([])));
4340
+ const hostnames = yield* deploymentApi.getHostnames(deploymentId).pipe(import_effect27.Effect.catchTag("DeploymentHostnamesFetchError", () => import_effect27.Effect.succeed([])));
3450
4341
  const active = hostnames.find((h) => h.active) ?? hostnames.at(0);
3451
4342
  if (active) {
3452
- yield* import_effect26.Console.log(`Deployed to https://${active.hostname}`);
4343
+ yield* import_effect27.Console.log(`Deployed to https://${active.hostname}`);
3453
4344
  } else {
3454
- yield* import_effect26.Console.log("Deployment is live.");
4345
+ yield* import_effect27.Console.log("Deployment is live.");
3455
4346
  }
3456
- return yield* import_effect26.Effect.fail("done");
4347
+ return yield* import_effect27.Effect.fail("done");
3457
4348
  }
3458
4349
  if (newStatus === "FAILED") {
3459
- return yield* import_effect26.Effect.fail(
4350
+ return yield* import_effect27.Effect.fail(
3460
4351
  new DeploymentFailedError({
3461
4352
  message: "The deployment failed. Please check the logs for more information."
3462
4353
  })
3463
4354
  );
3464
4355
  }
3465
4356
  }),
3466
- import_effect26.Schedule.addDelay(import_effect26.Schedule.forever, () => import_effect26.Duration.seconds(1))
4357
+ import_effect27.Schedule.addDelay(import_effect27.Schedule.forever, () => import_effect27.Duration.seconds(1))
3467
4358
  ).pipe(
3468
- import_effect26.Effect.catchAll((err) => {
3469
- if (err === "done") return import_effect26.Effect.void;
3470
- return import_effect26.Effect.fail(err);
4359
+ import_effect27.Effect.catchAll((err) => {
4360
+ if (err === "done") return import_effect27.Effect.void;
4361
+ return import_effect27.Effect.fail(err);
3471
4362
  })
3472
4363
  );
3473
4364
  });
3474
4365
  var printLog = (logEntry) => {
3475
4366
  switch (logEntry.type) {
3476
4367
  case "ERROR":
3477
- return import_effect26.Console.error(logEntry.message);
4368
+ return import_effect27.Console.error(logEntry.message);
3478
4369
  case "WARN":
3479
- return import_effect26.Console.warn(logEntry.message);
4370
+ return import_effect27.Console.warn(logEntry.message);
3480
4371
  default:
3481
- return import_effect26.Console.log(logEntry.message);
4372
+ return import_effect27.Console.log(logEntry.message);
3482
4373
  }
3483
4374
  };
3484
4375
 
3485
4376
  // src/commands/platform/index.ts
3486
- var platformBase = import_cli13.Command.make("platform", {}, () => import_effect27.Effect.void);
3487
- var platformCommand = platformBase.pipe(import_cli13.Command.withSubcommands([deployCommand]));
4377
+ var platformBase = import_cli14.Command.make("platform", {}, () => import_effect28.Effect.void);
4378
+ var platformCommand = platformBase.pipe(import_cli14.Command.withSubcommands([deployCommand]));
3488
4379
 
3489
4380
  // src/commands/setup/index.ts
3490
- var import_cli14 = require("@effect/cli");
3491
- var import_effect28 = require("effect");
3492
- var fileArg2 = import_cli14.Args.text({ name: "file" }).pipe(
3493
- import_cli14.Args.withDescription("Output file to write (default: .env.local)"),
3494
- import_cli14.Args.optional
4381
+ var import_cli15 = require("@effect/cli");
4382
+ var import_effect29 = require("effect");
4383
+ var fileArg2 = import_cli15.Args.text({ name: "file" }).pipe(
4384
+ import_cli15.Args.withDescription("Output file to write (default: .env.local)"),
4385
+ import_cli15.Args.optional
3495
4386
  );
3496
- var appOption7 = import_cli14.Options.text("app").pipe(
3497
- import_cli14.Options.withAlias("a"),
3498
- import_cli14.Options.withDescription("Application ID to set up (skips the interactive picker)"),
3499
- import_cli14.Options.optional
4387
+ var appOption8 = import_cli15.Options.text("app").pipe(
4388
+ import_cli15.Options.withAlias("a"),
4389
+ import_cli15.Options.withDescription("Application ID to set up (skips the interactive picker)"),
4390
+ import_cli15.Options.optional
3500
4391
  );
3501
- var orgOption7 = import_cli14.Options.text("org").pipe(
3502
- import_cli14.Options.withAlias("o"),
3503
- import_cli14.Options.withDescription("Only list applications belonging to this organization ID when picking"),
3504
- import_cli14.Options.optional
4392
+ var orgOption7 = import_cli15.Options.text("org").pipe(
4393
+ import_cli15.Options.withAlias("o"),
4394
+ import_cli15.Options.withDescription("Only list applications belonging to this organization ID when picking"),
4395
+ import_cli15.Options.optional
3505
4396
  );
3506
- var environmentOption2 = import_cli14.Options.text("environment").pipe(
3507
- import_cli14.Options.withAlias("e"),
3508
- import_cli14.Options.withDescription("Environment slug to resolve overrides for (e.g. preview)"),
3509
- import_cli14.Options.optional
4397
+ var environmentOption2 = import_cli15.Options.text("environment").pipe(
4398
+ import_cli15.Options.withAlias("e"),
4399
+ import_cli15.Options.withDescription("Environment slug to resolve overrides for (e.g. preview)"),
4400
+ import_cli15.Options.optional
3510
4401
  );
3511
- var yesOption3 = import_cli14.Options.boolean("yes").pipe(
3512
- import_cli14.Options.withAlias("y"),
3513
- import_cli14.Options.withDescription("Overwrite an existing file without confirmation")
4402
+ var yesOption3 = import_cli15.Options.boolean("yes").pipe(
4403
+ import_cli15.Options.withAlias("y"),
4404
+ import_cli15.Options.withDescription("Overwrite an existing file without confirmation")
3514
4405
  );
3515
- var rotateOption2 = import_cli14.Options.boolean("rotate").pipe(
3516
- import_cli14.Options.withDescription("Force a fresh API key, revoking the previous one")
4406
+ var rotateOption2 = import_cli15.Options.boolean("rotate").pipe(
4407
+ import_cli15.Options.withDescription("Force a fresh API key, revoking the previous one")
3517
4408
  );
3518
- var setupCommand = import_cli14.Command.make(
4409
+ var setupCommand = import_cli15.Command.make(
3519
4410
  "setup",
3520
4411
  {
3521
4412
  file: fileArg2,
3522
- app: appOption7,
4413
+ app: appOption8,
3523
4414
  org: orgOption7,
3524
4415
  environment: environmentOption2,
3525
4416
  yes: yesOption3,
3526
4417
  rotate: rotateOption2
3527
4418
  },
3528
- ({ file, app, org, environment, yes, rotate }) => import_effect28.Effect.gen(function* () {
4419
+ ({ file, app, org, environment, yes, rotate }) => import_effect29.Effect.gen(function* () {
3529
4420
  const apiClient = yield* ApiClientService;
3530
4421
  const projectLink = yield* ProjectLinkService;
3531
4422
  const envFile = yield* EnvFileService;
@@ -3533,29 +4424,29 @@ var setupCommand = import_cli14.Command.make(
3533
4424
  const cwd = process.cwd();
3534
4425
  const existingLink = yield* projectLink.load(cwd);
3535
4426
  let applicationId;
3536
- if (import_effect28.Option.isSome(existingLink)) {
4427
+ if (import_effect29.Option.isSome(existingLink)) {
3537
4428
  applicationId = existingLink.value.applicationId;
3538
- if (import_effect28.Option.isSome(app) || import_effect28.Option.isSome(org)) {
3539
- yield* import_effect28.Console.log(
4429
+ if (import_effect29.Option.isSome(app) || import_effect29.Option.isSome(org)) {
4430
+ yield* import_effect29.Console.log(
3540
4431
  `Note: this directory is already linked to ${applicationId}; ignoring --app/--org. Run "gigadrive unlink" first to target a different application.`
3541
4432
  );
3542
4433
  }
3543
4434
  } else {
3544
- const organizationId = import_effect28.Option.getOrUndefined(org);
4435
+ const organizationId = import_effect29.Option.getOrUndefined(org);
3545
4436
  const { items: apps } = yield* apiClient.request((client) => client.applications.list({ organizationId }));
3546
4437
  if (apps.length === 0) {
3547
- return yield* import_effect28.Effect.fail(
4438
+ return yield* import_effect29.Effect.fail(
3548
4439
  new NoApplicationsFoundError({
3549
4440
  message: 'No applications found. Create one in the Gigadrive console, then run "gigadrive setup" again.'
3550
4441
  })
3551
4442
  );
3552
4443
  }
3553
- const explicitAppId = import_effect28.Option.getOrUndefined(app);
4444
+ const explicitAppId = import_effect29.Option.getOrUndefined(app);
3554
4445
  let chosen;
3555
4446
  if (explicitAppId !== void 0) {
3556
4447
  const match = apps.find((a) => a.id === explicitAppId);
3557
4448
  if (match === void 0) {
3558
- return yield* import_effect28.Effect.fail(
4449
+ return yield* import_effect29.Effect.fail(
3559
4450
  new ApplicationNotFoundError({
3560
4451
  message: `Application "${explicitAppId}" was not found among your applications.`
3561
4452
  })
@@ -3565,8 +4456,8 @@ var setupCommand = import_cli14.Command.make(
3565
4456
  } else if (apps.length === 1) {
3566
4457
  chosen = apps[0];
3567
4458
  } else {
3568
- chosen = yield* import_cli14.Prompt.run(
3569
- import_cli14.Prompt.select({
4459
+ chosen = yield* import_cli15.Prompt.run(
4460
+ import_cli15.Prompt.select({
3570
4461
  message: "Select an application to set up",
3571
4462
  choices: apps.map((a) => ({ title: `${a.name} (${a.organization.name})`, value: a }))
3572
4463
  })
@@ -3574,83 +4465,83 @@ var setupCommand = import_cli14.Command.make(
3574
4465
  }
3575
4466
  applicationId = chosen.id;
3576
4467
  yield* projectLink.save(cwd, { applicationId, organizationId: chosen.organization.id });
3577
- yield* import_effect28.Console.log(`Linked this directory to application ${applicationId}.`);
4468
+ yield* import_effect29.Console.log(`Linked this directory to application ${applicationId}.`);
3578
4469
  }
3579
- const target = import_effect28.Option.getOrElse(file, () => ".env.local");
3580
- const environmentSlug = import_effect28.Option.getOrUndefined(environment);
4470
+ const target = import_effect29.Option.getOrElse(file, () => ".env.local");
4471
+ const environmentSlug = import_effect29.Option.getOrUndefined(environment);
3581
4472
  const { items, omittedSensitive } = yield* apiClient.request(
3582
4473
  (client) => client.applications.envVars.pull(applicationId, environmentSlug ? { environment: environmentSlug } : void 0)
3583
4474
  );
3584
4475
  const existing = yield* envFile.read(target);
3585
- if (import_effect28.Option.isSome(existing) && !yes) {
3586
- const overwrite = yield* import_cli14.Prompt.run(
3587
- import_cli14.Prompt.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
4476
+ if (import_effect29.Option.isSome(existing) && !yes) {
4477
+ const overwrite = yield* import_cli15.Prompt.run(
4478
+ import_cli15.Prompt.confirm({ message: `${target} already exists. Overwrite?`, initial: false })
3588
4479
  );
3589
4480
  if (!overwrite) {
3590
- return yield* import_effect28.Console.log("Cancelled.");
4481
+ return yield* import_effect29.Console.log("Cancelled.");
3591
4482
  }
3592
4483
  }
3593
4484
  const credentialEntries2 = yield* localCredentials.provision({
3594
4485
  applicationId,
3595
4486
  rotate,
3596
- existingContent: import_effect28.Option.getOrUndefined(existing)
4487
+ existingContent: import_effect29.Option.getOrUndefined(existing)
3597
4488
  });
3598
4489
  const entries = [...items.map((v) => ({ key: v.key, value: v.value })), ...credentialEntries2];
3599
4490
  yield* envFile.write(target, entries);
3600
4491
  const gitignored = yield* envFile.ensureGitignored(cwd, target);
3601
- yield* import_effect28.Console.log("\nLocal dev environment ready.");
3602
- yield* import_effect28.Console.log(` \u2022 Wrote ${items.length} variable(s) and API credentials to ${target}.`);
4492
+ yield* import_effect29.Console.log("\nLocal dev environment ready.");
4493
+ yield* import_effect29.Console.log(` \u2022 Wrote ${items.length} variable(s) and API credentials to ${target}.`);
3603
4494
  if (omittedSensitive > 0) {
3604
- yield* import_effect28.Console.log(
4495
+ yield* import_effect29.Console.log(
3605
4496
  ` \u2022 Skipped ${omittedSensitive} sensitive variable(s) (only production/preview hold secrets).`
3606
4497
  );
3607
4498
  }
3608
4499
  if (gitignored) {
3609
- yield* import_effect28.Console.log(` \u2022 Added ${target} to .gitignore.`);
4500
+ yield* import_effect29.Console.log(` \u2022 Added ${target} to .gitignore.`);
3610
4501
  }
3611
- yield* import_effect28.Console.log(" \u2022 Your app can now call the Gigadrive API via the SDK (GIGADRIVE_CLIENT_ID/SECRET).");
4502
+ yield* import_effect29.Console.log(" \u2022 Your app can now call the Gigadrive API via the SDK (GIGADRIVE_CLIENT_ID/SECRET).");
3612
4503
  }).pipe(
3613
- import_effect28.Effect.catchTags({
4504
+ import_effect29.Effect.catchTags({
3614
4505
  // Ctrl+C during a prompt is a deliberate cancel — exit 0.
3615
- QuitException: () => import_effect28.Console.log("Cancelled."),
3616
- NotAuthenticatedError: (err) => import_effect28.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
3617
- import_effect28.Effect.andThen(import_effect28.Effect.fail(err))
4506
+ QuitException: () => import_effect29.Console.log("Cancelled."),
4507
+ NotAuthenticatedError: (err) => import_effect29.Console.error('You are not logged in. Run "gigadrive login" to authenticate.').pipe(
4508
+ import_effect29.Effect.andThen(import_effect29.Effect.fail(err))
3618
4509
  ),
3619
- ApiRequestError: (err) => import_effect28.Console.error(`Setup failed: ${err.message}`).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3620
- NoApplicationsFoundError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3621
- ApplicationNotFoundError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3622
- ProjectLinkReadError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3623
- ProjectLinkWriteError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3624
- EnvFileReadError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3625
- EnvFileWriteError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3626
- DevCredentialsStoreReadError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err))),
3627
- DevCredentialsStoreWriteError: (err) => import_effect28.Console.error(err.message).pipe(import_effect28.Effect.andThen(import_effect28.Effect.fail(err)))
4510
+ ApiRequestError: (err) => import_effect29.Console.error(`Setup failed: ${err.message}`).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4511
+ NoApplicationsFoundError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4512
+ ApplicationNotFoundError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4513
+ ProjectLinkReadError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4514
+ ProjectLinkWriteError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4515
+ EnvFileReadError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4516
+ EnvFileWriteError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4517
+ DevCredentialsStoreReadError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err))),
4518
+ DevCredentialsStoreWriteError: (err) => import_effect29.Console.error(err.message).pipe(import_effect29.Effect.andThen(import_effect29.Effect.fail(err)))
3628
4519
  })
3629
4520
  )
3630
4521
  );
3631
4522
 
3632
4523
  // src/commands/whoami/index.ts
3633
- var import_cli15 = require("@effect/cli");
3634
- var import_effect29 = require("effect");
3635
- var whoamiCommand = import_cli15.Command.make(
4524
+ var import_cli16 = require("@effect/cli");
4525
+ var import_effect30 = require("effect");
4526
+ var whoamiCommand = import_cli16.Command.make(
3636
4527
  "whoami",
3637
4528
  {},
3638
- () => import_effect29.Effect.gen(function* () {
4529
+ () => import_effect30.Effect.gen(function* () {
3639
4530
  const auth = yield* AuthService;
3640
4531
  const info = yield* auth.getUserInfo;
3641
- yield* import_effect29.Console.log(JSON.stringify(info, null, 2));
4532
+ yield* import_effect30.Console.log(JSON.stringify(info, null, 2));
3642
4533
  }).pipe(
3643
- import_effect29.Effect.catchTags({
3644
- NotAuthenticatedError: () => import_effect29.Console.log('You are not logged in. Run "gigadrive login" to authenticate.'),
3645
- UserInfoFetchError: (err) => import_effect29.Console.error(`Failed to retrieve user info: ${err.message}`),
3646
- OAuthDiscoveryError: (err) => import_effect29.Console.error(`Failed to retrieve user info: ${err.message}`)
4534
+ import_effect30.Effect.catchTags({
4535
+ NotAuthenticatedError: () => import_effect30.Console.log('You are not logged in. Run "gigadrive login" to authenticate.'),
4536
+ UserInfoFetchError: (err) => import_effect30.Console.error(`Failed to retrieve user info: ${err.message}`),
4537
+ OAuthDiscoveryError: (err) => import_effect30.Console.error(`Failed to retrieve user info: ${err.message}`)
3647
4538
  })
3648
4539
  )
3649
4540
  );
3650
4541
 
3651
4542
  // src/index.ts
3652
- var gigadrive = import_cli16.Command.make("gigadrive", {}, () => import_effect30.Effect.void).pipe(
3653
- import_cli16.Command.withSubcommands([
4543
+ var gigadrive = import_cli17.Command.make("gigadrive", {}, () => import_effect31.Effect.void).pipe(
4544
+ import_cli17.Command.withSubcommands([
3654
4545
  loginCommand,
3655
4546
  logoutCommand,
3656
4547
  whoamiCommand,
@@ -3661,6 +4552,8 @@ var gigadrive = import_cli16.Command.make("gigadrive", {}, () => import_effect30
3661
4552
  deploymentsCommand,
3662
4553
  domainsCommand,
3663
4554
  aiCommand,
4555
+ microvmsCommand,
4556
+ vmCommand,
3664
4557
  buildCommand,
3665
4558
  debugCommand,
3666
4559
  deployCommand,
@@ -3668,11 +4561,11 @@ var gigadrive = import_cli16.Command.make("gigadrive", {}, () => import_effect30
3668
4561
  setupCommand
3669
4562
  ])
3670
4563
  );
3671
- var cli = import_cli16.Command.run(gigadrive, {
4564
+ var cli = import_cli17.Command.run(gigadrive, {
3672
4565
  name: "Gigadrive CLI",
3673
4566
  version: package_default.version
3674
4567
  });
3675
- var BaseServices = import_effect30.Layer.mergeAll(
4568
+ var BaseServices = import_effect31.Layer.mergeAll(
3676
4569
  OAuthConfigService.Default,
3677
4570
  AuthStorageService.Default,
3678
4571
  PackageManagerService.Default,
@@ -3682,13 +4575,13 @@ var BaseServices = import_effect30.Layer.mergeAll(
3682
4575
  ProjectLinkService.Default,
3683
4576
  EnvFileService.Default,
3684
4577
  DevCredentialsStore.Default
3685
- ).pipe(import_effect30.Layer.provideMerge(AuthService.Default));
3686
- var ApiClientLive = import_effect30.Layer.provide(ApiClientService.Default, BaseServices);
3687
- var DeploymentApiLive = import_effect30.Layer.provide(DeploymentApiService.Default, BaseServices);
3688
- var LocalCredentialsLive = import_effect30.Layer.provide(LocalCredentialsService.Default, BaseServices);
3689
- var ServicesLive = import_effect30.Layer.mergeAll(BaseServices, ApiClientLive, DeploymentApiLive, LocalCredentialsLive);
3690
- var AppLive = import_effect30.Layer.mergeAll(ServicesLive, import_effect30.Logger.minimumLogLevel(import_effect30.LogLevel.Info)).pipe(
3691
- import_effect30.Layer.provideMerge(import_platform_node.NodeContext.layer)
3692
- );
3693
- import_platform_node.NodeRuntime.runMain(import_effect30.Effect.provide(cli(process.argv), AppLive));
4578
+ ).pipe(import_effect31.Layer.provideMerge(AuthService.Default));
4579
+ var ApiClientLive = import_effect31.Layer.provide(ApiClientService.Default, BaseServices);
4580
+ var DeploymentApiLive = import_effect31.Layer.provide(DeploymentApiService.Default, BaseServices);
4581
+ var LocalCredentialsLive = import_effect31.Layer.provide(LocalCredentialsService.Default, BaseServices);
4582
+ var ServicesLive = import_effect31.Layer.mergeAll(BaseServices, ApiClientLive, DeploymentApiLive, LocalCredentialsLive);
4583
+ var AppLive = import_effect31.Layer.mergeAll(ServicesLive, import_effect31.Logger.minimumLogLevel(import_effect31.LogLevel.Info)).pipe(
4584
+ import_effect31.Layer.provideMerge(import_platform_node.NodeContext.layer)
4585
+ );
4586
+ import_platform_node.NodeRuntime.runMain(import_effect31.Effect.provide(cli(process.argv), AppLive));
3694
4587
  //# sourceMappingURL=index.cjs.map