unfazed 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +2 -2
- package/REFERENCE.md +2 -2
- package/dist/cli/login.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/eve-sandbox/daemon.d.ts +7 -1
- package/dist/eve-sandbox/daemon.js +27 -4
- package/dist/eve-sandbox/daemon.js.map +1 -1
- package/dist/eve-sandbox/index.d.ts +5 -0
- package/dist/eve-sandbox/index.js +18 -2
- package/dist/eve-sandbox/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/oidc.d.ts +3 -3
- package/dist/oidc.js +3 -3
- package/dist/oidc.js.map +1 -1
- package/dist/reference.generated.js +1 -1
- package/dist/reference.generated.js.map +1 -1
- package/package.json +1 -1
|
@@ -68,7 +68,23 @@ export class SandboxClient {
|
|
|
68
68
|
const signals = [AbortSignal.timeout(init.timeoutMs ?? CONTROL_TIMEOUT_MS), ...(init.signal ? [init.signal] : [])];
|
|
69
69
|
return await this.doFetch(`${this.base}${API_PREFIX}${path}`, { method, headers, body, signal: AbortSignal.any(signals) });
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* A box answering anything but the daemon's JSON (its URL serves the
|
|
73
|
+
* platform's placeholder page, or another app) has no sandbox daemon.
|
|
74
|
+
*/
|
|
75
|
+
notDaemon(response, what) {
|
|
76
|
+
const type = response.headers.get("content-type") ?? "";
|
|
77
|
+
if (type.includes("application/json") || type.includes("text/event-stream") || type.includes("application/octet-stream"))
|
|
78
|
+
return undefined;
|
|
79
|
+
return new UnfazedSandboxError(502, "daemon_not_running", `unfazed eve sandbox: ${what}: ${this.base} is not running the eve sandbox daemon (it answered ${response.status} ${type || "without a content type"}); ` +
|
|
80
|
+
"run `npx unfazed eve-sandbox setup` as root inside the box");
|
|
81
|
+
}
|
|
71
82
|
async ok(response, what) {
|
|
83
|
+
const foreign = this.notDaemon(response, what);
|
|
84
|
+
if (foreign) {
|
|
85
|
+
await response.body?.cancel().catch(() => undefined);
|
|
86
|
+
throw foreign;
|
|
87
|
+
}
|
|
72
88
|
if (response.ok)
|
|
73
89
|
return response;
|
|
74
90
|
let code = "http_" + response.status;
|
|
@@ -90,7 +106,7 @@ export class SandboxClient {
|
|
|
90
106
|
/** The session, or `null` when the box no longer has it. */
|
|
91
107
|
async get(sessionId) {
|
|
92
108
|
const res = await this.request("GET", `/sessions/${encodeURIComponent(sessionId)}`);
|
|
93
|
-
if (res.status === 410)
|
|
109
|
+
if (res.status === 410 && (res.headers.get("content-type") ?? "").includes("application/json"))
|
|
94
110
|
return null;
|
|
95
111
|
return (await (await this.ok(res, "resume the sandbox")).json());
|
|
96
112
|
}
|
|
@@ -217,7 +233,7 @@ function connect(client, sessionId, session) {
|
|
|
217
233
|
},
|
|
218
234
|
async readFile({ path, signal }) {
|
|
219
235
|
const res = await client.request("GET", `/sessions/${sid}/file?path=${encodeURIComponent(path)}`, { signal }).catch((e) => gone(e, sessionId));
|
|
220
|
-
if (res.status === 404)
|
|
236
|
+
if (res.status === 404 && (res.headers.get("content-type") ?? "").includes("application/json"))
|
|
221
237
|
return null;
|
|
222
238
|
await client.ok(res, `read ${path}`).catch((e) => gone(e, sessionId));
|
|
223
239
|
return new Uint8Array(await res.arrayBuffer());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eve-sandbox/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `unfazed/eve-sandbox` — eve sandboxes backed by an unfazed box.\n *\n * ```ts title=\"agent/sandbox.ts\"\n * import { defineSandbox } from \"eve/sandbox\";\n * import { UnfazedSandbox } from \"unfazed/eve-sandbox\";\n *\n * export const environment = UnfazedSandbox.environment({ box: \"my-sandbox-box\" });\n * export default defineSandbox(() => environment.open());\n * ```\n *\n * The box runs the sandbox daemon (`unfazed eve-sandbox setup` inside it).\n * Each eve session gets its own Unix user, home and workspace on the box; the\n * box keeps them across restarts and redeploys until the session ends. The\n * app authenticates with its workload identity token (audience: the box's\n * URL), so it needs no stored secret; the box's allow list names the apps it\n * serves.\n *\n * Requires the optional peer `eve` (0.90 or newer).\n */\n\nimport type { SandboxSession } from \"eve/sandbox\";\nimport {\n defineSandboxProvider,\n SandboxSessionGoneError,\n type SandboxConnection,\n type SandboxPrepareContext,\n type SandboxProvider,\n type SandboxProviderDefinition,\n type WithPrepareOption,\n} from \"eve/sandbox/provider\";\nimport { oidcToken } from \"../oidc.js\";\nimport {\n API_PREFIX,\n TEMPLATE_HOME,\n TEMPLATE_WORKSPACE,\n type ProcessEvent,\n type SessionInfo,\n type WireFile,\n} from \"./protocol.js\";\n\nexport { TEMPLATE_HOME, TEMPLATE_WORKSPACE } from \"./protocol.js\";\n\n/** Options of `UnfazedSandbox.environment()`. */\nexport interface UnfazedSandboxOptions {\n /** The box's alias; its URL is `https://<box>.unfz.to`. */\n readonly box?: string;\n /** The box's URL, instead of `box`. */\n readonly url?: string;\n /** Token audience; the box's URL by default. */\n readonly audience?: string;\n /** Supplies the bearer token; the app's workload identity token by default. */\n readonly token?: (audience: string) => Promise<string>;\n /** `fetch` used for every call (tests). */\n readonly fetch?: FetchLike;\n}\n\n/** The subset of `fetch` the client uses. */\nexport type FetchLike = (input: string, init: RequestInit) => Promise<Response>;\n\n/** The artifact `prepare()` ships with the deployment: the template files. */\nexport type UnfazedSandboxArtifact = {\n readonly v: 1;\n readonly files: readonly WireFile[];\n};\n\n/** What eve checkpoints for a session. */\nexport type UnfazedSandboxState = {\n readonly v: 1;\n readonly sessionId: string;\n readonly workspace: string;\n};\n\n/** Template files larger than this in total belong on the box, not in the artifact. */\nconst MAX_TEMPLATE_BYTES = 8 * 1024 * 1024;\n/** One `writeFiles` request carries at most this much content. */\nconst MAX_WRITE_BATCH = 16 * 1024 * 1024;\nconst CONTROL_TIMEOUT_MS = 60_000;\n\nexport class UnfazedSandboxError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = \"UnfazedSandboxError\";\n }\n}\n\nfunction boxUrl(options: UnfazedSandboxOptions): string {\n if (options.url) return options.url.replace(/\\/+$/, \"\");\n if (options.box) {\n if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(options.box)) throw new Error(`unfazed eve sandbox: invalid box alias \"${options.box}\"`);\n return `https://${options.box}.unfz.to`;\n }\n throw new Error(\"unfazed eve sandbox: pass { box } or { url }\");\n}\n\nconst toB64 = (bytes: Uint8Array) => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString(\"base64\");\n\n/** The daemon's HTTP API, one per environment. */\nexport class SandboxClient {\n private readonly doFetch: FetchLike;\n\n constructor(\n readonly base: string,\n private readonly bearer: () => Promise<string>,\n doFetch?: FetchLike,\n ) {\n this.doFetch = doFetch ?? ((input, init) => fetch(input, init));\n }\n\n async request(method: string, path: string, init: { body?: BodyInit; json?: unknown; signal?: AbortSignal; timeoutMs?: number } = {}): Promise<Response> {\n const headers: Record<string, string> = { authorization: `Bearer ${await this.bearer()}` };\n let body = init.body;\n if (init.json !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(init.json);\n }\n const signals = [AbortSignal.timeout(init.timeoutMs ?? CONTROL_TIMEOUT_MS), ...(init.signal ? [init.signal] : [])];\n return await this.doFetch(`${this.base}${API_PREFIX}${path}`, { method, headers, body, signal: AbortSignal.any(signals) });\n }\n\n async ok(response: Response, what: string): Promise<Response> {\n if (response.ok) return response;\n let code = \"http_\" + response.status;\n let message = \"\";\n try {\n const body = (await response.json()) as { error?: string; message?: string };\n code = body.error ?? code;\n message = body.message ?? \"\";\n } catch {\n // not JSON\n }\n throw new UnfazedSandboxError(response.status, code, `unfazed eve sandbox: ${what}: ${response.status} ${code}${message ? `: ${message}` : \"\"}`);\n }\n\n async ensure(sessionId: string, template: readonly WireFile[]): Promise<SessionInfo> {\n const res = await this.request(\"PUT\", `/sessions/${encodeURIComponent(sessionId)}`, { json: { template } });\n return (await (await this.ok(res, \"start the sandbox\")).json()) as SessionInfo;\n }\n\n /** The session, or `null` when the box no longer has it. */\n async get(sessionId: string): Promise<SessionInfo | null> {\n const res = await this.request(\"GET\", `/sessions/${encodeURIComponent(sessionId)}`);\n if (res.status === 410) return null;\n return (await (await this.ok(res, \"resume the sandbox\")).json()) as SessionInfo;\n }\n\n async delete(sessionId: string, signal?: AbortSignal): Promise<void> {\n const res = await this.request(\"DELETE\", `/sessions/${encodeURIComponent(sessionId)}`, { signal });\n await this.ok(res, \"delete the sandbox\");\n }\n}\n\n/** A sandbox session that is gone answers 410: eve then starts a fresh one. */\nfunction gone(error: unknown, sessionId: string): never {\n if (error instanceof UnfazedSandboxError && error.status === 410) {\n throw new SandboxSessionGoneError(`unfazed sandbox for session ${sessionId} is gone`);\n }\n throw error;\n}\n\n/** Reads a process's event stream, reconnecting after a dropped connection. */\nasync function followProcess(\n client: SandboxClient,\n id: string,\n onEvent: (event: ProcessEvent) => void,\n signal: AbortSignal,\n): Promise<number> {\n let after = 0;\n let failures = 0;\n for (;;) {\n try {\n const res = await client.ok(\n await client.request(\"GET\", `/processes/${id}/events?after=${after}`, { signal, timeoutMs: 24 * 60 * 60 * 1000 }),\n \"read process output\",\n );\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let cut: number;\n while ((cut = buffer.indexOf(\"\\n\\n\")) >= 0) {\n const frame = buffer.slice(0, cut);\n buffer = buffer.slice(cut + 2);\n for (const line of frame.split(\"\\n\")) {\n if (!line.startsWith(\"data: \")) continue;\n const event = JSON.parse(line.slice(6)) as ProcessEvent;\n after = event.s;\n failures = 0;\n onEvent(event);\n if (\"x\" in event) return event.x;\n }\n }\n }\n } catch (error) {\n if (signal.aborted) throw error;\n if (error instanceof UnfazedSandboxError && error.status < 500) throw error;\n if (++failures > 8) throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, Math.min(250 * 2 ** failures, 5000)));\n }\n}\n\nfunction connect(client: SandboxClient, sessionId: string, session: SessionInfo): SandboxConnection {\n const sid = encodeURIComponent(sessionId);\n return {\n workspaceRoot: session.workspace,\n\n async spawn({ command, env, workingDirectory, stdin, signal }) {\n const res = await client\n .request(\"POST\", `/sessions/${sid}/processes`, { json: { command, cwd: workingDirectory, env, stdin: stdin === true }, signal })\n .then((r) => client.ok(r, \"start a command\"))\n .catch((error) => gone(error, sessionId));\n const { id, pid } = (await res.json()) as { id: string; pid?: number };\n const kill = async () => {\n await client.request(\"POST\", `/processes/${id}/kill`).catch(() => undefined);\n };\n const follow = new AbortController();\n let out!: ReadableStreamDefaultController<Uint8Array>;\n let err!: ReadableStreamDefaultController<Uint8Array>;\n const stdout = new ReadableStream<Uint8Array>({ start: (c) => void (out = c) });\n const stderr = new ReadableStream<Uint8Array>({ start: (c) => void (err = c) });\n const closeStreams = (error?: unknown) => {\n for (const c of [out, err]) {\n try {\n error === undefined ? c.close() : c.error(error);\n } catch {\n // already closed\n }\n }\n };\n const exited = followProcess(\n client,\n id,\n (event) => {\n if (\"d\" in event) (event.f === 1 ? out : err).enqueue(new Uint8Array(Buffer.from(event.d, \"base64\")));\n },\n follow.signal,\n ).then(\n (exitCode) => {\n closeStreams();\n return { exitCode };\n },\n (error) => {\n closeStreams(error);\n throw error;\n },\n );\n if (signal) {\n const onAbort = () => void kill();\n if (signal.aborted) onAbort();\n else signal.addEventListener(\"abort\", onAbort, { once: true });\n void exited.finally(() => signal.removeEventListener(\"abort\", onAbort)).catch(() => undefined);\n }\n let pending = Promise.resolve();\n const input = stdin\n ? new WritableStream<Uint8Array>({\n write: (chunk) =>\n (pending = pending.then(async () => {\n await client.ok(await client.request(\"POST\", `/processes/${id}/stdin`, { body: new Uint8Array(chunk) }), \"write stdin\");\n })),\n close: () =>\n (pending = pending.then(async () => {\n await client.ok(await client.request(\"POST\", `/processes/${id}/stdin?eof=1`, { body: new Uint8Array() }), \"close stdin\");\n })),\n })\n : undefined;\n return {\n pid,\n stdin: input,\n stdout,\n stderr,\n wait: () => exited,\n kill,\n };\n },\n\n async readFile({ path, signal }) {\n const res = await client.request(\"GET\", `/sessions/${sid}/file?path=${encodeURIComponent(path)}`, { signal }).catch((e) => gone(e, sessionId));\n if (res.status === 404) return null;\n await client.ok(res, `read ${path}`).catch((e) => gone(e, sessionId));\n return new Uint8Array(await res.arrayBuffer());\n },\n\n async writeFiles({ files, signal }) {\n let batch: WireFile[] = [];\n let size = 0;\n const send = async () => {\n if (batch.length === 0) return;\n await client\n .ok(await client.request(\"POST\", `/sessions/${sid}/files`, { json: { files: batch }, signal }), \"write files\")\n .catch((e) => gone(e, sessionId));\n batch = [];\n size = 0;\n };\n for (const file of files) {\n if (size + file.content.byteLength > MAX_WRITE_BATCH) await send();\n batch.push({ path: file.path, b64: toB64(file.content) });\n size += file.content.byteLength;\n }\n await send();\n },\n\n async removePath({ path, force, recursive, signal }) {\n await client\n .ok(await client.request(\"POST\", `/sessions/${sid}/remove`, { json: { path, force: force === true, recursive: recursive === true }, signal }), `remove ${path}`)\n .catch((e) => gone(e, sessionId));\n },\n\n async stop() {\n const res = await client.request(\"POST\", `/sessions/${sid}/stop`);\n if (res.status === 410) return;\n await client.ok(res, \"stop the sandbox\");\n },\n\n // The box keeps the session's processes (an open browser) between steps;\n // its daemon stops a session's processes after it has been idle.\n async close() {},\n };\n}\n\n/**\n * Captures the workspace and skill files `ctx.populate()` writes at build\n * time, so every new session starts from them. A build-time command other\n * than eve's `$HOME` probe is refused: install tools on the box instead.\n */\nasync function captureTemplate(ctx: SandboxPrepareContext): Promise<WireFile[]> {\n const files = new Map<string, Uint8Array>();\n const empty = () => new ReadableStream<Uint8Array>({ start: (c) => c.close() });\n const capture: SandboxConnection = {\n workspaceRoot: TEMPLATE_WORKSPACE,\n async spawn({ command }) {\n if (command.trim() !== `printf '%s\\\\n' \"$HOME\"`) {\n throw new Error(\n \"unfazed eve sandbox: commands in prepare() do not run at build time; install tools on the box itself (see `unfazed eve-sandbox setup`)\",\n );\n }\n return {\n stdout: new ReadableStream<Uint8Array>({\n start(c) {\n c.enqueue(new TextEncoder().encode(`${TEMPLATE_HOME}\\n`));\n c.close();\n },\n }),\n stderr: empty(),\n wait: async () => ({ exitCode: 0 }),\n kill: async () => {},\n };\n },\n async readFile({ path }) {\n return files.get(path) ?? null;\n },\n async writeFiles({ files: written }) {\n for (const file of written) files.set(file.path, file.content);\n },\n async removePath({ path, recursive }) {\n files.delete(path);\n if (recursive) for (const key of [...files.keys()]) if (key.startsWith(`${path}/`)) files.delete(key);\n },\n async stop() {},\n };\n await ctx.populate(capture);\n let total = 0;\n const out: WireFile[] = [];\n for (const [path, content] of files) {\n total += content.byteLength;\n out.push({ path, b64: toB64(content) });\n }\n if (total > MAX_TEMPLATE_BYTES) {\n throw new Error(`unfazed eve sandbox: the workspace and skill files are ${total} bytes; at most ${MAX_TEMPLATE_BYTES} fit in a template`);\n }\n return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/** The provider's implementation (for tests); eve apps use {@link UnfazedSandbox}. */\nexport const unfazedSandboxDefinition: SandboxProviderDefinition<\n UnfazedSandboxOptions,\n undefined,\n UnfazedSandboxArtifact,\n UnfazedSandboxState,\n {}\n> = {\n name: \"unfazed\",\n stateProtocolVersion: 1,\n environment(options: UnfazedSandboxOptions) {\n const base = boxUrl(options);\n const audience = options.audience ?? base;\n const token = options.token ?? ((aud: string) => oidcToken(aud));\n const client = new SandboxClient(base, () => token(audience), options.fetch);\n return {\n async prepare(ctx): Promise<UnfazedSandboxArtifact> {\n return { v: 1, files: ctx.isEmpty ? [] : await captureTemplate(ctx) };\n },\n async start(ctx, _open: undefined, artifact: Readonly<UnfazedSandboxArtifact>) {\n const session = await client.ensure(ctx.session.id, artifact?.files ?? []);\n const state: UnfazedSandboxState = { v: 1, sessionId: ctx.session.id, workspace: session.workspace };\n return { connection: connect(client, ctx.session.id, session), state };\n },\n async resume(ctx, _artifact: Readonly<UnfazedSandboxArtifact>, state: Readonly<UnfazedSandboxState>) {\n const sessionId = typeof state?.sessionId === \"string\" ? state.sessionId : ctx.session.id;\n const session = await client.get(sessionId);\n if (session === null) throw new SandboxSessionGoneError(`unfazed sandbox for session ${sessionId} is gone`);\n return connect(client, sessionId, session);\n },\n async onSessionEnd(ctx, state: Readonly<UnfazedSandboxState> | null, { signal }) {\n const sessionId = typeof state?.sessionId === \"string\" ? state.sessionId : ctx.session.id;\n await client.delete(sessionId, signal);\n },\n // Keyed MCP tool sessions keep their sandbox between calls; the box\n // deletes a sandbox nobody used for its retention period.\n toolSessions: true as const,\n };\n },\n};\n\n/** The eve sandbox provider backed by an unfazed box. */\nexport const UnfazedSandbox: SandboxProvider<WithPrepareOption<UnfazedSandboxOptions, SandboxSession>, undefined, SandboxSession> =\n defineSandboxProvider(unfazedSandboxDefinition);\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eve-sandbox/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `unfazed/eve-sandbox` — eve sandboxes backed by an unfazed box.\n *\n * ```ts title=\"agent/sandbox.ts\"\n * import { defineSandbox } from \"eve/sandbox\";\n * import { UnfazedSandbox } from \"unfazed/eve-sandbox\";\n *\n * export const environment = UnfazedSandbox.environment({ box: \"my-sandbox-box\" });\n * export default defineSandbox(() => environment.open());\n * ```\n *\n * The box runs the sandbox daemon (`unfazed eve-sandbox setup` inside it).\n * Each eve session gets its own Unix user, home and workspace on the box; the\n * box keeps them across restarts and redeploys until the session ends. The\n * app authenticates with its workload identity token (audience: the box's\n * URL), so it needs no stored secret; the box's allow list names the apps it\n * serves.\n *\n * Requires the optional peer `eve` (0.90 or newer).\n */\n\nimport type { SandboxSession } from \"eve/sandbox\";\nimport {\n defineSandboxProvider,\n SandboxSessionGoneError,\n type SandboxConnection,\n type SandboxPrepareContext,\n type SandboxProvider,\n type SandboxProviderDefinition,\n type WithPrepareOption,\n} from \"eve/sandbox/provider\";\nimport { oidcToken } from \"../oidc.js\";\nimport {\n API_PREFIX,\n TEMPLATE_HOME,\n TEMPLATE_WORKSPACE,\n type ProcessEvent,\n type SessionInfo,\n type WireFile,\n} from \"./protocol.js\";\n\nexport { TEMPLATE_HOME, TEMPLATE_WORKSPACE } from \"./protocol.js\";\n\n/** Options of `UnfazedSandbox.environment()`. */\nexport interface UnfazedSandboxOptions {\n /** The box's alias; its URL is `https://<box>.unfz.to`. */\n readonly box?: string;\n /** The box's URL, instead of `box`. */\n readonly url?: string;\n /** Token audience; the box's URL by default. */\n readonly audience?: string;\n /** Supplies the bearer token; the app's workload identity token by default. */\n readonly token?: (audience: string) => Promise<string>;\n /** `fetch` used for every call (tests). */\n readonly fetch?: FetchLike;\n}\n\n/** The subset of `fetch` the client uses. */\nexport type FetchLike = (input: string, init: RequestInit) => Promise<Response>;\n\n/** The artifact `prepare()` ships with the deployment: the template files. */\nexport type UnfazedSandboxArtifact = {\n readonly v: 1;\n readonly files: readonly WireFile[];\n};\n\n/** What eve checkpoints for a session. */\nexport type UnfazedSandboxState = {\n readonly v: 1;\n readonly sessionId: string;\n readonly workspace: string;\n};\n\n/** Template files larger than this in total belong on the box, not in the artifact. */\nconst MAX_TEMPLATE_BYTES = 8 * 1024 * 1024;\n/** One `writeFiles` request carries at most this much content. */\nconst MAX_WRITE_BATCH = 16 * 1024 * 1024;\nconst CONTROL_TIMEOUT_MS = 60_000;\n\nexport class UnfazedSandboxError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = \"UnfazedSandboxError\";\n }\n}\n\nfunction boxUrl(options: UnfazedSandboxOptions): string {\n if (options.url) return options.url.replace(/\\/+$/, \"\");\n if (options.box) {\n if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(options.box)) throw new Error(`unfazed eve sandbox: invalid box alias \"${options.box}\"`);\n return `https://${options.box}.unfz.to`;\n }\n throw new Error(\"unfazed eve sandbox: pass { box } or { url }\");\n}\n\nconst toB64 = (bytes: Uint8Array) => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString(\"base64\");\n\n/** The daemon's HTTP API, one per environment. */\nexport class SandboxClient {\n private readonly doFetch: FetchLike;\n\n constructor(\n readonly base: string,\n private readonly bearer: () => Promise<string>,\n doFetch?: FetchLike,\n ) {\n this.doFetch = doFetch ?? ((input, init) => fetch(input, init));\n }\n\n async request(method: string, path: string, init: { body?: BodyInit; json?: unknown; signal?: AbortSignal; timeoutMs?: number } = {}): Promise<Response> {\n const headers: Record<string, string> = { authorization: `Bearer ${await this.bearer()}` };\n let body = init.body;\n if (init.json !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(init.json);\n }\n const signals = [AbortSignal.timeout(init.timeoutMs ?? CONTROL_TIMEOUT_MS), ...(init.signal ? [init.signal] : [])];\n return await this.doFetch(`${this.base}${API_PREFIX}${path}`, { method, headers, body, signal: AbortSignal.any(signals) });\n }\n\n /**\n * A box answering anything but the daemon's JSON (its URL serves the\n * platform's placeholder page, or another app) has no sandbox daemon.\n */\n private notDaemon(response: Response, what: string): UnfazedSandboxError | undefined {\n const type = response.headers.get(\"content-type\") ?? \"\";\n if (type.includes(\"application/json\") || type.includes(\"text/event-stream\") || type.includes(\"application/octet-stream\")) return undefined;\n return new UnfazedSandboxError(\n 502,\n \"daemon_not_running\",\n `unfazed eve sandbox: ${what}: ${this.base} is not running the eve sandbox daemon (it answered ${response.status} ${type || \"without a content type\"}); ` +\n \"run `npx unfazed eve-sandbox setup` as root inside the box\",\n );\n }\n\n async ok(response: Response, what: string): Promise<Response> {\n const foreign = this.notDaemon(response, what);\n if (foreign) {\n await response.body?.cancel().catch(() => undefined);\n throw foreign;\n }\n if (response.ok) return response;\n let code = \"http_\" + response.status;\n let message = \"\";\n try {\n const body = (await response.json()) as { error?: string; message?: string };\n code = body.error ?? code;\n message = body.message ?? \"\";\n } catch {\n // not JSON\n }\n throw new UnfazedSandboxError(response.status, code, `unfazed eve sandbox: ${what}: ${response.status} ${code}${message ? `: ${message}` : \"\"}`);\n }\n\n async ensure(sessionId: string, template: readonly WireFile[]): Promise<SessionInfo> {\n const res = await this.request(\"PUT\", `/sessions/${encodeURIComponent(sessionId)}`, { json: { template } });\n return (await (await this.ok(res, \"start the sandbox\")).json()) as SessionInfo;\n }\n\n /** The session, or `null` when the box no longer has it. */\n async get(sessionId: string): Promise<SessionInfo | null> {\n const res = await this.request(\"GET\", `/sessions/${encodeURIComponent(sessionId)}`);\n if (res.status === 410 && (res.headers.get(\"content-type\") ?? \"\").includes(\"application/json\")) return null;\n return (await (await this.ok(res, \"resume the sandbox\")).json()) as SessionInfo;\n }\n\n async delete(sessionId: string, signal?: AbortSignal): Promise<void> {\n const res = await this.request(\"DELETE\", `/sessions/${encodeURIComponent(sessionId)}`, { signal });\n await this.ok(res, \"delete the sandbox\");\n }\n}\n\n/** A sandbox session that is gone answers 410: eve then starts a fresh one. */\nfunction gone(error: unknown, sessionId: string): never {\n if (error instanceof UnfazedSandboxError && error.status === 410) {\n throw new SandboxSessionGoneError(`unfazed sandbox for session ${sessionId} is gone`);\n }\n throw error;\n}\n\n/** Reads a process's event stream, reconnecting after a dropped connection. */\nasync function followProcess(\n client: SandboxClient,\n id: string,\n onEvent: (event: ProcessEvent) => void,\n signal: AbortSignal,\n): Promise<number> {\n let after = 0;\n let failures = 0;\n for (;;) {\n try {\n const res = await client.ok(\n await client.request(\"GET\", `/processes/${id}/events?after=${after}`, { signal, timeoutMs: 24 * 60 * 60 * 1000 }),\n \"read process output\",\n );\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let cut: number;\n while ((cut = buffer.indexOf(\"\\n\\n\")) >= 0) {\n const frame = buffer.slice(0, cut);\n buffer = buffer.slice(cut + 2);\n for (const line of frame.split(\"\\n\")) {\n if (!line.startsWith(\"data: \")) continue;\n const event = JSON.parse(line.slice(6)) as ProcessEvent;\n after = event.s;\n failures = 0;\n onEvent(event);\n if (\"x\" in event) return event.x;\n }\n }\n }\n } catch (error) {\n if (signal.aborted) throw error;\n if (error instanceof UnfazedSandboxError && error.status < 500) throw error;\n if (++failures > 8) throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, Math.min(250 * 2 ** failures, 5000)));\n }\n}\n\nfunction connect(client: SandboxClient, sessionId: string, session: SessionInfo): SandboxConnection {\n const sid = encodeURIComponent(sessionId);\n return {\n workspaceRoot: session.workspace,\n\n async spawn({ command, env, workingDirectory, stdin, signal }) {\n const res = await client\n .request(\"POST\", `/sessions/${sid}/processes`, { json: { command, cwd: workingDirectory, env, stdin: stdin === true }, signal })\n .then((r) => client.ok(r, \"start a command\"))\n .catch((error) => gone(error, sessionId));\n const { id, pid } = (await res.json()) as { id: string; pid?: number };\n const kill = async () => {\n await client.request(\"POST\", `/processes/${id}/kill`).catch(() => undefined);\n };\n const follow = new AbortController();\n let out!: ReadableStreamDefaultController<Uint8Array>;\n let err!: ReadableStreamDefaultController<Uint8Array>;\n const stdout = new ReadableStream<Uint8Array>({ start: (c) => void (out = c) });\n const stderr = new ReadableStream<Uint8Array>({ start: (c) => void (err = c) });\n const closeStreams = (error?: unknown) => {\n for (const c of [out, err]) {\n try {\n error === undefined ? c.close() : c.error(error);\n } catch {\n // already closed\n }\n }\n };\n const exited = followProcess(\n client,\n id,\n (event) => {\n if (\"d\" in event) (event.f === 1 ? out : err).enqueue(new Uint8Array(Buffer.from(event.d, \"base64\")));\n },\n follow.signal,\n ).then(\n (exitCode) => {\n closeStreams();\n return { exitCode };\n },\n (error) => {\n closeStreams(error);\n throw error;\n },\n );\n if (signal) {\n const onAbort = () => void kill();\n if (signal.aborted) onAbort();\n else signal.addEventListener(\"abort\", onAbort, { once: true });\n void exited.finally(() => signal.removeEventListener(\"abort\", onAbort)).catch(() => undefined);\n }\n let pending = Promise.resolve();\n const input = stdin\n ? new WritableStream<Uint8Array>({\n write: (chunk) =>\n (pending = pending.then(async () => {\n await client.ok(await client.request(\"POST\", `/processes/${id}/stdin`, { body: new Uint8Array(chunk) }), \"write stdin\");\n })),\n close: () =>\n (pending = pending.then(async () => {\n await client.ok(await client.request(\"POST\", `/processes/${id}/stdin?eof=1`, { body: new Uint8Array() }), \"close stdin\");\n })),\n })\n : undefined;\n return {\n pid,\n stdin: input,\n stdout,\n stderr,\n wait: () => exited,\n kill,\n };\n },\n\n async readFile({ path, signal }) {\n const res = await client.request(\"GET\", `/sessions/${sid}/file?path=${encodeURIComponent(path)}`, { signal }).catch((e) => gone(e, sessionId));\n if (res.status === 404 && (res.headers.get(\"content-type\") ?? \"\").includes(\"application/json\")) return null;\n await client.ok(res, `read ${path}`).catch((e) => gone(e, sessionId));\n return new Uint8Array(await res.arrayBuffer());\n },\n\n async writeFiles({ files, signal }) {\n let batch: WireFile[] = [];\n let size = 0;\n const send = async () => {\n if (batch.length === 0) return;\n await client\n .ok(await client.request(\"POST\", `/sessions/${sid}/files`, { json: { files: batch }, signal }), \"write files\")\n .catch((e) => gone(e, sessionId));\n batch = [];\n size = 0;\n };\n for (const file of files) {\n if (size + file.content.byteLength > MAX_WRITE_BATCH) await send();\n batch.push({ path: file.path, b64: toB64(file.content) });\n size += file.content.byteLength;\n }\n await send();\n },\n\n async removePath({ path, force, recursive, signal }) {\n await client\n .ok(await client.request(\"POST\", `/sessions/${sid}/remove`, { json: { path, force: force === true, recursive: recursive === true }, signal }), `remove ${path}`)\n .catch((e) => gone(e, sessionId));\n },\n\n async stop() {\n const res = await client.request(\"POST\", `/sessions/${sid}/stop`);\n if (res.status === 410) return;\n await client.ok(res, \"stop the sandbox\");\n },\n\n // The box keeps the session's processes (an open browser) between steps;\n // its daemon stops a session's processes after it has been idle.\n async close() {},\n };\n}\n\n/**\n * Captures the workspace and skill files `ctx.populate()` writes at build\n * time, so every new session starts from them. A build-time command other\n * than eve's `$HOME` probe is refused: install tools on the box instead.\n */\nasync function captureTemplate(ctx: SandboxPrepareContext): Promise<WireFile[]> {\n const files = new Map<string, Uint8Array>();\n const empty = () => new ReadableStream<Uint8Array>({ start: (c) => c.close() });\n const capture: SandboxConnection = {\n workspaceRoot: TEMPLATE_WORKSPACE,\n async spawn({ command }) {\n if (command.trim() !== `printf '%s\\\\n' \"$HOME\"`) {\n throw new Error(\n \"unfazed eve sandbox: commands in prepare() do not run at build time; install tools on the box itself (see `unfazed eve-sandbox setup`)\",\n );\n }\n return {\n stdout: new ReadableStream<Uint8Array>({\n start(c) {\n c.enqueue(new TextEncoder().encode(`${TEMPLATE_HOME}\\n`));\n c.close();\n },\n }),\n stderr: empty(),\n wait: async () => ({ exitCode: 0 }),\n kill: async () => {},\n };\n },\n async readFile({ path }) {\n return files.get(path) ?? null;\n },\n async writeFiles({ files: written }) {\n for (const file of written) files.set(file.path, file.content);\n },\n async removePath({ path, recursive }) {\n files.delete(path);\n if (recursive) for (const key of [...files.keys()]) if (key.startsWith(`${path}/`)) files.delete(key);\n },\n async stop() {},\n };\n await ctx.populate(capture);\n let total = 0;\n const out: WireFile[] = [];\n for (const [path, content] of files) {\n total += content.byteLength;\n out.push({ path, b64: toB64(content) });\n }\n if (total > MAX_TEMPLATE_BYTES) {\n throw new Error(`unfazed eve sandbox: the workspace and skill files are ${total} bytes; at most ${MAX_TEMPLATE_BYTES} fit in a template`);\n }\n return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/** The provider's implementation (for tests); eve apps use {@link UnfazedSandbox}. */\nexport const unfazedSandboxDefinition: SandboxProviderDefinition<\n UnfazedSandboxOptions,\n undefined,\n UnfazedSandboxArtifact,\n UnfazedSandboxState,\n {}\n> = {\n name: \"unfazed\",\n stateProtocolVersion: 1,\n environment(options: UnfazedSandboxOptions) {\n const base = boxUrl(options);\n const audience = options.audience ?? base;\n const token = options.token ?? ((aud: string) => oidcToken(aud));\n const client = new SandboxClient(base, () => token(audience), options.fetch);\n return {\n async prepare(ctx): Promise<UnfazedSandboxArtifact> {\n return { v: 1, files: ctx.isEmpty ? [] : await captureTemplate(ctx) };\n },\n async start(ctx, _open: undefined, artifact: Readonly<UnfazedSandboxArtifact>) {\n const session = await client.ensure(ctx.session.id, artifact?.files ?? []);\n const state: UnfazedSandboxState = { v: 1, sessionId: ctx.session.id, workspace: session.workspace };\n return { connection: connect(client, ctx.session.id, session), state };\n },\n async resume(ctx, _artifact: Readonly<UnfazedSandboxArtifact>, state: Readonly<UnfazedSandboxState>) {\n const sessionId = typeof state?.sessionId === \"string\" ? state.sessionId : ctx.session.id;\n const session = await client.get(sessionId);\n if (session === null) throw new SandboxSessionGoneError(`unfazed sandbox for session ${sessionId} is gone`);\n return connect(client, sessionId, session);\n },\n async onSessionEnd(ctx, state: Readonly<UnfazedSandboxState> | null, { signal }) {\n const sessionId = typeof state?.sessionId === \"string\" ? state.sessionId : ctx.session.id;\n await client.delete(sessionId, signal);\n },\n // Keyed MCP tool sessions keep their sandbox between calls; the box\n // deletes a sandbox nobody used for its retention period.\n toolSessions: true as const,\n };\n },\n};\n\n/** The eve sandbox provider backed by an unfazed box. */\nexport const UnfazedSandbox: SandboxProvider<WithPrepareOption<UnfazedSandboxOptions, SandboxSession>, undefined, SandboxSession> =\n defineSandboxProvider(unfazedSandboxDefinition);\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export type { ClientOptions, FetchLike, GraphQLDocument, RequestOptions, ResultO
|
|
|
44
44
|
* ```ts
|
|
45
45
|
* import { oidc, oidcToken } from "unfazed";
|
|
46
46
|
* const jwt = await oidcToken("https://api.example.com");
|
|
47
|
-
* const fetch = oidc.fetchWithToken(process.env.
|
|
47
|
+
* const fetch = oidc.fetchWithToken(process.env.API_AUDIENCE);
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
export { OIDC_TOKEN_PATH, STATE_TOKEN_ENV_VAR, UnfazedOIDC, oidc, oidcToken } from "./oidc.js";
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ export { UnfazedClient, DEFAULT_BASE_URL, DEFAULT_ENDPOINT, ENDPOINT_ENV_VAR, GR
|
|
|
41
41
|
* ```ts
|
|
42
42
|
* import { oidc, oidcToken } from "unfazed";
|
|
43
43
|
* const jwt = await oidcToken("https://api.example.com");
|
|
44
|
-
* const fetch = oidc.fetchWithToken(process.env.
|
|
44
|
+
* const fetch = oidc.fetchWithToken(process.env.API_AUDIENCE);
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
47
|
export { OIDC_TOKEN_PATH, STATE_TOKEN_ENV_VAR, UnfazedOIDC, oidc, oidcToken } from "./oidc.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAoD/D;;;;GAIG;AACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,OAAO,GACR,MAAM,aAAa,CAAC;AAUrB;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAU/F,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AAKrB;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA4B;IAC5D,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAChE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * unfazed — official TypeScript SDK.\n *\n * The code-mode companion to the unfazed MCP server: write one script that\n * deploys, waits, reads logs and filters them locally, instead of chaining a\n * dozen tool calls through a model's context.\n *\n * ```ts\n * import { Unfazed } from \"unfazed\";\n *\n * const unfazed = new Unfazed(); // UNFZ_API_TOKEN, else your `npx unfazed login`\n * const ws = await unfazed.createWorkspace(\"hello\");\n * await unfazed.writeFiles(ws.id, {\n * \"server.ts\": `export default { fetch: () => new Response(\"hi\") }`,\n * });\n * const app = await unfazed.deployService({ workspaceId: ws.id, alias: \"hello\", wait: true });\n * console.log(app.url);\n * ```\n *\n * `help()` returns the reference: every method with its signature.\n *\n * Two layers, both public:\n * - {@link Unfazed} — the workflow layer (deploy + wait, one-commit batch\n * writes, history and restore, errors, events, one-call rollback).\n * - {@link UnfazedClient} — the typed GraphQL transport, for anything not wrapped.\n */\n\nimport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\nexport { Unfazed, parseDeploymentTimings } from \"./unfazed.js\";\nexport type {\n AppRef,\n ClientTimings,\n CommitDeploymentTarget,\n CommitDetail,\n CommitPage,\n CommitsLanded,\n CreateRunOptions,\n FollowUpRunOptions,\n DeleteAppOptions,\n DeleteAppResult,\n DeleteDeploymentOptions,\n DeleteWorkspaceOptions,\n Deployment,\n DeploymentEvent,\n DeployBoxOptions,\n DeployOptions,\n DeployResult,\n DeployServiceOptions,\n DeployStaticOptions,\n DeployTimings,\n EnvVar,\n ErrorGroup,\n ErrorGroupDetail,\n ErrorGroupPage,\n ErrorGroupRef,\n EventPage,\n EventsInput,\n EventType,\n FileCommit,\n FileContent,\n FilesMap,\n FileWriteOutcome,\n ListErrorsOptions,\n ListRunsFilter,\n LiveApp,\n PlatformEvent,\n PlatformTimings,\n RestoreVersionOptions,\n RestoreVersionResult,\n RollbackOptions,\n Run,\n TimingEvent,\n UnfazedOptions,\n Workspace,\n WorkspaceFile,\n WriteFilesOptions,\n WriteFilesResult,\n WrittenFile,\n} from \"./unfazed.js\";\n\n/**\n * The SDK's reference — every method with its signature — as a string:\n * `help()` for the page, `help(\"writeFiles\")` for one entry. The same page\n * ships as REFERENCE.md and is served at https://unfazed.dev/sdk.md.\n */\nexport { help, REFERENCE } from \"./reference.js\";\n\nexport {\n UnfazedClient,\n DEFAULT_BASE_URL,\n DEFAULT_ENDPOINT,\n ENDPOINT_ENV_VAR,\n GRAPHQL_PATH,\n TOKEN_ENV_VAR,\n VERSION,\n} from \"./client.js\";\nexport type {\n ClientOptions,\n FetchLike,\n GraphQLDocument,\n RequestOptions,\n ResultOf,\n VariablesOf,\n} from \"./client.js\";\n\n/**\n * Workload identity: OIDC tokens the platform issues to the app this code\n * runs in, with only its `UNFZ_STATE_TOKEN`. `oidc` is the process's own\n * {@link UnfazedOIDC}; `new Unfazed().oidc` is the same with the instance's\n * endpoint and fetch.\n *\n * ```ts\n * import { oidc, oidcToken } from \"unfazed\";\n * const jwt = await oidcToken(\"https://api.example.com\");\n * const fetch = oidc.fetchWithToken(process.env.
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAoD/D;;;;GAIG;AACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,OAAO,GACR,MAAM,aAAa,CAAC;AAUrB;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAU/F,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AAKrB;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA4B;IAC5D,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAChE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * unfazed — official TypeScript SDK.\n *\n * The code-mode companion to the unfazed MCP server: write one script that\n * deploys, waits, reads logs and filters them locally, instead of chaining a\n * dozen tool calls through a model's context.\n *\n * ```ts\n * import { Unfazed } from \"unfazed\";\n *\n * const unfazed = new Unfazed(); // UNFZ_API_TOKEN, else your `npx unfazed login`\n * const ws = await unfazed.createWorkspace(\"hello\");\n * await unfazed.writeFiles(ws.id, {\n * \"server.ts\": `export default { fetch: () => new Response(\"hi\") }`,\n * });\n * const app = await unfazed.deployService({ workspaceId: ws.id, alias: \"hello\", wait: true });\n * console.log(app.url);\n * ```\n *\n * `help()` returns the reference: every method with its signature.\n *\n * Two layers, both public:\n * - {@link Unfazed} — the workflow layer (deploy + wait, one-commit batch\n * writes, history and restore, errors, events, one-call rollback).\n * - {@link UnfazedClient} — the typed GraphQL transport, for anything not wrapped.\n */\n\nimport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\nexport { Unfazed, parseDeploymentTimings } from \"./unfazed.js\";\nexport type {\n AppRef,\n ClientTimings,\n CommitDeploymentTarget,\n CommitDetail,\n CommitPage,\n CommitsLanded,\n CreateRunOptions,\n FollowUpRunOptions,\n DeleteAppOptions,\n DeleteAppResult,\n DeleteDeploymentOptions,\n DeleteWorkspaceOptions,\n Deployment,\n DeploymentEvent,\n DeployBoxOptions,\n DeployOptions,\n DeployResult,\n DeployServiceOptions,\n DeployStaticOptions,\n DeployTimings,\n EnvVar,\n ErrorGroup,\n ErrorGroupDetail,\n ErrorGroupPage,\n ErrorGroupRef,\n EventPage,\n EventsInput,\n EventType,\n FileCommit,\n FileContent,\n FilesMap,\n FileWriteOutcome,\n ListErrorsOptions,\n ListRunsFilter,\n LiveApp,\n PlatformEvent,\n PlatformTimings,\n RestoreVersionOptions,\n RestoreVersionResult,\n RollbackOptions,\n Run,\n TimingEvent,\n UnfazedOptions,\n Workspace,\n WorkspaceFile,\n WriteFilesOptions,\n WriteFilesResult,\n WrittenFile,\n} from \"./unfazed.js\";\n\n/**\n * The SDK's reference — every method with its signature — as a string:\n * `help()` for the page, `help(\"writeFiles\")` for one entry. The same page\n * ships as REFERENCE.md and is served at https://unfazed.dev/sdk.md.\n */\nexport { help, REFERENCE } from \"./reference.js\";\n\nexport {\n UnfazedClient,\n DEFAULT_BASE_URL,\n DEFAULT_ENDPOINT,\n ENDPOINT_ENV_VAR,\n GRAPHQL_PATH,\n TOKEN_ENV_VAR,\n VERSION,\n} from \"./client.js\";\nexport type {\n ClientOptions,\n FetchLike,\n GraphQLDocument,\n RequestOptions,\n ResultOf,\n VariablesOf,\n} from \"./client.js\";\n\n/**\n * Workload identity: OIDC tokens the platform issues to the app this code\n * runs in, with only its `UNFZ_STATE_TOKEN`. `oidc` is the process's own\n * {@link UnfazedOIDC}; `new Unfazed().oidc` is the same with the instance's\n * endpoint and fetch.\n *\n * ```ts\n * import { oidc, oidcToken } from \"unfazed\";\n * const jwt = await oidcToken(\"https://api.example.com\");\n * const fetch = oidc.fetchWithToken(process.env.API_AUDIENCE);\n * ```\n */\nexport { OIDC_TOKEN_PATH, STATE_TOKEN_ENV_VAR, UnfazedOIDC, oidc, oidcToken } from \"./oidc.js\";\nexport type {\n FetchWithTokenOptions,\n OIDCOptions,\n OIDCToken,\n OIDCTokenOptions,\n OIDCTransport,\n TokenFetch,\n} from \"./oidc.js\";\n\nexport {\n DEPLOYMENT_NOT_FOUND,\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedConflictError,\n UnfazedDeployError,\n UnfazedError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedNotFoundError,\n UnfazedOIDCError,\n UnfazedRunError,\n UnfazedTimeoutError,\n UnfazedUnconfirmedAppsError,\n UnfazedWriteError,\n WORKSPACE_NOT_FOUND,\n isDuplicateWorkspaceName,\n isNotFoundSentinel,\n isTooManyWaits,\n} from \"./errors.js\";\nexport type { GraphQLResponseError, OIDCErrorCode } from \"./errors.js\";\n\nexport type { WaitOptions } from \"./wait.js\";\n\n/**\n * The stored login of `npx unfazed login`, which {@link UnfazedClient} uses when\n * no token is passed and `UNFZ_API_TOKEN` is unset. The helpers that read,\n * write and refresh it are in `unfazed/credentials`.\n */\nexport { CREDENTIALS_FILE, LOGIN_COMMAND, credentialsPath } from \"./credentials.js\";\nexport type { StoredCredentials } from \"./credentials.js\";\n\nexport { UnfazedInspectError } from \"./errors.js\";\nexport { APP_ENV_VAR } from \"./remote/app.js\";\nexport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\n/**\n * The `unfazed` backend of Workflow SDK's CLI: a read-mostly World over the\n * platform's workflow inspection API, for one app.\n *\n * ```sh\n * npx unfazed login # once: signs you in, in the browser\n * UNFZ_APP=birthday-card npx workflow inspect runs --backend unfazed\n * ```\n *\n * The token is `UNFZ_API_TOKEN` when set, else the stored login of\n * `npx unfazed login`, refreshed as it expires.\n *\n * The CLI (and its `--web` UI) resolve `unfazed` from the project and call\n * this with no arguments. The app is `UNFZ_APP`, else the `alias` (or the\n * single `apps[]` entry) of `unfazed.json` in the current directory. Reads\n * and `workflow cancel` work; every other write throws, since an app's own\n * World is `unfazed/workflow`. The module behind it loads on first call, so\n * `import \"unfazed\"` stays as light as before.\n */\nexport async function createWorld(options?: RemoteWorldOptions): Promise<RemoteWorld> {\n const { createRemoteWorld } = await import(\"./remote/world.js\");\n return createRemoteWorld(options);\n}\n\n/**\n * Every operation document the workflow layer uses, for callers that want the\n * typed documents but not the wrapper methods.\n */\nexport * as operations from \"./operations.js\";\n\n/**\n * Schema types generated from the platform's `graph/schema.graphqls`: enums\n * (`DeploymentStatus`, `ServiceMode`, ...), input objects\n * (`CreateDeploymentInput`, ...) and every operation's result type.\n */\nexport type * from \"./generated/graphql.js\";\n"]}
|
package/dist/oidc.d.ts
CHANGED
|
@@ -127,9 +127,9 @@ export declare class UnfazedOIDC {
|
|
|
127
127
|
*
|
|
128
128
|
* ```ts
|
|
129
129
|
* const openai = new OpenAI({
|
|
130
|
-
* baseURL: process.env.
|
|
130
|
+
* baseURL: process.env.API_BASE_URL,
|
|
131
131
|
* apiKey: "unused",
|
|
132
|
-
* fetch: unfazed.oidc.fetchWithToken(process.env.
|
|
132
|
+
* fetch: unfazed.oidc.fetchWithToken(process.env.API_AUDIENCE),
|
|
133
133
|
* });
|
|
134
134
|
* ```
|
|
135
135
|
*
|
|
@@ -144,7 +144,7 @@ export declare class UnfazedOIDC {
|
|
|
144
144
|
*
|
|
145
145
|
* ```ts
|
|
146
146
|
* import { oidc } from "unfazed";
|
|
147
|
-
* const fetchWithToken = oidc.fetchWithToken(process.env.
|
|
147
|
+
* const fetchWithToken = oidc.fetchWithToken(process.env.API_AUDIENCE);
|
|
148
148
|
* ```
|
|
149
149
|
*/
|
|
150
150
|
export declare const oidc: UnfazedOIDC;
|
package/dist/oidc.js
CHANGED
|
@@ -132,9 +132,9 @@ export class UnfazedOIDC {
|
|
|
132
132
|
*
|
|
133
133
|
* ```ts
|
|
134
134
|
* const openai = new OpenAI({
|
|
135
|
-
* baseURL: process.env.
|
|
135
|
+
* baseURL: process.env.API_BASE_URL,
|
|
136
136
|
* apiKey: "unused",
|
|
137
|
-
* fetch: unfazed.oidc.fetchWithToken(process.env.
|
|
137
|
+
* fetch: unfazed.oidc.fetchWithToken(process.env.API_AUDIENCE),
|
|
138
138
|
* });
|
|
139
139
|
* ```
|
|
140
140
|
*
|
|
@@ -257,7 +257,7 @@ export class UnfazedOIDC {
|
|
|
257
257
|
*
|
|
258
258
|
* ```ts
|
|
259
259
|
* import { oidc } from "unfazed";
|
|
260
|
-
* const fetchWithToken = oidc.fetchWithToken(process.env.
|
|
260
|
+
* const fetchWithToken = oidc.fetchWithToken(process.env.API_AUDIENCE);
|
|
261
261
|
* ```
|
|
262
262
|
*/
|
|
263
263
|
export const oidc = /* @__PURE__ */ new UnfazedOIDC();
|
package/dist/oidc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workload identity: OIDC tokens the platform issues to an app.\n *\n * An app running on unfazed (a service or a cron job) asks the control plane\n * for a short-lived JWT that names it, for one audience, and presents it to a\n * service that trusts the issuer `https://unfazed.dev/oidc` instead of a\n * static API key. The only credential is the app's own `UNFZ_STATE_TOKEN`,\n * which the platform puts into each of its VMs; no `UNFZ_API_TOKEN` is\n * involved.\n *\n * ```ts\n * import { oidcToken } from \"unfazed\";\n * const jwt = await oidcToken(\"https://api.example.com\");\n * ```\n *\n * Wire contract: `POST <platform>/oidc/token` with\n * `Authorization: Bearer $UNFZ_STATE_TOKEN` and `{ audience, ttl_seconds? }`;\n * 200 is `{ token, expires_at, token_type: \"Bearer\" }`, an error\n * `{ error, error_description }`.\n */\n\nimport {\n DEFAULT_ENDPOINT,\n VERSION,\n configuredEndpoint,\n platformBaseUrl,\n processEnv,\n untilAborted,\n} from \"./client.js\";\nimport { UnfazedConfigError, UnfazedOIDCError, UnfazedTimeoutError, type OIDCErrorCode } from \"./errors.js\";\n\n/** Environment variable holding the app's state token, set in every unfazed service and cron VM. */\nexport const STATE_TOKEN_ENV_VAR = \"UNFZ_STATE_TOKEN\";\n\n/** Path of the token endpoint under the platform origin. */\nexport const OIDC_TOKEN_PATH = \"/oidc/token\";\n\n/** A cached token is used until this share of its lifetime has passed. */\nconst REFRESH_AT = 0.8;\n/** Retries after the first attempt, for 503s, 429s and network failures. */\nconst MAX_RETRIES = 3;\n/** First retry delay; doubles per retry (200, 400, 800 ms). */\nconst BACKOFF_MS = 200;\n/** The longest `Retry-After` waited out; a longer one fails at once, carrying it. */\nconst MAX_RETRY_AFTER_MS = 10_000;\nconst MIN_TTL_SECONDS = 60;\nconst MAX_TTL_SECONDS = 3600;\n/** One attempt's timeout. */\nconst DEFAULT_TIMEOUT_MS = 10_000;\n/** 1 to 256 printable ASCII characters, none of them whitespace. */\nconst AUDIENCE = /^[\\x21-\\x7e]{1,256}$/;\n\n/**\n * The `fetch` the token request goes through: the global `fetch`, or anything\n * shaped like it (the client's `FetchLike` fits). `headers` on the response\n * is read for `Retry-After` when present.\n */\nexport type OIDCTransport = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n status: number;\n headers?: { get(name: string): string | null };\n text(): Promise<string>;\n}>;\n\n/** A `fetch` as the OpenAI SDK (and most HTTP clients) take one. */\nexport type TokenFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nexport interface OIDCOptions {\n /** The app's state token. Default: `UNFZ_STATE_TOKEN`, read on every call. */\n stateToken?: string;\n /**\n * Full GraphQL endpoint; tokens come from its origin. Wins over `baseUrl`.\n * Falls back to `UNFZ_API_URL`, then `https://unfazed.dev` (as the client).\n */\n endpoint?: string;\n /** Platform origin; `/oidc/token` is appended. */\n baseUrl?: string;\n /** Where `UNFZ_STATE_TOKEN` and `UNFZ_API_URL` are read from. Default: `process.env`. */\n env?: Record<string, string | undefined>;\n /**\n * The transport of the token request. Default: the global `fetch`. An\n * instance with its own `fetch` (or `now`) keeps its own cache; every other\n * one shares the process's.\n */\n fetch?: OIDCTransport;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n /** Timeout of one attempt, in ms. Default 10_000; 0 disables it. */\n timeoutMs?: number;\n /** Injected wall clock, ms (tests). */\n now?: () => number;\n /** Injected sleep between retries (tests). */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface OIDCTokenOptions {\n /** Token lifetime, a whole number of seconds from 60 to 3600. Default: the platform's. */\n ttlSeconds?: number;\n /**\n * Stops waiting, with {@link UnfazedTimeoutError}. A request other callers\n * wait on too keeps going (and fills the cache).\n */\n signal?: AbortSignal;\n}\n\n/** A token with its expiry, from {@link UnfazedOIDC.tokenResponse}. */\nexport interface OIDCToken {\n /** The JWT. */\n token: string;\n /** When it expires (the platform's `expires_at`). */\n expiresAt: Date;\n}\n\nexport interface FetchWithTokenOptions {\n /** Token lifetime, a whole number of seconds from 60 to 3600. Default: the platform's. */\n ttlSeconds?: number;\n /** The `fetch` that sends the request. Default: the global `fetch`, at call time. */\n fetch?: TokenFetch;\n}\n\ninterface Entry {\n token: string;\n expiresAtMs: number;\n /** When a call stops using it and asks for a new one (80% of its lifetime). */\n refreshAt: number;\n /** When it expires, on this process's clock. */\n validUntil: number;\n}\n\ninterface Store {\n cache: Map<string, Entry>;\n inflight: Map<string, Promise<Entry>>;\n}\n\n/**\n * One cache per process, not per copy of this module (a framework can bundle\n * the package into several server layers) nor per `Unfazed` instance (a\n * request handler that builds one per request must not ask for a token per\n * request). Keys carry the token URL and the state token, so instances\n * configured differently never share a token.\n */\nconst STORE_KEY = Symbol.for(\"unfazed//oidc/v1\");\n\nfunction sharedStore(): Store {\n const g = globalThis as typeof globalThis & Record<symbol, Store | undefined>;\n return (g[STORE_KEY] ??= newStore());\n}\n\nfunction newStore(): Store {\n return { cache: new Map(), inflight: new Map() };\n}\n\n/**\n * Workload identity tokens for the app this code runs in: `unfazed.oidc` on an\n * {@link import(\"./unfazed.js\").Unfazed} instance, or the standalone `oidc`.\n *\n * Tokens are cached per (audience, ttl) and reused until 80% of their\n * lifetime has passed; concurrent calls for the same pair share one request.\n * 503 (and a gateway's 502/504), 429 and network failures are retried up to 3\n * times (200, 400, 800 ms; a 429's or 503's `Retry-After` up to 10 s is waited\n * out instead); no other 4xx is retried. When a refresh fails for one of\n * those passing reasons, the token it was replacing is returned while it is\n * still valid.\n */\nexport class UnfazedOIDC {\n readonly #options: OIDCOptions;\n readonly #store: Store | undefined;\n\n constructor(options: OIDCOptions = {}) {\n this.#options = { ...options };\n this.#store = options.fetch || options.now ? newStore() : undefined;\n }\n\n /**\n * A JWT for `audience`, from the cache while it is fresh.\n *\n * ```ts\n * const jwt = await unfazed.oidc.token(\"https://api.example.com\", { ttlSeconds: 300 });\n * ```\n *\n * Throws {@link UnfazedOIDCError}: `not_in_app` without `UNFZ_STATE_TOKEN`,\n * `invalid_audience`, `invalid_request` (`ttlSeconds`), or the platform's\n * code.\n */\n async token(audience: string | undefined, options: OIDCTokenOptions = {}): Promise<string> {\n return (await this.tokenResponse(audience, options)).token;\n }\n\n /** {@link token}, with the token's expiry: `{ token, expiresAt }`. */\n async tokenResponse(audience: string | undefined, options: OIDCTokenOptions = {}): Promise<OIDCToken> {\n checkAudience(audience);\n const ttl = options.ttlSeconds;\n checkTtl(ttl);\n const { signal } = options;\n if (signal?.aborted) throw aborted();\n\n const stateToken = this.#stateToken();\n const url = this.#tokenUrl();\n const key = `${url}\\n${stateToken}\\n${audience}\\n${ttl ?? \"\"}`;\n const store = this.#store ?? sharedStore();\n\n const cached = store.cache.get(key);\n if (cached && this.#now() < cached.refreshAt) return view(cached);\n\n let pending = store.inflight.get(key);\n if (!pending) {\n pending = this.#issue(url, stateToken, audience, ttl)\n .then(\n (entry) => {\n store.cache.set(key, entry);\n return entry;\n },\n (error: unknown) => {\n // The control plane restarting must not take down an app whose\n // token is still good: fall back to the token being replaced.\n const previous = store.cache.get(key);\n if (previous && error instanceof UnfazedOIDCError && error.retryable && this.#now() < previous.validUntil) {\n return previous;\n }\n throw error;\n },\n )\n .finally(() => store.inflight.delete(key));\n // Every caller may stop waiting (its signal); the request then ends\n // with nobody listening, which must not be an unhandled rejection.\n pending.catch(() => {});\n store.inflight.set(key, pending);\n }\n return view(await untilAborted(pending, signal).catch(rethrowAbort));\n }\n\n /**\n * A `fetch` that sends every request with `Authorization: Bearer <token>`,\n * a fresh token for `audience` each time (from the cache while it lasts).\n * Every other header, the body and the signal pass through; an\n * `Authorization` already there is replaced.\n *\n * ```ts\n * const openai = new OpenAI({\n * baseURL: process.env.PRIVATEINFERENCE_BASE_URL,\n * apiKey: \"unused\",\n * fetch: unfazed.oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE),\n * });\n * ```\n *\n * An invalid `audience` (an unset variable among them) throws here, at\n * once; a missing `UNFZ_STATE_TOKEN` on the first request.\n */\n fetchWithToken(audience: string | undefined, options: FetchWithTokenOptions = {}): TokenFetch {\n checkAudience(audience);\n const aud: string = audience;\n const ttl = options.ttlSeconds;\n checkTtl(ttl);\n return async (input, init) => {\n const request = typeof Request === \"function\" && input instanceof Request ? input : undefined;\n const signal = init?.signal ?? request?.signal ?? undefined;\n const token = await this.token(aud, {\n ...(ttl === undefined ? {} : { ttlSeconds: ttl }),\n ...(signal ? { signal } : {}),\n });\n // init.headers replace a Request's own, as fetch() itself does.\n const headers = new Headers(init?.headers ?? request?.headers);\n headers.set(\"Authorization\", `Bearer ${token}`);\n const send = options.fetch ?? (globalFetch() as TokenFetch);\n return send(input, { ...init, headers });\n };\n }\n\n #now(): number {\n return (this.#options.now ?? Date.now)();\n }\n\n #stateToken(): string {\n const env = this.#options.env ?? processEnv();\n const token = (this.#options.stateToken ?? env[STATE_TOKEN_ENV_VAR])?.trim();\n if (!token) {\n throw new UnfazedOIDCError(\n \"not_in_app\",\n `unfazed: not running inside an unfazed app: ${STATE_TOKEN_ENV_VAR} is not set. ` +\n `OIDC tokens are issued to an app's services and cron jobs, for the ${STATE_TOKEN_ENV_VAR} ` +\n \"the platform gives each of them (no UNFZ_API_TOKEN needed).\",\n );\n }\n return token;\n }\n\n #tokenUrl(): string {\n const { endpoint, baseUrl } = this.#options;\n const configured = configuredEndpoint(\n { ...(endpoint ? { endpoint } : {}), ...(baseUrl ? { baseUrl } : {}) },\n this.#options.env ?? processEnv(),\n );\n return platformBaseUrl(configured ?? DEFAULT_ENDPOINT) + OIDC_TOKEN_PATH;\n }\n\n async #issue(url: string, stateToken: string, audience: string, ttl: number | undefined): Promise<Entry> {\n const transport = this.#options.fetch ?? (globalFetch() as OIDCTransport);\n const sleep = this.#options.sleep ?? defaultSleep;\n const userAgent = this.#options.userAgent ?? `unfazed-sdk/${VERSION}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${stateToken}`,\n // The edge refuses some generic agents; always name ourselves.\n ...(userAgent ? { \"User-Agent\": userAgent } : {}),\n };\n const body = JSON.stringify(ttl === undefined ? { audience } : { audience, ttl_seconds: ttl });\n const what = `unfazed: no OIDC token for audience ${JSON.stringify(audience)}`;\n\n for (let retries = 0; ; retries++) {\n const startedAt = this.#now();\n let answer: { status: number; text: string; retryAfter: string | null };\n try {\n answer = await this.#send(transport, url, headers, body);\n } catch (cause) {\n if (retries < MAX_RETRIES) {\n await sleep(BACKOFF_MS * 2 ** retries);\n continue;\n }\n throw new UnfazedOIDCError(\n \"unreachable\",\n `${what}: ${url} did not answer in ${retries + 1} attempts (${describe(cause)})`,\n { retryable: true, cause },\n );\n }\n const { status, text, retryAfter } = answer;\n if (status >= 200 && status < 300) return this.#entry(text, startedAt, what);\n\n const error = refusal(status, text, retryAfterMs(retryAfter, this.#now()), what);\n if (!error.retryable || retries >= MAX_RETRIES) throw error;\n const waitMs = error.retryAfterMs ?? BACKOFF_MS * 2 ** retries;\n if (waitMs > MAX_RETRY_AFTER_MS) throw error;\n await sleep(waitMs);\n }\n }\n\n async #send(\n transport: OIDCTransport,\n url: string,\n headers: Record<string, string>,\n body: string,\n ): Promise<{ status: number; text: string; retryAfter: string | null }> {\n const timeoutMs = this.#options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const controller = timeoutMs > 0 && typeof AbortController === \"function\" ? new AbortController() : undefined;\n const timer = controller ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n try {\n const signal = controller?.signal;\n const response = await untilAborted(\n transport(url, { method: \"POST\", headers, body, ...(signal ? { signal } : {}) }),\n signal,\n );\n const text = await untilAborted(response.text(), signal);\n return { status: response.status, text, retryAfter: response.headers?.get(\"retry-after\") ?? null };\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n }\n\n #entry(text: string, startedAt: number, what: string): Entry {\n const body = parseJson(text) as { token?: unknown; expires_at?: unknown } | undefined;\n const token = body?.token;\n const expiresAtMs = typeof body?.expires_at === \"string\" ? Date.parse(body.expires_at) : NaN;\n if (typeof token !== \"string\" || token === \"\" || !Number.isFinite(expiresAtMs)) {\n throw new UnfazedOIDCError(\n \"invalid_response\",\n `${what}: the platform answered 200 without a token and its expires_at: ${truncate(text)}`,\n );\n }\n // The JWT's own iat/exp are both the platform's clock, so its lifetime\n // holds even when this VM's clock is off (a guest resumed from a\n // snapshot); elapsed time is then measured on this clock alone.\n const lifetimeMs = jwtLifetimeMs(token) ?? expiresAtMs - startedAt;\n const life = Math.max(0, lifetimeMs);\n return {\n token,\n expiresAtMs,\n refreshAt: startedAt + REFRESH_AT * life,\n validUntil: startedAt + life,\n };\n }\n}\n\n/**\n * The process's own {@link UnfazedOIDC}: `UNFZ_STATE_TOKEN` and `UNFZ_API_URL`\n * read on every call, the global `fetch`, one cache.\n *\n * ```ts\n * import { oidc } from \"unfazed\";\n * const fetchWithToken = oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE);\n * ```\n */\nexport const oidc: UnfazedOIDC = /* @__PURE__ */ new UnfazedOIDC();\n\n/**\n * A JWT for `audience`, issued to the app this code runs in:\n * {@link UnfazedOIDC.token} on the process's {@link oidc}. Needs only\n * `UNFZ_STATE_TOKEN`.\n *\n * ```ts\n * import { oidcToken } from \"unfazed\";\n * const res = await fetch(url, { headers: { Authorization: `Bearer ${await oidcToken(url)}` } });\n * ```\n */\nexport function oidcToken(audience: string | undefined, options?: OIDCTokenOptions): Promise<string> {\n return oidc.token(audience, options);\n}\n\nfunction view(entry: Entry): OIDCToken {\n return { token: entry.token, expiresAt: new Date(entry.expiresAtMs) };\n}\n\nfunction checkAudience(audience: unknown): asserts audience is string {\n if (typeof audience === \"string\" && AUDIENCE.test(audience)) return;\n const got = typeof audience === \"string\" ? truncate(JSON.stringify(audience), 80) : String(audience);\n throw new UnfazedOIDCError(\n \"invalid_audience\",\n `unfazed: invalid OIDC audience ${got}: it must be 1 to 256 printable ASCII characters, without whitespace`,\n );\n}\n\nfunction checkTtl(ttl: unknown): void {\n if (ttl === undefined) return;\n if (typeof ttl === \"number\" && Number.isInteger(ttl) && ttl >= MIN_TTL_SECONDS && ttl <= MAX_TTL_SECONDS) return;\n throw new UnfazedOIDCError(\n \"invalid_request\",\n `unfazed: ttlSeconds must be a whole number from ${MIN_TTL_SECONDS} to ${MAX_TTL_SECONDS} (got ${String(ttl)})`,\n );\n}\n\n/** The error for a non-2xx answer; `retryable` says whether the loop asks again. */\nfunction refusal(status: number, text: string, retryAfter: number | undefined, what: string): UnfazedOIDCError {\n const body = parseJson(text) as { error?: unknown; error_description?: unknown } | undefined;\n const code = typeof body?.error === \"string\" && body.error ? body.error : codeForStatus(status);\n const description =\n typeof body?.error_description === \"string\" && body.error_description ? body.error_description : undefined;\n const detail = description ?? (body === undefined && text ? truncate(text) : undefined);\n // Never a 4xx but 429: the request itself is what is wrong.\n const retryable =\n status === 429 ||\n status === 502 ||\n status === 503 ||\n status === 504 ||\n (status >= 500 && code === \"temporarily_unavailable\");\n const hint = code === \"invalid_token\" ? ` (the platform refused ${STATE_TOKEN_ENV_VAR})` : \"\";\n const later = retryAfter === undefined ? \"\" : `; retry after ${Math.ceil(retryAfter / 1000)} s`;\n return new UnfazedOIDCError(code, `${what}: ${code} (HTTP ${status})${hint}${detail ? `: ${detail}` : \"\"}${later}`, {\n status,\n ...(description ? { description } : {}),\n retryable,\n ...(retryAfter === undefined ? {} : { retryAfterMs: retryAfter }),\n });\n}\n\nfunction codeForStatus(status: number): OIDCErrorCode {\n switch (status) {\n case 400:\n return \"invalid_request\";\n case 401:\n return \"invalid_token\";\n case 429:\n return \"rate_limited\";\n case 503:\n return \"temporarily_unavailable\";\n default:\n return \"http_error\";\n }\n}\n\n/** `Retry-After` in ms: delay-seconds or an HTTP-date; undefined when absent or unreadable. */\nfunction retryAfterMs(value: string | null, now: number): number | undefined {\n if (value === null) return undefined;\n const trimmed = value.trim();\n if (/^\\d+$/.test(trimmed)) return Number(trimmed) * 1000;\n const at = Date.parse(trimmed);\n return Number.isFinite(at) ? Math.max(0, at - now) : undefined;\n}\n\n/** `exp - iat` of a JWT, in ms, when both are there. */\nfunction jwtLifetimeMs(token: string): number | undefined {\n const payload = token.split(\".\")[1];\n if (!payload || typeof atob !== \"function\") return undefined;\n try {\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const claims = JSON.parse(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\"))) as {\n iat?: unknown;\n exp?: unknown;\n };\n const { iat, exp } = claims;\n if (typeof iat === \"number\" && typeof exp === \"number\" && Number.isFinite(iat) && exp > iat) {\n return (exp - iat) * 1000;\n }\n } catch {\n // Not a JWT we can read: the platform's expires_at decides.\n }\n return undefined;\n}\n\nfunction globalFetch(): (input: string, init: RequestInit) => Promise<Response> {\n const fetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof fetch !== \"function\") {\n throw new UnfazedConfigError(\"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch }.\");\n }\n return fetch.bind(globalThis) as (input: string, init: RequestInit) => Promise<Response>;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction aborted(): UnfazedTimeoutError {\n return new UnfazedTimeoutError(\"unfazed: OIDC token request aborted\");\n}\n\nfunction rethrowAbort(error: unknown): never {\n if (typeof error === \"object\" && error !== null && (error as { name?: unknown }).name === \"AbortError\") {\n throw aborted();\n }\n throw error;\n}\n\nfunction describe(cause: unknown): string {\n if (cause instanceof Error) return cause.name === \"AbortError\" ? \"timed out\" : cause.message;\n return String(cause);\n}\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction truncate(s: string, max = 300): string {\n return s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAsB,MAAM,aAAa,CAAC;AAE5G,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAEtD,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAE7C,0EAA0E;AAC1E,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,4EAA4E;AAC5E,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,+DAA+D;AAC/D,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,qFAAqF;AACrF,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,6BAA6B;AAC7B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,oEAAoE;AACpE,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AA2FxC;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAEjD,SAAS,WAAW;IAClB,MAAM,CAAC,GAAG,UAAmE,CAAC;IAC9E,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,WAAW;IACb,QAAQ,CAAc;IACtB,MAAM,CAAoB;IAEnC,YAAY,UAAuB,EAAE;QACnC,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,KAAK,CAAC,QAA4B,EAAE,UAA4B,EAAE;QACtE,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7D,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,aAAa,CAAC,QAA4B,EAAE,UAA4B,EAAE;QAC9E,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;QAC/B,QAAQ,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC3B,IAAI,MAAM,EAAE,OAAO;YAAE,MAAM,OAAO,EAAE,CAAC;QAErC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,GAAG,KAAK,UAAU,KAAK,QAAQ,KAAK,GAAG,IAAI,EAAE,EAAE,CAAC;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;QAE3C,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;QAElE,IAAI,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC;iBAClD,IAAI,CACH,CAAC,KAAK,EAAE,EAAE;gBACR,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC5B,OAAO,KAAK,CAAC;YACf,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;gBACjB,+DAA+D;gBAC/D,8DAA8D;gBAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACtC,IAAI,QAAQ,IAAI,KAAK,YAAY,gBAAgB,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC;oBAC1G,OAAO,QAAQ,CAAC;gBAClB,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC,CACF;iBACA,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7C,oEAAoE;YACpE,mEAAmE;YACnE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACxB,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IACvE,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,QAA4B,EAAE,UAAiC,EAAE;QAC9E,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,GAAG,GAAW,QAAQ,CAAC;QAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;QAC/B,QAAQ,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC3B,MAAM,OAAO,GAAG,OAAO,OAAO,KAAK,UAAU,IAAI,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9F,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,IAAI,SAAS,CAAC;YAC5D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;gBAClC,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;gBACjD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9B,CAAC,CAAC;YACH,gEAAgE;YAChE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;YAChD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,IAAK,WAAW,EAAiB,CAAC;YAC5D,OAAO,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3C,CAAC,CAAC;IACJ,CAAC;IAED,IAAI;QACF,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;IAED,WAAW;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAC7E,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,gBAAgB,CACxB,YAAY,EACZ,+CAA+C,mBAAmB,eAAe;gBAC/E,sEAAsE,mBAAmB,GAAG;gBAC5F,6DAA6D,CAChE,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS;QACP,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5C,MAAM,UAAU,GAAG,kBAAkB,CACnC,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EACtE,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,UAAU,EAAE,CAClC,CAAC;QACF,OAAO,eAAe,CAAC,UAAU,IAAI,gBAAgB,CAAC,GAAG,eAAe,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,UAAkB,EAAE,QAAgB,EAAE,GAAuB;QACrF,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAK,WAAW,EAAoB,CAAC;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,YAAY,CAAC;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,eAAe,OAAO,EAAE,CAAC;QACtE,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,UAAU,EAAE;YACrC,+DAA+D;YAC/D,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/F,MAAM,IAAI,GAAG,uCAAuC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAE/E,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,MAAmE,CAAC;YACxE,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;oBAC1B,MAAM,KAAK,CAAC,UAAU,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;oBACvC,SAAS;gBACX,CAAC;gBACD,MAAM,IAAI,gBAAgB,CACxB,aAAa,EACb,GAAG,IAAI,KAAK,GAAG,sBAAsB,OAAO,GAAG,CAAC,cAAc,QAAQ,CAAC,KAAK,CAAC,GAAG,EAChF,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAC3B,CAAC;YACJ,CAAC;YACD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;YAC5C,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG;gBAAE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAE7E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YACjF,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,OAAO,IAAI,WAAW;gBAAE,MAAM,KAAK,CAAC;YAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,IAAI,UAAU,GAAG,CAAC,IAAI,OAAO,CAAC;YAC/D,IAAI,MAAM,GAAG,kBAAkB;gBAAE,MAAM,KAAK,CAAC;YAC7C,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CACT,SAAwB,EACxB,GAAW,EACX,OAA+B,EAC/B,IAAY;QAEZ,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,kBAAkB,CAAC;QAChE,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,IAAI,OAAO,eAAe,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9G,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,EAAE,MAAM,CAAC;YAClC,MAAM,QAAQ,GAAG,MAAM,YAAY,CACjC,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAChF,MAAM,CACP,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;YACzD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC;QACrG,CAAC;gBAAS,CAAC;YACT,IAAI,KAAK,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,SAAiB,EAAE,IAAY;QAClD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAA0D,CAAC;QACtF,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC;QAC1B,MAAM,WAAW,GAAG,OAAO,IAAI,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC7F,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/E,MAAM,IAAI,gBAAgB,CACxB,kBAAkB,EAClB,GAAG,IAAI,mEAAmE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAC;QACJ,CAAC;QACD,uEAAuE;QACvE,iEAAiE;QACjE,gEAAgE;QAChE,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,WAAW,GAAG,SAAS,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QACrC,OAAO;YACL,KAAK;YACL,WAAW;YACX,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,IAAI;YACxC,UAAU,EAAE,SAAS,GAAG,IAAI;SAC7B,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,IAAI,GAAgB,eAAe,CAAC,IAAI,WAAW,EAAE,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,QAA4B,EAAE,OAA0B;IAChF,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,IAAI,CAAC,KAAY;IACxB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,aAAa,CAAC,QAAiB;IACtC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO;IACpE,MAAM,GAAG,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrG,MAAM,IAAI,gBAAgB,CACxB,kBAAkB,EAClB,kCAAkC,GAAG,sEAAsE,CAC5G,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,GAAY;IAC5B,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO;IAC9B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,eAAe,IAAI,GAAG,IAAI,eAAe;QAAE,OAAO;IACjH,MAAM,IAAI,gBAAgB,CACxB,iBAAiB,EACjB,mDAAmD,eAAe,OAAO,eAAe,SAAS,MAAM,CAAC,GAAG,CAAC,GAAG,CAChH,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,SAAS,OAAO,CAAC,MAAc,EAAE,IAAY,EAAE,UAA8B,EAAE,IAAY;IACzF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAiE,CAAC;IAC7F,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAChG,MAAM,WAAW,GACf,OAAO,IAAI,EAAE,iBAAiB,KAAK,QAAQ,IAAI,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7G,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACxF,4DAA4D;IAC5D,MAAM,SAAS,GACb,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,CAAC,MAAM,IAAI,GAAG,IAAI,IAAI,KAAK,yBAAyB,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,0BAA0B,mBAAmB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9F,MAAM,KAAK,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;IAChG,OAAO,IAAI,gBAAgB,CAAC,IAAI,EAAE,GAAG,IAAI,KAAK,IAAI,UAAU,MAAM,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE;QAClH,MAAM;QACN,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,SAAS;QACT,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;KAClE,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,MAAc;IACnC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,iBAAiB,CAAC;QAC3B,KAAK,GAAG;YACN,OAAO,eAAe,CAAC;QACzB,KAAK,GAAG;YACN,OAAO,cAAc,CAAC;QACxB,KAAK,GAAG;YACN,OAAO,yBAAyB,CAAC;QACnC;YACE,OAAO,YAAY,CAAC;IACxB,CAAC;AACH,CAAC;AAED,+FAA+F;AAC/F,SAAS,YAAY,CAAC,KAAoB,EAAE,GAAW;IACrD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED,wDAAwD;AACxD,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAGnF,CAAC;QACF,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;QAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;YAC5F,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,KAAK,GAAI,UAAkC,CAAC,KAAK,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,kBAAkB,CAAC,mEAAmE,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAA4D,CAAC;AAC3F,CAAC;AAED,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,OAAO;IACd,OAAO,IAAI,mBAAmB,CAAC,qCAAqC,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA4B,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACvG,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAC7F,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AACrD,CAAC","sourcesContent":["/**\n * Workload identity: OIDC tokens the platform issues to an app.\n *\n * An app running on unfazed (a service or a cron job) asks the control plane\n * for a short-lived JWT that names it, for one audience, and presents it to a\n * service that trusts the issuer `https://unfazed.dev/oidc` instead of a\n * static API key. The only credential is the app's own `UNFZ_STATE_TOKEN`,\n * which the platform puts into each of its VMs; no `UNFZ_API_TOKEN` is\n * involved.\n *\n * ```ts\n * import { oidcToken } from \"unfazed\";\n * const jwt = await oidcToken(\"https://api.example.com\");\n * ```\n *\n * Wire contract: `POST <platform>/oidc/token` with\n * `Authorization: Bearer $UNFZ_STATE_TOKEN` and `{ audience, ttl_seconds? }`;\n * 200 is `{ token, expires_at, token_type: \"Bearer\" }`, an error\n * `{ error, error_description }`.\n */\n\nimport {\n DEFAULT_ENDPOINT,\n VERSION,\n configuredEndpoint,\n platformBaseUrl,\n processEnv,\n untilAborted,\n} from \"./client.js\";\nimport { UnfazedConfigError, UnfazedOIDCError, UnfazedTimeoutError, type OIDCErrorCode } from \"./errors.js\";\n\n/** Environment variable holding the app's state token, set in every unfazed service and cron VM. */\nexport const STATE_TOKEN_ENV_VAR = \"UNFZ_STATE_TOKEN\";\n\n/** Path of the token endpoint under the platform origin. */\nexport const OIDC_TOKEN_PATH = \"/oidc/token\";\n\n/** A cached token is used until this share of its lifetime has passed. */\nconst REFRESH_AT = 0.8;\n/** Retries after the first attempt, for 503s, 429s and network failures. */\nconst MAX_RETRIES = 3;\n/** First retry delay; doubles per retry (200, 400, 800 ms). */\nconst BACKOFF_MS = 200;\n/** The longest `Retry-After` waited out; a longer one fails at once, carrying it. */\nconst MAX_RETRY_AFTER_MS = 10_000;\nconst MIN_TTL_SECONDS = 60;\nconst MAX_TTL_SECONDS = 3600;\n/** One attempt's timeout. */\nconst DEFAULT_TIMEOUT_MS = 10_000;\n/** 1 to 256 printable ASCII characters, none of them whitespace. */\nconst AUDIENCE = /^[\\x21-\\x7e]{1,256}$/;\n\n/**\n * The `fetch` the token request goes through: the global `fetch`, or anything\n * shaped like it (the client's `FetchLike` fits). `headers` on the response\n * is read for `Retry-After` when present.\n */\nexport type OIDCTransport = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n status: number;\n headers?: { get(name: string): string | null };\n text(): Promise<string>;\n}>;\n\n/** A `fetch` as the OpenAI SDK (and most HTTP clients) take one. */\nexport type TokenFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nexport interface OIDCOptions {\n /** The app's state token. Default: `UNFZ_STATE_TOKEN`, read on every call. */\n stateToken?: string;\n /**\n * Full GraphQL endpoint; tokens come from its origin. Wins over `baseUrl`.\n * Falls back to `UNFZ_API_URL`, then `https://unfazed.dev` (as the client).\n */\n endpoint?: string;\n /** Platform origin; `/oidc/token` is appended. */\n baseUrl?: string;\n /** Where `UNFZ_STATE_TOKEN` and `UNFZ_API_URL` are read from. Default: `process.env`. */\n env?: Record<string, string | undefined>;\n /**\n * The transport of the token request. Default: the global `fetch`. An\n * instance with its own `fetch` (or `now`) keeps its own cache; every other\n * one shares the process's.\n */\n fetch?: OIDCTransport;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n /** Timeout of one attempt, in ms. Default 10_000; 0 disables it. */\n timeoutMs?: number;\n /** Injected wall clock, ms (tests). */\n now?: () => number;\n /** Injected sleep between retries (tests). */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface OIDCTokenOptions {\n /** Token lifetime, a whole number of seconds from 60 to 3600. Default: the platform's. */\n ttlSeconds?: number;\n /**\n * Stops waiting, with {@link UnfazedTimeoutError}. A request other callers\n * wait on too keeps going (and fills the cache).\n */\n signal?: AbortSignal;\n}\n\n/** A token with its expiry, from {@link UnfazedOIDC.tokenResponse}. */\nexport interface OIDCToken {\n /** The JWT. */\n token: string;\n /** When it expires (the platform's `expires_at`). */\n expiresAt: Date;\n}\n\nexport interface FetchWithTokenOptions {\n /** Token lifetime, a whole number of seconds from 60 to 3600. Default: the platform's. */\n ttlSeconds?: number;\n /** The `fetch` that sends the request. Default: the global `fetch`, at call time. */\n fetch?: TokenFetch;\n}\n\ninterface Entry {\n token: string;\n expiresAtMs: number;\n /** When a call stops using it and asks for a new one (80% of its lifetime). */\n refreshAt: number;\n /** When it expires, on this process's clock. */\n validUntil: number;\n}\n\ninterface Store {\n cache: Map<string, Entry>;\n inflight: Map<string, Promise<Entry>>;\n}\n\n/**\n * One cache per process, not per copy of this module (a framework can bundle\n * the package into several server layers) nor per `Unfazed` instance (a\n * request handler that builds one per request must not ask for a token per\n * request). Keys carry the token URL and the state token, so instances\n * configured differently never share a token.\n */\nconst STORE_KEY = Symbol.for(\"unfazed//oidc/v1\");\n\nfunction sharedStore(): Store {\n const g = globalThis as typeof globalThis & Record<symbol, Store | undefined>;\n return (g[STORE_KEY] ??= newStore());\n}\n\nfunction newStore(): Store {\n return { cache: new Map(), inflight: new Map() };\n}\n\n/**\n * Workload identity tokens for the app this code runs in: `unfazed.oidc` on an\n * {@link import(\"./unfazed.js\").Unfazed} instance, or the standalone `oidc`.\n *\n * Tokens are cached per (audience, ttl) and reused until 80% of their\n * lifetime has passed; concurrent calls for the same pair share one request.\n * 503 (and a gateway's 502/504), 429 and network failures are retried up to 3\n * times (200, 400, 800 ms; a 429's or 503's `Retry-After` up to 10 s is waited\n * out instead); no other 4xx is retried. When a refresh fails for one of\n * those passing reasons, the token it was replacing is returned while it is\n * still valid.\n */\nexport class UnfazedOIDC {\n readonly #options: OIDCOptions;\n readonly #store: Store | undefined;\n\n constructor(options: OIDCOptions = {}) {\n this.#options = { ...options };\n this.#store = options.fetch || options.now ? newStore() : undefined;\n }\n\n /**\n * A JWT for `audience`, from the cache while it is fresh.\n *\n * ```ts\n * const jwt = await unfazed.oidc.token(\"https://api.example.com\", { ttlSeconds: 300 });\n * ```\n *\n * Throws {@link UnfazedOIDCError}: `not_in_app` without `UNFZ_STATE_TOKEN`,\n * `invalid_audience`, `invalid_request` (`ttlSeconds`), or the platform's\n * code.\n */\n async token(audience: string | undefined, options: OIDCTokenOptions = {}): Promise<string> {\n return (await this.tokenResponse(audience, options)).token;\n }\n\n /** {@link token}, with the token's expiry: `{ token, expiresAt }`. */\n async tokenResponse(audience: string | undefined, options: OIDCTokenOptions = {}): Promise<OIDCToken> {\n checkAudience(audience);\n const ttl = options.ttlSeconds;\n checkTtl(ttl);\n const { signal } = options;\n if (signal?.aborted) throw aborted();\n\n const stateToken = this.#stateToken();\n const url = this.#tokenUrl();\n const key = `${url}\\n${stateToken}\\n${audience}\\n${ttl ?? \"\"}`;\n const store = this.#store ?? sharedStore();\n\n const cached = store.cache.get(key);\n if (cached && this.#now() < cached.refreshAt) return view(cached);\n\n let pending = store.inflight.get(key);\n if (!pending) {\n pending = this.#issue(url, stateToken, audience, ttl)\n .then(\n (entry) => {\n store.cache.set(key, entry);\n return entry;\n },\n (error: unknown) => {\n // The control plane restarting must not take down an app whose\n // token is still good: fall back to the token being replaced.\n const previous = store.cache.get(key);\n if (previous && error instanceof UnfazedOIDCError && error.retryable && this.#now() < previous.validUntil) {\n return previous;\n }\n throw error;\n },\n )\n .finally(() => store.inflight.delete(key));\n // Every caller may stop waiting (its signal); the request then ends\n // with nobody listening, which must not be an unhandled rejection.\n pending.catch(() => {});\n store.inflight.set(key, pending);\n }\n return view(await untilAborted(pending, signal).catch(rethrowAbort));\n }\n\n /**\n * A `fetch` that sends every request with `Authorization: Bearer <token>`,\n * a fresh token for `audience` each time (from the cache while it lasts).\n * Every other header, the body and the signal pass through; an\n * `Authorization` already there is replaced.\n *\n * ```ts\n * const openai = new OpenAI({\n * baseURL: process.env.API_BASE_URL,\n * apiKey: \"unused\",\n * fetch: unfazed.oidc.fetchWithToken(process.env.API_AUDIENCE),\n * });\n * ```\n *\n * An invalid `audience` (an unset variable among them) throws here, at\n * once; a missing `UNFZ_STATE_TOKEN` on the first request.\n */\n fetchWithToken(audience: string | undefined, options: FetchWithTokenOptions = {}): TokenFetch {\n checkAudience(audience);\n const aud: string = audience;\n const ttl = options.ttlSeconds;\n checkTtl(ttl);\n return async (input, init) => {\n const request = typeof Request === \"function\" && input instanceof Request ? input : undefined;\n const signal = init?.signal ?? request?.signal ?? undefined;\n const token = await this.token(aud, {\n ...(ttl === undefined ? {} : { ttlSeconds: ttl }),\n ...(signal ? { signal } : {}),\n });\n // init.headers replace a Request's own, as fetch() itself does.\n const headers = new Headers(init?.headers ?? request?.headers);\n headers.set(\"Authorization\", `Bearer ${token}`);\n const send = options.fetch ?? (globalFetch() as TokenFetch);\n return send(input, { ...init, headers });\n };\n }\n\n #now(): number {\n return (this.#options.now ?? Date.now)();\n }\n\n #stateToken(): string {\n const env = this.#options.env ?? processEnv();\n const token = (this.#options.stateToken ?? env[STATE_TOKEN_ENV_VAR])?.trim();\n if (!token) {\n throw new UnfazedOIDCError(\n \"not_in_app\",\n `unfazed: not running inside an unfazed app: ${STATE_TOKEN_ENV_VAR} is not set. ` +\n `OIDC tokens are issued to an app's services and cron jobs, for the ${STATE_TOKEN_ENV_VAR} ` +\n \"the platform gives each of them (no UNFZ_API_TOKEN needed).\",\n );\n }\n return token;\n }\n\n #tokenUrl(): string {\n const { endpoint, baseUrl } = this.#options;\n const configured = configuredEndpoint(\n { ...(endpoint ? { endpoint } : {}), ...(baseUrl ? { baseUrl } : {}) },\n this.#options.env ?? processEnv(),\n );\n return platformBaseUrl(configured ?? DEFAULT_ENDPOINT) + OIDC_TOKEN_PATH;\n }\n\n async #issue(url: string, stateToken: string, audience: string, ttl: number | undefined): Promise<Entry> {\n const transport = this.#options.fetch ?? (globalFetch() as OIDCTransport);\n const sleep = this.#options.sleep ?? defaultSleep;\n const userAgent = this.#options.userAgent ?? `unfazed-sdk/${VERSION}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${stateToken}`,\n // The edge refuses some generic agents; always name ourselves.\n ...(userAgent ? { \"User-Agent\": userAgent } : {}),\n };\n const body = JSON.stringify(ttl === undefined ? { audience } : { audience, ttl_seconds: ttl });\n const what = `unfazed: no OIDC token for audience ${JSON.stringify(audience)}`;\n\n for (let retries = 0; ; retries++) {\n const startedAt = this.#now();\n let answer: { status: number; text: string; retryAfter: string | null };\n try {\n answer = await this.#send(transport, url, headers, body);\n } catch (cause) {\n if (retries < MAX_RETRIES) {\n await sleep(BACKOFF_MS * 2 ** retries);\n continue;\n }\n throw new UnfazedOIDCError(\n \"unreachable\",\n `${what}: ${url} did not answer in ${retries + 1} attempts (${describe(cause)})`,\n { retryable: true, cause },\n );\n }\n const { status, text, retryAfter } = answer;\n if (status >= 200 && status < 300) return this.#entry(text, startedAt, what);\n\n const error = refusal(status, text, retryAfterMs(retryAfter, this.#now()), what);\n if (!error.retryable || retries >= MAX_RETRIES) throw error;\n const waitMs = error.retryAfterMs ?? BACKOFF_MS * 2 ** retries;\n if (waitMs > MAX_RETRY_AFTER_MS) throw error;\n await sleep(waitMs);\n }\n }\n\n async #send(\n transport: OIDCTransport,\n url: string,\n headers: Record<string, string>,\n body: string,\n ): Promise<{ status: number; text: string; retryAfter: string | null }> {\n const timeoutMs = this.#options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const controller = timeoutMs > 0 && typeof AbortController === \"function\" ? new AbortController() : undefined;\n const timer = controller ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n try {\n const signal = controller?.signal;\n const response = await untilAborted(\n transport(url, { method: \"POST\", headers, body, ...(signal ? { signal } : {}) }),\n signal,\n );\n const text = await untilAborted(response.text(), signal);\n return { status: response.status, text, retryAfter: response.headers?.get(\"retry-after\") ?? null };\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n }\n\n #entry(text: string, startedAt: number, what: string): Entry {\n const body = parseJson(text) as { token?: unknown; expires_at?: unknown } | undefined;\n const token = body?.token;\n const expiresAtMs = typeof body?.expires_at === \"string\" ? Date.parse(body.expires_at) : NaN;\n if (typeof token !== \"string\" || token === \"\" || !Number.isFinite(expiresAtMs)) {\n throw new UnfazedOIDCError(\n \"invalid_response\",\n `${what}: the platform answered 200 without a token and its expires_at: ${truncate(text)}`,\n );\n }\n // The JWT's own iat/exp are both the platform's clock, so its lifetime\n // holds even when this VM's clock is off (a guest resumed from a\n // snapshot); elapsed time is then measured on this clock alone.\n const lifetimeMs = jwtLifetimeMs(token) ?? expiresAtMs - startedAt;\n const life = Math.max(0, lifetimeMs);\n return {\n token,\n expiresAtMs,\n refreshAt: startedAt + REFRESH_AT * life,\n validUntil: startedAt + life,\n };\n }\n}\n\n/**\n * The process's own {@link UnfazedOIDC}: `UNFZ_STATE_TOKEN` and `UNFZ_API_URL`\n * read on every call, the global `fetch`, one cache.\n *\n * ```ts\n * import { oidc } from \"unfazed\";\n * const fetchWithToken = oidc.fetchWithToken(process.env.API_AUDIENCE);\n * ```\n */\nexport const oidc: UnfazedOIDC = /* @__PURE__ */ new UnfazedOIDC();\n\n/**\n * A JWT for `audience`, issued to the app this code runs in:\n * {@link UnfazedOIDC.token} on the process's {@link oidc}. Needs only\n * `UNFZ_STATE_TOKEN`.\n *\n * ```ts\n * import { oidcToken } from \"unfazed\";\n * const res = await fetch(url, { headers: { Authorization: `Bearer ${await oidcToken(url)}` } });\n * ```\n */\nexport function oidcToken(audience: string | undefined, options?: OIDCTokenOptions): Promise<string> {\n return oidc.token(audience, options);\n}\n\nfunction view(entry: Entry): OIDCToken {\n return { token: entry.token, expiresAt: new Date(entry.expiresAtMs) };\n}\n\nfunction checkAudience(audience: unknown): asserts audience is string {\n if (typeof audience === \"string\" && AUDIENCE.test(audience)) return;\n const got = typeof audience === \"string\" ? truncate(JSON.stringify(audience), 80) : String(audience);\n throw new UnfazedOIDCError(\n \"invalid_audience\",\n `unfazed: invalid OIDC audience ${got}: it must be 1 to 256 printable ASCII characters, without whitespace`,\n );\n}\n\nfunction checkTtl(ttl: unknown): void {\n if (ttl === undefined) return;\n if (typeof ttl === \"number\" && Number.isInteger(ttl) && ttl >= MIN_TTL_SECONDS && ttl <= MAX_TTL_SECONDS) return;\n throw new UnfazedOIDCError(\n \"invalid_request\",\n `unfazed: ttlSeconds must be a whole number from ${MIN_TTL_SECONDS} to ${MAX_TTL_SECONDS} (got ${String(ttl)})`,\n );\n}\n\n/** The error for a non-2xx answer; `retryable` says whether the loop asks again. */\nfunction refusal(status: number, text: string, retryAfter: number | undefined, what: string): UnfazedOIDCError {\n const body = parseJson(text) as { error?: unknown; error_description?: unknown } | undefined;\n const code = typeof body?.error === \"string\" && body.error ? body.error : codeForStatus(status);\n const description =\n typeof body?.error_description === \"string\" && body.error_description ? body.error_description : undefined;\n const detail = description ?? (body === undefined && text ? truncate(text) : undefined);\n // Never a 4xx but 429: the request itself is what is wrong.\n const retryable =\n status === 429 ||\n status === 502 ||\n status === 503 ||\n status === 504 ||\n (status >= 500 && code === \"temporarily_unavailable\");\n const hint = code === \"invalid_token\" ? ` (the platform refused ${STATE_TOKEN_ENV_VAR})` : \"\";\n const later = retryAfter === undefined ? \"\" : `; retry after ${Math.ceil(retryAfter / 1000)} s`;\n return new UnfazedOIDCError(code, `${what}: ${code} (HTTP ${status})${hint}${detail ? `: ${detail}` : \"\"}${later}`, {\n status,\n ...(description ? { description } : {}),\n retryable,\n ...(retryAfter === undefined ? {} : { retryAfterMs: retryAfter }),\n });\n}\n\nfunction codeForStatus(status: number): OIDCErrorCode {\n switch (status) {\n case 400:\n return \"invalid_request\";\n case 401:\n return \"invalid_token\";\n case 429:\n return \"rate_limited\";\n case 503:\n return \"temporarily_unavailable\";\n default:\n return \"http_error\";\n }\n}\n\n/** `Retry-After` in ms: delay-seconds or an HTTP-date; undefined when absent or unreadable. */\nfunction retryAfterMs(value: string | null, now: number): number | undefined {\n if (value === null) return undefined;\n const trimmed = value.trim();\n if (/^\\d+$/.test(trimmed)) return Number(trimmed) * 1000;\n const at = Date.parse(trimmed);\n return Number.isFinite(at) ? Math.max(0, at - now) : undefined;\n}\n\n/** `exp - iat` of a JWT, in ms, when both are there. */\nfunction jwtLifetimeMs(token: string): number | undefined {\n const payload = token.split(\".\")[1];\n if (!payload || typeof atob !== \"function\") return undefined;\n try {\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const claims = JSON.parse(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\"))) as {\n iat?: unknown;\n exp?: unknown;\n };\n const { iat, exp } = claims;\n if (typeof iat === \"number\" && typeof exp === \"number\" && Number.isFinite(iat) && exp > iat) {\n return (exp - iat) * 1000;\n }\n } catch {\n // Not a JWT we can read: the platform's expires_at decides.\n }\n return undefined;\n}\n\nfunction globalFetch(): (input: string, init: RequestInit) => Promise<Response> {\n const fetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof fetch !== \"function\") {\n throw new UnfazedConfigError(\"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch }.\");\n }\n return fetch.bind(globalThis) as (input: string, init: RequestInit) => Promise<Response>;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction aborted(): UnfazedTimeoutError {\n return new UnfazedTimeoutError(\"unfazed: OIDC token request aborted\");\n}\n\nfunction rethrowAbort(error: unknown): never {\n if (typeof error === \"object\" && error !== null && (error as { name?: unknown }).name === \"AbortError\") {\n throw aborted();\n }\n throw error;\n}\n\nfunction describe(cause: unknown): string {\n if (cause instanceof Error) return cause.name === \"AbortError\" ? \"timed out\" : cause.message;\n return String(cause);\n}\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n}\n\nfunction truncate(s: string, max = 300): string {\n return s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// GENERATED by scripts/embed-reference.ts from REFERENCE.md. Do not edit:
|
|
2
2
|
// change REFERENCE.md and run `bun run embed-reference`.
|
|
3
3
|
/** The SDK's reference: every method with its signature (REFERENCE.md). */
|
|
4
|
-
export const REFERENCE = "# unfazed SDK reference\n\nEvery method of `Unfazed`, with its signature, in one page. In a script:\n`import { Unfazed, help } from \"unfazed\"; const unfazed = new Unfazed();`\n(run_script injects the credential; elsewhere `UNFZ_API_TOKEN` or\n`npx unfazed login`; `oidc` needs neither). `help()` returns this page;\n`help(\"writeFiles\")` one entry. Methods are async and throw an\n`UnfazedError` subclass on failure.\nEvery method takes an optional last `options` with `signal` (and, for single\nrequests, `timeoutMs`). The names follow the MCP tools: `listCommits` is\n`list_commits`, `waitForEvents` is `wait_for_events`.\n\n```ts\nimport { Unfazed } from \"unfazed\";\n\nexport default async () => {\n const unfazed = new Unfazed();\n const ws = await unfazed.ensureWorkspace(\"hello\");\n const wrote = await unfazed.writeFiles(ws.id, {\n \"server.ts\": `export default { fetch: () => new Response(\"hi\") }`,\n \"package.json\": JSON.stringify({ name: \"hello\" }),\n }, { commitMessage: \"First version\" }); // ONE request, ONE commit\n const app = await unfazed.deployService({ workspaceId: ws.id, alias: \"hello\", wait: true });\n return { url: app.url, commit: wrote.commit?.sha, timings: app.timings };\n};\n```\n\n## Saved, committed, deployed\n\nA file write has three states. **Saved**: the call returned; the app's\nfiles have it and the next deploy uses it. **Committed**: on an app with\nplatform git, `commit.committed && !commit.waiting`; it is in `listCommits`.\n**Deployed**: a deploy of those files went READY (`deployService({ wait:\ntrue })`). A write never deploys by itself, except when `commit.deploys` is\ntrue (the branch head is imported like a push).\n\n`commit.waiting: true` means saved, with the commit queued for the git\nserver: it lands in order, `sha` is null until then, nothing to retry.\n`waitForCommit` (or `writeFiles(…, { waitForCommit: true })`) resolves once\nit landed.\n\n## Workspaces\n\n### `createWorkspace(name: string): Promise<Workspace>`\nA new workspace: files, env, deployments. Names are unique per owner,\ncase-insensitively; prefer `ensureWorkspace` in re-runnable scripts.\n\n### `ensureWorkspace(name: string): Promise<Workspace>`\nThe workspace named `name`, created only when missing. Safe to re-run.\n\n### `listWorkspaces(): Promise<Workspace[]>`\n\n### `liveApps(workspaceId: string): Promise<LiveApp[]>`\nThe apps the workspace serves now: `{ alias, deploymentID, domains }`.\n\n### `deleteWorkspace(workspaceId: string, options?: { withDeployments?: true, confirmAliases: string[], ignoreNotFound?: boolean }): Promise<boolean>`\nDeletes it; with `withDeployments`, every app it serves, which\n`confirmAliases` must name exactly (`liveApps`). Kept 30 days for support.\n\n### `deleteApp(app: { workspaceId, alias }, options?: { ignoreNotFound?: boolean }): Promise<DeleteAppResult>`\nDeletes one app (every deployment of the alias, its App State, env and\nbindings); the workspace and its other apps stay.\n\n### `whoami(): Promise<Whoami>`\nWho the token is, its scopes, usage and limits.\n\n## Files\n\n### `writeFiles(workspaceId: string, files: Record<string, string | { text } | { base64 } | { bytes }>, options?: { commitMessage?: string, waitForCommit?: boolean | WaitOptions, allowPartial?: boolean }): Promise<WriteFilesResult>`\nWrites every file in ONE request, which is ONE commit on an app with\nplatform git (MCP `write_workspace_files`). Result: `{ files: [{ path, ok,\nerror, file }], written, failed, totalBytes, commit, commits, landed? }`;\n`commit` is `{ sha, branch, message, committed, waiting, waitingCount,\ndeploys, conflicts, writeSHA, paused }` or null without platform git. Not\ntransactional: when a file fails it throws `UnfazedWriteError` (`.result`,\n`.failed`) unless `allowPartial`. Up to 100 files and 32 MiB per request; a\nbigger map is several requests, each a commit.\n\n```ts\nconst out = await unfazed.writeFiles(ws.id, { \"a.ts\": a, \"logo.png\": { bytes } }, { commitMessage: \"Logo\" });\nif (out.commit?.conflicts.length) console.log(\"a push changed\", out.commit.conflicts, \"mine:\", out.commit.writeSHA);\n```\n\n### `writeFile(workspaceId: string, path: string, text: string, options?: { commitMessage?, waitForCommit? }): Promise<WorkspaceFile & { commit }>`\nOne text file: `writeFiles` with one entry.\n\n### `writeBinaryFile(workspaceId: string, path: string, content: Uint8Array | ArrayBuffer | { base64: string }, options?): Promise<WorkspaceFile & { commit }>`\n\n### `waitForCommit(workspaceId: string, waitOptions?: WaitOptions): Promise<{ branch, head, waitedMs }>`\nResolves once no write of the app waits for its commit; `head` then has\nevery earlier write. Immediate (`head: null`) without platform git. Default\ntimeout 2 minutes (`UnfazedTimeoutError`; the writes stay saved).\n\n### `readFile(workspaceId: string, path: string, input?: { offset?, limit? }): Promise<{ text, totalBytes, truncated, nextOffset, file } | null>`\n\n### `listFiles(workspaceId: string, input?: { limit?, offset?, order? }): Promise<WorkspaceFilePage>`\n\n### `deleteFile(workspaceId: string, path: string): Promise<boolean>`\n`false` when there was nothing to delete.\n\n## Deploys\n\n### `deployService(options: { workspaceId, alias?, wait?, waitOptions?, timings?, runtime?, serviceMode?, healthPath?, cronSchedule?, cronPath? }): Promise<DeployResult>`\nBuilds and deploys the workspace as an HTTP service (`server.ts` default\nexport, or `unfazed.json`). Returns BUILDING at once; with `wait: true`,\nresolves READY or throws `UnfazedDeployError` (`reason` FAILED with the\nbuild error, or TIMEOUT; default 10 minutes). Every call is a new release;\nthe same `alias` redeploys that app.\n\nWith `wait: true` the result has `timings`:\n`{ client: { createMs, waitMs, totalMs, waitRequests, waitMode },\nplatform: { totalMs, buildMs, promoteMs, phases, buildMode,\nartifactTransport, buildMemoryMiB } }`, both in milliseconds. `platform` is\nwhat the platform measured (created to READY); `client` is what you waited.\nThe wait is server-side, so the two differ by about one round trip.\n\n```ts\nconst app = await unfazed.deployService({ workspaceId: ws.id, alias: \"api\", wait: true });\napp.timings?.platform?.phases; // { vm_install: 900, vm_build: 2600, admission: 40, … }\n```\n\n### `deployStatic(options: { workspaceId, alias?, wait?, spaMode? }): Promise<DeployResult>`\n\n### `deployBox(options: { workspaceId, alias, wait?, resources?: { memMiB?, vcpus?, diskGiB? } }): Promise<DeployResult>`\nA persistent Linux VM over SSH (`sshCommand`). Always pass `alias`.\n\n### `deploy(input: CreateDeploymentInput, options?: { wait?, waitOptions?, timings? }): Promise<DeployResult>`\nAny `createDeployment` input.\n\n### `waitForDeployment(target: string | { workspaceId, gitCommit, alias?, after? }, waitOptions?: WaitOptions): Promise<Deployment>`\nWaits server-side (MCP `wait_for_deployment`: one request per 45 s at most,\nanswered when the status changes) until READY; throws `UnfazedDeployError`\non FAILED or timeout. A new app's landing on its server is waited for too.\n`waitOptions: { timeoutMs?, signal?, mode?: \"server\" | \"poll\" }`. The\nplatform holds at most 16 waits of one account at once; past that a wait\nby id polls instead.\n\nInstead of an id, name the deploy a git import makes of a commit (a\nrestore, a push or a sync deploys before any id exists): `{ workspaceId,\ngitCommit, alias?, after? }`. The wait covers the import too, and a failed\nimport throws `UnfazedDeployError` with its error. `alias` is required when\nseveral apps (`apps[]`) deployed the commit; `after` (a restore's\n`waitAfter`) counts only deployments begun after it.\n\n### `getDeployment(deploymentId: string): Promise<Deployment | null>`\n\n### `getDeploymentByAlias(alias: string): Promise<Deployment | null>`\n\n### `listDeployments(input?: { workspaceID?, alias?, kind?, limit?, offset? }): Promise<DeploymentPage>`\nNewest first.\n\n### `listReleases(alias: string): Promise<Deployment[]>`\nThe alias's history; `aliasHeld` marks the live one.\n\n### `rollback(options: { alias, to?, expectedCurrentId?, retries? }): Promise<Deployment>`\nPoints the alias back at a prior READY release (default: the one before\nthe live one). A bare `rollback({ alias })` toggles; pin `to` in anything\nre-runnable.\n\n### `deleteDeployment(deploymentId: string, options?: { ignoreNotFound? }): Promise<boolean>`\n\n## Runtime, logs, events, timings\n\n### `getStatus(deploymentId: string): Promise<ServiceStatus>`\n`{ running, health, inFlight, restartCount, memSizeMiB, lastError, … }`.\n\n### `getLogs(deploymentId: string, input?: { tailBytes? }): Promise<{ stdout, stderr, truncated, … }>`\nFilter in the script: `logs.stderr.split(\"\\n\").filter((l) => l.includes(\"ERROR\"))`.\n\n### `getEvents(deploymentId: string, limit?: number): Promise<DeploymentEvent[]>`\nThe latest `limit` (default 20) lifecycle events, oldest first: `{ type,\ntypeEnum, severity, message, vmID, metadata, timings, createdAt }`. `type`\nis the event's own name, as MCP reports it: `\"deployment.build_finished\"`,\n`\"deployment.phases\"`, `\"service.vm_started\"`. `typeEnum` is the GraphQL\nenum (`UNKNOWN` for names it lacks); match on `type`. A timing event's\n`timings` is what it measured, parsed by the platform; null otherwise.\n\n### `getDeploymentTimings(deploymentId: string): Promise<PlatformTimings | null>`\nA deploy's measured phases (ms), as the platform reads them from all its\ntiming events: `{ totalMs, buildMs, promoteMs, phases, buildMode,\nartifactTransport, buildMemoryMiB }`. `parseDeploymentTimings(events)` does\nthe same for events you already read, preferring each event's `timings`;\ndurations are whole, non-negative milliseconds (anything else is dropped).\n\n### `restartService(deploymentId: string)` / `stopService(deploymentId: string)`\n\n## Env and secrets\n\nSnapshotted into a release at deploy time: redeploy the alias to apply.\n\n### `setEnv(workspaceId, name, value)` / `setSecret(workspaceId, name, value)`: `Promise<EnvVar>`\nWorkspace defaults. A name is plain or secret, never both.\n\n### `setEnvMany(workspaceId: string, vars: Record<string, string>, options?: { secret?: boolean }): Promise<EnvVar[]>`\n\n### `deleteEnv(workspaceId, name): Promise<boolean>` / `listEnv(workspaceId): Promise<EnvVar[]>`\n\n### `setAppEnv(workspaceId, alias, name, value)` / `setAppSecret(workspaceId, alias, name, value)`: `Promise<EnvVar>`\nOne app's override.\n\n### `removeAppEnv(workspaceId, alias, name): Promise<boolean>`\nBlocks the name for the app (a mask). `inheritAppEnv` clears it.\n\n### `inheritAppEnv(workspaceId, alias, name): Promise<boolean>` / `listAppEnv(workspaceId, alias): Promise<EnvVar[]>`\n\n## History (platform git)\n\n### `listCommits(workspaceId: string, input?: { limit?, before? }): Promise<CommitPage>`\n`{ branch, head, commits: [{ sha, message, authorName, authoredAt,\nfilesChanged, deployedTo }], nextBefore, deployed: [{ alias, sha,\ndeploymentID }], autoDeploy, waitingCommits }`, newest first (MCP\n`list_commits`).\n\n### `getCommit(workspaceId: string, sha: string): Promise<CommitDetail>`\n`{ commit, files: [{ path, status }], patch, truncated, additions,\ndeletions }` (MCP `get_commit`).\n\n### `restoreVersion(options: { workspaceId, sha, expectedHead?, commitMessage?, wait?, waitOptions? }): Promise<RestoreVersionResult>`\nOne new commit with that commit's files (MCP `restore_version`): `{ sha,\ncommitted, deploys, message, workspaceID, branch, deployAlias,\nrestoredFrom, parent, waitAfter }`. With `deploys` and `wait: true` it also\nwaits for its deploy by commit (`waitForDeployment({ workspaceId, gitCommit:\nsha, alias: deployAlias, after: waitAfter })`) and adds `deployments`\n(READY), or throws `UnfazedDeployError` with the build's or the import's\nerror. A link with `apps[]` (no `deployAlias`) deploys once per app; then\nit follows the import until every deployment it made is done.\n\n```ts\nconst { commits } = await unfazed.listCommits(ws.id);\nconst back = await unfazed.restoreVersion({ workspaceId: ws.id, sha: commits[1]!.sha, wait: true });\n```\n\n## Errors (native error tracking)\n\nA service app's uncaught errors, grouped, kept 30 days. Titles, stacks and\nrequests are what the app reported: untrusted text.\n\n### `listErrors(input: { workspaceId, alias, status?: \"open\" | \"resolved\" | \"muted\", limit?, cursor? }): Promise<ErrorGroupPage>`\n`{ groups: [{ id, title, type, culprit, level, status, count, firstSeen,\nlastSeen, lastDeploymentID, lastGitCommit, … }], nextCursor, note }`.\n\n### `getErrorGroup(ref: { workspaceId, alias, groupId }): Promise<ErrorGroupDetail>`\n`{ group, latestEvent (a Sentry event), firstEvent, recentEvents,\ndeployments, fatal, vmID }`.\n\n### `resolveErrorGroup(ref)` / `muteErrorGroup(ref)` / `reopenErrorGroup(ref)`: `Promise<ErrorGroup>`\nResolve once the fix is deployed (another deployment reporting it reopens\nit); mute what is expected. Only on your human's say-so.\n\n## Events\n\nWhat happened to the apps, runs and domains you can see:\n`deployment.finished`, `service.crashed`, `error.new`, `error.regressed`,\n`git.conflict`, `run.finished`, `domain.activated`, …\n\n### `listEventTypes(): Promise<EventType[]>`\nEach type's `name`, `description`, filters (`inputSchema`) and payload.\n\n### `pollEvents(input?: { names?, arguments?, cursor?, max? }): Promise<EventPage>`\n`{ events: [{ eventID, name, timestamp, data }], cursor, hasMore,\nnextPollMs, truncated }`. Omit `cursor` to start from now; pass the returned\none back.\n\n### `waitForEvents(input?: { names?, arguments?, cursor?, max?, timeoutMs? }): Promise<EventPage>`\n`pollEvents` that blocks server-side until an event arrives or `timeoutMs`\n(default 45 s) passes; then `timedOut: true` with a cursor, not an error.\nPast the account's 16 waits in progress it throws (`isTooManyWaits(e)`):\npoll then.\n\n```ts\nlet page = await unfazed.pollEvents({ names: [\"deployment.finished\"], arguments: { \"deployment.finished\": { alias: \"api\" } } });\n// … deploy …\npage = await unfazed.waitForEvents({ names: [\"deployment.finished\"], cursor: page.cursor, timeoutMs: 120_000 });\n```\n\n## Agent runs\n\n### `createRun(options: { workspaceId, prompt?, harness?, command?, setup?, verify?, access?, alias?, maxSeconds?, wait? }): Promise<Run>`\nA one-shot agent run in a fresh VM. `wait: true` polls to SUCCEEDED,\nFAILED or CANCELLED (all returned, never thrown).\n\n### `waitForRun(runId: string, waitOptions?: WaitOptions): Promise<Run>`\n\n### `getRun(runId)` / `listRuns(filter?: { workspaceId?, status?, limit?, offset? })` / `cancelRun(runId)`\n\n### `followUpRun(runId: string, options: { prompt, mode?, wait? }): Promise<Run>`\n\n### `answerRun(runId: string, answer: string): Promise<Run>`\nAnswers a BLOCKED run's question.\n\n### `runEvents(runId)` / `runConsole(runId, { tailBytes? })` / `runTranscript(runId, { tailBytes? })`\n\n### `listRunHarnesses(): Promise<RunHarness[]>`\n\n## Workload identity\n\nOIDC tokens the platform issues to the app the code runs in (a service or a\ncron job): short-lived JWTs for one audience, from the issuer\n`https://unfazed.dev/oidc`, which a service that trusts it takes instead of\nan API key. Claims include `sub`, `aud`, `owner_id`, `project_id`, `app_id`,\n`alias`, `environment` and `deployment_id`. The only credential is the app's\n`UNFZ_STATE_TOKEN`, set in each of its VMs: no API token, so a bare\n`new Unfazed()` works in an app for this. Without an instance:\n`import { oidc, oidcToken } from \"unfazed\"` (`oidcToken(audience)` is\n`oidc.token(audience)`).\n\n### `oidc.token(audience: string, options?: { ttlSeconds?, signal? }): Promise<string>`\nA JWT for `audience` (1 to 256 printable ASCII characters, no whitespace);\n`ttlSeconds` from 60 to 3600, else the platform's default. Cached per\n(audience, ttl) until 80% of its lifetime; concurrent calls share one\nrequest. 503s, network failures and 429s (after their `Retry-After`) are\nretried up to 3 times; if a refresh still fails that way, the previous token\ncomes back while it is valid. Throws `UnfazedOIDCError` (`code`, `status`,\n`description`, `retryable`, `retryAfterMs`): `not_in_app` (no\n`UNFZ_STATE_TOKEN`), `invalid_audience`, `invalid_request`, `invalid_token`,\n`rate_limited`, `oidc_disabled`, `temporarily_unavailable`, `unreachable`.\n\n```ts\nconst jwt = await unfazed.oidc.token(\"https://api.example.com\", { ttlSeconds: 300 });\nawait fetch(\"https://api.example.com/v1/things\", { headers: { Authorization: `Bearer ${jwt}` } });\n```\n\n### `oidc.tokenResponse(audience: string, options?: { ttlSeconds?, signal? }): Promise<{ token, expiresAt: Date }>`\n`oidc.token` with the token's expiry.\n\n### `oidc.fetchWithToken(audience: string, options?: { ttlSeconds?, fetch? }): fetch`\nA `fetch` that sends each request with `Authorization: Bearer <token>`\n(replacing one already there), everything else as given: for a client that\ntakes a `fetch`. An invalid audience (an unset variable) throws at once.\n\n```ts\nconst openai = new OpenAI({\n baseURL: process.env.PRIVATEINFERENCE_BASE_URL,\n apiKey: \"unused\",\n fetch: unfazed.oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE),\n});\n```\n\n## Anything else\n\n`unfazed.client` is the typed GraphQL transport over the same credential:\n`await unfazed.client.query(document, variables)`. `operations` exports\nevery document the SDK uses.\n";
|
|
4
|
+
export const REFERENCE = "# unfazed SDK reference\n\nEvery method of `Unfazed`, with its signature, in one page. In a script:\n`import { Unfazed, help } from \"unfazed\"; const unfazed = new Unfazed();`\n(run_script injects the credential; elsewhere `UNFZ_API_TOKEN` or\n`npx unfazed login`; `oidc` needs neither). `help()` returns this page;\n`help(\"writeFiles\")` one entry. Methods are async and throw an\n`UnfazedError` subclass on failure.\nEvery method takes an optional last `options` with `signal` (and, for single\nrequests, `timeoutMs`). The names follow the MCP tools: `listCommits` is\n`list_commits`, `waitForEvents` is `wait_for_events`.\n\n```ts\nimport { Unfazed } from \"unfazed\";\n\nexport default async () => {\n const unfazed = new Unfazed();\n const ws = await unfazed.ensureWorkspace(\"hello\");\n const wrote = await unfazed.writeFiles(ws.id, {\n \"server.ts\": `export default { fetch: () => new Response(\"hi\") }`,\n \"package.json\": JSON.stringify({ name: \"hello\" }),\n }, { commitMessage: \"First version\" }); // ONE request, ONE commit\n const app = await unfazed.deployService({ workspaceId: ws.id, alias: \"hello\", wait: true });\n return { url: app.url, commit: wrote.commit?.sha, timings: app.timings };\n};\n```\n\n## Saved, committed, deployed\n\nA file write has three states. **Saved**: the call returned; the app's\nfiles have it and the next deploy uses it. **Committed**: on an app with\nplatform git, `commit.committed && !commit.waiting`; it is in `listCommits`.\n**Deployed**: a deploy of those files went READY (`deployService({ wait:\ntrue })`). A write never deploys by itself, except when `commit.deploys` is\ntrue (the branch head is imported like a push).\n\n`commit.waiting: true` means saved, with the commit queued for the git\nserver: it lands in order, `sha` is null until then, nothing to retry.\n`waitForCommit` (or `writeFiles(…, { waitForCommit: true })`) resolves once\nit landed.\n\n## Workspaces\n\n### `createWorkspace(name: string): Promise<Workspace>`\nA new workspace: files, env, deployments. Names are unique per owner,\ncase-insensitively; prefer `ensureWorkspace` in re-runnable scripts.\n\n### `ensureWorkspace(name: string): Promise<Workspace>`\nThe workspace named `name`, created only when missing. Safe to re-run.\n\n### `listWorkspaces(): Promise<Workspace[]>`\n\n### `liveApps(workspaceId: string): Promise<LiveApp[]>`\nThe apps the workspace serves now: `{ alias, deploymentID, domains }`.\n\n### `deleteWorkspace(workspaceId: string, options?: { withDeployments?: true, confirmAliases: string[], ignoreNotFound?: boolean }): Promise<boolean>`\nDeletes it; with `withDeployments`, every app it serves, which\n`confirmAliases` must name exactly (`liveApps`). Kept 30 days for support.\n\n### `deleteApp(app: { workspaceId, alias }, options?: { ignoreNotFound?: boolean }): Promise<DeleteAppResult>`\nDeletes one app (every deployment of the alias, its App State, env and\nbindings); the workspace and its other apps stay.\n\n### `whoami(): Promise<Whoami>`\nWho the token is, its scopes, usage and limits.\n\n## Files\n\n### `writeFiles(workspaceId: string, files: Record<string, string | { text } | { base64 } | { bytes }>, options?: { commitMessage?: string, waitForCommit?: boolean | WaitOptions, allowPartial?: boolean }): Promise<WriteFilesResult>`\nWrites every file in ONE request, which is ONE commit on an app with\nplatform git (MCP `write_workspace_files`). Result: `{ files: [{ path, ok,\nerror, file }], written, failed, totalBytes, commit, commits, landed? }`;\n`commit` is `{ sha, branch, message, committed, waiting, waitingCount,\ndeploys, conflicts, writeSHA, paused }` or null without platform git. Not\ntransactional: when a file fails it throws `UnfazedWriteError` (`.result`,\n`.failed`) unless `allowPartial`. Up to 100 files and 32 MiB per request; a\nbigger map is several requests, each a commit.\n\n```ts\nconst out = await unfazed.writeFiles(ws.id, { \"a.ts\": a, \"logo.png\": { bytes } }, { commitMessage: \"Logo\" });\nif (out.commit?.conflicts.length) console.log(\"a push changed\", out.commit.conflicts, \"mine:\", out.commit.writeSHA);\n```\n\n### `writeFile(workspaceId: string, path: string, text: string, options?: { commitMessage?, waitForCommit? }): Promise<WorkspaceFile & { commit }>`\nOne text file: `writeFiles` with one entry.\n\n### `writeBinaryFile(workspaceId: string, path: string, content: Uint8Array | ArrayBuffer | { base64: string }, options?): Promise<WorkspaceFile & { commit }>`\n\n### `waitForCommit(workspaceId: string, waitOptions?: WaitOptions): Promise<{ branch, head, waitedMs }>`\nResolves once no write of the app waits for its commit; `head` then has\nevery earlier write. Immediate (`head: null`) without platform git. Default\ntimeout 2 minutes (`UnfazedTimeoutError`; the writes stay saved).\n\n### `readFile(workspaceId: string, path: string, input?: { offset?, limit? }): Promise<{ text, totalBytes, truncated, nextOffset, file } | null>`\n\n### `listFiles(workspaceId: string, input?: { limit?, offset?, order? }): Promise<WorkspaceFilePage>`\n\n### `deleteFile(workspaceId: string, path: string): Promise<boolean>`\n`false` when there was nothing to delete.\n\n## Deploys\n\n### `deployService(options: { workspaceId, alias?, wait?, waitOptions?, timings?, runtime?, serviceMode?, healthPath?, cronSchedule?, cronPath? }): Promise<DeployResult>`\nBuilds and deploys the workspace as an HTTP service (`server.ts` default\nexport, or `unfazed.json`). Returns BUILDING at once; with `wait: true`,\nresolves READY or throws `UnfazedDeployError` (`reason` FAILED with the\nbuild error, or TIMEOUT; default 10 minutes). Every call is a new release;\nthe same `alias` redeploys that app.\n\nWith `wait: true` the result has `timings`:\n`{ client: { createMs, waitMs, totalMs, waitRequests, waitMode },\nplatform: { totalMs, buildMs, promoteMs, phases, buildMode,\nartifactTransport, buildMemoryMiB } }`, both in milliseconds. `platform` is\nwhat the platform measured (created to READY); `client` is what you waited.\nThe wait is server-side, so the two differ by about one round trip.\n\n```ts\nconst app = await unfazed.deployService({ workspaceId: ws.id, alias: \"api\", wait: true });\napp.timings?.platform?.phases; // { vm_install: 900, vm_build: 2600, admission: 40, … }\n```\n\n### `deployStatic(options: { workspaceId, alias?, wait?, spaMode? }): Promise<DeployResult>`\n\n### `deployBox(options: { workspaceId, alias, wait?, resources?: { memMiB?, vcpus?, diskGiB? } }): Promise<DeployResult>`\nA persistent Linux VM over SSH (`sshCommand`). Always pass `alias`.\n\n### `deploy(input: CreateDeploymentInput, options?: { wait?, waitOptions?, timings? }): Promise<DeployResult>`\nAny `createDeployment` input.\n\n### `waitForDeployment(target: string | { workspaceId, gitCommit, alias?, after? }, waitOptions?: WaitOptions): Promise<Deployment>`\nWaits server-side (MCP `wait_for_deployment`: one request per 45 s at most,\nanswered when the status changes) until READY; throws `UnfazedDeployError`\non FAILED or timeout. A new app's landing on its server is waited for too.\n`waitOptions: { timeoutMs?, signal?, mode?: \"server\" | \"poll\" }`. The\nplatform holds at most 16 waits of one account at once; past that a wait\nby id polls instead.\n\nInstead of an id, name the deploy a git import makes of a commit (a\nrestore, a push or a sync deploys before any id exists): `{ workspaceId,\ngitCommit, alias?, after? }`. The wait covers the import too, and a failed\nimport throws `UnfazedDeployError` with its error. `alias` is required when\nseveral apps (`apps[]`) deployed the commit; `after` (a restore's\n`waitAfter`) counts only deployments begun after it.\n\n### `getDeployment(deploymentId: string): Promise<Deployment | null>`\n\n### `getDeploymentByAlias(alias: string): Promise<Deployment | null>`\n\n### `listDeployments(input?: { workspaceID?, alias?, kind?, limit?, offset? }): Promise<DeploymentPage>`\nNewest first.\n\n### `listReleases(alias: string): Promise<Deployment[]>`\nThe alias's history; `aliasHeld` marks the live one.\n\n### `rollback(options: { alias, to?, expectedCurrentId?, retries? }): Promise<Deployment>`\nPoints the alias back at a prior READY release (default: the one before\nthe live one). A bare `rollback({ alias })` toggles; pin `to` in anything\nre-runnable.\n\n### `deleteDeployment(deploymentId: string, options?: { ignoreNotFound? }): Promise<boolean>`\n\n## Runtime, logs, events, timings\n\n### `getStatus(deploymentId: string): Promise<ServiceStatus>`\n`{ running, health, inFlight, restartCount, memSizeMiB, lastError, … }`.\n\n### `getLogs(deploymentId: string, input?: { tailBytes? }): Promise<{ stdout, stderr, truncated, … }>`\nFilter in the script: `logs.stderr.split(\"\\n\").filter((l) => l.includes(\"ERROR\"))`.\n\n### `getEvents(deploymentId: string, limit?: number): Promise<DeploymentEvent[]>`\nThe latest `limit` (default 20) lifecycle events, oldest first: `{ type,\ntypeEnum, severity, message, vmID, metadata, timings, createdAt }`. `type`\nis the event's own name, as MCP reports it: `\"deployment.build_finished\"`,\n`\"deployment.phases\"`, `\"service.vm_started\"`. `typeEnum` is the GraphQL\nenum (`UNKNOWN` for names it lacks); match on `type`. A timing event's\n`timings` is what it measured, parsed by the platform; null otherwise.\n\n### `getDeploymentTimings(deploymentId: string): Promise<PlatformTimings | null>`\nA deploy's measured phases (ms), as the platform reads them from all its\ntiming events: `{ totalMs, buildMs, promoteMs, phases, buildMode,\nartifactTransport, buildMemoryMiB }`. `parseDeploymentTimings(events)` does\nthe same for events you already read, preferring each event's `timings`;\ndurations are whole, non-negative milliseconds (anything else is dropped).\n\n### `restartService(deploymentId: string)` / `stopService(deploymentId: string)`\n\n## Env and secrets\n\nSnapshotted into a release at deploy time: redeploy the alias to apply.\n\n### `setEnv(workspaceId, name, value)` / `setSecret(workspaceId, name, value)`: `Promise<EnvVar>`\nWorkspace defaults. A name is plain or secret, never both.\n\n### `setEnvMany(workspaceId: string, vars: Record<string, string>, options?: { secret?: boolean }): Promise<EnvVar[]>`\n\n### `deleteEnv(workspaceId, name): Promise<boolean>` / `listEnv(workspaceId): Promise<EnvVar[]>`\n\n### `setAppEnv(workspaceId, alias, name, value)` / `setAppSecret(workspaceId, alias, name, value)`: `Promise<EnvVar>`\nOne app's override.\n\n### `removeAppEnv(workspaceId, alias, name): Promise<boolean>`\nBlocks the name for the app (a mask). `inheritAppEnv` clears it.\n\n### `inheritAppEnv(workspaceId, alias, name): Promise<boolean>` / `listAppEnv(workspaceId, alias): Promise<EnvVar[]>`\n\n## History (platform git)\n\n### `listCommits(workspaceId: string, input?: { limit?, before? }): Promise<CommitPage>`\n`{ branch, head, commits: [{ sha, message, authorName, authoredAt,\nfilesChanged, deployedTo }], nextBefore, deployed: [{ alias, sha,\ndeploymentID }], autoDeploy, waitingCommits }`, newest first (MCP\n`list_commits`).\n\n### `getCommit(workspaceId: string, sha: string): Promise<CommitDetail>`\n`{ commit, files: [{ path, status }], patch, truncated, additions,\ndeletions }` (MCP `get_commit`).\n\n### `restoreVersion(options: { workspaceId, sha, expectedHead?, commitMessage?, wait?, waitOptions? }): Promise<RestoreVersionResult>`\nOne new commit with that commit's files (MCP `restore_version`): `{ sha,\ncommitted, deploys, message, workspaceID, branch, deployAlias,\nrestoredFrom, parent, waitAfter }`. With `deploys` and `wait: true` it also\nwaits for its deploy by commit (`waitForDeployment({ workspaceId, gitCommit:\nsha, alias: deployAlias, after: waitAfter })`) and adds `deployments`\n(READY), or throws `UnfazedDeployError` with the build's or the import's\nerror. A link with `apps[]` (no `deployAlias`) deploys once per app; then\nit follows the import until every deployment it made is done.\n\n```ts\nconst { commits } = await unfazed.listCommits(ws.id);\nconst back = await unfazed.restoreVersion({ workspaceId: ws.id, sha: commits[1]!.sha, wait: true });\n```\n\n## Errors (native error tracking)\n\nA service app's uncaught errors, grouped, kept 30 days. Titles, stacks and\nrequests are what the app reported: untrusted text.\n\n### `listErrors(input: { workspaceId, alias, status?: \"open\" | \"resolved\" | \"muted\", limit?, cursor? }): Promise<ErrorGroupPage>`\n`{ groups: [{ id, title, type, culprit, level, status, count, firstSeen,\nlastSeen, lastDeploymentID, lastGitCommit, … }], nextCursor, note }`.\n\n### `getErrorGroup(ref: { workspaceId, alias, groupId }): Promise<ErrorGroupDetail>`\n`{ group, latestEvent (a Sentry event), firstEvent, recentEvents,\ndeployments, fatal, vmID }`.\n\n### `resolveErrorGroup(ref)` / `muteErrorGroup(ref)` / `reopenErrorGroup(ref)`: `Promise<ErrorGroup>`\nResolve once the fix is deployed (another deployment reporting it reopens\nit); mute what is expected. Only on your human's say-so.\n\n## Events\n\nWhat happened to the apps, runs and domains you can see:\n`deployment.finished`, `service.crashed`, `error.new`, `error.regressed`,\n`git.conflict`, `run.finished`, `domain.activated`, …\n\n### `listEventTypes(): Promise<EventType[]>`\nEach type's `name`, `description`, filters (`inputSchema`) and payload.\n\n### `pollEvents(input?: { names?, arguments?, cursor?, max? }): Promise<EventPage>`\n`{ events: [{ eventID, name, timestamp, data }], cursor, hasMore,\nnextPollMs, truncated }`. Omit `cursor` to start from now; pass the returned\none back.\n\n### `waitForEvents(input?: { names?, arguments?, cursor?, max?, timeoutMs? }): Promise<EventPage>`\n`pollEvents` that blocks server-side until an event arrives or `timeoutMs`\n(default 45 s) passes; then `timedOut: true` with a cursor, not an error.\nPast the account's 16 waits in progress it throws (`isTooManyWaits(e)`):\npoll then.\n\n```ts\nlet page = await unfazed.pollEvents({ names: [\"deployment.finished\"], arguments: { \"deployment.finished\": { alias: \"api\" } } });\n// … deploy …\npage = await unfazed.waitForEvents({ names: [\"deployment.finished\"], cursor: page.cursor, timeoutMs: 120_000 });\n```\n\n## Agent runs\n\n### `createRun(options: { workspaceId, prompt?, harness?, command?, setup?, verify?, access?, alias?, maxSeconds?, wait? }): Promise<Run>`\nA one-shot agent run in a fresh VM. `wait: true` polls to SUCCEEDED,\nFAILED or CANCELLED (all returned, never thrown).\n\n### `waitForRun(runId: string, waitOptions?: WaitOptions): Promise<Run>`\n\n### `getRun(runId)` / `listRuns(filter?: { workspaceId?, status?, limit?, offset? })` / `cancelRun(runId)`\n\n### `followUpRun(runId: string, options: { prompt, mode?, wait? }): Promise<Run>`\n\n### `answerRun(runId: string, answer: string): Promise<Run>`\nAnswers a BLOCKED run's question.\n\n### `runEvents(runId)` / `runConsole(runId, { tailBytes? })` / `runTranscript(runId, { tailBytes? })`\n\n### `listRunHarnesses(): Promise<RunHarness[]>`\n\n## Workload identity\n\nOIDC tokens the platform issues to the app the code runs in (a service or a\ncron job): short-lived JWTs for one audience, from the issuer\n`https://unfazed.dev/oidc`, which a service that trusts it takes instead of\nan API key. Claims include `sub`, `aud`, `owner_id`, `project_id`, `app_id`,\n`alias`, `environment` and `deployment_id`. The only credential is the app's\n`UNFZ_STATE_TOKEN`, set in each of its VMs: no API token, so a bare\n`new Unfazed()` works in an app for this. Without an instance:\n`import { oidc, oidcToken } from \"unfazed\"` (`oidcToken(audience)` is\n`oidc.token(audience)`).\n\n### `oidc.token(audience: string, options?: { ttlSeconds?, signal? }): Promise<string>`\nA JWT for `audience` (1 to 256 printable ASCII characters, no whitespace);\n`ttlSeconds` from 60 to 3600, else the platform's default. Cached per\n(audience, ttl) until 80% of its lifetime; concurrent calls share one\nrequest. 503s, network failures and 429s (after their `Retry-After`) are\nretried up to 3 times; if a refresh still fails that way, the previous token\ncomes back while it is valid. Throws `UnfazedOIDCError` (`code`, `status`,\n`description`, `retryable`, `retryAfterMs`): `not_in_app` (no\n`UNFZ_STATE_TOKEN`), `invalid_audience`, `invalid_request`, `invalid_token`,\n`rate_limited`, `oidc_disabled`, `temporarily_unavailable`, `unreachable`.\n\n```ts\nconst jwt = await unfazed.oidc.token(\"https://api.example.com\", { ttlSeconds: 300 });\nawait fetch(\"https://api.example.com/v1/things\", { headers: { Authorization: `Bearer ${jwt}` } });\n```\n\n### `oidc.tokenResponse(audience: string, options?: { ttlSeconds?, signal? }): Promise<{ token, expiresAt: Date }>`\n`oidc.token` with the token's expiry.\n\n### `oidc.fetchWithToken(audience: string, options?: { ttlSeconds?, fetch? }): fetch`\nA `fetch` that sends each request with `Authorization: Bearer <token>`\n(replacing one already there), everything else as given: for a client that\ntakes a `fetch`. An invalid audience (an unset variable) throws at once.\n\n```ts\nconst openai = new OpenAI({\n baseURL: process.env.API_BASE_URL,\n apiKey: \"unused\",\n fetch: unfazed.oidc.fetchWithToken(process.env.API_AUDIENCE),\n});\n```\n\n## Anything else\n\n`unfazed.client` is the typed GraphQL transport over the same credential:\n`await unfazed.client.query(document, variables)`. `operations` exports\nevery document the SDK uses.\n";
|
|
5
5
|
//# sourceMappingURL=reference.generated.js.map
|