@finalai/final-agent-provider 0.4.1 → 0.4.3

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.
@@ -17,7 +17,7 @@ const daytona = createDaytonaSandboxProvider({ credential: daytonaCredential, ne
17
17
 
18
18
  The factories are exported from `/sandboxes/egress`, `/sandboxes/computer`, and
19
19
  `@finalai/daytona-agent-provider/sandboxes/daytona`, respectively. Keep the trusted
20
- organization credential reader in host composition. A custom provider accepts
20
+ Device credential callback in host composition. A custom provider accepts
21
21
  the same generic extension: call `enabled`, validate its workspace identity, call
22
22
  `prepare` before provider fencing, and use `attachSandboxNetworking` after
23
23
  obtaining its raw handle. Supply the saved policy, restore/fence callbacks,
@@ -25,6 +25,17 @@ namespace existence check and deferred setup. Physical teardown calls `suspend`
25
25
  even if another cleanup step fails. Providers needing earlier namespace setup can
26
26
  use `controller` and `withSandboxNetworking` directly, as Final Computer does.
27
27
 
28
+ Device credential callbacks receive the organization and `actingUserId`. Device
29
+ operations use the current actor; background lease reconciliation may use the stored
30
+ lease owner instead of a present turn author. Hosts must authorize both forms of work.
31
+ Admission, policy persistence, and cleanup also make trusted service calls with
32
+ `actingUserId: null`. Enabling workspace Device networking requires a credential
33
+ accepted by those service endpoints for that case, separate from user-bound grants.
34
+ Returning `null` refuses the call before HTTP; a user-token-only configuration cannot
35
+ start or clean up networked workspaces. Its host must add service authentication
36
+ before enabling this extension. The SDK never reuses a user's cached grant for a
37
+ service call, another user, organization, or API origin.
38
+
28
39
  The extension owns Device authorization, enrollment, helper installation, leases
29
40
  and failover. Providers need no Device or Headscale implementation. Its generic
30
41
  controller also authorizes external editor access and reports its expiry limit;
@@ -40,7 +51,7 @@ an older API rejecting the field cannot silently grant longer access.
40
51
  3. Construct the manager with the raw session and privileged executor, then declare its policy on the provider handle with its binding access and current-actor reader.
41
52
  4. Detach on runtime shutdown. On physical stop/deletion, attempt fencing, lease cleanup and provider cleanup independently; retain saved desired settings for recreation.
42
53
 
43
- Inject the Device API URL, trusted organization credential reader, stable workspace ID, current acting user, and the raw Linux sandbox session. Pass that unguarded session into `createWorkspaceEgressManager`, then use `withWorkspaceEgress` to declare its execution policy and lifecycle hooks on the returned handle. `withWorkspaceEgress` constructs a checked handle through the generic SDK constructor, so direct callers are safe too. `defineSandboxProvider` automatically adds any host admission policy on start and resume without reinstalling the Device policy. Using a guarded session for privileged controller execution recursively deadlocks. `/sandboxes/egress-bootstrap` exports the exact controller source for custom privileged executors and manual acceptance. Custom executors run `python3 -I -c source source` with JSON on stdin, or execute a root-owned file providing `__file__`; the source argument permits persistence for later controller launches.
54
+ Inject the Device API URL, trusted Device credential callback, stable workspace ID, current acting user, and the raw Linux sandbox session. Pass that unguarded session into `createWorkspaceEgressManager`, then use `withWorkspaceEgress` to declare its execution policy and lifecycle hooks on the returned handle. `withWorkspaceEgress` constructs a checked handle through the generic SDK constructor, so direct callers are safe too. `defineSandboxProvider` automatically adds any host admission policy on start and resume without reinstalling the Device policy. Using a guarded session for privileged controller execution recursively deadlocks. `/sandboxes/egress-bootstrap` exports the exact controller source for custom privileged executors and manual acceptance. Custom executors run `python3 -I -c source source` with JSON on stdin, or execute a root-owned file providing `__file__`; the source argument permits persistence for later controller launches.
44
55
 
45
56
  The host owns the logical workspace identity and durable networking registration. Each identity must name one physical workspace at a time; do not reuse it for unrelated resources or derive authorization from opaque provider state. Editor contexts supply the same `workspaceId` as start/resume. When `networking` is configured, legacy editor states also require that host identity so the predicate can determine management before granting access. Managed editor startup is guarded. Missing enrolled workspace nodes automatically recover using the approved desired configuration.
46
57
 
@@ -12,6 +12,7 @@ export interface DeviceApiConfigIface {
12
12
  url: string;
13
13
  credential: (args: {
14
14
  organizationId: string;
15
+ actingUserId: string | null;
15
16
  }) => Promise<string | null>;
16
17
  credentialTtlMs?: number;
17
18
  fetch?: DeviceApiFetchIface;
@@ -15,7 +15,7 @@ import { jsonObjectSchema, jsonValueSchema } from "@finalai/agent-provider";
15
15
  import { SandboxEntryType } from "@finalai/agent-provider/sandbox";
16
16
  import * as z from "zod";
17
17
  import { decodeBase64, encodeBase64 } from "./bytes.mjs";
18
- import { DEVICE_API_ACTING_USER_HEADER, DEVICE_API_KEY_SECRET } from "./environment.mjs";
18
+ import { DEVICE_API_ACTING_USER_HEADER } from "./environment.mjs";
19
19
  import { DEVICE_API_ACTING_USER_HEADER as DEVICE_API_ACTING_USER_HEADER2 } from "./environment.mjs";
20
20
  var DeviceApiLeaseAccess = /* @__PURE__ */ ((DeviceApiLeaseAccess2) => {
21
21
  DeviceApiLeaseAccess2["Read"] = "read";
@@ -109,9 +109,9 @@ var DeviceApiClientErrorCode = /* @__PURE__ */ ((DeviceApiClientErrorCode2) => {
109
109
  return DeviceApiClientErrorCode2;
110
110
  })(DeviceApiClientErrorCode || {});
111
111
  const apiError = (status, code, message) => Object.assign(new Error(`Device API ${status} ${code}: ${message}`), { status, code });
112
- const credentialMissing = () => Object.assign(
112
+ const credentialMissing = (actingUserId) => Object.assign(
113
113
  new Error(
114
- `this organization has no Device API key for the device sandbox: create an organization API key and store it as the organization secret ${DEVICE_API_KEY_SECRET}`
114
+ `Device API access is unavailable ${actingUserId == null ? "for workspace networking (no acting user)" : "for this acting user"}: configure the credential callback`
115
115
  ),
116
116
  { status: null, code: "credential_missing" /* CredentialMissing */ }
117
117
  );
@@ -150,15 +150,19 @@ const createDeviceApiClient = (config) => {
150
150
  const body = errorBodySchema.safeParse(parseJson(text));
151
151
  return body.success ? apiError(response.status, body.data.error.code, body.data.error.message) : apiError(response.status, "http", text.slice(0, 200) || response.statusText);
152
152
  };
153
- const organizationKey = async (args) => {
154
- const { organizationId, reread, api } = args;
155
- const cacheKey = api.url + "\0" + organizationId;
153
+ const credentialFor = async (args) => {
154
+ const { organizationId, actingUserId, reread, api } = args;
155
+ const cacheKey = JSON.stringify([api.url, organizationId, actingUserId]);
156
+ const currentTime = now();
157
+ for (const [key2, entry] of keys) {
158
+ if (entry.expiresAtMs <= currentTime) keys.delete(key2);
159
+ }
156
160
  const cached = keys.get(cacheKey);
157
- if (cached != null && !reread && cached.expiresAtMs > now()) return cached.key;
158
- const key = await api.credential({ organizationId });
161
+ if (cached != null && !reread) return cached.key;
162
+ const key = await api.credential({ organizationId, actingUserId });
159
163
  if (key == null) {
160
164
  keys.delete(cacheKey);
161
- throw credentialMissing();
165
+ throw credentialMissing(actingUserId);
162
166
  }
163
167
  keys.set(cacheKey, { key, expiresAtMs: now() + (api.credentialTtlMs ?? 6e4) });
164
168
  return key;
@@ -172,7 +176,7 @@ const createDeviceApiClient = (config) => {
172
176
  if (actingUserId == null && !args.anonymous) throw noActingUser();
173
177
  const attempt = async (refresh) => {
174
178
  const headers = new Headers({
175
- authorization: `Bearer ${await organizationKey({ organizationId: args.caller.organizationId, reread: refresh, api })}`
179
+ authorization: `Bearer ${await credentialFor({ organizationId: args.caller.organizationId, actingUserId, reread: refresh, api })}`
176
180
  });
177
181
  if (actingUserId != null) headers.set(DEVICE_API_ACTING_USER_HEADER, actingUserId);
178
182
  if (args.contentType != null) headers.set("content-type", args.contentType);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/sandboxes/device-api.ts"],
4
- "sourcesContent": ["import {\n deviceEgressCreateRequestSchema,\n deviceEgressWorkspaceIdSchema,\n deviceEgressEnrollmentSchema,\n deviceEgressLeaseSchema,\n deviceEgressWorkspaceSchema,\n workspaceNetworkPolicySchema,\n deviceWorkspaceNetworkStateSchema,\n type WorkspaceNetworkPolicyIface,\n} from '@finalai/agent-protocol/device-egress';\nimport {\n ProcessInspectionOperation,\n processInspectionResultSchema,\n type ProcessInspectionRequestIface,\n type ProcessInspectionResultIface,\n} from '@finalai/agent-protocol/process-inspection';\nimport { jsonObjectSchema, jsonValueSchema, type JsonValue } from '@finalai/agent-provider';\nimport { SandboxEntryType, type SandboxActingUserIface } from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport { decodeBase64, encodeBase64 } from \"./bytes.mjs\";\nimport { DEVICE_API_ACTING_USER_HEADER, DEVICE_API_KEY_SECRET } from \"./environment.mjs\";\nimport type {\n SandboxTransportIface,\n TransportDirEntryIface,\n TransportOutputIface,\n TransportStatIface,\n} from \"./transport.mjs\";\n\n// A client of the Device API (device-api-service), the Final Device's backend,\n// as any vendor provider has a client of its vendor's API. The provider's\n// install config is only the API origin; every call carries the session\n// organization's own API key, kept as an organization secret\n// (DEVICE_API_KEY_SECRET) like any customer's vendor key, exactly as the\n// `computer` provider calls the Computers API. A Device is one person's\n// machine, so every call also names the user it acts for\n// (DEVICE_API_ACTING_USER_HEADER), and the Device API refuses a Device that user\n// does not own, whatever the key.\n\n// The Device API's header naming the user a request acts for (a Final user id).\nexport { DEVICE_API_ACTING_USER_HEADER } from \"./environment.mjs\";\n\n// Whom a call is made for: the organization whose key it carries, and the user\n// it acts for, read on each call.\nexport interface DeviceApiCallerIface {\n organizationId: string;\n actingUser: SandboxActingUserIface;\n}\n\nexport interface DeviceApiConfigIface {\n // The Device API origin.\n url: string;\n // The organization's Device API key (a Final organization API key), or null\n // when it stored none.\n credential: (args: { organizationId: string }) => Promise<string | null>;\n // How long a read key is reused before the secret is read again (default 60 s).\n credentialTtlMs?: number;\n fetch?: DeviceApiFetchIface;\n}\n\nexport interface DeviceApiFetchIface {\n (input: string, init: RequestInit): Promise<Response>;\n}\n\nexport enum DeviceApiLeaseAccess {\n Read = 'read',\n Write = 'write',\n}\n\nexport enum DeviceApiCapability {\n Files = 'files',\n Process = 'process',\n ComputerUse = 'computer-use',\n LocalMcp = 'local-mcp',\n}\n\nexport const DEVICE_API_COMPUTER_USE_SERVER = 'computer-use';\n\n// Unknown values from a newer API read as absent rather than failing the call.\nconst tolerantEnum = <T extends Record<string, string>>(values: T) =>\n z.string().transform((value): T[keyof T] | null => {\n const parsed = z.nativeEnum(values).safeParse(value);\n return parsed.success ? parsed.data : null;\n });\n\nconst deviceSchema = z.object({\n id: z.string(),\n organizationId: z.string(),\n displayName: z.string(),\n os: z.string(),\n online: z.boolean(),\n writable: z.boolean(),\n allowProcesses: z.boolean(),\n computerUseReady: z.boolean(),\n screenReady: z.boolean(),\n});\nexport type DeviceApiDeviceIface = z.infer<typeof deviceSchema>;\n\nconst leaseSchema = z.object({\n lease: z.string(),\n deviceId: z.string(),\n generation: z.number(),\n workspaceRoot: z.string(),\n platform: z.string(),\n access: z.nativeEnum(DeviceApiLeaseAccess),\n capabilities: z.array(tolerantEnum(DeviceApiCapability)),\n expiresAt: z.string(),\n});\nexport interface DeviceApiLeaseIface {\n lease: string;\n deviceId: string;\n generation: number;\n workspaceRoot: string;\n platform: string;\n access: DeviceApiLeaseAccess;\n capabilities: DeviceApiCapability[];\n expiresAtMs: number;\n}\n\ninterface ProcessInputBodyIface {\n bootId: string;\n dataBase64?: string;\n eof?: boolean;\n}\n\nconst errorBodySchema = z.object({ error: z.object({ code: z.string(), message: z.string() }) });\nconst statBodySchema = z.object({\n stat: z\n .object({\n type: tolerantEnum(SandboxEntryType),\n size: z.number(),\n modifiedAtMs: z.number().nullable(),\n mode: z.number().nullable(),\n })\n .nullable(),\n});\nconst dirBodySchema = z.object({\n entries: z.array(\n z.object({\n name: z.string(),\n type: tolerantEnum(SandboxEntryType),\n size: z.number().nullable(),\n }),\n ),\n});\nconst processSchema = z.object({ id: z.string(), bootId: z.string() });\nconst outputStreamSchema = z.object({\n dataBase64: z.string(),\n nextOffset: z.number(),\n eof: z.boolean(),\n});\nconst outputSchema = z.object({\n stdout: outputStreamSchema,\n stderr: outputStreamSchema,\n exited: z.boolean(),\n exitCode: z.number().nullable(),\n exitSignal: z.string().nullable(),\n});\nconst devicesSchema = z.object({ devices: z.array(deviceSchema) });\nconst serversSchema = z.object({ servers: z.array(z.string()) });\n// A Device server's tool as the API recorded it; checked against the tool\n// descriptor contract where the provider hands it on.\nconst catalogToolSchema = jsonObjectSchema;\nconst mcpCatalogSchema = z.object({\n computerUse: z.object({ tools: z.array(catalogToolSchema) }).nullable(),\n localServers: z\n .object({\n servers: z.array(\n z.object({ name: z.string(), tools: z.array(catalogToolSchema).nullable() }),\n ),\n })\n .nullable(),\n});\nexport type DeviceApiMcpCatalogIface = z.infer<typeof mcpCatalogSchema>;\nconst pipeReadSchema = z.object({ dataBase64: z.string().optional() });\nconst screenSchema = z.object({ url: z.string(), token: z.string(), expiresAt: z.string() });\nconst emptySchema = z.unknown();\n\n// One ranged read or write; the API splits it into the Device's own chunks.\nexport const DEVICE_API_FILE_CHUNK_BYTES = 4 * 1024 * 1024;\n// The API long-polls the Device for output up to this long.\nexport const DEVICE_API_OUTPUT_WAIT_MS = 15_000;\n\nexport enum DeviceApiClientErrorCode {\n // The organization stored no Device API key.\n CredentialMissing = 'credential_missing',\n // The session acts for no user, and a Device is only used for its owner.\n NoActingUser = 'no_acting_user',\n EgressEnrollmentLost = 'egress_enrollment_lost',\n}\n\nexport interface DeviceApiErrorIface extends Error {\n status: number | null;\n code: string;\n}\n\nconst apiError = (status: number, code: string, message: string): DeviceApiErrorIface =>\n Object.assign(new Error(`Device API ${status} ${code}: ${message}`), { status, code });\n\nconst credentialMissing = (): DeviceApiErrorIface =>\n Object.assign(\n new Error(\n `this organization has no Device API key for the device sandbox: create an organization API key and store it as the organization secret ${DEVICE_API_KEY_SECRET}`,\n ),\n { status: null, code: DeviceApiClientErrorCode.CredentialMissing },\n );\n\nconst noActingUser = (): DeviceApiErrorIface =>\n Object.assign(\n new Error(\n 'this session acts for no user, and a Final Device is only used by sessions acting for its owner',\n ),\n { status: null, code: DeviceApiClientErrorCode.NoActingUser },\n );\n\nconst statusSchema = z.object({ status: z.number() });\nexport const deviceApiCodeOf = (cause: unknown): string | null =>\n z.object({ code: z.string() }).safeParse(cause).data?.code ?? null;\n\nexport const deviceApiStatusOf = (cause: Error): number | null =>\n statusSchema.safeParse(cause).data?.status ?? null;\n\nconst parseJson = (text: string): JsonValue => {\n try {\n return jsonValueSchema.parse(JSON.parse(text));\n } catch {\n return null;\n }\n};\n\nconst leaseOf = (raw: z.infer<typeof leaseSchema>): DeviceApiLeaseIface => ({\n lease: raw.lease,\n deviceId: raw.deviceId,\n generation: raw.generation,\n workspaceRoot: raw.workspaceRoot,\n platform: raw.platform,\n access: raw.access,\n capabilities: raw.capabilities.filter((capability) => capability != null),\n expiresAtMs: Date.parse(raw.expiresAt),\n});\n\nexport interface DeviceApiClientIface {\n egress: {\n networkState(args: {\n organizationId: string;\n workspaceId: string;\n }): Promise<z.infer<typeof deviceWorkspaceNetworkStateSchema>>;\n setNetworkPolicy(args: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n policy: WorkspaceNetworkPolicyIface;\n initializeIfMissing?: boolean;\n }): Promise<void>;\n workspace(args: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n }): Promise<z.infer<typeof deviceEgressWorkspaceSchema>>;\n create(args: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n deviceIds: string[];\n dnsResolvers?: string[];\n }): Promise<z.infer<typeof deviceEgressEnrollmentSchema>>;\n renew(args: {\n caller: DeviceApiCallerIface;\n leaseId: string;\n }): Promise<z.infer<typeof deviceEgressLeaseSchema>>;\n activate(args: {\n caller: DeviceApiCallerIface;\n leaseId: string;\n }): Promise<z.infer<typeof deviceEgressLeaseSchema>>;\n revoke(args: {\n caller: DeviceApiCallerIface;\n leaseId: string;\n workspaceId: string;\n }): Promise<void>;\n disable(args: { caller: DeviceApiCallerIface; workspaceId: string }): Promise<void>;\n suspend(args: { caller: DeviceApiCallerIface; workspaceId: string }): Promise<void>;\n };\n releaseWorkspaceLeases(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n bindingId: string;\n }): Promise<void>;\n inspectProcesses(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n bindingId: string;\n request: ProcessInspectionRequestIface;\n }): Promise<ProcessInspectionResultIface>;\n getDevice(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n }): Promise<DeviceApiDeviceIface | null>;\n // The Devices of the user the call acts for.\n listDevices(args: { caller: DeviceApiCallerIface }): Promise<DeviceApiDeviceIface[]>;\n resolvePairing(args: {\n caller: DeviceApiCallerIface;\n code: string;\n }): Promise<DeviceApiDeviceIface | null>;\n lease(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n sessionId: string;\n bindingId: string;\n access: DeviceApiLeaseAccess;\n ttlMs: number;\n afterGeneration?: number;\n }): Promise<DeviceApiLeaseIface>;\n recoverLease(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n sessionId: string;\n bindingId: string;\n access: DeviceApiLeaseAccess;\n }): Promise<DeviceApiLeaseIface>;\n renew(args: {\n caller: DeviceApiCallerIface;\n lease: string;\n ttlMs: number;\n }): Promise<DeviceApiLeaseIface>;\n release(args: { caller: DeviceApiCallerIface; lease: string }): Promise<void>;\n // The sandbox transport over one lease; `lease()` is the current handle.\n transport(args: {\n caller: DeviceApiCallerIface;\n lease: () => Promise<string>;\n }): SandboxTransportIface;\n localMcpServers(args: { caller: DeviceApiCallerIface; lease: string }): Promise<string[]>;\n // What the Device's MCP servers last listed through the API, without a lease.\n mcpCatalog(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n }): Promise<DeviceApiMcpCatalogIface>;\n pipe(args: {\n caller: DeviceApiCallerIface;\n lease: string;\n server: string;\n action: 'open' | 'write' | 'read' | 'close';\n clientId: string;\n dataBase64?: string;\n signal?: AbortSignal;\n }): Promise<string>;\n screen(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n sessionId: string;\n viewOnly?: boolean;\n }): Promise<{ url: string; token: string; expiresAt: string }>;\n}\n\nexport const createDeviceApiClient = (config: {\n api: DeviceApiConfigIface | (() => DeviceApiConfigIface);\n fetch?: DeviceApiFetchIface;\n now?: () => number;\n}): DeviceApiClientIface => {\n const now = config.now ?? Date.now;\n const readApi = () => (typeof config.api === 'function' ? config.api() : config.api);\n const keys = new Map<string, { key: string; expiresAtMs: number }>();\n\n const failure = async (response: Response): Promise<DeviceApiErrorIface> => {\n // Read as text so proxy and load balancer error pages survive.\n const text = await response.text().catch(() => '');\n const body = errorBodySchema.safeParse(parseJson(text));\n return body.success\n ? apiError(response.status, body.data.error.code, body.data.error.message)\n : apiError(response.status, 'http', text.slice(0, 200) || response.statusText);\n };\n\n // The organization's key, reused for a minute so calls do not each read the secret store.\n const organizationKey = async (args: {\n organizationId: string;\n reread: boolean;\n api: DeviceApiConfigIface;\n }): Promise<string> => {\n const { organizationId, reread, api } = args;\n const cacheKey = api.url + '\\0' + organizationId;\n const cached = keys.get(cacheKey);\n if (cached != null && !reread && cached.expiresAtMs > now()) return cached.key;\n const key = await api.credential({ organizationId });\n if (key == null) {\n keys.delete(cacheKey);\n throw credentialMissing();\n }\n keys.set(cacheKey, { key, expiresAtMs: now() + (api.credentialTtlMs ?? 60_000) });\n return key;\n };\n\n // One authenticated call for the user it acts for; a rejected key is read\n // again once, in case it was rotated.\n const send = async (args: {\n caller: DeviceApiCallerIface;\n method: string;\n path: string;\n query?: Record<string, string>;\n body?: BodyInit;\n contentType?: string;\n signal?: AbortSignal;\n anonymous?: boolean;\n }): Promise<Response> => {\n const api = readApi();\n const origin = api.url.replace(/\\/+$/, '');\n const url = new URL(`${origin}${args.path}`);\n for (const [key, value] of Object.entries(args.query ?? {})) url.searchParams.set(key, value);\n const actingUserId = await args.caller.actingUser.current();\n if (actingUserId == null && !args.anonymous) throw noActingUser();\n const attempt = async (refresh: boolean): Promise<Response> => {\n const headers = new Headers({\n authorization: `Bearer ${await organizationKey({ organizationId: args.caller.organizationId, reread: refresh, api })}`,\n });\n if (actingUserId != null) headers.set(DEVICE_API_ACTING_USER_HEADER, actingUserId);\n if (args.contentType != null) headers.set('content-type', args.contentType);\n return (config.fetch ?? api.fetch ?? fetch)(url.toString(), {\n method: args.method,\n headers,\n body: args.body,\n signal: args.signal,\n });\n };\n let response = await attempt(false);\n if (response.status === 401) {\n await response.text().catch(() => '');\n response = await attempt(true);\n }\n return response;\n };\n\n const json = async <T extends z.ZodType>(\n schema: T,\n args: Parameters<typeof send>[0] & { json?: unknown },\n ): Promise<z.infer<T>> => {\n const response = await send({\n ...args,\n body: args.json === undefined ? args.body : JSON.stringify(args.json),\n contentType: args.json === undefined ? args.contentType : 'application/json',\n });\n if (!response.ok) throw await failure(response);\n const text = await response.text();\n return schema.parse(text === '' ? null : parseJson(text));\n };\n\n // A 404 reads as absent.\n const orNull = async <T>(work: Promise<T>): Promise<T | null> => {\n try {\n return await work;\n } catch (cause) {\n if (cause instanceof Error && deviceApiStatusOf(cause) === 404) return null;\n throw cause;\n }\n };\n\n const leasePath = (lease: string, rest: string): string =>\n `/v1/leases/${encodeURIComponent(lease)}${rest}`;\n\n const transport: DeviceApiClientIface['transport'] = ({ caller, lease }) => {\n const call = async <T extends z.ZodType>(\n schema: T,\n args: Omit<Parameters<typeof json>[1], 'caller' | 'path'> & { path: string },\n ): Promise<z.infer<T>> =>\n json(schema, { ...args, caller, path: leasePath(await lease(), args.path) });\n const processPath = (id: string, rest: string): string =>\n `/processes/${encodeURIComponent(id)}${rest}`;\n return {\n kind: 'device-api',\n fileChunkBytes: DEVICE_API_FILE_CHUNK_BYTES,\n outputWaitMs: DEVICE_API_OUTPUT_WAIT_MS,\n stat: async ({ path, signal }): Promise<TransportStatIface | null> => {\n const { stat } = await call(statBodySchema, {\n method: 'GET',\n path: '/files/stat',\n query: { path },\n signal,\n });\n return stat == null ? null : { ...stat, type: stat.type ?? SandboxEntryType.Other };\n },\n readDir: async ({ path, signal }): Promise<TransportDirEntryIface[]> =>\n (\n await call(dirBodySchema, { method: 'GET', path: '/files/dir', query: { path }, signal })\n ).entries.map((entry) => ({ ...entry, type: entry.type ?? SandboxEntryType.Other })),\n readChunk: async ({ path, offset, length, signal }) => {\n const response = await send({\n caller,\n method: 'GET',\n path: leasePath(await lease(), '/files/content'),\n query: { path, offset: String(offset), length: String(length) },\n signal,\n });\n if (!response.ok) throw await failure(response);\n const bytes = new Uint8Array(await response.arrayBuffer());\n return { bytes, eof: bytes.byteLength < length };\n },\n writeChunk: async ({ path, offset, bytes, signal }) => {\n await call(emptySchema, {\n method: 'PUT',\n path: '/files/content',\n query: { path, offset: String(offset) },\n body: new Blob([bytes.slice()]),\n contentType: 'application/octet-stream',\n signal,\n });\n },\n createDirectory: async ({ path, signal }) => {\n await call(emptySchema, {\n method: 'POST',\n path: '/files/directories',\n json: { path },\n signal,\n });\n },\n remove: async ({ path, recursive, force, signal }) => {\n await call(emptySchema, {\n method: 'DELETE',\n path: '/files',\n query: { path, recursive: String(recursive), force: String(force) },\n signal,\n });\n },\n exec: async ({ id, command, cwd, env, timeoutMs, signal }) =>\n call(processSchema, {\n method: 'POST',\n path: '/processes',\n json: { id, command, cwd, env, timeoutMs },\n signal,\n }),\n output: async ({ process, stdoutOffset, stderrOffset, waitMs, signal }) => {\n const output = await call(outputSchema, {\n method: 'POST',\n path: processPath(process.id, '/output'),\n json: { bootId: process.bootId, stdoutOffset, stderrOffset, waitMs },\n signal,\n });\n const stream = (value: z.infer<typeof outputStreamSchema>) => ({\n data: decodeBase64(value.dataBase64),\n nextOffset: value.nextOffset,\n eof: value.eof,\n });\n const result: TransportOutputIface = {\n stdout: stream(output.stdout),\n stderr: stream(output.stderr),\n exited: output.exited,\n exitCode: output.exitCode,\n exitSignal: output.exitSignal,\n };\n return result;\n },\n input: async ({ process, data, eof, signal, terminate, abortSignal }) => {\n if (terminate === true || signal != null) {\n await call(emptySchema, {\n method: 'POST',\n path: processPath(process.id, '/kill'),\n json:\n terminate === true ? { bootId: process.bootId } : { bootId: process.bootId, signal },\n signal: abortSignal,\n });\n if (terminate === true) return;\n }\n if ((data != null && data.byteLength > 0) || eof === true) {\n const body: ProcessInputBodyIface = {\n bootId: process.bootId,\n };\n if (data != null && data.byteLength > 0) body.dataBase64 = encodeBase64(data);\n if (eof === true) body.eof = true;\n await call(emptySchema, {\n method: 'POST',\n path: processPath(process.id, '/input'),\n json: body,\n signal: abortSignal,\n });\n }\n },\n close: () => undefined,\n };\n };\n\n return {\n egress: {\n networkState: ({ organizationId, workspaceId }) =>\n json(deviceWorkspaceNetworkStateSchema, {\n caller: { organizationId, actingUser: { current: async () => null } },\n anonymous: true,\n method: 'GET',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}/network-state`,\n signal: AbortSignal.timeout(30_000),\n }),\n setNetworkPolicy: async ({ caller, workspaceId, policy, initializeIfMissing }) => {\n const userId = await caller.actingUser.current();\n const anonymous = userId == null;\n const resolvedCaller = { ...caller, actingUser: { current: async () => userId } };\n const result = await json(z.object({ ok: z.boolean() }), {\n caller: resolvedCaller,\n method: initializeIfMissing === true ? 'POST' : 'PUT',\n anonymous,\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}/${initializeIfMissing === true ? 'network-state/initialize' : anonymous ? 'network-state' : 'network-policy'}`,\n json: workspaceNetworkPolicySchema.parse(policy),\n signal: AbortSignal.timeout(30_000),\n });\n if (!result.ok) throw new Error('Device API did not save the workspace network policy');\n },\n workspace: ({ caller, workspaceId }) =>\n json(deviceEgressWorkspaceSchema, {\n caller,\n method: 'GET',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}`,\n signal: AbortSignal.timeout(30_000),\n }),\n create: ({ caller, ...input }) =>\n json(deviceEgressEnrollmentSchema, {\n caller,\n method: 'POST',\n path: '/v1/egress/leases',\n json: deviceEgressCreateRequestSchema.parse(input),\n signal: AbortSignal.timeout(180_000),\n }),\n renew: ({ caller, leaseId }) =>\n json(deviceEgressLeaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/egress/leases/${encodeURIComponent(leaseId)}/renew`,\n signal: AbortSignal.timeout(180_000),\n }),\n activate: ({ caller, leaseId }) =>\n json(deviceEgressLeaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/egress/leases/${encodeURIComponent(leaseId)}/activate`,\n signal: AbortSignal.timeout(60_000),\n }),\n revoke: async ({ caller, leaseId, workspaceId }) => {\n await json(emptySchema, {\n caller,\n method: 'DELETE',\n json: { workspaceId },\n path: `/v1/egress/leases/${encodeURIComponent(leaseId)}`,\n signal: AbortSignal.timeout(60_000),\n });\n },\n suspend: async ({\n caller,\n workspaceId,\n }: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n }) => {\n const userId = await caller.actingUser.current();\n const anonymous = userId == null;\n const resolvedCaller = { ...caller, actingUser: { current: async () => userId } };\n await json(emptySchema, {\n caller: resolvedCaller,\n anonymous,\n method: 'POST',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}/${anonymous ? 'network-state/suspend' : 'suspend'}`,\n signal: AbortSignal.timeout(60_000),\n });\n },\n disable: async ({ caller, workspaceId }) => {\n await json(emptySchema, {\n caller,\n method: 'DELETE',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}`,\n signal: AbortSignal.timeout(60_000),\n });\n },\n },\n releaseWorkspaceLeases: async ({ caller, deviceId, bindingId }) => {\n const result = await json(z.object({ released: z.boolean() }), {\n caller,\n method: 'DELETE',\n path:\n '/v1/devices/' +\n encodeURIComponent(deviceId) +\n '/workspaces/' +\n encodeURIComponent(bindingId) +\n '/leases',\n });\n if (!result.released) throw new Error('The Device did not release its workspace leases');\n },\n inspectProcesses: ({ caller, deviceId, bindingId, request }) => {\n const suffix =\n request.operation === ProcessInspectionOperation.Available\n ? '/available'\n : 'processId' in request\n ? '/' + encodeURIComponent(request.processId)\n : '';\n const query: Record<string, string> = {};\n if (bindingId != null) query.bindingId = bindingId;\n if ('bootId' in request) query.bootId = request.bootId;\n if ('cursor' in request && request.cursor != null) query.cursor = request.cursor;\n if ('limit' in request && request.limit != null) query.limit = String(request.limit);\n return json(processInspectionResultSchema, {\n caller,\n method: request.operation === ProcessInspectionOperation.Stop ? 'DELETE' : 'GET',\n path: '/v1/devices/' + encodeURIComponent(deviceId) + '/processes' + suffix,\n query,\n });\n },\n getDevice: ({ caller, deviceId }) =>\n orNull(\n json(deviceSchema, {\n caller,\n method: 'GET',\n path: `/v1/devices/${encodeURIComponent(deviceId)}`,\n }),\n ),\n listDevices: async ({ caller }) =>\n (await json(devicesSchema, { caller, method: 'GET', path: '/v1/devices' })).devices,\n resolvePairing: ({ caller, code }) =>\n orNull(\n json(deviceSchema, {\n caller,\n method: 'POST',\n path: '/v1/pairings/resolve',\n json: { code },\n }),\n ),\n lease: async ({ caller, deviceId, sessionId, bindingId, access, ttlMs, afterGeneration }) =>\n leaseOf(\n await json(leaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/leases`,\n json: { sessionId, bindingId, access, ttlMs, afterGeneration },\n }),\n ),\n recoverLease: async ({ caller, deviceId, sessionId, bindingId, access }) =>\n leaseOf(\n await json(leaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/leases/recover`,\n json: { sessionId, bindingId, access },\n }),\n ),\n renew: async ({ caller, lease, ttlMs }) =>\n leaseOf(\n await json(leaseSchema, {\n caller,\n method: 'POST',\n path: leasePath(lease, '/renew'),\n json: { ttlMs },\n }),\n ),\n release: async ({ caller, lease }) => {\n await json(emptySchema, { caller, method: 'DELETE', path: leasePath(lease, '') });\n },\n transport,\n mcpCatalog: ({ caller, deviceId }) =>\n json(mcpCatalogSchema, {\n caller,\n method: 'GET',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/mcp`,\n }),\n localMcpServers: async ({ caller, lease }) =>\n (await json(serversSchema, { caller, method: 'GET', path: leasePath(lease, '/mcp') }))\n .servers,\n pipe: async ({ caller, lease, server, action, clientId, dataBase64, signal }) =>\n (\n await json(pipeReadSchema, {\n caller,\n method: 'POST',\n path: leasePath(lease, `/mcp/${encodeURIComponent(server)}/${action}`),\n json: dataBase64 == null ? { clientId } : { clientId, dataBase64 },\n signal,\n })\n ).dataBase64 ?? '',\n screen: ({ caller, deviceId, sessionId, viewOnly }) =>\n json(screenSchema, {\n caller,\n method: 'POST',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/screen`,\n json: { sessionId, viewOnly },\n }),\n };\n};\n"],
5
- "mappings": "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",
6
- "names": ["DEVICE_API_ACTING_USER_HEADER", "DeviceApiLeaseAccess", "DeviceApiCapability", "DeviceApiClientErrorCode"]
4
+ "sourcesContent": ["import {\n deviceEgressCreateRequestSchema,\n deviceEgressWorkspaceIdSchema,\n deviceEgressEnrollmentSchema,\n deviceEgressLeaseSchema,\n deviceEgressWorkspaceSchema,\n workspaceNetworkPolicySchema,\n deviceWorkspaceNetworkStateSchema,\n type WorkspaceNetworkPolicyIface,\n} from '@finalai/agent-protocol/device-egress';\nimport {\n ProcessInspectionOperation,\n processInspectionResultSchema,\n type ProcessInspectionRequestIface,\n type ProcessInspectionResultIface,\n} from '@finalai/agent-protocol/process-inspection';\nimport { jsonObjectSchema, jsonValueSchema, type JsonValue } from '@finalai/agent-provider';\nimport { SandboxEntryType, type SandboxActingUserIface } from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport { decodeBase64, encodeBase64 } from \"./bytes.mjs\";\nimport { DEVICE_API_ACTING_USER_HEADER } from \"./environment.mjs\";\nimport type {\n SandboxTransportIface,\n TransportDirEntryIface,\n TransportOutputIface,\n TransportStatIface,\n} from \"./transport.mjs\";\n\n// A client of the Device API, authenticated with host-issued user credentials\n// or customer-managed API keys. Device operations also name the acting user;\n// the API enforces ownership independently of the credential type.\n\n// The Device API's header naming the user a request acts for (a Final user id).\nexport { DEVICE_API_ACTING_USER_HEADER } from \"./environment.mjs\";\n\n// The organization and user a call acts for; the user is read on each call.\nexport interface DeviceApiCallerIface {\n organizationId: string;\n actingUser: SandboxActingUserIface;\n}\n\nexport interface DeviceApiConfigIface {\n // The Device API origin.\n url: string;\n // A credential for this organization and acting user; hosts may issue a\n // short-lived user token or return a customer-managed API key.\n // Workspace networking requires a trusted service credential when the user\n // is null (admission, policy persistence, and cleanup). User-only hosts cannot\n // enable that extension. Reconciliation may name a stored lease owner rather\n // than a present turn author; authorize that background work in host composition.\n credential: (args: {\n organizationId: string;\n actingUserId: string | null;\n }) => Promise<string | null>;\n // Per-user cache lifetime (default 60 s); keep below any issued token lifetime.\n credentialTtlMs?: number;\n fetch?: DeviceApiFetchIface;\n}\n\nexport interface DeviceApiFetchIface {\n (input: string, init: RequestInit): Promise<Response>;\n}\n\nexport enum DeviceApiLeaseAccess {\n Read = 'read',\n Write = 'write',\n}\n\nexport enum DeviceApiCapability {\n Files = 'files',\n Process = 'process',\n ComputerUse = 'computer-use',\n LocalMcp = 'local-mcp',\n}\n\nexport const DEVICE_API_COMPUTER_USE_SERVER = 'computer-use';\n\n// Unknown values from a newer API read as absent rather than failing the call.\nconst tolerantEnum = <T extends Record<string, string>>(values: T) =>\n z.string().transform((value): T[keyof T] | null => {\n const parsed = z.nativeEnum(values).safeParse(value);\n return parsed.success ? parsed.data : null;\n });\n\nconst deviceSchema = z.object({\n id: z.string(),\n organizationId: z.string(),\n displayName: z.string(),\n os: z.string(),\n online: z.boolean(),\n writable: z.boolean(),\n allowProcesses: z.boolean(),\n computerUseReady: z.boolean(),\n screenReady: z.boolean(),\n});\nexport type DeviceApiDeviceIface = z.infer<typeof deviceSchema>;\n\nconst leaseSchema = z.object({\n lease: z.string(),\n deviceId: z.string(),\n generation: z.number(),\n workspaceRoot: z.string(),\n platform: z.string(),\n access: z.nativeEnum(DeviceApiLeaseAccess),\n capabilities: z.array(tolerantEnum(DeviceApiCapability)),\n expiresAt: z.string(),\n});\nexport interface DeviceApiLeaseIface {\n lease: string;\n deviceId: string;\n generation: number;\n workspaceRoot: string;\n platform: string;\n access: DeviceApiLeaseAccess;\n capabilities: DeviceApiCapability[];\n expiresAtMs: number;\n}\n\ninterface ProcessInputBodyIface {\n bootId: string;\n dataBase64?: string;\n eof?: boolean;\n}\n\nconst errorBodySchema = z.object({ error: z.object({ code: z.string(), message: z.string() }) });\nconst statBodySchema = z.object({\n stat: z\n .object({\n type: tolerantEnum(SandboxEntryType),\n size: z.number(),\n modifiedAtMs: z.number().nullable(),\n mode: z.number().nullable(),\n })\n .nullable(),\n});\nconst dirBodySchema = z.object({\n entries: z.array(\n z.object({\n name: z.string(),\n type: tolerantEnum(SandboxEntryType),\n size: z.number().nullable(),\n }),\n ),\n});\nconst processSchema = z.object({ id: z.string(), bootId: z.string() });\nconst outputStreamSchema = z.object({\n dataBase64: z.string(),\n nextOffset: z.number(),\n eof: z.boolean(),\n});\nconst outputSchema = z.object({\n stdout: outputStreamSchema,\n stderr: outputStreamSchema,\n exited: z.boolean(),\n exitCode: z.number().nullable(),\n exitSignal: z.string().nullable(),\n});\nconst devicesSchema = z.object({ devices: z.array(deviceSchema) });\nconst serversSchema = z.object({ servers: z.array(z.string()) });\n// A Device server's tool as the API recorded it; checked against the tool\n// descriptor contract where the provider hands it on.\nconst catalogToolSchema = jsonObjectSchema;\nconst mcpCatalogSchema = z.object({\n computerUse: z.object({ tools: z.array(catalogToolSchema) }).nullable(),\n localServers: z\n .object({\n servers: z.array(\n z.object({ name: z.string(), tools: z.array(catalogToolSchema).nullable() }),\n ),\n })\n .nullable(),\n});\nexport type DeviceApiMcpCatalogIface = z.infer<typeof mcpCatalogSchema>;\nconst pipeReadSchema = z.object({ dataBase64: z.string().optional() });\nconst screenSchema = z.object({ url: z.string(), token: z.string(), expiresAt: z.string() });\nconst emptySchema = z.unknown();\n\n// One ranged read or write; the API splits it into the Device's own chunks.\nexport const DEVICE_API_FILE_CHUNK_BYTES = 4 * 1024 * 1024;\n// The API long-polls the Device for output up to this long.\nexport const DEVICE_API_OUTPUT_WAIT_MS = 15_000;\n\nexport enum DeviceApiClientErrorCode {\n // The host returned no credential for this user or trusted service call.\n CredentialMissing = 'credential_missing',\n // The session acts for no user, and a Device is only used for its owner.\n NoActingUser = 'no_acting_user',\n EgressEnrollmentLost = 'egress_enrollment_lost',\n}\n\nexport interface DeviceApiErrorIface extends Error {\n status: number | null;\n code: string;\n}\n\nconst apiError = (status: number, code: string, message: string): DeviceApiErrorIface =>\n Object.assign(new Error(`Device API ${status} ${code}: ${message}`), { status, code });\n\nconst credentialMissing = (actingUserId: string | null): DeviceApiErrorIface =>\n Object.assign(\n new Error(\n `Device API access is unavailable ${actingUserId == null ? 'for workspace networking (no acting user)' : 'for this acting user'}: configure the credential callback`,\n ),\n { status: null, code: DeviceApiClientErrorCode.CredentialMissing },\n );\n\nconst noActingUser = (): DeviceApiErrorIface =>\n Object.assign(\n new Error(\n 'this session acts for no user, and a Final Device is only used by sessions acting for its owner',\n ),\n { status: null, code: DeviceApiClientErrorCode.NoActingUser },\n );\n\nconst statusSchema = z.object({ status: z.number() });\nexport const deviceApiCodeOf = (cause: unknown): string | null =>\n z.object({ code: z.string() }).safeParse(cause).data?.code ?? null;\n\nexport const deviceApiStatusOf = (cause: Error): number | null =>\n statusSchema.safeParse(cause).data?.status ?? null;\n\nconst parseJson = (text: string): JsonValue => {\n try {\n return jsonValueSchema.parse(JSON.parse(text));\n } catch {\n return null;\n }\n};\n\nconst leaseOf = (raw: z.infer<typeof leaseSchema>): DeviceApiLeaseIface => ({\n lease: raw.lease,\n deviceId: raw.deviceId,\n generation: raw.generation,\n workspaceRoot: raw.workspaceRoot,\n platform: raw.platform,\n access: raw.access,\n capabilities: raw.capabilities.filter((capability) => capability != null),\n expiresAtMs: Date.parse(raw.expiresAt),\n});\n\nexport interface DeviceApiClientIface {\n egress: {\n networkState(args: {\n organizationId: string;\n workspaceId: string;\n }): Promise<z.infer<typeof deviceWorkspaceNetworkStateSchema>>;\n setNetworkPolicy(args: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n policy: WorkspaceNetworkPolicyIface;\n initializeIfMissing?: boolean;\n }): Promise<void>;\n workspace(args: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n }): Promise<z.infer<typeof deviceEgressWorkspaceSchema>>;\n create(args: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n deviceIds: string[];\n dnsResolvers?: string[];\n }): Promise<z.infer<typeof deviceEgressEnrollmentSchema>>;\n renew(args: {\n caller: DeviceApiCallerIface;\n leaseId: string;\n }): Promise<z.infer<typeof deviceEgressLeaseSchema>>;\n activate(args: {\n caller: DeviceApiCallerIface;\n leaseId: string;\n }): Promise<z.infer<typeof deviceEgressLeaseSchema>>;\n revoke(args: {\n caller: DeviceApiCallerIface;\n leaseId: string;\n workspaceId: string;\n }): Promise<void>;\n disable(args: { caller: DeviceApiCallerIface; workspaceId: string }): Promise<void>;\n suspend(args: { caller: DeviceApiCallerIface; workspaceId: string }): Promise<void>;\n };\n releaseWorkspaceLeases(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n bindingId: string;\n }): Promise<void>;\n inspectProcesses(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n bindingId: string;\n request: ProcessInspectionRequestIface;\n }): Promise<ProcessInspectionResultIface>;\n getDevice(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n }): Promise<DeviceApiDeviceIface | null>;\n // The Devices of the user the call acts for.\n listDevices(args: { caller: DeviceApiCallerIface }): Promise<DeviceApiDeviceIface[]>;\n resolvePairing(args: {\n caller: DeviceApiCallerIface;\n code: string;\n }): Promise<DeviceApiDeviceIface | null>;\n lease(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n sessionId: string;\n bindingId: string;\n access: DeviceApiLeaseAccess;\n ttlMs: number;\n afterGeneration?: number;\n }): Promise<DeviceApiLeaseIface>;\n recoverLease(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n sessionId: string;\n bindingId: string;\n access: DeviceApiLeaseAccess;\n }): Promise<DeviceApiLeaseIface>;\n renew(args: {\n caller: DeviceApiCallerIface;\n lease: string;\n ttlMs: number;\n }): Promise<DeviceApiLeaseIface>;\n release(args: { caller: DeviceApiCallerIface; lease: string }): Promise<void>;\n // The sandbox transport over one lease; `lease()` is the current handle.\n transport(args: {\n caller: DeviceApiCallerIface;\n lease: () => Promise<string>;\n }): SandboxTransportIface;\n localMcpServers(args: { caller: DeviceApiCallerIface; lease: string }): Promise<string[]>;\n // What the Device's MCP servers last listed through the API, without a lease.\n mcpCatalog(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n }): Promise<DeviceApiMcpCatalogIface>;\n pipe(args: {\n caller: DeviceApiCallerIface;\n lease: string;\n server: string;\n action: 'open' | 'write' | 'read' | 'close';\n clientId: string;\n dataBase64?: string;\n signal?: AbortSignal;\n }): Promise<string>;\n screen(args: {\n caller: DeviceApiCallerIface;\n deviceId: string;\n sessionId: string;\n viewOnly?: boolean;\n }): Promise<{ url: string; token: string; expiresAt: string }>;\n}\n\nexport const createDeviceApiClient = (config: {\n api: DeviceApiConfigIface | (() => DeviceApiConfigIface);\n fetch?: DeviceApiFetchIface;\n now?: () => number;\n}): DeviceApiClientIface => {\n const now = config.now ?? Date.now;\n const readApi = () => (typeof config.api === 'function' ? config.api() : config.api);\n const keys = new Map<string, { key: string; expiresAtMs: number }>();\n\n const failure = async (response: Response): Promise<DeviceApiErrorIface> => {\n // Read as text so proxy and load balancer error pages survive.\n const text = await response.text().catch(() => '');\n const body = errorBodySchema.safeParse(parseJson(text));\n return body.success\n ? apiError(response.status, body.data.error.code, body.data.error.message)\n : apiError(response.status, 'http', text.slice(0, 200) || response.statusText);\n };\n\n // Keep user and trusted service credentials separate, including across API origins.\n const credentialFor = async (args: {\n organizationId: string;\n actingUserId: string | null;\n reread: boolean;\n api: DeviceApiConfigIface;\n }): Promise<string> => {\n const { organizationId, actingUserId, reread, api } = args;\n const cacheKey = JSON.stringify([api.url, organizationId, actingUserId]);\n const currentTime = now();\n for (const [key, entry] of keys) {\n if (entry.expiresAtMs <= currentTime) keys.delete(key);\n }\n const cached = keys.get(cacheKey);\n if (cached != null && !reread) return cached.key;\n const key = await api.credential({ organizationId, actingUserId });\n if (key == null) {\n keys.delete(cacheKey);\n throw credentialMissing(actingUserId);\n }\n keys.set(cacheKey, { key, expiresAtMs: now() + (api.credentialTtlMs ?? 60_000) });\n return key;\n };\n\n // One authenticated call for the user it acts for; a rejected key is read\n // again once, in case it was rotated.\n const send = async (args: {\n caller: DeviceApiCallerIface;\n method: string;\n path: string;\n query?: Record<string, string>;\n body?: BodyInit;\n contentType?: string;\n signal?: AbortSignal;\n anonymous?: boolean;\n }): Promise<Response> => {\n const api = readApi();\n const origin = api.url.replace(/\\/+$/, '');\n const url = new URL(`${origin}${args.path}`);\n for (const [key, value] of Object.entries(args.query ?? {})) url.searchParams.set(key, value);\n const actingUserId = await args.caller.actingUser.current();\n if (actingUserId == null && !args.anonymous) throw noActingUser();\n const attempt = async (refresh: boolean): Promise<Response> => {\n const headers = new Headers({\n authorization: `Bearer ${await credentialFor({ organizationId: args.caller.organizationId, actingUserId, reread: refresh, api })}`,\n });\n if (actingUserId != null) headers.set(DEVICE_API_ACTING_USER_HEADER, actingUserId);\n if (args.contentType != null) headers.set('content-type', args.contentType);\n return (config.fetch ?? api.fetch ?? fetch)(url.toString(), {\n method: args.method,\n headers,\n body: args.body,\n signal: args.signal,\n });\n };\n let response = await attempt(false);\n if (response.status === 401) {\n await response.text().catch(() => '');\n response = await attempt(true);\n }\n return response;\n };\n\n const json = async <T extends z.ZodType>(\n schema: T,\n args: Parameters<typeof send>[0] & { json?: unknown },\n ): Promise<z.infer<T>> => {\n const response = await send({\n ...args,\n body: args.json === undefined ? args.body : JSON.stringify(args.json),\n contentType: args.json === undefined ? args.contentType : 'application/json',\n });\n if (!response.ok) throw await failure(response);\n const text = await response.text();\n return schema.parse(text === '' ? null : parseJson(text));\n };\n\n // A 404 reads as absent.\n const orNull = async <T>(work: Promise<T>): Promise<T | null> => {\n try {\n return await work;\n } catch (cause) {\n if (cause instanceof Error && deviceApiStatusOf(cause) === 404) return null;\n throw cause;\n }\n };\n\n const leasePath = (lease: string, rest: string): string =>\n `/v1/leases/${encodeURIComponent(lease)}${rest}`;\n\n const transport: DeviceApiClientIface['transport'] = ({ caller, lease }) => {\n const call = async <T extends z.ZodType>(\n schema: T,\n args: Omit<Parameters<typeof json>[1], 'caller' | 'path'> & { path: string },\n ): Promise<z.infer<T>> =>\n json(schema, { ...args, caller, path: leasePath(await lease(), args.path) });\n const processPath = (id: string, rest: string): string =>\n `/processes/${encodeURIComponent(id)}${rest}`;\n return {\n kind: 'device-api',\n fileChunkBytes: DEVICE_API_FILE_CHUNK_BYTES,\n outputWaitMs: DEVICE_API_OUTPUT_WAIT_MS,\n stat: async ({ path, signal }): Promise<TransportStatIface | null> => {\n const { stat } = await call(statBodySchema, {\n method: 'GET',\n path: '/files/stat',\n query: { path },\n signal,\n });\n return stat == null ? null : { ...stat, type: stat.type ?? SandboxEntryType.Other };\n },\n readDir: async ({ path, signal }): Promise<TransportDirEntryIface[]> =>\n (\n await call(dirBodySchema, { method: 'GET', path: '/files/dir', query: { path }, signal })\n ).entries.map((entry) => ({ ...entry, type: entry.type ?? SandboxEntryType.Other })),\n readChunk: async ({ path, offset, length, signal }) => {\n const response = await send({\n caller,\n method: 'GET',\n path: leasePath(await lease(), '/files/content'),\n query: { path, offset: String(offset), length: String(length) },\n signal,\n });\n if (!response.ok) throw await failure(response);\n const bytes = new Uint8Array(await response.arrayBuffer());\n return { bytes, eof: bytes.byteLength < length };\n },\n writeChunk: async ({ path, offset, bytes, signal }) => {\n await call(emptySchema, {\n method: 'PUT',\n path: '/files/content',\n query: { path, offset: String(offset) },\n body: new Blob([bytes.slice()]),\n contentType: 'application/octet-stream',\n signal,\n });\n },\n createDirectory: async ({ path, signal }) => {\n await call(emptySchema, {\n method: 'POST',\n path: '/files/directories',\n json: { path },\n signal,\n });\n },\n remove: async ({ path, recursive, force, signal }) => {\n await call(emptySchema, {\n method: 'DELETE',\n path: '/files',\n query: { path, recursive: String(recursive), force: String(force) },\n signal,\n });\n },\n exec: async ({ id, command, cwd, env, timeoutMs, signal }) =>\n call(processSchema, {\n method: 'POST',\n path: '/processes',\n json: { id, command, cwd, env, timeoutMs },\n signal,\n }),\n output: async ({ process, stdoutOffset, stderrOffset, waitMs, signal }) => {\n const output = await call(outputSchema, {\n method: 'POST',\n path: processPath(process.id, '/output'),\n json: { bootId: process.bootId, stdoutOffset, stderrOffset, waitMs },\n signal,\n });\n const stream = (value: z.infer<typeof outputStreamSchema>) => ({\n data: decodeBase64(value.dataBase64),\n nextOffset: value.nextOffset,\n eof: value.eof,\n });\n const result: TransportOutputIface = {\n stdout: stream(output.stdout),\n stderr: stream(output.stderr),\n exited: output.exited,\n exitCode: output.exitCode,\n exitSignal: output.exitSignal,\n };\n return result;\n },\n input: async ({ process, data, eof, signal, terminate, abortSignal }) => {\n if (terminate === true || signal != null) {\n await call(emptySchema, {\n method: 'POST',\n path: processPath(process.id, '/kill'),\n json:\n terminate === true ? { bootId: process.bootId } : { bootId: process.bootId, signal },\n signal: abortSignal,\n });\n if (terminate === true) return;\n }\n if ((data != null && data.byteLength > 0) || eof === true) {\n const body: ProcessInputBodyIface = {\n bootId: process.bootId,\n };\n if (data != null && data.byteLength > 0) body.dataBase64 = encodeBase64(data);\n if (eof === true) body.eof = true;\n await call(emptySchema, {\n method: 'POST',\n path: processPath(process.id, '/input'),\n json: body,\n signal: abortSignal,\n });\n }\n },\n close: () => undefined,\n };\n };\n\n return {\n egress: {\n networkState: ({ organizationId, workspaceId }) =>\n json(deviceWorkspaceNetworkStateSchema, {\n caller: { organizationId, actingUser: { current: async () => null } },\n anonymous: true,\n method: 'GET',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}/network-state`,\n signal: AbortSignal.timeout(30_000),\n }),\n setNetworkPolicy: async ({ caller, workspaceId, policy, initializeIfMissing }) => {\n const userId = await caller.actingUser.current();\n const anonymous = userId == null;\n const resolvedCaller = { ...caller, actingUser: { current: async () => userId } };\n const result = await json(z.object({ ok: z.boolean() }), {\n caller: resolvedCaller,\n method: initializeIfMissing === true ? 'POST' : 'PUT',\n anonymous,\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}/${initializeIfMissing === true ? 'network-state/initialize' : anonymous ? 'network-state' : 'network-policy'}`,\n json: workspaceNetworkPolicySchema.parse(policy),\n signal: AbortSignal.timeout(30_000),\n });\n if (!result.ok) throw new Error('Device API did not save the workspace network policy');\n },\n workspace: ({ caller, workspaceId }) =>\n json(deviceEgressWorkspaceSchema, {\n caller,\n method: 'GET',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}`,\n signal: AbortSignal.timeout(30_000),\n }),\n create: ({ caller, ...input }) =>\n json(deviceEgressEnrollmentSchema, {\n caller,\n method: 'POST',\n path: '/v1/egress/leases',\n json: deviceEgressCreateRequestSchema.parse(input),\n signal: AbortSignal.timeout(180_000),\n }),\n renew: ({ caller, leaseId }) =>\n json(deviceEgressLeaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/egress/leases/${encodeURIComponent(leaseId)}/renew`,\n signal: AbortSignal.timeout(180_000),\n }),\n activate: ({ caller, leaseId }) =>\n json(deviceEgressLeaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/egress/leases/${encodeURIComponent(leaseId)}/activate`,\n signal: AbortSignal.timeout(60_000),\n }),\n revoke: async ({ caller, leaseId, workspaceId }) => {\n await json(emptySchema, {\n caller,\n method: 'DELETE',\n json: { workspaceId },\n path: `/v1/egress/leases/${encodeURIComponent(leaseId)}`,\n signal: AbortSignal.timeout(60_000),\n });\n },\n suspend: async ({\n caller,\n workspaceId,\n }: {\n caller: DeviceApiCallerIface;\n workspaceId: string;\n }) => {\n const userId = await caller.actingUser.current();\n const anonymous = userId == null;\n const resolvedCaller = { ...caller, actingUser: { current: async () => userId } };\n await json(emptySchema, {\n caller: resolvedCaller,\n anonymous,\n method: 'POST',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}/${anonymous ? 'network-state/suspend' : 'suspend'}`,\n signal: AbortSignal.timeout(60_000),\n });\n },\n disable: async ({ caller, workspaceId }) => {\n await json(emptySchema, {\n caller,\n method: 'DELETE',\n path: `/v1/egress/workspaces/${encodeURIComponent(deviceEgressWorkspaceIdSchema.parse(workspaceId))}`,\n signal: AbortSignal.timeout(60_000),\n });\n },\n },\n releaseWorkspaceLeases: async ({ caller, deviceId, bindingId }) => {\n const result = await json(z.object({ released: z.boolean() }), {\n caller,\n method: 'DELETE',\n path:\n '/v1/devices/' +\n encodeURIComponent(deviceId) +\n '/workspaces/' +\n encodeURIComponent(bindingId) +\n '/leases',\n });\n if (!result.released) throw new Error('The Device did not release its workspace leases');\n },\n inspectProcesses: ({ caller, deviceId, bindingId, request }) => {\n const suffix =\n request.operation === ProcessInspectionOperation.Available\n ? '/available'\n : 'processId' in request\n ? '/' + encodeURIComponent(request.processId)\n : '';\n const query: Record<string, string> = {};\n if (bindingId != null) query.bindingId = bindingId;\n if ('bootId' in request) query.bootId = request.bootId;\n if ('cursor' in request && request.cursor != null) query.cursor = request.cursor;\n if ('limit' in request && request.limit != null) query.limit = String(request.limit);\n return json(processInspectionResultSchema, {\n caller,\n method: request.operation === ProcessInspectionOperation.Stop ? 'DELETE' : 'GET',\n path: '/v1/devices/' + encodeURIComponent(deviceId) + '/processes' + suffix,\n query,\n });\n },\n getDevice: ({ caller, deviceId }) =>\n orNull(\n json(deviceSchema, {\n caller,\n method: 'GET',\n path: `/v1/devices/${encodeURIComponent(deviceId)}`,\n }),\n ),\n listDevices: async ({ caller }) =>\n (await json(devicesSchema, { caller, method: 'GET', path: '/v1/devices' })).devices,\n resolvePairing: ({ caller, code }) =>\n orNull(\n json(deviceSchema, {\n caller,\n method: 'POST',\n path: '/v1/pairings/resolve',\n json: { code },\n }),\n ),\n lease: async ({ caller, deviceId, sessionId, bindingId, access, ttlMs, afterGeneration }) =>\n leaseOf(\n await json(leaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/leases`,\n json: { sessionId, bindingId, access, ttlMs, afterGeneration },\n }),\n ),\n recoverLease: async ({ caller, deviceId, sessionId, bindingId, access }) =>\n leaseOf(\n await json(leaseSchema, {\n caller,\n method: 'POST',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/leases/recover`,\n json: { sessionId, bindingId, access },\n }),\n ),\n renew: async ({ caller, lease, ttlMs }) =>\n leaseOf(\n await json(leaseSchema, {\n caller,\n method: 'POST',\n path: leasePath(lease, '/renew'),\n json: { ttlMs },\n }),\n ),\n release: async ({ caller, lease }) => {\n await json(emptySchema, { caller, method: 'DELETE', path: leasePath(lease, '') });\n },\n transport,\n mcpCatalog: ({ caller, deviceId }) =>\n json(mcpCatalogSchema, {\n caller,\n method: 'GET',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/mcp`,\n }),\n localMcpServers: async ({ caller, lease }) =>\n (await json(serversSchema, { caller, method: 'GET', path: leasePath(lease, '/mcp') }))\n .servers,\n pipe: async ({ caller, lease, server, action, clientId, dataBase64, signal }) =>\n (\n await json(pipeReadSchema, {\n caller,\n method: 'POST',\n path: leasePath(lease, `/mcp/${encodeURIComponent(server)}/${action}`),\n json: dataBase64 == null ? { clientId } : { clientId, dataBase64 },\n signal,\n })\n ).dataBase64 ?? '',\n screen: ({ caller, deviceId, sessionId, viewOnly }) =>\n json(screenSchema, {\n caller,\n method: 'POST',\n path: `/v1/devices/${encodeURIComponent(deviceId)}/screen`,\n json: { sessionId, viewOnly },\n }),\n };\n};\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,kBAAkB,uBAAuC;AAClE,SAAS,wBAAqD;AAC9D,YAAY,OAAO;AACnB,SAAS,cAAc,oBAAoB;AAC3C,SAAS,qCAAqC;AAa9C,SAAS,iCAAAA,sCAAqC;AA8BvC,IAAK,uBAAL,kBAAKC,0BAAL;AACL,EAAAA,sBAAA,UAAO;AACP,EAAAA,sBAAA,WAAQ;AAFE,SAAAA;AAAA,GAAA;AAKL,IAAK,sBAAL,kBAAKC,yBAAL;AACL,EAAAA,qBAAA,WAAQ;AACR,EAAAA,qBAAA,aAAU;AACV,EAAAA,qBAAA,iBAAc;AACd,EAAAA,qBAAA,cAAW;AAJD,SAAAA;AAAA,GAAA;AAOL,MAAM,iCAAiC;AAG9C,MAAM,eAAe,CAAmC,WACtD,EAAE,OAAO,EAAE,UAAU,CAAC,UAA6B;AACjD,QAAM,SAAS,EAAE,WAAW,MAAM,EAAE,UAAU,KAAK;AACnD,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC,CAAC;AAEH,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,IAAI,EAAE,OAAO;AAAA,EACb,gBAAgB,EAAE,OAAO;AAAA,EACzB,aAAa,EAAE,OAAO;AAAA,EACtB,IAAI,EAAE,OAAO;AAAA,EACb,QAAQ,EAAE,QAAQ;AAAA,EAClB,UAAU,EAAE,QAAQ;AAAA,EACpB,gBAAgB,EAAE,QAAQ;AAAA,EAC1B,kBAAkB,EAAE,QAAQ;AAAA,EAC5B,aAAa,EAAE,QAAQ;AACzB,CAAC;AAGD,MAAM,cAAc,EAAE,OAAO;AAAA,EAC3B,OAAO,EAAE,OAAO;AAAA,EAChB,UAAU,EAAE,OAAO;AAAA,EACnB,YAAY,EAAE,OAAO;AAAA,EACrB,eAAe,EAAE,OAAO;AAAA,EACxB,UAAU,EAAE,OAAO;AAAA,EACnB,QAAQ,EAAE,WAAW,oBAAoB;AAAA,EACzC,cAAc,EAAE,MAAM,aAAa,mBAAmB,CAAC;AAAA,EACvD,WAAW,EAAE,OAAO;AACtB,CAAC;AAkBD,MAAM,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAC/F,MAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,MAAM,EACH,OAAO;AAAA,IACN,MAAM,aAAa,gBAAgB;AAAA,IACnC,MAAM,EAAE,OAAO;AAAA,IACf,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,IAClC,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,CAAC,EACA,SAAS;AACd,CAAC;AACD,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,SAAS,EAAE;AAAA,IACT,EAAE,OAAO;AAAA,MACP,MAAM,EAAE,OAAO;AAAA,MACf,MAAM,aAAa,gBAAgB;AAAA,MACnC,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,CAAC;AAAA,EACH;AACF,CAAC;AACD,MAAM,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC;AACrE,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,YAAY,EAAE,OAAO;AAAA,EACrB,YAAY,EAAE,OAAO;AAAA,EACrB,KAAK,EAAE,QAAQ;AACjB,CAAC;AACD,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ,EAAE,QAAQ;AAAA,EAClB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,YAAY,EAAE,OAAO,EAAE,SAAS;AAClC,CAAC;AACD,MAAM,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,EAAE,CAAC;AACjE,MAAM,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;AAG/D,MAAM,oBAAoB;AAC1B,MAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,EAAE,CAAC,EAAE,SAAS;AAAA,EACtE,cAAc,EACX,OAAO;AAAA,IACN,SAAS,EAAE;AAAA,MACT,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,MAAM,iBAAiB,EAAE,SAAS,EAAE,CAAC;AAAA,IAC7E;AAAA,EACF,CAAC,EACA,SAAS;AACd,CAAC;AAED,MAAM,iBAAiB,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACrE,MAAM,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC;AAC3F,MAAM,cAAc,EAAE,QAAQ;AAGvB,MAAM,8BAA8B,IAAI,OAAO;AAE/C,MAAM,4BAA4B;AAElC,IAAK,2BAAL,kBAAKC,8BAAL;AAEL,EAAAA,0BAAA,uBAAoB;AAEpB,EAAAA,0BAAA,kBAAe;AACf,EAAAA,0BAAA,0BAAuB;AALb,SAAAA;AAAA,GAAA;AAaZ,MAAM,WAAW,CAAC,QAAgB,MAAc,YAC9C,OAAO,OAAO,IAAI,MAAM,cAAc,MAAM,IAAI,IAAI,KAAK,OAAO,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;AAEvF,MAAM,oBAAoB,CAAC,iBACzB,OAAO;AAAA,EACL,IAAI;AAAA,IACF,oCAAoC,gBAAgB,OAAO,8CAA8C,sBAAsB;AAAA,EACjI;AAAA,EACA,EAAE,QAAQ,MAAM,MAAM,6CAA2C;AACnE;AAEF,MAAM,eAAe,MACnB,OAAO;AAAA,EACL,IAAI;AAAA,IACF;AAAA,EACF;AAAA,EACA,EAAE,QAAQ,MAAM,MAAM,oCAAsC;AAC9D;AAEF,MAAM,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC7C,MAAM,kBAAkB,CAAC,UAC9B,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,KAAK,EAAE,MAAM,QAAQ;AAEzD,MAAM,oBAAoB,CAAC,UAChC,aAAa,UAAU,KAAK,EAAE,MAAM,UAAU;AAEhD,MAAM,YAAY,CAAC,SAA4B;AAC7C,MAAI;AACF,WAAO,gBAAgB,MAAM,KAAK,MAAM,IAAI,CAAC;AAAA,EAC/C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,UAAU,CAAC,SAA2D;AAAA,EAC1E,OAAO,IAAI;AAAA,EACX,UAAU,IAAI;AAAA,EACd,YAAY,IAAI;AAAA,EAChB,eAAe,IAAI;AAAA,EACnB,UAAU,IAAI;AAAA,EACd,QAAQ,IAAI;AAAA,EACZ,cAAc,IAAI,aAAa,OAAO,CAAC,eAAe,cAAc,IAAI;AAAA,EACxE,aAAa,KAAK,MAAM,IAAI,SAAS;AACvC;AA+GO,MAAM,wBAAwB,CAAC,WAIV;AAC1B,QAAM,MAAM,OAAO,OAAO,KAAK;AAC/B,QAAM,UAAU,MAAO,OAAO,OAAO,QAAQ,aAAa,OAAO,IAAI,IAAI,OAAO;AAChF,QAAM,OAAO,oBAAI,IAAkD;AAEnE,QAAM,UAAU,OAAO,aAAqD;AAE1E,UAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,UAAM,OAAO,gBAAgB,UAAU,UAAU,IAAI,CAAC;AACtD,WAAO,KAAK,UACR,SAAS,SAAS,QAAQ,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,IACvE,SAAS,SAAS,QAAQ,QAAQ,KAAK,MAAM,GAAG,GAAG,KAAK,SAAS,UAAU;AAAA,EACjF;AAGA,QAAM,gBAAgB,OAAO,SAKN;AACrB,UAAM,EAAE,gBAAgB,cAAc,QAAQ,IAAI,IAAI;AACtD,UAAM,WAAW,KAAK,UAAU,CAAC,IAAI,KAAK,gBAAgB,YAAY,CAAC;AACvE,UAAM,cAAc,IAAI;AACxB,eAAW,CAACC,MAAK,KAAK,KAAK,MAAM;AAC/B,UAAI,MAAM,eAAe,YAAa,MAAK,OAAOA,IAAG;AAAA,IACvD;AACA,UAAM,SAAS,KAAK,IAAI,QAAQ;AAChC,QAAI,UAAU,QAAQ,CAAC,OAAQ,QAAO,OAAO;AAC7C,UAAM,MAAM,MAAM,IAAI,WAAW,EAAE,gBAAgB,aAAa,CAAC;AACjE,QAAI,OAAO,MAAM;AACf,WAAK,OAAO,QAAQ;AACpB,YAAM,kBAAkB,YAAY;AAAA,IACtC;AACA,SAAK,IAAI,UAAU,EAAE,KAAK,aAAa,IAAI,KAAK,IAAI,mBAAmB,KAAQ,CAAC;AAChF,WAAO;AAAA,EACT;AAIA,QAAM,OAAO,OAAO,SASK;AACvB,UAAM,MAAM,QAAQ;AACpB,UAAM,SAAS,IAAI,IAAI,QAAQ,QAAQ,EAAE;AACzC,UAAM,MAAM,IAAI,IAAI,GAAG,MAAM,GAAG,KAAK,IAAI,EAAE;AAC3C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,EAAG,KAAI,aAAa,IAAI,KAAK,KAAK;AAC5F,UAAM,eAAe,MAAM,KAAK,OAAO,WAAW,QAAQ;AAC1D,QAAI,gBAAgB,QAAQ,CAAC,KAAK,UAAW,OAAM,aAAa;AAChE,UAAM,UAAU,OAAO,YAAwC;AAC7D,YAAM,UAAU,IAAI,QAAQ;AAAA,QAC1B,eAAe,UAAU,MAAM,cAAc,EAAE,gBAAgB,KAAK,OAAO,gBAAgB,cAAc,QAAQ,SAAS,IAAI,CAAC,CAAC;AAAA,MAClI,CAAC;AACD,UAAI,gBAAgB,KAAM,SAAQ,IAAI,+BAA+B,YAAY;AACjF,UAAI,KAAK,eAAe,KAAM,SAAQ,IAAI,gBAAgB,KAAK,WAAW;AAC1E,cAAQ,OAAO,SAAS,IAAI,SAAS,OAAO,IAAI,SAAS,GAAG;AAAA,QAC1D,QAAQ,KAAK;AAAA,QACb;AAAA,QACA,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AACA,QAAI,WAAW,MAAM,QAAQ,KAAK;AAClC,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACpC,iBAAW,MAAM,QAAQ,IAAI;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,OACX,QACA,SACwB;AACxB,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,GAAG;AAAA,MACH,MAAM,KAAK,SAAS,SAAY,KAAK,OAAO,KAAK,UAAU,KAAK,IAAI;AAAA,MACpE,aAAa,KAAK,SAAS,SAAY,KAAK,cAAc;AAAA,IAC5D,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,OAAM,MAAM,QAAQ,QAAQ;AAC9C,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO,OAAO,MAAM,SAAS,KAAK,OAAO,UAAU,IAAI,CAAC;AAAA,EAC1D;AAGA,QAAM,SAAS,OAAU,SAAwC;AAC/D,QAAI;AACF,aAAO,MAAM;AAAA,IACf,SAAS,OAAO;AACd,UAAI,iBAAiB,SAAS,kBAAkB,KAAK,MAAM,IAAK,QAAO;AACvE,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,OAAe,SAChC,cAAc,mBAAmB,KAAK,CAAC,GAAG,IAAI;AAEhD,QAAM,YAA+C,CAAC,EAAE,QAAQ,MAAM,MAAM;AAC1E,UAAM,OAAO,OACX,QACA,SAEA,KAAK,QAAQ,EAAE,GAAG,MAAM,QAAQ,MAAM,UAAU,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AAC7E,UAAM,cAAc,CAAC,IAAY,SAC/B,cAAc,mBAAmB,EAAE,CAAC,GAAG,IAAI;AAC7C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,cAAc;AAAA,MACd,MAAM,OAAO,EAAE,MAAM,OAAO,MAA0C;AACpE,cAAM,EAAE,KAAK,IAAI,MAAM,KAAK,gBAAgB;AAAA,UAC1C,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO,EAAE,KAAK;AAAA,UACd;AAAA,QACF,CAAC;AACD,eAAO,QAAQ,OAAO,OAAO,EAAE,GAAG,MAAM,MAAM,KAAK,QAAQ,iBAAiB,MAAM;AAAA,MACpF;AAAA,MACA,SAAS,OAAO,EAAE,MAAM,OAAO,OAE3B,MAAM,KAAK,eAAe,EAAE,QAAQ,OAAO,MAAM,cAAc,OAAO,EAAE,KAAK,GAAG,OAAO,CAAC,GACxF,QAAQ,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO,MAAM,MAAM,QAAQ,iBAAiB,MAAM,EAAE;AAAA,MACrF,WAAW,OAAO,EAAE,MAAM,QAAQ,QAAQ,OAAO,MAAM;AACrD,cAAM,WAAW,MAAM,KAAK;AAAA,UAC1B;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,UAAU,MAAM,MAAM,GAAG,gBAAgB;AAAA,UAC/C,OAAO,EAAE,MAAM,QAAQ,OAAO,MAAM,GAAG,QAAQ,OAAO,MAAM,EAAE;AAAA,UAC9D;AAAA,QACF,CAAC;AACD,YAAI,CAAC,SAAS,GAAI,OAAM,MAAM,QAAQ,QAAQ;AAC9C,cAAM,QAAQ,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC;AACzD,eAAO,EAAE,OAAO,KAAK,MAAM,aAAa,OAAO;AAAA,MACjD;AAAA,MACA,YAAY,OAAO,EAAE,MAAM,QAAQ,OAAO,OAAO,MAAM;AACrD,cAAM,KAAK,aAAa;AAAA,UACtB,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO,EAAE,MAAM,QAAQ,OAAO,MAAM,EAAE;AAAA,UACtC,MAAM,IAAI,KAAK,CAAC,MAAM,MAAM,CAAC,CAAC;AAAA,UAC9B,aAAa;AAAA,UACb;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,iBAAiB,OAAO,EAAE,MAAM,OAAO,MAAM;AAC3C,cAAM,KAAK,aAAa;AAAA,UACtB,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM,EAAE,KAAK;AAAA,UACb;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,OAAO,EAAE,MAAM,WAAW,OAAO,OAAO,MAAM;AACpD,cAAM,KAAK,aAAa;AAAA,UACtB,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO,EAAE,MAAM,WAAW,OAAO,SAAS,GAAG,OAAO,OAAO,KAAK,EAAE;AAAA,UAClE;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,MAAM,OAAO,EAAE,IAAI,SAAS,KAAK,KAAK,WAAW,OAAO,MACtD,KAAK,eAAe;AAAA,QAClB,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,MAAM,EAAE,IAAI,SAAS,KAAK,KAAK,UAAU;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,MACH,QAAQ,OAAO,EAAE,SAAS,cAAc,cAAc,QAAQ,OAAO,MAAM;AACzE,cAAM,SAAS,MAAM,KAAK,cAAc;AAAA,UACtC,QAAQ;AAAA,UACR,MAAM,YAAY,QAAQ,IAAI,SAAS;AAAA,UACvC,MAAM,EAAE,QAAQ,QAAQ,QAAQ,cAAc,cAAc,OAAO;AAAA,UACnE;AAAA,QACF,CAAC;AACD,cAAM,SAAS,CAAC,WAA+C;AAAA,UAC7D,MAAM,aAAa,MAAM,UAAU;AAAA,UACnC,YAAY,MAAM;AAAA,UAClB,KAAK,MAAM;AAAA,QACb;AACA,cAAM,SAA+B;AAAA,UACnC,QAAQ,OAAO,OAAO,MAAM;AAAA,UAC5B,QAAQ,OAAO,OAAO,MAAM;AAAA,UAC5B,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,YAAY,OAAO;AAAA,QACrB;AACA,eAAO;AAAA,MACT;AAAA,MACA,OAAO,OAAO,EAAE,SAAS,MAAM,KAAK,QAAQ,WAAW,YAAY,MAAM;AACvE,YAAI,cAAc,QAAQ,UAAU,MAAM;AACxC,gBAAM,KAAK,aAAa;AAAA,YACtB,QAAQ;AAAA,YACR,MAAM,YAAY,QAAQ,IAAI,OAAO;AAAA,YACrC,MACE,cAAc,OAAO,EAAE,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,QAAQ,QAAQ,OAAO;AAAA,YACrF,QAAQ;AAAA,UACV,CAAC;AACD,cAAI,cAAc,KAAM;AAAA,QAC1B;AACA,YAAK,QAAQ,QAAQ,KAAK,aAAa,KAAM,QAAQ,MAAM;AACzD,gBAAM,OAA8B;AAAA,YAClC,QAAQ,QAAQ;AAAA,UAClB;AACA,cAAI,QAAQ,QAAQ,KAAK,aAAa,EAAG,MAAK,aAAa,aAAa,IAAI;AAC5E,cAAI,QAAQ,KAAM,MAAK,MAAM;AAC7B,gBAAM,KAAK,aAAa;AAAA,YACtB,QAAQ;AAAA,YACR,MAAM,YAAY,QAAQ,IAAI,QAAQ;AAAA,YACtC,MAAM;AAAA,YACN,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,cAAc,CAAC,EAAE,gBAAgB,YAAY,MAC3C,KAAK,mCAAmC;AAAA,QACtC,QAAQ,EAAE,gBAAgB,YAAY,EAAE,SAAS,YAAY,KAAK,EAAE;AAAA,QACpE,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,MAAM,yBAAyB,mBAAmB,8BAA8B,MAAM,WAAW,CAAC,CAAC;AAAA,QACnG,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC,CAAC;AAAA,MACH,kBAAkB,OAAO,EAAE,QAAQ,aAAa,QAAQ,oBAAoB,MAAM;AAChF,cAAM,SAAS,MAAM,OAAO,WAAW,QAAQ;AAC/C,cAAM,YAAY,UAAU;AAC5B,cAAM,iBAAiB,EAAE,GAAG,QAAQ,YAAY,EAAE,SAAS,YAAY,OAAO,EAAE;AAChF,cAAM,SAAS,MAAM,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,GAAG;AAAA,UACvD,QAAQ;AAAA,UACR,QAAQ,wBAAwB,OAAO,SAAS;AAAA,UAChD;AAAA,UACA,MAAM,yBAAyB,mBAAmB,8BAA8B,MAAM,WAAW,CAAC,CAAC,IAAI,wBAAwB,OAAO,6BAA6B,YAAY,kBAAkB,gBAAgB;AAAA,UACjN,MAAM,6BAA6B,MAAM,MAAM;AAAA,UAC/C,QAAQ,YAAY,QAAQ,GAAM;AAAA,QACpC,CAAC;AACD,YAAI,CAAC,OAAO,GAAI,OAAM,IAAI,MAAM,sDAAsD;AAAA,MACxF;AAAA,MACA,WAAW,CAAC,EAAE,QAAQ,YAAY,MAChC,KAAK,6BAA6B;AAAA,QAChC;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,yBAAyB,mBAAmB,8BAA8B,MAAM,WAAW,CAAC,CAAC;AAAA,QACnG,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC,CAAC;AAAA,MACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,MAC1B,KAAK,8BAA8B;AAAA,QACjC;AAAA,QACA,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,MAAM,gCAAgC,MAAM,KAAK;AAAA,QACjD,QAAQ,YAAY,QAAQ,IAAO;AAAA,MACrC,CAAC;AAAA,MACH,OAAO,CAAC,EAAE,QAAQ,QAAQ,MACxB,KAAK,yBAAyB;AAAA,QAC5B;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,qBAAqB,mBAAmB,OAAO,CAAC;AAAA,QACtD,QAAQ,YAAY,QAAQ,IAAO;AAAA,MACrC,CAAC;AAAA,MACH,UAAU,CAAC,EAAE,QAAQ,QAAQ,MAC3B,KAAK,yBAAyB;AAAA,QAC5B;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,qBAAqB,mBAAmB,OAAO,CAAC;AAAA,QACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC,CAAC;AAAA,MACH,QAAQ,OAAO,EAAE,QAAQ,SAAS,YAAY,MAAM;AAClD,cAAM,KAAK,aAAa;AAAA,UACtB;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,EAAE,YAAY;AAAA,UACpB,MAAM,qBAAqB,mBAAmB,OAAO,CAAC;AAAA,UACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,QACpC,CAAC;AAAA,MACH;AAAA,MACA,SAAS,OAAO;AAAA,QACd;AAAA,QACA;AAAA,MACF,MAGM;AACJ,cAAM,SAAS,MAAM,OAAO,WAAW,QAAQ;AAC/C,cAAM,YAAY,UAAU;AAC5B,cAAM,iBAAiB,EAAE,GAAG,QAAQ,YAAY,EAAE,SAAS,YAAY,OAAO,EAAE;AAChF,cAAM,KAAK,aAAa;AAAA,UACtB,QAAQ;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,yBAAyB,mBAAmB,8BAA8B,MAAM,WAAW,CAAC,CAAC,IAAI,YAAY,0BAA0B,SAAS;AAAA,UACtJ,QAAQ,YAAY,QAAQ,GAAM;AAAA,QACpC,CAAC;AAAA,MACH;AAAA,MACA,SAAS,OAAO,EAAE,QAAQ,YAAY,MAAM;AAC1C,cAAM,KAAK,aAAa;AAAA,UACtB;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,yBAAyB,mBAAmB,8BAA8B,MAAM,WAAW,CAAC,CAAC;AAAA,UACnG,QAAQ,YAAY,QAAQ,GAAM;AAAA,QACpC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,wBAAwB,OAAO,EAAE,QAAQ,UAAU,UAAU,MAAM;AACjE,YAAM,SAAS,MAAM,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,GAAG;AAAA,QAC7D;AAAA,QACA,QAAQ;AAAA,QACR,MACE,iBACA,mBAAmB,QAAQ,IAC3B,iBACA,mBAAmB,SAAS,IAC5B;AAAA,MACJ,CAAC;AACD,UAAI,CAAC,OAAO,SAAU,OAAM,IAAI,MAAM,iDAAiD;AAAA,IACzF;AAAA,IACA,kBAAkB,CAAC,EAAE,QAAQ,UAAU,WAAW,QAAQ,MAAM;AAC9D,YAAM,SACJ,QAAQ,cAAc,2BAA2B,YAC7C,eACA,eAAe,UACb,MAAM,mBAAmB,QAAQ,SAAS,IAC1C;AACR,YAAM,QAAgC,CAAC;AACvC,UAAI,aAAa,KAAM,OAAM,YAAY;AACzC,UAAI,YAAY,QAAS,OAAM,SAAS,QAAQ;AAChD,UAAI,YAAY,WAAW,QAAQ,UAAU,KAAM,OAAM,SAAS,QAAQ;AAC1E,UAAI,WAAW,WAAW,QAAQ,SAAS,KAAM,OAAM,QAAQ,OAAO,QAAQ,KAAK;AACnF,aAAO,KAAK,+BAA+B;AAAA,QACzC;AAAA,QACA,QAAQ,QAAQ,cAAc,2BAA2B,OAAO,WAAW;AAAA,QAC3E,MAAM,iBAAiB,mBAAmB,QAAQ,IAAI,eAAe;AAAA,QACrE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,WAAW,CAAC,EAAE,QAAQ,SAAS,MAC7B;AAAA,MACE,KAAK,cAAc;AAAA,QACjB;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,eAAe,mBAAmB,QAAQ,CAAC;AAAA,MACnD,CAAC;AAAA,IACH;AAAA,IACF,aAAa,OAAO,EAAE,OAAO,OAC1B,MAAM,KAAK,eAAe,EAAE,QAAQ,QAAQ,OAAO,MAAM,cAAc,CAAC,GAAG;AAAA,IAC9E,gBAAgB,CAAC,EAAE,QAAQ,KAAK,MAC9B;AAAA,MACE,KAAK,cAAc;AAAA,QACjB;AAAA,QACA,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,MAAM,EAAE,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IACF,OAAO,OAAO,EAAE,QAAQ,UAAU,WAAW,WAAW,QAAQ,OAAO,gBAAgB,MACrF;AAAA,MACE,MAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,eAAe,mBAAmB,QAAQ,CAAC;AAAA,QACjD,MAAM,EAAE,WAAW,WAAW,QAAQ,OAAO,gBAAgB;AAAA,MAC/D,CAAC;AAAA,IACH;AAAA,IACF,cAAc,OAAO,EAAE,QAAQ,UAAU,WAAW,WAAW,OAAO,MACpE;AAAA,MACE,MAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,eAAe,mBAAmB,QAAQ,CAAC;AAAA,QACjD,MAAM,EAAE,WAAW,WAAW,OAAO;AAAA,MACvC,CAAC;AAAA,IACH;AAAA,IACF,OAAO,OAAO,EAAE,QAAQ,OAAO,MAAM,MACnC;AAAA,MACE,MAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,UAAU,OAAO,QAAQ;AAAA,QAC/B,MAAM,EAAE,MAAM;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,IACF,SAAS,OAAO,EAAE,QAAQ,MAAM,MAAM;AACpC,YAAM,KAAK,aAAa,EAAE,QAAQ,QAAQ,UAAU,MAAM,UAAU,OAAO,EAAE,EAAE,CAAC;AAAA,IAClF;AAAA,IACA;AAAA,IACA,YAAY,CAAC,EAAE,QAAQ,SAAS,MAC9B,KAAK,kBAAkB;AAAA,MACrB;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,eAAe,mBAAmB,QAAQ,CAAC;AAAA,IACnD,CAAC;AAAA,IACH,iBAAiB,OAAO,EAAE,QAAQ,MAAM,OACrC,MAAM,KAAK,eAAe,EAAE,QAAQ,QAAQ,OAAO,MAAM,UAAU,OAAO,MAAM,EAAE,CAAC,GACjF;AAAA,IACL,MAAM,OAAO,EAAE,QAAQ,OAAO,QAAQ,QAAQ,UAAU,YAAY,OAAO,OAEvE,MAAM,KAAK,gBAAgB;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,UAAU,OAAO,QAAQ,mBAAmB,MAAM,CAAC,IAAI,MAAM,EAAE;AAAA,MACrE,MAAM,cAAc,OAAO,EAAE,SAAS,IAAI,EAAE,UAAU,WAAW;AAAA,MACjE;AAAA,IACF,CAAC,GACD,cAAc;AAAA,IAClB,QAAQ,CAAC,EAAE,QAAQ,UAAU,WAAW,SAAS,MAC/C,KAAK,cAAc;AAAA,MACjB;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,eAAe,mBAAmB,QAAQ,CAAC;AAAA,MACjD,MAAM,EAAE,WAAW,SAAS;AAAA,IAC9B,CAAC;AAAA,EACL;AACF;",
6
+ "names": ["DEVICE_API_ACTING_USER_HEADER", "DeviceApiLeaseAccess", "DeviceApiCapability", "DeviceApiClientErrorCode", "key"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/sandboxes/device.ts"],
4
- "sourcesContent": ["import { ProcessInspectionOperation } from '@finalai/agent-protocol/process-inspection';\nimport {\n SandboxAccess,\n toolDescriptorSchema,\n type JsonObject,\n type JsonValue,\n type ToolDescriptorIface,\n} from '@finalai/agent-provider';\nimport {\n defineSandboxProvider,\n SandboxCapability,\n type McpChannelEndpointIface,\n type NetworkSandboxSessionIface,\n type SandboxProviderHandleIface,\n type SandboxProviderIface,\n type SandboxProviderImplementationIface,\n type SandboxStartContextIface,\n} from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport { commandEncoderFor } from \"./commands.mjs\";\nimport {\n createDeviceApiClient,\n DEVICE_API_COMPUTER_USE_SERVER,\n DeviceApiCapability,\n DeviceApiLeaseAccess,\n type DeviceApiCallerIface,\n type DeviceApiConfigIface,\n type DeviceApiDeviceIface,\n type DeviceApiFetchIface,\n type DeviceApiLeaseIface,\n} from \"./device-api.mjs\";\nimport { computerOpenOptionsSchema, deviceEnvironmentSchema } from \"./environment.mjs\";\nimport { createMcpPipe } from \"./mcp-pipe.mjs\";\nimport { sandboxPathsFor, SandboxPlatform } from \"./paths.mjs\";\nimport { createTransportSession, sessionForAccess } from \"./session.mjs\";\n\nexport { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from \"./device-api.mjs\";\nexport type { DeviceApiConfigIface, DeviceApiFetchIface } from \"./device-api.mjs\";\nexport { DEVICE_API_KEY_SECRET, DEVICE_SANDBOX_SPECIFIER } from \"./environment.mjs\";\nexport { SandboxPlatform } from \"./paths.mjs\";\n\n// The `device` sandbox: a user's own computer running Final Device. The\n// provider is a client of the Device API (device-api-service), the Device's\n// backend, exactly as a vendor provider is a client of its vendor's API: it\n// leases a workspace there and drives files, processes, computer use and the\n// screen through that API with the organization's own API key. It knows nothing of how\n// the Device API reaches the Device (DEVICE_DIRECT_ENDPOINT.md). A Device is one\n// person's machine: every call names the user the session acts for\n// (ctx.actingUser, read per call since it changes between turns), and the Device\n// API refuses a Device that user does not own.\n\nexport interface DeviceSandboxProviderConfigIface {\n // The Device API origin, and each organization's own API key for it.\n api: DeviceApiConfigIface;\n fetch?: DeviceApiFetchIface;\n // Device leases last at most an hour; renewed while in use.\n leaseTtlMs?: number;\n // How long start and resume wait for an offline Device to connect (default 0:\n // an offline Device fails at once); polled from 500 ms, doubling to 5 s.\n onlineWaitMs?: number;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string, fields?: Record<string, string | number | boolean | null>) => void;\n randomUuid?: () => string;\n now?: () => number;\n}\n\nconst deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });\nconst deviceStateSchema = z.object({\n version: z.literal(1),\n deviceId: z.string(),\n generation: z.number().int().min(1),\n access: z.nativeEnum(SandboxAccess),\n});\nexport type DeviceSandboxArtifact = z.infer<typeof deviceArtifactSchema>;\nexport type DeviceSandboxState = z.infer<typeof deviceStateSchema>;\n\nexport const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;\n\nexport interface DeviceSandboxSessionIface extends NetworkSandboxSessionIface {\n readonly capabilities: readonly SandboxCapability[];\n}\n\nexport type DeviceSandboxProviderIface = SandboxProviderIface<\n JsonValue,\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n>;\n\n// The Device API's recorded tools as tool descriptors; one that does not fit\n// the contract is left out.\nconst descriptorsOf = (tools: readonly JsonObject[]): ToolDescriptorIface[] =>\n tools.flatMap((tool) => {\n const parsed = toolDescriptorSchema.safeParse({\n ...tool,\n description: tool.description ?? tool.title ?? tool.name,\n });\n return parsed.success ? [parsed.data] : [];\n });\n\nconst LEASE_RENEW_MARGIN_MS = 5 * 60_000;\nconst ONLINE_POLL_MS = 500;\nconst ONLINE_POLL_MAX_MS = 5_000;\nconst MAX_LEASE_TTL_MS = 60 * 60_000;\nconst MIN_LEASE_TTL_MS = 60_000;\n\nconst platformOf = (platform: string): SandboxPlatform =>\n platform === 'windows' ? SandboxPlatform.Windows : SandboxPlatform.Posix;\n\nexport const createDeviceSandboxProvider = (\n config: DeviceSandboxProviderConfigIface,\n): DeviceSandboxProviderIface => {\n const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());\n const now = config.now ?? (() => Date.now());\n const log = config.log ?? (() => undefined);\n const leaseTtlMs = Math.max(\n MIN_LEASE_TTL_MS,\n Math.min(config.leaseTtlMs ?? 15 * 60_000, MAX_LEASE_TTL_MS),\n );\n const sleep =\n config.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const onlineWaitMs = config.onlineWaitMs ?? 0;\n const api = createDeviceApiClient({ api: config.api, fetch: config.fetch, now });\n\n // A Device runs when it is connected to the Device API; nothing is leased before.\n const awaitOnline = async (\n caller: DeviceApiCallerIface,\n deviceId: string,\n timeoutMs: number,\n ): Promise<DeviceApiDeviceIface> => {\n const deadline = now() + timeoutMs;\n let pollMs = ONLINE_POLL_MS;\n for (;;) {\n const device = await api.getDevice({ caller, deviceId });\n if (device == null) throw new Error(`device ${deviceId} was not found`);\n if (device.online) return device;\n const left = deadline - now();\n if (left <= 0) throw new Error(`${device.displayName} is not connected`);\n await sleep(Math.min(pollMs, left));\n pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);\n }\n };\n\n const attach = async (args: {\n ctx: SandboxStartContextIface;\n deviceId: string;\n afterGeneration: number | null;\n passive?: boolean;\n access: SandboxAccess;\n }): Promise<{\n handle: SandboxProviderHandleIface<DeviceSandboxSessionIface>;\n generation: number;\n }> => {\n const { ctx, access } = args;\n const caller: DeviceApiCallerIface = {\n organizationId: ctx.session.organizationId,\n actingUser: ctx.actingUser,\n };\n const bindingId = ctx.session.bindingId;\n // The provider's awaitRunning, before the lease.\n const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);\n // Processes need the Device's write lease, so execute access does too.\n if (access !== SandboxAccess.Read && !device.writable) {\n throw new Error(`${device.displayName} does not permit writes`);\n }\n const leaseAccess =\n access === SandboxAccess.Read ? DeviceApiLeaseAccess.Read : DeviceApiLeaseAccess.Write;\n let current: DeviceApiLeaseIface =\n args.passive === true\n ? await api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : await api.lease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n ttlMs: leaseTtlMs,\n afterGeneration: args.afterGeneration ?? undefined,\n });\n if (\n args.passive === true &&\n (current.deviceId !== device.id ||\n current.access !== leaseAccess ||\n current.generation < (args.afterGeneration ?? 0) ||\n current.expiresAtMs <= now())\n )\n throw new Error('The existing Device lease is unavailable');\n let renewing: Promise<DeviceApiLeaseIface> | null = null;\n // The current lease handle, renewed shortly before it lapses.\n const lease = async (): Promise<string> => {\n if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {\n renewing ??= (\n args.passive === true\n ? api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })\n ).finally(() => {\n renewing = null;\n });\n current = await renewing;\n }\n return current.lease;\n };\n let released = false;\n const release = async (): Promise<void> => {\n if (released) return;\n released = true;\n await api.release({ caller, lease: current.lease });\n };\n\n const platform = platformOf(current.platform);\n const paths = sandboxPathsFor(platform);\n const session = createTransportSession({\n id: `${device.id}:${bindingId}`,\n description: `${device.displayName} (Final Device)`,\n workspaceRoot: current.workspaceRoot,\n transport: api.transport({ caller, lease }),\n baseEnv: async () => ({}),\n randomUuid,\n paths,\n commands: commandEncoderFor(platform),\n network: {\n ports: [],\n getPortEndpoint: async () => {\n throw new Error('Final Device sandboxes expose no ports');\n },\n stop: release,\n destroy: release,\n },\n });\n\n const offered = new Set(current.capabilities);\n const processes = offered.has(DeviceApiCapability.Process);\n const localMcp = offered.has(DeviceApiCapability.LocalMcp);\n const computerUse = offered.has(DeviceApiCapability.ComputerUse);\n const capabilities = [SandboxCapability.Files];\n if (processes) capabilities.push(SandboxCapability.Process);\n if (localMcp) capabilities.push(SandboxCapability.LocalMcp);\n if (computerUse) capabilities.push(SandboxCapability.ComputerUse);\n if (device.screenReady) capabilities.push(SandboxCapability.Screen);\n\n // The Device's computer-use server, or one of its local MCP servers, is a byte\n // pipe in the Device API (`/v1/leases/:lease/mcp/:server/{open,write,read,close}`):\n // a channel endpoint whoever runs this handle opens in process.\n const channelEndpoint = (server: string): McpChannelEndpointIface => ({\n name: server,\n connect: async () => {\n const pipe = openMcpPipe(server);\n return {\n send: (message, options) => pipe.send(message, options?.abortSignal),\n receive: (options) => pipe.receive(options?.abortSignal),\n close: () => pipe.close(),\n };\n },\n });\n const openMcpPipe = (server: string) => {\n const clientId = randomUuid();\n const request = async (\n action: 'open' | 'write' | 'read' | 'close',\n dataBase64?: string,\n signal?: AbortSignal,\n ): Promise<string> =>\n api.pipe({\n caller,\n lease: await lease(),\n server,\n action,\n clientId,\n dataBase64,\n signal,\n });\n return createMcpPipe({\n wire: {\n open: async (signal) => {\n await request('open', undefined, signal);\n },\n write: async (data, signal) => {\n await request('write', data, signal);\n },\n read: (signal) => request('read', undefined, signal),\n close: async () => {\n await request('close').catch(() => undefined);\n },\n },\n });\n };\n const sandbox: DeviceSandboxSessionIface = { ...session, capabilities };\n if (computerUse) {\n sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);\n }\n if (localMcp) {\n sandbox.localMcp = async () =>\n (await api.localMcpServers({ caller, lease: await lease() })).map(channelEndpoint);\n }\n if (device.screenReady) {\n sandbox.screen = (options) =>\n api.screen({\n caller,\n deviceId: device.id,\n sessionId: randomUuid(),\n viewOnly: options?.viewOnly ?? true,\n });\n }\n log('device_sandbox.attached', { deviceId: device.id, generation: current.generation });\n return {\n generation: current.generation,\n handle: {\n // The Device enforces the read lease too; the view refuses before a round trip.\n sandbox: sessionForAccess(sandbox, access),\n onSandboxStop: release,\n onSandboxDelete: release,\n onRuntimeShutdown: async () => undefined,\n },\n };\n };\n\n const environment = (\n rawOptions?: JsonValue,\n ): SandboxProviderImplementationIface<\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n > => {\n const parsed = deviceEnvironmentSchema.safeParse(rawOptions ?? {});\n if (!parsed.success) throw new Error(`invalid device environment: ${parsed.error.message}`);\n const options = parsed.data;\n // The Device the environment names; the Device API checks on every call\n // that the acting user owns it.\n const resolveDeviceId = async (caller: DeviceApiCallerIface): Promise<string> => {\n if ('deviceId' in options) return options.deviceId;\n if ('pairing' in options) {\n const device = await api.resolvePairing({ caller, code: options.pairing });\n if (device == null) throw new Error('the Final Device pairing code is unknown or expired');\n return device.id;\n }\n // An unnamed binding uses the only Device of the user the session acts\n // for (the API lists only that user's Devices), never another member's.\n const devices = await api.listDevices({ caller });\n const [only] = devices;\n if (devices.length !== 1 || only == null) {\n throw new Error(\n devices.length === 0\n ? 'the user this session acts for has no Final Device'\n : 'the user this session acts for has several Final Devices; name one by id or pairing code',\n );\n }\n return only.id;\n };\n return {\n processes: async (ctx, rawState, request) => {\n const state = deviceStateSchema.parse(rawState);\n if (\n request.operation === ProcessInspectionOperation.Stop &&\n state.access === SandboxAccess.Read\n )\n throw new Error('stopping processes requires execute access');\n return api.inspectProcesses({\n caller: ctx,\n deviceId: state.deviceId,\n bindingId: ctx.bindingId,\n request,\n });\n },\n // What a lease on this Device grants, known from the registry without\n // leasing (the Device API's rule: processes, local MCP and computer use with\n // a write lease on a Device that allows processes). The computer-use\n // driver's readiness is checked when leased.\n capabilities: async (ctx) => {\n const device = await api.getDevice({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n if (device == null) return null;\n const offered = [SandboxCapability.Files];\n if (device.writable && device.allowProcesses) {\n offered.push(\n SandboxCapability.Process,\n SandboxCapability.LocalMcp,\n SandboxCapability.ComputerUse,\n );\n }\n if (device.screenReady) offered.push(SandboxCapability.Screen);\n return offered;\n },\n // What the Device's servers last listed through the Device API (it records\n // them from the MCP pipes), so the projection lists them without leasing.\n // A Device the API has never seen list gives unknown; its first lease does.\n mcpCatalog: async (ctx) => {\n const catalog = await api.mcpCatalog({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n const local = catalog.localServers;\n const localMcp: Record<string, ToolDescriptorIface[]> = {};\n let localKnown = local != null;\n for (const server of local?.servers ?? []) {\n if (server.tools == null) localKnown = false;\n else localMcp[server.name] = descriptorsOf(server.tools);\n }\n return {\n computerUse:\n catalog.computerUse == null ? null : descriptorsOf(catalog.computerUse.tools),\n localMcp: localKnown ? localMcp : null,\n };\n },\n awaitRunning: async (ctx, rawState, { timeoutMs }) => {\n const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);\n await awaitOnline(\n { organizationId: ctx.session.organizationId, actingUser: ctx.actingUser },\n deviceId,\n timeoutMs,\n );\n },\n artifactKey: async (ctx) => `device:${await resolveDeviceId(ctx)}`,\n prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(ctx) }),\n start: async (ctx, openOptions, rawArtifact) => {\n const artifact = deviceArtifactSchema.parse(rawArtifact);\n const access = computerOpenOptionsSchema.parse(openOptions ?? {}).access;\n const { handle, generation } = await attach({\n ctx,\n deviceId: artifact.deviceId,\n afterGeneration: null,\n access,\n });\n return { handle, state: { version: 1, deviceId: artifact.deviceId, generation, access } };\n },\n // A Device never takes a lease back at an old generation once it lapsed,\n // so every attach leases a later one.\n resume: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n })\n ).handle;\n },\n resumePassive: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n passive: true,\n })\n ).handle;\n },\n onSessionEnd: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n await api.releaseWorkspaceLeases({\n caller: { organizationId: ctx.session.organizationId, actingUser: ctx.actingUser },\n deviceId: state.deviceId,\n bindingId: ctx.session.bindingId,\n });\n },\n // The lease lapses on its own; the next generation retires its processes.\n park: async (_ctx, _artifact, rawState) => deviceStateSchema.parse(rawState),\n };\n };\n\n return defineSandboxProvider({\n name: 'device',\n stateProtocolVersion: 1,\n environment,\n });\n};\n"],
4
+ "sourcesContent": ["import { ProcessInspectionOperation } from '@finalai/agent-protocol/process-inspection';\nimport {\n SandboxAccess,\n toolDescriptorSchema,\n type JsonObject,\n type JsonValue,\n type ToolDescriptorIface,\n} from '@finalai/agent-provider';\nimport {\n defineSandboxProvider,\n SandboxCapability,\n type McpChannelEndpointIface,\n type NetworkSandboxSessionIface,\n type SandboxProviderHandleIface,\n type SandboxProviderIface,\n type SandboxProviderImplementationIface,\n type SandboxStartContextIface,\n} from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport { commandEncoderFor } from \"./commands.mjs\";\nimport {\n createDeviceApiClient,\n DEVICE_API_COMPUTER_USE_SERVER,\n DeviceApiCapability,\n DeviceApiLeaseAccess,\n type DeviceApiCallerIface,\n type DeviceApiConfigIface,\n type DeviceApiDeviceIface,\n type DeviceApiFetchIface,\n type DeviceApiLeaseIface,\n} from \"./device-api.mjs\";\nimport { computerOpenOptionsSchema, deviceEnvironmentSchema } from \"./environment.mjs\";\nimport { createMcpPipe } from \"./mcp-pipe.mjs\";\nimport { sandboxPathsFor, SandboxPlatform } from \"./paths.mjs\";\nimport { createTransportSession, sessionForAccess } from \"./session.mjs\";\n\nexport { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from \"./device-api.mjs\";\nexport type { DeviceApiConfigIface, DeviceApiFetchIface } from \"./device-api.mjs\";\nexport { DEVICE_API_KEY_SECRET, DEVICE_SANDBOX_SPECIFIER } from \"./environment.mjs\";\nexport { SandboxPlatform } from \"./paths.mjs\";\n\n// The `device` sandbox: a user's own computer running Final Device. The\n// provider is a client of the Device API (device-api-service), the Device's\n// backend, exactly as a vendor provider is a client of its vendor's API: it\n// leases a workspace there and drives files, processes, computer use and the\n// screen through that API with host-provided credentials. It knows nothing of how\n// the Device API reaches the Device (DEVICE_DIRECT_ENDPOINT.md). A Device is one\n// person's machine: every call names the user the session acts for\n// (ctx.actingUser, read per call since it changes between turns), and the Device\n// API refuses a Device that user does not own.\n\nexport interface DeviceSandboxProviderConfigIface {\n // The Device API origin and a credential callback scoped to organization/user.\n api: DeviceApiConfigIface;\n fetch?: DeviceApiFetchIface;\n // Device leases last at most an hour; renewed while in use.\n leaseTtlMs?: number;\n // How long start and resume wait for an offline Device to connect (default 0:\n // an offline Device fails at once); polled from 500 ms, doubling to 5 s.\n onlineWaitMs?: number;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string, fields?: Record<string, string | number | boolean | null>) => void;\n randomUuid?: () => string;\n now?: () => number;\n}\n\nconst deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });\nconst deviceStateSchema = z.object({\n version: z.literal(1),\n deviceId: z.string(),\n generation: z.number().int().min(1),\n access: z.nativeEnum(SandboxAccess),\n});\nexport type DeviceSandboxArtifact = z.infer<typeof deviceArtifactSchema>;\nexport type DeviceSandboxState = z.infer<typeof deviceStateSchema>;\n\nexport const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;\n\nexport interface DeviceSandboxSessionIface extends NetworkSandboxSessionIface {\n readonly capabilities: readonly SandboxCapability[];\n}\n\nexport type DeviceSandboxProviderIface = SandboxProviderIface<\n JsonValue,\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n>;\n\n// The Device API's recorded tools as tool descriptors; one that does not fit\n// the contract is left out.\nconst descriptorsOf = (tools: readonly JsonObject[]): ToolDescriptorIface[] =>\n tools.flatMap((tool) => {\n const parsed = toolDescriptorSchema.safeParse({\n ...tool,\n description: tool.description ?? tool.title ?? tool.name,\n });\n return parsed.success ? [parsed.data] : [];\n });\n\nconst LEASE_RENEW_MARGIN_MS = 5 * 60_000;\nconst ONLINE_POLL_MS = 500;\nconst ONLINE_POLL_MAX_MS = 5_000;\nconst MAX_LEASE_TTL_MS = 60 * 60_000;\nconst MIN_LEASE_TTL_MS = 60_000;\n\nconst platformOf = (platform: string): SandboxPlatform =>\n platform === 'windows' ? SandboxPlatform.Windows : SandboxPlatform.Posix;\n\nexport const createDeviceSandboxProvider = (\n config: DeviceSandboxProviderConfigIface,\n): DeviceSandboxProviderIface => {\n const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());\n const now = config.now ?? (() => Date.now());\n const log = config.log ?? (() => undefined);\n const leaseTtlMs = Math.max(\n MIN_LEASE_TTL_MS,\n Math.min(config.leaseTtlMs ?? 15 * 60_000, MAX_LEASE_TTL_MS),\n );\n const sleep =\n config.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const onlineWaitMs = config.onlineWaitMs ?? 0;\n const api = createDeviceApiClient({ api: config.api, fetch: config.fetch, now });\n\n // A Device runs when it is connected to the Device API; nothing is leased before.\n const awaitOnline = async (\n caller: DeviceApiCallerIface,\n deviceId: string,\n timeoutMs: number,\n ): Promise<DeviceApiDeviceIface> => {\n const deadline = now() + timeoutMs;\n let pollMs = ONLINE_POLL_MS;\n for (;;) {\n const device = await api.getDevice({ caller, deviceId });\n if (device == null) throw new Error(`device ${deviceId} was not found`);\n if (device.online) return device;\n const left = deadline - now();\n if (left <= 0) throw new Error(`${device.displayName} is not connected`);\n await sleep(Math.min(pollMs, left));\n pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);\n }\n };\n\n const attach = async (args: {\n ctx: SandboxStartContextIface;\n deviceId: string;\n afterGeneration: number | null;\n passive?: boolean;\n access: SandboxAccess;\n }): Promise<{\n handle: SandboxProviderHandleIface<DeviceSandboxSessionIface>;\n generation: number;\n }> => {\n const { ctx, access } = args;\n const caller: DeviceApiCallerIface = {\n organizationId: ctx.session.organizationId,\n actingUser: ctx.actingUser,\n };\n const bindingId = ctx.session.bindingId;\n // The provider's awaitRunning, before the lease.\n const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);\n // Processes need the Device's write lease, so execute access does too.\n if (access !== SandboxAccess.Read && !device.writable) {\n throw new Error(`${device.displayName} does not permit writes`);\n }\n const leaseAccess =\n access === SandboxAccess.Read ? DeviceApiLeaseAccess.Read : DeviceApiLeaseAccess.Write;\n let current: DeviceApiLeaseIface =\n args.passive === true\n ? await api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : await api.lease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n ttlMs: leaseTtlMs,\n afterGeneration: args.afterGeneration ?? undefined,\n });\n if (\n args.passive === true &&\n (current.deviceId !== device.id ||\n current.access !== leaseAccess ||\n current.generation < (args.afterGeneration ?? 0) ||\n current.expiresAtMs <= now())\n )\n throw new Error('The existing Device lease is unavailable');\n let renewing: Promise<DeviceApiLeaseIface> | null = null;\n // The current lease handle, renewed shortly before it lapses.\n const lease = async (): Promise<string> => {\n if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {\n renewing ??= (\n args.passive === true\n ? api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })\n ).finally(() => {\n renewing = null;\n });\n current = await renewing;\n }\n return current.lease;\n };\n let released = false;\n const release = async (): Promise<void> => {\n if (released) return;\n released = true;\n await api.release({ caller, lease: current.lease });\n };\n\n const platform = platformOf(current.platform);\n const paths = sandboxPathsFor(platform);\n const session = createTransportSession({\n id: `${device.id}:${bindingId}`,\n description: `${device.displayName} (Final Device)`,\n workspaceRoot: current.workspaceRoot,\n transport: api.transport({ caller, lease }),\n baseEnv: async () => ({}),\n randomUuid,\n paths,\n commands: commandEncoderFor(platform),\n network: {\n ports: [],\n getPortEndpoint: async () => {\n throw new Error('Final Device sandboxes expose no ports');\n },\n stop: release,\n destroy: release,\n },\n });\n\n const offered = new Set(current.capabilities);\n const processes = offered.has(DeviceApiCapability.Process);\n const localMcp = offered.has(DeviceApiCapability.LocalMcp);\n const computerUse = offered.has(DeviceApiCapability.ComputerUse);\n const capabilities = [SandboxCapability.Files];\n if (processes) capabilities.push(SandboxCapability.Process);\n if (localMcp) capabilities.push(SandboxCapability.LocalMcp);\n if (computerUse) capabilities.push(SandboxCapability.ComputerUse);\n if (device.screenReady) capabilities.push(SandboxCapability.Screen);\n\n // The Device's computer-use server, or one of its local MCP servers, is a byte\n // pipe in the Device API (`/v1/leases/:lease/mcp/:server/{open,write,read,close}`):\n // a channel endpoint whoever runs this handle opens in process.\n const channelEndpoint = (server: string): McpChannelEndpointIface => ({\n name: server,\n connect: async () => {\n const pipe = openMcpPipe(server);\n return {\n send: (message, options) => pipe.send(message, options?.abortSignal),\n receive: (options) => pipe.receive(options?.abortSignal),\n close: () => pipe.close(),\n };\n },\n });\n const openMcpPipe = (server: string) => {\n const clientId = randomUuid();\n const request = async (\n action: 'open' | 'write' | 'read' | 'close',\n dataBase64?: string,\n signal?: AbortSignal,\n ): Promise<string> =>\n api.pipe({\n caller,\n lease: await lease(),\n server,\n action,\n clientId,\n dataBase64,\n signal,\n });\n return createMcpPipe({\n wire: {\n open: async (signal) => {\n await request('open', undefined, signal);\n },\n write: async (data, signal) => {\n await request('write', data, signal);\n },\n read: (signal) => request('read', undefined, signal),\n close: async () => {\n await request('close').catch(() => undefined);\n },\n },\n });\n };\n const sandbox: DeviceSandboxSessionIface = { ...session, capabilities };\n if (computerUse) {\n sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);\n }\n if (localMcp) {\n sandbox.localMcp = async () =>\n (await api.localMcpServers({ caller, lease: await lease() })).map(channelEndpoint);\n }\n if (device.screenReady) {\n sandbox.screen = (options) =>\n api.screen({\n caller,\n deviceId: device.id,\n sessionId: randomUuid(),\n viewOnly: options?.viewOnly ?? true,\n });\n }\n log('device_sandbox.attached', { deviceId: device.id, generation: current.generation });\n return {\n generation: current.generation,\n handle: {\n // The Device enforces the read lease too; the view refuses before a round trip.\n sandbox: sessionForAccess(sandbox, access),\n onSandboxStop: release,\n onSandboxDelete: release,\n onRuntimeShutdown: async () => undefined,\n },\n };\n };\n\n const environment = (\n rawOptions?: JsonValue,\n ): SandboxProviderImplementationIface<\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n > => {\n const parsed = deviceEnvironmentSchema.safeParse(rawOptions ?? {});\n if (!parsed.success) throw new Error(`invalid device environment: ${parsed.error.message}`);\n const options = parsed.data;\n // The Device the environment names; the Device API checks on every call\n // that the acting user owns it.\n const resolveDeviceId = async (caller: DeviceApiCallerIface): Promise<string> => {\n if ('deviceId' in options) return options.deviceId;\n if ('pairing' in options) {\n const device = await api.resolvePairing({ caller, code: options.pairing });\n if (device == null) throw new Error('the Final Device pairing code is unknown or expired');\n return device.id;\n }\n // An unnamed binding uses the only Device of the user the session acts\n // for (the API lists only that user's Devices), never another member's.\n const devices = await api.listDevices({ caller });\n const [only] = devices;\n if (devices.length !== 1 || only == null) {\n throw new Error(\n devices.length === 0\n ? 'the user this session acts for has no Final Device'\n : 'the user this session acts for has several Final Devices; name one by id or pairing code',\n );\n }\n return only.id;\n };\n return {\n processes: async (ctx, rawState, request) => {\n const state = deviceStateSchema.parse(rawState);\n if (\n request.operation === ProcessInspectionOperation.Stop &&\n state.access === SandboxAccess.Read\n )\n throw new Error('stopping processes requires execute access');\n return api.inspectProcesses({\n caller: ctx,\n deviceId: state.deviceId,\n bindingId: ctx.bindingId,\n request,\n });\n },\n // What a lease on this Device grants, known from the registry without\n // leasing (the Device API's rule: processes, local MCP and computer use with\n // a write lease on a Device that allows processes). The computer-use\n // driver's readiness is checked when leased.\n capabilities: async (ctx) => {\n const device = await api.getDevice({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n if (device == null) return null;\n const offered = [SandboxCapability.Files];\n if (device.writable && device.allowProcesses) {\n offered.push(\n SandboxCapability.Process,\n SandboxCapability.LocalMcp,\n SandboxCapability.ComputerUse,\n );\n }\n if (device.screenReady) offered.push(SandboxCapability.Screen);\n return offered;\n },\n // What the Device's servers last listed through the Device API (it records\n // them from the MCP pipes), so the projection lists them without leasing.\n // A Device the API has never seen list gives unknown; its first lease does.\n mcpCatalog: async (ctx) => {\n const catalog = await api.mcpCatalog({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n const local = catalog.localServers;\n const localMcp: Record<string, ToolDescriptorIface[]> = {};\n let localKnown = local != null;\n for (const server of local?.servers ?? []) {\n if (server.tools == null) localKnown = false;\n else localMcp[server.name] = descriptorsOf(server.tools);\n }\n return {\n computerUse:\n catalog.computerUse == null ? null : descriptorsOf(catalog.computerUse.tools),\n localMcp: localKnown ? localMcp : null,\n };\n },\n awaitRunning: async (ctx, rawState, { timeoutMs }) => {\n const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);\n await awaitOnline(\n { organizationId: ctx.session.organizationId, actingUser: ctx.actingUser },\n deviceId,\n timeoutMs,\n );\n },\n artifactKey: async (ctx) => `device:${await resolveDeviceId(ctx)}`,\n prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(ctx) }),\n start: async (ctx, openOptions, rawArtifact) => {\n const artifact = deviceArtifactSchema.parse(rawArtifact);\n const access = computerOpenOptionsSchema.parse(openOptions ?? {}).access;\n const { handle, generation } = await attach({\n ctx,\n deviceId: artifact.deviceId,\n afterGeneration: null,\n access,\n });\n return { handle, state: { version: 1, deviceId: artifact.deviceId, generation, access } };\n },\n // A Device never takes a lease back at an old generation once it lapsed,\n // so every attach leases a later one.\n resume: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n })\n ).handle;\n },\n resumePassive: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n passive: true,\n })\n ).handle;\n },\n onSessionEnd: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n await api.releaseWorkspaceLeases({\n caller: { organizationId: ctx.session.organizationId, actingUser: ctx.actingUser },\n deviceId: state.deviceId,\n bindingId: ctx.session.bindingId,\n });\n },\n // The lease lapses on its own; the next generation retires its processes.\n park: async (_ctx, _artifact, rawState) => deviceStateSchema.parse(rawState),\n };\n };\n\n return defineSandboxProvider({\n name: 'device',\n stateProtocolVersion: 1,\n environment,\n });\n};\n"],
5
5
  "mappings": "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",
6
6
  "names": ["SandboxPlatform"]
7
7
  }
@@ -836,6 +836,7 @@ def apply(request):
836
836
  raise RuntimeError('Workspace egress lease is expired or exceeds fifteen minutes')
837
837
  config['bootId'] = BOOT_ID
838
838
  config['deadlineMonotonic'] = time.monotonic() + min(remaining, 15 * 60 * 1000) / 1000
839
+ # The sandbox user can replace staging files; validate and install the same bytes.
839
840
  staged = pathlib.Path(request['helperPath'])
840
841
  try: fd = os.open(staged, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
841
842
  except FileNotFoundError as failure: