@tangle-network/agent-app 0.60.196 → 0.60.197
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat-routes/index.js +2 -1
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chunk-7WEPGGOW.js +474 -0
- package/dist/chunk-7WEPGGOW.js.map +1 -0
- package/dist/chunk-O7UX55EU.js +62 -0
- package/dist/chunk-O7UX55EU.js.map +1 -0
- package/dist/{chunk-L5JLOC2F.js → chunk-QDKY5TBG.js} +1 -59
- package/dist/chunk-QDKY5TBG.js.map +1 -0
- package/dist/sandbox/index.js +2 -1
- package/dist/tools/index.js +8 -6
- package/dist/workspace-apps/cloud-artifact-origin.d.ts +94 -0
- package/dist/workspace-apps/cloud-artifacts.d.ts +135 -0
- package/dist/workspace-apps/index.d.ts +4 -0
- package/dist/workspace-apps/index.js +55 -0
- package/dist/workspace-apps/index.js.map +1 -1
- package/dist/workspace-apps/react.d.ts +25 -0
- package/dist/workspace-apps/react.js +65 -0
- package/dist/workspace-apps/react.js.map +1 -0
- package/package.json +6 -1
- package/dist/chunk-L5JLOC2F.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/workspace-apps/data-bridge.ts","../../src/workspace-apps/index.ts"],"sourcesContent":["import type { WorkspaceAppRecord } from './index'\n\nconst protocol = 'tangle.workspace-app-data.v1'\nconst maxBytes = 65_536\nconst maxPending = 16\nconst keyPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/\nconst idPattern = /^[A-Za-z0-9_-]{1,80}$/\n\nexport interface WorkspaceAppDataEntry { value: string; revision: number }\nexport class WorkspaceAppDataConflict extends Error {\n constructor() {\n super('Workspace app data changed. Read it again before saving.')\n this.name = 'WorkspaceAppDataConflict'\n }\n}\nexport interface WorkspaceAppDataHostOptions {\n /** Product-authorized registration for the current viewer. */\n app: Pick<WorkspaceAppRecord, 'id' | 'previewUrl' | 'status'>\n frame: HTMLIFrameElement\n /** Product callbacks are bound to the authorized business and app namespace. */\n read(key: string): Promise<WorkspaceAppDataEntry | null>\n /** Null creates only if absent; a revision updates only that version. */\n write(key: string, value: string, expectedRevision: number | null): Promise<{ revision: number }>\n remove(key: string, expectedRevision: number): Promise<void>\n}\nexport interface WorkspaceAppDataHost { dispose(): void }\nexport interface WorkspaceAppDataClientOptions {\n appId: string\n /** Exact authenticated product origin, provided by the product. */\n parentOrigin: string\n}\nexport interface WorkspaceAppDataClient {\n ready(): Promise<void>\n read(key: string): Promise<WorkspaceAppDataEntry | null>\n write(key: string, value: string, expectedRevision: number | null): Promise<{ revision: number }>\n remove(key: string, expectedRevision: number): Promise<void>\n dispose(): void\n}\n\ntype Action = 'ready' | 'read' | 'write' | 'remove'\ntype Packet = {\n protocol: typeof protocol\n appId: string\n requestId: string\n action: Action\n key?: string\n value?: string\n expectedRevision?: number | null\n ok?: boolean\n entry?: WorkspaceAppDataEntry | null\n revision?: number\n error?: 'invalid' | 'conflict' | 'unavailable'\n}\nfunction object(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\nfunction key(value: unknown): value is string {\n return typeof value === 'string' && keyPattern.test(value) && value !== '.' && value !== '..'\n}\nfunction revision(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value > 0\n}\nfunction bounded(value: unknown): value is string {\n return typeof value === 'string' && value.length <= maxBytes && new TextEncoder().encode(value).byteLength <= maxBytes\n}\nfunction appId(value: string): string {\n if (!idPattern.test(value)) throw new Error('Invalid workspace app ID')\n return value\n}\nfunction origin(value: string): string {\n const url = new URL(value)\n if (url.origin !== value || (url.protocol !== 'https:' &&\n !(url.protocol === 'http:' && ['localhost', '127.0.0.1'].includes(url.hostname)))) {\n throw new Error('Invalid workspace app data origin')\n }\n return url.origin\n}\nfunction packet(value: unknown): value is Packet {\n return object(value) && value.protocol === protocol &&\n typeof value.appId === 'string' && typeof value.requestId === 'string' &&\n /^[A-Za-z0-9_-]{1,64}$/.test(value.requestId) &&\n typeof value.action === 'string' && ['ready', 'read', 'write', 'remove'].includes(value.action)\n}\n\n/** Bind one registered preview to a product-authorized data adapter. */\nexport function createWorkspaceAppDataHost(options: WorkspaceAppDataHostOptions): WorkspaceAppDataHost {\n const { app, frame } = options\n appId(app.id)\n if (app.status !== 'ready') throw new Error('Workspace app is not ready')\n const preview = new URL(app.previewUrl)\n const previewOrigin = origin(preview.origin)\n if (preview.protocol !== 'https:' || preview.username || preview.password || preview.search || preview.hash ||\n new URL(frame.src).href !== preview.href) {\n throw new Error('Iframe does not render the registered workspace app preview')\n }\n let closed = false\n let active = 0\n let messagesLeft = 64\n let nextWindow = Date.now() + 1000\n const onMessage = (event: MessageEvent<unknown>): void => {\n if (closed || event.source !== frame.contentWindow || event.origin !== previewOrigin ||\n !packet(event.data) || event.data.appId !== app.id) return\n const now = Date.now()\n if (now >= nextWindow) { messagesLeft = 64; nextWindow = now + 1000 }\n if (messagesLeft-- <= 0) return\n const message = event.data\n const reply = (result: Pick<Packet, 'ok' | 'entry' | 'revision' | 'error'>): void => {\n if (closed || event.source !== frame.contentWindow) return\n frame.contentWindow?.postMessage({\n protocol, appId: app.id, requestId: message.requestId, action: message.action, ...result,\n } satisfies Packet, previewOrigin)\n }\n if (message.action === 'ready') { reply({ ok: true }); return }\n if (!key(message.key) ||\n (message.action === 'write' && (!bounded(message.value) ||\n !(message.expectedRevision === null || revision(message.expectedRevision)))) ||\n (message.action === 'remove' && !revision(message.expectedRevision))) {\n reply({ ok: false, error: 'invalid' }); return\n }\n if (active >= maxPending) { reply({ ok: false, error: 'unavailable' }); return }\n active += 1\n const checkedKey = message.key\n void (async () => {\n try {\n if (message.action === 'read') {\n const entry = await options.read(checkedKey)\n if (entry !== null && (!bounded(entry.value) || !revision(entry.revision))) throw new Error('Invalid entry')\n reply({ ok: true, entry })\n } else if (message.action === 'write') {\n const result = await options.write(checkedKey, message.value as string, message.expectedRevision as number | null)\n if (!revision(result.revision)) throw new Error('Invalid revision')\n reply({ ok: true, revision: result.revision })\n } else {\n await options.remove(checkedKey, message.expectedRevision as number)\n reply({ ok: true })\n }\n } catch (error) {\n reply({ ok: false, error: error instanceof WorkspaceAppDataConflict ? 'conflict' : 'unavailable' })\n } finally { active -= 1 }\n })()\n }\n window.addEventListener('message', onMessage)\n return { dispose() { closed = true; window.removeEventListener('message', onMessage) } }\n}\n\n/** Use inside a registered preview; no product credential crosses the frame. */\nexport function createWorkspaceAppDataClient(options: WorkspaceAppDataClientOptions): WorkspaceAppDataClient {\n const id = appId(options.appId)\n const parentOrigin = origin(options.parentOrigin)\n if (window.parent === window) throw new Error('Workspace app data requires an embedded preview')\n const pending = new Map<string, {\n action: Action\n resolve(value: Packet): void\n reject(error: Error): void\n timeout: ReturnType<typeof setTimeout>\n }>()\n let closed = false\n let readyPromise: Promise<void> | undefined\n const onMessage = (event: MessageEvent<unknown>): void => {\n if (closed || event.source !== window.parent || event.origin !== parentOrigin || !packet(event.data) ||\n event.data.appId !== id || typeof event.data.ok !== 'boolean') return\n const waiting = pending.get(event.data.requestId)\n if (!waiting || waiting.action !== event.data.action) return\n pending.delete(event.data.requestId)\n clearTimeout(waiting.timeout)\n if (event.data.ok) waiting.resolve(event.data)\n else waiting.reject(event.data.error === 'conflict' ? new WorkspaceAppDataConflict() :\n new Error('Workspace app data unavailable'))\n }\n window.addEventListener('message', onMessage)\n const send = (action: Action, fields: Partial<Packet> = {}, timeoutMs = 10_000): Promise<Packet> => {\n if (closed) return Promise.reject(new Error('Workspace app data client is closed'))\n if (pending.size >= maxPending) return Promise.reject(new Error('Too many workspace app data requests'))\n const requestId = crypto.randomUUID()\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => { pending.delete(requestId); reject(new Error('Workspace app data timed out')) }, timeoutMs)\n pending.set(requestId, { action, resolve, reject, timeout })\n window.parent.postMessage({ protocol, appId: id, requestId, action, ...fields } satisfies Packet, parentOrigin)\n })\n }\n const ready = (): Promise<void> => {\n if (readyPromise) return readyPromise\n readyPromise = new Promise<void>((resolve, reject) => {\n const deadline = Date.now() + 10_000\n const attempt = (): void => {\n if (closed) { reject(new Error('Workspace app data client is closed')); return }\n if (Date.now() >= deadline) { reject(new Error('Workspace app data host did not become ready')); return }\n void send('ready', {}, 300).then(() => resolve(), () => setTimeout(attempt, 200))\n }\n attempt()\n }).catch((error: unknown) => { readyPromise = undefined; throw error })\n return readyPromise\n }\n return {\n ready,\n async read(value) {\n if (!key(value)) throw new Error('Invalid workspace app data key')\n await ready()\n const response = await send('read', { key: value })\n if (response.entry !== null && (!object(response.entry) ||\n !bounded(response.entry.value) || !revision(response.entry.revision))) {\n throw new Error('Invalid workspace app data response')\n }\n return response.entry ?? null\n },\n async write(value, data, expectedRevision) {\n if (!key(value) || !bounded(data) || !(expectedRevision === null || revision(expectedRevision))) {\n throw new Error('Invalid workspace app data write')\n }\n await ready()\n const response = await send('write', { key: value, value: data, expectedRevision })\n if (!revision(response.revision)) throw new Error('Invalid workspace app data response')\n return { revision: response.revision }\n },\n async remove(value, expectedRevision) {\n if (!key(value) || !revision(expectedRevision)) throw new Error('Invalid workspace app data remove')\n await ready()\n await send('remove', { key: value, expectedRevision })\n },\n dispose() {\n closed = true\n window.removeEventListener('message', onMessage)\n for (const waiting of pending.values()) {\n clearTimeout(waiting.timeout)\n waiting.reject(new Error('Workspace app data client is closed'))\n }\n pending.clear()\n },\n }\n}\n","/** A sandbox app registered in one product workspace. Products persist and authorize these rows. */\nexport interface WorkspaceAppRecord {\n id: string\n workspaceId: string\n name: string\n sandboxId: string\n previewId: string\n port: number\n previewUrl: string\n status: 'starting' | 'ready' | 'unavailable'\n createdAt: string\n updatedAt: string\n}\n\n/** Structural shape returned by the Sandbox SDK previewLinks API. */\nexport interface WorkspaceAppPreviewLink {\n previewId: string\n sandboxId: string\n port: number\n protocol: 'tcp' | 'udp'\n hostname: string\n url: string\n status: 'provisioning' | 'ready' | 'error' | 'disabled'\n}\n\nexport interface WorkspaceAppIdentity {\n /** Assigned by the product, stable across preview updates and deliberate republishes. */\n id: string\n /** Taken from the authenticated route or session, never agent tool arguments. */\n workspaceId: string\n /** Current box resolved by the product from that workspace or session. */\n sandboxId: string\n name: string\n /** Preserve this when republishing the same app onto a replacement sandbox. */\n createdAt?: string\n updatedAt?: string\n}\n\nfunction nonempty(value: string, field: string): string {\n if (typeof value !== 'string' || !value.trim() || /[\\u0000-\\u001f\\u007f]/.test(value)) {\n throw new Error('Invalid workspace app ' + field)\n }\n return value.trim()\n}\n\nfunction checkedLink(link: WorkspaceAppPreviewLink, sandboxId: string): void {\n if (link.sandboxId !== sandboxId) {\n throw new Error('Preview link belongs to another sandbox')\n }\n nonempty(link.previewId, 'previewId')\n if (link.protocol !== 'tcp') throw new Error('Workspace app preview must use TCP')\n if (!Number.isInteger(link.port) || link.port < 1 || link.port > 65535) {\n throw new Error('Invalid workspace app port')\n }\n if (!['provisioning', 'ready', 'error', 'disabled'].includes(link.status)) {\n throw new Error('Invalid workspace app preview status')\n }\n let url: URL\n try {\n url = new URL(link.url)\n } catch {\n throw new Error('Invalid workspace app preview URL')\n }\n if (\n url.protocol !== 'https:' ||\n !url.hostname ||\n url.hostname !== link.hostname ||\n url.username ||\n url.password ||\n url.search ||\n url.hash\n ) {\n throw new Error('Invalid workspace app preview URL')\n }\n}\n\nfunction appStatus(status: WorkspaceAppPreviewLink['status']): WorkspaceAppRecord['status'] {\n if (status === 'ready' || status === 'provisioning') return 'starting'\n return 'unavailable'\n}\n\n/**\n * Build a durable registration from a preview link created on the box that\n * the authenticated product request resolved. The product owns row identity,\n * access policy, persistence, and the decision to publish.\n */\nexport function workspaceAppFromPreviewLink(\n identity: WorkspaceAppIdentity,\n link: WorkspaceAppPreviewLink,\n): WorkspaceAppRecord {\n const sandboxId = nonempty(identity.sandboxId, 'sandboxId')\n checkedLink(link, sandboxId)\n const timestamp = identity.updatedAt ?? new Date().toISOString()\n return {\n id: nonempty(identity.id, 'id'),\n workspaceId: nonempty(identity.workspaceId, 'workspaceId'),\n name: nonempty(identity.name, 'name'),\n sandboxId,\n previewId: link.previewId,\n port: link.port,\n previewUrl: link.url,\n status: appStatus(link.status),\n createdAt: identity.createdAt ?? timestamp,\n updatedAt: timestamp,\n }\n}\n\nexport interface WorkspaceAppHttpProof {\n /** The exact trusted SDK preview URL requested by the server. */\n previewUrl: string\n status: number\n contentType: string\n /** Number of bytes read from the response body, excluding headers. */\n bodyBytes: number\n checkedAt: string\n}\n\n/**\n * Mark an app ready only after the host fetched its HTML over the trusted\n * preview URL. The host owns the request timeout, redirect and credential\n * policy, authorization, and compare-and-swap storage update.\n */\nexport function confirmWorkspaceAppReady(\n app: WorkspaceAppRecord,\n proof: WorkspaceAppHttpProof,\n): WorkspaceAppRecord {\n if (\n proof.previewUrl !== app.previewUrl ||\n proof.status !== 200 ||\n !/^text\\/html(?:\\s*;|\\s*$)/i.test(proof.contentType) ||\n !Number.isInteger(proof.bodyBytes) ||\n proof.bodyBytes < 1 ||\n !Number.isFinite(Date.parse(proof.checkedAt))\n ) {\n throw new Error('Workspace app HTML preview is not ready')\n }\n return { ...app, status: 'ready', updatedAt: proof.checkedAt }\n}\n\n/** Refresh delivery state without changing which sandbox service this app means. */\nexport function refreshWorkspaceAppPreview(\n app: WorkspaceAppRecord,\n link: WorkspaceAppPreviewLink,\n updatedAt = new Date().toISOString(),\n): WorkspaceAppRecord {\n checkedLink(link, app.sandboxId)\n if (link.previewId !== app.previewId || link.port !== app.port) {\n throw new Error('Preview link does not match workspace app')\n }\n return {\n ...app,\n previewUrl: link.url,\n status: appStatus(link.status),\n updatedAt,\n }\n}\n\nexport type WorkspaceAppBuilderInstructionsOptions =\n | {\n /** Host tool that writes the registration after creating a trusted preview. */\n publishTool: string\n /** Optional host tool for finding app IDs the builder may update. */\n listTool?: string\n manifestPath?: never\n }\n | {\n /** Host-scoped manifest the builder writes after creating runnable projects. */\n manifestPath: string\n publishTool?: never\n listTool?: never\n }\n\nfunction toolName(value: string): string {\n if (!/^[A-Za-z][A-Za-z0-9_.-]*$/.test(value)) {\n throw new Error('Invalid workspace app tool name')\n }\n return value\n}\n\nfunction builderManifestPath(value: string): string {\n if (\n !/^[A-Za-z0-9._-]+(?:[/][A-Za-z0-9._-]+)*$/.test(value) ||\n value.split('/').some(segment => segment === '..' || segment === '.')\n ) {\n throw new Error('Invalid workspace app manifest path')\n }\n return value\n}\n\n/** Portable instructions for an agent profile that builds workspace apps. */\nexport function workspaceAppBuilderInstructions(\n options: WorkspaceAppBuilderInstructionsOptions,\n): string[] {\n const common = [\n 'Build a working HTTP application in the current sandbox when the user asks for an app.',\n 'Keep the same app ID for later revisions of that app.',\n 'Treat a preview link as public. Do not embed secrets or private business facts in client code or responses.',\n 'Read real business data through authorized product APIs. Do not fabricate records to make the app look complete.',\n 'Report the app as available only after the host confirms its preview is ready.',\n ] as const\n if (options.manifestPath !== undefined) {\n const path = builderManifestPath(options.manifestPath)\n return [\n common[0],\n 'Inspect the existing app projects and manifest before creating or updating an app.',\n 'Keep each runnable project under apps/<id> relative to the sandbox working directory. Give it a package.json dev script; its HTTP server must listen on 0.0.0.0 and use PORT when supplied.',\n 'Write ' + path + ' as JSON with exactly an apps array of { id, name, projectPath } records; each projectPath is apps/<id>.',\n 'Do not put URLs, credentials, tokens, sandbox IDs, or preview IDs in that manifest.',\n 'The host reads this manifest and owns server launch, port selection, preview creation, registration, and access policy.',\n ...common.slice(1),\n ]\n }\n const publish = toolName(options.publishTool)\n const list = options.listTool ? toolName(options.listTool) : undefined\n return [\n common[0],\n ...(list ? ['Use ' + list + ' to inspect the existing apps before creating or updating one.'] : []),\n 'Start its server on 0.0.0.0 using an available, unclaimed port.',\n 'Call ' + publish + ' with the app name, stable app ID when updating, and listening port.',\n 'Use the host tool result as the source of the registered app URL and status; never invent a preview URL.',\n ...common.slice(1),\n ]\n}\n\nexport {\n WorkspaceAppDataConflict,\n createWorkspaceAppDataClient,\n createWorkspaceAppDataHost,\n} from './data-bridge'\nexport type {\n WorkspaceAppDataClient,\n WorkspaceAppDataClientOptions,\n WorkspaceAppDataEntry,\n WorkspaceAppDataHost,\n WorkspaceAppDataHostOptions,\n} from './data-bridge'\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/workspace-apps/data-bridge.ts","../../src/workspace-apps/index.ts"],"sourcesContent":["import type { WorkspaceAppRecord } from './index'\n\nconst protocol = 'tangle.workspace-app-data.v1'\nconst maxBytes = 65_536\nconst maxPending = 16\nconst keyPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/\nconst idPattern = /^[A-Za-z0-9_-]{1,80}$/\n\nexport interface WorkspaceAppDataEntry { value: string; revision: number }\nexport class WorkspaceAppDataConflict extends Error {\n constructor() {\n super('Workspace app data changed. Read it again before saving.')\n this.name = 'WorkspaceAppDataConflict'\n }\n}\nexport interface WorkspaceAppDataHostOptions {\n /** Product-authorized registration for the current viewer. */\n app: Pick<WorkspaceAppRecord, 'id' | 'previewUrl' | 'status'>\n frame: HTMLIFrameElement\n /** Product callbacks are bound to the authorized business and app namespace. */\n read(key: string): Promise<WorkspaceAppDataEntry | null>\n /** Null creates only if absent; a revision updates only that version. */\n write(key: string, value: string, expectedRevision: number | null): Promise<{ revision: number }>\n remove(key: string, expectedRevision: number): Promise<void>\n}\nexport interface WorkspaceAppDataHost { dispose(): void }\nexport interface WorkspaceAppDataClientOptions {\n appId: string\n /** Exact authenticated product origin, provided by the product. */\n parentOrigin: string\n}\nexport interface WorkspaceAppDataClient {\n ready(): Promise<void>\n read(key: string): Promise<WorkspaceAppDataEntry | null>\n write(key: string, value: string, expectedRevision: number | null): Promise<{ revision: number }>\n remove(key: string, expectedRevision: number): Promise<void>\n dispose(): void\n}\n\ntype Action = 'ready' | 'read' | 'write' | 'remove'\ntype Packet = {\n protocol: typeof protocol\n appId: string\n requestId: string\n action: Action\n key?: string\n value?: string\n expectedRevision?: number | null\n ok?: boolean\n entry?: WorkspaceAppDataEntry | null\n revision?: number\n error?: 'invalid' | 'conflict' | 'unavailable'\n}\nfunction object(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\nfunction key(value: unknown): value is string {\n return typeof value === 'string' && keyPattern.test(value) && value !== '.' && value !== '..'\n}\nfunction revision(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value > 0\n}\nfunction bounded(value: unknown): value is string {\n return typeof value === 'string' && value.length <= maxBytes && new TextEncoder().encode(value).byteLength <= maxBytes\n}\nfunction appId(value: string): string {\n if (!idPattern.test(value)) throw new Error('Invalid workspace app ID')\n return value\n}\nfunction origin(value: string): string {\n const url = new URL(value)\n if (url.origin !== value || (url.protocol !== 'https:' &&\n !(url.protocol === 'http:' && ['localhost', '127.0.0.1'].includes(url.hostname)))) {\n throw new Error('Invalid workspace app data origin')\n }\n return url.origin\n}\nfunction packet(value: unknown): value is Packet {\n return object(value) && value.protocol === protocol &&\n typeof value.appId === 'string' && typeof value.requestId === 'string' &&\n /^[A-Za-z0-9_-]{1,64}$/.test(value.requestId) &&\n typeof value.action === 'string' && ['ready', 'read', 'write', 'remove'].includes(value.action)\n}\n\n/** Bind one registered preview to a product-authorized data adapter. */\nexport function createWorkspaceAppDataHost(options: WorkspaceAppDataHostOptions): WorkspaceAppDataHost {\n const { app, frame } = options\n appId(app.id)\n if (app.status !== 'ready') throw new Error('Workspace app is not ready')\n const preview = new URL(app.previewUrl)\n const previewOrigin = origin(preview.origin)\n if (preview.protocol !== 'https:' || preview.username || preview.password || preview.search || preview.hash ||\n new URL(frame.src).href !== preview.href) {\n throw new Error('Iframe does not render the registered workspace app preview')\n }\n let closed = false\n let active = 0\n let messagesLeft = 64\n let nextWindow = Date.now() + 1000\n const onMessage = (event: MessageEvent<unknown>): void => {\n if (closed || event.source !== frame.contentWindow || event.origin !== previewOrigin ||\n !packet(event.data) || event.data.appId !== app.id) return\n const now = Date.now()\n if (now >= nextWindow) { messagesLeft = 64; nextWindow = now + 1000 }\n if (messagesLeft-- <= 0) return\n const message = event.data\n const reply = (result: Pick<Packet, 'ok' | 'entry' | 'revision' | 'error'>): void => {\n if (closed || event.source !== frame.contentWindow) return\n frame.contentWindow?.postMessage({\n protocol, appId: app.id, requestId: message.requestId, action: message.action, ...result,\n } satisfies Packet, previewOrigin)\n }\n if (message.action === 'ready') { reply({ ok: true }); return }\n if (!key(message.key) ||\n (message.action === 'write' && (!bounded(message.value) ||\n !(message.expectedRevision === null || revision(message.expectedRevision)))) ||\n (message.action === 'remove' && !revision(message.expectedRevision))) {\n reply({ ok: false, error: 'invalid' }); return\n }\n if (active >= maxPending) { reply({ ok: false, error: 'unavailable' }); return }\n active += 1\n const checkedKey = message.key\n void (async () => {\n try {\n if (message.action === 'read') {\n const entry = await options.read(checkedKey)\n if (entry !== null && (!bounded(entry.value) || !revision(entry.revision))) throw new Error('Invalid entry')\n reply({ ok: true, entry })\n } else if (message.action === 'write') {\n const result = await options.write(checkedKey, message.value as string, message.expectedRevision as number | null)\n if (!revision(result.revision)) throw new Error('Invalid revision')\n reply({ ok: true, revision: result.revision })\n } else {\n await options.remove(checkedKey, message.expectedRevision as number)\n reply({ ok: true })\n }\n } catch (error) {\n reply({ ok: false, error: error instanceof WorkspaceAppDataConflict ? 'conflict' : 'unavailable' })\n } finally { active -= 1 }\n })()\n }\n window.addEventListener('message', onMessage)\n return { dispose() { closed = true; window.removeEventListener('message', onMessage) } }\n}\n\n/** Use inside a registered preview; no product credential crosses the frame. */\nexport function createWorkspaceAppDataClient(options: WorkspaceAppDataClientOptions): WorkspaceAppDataClient {\n const id = appId(options.appId)\n const parentOrigin = origin(options.parentOrigin)\n if (window.parent === window) throw new Error('Workspace app data requires an embedded preview')\n const pending = new Map<string, {\n action: Action\n resolve(value: Packet): void\n reject(error: Error): void\n timeout: ReturnType<typeof setTimeout>\n }>()\n let closed = false\n let readyPromise: Promise<void> | undefined\n const onMessage = (event: MessageEvent<unknown>): void => {\n if (closed || event.source !== window.parent || event.origin !== parentOrigin || !packet(event.data) ||\n event.data.appId !== id || typeof event.data.ok !== 'boolean') return\n const waiting = pending.get(event.data.requestId)\n if (!waiting || waiting.action !== event.data.action) return\n pending.delete(event.data.requestId)\n clearTimeout(waiting.timeout)\n if (event.data.ok) waiting.resolve(event.data)\n else waiting.reject(event.data.error === 'conflict' ? new WorkspaceAppDataConflict() :\n new Error('Workspace app data unavailable'))\n }\n window.addEventListener('message', onMessage)\n const send = (action: Action, fields: Partial<Packet> = {}, timeoutMs = 10_000): Promise<Packet> => {\n if (closed) return Promise.reject(new Error('Workspace app data client is closed'))\n if (pending.size >= maxPending) return Promise.reject(new Error('Too many workspace app data requests'))\n const requestId = crypto.randomUUID()\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => { pending.delete(requestId); reject(new Error('Workspace app data timed out')) }, timeoutMs)\n pending.set(requestId, { action, resolve, reject, timeout })\n window.parent.postMessage({ protocol, appId: id, requestId, action, ...fields } satisfies Packet, parentOrigin)\n })\n }\n const ready = (): Promise<void> => {\n if (readyPromise) return readyPromise\n readyPromise = new Promise<void>((resolve, reject) => {\n const deadline = Date.now() + 10_000\n const attempt = (): void => {\n if (closed) { reject(new Error('Workspace app data client is closed')); return }\n if (Date.now() >= deadline) { reject(new Error('Workspace app data host did not become ready')); return }\n void send('ready', {}, 300).then(() => resolve(), () => setTimeout(attempt, 200))\n }\n attempt()\n }).catch((error: unknown) => { readyPromise = undefined; throw error })\n return readyPromise\n }\n return {\n ready,\n async read(value) {\n if (!key(value)) throw new Error('Invalid workspace app data key')\n await ready()\n const response = await send('read', { key: value })\n if (response.entry !== null && (!object(response.entry) ||\n !bounded(response.entry.value) || !revision(response.entry.revision))) {\n throw new Error('Invalid workspace app data response')\n }\n return response.entry ?? null\n },\n async write(value, data, expectedRevision) {\n if (!key(value) || !bounded(data) || !(expectedRevision === null || revision(expectedRevision))) {\n throw new Error('Invalid workspace app data write')\n }\n await ready()\n const response = await send('write', { key: value, value: data, expectedRevision })\n if (!revision(response.revision)) throw new Error('Invalid workspace app data response')\n return { revision: response.revision }\n },\n async remove(value, expectedRevision) {\n if (!key(value) || !revision(expectedRevision)) throw new Error('Invalid workspace app data remove')\n await ready()\n await send('remove', { key: value, expectedRevision })\n },\n dispose() {\n closed = true\n window.removeEventListener('message', onMessage)\n for (const waiting of pending.values()) {\n clearTimeout(waiting.timeout)\n waiting.reject(new Error('Workspace app data client is closed'))\n }\n pending.clear()\n },\n }\n}\n","/** A sandbox app registered in one product workspace. Products persist and authorize these rows. */\nexport interface WorkspaceAppRecord {\n id: string\n workspaceId: string\n name: string\n sandboxId: string\n previewId: string\n port: number\n previewUrl: string\n status: 'starting' | 'ready' | 'unavailable'\n createdAt: string\n updatedAt: string\n}\n\n/** Structural shape returned by the Sandbox SDK previewLinks API. */\nexport interface WorkspaceAppPreviewLink {\n previewId: string\n sandboxId: string\n port: number\n protocol: 'tcp' | 'udp'\n hostname: string\n url: string\n status: 'provisioning' | 'ready' | 'error' | 'disabled'\n}\n\nexport interface WorkspaceAppIdentity {\n /** Assigned by the product, stable across preview updates and deliberate republishes. */\n id: string\n /** Taken from the authenticated route or session, never agent tool arguments. */\n workspaceId: string\n /** Current box resolved by the product from that workspace or session. */\n sandboxId: string\n name: string\n /** Preserve this when republishing the same app onto a replacement sandbox. */\n createdAt?: string\n updatedAt?: string\n}\n\nfunction nonempty(value: string, field: string): string {\n if (typeof value !== 'string' || !value.trim() || /[\\u0000-\\u001f\\u007f]/.test(value)) {\n throw new Error('Invalid workspace app ' + field)\n }\n return value.trim()\n}\n\nfunction checkedLink(link: WorkspaceAppPreviewLink, sandboxId: string): void {\n if (link.sandboxId !== sandboxId) {\n throw new Error('Preview link belongs to another sandbox')\n }\n nonempty(link.previewId, 'previewId')\n if (link.protocol !== 'tcp') throw new Error('Workspace app preview must use TCP')\n if (!Number.isInteger(link.port) || link.port < 1 || link.port > 65535) {\n throw new Error('Invalid workspace app port')\n }\n if (!['provisioning', 'ready', 'error', 'disabled'].includes(link.status)) {\n throw new Error('Invalid workspace app preview status')\n }\n let url: URL\n try {\n url = new URL(link.url)\n } catch {\n throw new Error('Invalid workspace app preview URL')\n }\n if (\n url.protocol !== 'https:' ||\n !url.hostname ||\n url.hostname !== link.hostname ||\n url.username ||\n url.password ||\n url.search ||\n url.hash\n ) {\n throw new Error('Invalid workspace app preview URL')\n }\n}\n\nfunction appStatus(status: WorkspaceAppPreviewLink['status']): WorkspaceAppRecord['status'] {\n if (status === 'ready' || status === 'provisioning') return 'starting'\n return 'unavailable'\n}\n\n/**\n * Build a durable registration from a preview link created on the box that\n * the authenticated product request resolved. The product owns row identity,\n * access policy, persistence, and the decision to publish.\n */\nexport function workspaceAppFromPreviewLink(\n identity: WorkspaceAppIdentity,\n link: WorkspaceAppPreviewLink,\n): WorkspaceAppRecord {\n const sandboxId = nonempty(identity.sandboxId, 'sandboxId')\n checkedLink(link, sandboxId)\n const timestamp = identity.updatedAt ?? new Date().toISOString()\n return {\n id: nonempty(identity.id, 'id'),\n workspaceId: nonempty(identity.workspaceId, 'workspaceId'),\n name: nonempty(identity.name, 'name'),\n sandboxId,\n previewId: link.previewId,\n port: link.port,\n previewUrl: link.url,\n status: appStatus(link.status),\n createdAt: identity.createdAt ?? timestamp,\n updatedAt: timestamp,\n }\n}\n\nexport interface WorkspaceAppHttpProof {\n /** The exact trusted SDK preview URL requested by the server. */\n previewUrl: string\n status: number\n contentType: string\n /** Number of bytes read from the response body, excluding headers. */\n bodyBytes: number\n checkedAt: string\n}\n\n/**\n * Mark an app ready only after the host fetched its HTML over the trusted\n * preview URL. The host owns the request timeout, redirect and credential\n * policy, authorization, and compare-and-swap storage update.\n */\nexport function confirmWorkspaceAppReady(\n app: WorkspaceAppRecord,\n proof: WorkspaceAppHttpProof,\n): WorkspaceAppRecord {\n if (\n proof.previewUrl !== app.previewUrl ||\n proof.status !== 200 ||\n !/^text\\/html(?:\\s*;|\\s*$)/i.test(proof.contentType) ||\n !Number.isInteger(proof.bodyBytes) ||\n proof.bodyBytes < 1 ||\n !Number.isFinite(Date.parse(proof.checkedAt))\n ) {\n throw new Error('Workspace app HTML preview is not ready')\n }\n return { ...app, status: 'ready', updatedAt: proof.checkedAt }\n}\n\n/** Refresh delivery state without changing which sandbox service this app means. */\nexport function refreshWorkspaceAppPreview(\n app: WorkspaceAppRecord,\n link: WorkspaceAppPreviewLink,\n updatedAt = new Date().toISOString(),\n): WorkspaceAppRecord {\n checkedLink(link, app.sandboxId)\n if (link.previewId !== app.previewId || link.port !== app.port) {\n throw new Error('Preview link does not match workspace app')\n }\n return {\n ...app,\n previewUrl: link.url,\n status: appStatus(link.status),\n updatedAt,\n }\n}\n\nexport type WorkspaceAppBuilderInstructionsOptions =\n | {\n /** Host tool that writes the registration after creating a trusted preview. */\n publishTool: string\n /** Optional host tool for finding app IDs the builder may update. */\n listTool?: string\n manifestPath?: never\n }\n | {\n /** Host-scoped manifest the builder writes after creating runnable projects. */\n manifestPath: string\n publishTool?: never\n listTool?: never\n }\n\nfunction toolName(value: string): string {\n if (!/^[A-Za-z][A-Za-z0-9_.-]*$/.test(value)) {\n throw new Error('Invalid workspace app tool name')\n }\n return value\n}\n\nfunction builderManifestPath(value: string): string {\n if (\n !/^[A-Za-z0-9._-]+(?:[/][A-Za-z0-9._-]+)*$/.test(value) ||\n value.split('/').some(segment => segment === '..' || segment === '.')\n ) {\n throw new Error('Invalid workspace app manifest path')\n }\n return value\n}\n\n/** Portable instructions for an agent profile that builds workspace apps. */\nexport function workspaceAppBuilderInstructions(\n options: WorkspaceAppBuilderInstructionsOptions,\n): string[] {\n const common = [\n 'Build a working HTTP application in the current sandbox when the user asks for an app.',\n 'Keep the same app ID for later revisions of that app.',\n 'Treat a preview link as public. Do not embed secrets or private business facts in client code or responses.',\n 'Read real business data through authorized product APIs. Do not fabricate records to make the app look complete.',\n 'Report the app as available only after the host confirms its preview is ready.',\n ] as const\n if (options.manifestPath !== undefined) {\n const path = builderManifestPath(options.manifestPath)\n return [\n common[0],\n 'Inspect the existing app projects and manifest before creating or updating an app.',\n 'Keep each runnable project under apps/<id> relative to the sandbox working directory. Give it a package.json dev script; its HTTP server must listen on 0.0.0.0 and use PORT when supplied.',\n 'Write ' + path + ' as JSON with exactly an apps array of { id, name, projectPath } records; each projectPath is apps/<id>.',\n 'Do not put URLs, credentials, tokens, sandbox IDs, or preview IDs in that manifest.',\n 'The host reads this manifest and owns server launch, port selection, preview creation, registration, and access policy.',\n ...common.slice(1),\n ]\n }\n const publish = toolName(options.publishTool)\n const list = options.listTool ? toolName(options.listTool) : undefined\n return [\n common[0],\n ...(list ? ['Use ' + list + ' to inspect the existing apps before creating or updating one.'] : []),\n 'Start its server on 0.0.0.0 using an available, unclaimed port.',\n 'Call ' + publish + ' with the app name, stable app ID when updating, and listening port.',\n 'Use the host tool result as the source of the registered app URL and status; never invent a preview URL.',\n ...common.slice(1),\n ]\n}\n\nexport {\n WorkspaceAppDataConflict,\n createWorkspaceAppDataClient,\n createWorkspaceAppDataHost,\n} from './data-bridge'\nexport type {\n WorkspaceAppDataClient,\n WorkspaceAppDataClientOptions,\n WorkspaceAppDataEntry,\n WorkspaceAppDataHost,\n WorkspaceAppDataHostOptions,\n} from './data-bridge'\n\nexport {\n CLOUD_ARTIFACT_MAX_BYTES,\n CLOUD_ARTIFACT_MAX_FILES,\n CLOUD_ARTIFACT_MAX_FILE_BYTES,\n CLOUD_ARTIFACT_SHARE_ROLE,\n CLOUD_ARTIFACT_VIEW_TTL_MS,\n CLOUD_ARTIFACT_WORKER_MODULE,\n CloudArtifactError,\n cloudArtifactContentType,\n cloudArtifactObjectKey,\n cloudArtifactReferences,\n cloudArtifactWorkspacePrefix,\n collectCloudArtifactBundle,\n createCloudArtifactId,\n createCloudArtifactShareToken,\n createCloudArtifactViewUrl,\n isCloudArtifactPath,\n prepareCloudArtifact,\n readCloudArtifactShareToken,\n readCloudArtifactViewUrl,\n resolveCloudArtifactReference,\n storeCloudArtifact,\n} from './cloud-artifacts'\nexport type {\n CloudArtifactErrorCode,\n CloudArtifactFile,\n CloudArtifactGrant,\n CloudArtifactInputFile,\n CloudArtifactKind,\n CloudArtifactRecord,\n CloudArtifactVersion,\n CloudArtifactView,\n PreparedCloudArtifact,\n} from './cloud-artifacts'\nexport {\n CLOUD_ARTIFACT_FRAME_SANDBOX,\n CLOUD_ARTIFACT_SCRIPT_HOSTS,\n cloudArtifactContentSecurityPolicy,\n createCloudArtifactOrigin,\n} from './cloud-artifact-origin'\nexport type {\n CloudArtifactOriginOptions,\n CloudArtifactWorkerCode,\n CloudArtifactWorkerLoader,\n} from './cloud-artifact-origin'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,YAAY;AAGX,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAClD,cAAc;AACZ,UAAM,0DAA0D;AAChE,SAAK,OAAO;AAAA,EACd;AACF;AAuCA,SAAS,OAAO,OAAkD;AAChE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AACA,SAAS,IAAI,OAAiC;AAC5C,SAAO,OAAO,UAAU,YAAY,WAAW,KAAK,KAAK,KAAK,UAAU,OAAO,UAAU;AAC3F;AACA,SAAS,SAAS,OAAiC;AACjD,SAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,QAAQ;AAC7E;AACA,SAAS,QAAQ,OAAiC;AAChD,SAAO,OAAO,UAAU,YAAY,MAAM,UAAU,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,EAAE,cAAc;AAChH;AACA,SAAS,MAAM,OAAuB;AACpC,MAAI,CAAC,UAAU,KAAK,KAAK,EAAG,OAAM,IAAI,MAAM,0BAA0B;AACtE,SAAO;AACT;AACA,SAAS,OAAO,OAAuB;AACrC,QAAM,MAAM,IAAI,IAAI,KAAK;AACzB,MAAI,IAAI,WAAW,SAAU,IAAI,aAAa,YAC5C,EAAE,IAAI,aAAa,WAAW,CAAC,aAAa,WAAW,EAAE,SAAS,IAAI,QAAQ,IAAK;AACnF,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,SAAO,IAAI;AACb;AACA,SAAS,OAAO,OAAiC;AAC/C,SAAO,OAAO,KAAK,KAAK,MAAM,aAAa,YACzC,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,cAAc,YAC9D,wBAAwB,KAAK,MAAM,SAAS,KAC5C,OAAO,MAAM,WAAW,YAAY,CAAC,SAAS,QAAQ,SAAS,QAAQ,EAAE,SAAS,MAAM,MAAM;AAClG;AAGO,SAAS,2BAA2B,SAA4D;AACrG,QAAM,EAAE,KAAK,MAAM,IAAI;AACvB,QAAM,IAAI,EAAE;AACZ,MAAI,IAAI,WAAW,QAAS,OAAM,IAAI,MAAM,4BAA4B;AACxE,QAAM,UAAU,IAAI,IAAI,IAAI,UAAU;AACtC,QAAM,gBAAgB,OAAO,QAAQ,MAAM;AAC3C,MAAI,QAAQ,aAAa,YAAY,QAAQ,YAAY,QAAQ,YAAY,QAAQ,UAAU,QAAQ,QACrG,IAAI,IAAI,MAAM,GAAG,EAAE,SAAS,QAAQ,MAAM;AAC1C,UAAM,IAAI,MAAM,6DAA6D;AAAA,EAC/E;AACA,MAAI,SAAS;AACb,MAAI,SAAS;AACb,MAAI,eAAe;AACnB,MAAI,aAAa,KAAK,IAAI,IAAI;AAC9B,QAAM,YAAY,CAAC,UAAuC;AACxD,QAAI,UAAU,MAAM,WAAW,MAAM,iBAAiB,MAAM,WAAW,iBACrE,CAAC,OAAO,MAAM,IAAI,KAAK,MAAM,KAAK,UAAU,IAAI,GAAI;AACtD,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,OAAO,YAAY;AAAE,qBAAe;AAAI,mBAAa,MAAM;AAAA,IAAK;AACpE,QAAI,kBAAkB,EAAG;AACzB,UAAM,UAAU,MAAM;AACtB,UAAM,QAAQ,CAAC,WAAsE;AACnF,UAAI,UAAU,MAAM,WAAW,MAAM,cAAe;AACpD,YAAM,eAAe,YAAY;AAAA,QAC/B;AAAA,QAAU,OAAO,IAAI;AAAA,QAAI,WAAW,QAAQ;AAAA,QAAW,QAAQ,QAAQ;AAAA,QAAQ,GAAG;AAAA,MACpF,GAAoB,aAAa;AAAA,IACnC;AACA,QAAI,QAAQ,WAAW,SAAS;AAAE,YAAM,EAAE,IAAI,KAAK,CAAC;AAAG;AAAA,IAAO;AAC9D,QAAI,CAAC,IAAI,QAAQ,GAAG,KACjB,QAAQ,WAAW,YAAY,CAAC,QAAQ,QAAQ,KAAK,KACpD,EAAE,QAAQ,qBAAqB,QAAQ,SAAS,QAAQ,gBAAgB,OACzE,QAAQ,WAAW,YAAY,CAAC,SAAS,QAAQ,gBAAgB,GAAI;AACtE,YAAM,EAAE,IAAI,OAAO,OAAO,UAAU,CAAC;AAAG;AAAA,IAC1C;AACA,QAAI,UAAU,YAAY;AAAE,YAAM,EAAE,IAAI,OAAO,OAAO,cAAc,CAAC;AAAG;AAAA,IAAO;AAC/E,cAAU;AACV,UAAM,aAAa,QAAQ;AAC3B,UAAM,YAAY;AAChB,UAAI;AACF,YAAI,QAAQ,WAAW,QAAQ;AAC7B,gBAAM,QAAQ,MAAM,QAAQ,KAAK,UAAU;AAC3C,cAAI,UAAU,SAAS,CAAC,QAAQ,MAAM,KAAK,KAAK,CAAC,SAAS,MAAM,QAAQ,GAAI,OAAM,IAAI,MAAM,eAAe;AAC3G,gBAAM,EAAE,IAAI,MAAM,MAAM,CAAC;AAAA,QAC3B,WAAW,QAAQ,WAAW,SAAS;AACrC,gBAAM,SAAS,MAAM,QAAQ,MAAM,YAAY,QAAQ,OAAiB,QAAQ,gBAAiC;AACjH,cAAI,CAAC,SAAS,OAAO,QAAQ,EAAG,OAAM,IAAI,MAAM,kBAAkB;AAClE,gBAAM,EAAE,IAAI,MAAM,UAAU,OAAO,SAAS,CAAC;AAAA,QAC/C,OAAO;AACL,gBAAM,QAAQ,OAAO,YAAY,QAAQ,gBAA0B;AACnE,gBAAM,EAAE,IAAI,KAAK,CAAC;AAAA,QACpB;AAAA,MACF,SAAS,OAAO;AACd,cAAM,EAAE,IAAI,OAAO,OAAO,iBAAiB,2BAA2B,aAAa,cAAc,CAAC;AAAA,MACpG,UAAE;AAAU,kBAAU;AAAA,MAAE;AAAA,IAC1B,GAAG;AAAA,EACL;AACA,SAAO,iBAAiB,WAAW,SAAS;AAC5C,SAAO,EAAE,UAAU;AAAE,aAAS;AAAM,WAAO,oBAAoB,WAAW,SAAS;AAAA,EAAE,EAAE;AACzF;AAGO,SAAS,6BAA6B,SAAgE;AAC3G,QAAM,KAAK,MAAM,QAAQ,KAAK;AAC9B,QAAM,eAAe,OAAO,QAAQ,YAAY;AAChD,MAAI,OAAO,WAAW,OAAQ,OAAM,IAAI,MAAM,iDAAiD;AAC/F,QAAM,UAAU,oBAAI,IAKjB;AACH,MAAI,SAAS;AACb,MAAI;AACJ,QAAM,YAAY,CAAC,UAAuC;AACxD,QAAI,UAAU,MAAM,WAAW,OAAO,UAAU,MAAM,WAAW,gBAAgB,CAAC,OAAO,MAAM,IAAI,KACjG,MAAM,KAAK,UAAU,MAAM,OAAO,MAAM,KAAK,OAAO,UAAW;AACjE,UAAM,UAAU,QAAQ,IAAI,MAAM,KAAK,SAAS;AAChD,QAAI,CAAC,WAAW,QAAQ,WAAW,MAAM,KAAK,OAAQ;AACtD,YAAQ,OAAO,MAAM,KAAK,SAAS;AACnC,iBAAa,QAAQ,OAAO;AAC5B,QAAI,MAAM,KAAK,GAAI,SAAQ,QAAQ,MAAM,IAAI;AAAA,QACxC,SAAQ,OAAO,MAAM,KAAK,UAAU,aAAa,IAAI,yBAAyB,IACjF,IAAI,MAAM,gCAAgC,CAAC;AAAA,EAC/C;AACA,SAAO,iBAAiB,WAAW,SAAS;AAC5C,QAAM,OAAO,CAAC,QAAgB,SAA0B,CAAC,GAAG,YAAY,QAA4B;AAClG,QAAI,OAAQ,QAAO,QAAQ,OAAO,IAAI,MAAM,qCAAqC,CAAC;AAClF,QAAI,QAAQ,QAAQ,WAAY,QAAO,QAAQ,OAAO,IAAI,MAAM,sCAAsC,CAAC;AACvG,UAAM,YAAY,OAAO,WAAW;AACpC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,WAAW,MAAM;AAAE,gBAAQ,OAAO,SAAS;AAAG,eAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,MAAE,GAAG,SAAS;AAC5H,cAAQ,IAAI,WAAW,EAAE,QAAQ,SAAS,QAAQ,QAAQ,CAAC;AAC3D,aAAO,OAAO,YAAY,EAAE,UAAU,OAAO,IAAI,WAAW,QAAQ,GAAG,OAAO,GAAoB,YAAY;AAAA,IAChH,CAAC;AAAA,EACH;AACA,QAAM,QAAQ,MAAqB;AACjC,QAAI,aAAc,QAAO;AACzB,mBAAe,IAAI,QAAc,CAAC,SAAS,WAAW;AACpD,YAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,YAAM,UAAU,MAAY;AAC1B,YAAI,QAAQ;AAAE,iBAAO,IAAI,MAAM,qCAAqC,CAAC;AAAG;AAAA,QAAO;AAC/E,YAAI,KAAK,IAAI,KAAK,UAAU;AAAE,iBAAO,IAAI,MAAM,8CAA8C,CAAC;AAAG;AAAA,QAAO;AACxG,aAAK,KAAK,SAAS,CAAC,GAAG,GAAG,EAAE,KAAK,MAAM,QAAQ,GAAG,MAAM,WAAW,SAAS,GAAG,CAAC;AAAA,MAClF;AACA,cAAQ;AAAA,IACV,CAAC,EAAE,MAAM,CAAC,UAAmB;AAAE,qBAAe;AAAW,YAAM;AAAA,IAAM,CAAC;AACtE,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM,KAAK,OAAO;AAChB,UAAI,CAAC,IAAI,KAAK,EAAG,OAAM,IAAI,MAAM,gCAAgC;AACjE,YAAM,MAAM;AACZ,YAAM,WAAW,MAAM,KAAK,QAAQ,EAAE,KAAK,MAAM,CAAC;AAClD,UAAI,SAAS,UAAU,SAAS,CAAC,OAAO,SAAS,KAAK,KACpD,CAAC,QAAQ,SAAS,MAAM,KAAK,KAAK,CAAC,SAAS,SAAS,MAAM,QAAQ,IAAI;AACvE,cAAM,IAAI,MAAM,qCAAqC;AAAA,MACvD;AACA,aAAO,SAAS,SAAS;AAAA,IAC3B;AAAA,IACA,MAAM,MAAM,OAAO,MAAM,kBAAkB;AACzC,UAAI,CAAC,IAAI,KAAK,KAAK,CAAC,QAAQ,IAAI,KAAK,EAAE,qBAAqB,QAAQ,SAAS,gBAAgB,IAAI;AAC/F,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AACA,YAAM,MAAM;AACZ,YAAM,WAAW,MAAM,KAAK,SAAS,EAAE,KAAK,OAAO,OAAO,MAAM,iBAAiB,CAAC;AAClF,UAAI,CAAC,SAAS,SAAS,QAAQ,EAAG,OAAM,IAAI,MAAM,qCAAqC;AACvF,aAAO,EAAE,UAAU,SAAS,SAAS;AAAA,IACvC;AAAA,IACA,MAAM,OAAO,OAAO,kBAAkB;AACpC,UAAI,CAAC,IAAI,KAAK,KAAK,CAAC,SAAS,gBAAgB,EAAG,OAAM,IAAI,MAAM,mCAAmC;AACnG,YAAM,MAAM;AACZ,YAAM,KAAK,UAAU,EAAE,KAAK,OAAO,iBAAiB,CAAC;AAAA,IACvD;AAAA,IACA,UAAU;AACR,eAAS;AACT,aAAO,oBAAoB,WAAW,SAAS;AAC/C,iBAAW,WAAW,QAAQ,OAAO,GAAG;AACtC,qBAAa,QAAQ,OAAO;AAC5B,gBAAQ,OAAO,IAAI,MAAM,qCAAqC,CAAC;AAAA,MACjE;AACA,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACF;;;AC/LA,SAAS,SAAS,OAAe,OAAuB;AACtD,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,KAAK,wBAAwB,KAAK,KAAK,GAAG;AACrF,UAAM,IAAI,MAAM,2BAA2B,KAAK;AAAA,EAClD;AACA,SAAO,MAAM,KAAK;AACpB;AAEA,SAAS,YAAY,MAA+B,WAAyB;AAC3E,MAAI,KAAK,cAAc,WAAW;AAChC,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AACA,WAAS,KAAK,WAAW,WAAW;AACpC,MAAI,KAAK,aAAa,MAAO,OAAM,IAAI,MAAM,oCAAoC;AACjF,MAAI,CAAC,OAAO,UAAU,KAAK,IAAI,KAAK,KAAK,OAAO,KAAK,KAAK,OAAO,OAAO;AACtE,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACA,MAAI,CAAC,CAAC,gBAAgB,SAAS,SAAS,UAAU,EAAE,SAAS,KAAK,MAAM,GAAG;AACzE,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,KAAK,GAAG;AAAA,EACxB,QAAQ;AACN,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,MACE,IAAI,aAAa,YACjB,CAAC,IAAI,YACL,IAAI,aAAa,KAAK,YACtB,IAAI,YACJ,IAAI,YACJ,IAAI,UACJ,IAAI,MACJ;AACA,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACF;AAEA,SAAS,UAAU,QAAyE;AAC1F,MAAI,WAAW,WAAW,WAAW,eAAgB,QAAO;AAC5D,SAAO;AACT;AAOO,SAAS,4BACd,UACA,MACoB;AACpB,QAAM,YAAY,SAAS,SAAS,WAAW,WAAW;AAC1D,cAAY,MAAM,SAAS;AAC3B,QAAM,YAAY,SAAS,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC/D,SAAO;AAAA,IACL,IAAI,SAAS,SAAS,IAAI,IAAI;AAAA,IAC9B,aAAa,SAAS,SAAS,aAAa,aAAa;AAAA,IACzD,MAAM,SAAS,SAAS,MAAM,MAAM;AAAA,IACpC;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,MAAM,KAAK;AAAA,IACX,YAAY,KAAK;AAAA,IACjB,QAAQ,UAAU,KAAK,MAAM;AAAA,IAC7B,WAAW,SAAS,aAAa;AAAA,IACjC,WAAW;AAAA,EACb;AACF;AAiBO,SAAS,yBACd,KACA,OACoB;AACpB,MACE,MAAM,eAAe,IAAI,cACzB,MAAM,WAAW,OACjB,CAAC,4BAA4B,KAAK,MAAM,WAAW,KACnD,CAAC,OAAO,UAAU,MAAM,SAAS,KACjC,MAAM,YAAY,KAClB,CAAC,OAAO,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,GAC5C;AACA,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AACA,SAAO,EAAE,GAAG,KAAK,QAAQ,SAAS,WAAW,MAAM,UAAU;AAC/D;AAGO,SAAS,2BACd,KACA,MACA,aAAY,oBAAI,KAAK,GAAE,YAAY,GACf;AACpB,cAAY,MAAM,IAAI,SAAS;AAC/B,MAAI,KAAK,cAAc,IAAI,aAAa,KAAK,SAAS,IAAI,MAAM;AAC9D,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,KAAK;AAAA,IACjB,QAAQ,UAAU,KAAK,MAAM;AAAA,IAC7B;AAAA,EACF;AACF;AAiBA,SAAS,SAAS,OAAuB;AACvC,MAAI,CAAC,4BAA4B,KAAK,KAAK,GAAG;AAC5C,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAuB;AAClD,MACE,CAAC,2CAA2C,KAAK,KAAK,KACtD,MAAM,MAAM,GAAG,EAAE,KAAK,aAAW,YAAY,QAAQ,YAAY,GAAG,GACpE;AACA,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AACA,SAAO;AACT;AAGO,SAAS,gCACd,SACU;AACV,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,QAAQ,iBAAiB,QAAW;AACtC,UAAM,OAAO,oBAAoB,QAAQ,YAAY;AACrD,WAAO;AAAA,MACL,OAAO,CAAC;AAAA,MACR;AAAA,MACA;AAAA,MACA,WAAW,OAAO;AAAA,MAClB;AAAA,MACA;AAAA,MACA,GAAG,OAAO,MAAM,CAAC;AAAA,IACnB;AAAA,EACF;AACA,QAAM,UAAU,SAAS,QAAQ,WAAW;AAC5C,QAAM,OAAO,QAAQ,WAAW,SAAS,QAAQ,QAAQ,IAAI;AAC7D,SAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,GAAI,OAAO,CAAC,SAAS,OAAO,gEAAgE,IAAI,CAAC;AAAA,IACjG;AAAA,IACA,UAAU,UAAU;AAAA,IACpB;AAAA,IACA,GAAG,OAAO,MAAM,CAAC;AAAA,EACnB;AACF;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MutationOutcome } from '../web-react/async/use-confirmed-mutation';
|
|
2
|
+
import { CLOUD_ARTIFACT_FRAME_SANDBOX } from './cloud-artifact-origin';
|
|
3
|
+
export { CLOUD_ARTIFACT_FRAME_SANDBOX };
|
|
4
|
+
/** Render a cloud artifact from its view URL on the artifact origin. */
|
|
5
|
+
export declare function CloudArtifactFrame({ src, title, className }: {
|
|
6
|
+
/** A view URL the server minted for this viewer (`createCloudArtifactViewUrl`). */
|
|
7
|
+
src: string;
|
|
8
|
+
title: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
/** A live public link to one artifact. */
|
|
12
|
+
export interface CloudArtifactPublicLink {
|
|
13
|
+
id: string;
|
|
14
|
+
url: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CloudArtifactSharePanelProps {
|
|
18
|
+
link: CloudArtifactPublicLink | null;
|
|
19
|
+
/** Whether this viewer may create or revoke the link (`CLOUD_ARTIFACT_SHARE_ROLE`). */
|
|
20
|
+
canShare: boolean;
|
|
21
|
+
onCreate(): Promise<MutationOutcome<CloudArtifactPublicLink>>;
|
|
22
|
+
onRevoke(link: CloudArtifactPublicLink): Promise<MutationOutcome<unknown>>;
|
|
23
|
+
}
|
|
24
|
+
/** Turn an artifact's public link on, copy it, or turn it off. */
|
|
25
|
+
export declare function CloudArtifactSharePanel({ link: initial, canShare, onCreate, onRevoke }: CloudArtifactSharePanelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLOUD_ARTIFACT_FRAME_SANDBOX
|
|
3
|
+
} from "../chunk-7WEPGGOW.js";
|
|
4
|
+
import "../chunk-O7UX55EU.js";
|
|
5
|
+
import "../chunk-S5SRJJQG.js";
|
|
6
|
+
|
|
7
|
+
// src/workspace-apps/react.tsx
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { Button, CopyButton, Input } from "@tangle-network/ui/primitives";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
function CloudArtifactFrame({ src, title, className }) {
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
"iframe",
|
|
14
|
+
{
|
|
15
|
+
src,
|
|
16
|
+
title,
|
|
17
|
+
className: className ?? "h-full w-full border-0 bg-white",
|
|
18
|
+
sandbox: CLOUD_ARTIFACT_FRAME_SANDBOX,
|
|
19
|
+
referrerPolicy: "no-referrer",
|
|
20
|
+
allow: "fullscreen; clipboard-write"
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function CloudArtifactSharePanel({ link: initial, canShare, onCreate, onRevoke }) {
|
|
25
|
+
const [link, setLink] = useState(initial);
|
|
26
|
+
const [busy, setBusy] = useState(false);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
const create = async () => {
|
|
29
|
+
setBusy(true);
|
|
30
|
+
setError(null);
|
|
31
|
+
const outcome = await onCreate();
|
|
32
|
+
setBusy(false);
|
|
33
|
+
if (outcome.succeeded) setLink(outcome.value);
|
|
34
|
+
else setError(outcome.message);
|
|
35
|
+
};
|
|
36
|
+
const revoke = async () => {
|
|
37
|
+
if (!link) return;
|
|
38
|
+
setBusy(true);
|
|
39
|
+
setError(null);
|
|
40
|
+
const outcome = await onRevoke(link);
|
|
41
|
+
setBusy(false);
|
|
42
|
+
if (outcome.succeeded) setLink(null);
|
|
43
|
+
else setError(outcome.message);
|
|
44
|
+
};
|
|
45
|
+
return /* @__PURE__ */ jsxs("section", { "aria-labelledby": "cloud-artifact-share-title", className: "flex flex-col gap-3 text-sm", children: [
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
47
|
+
/* @__PURE__ */ jsx("h2", { id: "cloud-artifact-share-title", className: "text-sm font-semibold text-foreground", children: "Public link" }),
|
|
48
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: link ? "Anyone with this link can open the page. Turning it off stops the link right away." : "Only members of this workspace can open this page." })
|
|
49
|
+
] }),
|
|
50
|
+
link ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center", children: [
|
|
51
|
+
/* @__PURE__ */ jsx(Input, { readOnly: true, value: link.url, "aria-label": "Public link", className: "min-w-0 flex-1 text-sm", onFocus: (event) => event.currentTarget.select() }),
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 gap-2", children: [
|
|
53
|
+
/* @__PURE__ */ jsx(CopyButton, { text: link.url, label: "Copy link", copiedLabel: "Copied" }),
|
|
54
|
+
canShare && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: busy, onClick: () => void revoke(), children: busy ? "Turning off\u2026" : "Turn off link" })
|
|
55
|
+
] })
|
|
56
|
+
] }) : canShare ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", disabled: busy, onClick: () => void create(), children: busy ? "Creating\u2026" : "Create public link" }) }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Editors and owners can share it." }),
|
|
57
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })
|
|
58
|
+
] });
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
CLOUD_ARTIFACT_FRAME_SANDBOX,
|
|
62
|
+
CloudArtifactFrame,
|
|
63
|
+
CloudArtifactSharePanel
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/workspace-apps/react.tsx"],"sourcesContent":["/**\n * Browser parts of cloud artifacts: the frame that renders one from the\n * artifact origin, and the owner's public-link control. Products supply the\n * minted view URL and the share calls; authorization stays on the server.\n */\nimport { useState } from 'react'\nimport { Button, CopyButton, Input } from '@tangle-network/ui/primitives'\nimport type { MutationOutcome } from '../web-react/async/use-confirmed-mutation'\nimport { CLOUD_ARTIFACT_FRAME_SANDBOX } from './cloud-artifact-origin'\n\nexport { CLOUD_ARTIFACT_FRAME_SANDBOX }\n\n/** Render a cloud artifact from its view URL on the artifact origin. */\nexport function CloudArtifactFrame({ src, title, className }: {\n /** A view URL the server minted for this viewer (`createCloudArtifactViewUrl`). */\n src: string\n title: string\n className?: string\n}) {\n return (\n <iframe\n src={src}\n title={title}\n className={className ?? 'h-full w-full border-0 bg-white'}\n sandbox={CLOUD_ARTIFACT_FRAME_SANDBOX}\n referrerPolicy=\"no-referrer\"\n allow=\"fullscreen; clipboard-write\"\n />\n )\n}\n\n/** A live public link to one artifact. */\nexport interface CloudArtifactPublicLink {\n id: string\n url: string\n createdAt: string\n}\n\nexport interface CloudArtifactSharePanelProps {\n link: CloudArtifactPublicLink | null\n /** Whether this viewer may create or revoke the link (`CLOUD_ARTIFACT_SHARE_ROLE`). */\n canShare: boolean\n onCreate(): Promise<MutationOutcome<CloudArtifactPublicLink>>\n onRevoke(link: CloudArtifactPublicLink): Promise<MutationOutcome<unknown>>\n}\n\n/** Turn an artifact's public link on, copy it, or turn it off. */\nexport function CloudArtifactSharePanel({ link: initial, canShare, onCreate, onRevoke }: CloudArtifactSharePanelProps) {\n const [link, setLink] = useState(initial)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const create = async () => {\n setBusy(true)\n setError(null)\n const outcome = await onCreate()\n setBusy(false)\n if (outcome.succeeded) setLink(outcome.value)\n else setError(outcome.message)\n }\n const revoke = async () => {\n if (!link) return\n setBusy(true)\n setError(null)\n const outcome = await onRevoke(link)\n setBusy(false)\n if (outcome.succeeded) setLink(null)\n else setError(outcome.message)\n }\n\n return (\n <section aria-labelledby=\"cloud-artifact-share-title\" className=\"flex flex-col gap-3 text-sm\">\n <div>\n <h2 id=\"cloud-artifact-share-title\" className=\"text-sm font-semibold text-foreground\">Public link</h2>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {link\n ? 'Anyone with this link can open the page. Turning it off stops the link right away.'\n : 'Only members of this workspace can open this page.'}\n </p>\n </div>\n {link ? (\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <Input readOnly value={link.url} aria-label=\"Public link\" className=\"min-w-0 flex-1 text-sm\" onFocus={(event) => event.currentTarget.select()} />\n <div className=\"flex shrink-0 gap-2\">\n <CopyButton text={link.url} label=\"Copy link\" copiedLabel=\"Copied\" />\n {canShare && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={busy} onClick={() => void revoke()}>\n {busy ? 'Turning off…' : 'Turn off link'}\n </Button>\n )}\n </div>\n </div>\n ) : canShare ? (\n <div>\n <Button type=\"button\" size=\"sm\" disabled={busy} onClick={() => void create()}>\n {busy ? 'Creating…' : 'Create public link'}\n </Button>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">Editors and owners can share it.</p>\n )}\n {error && <p role=\"alert\" className=\"text-sm text-destructive\">{error}</p>}\n </section>\n )\n}\n"],"mappings":";;;;;;;AAKA,SAAS,gBAAgB;AACzB,SAAS,QAAQ,YAAY,aAAa;AActC,cAoDE,YApDF;AAPG,SAAS,mBAAmB,EAAE,KAAK,OAAO,UAAU,GAKxD;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,WAAW,aAAa;AAAA,MACxB,SAAS;AAAA,MACT,gBAAe;AAAA,MACf,OAAM;AAAA;AAAA,EACR;AAEJ;AAkBO,SAAS,wBAAwB,EAAE,MAAM,SAAS,UAAU,UAAU,SAAS,GAAiC;AACrH,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,OAAO;AACxC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,QAAM,SAAS,YAAY;AACzB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,UAAM,UAAU,MAAM,SAAS;AAC/B,YAAQ,KAAK;AACb,QAAI,QAAQ,UAAW,SAAQ,QAAQ,KAAK;AAAA,QACvC,UAAS,QAAQ,OAAO;AAAA,EAC/B;AACA,QAAM,SAAS,YAAY;AACzB,QAAI,CAAC,KAAM;AACX,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,UAAM,UAAU,MAAM,SAAS,IAAI;AACnC,YAAQ,KAAK;AACb,QAAI,QAAQ,UAAW,SAAQ,IAAI;AAAA,QAC9B,UAAS,QAAQ,OAAO;AAAA,EAC/B;AAEA,SACE,qBAAC,aAAQ,mBAAgB,8BAA6B,WAAU,+BAC9D;AAAA,yBAAC,SACC;AAAA,0BAAC,QAAG,IAAG,8BAA6B,WAAU,yCAAwC,yBAAW;AAAA,MACjG,oBAAC,OAAE,WAAU,sCACV,iBACG,uFACA,sDACN;AAAA,OACF;AAAA,IACC,OACC,qBAAC,SAAI,WAAU,mDACb;AAAA,0BAAC,SAAM,UAAQ,MAAC,OAAO,KAAK,KAAK,cAAW,eAAc,WAAU,0BAAyB,SAAS,CAAC,UAAU,MAAM,cAAc,OAAO,GAAG;AAAA,MAC/I,qBAAC,SAAI,WAAU,uBACb;AAAA,4BAAC,cAAW,MAAM,KAAK,KAAK,OAAM,aAAY,aAAY,UAAS;AAAA,QAClE,YACC,oBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,KAAK,OAAO,GAC1F,iBAAO,sBAAiB,iBAC3B;AAAA,SAEJ;AAAA,OACF,IACE,WACF,oBAAC,SACC,8BAAC,UAAO,MAAK,UAAS,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,KAAK,OAAO,GACxE,iBAAO,mBAAc,sBACxB,GACF,IAEA,oBAAC,OAAE,WAAU,iCAAgC,8CAAgC;AAAA,IAE9E,SAAS,oBAAC,OAAE,MAAK,SAAQ,WAAU,4BAA4B,iBAAM;AAAA,KACxE;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.60.
|
|
3
|
+
"version": "0.60.197",
|
|
4
4
|
"packageManager": "pnpm@11.24.0",
|
|
5
5
|
"description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
|
|
6
6
|
"keywords": [
|
|
@@ -433,6 +433,11 @@
|
|
|
433
433
|
"import": "./dist/workspace-apps/index.js",
|
|
434
434
|
"default": "./dist/workspace-apps/index.js"
|
|
435
435
|
},
|
|
436
|
+
"./workspace-apps/react": {
|
|
437
|
+
"types": "./dist/workspace-apps/react.d.ts",
|
|
438
|
+
"import": "./dist/workspace-apps/react.js",
|
|
439
|
+
"default": "./dist/workspace-apps/react.js"
|
|
440
|
+
},
|
|
436
441
|
"./workspace-react": {
|
|
437
442
|
"types": "./dist/workspace-react/index.d.ts",
|
|
438
443
|
"import": "./dist/workspace-react/index.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/capability.ts","../src/tools/gating.ts","../src/tools/dispatch.ts","../src/tools/runtime.ts","../src/tools/http.ts"],"sourcesContent":["/**\n * Per-user capability token — the sandbox→app auth primitive behind the\n * `verifyToken` seam in {@link authenticateToolRequest}.\n *\n * An app-agent runs inside the sandbox and reaches the host app back over HTTP\n * (the app tools, the integration-invoke bridge). The route must act AS the\n * connecting user without trusting any model-supplied identity, so the turn\n * mints a short HMAC token bound to the user id and bakes it into the per-turn\n * MCP server header; the route verifies it to recover the user.\n *\n * `HMAC-SHA256(secret, \"user:<userId>\")`, base64url, with an app-chosen prefix.\n * The token encodes no scopes — the hub's policy engine authorizes per action.\n * Fail-closed: with no secret, no token is minted (the caller MUST omit the MCP\n * server rather than fake an authorized call). WebCrypto only — runs on\n * Workers, Node, and the browser with no Node `crypto` dependency.\n */\n\nimport {\n base64UrlDecodeText,\n base64UrlEncodeText,\n constantTimeEqual,\n hmacSha256Base64Url,\n} from '../crypto/web-token'\n\n/** Define options for creating and verifying capability tokens including secret and prefix */\nexport interface CapabilityTokenOptions {\n /** Shared HMAC secret. When absent, mint returns undefined / verify returns false. */\n secret?: string\n /** Token prefix (namespaces the credential; lets verify reject foreign tokens\n * cheaply). Default `cap_`. */\n prefix?: string\n}\n\n/** Mint a capability token for `userId`, or `undefined` when no secret is\n * configured (fail-closed — the caller omits the MCP server rather than fake it). */\nexport async function createCapabilityToken(userId: string, opts: CapabilityTokenOptions): Promise<string | undefined> {\n const secret = opts.secret?.trim()\n if (!secret) return undefined\n const prefix = opts.prefix ?? 'cap_'\n return `${prefix}${await sign(userId, secret)}`\n}\n\n/** Verify a capability token against `userId`. Returns false (never throws) for\n * an unconfigured secret, a wrong prefix, a malformed token, or a mismatch. */\nexport async function verifyCapabilityToken(userId: string, token: string, opts: CapabilityTokenOptions): Promise<boolean> {\n const secret = opts.secret?.trim()\n const prefix = opts.prefix ?? 'cap_'\n if (!secret || !token.startsWith(prefix)) return false\n const expected = `${prefix}${await sign(userId, secret)}`\n return constantTimeEqual(token, expected)\n}\n\n/** Define options for capability tokens that expire after a specified lifetime in milliseconds */\nexport interface ExpiringCapabilityTokenOptions extends CapabilityTokenOptions {\n /** Token lifetime. Expired tokens verify false regardless of signature. */\n expiresInMs: number\n /** Clock injection for tests; defaults to Date.now. */\n now?: () => number\n}\n\n/**\n * Mint an EXPIRING capability token: `<prefix><base64url(payload)>.<sig>` where\n * the payload carries `{ sub, exp, n }` (subject, epoch-ms expiry, random\n * nonce) and the signature is HMAC-SHA256 over the encoded payload. Use this\n * for user-initiated scoped channels (e.g. a per-sequence MCP endpoint) where\n * a captured token must not stay valid past its window; the bare\n * {@link createCapabilityToken} remains for turn-scoped tool bridges whose\n * mint+verify happen inside one request cycle. Fail-closed like the bare\n * variant: no secret → no token.\n */\nexport async function createExpiringCapabilityToken(subject: string, opts: ExpiringCapabilityTokenOptions): Promise<string | undefined> {\n const secret = opts.secret?.trim()\n if (!secret) return undefined\n if (!Number.isFinite(opts.expiresInMs) || opts.expiresInMs <= 0) throw new Error('expiresInMs must be a positive number')\n const prefix = opts.prefix ?? 'cap_'\n const now = opts.now ?? Date.now\n const payload = base64UrlEncodeText(JSON.stringify({ sub: subject, exp: now() + opts.expiresInMs, n: crypto.randomUUID() }))\n return `${prefix}${payload}.${await hmacSha256Base64Url(payload, secret)}`\n}\n\n/** Verify an expiring token against `subject`: prefix, payload integrity,\n * subject match, and expiry all checked; returns false (never throws) on any\n * failure including a malformed payload. */\nexport async function verifyExpiringCapabilityToken(subject: string, token: string, opts: CapabilityTokenOptions & { now?: () => number }): Promise<boolean> {\n const secret = opts.secret?.trim()\n const prefix = opts.prefix ?? 'cap_'\n if (!secret || !token.startsWith(prefix)) return false\n const body = token.slice(prefix.length)\n const dot = body.lastIndexOf('.')\n if (dot <= 0 || dot === body.length - 1) return false\n const payload = body.slice(0, dot)\n const sig = body.slice(dot + 1)\n if (!constantTimeEqual(sig, await hmacSha256Base64Url(payload, secret))) return false\n let parsed: { sub?: unknown; exp?: unknown }\n try {\n parsed = JSON.parse(base64UrlDecodeText(payload)) as { sub?: unknown; exp?: unknown }\n } catch {\n return false\n }\n if (parsed.sub !== subject) return false\n if (typeof parsed.exp !== 'number') return false\n const now = opts.now ?? Date.now\n return parsed.exp > now()\n}\n\nasync function sign(userId: string, secret: string): Promise<string> {\n return hmacSha256Base64Url(`user:${userId}`, secret)\n}\n","/**\n * Capability gating — compose an agent session's tool surface from a\n * product-defined capability registry.\n *\n * Products that let users pick what an agent can do (a \"Studio\" agent with the\n * full build toolset vs a clean \"Assistant\" with none) all need the same\n * mechanics: a registry of named capabilities, each unlocking proposal types\n * and/or named tool groups, resolved against the product's\n * {@link AppToolTaxonomy} into the concrete tools to expose. The mechanics are\n * generic and live here; the capability VOCABULARY (ids, labels, which\n * proposal types, which tool groups) is the product's.\n */\n\nimport type { AppToolTaxonomy } from './types'\n\n/** One toggleable tool group in a product's capability registry. */\nexport interface ToolCapability {\n id: string\n label: string\n description: string\n /** Proposal types this capability unlocks (intersected with the taxonomy,\n * so a capability can never widen the product's proposal surface). */\n proposalTypes?: readonly string[]\n /** Unlocks every taxonomy proposal type beyond the base set — the domain's\n * specialized long tail (risk_assessment, vuln_report, …). */\n domainActions?: boolean\n /** Named product tool groups (e.g. 'sandbox', 'integrations') this\n * capability unlocks. The vocabulary is the product's; the resolver only\n * unions them. */\n toolGroups?: readonly string[]\n}\n\n/** Resolve options for determining tool capabilities based on taxonomy, capabilities, and enabled IDs */\nexport interface ResolveToolCapabilitiesOptions {\n taxonomy: AppToolTaxonomy\n /** The product's full capability registry. */\n capabilities: readonly ToolCapability[]\n /** Enabled capability ids. `undefined` means full access (legacy callers\n * that don't send a capability set); an explicit `[]` means a pure chat\n * agent with no tools. Unknown ids are ignored. */\n enabled: readonly string[] | undefined\n /** The shared base proposal types `domainActions` excludes. Defaults to\n * every type some capability names explicitly via `proposalTypes` — i.e.\n * \"domain actions\" are the taxonomy types no capability claims. */\n baseProposalTypes?: readonly string[]\n}\n\n/** Describe resolved capabilities including proposal types and product tool groups to expose */\nexport interface ResolvedToolCapabilities {\n /** Proposal types to keep — feed to {@link restrictTaxonomy}. */\n proposalTypes: string[]\n /** Product tool groups to expose (deduped union across enabled caps). */\n toolGroups: string[]\n}\n\n/**\n * Resolve an enabled capability-id set against a taxonomy into the concrete\n * tool surface. Fail-closed: only types present in the taxonomy survive, and\n * an empty `enabled` set yields no tools at all.\n */\nexport function resolveToolCapabilities(\n opts: ResolveToolCapabilitiesOptions,\n): ResolvedToolCapabilities {\n const { taxonomy, capabilities, enabled } = opts\n if (enabled === undefined) {\n return {\n proposalTypes: [...taxonomy.proposalTypes],\n toolGroups: [...new Set(capabilities.flatMap((c) => c.toolGroups ?? []))],\n }\n }\n const base = new Set(\n opts.baseProposalTypes ?? capabilities.flatMap((c) => c.proposalTypes ?? []),\n )\n const domainTypes = taxonomy.proposalTypes.filter((t) => !base.has(t))\n const byId = new Map(capabilities.map((c) => [c.id, c]))\n\n const proposalTypes = new Set<string>()\n const toolGroups = new Set<string>()\n for (const id of enabled) {\n const cap = byId.get(id)\n if (!cap) continue\n for (const t of cap.proposalTypes ?? []) {\n if (taxonomy.proposalTypes.includes(t)) proposalTypes.add(t)\n }\n if (cap.domainActions) for (const t of domainTypes) proposalTypes.add(t)\n for (const g of cap.toolGroups ?? []) toolGroups.add(g)\n }\n return { proposalTypes: [...proposalTypes], toolGroups: [...toolGroups] }\n}\n\n/**\n * Restrict a taxonomy to a subset of proposal types, intersecting the\n * regulated subset too — the regulated label survives restriction, so a\n * narrowed agent can never launder a regulated type into an unregulated one.\n */\nexport function restrictTaxonomy(\n taxonomy: AppToolTaxonomy,\n allowed: readonly string[],\n): AppToolTaxonomy {\n const allow = new Set(allowed)\n return {\n proposalTypes: taxonomy.proposalTypes.filter((t) => allow.has(t)),\n regulatedTypes: taxonomy.regulatedTypes.filter((t) => allow.has(t)),\n }\n}\n","import { ToolInputError } from './errors'\nimport { isAppToolName } from './openai'\nimport { findCustomTool, type AppToolDefinition } from './registry'\nimport { assertRenderUiSchema } from './render-ui-schema'\nimport type {\n AppToolContext,\n AppToolHandlers,\n AppToolOutcome,\n AppToolProducedEvent,\n AppToolTaxonomy,\n} from './types'\n\n/** Define options for dispatching tools including handlers, taxonomy, custom tools, and approval policies */\nexport interface DispatchOptions {\n handlers: AppToolHandlers\n taxonomy: AppToolTaxonomy\n /** Product-registered tools beyond the four built-ins. A called name that is\n * not a built-in is dispatched to the matching {@link AppToolDefinition.execute}\n * through this same validation/outcome path. */\n customTools?: readonly AppToolDefinition[]\n /** Per-call approval policy. When provided it OVERRIDES the static\n * `taxonomy.regulatedTypes` membership check, so products can gate by\n * cost threshold, environment, or first-use instead of always/never.\n * Fail-closed: a predicate that throws counts as \"approval required\". */\n needsApproval?: (type: string, args: { title: string; description: string | null }, ctx: AppToolContext) => boolean | Promise<boolean>\n /** Called at the real side-effect site for proposals (proposal_created) and\n * generated views (artifact) so a consumer's completion oracle credits\n * persisted state. Omit when produced state isn't tracked. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\n/**\n * The ONE place an app-tool call is validated, dispatched to the product's\n * handler, and turned into an {@link AppToolOutcome} + produced events. Shared\n * by the HTTP route layer and the agent-runtime executor so both paths apply\n * identical validation and identical side effects. A {@link ToolInputError}\n * (bad input the agent can correct) and any other throw both become\n * `{ ok: false }` — a tool call never silently \"succeeds\" without its effect.\n */\nexport async function dispatchAppTool(\n toolName: string,\n rawArgs: Record<string, unknown>,\n ctx: AppToolContext,\n opts: DispatchOptions,\n): Promise<AppToolOutcome> {\n try {\n if (!isAppToolName(toolName)) {\n const custom = findCustomTool(toolName, opts.customTools)\n if (!custom) return { ok: false, code: 'unknown_tool', message: `${toolName} is not an app tool.` }\n // Custom tools own their own arg validation (their execute throws\n // ToolInputError for correctable input); the outer try/catch maps it.\n const result = await custom.execute(rawArgs, ctx)\n return { ok: true, result }\n }\n\n if (toolName === 'submit_proposal') {\n const type = String(rawArgs.type ?? '').trim()\n const title = String(rawArgs.title ?? '').trim()\n if (!type || !opts.taxonomy.proposalTypes.includes(type)) {\n return { ok: false, code: 'invalid_type', message: `type must be one of: ${opts.taxonomy.proposalTypes.join(', ')}.` }\n }\n if (!title) return { ok: false, code: 'missing_title', message: 'title is required.' }\n const description = rawArgs.description == null ? null : String(rawArgs.description)\n // Approval policy runs BEFORE the handler so the decision can gate the\n // side effect itself, not merely re-label it afterwards.\n let regulated = opts.taxonomy.regulatedTypes.includes(type)\n if (opts.needsApproval) {\n try {\n regulated = await opts.needsApproval(type, { title, description }, ctx)\n } catch {\n regulated = true // fail-closed: a broken policy means approval required\n }\n }\n const r = await opts.handlers.submitProposal({ type, title, description, regulated }, ctx)\n // Pass the handler's result through: products with immediate-execute\n // proposal types return status 'executed' plus their own fields\n // (e.g. datasetId) — the model must see what actually happened, not a\n // hard-coded \"queued for approval\".\n const { proposalId, deduped, status, ...extra } = r\n const effectiveStatus = status ?? 'queued_for_approval'\n opts.onProduced?.({\n type: 'proposal_created',\n proposalId,\n title,\n status: effectiveStatus === 'executed' ? 'executed' : 'pending',\n content: description ?? undefined,\n })\n return { ok: true, result: { ...extra, status: effectiveStatus, proposalId, deduped, regulated } }\n }\n\n if (toolName === 'schedule_followup') {\n const r = await opts.handlers.scheduleFollowup(\n { title: String(rawArgs.title ?? ''), dueDate: String(rawArgs.dueDate ?? ''), priority: rawArgs.priority as string | undefined },\n ctx,\n )\n return { ok: true, result: { followupId: r.id, dueDate: r.dueDate, deduped: r.deduped } }\n }\n\n if (toolName === 'render_ui') {\n assertRenderUiSchema(rawArgs.schema)\n const r = await opts.handlers.renderUi({ title: String(rawArgs.title ?? ''), schema: rawArgs.schema }, ctx)\n opts.onProduced?.({ type: 'artifact', path: r.path, content: r.content })\n return { ok: true, result: { path: r.path } }\n }\n\n // add_citation\n const r = await opts.handlers.addCitation(\n { path: String(rawArgs.path ?? ''), quote: String(rawArgs.quote ?? ''), label: rawArgs.label as string | undefined },\n ctx,\n )\n return { ok: true, result: { citationId: r.citationId, path: r.path } }\n } catch (err) {\n if (err instanceof ToolInputError) return { ok: false, code: err.code, message: err.message, status: err.status }\n return { ok: false, code: 'app_tool_error', message: err instanceof Error ? err.message : String(err), status: 500 }\n }\n}\n\n/** HTTP status for a failed outcome — the handler's `ToolInputError.status`\n * when present, else 400 for a validation reject. */\nexport function outcomeStatus(outcome: Extract<AppToolOutcome, { ok: false }>): number {\n return outcome.status ?? 400\n}\n","import { dispatchAppTool, type DispatchOptions } from './dispatch'\nimport type { AppToolContext, AppToolOutcome } from './types'\n\n/** Executes an app-tool call the model emits on the agent-runtime chat path.\n * Plug into `runChatThroughRuntime({ appToolExecutor })` (or any loop that\n * dispatches function tool_calls). */\nexport type AppToolRuntimeExecutor = (call: {\n toolName: string\n args: Record<string, unknown>\n}) => Promise<AppToolOutcome>\n\n/** Define options for executing runtime tasks with a trusted per-turn context */\nexport interface RuntimeExecutorOptions extends DispatchOptions {\n /** The trusted per-turn context — supplied directly (not from headers), since\n * the runtime path has no HTTP request. */\n ctx: AppToolContext\n}\n\n/**\n * Build the runtime executor for one turn. The agent-runtime backend must also\n * advertise the tools (`buildAppToolOpenAITools(taxonomy)` on the backend's\n * `tools`) for the model to call them; this executor fulfils each call against\n * the product's handlers and emits produced events via `opts.onProduced`.\n */\nexport function createAppToolRuntimeExecutor(opts: RuntimeExecutorOptions): AppToolRuntimeExecutor {\n return ({ toolName, args }) => dispatchAppTool(toolName, args, opts.ctx, opts)\n}\n","import { authenticateToolRequest, type ToolHeaderNames } from './auth'\nimport { dispatchAppTool, outcomeStatus, type DispatchOptions } from './dispatch'\nimport { buildAppToolOpenAITools, type AppToolName } from './openai'\nimport type { AppToolDefinition } from './registry'\nimport { createMcpToolHandler, type McpToolCallContent } from './mcp-rpc'\nimport type { AppToolOutcome } from './types'\n\n/** Define options for handling tool requests including tool identification and token verification */\nexport interface HandleToolRequestOptions extends DispatchOptions {\n /** Which app tool this route serves — a built-in name or a product-registered\n * {@link AppToolDefinition} (auto-added to `customTools` for dispatch). */\n tool: AppToolName | AppToolDefinition\n /** Verify the bearer capability token belongs to the header user. */\n verifyToken: (userId: string, bearer: string) => Promise<boolean>\n headerNames?: ToolHeaderNames\n /** Optional model-facing description used by the MCP tools/list response. */\n description?: string\n /** Product-specific values for the schedule_followup.priority schema. */\n priorityValues?: readonly string[]\n /** Optional success-message builder for a friendlier tool result. */\n message?: (result: unknown) => string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isMcpRequest(request: Request, value: unknown): boolean {\n // A valid request declares jsonrpc. An incomplete envelope has no reliable\n // body marker, so use the Streamable HTTP Accept contract as the explicit\n // transport discriminator before falling back to direct tool arguments.\n const acceptsMcp = request.headers\n .get('accept')\n ?.split(',')\n .some((part) => part.trim().toLowerCase() === 'text/event-stream')\n return (\n isRecord(value)\n && (\n 'jsonrpc' in value\n || acceptsMcp === true\n || request.headers.has('mcp-session-id')\n || request.headers.has('mcp-protocol-version')\n )\n )\n}\n\nfunction toolName(tool: AppToolName | AppToolDefinition): string {\n return typeof tool === 'string' ? tool : tool.name\n}\n\nfunction dispatchOptions(opts: HandleToolRequestOptions): DispatchOptions {\n return {\n ...opts,\n customTools:\n typeof opts.tool === 'string' ? opts.customTools : [...(opts.customTools ?? []), opts.tool],\n }\n}\n\nfunction mcpToolManifest(opts: HandleToolRequestOptions): {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n} {\n const name = toolName(opts.tool)\n const tools = buildAppToolOpenAITools(opts.taxonomy, {\n ...(opts.description && typeof opts.tool === 'string'\n ? { descriptions: { [opts.tool]: opts.description } }\n : {}),\n ...(opts.priorityValues ? { priorityValues: opts.priorityValues } : {}),\n ...(typeof opts.tool === 'string' ? {} : { customTools: [opts.tool] }),\n })\n const definition = tools.find((entry) => entry.function.name === name)\n if (!definition) {\n throw new Error(`No MCP manifest exists for app tool ${name}`)\n }\n return {\n name,\n description: definition.function.description,\n inputSchema: definition.function.parameters,\n }\n}\n\nfunction mcpToolResult(outcome: AppToolOutcome, opts: HandleToolRequestOptions): McpToolCallContent {\n if (!outcome.ok) {\n return {\n content: [{ type: 'text', text: `${outcome.code}: ${outcome.message}` }],\n isError: true,\n }\n }\n\n const value = outcome.result\n const payload = value !== null && typeof value === 'object' && !Array.isArray(value)\n ? { ok: true, ...value }\n : { ok: true, value }\n return {\n content: [{ type: 'text', text: JSON.stringify({\n ...payload,\n ...(opts.message ? { message: opts.message(value) } : {}),\n }) }],\n }\n}\n\nfunction createAppToolMcpHandler(\n authContext: Parameters<DispatchOptions['handlers']['submitProposal']>[1],\n opts: HandleToolRequestOptions,\n): (request: Request) => Promise<Response> {\n const manifest = mcpToolManifest(opts)\n return createMcpToolHandler({\n serverInfo: { name: 'agent-app-tool', version: '1' },\n tools: [{\n ...manifest,\n run: (args) => dispatchAppTool(manifest.name, args, authContext, dispatchOptions(opts)),\n }],\n buildEnv: () => ({}),\n formatResult: (result) => mcpToolResult(result as AppToolOutcome, opts),\n })\n}\n\n/**\n * Handle one app-tool HTTP request end to end — the sandbox MCP path. The\n * agent's per-turn HTTP MCP server POSTs here; this authenticates (header user\n * + capability token), reads the args (MCP-alias tolerant), dispatches to the\n * product handler, and returns a JSON Response. A product's route file becomes\n * a one-liner: `export const action = ({ request }) => handleAppToolRequest(request, cfg)`.\n */\nexport async function handleAppToolRequest(request: Request, opts: HandleToolRequestOptions): Promise<Response> {\n if (request.method !== 'POST') return Response.json({ error: 'Method not allowed' }, { status: 405 })\n\n const auth = await authenticateToolRequest(request, { verifyToken: opts.verifyToken, headerNames: opts.headerNames })\n if (!auth.ok) return auth.response\n\n let body: unknown\n try {\n body = await request.clone().json()\n } catch {\n return Response.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n\n // The same route supports the direct app-tool HTTP shape used by the\n // OpenAI/runtime surfaces and stateless Streamable HTTP MCP JSON-RPC used by\n // OpenCode. Keeping both at this boundary prevents every consumer from\n // writing a second route just to translate protocol envelopes.\n if (isMcpRequest(request, body)) {\n return createAppToolMcpHandler(auth.ctx, opts)(request)\n }\n\n if (!isRecord(body)) return Response.json({ error: 'Invalid JSON' }, { status: 400 })\n const args = (body.args ?? body.arguments ?? body) as Record<string, unknown>\n\n // A custom tool passed as `tool` is registered for this dispatch so the route\n // file stays a one-liner (no separate customTools wiring needed).\n const toolName = typeof opts.tool === 'string' ? opts.tool : opts.tool.name\n const outcome = await dispatchAppTool(toolName, args, auth.ctx, dispatchOptions(opts))\n if (!outcome.ok) {\n return Response.json({ error: outcome.code, message: outcome.message }, { status: outcomeStatus(outcome) })\n }\n const payload = outcome.result as Record<string, unknown>\n return Response.json({ ok: true, ...payload, ...(opts.message ? { message: opts.message(outcome.result) } : {}) })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmCA,eAAsB,sBAAsB,QAAgB,MAA2D;AACrH,QAAM,SAAS,KAAK,QAAQ,KAAK;AACjC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,KAAK,UAAU;AAC9B,SAAO,GAAG,MAAM,GAAG,MAAM,KAAK,QAAQ,MAAM,CAAC;AAC/C;AAIA,eAAsB,sBAAsB,QAAgB,OAAe,MAAgD;AACzH,QAAM,SAAS,KAAK,QAAQ,KAAK;AACjC,QAAM,SAAS,KAAK,UAAU;AAC9B,MAAI,CAAC,UAAU,CAAC,MAAM,WAAW,MAAM,EAAG,QAAO;AACjD,QAAM,WAAW,GAAG,MAAM,GAAG,MAAM,KAAK,QAAQ,MAAM,CAAC;AACvD,SAAO,kBAAkB,OAAO,QAAQ;AAC1C;AAoBA,eAAsB,8BAA8B,SAAiB,MAAmE;AACtI,QAAM,SAAS,KAAK,QAAQ,KAAK;AACjC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,CAAC,OAAO,SAAS,KAAK,WAAW,KAAK,KAAK,eAAe,EAAG,OAAM,IAAI,MAAM,uCAAuC;AACxH,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,MAAM,KAAK,OAAO,KAAK;AAC7B,QAAM,UAAU,oBAAoB,KAAK,UAAU,EAAE,KAAK,SAAS,KAAK,IAAI,IAAI,KAAK,aAAa,GAAG,OAAO,WAAW,EAAE,CAAC,CAAC;AAC3H,SAAO,GAAG,MAAM,GAAG,OAAO,IAAI,MAAM,oBAAoB,SAAS,MAAM,CAAC;AAC1E;AAKA,eAAsB,8BAA8B,SAAiB,OAAe,MAAyE;AAC3J,QAAM,SAAS,KAAK,QAAQ,KAAK;AACjC,QAAM,SAAS,KAAK,UAAU;AAC9B,MAAI,CAAC,UAAU,CAAC,MAAM,WAAW,MAAM,EAAG,QAAO;AACjD,QAAM,OAAO,MAAM,MAAM,OAAO,MAAM;AACtC,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI,OAAO,KAAK,QAAQ,KAAK,SAAS,EAAG,QAAO;AAChD,QAAM,UAAU,KAAK,MAAM,GAAG,GAAG;AACjC,QAAM,MAAM,KAAK,MAAM,MAAM,CAAC;AAC9B,MAAI,CAAC,kBAAkB,KAAK,MAAM,oBAAoB,SAAS,MAAM,CAAC,EAAG,QAAO;AAChF,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,oBAAoB,OAAO,CAAC;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,QAAS,QAAO;AACnC,MAAI,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC3C,QAAM,MAAM,KAAK,OAAO,KAAK;AAC7B,SAAO,OAAO,MAAM,IAAI;AAC1B;AAEA,eAAe,KAAK,QAAgB,QAAiC;AACnE,SAAO,oBAAoB,QAAQ,MAAM,IAAI,MAAM;AACrD;;;AC/CO,SAAS,wBACd,MAC0B;AAC1B,QAAM,EAAE,UAAU,cAAc,QAAQ,IAAI;AAC5C,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,MACL,eAAe,CAAC,GAAG,SAAS,aAAa;AAAA,MACzC,YAAY,CAAC,GAAG,IAAI,IAAI,aAAa,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;AAAA,IAC1E;AAAA,EACF;AACA,QAAM,OAAO,IAAI;AAAA,IACf,KAAK,qBAAqB,aAAa,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAAA,EAC7E;AACA,QAAM,cAAc,SAAS,cAAc,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;AACrE,QAAM,OAAO,IAAI,IAAI,aAAa,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAEvD,QAAM,gBAAgB,oBAAI,IAAY;AACtC,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,MAAM,SAAS;AACxB,UAAM,MAAM,KAAK,IAAI,EAAE;AACvB,QAAI,CAAC,IAAK;AACV,eAAW,KAAK,IAAI,iBAAiB,CAAC,GAAG;AACvC,UAAI,SAAS,cAAc,SAAS,CAAC,EAAG,eAAc,IAAI,CAAC;AAAA,IAC7D;AACA,QAAI,IAAI,cAAe,YAAW,KAAK,YAAa,eAAc,IAAI,CAAC;AACvE,eAAW,KAAK,IAAI,cAAc,CAAC,EAAG,YAAW,IAAI,CAAC;AAAA,EACxD;AACA,SAAO,EAAE,eAAe,CAAC,GAAG,aAAa,GAAG,YAAY,CAAC,GAAG,UAAU,EAAE;AAC1E;AAOO,SAAS,iBACd,UACA,SACiB;AACjB,QAAM,QAAQ,IAAI,IAAI,OAAO;AAC7B,SAAO;AAAA,IACL,eAAe,SAAS,cAAc,OAAO,CAAC,MAAM,MAAM,IAAI,CAAC,CAAC;AAAA,IAChE,gBAAgB,SAAS,eAAe,OAAO,CAAC,MAAM,MAAM,IAAI,CAAC,CAAC;AAAA,EACpE;AACF;;;ACjEA,eAAsB,gBACpBA,WACA,SACA,KACA,MACyB;AACzB,MAAI;AACF,QAAI,CAAC,cAAcA,SAAQ,GAAG;AAC5B,YAAM,SAAS,eAAeA,WAAU,KAAK,WAAW;AACxD,UAAI,CAAC,OAAQ,QAAO,EAAE,IAAI,OAAO,MAAM,gBAAgB,SAAS,GAAGA,SAAQ,uBAAuB;AAGlG,YAAM,SAAS,MAAM,OAAO,QAAQ,SAAS,GAAG;AAChD,aAAO,EAAE,IAAI,MAAM,OAAO;AAAA,IAC5B;AAEA,QAAIA,cAAa,mBAAmB;AAClC,YAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE,EAAE,KAAK;AAC7C,YAAM,QAAQ,OAAO,QAAQ,SAAS,EAAE,EAAE,KAAK;AAC/C,UAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,cAAc,SAAS,IAAI,GAAG;AACxD,eAAO,EAAE,IAAI,OAAO,MAAM,gBAAgB,SAAS,wBAAwB,KAAK,SAAS,cAAc,KAAK,IAAI,CAAC,IAAI;AAAA,MACvH;AACA,UAAI,CAAC,MAAO,QAAO,EAAE,IAAI,OAAO,MAAM,iBAAiB,SAAS,qBAAqB;AACrF,YAAM,cAAc,QAAQ,eAAe,OAAO,OAAO,OAAO,QAAQ,WAAW;AAGnF,UAAI,YAAY,KAAK,SAAS,eAAe,SAAS,IAAI;AAC1D,UAAI,KAAK,eAAe;AACtB,YAAI;AACF,sBAAY,MAAM,KAAK,cAAc,MAAM,EAAE,OAAO,YAAY,GAAG,GAAG;AAAA,QACxE,QAAQ;AACN,sBAAY;AAAA,QACd;AAAA,MACF;AACA,YAAMC,KAAI,MAAM,KAAK,SAAS,eAAe,EAAE,MAAM,OAAO,aAAa,UAAU,GAAG,GAAG;AAKzF,YAAM,EAAE,YAAY,SAAS,QAAQ,GAAG,MAAM,IAAIA;AAClD,YAAM,kBAAkB,UAAU;AAClC,WAAK,aAAa;AAAA,QAChB,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,QAAQ,oBAAoB,aAAa,aAAa;AAAA,QACtD,SAAS,eAAe;AAAA,MAC1B,CAAC;AACD,aAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,GAAG,OAAO,QAAQ,iBAAiB,YAAY,SAAS,UAAU,EAAE;AAAA,IACnG;AAEA,QAAID,cAAa,qBAAqB;AACpC,YAAMC,KAAI,MAAM,KAAK,SAAS;AAAA,QAC5B,EAAE,OAAO,OAAO,QAAQ,SAAS,EAAE,GAAG,SAAS,OAAO,QAAQ,WAAW,EAAE,GAAG,UAAU,QAAQ,SAA+B;AAAA,QAC/H;AAAA,MACF;AACA,aAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,YAAYA,GAAE,IAAI,SAASA,GAAE,SAAS,SAASA,GAAE,QAAQ,EAAE;AAAA,IAC1F;AAEA,QAAID,cAAa,aAAa;AAC5B,2BAAqB,QAAQ,MAAM;AACnC,YAAMC,KAAI,MAAM,KAAK,SAAS,SAAS,EAAE,OAAO,OAAO,QAAQ,SAAS,EAAE,GAAG,QAAQ,QAAQ,OAAO,GAAG,GAAG;AAC1G,WAAK,aAAa,EAAE,MAAM,YAAY,MAAMA,GAAE,MAAM,SAASA,GAAE,QAAQ,CAAC;AACxE,aAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,MAAMA,GAAE,KAAK,EAAE;AAAA,IAC9C;AAGA,UAAM,IAAI,MAAM,KAAK,SAAS;AAAA,MAC5B,EAAE,MAAM,OAAO,QAAQ,QAAQ,EAAE,GAAG,OAAO,OAAO,QAAQ,SAAS,EAAE,GAAG,OAAO,QAAQ,MAA4B;AAAA,MACnH;AAAA,IACF;AACA,WAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,YAAY,EAAE,YAAY,MAAM,EAAE,KAAK,EAAE;AAAA,EACxE,SAAS,KAAK;AACZ,QAAI,eAAe,eAAgB,QAAO,EAAE,IAAI,OAAO,MAAM,IAAI,MAAM,SAAS,IAAI,SAAS,QAAQ,IAAI,OAAO;AAChH,WAAO,EAAE,IAAI,OAAO,MAAM,kBAAkB,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAAG,QAAQ,IAAI;AAAA,EACrH;AACF;AAIO,SAAS,cAAc,SAAyD;AACrF,SAAO,QAAQ,UAAU;AAC3B;;;ACjGO,SAAS,6BAA6B,MAAsD;AACjG,SAAO,CAAC,EAAE,UAAAC,WAAU,KAAK,MAAM,gBAAgBA,WAAU,MAAM,KAAK,KAAK,IAAI;AAC/E;;;ACHA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,aAAa,SAAkB,OAAyB;AAI/D,QAAM,aAAa,QAAQ,QACxB,IAAI,QAAQ,GACX,MAAM,GAAG,EACV,KAAK,CAAC,SAAS,KAAK,KAAK,EAAE,YAAY,MAAM,mBAAmB;AACnE,SACE,SAAS,KAAK,MAEZ,aAAa,SACV,eAAe,QACf,QAAQ,QAAQ,IAAI,gBAAgB,KACpC,QAAQ,QAAQ,IAAI,sBAAsB;AAGnD;AAEA,SAAS,SAAS,MAA+C;AAC/D,SAAO,OAAO,SAAS,WAAW,OAAO,KAAK;AAChD;AAEA,SAAS,gBAAgB,MAAiD;AACxE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,aACE,OAAO,KAAK,SAAS,WAAW,KAAK,cAAc,CAAC,GAAI,KAAK,eAAe,CAAC,GAAI,KAAK,IAAI;AAAA,EAC9F;AACF;AAEA,SAAS,gBAAgB,MAIvB;AACA,QAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,QAAM,QAAQ,wBAAwB,KAAK,UAAU;AAAA,IACnD,GAAI,KAAK,eAAe,OAAO,KAAK,SAAS,WACzC,EAAE,cAAc,EAAE,CAAC,KAAK,IAAI,GAAG,KAAK,YAAY,EAAE,IAClD,CAAC;AAAA,IACL,GAAI,KAAK,iBAAiB,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;AAAA,IACrE,GAAI,OAAO,KAAK,SAAS,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,KAAK,IAAI,EAAE;AAAA,EACtE,CAAC;AACD,QAAM,aAAa,MAAM,KAAK,CAAC,UAAU,MAAM,SAAS,SAAS,IAAI;AACrE,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,uCAAuC,IAAI,EAAE;AAAA,EAC/D;AACA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,WAAW,SAAS;AAAA,IACjC,aAAa,WAAW,SAAS;AAAA,EACnC;AACF;AAEA,SAAS,cAAc,SAAyB,MAAoD;AAClG,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG,QAAQ,IAAI,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,MACvE,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,QAAQ,QAAQ;AACtB,QAAM,UAAU,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC/E,EAAE,IAAI,MAAM,GAAG,MAAM,IACrB,EAAE,IAAI,MAAM,MAAM;AACtB,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU;AAAA,MAC7C,GAAG;AAAA,MACH,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,KAAK,EAAE,IAAI,CAAC;AAAA,IACzD,CAAC,EAAE,CAAC;AAAA,EACN;AACF;AAEA,SAAS,wBACP,aACA,MACyC;AACzC,QAAM,WAAW,gBAAgB,IAAI;AACrC,SAAO,qBAAqB;AAAA,IAC1B,YAAY,EAAE,MAAM,kBAAkB,SAAS,IAAI;AAAA,IACnD,OAAO,CAAC;AAAA,MACN,GAAG;AAAA,MACH,KAAK,CAAC,SAAS,gBAAgB,SAAS,MAAM,MAAM,aAAa,gBAAgB,IAAI,CAAC;AAAA,IACxF,CAAC;AAAA,IACD,UAAU,OAAO,CAAC;AAAA,IAClB,cAAc,CAAC,WAAW,cAAc,QAA0B,IAAI;AAAA,EACxE,CAAC;AACH;AASA,eAAsB,qBAAqB,SAAkB,MAAmD;AAC9G,MAAI,QAAQ,WAAW,OAAQ,QAAO,SAAS,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAEpG,QAAM,OAAO,MAAM,wBAAwB,SAAS,EAAE,aAAa,KAAK,aAAa,aAAa,KAAK,YAAY,CAAC;AACpH,MAAI,CAAC,KAAK,GAAI,QAAO,KAAK;AAE1B,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,MAAM,EAAE,KAAK;AAAA,EACpC,QAAQ;AACN,WAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACjE;AAMA,MAAI,aAAa,SAAS,IAAI,GAAG;AAC/B,WAAO,wBAAwB,KAAK,KAAK,IAAI,EAAE,OAAO;AAAA,EACxD;AAEA,MAAI,CAAC,SAAS,IAAI,EAAG,QAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AACpF,QAAM,OAAQ,KAAK,QAAQ,KAAK,aAAa;AAI7C,QAAMC,YAAW,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAK,KAAK;AACvE,QAAM,UAAU,MAAM,gBAAgBA,WAAU,MAAM,KAAK,KAAK,gBAAgB,IAAI,CAAC;AACrF,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO,SAAS,KAAK,EAAE,OAAO,QAAQ,MAAM,SAAS,QAAQ,QAAQ,GAAG,EAAE,QAAQ,cAAc,OAAO,EAAE,CAAC;AAAA,EAC5G;AACA,QAAM,UAAU,QAAQ;AACxB,SAAO,SAAS,KAAK,EAAE,IAAI,MAAM,GAAG,SAAS,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,QAAQ,MAAM,EAAE,IAAI,CAAC,EAAG,CAAC;AACnH;","names":["toolName","r","toolName","toolName"]}
|