@skaile/provider-fly 0.0.0 → 0.1.0-beta.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @skaile/provider-fly
2
2
 
3
+ ## 0.1.0-beta.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ce3f33f`](https://github.com/skaile-ai/workspaces/commit/ce3f33f5b85f81d1c80689a99a8e63cf0c2afc07) Thanks [@mortegro](https://github.com/mortegro)! - Fix deploy-handle readiness timeout + dedupe deploy-target helpers.
8
+
9
+ - **Fix:** `DeployHandle.waitReady(timeoutMs)` silently ignored its `timeoutMs`
10
+ argument in the fly / k8s / vercel-sandbox targets, always using a hard-coded
11
+ internal deadline. The caller's timeout is now honored.
12
+ - **Fix (`@skaile/provider-fly`):** `deleteMachine` swallowed _all_ errors while
13
+ only intending to ignore a 404, so `stop()` could falsely report success on a
14
+ still-running (still-billing) machine. Non-404 errors now propagate.
15
+ - **Added (`@skaile/workspaces/plugin-registry`):** shared deploy helpers —
16
+ `buildStrategySchema` / `BuildStrategy`, abort-aware `sleep`, generic
17
+ `pollUntil`, and the `makeDeployHandle` factory — replacing the per-provider
18
+ copies of the sleep loop, handle wrapper, and build-strategy enum.
19
+
20
+ ## 0.1.0-beta.0
21
+
22
+ ### Minor Changes
23
+
24
+ - [#54](https://github.com/skaile-ai/workspaces/pull/54) [`7bdaf11`](https://github.com/skaile-ai/workspaces/commit/7bdaf115c251136288a94a7f880b3a0f52145be8) Thanks [@mortegro](https://github.com/mortegro)! - Initial release.
25
+
26
+ Five plugins for `@skaile/workspaces`' `pluginRegistry`, each exporting
27
+ `register(pluginRegistry)`:
28
+
29
+ - `@skaile/provider-fly` — Fly Machines deploy target (`tlsTermination: edge`,
30
+ `buildStrategy: remote`).
31
+ - `@skaile/provider-vercel-sandbox` — Vercel Sandbox deploy target
32
+ (`tlsTermination: edge`, `buildStrategy: remote`).
33
+ - `@skaile/provider-k8s` — Kubernetes deploy target, ClusterIP + port-forward
34
+ only in 0.1.0 (`tlsTermination: edge`, `buildStrategy: local`).
35
+ - `@skaile/connector-redis` — Redis connector (deps `ioredis`).
36
+ - `@skaile/connector-yjs` — Yjs CRDT connector (deps `yjs`).
37
+ </content>
38
+
3
39
  ## 0.1.0
4
40
 
5
41
  - Initial release.
package/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
1
  // src/target.ts
2
+ import {
3
+ buildStrategySchema,
4
+ makeDeployHandle,
5
+ pollUntil
6
+ } from "@skaile/workspaces/plugin-registry";
2
7
  import * as z from "zod";
3
8
 
4
9
  // src/fly-client.ts
@@ -63,7 +68,8 @@ var FlyClient = class {
63
68
  async deleteMachine(app, machineId, signal) {
64
69
  try {
65
70
  await this.request("DELETE", `/v1/apps/${app}/machines/${machineId}?force=true`, signal);
66
- } catch {
71
+ } catch (err) {
72
+ if (!/failed: 404\b/.test(err.message)) throw err;
67
73
  }
68
74
  }
69
75
  };
@@ -77,7 +83,7 @@ var flyConfigSchema = z.object({
77
83
  port: z.number().int().positive().default(8080),
78
84
  apiToken: z.string().min(1),
79
85
  apiBaseUrl: z.string().url().default("https://api.machines.dev"),
80
- buildStrategy: z.enum(["local", "remote", "managed"]).default("remote")
86
+ buildStrategy: buildStrategySchema.default("remote")
81
87
  }).refine((c) => c.buildStrategy === "remote" || c.image !== void 0, {
82
88
  message: "image required when buildStrategy is local or managed",
83
89
  path: ["image"]
@@ -94,40 +100,15 @@ function guestForSize(size) {
94
100
  const cpus = match ? Number(match[1]) : 1;
95
101
  return { cpu_kind: cpuKind, cpus, memory_mb: 256 * cpus };
96
102
  }
97
- async function sleep(ms, signal) {
98
- return await new Promise((resolve, reject) => {
99
- const timer = setTimeout(() => {
100
- signal.removeEventListener("abort", onAbort);
101
- resolve();
102
- }, ms);
103
- const onAbort = () => {
104
- clearTimeout(timer);
105
- reject(new Error("aborted while polling fly machine state"));
106
- };
107
- if (signal.aborted) onAbort();
108
- else signal.addEventListener("abort", onAbort);
109
- });
110
- }
111
103
  function buildHandle(client, payload, config, ctx, ready, initialState) {
112
- let state = initialState;
113
- return {
104
+ return makeDeployHandle({
114
105
  // Fly terminates TLS at the edge; the public form is the app's fly.dev host.
115
106
  wsUrl: `wss://${payload.app}.fly.dev:${config.port}`,
116
- wsAuth: ctx.wsAuth ? "<redacted>" : void 0,
117
- get state() {
118
- return state;
119
- },
107
+ ctx,
120
108
  payload,
121
- async waitReady(timeoutMs) {
122
- try {
123
- await ready();
124
- state = "ready";
125
- } catch (err) {
126
- state = "errored";
127
- throw err;
128
- }
129
- void timeoutMs;
130
- },
109
+ initialState,
110
+ defaultReadyTimeoutMs: DEFAULT_READY_TIMEOUT_MS,
111
+ ready,
131
112
  async health() {
132
113
  try {
133
114
  const machine = await client.getMachine(payload.app, payload.machineId, ctx.signal);
@@ -138,11 +119,9 @@ function buildHandle(client, payload, config, ctx, ready, initialState) {
138
119
  }
139
120
  },
140
121
  async stop() {
141
- state = "stopping";
142
122
  await client.deleteMachine(payload.app, payload.machineId, ctx.signal);
143
- state = "stopped";
144
123
  }
145
- };
124
+ });
146
125
  }
147
126
  function makeFlyDeployTarget(opts = {}) {
148
127
  const spawnFly = opts.spawnFly ?? defaultSpawnFly;
@@ -199,17 +178,13 @@ function makeFlyDeployTarget(opts = {}) {
199
178
  );
200
179
  log.info("fly machine created", { app: config.app, machineId: machine.id });
201
180
  const payload = { machineId: machine.id, app: config.app };
202
- const ready = async () => {
203
- const deadline = Date.now() + DEFAULT_READY_TIMEOUT_MS;
204
- let current = machine;
205
- while (current.state !== "started") {
206
- if (Date.now() > deadline) {
207
- throw new Error(`timed out waiting for fly machine ${machine.id} to start`);
208
- }
209
- await sleep(pollIntervalMs, ctx.signal);
210
- current = await client.getMachine(config.app, machine.id, ctx.signal);
211
- }
212
- };
181
+ const ready = (timeoutMs, signal) => pollUntil({
182
+ check: async () => (await client.getMachine(config.app, machine.id, signal)).state === "started",
183
+ timeoutMs,
184
+ intervalMs: pollIntervalMs,
185
+ signal,
186
+ timeoutMessage: `timed out waiting for fly machine ${machine.id} to start`
187
+ });
213
188
  return buildHandle(client, payload, config, ctx, ready, "starting");
214
189
  },
215
190
  async restore(payload, ctx) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/target.ts","../src/fly-client.ts","../src/index.ts"],"sourcesContent":["/**\n * `flyDeployTarget` — deploy a workspace as a Fly Machine and reach it over the\n * app's public edge (`wss://<app>.fly.dev`). `tlsTermination: \"edge\"` because\n * Fly terminates TLS at its anycast edge before forwarding to the machine.\n *\n * The Fly Machines API is plain HTTP, so this carries no SDK dependency — see\n * `fly-client.ts`. The CLI spawner and `fetch` are injectable through the\n * `opts` bag so the tests run without a network or a real `fly` binary.\n */\n\nimport type { DeployContext, DeployHandle, DeployTarget } from \"@skaile/workspaces/plugin-registry\";\nimport * as z from \"zod\";\nimport {\n defaultSpawnFly,\n type FetchImpl,\n FlyClient,\n type FlyMachine,\n type SpawnFly,\n} from \"./fly-client.js\";\n\nconst flyConfigSchema = z\n .object({\n app: z.string().min(1),\n region: z.string().default(\"ams\"),\n image: z.string().optional(),\n size: z.string().default(\"shared-cpu-1x\"),\n port: z.number().int().positive().default(8080),\n apiToken: z.string().min(1),\n apiBaseUrl: z.string().url().default(\"https://api.machines.dev\"),\n buildStrategy: z.enum([\"local\", \"remote\", \"managed\"]).default(\"remote\"),\n })\n .refine((c) => c.buildStrategy === \"remote\" || c.image !== undefined, {\n message: \"image required when buildStrategy is local or managed\",\n path: [\"image\"],\n });\n\ntype FlyConfig = z.infer<typeof flyConfigSchema>;\n\nconst flyPayloadSchema = z.object({\n machineId: z.string(),\n app: z.string(),\n});\n\ntype FlyPayload = z.infer<typeof flyPayloadSchema>;\n\nconst DEFAULT_POLL_INTERVAL_MS = 1_000;\nconst DEFAULT_READY_TIMEOUT_MS = 120_000;\n\n/** Injectable seams so the target's tests never touch a real network/CLI. */\nexport interface FlyTargetOptions {\n fetchImpl?: FetchImpl;\n spawnFly?: SpawnFly;\n pollIntervalMs?: number;\n}\n\n/** Map a Fly size string to the guest shape the Machines API expects. */\nfunction guestForSize(size: string): { cpu_kind: string; cpus: number; memory_mb: number } {\n // \"shared-cpu-1x\" → shared / 1 cpu / 256mb. We only special-case the cpu kind;\n // memory follows Fly's default for the smallest shared preset.\n const cpuKind = size.startsWith(\"performance\") ? \"performance\" : \"shared\";\n const match = size.match(/-(\\d+)x$/);\n const cpus = match ? Number(match[1]) : 1;\n return { cpu_kind: cpuKind, cpus, memory_mb: 256 * cpus };\n}\n\nasync function sleep(ms: number, signal: AbortSignal): Promise<void> {\n return await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error(\"aborted while polling fly machine state\"));\n };\n if (signal.aborted) onAbort();\n else signal.addEventListener(\"abort\", onAbort);\n });\n}\n\nfunction buildHandle(\n client: FlyClient,\n payload: FlyPayload,\n config: Pick<FlyConfig, \"port\">,\n ctx: DeployContext,\n ready: () => Promise<void>,\n initialState: DeployHandle[\"state\"],\n): DeployHandle<FlyPayload> {\n let state: DeployHandle[\"state\"] = initialState;\n return {\n // Fly terminates TLS at the edge; the public form is the app's fly.dev host.\n wsUrl: `wss://${payload.app}.fly.dev:${config.port}`,\n wsAuth: ctx.wsAuth ? \"<redacted>\" : undefined,\n get state() {\n return state;\n },\n payload,\n async waitReady(timeoutMs?: number) {\n try {\n await ready();\n state = \"ready\";\n } catch (err) {\n state = \"errored\";\n throw err;\n }\n void timeoutMs;\n },\n async health() {\n try {\n const machine = await client.getMachine(payload.app, payload.machineId, ctx.signal);\n const healthy = machine.state === \"started\";\n return healthy\n ? { healthy }\n : { healthy, detail: `machine ${payload.machineId} state=${machine.state}` };\n } catch (err) {\n return { healthy: false, detail: (err as Error).message };\n }\n },\n async stop() {\n state = \"stopping\";\n await client.deleteMachine(payload.app, payload.machineId, ctx.signal);\n state = \"stopped\";\n },\n };\n}\n\n/** Build the Fly deploy target with optional injected seams. */\nexport function makeFlyDeployTarget(\n opts: FlyTargetOptions = {},\n): DeployTarget<FlyConfig, FlyPayload> {\n const spawnFly = opts.spawnFly ?? defaultSpawnFly;\n const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n\n return {\n id: \"fly\",\n displayName: \"Fly Machines\",\n apiVersion: 1,\n tlsTermination: \"edge\",\n configSchema: flyConfigSchema as unknown as z.ZodType<FlyConfig>,\n payloadSchema: flyPayloadSchema,\n\n async create(config, ctx) {\n const log = ctx.log;\n const client = new FlyClient({\n apiBaseUrl: config.apiBaseUrl,\n apiToken: config.apiToken,\n fetchImpl: opts.fetchImpl,\n });\n\n // Resolve the image: remote builds publish one via `fly deploy`; local /\n // managed strategies require the operator to supply `config.image`.\n let image = config.image ?? ctx.imageRef;\n if (config.buildStrategy === \"remote\") {\n const res = await spawnFly([\"deploy\", \"--app\", config.app, \"--build-only\", \"--push\"], {\n cwd: process.cwd(),\n signal: ctx.signal,\n });\n if (res.exitCode !== 0) {\n throw new Error(\n `fly deploy (remote build) failed: ${res.stderr.trim() || res.stdout.trim()}`,\n );\n }\n // `fly deploy --build-only --push` prints the pushed image ref; fall back\n // to a registry-derived default when the operator pins one explicitly.\n const pushed = res.stdout.match(/image:\\s*(\\S+)/i)?.[1];\n image = config.image ?? pushed ?? `registry.fly.io/${config.app}:latest`;\n }\n if (!image) {\n throw new Error(\"no image resolved for fly deploy\");\n }\n\n const machine = await client.createMachine(\n config.app,\n {\n region: config.region,\n config: {\n image,\n env: ctx.env as Record<string, string>,\n guest: guestForSize(config.size),\n services: [\n {\n ports: [{ port: config.port, handlers: [\"tls\", \"http\"] }],\n protocol: \"tcp\",\n internal_port: config.port,\n },\n ],\n },\n },\n ctx.signal,\n );\n log.info(\"fly machine created\", { app: config.app, machineId: machine.id });\n\n const payload: FlyPayload = { machineId: machine.id, app: config.app };\n const ready = async () => {\n const deadline = Date.now() + DEFAULT_READY_TIMEOUT_MS;\n let current: FlyMachine = machine;\n while (current.state !== \"started\") {\n if (Date.now() > deadline) {\n throw new Error(`timed out waiting for fly machine ${machine.id} to start`);\n }\n await sleep(pollIntervalMs, ctx.signal);\n current = await client.getMachine(config.app, machine.id, ctx.signal);\n }\n };\n return buildHandle(client, payload, config, ctx, ready, \"starting\");\n },\n\n async restore(payload, ctx) {\n // Restore re-reads the machine; a started machine yields a live handle.\n // Config is not persisted, so apiBaseUrl/token come from the environment.\n const apiBaseUrl = ctx.env.FLY_API_HOSTNAME ?? \"https://api.machines.dev\";\n const apiToken = ctx.env.FLY_API_TOKEN ?? \"\";\n const client = new FlyClient({ apiBaseUrl, apiToken, fetchImpl: opts.fetchImpl });\n\n const machine = await client.getMachine(payload.app, payload.machineId, ctx.signal);\n if (machine.state !== \"started\") {\n throw new Error(`fly machine ${payload.machineId} is not started (state=${machine.state})`);\n }\n ctx.log.info(\"re-attached to fly machine\", {\n app: payload.app,\n machineId: payload.machineId,\n });\n // Port is not persisted in the payload; default to the standard ws port.\n return buildHandle(client, payload, { port: 8080 }, ctx, async () => {}, \"ready\");\n },\n };\n}\n\n/** Default Fly deploy target registered by `register()`. */\nexport const flyDeployTarget = makeFlyDeployTarget();\n","/**\n * Thin Fly Machines REST client over `globalThis.fetch`, plus an injectable\n * `fly` CLI spawn used only for the remote build strategy.\n *\n * The client carries no SDK dependency — Fly's Machines API is plain HTTP, so a\n * handful of typed `fetch` calls cover create/get/delete. `fetchImpl` and\n * `spawnFly` are injectable so the target's tests stay fully hermetic (no real\n * network, no real CLI).\n */\n\n/** Subset of the Fly Machine resource this provider reads. */\nexport interface FlyMachine {\n id: string;\n name?: string;\n state: string;\n region?: string;\n private_ip?: string;\n}\n\nexport type FetchImpl = (\n input: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n text(): Promise<string>;\n json(): Promise<unknown>;\n}>;\n\n/** Result of a spawned `fly` CLI invocation. */\nexport interface SpawnResult {\n exitCode: number;\n stdout: string;\n stderr: string;\n}\n\nexport type SpawnFly = (\n args: string[],\n opts: { cwd: string; signal: AbortSignal },\n) => Promise<SpawnResult>;\n\nexport interface FlyClientOptions {\n apiBaseUrl: string;\n apiToken: string;\n fetchImpl?: FetchImpl;\n}\n\n/** Default CLI spawner: runs the user's `fly` binary via `node:child_process`. */\nexport const defaultSpawnFly: SpawnFly = async (args, opts) => {\n const { spawn } = await import(\"node:child_process\");\n return await new Promise<SpawnResult>((resolve, reject) => {\n const child = spawn(\"fly\", args, { cwd: opts.cwd, signal: opts.signal });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (d: Buffer) => {\n stdout += d.toString();\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n stderr += d.toString();\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => resolve({ exitCode: code ?? 0, stdout, stderr }));\n });\n};\n\nexport class FlyClient {\n private readonly baseUrl: string;\n private readonly token: string;\n private readonly fetchImpl: FetchImpl;\n\n constructor(opts: FlyClientOptions) {\n this.baseUrl = opts.apiBaseUrl.replace(/\\/$/, \"\");\n this.token = opts.apiToken;\n this.fetchImpl = opts.fetchImpl ?? (globalThis.fetch as unknown as FetchImpl);\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async request(\n method: string,\n path: string,\n signal: AbortSignal,\n body?: unknown,\n ): Promise<unknown> {\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: this.headers(),\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\");\n throw new Error(`fly ${method} ${path} failed: ${res.status} ${detail}`);\n }\n // DELETE may return an empty body.\n const text = await res.text();\n return text ? JSON.parse(text) : {};\n }\n\n /** `POST /v1/apps/{app}/machines` — create a machine, returns its resource. */\n async createMachine(\n app: string,\n spec: {\n region: string;\n config: {\n image: string;\n env?: Record<string, string>;\n guest?: { cpu_kind: string; cpus: number; memory_mb: number };\n services?: unknown[];\n };\n },\n signal: AbortSignal,\n ): Promise<FlyMachine> {\n return (await this.request(\"POST\", `/v1/apps/${app}/machines`, signal, spec)) as FlyMachine;\n }\n\n /** `GET /v1/apps/{app}/machines/{id}`. */\n async getMachine(app: string, machineId: string, signal: AbortSignal): Promise<FlyMachine> {\n return (await this.request(\n \"GET\",\n `/v1/apps/${app}/machines/${machineId}`,\n signal,\n )) as FlyMachine;\n }\n\n /** `DELETE /v1/apps/{app}/machines/{id}?force=true` — idempotent teardown. */\n async deleteMachine(app: string, machineId: string, signal: AbortSignal): Promise<void> {\n try {\n await this.request(\"DELETE\", `/v1/apps/${app}/machines/${machineId}?force=true`, signal);\n } catch {\n // A 404 (already gone) is a successful teardown for our purposes.\n }\n }\n}\n","/**\n * `@skaile/provider-fly` — Fly Machines deploy target for `@skaile/workspaces`.\n *\n * Install via `skaile plugin install @skaile/provider-fly`; the plugin loader\n * calls `register(pluginRegistry)` at startup so `fly` becomes resolvable as a\n * `deploy.target`.\n */\n\nimport type { PluginRegistry } from \"@skaile/workspaces/plugin-registry\";\nimport { flyDeployTarget } from \"./target.js\";\n\nexport { flyDeployTarget, makeFlyDeployTarget } from \"./target.js\";\nexport type { FlyTargetOptions } from \"./target.js\";\n\n/** Register this plugin's deploy target with a registry. */\nexport function register(registry: PluginRegistry): void {\n registry.register(\"deployTarget\", flyDeployTarget);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/target.ts","../src/fly-client.ts","../src/index.ts"],"sourcesContent":["/**\n * `flyDeployTarget` — deploy a workspace as a Fly Machine and reach it over the\n * app's public edge (`wss://<app>.fly.dev`). `tlsTermination: \"edge\"` because\n * Fly terminates TLS at its anycast edge before forwarding to the machine.\n *\n * The Fly Machines API is plain HTTP, so this carries no SDK dependency — see\n * `fly-client.ts`. The CLI spawner and `fetch` are injectable through the\n * `opts` bag so the tests run without a network or a real `fly` binary.\n */\n\nimport {\n buildStrategySchema,\n type DeployContext,\n type DeployHandle,\n type DeployTarget,\n makeDeployHandle,\n pollUntil,\n} from \"@skaile/workspaces/plugin-registry\";\nimport * as z from \"zod\";\nimport {\n defaultSpawnFly,\n type FetchImpl,\n FlyClient,\n type SpawnFly,\n} from \"./fly-client.js\";\n\nconst flyConfigSchema = z\n .object({\n app: z.string().min(1),\n region: z.string().default(\"ams\"),\n image: z.string().optional(),\n size: z.string().default(\"shared-cpu-1x\"),\n port: z.number().int().positive().default(8080),\n apiToken: z.string().min(1),\n apiBaseUrl: z.string().url().default(\"https://api.machines.dev\"),\n buildStrategy: buildStrategySchema.default(\"remote\"),\n })\n .refine((c) => c.buildStrategy === \"remote\" || c.image !== undefined, {\n message: \"image required when buildStrategy is local or managed\",\n path: [\"image\"],\n });\n\ntype FlyConfig = z.infer<typeof flyConfigSchema>;\n\nconst flyPayloadSchema = z.object({\n machineId: z.string(),\n app: z.string(),\n});\n\ntype FlyPayload = z.infer<typeof flyPayloadSchema>;\n\nconst DEFAULT_POLL_INTERVAL_MS = 1_000;\nconst DEFAULT_READY_TIMEOUT_MS = 120_000;\n\n/** Injectable seams so the target's tests never touch a real network/CLI. */\nexport interface FlyTargetOptions {\n fetchImpl?: FetchImpl;\n spawnFly?: SpawnFly;\n pollIntervalMs?: number;\n}\n\n/** Map a Fly size string to the guest shape the Machines API expects. */\nfunction guestForSize(size: string): { cpu_kind: string; cpus: number; memory_mb: number } {\n // \"shared-cpu-1x\" → shared / 1 cpu / 256mb. We only special-case the cpu kind;\n // memory follows Fly's default for the smallest shared preset.\n const cpuKind = size.startsWith(\"performance\") ? \"performance\" : \"shared\";\n const match = size.match(/-(\\d+)x$/);\n const cpus = match ? Number(match[1]) : 1;\n return { cpu_kind: cpuKind, cpus, memory_mb: 256 * cpus };\n}\n\n/**\n * Build a `DeployHandle` for a Fly machine. The factory drives state\n * transitions; the seams here only do Fly-specific work (poll, probe, delete).\n */\nfunction buildHandle(\n client: FlyClient,\n payload: FlyPayload,\n config: Pick<FlyConfig, \"port\">,\n ctx: DeployContext,\n ready: (timeoutMs: number, signal: AbortSignal) => Promise<void>,\n initialState: DeployHandle[\"state\"],\n): DeployHandle<FlyPayload> {\n return makeDeployHandle<FlyPayload>({\n // Fly terminates TLS at the edge; the public form is the app's fly.dev host.\n wsUrl: `wss://${payload.app}.fly.dev:${config.port}`,\n ctx,\n payload,\n initialState,\n defaultReadyTimeoutMs: DEFAULT_READY_TIMEOUT_MS,\n ready,\n async health() {\n try {\n const machine = await client.getMachine(payload.app, payload.machineId, ctx.signal);\n const healthy = machine.state === \"started\";\n return healthy\n ? { healthy }\n : { healthy, detail: `machine ${payload.machineId} state=${machine.state}` };\n } catch (err) {\n return { healthy: false, detail: (err as Error).message };\n }\n },\n async stop() {\n await client.deleteMachine(payload.app, payload.machineId, ctx.signal);\n },\n });\n}\n\n/** Build the Fly deploy target with optional injected seams. */\nexport function makeFlyDeployTarget(\n opts: FlyTargetOptions = {},\n): DeployTarget<FlyConfig, FlyPayload> {\n const spawnFly = opts.spawnFly ?? defaultSpawnFly;\n const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n\n return {\n id: \"fly\",\n displayName: \"Fly Machines\",\n apiVersion: 1,\n tlsTermination: \"edge\",\n configSchema: flyConfigSchema as unknown as z.ZodType<FlyConfig>,\n payloadSchema: flyPayloadSchema,\n\n async create(config, ctx) {\n const log = ctx.log;\n const client = new FlyClient({\n apiBaseUrl: config.apiBaseUrl,\n apiToken: config.apiToken,\n fetchImpl: opts.fetchImpl,\n });\n\n // Resolve the image: remote builds publish one via `fly deploy`; local /\n // managed strategies require the operator to supply `config.image`.\n let image = config.image ?? ctx.imageRef;\n if (config.buildStrategy === \"remote\") {\n const res = await spawnFly([\"deploy\", \"--app\", config.app, \"--build-only\", \"--push\"], {\n cwd: process.cwd(),\n signal: ctx.signal,\n });\n if (res.exitCode !== 0) {\n throw new Error(\n `fly deploy (remote build) failed: ${res.stderr.trim() || res.stdout.trim()}`,\n );\n }\n // `fly deploy --build-only --push` prints the pushed image ref; fall back\n // to a registry-derived default when the operator pins one explicitly.\n const pushed = res.stdout.match(/image:\\s*(\\S+)/i)?.[1];\n image = config.image ?? pushed ?? `registry.fly.io/${config.app}:latest`;\n }\n if (!image) {\n throw new Error(\"no image resolved for fly deploy\");\n }\n\n const machine = await client.createMachine(\n config.app,\n {\n region: config.region,\n config: {\n image,\n env: ctx.env as Record<string, string>,\n guest: guestForSize(config.size),\n services: [\n {\n ports: [{ port: config.port, handlers: [\"tls\", \"http\"] }],\n protocol: \"tcp\",\n internal_port: config.port,\n },\n ],\n },\n },\n ctx.signal,\n );\n log.info(\"fly machine created\", { app: config.app, machineId: machine.id });\n\n const payload: FlyPayload = { machineId: machine.id, app: config.app };\n const ready = (timeoutMs: number, signal: AbortSignal) =>\n pollUntil({\n check: async () =>\n (await client.getMachine(config.app, machine.id, signal)).state === \"started\",\n timeoutMs,\n intervalMs: pollIntervalMs,\n signal,\n timeoutMessage: `timed out waiting for fly machine ${machine.id} to start`,\n });\n return buildHandle(client, payload, config, ctx, ready, \"starting\");\n },\n\n async restore(payload, ctx) {\n // Restore re-reads the machine; a started machine yields a live handle.\n // Config is not persisted, so apiBaseUrl/token come from the environment.\n const apiBaseUrl = ctx.env.FLY_API_HOSTNAME ?? \"https://api.machines.dev\";\n const apiToken = ctx.env.FLY_API_TOKEN ?? \"\";\n const client = new FlyClient({ apiBaseUrl, apiToken, fetchImpl: opts.fetchImpl });\n\n const machine = await client.getMachine(payload.app, payload.machineId, ctx.signal);\n if (machine.state !== \"started\") {\n throw new Error(`fly machine ${payload.machineId} is not started (state=${machine.state})`);\n }\n ctx.log.info(\"re-attached to fly machine\", {\n app: payload.app,\n machineId: payload.machineId,\n });\n // Port is not persisted in the payload; default to the standard ws port.\n return buildHandle(client, payload, { port: 8080 }, ctx, async () => {}, \"ready\");\n },\n };\n}\n\n/** Default Fly deploy target registered by `register()`. */\nexport const flyDeployTarget = makeFlyDeployTarget();\n","/**\n * Thin Fly Machines REST client over `globalThis.fetch`, plus an injectable\n * `fly` CLI spawn used only for the remote build strategy.\n *\n * The client carries no SDK dependency — Fly's Machines API is plain HTTP, so a\n * handful of typed `fetch` calls cover create/get/delete. `fetchImpl` and\n * `spawnFly` are injectable so the target's tests stay fully hermetic (no real\n * network, no real CLI).\n */\n\n/** Subset of the Fly Machine resource this provider reads. */\nexport interface FlyMachine {\n id: string;\n name?: string;\n state: string;\n region?: string;\n private_ip?: string;\n}\n\nexport type FetchImpl = (\n input: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n text(): Promise<string>;\n json(): Promise<unknown>;\n}>;\n\n/** Result of a spawned `fly` CLI invocation. */\nexport interface SpawnResult {\n exitCode: number;\n stdout: string;\n stderr: string;\n}\n\nexport type SpawnFly = (\n args: string[],\n opts: { cwd: string; signal: AbortSignal },\n) => Promise<SpawnResult>;\n\nexport interface FlyClientOptions {\n apiBaseUrl: string;\n apiToken: string;\n fetchImpl?: FetchImpl;\n}\n\n/** Default CLI spawner: runs the user's `fly` binary via `node:child_process`. */\nexport const defaultSpawnFly: SpawnFly = async (args, opts) => {\n const { spawn } = await import(\"node:child_process\");\n return await new Promise<SpawnResult>((resolve, reject) => {\n const child = spawn(\"fly\", args, { cwd: opts.cwd, signal: opts.signal });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (d: Buffer) => {\n stdout += d.toString();\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n stderr += d.toString();\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => resolve({ exitCode: code ?? 0, stdout, stderr }));\n });\n};\n\nexport class FlyClient {\n private readonly baseUrl: string;\n private readonly token: string;\n private readonly fetchImpl: FetchImpl;\n\n constructor(opts: FlyClientOptions) {\n this.baseUrl = opts.apiBaseUrl.replace(/\\/$/, \"\");\n this.token = opts.apiToken;\n this.fetchImpl = opts.fetchImpl ?? (globalThis.fetch as unknown as FetchImpl);\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async request(\n method: string,\n path: string,\n signal: AbortSignal,\n body?: unknown,\n ): Promise<unknown> {\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: this.headers(),\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\");\n throw new Error(`fly ${method} ${path} failed: ${res.status} ${detail}`);\n }\n // DELETE may return an empty body.\n const text = await res.text();\n return text ? JSON.parse(text) : {};\n }\n\n /** `POST /v1/apps/{app}/machines` — create a machine, returns its resource. */\n async createMachine(\n app: string,\n spec: {\n region: string;\n config: {\n image: string;\n env?: Record<string, string>;\n guest?: { cpu_kind: string; cpus: number; memory_mb: number };\n services?: unknown[];\n };\n },\n signal: AbortSignal,\n ): Promise<FlyMachine> {\n return (await this.request(\"POST\", `/v1/apps/${app}/machines`, signal, spec)) as FlyMachine;\n }\n\n /** `GET /v1/apps/{app}/machines/{id}`. */\n async getMachine(app: string, machineId: string, signal: AbortSignal): Promise<FlyMachine> {\n return (await this.request(\n \"GET\",\n `/v1/apps/${app}/machines/${machineId}`,\n signal,\n )) as FlyMachine;\n }\n\n /** `DELETE /v1/apps/{app}/machines/{id}?force=true` — idempotent teardown. */\n async deleteMachine(app: string, machineId: string, signal: AbortSignal): Promise<void> {\n try {\n await this.request(\"DELETE\", `/v1/apps/${app}/machines/${machineId}?force=true`, signal);\n } catch (err) {\n // Only a 404 (already gone) counts as success; re-throw anything else so a\n // network/auth/5xx failure can't masquerade as a completed teardown.\n if (!/failed: 404\\b/.test((err as Error).message)) throw err;\n }\n }\n}\n","/**\n * `@skaile/provider-fly` — Fly Machines deploy target for `@skaile/workspaces`.\n *\n * Install via `skaile plugin install @skaile/provider-fly`; the plugin loader\n * calls `register(pluginRegistry)` at startup so `fly` becomes resolvable as a\n * `deploy.target`.\n */\n\nimport type { PluginRegistry } from \"@skaile/workspaces/plugin-registry\";\nimport { flyDeployTarget } from \"./target.js\";\n\nexport { flyDeployTarget, makeFlyDeployTarget } from \"./target.js\";\nexport type { FlyTargetOptions } from \"./target.js\";\n\n/** Register this plugin's deploy target with a registry. */\nexport function register(registry: PluginRegistry): void {\n registry.register(\"deployTarget\", flyDeployTarget);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skaile/provider-fly",
3
- "version": "0.0.0",
3
+ "version": "0.1.0-beta.1",
4
4
  "description": "Fly Machines deploy target for @skaile/workspaces",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "@skaile/workspaces": ">=0.22.0-0"
26
26
  },
27
27
  "devDependencies": {
28
- "@skaile/workspaces": "0.21.0"
28
+ "@skaile/workspaces": "^0.22.0-beta.1"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "tsup",