unfazed 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +2 -2
- package/REFERENCE.md +2 -2
- package/dist/cli/login.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/eve-sandbox/daemon.d.ts +7 -1
- package/dist/eve-sandbox/daemon.js +27 -4
- package/dist/eve-sandbox/daemon.js.map +1 -1
- package/dist/eve-sandbox/index.d.ts +5 -0
- package/dist/eve-sandbox/index.js +18 -2
- package/dist/eve-sandbox/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/oidc.d.ts +3 -3
- package/dist/oidc.js +3 -3
- package/dist/oidc.js.map +1 -1
- package/dist/reference.generated.js +1 -1
- package/dist/reference.generated.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
Fixes for `unfazed/eve-sandbox`.
|
|
6
|
+
|
|
7
|
+
**Fixed**
|
|
8
|
+
|
|
9
|
+
- **The sandbox daemon now comes up after a box restart.** A box's boot
|
|
10
|
+
hooks run without the box's variables, so the daemon exited with "no
|
|
11
|
+
audience" on every boot and its hook restarted it forever. It now reads
|
|
12
|
+
the box's URL from the box's env snapshot (`/work/.unfazed/env.json`,
|
|
13
|
+
rebuilt on every boot) when its environment has none. An `audience` in
|
|
14
|
+
`config.json` still wins.
|
|
15
|
+
- **A box that runs no sandbox daemon is named as such.** Before, the provider
|
|
16
|
+
failed with a JSON parse error on the platform's placeholder page. Now
|
|
17
|
+
every call says `<box URL> is not running the eve sandbox daemon … run
|
|
18
|
+
\`npx unfazed eve-sandbox setup\` as root inside the box` (code
|
|
19
|
+
`daemon_not_running`).
|
|
20
|
+
|
|
3
21
|
## 0.10.0
|
|
4
22
|
|
|
5
23
|
eve sandboxes on unfazed boxes: an eve app on unfazed gets a real sandbox (a
|
package/README.md
CHANGED
|
@@ -634,9 +634,9 @@ import OpenAI from "openai";
|
|
|
634
634
|
import { oidc } from "unfazed";
|
|
635
635
|
|
|
636
636
|
const openai = new OpenAI({
|
|
637
|
-
baseURL: process.env.
|
|
637
|
+
baseURL: process.env.API_BASE_URL,
|
|
638
638
|
apiKey: "unused", // the token replaces it
|
|
639
|
-
fetch: oidc.fetchWithToken(process.env.
|
|
639
|
+
fetch: oidc.fetchWithToken(process.env.API_AUDIENCE),
|
|
640
640
|
});
|
|
641
641
|
```
|
|
642
642
|
|
package/REFERENCE.md
CHANGED
|
@@ -330,9 +330,9 @@ takes a `fetch`. An invalid audience (an unset variable) throws at once.
|
|
|
330
330
|
|
|
331
331
|
```ts
|
|
332
332
|
const openai = new OpenAI({
|
|
333
|
-
baseURL: process.env.
|
|
333
|
+
baseURL: process.env.API_BASE_URL,
|
|
334
334
|
apiKey: "unused",
|
|
335
|
-
fetch: unfazed.oidc.fetchWithToken(process.env.
|
|
335
|
+
fetch: unfazed.oidc.fetchWithToken(process.env.API_AUDIENCE),
|
|
336
336
|
});
|
|
337
337
|
```
|
|
338
338
|
|
package/dist/cli/login.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type StoredCredentials } from "../credentials.js";
|
|
2
2
|
import { UnfazedError } from "../errors.js";
|
|
3
|
-
export declare const USER_AGENT = "unfazed-cli/0.10.
|
|
3
|
+
export declare const USER_AGENT = "unfazed-cli/0.10.1 (+https://unfazed.dev/auth.md)";
|
|
4
4
|
/** What a login asks for: reading and deploying, like an MCP client. */
|
|
5
5
|
export declare const DEFAULT_SCOPE = "unfz:read unfz:deploy";
|
|
6
6
|
export declare const CLIENT_NAME = "unfazed CLI";
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LOGIN_COMMAND, openStoredLogin } from "./credentials.js";
|
|
2
2
|
import { UnfazedAuthError, UnfazedConfigError, UnfazedGraphQLError, UnfazedHTTPError, UnfazedTimeoutError, } from "./errors.js";
|
|
3
3
|
/** SDK version, sent as the User-Agent. Keep in sync with package.json. */
|
|
4
|
-
export const VERSION = "0.10.
|
|
4
|
+
export const VERSION = "0.10.1";
|
|
5
5
|
/**
|
|
6
6
|
* Default GraphQL endpoint.
|
|
7
7
|
*
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { LOGIN_COMMAND, openStoredLogin, type StoredLogin } from \"./credentials.js\";\nimport {\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedTimeoutError,\n type GraphQLResponseError,\n} from \"./errors.js\";\n\n/** SDK version, sent as the User-Agent. Keep in sync with package.json. */\nexport const VERSION = \"0.10.0\";\n\n/**\n * Default GraphQL endpoint.\n *\n * Note this is the FULL endpoint, not an origin: the platform's own documented\n * convention (`UNFZ_API_URL=\"https://unfazed.dev/query\"`, and cmd/showcase-deploy\n * in the unfazed repo) treats the /query URL as the configurable unit, so the\n * SDK matches it rather than inventing a base-URL convention.\n */\nexport const DEFAULT_ENDPOINT = \"https://unfazed.dev/query\";\n\n/** Platform origin, used to derive the endpoint when only `baseUrl` is given. */\nexport const DEFAULT_BASE_URL = \"https://unfazed.dev\";\n\n/** Path of the GraphQL endpoint under an origin. */\nexport const GRAPHQL_PATH = \"/query\";\n\n/**\n * Environment variable holding the bearer token.\n *\n * This is the platform's documented name (docs/agent-deployments.md,\n * internal/agentdocs/reference.md). It accepts either an operator API token or\n * an agent access token from POST /agents/register — /query takes both as\n * plain bearers.\n */\nexport const TOKEN_ENV_VAR = \"UNFZ_API_TOKEN\";\n\n/** Environment variable holding the full GraphQL endpoint URL. */\nexport const ENDPOINT_ENV_VAR = \"UNFZ_API_URL\";\n\n/** Minimal structural type of the `fetch` the transport needs. */\nexport type FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n text(): Promise<string>;\n}>;\n\nexport interface ClientOptions {\n /**\n * Bearer token. Falls back to `UNFZ_API_TOKEN`, then to the login\n * `npx unfazed login` stored (see {@link ClientOptions.login}). An agent gets\n * one from https://unfazed.dev/auth.md — the same token the MCP server uses.\n */\n token?: string;\n /**\n * Use the stored login of `npx unfazed login`\n * (`${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json`) when no token\n * is passed and `UNFZ_API_TOKEN` is unset. Its access token is refreshed\n * before it expires and when the platform refuses it. Default `true`; it is\n * only used for the endpoint it was issued by, and is the endpoint when none\n * is configured.\n */\n login?: boolean;\n /**\n * Where `UNFZ_API_TOKEN`, `UNFZ_API_URL` and the stored login's location\n * (`XDG_CONFIG_HOME`, `HOME`) are read from. Default: `process.env`.\n */\n env?: Record<string, string | undefined>;\n /**\n * Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to\n * `UNFZ_API_URL`, then the stored login's endpoint, then\n * {@link DEFAULT_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Platform origin; `/query` is appended. Convenience for self-hosted or\n * staging control planes where only the host differs.\n */\n baseUrl?: string;\n /** Extra headers merged into every request. Authorization always wins. */\n headers?: Record<string, string>;\n /**\n * Per-request timeout in ms. Default 60_000. Pass 0 to disable.\n *\n * Bounds the WHOLE request — connect, headers and reading the response body —\n * so a proxy that answers with headers and then stalls still fails on time.\n */\n timeoutMs?: number;\n /** Injection point for tests and instrumented/proxied transports. */\n fetch?: FetchLike;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n}\n\n/**\n * A GraphQL document.\n *\n * Structural rather than `string & {...}` on purpose: graphql-codegen emits\n * `TypedDocumentString`, a String SUBCLASS, which is not assignable to the\n * primitive `string`. Requiring only `toString()` plus the optional phantom\n * `__apiType` lets three things through with full inference:\n *\n * - generated `TypedDocumentString` documents (types inferred),\n * - documents assembled at runtime, like the batched writeFiles mutation,\n * - plain template-literal strings a caller wrote by hand.\n */\nexport interface GraphQLDocument<TResult, TVariables> {\n /** Phantom type carrier. Never called; present only to bind the generics. */\n __apiType?: (variables: TVariables) => TResult;\n toString(): string;\n}\n\n/** Result type carried by a typed document. */\nexport type ResultOf<T> = T extends GraphQLDocument<infer R, infer _V> ? R : unknown;\n\n/** Variables type carried by a typed document. */\nexport type VariablesOf<T> = T extends GraphQLDocument<infer _R, infer V> ? V : never;\n\n/** Per-request knobs. */\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n operationName?: string;\n}\n\n/**\n * Layer 1 — a typed, dependency-free GraphQL transport over `fetch`.\n *\n * This is the SDK's entire networking surface. It understands auth, timeouts\n * and the GraphQL error envelope, and nothing about deployments. The workflow\n * layer ({@link import(\"./unfazed.js\").Unfazed}) is built strictly on top of\n * it, so you can always drop down here for any operation the workflow layer\n * does not wrap — the schema has ~660 fields and the workflow layer\n * deliberately covers only the agent-facing ones.\n *\n * ```ts\n * const client = new UnfazedClient(); // UNFZ_API_TOKEN, else `npx unfazed login`\n * const data = await client.query<{ whoami: { name: string } }, never>(\n * `query { whoami { name } }`,\n * );\n * ```\n */\nexport class UnfazedClient {\n /** Resolved GraphQL endpoint URL. */\n readonly endpoint: string;\n\n /** A fixed bearer token, or the stored login handing out refreshed ones. */\n readonly #auth: { token: string } | { login: StoredLogin };\n readonly #headers: Record<string, string>;\n readonly #timeoutMs: number;\n readonly #fetch: FetchLike;\n\n constructor(options: ClientOptions = {}) {\n const env = options.env ?? processEnv();\n\n const injected = options.fetch;\n if (injected) {\n this.#fetch = injected;\n } else {\n const globalFetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof globalFetch !== \"function\") {\n throw new UnfazedConfigError(\n \"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch } to the client.\",\n );\n }\n this.#fetch = globalFetch.bind(globalThis) as FetchLike;\n }\n\n const ua = options.userAgent ?? `unfazed-sdk/${VERSION}`;\n\n const configured = configuredEndpoint(options, env);\n const token = options.token ?? env[TOKEN_ENV_VAR];\n if (token?.trim()) {\n this.#auth = { token: token.trim() };\n this.endpoint = configured ?? DEFAULT_ENDPOINT;\n } else {\n const stored =\n options.login === false\n ? {}\n : openStoredLogin({\n env,\n ...(configured ? { endpoint: configured } : {}),\n fetch: this.#fetch,\n // The edge refuses some generic agents; always name ourselves.\n userAgent: ua || `unfazed-sdk/${VERSION}`,\n });\n if (!stored.login) {\n throw new UnfazedAuthError(noTokenMessage(configured, stored.other?.endpoint));\n }\n this.#auth = { login: stored.login };\n this.endpoint = stored.login.endpoint;\n }\n\n this.#headers = { ...(options.headers ?? {}) };\n this.#timeoutMs = options.timeoutMs ?? 60_000;\n if (ua) this.#headers[\"User-Agent\"] = ua;\n }\n\n /**\n * Where the bearer token comes from: `\"token\"` (passed or\n * `UNFZ_API_TOKEN`) or `\"login\"` (the stored login of `npx unfazed login`).\n */\n get authSource(): \"token\" | \"login\" {\n return \"login\" in this.#auth ? \"login\" : \"token\";\n }\n\n /** Runs a query document. Identical wire behaviour to {@link mutate}. */\n query<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * Runs a mutation document. Distinct from {@link query} for call-site\n * readability only — GraphQL over HTTP POST makes no distinction.\n */\n mutate<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * The single code path behind {@link query} and {@link mutate}.\n *\n * Throws {@link UnfazedHTTPError} on non-2xx, {@link UnfazedGraphQLError}\n * when the body carries `errors`, {@link UnfazedTimeoutError} on timeout or\n * abort. Resolves to the `data` object on success.\n */\n async request<TResult = unknown>(\n document: string | { toString(): string },\n variables?: unknown,\n options: RequestOptions = {},\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n\n // `document` may be a TypedDocumentString (a String object). JSON.stringify\n // would serialise that fine, but String(...) keeps the wire body a plain\n // primitive regardless of how the caller obtained the document.\n const body = JSON.stringify({\n query: String(document),\n ...(variables === undefined ? {} : { variables }),\n ...(options.operationName ? { operationName: options.operationName } : {}),\n });\n\n let token = await this.#token();\n let { response, text } = await this.#send(body, token, timeoutMs, options.signal);\n // A stored login's access token can be refused before its recorded\n // expiry (the clock, a sign-out elsewhere): refresh once and retry. A\n // fixed token has nothing to refresh.\n if (response.status === 401 && \"login\" in this.#auth) {\n token = await this.#auth.login.refresh(token);\n ({ response, text } = await this.#send(body, token, timeoutMs, options.signal));\n }\n\n if (!response.ok) {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n let parsed: { data?: TResult; errors?: GraphQLResponseError[] };\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n // A GraphQL 200 can still be a failure, and can be a PARTIAL failure: the\n // batched writeFiles document resolves each aliased field independently.\n // Attaching `data` to the error is what lets a caller see which writes\n // landed before the failing one.\n if (parsed.errors?.length) {\n throw new UnfazedGraphQLError(parsed.errors, parsed.data);\n }\n if (parsed.data === undefined || parsed.data === null) {\n throw new UnfazedGraphQLError(\n [{ message: \"response contained neither data nor errors\" }],\n undefined,\n );\n }\n return parsed.data;\n }\n\n #token(): Promise<string> {\n return \"login\" in this.#auth ? this.#auth.login.token() : Promise.resolve(this.#auth.token);\n }\n\n async #send(\n body: string,\n token: string,\n timeoutMs: number,\n external: AbortSignal | undefined,\n ): Promise<{ response: Awaited<ReturnType<FetchLike>>; text: string }> {\n const { signal, cleanup, didTimeout } = makeSignal(timeoutMs, external);\n // The deadline covers BOTH phases — headers AND body. A proxy that answers\n // with headers and then stalls the body is a real failure mode, and\n // stopping the clock at the end of `fetch()` would leave `response.text()`\n // hanging forever. `untilAborted` additionally races the signal itself, so\n // an injected transport that ignores `signal` still cannot outlive the\n // timeout.\n try {\n const response = await untilAborted(\n this.#fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n ...this.#headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body,\n ...(signal ? { signal } : {}),\n }),\n signal,\n );\n const text = await untilAborted(response.text(), signal);\n return { response, text };\n } catch (cause) {\n if (didTimeout()) {\n throw new UnfazedTimeoutError(`unfazed: request timed out after ${timeoutMs}ms`, { cause });\n }\n if (isAbort(cause)) {\n throw new UnfazedTimeoutError(\"unfazed: request aborted\", { cause });\n }\n throw cause;\n } finally {\n cleanup();\n }\n }\n}\n\n/**\n * The platform origin an endpoint is served from:\n * `https://unfazed.dev/query` → `https://unfazed.dev`. A path before `/query`\n * (a control plane mounted under a prefix) is kept.\n */\nexport function platformBaseUrl(endpoint: string): string {\n return trimSlash(endpoint).replace(/\\/(?:query|graphql)$/, \"\");\n}\n\n/** The endpoint the options or `UNFZ_API_URL` name, or undefined for the default. */\nexport function configuredEndpoint(\n options: Pick<ClientOptions, \"endpoint\" | \"baseUrl\">,\n env: Record<string, string | undefined>,\n): string | undefined {\n if (options.endpoint) return options.endpoint;\n if (options.baseUrl) return trimSlash(options.baseUrl) + GRAPHQL_PATH;\n const fromEnv = env[ENDPOINT_ENV_VAR];\n // UNFZ_API_URL is documented as the full /query URL and is used verbatim.\n // Tolerate a bare origin anyway, since that is the obvious mistake and\n // guessing right costs nothing.\n if (fromEnv) {\n const trimmed = trimSlash(fromEnv);\n return /\\/query$|\\/graphql$/.test(trimmed) ? trimmed : trimmed + GRAPHQL_PATH;\n }\n return undefined;\n}\n\nfunction noTokenMessage(endpoint: string | undefined, otherLogin: string | undefined): string {\n if (endpoint && otherLogin) {\n return (\n `unfazed: no API token for ${endpoint}: the stored login is for ${otherLogin}. ` +\n `Run \\`${ENDPOINT_ENV_VAR}=${endpoint} ${LOGIN_COMMAND}\\`, or pass { token } / set ${TOKEN_ENV_VAR}.`\n );\n }\n return (\n `unfazed: not signed in. Run \\`${LOGIN_COMMAND}\\` to sign in with your account, ` +\n `or pass { token } / set ${TOKEN_ENV_VAR} (an agent token: ${DEFAULT_BASE_URL}/auth.md).`\n );\n}\n\nexport function processEnv(): Record<string, string | undefined> {\n // Guarded: the SDK must also work in a browser or worker, where there is no\n // `process` at all.\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};\n}\n\nfunction trimSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction isAbort(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { name?: string }).name === \"AbortError\";\n}\n\nfunction abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/**\n * Settles with `promise`, or rejects the moment `signal` aborts.\n *\n * A global `fetch` honours the signal for headers and body alike, but an\n * injected {@link FetchLike} — a proxy, an instrumented transport, a test\n * double — need not. Racing the signal makes the timeout an upper bound on the\n * whole request regardless of what the transport does with it. The loser's\n * rejection is swallowed by the already-settled promise rather than surfacing\n * as an unhandled rejection.\n */\nexport function untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) return Promise.reject(abortError());\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const done = () => signal.removeEventListener(\"abort\", onAbort);\n promise.then(\n (value) => {\n done();\n resolve(value);\n },\n (error: unknown) => {\n done();\n reject(error as Error);\n },\n );\n });\n}\n\n/**\n * Combines the request timeout with any caller-supplied signal.\n *\n * `didTimeout` is tracked separately from the abort so a caller's own\n * cancellation is not mislabelled as a timeout in the thrown message.\n */\nfunction makeSignal(\n timeoutMs: number,\n external?: AbortSignal,\n): { signal?: AbortSignal; cleanup: () => void; didTimeout: () => boolean } {\n if (!timeoutMs && !external) return { cleanup: () => {}, didTimeout: () => false };\n if (typeof AbortController !== \"function\") {\n return {\n ...(external ? { signal: external } : {}),\n cleanup: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n }\n\n const onExternalAbort = () => controller.abort();\n if (external) {\n if (external.aborted) controller.abort();\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", onExternalAbort);\n },\n didTimeout: () => timedOut,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { LOGIN_COMMAND, openStoredLogin, type StoredLogin } from \"./credentials.js\";\nimport {\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedTimeoutError,\n type GraphQLResponseError,\n} from \"./errors.js\";\n\n/** SDK version, sent as the User-Agent. Keep in sync with package.json. */\nexport const VERSION = \"0.10.1\";\n\n/**\n * Default GraphQL endpoint.\n *\n * Note this is the FULL endpoint, not an origin: the platform's own documented\n * convention (`UNFZ_API_URL=\"https://unfazed.dev/query\"`, and cmd/showcase-deploy\n * in the unfazed repo) treats the /query URL as the configurable unit, so the\n * SDK matches it rather than inventing a base-URL convention.\n */\nexport const DEFAULT_ENDPOINT = \"https://unfazed.dev/query\";\n\n/** Platform origin, used to derive the endpoint when only `baseUrl` is given. */\nexport const DEFAULT_BASE_URL = \"https://unfazed.dev\";\n\n/** Path of the GraphQL endpoint under an origin. */\nexport const GRAPHQL_PATH = \"/query\";\n\n/**\n * Environment variable holding the bearer token.\n *\n * This is the platform's documented name (docs/agent-deployments.md,\n * internal/agentdocs/reference.md). It accepts either an operator API token or\n * an agent access token from POST /agents/register — /query takes both as\n * plain bearers.\n */\nexport const TOKEN_ENV_VAR = \"UNFZ_API_TOKEN\";\n\n/** Environment variable holding the full GraphQL endpoint URL. */\nexport const ENDPOINT_ENV_VAR = \"UNFZ_API_URL\";\n\n/** Minimal structural type of the `fetch` the transport needs. */\nexport type FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n text(): Promise<string>;\n}>;\n\nexport interface ClientOptions {\n /**\n * Bearer token. Falls back to `UNFZ_API_TOKEN`, then to the login\n * `npx unfazed login` stored (see {@link ClientOptions.login}). An agent gets\n * one from https://unfazed.dev/auth.md — the same token the MCP server uses.\n */\n token?: string;\n /**\n * Use the stored login of `npx unfazed login`\n * (`${XDG_CONFIG_HOME:-~/.config}/unfazed/credentials.json`) when no token\n * is passed and `UNFZ_API_TOKEN` is unset. Its access token is refreshed\n * before it expires and when the platform refuses it. Default `true`; it is\n * only used for the endpoint it was issued by, and is the endpoint when none\n * is configured.\n */\n login?: boolean;\n /**\n * Where `UNFZ_API_TOKEN`, `UNFZ_API_URL` and the stored login's location\n * (`XDG_CONFIG_HOME`, `HOME`) are read from. Default: `process.env`.\n */\n env?: Record<string, string | undefined>;\n /**\n * Full GraphQL endpoint URL. Wins over `baseUrl`. Falls back to\n * `UNFZ_API_URL`, then the stored login's endpoint, then\n * {@link DEFAULT_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Platform origin; `/query` is appended. Convenience for self-hosted or\n * staging control planes where only the host differs.\n */\n baseUrl?: string;\n /** Extra headers merged into every request. Authorization always wins. */\n headers?: Record<string, string>;\n /**\n * Per-request timeout in ms. Default 60_000. Pass 0 to disable.\n *\n * Bounds the WHOLE request — connect, headers and reading the response body —\n * so a proxy that answers with headers and then stalls still fails on time.\n */\n timeoutMs?: number;\n /** Injection point for tests and instrumented/proxied transports. */\n fetch?: FetchLike;\n /** Overrides the default `unfazed-sdk/<version>` User-Agent. */\n userAgent?: string;\n}\n\n/**\n * A GraphQL document.\n *\n * Structural rather than `string & {...}` on purpose: graphql-codegen emits\n * `TypedDocumentString`, a String SUBCLASS, which is not assignable to the\n * primitive `string`. Requiring only `toString()` plus the optional phantom\n * `__apiType` lets three things through with full inference:\n *\n * - generated `TypedDocumentString` documents (types inferred),\n * - documents assembled at runtime, like the batched writeFiles mutation,\n * - plain template-literal strings a caller wrote by hand.\n */\nexport interface GraphQLDocument<TResult, TVariables> {\n /** Phantom type carrier. Never called; present only to bind the generics. */\n __apiType?: (variables: TVariables) => TResult;\n toString(): string;\n}\n\n/** Result type carried by a typed document. */\nexport type ResultOf<T> = T extends GraphQLDocument<infer R, infer _V> ? R : unknown;\n\n/** Variables type carried by a typed document. */\nexport type VariablesOf<T> = T extends GraphQLDocument<infer _R, infer V> ? V : never;\n\n/** Per-request knobs. */\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n operationName?: string;\n}\n\n/**\n * Layer 1 — a typed, dependency-free GraphQL transport over `fetch`.\n *\n * This is the SDK's entire networking surface. It understands auth, timeouts\n * and the GraphQL error envelope, and nothing about deployments. The workflow\n * layer ({@link import(\"./unfazed.js\").Unfazed}) is built strictly on top of\n * it, so you can always drop down here for any operation the workflow layer\n * does not wrap — the schema has ~660 fields and the workflow layer\n * deliberately covers only the agent-facing ones.\n *\n * ```ts\n * const client = new UnfazedClient(); // UNFZ_API_TOKEN, else `npx unfazed login`\n * const data = await client.query<{ whoami: { name: string } }, never>(\n * `query { whoami { name } }`,\n * );\n * ```\n */\nexport class UnfazedClient {\n /** Resolved GraphQL endpoint URL. */\n readonly endpoint: string;\n\n /** A fixed bearer token, or the stored login handing out refreshed ones. */\n readonly #auth: { token: string } | { login: StoredLogin };\n readonly #headers: Record<string, string>;\n readonly #timeoutMs: number;\n readonly #fetch: FetchLike;\n\n constructor(options: ClientOptions = {}) {\n const env = options.env ?? processEnv();\n\n const injected = options.fetch;\n if (injected) {\n this.#fetch = injected;\n } else {\n const globalFetch = (globalThis as { fetch?: unknown }).fetch;\n if (typeof globalFetch !== \"function\") {\n throw new UnfazedConfigError(\n \"unfazed: no global fetch. Use Bun, Node >= 20, or pass { fetch } to the client.\",\n );\n }\n this.#fetch = globalFetch.bind(globalThis) as FetchLike;\n }\n\n const ua = options.userAgent ?? `unfazed-sdk/${VERSION}`;\n\n const configured = configuredEndpoint(options, env);\n const token = options.token ?? env[TOKEN_ENV_VAR];\n if (token?.trim()) {\n this.#auth = { token: token.trim() };\n this.endpoint = configured ?? DEFAULT_ENDPOINT;\n } else {\n const stored =\n options.login === false\n ? {}\n : openStoredLogin({\n env,\n ...(configured ? { endpoint: configured } : {}),\n fetch: this.#fetch,\n // The edge refuses some generic agents; always name ourselves.\n userAgent: ua || `unfazed-sdk/${VERSION}`,\n });\n if (!stored.login) {\n throw new UnfazedAuthError(noTokenMessage(configured, stored.other?.endpoint));\n }\n this.#auth = { login: stored.login };\n this.endpoint = stored.login.endpoint;\n }\n\n this.#headers = { ...(options.headers ?? {}) };\n this.#timeoutMs = options.timeoutMs ?? 60_000;\n if (ua) this.#headers[\"User-Agent\"] = ua;\n }\n\n /**\n * Where the bearer token comes from: `\"token\"` (passed or\n * `UNFZ_API_TOKEN`) or `\"login\"` (the stored login of `npx unfazed login`).\n */\n get authSource(): \"token\" | \"login\" {\n return \"login\" in this.#auth ? \"login\" : \"token\";\n }\n\n /** Runs a query document. Identical wire behaviour to {@link mutate}. */\n query<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * Runs a mutation document. Distinct from {@link query} for call-site\n * readability only — GraphQL over HTTP POST makes no distinction.\n */\n mutate<TResult, TVariables>(\n document: GraphQLDocument<TResult, TVariables>,\n variables?: TVariables,\n options?: RequestOptions,\n ): Promise<TResult> {\n return this.request(document, variables, options);\n }\n\n /**\n * The single code path behind {@link query} and {@link mutate}.\n *\n * Throws {@link UnfazedHTTPError} on non-2xx, {@link UnfazedGraphQLError}\n * when the body carries `errors`, {@link UnfazedTimeoutError} on timeout or\n * abort. Resolves to the `data` object on success.\n */\n async request<TResult = unknown>(\n document: string | { toString(): string },\n variables?: unknown,\n options: RequestOptions = {},\n ): Promise<TResult> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n\n // `document` may be a TypedDocumentString (a String object). JSON.stringify\n // would serialise that fine, but String(...) keeps the wire body a plain\n // primitive regardless of how the caller obtained the document.\n const body = JSON.stringify({\n query: String(document),\n ...(variables === undefined ? {} : { variables }),\n ...(options.operationName ? { operationName: options.operationName } : {}),\n });\n\n let token = await this.#token();\n let { response, text } = await this.#send(body, token, timeoutMs, options.signal);\n // A stored login's access token can be refused before its recorded\n // expiry (the clock, a sign-out elsewhere): refresh once and retry. A\n // fixed token has nothing to refresh.\n if (response.status === 401 && \"login\" in this.#auth) {\n token = await this.#auth.login.refresh(token);\n ({ response, text } = await this.#send(body, token, timeoutMs, options.signal));\n }\n\n if (!response.ok) {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n let parsed: { data?: TResult; errors?: GraphQLResponseError[] };\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n throw new UnfazedHTTPError(response.status, text, this.endpoint);\n }\n\n // A GraphQL 200 can still be a failure, and can be a PARTIAL failure: the\n // batched writeFiles document resolves each aliased field independently.\n // Attaching `data` to the error is what lets a caller see which writes\n // landed before the failing one.\n if (parsed.errors?.length) {\n throw new UnfazedGraphQLError(parsed.errors, parsed.data);\n }\n if (parsed.data === undefined || parsed.data === null) {\n throw new UnfazedGraphQLError(\n [{ message: \"response contained neither data nor errors\" }],\n undefined,\n );\n }\n return parsed.data;\n }\n\n #token(): Promise<string> {\n return \"login\" in this.#auth ? this.#auth.login.token() : Promise.resolve(this.#auth.token);\n }\n\n async #send(\n body: string,\n token: string,\n timeoutMs: number,\n external: AbortSignal | undefined,\n ): Promise<{ response: Awaited<ReturnType<FetchLike>>; text: string }> {\n const { signal, cleanup, didTimeout } = makeSignal(timeoutMs, external);\n // The deadline covers BOTH phases — headers AND body. A proxy that answers\n // with headers and then stalls the body is a real failure mode, and\n // stopping the clock at the end of `fetch()` would leave `response.text()`\n // hanging forever. `untilAborted` additionally races the signal itself, so\n // an injected transport that ignores `signal` still cannot outlive the\n // timeout.\n try {\n const response = await untilAborted(\n this.#fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n ...this.#headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body,\n ...(signal ? { signal } : {}),\n }),\n signal,\n );\n const text = await untilAborted(response.text(), signal);\n return { response, text };\n } catch (cause) {\n if (didTimeout()) {\n throw new UnfazedTimeoutError(`unfazed: request timed out after ${timeoutMs}ms`, { cause });\n }\n if (isAbort(cause)) {\n throw new UnfazedTimeoutError(\"unfazed: request aborted\", { cause });\n }\n throw cause;\n } finally {\n cleanup();\n }\n }\n}\n\n/**\n * The platform origin an endpoint is served from:\n * `https://unfazed.dev/query` → `https://unfazed.dev`. A path before `/query`\n * (a control plane mounted under a prefix) is kept.\n */\nexport function platformBaseUrl(endpoint: string): string {\n return trimSlash(endpoint).replace(/\\/(?:query|graphql)$/, \"\");\n}\n\n/** The endpoint the options or `UNFZ_API_URL` name, or undefined for the default. */\nexport function configuredEndpoint(\n options: Pick<ClientOptions, \"endpoint\" | \"baseUrl\">,\n env: Record<string, string | undefined>,\n): string | undefined {\n if (options.endpoint) return options.endpoint;\n if (options.baseUrl) return trimSlash(options.baseUrl) + GRAPHQL_PATH;\n const fromEnv = env[ENDPOINT_ENV_VAR];\n // UNFZ_API_URL is documented as the full /query URL and is used verbatim.\n // Tolerate a bare origin anyway, since that is the obvious mistake and\n // guessing right costs nothing.\n if (fromEnv) {\n const trimmed = trimSlash(fromEnv);\n return /\\/query$|\\/graphql$/.test(trimmed) ? trimmed : trimmed + GRAPHQL_PATH;\n }\n return undefined;\n}\n\nfunction noTokenMessage(endpoint: string | undefined, otherLogin: string | undefined): string {\n if (endpoint && otherLogin) {\n return (\n `unfazed: no API token for ${endpoint}: the stored login is for ${otherLogin}. ` +\n `Run \\`${ENDPOINT_ENV_VAR}=${endpoint} ${LOGIN_COMMAND}\\`, or pass { token } / set ${TOKEN_ENV_VAR}.`\n );\n }\n return (\n `unfazed: not signed in. Run \\`${LOGIN_COMMAND}\\` to sign in with your account, ` +\n `or pass { token } / set ${TOKEN_ENV_VAR} (an agent token: ${DEFAULT_BASE_URL}/auth.md).`\n );\n}\n\nexport function processEnv(): Record<string, string | undefined> {\n // Guarded: the SDK must also work in a browser or worker, where there is no\n // `process` at all.\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};\n}\n\nfunction trimSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction isAbort(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as { name?: string }).name === \"AbortError\";\n}\n\nfunction abortError(): Error {\n const e = new Error(\"The operation was aborted.\");\n e.name = \"AbortError\";\n return e;\n}\n\n/**\n * Settles with `promise`, or rejects the moment `signal` aborts.\n *\n * A global `fetch` honours the signal for headers and body alike, but an\n * injected {@link FetchLike} — a proxy, an instrumented transport, a test\n * double — need not. Racing the signal makes the timeout an upper bound on the\n * whole request regardless of what the transport does with it. The loser's\n * rejection is swallowed by the already-settled promise rather than surfacing\n * as an unhandled rejection.\n */\nexport function untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) return Promise.reject(abortError());\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortError());\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const done = () => signal.removeEventListener(\"abort\", onAbort);\n promise.then(\n (value) => {\n done();\n resolve(value);\n },\n (error: unknown) => {\n done();\n reject(error as Error);\n },\n );\n });\n}\n\n/**\n * Combines the request timeout with any caller-supplied signal.\n *\n * `didTimeout` is tracked separately from the abort so a caller's own\n * cancellation is not mislabelled as a timeout in the thrown message.\n */\nfunction makeSignal(\n timeoutMs: number,\n external?: AbortSignal,\n): { signal?: AbortSignal; cleanup: () => void; didTimeout: () => boolean } {\n if (!timeoutMs && !external) return { cleanup: () => {}, didTimeout: () => false };\n if (typeof AbortController !== \"function\") {\n return {\n ...(external ? { signal: external } : {}),\n cleanup: () => {},\n didTimeout: () => false,\n };\n }\n\n const controller = new AbortController();\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n }\n\n const onExternalAbort = () => controller.abort();\n if (external) {\n if (external.aborted) controller.abort();\n else external.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n external?.removeEventListener(\"abort\", onExternalAbort);\n },\n didTimeout: () => timedOut,\n };\n}\n"]}
|
|
@@ -34,7 +34,13 @@ export interface DaemonConfig {
|
|
|
34
34
|
/** Extra environment for every command (e.g. PLAYWRIGHT_BROWSERS_PATH). */
|
|
35
35
|
readonly env: Readonly<Record<string, string>>;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The box's env snapshot (the work drive, rebuilt on every boot). The box
|
|
39
|
+
* init exports it to SSH sessions but not to boot hooks, so a daemon started
|
|
40
|
+
* at boot reads the box's URL from here.
|
|
41
|
+
*/
|
|
42
|
+
export declare const BOX_ENV_SNAPSHOT = "/work/.unfazed/env.json";
|
|
43
|
+
export declare function loadConfig(path?: string, env?: NodeJS.ProcessEnv, snapshot?: string): DaemonConfig;
|
|
38
44
|
export declare class TokenVerifier {
|
|
39
45
|
private readonly config;
|
|
40
46
|
private readonly fetchJson;
|
|
@@ -25,6 +25,12 @@ import { dirname } from "node:path";
|
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
import { API_PREFIX, DAEMON_VERSION, TEMPLATE_HOME, TEMPLATE_WORKSPACE, } from "./protocol.js";
|
|
27
27
|
const DEFAULT_CONFIG_PATH = "/data/eve-sandbox/config.json";
|
|
28
|
+
/**
|
|
29
|
+
* The box's env snapshot (the work drive, rebuilt on every boot). The box
|
|
30
|
+
* init exports it to SSH sessions but not to boot hooks, so a daemon started
|
|
31
|
+
* at boot reads the box's URL from here.
|
|
32
|
+
*/
|
|
33
|
+
export const BOX_ENV_SNAPSHOT = "/work/.unfazed/env.json";
|
|
28
34
|
const PATH_ENV = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
29
35
|
/** Output kept per process for a client that reads late or reconnects. */
|
|
30
36
|
const MAX_PROCESS_OUTPUT = 64 * 1024 * 1024;
|
|
@@ -34,12 +40,29 @@ const MAX_BODY = 128 * 1024 * 1024;
|
|
|
34
40
|
/** Root-owned file in a session's home whose mtime is the session's last call. */
|
|
35
41
|
const LAST_USED = ".eve-sandbox-last-used";
|
|
36
42
|
const SESSION_USER = /^eve[0-9a-f]{12}$/;
|
|
37
|
-
|
|
43
|
+
/** The box's own variables: the process's, else the box's env snapshot. */
|
|
44
|
+
function boxEnv(env, snapshot) {
|
|
45
|
+
if (env.UNFZ_PUBLIC_URL)
|
|
46
|
+
return env;
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(readFileSync(snapshot, "utf8"));
|
|
49
|
+
const out = { ...env };
|
|
50
|
+
for (const [key, value] of Object.entries(parsed))
|
|
51
|
+
if (typeof value === "string" && out[key] === undefined)
|
|
52
|
+
out[key] = value;
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return env;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function loadConfig(path = DEFAULT_CONFIG_PATH, env = process.env, snapshot = BOX_ENV_SNAPSHOT) {
|
|
38
60
|
const raw = existsSync(path) ? JSON.parse(readFileSync(path, "utf8")) : {};
|
|
39
|
-
const
|
|
61
|
+
const box = boxEnv(env, snapshot);
|
|
62
|
+
const audience = raw.audience ?? box.UNFZ_PUBLIC_URL ?? "";
|
|
40
63
|
if (!audience)
|
|
41
|
-
throw new Error(`eve sandbox: no audience (set "audience" in ${path} or UNFZ_PUBLIC_URL)`);
|
|
42
|
-
const envAllow = (
|
|
64
|
+
throw new Error(`eve sandbox: no audience (set "audience" in ${path}, or UNFZ_PUBLIC_URL; ${snapshot} has none either)`);
|
|
65
|
+
const envAllow = (box.EVE_SANDBOX_ALLOW ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
43
66
|
const allow = [...(raw.allow ?? []), ...envAllow];
|
|
44
67
|
if (allow.length === 0)
|
|
45
68
|
throw new Error(`eve sandbox: nothing is allowed (set "allow" in ${path} or EVE_SANDBOX_ALLOW)`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/eve-sandbox/daemon.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The eve sandbox daemon: runs as root inside an unfazed box and gives the\n * apps it trusts sandboxes, one Unix user per eve session.\n *\n * It serves the box's :3000, which unfazed publishes at the box's URL, and\n * accepts only workload identity tokens (OIDC, issuer\n * `https://unfazed.dev/oidc`) whose audience is that URL and whose app is on\n * its allow list. A session's commands and file operations run as that\n * session's user, so the kernel keeps sessions apart; the daemon itself only\n * creates and removes users and relays bytes.\n *\n * Started by a box boot hook (`/data/boot.d/50-eve-sandbox`, see setup.ts):\n *\n * node|bun <this file> serve [--config /data/eve-sandbox/config.json]\n *\n * and invoked as the session user for file operations:\n *\n * setpriv --reuid U --regid G --init-groups -- node|bun <this file> fileop\n */\n\nimport { spawn, spawnSync, type ChildProcess } from \"node:child_process\";\nimport { createHash, randomUUID, webcrypto } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, statSync, utimesSync, writeFileSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n API_PREFIX,\n DAEMON_VERSION,\n TEMPLATE_HOME,\n TEMPLATE_WORKSPACE,\n type EnsureSessionRequest,\n type ProcessEvent,\n type RemoveRequest,\n type SessionInfo,\n type SpawnRequest,\n type WireFile,\n} from \"./protocol.js\";\n\n// ---------------------------------------------------------------------------\n// Configuration\n\nexport interface DaemonConfig {\n /** The audience every token must carry: the box's URL. */\n readonly audience: string;\n /** App ids (`app_id` claim) allowed to use this box; `owner:<id>` allows every app of an owner. */\n readonly allow: readonly string[];\n readonly issuer: string;\n readonly port: number;\n /** Where session homes live. */\n readonly homeRoot: string;\n /** Kill a session's processes after this long without a call; 0 never. */\n readonly idleMinutes: number;\n /** Delete a session (user, home, files) after this many days without a call; 0 never. */\n readonly retentionDays: number;\n /** Extra environment for every command (e.g. PLAYWRIGHT_BROWSERS_PATH). */\n readonly env: Readonly<Record<string, string>>;\n}\n\nconst DEFAULT_CONFIG_PATH = \"/data/eve-sandbox/config.json\";\nconst PATH_ENV = \"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\";\n/** Output kept per process for a client that reads late or reconnects. */\nconst MAX_PROCESS_OUTPUT = 64 * 1024 * 1024;\n/** A finished process is forgotten this long after it exited. */\nconst PROCESS_RETENTION_MS = 10 * 60 * 1000;\nconst MAX_BODY = 128 * 1024 * 1024;\n/** Root-owned file in a session's home whose mtime is the session's last call. */\nconst LAST_USED = \".eve-sandbox-last-used\";\nconst SESSION_USER = /^eve[0-9a-f]{12}$/;\n\nexport function loadConfig(path = DEFAULT_CONFIG_PATH): DaemonConfig {\n const raw = existsSync(path) ? (JSON.parse(readFileSync(path, \"utf8\")) as Partial<DaemonConfig>) : {};\n const audience = raw.audience ?? process.env.UNFZ_PUBLIC_URL ?? \"\";\n if (!audience) throw new Error(`eve sandbox: no audience (set \"audience\" in ${path} or UNFZ_PUBLIC_URL)`);\n const envAllow = (process.env.EVE_SANDBOX_ALLOW ?? \"\").split(\",\").map((s) => s.trim()).filter(Boolean);\n const allow = [...(raw.allow ?? []), ...envAllow];\n if (allow.length === 0) throw new Error(`eve sandbox: nothing is allowed (set \"allow\" in ${path} or EVE_SANDBOX_ALLOW)`);\n return {\n audience: audience.replace(/\\/+$/, \"\"),\n allow,\n issuer: raw.issuer ?? \"https://unfazed.dev/oidc\",\n port: raw.port ?? 3000,\n homeRoot: raw.homeRoot ?? \"/home\",\n idleMinutes: raw.idleMinutes ?? 20,\n retentionDays: raw.retentionDays ?? 14,\n env: raw.env ?? {},\n };\n}\n\n// ---------------------------------------------------------------------------\n// Workload identity tokens (ES256 JWTs from the unfazed issuer)\n\ninterface Claims {\n readonly iss: string;\n readonly aud: string | readonly string[];\n readonly exp: number;\n readonly nbf?: number;\n readonly app_id?: string;\n readonly owner_id?: string;\n readonly alias?: string;\n}\n\nclass HttpError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message?: string,\n ) {\n super(message ?? code);\n }\n}\n\nfunction b64urlDecode(part: string): Uint8Array {\n return new Uint8Array(Buffer.from(part.replace(/-/g, \"+\").replace(/_/g, \"/\"), \"base64\"));\n}\n\nexport class TokenVerifier {\n private keys = new Map<string, CryptoKey>();\n private fetchedAt = 0;\n private refreshing: Promise<void> | undefined;\n\n constructor(\n private readonly config: Pick<DaemonConfig, \"audience\" | \"issuer\" | \"allow\">,\n private readonly fetchJson: (url: string) => Promise<unknown> = async (url) => {\n const res = await fetch(url, { signal: AbortSignal.timeout(10_000) });\n if (!res.ok) throw new Error(`${url}: ${res.status}`);\n return await res.json();\n },\n private readonly now: () => number = () => Date.now(),\n ) {}\n\n /** The calling app's namespace (its app id) for a valid, allowed token. */\n async verify(authorization: string | undefined): Promise<string> {\n const match = /^Bearer ([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+)$/.exec(authorization ?? \"\");\n if (!match) throw new HttpError(401, \"invalid_token\", \"a workload identity bearer token is required\");\n const [, h, p, s] = match as unknown as [string, string, string, string];\n let header: { alg?: string; kid?: string };\n let claims: Claims;\n try {\n header = JSON.parse(Buffer.from(b64urlDecode(h)).toString(\"utf8\"));\n claims = JSON.parse(Buffer.from(b64urlDecode(p)).toString(\"utf8\"));\n } catch {\n throw new HttpError(401, \"invalid_token\", \"malformed token\");\n }\n if (header.alg !== \"ES256\" || !header.kid) throw new HttpError(401, \"invalid_token\", \"unsupported token algorithm\");\n const key = await this.key(header.kid);\n const ok = await webcrypto.subtle.verify(\n { name: \"ECDSA\", hash: \"SHA-256\" },\n key,\n b64urlDecode(s),\n new TextEncoder().encode(`${h}.${p}`),\n );\n if (!ok) throw new HttpError(401, \"invalid_token\", \"bad signature\");\n const nowS = Math.floor(this.now() / 1000);\n const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];\n if (claims.iss !== this.config.issuer) throw new HttpError(401, \"invalid_token\", \"wrong issuer\");\n if (!audiences.some((a) => a.replace(/\\/+$/, \"\") === this.config.audience)) throw new HttpError(401, \"invalid_token\", \"wrong audience\");\n if (typeof claims.exp !== \"number\" || claims.exp < nowS - 30) throw new HttpError(401, \"invalid_token\", \"token expired\");\n if (typeof claims.nbf === \"number\" && claims.nbf > nowS + 30) throw new HttpError(401, \"invalid_token\", \"token not yet valid\");\n const app = claims.app_id ?? \"\";\n if (!app) throw new HttpError(403, \"forbidden\", \"the token names no app\");\n const allowed = this.config.allow.some((entry) =>\n entry.startsWith(\"owner:\") ? entry.slice(6) === claims.owner_id : entry === app,\n );\n if (!allowed) throw new HttpError(403, \"forbidden\", `app ${app} may not use this box`);\n return app;\n }\n\n private async key(kid: string): Promise<CryptoKey> {\n const cached = this.keys.get(kid);\n if (cached && this.now() - this.fetchedAt < 10 * 60 * 1000) return cached;\n // Unknown kid: refetch, but at most every 30 s.\n if (!cached || this.now() - this.fetchedAt >= 30_000) await this.refresh();\n const key = this.keys.get(kid);\n if (!key) throw new HttpError(401, \"invalid_token\", \"unknown signing key\");\n return key;\n }\n\n private async refresh(): Promise<void> {\n this.refreshing ??= (async () => {\n try {\n const discovery = (await this.fetchJson(`${this.config.issuer}/.well-known/openid-configuration`)) as { jwks_uri?: string };\n const jwks = (await this.fetchJson(discovery.jwks_uri ?? `${this.config.issuer}/jwks`)) as { keys?: JsonWebKey[] };\n const next = new Map<string, CryptoKey>();\n for (const jwk of jwks.keys ?? []) {\n const kid = (jwk as { kid?: string }).kid;\n if (!kid || jwk.kty !== \"EC\" || jwk.crv !== \"P-256\") continue;\n next.set(kid, await webcrypto.subtle.importKey(\"jwk\", jwk, { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\"verify\"]));\n }\n this.keys = next;\n this.fetchedAt = this.now();\n } finally {\n this.refreshing = undefined;\n }\n })();\n await this.refreshing;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sessions: one Unix user each\n\ninterface Session extends SessionInfo {\n readonly uid: number;\n readonly gid: number;\n}\n\n/** The Unix name of an app's session: bounded, derived from the ids only. */\nexport function sessionName(app: string, sessionId: string): string {\n return `eve${createHash(\"sha256\").update(`${app}\\n${sessionId}`).digest(\"hex\").slice(0, 12)}`;\n}\n\nfunction run(argv: readonly string[], input?: string | Uint8Array): { status: number; stdout: string; stderr: string } {\n const result = spawnSync(argv[0]!, argv.slice(1), { input, encoding: \"utf8\", env: { PATH: PATH_ENV } });\n return { status: result.status ?? 1, stdout: result.stdout ?? \"\", stderr: result.stderr ?? \"\" };\n}\n\nclass Sessions {\n private lastUsed = new Map<string, number>();\n\n constructor(private readonly config: DaemonConfig) {}\n\n private describe(name: string): Session | undefined {\n const entry = run([\"getent\", \"passwd\", name]);\n if (entry.status !== 0) return undefined;\n const [, , uid, gid, , home] = entry.stdout.trim().split(\":\");\n return { name, uid: Number(uid), gid: Number(gid), home: home!, workspace: `${home}/workspace` };\n }\n\n get(app: string, sessionId: string): Session | undefined {\n const session = this.describe(sessionName(app, sessionId));\n if (session) this.touch(session.name);\n return session;\n }\n\n ensure(app: string, sessionId: string, template: readonly WireFile[]): Session & { created: boolean } {\n const name = sessionName(app, sessionId);\n const existing = this.describe(name);\n if (existing) {\n this.touch(name);\n return { ...existing, created: false };\n }\n const home = `${this.config.homeRoot}/${name}`;\n const added = run([\"useradd\", \"--create-home\", \"--home-dir\", home, \"--shell\", \"/bin/bash\", \"--user-group\", \"--comment\", \"eve sandbox\", name]);\n const session = this.describe(name);\n if (!session) throw new HttpError(500, \"session_create_failed\", added.stderr.trim() || \"useradd failed\");\n run([\"chmod\", \"0700\", home]);\n const files = [\n { path: `${session.workspace}/.keep`, b64: \"\" },\n ...template.map((file) => ({ path: mapTemplatePath(file.path, session), b64: file.b64 })),\n ];\n fileop(session, { op: \"write\", files });\n this.touch(name);\n return { ...session, created: true };\n }\n\n stop(name: string): void {\n run([\"pkill\", \"-KILL\", \"-u\", name]);\n }\n\n delete(app: string, sessionId: string): void {\n const name = sessionName(app, sessionId);\n if (!this.describe(name)) return;\n this.stop(name);\n for (let attempt = 0; attempt < 5; attempt++) {\n if (run([\"userdel\", \"--remove\", name]).status === 0) break;\n this.stop(name);\n spawnSync(\"sleep\", [\"0.2\"]);\n }\n rmSync(`${this.config.homeRoot}/${name}`, { recursive: true, force: true });\n this.lastUsed.delete(name);\n }\n\n touch(name: string): void {\n const now = Date.now();\n const previous = this.lastUsed.get(name) ?? 0;\n this.lastUsed.set(name, now);\n if (now - previous < 60_000) return;\n const marker = `${this.config.homeRoot}/${name}/${LAST_USED}`;\n try {\n if (!existsSync(marker)) writeFileSync(marker, \"\");\n utimesSync(marker, now / 1000, now / 1000);\n } catch {\n // the home may be gone mid-delete\n }\n }\n\n /** Deletes sessions without a call for longer than the retention, users and homes included. */\n expireOld(): void {\n if (this.config.retentionDays <= 0) return;\n const limit = this.config.retentionDays * 24 * 60 * 60 * 1000;\n const passwd = run([\"getent\", \"passwd\"]).stdout.split(\"\\n\");\n for (const line of passwd) {\n const [name, , , , , home] = line.split(\":\");\n if (!name || !SESSION_USER.test(name) || home !== `${this.config.homeRoot}/${name}`) continue;\n let at: number;\n try {\n at = statSync(`${home}/${LAST_USED}`).mtimeMs;\n } catch {\n try {\n at = statSync(home).mtimeMs;\n } catch {\n at = 0;\n }\n }\n if (Date.now() - at > limit) {\n this.stop(name);\n run([\"userdel\", \"--remove\", name]);\n rmSync(home, { recursive: true, force: true });\n this.lastUsed.delete(name);\n }\n }\n }\n\n /** Kills the processes of sessions idle for longer than the limit. */\n reapIdle(): void {\n if (this.config.idleMinutes <= 0) return;\n const limit = this.config.idleMinutes * 60 * 1000;\n for (const [name, at] of this.lastUsed) {\n if (Date.now() - at > limit) {\n this.stop(name);\n this.lastUsed.delete(name);\n }\n }\n }\n}\n\nfunction mapTemplatePath(path: string, session: SessionInfo): string {\n if (path === TEMPLATE_WORKSPACE || path.startsWith(`${TEMPLATE_WORKSPACE}/`)) return session.workspace + path.slice(TEMPLATE_WORKSPACE.length);\n if (path === TEMPLATE_HOME || path.startsWith(`${TEMPLATE_HOME}/`)) return session.home + path.slice(TEMPLATE_HOME.length);\n return path;\n}\n\nfunction sessionEnv(session: Session, extra: Readonly<Record<string, string>>): Record<string, string> {\n return {\n PATH: PATH_ENV,\n HOME: session.home,\n USER: session.name,\n LOGNAME: session.name,\n SHELL: \"/bin/bash\",\n LANG: \"C.UTF-8\",\n ...extra,\n };\n}\n\nfunction asUser(session: Session, argv: readonly string[]): string[] {\n return [\"setpriv\", `--reuid=${session.uid}`, `--regid=${session.gid}`, \"--init-groups\", \"--\", ...argv];\n}\n\n// ---------------------------------------------------------------------------\n// File operations, run as the session user\n\ntype FileOp =\n | { op: \"read\"; path: string }\n | { op: \"write\"; files: readonly WireFile[] }\n | { op: \"remove\"; path: string; force: boolean; recursive: boolean };\n\nconst FILEOP_MISSING = 3;\nconst FILEOP_DIRECTORY = 4;\nconst FILEOP_FAILED = 5;\n\nfunction fileop(session: Session, request: FileOp): Buffer {\n // This module itself, whoever loaded it (its own entry or `unfazed eve-sandbox serve`).\n const argv = asUser(session, [process.execPath, fileURLToPath(import.meta.url), \"fileop\"]);\n const result = spawnSync(argv[0]!, argv.slice(1), {\n input: JSON.stringify(request),\n env: { PATH: PATH_ENV, HOME: session.home },\n maxBuffer: MAX_BODY,\n });\n const status = result.status ?? FILEOP_FAILED;\n if (status === 0) return result.stdout as Buffer;\n const message = (result.stderr?.toString() ?? \"\").trim().slice(0, 500);\n if (status === FILEOP_MISSING) throw new HttpError(404, \"not_found\", message || \"no such file\");\n if (status === FILEOP_DIRECTORY) throw new HttpError(409, \"is_directory\", message || \"the path is a directory\");\n throw new HttpError(422, \"file_operation_failed\", message || \"the file operation failed\");\n}\n\n/** The `fileop` subcommand: one request on stdin, run with the caller's own permissions. */\nexport function runFileOp(): never {\n try {\n const request = JSON.parse(readFileSync(0, \"utf8\")) as FileOp;\n if (request.op === \"read\") {\n let st;\n try {\n st = statSync(request.path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") process.exit(FILEOP_MISSING);\n throw error;\n }\n if (st.isDirectory()) process.exit(FILEOP_DIRECTORY);\n process.stdout.write(readFileSync(request.path), () => process.exit(0));\n return undefined as never;\n }\n if (request.op === \"write\") {\n for (const file of request.files) {\n if (!file.path.startsWith(\"/\")) throw new Error(`not an absolute path: ${file.path}`);\n mkdirSync(dirname(file.path), { recursive: true });\n writeFileSync(file.path, Buffer.from(file.b64, \"base64\"));\n }\n process.exit(0);\n }\n if (request.op === \"remove\") {\n if (!existsSync(request.path)) process.exit(request.force ? 0 : FILEOP_MISSING);\n rmSync(request.path, { recursive: request.recursive, force: request.force });\n process.exit(0);\n }\n throw new Error(\"unknown operation\");\n } catch (error) {\n process.stderr.write(String((error as Error).message ?? error));\n process.exit(FILEOP_FAILED);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Processes\n\ninterface Proc {\n readonly id: string;\n readonly app: string;\n readonly session: string;\n readonly child: ChildProcess;\n readonly events: ProcessEvent[];\n bytes: number;\n exit: number | undefined;\n exitedAt: number | undefined;\n readonly listeners: Set<() => void>;\n}\n\nconst SIGNALS: Record<string, number> = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGKILL: 9, SIGSEGV: 11, SIGPIPE: 13, SIGTERM: 15 };\n\nclass Processes {\n readonly all = new Map<string, Proc>();\n\n start(app: string, session: Session, request: SpawnRequest, baseEnv: Readonly<Record<string, string>>): Proc {\n if (!existsSync(request.cwd)) throw new HttpError(400, \"invalid_cwd\", `working directory does not exist: ${request.cwd}`);\n const argv = asUser(session, [\"bash\", \"-lc\", request.command]);\n const child = spawn(argv[0]!, argv.slice(1), {\n cwd: request.cwd,\n env: { ...sessionEnv(session, { AI_AGENT: \"eve\", ...baseEnv }), ...request.env },\n stdio: [request.stdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n // Its own process group, so a kill reaches the whole tree.\n detached: true,\n });\n const proc: Proc = {\n id: randomUUID(),\n app,\n session: session.name,\n child,\n events: [],\n bytes: 0,\n exit: undefined,\n exitedAt: undefined,\n listeners: new Set(),\n };\n const push = (event: ProcessEvent) => {\n proc.events.push(event);\n for (const listener of proc.listeners) listener();\n };\n const pipe = (fd: 1 | 2) => (chunk: Buffer) => {\n if (proc.bytes + chunk.length > MAX_PROCESS_OUTPUT) return;\n proc.bytes += chunk.length;\n push({ s: proc.events.length + 1, f: fd, d: chunk.toString(\"base64\") });\n };\n child.stdout?.on(\"data\", pipe(1));\n child.stderr?.on(\"data\", pipe(2));\n child.on(\"error\", (error) => {\n push({ s: proc.events.length + 1, f: 2, d: Buffer.from(`eve sandbox: ${error.message}\\n`).toString(\"base64\") });\n });\n child.on(\"close\", (code, signal) => {\n proc.exit = code ?? 128 + (signal ? (SIGNALS[signal] ?? 1) : 1);\n proc.exitedAt = Date.now();\n push({ s: proc.events.length + 1, x: proc.exit });\n });\n this.all.set(proc.id, proc);\n return proc;\n }\n\n kill(proc: Proc): void {\n if (proc.exit !== undefined || proc.child.pid === undefined) return;\n try {\n process.kill(-proc.child.pid, \"SIGKILL\");\n } catch {\n proc.child.kill(\"SIGKILL\");\n }\n }\n\n killSession(name: string): void {\n for (const proc of this.all.values()) if (proc.session === name) this.kill(proc);\n }\n\n prune(): void {\n for (const [id, proc] of this.all) {\n if (proc.exitedAt !== undefined && Date.now() - proc.exitedAt > PROCESS_RETENTION_MS) this.all.delete(id);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n\nasync function readBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of req) {\n size += (chunk as Buffer).length;\n if (size > MAX_BODY) throw new HttpError(413, \"too_large\");\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks);\n}\n\nasync function readJson<T>(req: IncomingMessage): Promise<T> {\n const body = await readBody(req);\n try {\n return JSON.parse(body.toString(\"utf8\") || \"{}\") as T;\n } catch {\n throw new HttpError(400, \"invalid_json\");\n }\n}\n\nfunction sendJson(res: ServerResponse, status: number, value: unknown): void {\n const body = JSON.stringify(value);\n res.writeHead(status, { \"content-type\": \"application/json\", \"content-length\": Buffer.byteLength(body), \"cache-control\": \"no-store\" });\n res.end(body);\n}\n\nexport function createDaemon(config: DaemonConfig, verifier = new TokenVerifier(config)) {\n const sessions = new Sessions(config);\n const processes = new Processes();\n\n const sessionOf = (app: string, id: string): Session => {\n const session = sessions.get(app, decodeURIComponent(id));\n if (!session) throw new HttpError(410, \"session_gone\", \"no such sandbox session\");\n return session;\n };\n const procOf = (app: string, id: string): Proc => {\n const proc = processes.all.get(id);\n if (!proc || proc.app !== app) throw new HttpError(404, \"no_such_process\");\n sessions.touch(proc.session);\n return proc;\n };\n\n async function route(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url ?? \"/\", \"http://box\");\n const path = url.pathname;\n const method = req.method ?? \"GET\";\n if (method === \"GET\" && path === `${API_PREFIX}/health`) return sendJson(res, 200, { ok: true, version: DAEMON_VERSION });\n if (!path.startsWith(`${API_PREFIX}/`)) throw new HttpError(404, \"not_found\");\n const app = await verifier.verify(req.headers.authorization);\n const parts = path.slice(API_PREFIX.length + 1).split(\"/\");\n\n if (parts[0] === \"sessions\" && parts[1]) {\n const sessionId = decodeURIComponent(parts[1]);\n const sub = parts[2];\n if (!sub && method === \"PUT\") {\n const body = await readJson<EnsureSessionRequest>(req);\n const session = sessions.ensure(app, sessionId, body.template ?? []);\n return sendJson(res, 200, { name: session.name, home: session.home, workspace: session.workspace, created: session.created });\n }\n if (!sub && method === \"GET\") {\n const session = sessionOf(app, parts[1]);\n return sendJson(res, 200, { name: session.name, home: session.home, workspace: session.workspace });\n }\n if (!sub && method === \"DELETE\") {\n processes.killSession(sessionName(app, sessionId));\n sessions.delete(app, sessionId);\n return sendJson(res, 200, { deleted: true });\n }\n const session = sessionOf(app, parts[1]);\n if (sub === \"stop\" && method === \"POST\") {\n processes.killSession(session.name);\n sessions.stop(session.name);\n return sendJson(res, 200, { stopped: true });\n }\n if (sub === \"processes\" && method === \"POST\") {\n const body = await readJson<SpawnRequest>(req);\n if (typeof body.command !== \"string\" || typeof body.cwd !== \"string\" || !body.cwd.startsWith(\"/\")) throw new HttpError(400, \"invalid_request\");\n const env: Record<string, string> = {};\n for (const [key, value] of Object.entries(body.env ?? {})) if (typeof value === \"string\") env[key] = value;\n const proc = processes.start(app, session, { command: body.command, cwd: body.cwd, env, stdin: body.stdin === true }, config.env);\n return sendJson(res, 200, { id: proc.id, pid: proc.child.pid });\n }\n if (sub === \"file\" && method === \"GET\") {\n const target = url.searchParams.get(\"path\") ?? \"\";\n if (!target.startsWith(\"/\")) throw new HttpError(400, \"invalid_path\");\n const bytes = fileop(session, { op: \"read\", path: target });\n res.writeHead(200, { \"content-type\": \"application/octet-stream\", \"content-length\": bytes.length, \"cache-control\": \"no-store\" });\n res.end(bytes);\n return;\n }\n if (sub === \"files\" && method === \"POST\") {\n const body = await readJson<{ files?: WireFile[] }>(req);\n const files = (body.files ?? []).filter((f) => typeof f.path === \"string\" && typeof f.b64 === \"string\");\n if (files.some((f) => !f.path.startsWith(\"/\"))) throw new HttpError(400, \"invalid_path\");\n if (files.length > 0) fileop(session, { op: \"write\", files });\n return sendJson(res, 200, { written: files.length });\n }\n if (sub === \"remove\" && method === \"POST\") {\n const body = await readJson<RemoveRequest>(req);\n if (typeof body.path !== \"string\" || !body.path.startsWith(\"/\")) throw new HttpError(400, \"invalid_path\");\n fileop(session, { op: \"remove\", path: body.path, force: body.force === true, recursive: body.recursive === true });\n return sendJson(res, 200, { removed: true });\n }\n }\n\n if (parts[0] === \"processes\" && parts[1]) {\n const proc = procOf(app, parts[1]);\n const sub = parts[2];\n if (sub === \"events\" && method === \"GET\") return streamEvents(req, res, proc, Number(url.searchParams.get(\"after\") ?? \"0\") || 0);\n if (sub === \"stdin\" && method === \"POST\") {\n const body = await readBody(req);\n const stdin = proc.child.stdin;\n if (!stdin) throw new HttpError(409, \"no_stdin\");\n if (body.length > 0) await new Promise<void>((resolve, reject) => stdin.write(body, (e) => (e ? reject(e) : resolve())));\n if (url.searchParams.get(\"eof\") === \"1\") stdin.end();\n return sendJson(res, 200, { written: body.length });\n }\n if (sub === \"kill\" && method === \"POST\") {\n processes.kill(proc);\n return sendJson(res, 200, { killed: true });\n }\n }\n throw new HttpError(404, \"not_found\");\n }\n\n function streamEvents(req: IncomingMessage, res: ServerResponse, proc: Proc, after: number): void {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store, no-transform\",\n \"x-accel-buffering\": \"no\",\n connection: \"keep-alive\",\n });\n let sent = after;\n const flush = () => {\n while (sent < proc.events.length) {\n const event = proc.events[sent]!;\n sent = event.s;\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n if (\"x\" in event) {\n finish();\n return;\n }\n }\n };\n const heartbeat = setInterval(() => res.write(\": keep-alive\\n\\n\"), 15_000);\n const finish = () => {\n clearInterval(heartbeat);\n proc.listeners.delete(flush);\n res.end();\n };\n req.on(\"close\", () => {\n clearInterval(heartbeat);\n proc.listeners.delete(flush);\n });\n proc.listeners.add(flush);\n flush();\n }\n\n const server = createServer((req, res) => {\n route(req, res).catch((error: unknown) => {\n if (res.headersSent) {\n res.end();\n return;\n }\n if (error instanceof HttpError) return sendJson(res, error.status, { error: error.code, message: error.message });\n sendJson(res, 500, { error: \"internal\", message: String((error as Error)?.message ?? error).slice(0, 300) });\n });\n });\n server.requestTimeout = 0;\n server.headersTimeout = 60_000;\n const timers = [\n setInterval(() => processes.prune(), 60_000),\n setInterval(() => sessions.reapIdle(), 60_000),\n setInterval(() => sessions.expireOld(), 60 * 60 * 1000),\n ];\n return {\n server,\n listen(port = config.port) {\n return new Promise<void>((resolve) => server.listen(port, \"0.0.0.0\", () => resolve()));\n },\n close() {\n for (const timer of timers) clearInterval(timer);\n server.close();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n\nfunction argValue(flag: string): string | undefined {\n const index = process.argv.indexOf(flag);\n return index >= 0 ? process.argv[index + 1] : undefined;\n}\n\nconst isMain = (() => {\n try {\n return import.meta.url === `file://${process.argv[1]}` || (process.argv[1] ?? \"\").endsWith(\"eve-sandbox-daemon.js\");\n } catch {\n return false;\n }\n})();\n\nif (isMain) {\n const command = process.argv[2];\n if (command === \"fileop\") runFileOp();\n if (command === \"serve\") {\n const config = loadConfig(argValue(\"--config\"));\n const daemon = createDaemon(config);\n await daemon.listen();\n process.stdout.write(`eve sandbox daemon ${DAEMON_VERSION} on :${config.port} for ${config.audience}, ${config.allow.length} allowed\\n`);\n } else if (command !== \"fileop\") {\n process.stderr.write(\"usage: eve-sandbox-daemon serve [--config <path>] | fileop\\n\");\n process.exit(2);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/eve-sandbox/daemon.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The eve sandbox daemon: runs as root inside an unfazed box and gives the\n * apps it trusts sandboxes, one Unix user per eve session.\n *\n * It serves the box's :3000, which unfazed publishes at the box's URL, and\n * accepts only workload identity tokens (OIDC, issuer\n * `https://unfazed.dev/oidc`) whose audience is that URL and whose app is on\n * its allow list. A session's commands and file operations run as that\n * session's user, so the kernel keeps sessions apart; the daemon itself only\n * creates and removes users and relays bytes.\n *\n * Started by a box boot hook (`/data/boot.d/50-eve-sandbox`, see setup.ts):\n *\n * node|bun <this file> serve [--config /data/eve-sandbox/config.json]\n *\n * and invoked as the session user for file operations:\n *\n * setpriv --reuid U --regid G --init-groups -- node|bun <this file> fileop\n */\n\nimport { spawn, spawnSync, type ChildProcess } from \"node:child_process\";\nimport { createHash, randomUUID, webcrypto } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, statSync, utimesSync, writeFileSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n API_PREFIX,\n DAEMON_VERSION,\n TEMPLATE_HOME,\n TEMPLATE_WORKSPACE,\n type EnsureSessionRequest,\n type ProcessEvent,\n type RemoveRequest,\n type SessionInfo,\n type SpawnRequest,\n type WireFile,\n} from \"./protocol.js\";\n\n// ---------------------------------------------------------------------------\n// Configuration\n\nexport interface DaemonConfig {\n /** The audience every token must carry: the box's URL. */\n readonly audience: string;\n /** App ids (`app_id` claim) allowed to use this box; `owner:<id>` allows every app of an owner. */\n readonly allow: readonly string[];\n readonly issuer: string;\n readonly port: number;\n /** Where session homes live. */\n readonly homeRoot: string;\n /** Kill a session's processes after this long without a call; 0 never. */\n readonly idleMinutes: number;\n /** Delete a session (user, home, files) after this many days without a call; 0 never. */\n readonly retentionDays: number;\n /** Extra environment for every command (e.g. PLAYWRIGHT_BROWSERS_PATH). */\n readonly env: Readonly<Record<string, string>>;\n}\n\nconst DEFAULT_CONFIG_PATH = \"/data/eve-sandbox/config.json\";\n/**\n * The box's env snapshot (the work drive, rebuilt on every boot). The box\n * init exports it to SSH sessions but not to boot hooks, so a daemon started\n * at boot reads the box's URL from here.\n */\nexport const BOX_ENV_SNAPSHOT = \"/work/.unfazed/env.json\";\nconst PATH_ENV = \"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\";\n/** Output kept per process for a client that reads late or reconnects. */\nconst MAX_PROCESS_OUTPUT = 64 * 1024 * 1024;\n/** A finished process is forgotten this long after it exited. */\nconst PROCESS_RETENTION_MS = 10 * 60 * 1000;\nconst MAX_BODY = 128 * 1024 * 1024;\n/** Root-owned file in a session's home whose mtime is the session's last call. */\nconst LAST_USED = \".eve-sandbox-last-used\";\nconst SESSION_USER = /^eve[0-9a-f]{12}$/;\n\n/** The box's own variables: the process's, else the box's env snapshot. */\nfunction boxEnv(env: NodeJS.ProcessEnv, snapshot: string): Record<string, string | undefined> {\n if (env.UNFZ_PUBLIC_URL) return env;\n try {\n const parsed = JSON.parse(readFileSync(snapshot, \"utf8\")) as Record<string, unknown>;\n const out: Record<string, string | undefined> = { ...env };\n for (const [key, value] of Object.entries(parsed)) if (typeof value === \"string\" && out[key] === undefined) out[key] = value;\n return out;\n } catch {\n return env;\n }\n}\n\nexport function loadConfig(path = DEFAULT_CONFIG_PATH, env: NodeJS.ProcessEnv = process.env, snapshot = BOX_ENV_SNAPSHOT): DaemonConfig {\n const raw = existsSync(path) ? (JSON.parse(readFileSync(path, \"utf8\")) as Partial<DaemonConfig>) : {};\n const box = boxEnv(env, snapshot);\n const audience = raw.audience ?? box.UNFZ_PUBLIC_URL ?? \"\";\n if (!audience) throw new Error(`eve sandbox: no audience (set \"audience\" in ${path}, or UNFZ_PUBLIC_URL; ${snapshot} has none either)`);\n const envAllow = (box.EVE_SANDBOX_ALLOW ?? \"\").split(\",\").map((s) => s.trim()).filter(Boolean);\n const allow = [...(raw.allow ?? []), ...envAllow];\n if (allow.length === 0) throw new Error(`eve sandbox: nothing is allowed (set \"allow\" in ${path} or EVE_SANDBOX_ALLOW)`);\n return {\n audience: audience.replace(/\\/+$/, \"\"),\n allow,\n issuer: raw.issuer ?? \"https://unfazed.dev/oidc\",\n port: raw.port ?? 3000,\n homeRoot: raw.homeRoot ?? \"/home\",\n idleMinutes: raw.idleMinutes ?? 20,\n retentionDays: raw.retentionDays ?? 14,\n env: raw.env ?? {},\n };\n}\n\n// ---------------------------------------------------------------------------\n// Workload identity tokens (ES256 JWTs from the unfazed issuer)\n\ninterface Claims {\n readonly iss: string;\n readonly aud: string | readonly string[];\n readonly exp: number;\n readonly nbf?: number;\n readonly app_id?: string;\n readonly owner_id?: string;\n readonly alias?: string;\n}\n\nclass HttpError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message?: string,\n ) {\n super(message ?? code);\n }\n}\n\nfunction b64urlDecode(part: string): Uint8Array {\n return new Uint8Array(Buffer.from(part.replace(/-/g, \"+\").replace(/_/g, \"/\"), \"base64\"));\n}\n\nexport class TokenVerifier {\n private keys = new Map<string, CryptoKey>();\n private fetchedAt = 0;\n private refreshing: Promise<void> | undefined;\n\n constructor(\n private readonly config: Pick<DaemonConfig, \"audience\" | \"issuer\" | \"allow\">,\n private readonly fetchJson: (url: string) => Promise<unknown> = async (url) => {\n const res = await fetch(url, { signal: AbortSignal.timeout(10_000) });\n if (!res.ok) throw new Error(`${url}: ${res.status}`);\n return await res.json();\n },\n private readonly now: () => number = () => Date.now(),\n ) {}\n\n /** The calling app's namespace (its app id) for a valid, allowed token. */\n async verify(authorization: string | undefined): Promise<string> {\n const match = /^Bearer ([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+)$/.exec(authorization ?? \"\");\n if (!match) throw new HttpError(401, \"invalid_token\", \"a workload identity bearer token is required\");\n const [, h, p, s] = match as unknown as [string, string, string, string];\n let header: { alg?: string; kid?: string };\n let claims: Claims;\n try {\n header = JSON.parse(Buffer.from(b64urlDecode(h)).toString(\"utf8\"));\n claims = JSON.parse(Buffer.from(b64urlDecode(p)).toString(\"utf8\"));\n } catch {\n throw new HttpError(401, \"invalid_token\", \"malformed token\");\n }\n if (header.alg !== \"ES256\" || !header.kid) throw new HttpError(401, \"invalid_token\", \"unsupported token algorithm\");\n const key = await this.key(header.kid);\n const ok = await webcrypto.subtle.verify(\n { name: \"ECDSA\", hash: \"SHA-256\" },\n key,\n b64urlDecode(s),\n new TextEncoder().encode(`${h}.${p}`),\n );\n if (!ok) throw new HttpError(401, \"invalid_token\", \"bad signature\");\n const nowS = Math.floor(this.now() / 1000);\n const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];\n if (claims.iss !== this.config.issuer) throw new HttpError(401, \"invalid_token\", \"wrong issuer\");\n if (!audiences.some((a) => a.replace(/\\/+$/, \"\") === this.config.audience)) throw new HttpError(401, \"invalid_token\", \"wrong audience\");\n if (typeof claims.exp !== \"number\" || claims.exp < nowS - 30) throw new HttpError(401, \"invalid_token\", \"token expired\");\n if (typeof claims.nbf === \"number\" && claims.nbf > nowS + 30) throw new HttpError(401, \"invalid_token\", \"token not yet valid\");\n const app = claims.app_id ?? \"\";\n if (!app) throw new HttpError(403, \"forbidden\", \"the token names no app\");\n const allowed = this.config.allow.some((entry) =>\n entry.startsWith(\"owner:\") ? entry.slice(6) === claims.owner_id : entry === app,\n );\n if (!allowed) throw new HttpError(403, \"forbidden\", `app ${app} may not use this box`);\n return app;\n }\n\n private async key(kid: string): Promise<CryptoKey> {\n const cached = this.keys.get(kid);\n if (cached && this.now() - this.fetchedAt < 10 * 60 * 1000) return cached;\n // Unknown kid: refetch, but at most every 30 s.\n if (!cached || this.now() - this.fetchedAt >= 30_000) await this.refresh();\n const key = this.keys.get(kid);\n if (!key) throw new HttpError(401, \"invalid_token\", \"unknown signing key\");\n return key;\n }\n\n private async refresh(): Promise<void> {\n this.refreshing ??= (async () => {\n try {\n const discovery = (await this.fetchJson(`${this.config.issuer}/.well-known/openid-configuration`)) as { jwks_uri?: string };\n const jwks = (await this.fetchJson(discovery.jwks_uri ?? `${this.config.issuer}/jwks`)) as { keys?: JsonWebKey[] };\n const next = new Map<string, CryptoKey>();\n for (const jwk of jwks.keys ?? []) {\n const kid = (jwk as { kid?: string }).kid;\n if (!kid || jwk.kty !== \"EC\" || jwk.crv !== \"P-256\") continue;\n next.set(kid, await webcrypto.subtle.importKey(\"jwk\", jwk, { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\"verify\"]));\n }\n this.keys = next;\n this.fetchedAt = this.now();\n } finally {\n this.refreshing = undefined;\n }\n })();\n await this.refreshing;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sessions: one Unix user each\n\ninterface Session extends SessionInfo {\n readonly uid: number;\n readonly gid: number;\n}\n\n/** The Unix name of an app's session: bounded, derived from the ids only. */\nexport function sessionName(app: string, sessionId: string): string {\n return `eve${createHash(\"sha256\").update(`${app}\\n${sessionId}`).digest(\"hex\").slice(0, 12)}`;\n}\n\nfunction run(argv: readonly string[], input?: string | Uint8Array): { status: number; stdout: string; stderr: string } {\n const result = spawnSync(argv[0]!, argv.slice(1), { input, encoding: \"utf8\", env: { PATH: PATH_ENV } });\n return { status: result.status ?? 1, stdout: result.stdout ?? \"\", stderr: result.stderr ?? \"\" };\n}\n\nclass Sessions {\n private lastUsed = new Map<string, number>();\n\n constructor(private readonly config: DaemonConfig) {}\n\n private describe(name: string): Session | undefined {\n const entry = run([\"getent\", \"passwd\", name]);\n if (entry.status !== 0) return undefined;\n const [, , uid, gid, , home] = entry.stdout.trim().split(\":\");\n return { name, uid: Number(uid), gid: Number(gid), home: home!, workspace: `${home}/workspace` };\n }\n\n get(app: string, sessionId: string): Session | undefined {\n const session = this.describe(sessionName(app, sessionId));\n if (session) this.touch(session.name);\n return session;\n }\n\n ensure(app: string, sessionId: string, template: readonly WireFile[]): Session & { created: boolean } {\n const name = sessionName(app, sessionId);\n const existing = this.describe(name);\n if (existing) {\n this.touch(name);\n return { ...existing, created: false };\n }\n const home = `${this.config.homeRoot}/${name}`;\n const added = run([\"useradd\", \"--create-home\", \"--home-dir\", home, \"--shell\", \"/bin/bash\", \"--user-group\", \"--comment\", \"eve sandbox\", name]);\n const session = this.describe(name);\n if (!session) throw new HttpError(500, \"session_create_failed\", added.stderr.trim() || \"useradd failed\");\n run([\"chmod\", \"0700\", home]);\n const files = [\n { path: `${session.workspace}/.keep`, b64: \"\" },\n ...template.map((file) => ({ path: mapTemplatePath(file.path, session), b64: file.b64 })),\n ];\n fileop(session, { op: \"write\", files });\n this.touch(name);\n return { ...session, created: true };\n }\n\n stop(name: string): void {\n run([\"pkill\", \"-KILL\", \"-u\", name]);\n }\n\n delete(app: string, sessionId: string): void {\n const name = sessionName(app, sessionId);\n if (!this.describe(name)) return;\n this.stop(name);\n for (let attempt = 0; attempt < 5; attempt++) {\n if (run([\"userdel\", \"--remove\", name]).status === 0) break;\n this.stop(name);\n spawnSync(\"sleep\", [\"0.2\"]);\n }\n rmSync(`${this.config.homeRoot}/${name}`, { recursive: true, force: true });\n this.lastUsed.delete(name);\n }\n\n touch(name: string): void {\n const now = Date.now();\n const previous = this.lastUsed.get(name) ?? 0;\n this.lastUsed.set(name, now);\n if (now - previous < 60_000) return;\n const marker = `${this.config.homeRoot}/${name}/${LAST_USED}`;\n try {\n if (!existsSync(marker)) writeFileSync(marker, \"\");\n utimesSync(marker, now / 1000, now / 1000);\n } catch {\n // the home may be gone mid-delete\n }\n }\n\n /** Deletes sessions without a call for longer than the retention, users and homes included. */\n expireOld(): void {\n if (this.config.retentionDays <= 0) return;\n const limit = this.config.retentionDays * 24 * 60 * 60 * 1000;\n const passwd = run([\"getent\", \"passwd\"]).stdout.split(\"\\n\");\n for (const line of passwd) {\n const [name, , , , , home] = line.split(\":\");\n if (!name || !SESSION_USER.test(name) || home !== `${this.config.homeRoot}/${name}`) continue;\n let at: number;\n try {\n at = statSync(`${home}/${LAST_USED}`).mtimeMs;\n } catch {\n try {\n at = statSync(home).mtimeMs;\n } catch {\n at = 0;\n }\n }\n if (Date.now() - at > limit) {\n this.stop(name);\n run([\"userdel\", \"--remove\", name]);\n rmSync(home, { recursive: true, force: true });\n this.lastUsed.delete(name);\n }\n }\n }\n\n /** Kills the processes of sessions idle for longer than the limit. */\n reapIdle(): void {\n if (this.config.idleMinutes <= 0) return;\n const limit = this.config.idleMinutes * 60 * 1000;\n for (const [name, at] of this.lastUsed) {\n if (Date.now() - at > limit) {\n this.stop(name);\n this.lastUsed.delete(name);\n }\n }\n }\n}\n\nfunction mapTemplatePath(path: string, session: SessionInfo): string {\n if (path === TEMPLATE_WORKSPACE || path.startsWith(`${TEMPLATE_WORKSPACE}/`)) return session.workspace + path.slice(TEMPLATE_WORKSPACE.length);\n if (path === TEMPLATE_HOME || path.startsWith(`${TEMPLATE_HOME}/`)) return session.home + path.slice(TEMPLATE_HOME.length);\n return path;\n}\n\nfunction sessionEnv(session: Session, extra: Readonly<Record<string, string>>): Record<string, string> {\n return {\n PATH: PATH_ENV,\n HOME: session.home,\n USER: session.name,\n LOGNAME: session.name,\n SHELL: \"/bin/bash\",\n LANG: \"C.UTF-8\",\n ...extra,\n };\n}\n\nfunction asUser(session: Session, argv: readonly string[]): string[] {\n return [\"setpriv\", `--reuid=${session.uid}`, `--regid=${session.gid}`, \"--init-groups\", \"--\", ...argv];\n}\n\n// ---------------------------------------------------------------------------\n// File operations, run as the session user\n\ntype FileOp =\n | { op: \"read\"; path: string }\n | { op: \"write\"; files: readonly WireFile[] }\n | { op: \"remove\"; path: string; force: boolean; recursive: boolean };\n\nconst FILEOP_MISSING = 3;\nconst FILEOP_DIRECTORY = 4;\nconst FILEOP_FAILED = 5;\n\nfunction fileop(session: Session, request: FileOp): Buffer {\n // This module itself, whoever loaded it (its own entry or `unfazed eve-sandbox serve`).\n const argv = asUser(session, [process.execPath, fileURLToPath(import.meta.url), \"fileop\"]);\n const result = spawnSync(argv[0]!, argv.slice(1), {\n input: JSON.stringify(request),\n env: { PATH: PATH_ENV, HOME: session.home },\n maxBuffer: MAX_BODY,\n });\n const status = result.status ?? FILEOP_FAILED;\n if (status === 0) return result.stdout as Buffer;\n const message = (result.stderr?.toString() ?? \"\").trim().slice(0, 500);\n if (status === FILEOP_MISSING) throw new HttpError(404, \"not_found\", message || \"no such file\");\n if (status === FILEOP_DIRECTORY) throw new HttpError(409, \"is_directory\", message || \"the path is a directory\");\n throw new HttpError(422, \"file_operation_failed\", message || \"the file operation failed\");\n}\n\n/** The `fileop` subcommand: one request on stdin, run with the caller's own permissions. */\nexport function runFileOp(): never {\n try {\n const request = JSON.parse(readFileSync(0, \"utf8\")) as FileOp;\n if (request.op === \"read\") {\n let st;\n try {\n st = statSync(request.path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") process.exit(FILEOP_MISSING);\n throw error;\n }\n if (st.isDirectory()) process.exit(FILEOP_DIRECTORY);\n process.stdout.write(readFileSync(request.path), () => process.exit(0));\n return undefined as never;\n }\n if (request.op === \"write\") {\n for (const file of request.files) {\n if (!file.path.startsWith(\"/\")) throw new Error(`not an absolute path: ${file.path}`);\n mkdirSync(dirname(file.path), { recursive: true });\n writeFileSync(file.path, Buffer.from(file.b64, \"base64\"));\n }\n process.exit(0);\n }\n if (request.op === \"remove\") {\n if (!existsSync(request.path)) process.exit(request.force ? 0 : FILEOP_MISSING);\n rmSync(request.path, { recursive: request.recursive, force: request.force });\n process.exit(0);\n }\n throw new Error(\"unknown operation\");\n } catch (error) {\n process.stderr.write(String((error as Error).message ?? error));\n process.exit(FILEOP_FAILED);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Processes\n\ninterface Proc {\n readonly id: string;\n readonly app: string;\n readonly session: string;\n readonly child: ChildProcess;\n readonly events: ProcessEvent[];\n bytes: number;\n exit: number | undefined;\n exitedAt: number | undefined;\n readonly listeners: Set<() => void>;\n}\n\nconst SIGNALS: Record<string, number> = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGKILL: 9, SIGSEGV: 11, SIGPIPE: 13, SIGTERM: 15 };\n\nclass Processes {\n readonly all = new Map<string, Proc>();\n\n start(app: string, session: Session, request: SpawnRequest, baseEnv: Readonly<Record<string, string>>): Proc {\n if (!existsSync(request.cwd)) throw new HttpError(400, \"invalid_cwd\", `working directory does not exist: ${request.cwd}`);\n const argv = asUser(session, [\"bash\", \"-lc\", request.command]);\n const child = spawn(argv[0]!, argv.slice(1), {\n cwd: request.cwd,\n env: { ...sessionEnv(session, { AI_AGENT: \"eve\", ...baseEnv }), ...request.env },\n stdio: [request.stdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n // Its own process group, so a kill reaches the whole tree.\n detached: true,\n });\n const proc: Proc = {\n id: randomUUID(),\n app,\n session: session.name,\n child,\n events: [],\n bytes: 0,\n exit: undefined,\n exitedAt: undefined,\n listeners: new Set(),\n };\n const push = (event: ProcessEvent) => {\n proc.events.push(event);\n for (const listener of proc.listeners) listener();\n };\n const pipe = (fd: 1 | 2) => (chunk: Buffer) => {\n if (proc.bytes + chunk.length > MAX_PROCESS_OUTPUT) return;\n proc.bytes += chunk.length;\n push({ s: proc.events.length + 1, f: fd, d: chunk.toString(\"base64\") });\n };\n child.stdout?.on(\"data\", pipe(1));\n child.stderr?.on(\"data\", pipe(2));\n child.on(\"error\", (error) => {\n push({ s: proc.events.length + 1, f: 2, d: Buffer.from(`eve sandbox: ${error.message}\\n`).toString(\"base64\") });\n });\n child.on(\"close\", (code, signal) => {\n proc.exit = code ?? 128 + (signal ? (SIGNALS[signal] ?? 1) : 1);\n proc.exitedAt = Date.now();\n push({ s: proc.events.length + 1, x: proc.exit });\n });\n this.all.set(proc.id, proc);\n return proc;\n }\n\n kill(proc: Proc): void {\n if (proc.exit !== undefined || proc.child.pid === undefined) return;\n try {\n process.kill(-proc.child.pid, \"SIGKILL\");\n } catch {\n proc.child.kill(\"SIGKILL\");\n }\n }\n\n killSession(name: string): void {\n for (const proc of this.all.values()) if (proc.session === name) this.kill(proc);\n }\n\n prune(): void {\n for (const [id, proc] of this.all) {\n if (proc.exitedAt !== undefined && Date.now() - proc.exitedAt > PROCESS_RETENTION_MS) this.all.delete(id);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n\nasync function readBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of req) {\n size += (chunk as Buffer).length;\n if (size > MAX_BODY) throw new HttpError(413, \"too_large\");\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks);\n}\n\nasync function readJson<T>(req: IncomingMessage): Promise<T> {\n const body = await readBody(req);\n try {\n return JSON.parse(body.toString(\"utf8\") || \"{}\") as T;\n } catch {\n throw new HttpError(400, \"invalid_json\");\n }\n}\n\nfunction sendJson(res: ServerResponse, status: number, value: unknown): void {\n const body = JSON.stringify(value);\n res.writeHead(status, { \"content-type\": \"application/json\", \"content-length\": Buffer.byteLength(body), \"cache-control\": \"no-store\" });\n res.end(body);\n}\n\nexport function createDaemon(config: DaemonConfig, verifier = new TokenVerifier(config)) {\n const sessions = new Sessions(config);\n const processes = new Processes();\n\n const sessionOf = (app: string, id: string): Session => {\n const session = sessions.get(app, decodeURIComponent(id));\n if (!session) throw new HttpError(410, \"session_gone\", \"no such sandbox session\");\n return session;\n };\n const procOf = (app: string, id: string): Proc => {\n const proc = processes.all.get(id);\n if (!proc || proc.app !== app) throw new HttpError(404, \"no_such_process\");\n sessions.touch(proc.session);\n return proc;\n };\n\n async function route(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url ?? \"/\", \"http://box\");\n const path = url.pathname;\n const method = req.method ?? \"GET\";\n if (method === \"GET\" && path === `${API_PREFIX}/health`) return sendJson(res, 200, { ok: true, version: DAEMON_VERSION });\n if (!path.startsWith(`${API_PREFIX}/`)) throw new HttpError(404, \"not_found\");\n const app = await verifier.verify(req.headers.authorization);\n const parts = path.slice(API_PREFIX.length + 1).split(\"/\");\n\n if (parts[0] === \"sessions\" && parts[1]) {\n const sessionId = decodeURIComponent(parts[1]);\n const sub = parts[2];\n if (!sub && method === \"PUT\") {\n const body = await readJson<EnsureSessionRequest>(req);\n const session = sessions.ensure(app, sessionId, body.template ?? []);\n return sendJson(res, 200, { name: session.name, home: session.home, workspace: session.workspace, created: session.created });\n }\n if (!sub && method === \"GET\") {\n const session = sessionOf(app, parts[1]);\n return sendJson(res, 200, { name: session.name, home: session.home, workspace: session.workspace });\n }\n if (!sub && method === \"DELETE\") {\n processes.killSession(sessionName(app, sessionId));\n sessions.delete(app, sessionId);\n return sendJson(res, 200, { deleted: true });\n }\n const session = sessionOf(app, parts[1]);\n if (sub === \"stop\" && method === \"POST\") {\n processes.killSession(session.name);\n sessions.stop(session.name);\n return sendJson(res, 200, { stopped: true });\n }\n if (sub === \"processes\" && method === \"POST\") {\n const body = await readJson<SpawnRequest>(req);\n if (typeof body.command !== \"string\" || typeof body.cwd !== \"string\" || !body.cwd.startsWith(\"/\")) throw new HttpError(400, \"invalid_request\");\n const env: Record<string, string> = {};\n for (const [key, value] of Object.entries(body.env ?? {})) if (typeof value === \"string\") env[key] = value;\n const proc = processes.start(app, session, { command: body.command, cwd: body.cwd, env, stdin: body.stdin === true }, config.env);\n return sendJson(res, 200, { id: proc.id, pid: proc.child.pid });\n }\n if (sub === \"file\" && method === \"GET\") {\n const target = url.searchParams.get(\"path\") ?? \"\";\n if (!target.startsWith(\"/\")) throw new HttpError(400, \"invalid_path\");\n const bytes = fileop(session, { op: \"read\", path: target });\n res.writeHead(200, { \"content-type\": \"application/octet-stream\", \"content-length\": bytes.length, \"cache-control\": \"no-store\" });\n res.end(bytes);\n return;\n }\n if (sub === \"files\" && method === \"POST\") {\n const body = await readJson<{ files?: WireFile[] }>(req);\n const files = (body.files ?? []).filter((f) => typeof f.path === \"string\" && typeof f.b64 === \"string\");\n if (files.some((f) => !f.path.startsWith(\"/\"))) throw new HttpError(400, \"invalid_path\");\n if (files.length > 0) fileop(session, { op: \"write\", files });\n return sendJson(res, 200, { written: files.length });\n }\n if (sub === \"remove\" && method === \"POST\") {\n const body = await readJson<RemoveRequest>(req);\n if (typeof body.path !== \"string\" || !body.path.startsWith(\"/\")) throw new HttpError(400, \"invalid_path\");\n fileop(session, { op: \"remove\", path: body.path, force: body.force === true, recursive: body.recursive === true });\n return sendJson(res, 200, { removed: true });\n }\n }\n\n if (parts[0] === \"processes\" && parts[1]) {\n const proc = procOf(app, parts[1]);\n const sub = parts[2];\n if (sub === \"events\" && method === \"GET\") return streamEvents(req, res, proc, Number(url.searchParams.get(\"after\") ?? \"0\") || 0);\n if (sub === \"stdin\" && method === \"POST\") {\n const body = await readBody(req);\n const stdin = proc.child.stdin;\n if (!stdin) throw new HttpError(409, \"no_stdin\");\n if (body.length > 0) await new Promise<void>((resolve, reject) => stdin.write(body, (e) => (e ? reject(e) : resolve())));\n if (url.searchParams.get(\"eof\") === \"1\") stdin.end();\n return sendJson(res, 200, { written: body.length });\n }\n if (sub === \"kill\" && method === \"POST\") {\n processes.kill(proc);\n return sendJson(res, 200, { killed: true });\n }\n }\n throw new HttpError(404, \"not_found\");\n }\n\n function streamEvents(req: IncomingMessage, res: ServerResponse, proc: Proc, after: number): void {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store, no-transform\",\n \"x-accel-buffering\": \"no\",\n connection: \"keep-alive\",\n });\n let sent = after;\n const flush = () => {\n while (sent < proc.events.length) {\n const event = proc.events[sent]!;\n sent = event.s;\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n if (\"x\" in event) {\n finish();\n return;\n }\n }\n };\n const heartbeat = setInterval(() => res.write(\": keep-alive\\n\\n\"), 15_000);\n const finish = () => {\n clearInterval(heartbeat);\n proc.listeners.delete(flush);\n res.end();\n };\n req.on(\"close\", () => {\n clearInterval(heartbeat);\n proc.listeners.delete(flush);\n });\n proc.listeners.add(flush);\n flush();\n }\n\n const server = createServer((req, res) => {\n route(req, res).catch((error: unknown) => {\n if (res.headersSent) {\n res.end();\n return;\n }\n if (error instanceof HttpError) return sendJson(res, error.status, { error: error.code, message: error.message });\n sendJson(res, 500, { error: \"internal\", message: String((error as Error)?.message ?? error).slice(0, 300) });\n });\n });\n server.requestTimeout = 0;\n server.headersTimeout = 60_000;\n const timers = [\n setInterval(() => processes.prune(), 60_000),\n setInterval(() => sessions.reapIdle(), 60_000),\n setInterval(() => sessions.expireOld(), 60 * 60 * 1000),\n ];\n return {\n server,\n listen(port = config.port) {\n return new Promise<void>((resolve) => server.listen(port, \"0.0.0.0\", () => resolve()));\n },\n close() {\n for (const timer of timers) clearInterval(timer);\n server.close();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n\nfunction argValue(flag: string): string | undefined {\n const index = process.argv.indexOf(flag);\n return index >= 0 ? process.argv[index + 1] : undefined;\n}\n\nconst isMain = (() => {\n try {\n return import.meta.url === `file://${process.argv[1]}` || (process.argv[1] ?? \"\").endsWith(\"eve-sandbox-daemon.js\");\n } catch {\n return false;\n }\n})();\n\nif (isMain) {\n const command = process.argv[2];\n if (command === \"fileop\") runFileOp();\n if (command === \"serve\") {\n const config = loadConfig(argValue(\"--config\"));\n const daemon = createDaemon(config);\n await daemon.listen();\n process.stdout.write(`eve sandbox daemon ${DAEMON_VERSION} on :${config.port} for ${config.audience}, ${config.allow.length} allowed\\n`);\n } else if (command !== \"fileop\") {\n process.stderr.write(\"usage: eve-sandbox-daemon serve [--config <path>] | fileop\\n\");\n process.exit(2);\n }\n}\n"]}
|
|
@@ -65,6 +65,11 @@ export declare class SandboxClient {
|
|
|
65
65
|
signal?: AbortSignal;
|
|
66
66
|
timeoutMs?: number;
|
|
67
67
|
}): Promise<Response>;
|
|
68
|
+
/**
|
|
69
|
+
* A box answering anything but the daemon's JSON (its URL serves the
|
|
70
|
+
* platform's placeholder page, or another app) has no sandbox daemon.
|
|
71
|
+
*/
|
|
72
|
+
private notDaemon;
|
|
68
73
|
ok(response: Response, what: string): Promise<Response>;
|
|
69
74
|
ensure(sessionId: string, template: readonly WireFile[]): Promise<SessionInfo>;
|
|
70
75
|
/** The session, or `null` when the box no longer has it. */
|