@dxos/edge-client 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/chunk-edge-http-client.mjs +47 -24
- package/dist/lib/chunk-edge-http-client.mjs.map +1 -1
- package/dist/lib/index.mjs +60 -39
- package/dist/lib/index.mjs.map +1 -1
- package/dist/types/src/auth-challenge.d.ts +2 -1
- package/dist/types/src/auth-challenge.d.ts.map +1 -1
- package/dist/types/src/base-http-client.d.ts +1 -1
- package/dist/types/src/base-http-client.d.ts.map +1 -1
- package/dist/types/src/client-version.d.ts +15 -0
- package/dist/types/src/client-version.d.ts.map +1 -0
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/auth-challenge.test.ts +17 -2
- package/src/auth-challenge.ts +9 -2
- package/src/base-http-client.ts +15 -2
- package/src/client-version.ts +44 -0
- package/src/edge-client.test.ts +28 -2
- package/src/edge-client.ts +25 -3
- package/src/edge-http-client.test.ts +43 -0
- package/src/edge-http-client.ts +1 -4
- package/src/edge-ws-connection.ts +3 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-edge-http-client.mjs","names":[],"sources":["../../src/auth-challenge.ts","../../src/errors.ts","../../src/http-client.ts","../../src/utils.ts","../../src/base-http-client.ts","../../src/edge-http-client.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { toBinary } from '@bufbuild/protobuf';\nimport { base64Decode } from '@bufbuild/protobuf/wire';\n\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { PresentationSchema } from '@dxos/protocols/buf/dxos/halo/credentials_pb';\n\nimport { type EdgeIdentity } from './edge-identity.ts';\n\n/**\n * The VerifiablePresentation challenge/response handshake, shared by the HTTP and WebSocket\n * clients. Kept apart from {@link EdgeIdentity}, which is only the identity contract: this module\n * owns the wire format (header parsing, `/auth` round trip) rather than who is authenticating.\n *\n * Two ways in, because a client must work against servers on either side of the change that made\n * `/auth` answer 200:\n * - {@link fetchAuthChallenge} asks `/auth` up front. Preferred — acquiring a challenge is a\n * successful operation.\n * - {@link handleAuthChallenge} reads the challenge off a 401 that already happened. The lazy\n * fallback, and the only option on servers whose `/auth` can just reject.\n */\n\nconst VP_SCHEME = 'VerifiablePresentation';\n\n/**\n * Pull the challenge out of a `WWW-Authenticate` value.\n *\n * `WWW-Authenticate` carries a comma-separated *list* of challenges (RFC 9110 §11.6.1), so the\n * VP challenge is not necessarily first — edge emits `Bearer realm=\"dxos\", VerifiablePresentation …`\n * whenever both auth methods are allowed. Values may be quoted or a bare token; base64's `/` and\n * `=` are not `tchar`, so a real nonce is always quoted, while the historical `challenge=TODO`\n * placeholder is not.\n *\n * An empty challenge (`challenge=\"\"`) is reported as absent rather than as the empty string. Edge\n * emits exactly that when its server keypair is unconfigured, and there is nothing a caller can\n * sign — treating it as present would route the request through the auth path only to fail on the\n * missing challenge, masking the original response.\n *\n * Only the VP challenge's own parameters are read. Two things would otherwise be mistaken for it:\n * a `Bearer realm=\"VerifiablePresentation challenge=…\"`, which declares Bearer alone, and a\n * `challenge` belonging to a later scheme when the VP challenge carries none. Signing either would\n * retry an unrelated 401, running a non-idempotent request twice.\n */\nexport const parseChallengeHeader = (header: string | null | undefined): string | undefined => {\n if (!header) {\n return undefined;\n }\n\n // Segment, rather than regex over the whole header: an auth-param value may contain a comma\n // inside quotes, so no single delimiter separates challenges.\n const segments = splitOutsideQuotes(header);\n let parameters: string[] | undefined;\n for (let index = 0; index < segments.length && parameters === undefined; index++) {\n const afterScheme = stripVpScheme(segments[index]);\n if (afterScheme !== undefined) {\n parameters = [afterScheme, ...segments.slice(index + 1)];\n }\n }\n if (parameters === undefined) {\n return undefined;\n }\n\n for (const parameter of parameters) {\n const match = /^\\s*challenge\\s*=\\s*(?:\"([^\"]*)\"|([^\\s,]*))/i.exec(parameter);\n if (match) {\n return (match[1] ?? match[2]) || undefined;\n }\n // A segment that is not an auth-param starts the next challenge, whose params are not ours.\n if (parameter.trim().length > 0 && !/^\\s*[!#$%&'*+\\-.^_`|~\\w]+\\s*=/.test(parameter)) {\n return undefined;\n }\n }\n return undefined;\n};\n\n/**\n * The segment's text after the VP auth-scheme token, or undefined if it does not start with it.\n * The scheme must be a whole token, so a longer scheme merely beginning with it does not match.\n */\nconst stripVpScheme = (segment: string): string | undefined => {\n const trimmed = segment.trimStart();\n if (!trimmed.toLowerCase().startsWith(VP_SCHEME.toLowerCase())) {\n return undefined;\n }\n const remainder = trimmed.slice(VP_SCHEME.length);\n return remainder === '' || remainder.startsWith(' ') || remainder.startsWith('\\t') ? remainder : undefined;\n};\n\n/** Split on commas that are not inside a quoted string. */\nconst splitOutsideQuotes = (header: string): string[] => {\n const segments: string[] = [];\n let current = '';\n let inQuotes = false;\n for (let index = 0; index < header.length; index++) {\n const character = header[index];\n if (inQuotes) {\n current += character;\n if (character === '\\\\' && index + 1 < header.length) {\n current += header[++index];\n } else if (character === '\"') {\n inQuotes = false;\n }\n continue;\n }\n if (character === '\"') {\n inQuotes = true;\n current += character;\n } else if (character === ',') {\n segments.push(current);\n current = '';\n } else {\n current += character;\n }\n }\n segments.push(current);\n return segments;\n};\n\n/**\n * Read the challenge from an `/auth` response.\n *\n * Two shapes are accepted so a client works against servers on either side of the change:\n * - `200` with `{ success: true, data: { challenge } }` — the current shape. Acquiring a challenge\n * is a successful operation, so it is reported as one.\n * - `401` with `WWW-Authenticate` — every protected resource, plus older servers whose `/auth`\n * could only issue a challenge by failing.\n */\nexport const readAuthChallenge = async (response: Response): Promise<string | undefined> => {\n const headerChallenge = parseChallengeHeader(response.headers.get('WWW-Authenticate'));\n if (headerChallenge) {\n return headerChallenge;\n }\n if (!response.ok) {\n return undefined;\n }\n try {\n const body = await response.clone().json();\n const challenge = body?.data?.challenge ?? body?.challenge;\n return typeof challenge === 'string' && challenge.length > 0 ? challenge : undefined;\n } catch (error) {\n log.verbose('auth challenge response was not JSON', { error });\n return undefined;\n }\n};\n\nexport type AuthChallengeInfo = {\n /** Base64 challenge nonce. */\n challenge: string;\n /**\n * Server-advertised challenge validity. Absent on servers predating the TTL contract — there the\n * only refresh signal is the eventual 401.\n */\n expiresInMs?: number;\n};\n\n/**\n * Fetch a challenge nonce from the edge `/auth` endpoint.\n *\n * This is the deliberate, up-front way to obtain a challenge. The alternative — firing a request\n * you expect to be rejected and reading the challenge off the 401 — logs an error in every browser\n * console and records a routine auth failure server-side on each client boot.\n *\n * Returns undefined if the endpoint is unreachable or answers in neither known shape; callers fall\n * back to the 401 path, which still works against every server.\n */\nexport const fetchAuthChallengeInfo = async (baseHttpUrl: string | URL): Promise<AuthChallengeInfo | undefined> => {\n try {\n const response = await fetch(new URL('/auth', baseHttpUrl));\n const challenge = await readAuthChallenge(response);\n if (!challenge) {\n log.verbose('no challenge in /auth response', { status: response.status });\n return undefined;\n }\n let expiresInMs: number | undefined;\n try {\n const body = await response.clone().json();\n const advertised = body?.data?.expiresInMs;\n // Finite-positive only: `1e400` parses to `Infinity`, which would schedule a refresh at never.\n expiresInMs = Number.isFinite(advertised) && advertised > 0 ? advertised : undefined;\n } catch {\n // Header-shaped challenge (the 401 fallback) — no JSON body to read a TTL from.\n }\n return { challenge, expiresInMs };\n } catch (error) {\n log.verbose('failed to fetch auth challenge', { error });\n return undefined;\n }\n};\n\n/** The challenge alone; see {@link fetchAuthChallengeInfo}. */\nexport const fetchAuthChallenge = async (baseHttpUrl: string | URL): Promise<string | undefined> =>\n (await fetchAuthChallengeInfo(baseHttpUrl))?.challenge;\n\n/**\n * Sign a base64 challenge, returning the encoded presentation.\n *\n * The bytes are returned unencoded because the two transports frame them differently: HTTP wraps\n * them in an `Authorization` header, the WebSocket path in a subprotocol token.\n */\nexport const presentCredentialsForChallenge = async (\n identity: EdgeIdentity,\n challenge: string,\n): Promise<Uint8Array> => {\n const presentation = await identity.presentCredentials({ challenge: base64Decode(challenge) });\n return toBinary(PresentationSchema, presentation);\n};\n\nexport type ChallengeAuthentication = {\n /** Encoded presentation bound to the fetched challenge. */\n presentation: Uint8Array;\n /** TTL advertised beside the challenge, when the server provides one. */\n expiresInMs?: number;\n};\n\n/**\n * Obtain a challenge from `/auth` and sign it.\n * Returns undefined when no challenge could be obtained, leaving the caller on the 401 fallback.\n */\nexport const authenticateViaChallengeEndpoint = async (\n baseHttpUrl: string | URL,\n identity: EdgeIdentity,\n): Promise<ChallengeAuthentication | undefined> => {\n const info = await fetchAuthChallengeInfo(baseHttpUrl);\n if (info === undefined) {\n return undefined;\n }\n return {\n presentation: await presentCredentialsForChallenge(identity, info.challenge),\n expiresInMs: info.expiresInMs,\n };\n};\n\n/**\n * Sign the challenge carried by a rejected response.\n * Retained for the lazy path: a request that 401s is retried with a presentation bound to the\n * challenge that rejection carried.\n */\nexport const handleAuthChallenge = async (failedResponse: Response, identity: EdgeIdentity): Promise<Uint8Array> => {\n const challenge = await readAuthChallenge(failedResponse);\n invariant(challenge !== undefined, 'No auth challenge in response.');\n return presentCredentialsForChallenge(identity, challenge);\n};\n","import { BaseError } from '@dxos/errors';\n\n//\n// Copyright 2024 DXOS.org\n//\n\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\n }\n}\n\n/** EDGE client operation failed. The underlying failure, where there is one, is the `cause`. */\nexport class EdgeClientError extends BaseError.extend('EdgeClientError', 'EDGE client operation failed.') {}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport type * as HttpClient from 'effect/http/HttpClient';\nimport type * as HttpClientError from 'effect/http/HttpClientError';\nimport type * as HttpClientResponse from 'effect/http/HttpClientResponse';\nimport * as Layer from 'effect/Layer';\nimport * as Schedule from 'effect/Schedule';\n\nimport { log } from '@dxos/log';\nimport { EdgeCredentialsHeaderCodec } from '@dxos/protocols';\n\nimport { EdgeClientError } from './errors.ts';\n\n// TODO(burdon): Factor out.\n\nexport type RetryOptions = {\n timeout: Duration.Duration;\n retryTimes: number;\n retryBaseDelay: Duration.Duration;\n};\n\n// Layer pattern.\nexport class HttpConfig extends Context.Service<HttpConfig, RetryOptions>()('HttpConfig') {\n static default = Layer.succeed(HttpConfig, {\n timeout: Duration.millis(1_000),\n retryTimes: 3,\n retryBaseDelay: Duration.millis(1_000),\n });\n}\n\n// HOC pattern.\nexport const withRetry = (\n effect: Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError, HttpClient.HttpClient>,\n {\n timeout = Duration.millis(1_000),\n retryBaseDelay = Duration.millis(1_000),\n retryTimes = 3,\n }: Partial<RetryOptions> = {},\n) => {\n return effect.pipe(\n Effect.flatMap((res): Effect.Effect<unknown, EdgeClientError | HttpClientError.HttpClientError> =>\n // Treat 500 errors as retryable?\n res.status === 500 ? Effect.fail(new EdgeClientError({ message: res.status.toString() })) : res.json,\n ),\n Effect.timeout(timeout),\n Effect.retry({\n schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),\n times: retryTimes,\n }),\n );\n};\n\nexport const withRetryConfig = (\n effect: Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError, HttpClient.HttpClient>,\n) =>\n Effect.gen(function* () {\n const config = yield* HttpConfig;\n return yield* withRetry(effect, config);\n });\n\nexport const withLogging = <A extends HttpClientResponse.HttpClientResponse, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n // v4's `tap` requires an Effect; a bare side effect has to be lifted.\n Effect.tap((res) =>\n Effect.sync(() => {\n log.info('response', { status: res.status });\n }),\n ),\n );\n\n/**\n * Frame an encoded verifiable presentation as an `Authorization` header value.\n * Kept as a named export for existing callers; the wire format itself lives in\n * {@link EdgeCredentialsHeaderCodec} so the client and the Edge worker share one definition.\n */\nexport const encodeAuthHeader = (presentation: Uint8Array): string => EdgeCredentialsHeaderCodec.encode(presentation);\n","//\n// Copyright 2024 DXOS.org\n//\n\nexport const getEdgeUrlWithProtocol = (baseUrl: string, protocol: 'http' | 'ws') => {\n const isSecure = baseUrl.startsWith('https') || baseUrl.startsWith('wss');\n const url = new URL(baseUrl);\n url.protocol = protocol + (isSecure ? 's' : '');\n return url.toString();\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { sleep } from '@dxos/async';\nimport { Context, TRACE_SPAN_ATTRIBUTE, type TraceContextData } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { EDGE_CLIENT_TAG_HEADER, EdgeAuthChallengeError, EdgeCallFailedError, type EdgeFailure } from '@dxos/protocols';\n\nimport { authenticateViaChallengeEndpoint, handleAuthChallenge, parseChallengeHeader } from './auth-challenge.ts';\nimport { type EdgeIdentity } from './edge-identity.ts';\nimport { encodeAuthHeader } from './http-client.ts';\nimport { getEdgeUrlWithProtocol } from './utils.ts';\n\nconst DEFAULT_RETRY_TIMEOUT = 1500;\nconst DEFAULT_RETRY_JITTER = 500;\nconst DEFAULT_MAX_RETRIES_COUNT = 3;\nconst WARNING_BODY_SIZE = 10 * 1024 * 1024; // 10MB\n\nexport type RetryConfig = {\n /** Number of retries, not counting the initial request. */\n count: number;\n /** Delay before retries in ms. */\n timeout?: number;\n /** Random additional delay to spread retries. */\n jitter?: number;\n};\n\nexport type EdgeHttpCallArgs = {\n retry?: RetryConfig;\n /**\n * Force authentication by pre-fetching `/auth` to obtain the challenge before\n * sending the body. Use for requests with large bodies to avoid sending twice.\n * Not available on HubHttpClient (hub-service has no `/auth` endpoint).\n */\n auth?: boolean;\n};\n\nexport type BaseHttpClientOptions = {\n /**\n * Tag included in the {@link EDGE_CLIENT_TAG_HEADER} header on every request.\n * Used on Edge to classify traffic for metering (e.g. `ci-e2e`).\n */\n clientTag?: string;\n /**\n * Admin API key, sent as `Authorization: Bearer` on every request in place of the identity\n * verifiable-presentation flow — for headless callers (CI) that hold no HALO identity.\n */\n apiKey?: string;\n};\n\ntype HttpRequestArgs = {\n method: string;\n retry?: RetryConfig;\n body?: any;\n /** @default true */\n json?: boolean;\n auth?: boolean;\n};\n\nexport type RawHttpRequestArgs = {\n method: string;\n body?: BodyInit;\n headers?: Record<string, string>;\n retry?: RetryConfig;\n auth?: boolean;\n};\n\nexport abstract class BaseHttpClient {\n protected readonly _baseUrl: string;\n protected readonly _clientTag: string | undefined;\n protected readonly _apiKey: string | undefined;\n protected _edgeIdentity: EdgeIdentity | undefined;\n /** Auth header cached until it goes stale (see `_authHeaderIsStale`) or a 401 replaces it. */\n protected _authHeader: string | undefined;\n /** When the current `_authHeader` was signed; with the TTL below it decides proactive refresh. */\n private _authAcquiredAt: number | undefined;\n /** Last TTL `/auth` advertised — a server constant, so 401-minted headers reuse it. */\n private _authTtlMs: number | undefined;\n /** Single-flight guard so concurrent requests with a stale header share one `/auth` round trip. */\n private _authPrefetch: Promise<void> | undefined;\n\n constructor(baseUrl: string, options?: BaseHttpClientOptions) {\n // Slash-terminated: `new URL('account/me', '…/hub')` would otherwise drop the `/hub` prefix.\n const url = getEdgeUrlWithProtocol(baseUrl, 'http');\n this._baseUrl = url.endsWith('/') ? url : `${url}/`;\n this._clientTag = options?.clientTag;\n this._apiKey = options?.apiKey;\n log('created', { url: this._baseUrl });\n }\n\n get baseUrl() {\n return this._baseUrl;\n }\n\n /**\n * The `Authorization` header this client would send on an authenticated call, minting one first\n * if none is cached or the cached one has gone stale.\n *\n * For callers that have to make an EDGE request outside this client — the native OAuth flow\n * issues `/oauth/initiate` from Rust, the only place the `Origin` header can be set to the\n * loopback callback server. Resolves undefined when there is nothing to present (no identity, or\n * the challenge round trip failed), which EDGE endpoints that `skipAuth` accept.\n */\n async getAuthHeader(): Promise<string | undefined> {\n if (this._apiKey) {\n return undefined;\n }\n const identity = this._edgeIdentity;\n if (!this._authHeader || this._authHeaderIsStale()) {\n await this._prefetchAuthHeader();\n }\n // `setIdentity` may have swapped identities while the prefetch was in flight, so the cached\n // header now belongs to the new one — handing it back would sign the caller's request, made on\n // behalf of the old identity, as somebody else.\n if (this._edgeIdentity !== identity) {\n return undefined;\n }\n return this._authHeader;\n }\n\n setIdentity(identity: EdgeIdentity): void {\n if (this._edgeIdentity?.identityDid !== identity.identityDid || this._edgeIdentity?.peerKey !== identity.peerKey) {\n this._edgeIdentity = identity;\n this._authHeader = undefined;\n this._authAcquiredAt = undefined;\n // Drop any in-flight prefetch: it authenticates the previous identity, and awaiting it\n // would commit that identity's header for requests now belonging to the new one.\n this._authPrefetch = undefined;\n }\n }\n\n protected async _call<T>(ctx: Context, url: URL, args: HttpRequestArgs): Promise<T> {\n const shouldRetry = createRetryHandler(args);\n // Log presence/size only — never log raw body contents which may contain PII.\n log('fetch', {\n url,\n hasBody: args.body !== undefined,\n bodySize: typeof args.body === 'string' ? args.body.length : undefined,\n });\n\n const traceHeaders = getTraceHeaders(ctx);\n\n let handledAuth = false;\n const tryCount = 1;\n while (true) {\n let processingError: EdgeCallFailedError | undefined = undefined;\n try {\n if (args.auth && !this._apiKey && (!this._authHeader || this._authHeaderIsStale())) {\n await this._prefetchAuthHeader();\n }\n\n const request = createRequest(args, this._authHeader, traceHeaders, this._clientTag, this._apiKey);\n log('call', { url, tryCount, authHeader: !!this._authHeader });\n const response = await fetch(url, request);\n\n if (response.ok) {\n const contentType = response.headers.get('Content-Type') ?? '';\n // No-content responses (204, empty body, non-JSON) — return undefined.\n if (\n response.status === 204 ||\n response.headers.get('Content-Length') === '0' ||\n !contentType.includes('application/json')\n ) {\n return undefined as T;\n }\n const body = await response.clone().json();\n if (typeof body !== 'object' || body === null) {\n return body;\n }\n if (!('success' in body)) {\n return body;\n }\n if (body.success) {\n return body.data;\n }\n } else if (response.status === 401 && hasVpChallenge(response) && !handledAuth) {\n // Only retry edge auth when the 401 came from edge's own auth layer. Edge always sets a\n // VP challenge on its own 401s; upstream-forwarded 401s carry none, or carry an\n // unrelated scheme.\n this._authHeader = await this._handleUnauthorized(response);\n handledAuth = true;\n continue;\n }\n\n const contentType = response.headers.get('Content-Type') ?? '';\n const body: EdgeFailure = contentType.startsWith('application/json')\n ? await response.clone().json()\n : undefined;\n\n invariant(!body?.success, 'Expected body to not be a failure response or undefined.');\n\n if (body?.data?.type === 'auth_challenge' && typeof body?.data?.challenge === 'string') {\n processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);\n } else if (body?.success === false) {\n processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);\n } else {\n invariant(!response.ok, 'Expected response to not be ok.');\n processingError = await EdgeCallFailedError.fromHttpFailure(response);\n }\n } catch (error: any) {\n // A thrown EdgeCallFailedError already carries its retry semantics (e.g. the terminal\n // rejected-api-key 401) — wrapping it as a processing failure would mark it retryable.\n processingError =\n error instanceof EdgeCallFailedError ? error : EdgeCallFailedError.fromProcessingFailureCause(error);\n }\n\n if (processingError?.isRetryable && (await shouldRetry(ctx, processingError.retryAfterMs))) {\n log.verbose('retrying request', { url, processingError });\n } else {\n throw processingError!;\n }\n }\n }\n\n /**\n * Like {@link _call} but returns the raw `Response` instead of parsing a JSON envelope — for\n * endpoints with binary or absent response bodies (e.g. blob storage). A 404 is returned to the\n * caller rather than thrown, since \"not found\" is an expected outcome for lookups; all other\n * non-ok, non-retryable statuses throw `EdgeCallFailedError`, mirroring `_call`.\n *\n * NOTE: Duplicates `_call`'s auth/retry loop rather than sharing it, to avoid touching `_call`'s\n * broadly-depended-on JSON-envelope behavior. `EdgeHttpClient.aiRequest`'s separate\n * duplicate loop is a follow-up candidate for consolidating onto this method.\n */\n protected async _callRaw(ctx: Context, url: URL, args: RawHttpRequestArgs): Promise<Response> {\n const shouldRetry = createRetryHandler(args);\n log('fetch', { url, hasBody: args.body !== undefined });\n\n const traceHeaders = getTraceHeaders(ctx);\n\n let handledAuth = false;\n while (true) {\n let processingError: EdgeCallFailedError | undefined;\n try {\n if (args.auth && !this._apiKey && (!this._authHeader || this._authHeaderIsStale())) {\n await this._prefetchAuthHeader();\n }\n\n const headers: Record<string, string> = { ...args.headers };\n if (this._authHeader) {\n headers['Authorization'] = this._authHeader;\n } else if (this._apiKey) {\n // Canonical edgeAuth admin-key form; never collides with the VP header, since the\n // api-key path skips the auth flow that would populate it.\n headers['Authorization'] = `Bearer ${this._apiKey}`;\n }\n if (traceHeaders) {\n Object.assign(headers, traceHeaders);\n }\n if (this._clientTag) {\n headers[EDGE_CLIENT_TAG_HEADER] = this._clientTag;\n }\n\n const response = await fetch(url, { method: args.method, body: args.body, headers });\n\n if (response.ok || response.status === 404) {\n return response;\n }\n\n if (response.status === 401 && hasVpChallenge(response) && !handledAuth) {\n this._authHeader = await this._handleUnauthorized(response);\n handledAuth = true;\n continue;\n }\n\n processingError = await EdgeCallFailedError.fromHttpFailure(response);\n } catch (error: any) {\n // A thrown EdgeCallFailedError already carries its retry semantics (e.g. the terminal\n // rejected-api-key 401) — wrapping it as a processing failure would mark it retryable.\n processingError =\n error instanceof EdgeCallFailedError ? error : EdgeCallFailedError.fromProcessingFailureCause(error);\n }\n\n if (processingError?.isRetryable && (await shouldRetry(ctx, processingError.retryAfterMs))) {\n log.verbose('retrying raw request', { url, processingError });\n } else {\n throw processingError!;\n }\n }\n }\n\n /**\n * Acquire an auth header up front by asking `/auth` for a challenge.\n *\n * Best-effort: a failure here leaves `_authHeader` unset and the request proceeds\n * unauthenticated, falling back to the 401-and-retry path below. That fallback is what keeps\n * this working against servers whose `/auth` only issues a challenge by rejecting.\n *\n * The `catch` is what makes that true for *signing* failures, not just network ones:\n * `fetchAuthChallengeInfo` swallows its own fetch errors, but `presentCredentials` throws on a\n * device with no HALO chain (mid-invitation), and without this the rejection would surface from\n * `_call` as a failed request — turning a call that used to succeed unauthenticated into an error.\n */\n private _prefetchAuthHeader(): Promise<void> {\n if (this._authPrefetch) {\n return this._authPrefetch;\n }\n const prefetch: Promise<void> = this._prefetchAuthHeaderOnce()\n .catch((err) => {\n log.verbose('auth prefetch failed; proceeding unauthenticated', { err });\n })\n .finally(() => {\n // Only if this promise still owns the guard: `setIdentity` clears it mid-flight, so a\n // newer prefetch may already have claimed it, and clearing that one would let concurrent\n // callers each start their own `/auth` round trip and credential presentation.\n if (this._authPrefetch === prefetch) {\n this._authPrefetch = undefined;\n }\n });\n this._authPrefetch = prefetch;\n return prefetch;\n }\n\n private async _prefetchAuthHeaderOnce(): Promise<void> {\n const identity = this._edgeIdentity;\n if (!identity) {\n log.verbose('auth prefetch skipped: no identity set');\n return;\n }\n const authentication = await authenticateViaChallengeEndpoint(this._baseUrl, identity);\n // `setIdentity` may have swapped identities while the challenge round trip was in flight;\n // committing then would send the new identity's requests signed as the old one.\n if (authentication && this._edgeIdentity === identity) {\n this._authHeader = encodeAuthHeader(authentication.presentation);\n this._recordAuthAcquired(authentication.expiresInMs);\n }\n }\n\n /** Stale means past the proactive-refresh point, not necessarily rejected yet. */\n private _authHeaderIsStale(): boolean {\n if (this._authAcquiredAt === undefined || this._authTtlMs === undefined) {\n return false;\n }\n // Refresh a margin ahead of expiry, floored at half the TTL so tiny windows still refresh.\n const refreshAfterMs = Math.max(this._authTtlMs - AUTH_REFRESH_MARGIN_MS, Math.floor(this._authTtlMs / 2));\n return Date.now() - this._authAcquiredAt >= refreshAfterMs;\n }\n\n /**\n * Record when the current header was signed and the TTL advertised beside its challenge — only\n * originals; the refresh point is derived at read time. Without an advertised TTL (older\n * servers, the 401-minted path) the last known one is reused; if none was ever advertised the\n * header lives until a 401, exactly as before proactive refresh existed.\n */\n private _recordAuthAcquired(expiresInMs: number | undefined): void {\n this._authAcquiredAt = Date.now();\n this._authTtlMs = expiresInMs ?? this._authTtlMs;\n }\n\n protected async _handleUnauthorized(response: Response): Promise<string> {\n // A rejected API key is terminal — there is no challenge an identityless caller could answer.\n if (this._apiKey) {\n throw await EdgeCallFailedError.fromHttpFailure(response);\n }\n if (!this._edgeIdentity) {\n log.warn('unauthorized response received before identity was set');\n throw await EdgeCallFailedError.fromHttpFailure(response);\n }\n const challenge = await handleAuthChallenge(response, this._edgeIdentity);\n // The fresh header starts a new window; the 401 body carries no TTL, so the last one applies.\n this._recordAuthAcquired(undefined);\n return encodeAuthHeader(challenge);\n }\n}\n\n/** Refresh the cached auth header this long before its advertised expiry, absorbing request latency and clock skew. */\nconst AUTH_REFRESH_MARGIN_MS = 30_000;\n\n/**\n * Whether a response carries a VerifiablePresentation challenge we can actually answer.\n *\n * Header *presence* is not enough: a 401 forwarded from upstream may carry an unrelated scheme\n * (`Bearer realm=\"…\"`), and edge itself emits `challenge=\"\"` when its server keypair is\n * unconfigured. Neither yields something signable, and retrying those through the auth path would\n * fail on the missing challenge and mask the real error.\n */\nconst hasVpChallenge = (response: Response): boolean =>\n parseChallengeHeader(response.headers.get('WWW-Authenticate')) !== undefined;\n\nconst createRequest = (\n { method, body, json = true }: HttpRequestArgs,\n authHeader: string | undefined,\n traceHeaders?: Record<string, string>,\n clientTag?: string,\n apiKey?: string,\n): RequestInit => {\n let requestBody: BodyInit | undefined;\n const headers: HeadersInit = {};\n\n if (json) {\n requestBody = body === undefined ? undefined : JSON.stringify(body);\n headers['Content-Type'] = 'application/json';\n } else {\n requestBody = body;\n }\n\n if (typeof requestBody === 'string' && requestBody.length > WARNING_BODY_SIZE) {\n log.warn('Request with large body', { bodySize: requestBody.length });\n }\n\n if (authHeader) {\n headers['Authorization'] = authHeader;\n } else if (apiKey) {\n // Canonical edgeAuth admin-key form; never collides with the VP header, since the\n // api-key path skips the auth flow that would populate it.\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (traceHeaders) {\n Object.assign(headers, traceHeaders);\n }\n\n if (clientTag) {\n headers[EDGE_CLIENT_TAG_HEADER] = clientTag;\n }\n\n return { method, body: requestBody, headers };\n};\n\nconst getTraceHeaders = (ctx: Context): Record<string, string> | undefined => {\n const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE) as TraceContextData | undefined;\n if (!traceCtx) {\n return undefined;\n }\n const headers: Record<string, string> = { traceparent: traceCtx.traceparent };\n if (traceCtx.tracestate) {\n headers.tracestate = traceCtx.tracestate;\n }\n return headers;\n};\n\n/** @deprecated */\nconst createRetryHandler = ({ retry }: HttpRequestArgs) => {\n if (!retry || retry.count < 1) {\n return async () => false;\n }\n let retries = 0;\n const maxRetries = retry.count ?? DEFAULT_MAX_RETRIES_COUNT;\n const baseTimeout = retry.timeout ?? DEFAULT_RETRY_TIMEOUT;\n const jitter = retry.jitter ?? DEFAULT_RETRY_JITTER;\n return async (ctx: Context, retryAfter?: number) => {\n if (++retries > maxRetries || ctx.disposed) {\n return false;\n }\n if (retryAfter) {\n await sleep(retryAfter);\n } else {\n const timeout = baseTimeout + Math.random() * jitter;\n await sleep(timeout);\n }\n return true;\n };\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport * as EffectContext from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as FetchHttpClient from 'effect/http/FetchHttpClient';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport * as HttpClientRequest from 'effect/http/HttpClientRequest';\nimport * as Layer from 'effect/Layer';\nimport * as Schema from 'effect/Schema';\n\nimport { type Context } from '@dxos/context';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { invariant } from '@dxos/invariant';\nimport { type SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { type ProcessProtocol } from '@dxos/protocols';\nimport {\n type CompleteOAuthRegistrationRequest,\n type CompleteOAuthRegistrationResponse,\n type CreateAgentRequestBody,\n type CreateAgentResponseBody,\n EDGE_CLIENT_TAG_HEADER,\n type EdgeStatus,\n type ExecuteWorkflowResponseBody,\n type FeedProtocol,\n type GetAccessTokenRequest,\n type GetAccessTokenResponseBody,\n type GetAgentStatusResponseBody,\n type GetNotarizationResponseBody,\n type GetPluginsResponseBody,\n type InboxListResponse,\n InboxListResponseSchema,\n type InboxSendResponse,\n InboxSendResponseSchema,\n type IndexerHeadsResponse,\n type InitiateOAuthFlowRequest,\n type InitiateOAuthFlowResponse,\n type JoinSpaceRequest,\n type JoinSpaceResponseBody,\n type ObjectId,\n type PostNotarizationRequestBody,\n type RecoverIdentityRequest,\n type RecoverIdentityResponseBody,\n type SerializedError,\n type UploadFunctionRequest,\n type UploadFunctionResponseBody,\n} from '@dxos/protocols';\nimport {\n type QueryRequest as QueryRequestProto,\n type QueryResponse as QueryResponseProto,\n} from '@dxos/protocols/buf/dxos/echo/query_pb';\nimport { createUrl } from '@dxos/util';\n\nimport { BaseHttpClient, type BaseHttpClientOptions, type EdgeHttpCallArgs } from './base-http-client.ts';\nimport { proxyFetchLegacy } from './cors-proxy.ts';\nimport { HttpConfig, withLogging, withRetryConfig } from './http-client.ts';\n\nexport type { EdgeHttpCallArgs, RetryConfig } from './base-http-client.ts';\n\n/** Arguments for {@link EdgeHttpClient.request}; authenticates unless `auth` is `false`. */\nexport type EdgeRequestArgs = EdgeHttpCallArgs & {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** JSON-serializable request body. */\n body?: unknown;\n};\n\n/**\n * HTTP wire shape returned by `/queue/.../query`.\n */\nexport type EdgeQueryQueueResponse = {\n objects?: unknown[];\n nextCursor?: string;\n prevCursor?: string;\n};\n\nexport type UploadPluginBundleRequest = {\n slug: string;\n version: string;\n files: { path: string; content: string }[];\n};\n\nexport type CreateApiTokenRequest = {\n label: string;\n /** Epoch milliseconds; the token never expires when omitted. */\n expiresAt?: number;\n};\n\n/** A minted token: `token` is the secret, returned once; the rest is its non-secret summary. */\nexport type CreateApiTokenResponse = {\n token: string;\n id: string;\n prefix: string;\n label: string;\n expiresAt?: number | null;\n};\n\nexport type TriggersDispatcherStatus = {\n isActive: boolean;\n nextCronTaskRunTimestamp?: number;\n registeredTriggers: string[];\n stopAfterTimestamp?: number;\n remainingMs?: number;\n nextAlarmTimestamp?: number;\n};\n\nexport type GetCronTriggersResponse = {\n cronIds: string[];\n};\n\n/**\n * Per-trigger runtime status reported by the EDGE dispatcher, keyed to the\n * trigger's ECHO object id so a client can correlate it with the replicated\n * `Trigger` object in its local database.\n *\n * Served by compute-service at `GET /compute/triggers/{spaceId}` (see\n * {@link EdgeHttpClient.getSpaceTriggers}).\n */\nexport type EdgeTriggerStatus = {\n /** ECHO object id of the trigger. */\n triggerId: ObjectId;\n /** Whether the EDGE dispatcher currently has this trigger registered. */\n registered: boolean;\n kind: 'timer' | 'subscription' | 'email' | 'webhook' | 'feed' | 'direct';\n /** Next scheduled cron execution (epoch ms). Set only for `timer` triggers. */\n nextExecutionTimestamp?: number;\n /** Cooldown expiry after a failure (epoch ms). */\n cooldownUntilTimestamp?: number;\n /** Outcome of the most recent invocation on the edge. */\n lastResult?: {\n status: 'success' | 'failure';\n /** Completion time (epoch ms). */\n timestamp: number;\n error?: SerializedError;\n };\n};\n\n/**\n * Response of `GET /compute/triggers/{spaceId}`: the full list of triggers registered on a space's\n * EDGE dispatcher, with runtime status. Polled by the remote trigger monitor to surface edge\n * trigger state.\n */\nexport type GetSpaceTriggersResponse = {\n /** Whether the space's edge dispatcher is active. */\n isActive: boolean;\n triggers: EdgeTriggerStatus[];\n};\n\nexport type EdgeHttpClientOptions = BaseHttpClientOptions;\n\n/** What `finalize` reports back: the content-addressed key the bytes landed under, and their size. */\nexport type FinalizedUpload = { key: string; size: number; contentType?: string };\n\n/**\n * Validates the finalize response rather than asserting its shape.\n *\n * The body is untrusted network input, and both fields are load-bearing downstream: `key` is fed\n * to `fromDigestHex`, whose parser does not reject malformed hex and would silently mint a bogus\n * `ni:` URI, and `size` is recorded on the Blob object as the authoritative byte count. A cast\n * would let either through.\n */\nconst parseFinalizeResponse = (body: unknown): FinalizedUpload => {\n if (typeof body !== 'object' || body === null) {\n throw new Error('Blob upload finalize returned a non-object body.');\n }\n const { key, size, contentType } = body as Record<string, unknown>;\n // 64 lowercase hex characters: the SHA-256 digest this store is keyed by, and nothing else.\n if (typeof key !== 'string' || !/^[0-9a-f]{64}$/.test(key)) {\n throw new Error('Blob upload finalize returned no valid content key.');\n }\n if (typeof size !== 'number' || !Number.isInteger(size) || size < 0) {\n throw new Error('Blob upload finalize returned no valid size.');\n }\n if (contentType !== undefined && typeof contentType !== 'string') {\n throw new Error('Blob upload finalize returned a non-string content type.');\n }\n return { key, size, ...(contentType === undefined ? {} : { contentType }) };\n};\n\nexport class EdgeHttpClientService extends EffectContext.Service<EdgeHttpClientService, EdgeHttpClient>()(\n '@dxos/edge-client/EdgeHttpClient',\n) {}\n\n/**\n * HTTP client for the edge worker API (spaces, queues, functions, agents, etc.).\n *\n * Hub-service API (accounts, invitations) lives in {@link HubHttpClient} — the two\n * services run at different URLs and are never both available from the same base URL.\n */\n/** Upstream service the EDGE AI proxy forwards to; selects the `/ai/generate/<service>` route. */\nexport type EdgeAiService =\n | 'anthropic'\n | 'deepseek'\n | 'typesafe'\n /** TypeSafe's System One wire, answered by Workers AI's `typesafe/jev` on EDGE's Cloudflare account. */\n | 'workers-ai/typesafe';\n\nexport class EdgeHttpClient extends BaseHttpClient {\n constructor(baseUrl: string, options?: EdgeHttpClientOptions) {\n super(baseUrl, options);\n log('created', { url: this.baseUrl });\n }\n\n //\n // Status\n //\n\n public async getStatus(ctx: Context, args?: EdgeHttpCallArgs): Promise<EdgeStatus> {\n return this._call(ctx, new URL('/status', this.baseUrl), { ...args, method: 'GET', auth: true });\n }\n\n //\n // Generic\n //\n\n /**\n * Authenticated JSON call to an EDGE route that has no dedicated method here (e.g. a service a\n * plugin owns end to end). Resolves to the response envelope's unwrapped `data`, left `unknown`\n * so the caller decodes it against its own schema.\n */\n public async request(ctx: Context, path: string, args: EdgeRequestArgs): Promise<unknown> {\n const { method, body, ...rest } = args;\n return this._call<unknown>(ctx, new URL(path, this.baseUrl), { auth: true, ...rest, method, body });\n }\n\n //\n // Agents\n //\n\n public createAgent(\n ctx: Context,\n body: CreateAgentRequestBody,\n args?: EdgeHttpCallArgs,\n ): Promise<CreateAgentResponseBody> {\n return this._call(ctx, new URL('/identity/agents/create', this.baseUrl), {\n ...args,\n method: 'POST',\n body,\n auth: true,\n });\n }\n\n public getAgentStatus(\n ctx: Context,\n request: { ownerIdentityDid: string },\n args?: EdgeHttpCallArgs,\n ): Promise<GetAgentStatusResponseBody> {\n return this._call(ctx, new URL(`/identity/users/${request.ownerIdentityDid}/agent/status`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n //\n // Credentials\n //\n\n public getCredentialsForNotarization(\n ctx: Context,\n spaceId: SpaceId,\n args?: EdgeHttpCallArgs,\n ): Promise<GetNotarizationResponseBody> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/notarization`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n public async notarizeCredentials(\n ctx: Context,\n spaceId: SpaceId,\n body: PostNotarizationRequestBody,\n args?: EdgeHttpCallArgs,\n ): Promise<void> {\n await this._call(ctx, new URL(`/db/spaces/${spaceId}/notarization`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Identity\n //\n\n public async recoverIdentity(\n ctx: Context,\n body: RecoverIdentityRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<RecoverIdentityResponseBody> {\n return this._call(ctx, new URL('/db/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });\n }\n\n /**\n * Names the space's root document, which edge cannot derive, and returns the root in force —\n * not necessarily the one offered, since the record is write-once.\n */\n public async recordSpaceRoot(\n ctx: Context,\n spaceId: SpaceId,\n body: { rootDocumentUrl: string },\n args?: EdgeHttpCallArgs,\n ): Promise<{ rootDocumentUrl: string }> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/root`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Invitations (space join)\n //\n\n public async joinSpaceByInvitation(\n ctx: Context,\n spaceId: SpaceId,\n body: JoinSpaceRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<JoinSpaceResponseBody> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/join`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Inbox (user-to-user notices)\n //\n\n /**\n * Leaves a notice in another identity's inbox; the sender is the identity this client authenticates as.\n * @param payload Opaque to EDGE; the recipient verifies it.\n */\n public async sendInboxMessage(\n ctx: Context,\n recipientDid: string,\n payload: string,\n args?: EdgeHttpCallArgs,\n ): Promise<InboxSendResponse> {\n const response = await this._call(ctx, new URL(`/inbox/${encodeURIComponent(recipientDid)}`, this.baseUrl), {\n ...args,\n body: { payload },\n method: 'POST',\n auth: true,\n });\n return Schema.decodeUnknownSync(InboxSendResponseSchema)(response);\n }\n\n /**\n * Lists the pending notices addressed to this client's identity.\n */\n public async listInbox(ctx: Context, args?: EdgeHttpCallArgs): Promise<InboxListResponse> {\n const response = await this._call(ctx, new URL('/inbox', this.baseUrl), { ...args, method: 'GET', auth: true });\n return Schema.decodeUnknownSync(InboxListResponseSchema)(response);\n }\n\n /**\n * Removes notices from this identity's inbox on every device.\n */\n public async ackInbox(ctx: Context, ids: readonly string[], args?: EdgeHttpCallArgs): Promise<void> {\n await this._call(ctx, new URL('/inbox/ack', this.baseUrl), {\n ...args,\n body: { ids },\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // OAuth\n //\n\n public async initiateOAuthFlow(\n ctx: Context,\n body: InitiateOAuthFlowRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<InitiateOAuthFlowResponse> {\n return this._call(ctx, new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST', auth: true });\n }\n\n public async completeOAuthRegistration(\n ctx: Context,\n body: CompleteOAuthRegistrationRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<CompleteOAuthRegistrationResponse> {\n return this._call(ctx, new URL('/oauth/registration/complete', this.baseUrl), { ...args, body, method: 'POST' });\n }\n\n /**\n * Resolves the live access token behind a `MANAGED_ACCESS_TOKEN` placeholder. Authorized by the\n * caller's presentation: EDGE serves it only to members of the owning space.\n */\n public async getAccessToken(\n ctx: Context,\n body: GetAccessTokenRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<GetAccessTokenResponseBody> {\n return this._call(ctx, new URL('/oauth/token', this.baseUrl), { ...args, body, method: 'POST', auth: true });\n }\n\n //\n // Queues\n //\n\n public async queryQueue(\n ctx: Context,\n subspaceTag: string,\n spaceId: SpaceId,\n query: FeedProtocol.FeedQuery,\n args?: EdgeHttpCallArgs,\n ): Promise<EdgeQueryQueueResponse> {\n const queueId = query.feedIds?.[0];\n invariant(queueId, 'queueId required');\n return this._call(\n ctx,\n createUrl(new URL(`/db/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {\n after: query.after,\n before: query.before,\n limit: query.limit,\n reverse: query.reverse,\n objectIds: query.objectIds?.join(','),\n }),\n { ...args, method: 'GET', auth: true },\n );\n }\n\n public async insertIntoQueue(\n ctx: Context,\n subspaceTag: string,\n spaceId: SpaceId,\n queueId: ObjectId,\n objects: unknown[],\n args?: EdgeHttpCallArgs,\n ): Promise<void> {\n return this._call(ctx, new URL(`/db/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {\n ...args,\n body: { objects },\n method: 'POST',\n auth: true,\n });\n }\n\n public async deleteFromQueue(\n ctx: Context,\n subspaceTag: string,\n spaceId: SpaceId,\n queueId: ObjectId,\n objectIds: ObjectId[],\n args?: EdgeHttpCallArgs,\n ): Promise<void> {\n return this._call(\n ctx,\n createUrl(new URL(`/db/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {\n ids: objectIds.join(','),\n }),\n { ...args, method: 'DELETE', auth: true },\n );\n }\n\n //\n // Blobs\n //\n\n /**\n * Builds the URL for the blob stored under `key`. `key` is URL-encoded for defense in depth —\n * callers pass a lowercase hex SHA-256 digest (extracted from an `ni:` URI by the edge backend).\n */\n public getBlobUrl(key: string): URL {\n return new URL(`/blob/file/${encodeURIComponent(key)}`, this.baseUrl);\n }\n\n /**\n * Uploads bytes to the edge blob service, keyed by content hash. Pre-fetches `/auth` (`auth:\n * true`) so large bodies aren't sent twice on an auth challenge.\n */\n public async putBlob(\n ctx: Context,\n key: string,\n data: Uint8Array,\n args?: EdgeHttpCallArgs & { contentType?: string },\n ): Promise<void> {\n const headers: Record<string, string> = {};\n if (args?.contentType) {\n headers['Content-Type'] = args.contentType;\n }\n await this._callRaw(ctx, this.getBlobUrl(key), {\n retry: args?.retry,\n auth: args?.auth ?? true,\n method: 'POST',\n // `Uint8Array` is generic over `ArrayBufferLike` (incl. `SharedArrayBuffer`) while DOM's\n // `BodyInit` only covers `ArrayBuffer`-backed views — a gap between the DOM lib types and\n // the TS standard lib, not fixable by typing `data` differently.\n body: data as BodyInit,\n headers,\n });\n }\n\n /**\n * Admits a completed direct upload into the content-addressed store, returning the key it landed\n * under along with what the service actually received.\n *\n * The bytes were PUT straight to a signed URL by a third party — typically an agent's `curl` —\n * so they never pass through this client; this call only tells the service to promote them. The\n * size and content type come from the service for the same reason.\n */\n public async finalizeBlobUpload(ctx: Context, uploadId: string, args?: EdgeHttpCallArgs): Promise<FinalizedUpload> {\n const url = new URL(`/blob/upload/${encodeURIComponent(uploadId)}/finalize`, this.baseUrl);\n const response = await this._callRaw(ctx, url, { ...args, method: 'POST', auth: args?.auth ?? true });\n return parseFinalizeResponse(await response.json());\n }\n\n /**\n * Downloads bytes previously stored with {@link putBlob}. Returns `undefined` if `key` is not\n * found.\n */\n public async getBlob(ctx: Context, key: string, args?: EdgeHttpCallArgs): Promise<Uint8Array | undefined> {\n const response = await this._callRaw(ctx, this.getBlobUrl(key), {\n ...args,\n method: 'GET',\n auth: args?.auth ?? true,\n });\n if (response.status === 404) {\n return undefined;\n }\n return new Uint8Array(await response.arrayBuffer());\n }\n\n /**\n * Checks whether bytes are stored under `key`, without downloading them.\n */\n public async hasBlob(ctx: Context, key: string, args?: EdgeHttpCallArgs): Promise<boolean> {\n const response = await this._callRaw(ctx, this.getBlobUrl(key), {\n ...args,\n method: 'HEAD',\n auth: args?.auth ?? true,\n });\n return response.status !== 404;\n }\n\n /**\n * Deletes bytes stored under `key`. Not called by any core `Blob.remove` path in v1 (deletion is\n * deferred), provided for completeness.\n */\n public async deleteBlob(ctx: Context, key: string, args?: EdgeHttpCallArgs): Promise<void> {\n await this._callRaw(ctx, this.getBlobUrl(key), { ...args, method: 'DELETE', auth: args?.auth ?? true });\n }\n\n //\n // Functions\n //\n\n public async uploadFunction(\n ctx: Context,\n pathParts: { functionId?: string },\n body: UploadFunctionRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<UploadFunctionResponseBody> {\n const formData = new FormData();\n formData.append('name', body.name ?? '');\n formData.append('version', body.version);\n // The function owner is the authenticated identity (edge requires ownerUri === presenter DID).\n // Prefer the connected identity's DID; otherwise use the DID supplied on the request body.\n const ownerUri = this._edgeIdentity?.identityDid ?? body.ownerUri;\n formData.append('ownerUri', ownerUri);\n formData.append('entryPoint', body.entryPoint);\n body.runtime && formData.append('runtime', body.runtime);\n for (const [filename, content] of Object.entries(body.assets)) {\n formData.append(\n 'assets',\n new Blob([content as Uint8Array<ArrayBuffer>], { type: getFileMimeType(filename) }),\n filename,\n );\n }\n const path = ['/compute/functions', ...(pathParts.functionId ? [pathParts.functionId] : [])].join('/');\n return this._call(ctx, new URL(path, this.baseUrl), {\n ...args,\n body: formData,\n method: 'PUT',\n json: false,\n auth: true,\n });\n }\n\n public async listFunctions(ctx: Context, args?: EdgeHttpCallArgs): Promise<any> {\n return this._call(ctx, new URL('/compute/functions', this.baseUrl), { ...args, method: 'GET', auth: true });\n }\n\n public async invokeFunction(\n ctx: Context,\n params: {\n functionId: string;\n version?: string;\n spaceId?: SpaceId;\n cpuTimeLimit?: number;\n subrequestsLimit?: number;\n },\n input: unknown,\n args?: EdgeHttpCallArgs,\n ): Promise<any> {\n const url = new URL(`/compute/functions/${params.functionId}`, this.baseUrl);\n if (params.version) {\n url.searchParams.set('version', params.version);\n }\n if (params.spaceId) {\n url.searchParams.set('spaceId', params.spaceId.toString());\n }\n if (params.cpuTimeLimit) {\n url.searchParams.set('cpuTimeLimit', params.cpuTimeLimit.toString());\n }\n if (params.subrequestsLimit) {\n url.searchParams.set('subrequestsLimit', params.subrequestsLimit.toString());\n }\n return this._call(ctx, url, { ...args, body: input, method: 'POST', auth: true });\n }\n\n //\n // Workflows\n //\n\n public async executeWorkflow(\n ctx: Context,\n spaceId: SpaceId,\n graphId: ObjectId,\n input: any,\n args?: EdgeHttpCallArgs,\n ): Promise<ExecuteWorkflowResponseBody> {\n return this._call(ctx, new URL(`/compute/workflows/${spaceId}/${graphId}`, this.baseUrl), {\n ...args,\n body: input,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Triggers\n //\n\n public async getCronTriggers(ctx: Context, spaceId: SpaceId): Promise<GetCronTriggersResponse> {\n return this._call<GetCronTriggersResponse>(\n ctx,\n new URL(`/compute/functions/${spaceId}/triggers/crons`, this.baseUrl),\n {\n method: 'GET',\n auth: true,\n },\n );\n }\n\n public async getTriggersDispatcherStatus(\n ctx: Context,\n spaceId: SpaceId,\n args?: EdgeHttpCallArgs,\n ): Promise<TriggersDispatcherStatus> {\n return this._call<TriggersDispatcherStatus>(ctx, new URL(`/compute/triggers/${spaceId}/status`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n public async forceRunCronTrigger(ctx: Context, spaceId: SpaceId, triggerId: ObjectId) {\n return this._call(ctx, new URL(`/compute/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {\n method: 'POST',\n auth: true,\n });\n }\n\n /**\n * Cancels the current run of a cron trigger on the EDGE dispatcher — its in-flight execution and\n * `runAgain` continuation chain. The trigger stays enabled, so its schedule keeps firing.\n */\n public async cancelTriggerRun(ctx: Context, spaceId: SpaceId, triggerId: ObjectId) {\n return this._call(ctx, new URL(`/compute/functions/${spaceId}/triggers/crons/${triggerId}/cancel`, this.baseUrl), {\n method: 'POST',\n auth: true,\n });\n }\n\n /**\n * Returns the full list of triggers registered on a space's EDGE dispatcher, with per-trigger\n * runtime status. Polled by the remote trigger monitor to surface edge trigger state.\n *\n * TODO(edge): Proposed endpoint; not yet implemented server-side.\n */\n public async getSpaceTriggers(\n ctx: Context,\n spaceId: SpaceId,\n args?: EdgeHttpCallArgs,\n ): Promise<GetSpaceTriggersResponse> {\n return this._call<GetSpaceTriggersResponse>(ctx, new URL(`/compute/triggers/${spaceId}`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n //\n // Query\n //\n\n public async execQuery(\n ctx: Context,\n spaceId: SpaceId,\n body: QueryRequestProto,\n args?: EdgeHttpCallArgs,\n ): Promise<QueryResponseProto> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/exec-query`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n /** Heads of every document in the space as last indexed by EDGE. */\n public async getIndexerHeads(ctx: Context, spaceId: SpaceId, args?: EdgeHttpCallArgs): Promise<IndexerHeadsResponse> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/indexer-heads`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n //\n // Registry\n //\n\n public async getRegistryPlugins(ctx: Context, args?: EdgeHttpCallArgs): Promise<GetPluginsResponseBody> {\n return this._call(ctx, new URL('/registry/plugins', this.baseUrl), { ...args, method: 'GET' });\n }\n\n /**\n * Uploads a built plugin bundle to the registry's R2-backed hosting. Authenticated\n * with the caller's hub identity (verifiable presentation) — `setIdentity` must\n * have been called. Returns the canonical `moduleUrl` (the hosted `manifest.json`).\n */\n public async uploadPluginBundle(\n ctx: Context,\n request: UploadPluginBundleRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<{ moduleUrl: string }> {\n return this._call(ctx, new URL('/registry/upload', this.baseUrl), {\n body: request,\n method: 'POST',\n auth: true,\n ...args,\n });\n }\n\n /**\n * Your private plugins: releases uploaded with {@link uploadPrivatePluginBundle}, listed only to the\n * identity that published them. Same shape as {@link getRegistryPlugins}, so callers merge the two.\n */\n public async getPrivateRegistryPlugins(ctx: Context, args?: EdgeHttpCallArgs): Promise<GetPluginsResponseBody> {\n return this._call(ctx, new URL('/registry/private/plugins', this.baseUrl), { auth: true, ...args, method: 'GET' });\n }\n\n /**\n * Uploads a private plugin release, which is hosted like a public one but written to no AT Protocol\n * repo: the registry records it against the caller, authenticated by identity or by API key.\n */\n public async uploadPrivatePluginBundle(\n ctx: Context,\n request: UploadPluginBundleRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<{ moduleUrl: string }> {\n return this._call(ctx, new URL('/registry/private/upload', this.baseUrl), {\n body: request,\n method: 'POST',\n auth: true,\n ...args,\n });\n }\n\n //\n // API tokens\n //\n\n /**\n * Mints a personal API token bound to this client's identity, for a process that cannot sign as it\n * (a CLI in a sandbox, a CI job). Hub accepts only a verifiable presentation here, never a token.\n */\n public async createApiToken(\n ctx: Context,\n request: CreateApiTokenRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<CreateApiTokenResponse> {\n return this._call(ctx, new URL('/hub/api/api-tokens', this.baseUrl), {\n body: request,\n method: 'POST',\n auth: true,\n ...args,\n });\n }\n\n //\n // Proxy\n //\n\n /**\n * Fetch through the edge proxy for third-party REST APIs.\n * TEMPORARY: currently routes through legacy open proxy. See https://github.com/dxos/edge/pull/576.\n */\n public async proxyFetch(target: URL, init: RequestInit = {}): Promise<Response> {\n return proxyFetchLegacy(target, init, this._clientTag);\n }\n\n //\n // AI service.\n //\n\n /**\n * Issue an authenticated request to the EDGE AI route (`/ai/generate/<service>/*`), which proxies\n * to the AI service. Used as the backend HTTP client for the edge AI providers (see\n * {@link EdgeAiHttpClient}); `service` selects the upstream the proxy forwards to.\n *\n * Returns the raw `Response` so streaming bodies are forwarded unchanged to `@effect/ai`.\n * Requires an identity to have been set via {@link setIdentity}.\n */\n // TODO(mykola): Merge into `BaseHttpClient._call` once it can return a streaming/raw `Response`;\n // the auth/retry loop below duplicates the one in `_call`.\n public async aiRequest(service: EdgeAiService, request: Request): Promise<Response> {\n const incoming = new URL(request.url);\n const base = this.baseUrl.replace(/\\/$/, '');\n const target = new URL(`${base}/ai/generate/${service}${incoming.pathname}${incoming.search}`);\n\n const method = request.method;\n const body = method === 'GET' || method === 'HEAD' ? undefined : await request.arrayBuffer();\n\n let handledAuth = false;\n while (true) {\n if (!this._authHeader) {\n const authResponse = await fetch(new URL('/auth', this.baseUrl));\n if (authResponse.status === 401) {\n this._authHeader = await this._handleUnauthorized(authResponse);\n }\n }\n\n const headers = new Headers(request.headers);\n if (this._authHeader) {\n headers.set('Authorization', this._authHeader);\n }\n if (this._clientTag) {\n headers.set(EDGE_CLIENT_TAG_HEADER, this._clientTag);\n }\n\n // `redirect: 'error'` rather than the default 'follow': these headers carry the EDGE auth\n // credential and, on a BYOK request, the user's own provider key, and custom headers are not\n // guaranteed to be stripped on a cross-origin redirect.\n const response = await fetch(target, { method, headers, body, redirect: 'error', signal: request.signal });\n // Only retry edge auth when the 401 came from edge's own auth layer. Edge always sets\n // `WWW-Authenticate` on its own 401s; upstream-forwarded 401s (e.g. invalid BYOK rejected\n // by Anthropic) lack it and must be surfaced verbatim.\n if (response.status === 401 && response.headers.get('WWW-Authenticate') !== null && !handledAuth) {\n this._authHeader = await this._handleUnauthorized(response);\n handledAuth = true;\n continue;\n }\n\n return response;\n }\n }\n\n //\n // Internal (Effect-based, used by tests)\n //\n\n public async _fetch<T>(url: URL, _args: { method: string }): Promise<T> {\n return Function.pipe(\n HttpClient.execute(HttpClientRequest.make(_args.method as any)(url.toString())),\n withLogging,\n withRetryConfig,\n Effect.provide(Layer.provideMerge(FetchHttpClient.layer, HttpConfig.default)),\n Effect.withSpan('EdgeHttpClient'),\n EffectEx.runAndForwardErrors,\n ) as T;\n }\n\n //\n // Process control (see `ProcessProtocol`). The EDGE host runs `Process` instances — agents\n // first — that outlive the client; these are the routes that drive one.\n //\n\n /**\n * Spawns one of the EDGE host's built-in processes in `spaceId`. A process definition cannot cross\n * the wire, so the request names the process by its `Process.key`.\n */\n public async spawnProcess(\n ctx: Context,\n spaceId: SpaceId,\n body: ProcessProtocol.SpawnProcessRequest,\n ): Promise<ProcessProtocol.SpawnProcessResponse> {\n return this._call<ProcessProtocol.SpawnProcessResponse>(\n ctx,\n new URL(`/compute/processes/${spaceId}`, this.baseUrl),\n {\n body,\n method: 'POST',\n auth: true,\n },\n );\n }\n\n public async listProcesses(\n ctx: Context,\n spaceId: SpaceId,\n query?: ProcessProtocol.ListProcessesQuery,\n ): Promise<ProcessProtocol.ListProcessesResponse> {\n const url = new URL(`/compute/processes/${spaceId}`, this.baseUrl);\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n return this._call<ProcessProtocol.ListProcessesResponse>(ctx, url, {\n method: 'GET',\n auth: true,\n });\n }\n\n public async getProcess(ctx: Context, spaceId: SpaceId, pid: string): Promise<ProcessProtocol.ProcessInfo> {\n return this._call<ProcessProtocol.ProcessInfo>(\n ctx,\n new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}`, this.baseUrl),\n {\n method: 'GET',\n auth: true,\n },\n );\n }\n\n /**\n * Terminates the process and clears its durable storage on the host.\n *\n * `idempotencyKey` travels as a query parameter rather than a body: the route is a DELETE, and a\n * body there is not reliably forwarded.\n */\n public async terminateProcess(\n ctx: Context,\n spaceId: SpaceId,\n pid: string,\n options?: { idempotencyKey?: ProcessProtocol.IdempotencyKey },\n ): Promise<void> {\n const url = new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}`, this.baseUrl);\n if (options?.idempotencyKey !== undefined) {\n url.searchParams.set('idempotencyKey', options.idempotencyKey);\n }\n await this._call(ctx, url, {\n method: 'DELETE',\n auth: true,\n });\n }\n\n /** Submits an input already encoded via the process definition's input schema. */\n public async submitProcessInput(\n ctx: Context,\n spaceId: SpaceId,\n pid: string,\n body: ProcessProtocol.SubmitInputRequest,\n ): Promise<void> {\n await this._call(ctx, new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}/input`, this.baseUrl), {\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n /**\n * URL of the process's RPC endpoint. The surface is served as effect-rpc-over-HTTP, so callers\n * drive it with an `RpcClient` over this URL rather than through this client's JSON envelope;\n * {@link getAuthHeader} supplies the credential for those requests.\n */\n public processRpcUrl(spaceId: SpaceId, pid: string): URL {\n return new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}/rpc`, this.baseUrl);\n }\n\n /**\n * Reads the process's outputs and ephemeral trace at or after `cursor`, plus its state at read\n * time. Cursor-based so a client that reloads resumes an in-flight remote process where it left off.\n */\n public async readProcessEvents(\n ctx: Context,\n spaceId: SpaceId,\n pid: string,\n cursor: number,\n ): Promise<ProcessProtocol.ProcessEventsResponse> {\n const url = new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}/events`, this.baseUrl);\n url.searchParams.set('cursor', String(cursor));\n return this._call<ProcessProtocol.ProcessEventsResponse>(ctx, url, {\n method: 'GET',\n auth: true,\n });\n }\n}\n\nconst getFileMimeType = (filename: string) =>\n ['.js', '.mjs'].some((ext) => filename.endsWith(ext))\n ? 'application/javascript+module'\n : filename.endsWith('.wasm')\n ? 'application/wasm'\n : 'application/octet-stream';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-edge-http-client.mjs","names":[],"sources":["../../package.json","../../src/client-version.ts","../../src/auth-challenge.ts","../../src/errors.ts","../../src/http-client.ts","../../src/utils.ts","../../src/base-http-client.ts","../../src/edge-http-client.ts"],"sourcesContent":["{\n \"name\": \"@dxos/edge-client\",\n \"version\": \"0.14.0\",\n \"description\": \"EDGE Client\",\n \"homepage\": \"https://dxos.org\",\n \"bugs\": \"https://github.com/dxos/dxos/issues\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/dxos/dxos\"\n },\n \"license\": \"FSL-1.1-Apache-2.0\",\n \"author\": \"DXOS.org\",\n \"sideEffects\": false,\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"source\": \"./src/index.ts\",\n \"types\": \"./dist/types/src/index.d.ts\",\n \"import\": \"./dist/lib/index.mjs\"\n },\n \"./cors-proxy\": {\n \"source\": \"./src/cors-proxy.ts\",\n \"types\": \"./dist/types/src/cors-proxy.d.ts\",\n \"import\": \"./dist/lib/cors-proxy.mjs\"\n },\n \"./muxer\": {\n \"source\": \"./src/edge-ws-muxer.ts\",\n \"types\": \"./dist/types/src/edge-ws-muxer.d.ts\",\n \"import\": \"./dist/lib/edge-ws-muxer.mjs\"\n },\n \"./service\": {\n \"source\": \"./src/service/index.ts\",\n \"types\": \"./dist/types/src/service/index.d.ts\",\n \"import\": \"./dist/lib/service.mjs\"\n },\n \"./testing\": {\n \"source\": \"./src/testing/index.ts\",\n \"types\": \"./dist/types/src/testing/index.d.ts\",\n \"import\": \"./dist/lib/testing.mjs\"\n },\n \"./http\": {\n \"source\": \"./src/edge-http-client.ts\",\n \"types\": \"./dist/types/src/edge-http-client.d.ts\",\n \"import\": \"./dist/lib/edge-http-client.mjs\"\n }\n },\n \"types\": \"dist/types/src/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \"http\": [\n \"dist/types/src/edge-http-client.d.ts\"\n ]\n }\n },\n \"files\": [\n \"dist\",\n \"src\",\n \"README.md\"\n ],\n \"dependencies\": {\n \"@bufbuild/protobuf\": \"catalog:\",\n \"@dxos/async\": \"workspace:*\",\n \"@dxos/context\": \"workspace:*\",\n \"@dxos/credentials\": \"workspace:*\",\n \"@dxos/crypto\": \"workspace:*\",\n \"@dxos/effect\": \"workspace:*\",\n \"@dxos/errors\": \"workspace:*\",\n \"@dxos/invariant\": \"workspace:*\",\n \"@dxos/keyring\": \"workspace:*\",\n \"@dxos/keys\": \"workspace:*\",\n \"@dxos/log\": \"workspace:*\",\n \"@dxos/node-std\": \"workspace:*\",\n \"@dxos/protocols\": \"workspace:*\",\n \"@dxos/tracing\": \"workspace:*\",\n \"@dxos/util\": \"workspace:*\",\n \"isomorphic-ws\": \"catalog:\",\n \"ws\": \"catalog:\"\n },\n \"devDependencies\": {\n \"@dxos/test-utils\": \"workspace:*\"\n },\n \"peerDependencies\": {\n \"effect\": \"catalog:\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport {\n EDGE_CLIENT_TOO_OLD,\n EDGE_CLIENT_VERSION_HEADER,\n EDGE_CLIENT_VERSION_PROTOCOL_PREFIX,\n EdgeClientTooOldError,\n type EdgeFailure,\n} from '@dxos/protocols';\n\nimport { version } from '../package.json';\n\n/**\n * This SDK's version, which EDGE checks against the oldest it serves. It releases in lockstep with the rest of the\n * SDK, and Vite tree-shakes the import to this one field.\n */\nexport const CLIENT_SDK_VERSION: string = version;\n\n/** Headers advertising {@link CLIENT_SDK_VERSION} on a request of the EDGE connect flow (`/auth` and its fallback). */\nexport const clientVersionHeaders = (): Record<string, string> => ({\n [EDGE_CLIENT_VERSION_HEADER]: CLIENT_SDK_VERSION,\n});\n\n/** The same advertisement for the WebSocket upgrade, where a browser can set no header but the subprotocol list. */\nexport const clientVersionProtocol = (): string => `${EDGE_CLIENT_VERSION_PROTOCOL_PREFIX}${CLIENT_SDK_VERSION}`;\n\n/**\n * Throws {@link EdgeClientTooOldError} when `response` is EDGE's failure envelope refusing this SDK. Matched on the\n * envelope's `data.type`, as any WebSocket endpoint answers a plain GET with a bare 426 \"Upgrade Required\".\n */\nexport const assertClientSupported = async (response: Response): Promise<void> => {\n if (response.ok || !response.headers.get('Content-Type')?.startsWith('application/json')) {\n return;\n }\n const body: EdgeFailure | undefined = await response\n .clone()\n .json()\n .catch(() => undefined);\n if (body?.success === false && body.data?.type === EDGE_CLIENT_TOO_OLD) {\n throw new EdgeClientTooOldError(body, response.status);\n }\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { toBinary } from '@bufbuild/protobuf';\nimport { base64Decode } from '@bufbuild/protobuf/wire';\n\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { EdgeClientTooOldError } from '@dxos/protocols';\nimport { PresentationSchema } from '@dxos/protocols/buf/dxos/halo/credentials_pb';\n\nimport { assertClientSupported, clientVersionHeaders } from './client-version.ts';\nimport { type EdgeIdentity } from './edge-identity.ts';\n\n/**\n * The VerifiablePresentation challenge/response handshake, shared by the HTTP and WebSocket\n * clients. Kept apart from {@link EdgeIdentity}, which is only the identity contract: this module\n * owns the wire format (header parsing, `/auth` round trip) rather than who is authenticating.\n *\n * Two ways in, because a client must work against servers on either side of the change that made\n * `/auth` answer 200:\n * - {@link fetchAuthChallenge} asks `/auth` up front. Preferred — acquiring a challenge is a\n * successful operation.\n * - {@link handleAuthChallenge} reads the challenge off a 401 that already happened. The lazy\n * fallback, and the only option on servers whose `/auth` can just reject.\n */\n\nconst VP_SCHEME = 'VerifiablePresentation';\n\n/**\n * Pull the challenge out of a `WWW-Authenticate` value.\n *\n * `WWW-Authenticate` carries a comma-separated *list* of challenges (RFC 9110 §11.6.1), so the\n * VP challenge is not necessarily first — edge emits `Bearer realm=\"dxos\", VerifiablePresentation …`\n * whenever both auth methods are allowed. Values may be quoted or a bare token; base64's `/` and\n * `=` are not `tchar`, so a real nonce is always quoted, while the historical `challenge=TODO`\n * placeholder is not.\n *\n * An empty challenge (`challenge=\"\"`) is reported as absent rather than as the empty string. Edge\n * emits exactly that when its server keypair is unconfigured, and there is nothing a caller can\n * sign — treating it as present would route the request through the auth path only to fail on the\n * missing challenge, masking the original response.\n *\n * Only the VP challenge's own parameters are read. Two things would otherwise be mistaken for it:\n * a `Bearer realm=\"VerifiablePresentation challenge=…\"`, which declares Bearer alone, and a\n * `challenge` belonging to a later scheme when the VP challenge carries none. Signing either would\n * retry an unrelated 401, running a non-idempotent request twice.\n */\nexport const parseChallengeHeader = (header: string | null | undefined): string | undefined => {\n if (!header) {\n return undefined;\n }\n\n // Segment, rather than regex over the whole header: an auth-param value may contain a comma\n // inside quotes, so no single delimiter separates challenges.\n const segments = splitOutsideQuotes(header);\n let parameters: string[] | undefined;\n for (let index = 0; index < segments.length && parameters === undefined; index++) {\n const afterScheme = stripVpScheme(segments[index]);\n if (afterScheme !== undefined) {\n parameters = [afterScheme, ...segments.slice(index + 1)];\n }\n }\n if (parameters === undefined) {\n return undefined;\n }\n\n for (const parameter of parameters) {\n const match = /^\\s*challenge\\s*=\\s*(?:\"([^\"]*)\"|([^\\s,]*))/i.exec(parameter);\n if (match) {\n return (match[1] ?? match[2]) || undefined;\n }\n // A segment that is not an auth-param starts the next challenge, whose params are not ours.\n if (parameter.trim().length > 0 && !/^\\s*[!#$%&'*+\\-.^_`|~\\w]+\\s*=/.test(parameter)) {\n return undefined;\n }\n }\n return undefined;\n};\n\n/**\n * The segment's text after the VP auth-scheme token, or undefined if it does not start with it.\n * The scheme must be a whole token, so a longer scheme merely beginning with it does not match.\n */\nconst stripVpScheme = (segment: string): string | undefined => {\n const trimmed = segment.trimStart();\n if (!trimmed.toLowerCase().startsWith(VP_SCHEME.toLowerCase())) {\n return undefined;\n }\n const remainder = trimmed.slice(VP_SCHEME.length);\n return remainder === '' || remainder.startsWith(' ') || remainder.startsWith('\\t') ? remainder : undefined;\n};\n\n/** Split on commas that are not inside a quoted string. */\nconst splitOutsideQuotes = (header: string): string[] => {\n const segments: string[] = [];\n let current = '';\n let inQuotes = false;\n for (let index = 0; index < header.length; index++) {\n const character = header[index];\n if (inQuotes) {\n current += character;\n if (character === '\\\\' && index + 1 < header.length) {\n current += header[++index];\n } else if (character === '\"') {\n inQuotes = false;\n }\n continue;\n }\n if (character === '\"') {\n inQuotes = true;\n current += character;\n } else if (character === ',') {\n segments.push(current);\n current = '';\n } else {\n current += character;\n }\n }\n segments.push(current);\n return segments;\n};\n\n/**\n * Read the challenge from an `/auth` response.\n *\n * Two shapes are accepted so a client works against servers on either side of the change:\n * - `200` with `{ success: true, data: { challenge } }` — the current shape. Acquiring a challenge\n * is a successful operation, so it is reported as one.\n * - `401` with `WWW-Authenticate` — every protected resource, plus older servers whose `/auth`\n * could only issue a challenge by failing.\n */\nexport const readAuthChallenge = async (response: Response): Promise<string | undefined> => {\n const headerChallenge = parseChallengeHeader(response.headers.get('WWW-Authenticate'));\n if (headerChallenge) {\n return headerChallenge;\n }\n if (!response.ok) {\n return undefined;\n }\n try {\n const body = await response.clone().json();\n const challenge = body?.data?.challenge ?? body?.challenge;\n return typeof challenge === 'string' && challenge.length > 0 ? challenge : undefined;\n } catch (error) {\n log.verbose('auth challenge response was not JSON', { error });\n return undefined;\n }\n};\n\nexport type AuthChallengeInfo = {\n /** Base64 challenge nonce. */\n challenge: string;\n /**\n * Server-advertised challenge validity. Absent on servers predating the TTL contract — there the\n * only refresh signal is the eventual 401.\n */\n expiresInMs?: number;\n};\n\n/**\n * Fetch a challenge nonce from the edge `/auth` endpoint.\n *\n * This is the deliberate, up-front way to obtain a challenge. The alternative — firing a request\n * you expect to be rejected and reading the challenge off the 401 — logs an error in every browser\n * console and records a routine auth failure server-side on each client boot.\n *\n * Returns undefined if the endpoint is unreachable or answers in neither known shape; callers fall\n * back to the 401 path, which still works against every server. Throws {@link EdgeClientTooOldError} when EDGE refuses\n * this SDK, which no fallback can get past.\n */\nexport const fetchAuthChallengeInfo = async (baseHttpUrl: string | URL): Promise<AuthChallengeInfo | undefined> => {\n try {\n const response = await fetch(new URL('/auth', baseHttpUrl), { headers: clientVersionHeaders() });\n await assertClientSupported(response);\n const challenge = await readAuthChallenge(response);\n if (!challenge) {\n log.verbose('no challenge in /auth response', { status: response.status });\n return undefined;\n }\n let expiresInMs: number | undefined;\n try {\n const body = await response.clone().json();\n const advertised = body?.data?.expiresInMs;\n // Finite-positive only: `1e400` parses to `Infinity`, which would schedule a refresh at never.\n expiresInMs = Number.isFinite(advertised) && advertised > 0 ? advertised : undefined;\n } catch {\n // Header-shaped challenge (the 401 fallback) — no JSON body to read a TTL from.\n }\n return { challenge, expiresInMs };\n } catch (error) {\n if (error instanceof EdgeClientTooOldError) {\n throw error;\n }\n log.verbose('failed to fetch auth challenge', { error });\n return undefined;\n }\n};\n\n/** The challenge alone; see {@link fetchAuthChallengeInfo}. */\nexport const fetchAuthChallenge = async (baseHttpUrl: string | URL): Promise<string | undefined> =>\n (await fetchAuthChallengeInfo(baseHttpUrl))?.challenge;\n\n/**\n * Sign a base64 challenge, returning the encoded presentation.\n *\n * The bytes are returned unencoded because the two transports frame them differently: HTTP wraps\n * them in an `Authorization` header, the WebSocket path in a subprotocol token.\n */\nexport const presentCredentialsForChallenge = async (\n identity: EdgeIdentity,\n challenge: string,\n): Promise<Uint8Array> => {\n const presentation = await identity.presentCredentials({ challenge: base64Decode(challenge) });\n return toBinary(PresentationSchema, presentation);\n};\n\nexport type ChallengeAuthentication = {\n /** Encoded presentation bound to the fetched challenge. */\n presentation: Uint8Array;\n /** TTL advertised beside the challenge, when the server provides one. */\n expiresInMs?: number;\n};\n\n/**\n * Obtain a challenge from `/auth` and sign it.\n * Returns undefined when no challenge could be obtained, leaving the caller on the 401 fallback.\n */\nexport const authenticateViaChallengeEndpoint = async (\n baseHttpUrl: string | URL,\n identity: EdgeIdentity,\n): Promise<ChallengeAuthentication | undefined> => {\n const info = await fetchAuthChallengeInfo(baseHttpUrl);\n if (info === undefined) {\n return undefined;\n }\n return {\n presentation: await presentCredentialsForChallenge(identity, info.challenge),\n expiresInMs: info.expiresInMs,\n };\n};\n\n/**\n * Sign the challenge carried by a rejected response.\n * Retained for the lazy path: a request that 401s is retried with a presentation bound to the\n * challenge that rejection carried.\n */\nexport const handleAuthChallenge = async (failedResponse: Response, identity: EdgeIdentity): Promise<Uint8Array> => {\n const challenge = await readAuthChallenge(failedResponse);\n invariant(challenge !== undefined, 'No auth challenge in response.');\n return presentCredentialsForChallenge(identity, challenge);\n};\n","import { BaseError } from '@dxos/errors';\n\n//\n// Copyright 2024 DXOS.org\n//\n\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\n }\n}\n\n/** EDGE client operation failed. The underlying failure, where there is one, is the `cause`. */\nexport class EdgeClientError extends BaseError.extend('EdgeClientError', 'EDGE client operation failed.') {}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport type * as HttpClient from 'effect/http/HttpClient';\nimport type * as HttpClientError from 'effect/http/HttpClientError';\nimport type * as HttpClientResponse from 'effect/http/HttpClientResponse';\nimport * as Layer from 'effect/Layer';\nimport * as Schedule from 'effect/Schedule';\n\nimport { log } from '@dxos/log';\nimport { EdgeCredentialsHeaderCodec } from '@dxos/protocols';\n\nimport { EdgeClientError } from './errors.ts';\n\n// TODO(burdon): Factor out.\n\nexport type RetryOptions = {\n timeout: Duration.Duration;\n retryTimes: number;\n retryBaseDelay: Duration.Duration;\n};\n\n// Layer pattern.\nexport class HttpConfig extends Context.Service<HttpConfig, RetryOptions>()('HttpConfig') {\n static default = Layer.succeed(HttpConfig, {\n timeout: Duration.millis(1_000),\n retryTimes: 3,\n retryBaseDelay: Duration.millis(1_000),\n });\n}\n\n// HOC pattern.\nexport const withRetry = (\n effect: Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError, HttpClient.HttpClient>,\n {\n timeout = Duration.millis(1_000),\n retryBaseDelay = Duration.millis(1_000),\n retryTimes = 3,\n }: Partial<RetryOptions> = {},\n) => {\n return effect.pipe(\n Effect.flatMap((res): Effect.Effect<unknown, EdgeClientError | HttpClientError.HttpClientError> =>\n // Treat 500 errors as retryable?\n res.status === 500 ? Effect.fail(new EdgeClientError({ message: res.status.toString() })) : res.json,\n ),\n Effect.timeout(timeout),\n Effect.retry({\n schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),\n times: retryTimes,\n }),\n );\n};\n\nexport const withRetryConfig = (\n effect: Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError, HttpClient.HttpClient>,\n) =>\n Effect.gen(function* () {\n const config = yield* HttpConfig;\n return yield* withRetry(effect, config);\n });\n\nexport const withLogging = <A extends HttpClientResponse.HttpClientResponse, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n // v4's `tap` requires an Effect; a bare side effect has to be lifted.\n Effect.tap((res) =>\n Effect.sync(() => {\n log.info('response', { status: res.status });\n }),\n ),\n );\n\n/**\n * Frame an encoded verifiable presentation as an `Authorization` header value.\n * Kept as a named export for existing callers; the wire format itself lives in\n * {@link EdgeCredentialsHeaderCodec} so the client and the Edge worker share one definition.\n */\nexport const encodeAuthHeader = (presentation: Uint8Array): string => EdgeCredentialsHeaderCodec.encode(presentation);\n","//\n// Copyright 2024 DXOS.org\n//\n\nexport const getEdgeUrlWithProtocol = (baseUrl: string, protocol: 'http' | 'ws') => {\n const isSecure = baseUrl.startsWith('https') || baseUrl.startsWith('wss');\n const url = new URL(baseUrl);\n url.protocol = protocol + (isSecure ? 's' : '');\n return url.toString();\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { sleep } from '@dxos/async';\nimport { Context, TRACE_SPAN_ATTRIBUTE, type TraceContextData } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport {\n EDGE_CLIENT_TAG_HEADER,\n EDGE_CLIENT_TOO_OLD,\n EdgeAuthChallengeError,\n EdgeCallFailedError,\n EdgeClientTooOldError,\n type EdgeFailure,\n} from '@dxos/protocols';\n\nimport { authenticateViaChallengeEndpoint, handleAuthChallenge, parseChallengeHeader } from './auth-challenge.ts';\nimport { type EdgeIdentity } from './edge-identity.ts';\nimport { encodeAuthHeader } from './http-client.ts';\nimport { getEdgeUrlWithProtocol } from './utils.ts';\n\nconst DEFAULT_RETRY_TIMEOUT = 1500;\nconst DEFAULT_RETRY_JITTER = 500;\nconst DEFAULT_MAX_RETRIES_COUNT = 3;\nconst WARNING_BODY_SIZE = 10 * 1024 * 1024; // 10MB\n\nexport type RetryConfig = {\n /** Number of retries, not counting the initial request. */\n count: number;\n /** Delay before retries in ms. */\n timeout?: number;\n /** Random additional delay to spread retries. */\n jitter?: number;\n};\n\nexport type EdgeHttpCallArgs = {\n retry?: RetryConfig;\n /**\n * Force authentication by pre-fetching `/auth` to obtain the challenge before\n * sending the body. Use for requests with large bodies to avoid sending twice.\n * Not available on HubHttpClient (hub-service has no `/auth` endpoint).\n */\n auth?: boolean;\n};\n\nexport type BaseHttpClientOptions = {\n /**\n * Tag included in the {@link EDGE_CLIENT_TAG_HEADER} header on every request.\n * Used on Edge to classify traffic for metering (e.g. `ci-e2e`).\n */\n clientTag?: string;\n /**\n * Admin API key, sent as `Authorization: Bearer` on every request in place of the identity\n * verifiable-presentation flow — for headless callers (CI) that hold no HALO identity.\n */\n apiKey?: string;\n};\n\ntype HttpRequestArgs = {\n method: string;\n retry?: RetryConfig;\n body?: any;\n /** @default true */\n json?: boolean;\n auth?: boolean;\n};\n\nexport type RawHttpRequestArgs = {\n method: string;\n body?: BodyInit;\n headers?: Record<string, string>;\n retry?: RetryConfig;\n auth?: boolean;\n};\n\nexport abstract class BaseHttpClient {\n protected readonly _baseUrl: string;\n protected readonly _clientTag: string | undefined;\n protected readonly _apiKey: string | undefined;\n protected _edgeIdentity: EdgeIdentity | undefined;\n /** Auth header cached until it goes stale (see `_authHeaderIsStale`) or a 401 replaces it. */\n protected _authHeader: string | undefined;\n /** When the current `_authHeader` was signed; with the TTL below it decides proactive refresh. */\n private _authAcquiredAt: number | undefined;\n /** Last TTL `/auth` advertised — a server constant, so 401-minted headers reuse it. */\n private _authTtlMs: number | undefined;\n /** Single-flight guard so concurrent requests with a stale header share one `/auth` round trip. */\n private _authPrefetch: Promise<void> | undefined;\n\n constructor(baseUrl: string, options?: BaseHttpClientOptions) {\n // Slash-terminated: `new URL('account/me', '…/hub')` would otherwise drop the `/hub` prefix.\n const url = getEdgeUrlWithProtocol(baseUrl, 'http');\n this._baseUrl = url.endsWith('/') ? url : `${url}/`;\n this._clientTag = options?.clientTag;\n this._apiKey = options?.apiKey;\n log('created', { url: this._baseUrl });\n }\n\n get baseUrl() {\n return this._baseUrl;\n }\n\n /**\n * The `Authorization` header this client would send on an authenticated call, minting one first\n * if none is cached or the cached one has gone stale.\n *\n * For callers that have to make an EDGE request outside this client — the native OAuth flow\n * issues `/oauth/initiate` from Rust, the only place the `Origin` header can be set to the\n * loopback callback server. Resolves undefined when there is nothing to present (no identity, or\n * the challenge round trip failed), which EDGE endpoints that `skipAuth` accept.\n */\n async getAuthHeader(): Promise<string | undefined> {\n if (this._apiKey) {\n return undefined;\n }\n const identity = this._edgeIdentity;\n if (!this._authHeader || this._authHeaderIsStale()) {\n await this._prefetchAuthHeader();\n }\n // `setIdentity` may have swapped identities while the prefetch was in flight, so the cached\n // header now belongs to the new one — handing it back would sign the caller's request, made on\n // behalf of the old identity, as somebody else.\n if (this._edgeIdentity !== identity) {\n return undefined;\n }\n return this._authHeader;\n }\n\n setIdentity(identity: EdgeIdentity): void {\n if (this._edgeIdentity?.identityDid !== identity.identityDid || this._edgeIdentity?.peerKey !== identity.peerKey) {\n this._edgeIdentity = identity;\n this._authHeader = undefined;\n this._authAcquiredAt = undefined;\n // Drop any in-flight prefetch: it authenticates the previous identity, and awaiting it\n // would commit that identity's header for requests now belonging to the new one.\n this._authPrefetch = undefined;\n }\n }\n\n protected async _call<T>(ctx: Context, url: URL, args: HttpRequestArgs): Promise<T> {\n const shouldRetry = createRetryHandler(args);\n // Log presence/size only — never log raw body contents which may contain PII.\n log('fetch', {\n url,\n hasBody: args.body !== undefined,\n bodySize: typeof args.body === 'string' ? args.body.length : undefined,\n });\n\n const traceHeaders = getTraceHeaders(ctx);\n\n let handledAuth = false;\n const tryCount = 1;\n while (true) {\n let processingError: EdgeCallFailedError | undefined = undefined;\n try {\n if (args.auth && !this._apiKey && (!this._authHeader || this._authHeaderIsStale())) {\n await this._prefetchAuthHeader();\n }\n\n const request = createRequest(args, this._authHeader, traceHeaders, this._clientTag, this._apiKey);\n log('call', { url, tryCount, authHeader: !!this._authHeader });\n const response = await fetch(url, request);\n\n if (response.ok) {\n const contentType = response.headers.get('Content-Type') ?? '';\n // No-content responses (204, empty body, non-JSON) — return undefined.\n if (\n response.status === 204 ||\n response.headers.get('Content-Length') === '0' ||\n !contentType.includes('application/json')\n ) {\n return undefined as T;\n }\n const body = await response.clone().json();\n if (typeof body !== 'object' || body === null) {\n return body;\n }\n if (!('success' in body)) {\n return body;\n }\n if (body.success) {\n return body.data;\n }\n } else if (response.status === 401 && hasVpChallenge(response) && !handledAuth) {\n // Only retry edge auth when the 401 came from edge's own auth layer. Edge always sets a\n // VP challenge on its own 401s; upstream-forwarded 401s carry none, or carry an\n // unrelated scheme.\n this._authHeader = await this._handleUnauthorized(response);\n handledAuth = true;\n continue;\n }\n\n const contentType = response.headers.get('Content-Type') ?? '';\n const body: EdgeFailure = contentType.startsWith('application/json')\n ? await response.clone().json()\n : undefined;\n\n invariant(!body?.success, 'Expected body to not be a failure response or undefined.');\n\n if (body?.data?.type === 'auth_challenge' && typeof body?.data?.challenge === 'string') {\n processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);\n } else if (body?.success === false && body.data?.type === EDGE_CLIENT_TOO_OLD) {\n processingError = new EdgeClientTooOldError(body, response.status);\n } else if (body?.success === false) {\n processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);\n } else {\n invariant(!response.ok, 'Expected response to not be ok.');\n processingError = await EdgeCallFailedError.fromHttpFailure(response);\n }\n } catch (error: any) {\n // A thrown EdgeCallFailedError already carries its retry semantics (e.g. the terminal\n // rejected-api-key 401) — wrapping it as a processing failure would mark it retryable.\n processingError =\n error instanceof EdgeCallFailedError ? error : EdgeCallFailedError.fromProcessingFailureCause(error);\n }\n\n if (processingError?.isRetryable && (await shouldRetry(ctx, processingError.retryAfterMs))) {\n log.verbose('retrying request', { url, processingError });\n } else {\n throw processingError!;\n }\n }\n }\n\n /**\n * Like {@link _call} but returns the raw `Response` instead of parsing a JSON envelope — for\n * endpoints with binary or absent response bodies (e.g. blob storage). A 404 is returned to the\n * caller rather than thrown, since \"not found\" is an expected outcome for lookups; all other\n * non-ok, non-retryable statuses throw `EdgeCallFailedError`, mirroring `_call`.\n *\n * NOTE: Duplicates `_call`'s auth/retry loop rather than sharing it, to avoid touching `_call`'s\n * broadly-depended-on JSON-envelope behavior. `EdgeHttpClient.aiRequest`'s separate\n * duplicate loop is a follow-up candidate for consolidating onto this method.\n */\n protected async _callRaw(ctx: Context, url: URL, args: RawHttpRequestArgs): Promise<Response> {\n const shouldRetry = createRetryHandler(args);\n log('fetch', { url, hasBody: args.body !== undefined });\n\n const traceHeaders = getTraceHeaders(ctx);\n\n let handledAuth = false;\n while (true) {\n let processingError: EdgeCallFailedError | undefined;\n try {\n if (args.auth && !this._apiKey && (!this._authHeader || this._authHeaderIsStale())) {\n await this._prefetchAuthHeader();\n }\n\n const headers: Record<string, string> = { ...args.headers };\n if (this._authHeader) {\n headers['Authorization'] = this._authHeader;\n } else if (this._apiKey) {\n // Canonical edgeAuth admin-key form; never collides with the VP header, since the\n // api-key path skips the auth flow that would populate it.\n headers['Authorization'] = `Bearer ${this._apiKey}`;\n }\n if (traceHeaders) {\n Object.assign(headers, traceHeaders);\n }\n if (this._clientTag) {\n headers[EDGE_CLIENT_TAG_HEADER] = this._clientTag;\n }\n\n const response = await fetch(url, { method: args.method, body: args.body, headers });\n\n if (response.ok || response.status === 404) {\n return response;\n }\n\n if (response.status === 401 && hasVpChallenge(response) && !handledAuth) {\n this._authHeader = await this._handleUnauthorized(response);\n handledAuth = true;\n continue;\n }\n\n processingError = await EdgeCallFailedError.fromHttpFailure(response);\n } catch (error: any) {\n // A thrown EdgeCallFailedError already carries its retry semantics (e.g. the terminal\n // rejected-api-key 401) — wrapping it as a processing failure would mark it retryable.\n processingError =\n error instanceof EdgeCallFailedError ? error : EdgeCallFailedError.fromProcessingFailureCause(error);\n }\n\n if (processingError?.isRetryable && (await shouldRetry(ctx, processingError.retryAfterMs))) {\n log.verbose('retrying raw request', { url, processingError });\n } else {\n throw processingError!;\n }\n }\n }\n\n /**\n * Acquire an auth header up front by asking `/auth` for a challenge.\n *\n * Best-effort: a failure here leaves `_authHeader` unset and the request proceeds\n * unauthenticated, falling back to the 401-and-retry path below. That fallback is what keeps\n * this working against servers whose `/auth` only issues a challenge by rejecting.\n *\n * The `catch` is what makes that true for *signing* failures, not just network ones:\n * `fetchAuthChallengeInfo` swallows its own fetch errors, but `presentCredentials` throws on a\n * device with no HALO chain (mid-invitation), and without this the rejection would surface from\n * `_call` as a failed request — turning a call that used to succeed unauthenticated into an error.\n */\n protected _prefetchAuthHeader(): Promise<void> {\n if (this._authPrefetch) {\n return this._authPrefetch;\n }\n const prefetch: Promise<void> = this._prefetchAuthHeaderOnce()\n .catch((err) => {\n // Unauthenticated would only be refused again, and silently; the caller has to see it.\n if (err instanceof EdgeClientTooOldError) {\n throw err;\n }\n log.verbose('auth prefetch failed; proceeding unauthenticated', { err });\n })\n .finally(() => {\n // Only if this promise still owns the guard: `setIdentity` clears it mid-flight, so a\n // newer prefetch may already have claimed it, and clearing that one would let concurrent\n // callers each start their own `/auth` round trip and credential presentation.\n if (this._authPrefetch === prefetch) {\n this._authPrefetch = undefined;\n }\n });\n this._authPrefetch = prefetch;\n return prefetch;\n }\n\n private async _prefetchAuthHeaderOnce(): Promise<void> {\n const identity = this._edgeIdentity;\n if (!identity) {\n log.verbose('auth prefetch skipped: no identity set');\n return;\n }\n const authentication = await authenticateViaChallengeEndpoint(this._baseUrl, identity);\n // `setIdentity` may have swapped identities while the challenge round trip was in flight;\n // committing then would send the new identity's requests signed as the old one.\n if (authentication && this._edgeIdentity === identity) {\n this._authHeader = encodeAuthHeader(authentication.presentation);\n this._recordAuthAcquired(authentication.expiresInMs);\n }\n }\n\n /** Stale means past the proactive-refresh point, not necessarily rejected yet. */\n private _authHeaderIsStale(): boolean {\n if (this._authAcquiredAt === undefined || this._authTtlMs === undefined) {\n return false;\n }\n // Refresh a margin ahead of expiry, floored at half the TTL so tiny windows still refresh.\n const refreshAfterMs = Math.max(this._authTtlMs - AUTH_REFRESH_MARGIN_MS, Math.floor(this._authTtlMs / 2));\n return Date.now() - this._authAcquiredAt >= refreshAfterMs;\n }\n\n /**\n * Record when the current header was signed and the TTL advertised beside its challenge — only\n * originals; the refresh point is derived at read time. Without an advertised TTL (older\n * servers, the 401-minted path) the last known one is reused; if none was ever advertised the\n * header lives until a 401, exactly as before proactive refresh existed.\n */\n private _recordAuthAcquired(expiresInMs: number | undefined): void {\n this._authAcquiredAt = Date.now();\n this._authTtlMs = expiresInMs ?? this._authTtlMs;\n }\n\n protected async _handleUnauthorized(response: Response): Promise<string> {\n // A rejected API key is terminal — there is no challenge an identityless caller could answer.\n if (this._apiKey) {\n throw await EdgeCallFailedError.fromHttpFailure(response);\n }\n if (!this._edgeIdentity) {\n log.warn('unauthorized response received before identity was set');\n throw await EdgeCallFailedError.fromHttpFailure(response);\n }\n const challenge = await handleAuthChallenge(response, this._edgeIdentity);\n // The fresh header starts a new window; the 401 body carries no TTL, so the last one applies.\n this._recordAuthAcquired(undefined);\n return encodeAuthHeader(challenge);\n }\n}\n\n/** Refresh the cached auth header this long before its advertised expiry, absorbing request latency and clock skew. */\nconst AUTH_REFRESH_MARGIN_MS = 30_000;\n\n/**\n * Whether a response carries a VerifiablePresentation challenge we can actually answer.\n *\n * Header *presence* is not enough: a 401 forwarded from upstream may carry an unrelated scheme\n * (`Bearer realm=\"…\"`), and edge itself emits `challenge=\"\"` when its server keypair is\n * unconfigured. Neither yields something signable, and retrying those through the auth path would\n * fail on the missing challenge and mask the real error.\n */\nconst hasVpChallenge = (response: Response): boolean =>\n parseChallengeHeader(response.headers.get('WWW-Authenticate')) !== undefined;\n\nconst createRequest = (\n { method, body, json = true }: HttpRequestArgs,\n authHeader: string | undefined,\n traceHeaders?: Record<string, string>,\n clientTag?: string,\n apiKey?: string,\n): RequestInit => {\n let requestBody: BodyInit | undefined;\n const headers: HeadersInit = {};\n\n if (json) {\n requestBody = body === undefined ? undefined : JSON.stringify(body);\n headers['Content-Type'] = 'application/json';\n } else {\n requestBody = body;\n }\n\n if (typeof requestBody === 'string' && requestBody.length > WARNING_BODY_SIZE) {\n log.warn('Request with large body', { bodySize: requestBody.length });\n }\n\n if (authHeader) {\n headers['Authorization'] = authHeader;\n } else if (apiKey) {\n // Canonical edgeAuth admin-key form; never collides with the VP header, since the\n // api-key path skips the auth flow that would populate it.\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (traceHeaders) {\n Object.assign(headers, traceHeaders);\n }\n\n if (clientTag) {\n headers[EDGE_CLIENT_TAG_HEADER] = clientTag;\n }\n\n return { method, body: requestBody, headers };\n};\n\nconst getTraceHeaders = (ctx: Context): Record<string, string> | undefined => {\n const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE) as TraceContextData | undefined;\n if (!traceCtx) {\n return undefined;\n }\n const headers: Record<string, string> = { traceparent: traceCtx.traceparent };\n if (traceCtx.tracestate) {\n headers.tracestate = traceCtx.tracestate;\n }\n return headers;\n};\n\n/** @deprecated */\nconst createRetryHandler = ({ retry }: HttpRequestArgs) => {\n if (!retry || retry.count < 1) {\n return async () => false;\n }\n let retries = 0;\n const maxRetries = retry.count ?? DEFAULT_MAX_RETRIES_COUNT;\n const baseTimeout = retry.timeout ?? DEFAULT_RETRY_TIMEOUT;\n const jitter = retry.jitter ?? DEFAULT_RETRY_JITTER;\n return async (ctx: Context, retryAfter?: number) => {\n if (++retries > maxRetries || ctx.disposed) {\n return false;\n }\n if (retryAfter) {\n await sleep(retryAfter);\n } else {\n const timeout = baseTimeout + Math.random() * jitter;\n await sleep(timeout);\n }\n return true;\n };\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport * as EffectContext from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as FetchHttpClient from 'effect/http/FetchHttpClient';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport * as HttpClientRequest from 'effect/http/HttpClientRequest';\nimport * as Layer from 'effect/Layer';\nimport * as Schema from 'effect/Schema';\n\nimport { type Context } from '@dxos/context';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { invariant } from '@dxos/invariant';\nimport { type SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { type ProcessProtocol } from '@dxos/protocols';\nimport {\n type CompleteOAuthRegistrationRequest,\n type CompleteOAuthRegistrationResponse,\n type CreateAgentRequestBody,\n type CreateAgentResponseBody,\n EDGE_CLIENT_TAG_HEADER,\n type EdgeStatus,\n type ExecuteWorkflowResponseBody,\n type FeedProtocol,\n type GetAccessTokenRequest,\n type GetAccessTokenResponseBody,\n type GetAgentStatusResponseBody,\n type GetNotarizationResponseBody,\n type GetPluginsResponseBody,\n type InboxListResponse,\n InboxListResponseSchema,\n type InboxSendResponse,\n InboxSendResponseSchema,\n type IndexerHeadsResponse,\n type InitiateOAuthFlowRequest,\n type InitiateOAuthFlowResponse,\n type JoinSpaceRequest,\n type JoinSpaceResponseBody,\n type ObjectId,\n type PostNotarizationRequestBody,\n type RecoverIdentityRequest,\n type RecoverIdentityResponseBody,\n type SerializedError,\n type UploadFunctionRequest,\n type UploadFunctionResponseBody,\n} from '@dxos/protocols';\nimport {\n type QueryRequest as QueryRequestProto,\n type QueryResponse as QueryResponseProto,\n} from '@dxos/protocols/buf/dxos/echo/query_pb';\nimport { createUrl } from '@dxos/util';\n\nimport { BaseHttpClient, type BaseHttpClientOptions, type EdgeHttpCallArgs } from './base-http-client.ts';\nimport { proxyFetchLegacy } from './cors-proxy.ts';\nimport { HttpConfig, withLogging, withRetryConfig } from './http-client.ts';\n\nexport type { EdgeHttpCallArgs, RetryConfig } from './base-http-client.ts';\n\n/** Arguments for {@link EdgeHttpClient.request}; authenticates unless `auth` is `false`. */\nexport type EdgeRequestArgs = EdgeHttpCallArgs & {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** JSON-serializable request body. */\n body?: unknown;\n};\n\n/**\n * HTTP wire shape returned by `/queue/.../query`.\n */\nexport type EdgeQueryQueueResponse = {\n objects?: unknown[];\n nextCursor?: string;\n prevCursor?: string;\n};\n\nexport type UploadPluginBundleRequest = {\n slug: string;\n version: string;\n files: { path: string; content: string }[];\n};\n\nexport type CreateApiTokenRequest = {\n label: string;\n /** Epoch milliseconds; the token never expires when omitted. */\n expiresAt?: number;\n};\n\n/** A minted token: `token` is the secret, returned once; the rest is its non-secret summary. */\nexport type CreateApiTokenResponse = {\n token: string;\n id: string;\n prefix: string;\n label: string;\n expiresAt?: number | null;\n};\n\nexport type TriggersDispatcherStatus = {\n isActive: boolean;\n nextCronTaskRunTimestamp?: number;\n registeredTriggers: string[];\n stopAfterTimestamp?: number;\n remainingMs?: number;\n nextAlarmTimestamp?: number;\n};\n\nexport type GetCronTriggersResponse = {\n cronIds: string[];\n};\n\n/**\n * Per-trigger runtime status reported by the EDGE dispatcher, keyed to the\n * trigger's ECHO object id so a client can correlate it with the replicated\n * `Trigger` object in its local database.\n *\n * Served by compute-service at `GET /compute/triggers/{spaceId}` (see\n * {@link EdgeHttpClient.getSpaceTriggers}).\n */\nexport type EdgeTriggerStatus = {\n /** ECHO object id of the trigger. */\n triggerId: ObjectId;\n /** Whether the EDGE dispatcher currently has this trigger registered. */\n registered: boolean;\n kind: 'timer' | 'subscription' | 'email' | 'webhook' | 'feed' | 'direct';\n /** Next scheduled cron execution (epoch ms). Set only for `timer` triggers. */\n nextExecutionTimestamp?: number;\n /** Cooldown expiry after a failure (epoch ms). */\n cooldownUntilTimestamp?: number;\n /** Outcome of the most recent invocation on the edge. */\n lastResult?: {\n status: 'success' | 'failure';\n /** Completion time (epoch ms). */\n timestamp: number;\n error?: SerializedError;\n };\n};\n\n/**\n * Response of `GET /compute/triggers/{spaceId}`: the full list of triggers registered on a space's\n * EDGE dispatcher, with runtime status. Polled by the remote trigger monitor to surface edge\n * trigger state.\n */\nexport type GetSpaceTriggersResponse = {\n /** Whether the space's edge dispatcher is active. */\n isActive: boolean;\n triggers: EdgeTriggerStatus[];\n};\n\nexport type EdgeHttpClientOptions = BaseHttpClientOptions;\n\n/** What `finalize` reports back: the content-addressed key the bytes landed under, and their size. */\nexport type FinalizedUpload = { key: string; size: number; contentType?: string };\n\n/**\n * Validates the finalize response rather than asserting its shape.\n *\n * The body is untrusted network input, and both fields are load-bearing downstream: `key` is fed\n * to `fromDigestHex`, whose parser does not reject malformed hex and would silently mint a bogus\n * `ni:` URI, and `size` is recorded on the Blob object as the authoritative byte count. A cast\n * would let either through.\n */\nconst parseFinalizeResponse = (body: unknown): FinalizedUpload => {\n if (typeof body !== 'object' || body === null) {\n throw new Error('Blob upload finalize returned a non-object body.');\n }\n const { key, size, contentType } = body as Record<string, unknown>;\n // 64 lowercase hex characters: the SHA-256 digest this store is keyed by, and nothing else.\n if (typeof key !== 'string' || !/^[0-9a-f]{64}$/.test(key)) {\n throw new Error('Blob upload finalize returned no valid content key.');\n }\n if (typeof size !== 'number' || !Number.isInteger(size) || size < 0) {\n throw new Error('Blob upload finalize returned no valid size.');\n }\n if (contentType !== undefined && typeof contentType !== 'string') {\n throw new Error('Blob upload finalize returned a non-string content type.');\n }\n return { key, size, ...(contentType === undefined ? {} : { contentType }) };\n};\n\nexport class EdgeHttpClientService extends EffectContext.Service<EdgeHttpClientService, EdgeHttpClient>()(\n '@dxos/edge-client/EdgeHttpClient',\n) {}\n\n/**\n * HTTP client for the edge worker API (spaces, queues, functions, agents, etc.).\n *\n * Hub-service API (accounts, invitations) lives in {@link HubHttpClient} — the two\n * services run at different URLs and are never both available from the same base URL.\n */\n/** Upstream service the EDGE AI proxy forwards to; selects the `/ai/generate/<service>` route. */\nexport type EdgeAiService =\n | 'anthropic'\n | 'deepseek'\n | 'typesafe'\n /** TypeSafe's System One wire, answered by Workers AI's `typesafe/jev` on EDGE's Cloudflare account. */\n | 'workers-ai/typesafe';\n\nexport class EdgeHttpClient extends BaseHttpClient {\n constructor(baseUrl: string, options?: EdgeHttpClientOptions) {\n super(baseUrl, options);\n log('created', { url: this.baseUrl });\n }\n\n //\n // Status\n //\n\n public async getStatus(ctx: Context, args?: EdgeHttpCallArgs): Promise<EdgeStatus> {\n return this._call(ctx, new URL('/status', this.baseUrl), { ...args, method: 'GET', auth: true });\n }\n\n //\n // Generic\n //\n\n /**\n * Authenticated JSON call to an EDGE route that has no dedicated method here (e.g. a service a\n * plugin owns end to end). Resolves to the response envelope's unwrapped `data`, left `unknown`\n * so the caller decodes it against its own schema.\n */\n public async request(ctx: Context, path: string, args: EdgeRequestArgs): Promise<unknown> {\n const { method, body, ...rest } = args;\n return this._call<unknown>(ctx, new URL(path, this.baseUrl), { auth: true, ...rest, method, body });\n }\n\n //\n // Agents\n //\n\n public createAgent(\n ctx: Context,\n body: CreateAgentRequestBody,\n args?: EdgeHttpCallArgs,\n ): Promise<CreateAgentResponseBody> {\n return this._call(ctx, new URL('/identity/agents/create', this.baseUrl), {\n ...args,\n method: 'POST',\n body,\n auth: true,\n });\n }\n\n public getAgentStatus(\n ctx: Context,\n request: { ownerIdentityDid: string },\n args?: EdgeHttpCallArgs,\n ): Promise<GetAgentStatusResponseBody> {\n return this._call(ctx, new URL(`/identity/users/${request.ownerIdentityDid}/agent/status`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n //\n // Credentials\n //\n\n public getCredentialsForNotarization(\n ctx: Context,\n spaceId: SpaceId,\n args?: EdgeHttpCallArgs,\n ): Promise<GetNotarizationResponseBody> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/notarization`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n public async notarizeCredentials(\n ctx: Context,\n spaceId: SpaceId,\n body: PostNotarizationRequestBody,\n args?: EdgeHttpCallArgs,\n ): Promise<void> {\n await this._call(ctx, new URL(`/db/spaces/${spaceId}/notarization`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Identity\n //\n\n public async recoverIdentity(\n ctx: Context,\n body: RecoverIdentityRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<RecoverIdentityResponseBody> {\n return this._call(ctx, new URL('/db/identity/recover', this.baseUrl), { ...args, body, method: 'POST' });\n }\n\n /**\n * Names the space's root document, which edge cannot derive, and returns the root in force —\n * not necessarily the one offered, since the record is write-once.\n */\n public async recordSpaceRoot(\n ctx: Context,\n spaceId: SpaceId,\n body: { rootDocumentUrl: string },\n args?: EdgeHttpCallArgs,\n ): Promise<{ rootDocumentUrl: string }> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/root`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Invitations (space join)\n //\n\n public async joinSpaceByInvitation(\n ctx: Context,\n spaceId: SpaceId,\n body: JoinSpaceRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<JoinSpaceResponseBody> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/join`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Inbox (user-to-user notices)\n //\n\n /**\n * Leaves a notice in another identity's inbox; the sender is the identity this client authenticates as.\n * @param payload Opaque to EDGE; the recipient verifies it.\n */\n public async sendInboxMessage(\n ctx: Context,\n recipientDid: string,\n payload: string,\n args?: EdgeHttpCallArgs,\n ): Promise<InboxSendResponse> {\n const response = await this._call(ctx, new URL(`/inbox/${encodeURIComponent(recipientDid)}`, this.baseUrl), {\n ...args,\n body: { payload },\n method: 'POST',\n auth: true,\n });\n return Schema.decodeUnknownSync(InboxSendResponseSchema)(response);\n }\n\n /**\n * Lists the pending notices addressed to this client's identity.\n */\n public async listInbox(ctx: Context, args?: EdgeHttpCallArgs): Promise<InboxListResponse> {\n const response = await this._call(ctx, new URL('/inbox', this.baseUrl), { ...args, method: 'GET', auth: true });\n return Schema.decodeUnknownSync(InboxListResponseSchema)(response);\n }\n\n /**\n * Removes notices from this identity's inbox on every device.\n */\n public async ackInbox(ctx: Context, ids: readonly string[], args?: EdgeHttpCallArgs): Promise<void> {\n await this._call(ctx, new URL('/inbox/ack', this.baseUrl), {\n ...args,\n body: { ids },\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // OAuth\n //\n\n public async initiateOAuthFlow(\n ctx: Context,\n body: InitiateOAuthFlowRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<InitiateOAuthFlowResponse> {\n return this._call(ctx, new URL('/oauth/initiate', this.baseUrl), { ...args, body, method: 'POST', auth: true });\n }\n\n public async completeOAuthRegistration(\n ctx: Context,\n body: CompleteOAuthRegistrationRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<CompleteOAuthRegistrationResponse> {\n return this._call(ctx, new URL('/oauth/registration/complete', this.baseUrl), { ...args, body, method: 'POST' });\n }\n\n /**\n * Resolves the live access token behind a `MANAGED_ACCESS_TOKEN` placeholder. Authorized by the\n * caller's presentation: EDGE serves it only to members of the owning space.\n */\n public async getAccessToken(\n ctx: Context,\n body: GetAccessTokenRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<GetAccessTokenResponseBody> {\n return this._call(ctx, new URL('/oauth/token', this.baseUrl), { ...args, body, method: 'POST', auth: true });\n }\n\n //\n // Queues\n //\n\n public async queryQueue(\n ctx: Context,\n subspaceTag: string,\n spaceId: SpaceId,\n query: FeedProtocol.FeedQuery,\n args?: EdgeHttpCallArgs,\n ): Promise<EdgeQueryQueueResponse> {\n const queueId = query.feedIds?.[0];\n invariant(queueId, 'queueId required');\n return this._call(\n ctx,\n createUrl(new URL(`/db/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {\n after: query.after,\n before: query.before,\n limit: query.limit,\n reverse: query.reverse,\n objectIds: query.objectIds?.join(','),\n }),\n { ...args, method: 'GET', auth: true },\n );\n }\n\n public async insertIntoQueue(\n ctx: Context,\n subspaceTag: string,\n spaceId: SpaceId,\n queueId: ObjectId,\n objects: unknown[],\n args?: EdgeHttpCallArgs,\n ): Promise<void> {\n return this._call(ctx, new URL(`/db/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {\n ...args,\n body: { objects },\n method: 'POST',\n auth: true,\n });\n }\n\n public async deleteFromQueue(\n ctx: Context,\n subspaceTag: string,\n spaceId: SpaceId,\n queueId: ObjectId,\n objectIds: ObjectId[],\n args?: EdgeHttpCallArgs,\n ): Promise<void> {\n return this._call(\n ctx,\n createUrl(new URL(`/db/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {\n ids: objectIds.join(','),\n }),\n { ...args, method: 'DELETE', auth: true },\n );\n }\n\n //\n // Blobs\n //\n\n /**\n * Builds the URL for the blob stored under `key`. `key` is URL-encoded for defense in depth —\n * callers pass a lowercase hex SHA-256 digest (extracted from an `ni:` URI by the edge backend).\n */\n public getBlobUrl(key: string): URL {\n return new URL(`/blob/file/${encodeURIComponent(key)}`, this.baseUrl);\n }\n\n /**\n * Uploads bytes to the edge blob service, keyed by content hash. Pre-fetches `/auth` (`auth:\n * true`) so large bodies aren't sent twice on an auth challenge.\n */\n public async putBlob(\n ctx: Context,\n key: string,\n data: Uint8Array,\n args?: EdgeHttpCallArgs & { contentType?: string },\n ): Promise<void> {\n const headers: Record<string, string> = {};\n if (args?.contentType) {\n headers['Content-Type'] = args.contentType;\n }\n await this._callRaw(ctx, this.getBlobUrl(key), {\n retry: args?.retry,\n auth: args?.auth ?? true,\n method: 'POST',\n // `Uint8Array` is generic over `ArrayBufferLike` (incl. `SharedArrayBuffer`) while DOM's\n // `BodyInit` only covers `ArrayBuffer`-backed views — a gap between the DOM lib types and\n // the TS standard lib, not fixable by typing `data` differently.\n body: data as BodyInit,\n headers,\n });\n }\n\n /**\n * Admits a completed direct upload into the content-addressed store, returning the key it landed\n * under along with what the service actually received.\n *\n * The bytes were PUT straight to a signed URL by a third party — typically an agent's `curl` —\n * so they never pass through this client; this call only tells the service to promote them. The\n * size and content type come from the service for the same reason.\n */\n public async finalizeBlobUpload(ctx: Context, uploadId: string, args?: EdgeHttpCallArgs): Promise<FinalizedUpload> {\n const url = new URL(`/blob/upload/${encodeURIComponent(uploadId)}/finalize`, this.baseUrl);\n const response = await this._callRaw(ctx, url, { ...args, method: 'POST', auth: args?.auth ?? true });\n return parseFinalizeResponse(await response.json());\n }\n\n /**\n * Downloads bytes previously stored with {@link putBlob}. Returns `undefined` if `key` is not\n * found.\n */\n public async getBlob(ctx: Context, key: string, args?: EdgeHttpCallArgs): Promise<Uint8Array | undefined> {\n const response = await this._callRaw(ctx, this.getBlobUrl(key), {\n ...args,\n method: 'GET',\n auth: args?.auth ?? true,\n });\n if (response.status === 404) {\n return undefined;\n }\n return new Uint8Array(await response.arrayBuffer());\n }\n\n /**\n * Checks whether bytes are stored under `key`, without downloading them.\n */\n public async hasBlob(ctx: Context, key: string, args?: EdgeHttpCallArgs): Promise<boolean> {\n const response = await this._callRaw(ctx, this.getBlobUrl(key), {\n ...args,\n method: 'HEAD',\n auth: args?.auth ?? true,\n });\n return response.status !== 404;\n }\n\n /**\n * Deletes bytes stored under `key`. Not called by any core `Blob.remove` path in v1 (deletion is\n * deferred), provided for completeness.\n */\n public async deleteBlob(ctx: Context, key: string, args?: EdgeHttpCallArgs): Promise<void> {\n await this._callRaw(ctx, this.getBlobUrl(key), { ...args, method: 'DELETE', auth: args?.auth ?? true });\n }\n\n //\n // Functions\n //\n\n public async uploadFunction(\n ctx: Context,\n pathParts: { functionId?: string },\n body: UploadFunctionRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<UploadFunctionResponseBody> {\n const formData = new FormData();\n formData.append('name', body.name ?? '');\n formData.append('version', body.version);\n // The function owner is the authenticated identity (edge requires ownerUri === presenter DID).\n // Prefer the connected identity's DID; otherwise use the DID supplied on the request body.\n const ownerUri = this._edgeIdentity?.identityDid ?? body.ownerUri;\n formData.append('ownerUri', ownerUri);\n formData.append('entryPoint', body.entryPoint);\n body.runtime && formData.append('runtime', body.runtime);\n for (const [filename, content] of Object.entries(body.assets)) {\n formData.append(\n 'assets',\n new Blob([content as Uint8Array<ArrayBuffer>], { type: getFileMimeType(filename) }),\n filename,\n );\n }\n const path = ['/compute/functions', ...(pathParts.functionId ? [pathParts.functionId] : [])].join('/');\n return this._call(ctx, new URL(path, this.baseUrl), {\n ...args,\n body: formData,\n method: 'PUT',\n json: false,\n auth: true,\n });\n }\n\n public async listFunctions(ctx: Context, args?: EdgeHttpCallArgs): Promise<any> {\n return this._call(ctx, new URL('/compute/functions', this.baseUrl), { ...args, method: 'GET', auth: true });\n }\n\n public async invokeFunction(\n ctx: Context,\n params: {\n functionId: string;\n version?: string;\n spaceId?: SpaceId;\n cpuTimeLimit?: number;\n subrequestsLimit?: number;\n },\n input: unknown,\n args?: EdgeHttpCallArgs,\n ): Promise<any> {\n const url = new URL(`/compute/functions/${params.functionId}`, this.baseUrl);\n if (params.version) {\n url.searchParams.set('version', params.version);\n }\n if (params.spaceId) {\n url.searchParams.set('spaceId', params.spaceId.toString());\n }\n if (params.cpuTimeLimit) {\n url.searchParams.set('cpuTimeLimit', params.cpuTimeLimit.toString());\n }\n if (params.subrequestsLimit) {\n url.searchParams.set('subrequestsLimit', params.subrequestsLimit.toString());\n }\n return this._call(ctx, url, { ...args, body: input, method: 'POST', auth: true });\n }\n\n //\n // Workflows\n //\n\n public async executeWorkflow(\n ctx: Context,\n spaceId: SpaceId,\n graphId: ObjectId,\n input: any,\n args?: EdgeHttpCallArgs,\n ): Promise<ExecuteWorkflowResponseBody> {\n return this._call(ctx, new URL(`/compute/workflows/${spaceId}/${graphId}`, this.baseUrl), {\n ...args,\n body: input,\n method: 'POST',\n auth: true,\n });\n }\n\n //\n // Triggers\n //\n\n public async getCronTriggers(ctx: Context, spaceId: SpaceId): Promise<GetCronTriggersResponse> {\n return this._call<GetCronTriggersResponse>(\n ctx,\n new URL(`/compute/functions/${spaceId}/triggers/crons`, this.baseUrl),\n {\n method: 'GET',\n auth: true,\n },\n );\n }\n\n public async getTriggersDispatcherStatus(\n ctx: Context,\n spaceId: SpaceId,\n args?: EdgeHttpCallArgs,\n ): Promise<TriggersDispatcherStatus> {\n return this._call<TriggersDispatcherStatus>(ctx, new URL(`/compute/triggers/${spaceId}/status`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n public async forceRunCronTrigger(ctx: Context, spaceId: SpaceId, triggerId: ObjectId) {\n return this._call(ctx, new URL(`/compute/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {\n method: 'POST',\n auth: true,\n });\n }\n\n /**\n * Cancels the current run of a cron trigger on the EDGE dispatcher — its in-flight execution and\n * `runAgain` continuation chain. The trigger stays enabled, so its schedule keeps firing.\n */\n public async cancelTriggerRun(ctx: Context, spaceId: SpaceId, triggerId: ObjectId) {\n return this._call(ctx, new URL(`/compute/functions/${spaceId}/triggers/crons/${triggerId}/cancel`, this.baseUrl), {\n method: 'POST',\n auth: true,\n });\n }\n\n /**\n * Returns the full list of triggers registered on a space's EDGE dispatcher, with per-trigger\n * runtime status. Polled by the remote trigger monitor to surface edge trigger state.\n *\n * TODO(edge): Proposed endpoint; not yet implemented server-side.\n */\n public async getSpaceTriggers(\n ctx: Context,\n spaceId: SpaceId,\n args?: EdgeHttpCallArgs,\n ): Promise<GetSpaceTriggersResponse> {\n return this._call<GetSpaceTriggersResponse>(ctx, new URL(`/compute/triggers/${spaceId}`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n //\n // Query\n //\n\n public async execQuery(\n ctx: Context,\n spaceId: SpaceId,\n body: QueryRequestProto,\n args?: EdgeHttpCallArgs,\n ): Promise<QueryResponseProto> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/exec-query`, this.baseUrl), {\n ...args,\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n /** Heads of every document in the space as last indexed by EDGE. */\n public async getIndexerHeads(ctx: Context, spaceId: SpaceId, args?: EdgeHttpCallArgs): Promise<IndexerHeadsResponse> {\n return this._call(ctx, new URL(`/db/spaces/${spaceId}/indexer-heads`, this.baseUrl), {\n ...args,\n method: 'GET',\n auth: true,\n });\n }\n\n //\n // Registry\n //\n\n public async getRegistryPlugins(ctx: Context, args?: EdgeHttpCallArgs): Promise<GetPluginsResponseBody> {\n return this._call(ctx, new URL('/registry/plugins', this.baseUrl), { ...args, method: 'GET' });\n }\n\n /**\n * Uploads a built plugin bundle to the registry's R2-backed hosting. Authenticated\n * with the caller's hub identity (verifiable presentation) — `setIdentity` must\n * have been called. Returns the canonical `moduleUrl` (the hosted `manifest.json`).\n */\n public async uploadPluginBundle(\n ctx: Context,\n request: UploadPluginBundleRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<{ moduleUrl: string }> {\n return this._call(ctx, new URL('/registry/upload', this.baseUrl), {\n body: request,\n method: 'POST',\n auth: true,\n ...args,\n });\n }\n\n /**\n * Your private plugins: releases uploaded with {@link uploadPrivatePluginBundle}, listed only to the\n * identity that published them. Same shape as {@link getRegistryPlugins}, so callers merge the two.\n */\n public async getPrivateRegistryPlugins(ctx: Context, args?: EdgeHttpCallArgs): Promise<GetPluginsResponseBody> {\n return this._call(ctx, new URL('/registry/private/plugins', this.baseUrl), { auth: true, ...args, method: 'GET' });\n }\n\n /**\n * Uploads a private plugin release, which is hosted like a public one but written to no AT Protocol\n * repo: the registry records it against the caller, authenticated by identity or by API key.\n */\n public async uploadPrivatePluginBundle(\n ctx: Context,\n request: UploadPluginBundleRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<{ moduleUrl: string }> {\n return this._call(ctx, new URL('/registry/private/upload', this.baseUrl), {\n body: request,\n method: 'POST',\n auth: true,\n ...args,\n });\n }\n\n //\n // API tokens\n //\n\n /**\n * Mints a personal API token bound to this client's identity, for a process that cannot sign as it\n * (a CLI in a sandbox, a CI job). Hub accepts only a verifiable presentation here, never a token.\n */\n public async createApiToken(\n ctx: Context,\n request: CreateApiTokenRequest,\n args?: EdgeHttpCallArgs,\n ): Promise<CreateApiTokenResponse> {\n return this._call(ctx, new URL('/hub/api/api-tokens', this.baseUrl), {\n body: request,\n method: 'POST',\n auth: true,\n ...args,\n });\n }\n\n //\n // Proxy\n //\n\n /**\n * Fetch through the edge proxy for third-party REST APIs.\n * TEMPORARY: currently routes through legacy open proxy. See https://github.com/dxos/edge/pull/576.\n */\n public async proxyFetch(target: URL, init: RequestInit = {}): Promise<Response> {\n return proxyFetchLegacy(target, init, this._clientTag);\n }\n\n //\n // AI service.\n //\n\n /**\n * Issue an authenticated request to the EDGE AI route (`/ai/generate/<service>/*`), which proxies\n * to the AI service. Used as the backend HTTP client for the edge AI providers (see\n * {@link EdgeAiHttpClient}); `service` selects the upstream the proxy forwards to.\n *\n * Returns the raw `Response` so streaming bodies are forwarded unchanged to `@effect/ai`.\n * Requires an identity to have been set via {@link setIdentity}.\n */\n // TODO(mykola): Merge into `BaseHttpClient._call` once it can return a streaming/raw `Response`;\n // the auth/retry loop below duplicates the one in `_call`.\n public async aiRequest(service: EdgeAiService, request: Request): Promise<Response> {\n const incoming = new URL(request.url);\n const base = this.baseUrl.replace(/\\/$/, '');\n const target = new URL(`${base}/ai/generate/${service}${incoming.pathname}${incoming.search}`);\n\n const method = request.method;\n const body = method === 'GET' || method === 'HEAD' ? undefined : await request.arrayBuffer();\n\n let handledAuth = false;\n while (true) {\n if (!this._authHeader) {\n await this._prefetchAuthHeader();\n }\n\n const headers = new Headers(request.headers);\n if (this._authHeader) {\n headers.set('Authorization', this._authHeader);\n }\n if (this._clientTag) {\n headers.set(EDGE_CLIENT_TAG_HEADER, this._clientTag);\n }\n\n // `redirect: 'error'` rather than the default 'follow': these headers carry the EDGE auth\n // credential and, on a BYOK request, the user's own provider key, and custom headers are not\n // guaranteed to be stripped on a cross-origin redirect.\n const response = await fetch(target, { method, headers, body, redirect: 'error', signal: request.signal });\n // Only retry edge auth when the 401 came from edge's own auth layer. Edge always sets\n // `WWW-Authenticate` on its own 401s; upstream-forwarded 401s (e.g. invalid BYOK rejected\n // by Anthropic) lack it and must be surfaced verbatim.\n if (response.status === 401 && response.headers.get('WWW-Authenticate') !== null && !handledAuth) {\n this._authHeader = await this._handleUnauthorized(response);\n handledAuth = true;\n continue;\n }\n\n return response;\n }\n }\n\n //\n // Internal (Effect-based, used by tests)\n //\n\n public async _fetch<T>(url: URL, _args: { method: string }): Promise<T> {\n return Function.pipe(\n HttpClient.execute(HttpClientRequest.make(_args.method as any)(url.toString())),\n withLogging,\n withRetryConfig,\n Effect.provide(Layer.provideMerge(FetchHttpClient.layer, HttpConfig.default)),\n Effect.withSpan('EdgeHttpClient'),\n EffectEx.runAndForwardErrors,\n ) as T;\n }\n\n //\n // Process control (see `ProcessProtocol`). The EDGE host runs `Process` instances — agents\n // first — that outlive the client; these are the routes that drive one.\n //\n\n /**\n * Spawns one of the EDGE host's built-in processes in `spaceId`. A process definition cannot cross\n * the wire, so the request names the process by its `Process.key`.\n */\n public async spawnProcess(\n ctx: Context,\n spaceId: SpaceId,\n body: ProcessProtocol.SpawnProcessRequest,\n ): Promise<ProcessProtocol.SpawnProcessResponse> {\n return this._call<ProcessProtocol.SpawnProcessResponse>(\n ctx,\n new URL(`/compute/processes/${spaceId}`, this.baseUrl),\n {\n body,\n method: 'POST',\n auth: true,\n },\n );\n }\n\n public async listProcesses(\n ctx: Context,\n spaceId: SpaceId,\n query?: ProcessProtocol.ListProcessesQuery,\n ): Promise<ProcessProtocol.ListProcessesResponse> {\n const url = new URL(`/compute/processes/${spaceId}`, this.baseUrl);\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n return this._call<ProcessProtocol.ListProcessesResponse>(ctx, url, {\n method: 'GET',\n auth: true,\n });\n }\n\n public async getProcess(ctx: Context, spaceId: SpaceId, pid: string): Promise<ProcessProtocol.ProcessInfo> {\n return this._call<ProcessProtocol.ProcessInfo>(\n ctx,\n new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}`, this.baseUrl),\n {\n method: 'GET',\n auth: true,\n },\n );\n }\n\n /**\n * Terminates the process and clears its durable storage on the host.\n *\n * `idempotencyKey` travels as a query parameter rather than a body: the route is a DELETE, and a\n * body there is not reliably forwarded.\n */\n public async terminateProcess(\n ctx: Context,\n spaceId: SpaceId,\n pid: string,\n options?: { idempotencyKey?: ProcessProtocol.IdempotencyKey },\n ): Promise<void> {\n const url = new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}`, this.baseUrl);\n if (options?.idempotencyKey !== undefined) {\n url.searchParams.set('idempotencyKey', options.idempotencyKey);\n }\n await this._call(ctx, url, {\n method: 'DELETE',\n auth: true,\n });\n }\n\n /** Submits an input already encoded via the process definition's input schema. */\n public async submitProcessInput(\n ctx: Context,\n spaceId: SpaceId,\n pid: string,\n body: ProcessProtocol.SubmitInputRequest,\n ): Promise<void> {\n await this._call(ctx, new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}/input`, this.baseUrl), {\n body,\n method: 'POST',\n auth: true,\n });\n }\n\n /**\n * URL of the process's RPC endpoint. The surface is served as effect-rpc-over-HTTP, so callers\n * drive it with an `RpcClient` over this URL rather than through this client's JSON envelope;\n * {@link getAuthHeader} supplies the credential for those requests.\n */\n public processRpcUrl(spaceId: SpaceId, pid: string): URL {\n return new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}/rpc`, this.baseUrl);\n }\n\n /**\n * Reads the process's outputs and ephemeral trace at or after `cursor`, plus its state at read\n * time. Cursor-based so a client that reloads resumes an in-flight remote process where it left off.\n */\n public async readProcessEvents(\n ctx: Context,\n spaceId: SpaceId,\n pid: string,\n cursor: number,\n ): Promise<ProcessProtocol.ProcessEventsResponse> {\n const url = new URL(`/compute/processes/${spaceId}/${encodeURIComponent(pid)}/events`, this.baseUrl);\n url.searchParams.set('cursor', String(cursor));\n return this._call<ProcessProtocol.ProcessEventsResponse>(ctx, url, {\n method: 'GET',\n auth: true,\n });\n }\n}\n\nconst getFileMimeType = (filename: string) =>\n ['.js', '.mjs'].some((ext) => filename.endsWith(ext))\n ? 'application/javascript+module'\n : filename.endsWith('.wasm')\n ? 'application/wasm'\n : 'application/octet-stream';\n"],"mappings":"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"}
|