unfazed 0.0.1 → 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Every GraphQL document the workflow layer sends.\n *\n * These are wrapped in `graphql(...)` so graphql-codegen validates them\n * against the vendored schema at codegen time and emits exact result/variable\n * types. A field that stops existing server-side turns into a compile error\n * here rather than a runtime surprise in an agent's script.\n *\n * The one document NOT defined here is the batched file write: it has a\n * variable number of aliased root fields, so it is assembled at runtime in\n * `unfazed.ts`. See the comment there.\n */\nimport { graphql } from \"./generated/gql.js\";\n\n/**\n * Deployment fields the SDK returns everywhere.\n *\n * `id` + `aliasRevision` + `aliasHeld` are the rollback compare-and-swap\n * triple; `previewUrl` is the immutable by-id URL you can verify BEFORE the\n * alias moves; `error` carries the build failure text when status is FAILED.\n */\nexport const DeploymentFieldsFragment = graphql(`\n fragment DeploymentFields on Deployment {\n id\n workspaceID\n kind\n runtime\n alias\n serviceMode\n healthPath\n cronSchedule\n cronPath\n url\n previewUrl\n sshCommand\n aliasHeld\n aliasRevision\n status\n error\n filesCount\n sizeBytes\n size\n spaMode\n gitRepo\n gitCommit\n createdAt\n }\n`);\n\nexport const WhoamiDocument = graphql(`\n query SdkWhoami {\n whoami {\n kind\n userID\n name\n agentID\n agentName\n claimed\n claimedBy\n scopes\n expiresAt\n usage {\n month\n requests\n limit\n }\n resources {\n workspaces\n workspaceLimit\n services\n serviceLimit\n }\n }\n }\n`);\n\nexport const CreateWorkspaceDocument = graphql(`\n mutation SdkCreateWorkspace($name: String!) {\n createWorkspace(input: { name: $name }) {\n workspace {\n id\n name\n ownerId\n projectId\n createdAt\n updatedAt\n }\n }\n }\n`);\n\nexport const ListWorkspacesDocument = graphql(`\n query SdkListWorkspaces {\n workspaces {\n id\n name\n ownerId\n projectId\n createdAt\n updatedAt\n }\n }\n`);\n\nexport const DeleteWorkspaceDocument = graphql(`\n mutation SdkDeleteWorkspace($workspaceID: ID!) {\n deleteWorkspace(input: { workspaceID: $workspaceID }) {\n ok\n }\n }\n`);\n\nexport const WriteTextFileDocument = graphql(`\n mutation SdkWriteTextFile($workspaceID: ID!, $path: String!, $text: String!) {\n writeWorkspaceTextFile(input: { workspaceID: $workspaceID, path: $path, text: $text }) {\n file {\n path\n sizeBytes\n size\n sha256\n updatedAt\n }\n }\n }\n`);\n\nexport const WriteBase64FileDocument = graphql(`\n mutation SdkWriteBase64File($workspaceID: ID!, $path: String!, $contentBase64: String!) {\n writeWorkspaceBase64File(\n input: { workspaceID: $workspaceID, path: $path, contentBase64: $contentBase64 }\n ) {\n file {\n path\n sizeBytes\n size\n sha256\n updatedAt\n }\n }\n }\n`);\n\nexport const ListFilesDocument = graphql(`\n query SdkListFiles($input: ListWorkspaceFilesInput!) {\n workspaceFilesPage(input: $input) {\n items {\n path\n sizeBytes\n size\n sha256\n updatedAt\n }\n total\n limit\n offset\n hasMore\n nextOffset\n }\n }\n`);\n\nexport const ReadFileDocument = graphql(`\n query SdkReadFile($input: ReadWorkspaceFileInput!) {\n readWorkspaceFile(input: $input) {\n text\n offset\n limit\n totalBytes\n truncated\n nextOffset\n file {\n path\n sizeBytes\n size\n sha256\n updatedAt\n }\n }\n }\n`);\n\nexport const DeleteFileDocument = graphql(`\n mutation SdkDeleteFile($workspaceID: ID!, $path: String!) {\n deleteWorkspaceFile(input: { workspaceID: $workspaceID, path: $path }) {\n ok\n }\n }\n`);\n\nexport const CreateDeploymentDocument = graphql(`\n mutation SdkCreateDeployment($input: CreateDeploymentInput!) {\n createDeployment(input: $input) {\n deployment {\n ...DeploymentFields\n }\n }\n }\n`);\n\nexport const DeployWorkspaceDocument = graphql(`\n mutation SdkDeployWorkspace($input: DeployWorkspaceInput!) {\n deployWorkspace(input: $input) {\n deployment {\n ...DeploymentFields\n }\n }\n }\n`);\n\nexport const GetDeploymentDocument = graphql(`\n query SdkGetDeployment($id: ID!) {\n deployment(id: $id) {\n ...DeploymentFields\n }\n }\n`);\n\n/**\n * The poll document, deliberately narrow.\n *\n * Waiting can run for minutes at a few seconds per tick; selecting the full\n * fragment every time would cost the server (and an agent's rate limit) far\n * more than the four fields the state machine actually reads.\n */\nexport const PollDeploymentDocument = graphql(`\n query SdkPollDeployment($id: ID!) {\n deployment(id: $id) {\n id\n status\n error\n aliasHeld\n url\n previewUrl\n }\n }\n`);\n\nexport const GetDeploymentByAliasDocument = graphql(`\n query SdkGetDeploymentByAlias($alias: String!) {\n deploymentByAlias(alias: $alias) {\n ...DeploymentFields\n }\n }\n`);\n\nexport const ListDeploymentsDocument = graphql(`\n query SdkListDeployments($input: ListDeploymentsInput) {\n deploymentsPage(input: $input) {\n items {\n ...DeploymentFields\n }\n total\n limit\n offset\n hasMore\n nextOffset\n }\n }\n`);\n\nexport const ListReleasesDocument = graphql(`\n query SdkListReleases($alias: String!) {\n deploymentReleases(alias: $alias) {\n ...DeploymentFields\n }\n }\n`);\n\nexport const RollbackDocument = graphql(`\n mutation SdkRollback(\n $deploymentID: ID!\n $expectedCurrentDeploymentID: ID!\n $expectedCurrentAliasRevision: String!\n ) {\n rollbackDeployment(\n input: {\n deploymentID: $deploymentID\n expectedCurrentDeploymentID: $expectedCurrentDeploymentID\n expectedCurrentAliasRevision: $expectedCurrentAliasRevision\n }\n ) {\n ...DeploymentFields\n }\n }\n`);\n\nexport const ServiceStatusDocument = graphql(`\n query SdkServiceStatus($deploymentID: ID!) {\n serviceStatus(deploymentID: $deploymentID) {\n deploymentID\n alias\n serviceMode\n running\n health\n vmID\n inFlight\n alwaysOn\n memSizeMiB\n startedAt\n lastUsedAt\n lastStoppedAt\n restartCount\n lastError\n }\n }\n`);\n\nexport const ServiceLogsDocument = graphql(`\n query SdkServiceLogs($deploymentID: ID!, $tailBytes: Int) {\n serviceLogs(deploymentID: $deploymentID, tailBytes: $tailBytes) {\n deploymentID\n stdout\n stderr\n truncated\n stdoutBytes\n stderrBytes\n tailBytes\n }\n }\n`);\n\nexport const DeploymentEventsDocument = graphql(`\n query SdkDeploymentEvents($deploymentID: ID!, $limit: Int) {\n deploymentEvents(deploymentID: $deploymentID, limit: $limit) {\n id\n deploymentID\n type\n rawType\n severity\n message\n vmID\n createdAt\n }\n }\n`);\n\nexport const RestartServiceDocument = graphql(`\n mutation SdkRestartService($deploymentID: ID!) {\n restartService(input: { deploymentID: $deploymentID }) {\n deploymentID\n running\n health\n vmID\n restartCount\n lastError\n }\n }\n`);\n\nexport const StopServiceDocument = graphql(`\n mutation SdkStopService($deploymentID: ID!) {\n stopService(input: { deploymentID: $deploymentID }) {\n deploymentID\n running\n health\n vmID\n restartCount\n lastError\n }\n }\n`);\n\nexport const DeleteDeploymentDocument = graphql(`\n mutation SdkDeleteDeployment($deploymentID: ID!) {\n deleteDeployment(input: { deploymentID: $deploymentID }) {\n ok\n }\n }\n`);\n\n/* -------------------------------------------------------------------------\n * Env & secrets\n *\n * Workspace-scoped values are SERVICE defaults for every app in the source;\n * app-scoped values override them for one alias. Both are snapshotted into a\n * release at deploy time, so a change only reaches a running app after a\n * redeploy — the workflow layer's doc comments repeat this where it matters.\n * ---------------------------------------------------------------------- */\n\nexport const SetWorkspaceEnvDocument = graphql(`\n mutation SdkSetWorkspaceEnv($workspaceID: ID!, $name: String!, $value: String!) {\n setWorkspaceEnv(input: { workspaceID: $workspaceID, name: $name, value: $value }) {\n env {\n name\n value\n secret\n alias\n inherited\n masked\n updatedAt\n }\n }\n }\n`);\n\nexport const SetWorkspaceSecretDocument = graphql(`\n mutation SdkSetWorkspaceSecret($workspaceID: ID!, $name: String!, $value: String!) {\n setWorkspaceSecret(input: { workspaceID: $workspaceID, name: $name, value: $value }) {\n env {\n name\n value\n secret\n alias\n inherited\n masked\n updatedAt\n }\n }\n }\n`);\n\nexport const DeleteWorkspaceEnvDocument = graphql(`\n mutation SdkDeleteWorkspaceEnv($workspaceID: ID!, $name: String!) {\n deleteWorkspaceEnv(input: { workspaceID: $workspaceID, name: $name }) {\n ok\n }\n }\n`);\n\nexport const ListWorkspaceEnvDocument = graphql(`\n query SdkListWorkspaceEnv($workspaceID: ID!) {\n workspaceEnv(workspaceID: $workspaceID) {\n name\n value\n secret\n alias\n inherited\n masked\n updatedAt\n }\n }\n`);\n\nexport const SetAppEnvDocument = graphql(`\n mutation SdkSetAppEnv($workspaceID: ID!, $alias: String!, $name: String!, $value: String!) {\n setAppEnv(input: { workspaceID: $workspaceID, alias: $alias, name: $name, value: $value }) {\n env {\n name\n value\n secret\n alias\n inherited\n masked\n updatedAt\n }\n }\n }\n`);\n\nexport const SetAppSecretDocument = graphql(`\n mutation SdkSetAppSecret($workspaceID: ID!, $alias: String!, $name: String!, $value: String!) {\n setAppSecret(input: { workspaceID: $workspaceID, alias: $alias, name: $name, value: $value }) {\n env {\n name\n value\n secret\n alias\n inherited\n masked\n updatedAt\n }\n }\n }\n`);\n\nexport const RemoveAppEnvDocument = graphql(`\n mutation SdkRemoveAppEnv($workspaceID: ID!, $alias: String!, $name: String!) {\n removeAppEnv(input: { workspaceID: $workspaceID, alias: $alias, name: $name }) {\n ok\n }\n }\n`);\n\nexport const InheritAppEnvDocument = graphql(`\n mutation SdkInheritAppEnv($workspaceID: ID!, $alias: String!, $name: String!) {\n inheritAppEnv(input: { workspaceID: $workspaceID, alias: $alias, name: $name }) {\n ok\n }\n }\n`);\n\nexport const ListAppEnvDocument = graphql(`\n query SdkListAppEnv($workspaceID: ID!, $alias: String!) {\n appEnv(workspaceID: $workspaceID, alias: $alias) {\n name\n value\n secret\n alias\n inherited\n masked\n updatedAt\n }\n }\n`);\n"]}
@@ -0,0 +1,445 @@
1
+ import { UnfazedClient, type ClientOptions, type RequestOptions } from "./client.js";
2
+ import type { CreateDeploymentInput, DeploymentFieldsFragment, ListDeploymentsInput, ServiceMode, ServiceRuntime } from "./generated/graphql.js";
3
+ import { type WaitOptions } from "./wait.js";
4
+ /** A deployment as the workflow layer returns it. */
5
+ export type Deployment = DeploymentFieldsFragment;
6
+ /** A file record returned by any write/list operation. */
7
+ export interface WorkspaceFile {
8
+ path: string;
9
+ sizeBytes: number;
10
+ size: string;
11
+ sha256: string;
12
+ updatedAt: string;
13
+ }
14
+ /** One env var in a workspace- or app-scoped view. Secret values are never returned. */
15
+ export interface EnvVar {
16
+ name: string;
17
+ value: string | null;
18
+ secret: boolean;
19
+ alias: string | null;
20
+ inherited: boolean;
21
+ masked: boolean;
22
+ updatedAt: string;
23
+ }
24
+ /**
25
+ * A file's contents for {@link Unfazed.writeFiles}.
26
+ *
27
+ * A bare string is text — the overwhelmingly common case, and what makes
28
+ * `{ "server.ts": code }` read well. Binary goes through `bytes` or `base64`,
29
+ * which route to `writeWorkspaceBase64File` instead.
30
+ */
31
+ export type FileContent = string | {
32
+ text: string;
33
+ } | {
34
+ base64: string;
35
+ } | {
36
+ bytes: Uint8Array | ArrayBuffer;
37
+ };
38
+ /** Files keyed by workspace-relative path. */
39
+ export type FilesMap = Record<string, FileContent>;
40
+ export interface WriteFilesOptions {
41
+ /**
42
+ * Max files per GraphQL request. Default 20.
43
+ *
44
+ * The batch is one document with N aliased root fields; too many at once
45
+ * makes a single oversized request whose failure is all-or-nothing to
46
+ * diagnose. Twenty keeps a typical app to one or two round trips.
47
+ */
48
+ batchSize?: number;
49
+ /**
50
+ * Max total payload bytes per request. Default 4 MiB. Whichever limit trips
51
+ * first closes the batch, so one large file cannot blow the request up.
52
+ */
53
+ maxBatchBytes?: number;
54
+ signal?: AbortSignal;
55
+ }
56
+ /** Common options for the deploy helpers. */
57
+ export interface DeployOptions {
58
+ workspaceId: string;
59
+ alias?: string;
60
+ /**
61
+ * Block until the deployment reaches a terminal status.
62
+ *
63
+ * `createDeployment` returns BUILDING immediately; the build, VM boot and
64
+ * health probe run in the background. With `wait: true` the call resolves
65
+ * only on READY and throws {@link UnfazedDeployError} on FAILED — which is
66
+ * what makes a one-script deploy possible.
67
+ */
68
+ wait?: boolean;
69
+ /** Backoff/timeout knobs used when `wait` is true. */
70
+ waitOptions?: WaitOptions;
71
+ signal?: AbortSignal;
72
+ }
73
+ export interface DeployServiceOptions extends DeployOptions {
74
+ /** Omit to auto-detect: a root go.mod+main.go is GO, otherwise BUN. */
75
+ runtime?: ServiceRuntime;
76
+ /** Omit for ON_DEMAND. Only set ALWAYS_ON to keep the VM warm. */
77
+ serviceMode?: ServiceMode;
78
+ healthPath?: string;
79
+ /** Standard 5-field cron spec. Prefer ON_DEMAND + cron over ALWAYS_ON. */
80
+ cronSchedule?: string;
81
+ /** Path the cron tick POSTs to. Defaults to /cron when cronSchedule is set. */
82
+ cronPath?: string;
83
+ }
84
+ export interface DeployStaticOptions extends DeployOptions {
85
+ /** Serve index.html for missing paths (single-page apps). */
86
+ spaMode?: boolean;
87
+ }
88
+ export interface DeployBoxOptions extends DeployOptions {
89
+ /** VM size. Omit for platform defaults (2048 MiB / 2 vCPU / 8 GiB disk). */
90
+ resources?: {
91
+ memMiB?: number;
92
+ vcpus?: number;
93
+ diskGiB?: number;
94
+ };
95
+ }
96
+ export interface RollbackOptions {
97
+ /** The alias to roll back. */
98
+ alias: string;
99
+ /**
100
+ * Release to restore. Omit to pick the most recent READY release that is not
101
+ * the current holder — i.e. "undo the last deploy".
102
+ */
103
+ to?: string;
104
+ /**
105
+ * Re-list-and-retry attempts when the compare-and-swap guard is rejected.
106
+ * Default 1. Each retry refreshes the guard from a fresh read; the TARGET
107
+ * release stays pinned, so a retry can never silently roll back to something
108
+ * other than what was chosen.
109
+ */
110
+ retries?: number;
111
+ signal?: AbortSignal;
112
+ }
113
+ export interface UnfazedOptions extends ClientOptions {
114
+ /** Use an existing core client instead of constructing one. */
115
+ client?: UnfazedClient;
116
+ }
117
+ /**
118
+ * Layer 2 — the agent workflow layer.
119
+ *
120
+ * Wraps the chains an agent actually performs (create → write → deploy → wait
121
+ * → read logs → roll back) so a code-mode script does in one call what would
122
+ * otherwise be many round trips through a model's context. Everything here is
123
+ * built on {@link UnfazedClient}, exposed as `.client` for anything not
124
+ * wrapped.
125
+ *
126
+ * ```ts
127
+ * const unfazed = new Unfazed(); // reads UNFZ_API_TOKEN
128
+ * const ws = await unfazed.createWorkspace("hello");
129
+ * await unfazed.writeFiles(ws.id, { "server.ts": code });
130
+ * const app = await unfazed.deployService({ workspaceId: ws.id, alias: "hello", wait: true });
131
+ * console.log(app.url);
132
+ * ```
133
+ */
134
+ export declare class Unfazed {
135
+ #private;
136
+ /** The underlying typed GraphQL client. Use it for un-wrapped operations. */
137
+ readonly client: UnfazedClient;
138
+ constructor(options?: UnfazedOptions);
139
+ /** Who this token authenticates as, plus usage and resource limits. */
140
+ whoami(options?: RequestOptions): Promise<{
141
+ kind: import("./index.js").CurrentActorKind;
142
+ userID: string;
143
+ name: string;
144
+ agentID: string | null;
145
+ agentName: string | null;
146
+ claimed: boolean;
147
+ claimedBy: string | null;
148
+ scopes: Array<string>;
149
+ expiresAt: string | null;
150
+ usage: {
151
+ month: string;
152
+ requests: number;
153
+ limit: number;
154
+ } | null;
155
+ resources: {
156
+ workspaces: number;
157
+ workspaceLimit: number;
158
+ services: number;
159
+ serviceLimit: number;
160
+ } | null;
161
+ }>;
162
+ /** Creates a source workspace — the container for files, env and deployments. */
163
+ createWorkspace(name: string, options?: RequestOptions): Promise<{
164
+ id: string;
165
+ name: string;
166
+ ownerId: string;
167
+ projectId: string | null;
168
+ createdAt: string;
169
+ updatedAt: string;
170
+ }>;
171
+ /** Workspaces visible to this token. */
172
+ listWorkspaces(options?: RequestOptions): Promise<{
173
+ id: string;
174
+ name: string;
175
+ ownerId: string;
176
+ projectId: string | null;
177
+ createdAt: string;
178
+ updatedAt: string;
179
+ }[]>;
180
+ /**
181
+ * Deletes a workspace and everything anchored to it — deployments, env, and
182
+ * a BOX's persistent data disk. Not recoverable.
183
+ */
184
+ deleteWorkspace(workspaceId: string, options?: RequestOptions): Promise<boolean>;
185
+ /**
186
+ * Writes many files in as few requests as possible.
187
+ *
188
+ * The API is one-file-per-mutation, so writing a 12-file app naively costs 12
189
+ * round trips. This batches them into a single GraphQL document with one
190
+ * ALIASED root field per file (`f0: writeWorkspaceTextFile(...)`,
191
+ * `f1: ...`), which the server resolves in one request.
192
+ *
193
+ * ```ts
194
+ * await unfazed.writeFiles(ws.id, {
195
+ * "server.ts": serverCode,
196
+ * "unfazed.json": JSON.stringify({ entry: "server.ts" }),
197
+ * "public/logo.png": { bytes: pngBytes },
198
+ * });
199
+ * ```
200
+ *
201
+ * Batches are NOT atomic: GraphQL resolves each aliased field independently,
202
+ * so a partial failure leaves earlier writes committed. The thrown
203
+ * {@link UnfazedGraphQLError} carries `data` with whichever aliases
204
+ * succeeded, and `error.path` identifies the ones that did not.
205
+ */
206
+ writeFiles(workspaceId: string, files: FilesMap, options?: WriteFilesOptions): Promise<WorkspaceFile[]>;
207
+ /** Writes one text file. Prefer {@link writeFiles} for more than one. */
208
+ writeFile(workspaceId: string, path: string, text: string, options?: RequestOptions): Promise<WorkspaceFile>;
209
+ /** Writes one binary file. Accepts raw bytes or a base64 string. */
210
+ writeBinaryFile(workspaceId: string, path: string, content: Uint8Array | ArrayBuffer | {
211
+ base64: string;
212
+ }, options?: RequestOptions): Promise<WorkspaceFile>;
213
+ /** Lists workspace files (paginated). */
214
+ listFiles(workspaceId: string, input?: {
215
+ limit?: number;
216
+ offset?: number;
217
+ order?: string;
218
+ }, options?: RequestOptions): Promise<{
219
+ total: number;
220
+ limit: number;
221
+ offset: number;
222
+ hasMore: boolean;
223
+ nextOffset: number | null;
224
+ items: Array<{
225
+ path: string;
226
+ sizeBytes: number;
227
+ size: string;
228
+ sha256: string;
229
+ updatedAt: string;
230
+ }>;
231
+ }>;
232
+ /** Reads a text file, optionally a byte range of it. Null when absent. */
233
+ readFile(workspaceId: string, path: string, input?: {
234
+ offset?: number;
235
+ limit?: number;
236
+ }, options?: RequestOptions): Promise<{
237
+ text: string;
238
+ offset: number;
239
+ limit: number;
240
+ totalBytes: number;
241
+ truncated: boolean;
242
+ nextOffset: number | null;
243
+ file: {
244
+ path: string;
245
+ sizeBytes: number;
246
+ size: string;
247
+ sha256: string;
248
+ updatedAt: string;
249
+ };
250
+ } | null>;
251
+ /** Deletes a workspace file. */
252
+ deleteFile(workspaceId: string, path: string, options?: RequestOptions): Promise<boolean>;
253
+ /**
254
+ * Deploys the workspace as a SERVICE (an HTTP server).
255
+ *
256
+ * With `wait: true` this resolves only once the build, VM boot and health
257
+ * probe have all passed.
258
+ */
259
+ deployService(options: DeployServiceOptions): Promise<Deployment>;
260
+ /** Deploys the workspace as a STATIC site. */
261
+ deployStatic(options: DeployStaticOptions): Promise<Deployment>;
262
+ /**
263
+ * Deploys a BOX: a persistent always-on Linux VM with a data disk that
264
+ * survives stops and redeploys. A dev machine, not an HTTP service. The
265
+ * result's `sshCommand` is how you get in.
266
+ */
267
+ deployBox(options: DeployBoxOptions): Promise<Deployment>;
268
+ /** Escape hatch: deploy with a raw CreateDeploymentInput. */
269
+ deploy(input: CreateDeploymentInput, options?: Omit<DeployOptions, "workspaceId">): Promise<Deployment>;
270
+ /**
271
+ * Polls a deployment until it is READY, throwing on FAILED or timeout.
272
+ *
273
+ * BUILDING is the only non-terminal status; READY and FAILED are terminal.
274
+ * The poll query selects six fields rather than the full deployment, because
275
+ * this runs every few seconds for potentially minutes — then the READY
276
+ * deployment is re-read in full so callers get the same shape `deployService`
277
+ * returns without `wait`.
278
+ */
279
+ waitForDeployment(deploymentId: string, waitOptions?: WaitOptions): Promise<Deployment>;
280
+ /** Reads one deployment by id. Null when absent. */
281
+ getDeployment(deploymentId: string, options?: RequestOptions): Promise<Deployment | null>;
282
+ /** Reads the deployment currently serving an alias. Null when absent. */
283
+ getDeploymentByAlias(alias: string, options?: RequestOptions): Promise<Deployment | null>;
284
+ /** Lists deployments (paginated, filterable by kind/alias/workspace). */
285
+ listDeployments(input?: ListDeploymentsInput, options?: RequestOptions): Promise<{
286
+ total: number;
287
+ limit: number;
288
+ offset: number;
289
+ hasMore: boolean;
290
+ nextOffset: number | null;
291
+ items: Array<{
292
+ id: string;
293
+ workspaceID: string;
294
+ kind: import("./index.js").DeploymentKind;
295
+ runtime: ServiceRuntime | null;
296
+ alias: string | null;
297
+ serviceMode: ServiceMode;
298
+ healthPath: string | null;
299
+ cronSchedule: string | null;
300
+ cronPath: string | null;
301
+ url: string;
302
+ previewUrl: string;
303
+ sshCommand: string | null;
304
+ aliasHeld: boolean;
305
+ aliasRevision: string;
306
+ status: import("./index.js").DeploymentStatus;
307
+ error: string | null;
308
+ filesCount: number;
309
+ sizeBytes: number;
310
+ size: string;
311
+ spaMode: boolean;
312
+ gitRepo: string | null;
313
+ gitCommit: string | null;
314
+ createdAt: string;
315
+ }>;
316
+ }>;
317
+ /** Live VM status: health, in-flight requests, restarts, RAM ceiling. */
318
+ getStatus(deploymentId: string, options?: RequestOptions): Promise<{
319
+ deploymentID: string;
320
+ alias: string | null;
321
+ serviceMode: ServiceMode;
322
+ running: boolean;
323
+ health: string;
324
+ vmID: string | null;
325
+ inFlight: number;
326
+ alwaysOn: boolean;
327
+ memSizeMiB: number;
328
+ startedAt: string | null;
329
+ lastUsedAt: string | null;
330
+ lastStoppedAt: string | null;
331
+ restartCount: number;
332
+ lastError: string | null;
333
+ }>;
334
+ /**
335
+ * Tail of the service's stdout/stderr.
336
+ *
337
+ * The point of doing this from a script rather than through tool calls: you
338
+ * can filter locally. `(await unfazed.getLogs(id)).stderr.split("\n")
339
+ * .filter(l => l.includes("ERROR"))` costs a model zero context, where the
340
+ * same tail through an MCP tool spends all of it.
341
+ */
342
+ getLogs(deploymentId: string, input?: {
343
+ tailBytes?: number;
344
+ }, options?: RequestOptions): Promise<{
345
+ deploymentID: string;
346
+ stdout: string;
347
+ stderr: string;
348
+ truncated: boolean;
349
+ stdoutBytes: number;
350
+ stderrBytes: number;
351
+ tailBytes: number;
352
+ }>;
353
+ /** Deployment lifecycle events — build, boot, probe, cron, restarts. */
354
+ getEvents(deploymentId: string, limit?: number, options?: RequestOptions): Promise<{
355
+ id: string;
356
+ deploymentID: string;
357
+ type: import("./index.js").DeploymentEventType;
358
+ rawType: string;
359
+ severity: import("./index.js").DeploymentEventSeverity;
360
+ message: string;
361
+ vmID: string | null;
362
+ createdAt: string;
363
+ }[]>;
364
+ /** Restarts a service's VM. */
365
+ restartService(deploymentId: string, options?: RequestOptions): Promise<{
366
+ deploymentID: string;
367
+ running: boolean;
368
+ health: string;
369
+ vmID: string | null;
370
+ restartCount: number;
371
+ lastError: string | null;
372
+ }>;
373
+ /** Stops a service's VM. An on-demand service cold-starts again on request. */
374
+ stopService(deploymentId: string, options?: RequestOptions): Promise<{
375
+ deploymentID: string;
376
+ running: boolean;
377
+ health: string;
378
+ vmID: string | null;
379
+ restartCount: number;
380
+ lastError: string | null;
381
+ }>;
382
+ /** Deletes a deployment. */
383
+ deleteDeployment(deploymentId: string, options?: RequestOptions): Promise<boolean>;
384
+ /** Sets a workspace-default env var. Redeploy the alias to apply it. */
385
+ setEnv(workspaceId: string, name: string, value: string, options?: RequestOptions): Promise<EnvVar>;
386
+ /** Sets a workspace-default secret. The value is never readable again. */
387
+ setSecret(workspaceId: string, name: string, value: string, options?: RequestOptions): Promise<EnvVar>;
388
+ /**
389
+ * Sets many workspace env vars in one request, via aliased mutations —
390
+ * the same batching trick as {@link writeFiles}.
391
+ *
392
+ * Pass `{ secret: true }` to route them all to `setWorkspaceSecret`.
393
+ */
394
+ setEnvMany(workspaceId: string, vars: Record<string, string>, options?: {
395
+ secret?: boolean;
396
+ signal?: AbortSignal;
397
+ }): Promise<EnvVar[]>;
398
+ /** Deletes a workspace-default env var. */
399
+ deleteEnv(workspaceId: string, name: string, options?: RequestOptions): Promise<boolean>;
400
+ /** Workspace-default env vars. Secret values are redacted. */
401
+ listEnv(workspaceId: string, options?: RequestOptions): Promise<EnvVar[]>;
402
+ /** Sets an env var for ONE alias, overriding the workspace default. */
403
+ setAppEnv(workspaceId: string, alias: string, name: string, value: string, options?: RequestOptions): Promise<EnvVar>;
404
+ /** Sets a secret for ONE alias, overriding the workspace default. */
405
+ setAppSecret(workspaceId: string, alias: string, name: string, value: string, options?: RequestOptions): Promise<EnvVar>;
406
+ /**
407
+ * REMOVE: blocks this name for the app entirely — the app override, the
408
+ * workspace default, and integration enrichment. Not the same as deleting an
409
+ * override; use {@link inheritAppEnv} for that.
410
+ */
411
+ removeAppEnv(workspaceId: string, alias: string, name: string, options?: RequestOptions): Promise<boolean>;
412
+ /** INHERIT: clears the app override/mask so workspace and integration values apply again. */
413
+ inheritAppEnv(workspaceId: string, alias: string, name: string, options?: RequestOptions): Promise<boolean>;
414
+ /** The effective env view for one alias: overrides, inherited and masked. */
415
+ listAppEnv(workspaceId: string, alias: string, options?: RequestOptions): Promise<EnvVar[]>;
416
+ /**
417
+ * An alias's terminal release history, newest first.
418
+ *
419
+ * Exactly one entry has `aliasHeld: true` — the live release. FAILED entries
420
+ * appear on purpose (they are rejected promotions, so a kept-live previous
421
+ * release is never mistaken for a silent fallback) and cannot be rolled back
422
+ * to. Retention keeps only the most recent few per alias, default 5.
423
+ */
424
+ listReleases(alias: string, options?: RequestOptions): Promise<Deployment[]>;
425
+ /**
426
+ * Rolls an alias back to a prior release, handling the whole compare-and-swap
427
+ * protocol in one call.
428
+ *
429
+ * The server requires the caller to prove it observed the current state: you
430
+ * must pass the live release's id AND its opaque `aliasRevision`, and the
431
+ * swap fails if either changed since you listed — including same-id ABA. So
432
+ * this method does the full dance: list releases, find the `aliasHeld` entry,
433
+ * pick the target, and submit both guard values.
434
+ *
435
+ * On a rejected guard it re-lists and retries (default once). The TARGET is
436
+ * pinned across retries while the guard is refreshed, so a concurrent deploy
437
+ * can never redirect the rollback to a different release than the one chosen.
438
+ *
439
+ * ```ts
440
+ * await unfazed.rollback({ alias: "my-app" }); // undo last deploy
441
+ * await unfazed.rollback({ alias: "my-app", to: relId }); // to a specific release
442
+ * ```
443
+ */
444
+ rollback(options: RollbackOptions): Promise<Deployment>;
445
+ }