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