import {
clean,
machinesGet,
machinesListForApp,
machinesListForOrg,
machinesRequest,
mutationPreview,
resolveOrganizationSlug,
summarizeMachine,
} from './fly-core.ts'
import type { FlyMachineMutationInput, FlyMachinesInput } from './types.ts'
export async function listMachines(params: FlyMachinesInput = {}) {
const appName = String(params.appName || params.app || '').trim()
const appId = String(params.appId || '').trim()
const state = String(params.state || '').trim() || undefined
if (appName) {
const machines = await machinesListForApp(appName, { ...params, state })
const summarized = machines.map((machine: any) => summarizeMachine(machine, { name: appName }))
return { count: summarized.length, appName, machines: summarized }
}
if (appId && !appName) {
const orgResult = await machinesListForOrg({ ...params, state })
const machines = orgResult.machines
.filter((machine: any) => machine?.app?.id === appId || machine?.app_id === appId)
.map((machine: any) => summarizeMachine(machine))
return {
count: machines.length,
organization: orgResult.organization,
machines,
}
}
const orgResult = await machinesListForOrg({ ...params, state })
const machines = orgResult.machines.map((machine: any) => summarizeMachine(machine))
return {
count: machines.length,
organization: orgResult.organization,
orgSlug: orgResult.organization.slug,
machines,
}
}
export async function getMachine(params: FlyMachinesInput = {}) {
const appName = String(params.appName || params.app || '').trim()
const machineId = String(params.machineId || params.id || '').trim()
if (!appName) throw new Error('appName is required.')
if (!machineId) throw new Error('machineId is required.')
const machine = await machinesGet(appName, machineId, params)
return {
machine: summarizeMachine(machine, { name: appName }),
raw: machine,
}
}
function mutationTarget(params: FlyMachineMutationInput) {
const appName = clean(params.appName || params.app)
const machineId = clean(params.machineId || params.id)
if (!appName) throw new Error('appName is required.')
if (!machineId) throw new Error('machineId is required.')
return { appName, machineId }
}
function machinePath(appName: string, machineId: string, suffix = '') {
return (
'/apps/' +
encodeURIComponent(appName) +
'/machines/' +
encodeURIComponent(machineId) +
suffix
)
}
/**
* Start a Fly Machine. Pass `dryRun: true` to preview without contacting Fly.
*/
export async function startMachine(params: FlyMachineMutationInput = {}) {
const { appName, machineId } = mutationTarget(params)
const path = machinePath(appName, machineId, '/start')
const preview = mutationPreview(params, {
action: 'start machine ' + machineId + ' on ' + appName,
method: 'POST',
path,
})
if (preview) return preview
const result = await machinesRequest(path, { ...params, method: 'POST' })
return { ok: true, action: 'start', appName, machineId, result }
}
/**
* Stop a Fly Machine. Pass `dryRun: true` to preview without contacting Fly.
*/
export async function stopMachine(params: FlyMachineMutationInput = {}) {
const { appName, machineId } = mutationTarget(params)
const path = machinePath(appName, machineId, '/stop')
const body: Record<string, unknown> = {}
if (params.timeout != null) body.timeout = params.timeout
if (clean(params.signal)) body.signal = clean(params.signal)
const hasBody = Object.keys(body).length > 0
const preview = mutationPreview(params, {
action: 'stop machine ' + machineId + ' on ' + appName,
method: 'POST',
path,
body: hasBody ? body : undefined,
})
if (preview) return preview
const result = await machinesRequest(path, {
...params,
method: 'POST',
body: hasBody ? body : undefined,
})
return { ok: true, action: 'stop', appName, machineId, result }
}
/**
* Destroy a Fly Machine. Requires `confirm: true`. Pass `dryRun: true` to preview.
* Never call this from a smoke test or scheduled job.
*/
export async function destroyMachine(params: FlyMachineMutationInput = {}) {
const { appName, machineId } = mutationTarget(params)
const path = machinePath(appName, machineId)
const query = params.force === true ? { force: true } : undefined
const preview = mutationPreview(params, {
action: 'destroy machine ' + machineId + ' on ' + appName,
method: 'DELETE',
path,
query,
requireConfirm: true,
})
if (preview) return preview
const result = await machinesRequest(path, { ...params, method: 'DELETE', query })
return { ok: true, action: 'destroy', appName, machineId, result }
}
/**
* List Fly machines via Machines REST (org-wide or by `appName`), optionally filtered by `state`.
* @returns Machine count and summarized machine records.
* @example
* import machines from 'kody:@kody/fly/machines'
* const result = await machines({ organizationSlug: 'my-org', state: 'started' })
* // => { count: 5, machines: [...] }
*/
export default async function machines(params: FlyMachinesInput & FlyMachineMutationInput = {}) {
if (params.machineId || params.id) {
return await getMachine(params)
}
return await listMachines(params)
}
export { resolveOrganizationSlug }