unfazed 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -29
- 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/index.d.ts +12 -2
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- 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 +1 -1
- package/dist/unfazed.js +1 -1
- package/dist/unfazed.js.map +1 -1
- package/package.json +18 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"node\" />\n/**\n * `npx unfazed login`, `whoami` and `logout`: the CLI behind the package's\n * `unfazed` bin. Only `../cli.ts` loads this module — `import \"unfazed\"`\n * never does — so it may use Node built-ins directly.\n *\n * Login is the OAuth 2.1 authorization code flow with PKCE against the\n * platform's own authorization server (the one MCP clients sign in through):\n *\n * - discovery: RFC 8414 metadata at `/.well-known/oauth-authorization-server`;\n * - a public client registered once per server (RFC 7591) with the loopback\n * redirect `http://127.0.0.1/callback`, cached beside the login. unfazed,\n * as RFC 8252 §7.3 asks, matches a loopback redirect on everything but the\n * port, so every login listens on a fresh random port under one client;\n * - PKCE S256 (RFC 7636), `state`, and RFC 9207's `iss` on the answer;\n * - a one-shot listener on 127.0.0.1 receives the code, which is exchanged\n * for an access and a refresh token, stored 0600 where the SDK reads them\n * (`../credentials.ts`);\n * - when the browser runs on another machine (SSH), the person pastes the\n * address it ended up on — the loopback URL that cannot load there.\n *\n * Logout revokes (RFC 7009) when the server advertises revocation, then\n * deletes the login. Every request names itself in User-Agent: the\n * platform's edge refuses some generic agents.\n */\nimport { spawn } from \"node:child_process\";\nimport { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { DEFAULT_ENDPOINT, ENDPOINT_ENV_VAR, TOKEN_ENV_VAR, UnfazedClient, VERSION, type FetchLike } from \"../client.js\";\nimport {\n LOGIN_COMMAND,\n credentialsDir,\n credentialsPath,\n deleteCredentials,\n oauthErrorText,\n postTokenForm,\n readCredentials,\n tokenFields,\n writeCredentials,\n type StoredCredentials,\n type TokenResponse,\n} from \"../credentials.js\";\nimport { UnfazedAuthError, UnfazedError, UnfazedHTTPError } from \"../errors.js\";\n\nexport const USER_AGENT = `unfazed-cli/${VERSION} (+https://unfazed.dev/auth.md)`;\n/** What a login asks for: reading and deploying, like an MCP client. */\nexport const DEFAULT_SCOPE = \"unfz:read unfz:deploy\";\nexport const CLIENT_NAME = \"unfazed CLI\";\nexport const CALLBACK_PATH = \"/callback\";\n/** The redirect registered once; each login uses it with its own port. */\nexport const REGISTERED_REDIRECT_URI = `http://127.0.0.1${CALLBACK_PATH}`;\n/** Registered clients, per issuer, beside the login. */\nexport const CLIENTS_FILE = \"oauth-clients.json\";\n/** How long a login waits for the browser. */\nexport const LOGIN_TIMEOUT_MS = 10 * 60_000;\n\nexport type Env = Record<string, string | undefined>;\n\n/** The `fetch` the CLI needs; the global one fits. */\nexport type Fetch = (\n input: string,\n init: { method: string; headers: Record<string, string>; body?: string; redirect?: \"manual\" | \"follow\"; signal?: AbortSignal },\n) => Promise<{ ok: boolean; status: number; text(): Promise<string> }>;\n\n/** A login that could not be completed; the message says why and what to do. */\nexport class UnfazedLoginError extends UnfazedError {}\n\nconst globalFetch = (): Fetch => globalThis.fetch.bind(globalThis) as unknown as Fetch;\n\n/* ------------------------------------------------------------------ OAuth */\n\n/** The GraphQL endpoint: given, else `UNFZ_API_URL`, else unfazed.dev's; a bare origin gets `/query`. */\nexport function resolveEndpoint(raw: string | undefined, env: Env): string {\n const value = (raw ?? env[ENDPOINT_ENV_VAR] ?? DEFAULT_ENDPOINT).trim().replace(/\\/+$/, \"\");\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new UnfazedLoginError(`unfazed: ${value} is not a URL. Pass the platform's address, e.g. --endpoint https://unfazed.dev`);\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") throw new UnfazedLoginError(`unfazed: ${value} is not an http(s) URL.`);\n // As the SDK reads UNFZ_API_URL: the full /query URL, or an origin.\n return /\\/query$|\\/graphql$/.test(value) ? value : `${value}/query`;\n}\n\n/** Whether a URL may carry tokens: https, or plain http to this machine only. */\nexport function safeURL(raw: unknown): raw is string {\n if (typeof raw !== \"string\") return false;\n try {\n const url = new URL(raw);\n if (url.protocol === \"https:\") return true;\n return url.protocol === \"http:\" && [\"127.0.0.1\", \"localhost\", \"[::1]\"].includes(url.hostname);\n } catch {\n return false;\n }\n}\n\n/** RFC 8414 authorization server metadata: the fields the CLI uses. */\nexport interface AuthServerMetadata {\n issuer: string;\n authorization_endpoint: string;\n token_endpoint: string;\n registration_endpoint?: string;\n revocation_endpoint?: string;\n code_challenge_methods_supported?: string[];\n response_types_supported?: string[];\n authorization_response_iss_parameter_supported?: boolean;\n}\n\n/** Reads and checks the metadata of the server at `origin` (e.g. https://unfazed.dev). */\nexport async function discover(origin: string, fetch: Fetch): Promise<AuthServerMetadata> {\n const url = `${origin}/.well-known/oauth-authorization-server`;\n let status: number;\n let text: string;\n try {\n const response = await fetch(url, { method: \"GET\", headers: { Accept: \"application/json\", \"User-Agent\": USER_AGENT } });\n status = response.status;\n text = await response.text();\n } catch (error) {\n throw new UnfazedLoginError(`unfazed: could not reach ${origin} (${(error as Error).message}).`, { cause: error });\n }\n if (status !== 200) throw new UnfazedLoginError(`unfazed: ${url} answered HTTP ${status}; is ${origin} an unfazed server?`);\n let metadata: Partial<AuthServerMetadata>;\n try {\n metadata = JSON.parse(text) as Partial<AuthServerMetadata>;\n } catch {\n throw new UnfazedLoginError(`unfazed: ${url} is not JSON; is ${origin} an unfazed server?`);\n }\n // RFC 8414 §3.3: the issuer must be the one asked, or the metadata is not its.\n if (typeof metadata.issuer !== \"string\" || metadata.issuer.replace(/\\/+$/, \"\") !== origin) {\n throw new UnfazedLoginError(`unfazed: ${url} names issuer ${JSON.stringify(metadata.issuer)}, not ${origin}.`);\n }\n for (const field of [\"authorization_endpoint\", \"token_endpoint\"] as const) {\n if (!safeURL(metadata[field])) throw new UnfazedLoginError(`unfazed: ${url} has no usable ${field}.`);\n }\n for (const field of [\"registration_endpoint\", \"revocation_endpoint\"] as const) {\n if (metadata[field] !== undefined && !safeURL(metadata[field])) delete metadata[field];\n }\n if (metadata.code_challenge_methods_supported && !metadata.code_challenge_methods_supported.includes(\"S256\")) {\n throw new UnfazedLoginError(`unfazed: ${origin} does not support PKCE S256, which the login requires.`);\n }\n if (metadata.response_types_supported && !metadata.response_types_supported.includes(\"code\")) {\n throw new UnfazedLoginError(`unfazed: ${origin} does not support the authorization code flow.`);\n }\n return { ...metadata, issuer: origin } as AuthServerMetadata;\n}\n\n/** A PKCE verifier and its S256 challenge. */\nexport function pkcePair(): { verifier: string; challenge: string } {\n const verifier = randomBytes(32).toString(\"base64url\");\n return { verifier, challenge: createHash(\"sha256\").update(verifier).digest(\"base64url\") };\n}\n\n/** A client the CLI registered at one issuer. */\nexport interface ClientRecord {\n client_id: string;\n redirect_uri: string;\n scope: string;\n registered_at: string;\n}\n\nfunction clientsPath(env: Env): string | undefined {\n const dir = credentialsDir(env);\n return dir ? join(dir, CLIENTS_FILE) : undefined;\n}\n\nfunction readClients(env: Env): Record<string, ClientRecord> {\n const path = clientsPath(env);\n if (!path) return {};\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, ClientRecord>) : {};\n } catch {\n // Missing or unreadable: register again, which costs one request.\n return {};\n }\n}\n\nfunction writeClients(env: Env, clients: Record<string, ClientRecord>): void {\n const path = clientsPath(env);\n if (!path) return;\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n const temp = `${path}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try {\n writeFileSync(temp, JSON.stringify(clients, null, 2) + \"\\n\", { mode: 0o600, flag: \"wx\" });\n chmodSync(temp, 0o600);\n renameSync(temp, path);\n } catch (error) {\n try {\n unlinkSync(temp);\n } catch {\n // never created, or already renamed\n }\n throw error;\n }\n}\n\n/** The cached client for `issuer` that may ask for `scope`, if any. */\nexport function cachedClient(env: Env, issuer: string, scope: string): ClientRecord | undefined {\n const record = readClients(env)[issuer];\n if (!record || typeof record.client_id !== \"string\" || record.redirect_uri !== REGISTERED_REDIRECT_URI) return undefined;\n const registered = new Set(String(record.scope ?? \"\").split(/\\s+/));\n return scope.split(/\\s+/).every((s) => registered.has(s)) ? record : undefined;\n}\n\nfunction forgetClient(env: Env, issuer: string): void {\n const clients = readClients(env);\n if (!(issuer in clients)) return;\n delete clients[issuer];\n writeClients(env, clients);\n}\n\n/** Registers the CLI as a public client at the server (RFC 7591) and caches it. */\nasync function registerClient(metadata: AuthServerMetadata, scope: string, env: Env, fetch: Fetch): Promise<ClientRecord> {\n if (!metadata.registration_endpoint) {\n throw new UnfazedLoginError(`unfazed: ${metadata.issuer} offers no client registration, which the login needs.`);\n }\n const response = await fetch(metadata.registration_endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Accept: \"application/json\", \"User-Agent\": USER_AGENT },\n body: JSON.stringify({\n client_name: CLIENT_NAME,\n redirect_uris: [REGISTERED_REDIRECT_URI],\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n application_type: \"native\",\n scope,\n }),\n });\n const body = parseObject(await response.text());\n if (response.status !== 201 && response.status !== 200) {\n throw new UnfazedLoginError(`unfazed: ${metadata.issuer} refused to register the CLI (${oauthErrorText(response.status, body)}).`);\n }\n if (typeof body[\"client_id\"] !== \"string\" || !body[\"client_id\"]) {\n throw new UnfazedLoginError(`unfazed: ${metadata.issuer} registered the CLI without a client_id.`);\n }\n const record: ClientRecord = {\n client_id: body[\"client_id\"],\n redirect_uri: REGISTERED_REDIRECT_URI,\n scope: typeof body[\"scope\"] === \"string\" && body[\"scope\"] ? body[\"scope\"] : scope,\n registered_at: new Date().toISOString(),\n };\n const clients = readClients(env);\n clients[metadata.issuer] = record;\n writeClients(env, clients);\n return record;\n}\n\nfunction authorizeURL(\n metadata: AuthServerMetadata,\n params: { clientId: string; redirectUri: string; scope: string; state: string; challenge: string },\n): string {\n const url = new URL(metadata.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", params.clientId);\n url.searchParams.set(\"redirect_uri\", params.redirectUri);\n url.searchParams.set(\"scope\", params.scope);\n url.searchParams.set(\"state\", params.state);\n url.searchParams.set(\"code_challenge\", params.challenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n return url.toString();\n}\n\n/**\n * One GET of the consent page before a browser sees it. A server that forgot\n * the cached client, or refuses the redirect, answers 400 here, so the CLI can\n * register again or say why instead of leaving the person on an error page.\n */\nasync function preflight(url: string, fetch: Fetch): Promise<{ ok: true } | { ok: false; reason: string }> {\n const response = await fetch(url, { method: \"GET\", headers: { Accept: \"text/html\", \"User-Agent\": USER_AGENT }, redirect: \"manual\" });\n const text = await response.text();\n if (response.status < 400) return { ok: true };\n return { ok: false, reason: oauthErrorText(response.status, parseObject(text)) };\n}\n\nasync function exchangeCode(\n metadata: AuthServerMetadata,\n params: { code: string; redirectUri: string; clientId: string; verifier: string },\n fetch: Fetch,\n): Promise<TokenResponse> {\n const { status, body } = await postTokenForm(\n fetch as FetchLike,\n metadata.token_endpoint,\n { grant_type: \"authorization_code\", code: params.code, redirect_uri: params.redirectUri, client_id: params.clientId, code_verifier: params.verifier },\n USER_AGENT,\n );\n if (status !== 200 || typeof body[\"access_token\"] !== \"string\" || !body[\"access_token\"]) {\n throw new UnfazedLoginError(`unfazed: ${metadata.issuer} did not issue tokens (${oauthErrorText(status, body)}). Run \\`${LOGIN_COMMAND}\\` again.`);\n }\n return body as unknown as TokenResponse;\n}\n\nasync function revokeToken(endpoint: string, params: { token: string; hint: string; clientId: string }, fetch: Fetch): Promise<boolean> {\n try {\n const { status } = await postTokenForm(\n fetch as FetchLike,\n endpoint,\n { token: params.token, token_type_hint: params.hint, client_id: params.clientId },\n USER_AGENT,\n );\n return status === 200;\n } catch {\n return false;\n }\n}\n\n/* --------------------------------------------------------------- commands */\n\nexport interface LoginOptions {\n /** GraphQL endpoint or platform origin. Default: `UNFZ_API_URL`, else https://unfazed.dev. */\n endpoint?: string;\n /** Space-separated scopes. Default: `unfz:read unfz:deploy`. */\n scope?: string;\n /** Default `process.env`: where `UNFZ_API_URL` and the login's location come from. */\n env?: Env;\n fetch?: Fetch;\n /** Opens the sign-in URL; resolves to whether a browser started. Default: print only. */\n openBrowser?: (url: string) => boolean | Promise<boolean>;\n /** Where progress goes (never a token). Default: stderr. */\n log?: (line: string) => void;\n /** Where a pasted answer is read from (a browser on another machine). Default: none. */\n input?: NodeJS.ReadableStream;\n /** How long to wait for the browser. Default {@link LOGIN_TIMEOUT_MS}. */\n timeoutMs?: number;\n}\n\n/** Signs in through the browser and stores the login. Resolves to it and where it was stored. */\nexport async function login(options: LoginOptions = {}): Promise<{ credentials: StoredCredentials; path: string }> {\n const env = options.env ?? process.env;\n const fetch = options.fetch ?? globalFetch();\n const log = options.log ?? ((line: string) => process.stderr.write(line + \"\\n\"));\n const scopes = (options.scope ?? DEFAULT_SCOPE).split(/[\\s,]+/).filter(Boolean);\n if (scopes.length === 0) throw new UnfazedLoginError(\"unfazed: --scope names no scope.\");\n const scope = [...new Set(scopes)].join(\" \");\n const endpoint = resolveEndpoint(options.endpoint, env);\n const origin = new URL(endpoint).origin;\n const path = credentialsPath(env);\n if (!path) throw new UnfazedLoginError(\"unfazed: no home directory to store the login in (set HOME or XDG_CONFIG_HOME).\");\n\n const metadata = await discover(origin, fetch);\n const listener = await listen();\n try {\n const redirectUri = `http://127.0.0.1:${listener.port}${CALLBACK_PATH}`;\n const { verifier, challenge } = pkcePair();\n const state = randomBytes(24).toString(\"base64url\");\n\n // A cached client the server no longer knows is registered again, once.\n let cached = cachedClient(env, metadata.issuer, scope);\n let client: ClientRecord;\n let url: string;\n for (let attempt = 0; ; attempt++) {\n client = cached ?? (await registerClient(metadata, scope, env, fetch));\n url = authorizeURL(metadata, { clientId: client.client_id, redirectUri, scope, state, challenge });\n const checked = await preflight(url, fetch);\n if (checked.ok) break;\n if (attempt > 0) throw new UnfazedLoginError(`unfazed: ${metadata.issuer} refused the sign-in request (${checked.reason}).`);\n forgetClient(env, metadata.issuer);\n cached = undefined;\n }\n\n if (await options.openBrowser?.(url)) {\n log(`Opening your browser to sign in to ${origin}.\\nIf it does not open, visit:\\n\\n ${url}\\n`);\n } else {\n log(`Open this URL in a browser to sign in to ${origin}:\\n\\n ${url}\\n`);\n }\n if (options.input) {\n log(\n `Browser on another machine? Sign in there, then paste the address it ends up on\\n` +\n `(http://127.0.0.1:${listener.port}${CALLBACK_PATH}?…, which will not load there) here and press Enter.`,\n );\n }\n log(\"Waiting for the sign-in…\");\n\n const callback = await waitForCallback(listener, state, options.input, options.timeoutMs ?? LOGIN_TIMEOUT_MS, log);\n try {\n const credentials = await finish(callback.params, { metadata, client, endpoint, redirectUri, verifier, fetch });\n writeCredentials(path, credentials);\n callback.respond?.(200, page(\"Signed in to unfazed\", `as <b>${escapeHTML(accountName(credentials))}</b>. You can close this tab and return to the terminal.`));\n return { credentials, path };\n } catch (error) {\n callback.respond?.(400, page(\"Sign-in failed\", `${escapeHTML((error as Error).message)}<br>Return to the terminal.`));\n throw error;\n }\n } finally {\n listener.close();\n }\n}\n\n/** Checks the answer, exchanges the code, and asks the platform who signed in. */\nasync function finish(\n params: URLSearchParams,\n ctx: { metadata: AuthServerMetadata; client: ClientRecord; endpoint: string; redirectUri: string; verifier: string; fetch: Fetch },\n): Promise<StoredCredentials> {\n const error = params.get(\"error\");\n if (error) {\n const description = params.get(\"error_description\");\n throw new UnfazedLoginError(`unfazed: the sign-in was not completed (${description ? `${error}: ${description}` : error}).`);\n }\n // RFC 9207: a server that says it sends `iss` must send its own, which\n // rules out a code from another server mixed into this flow.\n const iss = params.get(\"iss\");\n if ((iss !== null || ctx.metadata.authorization_response_iss_parameter_supported) && iss?.replace(/\\/+$/, \"\") !== ctx.metadata.issuer) {\n throw new UnfazedLoginError(`unfazed: the sign-in answer came from ${JSON.stringify(iss)}, not ${ctx.metadata.issuer}.`);\n }\n const code = params.get(\"code\");\n if (!code) throw new UnfazedLoginError(\"unfazed: the sign-in answer carries no code.\");\n\n const tokens = await exchangeCode(ctx.metadata, { code, redirectUri: ctx.redirectUri, clientId: ctx.client.client_id, verifier: ctx.verifier }, ctx.fetch);\n const actor = await whoamiWith(\n new UnfazedClient({ token: tokens.access_token, endpoint: ctx.endpoint, login: false, userAgent: USER_AGENT, fetch: ctx.fetch as FetchLike }),\n );\n const now = new Date().toISOString();\n return {\n version: 1,\n endpoint: ctx.endpoint,\n issuer: ctx.metadata.issuer,\n token_endpoint: ctx.metadata.token_endpoint,\n ...(ctx.metadata.revocation_endpoint ? { revocation_endpoint: ctx.metadata.revocation_endpoint } : {}),\n client_id: ctx.client.client_id,\n ...tokenFields(tokens),\n account: { kind: actor.kind, user_id: actor.userID, name: actor.name },\n created_at: now,\n updated_at: now,\n };\n}\n\n/** The fields `whoami` shows. */\nexport interface Actor {\n kind: string;\n userID: string;\n name: string;\n scopes: string[];\n}\n\nasync function whoamiWith(client: UnfazedClient): Promise<Actor> {\n const data = await client.query<{ whoami: Actor }, Record<string, never>>(\"query UnfazedCliWhoami { whoami { kind userID name scopes } }\");\n return data.whoami;\n}\n\nexport interface WhoamiResult {\n actor: Actor;\n endpoint: string;\n /** `token`: UNFZ_API_TOKEN; `login`: the stored login at `path`. */\n source: \"token\" | \"login\";\n path?: string;\n}\n\n/** Who the SDK acts as here: `UNFZ_API_TOKEN` when set, else the stored login (refreshed as needed). */\nexport async function whoami(options: { env?: Env; fetch?: Fetch } = {}): Promise<WhoamiResult> {\n const env = options.env ?? process.env;\n const client = new UnfazedClient({ env, userAgent: USER_AGENT, ...(options.fetch ? { fetch: options.fetch as FetchLike } : {}) });\n let actor: Actor;\n try {\n actor = await whoamiWith(client);\n } catch (error) {\n if (error instanceof UnfazedHTTPError && error.status === 401) {\n throw new UnfazedAuthError(\n client.authSource === \"token\"\n ? `unfazed: the platform refused ${TOKEN_ENV_VAR}. Unset it to use your \\`${LOGIN_COMMAND}\\`.`\n : `unfazed: the platform refused the stored login. Run \\`${LOGIN_COMMAND}\\` again.`,\n { cause: error },\n );\n }\n throw error;\n }\n const path = client.authSource === \"login\" ? credentialsPath(env) : undefined;\n return { actor, endpoint: client.endpoint, source: client.authSource, ...(path ? { path } : {}) };\n}\n\nexport interface LogoutResult {\n /** Whether there was a login to remove. */\n signedOut: boolean;\n issuer?: string;\n /** Whether the server revoked the tokens. */\n revoked: boolean;\n /** Whether the server offers revocation (RFC 7009). */\n revocationSupported: boolean;\n}\n\n/**\n * Revokes the stored tokens when the server advertises revocation, then\n * deletes the login. The endpoint is read from the server's metadata again,\n * so one the server gained since the login is used.\n */\nexport async function logout(options: { env?: Env; fetch?: Fetch } = {}): Promise<LogoutResult> {\n const env = options.env ?? process.env;\n const fetch = options.fetch ?? globalFetch();\n const none: LogoutResult = { signedOut: false, revoked: false, revocationSupported: false };\n const path = credentialsPath(env);\n if (!path) return none;\n let credentials: StoredCredentials | undefined;\n try {\n credentials = readCredentials(path);\n } catch {\n // An unreadable login is removed all the same.\n return { ...none, signedOut: deleteCredentials(path) };\n }\n if (!credentials) return none;\n\n let endpoint = credentials.revocation_endpoint;\n try {\n endpoint = (await discover(credentials.issuer, fetch)).revocation_endpoint;\n } catch {\n // Offline or gone: what the login recorded.\n }\n let revoked = false;\n if (endpoint) {\n const results: boolean[] = [];\n if (credentials.refresh_token) {\n results.push(await revokeToken(endpoint, { token: credentials.refresh_token, hint: \"refresh_token\", clientId: credentials.client_id }, fetch));\n }\n results.push(await revokeToken(endpoint, { token: credentials.access_token, hint: \"access_token\", clientId: credentials.client_id }, fetch));\n revoked = results.every(Boolean);\n }\n deleteCredentials(path);\n return { signedOut: true, issuer: credentials.issuer, revoked, revocationSupported: Boolean(endpoint) };\n}\n\nexport function accountName(credentials: StoredCredentials): string {\n return credentials.account?.name || credentials.account?.user_id || \"your account\";\n}\n\n/* ---------------------------------------------------------------- browser */\n\n/**\n * Whether a browser can be expected here. Over SSH, or on Linux without a\n * display, the login prints the URL instead (and takes the pasted answer).\n */\nexport function canOpenBrowser(env: Env, platform: NodeJS.Platform = process.platform): boolean {\n const custom = env[\"BROWSER\"]?.trim();\n if (custom) return custom !== \"none\";\n if (env[\"SSH_CONNECTION\"] || env[\"SSH_TTY\"]) return false;\n if (platform === \"darwin\" || platform === \"win32\") return true;\n return Boolean(env[\"DISPLAY\"] || env[\"WAYLAND_DISPLAY\"]);\n}\n\n/** The command that opens `url` — `$BROWSER`, `open`, `xdg-open` or the Windows URL handler — as argv, never a shell line. */\nexport function browserCommand(url: string, env: Env, platform: NodeJS.Platform = process.platform): string[] {\n const custom = env[\"BROWSER\"]?.trim();\n if (custom) {\n const parts = custom.split(/\\s+/);\n return parts.some((p) => p.includes(\"%s\")) ? parts.map((p) => p.replaceAll(\"%s\", url)) : [...parts, url];\n }\n if (platform === \"darwin\") return [\"open\", url];\n if (platform === \"win32\") return [\"rundll32\", \"url.dll,FileProtocolHandler\", url];\n return [\"xdg-open\", url];\n}\n\n/** Starts the browser on `url`. Resolves to whether it could be started. */\nexport function openBrowser(url: string, env: Env = process.env, platform: NodeJS.Platform = process.platform): Promise<boolean> {\n if (!canOpenBrowser(env, platform)) return Promise.resolve(false);\n const [command, ...args] = browserCommand(url, env, platform);\n return new Promise((resolve) => {\n try {\n const child = spawn(command!, args, { stdio: \"ignore\", detached: true, env: env as NodeJS.ProcessEnv });\n child.once(\"error\", () => resolve(false));\n child.once(\"spawn\", () => {\n child.unref();\n resolve(true);\n });\n } catch {\n resolve(false);\n }\n });\n}\n\n/* --------------------------------------------------------------- listener */\n\n/** An answer to the sign-in, from the listener or pasted. */\ninterface Callback {\n params: URLSearchParams;\n /** Answers the browser, when it came through the listener. */\n respond?: (status: number, html: string) => void;\n}\n\ninterface Listener {\n port: number;\n next(): Promise<Callback>;\n close(): void;\n}\n\n/** A one-shot HTTP listener on 127.0.0.1 and a port the OS picks. */\nasync function listen(): Promise<Listener> {\n const queue: Callback[] = [];\n let waiter: ((callback: Callback) => void) | undefined;\n const server = createServer((request: IncomingMessage, response: ServerResponse) => {\n const url = new URL(request.url ?? \"/\", \"http://127.0.0.1\");\n if (request.method !== \"GET\" || url.pathname !== CALLBACK_PATH) {\n response.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" }).end(\"not found\");\n return;\n }\n let answered = false;\n const callback: Callback = {\n params: url.searchParams,\n respond: (status, html) => {\n if (answered) return;\n answered = true;\n response\n .writeHead(status, { \"Content-Type\": \"text/html; charset=utf-8\", \"Cache-Control\": \"no-store\", \"Referrer-Policy\": \"no-referrer\", Connection: \"close\" })\n .end(html);\n },\n };\n const w = waiter;\n waiter = undefined;\n if (w) w(callback);\n else queue.push(callback);\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => resolve());\n });\n return {\n port: (server.address() as AddressInfo).port,\n next: () => {\n const queued = queue.shift();\n return queued ? Promise.resolve(queued) : new Promise((resolve) => (waiter = resolve));\n },\n close: () => {\n // Stop listening and drop idle keep-alive sockets (a browser's\n // preconnects); a page still being sent finishes first, and anything\n // left is cut after a moment rather than holding the process open.\n const s = server as { closeIdleConnections?: () => void; closeAllConnections?: () => void };\n server.close();\n s.closeIdleConnections?.();\n setTimeout(() => s.closeAllConnections?.(), 2_000).unref();\n },\n };\n}\n\n/**\n * The first answer that belongs to this sign-in, from the browser or pasted.\n * An answer with another `state` (an older tab, a forgery) is turned away and\n * the wait goes on.\n */\nasync function waitForCallback(\n listener: Listener,\n state: string,\n input: NodeJS.ReadableStream | undefined,\n timeoutMs: number,\n log: (line: string) => void,\n): Promise<Callback> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const lines = input ? createInterface({ input, terminal: false }) : undefined;\n const pasted: Callback[] = [];\n let wakePasted: (() => void) | undefined;\n lines?.on(\"line\", (line) => {\n const params = pastedParams(line);\n if (!params) {\n if (line.trim()) log(`That is not the address the browser ended up on (it starts with http://127.0.0.1:${listener.port}${CALLBACK_PATH}?).`);\n return;\n }\n pasted.push({ params });\n wakePasted?.();\n });\n try {\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new UnfazedLoginError(`unfazed: no sign-in within ${Math.max(1, Math.round(timeoutMs / 60_000))} min. Run \\`${LOGIN_COMMAND}\\` again.`)),\n timeoutMs,\n );\n });\n for (;;) {\n const fromPaste = new Promise<Callback>((resolve) => {\n const take = () => {\n const next = pasted.shift();\n if (next) resolve(next);\n };\n wakePasted = take;\n take();\n });\n const callback = await Promise.race([listener.next(), fromPaste, timeout]);\n if (sameState(callback.params.get(\"state\"), state)) return callback;\n log(\"Ignored a sign-in answer that belongs to another attempt (its state does not match).\");\n callback.respond?.(400, page(\"Not this sign-in\", \"This answer belongs to another sign-in attempt. Use the link the terminal printed last.\"));\n }\n } finally {\n clearTimeout(timer);\n lines?.close();\n }\n}\n\nfunction pastedParams(line: string): URLSearchParams | undefined {\n try {\n const url = new URL(line.trim());\n if (url.pathname !== CALLBACK_PATH || (!url.searchParams.has(\"code\") && !url.searchParams.has(\"error\"))) return undefined;\n return url.searchParams;\n } catch {\n return undefined;\n }\n}\n\nfunction sameState(got: string | null, want: string): boolean {\n if (got === null) return false;\n const a = Buffer.from(got);\n const b = Buffer.from(want);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction parseObject(text: string): Record<string, unknown> {\n try {\n const parsed = JSON.parse(text) as unknown;\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nfunction page(title: string, body: string): string {\n return (\n `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">` +\n `<title>${escapeHTML(title)} · unfazed</title>` +\n `<style>body{font:16px/1.5 system-ui,-apple-system,sans-serif;max-width:34rem;margin:18vh auto;padding:0 1.5rem;color:#111;background:#fff}` +\n `h1{font-size:1.4rem;margin:0 0 .5rem}p{color:#444}@media(prefers-color-scheme:dark){body{color:#eee;background:#111}p{color:#bbb}}</style>` +\n `</head><body><h1>${escapeHTML(title)}</h1><p>${body}</p></body></html>`\n );\n}\n\nfunction escapeHTML(text: string): string {\n return text.replace(/[&<>\"']/g, (c) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" })[c]!);\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Env } from "./login.js";
|
|
2
|
+
/** The Workflow SDK major the `unfazed` backend is written for. */
|
|
3
|
+
export declare const WORKFLOW_MAJOR = 5;
|
|
4
|
+
/** The package entry whose `createWorld()` is the backend: `dist/index.js`. */
|
|
5
|
+
export declare function backendPath(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Takes `--app <alias>` out of the arguments, and any `--backend unfazed`
|
|
8
|
+
* (which would make the CLI resolve `unfazed` from the current directory
|
|
9
|
+
* again). Everything else is the Workflow CLI's.
|
|
10
|
+
*/
|
|
11
|
+
export declare function splitArgs(args: string[]): {
|
|
12
|
+
app?: string;
|
|
13
|
+
rest: string[];
|
|
14
|
+
};
|
|
15
|
+
/** The project's own Workflow CLI (`node_modules/workflow`, any ancestor), when it is of {@link WORKFLOW_MAJOR}. */
|
|
16
|
+
export declare function localWorkflowCli(cwd: string): string | undefined;
|
|
17
|
+
/** How the Workflow CLI is started: argv before its own arguments. */
|
|
18
|
+
export declare function workflowCommand(cwd: string): {
|
|
19
|
+
command: string;
|
|
20
|
+
args: string[];
|
|
21
|
+
local: boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Runs the Workflow CLI over the app and resolves to its exit code. Fails
|
|
25
|
+
* before starting it when there is no login or no app to read, saying how to
|
|
26
|
+
* fix that.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runWorkflow(args: string[], options?: {
|
|
29
|
+
env?: Env;
|
|
30
|
+
cwd?: string;
|
|
31
|
+
}): Promise<number>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* `npx unfazed workflow <Workflow CLI args> [--app <alias>]`: Workflow SDK's
|
|
4
|
+
* own CLI (`workflow inspect …`, `workflow cancel …`, `workflow web`) over an
|
|
5
|
+
* unfazed app, from any directory.
|
|
6
|
+
*
|
|
7
|
+
* `npx workflow … --backend unfazed` resolves the backend from the current
|
|
8
|
+
* project (`createRequire(<cwd>/package.json).resolve("unfazed")`), so it only
|
|
9
|
+
* works where `unfazed` is installed. This passes the absolute path of this
|
|
10
|
+
* package's own entry instead — `require.resolve` takes absolute paths as
|
|
11
|
+
* they are, and the entry's `createWorld()` is the backend — through
|
|
12
|
+
* `WORKFLOW_TARGET_WORLD`, which the CLI's `--backend` flag defaults from
|
|
13
|
+
* (and which its web UI resolves the same way). The token is the stored
|
|
14
|
+
* login of `npx unfazed login` (or `UNFZ_API_TOKEN`); the app is `--app`,
|
|
15
|
+
* else `UNFZ_APP`, else `./unfazed.json`.
|
|
16
|
+
*
|
|
17
|
+
* The Workflow CLI is the project's own `workflow` when one of the major this
|
|
18
|
+
* World is written for is installed, else `npx -y workflow@5`. Its stdio and
|
|
19
|
+
* exit code are passed through.
|
|
20
|
+
*/
|
|
21
|
+
import { spawn } from "node:child_process";
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
import { dirname, join, parse } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { TOKEN_ENV_VAR } from "../client.js";
|
|
26
|
+
import { LOGIN_COMMAND, credentialsPath, readCredentials } from "../credentials.js";
|
|
27
|
+
import { APP_ENV_VAR, resolveAlias } from "../remote/app.js";
|
|
28
|
+
import { UnfazedLoginError } from "./login.js";
|
|
29
|
+
/** The Workflow SDK major the `unfazed` backend is written for. */
|
|
30
|
+
export const WORKFLOW_MAJOR = 5;
|
|
31
|
+
/** The package entry whose `createWorld()` is the backend: `dist/index.js`. */
|
|
32
|
+
export function backendPath() {
|
|
33
|
+
return fileURLToPath(new URL("../index.js", import.meta.url));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Takes `--app <alias>` out of the arguments, and any `--backend unfazed`
|
|
37
|
+
* (which would make the CLI resolve `unfazed` from the current directory
|
|
38
|
+
* again). Everything else is the Workflow CLI's.
|
|
39
|
+
*/
|
|
40
|
+
export function splitArgs(args) {
|
|
41
|
+
const rest = [];
|
|
42
|
+
let app;
|
|
43
|
+
for (let i = 0; i < args.length; i++) {
|
|
44
|
+
const arg = args[i];
|
|
45
|
+
if (arg === "--") {
|
|
46
|
+
rest.push(...args.slice(i));
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
if (arg === "--app" || arg.startsWith("--app=")) {
|
|
50
|
+
const value = arg === "--app" ? args[++i] : arg.slice("--app=".length);
|
|
51
|
+
if (!value || value.startsWith("-"))
|
|
52
|
+
throw new UnfazedLoginError("unfazed: --app needs the app's alias, e.g. --app birthday-card");
|
|
53
|
+
app = value;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if ((arg === "--backend" || arg === "-b") && args[i + 1] === "unfazed") {
|
|
57
|
+
i++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (arg === "--backend=unfazed" || arg === "-b=unfazed")
|
|
61
|
+
continue;
|
|
62
|
+
rest.push(arg);
|
|
63
|
+
}
|
|
64
|
+
return app === undefined ? { rest } : { app, rest };
|
|
65
|
+
}
|
|
66
|
+
/** The project's own Workflow CLI (`node_modules/workflow`, any ancestor), when it is of {@link WORKFLOW_MAJOR}. */
|
|
67
|
+
export function localWorkflowCli(cwd) {
|
|
68
|
+
for (let dir = cwd;; dir = dirname(dir)) {
|
|
69
|
+
const pkgPath = join(dir, "node_modules", "workflow", "package.json");
|
|
70
|
+
try {
|
|
71
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
72
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["workflow"];
|
|
73
|
+
if (bin && Number(String(pkg.version).split(".")[0]) === WORKFLOW_MAJOR)
|
|
74
|
+
return join(dirname(pkgPath), bin);
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// not here; look further up
|
|
79
|
+
}
|
|
80
|
+
if (dir === parse(dir).root)
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** How the Workflow CLI is started: argv before its own arguments. */
|
|
85
|
+
export function workflowCommand(cwd) {
|
|
86
|
+
const local = localWorkflowCli(cwd);
|
|
87
|
+
if (local) {
|
|
88
|
+
// The Workflow CLI is a Node program; under Bun, prefer Node when it is there.
|
|
89
|
+
const node = process.versions["bun"] ? "node" : process.execPath;
|
|
90
|
+
return { command: node, args: [local], local: true };
|
|
91
|
+
}
|
|
92
|
+
return { command: "npx", args: ["-y", `workflow@${WORKFLOW_MAJOR}`], local: false };
|
|
93
|
+
}
|
|
94
|
+
/** Whether the arguments only ask the Workflow CLI for help or its version. */
|
|
95
|
+
function helpOnly(rest) {
|
|
96
|
+
return rest.length === 0 || rest.some((a) => a === "--help" || a === "-h" || a === "help" || a === "--version");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Runs the Workflow CLI over the app and resolves to its exit code. Fails
|
|
100
|
+
* before starting it when there is no login or no app to read, saying how to
|
|
101
|
+
* fix that.
|
|
102
|
+
*/
|
|
103
|
+
export async function runWorkflow(args, options = {}) {
|
|
104
|
+
const env = options.env ?? process.env;
|
|
105
|
+
const cwd = options.cwd ?? process.cwd();
|
|
106
|
+
const { app, rest } = splitArgs(args);
|
|
107
|
+
if (!helpOnly(rest)) {
|
|
108
|
+
const path = credentialsPath(env);
|
|
109
|
+
if (!env[TOKEN_ENV_VAR]?.trim() && !(path && readCredentials(path))) {
|
|
110
|
+
throw new UnfazedLoginError(`unfazed: not signed in. Run \`${LOGIN_COMMAND}\` first (or set ${TOKEN_ENV_VAR}).`);
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
resolveAlias({ alias: app, env, cwd });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new UnfazedLoginError(`unfazed: which app? Pass --app <alias> (the <alias> of https://<alias>.unfz.to), e.g.\n` +
|
|
117
|
+
` npx unfazed workflow inspect runs --app my-app\n` +
|
|
118
|
+
`or set ${APP_ENV_VAR}, or run it from the app's directory with "alias" in its unfazed.json.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const childEnv = { ...env, WORKFLOW_TARGET_WORLD: backendPath() };
|
|
122
|
+
if (app)
|
|
123
|
+
childEnv[APP_ENV_VAR] = app;
|
|
124
|
+
const { command, args: prefix } = workflowCommand(cwd);
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const child = spawn(command, [...prefix, ...rest], {
|
|
127
|
+
cwd,
|
|
128
|
+
env: childEnv,
|
|
129
|
+
stdio: "inherit",
|
|
130
|
+
// npx is npx.cmd on Windows, which only a shell starts.
|
|
131
|
+
shell: process.platform === "win32" && command === "npx",
|
|
132
|
+
});
|
|
133
|
+
// A signal to this process goes on to the Workflow CLI (Ctrl-C in a
|
|
134
|
+
// terminal reaches both; a second SIGINT is harmless), and this process
|
|
135
|
+
// stays to pass on its exit code.
|
|
136
|
+
const signals = process;
|
|
137
|
+
const onInt = () => child.kill("SIGINT");
|
|
138
|
+
const onTerm = () => child.kill("SIGTERM");
|
|
139
|
+
signals.on("SIGINT", onInt);
|
|
140
|
+
signals.on("SIGTERM", onTerm);
|
|
141
|
+
const done = () => {
|
|
142
|
+
signals.off("SIGINT", onInt);
|
|
143
|
+
signals.off("SIGTERM", onTerm);
|
|
144
|
+
};
|
|
145
|
+
child.once("error", (error) => {
|
|
146
|
+
done();
|
|
147
|
+
reject(new UnfazedLoginError(error.code === "ENOENT"
|
|
148
|
+
? `unfazed: could not start the Workflow CLI (${command} not found). Install Node.js >= 20, which brings npx.`
|
|
149
|
+
: `unfazed: could not start the Workflow CLI (${error.message}).`));
|
|
150
|
+
});
|
|
151
|
+
child.once("exit", (code, signal) => {
|
|
152
|
+
done();
|
|
153
|
+
resolve(code ?? (signal === "SIGINT" ? 130 : 1));
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/cli/workflow.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"node\" />\n/**\n * `npx unfazed workflow <Workflow CLI args> [--app <alias>]`: Workflow SDK's\n * own CLI (`workflow inspect …`, `workflow cancel …`, `workflow web`) over an\n * unfazed app, from any directory.\n *\n * `npx workflow … --backend unfazed` resolves the backend from the current\n * project (`createRequire(<cwd>/package.json).resolve(\"unfazed\")`), so it only\n * works where `unfazed` is installed. This passes the absolute path of this\n * package's own entry instead — `require.resolve` takes absolute paths as\n * they are, and the entry's `createWorld()` is the backend — through\n * `WORKFLOW_TARGET_WORLD`, which the CLI's `--backend` flag defaults from\n * (and which its web UI resolves the same way). The token is the stored\n * login of `npx unfazed login` (or `UNFZ_API_TOKEN`); the app is `--app`,\n * else `UNFZ_APP`, else `./unfazed.json`.\n *\n * The Workflow CLI is the project's own `workflow` when one of the major this\n * World is written for is installed, else `npx -y workflow@5`. Its stdio and\n * exit code are passed through.\n */\nimport { spawn } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join, parse } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { TOKEN_ENV_VAR } from \"../client.js\";\nimport { LOGIN_COMMAND, credentialsPath, readCredentials } from \"../credentials.js\";\nimport { APP_ENV_VAR, resolveAlias } from \"../remote/app.js\";\nimport { UnfazedLoginError, type Env } from \"./login.js\";\n\n/** The Workflow SDK major the `unfazed` backend is written for. */\nexport const WORKFLOW_MAJOR = 5;\n\n/** The package entry whose `createWorld()` is the backend: `dist/index.js`. */\nexport function backendPath(): string {\n return fileURLToPath(new URL(\"../index.js\", import.meta.url));\n}\n\n/**\n * Takes `--app <alias>` out of the arguments, and any `--backend unfazed`\n * (which would make the CLI resolve `unfazed` from the current directory\n * again). Everything else is the Workflow CLI's.\n */\nexport function splitArgs(args: string[]): { app?: string; rest: string[] } {\n const rest: string[] = [];\n let app: string | undefined;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n if (arg === \"--\") {\n rest.push(...args.slice(i));\n break;\n }\n if (arg === \"--app\" || arg.startsWith(\"--app=\")) {\n const value = arg === \"--app\" ? args[++i] : arg.slice(\"--app=\".length);\n if (!value || value.startsWith(\"-\")) throw new UnfazedLoginError(\"unfazed: --app needs the app's alias, e.g. --app birthday-card\");\n app = value;\n continue;\n }\n if ((arg === \"--backend\" || arg === \"-b\") && args[i + 1] === \"unfazed\") {\n i++;\n continue;\n }\n if (arg === \"--backend=unfazed\" || arg === \"-b=unfazed\") continue;\n rest.push(arg);\n }\n return app === undefined ? { rest } : { app, rest };\n}\n\n/** The project's own Workflow CLI (`node_modules/workflow`, any ancestor), when it is of {@link WORKFLOW_MAJOR}. */\nexport function localWorkflowCli(cwd: string): string | undefined {\n for (let dir = cwd; ; dir = dirname(dir)) {\n const pkgPath = join(dir, \"node_modules\", \"workflow\", \"package.json\");\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version?: string; bin?: string | Record<string, string> };\n const bin = typeof pkg.bin === \"string\" ? pkg.bin : pkg.bin?.[\"workflow\"];\n if (bin && Number(String(pkg.version).split(\".\")[0]) === WORKFLOW_MAJOR) return join(dirname(pkgPath), bin);\n return undefined;\n } catch {\n // not here; look further up\n }\n if (dir === parse(dir).root) return undefined;\n }\n}\n\n/** How the Workflow CLI is started: argv before its own arguments. */\nexport function workflowCommand(cwd: string): { command: string; args: string[]; local: boolean } {\n const local = localWorkflowCli(cwd);\n if (local) {\n // The Workflow CLI is a Node program; under Bun, prefer Node when it is there.\n const node = process.versions[\"bun\"] ? \"node\" : process.execPath;\n return { command: node, args: [local], local: true };\n }\n return { command: \"npx\", args: [\"-y\", `workflow@${WORKFLOW_MAJOR}`], local: false };\n}\n\n/** Whether the arguments only ask the Workflow CLI for help or its version. */\nfunction helpOnly(rest: string[]): boolean {\n return rest.length === 0 || rest.some((a) => a === \"--help\" || a === \"-h\" || a === \"help\" || a === \"--version\");\n}\n\n/**\n * Runs the Workflow CLI over the app and resolves to its exit code. Fails\n * before starting it when there is no login or no app to read, saying how to\n * fix that.\n */\nexport async function runWorkflow(args: string[], options: { env?: Env; cwd?: string } = {}): Promise<number> {\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n const { app, rest } = splitArgs(args);\n\n if (!helpOnly(rest)) {\n const path = credentialsPath(env);\n if (!env[TOKEN_ENV_VAR]?.trim() && !(path && readCredentials(path))) {\n throw new UnfazedLoginError(`unfazed: not signed in. Run \\`${LOGIN_COMMAND}\\` first (or set ${TOKEN_ENV_VAR}).`);\n }\n try {\n resolveAlias({ alias: app, env, cwd });\n } catch {\n throw new UnfazedLoginError(\n `unfazed: which app? Pass --app <alias> (the <alias> of https://<alias>.unfz.to), e.g.\\n` +\n ` npx unfazed workflow inspect runs --app my-app\\n` +\n `or set ${APP_ENV_VAR}, or run it from the app's directory with \"alias\" in its unfazed.json.`,\n );\n }\n }\n\n const childEnv: Env = { ...env, WORKFLOW_TARGET_WORLD: backendPath() };\n if (app) childEnv[APP_ENV_VAR] = app;\n const { command, args: prefix } = workflowCommand(cwd);\n return new Promise<number>((resolve, reject) => {\n const child = spawn(command, [...prefix, ...rest], {\n cwd,\n env: childEnv as NodeJS.ProcessEnv,\n stdio: \"inherit\",\n // npx is npx.cmd on Windows, which only a shell starts.\n shell: process.platform === \"win32\" && command === \"npx\",\n });\n // A signal to this process goes on to the Workflow CLI (Ctrl-C in a\n // terminal reaches both; a second SIGINT is harmless), and this process\n // stays to pass on its exit code.\n const signals = process as unknown as NodeJS.EventEmitter;\n const onInt = () => child.kill(\"SIGINT\");\n const onTerm = () => child.kill(\"SIGTERM\");\n signals.on(\"SIGINT\", onInt);\n signals.on(\"SIGTERM\", onTerm);\n const done = () => {\n signals.off(\"SIGINT\", onInt);\n signals.off(\"SIGTERM\", onTerm);\n };\n child.once(\"error\", (error) => {\n done();\n reject(\n new UnfazedLoginError(\n (error as NodeJS.ErrnoException).code === \"ENOENT\"\n ? `unfazed: could not start the Workflow CLI (${command} not found). Install Node.js >= 20, which brings npx.`\n : `unfazed: could not start the Workflow CLI (${error.message}).`,\n ),\n );\n });\n child.once(\"exit\", (code, signal) => {\n done();\n resolve(code ?? (signal === \"SIGINT\" ? 130 : 1));\n });\n });\n}\n"]}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/**
|
|
4
|
+
* The `unfazed` bin:
|
|
5
|
+
*
|
|
6
|
+
* npx unfazed login sign in in the browser; the SDK and
|
|
7
|
+
* `npx workflow … --backend unfazed` then use the login
|
|
8
|
+
* npx unfazed whoami who the SDK acts as here
|
|
9
|
+
* npx unfazed logout revoke (when the server offers it) and forget the login
|
|
10
|
+
* npx unfazed workflow inspect runs --app <alias>
|
|
11
|
+
* Workflow SDK's CLI over an app, from any directory
|
|
12
|
+
*
|
|
13
|
+
* Nothing imports this file: `import "unfazed"` never loads the CLI.
|
|
14
|
+
*/
|
|
15
|
+
import { parseArgs } from "node:util";
|
|
16
|
+
import { VERSION } from "./client.js";
|
|
17
|
+
import { DEFAULT_SCOPE, accountName, canOpenBrowser, login, logout, openBrowser, whoami } from "./cli/login.js";
|
|
18
|
+
import { runWorkflow } from "./cli/workflow.js";
|
|
19
|
+
const HELP = `unfazed ${VERSION} — sign in to unfazed from a terminal
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
npx unfazed login [--endpoint <url>] [--scope "<scopes>"] [--no-browser]
|
|
23
|
+
npx unfazed whoami [--json]
|
|
24
|
+
npx unfazed logout
|
|
25
|
+
npx unfazed workflow <Workflow CLI arguments> [--app <alias>]
|
|
26
|
+
|
|
27
|
+
Commands:
|
|
28
|
+
login Sign in to unfazed in the browser. The login is stored in
|
|
29
|
+
\${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json (mode 0600);
|
|
30
|
+
the unfazed SDK and \`npx workflow … --backend unfazed\` use it
|
|
31
|
+
whenever UNFZ_API_TOKEN is unset, refreshing it as it expires.
|
|
32
|
+
whoami Show who the SDK acts as here: UNFZ_API_TOKEN when set, else the login.
|
|
33
|
+
logout Revoke the login's tokens when the server offers revocation, and
|
|
34
|
+
delete the stored login.
|
|
35
|
+
workflow Workflow SDK's CLI over an unfazed app, from any directory:
|
|
36
|
+
npx unfazed workflow inspect runs --app birthday-card
|
|
37
|
+
npx unfazed workflow inspect run <run-id> --app birthday-card
|
|
38
|
+
npx unfazed workflow cancel <run-id> --app birthday-card
|
|
39
|
+
npx unfazed workflow web --app birthday-card
|
|
40
|
+
The app is --app, else UNFZ_APP, else ./unfazed.json; the token is
|
|
41
|
+
the login (or UNFZ_API_TOKEN). Runs the project's own \`workflow\`
|
|
42
|
+
(major 5) when installed, else \`npx -y workflow@5\`.
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--endpoint <url> The platform: its origin or GraphQL URL
|
|
46
|
+
(default: UNFZ_API_URL, else https://unfazed.dev)
|
|
47
|
+
--scope <scopes> Scopes to ask for (default: "${DEFAULT_SCOPE}")
|
|
48
|
+
--no-browser Print the sign-in URL instead of opening a browser
|
|
49
|
+
--json whoami: print JSON
|
|
50
|
+
-h, --help Show this help
|
|
51
|
+
-v, --version Show the version
|
|
52
|
+
`;
|
|
53
|
+
/** Unwinds `main` once {@link fail} has scheduled the exit. */
|
|
54
|
+
class Exit extends Error {
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Exits once stdout and stderr are flushed (pipes are asynchronous on macOS),
|
|
58
|
+
* so nothing a command started — a browser's keep-alive socket — holds the
|
|
59
|
+
* terminal, and no output is cut.
|
|
60
|
+
*/
|
|
61
|
+
function exit(code) {
|
|
62
|
+
process.exitCode = code;
|
|
63
|
+
let pending = 2;
|
|
64
|
+
const done = () => {
|
|
65
|
+
if (--pending === 0)
|
|
66
|
+
process.exit(code);
|
|
67
|
+
};
|
|
68
|
+
process.stdout.write("", done);
|
|
69
|
+
process.stderr.write("", done);
|
|
70
|
+
}
|
|
71
|
+
function fail(message) {
|
|
72
|
+
process.stderr.write(message.replace(/\s+$/, "") + "\n");
|
|
73
|
+
exit(1);
|
|
74
|
+
throw new Exit();
|
|
75
|
+
}
|
|
76
|
+
async function main(argv) {
|
|
77
|
+
// Everything after `workflow` is the Workflow CLI's (but --app).
|
|
78
|
+
if (argv[0] === "workflow") {
|
|
79
|
+
process.exitCode = await runWorkflow(argv.slice(1));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
let parsed;
|
|
83
|
+
const parse = (args) => parseArgs({
|
|
84
|
+
args,
|
|
85
|
+
allowPositionals: true,
|
|
86
|
+
options: {
|
|
87
|
+
endpoint: { type: "string" },
|
|
88
|
+
scope: { type: "string" },
|
|
89
|
+
"no-browser": { type: "boolean" },
|
|
90
|
+
json: { type: "boolean" },
|
|
91
|
+
help: { type: "boolean", short: "h" },
|
|
92
|
+
version: { type: "boolean", short: "v" },
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
try {
|
|
96
|
+
parsed = parse(argv);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
fail(`unfazed: ${error.message}\n\n${HELP}`);
|
|
100
|
+
}
|
|
101
|
+
const { values, positionals } = parsed;
|
|
102
|
+
if (values.version) {
|
|
103
|
+
process.stdout.write(`${VERSION}\n`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const [command, ...rest] = positionals;
|
|
107
|
+
if (values.help || command === "help") {
|
|
108
|
+
process.stdout.write(HELP);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (command === undefined)
|
|
112
|
+
fail(HELP);
|
|
113
|
+
if (rest.length > 0)
|
|
114
|
+
fail(`unfazed: unexpected argument ${JSON.stringify(rest[0])}\n\n${HELP}`);
|
|
115
|
+
switch (command) {
|
|
116
|
+
case "login": {
|
|
117
|
+
const browser = !values["no-browser"] && canOpenBrowser(process.env);
|
|
118
|
+
const { credentials, path } = await login({
|
|
119
|
+
...(values.endpoint ? { endpoint: values.endpoint } : {}),
|
|
120
|
+
...(values.scope ? { scope: values.scope } : {}),
|
|
121
|
+
...(browser ? { openBrowser: (url) => openBrowser(url) } : {}),
|
|
122
|
+
// A browser on another machine: the person pastes where it ended up.
|
|
123
|
+
...(process.stdin.isTTY ? { input: process.stdin } : {}),
|
|
124
|
+
});
|
|
125
|
+
const kind = credentials.account?.kind ? ` (${credentials.account.kind.toLowerCase()})` : "";
|
|
126
|
+
process.stderr.write(`\nSigned in to ${credentials.issuer} as ${accountName(credentials)}${kind}.\n` +
|
|
127
|
+
`The login is stored in ${path}.\n` +
|
|
128
|
+
`The unfazed SDK and \`npx workflow … --backend unfazed\` use it when UNFZ_API_TOKEN is unset.\n`);
|
|
129
|
+
if (process.env["UNFZ_API_TOKEN"])
|
|
130
|
+
process.stderr.write("UNFZ_API_TOKEN is set in this shell and takes precedence; unset it to use the login.\n");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
case "whoami": {
|
|
134
|
+
const result = await whoami();
|
|
135
|
+
if (values.json) {
|
|
136
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const { actor } = result;
|
|
140
|
+
process.stdout.write(`${actor.name} (${actor.kind.toLowerCase()} ${actor.userID})\n` +
|
|
141
|
+
` endpoint ${result.endpoint}\n` +
|
|
142
|
+
` scopes ${actor.scopes.join(" ") || "(none)"}\n` +
|
|
143
|
+
` via ${result.source === "token" ? "UNFZ_API_TOKEN" : `the stored login (${result.path})`}\n`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
case "logout": {
|
|
147
|
+
const result = await logout();
|
|
148
|
+
if (!result.signedOut) {
|
|
149
|
+
process.stderr.write("Not signed in.\n");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const revocation = result.revoked
|
|
153
|
+
? "The tokens were revoked."
|
|
154
|
+
: result.revocationSupported
|
|
155
|
+
? "Revoking the tokens failed; they stop working when they expire."
|
|
156
|
+
: "The server offers no token revocation; the deleted tokens stop working when they expire.";
|
|
157
|
+
process.stderr.write(`Signed out of ${result.issuer}. ${revocation}\n`);
|
|
158
|
+
if (process.env["UNFZ_API_TOKEN"])
|
|
159
|
+
process.stderr.write("UNFZ_API_TOKEN is still set in this shell.\n");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
fail(`unfazed: unknown command ${JSON.stringify(command)}\n\n${HELP}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
main(process.argv.slice(2)).then(() => exit(typeof process.exitCode === "number" ? process.exitCode : 0), (error) => {
|
|
167
|
+
if (error instanceof Exit)
|
|
168
|
+
return;
|
|
169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
170
|
+
process.stderr.write((message.startsWith("unfazed") ? message : `unfazed: ${message}`) + "\n");
|
|
171
|
+
exit(1);
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,8BAA8B;AAC9B;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAChH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,IAAI,GAAG,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDA4BoB,aAAa;;;;;CAK/D,CAAC;AAEF,+DAA+D;AAC/D,MAAM,IAAK,SAAQ,KAAK;CAAG;AAE3B;;;;GAIG;AACH,SAAS,IAAI,CAAC,IAAY;IACxB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IACxB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,IAAI,EAAE,OAAO,KAAK,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,CAAC,CAAC,CAAC;IACR,MAAM,IAAI,IAAI,EAAE,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,IAAc;IAChC,iEAAiE;IACjE,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;QAC3B,OAAO,CAAC,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IACD,IAAI,MAAgC,CAAC;IACrC,MAAM,KAAK,GAAG,CAAC,IAAc,EAAE,EAAE,CAC/B,SAAS,CAAC;QACR,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE;YACP,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC5B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YACjC,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YACzB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;YACrC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACzC;KACF,CAAC,CAAC;IACL,IAAI,CAAC;QACH,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,YAAa,KAAe,CAAC,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACvC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IACD,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,WAAW,CAAC;IACvC,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,gCAAgC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAEhG,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACrE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC;gBACxC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChD,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtE,qEAAqE;gBACrE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzD,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7F,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kBAAkB,WAAW,CAAC,MAAM,OAAO,WAAW,CAAC,WAAW,CAAC,GAAG,IAAI,KAAK;gBAC7E,0BAA0B,IAAI,KAAK;gBACnC,iGAAiG,CACpG,CAAC;YACF,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wFAAwF,CAAC,CAAC;YAClJ,OAAO;QACT,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;YAC9B,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;YACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,MAAM,KAAK;gBAC7D,eAAe,MAAM,CAAC,QAAQ,IAAI;gBAClC,eAAe,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,IAAI;gBACrD,eAAe,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,qBAAqB,MAAM,CAAC,IAAI,GAAG,IAAI,CACxG,CAAC;YACF,OAAO;QACT,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBACzC,OAAO;YACT,CAAC;YACD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO;gBAC/B,CAAC,CAAC,0BAA0B;gBAC5B,CAAC,CAAC,MAAM,CAAC,mBAAmB;oBAC1B,CAAC,CAAC,iEAAiE;oBACnE,CAAC,CAAC,0FAA0F,CAAC;YACjG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC;YACxE,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YACxG,OAAO;QACT,CAAC;QACD;YACE,IAAI,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EACvE,CAAC,KAAc,EAAE,EAAE;IACjB,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO;IAClC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/F,IAAI,CAAC,CAAC,CAAC,CAAC;AACV,CAAC,CACF,CAAC","sourcesContent":["#!/usr/bin/env node\n/// <reference types=\"node\" />\n/**\n * The `unfazed` bin:\n *\n * npx unfazed login sign in in the browser; the SDK and\n * `npx workflow … --backend unfazed` then use the login\n * npx unfazed whoami who the SDK acts as here\n * npx unfazed logout revoke (when the server offers it) and forget the login\n * npx unfazed workflow inspect runs --app <alias>\n * Workflow SDK's CLI over an app, from any directory\n *\n * Nothing imports this file: `import \"unfazed\"` never loads the CLI.\n */\nimport { parseArgs } from \"node:util\";\nimport { VERSION } from \"./client.js\";\nimport { DEFAULT_SCOPE, accountName, canOpenBrowser, login, logout, openBrowser, whoami } from \"./cli/login.js\";\nimport { runWorkflow } from \"./cli/workflow.js\";\n\nconst HELP = `unfazed ${VERSION} — sign in to unfazed from a terminal\n\nUsage:\n npx unfazed login [--endpoint <url>] [--scope \"<scopes>\"] [--no-browser]\n npx unfazed whoami [--json]\n npx unfazed logout\n npx unfazed workflow <Workflow CLI arguments> [--app <alias>]\n\nCommands:\n login Sign in to unfazed in the browser. The login is stored in\n \\${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json (mode 0600);\n the unfazed SDK and \\`npx workflow … --backend unfazed\\` use it\n whenever UNFZ_API_TOKEN is unset, refreshing it as it expires.\n whoami Show who the SDK acts as here: UNFZ_API_TOKEN when set, else the login.\n logout Revoke the login's tokens when the server offers revocation, and\n delete the stored login.\n workflow Workflow SDK's CLI over an unfazed app, from any directory:\n npx unfazed workflow inspect runs --app birthday-card\n npx unfazed workflow inspect run <run-id> --app birthday-card\n npx unfazed workflow cancel <run-id> --app birthday-card\n npx unfazed workflow web --app birthday-card\n The app is --app, else UNFZ_APP, else ./unfazed.json; the token is\n the login (or UNFZ_API_TOKEN). Runs the project's own \\`workflow\\`\n (major 5) when installed, else \\`npx -y workflow@5\\`.\n\nOptions:\n --endpoint <url> The platform: its origin or GraphQL URL\n (default: UNFZ_API_URL, else https://unfazed.dev)\n --scope <scopes> Scopes to ask for (default: \"${DEFAULT_SCOPE}\")\n --no-browser Print the sign-in URL instead of opening a browser\n --json whoami: print JSON\n -h, --help Show this help\n -v, --version Show the version\n`;\n\n/** Unwinds `main` once {@link fail} has scheduled the exit. */\nclass Exit extends Error {}\n\n/**\n * Exits once stdout and stderr are flushed (pipes are asynchronous on macOS),\n * so nothing a command started — a browser's keep-alive socket — holds the\n * terminal, and no output is cut.\n */\nfunction exit(code: number): void {\n process.exitCode = code;\n let pending = 2;\n const done = () => {\n if (--pending === 0) process.exit(code);\n };\n process.stdout.write(\"\", done);\n process.stderr.write(\"\", done);\n}\n\nfunction fail(message: string): never {\n process.stderr.write(message.replace(/\\s+$/, \"\") + \"\\n\");\n exit(1);\n throw new Exit();\n}\n\nasync function main(argv: string[]): Promise<void> {\n // Everything after `workflow` is the Workflow CLI's (but --app).\n if (argv[0] === \"workflow\") {\n process.exitCode = await runWorkflow(argv.slice(1));\n return;\n }\n let parsed: ReturnType<typeof parse>;\n const parse = (args: string[]) =>\n parseArgs({\n args,\n allowPositionals: true,\n options: {\n endpoint: { type: \"string\" },\n scope: { type: \"string\" },\n \"no-browser\": { type: \"boolean\" },\n json: { type: \"boolean\" },\n help: { type: \"boolean\", short: \"h\" },\n version: { type: \"boolean\", short: \"v\" },\n },\n });\n try {\n parsed = parse(argv);\n } catch (error) {\n fail(`unfazed: ${(error as Error).message}\\n\\n${HELP}`);\n }\n const { values, positionals } = parsed;\n if (values.version) {\n process.stdout.write(`${VERSION}\\n`);\n return;\n }\n const [command, ...rest] = positionals;\n if (values.help || command === \"help\") {\n process.stdout.write(HELP);\n return;\n }\n if (command === undefined) fail(HELP);\n if (rest.length > 0) fail(`unfazed: unexpected argument ${JSON.stringify(rest[0])}\\n\\n${HELP}`);\n\n switch (command) {\n case \"login\": {\n const browser = !values[\"no-browser\"] && canOpenBrowser(process.env);\n const { credentials, path } = await login({\n ...(values.endpoint ? { endpoint: values.endpoint } : {}),\n ...(values.scope ? { scope: values.scope } : {}),\n ...(browser ? { openBrowser: (url: string) => openBrowser(url) } : {}),\n // A browser on another machine: the person pastes where it ended up.\n ...(process.stdin.isTTY ? { input: process.stdin } : {}),\n });\n const kind = credentials.account?.kind ? ` (${credentials.account.kind.toLowerCase()})` : \"\";\n process.stderr.write(\n `\\nSigned in to ${credentials.issuer} as ${accountName(credentials)}${kind}.\\n` +\n `The login is stored in ${path}.\\n` +\n `The unfazed SDK and \\`npx workflow … --backend unfazed\\` use it when UNFZ_API_TOKEN is unset.\\n`,\n );\n if (process.env[\"UNFZ_API_TOKEN\"]) process.stderr.write(\"UNFZ_API_TOKEN is set in this shell and takes precedence; unset it to use the login.\\n\");\n return;\n }\n case \"whoami\": {\n const result = await whoami();\n if (values.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n return;\n }\n const { actor } = result;\n process.stdout.write(\n `${actor.name} (${actor.kind.toLowerCase()} ${actor.userID})\\n` +\n ` endpoint ${result.endpoint}\\n` +\n ` scopes ${actor.scopes.join(\" \") || \"(none)\"}\\n` +\n ` via ${result.source === \"token\" ? \"UNFZ_API_TOKEN\" : `the stored login (${result.path})`}\\n`,\n );\n return;\n }\n case \"logout\": {\n const result = await logout();\n if (!result.signedOut) {\n process.stderr.write(\"Not signed in.\\n\");\n return;\n }\n const revocation = result.revoked\n ? \"The tokens were revoked.\"\n : result.revocationSupported\n ? \"Revoking the tokens failed; they stop working when they expire.\"\n : \"The server offers no token revocation; the deleted tokens stop working when they expire.\";\n process.stderr.write(`Signed out of ${result.issuer}. ${revocation}\\n`);\n if (process.env[\"UNFZ_API_TOKEN\"]) process.stderr.write(\"UNFZ_API_TOKEN is still set in this shell.\\n\");\n return;\n }\n default:\n fail(`unfazed: unknown command ${JSON.stringify(command)}\\n\\n${HELP}`);\n }\n}\n\nmain(process.argv.slice(2)).then(\n () => exit(typeof process.exitCode === \"number\" ? process.exitCode : 0),\n (error: unknown) => {\n if (error instanceof Exit) return;\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write((message.startsWith(\"unfazed\") ? message : `unfazed: ${message}`) + \"\\n\");\n exit(1);\n },\n);\n"]}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** SDK version, sent as the User-Agent. Keep in sync with package.json. */
|
|
2
|
-
export declare const VERSION = "0.6.
|
|
2
|
+
export declare const VERSION = "0.6.1";
|
|
3
3
|
/**
|
|
4
4
|
* Default GraphQL endpoint.
|
|
5
5
|
*
|
|
@@ -37,13 +37,29 @@ export type FetchLike = (input: string, init: {
|
|
|
37
37
|
}>;
|
|
38
38
|
export interface ClientOptions {
|
|
39
39
|
/**
|
|
40
|
-
* Bearer token. Falls back to `UNFZ_API_TOKEN
|
|
41
|
-
*
|
|
40
|
+
* Bearer token. Falls back to `UNFZ_API_TOKEN`, then to the login
|
|
41
|
+
* `npx unfazed login` stored (see {@link ClientOptions.login}). An agent gets
|
|
42
|
+
* one from https://unfazed.dev/auth.md — the same token the MCP server uses.
|
|
42
43
|
*/
|
|
43
44
|
token?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Use the stored login of `npx unfazed login`
|
|
47
|
+
* (`${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json`) when no token
|
|
48
|
+
* is passed and `UNFZ_API_TOKEN` is unset. Its access token is refreshed
|
|
49
|
+
* before it expires and when the platform refuses it. Default `true`; it is
|
|
50
|
+
* only used for the endpoint it was issued by, and is the endpoint when none
|
|
51
|
+
* is configured.
|
|
52
|
+
*/
|
|
53
|
+
login?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Where `UNFZ_API_TOKEN`, `UNFZ_API_URL` and the stored login's location
|
|
56
|
+
* (`XDG_CONFIG_HOME`, `HOME`) are read from. Default: `process.env`.
|
|
57
|
+
*/
|
|
58
|
+
env?: Record<string, string | undefined>;
|
|
44
59
|
/**
|
|
45
60
|
* Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to
|
|
46
|
-
* `UNFZ_API_URL`, then
|
|
61
|
+
* `UNFZ_API_URL`, then the stored login's endpoint, then
|
|
62
|
+
* {@link DEFAULT_ENDPOINT}.
|
|
47
63
|
*/
|
|
48
64
|
endpoint?: string;
|
|
49
65
|
/**
|
|
@@ -103,7 +119,7 @@ export interface RequestOptions {
|
|
|
103
119
|
* deliberately covers only the agent-facing ones.
|
|
104
120
|
*
|
|
105
121
|
* ```ts
|
|
106
|
-
* const client = new UnfazedClient();
|
|
122
|
+
* const client = new UnfazedClient(); // UNFZ_API_TOKEN, else `npx unfazed login`
|
|
107
123
|
* const data = await client.query<{ whoami: { name: string } }, never>(
|
|
108
124
|
* `query { whoami { name } }`,
|
|
109
125
|
* );
|
|
@@ -114,6 +130,11 @@ export declare class UnfazedClient {
|
|
|
114
130
|
/** Resolved GraphQL endpoint URL. */
|
|
115
131
|
readonly endpoint: string;
|
|
116
132
|
constructor(options?: ClientOptions);
|
|
133
|
+
/**
|
|
134
|
+
* Where the bearer token comes from: `"token"` (passed or
|
|
135
|
+
* `UNFZ_API_TOKEN`) or `"login"` (the stored login of `npx unfazed login`).
|
|
136
|
+
*/
|
|
137
|
+
get authSource(): "token" | "login";
|
|
117
138
|
/** Runs a query document. Identical wire behaviour to {@link mutate}. */
|
|
118
139
|
query<TResult, TVariables>(document: GraphQLDocument<TResult, TVariables>, variables?: TVariables, options?: RequestOptions): Promise<TResult>;
|
|
119
140
|
/**
|