unfazed 0.6.0 → 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 +299 -78
- package/REFERENCE.md +284 -0
- package/dist/cli/login.d.ts +129 -0
- package/dist/cli/login.js +596 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/workflow.d.ts +31 -0
- package/dist/cli/workflow.js +157 -0
- package/dist/cli/workflow.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +26 -5
- package/dist/client.js +93 -48
- package/dist/client.js.map +1 -1
- package/dist/credentials.d.ts +123 -0
- package/dist/credentials.js +317 -0
- package/dist/credentials.js.map +1 -0
- 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 +25 -6
- package/dist/index.js +23 -5
- 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/remote/app.js +2 -1
- package/dist/remote/app.js.map +1 -1
- package/dist/remote/world.d.ts +2 -2
- package/dist/remote/world.js +32 -16
- package/dist/remote/world.js.map +1 -1
- package/dist/unfazed.d.ts +545 -68
- package/dist/unfazed.js +688 -64
- 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 +22 -6
- 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.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { type StoredCredentials } from "../credentials.js";
|
|
2
|
+
import { UnfazedError } from "../errors.js";
|
|
3
|
+
export declare const USER_AGENT = "unfazed-cli/0.8.0 (+https://unfazed.dev/auth.md)";
|
|
4
|
+
/** What a login asks for: reading and deploying, like an MCP client. */
|
|
5
|
+
export declare const DEFAULT_SCOPE = "unfz:read unfz:deploy";
|
|
6
|
+
export declare const CLIENT_NAME = "unfazed CLI";
|
|
7
|
+
export declare const CALLBACK_PATH = "/callback";
|
|
8
|
+
/** The redirect registered once; each login uses it with its own port. */
|
|
9
|
+
export declare const REGISTERED_REDIRECT_URI = "http://127.0.0.1/callback";
|
|
10
|
+
/** Registered clients, per issuer, beside the login. */
|
|
11
|
+
export declare const CLIENTS_FILE = "oauth-clients.json";
|
|
12
|
+
/** How long a login waits for the browser. */
|
|
13
|
+
export declare const LOGIN_TIMEOUT_MS: number;
|
|
14
|
+
export type Env = Record<string, string | undefined>;
|
|
15
|
+
/** The `fetch` the CLI needs; the global one fits. */
|
|
16
|
+
export type Fetch = (input: string, init: {
|
|
17
|
+
method: string;
|
|
18
|
+
headers: Record<string, string>;
|
|
19
|
+
body?: string;
|
|
20
|
+
redirect?: "manual" | "follow";
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}) => Promise<{
|
|
23
|
+
ok: boolean;
|
|
24
|
+
status: number;
|
|
25
|
+
text(): Promise<string>;
|
|
26
|
+
}>;
|
|
27
|
+
/** A login that could not be completed; the message says why and what to do. */
|
|
28
|
+
export declare class UnfazedLoginError extends UnfazedError {
|
|
29
|
+
}
|
|
30
|
+
/** The GraphQL endpoint: given, else `UNFZ_API_URL`, else unfazed.dev's; a bare origin gets `/query`. */
|
|
31
|
+
export declare function resolveEndpoint(raw: string | undefined, env: Env): string;
|
|
32
|
+
/** Whether a URL may carry tokens: https, or plain http to this machine only. */
|
|
33
|
+
export declare function safeURL(raw: unknown): raw is string;
|
|
34
|
+
/** RFC 8414 authorization server metadata: the fields the CLI uses. */
|
|
35
|
+
export interface AuthServerMetadata {
|
|
36
|
+
issuer: string;
|
|
37
|
+
authorization_endpoint: string;
|
|
38
|
+
token_endpoint: string;
|
|
39
|
+
registration_endpoint?: string;
|
|
40
|
+
revocation_endpoint?: string;
|
|
41
|
+
code_challenge_methods_supported?: string[];
|
|
42
|
+
response_types_supported?: string[];
|
|
43
|
+
authorization_response_iss_parameter_supported?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** Reads and checks the metadata of the server at `origin` (e.g. https://unfazed.dev). */
|
|
46
|
+
export declare function discover(origin: string, fetch: Fetch): Promise<AuthServerMetadata>;
|
|
47
|
+
/** A PKCE verifier and its S256 challenge. */
|
|
48
|
+
export declare function pkcePair(): {
|
|
49
|
+
verifier: string;
|
|
50
|
+
challenge: string;
|
|
51
|
+
};
|
|
52
|
+
/** A client the CLI registered at one issuer. */
|
|
53
|
+
export interface ClientRecord {
|
|
54
|
+
client_id: string;
|
|
55
|
+
redirect_uri: string;
|
|
56
|
+
scope: string;
|
|
57
|
+
registered_at: string;
|
|
58
|
+
}
|
|
59
|
+
/** The cached client for `issuer` that may ask for `scope`, if any. */
|
|
60
|
+
export declare function cachedClient(env: Env, issuer: string, scope: string): ClientRecord | undefined;
|
|
61
|
+
export interface LoginOptions {
|
|
62
|
+
/** GraphQL endpoint or platform origin. Default: `UNFZ_API_URL`, else https://unfazed.dev. */
|
|
63
|
+
endpoint?: string;
|
|
64
|
+
/** Space-separated scopes. Default: `unfz:read unfz:deploy`. */
|
|
65
|
+
scope?: string;
|
|
66
|
+
/** Default `process.env`: where `UNFZ_API_URL` and the login's location come from. */
|
|
67
|
+
env?: Env;
|
|
68
|
+
fetch?: Fetch;
|
|
69
|
+
/** Opens the sign-in URL; resolves to whether a browser started. Default: print only. */
|
|
70
|
+
openBrowser?: (url: string) => boolean | Promise<boolean>;
|
|
71
|
+
/** Where progress goes (never a token). Default: stderr. */
|
|
72
|
+
log?: (line: string) => void;
|
|
73
|
+
/** Where a pasted answer is read from (a browser on another machine). Default: none. */
|
|
74
|
+
input?: NodeJS.ReadableStream;
|
|
75
|
+
/** How long to wait for the browser. Default {@link LOGIN_TIMEOUT_MS}. */
|
|
76
|
+
timeoutMs?: number;
|
|
77
|
+
}
|
|
78
|
+
/** Signs in through the browser and stores the login. Resolves to it and where it was stored. */
|
|
79
|
+
export declare function login(options?: LoginOptions): Promise<{
|
|
80
|
+
credentials: StoredCredentials;
|
|
81
|
+
path: string;
|
|
82
|
+
}>;
|
|
83
|
+
/** The fields `whoami` shows. */
|
|
84
|
+
export interface Actor {
|
|
85
|
+
kind: string;
|
|
86
|
+
userID: string;
|
|
87
|
+
name: string;
|
|
88
|
+
scopes: string[];
|
|
89
|
+
}
|
|
90
|
+
export interface WhoamiResult {
|
|
91
|
+
actor: Actor;
|
|
92
|
+
endpoint: string;
|
|
93
|
+
/** `token`: UNFZ_API_TOKEN; `login`: the stored login at `path`. */
|
|
94
|
+
source: "token" | "login";
|
|
95
|
+
path?: string;
|
|
96
|
+
}
|
|
97
|
+
/** Who the SDK acts as here: `UNFZ_API_TOKEN` when set, else the stored login (refreshed as needed). */
|
|
98
|
+
export declare function whoami(options?: {
|
|
99
|
+
env?: Env;
|
|
100
|
+
fetch?: Fetch;
|
|
101
|
+
}): Promise<WhoamiResult>;
|
|
102
|
+
export interface LogoutResult {
|
|
103
|
+
/** Whether there was a login to remove. */
|
|
104
|
+
signedOut: boolean;
|
|
105
|
+
issuer?: string;
|
|
106
|
+
/** Whether the server revoked the tokens. */
|
|
107
|
+
revoked: boolean;
|
|
108
|
+
/** Whether the server offers revocation (RFC 7009). */
|
|
109
|
+
revocationSupported: boolean;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Revokes the stored tokens when the server advertises revocation, then
|
|
113
|
+
* deletes the login. The endpoint is read from the server's metadata again,
|
|
114
|
+
* so one the server gained since the login is used.
|
|
115
|
+
*/
|
|
116
|
+
export declare function logout(options?: {
|
|
117
|
+
env?: Env;
|
|
118
|
+
fetch?: Fetch;
|
|
119
|
+
}): Promise<LogoutResult>;
|
|
120
|
+
export declare function accountName(credentials: StoredCredentials): string;
|
|
121
|
+
/**
|
|
122
|
+
* Whether a browser can be expected here. Over SSH, or on Linux without a
|
|
123
|
+
* display, the login prints the URL instead (and takes the pasted answer).
|
|
124
|
+
*/
|
|
125
|
+
export declare function canOpenBrowser(env: Env, platform?: NodeJS.Platform): boolean;
|
|
126
|
+
/** The command that opens `url` — `$BROWSER`, `open`, `xdg-open` or the Windows URL handler — as argv, never a shell line. */
|
|
127
|
+
export declare function browserCommand(url: string, env: Env, platform?: NodeJS.Platform): string[];
|
|
128
|
+
/** Starts the browser on `url`. Resolves to whether it could be started. */
|
|
129
|
+
export declare function openBrowser(url: string, env?: Env, platform?: NodeJS.Platform): Promise<boolean>;
|