gigadrive 2.13.0 → 2.15.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
@@ -32,7 +32,7 @@ var import_effect31 = require("effect");
32
32
  // package.json
33
33
  var package_default = {
34
34
  name: "gigadrive",
35
- version: "2.13.0",
35
+ version: "2.15.0",
36
36
  repository: {
37
37
  type: "git",
38
38
  url: "https://github.com/Gigadrive/sdk.git",
@@ -3099,6 +3099,37 @@ var formatImageTable = (images) => formatTable([
3099
3099
  ["NAME", "STATUS", "SOURCE", "ID"],
3100
3100
  ...images.map((image) => [image.name, image.status, imageSource(image), image.id])
3101
3101
  ]);
3102
+ var formatWebhookTable = (webhooks) => formatTable([
3103
+ ["URL", "EVENTS", "STATUS", "ID"],
3104
+ ...webhooks.map((webhook) => [
3105
+ webhook.url,
3106
+ webhook.events.join(","),
3107
+ webhook.enabled ? "enabled" : "disabled",
3108
+ webhook.id
3109
+ ])
3110
+ ]);
3111
+ var formatVolumeTable = (volumes) => formatTable([
3112
+ ["NAME", "SIZE", "STORED", "STATUS", "LAST SAVED", "ID"],
3113
+ ...volumes.map((volume) => [
3114
+ volume.name,
3115
+ `${String(volume.sizeMb)} MiB`,
3116
+ volume.storedBytes === null ? "-" : formatBytes(volume.storedBytes),
3117
+ volume.attachedMicrovmId ? `${volume.status} (${volume.attachedMicrovmId})` : volume.status,
3118
+ volume.lastSaveError ? `failed: ${volume.lastSaveError}` : volume.lastSavedAt ?? "never",
3119
+ volume.id
3120
+ ])
3121
+ ]);
3122
+ var formatDeliveryTable = (deliveries) => formatTable([
3123
+ ["CREATED", "EVENT", "STATUS", "ATTEMPTS", "RESPONSE", "MICROVM"],
3124
+ ...deliveries.map((delivery) => [
3125
+ delivery.createdAt,
3126
+ delivery.eventType,
3127
+ delivery.status,
3128
+ String(delivery.attempts),
3129
+ delivery.lastStatusCode === null ? delivery.lastError ?? "-" : String(delivery.lastStatusCode),
3130
+ delivery.microvmId
3131
+ ])
3132
+ ]);
3102
3133
  var formatBytes = (bytes) => {
3103
3134
  if (bytes < 1024) return `${bytes} B`;
3104
3135
  const units = ["KiB", "MiB", "GiB", "TiB"];
@@ -3333,6 +3364,25 @@ var withClient = (run) => import_effect24.Effect.gen(function* () {
3333
3364
  return yield* import_effect24.Effect.tryPromise({ try: () => run(client), catch: toApiRequestError });
3334
3365
  });
3335
3366
  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;
3367
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3368
+ var parseVolumeFlags = (values) => import_effect24.Effect.gen(function* () {
3369
+ if (values.length > 4) {
3370
+ return yield* new InvalidMicroVmOptionsError({ message: "--volume takes at most 4 volumes." });
3371
+ }
3372
+ const volumes = [];
3373
+ for (const value of values) {
3374
+ const separator = value.indexOf(":");
3375
+ const volumeId = separator > 0 ? value.slice(0, separator) : "";
3376
+ const mountPath = separator > 0 ? value.slice(separator + 1) : "";
3377
+ if (volumeId === "" || !mountPath.startsWith("/")) {
3378
+ return yield* new InvalidMicroVmOptionsError({
3379
+ message: `--volume must be VOLUME:PATH with an absolute path, such as cache:/cache (got ${value}).`
3380
+ });
3381
+ }
3382
+ volumes.push({ volumeId, mountPath });
3383
+ }
3384
+ return volumes;
3385
+ });
3336
3386
  var checkEgressFlags = (denyEgress, allowEgress) => import_effect24.Effect.gen(function* () {
3337
3387
  if (denyEgress && allowEgress.length > 0) {
3338
3388
  return yield* new InvalidMicroVmOptionsError({
@@ -3363,6 +3413,10 @@ var createInput = (flags) => import_effect24.Effect.gen(function* () {
3363
3413
  if (badPort !== void 0) {
3364
3414
  return yield* new InvalidMicroVmOptionsError({ message: `--port must be between 1 and 65535 (got ${badPort}).` });
3365
3415
  }
3416
+ const volumes = yield* parseVolumeFlags(flags.volumes);
3417
+ if (volumes.length > 0 && flags.pauseOnTimeout) {
3418
+ return yield* new InvalidMicroVmOptionsError({ message: "--volume cannot be combined with --pause-on-timeout." });
3419
+ }
3366
3420
  const applicationId = yield* resolveApplicationId(flags.app);
3367
3421
  const input = {
3368
3422
  ...applicationId ? { applicationId } : {},
@@ -3379,6 +3433,7 @@ var createInput = (flags) => import_effect24.Effect.gen(function* () {
3379
3433
  ...flags.ports.length > 0 ? { ports: [...flags.ports] } : {},
3380
3434
  ...env ? { env } : {},
3381
3435
  ...metadata ? { metadata } : {},
3436
+ ...volumes.length > 0 ? { volumes } : {},
3382
3437
  ...flags.denyEgress || flags.privatePreviews || flags.allowEgress.length > 0 ? {
3383
3438
  network: {
3384
3439
  egress: flags.denyEgress ? "deny" : "allow",
@@ -3392,7 +3447,26 @@ var createInput = (flags) => import_effect24.Effect.gen(function* () {
3392
3447
  });
3393
3448
  var createMicroVm = (flags) => import_effect24.Effect.gen(function* () {
3394
3449
  const apiClient = yield* ApiClientService;
3395
- const input = yield* createInput(flags);
3450
+ const parsed = yield* createInput(flags);
3451
+ const named = (parsed.volumes ?? []).filter((volume) => !UUID.test(volume.volumeId));
3452
+ const known = named.length > 0 ? yield* apiClient.request(
3453
+ (client) => client.microvms.volumes.list({ ...parsed.applicationId ? { applicationId: parsed.applicationId } : {} })
3454
+ ) : [];
3455
+ const volumes = [];
3456
+ for (const volume of parsed.volumes ?? []) {
3457
+ if (UUID.test(volume.volumeId)) {
3458
+ volumes.push(volume);
3459
+ continue;
3460
+ }
3461
+ const match = known.find((entry) => entry.name === volume.volumeId);
3462
+ if (match === void 0) {
3463
+ return yield* new InvalidMicroVmOptionsError({
3464
+ message: `No volume is named ${volume.volumeId}. Create it with "gigadrive microvms volumes create ${volume.volumeId}".`
3465
+ });
3466
+ }
3467
+ volumes.push({ volumeId: match.id, mountPath: volume.mountPath });
3468
+ }
3469
+ const input = volumes.length > 0 ? { ...parsed, volumes } : parsed;
3396
3470
  if (!flags.json) yield* import_effect24.Console.error(`Creating a microVM from ${input.image ?? "node22"}...`);
3397
3471
  const vm = yield* apiClient.request((client) => client.microvms.create(input));
3398
3472
  if (flags.json) {
@@ -3463,6 +3537,13 @@ var createCommand = import_cli12.Command.make(
3463
3537
  "Resume the paused microVM when a command, file call or public preview request reaches it"
3464
3538
  )
3465
3539
  ),
3540
+ volumes: import_cli12.Options.text("volume").pipe(
3541
+ import_cli12.Options.withAlias("v"),
3542
+ import_cli12.Options.withDescription(
3543
+ "Attach a volume as VOLUME:PATH, by ID or name, such as cache:/cache (repeatable, up to 4; saved when the microVM stops)"
3544
+ ),
3545
+ import_cli12.Options.repeated
3546
+ ),
3466
3547
  region: import_cli12.Options.text("region").pipe(import_cli12.Options.withDescription("Region to place the microVM in"), import_cli12.Options.optional),
3467
3548
  json: jsonOption2
3468
3549
  },
@@ -3945,6 +4026,205 @@ var imagesCommand = import_cli12.Command.make("images", {}, () => import_effect2
3945
4026
  import_cli12.Command.withDescription("Built-in and custom microVM images"),
3946
4027
  import_cli12.Command.withSubcommands([imagesListCommand, imagesCreateCommand, imagesRemoveCommand])
3947
4028
  );
4029
+ var volumeIdArg = import_cli12.Args.text({ name: "volume-id" }).pipe(import_cli12.Args.withDescription("The volume ID"));
4030
+ var volumesListCommand = import_cli12.Command.make(
4031
+ "ls",
4032
+ { app: appOption6, json: jsonOption2 },
4033
+ ({ app, json }) => import_effect24.Effect.gen(function* () {
4034
+ const apiClient = yield* ApiClientService;
4035
+ const applicationId = yield* resolveApplicationId(app);
4036
+ const volumes = yield* apiClient.request((client) => client.microvms.volumes.list({ applicationId }));
4037
+ if (json) return yield* import_effect24.Console.log(JSON.stringify(volumes, null, 2));
4038
+ if (volumes.length === 0)
4039
+ return yield* import_effect24.Console.log('No volumes yet. Create one with "gigadrive microvms volumes create <name>".');
4040
+ for (const line of formatVolumeTable(volumes)) yield* import_effect24.Console.log(line);
4041
+ }).pipe(reportErrors2("list volumes"))
4042
+ ).pipe(import_cli12.Command.withDescription("List the application's volumes"));
4043
+ var createVolume = (flags) => import_effect24.Effect.gen(function* () {
4044
+ const apiClient = yield* ApiClientService;
4045
+ const applicationId = yield* resolveApplicationId(flags.app);
4046
+ const volume = yield* apiClient.request(
4047
+ (client) => client.microvms.volumes.create({
4048
+ name: flags.name,
4049
+ ...import_effect24.Option.match(flags.size, { onNone: () => ({}), onSome: (sizeMb) => ({ sizeMb }) }),
4050
+ ...applicationId ? { applicationId } : {}
4051
+ })
4052
+ );
4053
+ if (flags.json) {
4054
+ yield* import_effect24.Console.log(JSON.stringify(volume, null, 2));
4055
+ } else {
4056
+ yield* import_effect24.Console.log(`Created volume ${volume.name} (${String(volume.sizeMb)} MiB, ${volume.id}).`);
4057
+ yield* import_effect24.Console.log(`Attach it: gigadrive microvms create --volume ${volume.name}:/data`);
4058
+ }
4059
+ return volume;
4060
+ });
4061
+ var volumesCreateCommand = import_cli12.Command.make(
4062
+ "create",
4063
+ {
4064
+ name: import_cli12.Args.text({ name: "name" }).pipe(
4065
+ import_cli12.Args.withDescription("Lowercase letters, digits and -, unique per application")
4066
+ ),
4067
+ size: import_cli12.Options.integer("size").pipe(
4068
+ import_cli12.Options.withDescription("Size in MiB, 1024 to 51200 (default: 10240)"),
4069
+ import_cli12.Options.optional
4070
+ ),
4071
+ app: appOption6,
4072
+ json: jsonOption2
4073
+ },
4074
+ (flags) => createVolume(flags).pipe(import_effect24.Effect.asVoid, reportErrors2("create the volume"))
4075
+ ).pipe(import_cli12.Command.withDescription("Create an empty volume"));
4076
+ var volumesRenameCommand = import_cli12.Command.make(
4077
+ "rename",
4078
+ { volumeId: volumeIdArg, name: import_cli12.Args.text({ name: "name" }).pipe(import_cli12.Args.withDescription("The new name")) },
4079
+ ({ volumeId, name }) => import_effect24.Effect.gen(function* () {
4080
+ const apiClient = yield* ApiClientService;
4081
+ const volume = yield* apiClient.request((client) => client.microvms.volumes.rename(volumeId, name));
4082
+ yield* import_effect24.Console.log(`Renamed the volume to ${volume.name}.`);
4083
+ }).pipe(reportErrors2("rename the volume"))
4084
+ ).pipe(import_cli12.Command.withDescription("Rename a volume"));
4085
+ var volumesSaveCommand = import_cli12.Command.make(
4086
+ "save",
4087
+ { microvmId: import_cli12.Args.text({ name: "microvm-id" }).pipe(import_cli12.Args.withDescription("A running microVM with volumes")) },
4088
+ ({ microvmId }) => import_effect24.Effect.gen(function* () {
4089
+ const apiClient = yield* ApiClientService;
4090
+ yield* apiClient.request((client) => client.microvms.saveVolumes(microvmId));
4091
+ yield* import_effect24.Console.log(`Saved the volumes of ${microvmId}.`);
4092
+ }).pipe(reportErrors2("save the volumes"))
4093
+ ).pipe(import_cli12.Command.withDescription("Save a running microVM's volumes without stopping it"));
4094
+ var volumesRemoveCommand = import_cli12.Command.make(
4095
+ "rm",
4096
+ { volumeId: volumeIdArg },
4097
+ ({ volumeId }) => import_effect24.Effect.gen(function* () {
4098
+ const apiClient = yield* ApiClientService;
4099
+ yield* apiClient.request((client) => client.microvms.volumes.delete(volumeId));
4100
+ yield* import_effect24.Console.log(`Deleted volume ${volumeId}.`);
4101
+ }).pipe(reportErrors2("delete the volume"))
4102
+ ).pipe(import_cli12.Command.withDescription("Delete a volume and its contents (it must not be attached)"));
4103
+ var volumesCommand = import_cli12.Command.make("volumes", {}, () => import_effect24.Effect.void).pipe(
4104
+ import_cli12.Command.withDescription("Persistent filesystems that outlive the microVMs they are attached to"),
4105
+ import_cli12.Command.withSubcommands([
4106
+ volumesListCommand,
4107
+ volumesCreateCommand,
4108
+ volumesRenameCommand,
4109
+ volumesSaveCommand,
4110
+ volumesRemoveCommand
4111
+ ])
4112
+ );
4113
+ var WEBHOOK_EVENTS = [
4114
+ "microvm.created",
4115
+ "microvm.running",
4116
+ "microvm.paused",
4117
+ "microvm.stopped",
4118
+ "microvm.failed"
4119
+ ];
4120
+ var webhookIdArg = import_cli12.Args.text({ name: "webhook-id" }).pipe(import_cli12.Args.withDescription("The webhook ID"));
4121
+ var webhooksListCommand = import_cli12.Command.make(
4122
+ "ls",
4123
+ { app: appOption6, json: jsonOption2 },
4124
+ ({ app, json }) => import_effect24.Effect.gen(function* () {
4125
+ const apiClient = yield* ApiClientService;
4126
+ const applicationId = yield* resolveApplicationId(app);
4127
+ const webhooks = yield* apiClient.request((client) => client.microvms.webhooks.list({ applicationId }));
4128
+ if (json) return yield* import_effect24.Console.log(JSON.stringify(webhooks, null, 2));
4129
+ if (webhooks.length === 0)
4130
+ return yield* import_effect24.Console.log('No webhooks yet. Add one with "gigadrive microvms webhooks add".');
4131
+ for (const line of formatWebhookTable(webhooks)) yield* import_effect24.Console.log(line);
4132
+ }).pipe(reportErrors2("list webhooks"))
4133
+ ).pipe(import_cli12.Command.withDescription("List the application's microVM webhooks"));
4134
+ var addWebhook = (flags) => import_effect24.Effect.gen(function* () {
4135
+ const apiClient = yield* ApiClientService;
4136
+ const unknown = flags.events.find((event) => !WEBHOOK_EVENTS.includes(event));
4137
+ if (unknown !== void 0) {
4138
+ return yield* new InvalidMicroVmOptionsError({
4139
+ message: `${unknown} is not a microVM event. Use ${WEBHOOK_EVENTS.join(", ")}.`
4140
+ });
4141
+ }
4142
+ const events = flags.events.length > 0 ? [...new Set(flags.events)] : WEBHOOK_EVENTS;
4143
+ const applicationId = yield* resolveApplicationId(flags.app);
4144
+ const webhook = yield* apiClient.request(
4145
+ (client) => client.microvms.webhooks.create({
4146
+ url: flags.url,
4147
+ events,
4148
+ ...import_effect24.Option.isSome(flags.description) ? { description: flags.description.value } : {},
4149
+ ...applicationId ? { applicationId } : {}
4150
+ })
4151
+ );
4152
+ if (flags.json) {
4153
+ yield* import_effect24.Console.log(JSON.stringify(webhook, null, 2));
4154
+ } else {
4155
+ yield* import_effect24.Console.log(`Added webhook ${webhook.id} for ${webhook.events.join(", ")}.`);
4156
+ yield* import_effect24.Console.log(`Signing secret (shown only now): ${webhook.secret}`);
4157
+ }
4158
+ return webhook;
4159
+ });
4160
+ var webhooksAddCommand = import_cli12.Command.make(
4161
+ "add",
4162
+ {
4163
+ url: import_cli12.Args.text({ name: "url" }).pipe(import_cli12.Args.withDescription("HTTPS URL on a public host")),
4164
+ events: import_cli12.Options.text("event").pipe(
4165
+ import_cli12.Options.withDescription(`Event to send (repeatable; default: all of ${WEBHOOK_EVENTS.join(", ")})`),
4166
+ import_cli12.Options.repeated
4167
+ ),
4168
+ description: import_cli12.Options.text("description").pipe(import_cli12.Options.withDescription("A note for yourself"), import_cli12.Options.optional),
4169
+ app: appOption6,
4170
+ json: jsonOption2
4171
+ },
4172
+ (flags) => addWebhook(flags).pipe(import_effect24.Effect.asVoid, reportErrors2("add the webhook"))
4173
+ ).pipe(import_cli12.Command.withDescription("Add an endpoint for microVM lifecycle events and print its signing secret"));
4174
+ var webhooksEnableCommand = (enabled) => import_cli12.Command.make(
4175
+ enabled ? "enable" : "disable",
4176
+ { webhookId: webhookIdArg },
4177
+ ({ webhookId }) => import_effect24.Effect.gen(function* () {
4178
+ const apiClient = yield* ApiClientService;
4179
+ const webhook = yield* apiClient.request((client) => client.microvms.webhooks.update(webhookId, { enabled }));
4180
+ yield* import_effect24.Console.log(`${webhook.url} is ${webhook.enabled ? "enabled" : "disabled"}.`);
4181
+ }).pipe(reportErrors2(enabled ? "enable the webhook" : "disable the webhook"))
4182
+ ).pipe(
4183
+ import_cli12.Command.withDescription(
4184
+ enabled ? "Send events to a webhook again" : "Stop sending events to a webhook; waiting retries are held"
4185
+ )
4186
+ );
4187
+ var webhooksSecretCommand = import_cli12.Command.make(
4188
+ "rotate-secret",
4189
+ { webhookId: webhookIdArg },
4190
+ ({ webhookId }) => import_effect24.Effect.gen(function* () {
4191
+ const apiClient = yield* ApiClientService;
4192
+ const webhook = yield* apiClient.request((client) => client.microvms.webhooks.rotateSecret(webhookId));
4193
+ yield* import_effect24.Console.log(`New signing secret (shown only now): ${webhook.secret}`);
4194
+ }).pipe(reportErrors2("replace the signing secret"))
4195
+ ).pipe(import_cli12.Command.withDescription("Replace the signing secret; the old one stops signing at once"));
4196
+ var webhooksDeliveriesCommand = import_cli12.Command.make(
4197
+ "deliveries",
4198
+ { webhookId: webhookIdArg, json: jsonOption2 },
4199
+ ({ webhookId, json }) => import_effect24.Effect.gen(function* () {
4200
+ const apiClient = yield* ApiClientService;
4201
+ const deliveries = yield* apiClient.request((client) => client.microvms.webhooks.listDeliveries(webhookId));
4202
+ if (json) return yield* import_effect24.Console.log(JSON.stringify(deliveries, null, 2));
4203
+ if (deliveries.length === 0) return yield* import_effect24.Console.log("Nothing has been sent yet.");
4204
+ for (const line of formatDeliveryTable(deliveries)) yield* import_effect24.Console.log(line);
4205
+ }).pipe(reportErrors2("list the deliveries"))
4206
+ ).pipe(import_cli12.Command.withDescription("Show a webhook's 50 most recent deliveries"));
4207
+ var webhooksRemoveCommand = import_cli12.Command.make(
4208
+ "rm",
4209
+ { webhookId: webhookIdArg },
4210
+ ({ webhookId }) => import_effect24.Effect.gen(function* () {
4211
+ const apiClient = yield* ApiClientService;
4212
+ yield* apiClient.request((client) => client.microvms.webhooks.delete(webhookId));
4213
+ yield* import_effect24.Console.log(`Deleted webhook ${webhookId}.`);
4214
+ }).pipe(reportErrors2("delete the webhook"))
4215
+ ).pipe(import_cli12.Command.withDescription("Delete a webhook"));
4216
+ var webhooksCommand = import_cli12.Command.make("webhooks", {}, () => import_effect24.Effect.void).pipe(
4217
+ import_cli12.Command.withDescription("Signed HTTPS requests when a microVM is created, starts, pauses, stops or fails"),
4218
+ import_cli12.Command.withSubcommands([
4219
+ webhooksListCommand,
4220
+ webhooksAddCommand,
4221
+ webhooksEnableCommand(true),
4222
+ webhooksEnableCommand(false),
4223
+ webhooksSecretCommand,
4224
+ webhooksDeliveriesCommand,
4225
+ webhooksRemoveCommand
4226
+ ])
4227
+ );
3948
4228
  var SUBCOMMANDS = [
3949
4229
  createCommand,
3950
4230
  listCommand2,
@@ -3957,7 +4237,9 @@ var SUBCOMMANDS = [
3957
4237
  removeCommand2,
3958
4238
  extendCommand,
3959
4239
  networkCommand,
3960
- imagesCommand
4240
+ imagesCommand,
4241
+ volumesCommand,
4242
+ webhooksCommand
3961
4243
  ];
3962
4244
  var microvmsCommand = import_cli12.Command.make("microvms", {}, () => import_effect24.Effect.void).pipe(
3963
4245
  import_cli12.Command.withDescription("Isolated Linux machines for running code: create, exec, shell, cp, stop"),