unfazed 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/README.md +201 -52
- package/REFERENCE.md +284 -0
- 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/errors.d.ts +64 -0
- package/dist/errors.js +95 -0
- package/dist/errors.js.map +1 -1
- package/dist/generated/gql.d.ts +78 -2
- package/dist/generated/gql.js +21 -2
- package/dist/generated/gql.js.map +1 -1
- package/dist/generated/graphql.d.ts +505 -6
- package/dist/generated/graphql.js +439 -2
- package/dist/generated/graphql.js.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/operations.d.ts +116 -5
- package/dist/operations.js +379 -7
- package/dist/operations.js.map +1 -1
- package/dist/reference.d.ts +18 -0
- package/dist/reference.generated.d.ts +2 -0
- package/dist/reference.generated.js +5 -0
- package/dist/reference.generated.js.map +1 -0
- package/dist/reference.js +38 -0
- package/dist/reference.js.map +1 -0
- package/dist/unfazed.d.ts +544 -67
- package/dist/unfazed.js +687 -63
- package/dist/unfazed.js.map +1 -1
- package/dist/wait.d.ts +7 -0
- package/dist/wait.js.map +1 -1
- package/dist/workflow/nitro.js +5 -3
- package/dist/workflow/nitro.js.map +1 -1
- package/package.json +8 -4
- package/schema/SOURCE.json +4 -4
- package/schema/schema.graphqls +1628 -30
package/REFERENCE.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# unfazed SDK reference
|
|
2
|
+
|
|
3
|
+
Every method of `Unfazed`, with its signature, in one page. In a script:
|
|
4
|
+
`import { Unfazed, help } from "unfazed"; const unfazed = new Unfazed();`
|
|
5
|
+
(run_script injects the credential; elsewhere `UNFZ_API_TOKEN` or
|
|
6
|
+
`npx unfazed login`). `help()` returns this page; `help("writeFiles")` one
|
|
7
|
+
entry. Methods are async and throw an `UnfazedError` subclass on failure.
|
|
8
|
+
Every method takes an optional last `options` with `signal` (and, for single
|
|
9
|
+
requests, `timeoutMs`). The names follow the MCP tools: `listCommits` is
|
|
10
|
+
`list_commits`, `waitForEvents` is `wait_for_events`.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { Unfazed } from "unfazed";
|
|
14
|
+
|
|
15
|
+
export default async () => {
|
|
16
|
+
const unfazed = new Unfazed();
|
|
17
|
+
const ws = await unfazed.ensureWorkspace("hello");
|
|
18
|
+
const wrote = await unfazed.writeFiles(ws.id, {
|
|
19
|
+
"server.ts": `export default { fetch: () => new Response("hi") }`,
|
|
20
|
+
"package.json": JSON.stringify({ name: "hello" }),
|
|
21
|
+
}, { commitMessage: "First version" }); // ONE request, ONE commit
|
|
22
|
+
const app = await unfazed.deployService({ workspaceId: ws.id, alias: "hello", wait: true });
|
|
23
|
+
return { url: app.url, commit: wrote.commit?.sha, timings: app.timings };
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Saved, committed, deployed
|
|
28
|
+
|
|
29
|
+
A file write has three states. **Saved**: the call returned; the app's
|
|
30
|
+
files have it and the next deploy uses it. **Committed**: on an app with
|
|
31
|
+
platform git, `commit.committed && !commit.waiting`; it is in `listCommits`.
|
|
32
|
+
**Deployed**: a deploy of those files went READY (`deployService({ wait:
|
|
33
|
+
true })`). A write never deploys by itself, except when `commit.deploys` is
|
|
34
|
+
true (the branch head is imported like a push).
|
|
35
|
+
|
|
36
|
+
`commit.waiting: true` means saved, with the commit queued for the git
|
|
37
|
+
server: it lands in order, `sha` is null until then, nothing to retry.
|
|
38
|
+
`waitForCommit` (or `writeFiles(…, { waitForCommit: true })`) resolves once
|
|
39
|
+
it landed.
|
|
40
|
+
|
|
41
|
+
## Workspaces
|
|
42
|
+
|
|
43
|
+
### `createWorkspace(name: string): Promise<Workspace>`
|
|
44
|
+
A new workspace: files, env, deployments. Names are unique per owner,
|
|
45
|
+
case-insensitively; prefer `ensureWorkspace` in re-runnable scripts.
|
|
46
|
+
|
|
47
|
+
### `ensureWorkspace(name: string): Promise<Workspace>`
|
|
48
|
+
The workspace named `name`, created only when missing. Safe to re-run.
|
|
49
|
+
|
|
50
|
+
### `listWorkspaces(): Promise<Workspace[]>`
|
|
51
|
+
|
|
52
|
+
### `liveApps(workspaceId: string): Promise<LiveApp[]>`
|
|
53
|
+
The apps the workspace serves now: `{ alias, deploymentID, domains }`.
|
|
54
|
+
|
|
55
|
+
### `deleteWorkspace(workspaceId: string, options?: { withDeployments?: true, confirmAliases: string[], ignoreNotFound?: boolean }): Promise<boolean>`
|
|
56
|
+
Deletes it; with `withDeployments`, every app it serves, which
|
|
57
|
+
`confirmAliases` must name exactly (`liveApps`). Kept 30 days for support.
|
|
58
|
+
|
|
59
|
+
### `deleteApp(app: { workspaceId, alias }, options?: { ignoreNotFound?: boolean }): Promise<DeleteAppResult>`
|
|
60
|
+
Deletes one app (every deployment of the alias, its App State, env and
|
|
61
|
+
bindings); the workspace and its other apps stay.
|
|
62
|
+
|
|
63
|
+
### `whoami(): Promise<Whoami>`
|
|
64
|
+
Who the token is, its scopes, usage and limits.
|
|
65
|
+
|
|
66
|
+
## Files
|
|
67
|
+
|
|
68
|
+
### `writeFiles(workspaceId: string, files: Record<string, string | { text } | { base64 } | { bytes }>, options?: { commitMessage?: string, waitForCommit?: boolean | WaitOptions, allowPartial?: boolean }): Promise<WriteFilesResult>`
|
|
69
|
+
Writes every file in ONE request, which is ONE commit on an app with
|
|
70
|
+
platform git (MCP `write_workspace_files`). Result: `{ files: [{ path, ok,
|
|
71
|
+
error, file }], written, failed, totalBytes, commit, commits, landed? }`;
|
|
72
|
+
`commit` is `{ sha, branch, message, committed, waiting, waitingCount,
|
|
73
|
+
deploys, conflicts, writeSHA, paused }` or null without platform git. Not
|
|
74
|
+
transactional: when a file fails it throws `UnfazedWriteError` (`.result`,
|
|
75
|
+
`.failed`) unless `allowPartial`. Up to 100 files and 32 MiB per request; a
|
|
76
|
+
bigger map is several requests, each a commit.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const out = await unfazed.writeFiles(ws.id, { "a.ts": a, "logo.png": { bytes } }, { commitMessage: "Logo" });
|
|
80
|
+
if (out.commit?.conflicts.length) console.log("a push changed", out.commit.conflicts, "mine:", out.commit.writeSHA);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `writeFile(workspaceId: string, path: string, text: string, options?: { commitMessage?, waitForCommit? }): Promise<WorkspaceFile & { commit }>`
|
|
84
|
+
One text file: `writeFiles` with one entry.
|
|
85
|
+
|
|
86
|
+
### `writeBinaryFile(workspaceId: string, path: string, content: Uint8Array | ArrayBuffer | { base64: string }, options?): Promise<WorkspaceFile & { commit }>`
|
|
87
|
+
|
|
88
|
+
### `waitForCommit(workspaceId: string, waitOptions?: WaitOptions): Promise<{ branch, head, waitedMs }>`
|
|
89
|
+
Resolves once no write of the app waits for its commit; `head` then has
|
|
90
|
+
every earlier write. Immediate (`head: null`) without platform git. Default
|
|
91
|
+
timeout 2 minutes (`UnfazedTimeoutError`; the writes stay saved).
|
|
92
|
+
|
|
93
|
+
### `readFile(workspaceId: string, path: string, input?: { offset?, limit? }): Promise<{ text, totalBytes, truncated, nextOffset, file } | null>`
|
|
94
|
+
|
|
95
|
+
### `listFiles(workspaceId: string, input?: { limit?, offset?, order? }): Promise<WorkspaceFilePage>`
|
|
96
|
+
|
|
97
|
+
### `deleteFile(workspaceId: string, path: string): Promise<boolean>`
|
|
98
|
+
`false` when there was nothing to delete.
|
|
99
|
+
|
|
100
|
+
## Deploys
|
|
101
|
+
|
|
102
|
+
### `deployService(options: { workspaceId, alias?, wait?, waitOptions?, timings?, runtime?, serviceMode?, healthPath?, cronSchedule?, cronPath? }): Promise<DeployResult>`
|
|
103
|
+
Builds and deploys the workspace as an HTTP service (`server.ts` default
|
|
104
|
+
export, or `unfazed.json`). Returns BUILDING at once; with `wait: true`,
|
|
105
|
+
resolves READY or throws `UnfazedDeployError` (`reason` FAILED with the
|
|
106
|
+
build error, or TIMEOUT; default 10 minutes). Every call is a new release;
|
|
107
|
+
the same `alias` redeploys that app.
|
|
108
|
+
|
|
109
|
+
With `wait: true` the result has `timings`:
|
|
110
|
+
`{ client: { createMs, waitMs, totalMs, waitRequests, waitMode },
|
|
111
|
+
platform: { totalMs, buildMs, promoteMs, phases, buildMode,
|
|
112
|
+
artifactTransport, buildMemoryMiB } }`, both in milliseconds. `platform` is
|
|
113
|
+
what the platform measured (created to READY); `client` is what you waited.
|
|
114
|
+
The wait is server-side, so the two differ by about one round trip.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const app = await unfazed.deployService({ workspaceId: ws.id, alias: "api", wait: true });
|
|
118
|
+
app.timings?.platform?.phases; // { vm_install: 900, vm_build: 2600, admission: 40, … }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `deployStatic(options: { workspaceId, alias?, wait?, spaMode? }): Promise<DeployResult>`
|
|
122
|
+
|
|
123
|
+
### `deployBox(options: { workspaceId, alias, wait?, resources?: { memMiB?, vcpus?, diskGiB? } }): Promise<DeployResult>`
|
|
124
|
+
A persistent Linux VM over SSH (`sshCommand`). Always pass `alias`.
|
|
125
|
+
|
|
126
|
+
### `deploy(input: CreateDeploymentInput, options?: { wait?, waitOptions?, timings? }): Promise<DeployResult>`
|
|
127
|
+
Any `createDeployment` input.
|
|
128
|
+
|
|
129
|
+
### `waitForDeployment(deploymentId: string, waitOptions?: WaitOptions): Promise<Deployment>`
|
|
130
|
+
Waits server-side (MCP `wait_for_deployment`: one request per 45 s at most,
|
|
131
|
+
answered when the status changes) until READY; throws `UnfazedDeployError`
|
|
132
|
+
on FAILED or timeout. A new app's landing on its server is waited for too.
|
|
133
|
+
`waitOptions: { timeoutMs?, signal?, mode?: "server" | "poll" }`. The
|
|
134
|
+
platform holds at most 16 waits of one account at once; past that a wait
|
|
135
|
+
polls instead.
|
|
136
|
+
|
|
137
|
+
### `getDeployment(deploymentId: string): Promise<Deployment | null>`
|
|
138
|
+
|
|
139
|
+
### `getDeploymentByAlias(alias: string): Promise<Deployment | null>`
|
|
140
|
+
|
|
141
|
+
### `listDeployments(input?: { workspaceID?, alias?, kind?, limit?, offset? }): Promise<DeploymentPage>`
|
|
142
|
+
Newest first.
|
|
143
|
+
|
|
144
|
+
### `listReleases(alias: string): Promise<Deployment[]>`
|
|
145
|
+
The alias's history; `aliasHeld` marks the live one.
|
|
146
|
+
|
|
147
|
+
### `rollback(options: { alias, to?, expectedCurrentId?, retries? }): Promise<Deployment>`
|
|
148
|
+
Points the alias back at a prior READY release (default: the one before
|
|
149
|
+
the live one). A bare `rollback({ alias })` toggles; pin `to` in anything
|
|
150
|
+
re-runnable.
|
|
151
|
+
|
|
152
|
+
### `deleteDeployment(deploymentId: string, options?: { ignoreNotFound? }): Promise<boolean>`
|
|
153
|
+
|
|
154
|
+
## Runtime, logs, events, timings
|
|
155
|
+
|
|
156
|
+
### `getStatus(deploymentId: string): Promise<ServiceStatus>`
|
|
157
|
+
`{ running, health, inFlight, restartCount, memSizeMiB, lastError, … }`.
|
|
158
|
+
|
|
159
|
+
### `getLogs(deploymentId: string, input?: { tailBytes? }): Promise<{ stdout, stderr, truncated, … }>`
|
|
160
|
+
Filter in the script: `logs.stderr.split("\n").filter((l) => l.includes("ERROR"))`.
|
|
161
|
+
|
|
162
|
+
### `getEvents(deploymentId: string, limit?: number): Promise<DeploymentEvent[]>`
|
|
163
|
+
The latest `limit` (default 20) lifecycle events, oldest first: `{ type,
|
|
164
|
+
typeEnum, severity, message, vmID, metadata, createdAt }`. `type` is the
|
|
165
|
+
event's own name, as MCP reports it: `"deployment.build_finished"`,
|
|
166
|
+
`"deployment.phases"`, `"service.vm_started"`. `typeEnum` is the GraphQL
|
|
167
|
+
enum (`UNKNOWN` for names it lacks); match on `type`.
|
|
168
|
+
|
|
169
|
+
### `getDeploymentTimings(deploymentId: string): Promise<PlatformTimings | null>`
|
|
170
|
+
A deploy's measured phases (ms) from its timing events: `{ totalMs,
|
|
171
|
+
buildMs, promoteMs, phases, buildMode, artifactTransport, buildMemoryMiB }`.
|
|
172
|
+
`parseDeploymentTimings(events)` does the same for events you already read.
|
|
173
|
+
|
|
174
|
+
### `restartService(deploymentId: string)` / `stopService(deploymentId: string)`
|
|
175
|
+
|
|
176
|
+
## Env and secrets
|
|
177
|
+
|
|
178
|
+
Snapshotted into a release at deploy time: redeploy the alias to apply.
|
|
179
|
+
|
|
180
|
+
### `setEnv(workspaceId, name, value)` / `setSecret(workspaceId, name, value)`: `Promise<EnvVar>`
|
|
181
|
+
Workspace defaults. A name is plain or secret, never both.
|
|
182
|
+
|
|
183
|
+
### `setEnvMany(workspaceId: string, vars: Record<string, string>, options?: { secret?: boolean }): Promise<EnvVar[]>`
|
|
184
|
+
|
|
185
|
+
### `deleteEnv(workspaceId, name): Promise<boolean>` / `listEnv(workspaceId): Promise<EnvVar[]>`
|
|
186
|
+
|
|
187
|
+
### `setAppEnv(workspaceId, alias, name, value)` / `setAppSecret(workspaceId, alias, name, value)`: `Promise<EnvVar>`
|
|
188
|
+
One app's override.
|
|
189
|
+
|
|
190
|
+
### `removeAppEnv(workspaceId, alias, name): Promise<boolean>`
|
|
191
|
+
Blocks the name for the app (a mask). `inheritAppEnv` clears it.
|
|
192
|
+
|
|
193
|
+
### `inheritAppEnv(workspaceId, alias, name): Promise<boolean>` / `listAppEnv(workspaceId, alias): Promise<EnvVar[]>`
|
|
194
|
+
|
|
195
|
+
## History (platform git)
|
|
196
|
+
|
|
197
|
+
### `listCommits(workspaceId: string, input?: { limit?, before? }): Promise<CommitPage>`
|
|
198
|
+
`{ branch, head, commits: [{ sha, message, authorName, authoredAt,
|
|
199
|
+
filesChanged, deployedTo }], nextBefore, deployed: [{ alias, sha,
|
|
200
|
+
deploymentID }], autoDeploy, waitingCommits }`, newest first (MCP
|
|
201
|
+
`list_commits`).
|
|
202
|
+
|
|
203
|
+
### `getCommit(workspaceId: string, sha: string): Promise<CommitDetail>`
|
|
204
|
+
`{ commit, files: [{ path, status }], patch, truncated, additions,
|
|
205
|
+
deletions }` (MCP `get_commit`).
|
|
206
|
+
|
|
207
|
+
### `restoreVersion(options: { workspaceId, sha, expectedHead?, commitMessage?, wait?, waitOptions? }): Promise<RestoreVersionResult>`
|
|
208
|
+
One new commit with that commit's files (MCP `restore_version`): `{ sha,
|
|
209
|
+
committed, deploys, message, branch, deployAlias, restoredFrom, parent }`.
|
|
210
|
+
With `deploys` and `wait: true` it also waits for the import that deploys
|
|
211
|
+
it and adds `deployments` (READY), or throws `UnfazedDeployError`.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const { commits } = await unfazed.listCommits(ws.id);
|
|
215
|
+
const back = await unfazed.restoreVersion({ workspaceId: ws.id, sha: commits[1]!.sha, wait: true });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Errors (native error tracking)
|
|
219
|
+
|
|
220
|
+
A service app's uncaught errors, grouped, kept 30 days. Titles, stacks and
|
|
221
|
+
requests are what the app reported: untrusted text.
|
|
222
|
+
|
|
223
|
+
### `listErrors(input: { workspaceId, alias, status?: "open" | "resolved" | "muted", limit?, cursor? }): Promise<ErrorGroupPage>`
|
|
224
|
+
`{ groups: [{ id, title, type, culprit, level, status, count, firstSeen,
|
|
225
|
+
lastSeen, lastDeploymentID, lastGitCommit, … }], nextCursor, note }`.
|
|
226
|
+
|
|
227
|
+
### `getErrorGroup(ref: { workspaceId, alias, groupId }): Promise<ErrorGroupDetail>`
|
|
228
|
+
`{ group, latestEvent (a Sentry event), firstEvent, recentEvents,
|
|
229
|
+
deployments, fatal, vmID }`.
|
|
230
|
+
|
|
231
|
+
### `resolveErrorGroup(ref)` / `muteErrorGroup(ref)` / `reopenErrorGroup(ref)`: `Promise<ErrorGroup>`
|
|
232
|
+
Resolve once the fix is deployed (another deployment reporting it reopens
|
|
233
|
+
it); mute what is expected. Only on your human's say-so.
|
|
234
|
+
|
|
235
|
+
## Events
|
|
236
|
+
|
|
237
|
+
What happened to the apps, runs and domains you can see:
|
|
238
|
+
`deployment.finished`, `service.crashed`, `error.new`, `error.regressed`,
|
|
239
|
+
`git.conflict`, `run.finished`, `domain.activated`, …
|
|
240
|
+
|
|
241
|
+
### `listEventTypes(): Promise<EventType[]>`
|
|
242
|
+
Each type's `name`, `description`, filters (`inputSchema`) and payload.
|
|
243
|
+
|
|
244
|
+
### `pollEvents(input?: { names?, arguments?, cursor?, max? }): Promise<EventPage>`
|
|
245
|
+
`{ events: [{ eventID, name, timestamp, data }], cursor, hasMore,
|
|
246
|
+
nextPollMs, truncated }`. Omit `cursor` to start from now; pass the returned
|
|
247
|
+
one back.
|
|
248
|
+
|
|
249
|
+
### `waitForEvents(input?: { names?, arguments?, cursor?, max?, timeoutMs? }): Promise<EventPage>`
|
|
250
|
+
`pollEvents` that blocks server-side until an event arrives or `timeoutMs`
|
|
251
|
+
(default 45 s) passes; then `timedOut: true` with a cursor, not an error.
|
|
252
|
+
Past the account's 16 waits in progress it throws (`isTooManyWaits(e)`):
|
|
253
|
+
poll then.
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
let page = await unfazed.pollEvents({ names: ["deployment.finished"], arguments: { "deployment.finished": { alias: "api" } } });
|
|
257
|
+
// … deploy …
|
|
258
|
+
page = await unfazed.waitForEvents({ names: ["deployment.finished"], cursor: page.cursor, timeoutMs: 120_000 });
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Agent runs
|
|
262
|
+
|
|
263
|
+
### `createRun(options: { workspaceId, prompt?, harness?, command?, setup?, verify?, access?, alias?, maxSeconds?, wait? }): Promise<Run>`
|
|
264
|
+
A one-shot agent run in a fresh VM. `wait: true` polls to SUCCEEDED,
|
|
265
|
+
FAILED or CANCELLED (all returned, never thrown).
|
|
266
|
+
|
|
267
|
+
### `waitForRun(runId: string, waitOptions?: WaitOptions): Promise<Run>`
|
|
268
|
+
|
|
269
|
+
### `getRun(runId)` / `listRuns(filter?: { workspaceId?, status?, limit?, offset? })` / `cancelRun(runId)`
|
|
270
|
+
|
|
271
|
+
### `followUpRun(runId: string, options: { prompt, mode?, wait? }): Promise<Run>`
|
|
272
|
+
|
|
273
|
+
### `answerRun(runId: string, answer: string): Promise<Run>`
|
|
274
|
+
Answers a BLOCKED run's question.
|
|
275
|
+
|
|
276
|
+
### `runEvents(runId)` / `runConsole(runId, { tailBytes? })` / `runTranscript(runId, { tailBytes? })`
|
|
277
|
+
|
|
278
|
+
### `listRunHarnesses(): Promise<RunHarness[]>`
|
|
279
|
+
|
|
280
|
+
## Anything else
|
|
281
|
+
|
|
282
|
+
`unfazed.client` is the typed GraphQL transport over the same credential:
|
|
283
|
+
`await unfazed.client.query(document, variables)`. `operations` exports
|
|
284
|
+
every document the SDK uses.
|
package/dist/cli/login.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type StoredCredentials } from "../credentials.js";
|
|
2
2
|
import { UnfazedError } from "../errors.js";
|
|
3
|
-
export declare const USER_AGENT = "unfazed-cli/0.
|
|
3
|
+
export declare const USER_AGENT = "unfazed-cli/0.8.0 (+https://unfazed.dev/auth.md)";
|
|
4
4
|
/** What a login asks for: reading and deploying, like an MCP client. */
|
|
5
5
|
export declare const DEFAULT_SCOPE = "unfz:read unfz:deploy";
|
|
6
6
|
export declare const CLIENT_NAME = "unfazed CLI";
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LOGIN_COMMAND, openStoredLogin } from "./credentials.js";
|
|
2
2
|
import { UnfazedAuthError, UnfazedConfigError, UnfazedGraphQLError, UnfazedHTTPError, UnfazedTimeoutError, } from "./errors.js";
|
|
3
3
|
/** SDK version, sent as the User-Agent. Keep in sync with package.json. */
|
|
4
|
-
export const VERSION = "0.
|
|
4
|
+
export const VERSION = "0.8.0";
|
|
5
5
|
/**
|
|
6
6
|
* Default GraphQL endpoint.
|
|
7
7
|
*
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { LOGIN_COMMAND, openStoredLogin, type StoredLogin } from \"./credentials.js\";\nimport {\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedTimeoutError,\n type GraphQLResponseError,\n} from \"./errors.js\";\n\n/** SDK version, sent as the User-Agent. Keep in sync with package.json. */\nexport const VERSION = \"0.6.1\";\n\n/**\n * Default GraphQL endpoint.\n *\n * Note this is the FULL endpoint, not an origin: the platform's own documented\n * convention (`UNFZ_API_URL=\"https://unfazed.dev/query\"`, and cmd/showcase-deploy\n * in the unfazed repo) treats the /query URL as the configurable unit, so the\n * SDK matches it rather than inventing a base-URL convention.\n */\nexport const DEFAULT_ENDPOINT = \"https://unfazed.dev/query\";\n\n/** Platform origin, used to derive the endpoint when only `baseUrl` is given. */\nexport const DEFAULT_BASE_URL = \"https://unfazed.dev\";\n\n/** Path of the GraphQL endpoint under an origin. */\nexport const GRAPHQL_PATH = \"/query\";\n\n/**\n * Environment variable holding the bearer token.\n *\n * This is the platform's documented name (docs/agent-deployments.md,\n * internal/agentdocs/reference.md). It accepts either an operator API token or\n * an agent access token from POST /agents/register — /query takes both as\n * plain bearers.\n */\nexport const TOKEN_ENV_VAR = \"UNFZ_API_TOKEN\";\n\n/** Environment variable holding the full GraphQL endpoint URL. */\nexport const ENDPOINT_ENV_VAR = \"UNFZ_API_URL\";\n\n/** Minimal structural type of the `fetch` the transport needs. */\nexport type FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n text(): Promise<string>;\n}>;\n\nexport interface ClientOptions {\n /**\n * Bearer token. Falls back to `UNFZ_API_TOKEN`, then to the login\n * `npx unfazed login` stored (see {@link ClientOptions.login}). An agent gets\n * one from https://unfazed.dev/auth.md — the same token the MCP server uses.\n */\n token?: string;\n /**\n * Use the stored login of `npx unfazed login`\n * (`${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json`) when no token\n * is passed and `UNFZ_API_TOKEN` is unset. Its access token is refreshed\n * before it expires and when the platform refuses it. Default `true`; it is\n * only used for the endpoint it was issued by, and is the endpoint when none\n * is configured.\n */\n login?: boolean;\n /**\n * Where `UNFZ_API_TOKEN`, `UNFZ_API_URL` and the stored login's location\n * (`XDG_CONFIG_HOME`, `HOME`) are read from. Default: `process.env`.\n */\n env?: Record<string, string | undefined>;\n /**\n * Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to\n * `UNFZ_API_URL`, then the stored login's endpoint, then\n * {@link DEFAULT_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Platform origin; `/query` is appended. Convenience for self-hosted or\n * staging control planes where only the host differs.\n */\n baseUrl?: string;\n /** Extra headers merged into every request. Authorization always wins. */\n headers?: Record<string, string>;\n /**\n * Per-request timeout in ms. Default 60_000. Pass 0 to disable.\n *\n * Bounds the WHOLE request — connect, headers and reading the response body —\n * so a proxy that answers with headers and then stalls still fails on time.\n */\n timeoutMs?: number;\n /** Injection point for tests and instrumented/proxied transports. */\n fetch?: FetchLike;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n}\n\n/**\n * A GraphQL document.\n *\n * Structural rather than `string & {...}` on purpose: graphql-codegen emits\n * `TypedDocumentString`, a String SUBCLASS, which is not assignable to the\n * primitive `string`. Requiring only `toString()` plus the optional phantom\n * `__apiType` lets three things through with full inference:\n *\n * - generated `TypedDocumentString` documents (types inferred),\n * - documents assembled at runtime, like the batched writeFiles mutation,\n * - plain template-literal strings a caller wrote by hand.\n */\nexport interface GraphQLDocument<TResult, TVariables> {\n /** Phantom type carrier. Never called; present only to bind the generics. */\n __apiType?: (variables: TVariables) => TResult;\n toString(): string;\n}\n\n/** Result type carried by a typed document. */\nexport type ResultOf<T> = T extends GraphQLDocument<infer R, infer _V> ? R : unknown;\n\n/** Variables type carried by a typed document. */\nexport type VariablesOf<T> = T extends GraphQLDocument<infer _R, infer V> ? V : never;\n\n/** Per-request knobs. */\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n operationName?: string;\n}\n\n/**\n * Layer 1 — a typed, dependency-free GraphQL transport over `fetch`.\n *\n * This is the SDK's entire networking surface. It understands auth, timeouts\n * and the GraphQL error envelope, and nothing about deployments. The workflow\n * layer ({@link import(\"./unfazed.js\").Unfazed}) is built strictly on top of\n * it, so you can always drop down here for any operation the workflow layer\n * does not wrap — the schema has ~660 fields and the workflow layer\n * deliberately covers only the agent-facing ones.\n *\n * ```ts\n * const client = new UnfazedClient(); // UNFZ_API_TOKEN, else `npx unfazed login`\n * const data = await client.query<{ whoami: { name: string } }, never>(\n * `query { whoami { name } }`,\n * );\n * ```\n */\nexport class UnfazedClient {\n /** Resolved GraphQL endpoint URL. */\n readonly endpoint: string;\n\n /** A fixed bearer token, or the stored login handing out refreshed ones. */\n readonly #auth: { token: string } | { login: StoredLogin };\n readonly #headers: Record<string, string>;\n readonly #timeoutMs: number;\n readonly #fetch: FetchLike;\n\n constructor(options: ClientOptions = {}) {\n const env = options.env ?? processEnv();\n\n const injected = options.fetch;\n if (injected) {\n this.#fetch = injected;\n } else {\n const globalFetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof globalFetch !== \"function\") {\n throw new UnfazedConfigError(\n \"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch } to the client.\",\n );\n }\n this.#fetch = globalFetch.bind(globalThis) as FetchLike;\n }\n\n const ua = options.userAgent ?? `unfazed-sdk/${VERSION}`;\n\n const configured = configuredEndpoint(options, env);\n const token = options.token ?? env[TOKEN_ENV_VAR];\n if (token?.trim()) {\n this.#auth = { token: token.trim() };\n this.endpoint = configured ?? DEFAULT_ENDPOINT;\n } else {\n const stored =\n options.login === false\n ? {}\n : openStoredLogin({\n env,\n ...(configured ? { endpoint: configured } : {}),\n fetch: this.#fetch,\n // The edge refuses some generic agents; always name ourselves.\n userAgent: ua || `unfazed-sdk/${VERSION}`,\n });\n if (!stored.login) {\n throw new UnfazedAuthError(noTokenMessage(configured, stored.other?.endpoint));\n }\n this.#auth = { login: stored.login };\n this.endpoint = stored.login.endpoint;\n }\n\n this.#headers = { ...(options.headers ?? {}) };\n this.#timeoutMs = options.timeoutMs ?? 60_000;\n if (ua) this.#headers[\"User-Agent\"] = ua;\n }\n\n /**\n * Where the bearer token comes from: `\"token\"` (passed or\n * `UNFZ_API_TOKEN`) or `\"login\"` (the stored login of `npx unfazed login`).\n */\n get authSource(): \"token\" | \"login\" {\n return \"login\" in this.#auth ? \"login\" : \"token\";\n }\n\n /** Runs a query document. Identical wire behaviour to {@link mutate}. */\n query<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * Runs a mutation document. Distinct from {@link query} for call-site\n * readability only — GraphQL over HTTP POST makes no distinction.\n */\n mutate<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * The single code path behind {@link query} and {@link mutate}.\n *\n * Throws {@link UnfazedHTTPError} on non-2xx, {@link UnfazedGraphQLError}\n * when the body carries `errors`, {@link UnfazedTimeoutError} on timeout or\n * abort. Resolves to the `data` object on success.\n */\n async request<TResult = unknown>(\n document: string | { toString(): string },\n variables?: unknown,\n options: RequestOptions = {},\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n\n // `document` may be a TypedDocumentString (a String object). JSON.stringify\n // would serialise that fine, but String(...) keeps the wire body a plain\n // primitive regardless of how the caller obtained the document.\n const body = JSON.stringify({\n query: String(document),\n ...(variables === undefined ? {} : { variables }),\n ...(options.operationName ? { operationName: options.operationName } : {}),\n });\n\n let token = await this.#token();\n let { response, text } = await this.#send(body, token, timeoutMs, options.signal);\n // A stored login's access token can be refused before its recorded\n // expiry (the clock, a sign-out elsewhere): refresh once and retry. A\n // fixed token has nothing to refresh.\n if (response.status === 401 && \"login\" in this.#auth) {\n token = await this.#auth.login.refresh(token);\n ({ response, text } = await this.#send(body, token, timeoutMs, options.signal));\n }\n\n if (!response.ok) {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n let parsed: { data?: TResult; errors?: GraphQLResponseError[] };\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n // A GraphQL 200 can still be a failure, and can be a PARTIAL failure: the\n // batched writeFiles document resolves each aliased field independently.\n // Attaching `data` to the error is what lets a caller see which writes\n // landed before the failing one.\n if (parsed.errors?.length) {\n throw new UnfazedGraphQLError(parsed.errors, parsed.data);\n }\n if (parsed.data === undefined || parsed.data === null) {\n throw new UnfazedGraphQLError(\n [{ message: \"response contained neither data nor errors\" }],\n undefined,\n );\n }\n return parsed.data;\n }\n\n #token(): Promise<string> {\n return \"login\" in this.#auth ? this.#auth.login.token() : Promise.resolve(this.#auth.token);\n }\n\n async #send(\n body: string,\n token: string,\n timeoutMs: number,\n external: AbortSignal | undefined,\n ): Promise<{ response: Awaited<ReturnType<FetchLike>>; text: string }> {\n const { signal, cleanup, didTimeout } = makeSignal(timeoutMs, external);\n // The deadline covers BOTH phases — headers AND body. A proxy that answers\n // with headers and then stalls the body is a real failure mode, and\n // stopping the clock at the end of `fetch()` would leave `response.text()`\n // hanging forever. `untilAborted` additionally races the signal itself, so\n // an injected transport that ignores `signal` still cannot outlive the\n // timeout.\n try {\n const response = await untilAborted(\n this.#fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n ...this.#headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body,\n ...(signal ? { signal } : {}),\n }),\n signal,\n );\n const text = await untilAborted(response.text(), signal);\n return { response, text };\n } catch (cause) {\n if (didTimeout()) {\n throw new UnfazedTimeoutError(`unfazed: request timed out after ${timeoutMs}ms`, { cause });\n }\n if (isAbort(cause)) {\n throw new UnfazedTimeoutError(\"unfazed: request aborted\", { cause });\n }\n throw cause;\n } finally {\n cleanup();\n }\n }\n}\n\n/** The endpoint the options or `UNFZ_API_URL` name, or undefined for the default. */\nfunction configuredEndpoint(options: ClientOptions, env: Record<string, string | undefined>): string | undefined {\n if (options.endpoint) return options.endpoint;\n if (options.baseUrl) return trimSlash(options.baseUrl) + GRAPHQL_PATH;\n const fromEnv = env[ENDPOINT_ENV_VAR];\n // UNFZ_API_URL is documented as the full /query URL and is used verbatim.\n // Tolerate a bare origin anyway, since that is the obvious mistake and\n // guessing right costs nothing.\n if (fromEnv) {\n const trimmed = trimSlash(fromEnv);\n return /\\/query$|\\/graphql$/.test(trimmed) ? trimmed : trimmed + GRAPHQL_PATH;\n }\n return undefined;\n}\n\nfunction noTokenMessage(endpoint: string | undefined, otherLogin: string | undefined): string {\n if (endpoint && otherLogin) {\n return (\n `unfazed: no API token for ${endpoint}: the stored login is for ${otherLogin}. ` +\n `Run \\`${ENDPOINT_ENV_VAR}=${endpoint} ${LOGIN_COMMAND}\\`, or pass { token } / set ${TOKEN_ENV_VAR}.`\n );\n }\n return (\n `unfazed: not signed in. Run \\`${LOGIN_COMMAND}\\` to sign in with your account, ` +\n `or pass { token } / set ${TOKEN_ENV_VAR} (an agent token: ${DEFAULT_BASE_URL}/auth.md).`\n );\n}\n\nfunction processEnv(): Record<string, string | undefined> {\n // Guarded: the SDK must also work in a browser or worker, where there is no\n // `process` at all.\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};\n}\n\nfunction trimSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction isAbort(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { name?: string }).name === \"AbortError\";\n}\n\nfunction abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/**\n * Settles with `promise`, or rejects the moment `signal` aborts.\n *\n * A global `fetch` honours the signal for headers and body alike, but an\n * injected {@link FetchLike} — a proxy, an instrumented transport, a test\n * double — need not. Racing the signal makes the timeout an upper bound on the\n * whole request regardless of what the transport does with it. The loser's\n * rejection is swallowed by the already-settled promise rather than surfacing\n * as an unhandled rejection.\n */\nfunction untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) return Promise.reject(abortError());\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const done = () => signal.removeEventListener(\"abort\", onAbort);\n promise.then(\n (value) => {\n done();\n resolve(value);\n },\n (error: unknown) => {\n done();\n reject(error as Error);\n },\n );\n });\n}\n\n/**\n * Combines the request timeout with any caller-supplied signal.\n *\n * `didTimeout` is tracked separately from the abort so a caller's own\n * cancellation is not mislabelled as a timeout in the thrown message.\n */\nfunction makeSignal(\n timeoutMs: number,\n external?: AbortSignal,\n): { signal?: AbortSignal; cleanup: () => void; didTimeout: () => boolean } {\n if (!timeoutMs && !external) return { cleanup: () => {}, didTimeout: () => false };\n if (typeof AbortController !== \"function\") {\n return {\n ...(external ? { signal: external } : {}),\n cleanup: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n }\n\n const onExternalAbort = () => controller.abort();\n if (external) {\n if (external.aborted) controller.abort();\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", onExternalAbort);\n },\n didTimeout: () => timedOut,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAoB,MAAM,kBAAkB,CAAC;AACpF,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AAErB,2EAA2E;AAC3E,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AAEtD,oDAAoD;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAE9C,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AA+F/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,aAAa;IACxB,qCAAqC;IAC5B,QAAQ,CAAS;IAE1B,4EAA4E;IACnE,KAAK,CAA6C;IAClD,QAAQ,CAAyB;IACjC,UAAU,CAAS;IACnB,MAAM,CAAY;IAE3B,YAAY,UAAyB,EAAE;QACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;QAExC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC;QAC/B,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAI,UAAkC,CAAC,KAAK,CAAC;YAC9D,IAAI,OAAO,WAAW,KAAK,UAAU,EAAE,CAAC;gBACtC,MAAM,IAAI,kBAAkB,CAC1B,iFAAiF,CAClF,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,CAAc,CAAC;QAC1D,CAAC;QAED,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,IAAI,eAAe,OAAO,EAAE,CAAC;QAEzD,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;QAClD,IAAI,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACrC,IAAI,CAAC,QAAQ,GAAG,UAAU,IAAI,gBAAgB,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GACV,OAAO,CAAC,KAAK,KAAK,KAAK;gBACrB,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,eAAe,CAAC;oBACd,GAAG;oBACH,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/C,KAAK,EAAE,IAAI,CAAC,MAAM;oBAClB,+DAA+D;oBAC/D,SAAS,EAAE,EAAE,IAAI,eAAe,OAAO,EAAE;iBAC1C,CAAC,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAClB,MAAM,IAAI,gBAAgB,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;YACjF,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;QACxC,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;QAC9C,IAAI,EAAE;YAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACH,IAAI,UAAU;QACZ,OAAO,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACnD,CAAC;IAED,yEAAyE;IACzE,KAAK,CACH,QAA8C,EAC9C,SAAsB,EACtB,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,MAAM,CACJ,QAA8C,EAC9C,SAAsB,EACtB,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CACX,QAAyC,EACzC,SAAmB,EACnB,UAA0B,EAAE;QAE5B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC;QAEvD,4EAA4E;QAC5E,yEAAyE;QACzE,gEAAgE;QAChE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC;YACvB,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;YACjD,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3E,CAAC,CAAC;QAEH,IAAI,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAClF,mEAAmE;QACnE,sEAAsE;QACtE,sCAAsC;QACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACrD,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9C,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAClF,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,gBAAgB,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,MAA2D,CAAC;QAChE,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,gBAAgB,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,CAAC;QAED,0EAA0E;QAC1E,yEAAyE;QACzE,uEAAuE;QACvE,iCAAiC;QACjC,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACtD,MAAM,IAAI,mBAAmB,CAC3B,CAAC,EAAE,OAAO,EAAE,4CAA4C,EAAE,CAAC,EAC3D,SAAS,CACV,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,MAAM;QACJ,OAAO,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9F,CAAC;IAED,KAAK,CAAC,KAAK,CACT,IAAY,EACZ,KAAa,EACb,SAAiB,EACjB,QAAiC;QAEjC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACxE,2EAA2E;QAC3E,oEAAoE;QACpE,2EAA2E;QAC3E,2EAA2E;QAC3E,uEAAuE;QACvE,WAAW;QACX,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,YAAY,CACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACzB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,GAAG,IAAI,CAAC,QAAQ;oBAChB,cAAc,EAAE,kBAAkB;oBAClC,MAAM,EAAE,kBAAkB;oBAC1B,aAAa,EAAE,UAAU,KAAK,EAAE;iBACjC;gBACD,IAAI;gBACJ,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9B,CAAC,EACF,MAAM,CACP,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;YACzD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,UAAU,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,mBAAmB,CAAC,oCAAoC,SAAS,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9F,CAAC;YACD,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnB,MAAM,IAAI,mBAAmB,CAAC,0BAA0B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACvE,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,qFAAqF;AACrF,SAAS,kBAAkB,CAAC,OAAsB,EAAE,GAAuC;IACzF,IAAI,OAAO,CAAC,QAAQ;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC;IAC9C,IAAI,OAAO,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC;IACtE,MAAM,OAAO,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,0EAA0E;IAC1E,uEAAuE;IACvE,gCAAgC;IAChC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,YAAY,CAAC;IAChF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,QAA4B,EAAE,UAA8B;IAClF,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;QAC3B,OAAO,CACL,6BAA6B,QAAQ,6BAA6B,UAAU,IAAI;YAChF,SAAS,gBAAgB,IAAI,QAAQ,IAAI,aAAa,+BAA+B,aAAa,GAAG,CACtG,CAAC;IACJ,CAAC;IACD,OAAO,CACL,iCAAiC,aAAa,mCAAmC;QACjF,2BAA2B,aAAa,qBAAqB,gBAAgB,YAAY,CAC1F,CAAC;AACJ,CAAC;AAED,SAAS,UAAU;IACjB,4EAA4E;IAC5E,oBAAoB;IACpB,OAAQ,UAAyE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;AACvG,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,OAAO,CAAC,CAAU;IACzB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAK,CAAuB,CAAC,IAAI,KAAK,YAAY,CAAC;AAC/F,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAClD,CAAC,CAAC,IAAI,GAAG,YAAY,CAAC;IACtB,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,YAAY,CAAI,OAAmB,EAAE,MAA+B;IAC3E,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACxD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CACV,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,EAAE,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;YACjB,IAAI,EAAE,CAAC;YACP,MAAM,CAAC,KAAc,CAAC,CAAC;QACzB,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CACjB,SAAiB,EACjB,QAAsB;IAEtB,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;IACnF,IAAI,OAAO,eAAe,KAAK,UAAU,EAAE,CAAC;QAC1C,OAAO;YACL,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;YACjB,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK;SACxB,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,KAAgD,CAAC;IAErD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,QAAQ,GAAG,IAAI,CAAC;YAChB,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,SAAS,CAAC,CAAC;IAChB,CAAC;IAED,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACjD,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,OAAO;YAAE,UAAU,CAAC,KAAK,EAAE,CAAC;;YACpC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,KAAK,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC7C,QAAQ,EAAE,mBAAmB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QAC1D,CAAC;QACD,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ;KAC3B,CAAC;AACJ,CAAC","sourcesContent":["import { LOGIN_COMMAND, openStoredLogin, type StoredLogin } from \"./credentials.js\";\nimport {\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedTimeoutError,\n type GraphQLResponseError,\n} from \"./errors.js\";\n\n/** SDK version, sent as the User-Agent. Keep in sync with package.json. */\nexport const VERSION = \"0.8.0\";\n\n/**\n * Default GraphQL endpoint.\n *\n * Note this is the FULL endpoint, not an origin: the platform's own documented\n * convention (`UNFZ_API_URL=\"https://unfazed.dev/query\"`, and cmd/showcase-deploy\n * in the unfazed repo) treats the /query URL as the configurable unit, so the\n * SDK matches it rather than inventing a base-URL convention.\n */\nexport const DEFAULT_ENDPOINT = \"https://unfazed.dev/query\";\n\n/** Platform origin, used to derive the endpoint when only `baseUrl` is given. */\nexport const DEFAULT_BASE_URL = \"https://unfazed.dev\";\n\n/** Path of the GraphQL endpoint under an origin. */\nexport const GRAPHQL_PATH = \"/query\";\n\n/**\n * Environment variable holding the bearer token.\n *\n * This is the platform's documented name (docs/agent-deployments.md,\n * internal/agentdocs/reference.md). It accepts either an operator API token or\n * an agent access token from POST /agents/register — /query takes both as\n * plain bearers.\n */\nexport const TOKEN_ENV_VAR = \"UNFZ_API_TOKEN\";\n\n/** Environment variable holding the full GraphQL endpoint URL. */\nexport const ENDPOINT_ENV_VAR = \"UNFZ_API_URL\";\n\n/** Minimal structural type of the `fetch` the transport needs. */\nexport type FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n text(): Promise<string>;\n}>;\n\nexport interface ClientOptions {\n /**\n * Bearer token. Falls back to `UNFZ_API_TOKEN`, then to the login\n * `npx unfazed login` stored (see {@link ClientOptions.login}). An agent gets\n * one from https://unfazed.dev/auth.md — the same token the MCP server uses.\n */\n token?: string;\n /**\n * Use the stored login of `npx unfazed login`\n * (`${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json`) when no token\n * is passed and `UNFZ_API_TOKEN` is unset. Its access token is refreshed\n * before it expires and when the platform refuses it. Default `true`; it is\n * only used for the endpoint it was issued by, and is the endpoint when none\n * is configured.\n */\n login?: boolean;\n /**\n * Where `UNFZ_API_TOKEN`, `UNFZ_API_URL` and the stored login's location\n * (`XDG_CONFIG_HOME`, `HOME`) are read from. Default: `process.env`.\n */\n env?: Record<string, string | undefined>;\n /**\n * Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to\n * `UNFZ_API_URL`, then the stored login's endpoint, then\n * {@link DEFAULT_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Platform origin; `/query` is appended. Convenience for self-hosted or\n * staging control planes where only the host differs.\n */\n baseUrl?: string;\n /** Extra headers merged into every request. Authorization always wins. */\n headers?: Record<string, string>;\n /**\n * Per-request timeout in ms. Default 60_000. Pass 0 to disable.\n *\n * Bounds the WHOLE request — connect, headers and reading the response body —\n * so a proxy that answers with headers and then stalls still fails on time.\n */\n timeoutMs?: number;\n /** Injection point for tests and instrumented/proxied transports. */\n fetch?: FetchLike;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n}\n\n/**\n * A GraphQL document.\n *\n * Structural rather than `string & {...}` on purpose: graphql-codegen emits\n * `TypedDocumentString`, a String SUBCLASS, which is not assignable to the\n * primitive `string`. Requiring only `toString()` plus the optional phantom\n * `__apiType` lets three things through with full inference:\n *\n * - generated `TypedDocumentString` documents (types inferred),\n * - documents assembled at runtime, like the batched writeFiles mutation,\n * - plain template-literal strings a caller wrote by hand.\n */\nexport interface GraphQLDocument<TResult, TVariables> {\n /** Phantom type carrier. Never called; present only to bind the generics. */\n __apiType?: (variables: TVariables) => TResult;\n toString(): string;\n}\n\n/** Result type carried by a typed document. */\nexport type ResultOf<T> = T extends GraphQLDocument<infer R, infer _V> ? R : unknown;\n\n/** Variables type carried by a typed document. */\nexport type VariablesOf<T> = T extends GraphQLDocument<infer _R, infer V> ? V : never;\n\n/** Per-request knobs. */\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n operationName?: string;\n}\n\n/**\n * Layer 1 — a typed, dependency-free GraphQL transport over `fetch`.\n *\n * This is the SDK's entire networking surface. It understands auth, timeouts\n * and the GraphQL error envelope, and nothing about deployments. The workflow\n * layer ({@link import(\"./unfazed.js\").Unfazed}) is built strictly on top of\n * it, so you can always drop down here for any operation the workflow layer\n * does not wrap — the schema has ~660 fields and the workflow layer\n * deliberately covers only the agent-facing ones.\n *\n * ```ts\n * const client = new UnfazedClient(); // UNFZ_API_TOKEN, else `npx unfazed login`\n * const data = await client.query<{ whoami: { name: string } }, never>(\n * `query { whoami { name } }`,\n * );\n * ```\n */\nexport class UnfazedClient {\n /** Resolved GraphQL endpoint URL. */\n readonly endpoint: string;\n\n /** A fixed bearer token, or the stored login handing out refreshed ones. */\n readonly #auth: { token: string } | { login: StoredLogin };\n readonly #headers: Record<string, string>;\n readonly #timeoutMs: number;\n readonly #fetch: FetchLike;\n\n constructor(options: ClientOptions = {}) {\n const env = options.env ?? processEnv();\n\n const injected = options.fetch;\n if (injected) {\n this.#fetch = injected;\n } else {\n const globalFetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof globalFetch !== \"function\") {\n throw new UnfazedConfigError(\n \"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch } to the client.\",\n );\n }\n this.#fetch = globalFetch.bind(globalThis) as FetchLike;\n }\n\n const ua = options.userAgent ?? `unfazed-sdk/${VERSION}`;\n\n const configured = configuredEndpoint(options, env);\n const token = options.token ?? env[TOKEN_ENV_VAR];\n if (token?.trim()) {\n this.#auth = { token: token.trim() };\n this.endpoint = configured ?? DEFAULT_ENDPOINT;\n } else {\n const stored =\n options.login === false\n ? {}\n : openStoredLogin({\n env,\n ...(configured ? { endpoint: configured } : {}),\n fetch: this.#fetch,\n // The edge refuses some generic agents; always name ourselves.\n userAgent: ua || `unfazed-sdk/${VERSION}`,\n });\n if (!stored.login) {\n throw new UnfazedAuthError(noTokenMessage(configured, stored.other?.endpoint));\n }\n this.#auth = { login: stored.login };\n this.endpoint = stored.login.endpoint;\n }\n\n this.#headers = { ...(options.headers ?? {}) };\n this.#timeoutMs = options.timeoutMs ?? 60_000;\n if (ua) this.#headers[\"User-Agent\"] = ua;\n }\n\n /**\n * Where the bearer token comes from: `\"token\"` (passed or\n * `UNFZ_API_TOKEN`) or `\"login\"` (the stored login of `npx unfazed login`).\n */\n get authSource(): \"token\" | \"login\" {\n return \"login\" in this.#auth ? \"login\" : \"token\";\n }\n\n /** Runs a query document. Identical wire behaviour to {@link mutate}. */\n query<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * Runs a mutation document. Distinct from {@link query} for call-site\n * readability only — GraphQL over HTTP POST makes no distinction.\n */\n mutate<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * The single code path behind {@link query} and {@link mutate}.\n *\n * Throws {@link UnfazedHTTPError} on non-2xx, {@link UnfazedGraphQLError}\n * when the body carries `errors`, {@link UnfazedTimeoutError} on timeout or\n * abort. Resolves to the `data` object on success.\n */\n async request<TResult = unknown>(\n document: string | { toString(): string },\n variables?: unknown,\n options: RequestOptions = {},\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n\n // `document` may be a TypedDocumentString (a String object). JSON.stringify\n // would serialise that fine, but String(...) keeps the wire body a plain\n // primitive regardless of how the caller obtained the document.\n const body = JSON.stringify({\n query: String(document),\n ...(variables === undefined ? {} : { variables }),\n ...(options.operationName ? { operationName: options.operationName } : {}),\n });\n\n let token = await this.#token();\n let { response, text } = await this.#send(body, token, timeoutMs, options.signal);\n // A stored login's access token can be refused before its recorded\n // expiry (the clock, a sign-out elsewhere): refresh once and retry. A\n // fixed token has nothing to refresh.\n if (response.status === 401 && \"login\" in this.#auth) {\n token = await this.#auth.login.refresh(token);\n ({ response, text } = await this.#send(body, token, timeoutMs, options.signal));\n }\n\n if (!response.ok) {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n let parsed: { data?: TResult; errors?: GraphQLResponseError[] };\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n // A GraphQL 200 can still be a failure, and can be a PARTIAL failure: the\n // batched writeFiles document resolves each aliased field independently.\n // Attaching `data` to the error is what lets a caller see which writes\n // landed before the failing one.\n if (parsed.errors?.length) {\n throw new UnfazedGraphQLError(parsed.errors, parsed.data);\n }\n if (parsed.data === undefined || parsed.data === null) {\n throw new UnfazedGraphQLError(\n [{ message: \"response contained neither data nor errors\" }],\n undefined,\n );\n }\n return parsed.data;\n }\n\n #token(): Promise<string> {\n return \"login\" in this.#auth ? this.#auth.login.token() : Promise.resolve(this.#auth.token);\n }\n\n async #send(\n body: string,\n token: string,\n timeoutMs: number,\n external: AbortSignal | undefined,\n ): Promise<{ response: Awaited<ReturnType<FetchLike>>; text: string }> {\n const { signal, cleanup, didTimeout } = makeSignal(timeoutMs, external);\n // The deadline covers BOTH phases — headers AND body. A proxy that answers\n // with headers and then stalls the body is a real failure mode, and\n // stopping the clock at the end of `fetch()` would leave `response.text()`\n // hanging forever. `untilAborted` additionally races the signal itself, so\n // an injected transport that ignores `signal` still cannot outlive the\n // timeout.\n try {\n const response = await untilAborted(\n this.#fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n ...this.#headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body,\n ...(signal ? { signal } : {}),\n }),\n signal,\n );\n const text = await untilAborted(response.text(), signal);\n return { response, text };\n } catch (cause) {\n if (didTimeout()) {\n throw new UnfazedTimeoutError(`unfazed: request timed out after ${timeoutMs}ms`, { cause });\n }\n if (isAbort(cause)) {\n throw new UnfazedTimeoutError(\"unfazed: request aborted\", { cause });\n }\n throw cause;\n } finally {\n cleanup();\n }\n }\n}\n\n/** The endpoint the options or `UNFZ_API_URL` name, or undefined for the default. */\nfunction configuredEndpoint(options: ClientOptions, env: Record<string, string | undefined>): string | undefined {\n if (options.endpoint) return options.endpoint;\n if (options.baseUrl) return trimSlash(options.baseUrl) + GRAPHQL_PATH;\n const fromEnv = env[ENDPOINT_ENV_VAR];\n // UNFZ_API_URL is documented as the full /query URL and is used verbatim.\n // Tolerate a bare origin anyway, since that is the obvious mistake and\n // guessing right costs nothing.\n if (fromEnv) {\n const trimmed = trimSlash(fromEnv);\n return /\\/query$|\\/graphql$/.test(trimmed) ? trimmed : trimmed + GRAPHQL_PATH;\n }\n return undefined;\n}\n\nfunction noTokenMessage(endpoint: string | undefined, otherLogin: string | undefined): string {\n if (endpoint && otherLogin) {\n return (\n `unfazed: no API token for ${endpoint}: the stored login is for ${otherLogin}. ` +\n `Run \\`${ENDPOINT_ENV_VAR}=${endpoint} ${LOGIN_COMMAND}\\`, or pass { token } / set ${TOKEN_ENV_VAR}.`\n );\n }\n return (\n `unfazed: not signed in. Run \\`${LOGIN_COMMAND}\\` to sign in with your account, ` +\n `or pass { token } / set ${TOKEN_ENV_VAR} (an agent token: ${DEFAULT_BASE_URL}/auth.md).`\n );\n}\n\nfunction processEnv(): Record<string, string | undefined> {\n // Guarded: the SDK must also work in a browser or worker, where there is no\n // `process` at all.\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};\n}\n\nfunction trimSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction isAbort(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { name?: string }).name === \"AbortError\";\n}\n\nfunction abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/**\n * Settles with `promise`, or rejects the moment `signal` aborts.\n *\n * A global `fetch` honours the signal for headers and body alike, but an\n * injected {@link FetchLike} — a proxy, an instrumented transport, a test\n * double — need not. Racing the signal makes the timeout an upper bound on the\n * whole request regardless of what the transport does with it. The loser's\n * rejection is swallowed by the already-settled promise rather than surfacing\n * as an unhandled rejection.\n */\nfunction untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) return Promise.reject(abortError());\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const done = () => signal.removeEventListener(\"abort\", onAbort);\n promise.then(\n (value) => {\n done();\n resolve(value);\n },\n (error: unknown) => {\n done();\n reject(error as Error);\n },\n );\n });\n}\n\n/**\n * Combines the request timeout with any caller-supplied signal.\n *\n * `didTimeout` is tracked separately from the abort so a caller's own\n * cancellation is not mislabelled as a timeout in the thrown message.\n */\nfunction makeSignal(\n timeoutMs: number,\n external?: AbortSignal,\n): { signal?: AbortSignal; cleanup: () => void; didTimeout: () => boolean } {\n if (!timeoutMs && !external) return { cleanup: () => {}, didTimeout: () => false };\n if (typeof AbortController !== \"function\") {\n return {\n ...(external ? { signal: external } : {}),\n cleanup: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n }\n\n const onExternalAbort = () => controller.abort();\n if (external) {\n if (external.aborted) controller.abort();\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", onExternalAbort);\n },\n didTimeout: () => timedOut,\n };\n}\n"]}
|
package/dist/errors.d.ts
CHANGED
|
@@ -45,9 +45,66 @@ export declare class UnfazedGraphQLError extends UnfazedError {
|
|
|
45
45
|
readonly data: unknown;
|
|
46
46
|
constructor(errors: GraphQLResponseError[], data: unknown);
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* The server refused a delete that would take apps offline without the call
|
|
50
|
+
* naming exactly those apps, and deleted nothing.
|
|
51
|
+
*
|
|
52
|
+
* `deleteWorkspace` with `withDeployments` takes down every app the workspace
|
|
53
|
+
* serves, so its `confirmAliases` must equal the workspace's live apps — no
|
|
54
|
+
* more, no fewer. A missing or extra alias, or an app that went live after the
|
|
55
|
+
* check, is refused. The message is the server's, verbatim: it lists each live
|
|
56
|
+
* app with its custom domains and says how to name them. Look the apps up
|
|
57
|
+
* deliberately with `unfazed.liveApps(workspaceId)`, decide, and delete again,
|
|
58
|
+
* or delete one app with `unfazed.deleteApp`.
|
|
59
|
+
*
|
|
60
|
+
* Still an {@link UnfazedGraphQLError}, so existing `catch` sites keep matching.
|
|
61
|
+
*/
|
|
62
|
+
export declare class UnfazedUnconfirmedAppsError extends UnfazedGraphQLError {
|
|
63
|
+
/** The workspace whose delete was refused. */
|
|
64
|
+
readonly workspaceId: string;
|
|
65
|
+
/** The aliases the refused call named. */
|
|
66
|
+
readonly confirmAliases: readonly string[];
|
|
67
|
+
constructor(workspaceId: string, confirmAliases: readonly string[], errors: GraphQLResponseError[], data: unknown);
|
|
68
|
+
}
|
|
69
|
+
/** Did the server refuse a delete because the named apps are not exactly the live ones? */
|
|
70
|
+
export declare function isUnconfirmedAppsRefusal(error: unknown): error is UnfazedGraphQLError;
|
|
48
71
|
/** A request exceeded `timeoutMs`, or the caller's AbortSignal fired. */
|
|
49
72
|
export declare class UnfazedTimeoutError extends UnfazedError {
|
|
50
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* `writeFiles` wrote some files and not others.
|
|
76
|
+
*
|
|
77
|
+
* Writes are not transactional, exactly as MCP `write_workspace_files`: the
|
|
78
|
+
* files listed in `result.files` with `ok: true` ARE written (and, on an app
|
|
79
|
+
* with platform git, committed), the others are not. `result` is the whole
|
|
80
|
+
* answer, `failed` names the paths that did not land and why. Retry only
|
|
81
|
+
* those paths. Pass `allowPartial: true` to get the result instead of this
|
|
82
|
+
* error.
|
|
83
|
+
*/
|
|
84
|
+
export declare class UnfazedWriteError extends UnfazedError {
|
|
85
|
+
/** What the call wrote: every file's outcome and the commit. */
|
|
86
|
+
readonly result: unknown;
|
|
87
|
+
/** The paths that were not written, with the server's reason. */
|
|
88
|
+
readonly failed: {
|
|
89
|
+
path: string;
|
|
90
|
+
error: string;
|
|
91
|
+
}[];
|
|
92
|
+
constructor(result: unknown, failed: {
|
|
93
|
+
path: string;
|
|
94
|
+
error: string;
|
|
95
|
+
}[]);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Did the platform refuse a long wait because the caller (or the server) has
|
|
99
|
+
* too many in progress? Nothing was waited on; polling is the way on.
|
|
100
|
+
*/
|
|
101
|
+
export declare function isTooManyWaits(error: unknown): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Did the server refuse the document because it does not know `field` (a
|
|
104
|
+
* platform older than this SDK)? The SDK uses it to fall back to the older
|
|
105
|
+
* way of doing the same thing, never to hide a real error.
|
|
106
|
+
*/
|
|
107
|
+
export declare function isUnsupportedField(error: unknown, field: string): boolean;
|
|
51
108
|
/**
|
|
52
109
|
* Waiting on a deploy ended badly.
|
|
53
110
|
*
|
|
@@ -143,6 +200,13 @@ export declare const DEPLOYMENT_NOT_FOUND = "deployment was not found";
|
|
|
143
200
|
* anything else is a genuine error and must keep throwing.
|
|
144
201
|
*/
|
|
145
202
|
export declare function isNotFoundSentinel(error: unknown, sentinel: string): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Does this error say that exactly `alias` is not an app of the workspace?
|
|
205
|
+
*
|
|
206
|
+
* The workspace name in the message varies, so this matches the shape, then
|
|
207
|
+
* the alias itself: a refusal about a different alias still throws.
|
|
208
|
+
*/
|
|
209
|
+
export declare function isAppNotFound(error: unknown, alias: string): boolean;
|
|
146
210
|
/** Did `createWorkspace` fail because the name is already taken by this owner? */
|
|
147
211
|
export declare function isDuplicateWorkspaceName(error: unknown): boolean;
|
|
148
212
|
/**
|
package/dist/errors.js
CHANGED
|
@@ -49,9 +49,88 @@ export class UnfazedGraphQLError extends UnfazedError {
|
|
|
49
49
|
this.data = data;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The server refused a delete that would take apps offline without the call
|
|
54
|
+
* naming exactly those apps, and deleted nothing.
|
|
55
|
+
*
|
|
56
|
+
* `deleteWorkspace` with `withDeployments` takes down every app the workspace
|
|
57
|
+
* serves, so its `confirmAliases` must equal the workspace's live apps — no
|
|
58
|
+
* more, no fewer. A missing or extra alias, or an app that went live after the
|
|
59
|
+
* check, is refused. The message is the server's, verbatim: it lists each live
|
|
60
|
+
* app with its custom domains and says how to name them. Look the apps up
|
|
61
|
+
* deliberately with `unfazed.liveApps(workspaceId)`, decide, and delete again,
|
|
62
|
+
* or delete one app with `unfazed.deleteApp`.
|
|
63
|
+
*
|
|
64
|
+
* Still an {@link UnfazedGraphQLError}, so existing `catch` sites keep matching.
|
|
65
|
+
*/
|
|
66
|
+
export class UnfazedUnconfirmedAppsError extends UnfazedGraphQLError {
|
|
67
|
+
/** The workspace whose delete was refused. */
|
|
68
|
+
workspaceId;
|
|
69
|
+
/** The aliases the refused call named. */
|
|
70
|
+
confirmAliases;
|
|
71
|
+
constructor(workspaceId, confirmAliases, errors, data) {
|
|
72
|
+
super(errors, data);
|
|
73
|
+
this.workspaceId = workspaceId;
|
|
74
|
+
this.confirmAliases = confirmAliases;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* How the server opens every refusal of a delete whose named apps do not match
|
|
79
|
+
* the live ones (usecases.UnconfirmedAppsError, and the race where an app went
|
|
80
|
+
* live after the check). Anchored at the start, so a message that merely
|
|
81
|
+
* mentions the phrase is not mistaken for one.
|
|
82
|
+
*/
|
|
83
|
+
const NOTHING_WAS_DELETED = /^nothing was deleted:/i;
|
|
84
|
+
/** Did the server refuse a delete because the named apps are not exactly the live ones? */
|
|
85
|
+
export function isUnconfirmedAppsRefusal(error) {
|
|
86
|
+
return (error instanceof UnfazedGraphQLError &&
|
|
87
|
+
error.errors.some((e) => NOTHING_WAS_DELETED.test(e.message.trim())));
|
|
88
|
+
}
|
|
52
89
|
/** A request exceeded `timeoutMs`, or the caller's AbortSignal fired. */
|
|
53
90
|
export class UnfazedTimeoutError extends UnfazedError {
|
|
54
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* `writeFiles` wrote some files and not others.
|
|
94
|
+
*
|
|
95
|
+
* Writes are not transactional, exactly as MCP `write_workspace_files`: the
|
|
96
|
+
* files listed in `result.files` with `ok: true` ARE written (and, on an app
|
|
97
|
+
* with platform git, committed), the others are not. `result` is the whole
|
|
98
|
+
* answer, `failed` names the paths that did not land and why. Retry only
|
|
99
|
+
* those paths. Pass `allowPartial: true` to get the result instead of this
|
|
100
|
+
* error.
|
|
101
|
+
*/
|
|
102
|
+
export class UnfazedWriteError extends UnfazedError {
|
|
103
|
+
/** What the call wrote: every file's outcome and the commit. */
|
|
104
|
+
result;
|
|
105
|
+
/** The paths that were not written, with the server's reason. */
|
|
106
|
+
failed;
|
|
107
|
+
constructor(result, failed) {
|
|
108
|
+
super(`unfazed: ${failed.length} file(s) were not written (the others were): ` +
|
|
109
|
+
failed.map((f) => `${f.path}: ${f.error}`).join("; "));
|
|
110
|
+
this.result = result;
|
|
111
|
+
this.failed = failed;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Did the platform refuse a long wait because the caller (or the server) has
|
|
116
|
+
* too many in progress? Nothing was waited on; polling is the way on.
|
|
117
|
+
*/
|
|
118
|
+
export function isTooManyWaits(error) {
|
|
119
|
+
return (error instanceof UnfazedGraphQLError &&
|
|
120
|
+
error.errors.some((e) => /waits in progress|long wait per operation/i.test(e.message)));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Did the server refuse the document because it does not know `field` (a
|
|
124
|
+
* platform older than this SDK)? The SDK uses it to fall back to the older
|
|
125
|
+
* way of doing the same thing, never to hide a real error.
|
|
126
|
+
*/
|
|
127
|
+
export function isUnsupportedField(error, field) {
|
|
128
|
+
if (!(error instanceof UnfazedGraphQLError))
|
|
129
|
+
return false;
|
|
130
|
+
const quoted = `"${field}"`;
|
|
131
|
+
return error.errors.some((e) => e.message.includes(quoted) &&
|
|
132
|
+
/Cannot query field|is not defined by type|Unknown argument|Unknown type|not defined on/i.test(e.message));
|
|
133
|
+
}
|
|
55
134
|
/**
|
|
56
135
|
* Waiting on a deploy ended badly.
|
|
57
136
|
*
|
|
@@ -163,6 +242,22 @@ export function isNotFoundSentinel(error, sentinel) {
|
|
|
163
242
|
return (error instanceof UnfazedGraphQLError &&
|
|
164
243
|
error.errors.some((e) => e.message.trim() === sentinel));
|
|
165
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* The server's "no such app here" refusal of `deleteApp`:
|
|
247
|
+
* `app "<alias>" was not found in workspace "<workspace name>"`. Anchored at
|
|
248
|
+
* both ends, like the exact sentinels above.
|
|
249
|
+
*/
|
|
250
|
+
const APP_NOT_FOUND = /^app "(.+)" was not found in workspace ".*"$/;
|
|
251
|
+
/**
|
|
252
|
+
* Does this error say that exactly `alias` is not an app of the workspace?
|
|
253
|
+
*
|
|
254
|
+
* The workspace name in the message varies, so this matches the shape, then
|
|
255
|
+
* the alias itself: a refusal about a different alias still throws.
|
|
256
|
+
*/
|
|
257
|
+
export function isAppNotFound(error, alias) {
|
|
258
|
+
return (error instanceof UnfazedGraphQLError &&
|
|
259
|
+
error.errors.some((e) => APP_NOT_FOUND.exec(e.message.trim())?.[1] === alias.trim()));
|
|
260
|
+
}
|
|
166
261
|
/**
|
|
167
262
|
* Message shape of the server's per-owner duplicate workspace name error:
|
|
168
263
|
* `a workspace named "x" already exists; reuse it (id <uuid>) or pick a
|