@molecule/api-code-sandbox-flyio 1.3.8 → 1.4.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/README.md +72 -139
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +30 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-11-
|
|
6
|
+
Generated: 2026-11-04T14:19:55.149Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-code-sandbox-flyio
|
|
@@ -484,169 +484,95 @@ Every option is also readable from an environment variable (see
|
|
|
484
484
|
|
|
485
485
|
```typescript
|
|
486
486
|
interface FlyioConfig {
|
|
487
|
-
/**
|
|
488
|
-
* Fly API token (sent as `Authorization: Bearer <token>`). Falls back to
|
|
489
|
-
* `FLY_API_TOKEN`, then `FLY_ACCESS_TOKEN`. Required — the provider throws a
|
|
490
|
-
* named error on the first API call if none is resolvable.
|
|
491
|
-
*/
|
|
487
|
+
/** Fly API token (sent as `Authorization: Bearer <token>`). */
|
|
492
488
|
apiToken?: string
|
|
493
|
-
/**
|
|
494
|
-
* Machines API base URL, INCLUDING the `/v1` path segment. Defaults to
|
|
495
|
-
* `FLY_API_HOSTNAME` (with `/v1` appended when the value has no path) or
|
|
496
|
-
* `https://api.machines.dev/v1`. From inside a Fly private network the
|
|
497
|
-
* documented internal endpoint is `http://_api.internal:4280/v1`.
|
|
498
|
-
*/
|
|
489
|
+
/** Machines API base URL, INCLUDING the `/v1` path segment. */
|
|
499
490
|
apiUrl?: string
|
|
500
|
-
/**
|
|
501
|
-
* Fly organization slug that owns the sandbox apps. Falls back to
|
|
502
|
-
* `FLY_ORG_SLUG`, then `personal`. Required for app creation and for
|
|
503
|
-
* {@link FlyioConfig.appPerProject} listing.
|
|
504
|
-
*/
|
|
491
|
+
/** Fly organization slug that owns the sandbox apps. */
|
|
505
492
|
orgSlug?: string
|
|
506
|
-
/**
|
|
507
|
-
* Shared Fly app that holds every sandbox Machine. Only used when
|
|
508
|
-
* `appPerProject` is `false`. Falls back to `FLY_SANDBOX_APP`.
|
|
509
|
-
*
|
|
510
|
-
* A shared app puts every tenant on ONE 6PN private network, where each
|
|
511
|
-
* sandbox can reach every other sandbox's dev-server ports by private IPv6.
|
|
512
|
-
* That is the same cross-tenant exposure the Docker bond's `bridge` network
|
|
513
|
-
* has, so this mode is REFUSED in production.
|
|
514
|
-
*/
|
|
493
|
+
/** Shared Fly app that holds every sandbox Machine. */
|
|
515
494
|
appName?: string
|
|
516
|
-
/**
|
|
517
|
-
* Name prefix for the per-project Fly app when `appPerProject` is `true`
|
|
518
|
-
* (default). The app is `<appPrefix>-<sanitized projectId>`. Falls back to
|
|
519
|
-
* `FLY_SANDBOX_APP_PREFIX`, then `mol-sandbox`.
|
|
520
|
-
*/
|
|
495
|
+
/** Name prefix for the per-project Fly app when `appPerProject` is `true` (default). */
|
|
521
496
|
appPrefix?: string
|
|
522
|
-
/**
|
|
523
|
-
* Create one Fly app — on its own custom 6PN private network — per project,
|
|
524
|
-
* so tenants are network-isolated from each other. Defaults to `true`.
|
|
525
|
-
* Set `false` (or `FLY_SANDBOX_APP_PER_PROJECT=false`) only for a
|
|
526
|
-
* single-tenant deployment; it is refused in production.
|
|
527
|
-
*/
|
|
497
|
+
/** Create one Fly app — on its own custom 6PN private network — per project, so tenants are network-isolated from each other. */
|
|
528
498
|
appPerProject?: boolean
|
|
529
|
-
/**
|
|
530
|
-
* Custom 6PN network name for a per-project app. Defaults to the app name, so
|
|
531
|
-
* every project lands on its own private network. Ignored when
|
|
532
|
-
* `appPerProject` is `false`. A Fly app's network CANNOT be changed after
|
|
533
|
-
* creation.
|
|
534
|
-
*/
|
|
499
|
+
/** Custom 6PN network name for a per-project app. */
|
|
535
500
|
network?: string
|
|
536
|
-
/**
|
|
537
|
-
* Default Fly region for Machines and volumes (e.g. `iad`). Defaults to
|
|
538
|
-
* `iad`; `SandboxConfig.region` overrides it for one recorded project create.
|
|
539
|
-
* The API Machine's own `FLY_REGION` is never a tenant-placement fallback.
|
|
540
|
-
*/
|
|
501
|
+
/** Default Fly region for Machines and volumes (e.g. `iad`). */
|
|
541
502
|
region?: string
|
|
542
|
-
/**
|
|
543
|
-
* OCI image every sandbox Machine runs. Must be pullable by Fly — a tag in
|
|
544
|
-
* the org's `registry.fly.io` repository or a public registry. A LOCAL
|
|
545
|
-
* `molecule-sandbox:latest` is not reachable by Fly; push it first. Falls
|
|
546
|
-
* back to `FLY_SANDBOX_IMAGE`, then `registry.fly.io/molecule-sandbox:latest`.
|
|
547
|
-
*/
|
|
503
|
+
/** OCI image every sandbox Machine runs. */
|
|
548
504
|
baseImage?: string
|
|
549
|
-
/** Default vCPU count (`guest.cpus`).
|
|
505
|
+
/** Default vCPU count (`guest.cpus`). */
|
|
550
506
|
defaultCpu?: number
|
|
551
|
-
/** Default `guest.cpu_kind` — `shared` or `performance`.
|
|
507
|
+
/** Default `guest.cpu_kind` — `shared` or `performance`. */
|
|
552
508
|
defaultCpuKind?: string
|
|
553
|
-
/** Default memory in MB (`guest.memory_mb`).
|
|
509
|
+
/** Default memory in MB (`guest.memory_mb`). */
|
|
554
510
|
defaultMemoryMB?: number
|
|
555
|
-
/**
|
|
556
|
-
* Size in GB of the volume created for a sandbox's `/workspace` when
|
|
557
|
-
* `SandboxConfig.volumeName` is set. Fly volumes are sized in whole GB, so the
|
|
558
|
-
* core's `resources.diskMB` is rounded UP to the next GB. Defaults to 10.
|
|
559
|
-
*/
|
|
511
|
+
/** Size in GB of the volume created for a sandbox's `/workspace` when `SandboxConfig.volumeName` is set. */
|
|
560
512
|
defaultVolumeGB?: number
|
|
561
|
-
/** Internal port the preview service forwards to (the Vite dev server).
|
|
513
|
+
/** Internal port the preview service forwards to (the Vite dev server). */
|
|
562
514
|
previewPort?: number
|
|
563
|
-
/**
|
|
564
|
-
* Preview URL template. Placeholders `{app}`, `{machineId}` and `{port}` are
|
|
565
|
-
* all replaced globally. Defaults to `https://{app}.fly.dev`.
|
|
566
|
-
*
|
|
567
|
-
* For a control plane that runs on the SAME Fly 6PN, the private form
|
|
568
|
-
* `http://{machineId}.vm.{app}.internal:{port}` reaches the Machine with no
|
|
569
|
-
* public exposure at all — but it does NOT work across custom 6PNs, which is
|
|
570
|
-
* exactly what `appPerProject` creates. See the module `@remarks`.
|
|
571
|
-
*/
|
|
515
|
+
/** Preview URL template. */
|
|
572
516
|
previewUrlTemplate?: string
|
|
573
|
-
/**
|
|
574
|
-
* Attach a public Fly Proxy service (`80` → http, `443` → tls+http) forwarding
|
|
575
|
-
* to {@link FlyioConfig.previewPort}. Defaults to `true`. Set `false` for a
|
|
576
|
-
* fully private sandbox reached only over 6PN/Flycast.
|
|
577
|
-
*/
|
|
517
|
+
/** Attach a public Fly Proxy service (`80` → http, `443` → tls+http) forwarding to {@link FlyioConfig.previewPort}. */
|
|
578
518
|
publicService?: boolean
|
|
579
|
-
/**
|
|
580
|
-
* Fly Proxy idle behaviour for the preview service. Defaults to `'off'` —
|
|
581
|
-
* never idle the Machine — because the autostop timer counts the PROXY
|
|
582
|
-
* service's idle time, not the workload's, so a sandbox with nobody looking at
|
|
583
|
-
* its preview is suspended out from under a build that is still running.
|
|
584
|
-
* `suspend` is the scale-to-zero mapping (resume from a memory snapshot on the
|
|
585
|
-
* next request) and `stop` is a full stop with a cold boot on wake; opt into
|
|
586
|
-
* either deliberately.
|
|
587
|
-
*
|
|
588
|
-
* This is the provider's STEADY-STATE policy, reported as
|
|
589
|
-
* `SandboxProvider.defaultIdleAction`. A caller can override it for one sandbox
|
|
590
|
-
* with `SandboxConfig.idleAction` and restore this value afterwards with
|
|
591
|
-
* `Sandbox.setIdleAction` — which is what a production deploy does so its
|
|
592
|
-
* destination cannot suspend mid-copy.
|
|
593
|
-
*/
|
|
519
|
+
/** Fly Proxy idle behaviour for the preview service. */
|
|
594
520
|
autostop?: 'off' | 'stop' | 'suspend'
|
|
595
|
-
/**
|
|
596
|
-
* Allocate a shared Anycast IPv4 for a newly-created app so `<app>.fly.dev`
|
|
597
|
-
* serves traffic. Defaults to `false` (opt-in). See the module `@remarks` —
|
|
598
|
-
* the accepted `type` values for the IP-assignment endpoint are NOT enumerated
|
|
599
|
-
* in Fly's OpenAPI specification.
|
|
600
|
-
*/
|
|
521
|
+
/** Allocate a shared Anycast IPv4 for a newly-created app so `<app>.fly.dev` serves traffic. */
|
|
601
522
|
assignSharedIpv4?: boolean
|
|
602
|
-
/** IP type requested when {@link FlyioConfig.assignSharedIpv4} is on.
|
|
523
|
+
/** IP type requested when {@link FlyioConfig.assignSharedIpv4} is on. */
|
|
603
524
|
ipAssignmentType?: string
|
|
604
|
-
/** Prefix for the Machine metadata keys this provider owns.
|
|
525
|
+
/** Prefix for the Machine metadata keys this provider owns. */
|
|
605
526
|
metadataPrefix?: string
|
|
606
|
-
/** Durable create-attempt ownership.
|
|
527
|
+
/** Durable create-attempt ownership. */
|
|
607
528
|
createAttemptJournal?: FlyCreateAttemptJournal
|
|
608
|
-
/** Timeout for a single Machines API request, in ms.
|
|
529
|
+
/** Timeout for a single Machines API request, in ms. */
|
|
609
530
|
requestTimeoutMs?: number
|
|
610
|
-
/**
|
|
611
|
-
* TOTAL budget, in seconds, that `create()`/`start()`/`wake()` block waiting
|
|
612
|
-
* for the Machine to actually reach `started`. Fly's `GET .../wait` blocks
|
|
613
|
-
* for at most 60 seconds per call, so a larger budget is spent as consecutive
|
|
614
|
-
* wait rounds. Defaults to 180 — a Machine whose image is not yet cached on
|
|
615
|
-
* its host (every first boot after an image push) pulls it before starting,
|
|
616
|
-
* and that alone can exceed a single 60 s round.
|
|
617
|
-
*
|
|
618
|
-
* Without this wait, `start()` resolves the moment Fly ACCEPTS the request,
|
|
619
|
-
* and the caller's very next `exec` hits a Machine that is not running yet.
|
|
620
|
-
*/
|
|
531
|
+
/** TOTAL budget, in seconds, that `create()`/`start()`/`wake()` block waiting for the Machine to actually reach `started`. */
|
|
621
532
|
startTimeoutSeconds?: number
|
|
622
|
-
/**
|
|
623
|
-
* Ports a sandbox Machine may open outbound connections on. Setting this makes
|
|
624
|
-
* the provider apply a Fly **network policy** to every app it provisions;
|
|
625
|
-
* leaving it unset applies no policy at all, and `verifyEgress()` will then
|
|
626
|
-
* observe (correctly) that egress is `open`. Falls back to
|
|
627
|
-
* `FLY_SANDBOX_EGRESS_ALLOWED_PORTS` (`tcp:3128,udp:53`).
|
|
628
|
-
*
|
|
629
|
-
* Read {@link https://fly.io/docs/machines/guides-examples/network-policies/}
|
|
630
|
-
* before choosing a value, because the mechanism is narrower than it looks:
|
|
631
|
-
* rules match protocol and port ONLY — no host, no CIDR, no ranges — so this
|
|
632
|
-
* can never be a host allowlist. `tcp:443` lets a sandbox reach EVERY host on
|
|
633
|
-
* the internet that listens on 443. To get a host allowlist, allow only the
|
|
634
|
-
* port of an egress proxy you control and route sandbox traffic through it.
|
|
635
|
-
*
|
|
636
|
-
* An empty array is rejected rather than treated as "deny all": Fly documents
|
|
637
|
-
* the deny default as a consequence of an `allow` rule existing, and says
|
|
638
|
-
* nothing about a rule with no ports.
|
|
639
|
-
*/
|
|
533
|
+
/** Ports a sandbox Machine may open outbound connections on. */
|
|
640
534
|
egressAllowedPorts?: FlyNetworkPolicyPort[]
|
|
641
|
-
/** Name of the egress policy this provider owns on each app.
|
|
535
|
+
/** Name of the egress policy this provider owns on each app. */
|
|
642
536
|
egressPolicyName?: string
|
|
643
|
-
/**
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
537
|
+
/** Apps every sandbox must be able to reach ACROSS its per-project 6PN — the tenant Postgres cluster and the control plane's egress proxy. */
|
|
538
|
+
privateServices?: FlyPrivateService[]
|
|
539
|
+
/** Per-create regional overrides for {@link FlyioConfig.privateServices}. */
|
|
540
|
+
privateServicesByRegion?: Record<string, FlyPrivateService[]>
|
|
541
|
+
/** `type` sent to `POST /apps/{app}/ip_assignments` when allocating a Flycast address. */
|
|
542
|
+
privateIpAssignmentType?: string
|
|
543
|
+
/** Literal `ip:port` targets `verifyEgress()` attempts raw TCP connections to. */
|
|
544
|
+
egressProbeTargets?: string[]
|
|
545
|
+
/** Per-connection timeout for the egress probe, in ms. */
|
|
546
|
+
egressProbeTimeoutMs?: number
|
|
547
|
+
/** Image the throwaway `verifyEgress()` probe Machine runs. */
|
|
548
|
+
egressProbeImage?: string
|
|
549
|
+
/** Bucket holding sandbox templates. */
|
|
550
|
+
templateBucket?: string
|
|
551
|
+
/** S3 endpoint for {@link FlyioConfig.templateBucket}, e.g. Tigris's `https://t3.storage.dev`. */
|
|
552
|
+
templateEndpoint?: string
|
|
553
|
+
/** Region used to sign template-store requests. */
|
|
554
|
+
templateRegion?: string
|
|
555
|
+
/** Access key for the template store. */
|
|
556
|
+
templateAccessKeyId?: string
|
|
557
|
+
/** Secret key for the template store. */
|
|
558
|
+
templateSecretAccessKey?: string
|
|
559
|
+
/** Session token for temporary template-store credentials. */
|
|
560
|
+
templateSessionToken?: string
|
|
561
|
+
/** Key prefix every template object lives under. */
|
|
562
|
+
templatePrefix?: string
|
|
563
|
+
/** Address the template bucket path-style (`https://endpoint/bucket/key`) instead of virtual-host style. */
|
|
564
|
+
templateForcePathStyle?: boolean
|
|
565
|
+
/** Lifetime of the presigned capture/restore URL handed to a sandbox, in seconds. */
|
|
566
|
+
templateUrlExpirySeconds?: number
|
|
567
|
+
/** Wall-clock budget for one capture or restore transfer, in ms. */
|
|
568
|
+
templateTransferTimeoutMs?: number
|
|
569
|
+
/** Largest template archive this provider will store, in bytes. */
|
|
570
|
+
templateMaxArchiveBytes?: number
|
|
571
|
+
}
|
|
648
572
|
```
|
|
649
573
|
|
|
574
|
+
_(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/api-code-sandbox-flyio/dist/types.d.ts`.)_
|
|
575
|
+
|
|
650
576
|
#### `ProcessEnv`
|
|
651
577
|
|
|
652
578
|
Environment variables the Fly.io sandbox provider reads. Every one is
|
|
@@ -1605,8 +1531,15 @@ its own 6PN: under a policy allowing only `udp:53`, `molecule-pg-tenant.flycast`
|
|
|
1605
1531
|
and `molecule-api.flycast` both RESOLVED and every TCP connect to them was
|
|
1606
1532
|
dropped; re-applying the policy with `tcp:443` opened 443 and nothing else.
|
|
1607
1533
|
Fly's sentence is about INGRESS — a Machine's egress TOWARD a Flycast address
|
|
1608
|
-
|
|
1609
|
-
|
|
1534
|
+
is filtered like any other. Drop a declared service's port and every database
|
|
1535
|
+
connection in the fleet goes with it.
|
|
1536
|
+
|
|
1537
|
+
The cost of each derived port, stated plainly because a Fly policy has no
|
|
1538
|
+
destination field of any kind (also measured — see the `egress.ts` module
|
|
1539
|
+
description): the port is opened to EVERY host on the internet, not only to
|
|
1540
|
+
the private service it was derived for. That residual cannot be closed at this
|
|
1541
|
+
layer; it is named in `verifyEgress()`'s verdict and in
|
|
1542
|
+
`docs/sandbox-egress-enforcement.md`.
|
|
1610
1543
|
|
|
1611
1544
|
```typescript
|
|
1612
1545
|
function mergeEgressPorts(
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAaV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EACL,YAAY,EAKb,MAAM,UAAU,CAAA;AA8BjB,OAAO,KAAK,EAEV,wBAAwB,EAEzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;AAyBnB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAIjG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE5E;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAaV,eAAe,EAGhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EACL,YAAY,EAKb,MAAM,UAAU,CAAA;AA8BjB,OAAO,KAAK,EAEV,wBAAwB,EAEzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAIV,WAAW,EAUZ,MAAM,YAAY,CAAA;AAyBnB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAIjG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE5E;AA4zID;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,GACzB,eAAe,GAAG,wBAAwB,CAE5C"}
|
package/dist/provider.js
CHANGED
|
@@ -3336,6 +3336,19 @@ class FlyioSandboxProvider {
|
|
|
3336
3336
|
`${totalSeconds}s (last observed state: ${lastState ?? 'unknown'}).`,
|
|
3337
3337
|
}), { cause: lastError });
|
|
3338
3338
|
}
|
|
3339
|
+
/**
|
|
3340
|
+
* Activate an already populated Machine with one full-config update: startup command and idle
|
|
3341
|
+
* policy change together. The former command keeps the workload stopped, so the update's reboot
|
|
3342
|
+
* is the workload's first boot. Fly preserves parked Machines with skip_launch; startMachine
|
|
3343
|
+
* explicitly wakes those and waits, and is idempotent after a running Machine's update.
|
|
3344
|
+
*/
|
|
3345
|
+
async activate(id, options) {
|
|
3346
|
+
if (!options.command.length)
|
|
3347
|
+
throw new Error('A sandbox activation needs a startup command');
|
|
3348
|
+
const { app, machineId } = parseSandboxId(id);
|
|
3349
|
+
await this.applyIdleAction(app, machineId, options.idleAction, options.command);
|
|
3350
|
+
await this.startMachine(app, machineId);
|
|
3351
|
+
}
|
|
3339
3352
|
/**
|
|
3340
3353
|
* Applies an idle policy to a running Machine's public service, in place.
|
|
3341
3354
|
*
|
|
@@ -3356,10 +3369,11 @@ class FlyioSandboxProvider {
|
|
|
3356
3369
|
* @param app - The Fly app name.
|
|
3357
3370
|
* @param machineId - The Fly Machine id.
|
|
3358
3371
|
* @param action - The idle action to apply to every service.
|
|
3372
|
+
* @param command - An activation command installed in the SAME update as the policy.
|
|
3359
3373
|
* @throws {Error} When the Machine or its config cannot be read, the update is
|
|
3360
3374
|
* rejected, or a rebooted Machine does not come back to `started`.
|
|
3361
3375
|
*/
|
|
3362
|
-
async applyIdleAction(app, machineId, action) {
|
|
3376
|
+
async applyIdleAction(app, machineId, action, command) {
|
|
3363
3377
|
const machine = await this.client.request(`/apps/${app}/machines/${machineId}`, {
|
|
3364
3378
|
nullOn: [404],
|
|
3365
3379
|
});
|
|
@@ -3370,14 +3384,21 @@ class FlyioSandboxProvider {
|
|
|
3370
3384
|
}));
|
|
3371
3385
|
}
|
|
3372
3386
|
const services = machine.config.services;
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
if (
|
|
3376
|
-
return;
|
|
3387
|
+
const samePolicy = !services?.length || services.every((service) => (service.autostop ?? 'off') === action);
|
|
3388
|
+
const sameCommand = !command || JSON.stringify(machine.config.init?.exec) === JSON.stringify(command);
|
|
3389
|
+
if (samePolicy && sameCommand)
|
|
3390
|
+
return mapMachineState(machine.state ?? 'created');
|
|
3377
3391
|
const config = {
|
|
3378
3392
|
...machine.config,
|
|
3379
|
-
services
|
|
3393
|
+
...(services
|
|
3394
|
+
? { services: services.map((service) => ({ ...service, autostop: action })) }
|
|
3395
|
+
: {}),
|
|
3380
3396
|
};
|
|
3397
|
+
if (command) {
|
|
3398
|
+
config.init = { ...machine.config.init, exec: [...command] };
|
|
3399
|
+
delete config.init.entrypoint;
|
|
3400
|
+
delete config.init.cmd;
|
|
3401
|
+
}
|
|
3381
3402
|
// A running Machine reboots to apply the config, so wait for it; a parked
|
|
3382
3403
|
// one is updated without being started (there is nothing to wait for).
|
|
3383
3404
|
const wasRunning = machine.state === 'started' || machine.state === 'starting';
|
|
@@ -3394,6 +3415,7 @@ class FlyioSandboxProvider {
|
|
|
3394
3415
|
});
|
|
3395
3416
|
if (wasRunning)
|
|
3396
3417
|
await this.waitForStarted(app, machineId);
|
|
3418
|
+
return wasRunning ? 'running' : mapMachineState(machine.state ?? 'created');
|
|
3397
3419
|
}
|
|
3398
3420
|
/**
|
|
3399
3421
|
* Builds the `Sandbox` facade for one Machine.
|
|
@@ -3442,11 +3464,10 @@ class FlyioSandboxProvider {
|
|
|
3442
3464
|
/**
|
|
3443
3465
|
* Applies an idle policy to this Machine's public service (see
|
|
3444
3466
|
* {@link FlyioSandboxProvider.applyIdleAction} for the endpoint and the
|
|
3445
|
-
* reboot it costs).
|
|
3467
|
+
* reboot it costs). A parked Machine stays parked; a rebooted Machine is running again.
|
|
3446
3468
|
*/
|
|
3447
3469
|
async setIdleAction(action) {
|
|
3448
|
-
await provider.applyIdleAction(app, machineId, action);
|
|
3449
|
-
this.status = 'running';
|
|
3470
|
+
this.status = await provider.applyIdleAction(app, machineId, action);
|
|
3450
3471
|
},
|
|
3451
3472
|
async exec(command, opts) {
|
|
3452
3473
|
return await execCommand(exec, command, opts, WORKSPACE_PATH);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-code-sandbox-flyio",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Fly.io Machines (Firecracker microVM) code sandbox provider",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"license": "Apache-2.0",
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@molecule/api-bond": "1.0.4",
|
|
34
|
-
"@molecule/api-code-sandbox": "1.
|
|
34
|
+
"@molecule/api-code-sandbox": "1.7.0",
|
|
35
35
|
"@types/node": "26.6.5",
|
|
36
36
|
"typescript": "6.0.3",
|
|
37
37
|
"vitest": "4.1.12"
|