unfazed 0.2.0 → 0.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 CHANGED
@@ -194,6 +194,19 @@ The chains an agent actually performs. Reach `client` from it any time:
194
194
  | `listReleases(alias)` | newest first; one entry has `aliasHeld: true` |
195
195
  | `rollback({ alias, to?, expectedCurrentId?, retries? })` | **whole compare-and-swap in one call** |
196
196
 
197
+ **Agent runs**
198
+ | Method | Notes |
199
+ | --- | --- |
200
+ | `createRun(opts)` | `wait: true` polls to a terminal status; a boot-failed or exit-nonzero run comes back as a value, never thrown. `verify: [...]` commands run after the agent and decide whether the task is done |
201
+ | `waitForRun(id, opts?)` | standalone poller; only a timeout throws |
202
+ | `getRun(id)` / `listRuns(filter?)` | |
203
+ | `cancelRun(id)` | idempotent on a CANCELLED run; a finished run is refused |
204
+ | `followUpRun(id, { prompt, wait? })` | continues a finished run (or approves a `mode: "plan"` run) with the parent's context prepended; `parentRunID`/`depth` on the result |
205
+ | `answerRun(id, answer)` | answers a BLOCKED run's `question` (human in the loop); `approval: "human"` on `createRun` routes the harness's permission prompts here |
206
+ | `runEvents(id, { limit? })` | the run's own history: started, cancel requested, settled |
207
+ | `runConsole(id, { tailBytes? })` | live tail while RUNNING |
208
+ | `runTranscript(id, { tailBytes? })` | the stored transcript once settled |
209
+
197
210
  ## The two things worth knowing
198
211
 
199
212
  ### `writeFiles` batches, because the API is one-file-per-mutation
@@ -334,6 +347,71 @@ ignoring the boolean.
334
347
  reason: a rejected name leaves the ones before it committed, with the successful
335
348
  aliases in the thrown error's `data`.
336
349
 
350
+ ## Agent runs
351
+
352
+ A **run** is the past-tense counterpart to a deployment: boot a fresh, ephemeral
353
+ VM off the box image, hand it a prompt, let a harness (`claude-code`, `codex`,
354
+ or your own `command`) work headlessly for up to an hour, keep the transcript,
355
+ meter what it cost, and destroy the VM when it exits. Deployments answer "what
356
+ is running"; runs answer "what happened".
357
+
358
+ ```ts
359
+ const run = await unfazed.createRun({
360
+ workspaceId: ws.id,
361
+ harness: "claude-code",
362
+ prompt: "Fix the failing test in test/foo.test.ts and open a PR.",
363
+ wait: true,
364
+ });
365
+
366
+ console.log(run.status, run.usage.mibSeconds);
367
+ if (run.status === "FAILED") console.error(run.error);
368
+ ```
369
+
370
+ `createRun` itself returns as soon as the VM has started — **RUNNING**, or
371
+ **FAILED** with `error` set if it could not even boot. `wait: true` additionally
372
+ polls a RUNNING run through to a terminal status.
373
+
374
+ **A FAILED or CANCELLED run is a normal return value, never thrown** — unlike
375
+ `deployService`, where a FAILED build throws `UnfazedDeployError`. A run that
376
+ executed and exited non-zero is a completed answer, not a broken build: read
377
+ `run.error` / `run.exitCode` off the result instead of wrapping the call in
378
+ `try/catch`. `waitForRun` (and `createRun({ wait: true })`, which calls it)
379
+ throws only on **timeout**, as `UnfazedRunError` with `reason: "TIMEOUT"` —
380
+ the run is, as far as the last poll showed, still QUEUED or RUNNING, and
381
+ waiting longer may yet succeed:
382
+
383
+ ```ts
384
+ try {
385
+ const run = await unfazed.waitForRun(run.id, { timeoutMs: 10 * 60_000 });
386
+ // run.status is SUCCEEDED, FAILED or CANCELLED here — check it yourself.
387
+ // SUCCEEDED means the harness exited cleanly, not that the task is done:
388
+ // run.result is the harness's own report ({ status, detail, text } — text
389
+ // is its final message) and run.verifyExitCode the verdict of your
390
+ // `verify` commands, when you passed any.
391
+ } catch (e) {
392
+ if (e instanceof UnfazedRunError && e.reason === "TIMEOUT") {
393
+ console.log("still going; call waitForRun again to keep waiting");
394
+ } else {
395
+ throw e;
396
+ }
397
+ }
398
+ ```
399
+
400
+ While a run is RUNNING, `runConsole(id, { tailBytes? })` tails its live,
401
+ redacted console output; once it settles, `runConsole` comes back empty with
402
+ `running: false` and `runTranscript(id, { tailBytes? })` returns the bounded
403
+ end of the stored transcript instead (`run.transcriptAvailable` says which one
404
+ to reach for). `runEvents(id, { limit? })` is the run's own history — started,
405
+ cancel requested, settled.
406
+
407
+ **Inside a run, `new Unfazed()` is already authenticated.** The platform
408
+ injects `UNFZ_API_TOKEN` and `UNFZ_API_URL` (plus `UNFZ_MCP_URL`) into every
409
+ run with at most your own scopes, valid for the run's wall clock and revoked
410
+ when it settles — the same delegated-credential mechanism `run_script` uses.
411
+ So an agent working inside a run can create workspaces, deploy, and read logs
412
+ through this same SDK with zero configuration and no credential ever written
413
+ into the prompt.
414
+
337
415
  ## Development
338
416
 
339
417
  ```sh
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** SDK version, sent as the User-Agent. Keep in sync with package.json. */
2
- export declare const VERSION = "0.2.0";
2
+ export declare const VERSION = "0.4.0";
3
3
  /**
4
4
  * Default GraphQL endpoint.
5
5
  *
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { UnfazedAuthError, UnfazedConfigError, UnfazedGraphQLError, UnfazedHTTPError, UnfazedTimeoutError, } from "./errors.js";
2
2
  /** SDK version, sent as the User-Agent. Keep in sync with package.json. */
3
- export const VERSION = "0.2.0";
3
+ export const VERSION = "0.4.0";
4
4
  /**
5
5
  * Default GraphQL endpoint.
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedTimeoutError,\n type GraphQLResponseError,\n} from \"./errors.js\";\n\n/** SDK version, sent as the User-Agent. Keep in sync with package.json. */\nexport const VERSION = \"0.2.0\";\n\n/**\n * Default GraphQL endpoint.\n *\n * Note this is the FULL endpoint, not an origin: the platform's own documented\n * convention (`UNFZ_API_URL=\"https://unfazed.dev/query\"`, and cmd/showcase-deploy\n * in the unfazed repo) treats the /query URL as the configurable unit, so the\n * SDK matches it rather than inventing a base-URL convention.\n */\nexport const DEFAULT_ENDPOINT = \"https://unfazed.dev/query\";\n\n/** Platform origin, used to derive the endpoint when only `baseUrl` is given. */\nexport const DEFAULT_BASE_URL = \"https://unfazed.dev\";\n\n/** Path of the GraphQL endpoint under an origin. */\nexport const GRAPHQL_PATH = \"/query\";\n\n/**\n * Environment variable holding the bearer token.\n *\n * This is the platform's documented name (docs/agent-deployments.md,\n * internal/agentdocs/reference.md). It accepts either an operator API token or\n * an agent access token from POST /agents/register — /query takes both as\n * plain bearers.\n */\nexport const TOKEN_ENV_VAR = \"UNFZ_API_TOKEN\";\n\n/** Environment variable holding the full GraphQL endpoint URL. */\nexport const ENDPOINT_ENV_VAR = \"UNFZ_API_URL\";\n\n/** Minimal structural type of the `fetch` the transport needs. */\nexport type FetchLike = (\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}>;\n\nexport interface ClientOptions {\n /**\n * Bearer token. Falls back to `UNFZ_API_TOKEN`. Get one from\n * https://unfazed.dev/auth.md — the same token the MCP server uses.\n */\n token?: string;\n /**\n * Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to\n * `UNFZ_API_URL`, then {@link DEFAULT_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Platform origin; `/query` is appended. Convenience for self-hosted or\n * staging control planes where only the host differs.\n */\n baseUrl?: string;\n /** Extra headers merged into every request. Authorization always wins. */\n headers?: Record<string, string>;\n /**\n * Per-request timeout in ms. Default 60_000. Pass 0 to disable.\n *\n * Bounds the WHOLE request — connect, headers and reading the response body —\n * so a proxy that answers with headers and then stalls still fails on time.\n */\n timeoutMs?: number;\n /** Injection point for tests and instrumented/proxied transports. */\n fetch?: FetchLike;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n}\n\n/**\n * A GraphQL document.\n *\n * Structural rather than `string & {...}` on purpose: graphql-codegen emits\n * `TypedDocumentString`, a String SUBCLASS, which is not assignable to the\n * primitive `string`. Requiring only `toString()` plus the optional phantom\n * `__apiType` lets three things through with full inference:\n *\n * - generated `TypedDocumentString` documents (types inferred),\n * - documents assembled at runtime, like the batched writeFiles mutation,\n * - plain template-literal strings a caller wrote by hand.\n */\nexport interface GraphQLDocument<TResult, TVariables> {\n /** Phantom type carrier. Never called; present only to bind the generics. */\n __apiType?: (variables: TVariables) => TResult;\n toString(): string;\n}\n\n/** Result type carried by a typed document. */\nexport type ResultOf<T> = T extends GraphQLDocument<infer R, infer _V> ? R : unknown;\n\n/** Variables type carried by a typed document. */\nexport type VariablesOf<T> = T extends GraphQLDocument<infer _R, infer V> ? V : never;\n\n/** Per-request knobs. */\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n operationName?: string;\n}\n\n/**\n * Layer 1 — a typed, dependency-free GraphQL transport over `fetch`.\n *\n * This is the SDK's entire networking surface. It understands auth, timeouts\n * and the GraphQL error envelope, and nothing about deployments. The workflow\n * layer ({@link import(\"./unfazed.js\").Unfazed}) is built strictly on top of\n * it, so you can always drop down here for any operation the workflow layer\n * does not wrap — the schema has ~660 fields and the workflow layer\n * deliberately covers only the agent-facing ones.\n *\n * ```ts\n * const client = new UnfazedClient(); // reads UNFZ_API_TOKEN\n * const data = await client.query<{ whoami: { name: string } }, never>(\n * `query { whoami { name } }`,\n * );\n * ```\n */\nexport class UnfazedClient {\n /** Resolved GraphQL endpoint URL. */\n readonly endpoint: string;\n\n readonly #token: string;\n readonly #headers: Record<string, string>;\n readonly #timeoutMs: number;\n readonly #fetch: FetchLike;\n\n constructor(options: ClientOptions = {}) {\n const token = options.token ?? readEnv(TOKEN_ENV_VAR);\n if (!token) {\n throw new UnfazedAuthError(\n `unfazed: no API token. Pass { token } or set ${TOKEN_ENV_VAR}. ` +\n `Get one at ${DEFAULT_BASE_URL}/auth.md`,\n );\n }\n this.#token = token.trim();\n\n this.endpoint = resolveEndpoint(options);\n\n this.#headers = { ...(options.headers ?? {}) };\n this.#timeoutMs = options.timeoutMs ?? 60_000;\n\n const injected = options.fetch;\n if (injected) {\n this.#fetch = injected;\n } else {\n const globalFetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof globalFetch !== \"function\") {\n throw new UnfazedConfigError(\n \"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch } to the client.\",\n );\n }\n this.#fetch = globalFetch.bind(globalThis) as FetchLike;\n }\n\n const ua = options.userAgent ?? `unfazed-sdk/${VERSION}`;\n if (ua) this.#headers[\"User-Agent\"] = ua;\n }\n\n /** Runs a query document. Identical wire behaviour to {@link mutate}. */\n query<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * Runs a mutation document. Distinct from {@link query} for call-site\n * readability only — GraphQL over HTTP POST makes no distinction.\n */\n mutate<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * The single code path behind {@link query} and {@link mutate}.\n *\n * Throws {@link UnfazedHTTPError} on non-2xx, {@link UnfazedGraphQLError}\n * when the body carries `errors`, {@link UnfazedTimeoutError} on timeout or\n * abort. Resolves to the `data` object on success.\n */\n async request<TResult = unknown>(\n document: string | { toString(): string },\n variables?: unknown,\n options: RequestOptions = {},\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n const { signal, cleanup, didTimeout } = makeSignal(timeoutMs, options.signal);\n\n // `document` may be a TypedDocumentString (a String object). JSON.stringify\n // would serialise that fine, but String(...) keeps the wire body a plain\n // primitive regardless of how the caller obtained the document.\n const body = JSON.stringify({\n query: String(document),\n ...(variables === undefined ? {} : { variables }),\n ...(options.operationName ? { operationName: options.operationName } : {}),\n });\n\n // The deadline covers BOTH phases — headers AND body. A proxy that answers\n // with headers and then stalls the body is a real failure mode, and\n // stopping the clock at the end of `fetch()` would leave `response.text()`\n // hanging forever. `untilAborted` additionally races the signal itself, so\n // an injected transport that ignores `signal` still cannot outlive the\n // timeout.\n let response: Awaited<ReturnType<FetchLike>>;\n let text: string;\n try {\n response = await untilAborted(\n this.#fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n ...this.#headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${this.#token}`,\n },\n body,\n ...(signal ? { signal } : {}),\n }),\n signal,\n );\n text = await untilAborted(response.text(), signal);\n } catch (cause) {\n if (didTimeout()) {\n throw new UnfazedTimeoutError(`unfazed: request timed out after ${timeoutMs}ms`, { cause });\n }\n if (isAbort(cause)) {\n throw new UnfazedTimeoutError(\"unfazed: request aborted\", { cause });\n }\n throw cause;\n } finally {\n cleanup();\n }\n\n if (!response.ok) {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n let parsed: { data?: TResult; errors?: GraphQLResponseError[] };\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n // A GraphQL 200 can still be a failure, and can be a PARTIAL failure: the\n // batched writeFiles document resolves each aliased field independently.\n // Attaching `data` to the error is what lets a caller see which writes\n // landed before the failing one.\n if (parsed.errors?.length) {\n throw new UnfazedGraphQLError(parsed.errors, parsed.data);\n }\n if (parsed.data === undefined || parsed.data === null) {\n throw new UnfazedGraphQLError(\n [{ message: \"response contained neither data nor errors\" }],\n undefined,\n );\n }\n return parsed.data;\n }\n}\n\nfunction resolveEndpoint(options: ClientOptions): string {\n if (options.endpoint) return options.endpoint;\n if (options.baseUrl) return trimSlash(options.baseUrl) + GRAPHQL_PATH;\n const env = readEnv(ENDPOINT_ENV_VAR);\n // UNFZ_API_URL is documented as the full /query URL and is used verbatim.\n // Tolerate a bare origin anyway, since that is the obvious mistake and\n // guessing right costs nothing.\n if (env) {\n const trimmed = trimSlash(env);\n return /\\/query$|\\/graphql$/.test(trimmed) ? trimmed : trimmed + GRAPHQL_PATH;\n }\n return DEFAULT_ENDPOINT;\n}\n\nfunction readEnv(name: string): string | undefined {\n // Guarded: the SDK must also work in a browser or worker, where there is no\n // `process` at all.\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n ?.env;\n return env?.[name] || undefined;\n}\n\nfunction trimSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction isAbort(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { name?: string }).name === \"AbortError\";\n}\n\nfunction abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/**\n * Settles with `promise`, or rejects the moment `signal` aborts.\n *\n * A global `fetch` honours the signal for headers and body alike, but an\n * injected {@link FetchLike} — a proxy, an instrumented transport, a test\n * double — need not. Racing the signal makes the timeout an upper bound on the\n * whole request regardless of what the transport does with it. The loser's\n * rejection is swallowed by the already-settled promise rather than surfacing\n * as an unhandled rejection.\n */\nfunction untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) return Promise.reject(abortError());\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const done = () => signal.removeEventListener(\"abort\", onAbort);\n promise.then(\n (value) => {\n done();\n resolve(value);\n },\n (error: unknown) => {\n done();\n reject(error as Error);\n },\n );\n });\n}\n\n/**\n * Combines the request timeout with any caller-supplied signal.\n *\n * `didTimeout` is tracked separately from the abort so a caller's own\n * cancellation is not mislabelled as a timeout in the thrown message.\n */\nfunction makeSignal(\n timeoutMs: number,\n external?: AbortSignal,\n): { signal?: AbortSignal; cleanup: () => void; didTimeout: () => boolean } {\n if (!timeoutMs && !external) return { cleanup: () => {}, didTimeout: () => false };\n if (typeof AbortController !== \"function\") {\n return {\n ...(external ? { signal: external } : {}),\n cleanup: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n }\n\n const onExternalAbort = () => controller.abort();\n if (external) {\n if (external.aborted) controller.abort();\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", onExternalAbort);\n },\n didTimeout: () => timedOut,\n };\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedTimeoutError,\n type GraphQLResponseError,\n} from \"./errors.js\";\n\n/** SDK version, sent as the User-Agent. Keep in sync with package.json. */\nexport const VERSION = \"0.4.0\";\n\n/**\n * Default GraphQL endpoint.\n *\n * Note this is the FULL endpoint, not an origin: the platform's own documented\n * convention (`UNFZ_API_URL=\"https://unfazed.dev/query\"`, and cmd/showcase-deploy\n * in the unfazed repo) treats the /query URL as the configurable unit, so the\n * SDK matches it rather than inventing a base-URL convention.\n */\nexport const DEFAULT_ENDPOINT = \"https://unfazed.dev/query\";\n\n/** Platform origin, used to derive the endpoint when only `baseUrl` is given. */\nexport const DEFAULT_BASE_URL = \"https://unfazed.dev\";\n\n/** Path of the GraphQL endpoint under an origin. */\nexport const GRAPHQL_PATH = \"/query\";\n\n/**\n * Environment variable holding the bearer token.\n *\n * This is the platform's documented name (docs/agent-deployments.md,\n * internal/agentdocs/reference.md). It accepts either an operator API token or\n * an agent access token from POST /agents/register — /query takes both as\n * plain bearers.\n */\nexport const TOKEN_ENV_VAR = \"UNFZ_API_TOKEN\";\n\n/** Environment variable holding the full GraphQL endpoint URL. */\nexport const ENDPOINT_ENV_VAR = \"UNFZ_API_URL\";\n\n/** Minimal structural type of the `fetch` the transport needs. */\nexport type FetchLike = (\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}>;\n\nexport interface ClientOptions {\n /**\n * Bearer token. Falls back to `UNFZ_API_TOKEN`. Get one from\n * https://unfazed.dev/auth.md — the same token the MCP server uses.\n */\n token?: string;\n /**\n * Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to\n * `UNFZ_API_URL`, then {@link DEFAULT_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Platform origin; `/query` is appended. Convenience for self-hosted or\n * staging control planes where only the host differs.\n */\n baseUrl?: string;\n /** Extra headers merged into every request. Authorization always wins. */\n headers?: Record<string, string>;\n /**\n * Per-request timeout in ms. Default 60_000. Pass 0 to disable.\n *\n * Bounds the WHOLE request — connect, headers and reading the response body —\n * so a proxy that answers with headers and then stalls still fails on time.\n */\n timeoutMs?: number;\n /** Injection point for tests and instrumented/proxied transports. */\n fetch?: FetchLike;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n}\n\n/**\n * A GraphQL document.\n *\n * Structural rather than `string & {...}` on purpose: graphql-codegen emits\n * `TypedDocumentString`, a String SUBCLASS, which is not assignable to the\n * primitive `string`. Requiring only `toString()` plus the optional phantom\n * `__apiType` lets three things through with full inference:\n *\n * - generated `TypedDocumentString` documents (types inferred),\n * - documents assembled at runtime, like the batched writeFiles mutation,\n * - plain template-literal strings a caller wrote by hand.\n */\nexport interface GraphQLDocument<TResult, TVariables> {\n /** Phantom type carrier. Never called; present only to bind the generics. */\n __apiType?: (variables: TVariables) => TResult;\n toString(): string;\n}\n\n/** Result type carried by a typed document. */\nexport type ResultOf<T> = T extends GraphQLDocument<infer R, infer _V> ? R : unknown;\n\n/** Variables type carried by a typed document. */\nexport type VariablesOf<T> = T extends GraphQLDocument<infer _R, infer V> ? V : never;\n\n/** Per-request knobs. */\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n operationName?: string;\n}\n\n/**\n * Layer 1 — a typed, dependency-free GraphQL transport over `fetch`.\n *\n * This is the SDK's entire networking surface. It understands auth, timeouts\n * and the GraphQL error envelope, and nothing about deployments. The workflow\n * layer ({@link import(\"./unfazed.js\").Unfazed}) is built strictly on top of\n * it, so you can always drop down here for any operation the workflow layer\n * does not wrap — the schema has ~660 fields and the workflow layer\n * deliberately covers only the agent-facing ones.\n *\n * ```ts\n * const client = new UnfazedClient(); // reads UNFZ_API_TOKEN\n * const data = await client.query<{ whoami: { name: string } }, never>(\n * `query { whoami { name } }`,\n * );\n * ```\n */\nexport class UnfazedClient {\n /** Resolved GraphQL endpoint URL. */\n readonly endpoint: string;\n\n readonly #token: string;\n readonly #headers: Record<string, string>;\n readonly #timeoutMs: number;\n readonly #fetch: FetchLike;\n\n constructor(options: ClientOptions = {}) {\n const token = options.token ?? readEnv(TOKEN_ENV_VAR);\n if (!token) {\n throw new UnfazedAuthError(\n `unfazed: no API token. Pass { token } or set ${TOKEN_ENV_VAR}. ` +\n `Get one at ${DEFAULT_BASE_URL}/auth.md`,\n );\n }\n this.#token = token.trim();\n\n this.endpoint = resolveEndpoint(options);\n\n this.#headers = { ...(options.headers ?? {}) };\n this.#timeoutMs = options.timeoutMs ?? 60_000;\n\n const injected = options.fetch;\n if (injected) {\n this.#fetch = injected;\n } else {\n const globalFetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof globalFetch !== \"function\") {\n throw new UnfazedConfigError(\n \"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch } to the client.\",\n );\n }\n this.#fetch = globalFetch.bind(globalThis) as FetchLike;\n }\n\n const ua = options.userAgent ?? `unfazed-sdk/${VERSION}`;\n if (ua) this.#headers[\"User-Agent\"] = ua;\n }\n\n /** Runs a query document. Identical wire behaviour to {@link mutate}. */\n query<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * Runs a mutation document. Distinct from {@link query} for call-site\n * readability only — GraphQL over HTTP POST makes no distinction.\n */\n mutate<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * The single code path behind {@link query} and {@link mutate}.\n *\n * Throws {@link UnfazedHTTPError} on non-2xx, {@link UnfazedGraphQLError}\n * when the body carries `errors`, {@link UnfazedTimeoutError} on timeout or\n * abort. Resolves to the `data` object on success.\n */\n async request<TResult = unknown>(\n document: string | { toString(): string },\n variables?: unknown,\n options: RequestOptions = {},\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n const { signal, cleanup, didTimeout } = makeSignal(timeoutMs, options.signal);\n\n // `document` may be a TypedDocumentString (a String object). JSON.stringify\n // would serialise that fine, but String(...) keeps the wire body a plain\n // primitive regardless of how the caller obtained the document.\n const body = JSON.stringify({\n query: String(document),\n ...(variables === undefined ? {} : { variables }),\n ...(options.operationName ? { operationName: options.operationName } : {}),\n });\n\n // The deadline covers BOTH phases — headers AND body. A proxy that answers\n // with headers and then stalls the body is a real failure mode, and\n // stopping the clock at the end of `fetch()` would leave `response.text()`\n // hanging forever. `untilAborted` additionally races the signal itself, so\n // an injected transport that ignores `signal` still cannot outlive the\n // timeout.\n let response: Awaited<ReturnType<FetchLike>>;\n let text: string;\n try {\n response = await untilAborted(\n this.#fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n ...this.#headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${this.#token}`,\n },\n body,\n ...(signal ? { signal } : {}),\n }),\n signal,\n );\n text = await untilAborted(response.text(), signal);\n } catch (cause) {\n if (didTimeout()) {\n throw new UnfazedTimeoutError(`unfazed: request timed out after ${timeoutMs}ms`, { cause });\n }\n if (isAbort(cause)) {\n throw new UnfazedTimeoutError(\"unfazed: request aborted\", { cause });\n }\n throw cause;\n } finally {\n cleanup();\n }\n\n if (!response.ok) {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n let parsed: { data?: TResult; errors?: GraphQLResponseError[] };\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n // A GraphQL 200 can still be a failure, and can be a PARTIAL failure: the\n // batched writeFiles document resolves each aliased field independently.\n // Attaching `data` to the error is what lets a caller see which writes\n // landed before the failing one.\n if (parsed.errors?.length) {\n throw new UnfazedGraphQLError(parsed.errors, parsed.data);\n }\n if (parsed.data === undefined || parsed.data === null) {\n throw new UnfazedGraphQLError(\n [{ message: \"response contained neither data nor errors\" }],\n undefined,\n );\n }\n return parsed.data;\n }\n}\n\nfunction resolveEndpoint(options: ClientOptions): string {\n if (options.endpoint) return options.endpoint;\n if (options.baseUrl) return trimSlash(options.baseUrl) + GRAPHQL_PATH;\n const env = readEnv(ENDPOINT_ENV_VAR);\n // UNFZ_API_URL is documented as the full /query URL and is used verbatim.\n // Tolerate a bare origin anyway, since that is the obvious mistake and\n // guessing right costs nothing.\n if (env) {\n const trimmed = trimSlash(env);\n return /\\/query$|\\/graphql$/.test(trimmed) ? trimmed : trimmed + GRAPHQL_PATH;\n }\n return DEFAULT_ENDPOINT;\n}\n\nfunction readEnv(name: string): string | undefined {\n // Guarded: the SDK must also work in a browser or worker, where there is no\n // `process` at all.\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n ?.env;\n return env?.[name] || undefined;\n}\n\nfunction trimSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction isAbort(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { name?: string }).name === \"AbortError\";\n}\n\nfunction abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/**\n * Settles with `promise`, or rejects the moment `signal` aborts.\n *\n * A global `fetch` honours the signal for headers and body alike, but an\n * injected {@link FetchLike} — a proxy, an instrumented transport, a test\n * double — need not. Racing the signal makes the timeout an upper bound on the\n * whole request regardless of what the transport does with it. The loser's\n * rejection is swallowed by the already-settled promise rather than surfacing\n * as an unhandled rejection.\n */\nfunction untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) return Promise.reject(abortError());\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const done = () => signal.removeEventListener(\"abort\", onAbort);\n promise.then(\n (value) => {\n done();\n resolve(value);\n },\n (error: unknown) => {\n done();\n reject(error as Error);\n },\n );\n });\n}\n\n/**\n * Combines the request timeout with any caller-supplied signal.\n *\n * `didTimeout` is tracked separately from the abort so a caller's own\n * cancellation is not mislabelled as a timeout in the thrown message.\n */\nfunction makeSignal(\n timeoutMs: number,\n external?: AbortSignal,\n): { signal?: AbortSignal; cleanup: () => void; didTimeout: () => boolean } {\n if (!timeoutMs && !external) return { cleanup: () => {}, didTimeout: () => false };\n if (typeof AbortController !== \"function\") {\n return {\n ...(external ? { signal: external } : {}),\n cleanup: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n }\n\n const onExternalAbort = () => controller.abort();\n if (external) {\n if (external.aborted) controller.abort();\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", onExternalAbort);\n },\n didTimeout: () => timedOut,\n };\n}\n"]}
package/dist/errors.d.ts CHANGED
@@ -63,6 +63,24 @@ export declare class UnfazedDeployError extends UnfazedError {
63
63
  readonly deployment: unknown;
64
64
  constructor(reason: "FAILED" | "TIMEOUT", deploymentId: string, deployment: unknown, message: string);
65
65
  }
66
+ /**
67
+ * Waiting on a run timed out.
68
+ *
69
+ * Unlike {@link UnfazedDeployError}, a run's FAILED status is never thrown
70
+ * here: {@link import("./unfazed.js").Unfazed.waitForRun} resolves on
71
+ * SUCCEEDED, FAILED and CANCELLED alike, because a run that executed and
72
+ * exited non-zero is a completed answer — read `run.error`/`run.exitCode` —
73
+ * not a broken build the way a FAILED deployment is. TIMEOUT is the only
74
+ * outcome still worth an exception: the run is, as far as the last poll
75
+ * showed, still QUEUED or RUNNING, and waiting longer may yet succeed.
76
+ */
77
+ export declare class UnfazedRunError extends UnfazedError {
78
+ readonly reason: "TIMEOUT";
79
+ readonly runId: string;
80
+ /** Last run snapshot observed before giving up. */
81
+ readonly run: unknown;
82
+ constructor(reason: "TIMEOUT", runId: string, run: unknown, message: string);
83
+ }
66
84
  /**
67
85
  * A rollback's compare-and-swap guard was rejected: the alias moved between
68
86
  * reading the current release and swapping it.
package/dist/errors.js CHANGED
@@ -72,6 +72,29 @@ export class UnfazedDeployError extends UnfazedError {
72
72
  this.deployment = deployment;
73
73
  }
74
74
  }
75
+ /**
76
+ * Waiting on a run timed out.
77
+ *
78
+ * Unlike {@link UnfazedDeployError}, a run's FAILED status is never thrown
79
+ * here: {@link import("./unfazed.js").Unfazed.waitForRun} resolves on
80
+ * SUCCEEDED, FAILED and CANCELLED alike, because a run that executed and
81
+ * exited non-zero is a completed answer — read `run.error`/`run.exitCode` —
82
+ * not a broken build the way a FAILED deployment is. TIMEOUT is the only
83
+ * outcome still worth an exception: the run is, as far as the last poll
84
+ * showed, still QUEUED or RUNNING, and waiting longer may yet succeed.
85
+ */
86
+ export class UnfazedRunError extends UnfazedError {
87
+ reason;
88
+ runId;
89
+ /** Last run snapshot observed before giving up. */
90
+ run;
91
+ constructor(reason, runId, run, message) {
92
+ super(message);
93
+ this.reason = reason;
94
+ this.runId = runId;
95
+ this.run = run;
96
+ }
97
+ }
75
98
  /**
76
99
  * A rollback's compare-and-swap guard was rejected: the alias moved between
77
100
  * reading the current release and swapping it.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Error taxonomy.\n *\n * Everything the SDK throws is an {@link UnfazedError}, so a code-mode script\n * can `catch (e) { if (e instanceof UnfazedError) ... }` and still\n * discriminate on the subclass to decide whether to retry.\n */\n\n/** A single entry from a GraphQL response's `errors` array. */\nexport interface GraphQLResponseError {\n message: string;\n path?: (string | number)[];\n extensions?: Record<string, unknown>;\n}\n\n/** Base class for everything this SDK throws. */\nexport class UnfazedError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/** No token was passed and no token environment variable was set. */\nexport class UnfazedAuthError extends UnfazedError {}\n\n/** The runtime has no global `fetch` and none was injected. */\nexport class UnfazedConfigError extends UnfazedError {}\n\n/**\n * The endpoint answered with a non-2xx status. `status` is the HTTP status and\n * `body` the response text, which is where the platform reports non-GraphQL\n * failures such as an expired or wrong-scope bearer token.\n */\nexport class UnfazedHTTPError extends UnfazedError {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string, url: string) {\n super(`unfazed: HTTP ${status} from ${url}${body ? `: ${truncate(body)}` : \"\"}`);\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * The endpoint answered 200 with a GraphQL `errors` array.\n *\n * Partial `data` is preserved deliberately: the batched writeFiles document\n * resolves each aliased field independently, so one path can fail while the\n * others commit, and the caller needs to see which landed.\n */\nexport class UnfazedGraphQLError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n readonly data: unknown;\n\n constructor(errors: GraphQLResponseError[], data: unknown) {\n const summary = errors.map((e) => e.message).join(\"; \") || \"unknown GraphQL error\";\n super(`unfazed: ${summary}`);\n this.errors = errors;\n this.data = data;\n }\n}\n\n/** A request exceeded `timeoutMs`, or the caller's AbortSignal fired. */\nexport class UnfazedTimeoutError extends UnfazedError {}\n\n/**\n * Waiting on a deploy ended badly.\n *\n * `reason` separates the two outcomes because they need different handling:\n * FAILED means the build finished and is broken (read `deployment.error` and\n * fix the source — retrying will not help), TIMEOUT means it is still BUILDING\n * and polling longer may still succeed.\n */\nexport class UnfazedDeployError extends UnfazedError {\n readonly reason: \"FAILED\" | \"TIMEOUT\";\n readonly deploymentId: string;\n /** Last deployment snapshot observed before giving up. */\n readonly deployment: unknown;\n\n constructor(\n reason: \"FAILED\" | \"TIMEOUT\",\n deploymentId: string,\n deployment: unknown,\n message: string,\n ) {\n super(message);\n this.reason = reason;\n this.deploymentId = deploymentId;\n this.deployment = deployment;\n }\n}\n\n/**\n * A rollback's compare-and-swap guard was rejected: the alias moved between\n * reading the current release and swapping it.\n *\n * Recovery is always \"re-list releases and rebuild the guard\" — never \"reuse\n * the old tuple\", because the server checks the holder id, epoch AND opaque\n * revision to close same-id ABA. `Unfazed.rollback` does that re-list for you\n * once by default.\n */\nexport class UnfazedConflictError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n\n constructor(message: string, errors: GraphQLResponseError[]) {\n super(message);\n this.errors = errors;\n }\n}\n\n/** The requested alias, deployment or release does not exist (or was pruned). */\nexport class UnfazedNotFoundError extends UnfazedError {}\n\n/**\n * The server's exact \"no such workspace\" sentinel.\n *\n * A missing workspace and a workspace belonging to another tenant return the\n * SAME message on purpose, so the API cannot be used to probe for someone\n * else's ids. Treat it as \"not yours to see\", not strictly as \"absent\".\n */\nexport const WORKSPACE_NOT_FOUND = \"workspace was not found\";\n\n/** The server's exact \"no such deployment\" sentinel. See {@link WORKSPACE_NOT_FOUND}. */\nexport const DEPLOYMENT_NOT_FOUND = \"deployment was not found\";\n\n/**\n * Does this error carry EXACTLY the given not-found sentinel?\n *\n * Exact equality, deliberately. The tempting version — \"does any message\n * contain 'not found'\" — also swallows `release not found in history`,\n * `integration not found` and every future message that happens to use the\n * phrase, turning real failures into a silent `false` from a delete. These two\n * strings are stable authz sentinels with test coverage on the server side;\n * anything else is a genuine error and must keep throwing.\n */\nexport function isNotFoundSentinel(error: unknown, sentinel: string): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => e.message.trim() === sentinel)\n );\n}\n\n/**\n * Message shape of the server's per-owner duplicate workspace name error:\n * `a workspace named \"x\" already exists; reuse it (id <uuid>) or pick a\n * different name`.\n *\n * Used only to turn a lost `ensureWorkspace` race into a re-read. Requiring\n * both \"workspace\" and \"already exists\" in one clause keeps it off unrelated\n * conflicts (a duplicate ALIAS, say), and a false negative is safe: the\n * caller just gets the server's error verbatim.\n */\nconst DUPLICATE_WORKSPACE_NAME = /\\bworkspace\\b[^.;]{0,80}\\balready exists\\b/i;\n\n/** Did `createWorkspace` fail because the name is already taken by this owner? */\nexport function isDuplicateWorkspaceName(error: unknown): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => DUPLICATE_WORKSPACE_NAME.test(e.message))\n );\n}\n\nfunction truncate(s: string, max = 500): string {\n return s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n\n/**\n * Message shapes that mean \"the alias moved under you\".\n *\n * The first two are the platform's ACTUAL strings. A rejected rollback guard\n * returns `store.ErrAliasMoved` verbatim — every guard point in the rollback\n * and adopt paths returns that one sentinel unwrapped — and the staged-service\n * rollback wraps the same guard with its own prefix. The rest are defensive:\n * the guard ARGUMENTS named back at you, or the alias-revision concept in any\n * spelling, or `alias` sitting in the same clause as a conflict verb, so a\n * rewording on the server side does not silently disable the retry.\n *\n * Deliberately NOT matched: a bare \"conflict\", \"changed\" or \"stale\" anywhere in\n * any message. Those fire on errors that have nothing to do with the alias\n * compare-and-swap, and a false positive is not free — it spends a pointless\n * re-list AND rewraps an unrelated failure as {@link UnfazedConflictError},\n * whose message would then blame concurrent deploys for, say, a permission\n * problem.\n */\nconst CAS_PATTERNS: RegExp[] = [\n /alias was repointed by another change/i,\n /became stale before alias change/i,\n /expected[\\s_-]*current/i,\n /alias[\\s_-]*revision/i,\n /\\balias\\b[^.;]{0,80}\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b/i,\n /\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b[^.;]{0,80}\\balias\\b/i,\n];\n\n/**\n * Heuristic: does this GraphQL error set look like a rejected compare-and-swap?\n *\n * The server carries no machine-readable code in `extensions` — its error\n * presenter returns gqlgen's default untouched — so this has to match message\n * text. It is used ONLY to decide whether to re-list and retry once: a false\n * negative costs a surfaced error the caller can retry by hand, a false\n * positive costs an extra read and a misleading error class. Neither can\n * promote the wrong release, because the retry rebuilds the guard from a fresh\n * read and the server re-validates it.\n */\nexport function looksLikeCASConflict(errors: GraphQLResponseError[]): boolean {\n return errors.some((e) => CAS_PATTERNS.some((pattern) => pattern.test(e.message)));\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Error taxonomy.\n *\n * Everything the SDK throws is an {@link UnfazedError}, so a code-mode script\n * can `catch (e) { if (e instanceof UnfazedError) ... }` and still\n * discriminate on the subclass to decide whether to retry.\n */\n\n/** A single entry from a GraphQL response's `errors` array. */\nexport interface GraphQLResponseError {\n message: string;\n path?: (string | number)[];\n extensions?: Record<string, unknown>;\n}\n\n/** Base class for everything this SDK throws. */\nexport class UnfazedError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/** No token was passed and no token environment variable was set. */\nexport class UnfazedAuthError extends UnfazedError {}\n\n/** The runtime has no global `fetch` and none was injected. */\nexport class UnfazedConfigError extends UnfazedError {}\n\n/**\n * The endpoint answered with a non-2xx status. `status` is the HTTP status and\n * `body` the response text, which is where the platform reports non-GraphQL\n * failures such as an expired or wrong-scope bearer token.\n */\nexport class UnfazedHTTPError extends UnfazedError {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string, url: string) {\n super(`unfazed: HTTP ${status} from ${url}${body ? `: ${truncate(body)}` : \"\"}`);\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * The endpoint answered 200 with a GraphQL `errors` array.\n *\n * Partial `data` is preserved deliberately: the batched writeFiles document\n * resolves each aliased field independently, so one path can fail while the\n * others commit, and the caller needs to see which landed.\n */\nexport class UnfazedGraphQLError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n readonly data: unknown;\n\n constructor(errors: GraphQLResponseError[], data: unknown) {\n const summary = errors.map((e) => e.message).join(\"; \") || \"unknown GraphQL error\";\n super(`unfazed: ${summary}`);\n this.errors = errors;\n this.data = data;\n }\n}\n\n/** A request exceeded `timeoutMs`, or the caller's AbortSignal fired. */\nexport class UnfazedTimeoutError extends UnfazedError {}\n\n/**\n * Waiting on a deploy ended badly.\n *\n * `reason` separates the two outcomes because they need different handling:\n * FAILED means the build finished and is broken (read `deployment.error` and\n * fix the source — retrying will not help), TIMEOUT means it is still BUILDING\n * and polling longer may still succeed.\n */\nexport class UnfazedDeployError extends UnfazedError {\n readonly reason: \"FAILED\" | \"TIMEOUT\";\n readonly deploymentId: string;\n /** Last deployment snapshot observed before giving up. */\n readonly deployment: unknown;\n\n constructor(\n reason: \"FAILED\" | \"TIMEOUT\",\n deploymentId: string,\n deployment: unknown,\n message: string,\n ) {\n super(message);\n this.reason = reason;\n this.deploymentId = deploymentId;\n this.deployment = deployment;\n }\n}\n\n/**\n * Waiting on a run timed out.\n *\n * Unlike {@link UnfazedDeployError}, a run's FAILED status is never thrown\n * here: {@link import(\"./unfazed.js\").Unfazed.waitForRun} resolves on\n * SUCCEEDED, FAILED and CANCELLED alike, because a run that executed and\n * exited non-zero is a completed answer — read `run.error`/`run.exitCode` —\n * not a broken build the way a FAILED deployment is. TIMEOUT is the only\n * outcome still worth an exception: the run is, as far as the last poll\n * showed, still QUEUED or RUNNING, and waiting longer may yet succeed.\n */\nexport class UnfazedRunError extends UnfazedError {\n readonly reason: \"TIMEOUT\";\n readonly runId: string;\n /** Last run snapshot observed before giving up. */\n readonly run: unknown;\n\n constructor(reason: \"TIMEOUT\", runId: string, run: unknown, message: string) {\n super(message);\n this.reason = reason;\n this.runId = runId;\n this.run = run;\n }\n}\n\n/**\n * A rollback's compare-and-swap guard was rejected: the alias moved between\n * reading the current release and swapping it.\n *\n * Recovery is always \"re-list releases and rebuild the guard\" — never \"reuse\n * the old tuple\", because the server checks the holder id, epoch AND opaque\n * revision to close same-id ABA. `Unfazed.rollback` does that re-list for you\n * once by default.\n */\nexport class UnfazedConflictError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n\n constructor(message: string, errors: GraphQLResponseError[]) {\n super(message);\n this.errors = errors;\n }\n}\n\n/** The requested alias, deployment or release does not exist (or was pruned). */\nexport class UnfazedNotFoundError extends UnfazedError {}\n\n/**\n * The server's exact \"no such workspace\" sentinel.\n *\n * A missing workspace and a workspace belonging to another tenant return the\n * SAME message on purpose, so the API cannot be used to probe for someone\n * else's ids. Treat it as \"not yours to see\", not strictly as \"absent\".\n */\nexport const WORKSPACE_NOT_FOUND = \"workspace was not found\";\n\n/** The server's exact \"no such deployment\" sentinel. See {@link WORKSPACE_NOT_FOUND}. */\nexport const DEPLOYMENT_NOT_FOUND = \"deployment was not found\";\n\n/**\n * Does this error carry EXACTLY the given not-found sentinel?\n *\n * Exact equality, deliberately. The tempting version — \"does any message\n * contain 'not found'\" — also swallows `release not found in history`,\n * `integration not found` and every future message that happens to use the\n * phrase, turning real failures into a silent `false` from a delete. These two\n * strings are stable authz sentinels with test coverage on the server side;\n * anything else is a genuine error and must keep throwing.\n */\nexport function isNotFoundSentinel(error: unknown, sentinel: string): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => e.message.trim() === sentinel)\n );\n}\n\n/**\n * Message shape of the server's per-owner duplicate workspace name error:\n * `a workspace named \"x\" already exists; reuse it (id <uuid>) or pick a\n * different name`.\n *\n * Used only to turn a lost `ensureWorkspace` race into a re-read. Requiring\n * both \"workspace\" and \"already exists\" in one clause keeps it off unrelated\n * conflicts (a duplicate ALIAS, say), and a false negative is safe: the\n * caller just gets the server's error verbatim.\n */\nconst DUPLICATE_WORKSPACE_NAME = /\\bworkspace\\b[^.;]{0,80}\\balready exists\\b/i;\n\n/** Did `createWorkspace` fail because the name is already taken by this owner? */\nexport function isDuplicateWorkspaceName(error: unknown): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => DUPLICATE_WORKSPACE_NAME.test(e.message))\n );\n}\n\nfunction truncate(s: string, max = 500): string {\n return s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n\n/**\n * Message shapes that mean \"the alias moved under you\".\n *\n * The first two are the platform's ACTUAL strings. A rejected rollback guard\n * returns `store.ErrAliasMoved` verbatim — every guard point in the rollback\n * and adopt paths returns that one sentinel unwrapped — and the staged-service\n * rollback wraps the same guard with its own prefix. The rest are defensive:\n * the guard ARGUMENTS named back at you, or the alias-revision concept in any\n * spelling, or `alias` sitting in the same clause as a conflict verb, so a\n * rewording on the server side does not silently disable the retry.\n *\n * Deliberately NOT matched: a bare \"conflict\", \"changed\" or \"stale\" anywhere in\n * any message. Those fire on errors that have nothing to do with the alias\n * compare-and-swap, and a false positive is not free — it spends a pointless\n * re-list AND rewraps an unrelated failure as {@link UnfazedConflictError},\n * whose message would then blame concurrent deploys for, say, a permission\n * problem.\n */\nconst CAS_PATTERNS: RegExp[] = [\n /alias was repointed by another change/i,\n /became stale before alias change/i,\n /expected[\\s_-]*current/i,\n /alias[\\s_-]*revision/i,\n /\\balias\\b[^.;]{0,80}\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b/i,\n /\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b[^.;]{0,80}\\balias\\b/i,\n];\n\n/**\n * Heuristic: does this GraphQL error set look like a rejected compare-and-swap?\n *\n * The server carries no machine-readable code in `extensions` — its error\n * presenter returns gqlgen's default untouched — so this has to match message\n * text. It is used ONLY to decide whether to re-list and retry once: a false\n * negative costs a surfaced error the caller can retry by hand, a false\n * positive costs an extra read and a misleading error class. Neither can\n * promote the wrong release, because the retry rebuilds the guard from a fresh\n * read and the server re-validates it.\n */\nexport function looksLikeCASConflict(errors: GraphQLResponseError[]): boolean {\n return errors.some((e) => CAS_PATTERNS.some((pattern) => pattern.test(e.message)));\n}\n"]}
@@ -130,3 +130,47 @@ export declare function graphql(source: "\n mutation SdkInheritAppEnv($workspac
130
130
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
131
131
  */
132
132
  export declare function graphql(source: "\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"): typeof import('./graphql.js').SdkListAppEnvDocument;
133
+ /**
134
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
135
+ */
136
+ export declare function graphql(source: "\n fragment RunFields on Run {\n id\n workspaceID\n ownerId\n status\n rawStatus\n command\n harness\n prompt\n setup\n skills\n verify\n approval\n mode\n parentRunID\n depth\n question {\n id\n text\n options\n askedAt\n }\n memoryMiB\n maxSeconds\n metadata\n error\n exitCode\n result {\n status\n detail\n text\n }\n verifyExitCode\n vmID\n transcriptAvailable\n usage {\n vmSeconds\n memoryMiB\n mibSeconds\n }\n createdAt\n startedAt\n settledAt\n }\n"): typeof import('./graphql.js').RunFieldsFragmentDoc;
137
+ /**
138
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
139
+ */
140
+ export declare function graphql(source: "\n mutation SdkCreateRun($input: CreateRunInput!) {\n createRun(input: $input) {\n ...RunFields\n }\n }\n"): typeof import('./graphql.js').SdkCreateRunDocument;
141
+ /**
142
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
143
+ */
144
+ export declare function graphql(source: "\n query SdkGetRun($id: ID!) {\n run(id: $id) {\n ...RunFields\n }\n }\n"): typeof import('./graphql.js').SdkGetRunDocument;
145
+ /**
146
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
147
+ */
148
+ export declare function graphql(source: "\n query SdkPollRun($id: ID!) {\n run(id: $id) {\n id\n status\n error\n exitCode\n }\n }\n"): typeof import('./graphql.js').SdkPollRunDocument;
149
+ /**
150
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
151
+ */
152
+ export declare function graphql(source: "\n query SdkListRuns($input: ListRunsInput) {\n runs(input: $input) {\n items {\n ...RunFields\n }\n total\n limit\n offset\n hasMore\n nextOffset\n }\n }\n"): typeof import('./graphql.js').SdkListRunsDocument;
153
+ /**
154
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
155
+ */
156
+ export declare function graphql(source: "\n mutation SdkCancelRun($id: ID!) {\n cancelRun(id: $id) {\n ...RunFields\n }\n }\n"): typeof import('./graphql.js').SdkCancelRunDocument;
157
+ /**
158
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
159
+ */
160
+ export declare function graphql(source: "\n query SdkRunEvents($runID: ID!, $limit: Int) {\n runEvents(runID: $runID, limit: $limit) {\n id\n runID\n type\n severity\n message\n metadata\n createdAt\n }\n }\n"): typeof import('./graphql.js').SdkRunEventsDocument;
161
+ /**
162
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
163
+ */
164
+ export declare function graphql(source: "\n query SdkRunConsole($id: ID!, $tailBytes: Int) {\n runConsole(id: $id, tailBytes: $tailBytes) {\n runID\n running\n text\n }\n }\n"): typeof import('./graphql.js').SdkRunConsoleDocument;
165
+ /**
166
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
167
+ */
168
+ export declare function graphql(source: "\n query SdkRunTranscript($id: ID!, $tailBytes: Int) {\n runTranscript(id: $id, tailBytes: $tailBytes) {\n runID\n available\n text\n totalBytes\n truncated\n }\n }\n"): typeof import('./graphql.js').SdkRunTranscriptDocument;
169
+ /**
170
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
171
+ */
172
+ export declare function graphql(source: "\n mutation SdkAnswerRun($id: ID!, $answer: String!) {\n answerRun(id: $id, answer: $answer) {\n ...RunFields\n }\n }\n"): typeof import('./graphql.js').SdkAnswerRunDocument;
173
+ /**
174
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
175
+ */
176
+ export declare function graphql(source: "\n mutation SdkFollowUpRun($input: FollowUpRunInput!) {\n followUpRun(input: $input) {\n ...RunFields\n }\n }\n"): typeof import('./graphql.js').SdkFollowUpRunDocument;
@@ -34,6 +34,17 @@ const documents = {
34
34
  "\n mutation SdkRemoveAppEnv($workspaceID: ID!, $alias: String!, $name: String!) {\n removeAppEnv(input: { workspaceID: $workspaceID, alias: $alias, name: $name }) {\n ok\n }\n }\n": types.SdkRemoveAppEnvDocument,
35
35
  "\n mutation SdkInheritAppEnv($workspaceID: ID!, $alias: String!, $name: String!) {\n inheritAppEnv(input: { workspaceID: $workspaceID, alias: $alias, name: $name }) {\n ok\n }\n }\n": types.SdkInheritAppEnvDocument,
36
36
  "\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": types.SdkListAppEnvDocument,
37
+ "\n fragment RunFields on Run {\n id\n workspaceID\n ownerId\n status\n rawStatus\n command\n harness\n prompt\n setup\n skills\n verify\n approval\n mode\n parentRunID\n depth\n question {\n id\n text\n options\n askedAt\n }\n memoryMiB\n maxSeconds\n metadata\n error\n exitCode\n result {\n status\n detail\n text\n }\n verifyExitCode\n vmID\n transcriptAvailable\n usage {\n vmSeconds\n memoryMiB\n mibSeconds\n }\n createdAt\n startedAt\n settledAt\n }\n": types.RunFieldsFragmentDoc,
38
+ "\n mutation SdkCreateRun($input: CreateRunInput!) {\n createRun(input: $input) {\n ...RunFields\n }\n }\n": types.SdkCreateRunDocument,
39
+ "\n query SdkGetRun($id: ID!) {\n run(id: $id) {\n ...RunFields\n }\n }\n": types.SdkGetRunDocument,
40
+ "\n query SdkPollRun($id: ID!) {\n run(id: $id) {\n id\n status\n error\n exitCode\n }\n }\n": types.SdkPollRunDocument,
41
+ "\n query SdkListRuns($input: ListRunsInput) {\n runs(input: $input) {\n items {\n ...RunFields\n }\n total\n limit\n offset\n hasMore\n nextOffset\n }\n }\n": types.SdkListRunsDocument,
42
+ "\n mutation SdkCancelRun($id: ID!) {\n cancelRun(id: $id) {\n ...RunFields\n }\n }\n": types.SdkCancelRunDocument,
43
+ "\n query SdkRunEvents($runID: ID!, $limit: Int) {\n runEvents(runID: $runID, limit: $limit) {\n id\n runID\n type\n severity\n message\n metadata\n createdAt\n }\n }\n": types.SdkRunEventsDocument,
44
+ "\n query SdkRunConsole($id: ID!, $tailBytes: Int) {\n runConsole(id: $id, tailBytes: $tailBytes) {\n runID\n running\n text\n }\n }\n": types.SdkRunConsoleDocument,
45
+ "\n query SdkRunTranscript($id: ID!, $tailBytes: Int) {\n runTranscript(id: $id, tailBytes: $tailBytes) {\n runID\n available\n text\n totalBytes\n truncated\n }\n }\n": types.SdkRunTranscriptDocument,
46
+ "\n mutation SdkAnswerRun($id: ID!, $answer: String!) {\n answerRun(id: $id, answer: $answer) {\n ...RunFields\n }\n }\n": types.SdkAnswerRunDocument,
47
+ "\n mutation SdkFollowUpRun($input: FollowUpRunInput!) {\n followUpRun(input: $input) {\n ...RunFields\n }\n }\n": types.SdkFollowUpRunDocument,
37
48
  };
38
49
  export function graphql(source) {
39
50
  return documents[source] ?? {};