@imgly/codesign-mcp 0.1.7 → 0.1.8
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/assets/docs/api/index.d.ts +9 -3
- package/assets/docs/guide/SKILL.md +2 -2
- package/assets/docs/guide/api/node/classes/creativeengine.md +13 -13
- package/assets/docs/guide/edit-image/transform/scale.md +1 -3
- package/assets/docs/guide/filters-and-effects/duotone.md +1 -1
- package/assets/docs/guide/get-started/bun.md +0 -2
- package/assets/docs/guide/get-started/deno.md +0 -1
- package/assets/docs/guide/get-started/vanilla.md +0 -2
- package/assets/docs/guide/import-media/default-assets.md +20 -24
- package/assets/docs/guide/plugins/print-ready-pdf.md +0 -2
- package/assets/docs/guide/serve-assets.md +80 -23
- package/assets/docs/handbook/SKILL.md +6 -7
- package/dist/assets/inline.generated.js +12 -12
- package/dist/assets/inline.generated.js.map +1 -1
- package/dist/cli/http.d.ts.map +1 -1
- package/dist/cli/http.js +17 -3
- package/dist/cli/http.js.map +1 -1
- package/dist/cli/stdio.d.ts.map +1 -1
- package/dist/cli/stdio.js +44 -15
- package/dist/cli/stdio.js.map +1 -1
- package/dist/cli/trial-license.generated.d.ts +1 -1
- package/dist/cli/trial-license.generated.js +1 -1
- package/dist/cli/trial-license.generated.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/diagnostics/bundle.d.ts +3 -0
- package/dist/diagnostics/bundle.d.ts.map +1 -1
- package/dist/diagnostics/bundle.js +25 -22
- package/dist/diagnostics/bundle.js.map +1 -1
- package/dist/diagnostics/prompt.d.ts +3 -0
- package/dist/diagnostics/prompt.d.ts.map +1 -0
- package/dist/diagnostics/prompt.js +21 -0
- package/dist/diagnostics/prompt.js.map +1 -0
- package/dist/diagnostics/router.d.ts +12 -0
- package/dist/diagnostics/router.d.ts.map +1 -0
- package/dist/diagnostics/router.js +64 -0
- package/dist/diagnostics/router.js.map +1 -0
- package/dist/diagnostics/spa-assets.generated.d.ts +2 -0
- package/dist/diagnostics/spa-assets.generated.d.ts.map +1 -0
- package/dist/diagnostics/spa-assets.generated.js +1231 -0
- package/dist/diagnostics/spa-assets.generated.js.map +1 -0
- package/dist/diagnostics/status.d.ts +38 -0
- package/dist/diagnostics/status.d.ts.map +1 -0
- package/dist/diagnostics/status.js +53 -0
- package/dist/diagnostics/status.js.map +1 -0
- package/dist/diagnostics/summary.d.ts +48 -0
- package/dist/diagnostics/summary.d.ts.map +1 -0
- package/dist/diagnostics/summary.js +116 -0
- package/dist/diagnostics/summary.js.map +1 -0
- package/dist/diagnostics/tool.d.ts +16 -0
- package/dist/diagnostics/tool.d.ts.map +1 -0
- package/dist/diagnostics/tool.js +90 -0
- package/dist/diagnostics/tool.js.map +1 -0
- package/dist/engine/asset-sources.d.ts +8 -0
- package/dist/engine/asset-sources.d.ts.map +1 -1
- package/dist/engine/asset-sources.js +9 -2
- package/dist/engine/asset-sources.js.map +1 -1
- package/dist/engine/uri-resolver.js.map +1 -1
- package/dist/server.d.ts +25 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +24 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/engine.d.ts +79 -14
- package/dist/tools/engine.d.ts.map +1 -1
- package/dist/tools/engine.js +119 -20
- package/dist/tools/engine.js.map +1 -1
- package/dist/tools/export.d.ts.map +1 -1
- package/dist/tools/export.js +34 -4
- package/dist/tools/export.js.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/load.d.ts.map +1 -1
- package/dist/tools/load.js +26 -19
- package/dist/tools/load.js.map +1 -1
- package/dist/tools/preview.d.ts.map +1 -1
- package/dist/tools/preview.js +60 -40
- package/dist/tools/preview.js.map +1 -1
- package/dist/tools/{open-editor.d.ts → view.d.ts} +3 -3
- package/dist/tools/view.d.ts.map +1 -0
- package/dist/tools/{open-editor.js → view.js} +20 -18
- package/dist/tools/view.js.map +1 -0
- package/dist/viewer/listener.d.ts +2 -0
- package/dist/viewer/listener.d.ts.map +1 -1
- package/dist/viewer/listener.js +2 -0
- package/dist/viewer/listener.js.map +1 -1
- package/dist/viewer/presigned-r2-url-builder.d.ts +1 -1
- package/dist/viewer/presigned-r2-url-builder.js +1 -1
- package/dist/viewer/presigned-r2-url-builder.js.map +1 -1
- package/dist/viewer/router.d.ts.map +1 -1
- package/dist/viewer/router.js +8 -17
- package/dist/viewer/router.js.map +1 -1
- package/dist/viewer/session-mount.js.map +1 -1
- package/dist/viewer/spa-assets.generated.d.ts.map +1 -1
- package/dist/viewer/spa-assets.generated.js +750 -869
- package/dist/viewer/spa-assets.generated.js.map +1 -1
- package/dist/viewer/url-builder.d.ts +4 -3
- package/dist/viewer/url-builder.d.ts.map +1 -1
- package/dist/viewer/url-builder.js +4 -4
- package/dist/viewer/url-builder.js.map +1 -1
- package/package.json +6 -5
- package/dist/tools/open-editor.d.ts.map +0 -1
- package/dist/tools/open-editor.js.map +0 -1
package/dist/tools/export.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/tools/export.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/tools/export.ts\n//\n// `export` — spec §6.2. Writes a revision-pinned artifact into\n// `exports/<shard>/<rev>/<format>.<ext>` and returns a workspace URI envelope:\n// `uri` (the durable `workspace://` handle) plus `httpUrl` (the `/r/` fetch\n// link). `path` (an absolute disk path) is returned ONLY when the caller\n// passes an explicit `outPath` — a normal workspace export never discloses\n// the workspace's on-disk location.\n//\n// `target=video` is deferred to Phase 4.\nimport { promises as nodeFs } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport type { Mutex } from '../engine/mutex.js';\nimport type { EngineSession } from '../engine/session.js';\nimport type { RevisionStore } from '../revisions/store.js';\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport type { ViewerUrlBuilder } from '../viewer/url-builder.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\nimport {\n NOOP_TELEMETRY_SINK,\n SpanRecorder,\n summarizeResult,\n toErrorShape,\n type TelemetrySink\n} from './spans.js';\n\ntype Target = 'pdf' | 'image' | 'video';\ntype Format = 'pdf' | 'png' | 'jpeg' | 'webp' | 'mp4';\n\ninterface MinimalEngine {\n block: {\n export(blockId: number, mimeType: string, options?: unknown): Promise<Blob>;\n };\n}\n\ninterface ExportArgs {\n target: Target;\n format: Format;\n revision: string;\n blockId?: number;\n outPath?: string;\n}\n\nconst TARGET_MIME: Record<'pdf' | 'image', Record<string, string>> = {\n pdf: { pdf: 'application/pdf' },\n image: { png: 'image/png', jpeg: 'image/jpeg', webp: 'image/webp' }\n};\n\nconst FORMAT_EXT: Record<Format, string> = {\n pdf: 'pdf',\n png: 'png',\n jpeg: 'jpg',\n webp: 'webp',\n mp4: 'mp4'\n};\n\nconst DESCRIPTION = [\n 'Export a revision to pdf/image. Renders `blockId` and writes the result into the workspace under `exports/<shard>/<rev>/<target>.<ext>` (revision-pinned, deterministic).',\n '',\n 'Returns JSON: `{ uri, httpUrl?, path?, bytes, format, revision }`. `uri` (a `workspace://` URI) is the durable handle. `httpUrl` is a fetch link for opening the artifact now — present for workspace exports, tied to this process. `path` (absolute disk path) is returned ONLY when you passed `outPath`.',\n '',\n '`target=video` is deferred to Phase 4.'\n].join('\\n');\n\n// The schema is built per-backend: `outPath` is advertised ONLY when the\n// backend honours it (Local). On Hosted the property is omitted entirely, so\n// an agent never sees an affordance it can't use — it can't try a local path\n// and hit a runtime rejection. The runtime guard in `call` stays as a backstop.\nfunction buildExportInputSchema(allowOutPath: boolean) {\n const properties: Record<string, unknown> = {\n target: { enum: ['pdf', 'image', 'video'] },\n format: { enum: ['pdf', 'png', 'jpeg', 'webp', 'mp4'] },\n revision: { type: 'string' as const },\n blockId: {\n type: 'integer' as const,\n description: 'Required for pdf and image targets.'\n }\n };\n if (allowOutPath) {\n properties.outPath = {\n type: 'string' as const,\n description: 'Local-only escape hatch; absolute path.'\n };\n }\n return {\n type: 'object' as const,\n properties,\n required: ['target', 'format', 'revision'],\n additionalProperties: false\n };\n}\n\nexport interface CreateExportToolOptions<E = unknown> {\n fs: WorkspaceFS;\n store: RevisionStore;\n session: EngineSession<E>;\n /** Builds the `httpUrl` for workspace-written artifacts (the `/r/` route). */\n urlBuilder: ViewerUrlBuilder;\n /** Whether to honour an `outPath` argument (Local: yes; Hosted/sandbox: no). */\n allowOutPath?: boolean;\n /** Guards the shared engine — load-then-export must not interleave with another session's edit (see engine/mutex.ts). */\n engineLock: Mutex;\n /** Diagnostics telemetry port. Absent → no-op (no timing overhead beyond a few clocks). */\n telemetry?: TelemetrySink;\n}\n\nexport function createExportTool<E = unknown>(\n opts: CreateExportToolOptions<E>\n): CodesignMcpToolRegistration {\n const {\n fs,\n store,\n session,\n urlBuilder,\n allowOutPath = false,\n engineLock\n } = opts;\n const telemetry = opts.telemetry ?? NOOP_TELEMETRY_SINK;\n return {\n name: 'export',\n description: DESCRIPTION,\n inputSchema: buildExportInputSchema(allowOutPath),\n call: async (rawArgs) => {\n const rec = new SpanRecorder();\n const startedAt = performance.now();\n type CallResult = Awaited<\n ReturnType<CodesignMcpToolRegistration['call']>\n >;\n let result: CallResult | undefined;\n let threw: unknown;\n let revision: string | undefined;\n try {\n result = await (async (): Promise<CallResult> => {\n const args = (rawArgs ?? {}) as Partial<ExportArgs>;\n if (\n typeof args.target !== 'string' ||\n typeof args.format !== 'string' ||\n typeof args.revision !== 'string'\n ) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'export: target, format, and revision are required.'\n }\n ]\n };\n }\n if (args.target === 'video') {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'export: target=video is deferred to Phase 4.'\n }\n ]\n };\n }\n if (args.outPath !== undefined && !allowOutPath) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'export: `outPath` is not supported on this (hosted) backend. Omit it — the artifact is written to the workspace and returned as `httpUrl`; fetch that URL to download the bytes to disk.'\n }\n ]\n };\n }\n const rev = await rec.time('workspace_read', () =>\n store.read(args.revision as string)\n );\n if (typeof args.blockId !== 'number') {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `export: target=${args.target} requires \\`blockId\\`.`\n }\n ]\n };\n }\n const mimeMap = TARGET_MIME[args.target as 'pdf' | 'image'];\n const mime = mimeMap?.[args.format];\n if (!mime) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `export: invalid format=${args.format} for target=${args.target}.`\n }\n ]\n };\n }\n // Capture validated args into locals so the locked closure keeps the\n // type narrowing the top-level guards established.\n const revId = args.revision;\n const blockId = args.blockId;\n const sceneBytes = rev.scene;\n // load-into-engine then export reads the shared engine across awaits —\n // serialize against any concurrent engine-touching tool call.\n const beforeLock = performance.now();\n const bytes = await engineLock.runExclusive(async () => {\n rec.add('lock_wait', performance.now() - beforeLock);\n const slot = await rec.time('engine_scene_load', async () => {\n await session.populate(revId, sceneBytes);\n return session.acquire(revId);\n });\n const eng = slot.engine as MinimalEngine;\n const blob = await rec.time('engine_execute', () =>\n eng.block.export(blockId, mime, {})\n );\n return new Uint8Array(await blob.arrayBuffer());\n });\n const shard = args.revision.slice(0, 2);\n const rest = args.revision.slice(2);\n const ext = FORMAT_EXT[args.format];\n const relPath = `exports/${shard}/${rest}/${args.target}.${ext}`;\n let absPath: string | undefined;\n let httpUrl: string | undefined;\n if (args.outPath !== undefined) {\n // Local-only escape hatch: write through real disk fs, NOT through\n // the workspace abstraction. The caller chose this path explicitly,\n // so echoing it back is a confirmation, not a workspace-path leak.\n await nodeFs.mkdir(dirname(args.outPath), { recursive: true });\n await rec.time('workspace_write', async () => {\n await nodeFs.writeFile(args.outPath as string, bytes);\n });\n absPath = args.outPath;\n } else {\n // Workspace write — goes through the WorkspaceFS abstraction. The\n // artifact is reachable over HTTP via the `/r/` route; the absolute\n // disk path is deliberately NOT disclosed.\n await rec.time('workspace_write', async () => {\n await fs.writeFile(relPath, bytes);\n });\n httpUrl = await urlBuilder.forDownload(relPath);\n }\n revision = args.revision;\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({\n uri: `workspace://${relPath}`,\n ...(httpUrl ? { httpUrl } : {}),\n ...(absPath ? { path: absPath } : {}),\n bytes: bytes.byteLength,\n format: args.format,\n revision: args.revision\n })\n }\n ]\n };\n })();\n return result;\n } catch (err) {\n threw = err;\n throw err;\n } finally {\n telemetry.record({\n tool: 'export',\n succeeded: threw === undefined && result?.isError !== true,\n totalMs: performance.now() - startedAt,\n spans: rec.list(),\n args: rawArgs,\n ...(result !== undefined ? { result: summarizeResult(result) } : {}),\n ...(revision ? { revision } : {}),\n ...(threw ? { error: toErrorShape(threw) } : {})\n });\n }\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/tools/export.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/tools/export.ts\n//\n// `export` — spec §6.2. Writes a revision-pinned artifact into\n// `exports/<shard>/<rev>/<format>.<ext>` and returns a workspace URI envelope:\n// `uri` (the durable `workspace://` handle) plus `httpUrl` (the `/r/` fetch\n// link). `path` (an absolute disk path) is returned ONLY when the caller\n// passes an explicit `outPath` — a normal workspace export never discloses\n// the workspace's on-disk location.\n//\n// `target=video` is deferred to Phase 4.\nimport { promises as nodeFs } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport type { Mutex } from '../engine/mutex.js';\nimport type { EngineSession } from '../engine/session.js';\nimport type { RevisionStore } from '../revisions/store.js';\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport type { ViewerUrlBuilder } from '../viewer/url-builder.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\nimport {\n NOOP_TELEMETRY_SINK,\n SpanRecorder,\n summarizeResult,\n toErrorShape,\n type TelemetrySink\n} from './spans.js';\n\ntype Target = 'pdf' | 'image' | 'video';\ntype Format = 'pdf' | 'png' | 'jpeg' | 'webp' | 'mp4';\n\ninterface MinimalEngine {\n block: {\n export(blockId: number, mimeType: string, options?: unknown): Promise<Blob>;\n };\n}\n\ninterface ExportArgs {\n target: Target;\n format: Format;\n revision: string;\n blockId?: number;\n outPath?: string;\n}\n\nconst TARGET_MIME: Record<'pdf' | 'image', Record<string, string>> = {\n pdf: { pdf: 'application/pdf' },\n image: { png: 'image/png', jpeg: 'image/jpeg', webp: 'image/webp' }\n};\n\nconst FORMAT_EXT: Record<Format, string> = {\n pdf: 'pdf',\n png: 'png',\n jpeg: 'jpg',\n webp: 'webp',\n mp4: 'mp4'\n};\n\nconst DESCRIPTION = [\n 'Export a revision to pdf/image. Renders `blockId` and writes the result into the workspace under `exports/<shard>/<rev>/<target>.<ext>` (revision-pinned, deterministic).',\n '',\n 'Returns JSON: `{ uri, httpUrl?, path?, bytes, format, revision }`. `uri` (a `workspace://` URI) is the durable handle. `httpUrl` is a fetch link for opening the artifact now — present for workspace exports, tied to this process. `path` (absolute disk path) is returned ONLY when you passed `outPath`.',\n '',\n '`target=video` is deferred to Phase 4.'\n].join('\\n');\n\n// The schema is built per-backend: `outPath` is advertised ONLY when the\n// backend honours it (Local). On Hosted the property is omitted entirely, so\n// an agent never sees an affordance it can't use — it can't try a local path\n// and hit a runtime rejection. The runtime guard in `call` stays as a backstop.\nfunction buildExportInputSchema(allowOutPath: boolean) {\n const properties: Record<string, unknown> = {\n target: { enum: ['pdf', 'image', 'video'] },\n format: { enum: ['pdf', 'png', 'jpeg', 'webp', 'mp4'] },\n revision: { type: 'string' as const },\n blockId: {\n type: 'integer' as const,\n description: 'Required for pdf and image targets.'\n }\n };\n if (allowOutPath) {\n properties.outPath = {\n type: 'string' as const,\n description: 'Local-only escape hatch; absolute path.'\n };\n }\n return {\n type: 'object' as const,\n properties,\n required: ['target', 'format', 'revision'],\n additionalProperties: false\n };\n}\n\nexport interface CreateExportToolOptions<E = unknown> {\n fs: WorkspaceFS;\n store: RevisionStore;\n session: EngineSession<E>;\n /** Builds the `httpUrl` for workspace-written artifacts (the `/r/` route). */\n urlBuilder: ViewerUrlBuilder;\n /** Whether to honour an `outPath` argument (Local: yes; Hosted/sandbox: no). */\n allowOutPath?: boolean;\n /** Guards the shared engine — load-then-export must not interleave with another session's edit (see engine/mutex.ts). */\n engineLock: Mutex;\n /** Diagnostics telemetry port. Absent → no-op (no timing overhead beyond a few clocks). */\n telemetry?: TelemetrySink;\n}\n\nexport function createExportTool<E = unknown>(\n opts: CreateExportToolOptions<E>\n): CodesignMcpToolRegistration {\n const {\n fs,\n store,\n session,\n urlBuilder,\n allowOutPath = false,\n engineLock\n } = opts;\n const telemetry = opts.telemetry ?? NOOP_TELEMETRY_SINK;\n return {\n name: 'export',\n description: DESCRIPTION,\n inputSchema: buildExportInputSchema(allowOutPath),\n call: async (rawArgs) => {\n const rec = new SpanRecorder();\n const startedAt = performance.now();\n type CallResult = Awaited<\n ReturnType<CodesignMcpToolRegistration['call']>\n >;\n let result: CallResult | undefined;\n let threw: unknown;\n let revision: string | undefined;\n let exportError: unknown;\n try {\n result = await (async (): Promise<CallResult> => {\n const args = (rawArgs ?? {}) as Partial<ExportArgs>;\n if (\n typeof args.target !== 'string' ||\n typeof args.format !== 'string' ||\n typeof args.revision !== 'string'\n ) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'export: target, format, and revision are required.'\n }\n ]\n };\n }\n if (args.target === 'video') {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'export: target=video is deferred to Phase 4.'\n }\n ]\n };\n }\n if (args.outPath !== undefined && !allowOutPath) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'export: `outPath` is not supported on this (hosted) backend. Omit it — the artifact is written to the workspace and returned as `httpUrl`; fetch that URL to download the bytes to disk.'\n }\n ]\n };\n }\n const rev = await rec.time('workspace_read', () =>\n store.read(args.revision as string)\n );\n if (typeof args.blockId !== 'number') {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `export: target=${args.target} requires \\`blockId\\`.`\n }\n ]\n };\n }\n const mimeMap = TARGET_MIME[args.target as 'pdf' | 'image'];\n const mime = mimeMap?.[args.format];\n if (!mime) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `export: invalid format=${args.format} for target=${args.target}.`\n }\n ]\n };\n }\n // Capture validated args into locals so the locked closure keeps the\n // type narrowing the top-level guards established.\n const revId = args.revision;\n const blockId = args.blockId;\n const sceneBytes = rev.scene;\n // load-into-engine then export reads the shared engine across awaits —\n // serialize against any concurrent engine-touching tool call.\n const beforeLock = performance.now();\n const rendered = await engineLock.runExclusive(\n async (): Promise<{ bytes: Uint8Array } | { error: unknown }> => {\n rec.add('lock_wait', performance.now() - beforeLock);\n const slot = await rec.time('engine_scene_load', async () => {\n await session.populate(revId, sceneBytes);\n return session.acquire(revId);\n });\n const eng = slot.engine as MinimalEngine;\n // We loaded `revId` under the lock just above, so the correct scene\n // is current. If the render throws, `blockId` genuinely isn't in\n // this revision's scene (a wrong or removed id) — return a legible\n // tool error the model can act on (mirroring `edit`'s dirty-on-throw\n // path) instead of rethrowing into an opaque MCP -32603.\n try {\n const blob = await rec.time('engine_execute', () =>\n eng.block.export(blockId, mime, {})\n );\n return { bytes: new Uint8Array(await blob.arrayBuffer()) };\n } catch (err) {\n return { error: err };\n }\n }\n );\n if ('error' in rendered) {\n exportError = rendered.error;\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `export: could not render block ${blockId} in revision ${revId}. That block id isn't in this revision's scene — double-check the id (use \\`inspect\\` to see the revision's structure), or it may have been removed in this revision. ${\n rendered.error instanceof Error\n ? (rendered.error.stack ?? rendered.error.message)\n : String(rendered.error)\n }`\n }\n ]\n };\n }\n const bytes = rendered.bytes;\n const shard = args.revision.slice(0, 2);\n const rest = args.revision.slice(2);\n const ext = FORMAT_EXT[args.format];\n const relPath = `exports/${shard}/${rest}/${args.target}.${ext}`;\n let absPath: string | undefined;\n let httpUrl: string | undefined;\n if (args.outPath !== undefined) {\n // Local-only escape hatch: write through real disk fs, NOT through\n // the workspace abstraction. The caller chose this path explicitly,\n // so echoing it back is a confirmation, not a workspace-path leak.\n await nodeFs.mkdir(dirname(args.outPath), { recursive: true });\n await rec.time('workspace_write', async () => {\n await nodeFs.writeFile(args.outPath as string, bytes);\n });\n absPath = args.outPath;\n } else {\n // Workspace write — goes through the WorkspaceFS abstraction. The\n // artifact is reachable over HTTP via the `/r/` route; the absolute\n // disk path is deliberately NOT disclosed.\n await rec.time('workspace_write', async () => {\n await fs.writeFile(relPath, bytes);\n });\n httpUrl = await urlBuilder.forDownload(relPath);\n }\n revision = args.revision;\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({\n uri: `workspace://${relPath}`,\n ...(httpUrl ? { httpUrl } : {}),\n ...(absPath ? { path: absPath } : {}),\n bytes: bytes.byteLength,\n format: args.format,\n revision: args.revision\n })\n }\n ]\n };\n })();\n return result;\n } catch (err) {\n threw = err;\n throw err;\n } finally {\n telemetry.record({\n tool: 'export',\n succeeded: threw === undefined && result?.isError !== true,\n totalMs: performance.now() - startedAt,\n spans: rec.list(),\n args: rawArgs,\n ...(result !== undefined ? { result: summarizeResult(result) } : {}),\n ...(revision ? { revision } : {}),\n ...(threw\n ? { error: toErrorShape(threw) }\n : exportError\n ? { error: toErrorShape(exportError) }\n : {})\n });\n }\n }\n };\n}\n"]}
|
package/dist/tools/index.js
CHANGED
|
@@ -18,11 +18,11 @@ import { createImportTool } from './import.js';
|
|
|
18
18
|
import { createInspectTool } from './inspect.js';
|
|
19
19
|
import { createListTool } from './list.js';
|
|
20
20
|
import { createLoadTool } from './load.js';
|
|
21
|
-
import { createOpenEditorTool } from './open-editor.js';
|
|
22
21
|
import { createPreviewTool } from './preview.js';
|
|
23
22
|
import { createRenameTool } from './rename.js';
|
|
24
23
|
import { createSaveTool } from './save.js';
|
|
25
24
|
import { wrapWithTelemetry } from './telemetry-wrapper.js';
|
|
25
|
+
import { createViewTool } from './view.js';
|
|
26
26
|
// Beta kill-switch for asset_generate. Off for the beta (the feature isn't
|
|
27
27
|
// supported yet); set to true to re-expose the tool once it ships.
|
|
28
28
|
const ASSET_GENERATE_ENABLED = false;
|
|
@@ -75,7 +75,7 @@ export function buildDesignToolRegistrations(opts) {
|
|
|
75
75
|
createHistoryTool({ store: opts.store }),
|
|
76
76
|
createInspectTool({ store: opts.store }),
|
|
77
77
|
createRenameTool({ store: opts.store }),
|
|
78
|
-
|
|
78
|
+
createViewTool({ urlBuilder: opts.urlBuilder, store: opts.store }),
|
|
79
79
|
createAssetSearchTool({
|
|
80
80
|
session: opts.session,
|
|
81
81
|
engineLock: opts.engineLock
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/tools/index.ts\n//\n// Builds the design + workspace tool registrations from a fully-DI'd set of\n// primitives: the always-on design + workspace tools, plus `asset_generate`\n// — currently hidden for the beta (see ASSET_GENERATE_ENABLED below), and\n// otherwise gated on a usable gateway (Local: API_KEY set). A hidden tool\n// never shows up in tools/list, so the model won't attempt a call that can\n// only fail.\n// Each variant's CLI bootstrap (cli/stdio.ts, cli/http.ts)\n// constructs these primitives once and passes them in; this file owns no\n// engine bootstrap, no FS bootstrap, no gateway selection.\n\nimport type { Mutex } from '../engine/mutex.js';\nimport type { EngineSession } from '../engine/session.js';\nimport type { GatewayClient } from '../gateway/types.js';\nimport type { RevisionStore } from '../revisions/store.js';\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport type { ViewerUrlBuilder } from '../viewer/url-builder.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\nimport type { TelemetrySink } from './spans.js';\nimport { createAssetGenerateTool } from './asset-generate.js';\nimport { createAssetSearchTool } from './asset-search.js';\nimport { createEditTool } from './edit.js';\nimport { createExportTool } from './export.js';\nimport { createHistoryTool } from './history.js';\nimport { createImportTool } from './import.js';\nimport { createInspectTool } from './inspect.js';\nimport { createListTool } from './list.js';\nimport { createLoadTool } from './load.js';\nimport { createOpenEditorTool } from './open-editor.js';\nimport { createPreviewTool } from './preview.js';\nimport { createRenameTool } from './rename.js';\nimport { createSaveTool } from './save.js';\nimport { wrapWithTelemetry } from './telemetry-wrapper.js';\n\n// Beta kill-switch for asset_generate. Off for the beta (the feature isn't\n// supported yet); set to true to re-expose the tool once it ships.\nconst ASSET_GENERATE_ENABLED = false;\n\nexport interface BuildDesignToolRegistrationsOptions<E = unknown> {\n fs: WorkspaceFS;\n store: RevisionStore;\n session: EngineSession<E>;\n /**\n * Serializes access to the shared CE.SDK engine across the edit/preview/\n * export tools. One engine today (process singleton) ⇒ one lock shared by\n * every session. See engine/mutex.ts and the bootstrap (cli/http.ts,\n * cli/stdio.ts) for where it is created.\n */\n engineLock: Mutex;\n gateway: GatewayClient;\n urlBuilder: ViewerUrlBuilder;\n /** True on Local (the `outPath?` escape hatch is allowed); false on Hosted/sandbox. */\n allowOutPath: boolean;\n /** Called when `edit` writes a new leaf revision (viewer SSE hook). */\n onLeafWritten?: (event: {\n revision: string;\n rootRevision: string;\n }) => void | Promise<void>;\n /** Render a thumbnail for a newly committed revision. Optional; best-effort. */\n renderThumbnail?: (event: {\n revision: string;\n engine: E;\n }) => Promise<Uint8Array>;\n /** Diagnostics telemetry port, threaded into the engine tools (edit/preview/export). */\n telemetry?: TelemetrySink;\n}\n\nexport function buildDesignToolRegistrations<E = unknown>(\n opts: BuildDesignToolRegistrationsOptions<E>\n): CodesignMcpToolRegistration[] {\n const registrations: CodesignMcpToolRegistration[] = [\n createEditTool({\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n onLeafWritten: opts.onLeafWritten,\n renderThumbnail: opts.renderThumbnail,\n fs: opts.fs,\n telemetry: opts.telemetry\n }),\n createPreviewTool({\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n telemetry: opts.telemetry\n }),\n createExportTool({\n fs: opts.fs,\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n urlBuilder: opts.urlBuilder,\n allowOutPath: opts.allowOutPath,\n telemetry: opts.telemetry\n }),\n createSaveTool({\n fs: opts.fs,\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n urlBuilder: opts.urlBuilder,\n allowOutPath: opts.allowOutPath\n }),\n createLoadTool({\n fs: opts.fs,\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n // Same Local-only disk gate as save/export's outPath: a `{ path }`\n // source only makes sense where the process shares a disk with the user.\n allowPath: opts.allowOutPath,\n onLeafWritten: opts.onLeafWritten,\n renderThumbnail: opts.renderThumbnail\n }),\n createListTool({ store: opts.store }),\n createHistoryTool({ store: opts.store }),\n createInspectTool({ store: opts.store }),\n createRenameTool({ store: opts.store }),\n createOpenEditorTool({ urlBuilder: opts.urlBuilder, store: opts.store }),\n createAssetSearchTool({\n session: opts.session,\n engineLock: opts.engineLock\n })\n ];\n\n // `import` is Local-only for now: its sole source arm (`{ path }`) needs a\n // server that shares the caller's disk. On Hosted there is no byte transport\n // until CODE-42, so we HIDE the tool there rather than show the model a tool\n // whose every call would fail. `allowOutPath` is the same Local(true)/\n // Hosted(false) signal export/save already use.\n if (opts.allowOutPath) {\n registrations.push(\n createImportTool({\n fs: opts.fs,\n urlBuilder: opts.urlBuilder,\n allowLocalPath: true\n })\n );\n }\n\n // BETA: asset_generate is hidden for the beta — not supported yet, and we\n // don't want the model to see a tool it can't use. The implementation\n // (asset-generate.ts, gateway plumbing) is intentionally kept intact; flip\n // ASSET_GENERATE_ENABLED back to true to re-expose it. Hiding the tool also\n // drops the handbook/instructions hint, which keys off tool presence.\n // Opt-in: only expose asset_generate when the gateway can fulfil a request.\n if (ASSET_GENERATE_ENABLED && opts.gateway.available) {\n registrations.push(\n createAssetGenerateTool({\n gateway: opts.gateway,\n fs: opts.fs,\n urlBuilder: opts.urlBuilder\n })\n );\n }\n\n // Diagnostics coverage. The engine tools (edit/preview/export) self-report a\n // rich ToolCallReport with phase spans (they were passed `telemetry` above).\n // Wrap EVERY other tool so its calls are recorded too — args, result, timing,\n // success — just with empty spans. Without a telemetry sink, nothing is\n // wrapped (zero overhead, byte-equivalent to before).\n if (!opts.telemetry) return registrations;\n const telemetry = opts.telemetry;\n const selfReporting = new Set(['edit', 'preview', 'export']);\n return registrations.map((registration) =>\n selfReporting.has(registration.name)\n ? registration\n : wrapWithTelemetry(registration, telemetry)\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/tools/index.ts\n//\n// Builds the design + workspace tool registrations from a fully-DI'd set of\n// primitives: the always-on design + workspace tools, plus `asset_generate`\n// — currently hidden for the beta (see ASSET_GENERATE_ENABLED below), and\n// otherwise gated on a usable gateway (Local: API_KEY set). A hidden tool\n// never shows up in tools/list, so the model won't attempt a call that can\n// only fail.\n// Each variant's CLI bootstrap (cli/stdio.ts, cli/http.ts)\n// constructs these primitives once and passes them in; this file owns no\n// engine bootstrap, no FS bootstrap, no gateway selection.\n\nimport type { Mutex } from '../engine/mutex.js';\nimport type { EngineSession } from '../engine/session.js';\nimport type { GatewayClient } from '../gateway/types.js';\nimport type { RevisionStore } from '../revisions/store.js';\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport type { ViewerUrlBuilder } from '../viewer/url-builder.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\nimport type { TelemetrySink } from './spans.js';\nimport { createAssetGenerateTool } from './asset-generate.js';\nimport { createAssetSearchTool } from './asset-search.js';\nimport { createEditTool } from './edit.js';\nimport { createExportTool } from './export.js';\nimport { createHistoryTool } from './history.js';\nimport { createImportTool } from './import.js';\nimport { createInspectTool } from './inspect.js';\nimport { createListTool } from './list.js';\nimport { createLoadTool } from './load.js';\nimport { createPreviewTool } from './preview.js';\nimport { createRenameTool } from './rename.js';\nimport { createSaveTool } from './save.js';\nimport { wrapWithTelemetry } from './telemetry-wrapper.js';\nimport { createViewTool } from './view.js';\n\n// Beta kill-switch for asset_generate. Off for the beta (the feature isn't\n// supported yet); set to true to re-expose the tool once it ships.\nconst ASSET_GENERATE_ENABLED = false;\n\nexport interface BuildDesignToolRegistrationsOptions<E = unknown> {\n fs: WorkspaceFS;\n store: RevisionStore;\n session: EngineSession<E>;\n /**\n * Serializes access to the shared CE.SDK engine across the edit/preview/\n * export tools. One engine today (process singleton) ⇒ one lock shared by\n * every session. See engine/mutex.ts and the bootstrap (cli/http.ts,\n * cli/stdio.ts) for where it is created.\n */\n engineLock: Mutex;\n gateway: GatewayClient;\n urlBuilder: ViewerUrlBuilder;\n /** True on Local (the `outPath?` escape hatch is allowed); false on Hosted/sandbox. */\n allowOutPath: boolean;\n /** Called when `edit` writes a new leaf revision (viewer SSE hook). */\n onLeafWritten?: (event: {\n revision: string;\n rootRevision: string;\n }) => void | Promise<void>;\n /** Render a thumbnail for a newly committed revision. Optional; best-effort. */\n renderThumbnail?: (event: {\n revision: string;\n engine: E;\n }) => Promise<Uint8Array>;\n /** Diagnostics telemetry port, threaded into the engine tools (edit/preview/export). */\n telemetry?: TelemetrySink;\n}\n\nexport function buildDesignToolRegistrations<E = unknown>(\n opts: BuildDesignToolRegistrationsOptions<E>\n): CodesignMcpToolRegistration[] {\n const registrations: CodesignMcpToolRegistration[] = [\n createEditTool({\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n onLeafWritten: opts.onLeafWritten,\n renderThumbnail: opts.renderThumbnail,\n fs: opts.fs,\n telemetry: opts.telemetry\n }),\n createPreviewTool({\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n telemetry: opts.telemetry\n }),\n createExportTool({\n fs: opts.fs,\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n urlBuilder: opts.urlBuilder,\n allowOutPath: opts.allowOutPath,\n telemetry: opts.telemetry\n }),\n createSaveTool({\n fs: opts.fs,\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n urlBuilder: opts.urlBuilder,\n allowOutPath: opts.allowOutPath\n }),\n createLoadTool({\n fs: opts.fs,\n store: opts.store,\n session: opts.session,\n engineLock: opts.engineLock,\n // Same Local-only disk gate as save/export's outPath: a `{ path }`\n // source only makes sense where the process shares a disk with the user.\n allowPath: opts.allowOutPath,\n onLeafWritten: opts.onLeafWritten,\n renderThumbnail: opts.renderThumbnail\n }),\n createListTool({ store: opts.store }),\n createHistoryTool({ store: opts.store }),\n createInspectTool({ store: opts.store }),\n createRenameTool({ store: opts.store }),\n createViewTool({ urlBuilder: opts.urlBuilder, store: opts.store }),\n createAssetSearchTool({\n session: opts.session,\n engineLock: opts.engineLock\n })\n ];\n\n // `import` is Local-only for now: its sole source arm (`{ path }`) needs a\n // server that shares the caller's disk. On Hosted there is no byte transport\n // until CODE-42, so we HIDE the tool there rather than show the model a tool\n // whose every call would fail. `allowOutPath` is the same Local(true)/\n // Hosted(false) signal export/save already use.\n if (opts.allowOutPath) {\n registrations.push(\n createImportTool({\n fs: opts.fs,\n urlBuilder: opts.urlBuilder,\n allowLocalPath: true\n })\n );\n }\n\n // BETA: asset_generate is hidden for the beta — not supported yet, and we\n // don't want the model to see a tool it can't use. The implementation\n // (asset-generate.ts, gateway plumbing) is intentionally kept intact; flip\n // ASSET_GENERATE_ENABLED back to true to re-expose it. Hiding the tool also\n // drops the handbook/instructions hint, which keys off tool presence.\n // Opt-in: only expose asset_generate when the gateway can fulfil a request.\n if (ASSET_GENERATE_ENABLED && opts.gateway.available) {\n registrations.push(\n createAssetGenerateTool({\n gateway: opts.gateway,\n fs: opts.fs,\n urlBuilder: opts.urlBuilder\n })\n );\n }\n\n // Diagnostics coverage. The engine tools (edit/preview/export) self-report a\n // rich ToolCallReport with phase spans (they were passed `telemetry` above).\n // Wrap EVERY other tool so its calls are recorded too — args, result, timing,\n // success — just with empty spans. Without a telemetry sink, nothing is\n // wrapped (zero overhead, byte-equivalent to before).\n if (!opts.telemetry) return registrations;\n const telemetry = opts.telemetry;\n const selfReporting = new Set(['edit', 'preview', 'export']);\n return registrations.map((registration) =>\n selfReporting.has(registration.name)\n ? registration\n : wrapWithTelemetry(registration, telemetry)\n );\n}\n"]}
|
package/dist/tools/load.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/tools/load.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/tools/load.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAyUzD,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,OAAO;IAChD,EAAE,EAAE,WAAW,CAAC;IAChB,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,0GAA0G;IAC1G,UAAU,EAAE,KAAK,CAAC;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACtB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,oFAAoF;IACpF,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,CAAC,CAAC;KACX,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EACxC,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC7B,2BAA2B,CAmK7B"}
|
package/dist/tools/load.js
CHANGED
|
@@ -123,15 +123,16 @@ const MIME_EXT = {
|
|
|
123
123
|
function extForMime(mime) {
|
|
124
124
|
return MIME_EXT[mime] ?? 'bin';
|
|
125
125
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
/**
|
|
127
|
+
* The buffer URI of a transient resource. @cesdk/node (WASM) exposes it as
|
|
128
|
+
* `.URL`; @cesdk/node-native exposes it as `.uri`.
|
|
129
|
+
*/
|
|
130
|
+
function transientResourceUri(resource) {
|
|
131
|
+
const uri = resource.URL ?? resource.uri;
|
|
132
|
+
if (uri === undefined) {
|
|
133
|
+
throw new Error('load: transient resource has neither URL nor uri');
|
|
133
134
|
}
|
|
134
|
-
return
|
|
135
|
+
return uri;
|
|
135
136
|
}
|
|
136
137
|
/**
|
|
137
138
|
* Persist one engine resource's bytes into the content-addressed asset store
|
|
@@ -139,12 +140,12 @@ function concatChunks(chunks) {
|
|
|
139
140
|
* identical path regardless of caller (automatic dedup).
|
|
140
141
|
*/
|
|
141
142
|
async function persistResourceBytes(eng, fs, uri) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const bytes =
|
|
143
|
+
// getBufferLength + getBufferData is the engine-portable way to read a
|
|
144
|
+
// transient (buffer:) resource's bytes: it exists on both @cesdk/node (WASM)
|
|
145
|
+
// and @cesdk/node-native, whereas getResourceData is WASM-only. slice()
|
|
146
|
+
// copies because the engine may reuse the returned buffer.
|
|
147
|
+
const length = eng.editor.getBufferLength(uri);
|
|
148
|
+
const bytes = eng.editor.getBufferData(uri, 0, length).slice();
|
|
148
149
|
const sha = await shortHash(bytes);
|
|
149
150
|
const ext = extForMime(await eng.editor.getMimeType(uri));
|
|
150
151
|
const relPath = `assets/${sha}.${ext}`;
|
|
@@ -156,6 +157,11 @@ async function persistResourceBytes(eng, fs, uri) {
|
|
|
156
157
|
/** The engine's error code when a serialized scene still carries a scheme
|
|
157
158
|
* outside `allowedResourceSchemes` (e.g. a lingering `buffer:`). */
|
|
158
159
|
const DISALLOWED_SCHEMES_CODE = 'SCENE.DISALLOWED_SCHEMES';
|
|
160
|
+
// `@cesdk/node-native` (as of 1.79.0-nightly.20260708) does not set `err.code`
|
|
161
|
+
// on engine errors, so this predicate never matches on native and the
|
|
162
|
+
// duplicate-transient drain retry below is effectively WASM-only until the
|
|
163
|
+
// upstream fix lands. The first-save path (which normally succeeds after
|
|
164
|
+
// explode) is unaffected.
|
|
159
165
|
function isDisallowedSchemesError(err) {
|
|
160
166
|
return (err instanceof Error &&
|
|
161
167
|
err.code === DISALLOWED_SCHEMES_CODE);
|
|
@@ -171,9 +177,10 @@ async function explodeArchiveIntoWorkspace(eng, fs) {
|
|
|
171
177
|
// Persist + relocate every transient, remembering each URL's workspace path.
|
|
172
178
|
const persistedByUrl = new Map();
|
|
173
179
|
for (const resource of eng.editor.findAllTransientResources()) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
180
|
+
const url = transientResourceUri(resource);
|
|
181
|
+
const relPath = await persistResourceBytes(eng, fs, url);
|
|
182
|
+
persistedByUrl.set(url, relPath);
|
|
183
|
+
eng.editor.relocateResource(url, `workspace://${relPath}`);
|
|
177
184
|
}
|
|
178
185
|
try {
|
|
179
186
|
// Common case: every transient relocated cleanly, so no scheme is disallowed.
|
|
@@ -191,7 +198,7 @@ async function explodeArchiveIntoWorkspace(eng, fs) {
|
|
|
191
198
|
// the OUTPUT to its already-persisted workspace:// form.
|
|
192
199
|
//
|
|
193
200
|
// Two engine constraints shape this (CE.SDK v1.77): (1) the callback MUST
|
|
194
|
-
// NOT re-enter the engine —
|
|
201
|
+
// NOT re-enter the engine — getBufferData/getMimeType from inside it
|
|
195
202
|
// deadlocks saveToString (it holds the engine while awaiting the callback),
|
|
196
203
|
// so every straggler must be a transient we persisted above and the
|
|
197
204
|
// callback is a pure lookup; (2) we attach the callback ONLY here, on the
|
|
@@ -256,7 +263,7 @@ function classifyUri(uri) {
|
|
|
256
263
|
function classifyAssets(eng) {
|
|
257
264
|
const uris = new Set([
|
|
258
265
|
...eng.editor.findAllMediaURIs(),
|
|
259
|
-
...eng.editor.findAllTransientResources().map(
|
|
266
|
+
...eng.editor.findAllTransientResources().map(transientResourceUri)
|
|
260
267
|
]);
|
|
261
268
|
return [...uris].map((uri) => ({ uri, class: classifyUri(uri) }));
|
|
262
269
|
}
|
package/dist/tools/load.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/tools/load.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/tools/load.ts\n//\n// `load` — ingest a native CE.SDK `.scene` file or `.zip` scene archive as a\n// NEW design root revision (the inverse of `save`). Archives are \"exploded\":\n// after `loadFromArchiveURL` their bundled asset bytes live in the engine as\n// ephemeral `buffer:` resources (which scene-level `saveToString` rejects —\n// the CODE-3 bug); we extract each one into the content-addressed asset store\n// (`assets/<sha>.<ext>`), relocate it to a durable `workspace://` URI, and\n// only then serialize. Bare `.scene` files carry no asset bytes: they persist\n// as-is, plus a classification report of every referenced asset URI.\n//\n// Both source variants normalize to bytes up front (fetch for `url`, disk\n// read for `path` — Local only), then dispatch on the zip magic bytes.\n// Archive bytes are staged to a temp file and loaded via a `file://` URL:\n// scene-level CE.SDK v1.77 has no `loadFromArchive(path/bytes)`, only\n// `loadFromArchiveURL`, and `file` is a first-class engine scheme.\n\nimport { promises as nodeFs } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { Mutex } from '../engine/mutex.js';\nimport { SCENE_ALLOWED_RESOURCE_SCHEMES } from '../engine/scene-resource-schemes.js';\nimport type { EngineSession } from '../engine/session.js';\nimport type { RevisionStore } from '../revisions/store.js';\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport { shortHash } from '../workspace/content-hash.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\n\n/** The slice of CreativeEngine this tool touches — keeps it testable. */\ninterface MinimalLoadEngine {\n scene: {\n loadFromArchiveURL(\n url: string,\n overrideEditorConfig?: boolean,\n waitForResources?: boolean\n ): Promise<number>;\n loadFromString(\n content: string,\n overrideEditorConfig?: boolean,\n waitForResources?: boolean\n ): Promise<number>;\n saveToString(options?: {\n allowedResourceSchemes?: string[];\n onDisallowedResourceScheme?: (\n url: string,\n dataHash: string\n ) => Promise<string>;\n }): Promise<string>;\n };\n editor: {\n findAllTransientResources(): Array<{ URL: string; size: number }>;\n findAllMediaURIs(): string[];\n getResourceData(\n uri: string,\n chunkSize: number,\n onData: (result: Uint8Array) => boolean\n ): void;\n relocateResource(currentUrl: string, relocatedUrl: string): void;\n getMimeType(uri: string): Promise<string>;\n };\n}\n\ntype LoadSource = { url: string } | { path: string };\n\ninterface LoadArgs {\n source: LoadSource;\n title?: string;\n}\n\nconst DESCRIPTION = [\n 'Load a native CE.SDK design — a `.scene` file or a `.zip` scene archive (e.g. a template) — as a NEW design root revision. The inverse of `save`.',\n '',\n 'Source: `{ url }` (http/https) or `{ path }` (absolute local file path; Local only). Archives are ingested: bundled asset bytes are extracted into the workspace as content-addressed `assets/<sha>.<ext>` files, so the loaded design is durable and behaves exactly like a natively-built one.',\n '',\n 'Returns JSON `{ revision, title }` — continue with `edit({ parent: revision, … })`. Bare `.scene` loads additionally return `assets`: a classification of every referenced asset URI by scheme (`workspace` / `external` / `bundle` / `transient` / `other`). External URLs are NOT fetched.'\n].join('\\n');\n\n// The schema is built per-backend, like `save`'s outPath: the `{ path }`\n// source arm is advertised ONLY where local disk paths exist (Local).\n//\n// `source` is a FLAT object with optional `url`/`path` — deliberately NOT a\n// `oneOf` union. A `oneOf` here is the single construct most likely to break\n// tool calling: OpenAI bans it in strict mode, and weaker models (e.g.\n// openrouter:xiaomi/mimo-v2.5-pro, and Claude per samchon/openapi#88) serialize\n// the whole union value as an escaped JSON string, which then fails validation.\n// The \"exactly one of url/path\" invariant is enforced in `call` below, with a\n// clear error message instead of an opaque oneOf mismatch. This mirrors\n// `import`'s `source` shape. See test/tools/schema-best-practices.test.ts.\nfunction buildLoadInputSchema(allowPath: boolean) {\n const sourceProperties: Record<string, unknown> = {\n url: {\n type: 'string' as const,\n description: 'http(s) URL of a `.scene` file or `.zip` scene archive.'\n }\n };\n if (allowPath) {\n sourceProperties.path = {\n type: 'string' as const,\n description:\n 'Absolute local file path of a `.scene` file or `.zip` scene archive. Local-only.'\n };\n }\n return {\n type: 'object' as const,\n properties: {\n source: {\n type: 'object' as const,\n description: allowPath\n ? 'The design to load. Provide exactly one of `url` (http/https) or `path` (absolute local file path; Local only).'\n : 'The design to load. Provide `url` (http/https).',\n properties: sourceProperties,\n additionalProperties: false\n },\n title: {\n type: 'string' as const,\n description:\n \"Display name for the new design. Defaults to the source file's basename.\"\n }\n },\n required: ['source'],\n additionalProperties: false\n };\n}\n\n/** Standard zip local-file-header magic: PK\\x03\\x04. */\nfunction isZip(bytes: Uint8Array): boolean {\n return (\n bytes.length >= 4 &&\n bytes[0] === 0x50 &&\n bytes[1] === 0x4b &&\n bytes[2] === 0x03 &&\n bytes[3] === 0x04\n );\n}\n\n/** Basename of the source, minus a `.zip`/`.scene` extension. */\nfunction deriveTitle(source: LoadSource): string | undefined {\n let base: string;\n if ('url' in source) {\n const raw = new URL(source.url).pathname.split('/').pop() ?? '';\n try {\n base = decodeURIComponent(raw);\n } catch {\n base = raw; // malformed percent sequence — use the basename as-is\n }\n } else {\n // Local paths are literal filenames — never percent-decode them.\n base = source.path.split('/').pop() ?? '';\n }\n const name = base.replace(/\\.(zip|scene)$/i, '');\n return name === '' ? undefined : name;\n}\n\n// Broad enough for what CE.SDK archives actually bundle (images, fonts,\n// audio/video). Unknown types get a neutral `.bin` — the workspace store and\n// the /r/ route are extension-agnostic.\nconst MIME_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/svg+xml': 'svg',\n 'video/mp4': 'mp4',\n 'video/quicktime': 'mov',\n 'audio/mpeg': 'mp3',\n 'audio/wav': 'wav',\n 'audio/x-wav': 'wav',\n 'audio/mp4': 'm4a',\n 'font/ttf': 'ttf',\n 'font/otf': 'otf',\n 'font/woff': 'woff',\n 'font/woff2': 'woff2',\n 'application/x-font-ttf': 'ttf',\n 'application/font-sfnt': 'ttf'\n};\n\nfunction extForMime(mime: string): string {\n return MIME_EXT[mime] ?? 'bin';\n}\n\nfunction concatChunks(chunks: Uint8Array[]): Uint8Array {\n const total = chunks.reduce((n, c) => n + c.byteLength, 0);\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\n/**\n * Persist one engine resource's bytes into the content-addressed asset store\n * and return its workspace-relative path. Identical bytes land at the\n * identical path regardless of caller (automatic dedup).\n */\nasync function persistResourceBytes(\n eng: MinimalLoadEngine,\n fs: WorkspaceFS,\n uri: string\n): Promise<string> {\n const chunks: Uint8Array[] = [];\n eng.editor.getResourceData(uri, 1 << 20, (chunk) => {\n chunks.push(chunk.slice()); // copy — the engine may reuse the buffer\n return true;\n });\n const bytes = concatChunks(chunks);\n const sha = await shortHash(bytes);\n const ext = extForMime(await eng.editor.getMimeType(uri));\n const relPath = `assets/${sha}.${ext}`;\n if (!(await fs.exists(relPath))) {\n await fs.writeFile(relPath, bytes);\n }\n return relPath;\n}\n\n/** The engine's error code when a serialized scene still carries a scheme\n * outside `allowedResourceSchemes` (e.g. a lingering `buffer:`). */\nconst DISALLOWED_SCHEMES_CODE = 'SCENE.DISALLOWED_SCHEMES';\n\nfunction isDisallowedSchemesError(err: unknown): boolean {\n return (\n err instanceof Error &&\n (err as Error & { code?: string }).code === DISALLOWED_SCHEMES_CODE\n );\n}\n\n/**\n * Extract every transient (`buffer:`) resource into the content-addressed\n * asset store and relocate it to its durable `workspace://` URI, then\n * serialize with the shared scene-scheme allowlist (which permits `workspace`\n * but not `buffer` — see scene-resource-schemes.ts), so a missed transient\n * still fails loudly instead of silently persisting an unloadable scene.\n */\nasync function explodeArchiveIntoWorkspace(\n eng: MinimalLoadEngine,\n fs: WorkspaceFS\n): Promise<string> {\n // Persist + relocate every transient, remembering each URL's workspace path.\n const persistedByUrl = new Map<string, string>();\n for (const resource of eng.editor.findAllTransientResources()) {\n const relPath = await persistResourceBytes(eng, fs, resource.URL);\n persistedByUrl.set(resource.URL, relPath);\n eng.editor.relocateResource(resource.URL, `workspace://${relPath}`);\n }\n try {\n // Common case: every transient relocated cleanly, so no scheme is disallowed.\n return await eng.scene.saveToString({\n allowedResourceSchemes: SCENE_ALLOWED_RESOURCE_SCHEMES\n });\n } catch (err) {\n if (!isDisallowedSchemesError(err)) throw err;\n // A duplicate-content transient survived: relocateResource no-ops when two\n // transients share a content-addressed target (seen with duplicated fonts\n // in premium-template archives), so the engine still serializes that\n // straggler's buffer: URL. Retry with a drain callback that rewrites it in\n // the OUTPUT to its already-persisted workspace:// form.\n //\n // Two engine constraints shape this (CE.SDK v1.77): (1) the callback MUST\n // NOT re-enter the engine — getResourceData/getMimeType from inside it\n // deadlocks saveToString (it holds the engine while awaiting the callback),\n // so every straggler must be a transient we persisted above and the\n // callback is a pure lookup; (2) we attach the callback ONLY here, on the\n // failure path — passing onDisallowedResourceScheme when nothing is\n // disallowed hangs saveToString.\n const scene = await eng.scene.saveToString({\n allowedResourceSchemes: SCENE_ALLOWED_RESOURCE_SCHEMES,\n onDisallowedResourceScheme: async (url) => {\n const relPath = persistedByUrl.get(url);\n if (relPath === undefined) {\n // A buffer: URL we never enumerated as a transient — should not\n // happen; fail loudly rather than persist an unloadable scene.\n throw new Error(\n `load: cannot drain transient \"${url}\" — not persisted during explode.`\n );\n }\n return `workspace://${relPath}`;\n }\n });\n // The drain fixed the serialized OUTPUT, but the engine still holds the\n // un-relocatable straggler in memory — its live state now diverges from the\n // persisted scene. `load` commits this slot warm for the next edit, so\n // reload the drained scene to realign: reloading collapses the duplicate\n // typeface onto its single workspace:// URI, leaving no buffer: straggler.\n // Without this, the next edit reuses the warm slot and its plain\n // saveToString (no drain) fails on the lingering buffer: URL.\n await eng.scene.loadFromString(scene, false, true);\n return scene;\n }\n}\n\ntype AssetClass = 'workspace' | 'external' | 'bundle' | 'transient' | 'other';\n\ninterface AssetReportEntry {\n uri: string;\n class: AssetClass;\n}\n\n/**\n * Classify one asset URI by its scheme. Only a `buffer:` URI is genuinely\n * `transient` (in-memory, silently lost on the next save); every scheme in\n * SCENE_ALLOWED_RESOURCE_SCHEMES (workspace/http/https/bundle/file/…) is durable.\n */\nfunction classifyUri(uri: string): AssetClass {\n if (uri.startsWith('workspace://')) return 'workspace';\n if (uri.startsWith('http://') || uri.startsWith('https://'))\n return 'external';\n if (uri.startsWith('bundle://')) return 'bundle';\n if (uri.startsWith('buffer:')) return 'transient';\n return 'other';\n}\n\n/**\n * Classify every asset reference of a bare `.scene` (which carries no asset\n * bytes) by scheme. A referenced asset does NOT reliably surface in\n * `findAllMediaURIs()` alone: durable re-fetchable refs the engine keeps as\n * URLs (e.g. http(s)) come back there, while anything it had to load into\n * memory — `workspace://` (resolved via our URI resolver), `bundle://`\n * fallbacks, and genuine `buffer:` orphans — comes back from\n * `findAllTransientResources()` carrying its real URI. So we read BOTH lists\n * and classify by scheme, deduped by URI.\n *\n * Verified against the real engine (CODE-3): after a waitForResources load a\n * resolved `workspace://` asset appears ONLY as a transient resource (never in\n * findAllMediaURIs), and an UNRESOLVABLE `workspace://` ref is silently dropped\n * and substituted (e.g. a bundled fallback font) — so there is no reliable\n * \"dangling\" signal to report, and no such class exists. External URLs are\n * reported, never fetched.\n */\nfunction classifyAssets(eng: MinimalLoadEngine): AssetReportEntry[] {\n const uris = new Set<string>([\n ...eng.editor.findAllMediaURIs(),\n ...eng.editor.findAllTransientResources().map((r) => r.URL)\n ]);\n return [...uris].map((uri) => ({ uri, class: classifyUri(uri) }));\n}\n\nfunction errorResult(text: string): {\n isError: true;\n content: Array<{ type: 'text'; text: string }>;\n} {\n return { isError: true, content: [{ type: 'text' as const, text }] };\n}\n\nexport interface CreateLoadToolOptions<E = unknown> {\n fs: WorkspaceFS;\n store: RevisionStore;\n session: EngineSession<E>;\n /** Guards the shared engine — load-then-explode must not interleave with another engine-touching call. */\n engineLock: Mutex;\n /** Whether to honour a `{ path }` source (Local: yes; Hosted/sandbox: no). */\n allowPath?: boolean;\n /** Called after the new root revision is written (viewer SSE hook). */\n onLeafWritten?: (event: {\n revision: string;\n rootRevision: string;\n }) => void | Promise<void>;\n /** Render a thumbnail for the new revision. Best-effort: failures are swallowed. */\n renderThumbnail?: (event: {\n revision: string;\n engine: E;\n }) => Promise<Uint8Array>;\n}\n\nexport function createLoadTool<E = unknown>(\n opts: CreateLoadToolOptions<E>\n): CodesignMcpToolRegistration {\n const {\n fs,\n store,\n session,\n engineLock,\n allowPath = false,\n onLeafWritten,\n renderThumbnail\n } = opts;\n return {\n name: 'load',\n description: DESCRIPTION,\n inputSchema: buildLoadInputSchema(allowPath),\n call: async (rawArgs) => {\n const args = (rawArgs ?? {}) as Partial<LoadArgs>;\n const source = args.source;\n if (\n source === undefined ||\n typeof source !== 'object' ||\n 'url' in source === 'path' in source\n ) {\n return errorResult(\n 'load: `source` is required — exactly one of `{ url }` or `{ path }`.'\n );\n }\n if ('path' in source && !allowPath) {\n return errorResult(\n 'load: `source.path` is not supported on this (hosted) backend. Pass a `{ url }` source instead.'\n );\n }\n // Ingest bytes OUTSIDE the engine lock — network/disk I/O must not\n // block concurrent engine work.\n let bytes: Uint8Array;\n if ('url' in source) {\n let parsed: URL;\n try {\n parsed = new URL(source.url);\n } catch {\n return errorResult(`load: invalid URL \"${source.url}\".`);\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return errorResult('load: `source.url` must be http(s).');\n }\n let resp: Response;\n try {\n resp = await fetch(source.url);\n } catch (err) {\n return errorResult(\n `load: fetching \"${source.url}\" failed — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n if (!resp.ok) {\n return errorResult(\n `load: fetching \"${source.url}\" failed with HTTP ${resp.status}.`\n );\n }\n try {\n bytes = new Uint8Array(await resp.arrayBuffer());\n } catch (err) {\n return errorResult(\n `load: reading the response body of \"${source.url}\" failed — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n } else {\n try {\n bytes = new Uint8Array(await nodeFs.readFile(source.path));\n } catch (err) {\n return errorResult(\n `load: reading \"${source.path}\" failed — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n const title = args.title ?? deriveTitle(source);\n // The engine is loaded and mutated across several awaits — serialize\n // against any other engine-touching tool call.\n return engineLock.runExclusive(async () => {\n const slot = await session.acquire(null);\n const eng = slot.engine as MinimalLoadEngine;\n try {\n let scene: string;\n let assets: AssetReportEntry[] | undefined;\n if (isZip(bytes)) {\n // Stage to a temp file and load via file:// — scene-level CE.SDK\n // has no loadFromArchive(bytes/path), only loadFromArchiveURL.\n const stagedDir = await nodeFs.mkdtemp(\n join(tmpdir(), 'codesign-load-')\n );\n const staged = join(stagedDir, 'archive.zip');\n try {\n await nodeFs.writeFile(staged, bytes);\n // waitForResources: the archive's bundled bytes must be fully\n // materialized as buffer: resources before we enumerate them.\n await eng.scene.loadFromArchiveURL(\n pathToFileURL(staged).href,\n false,\n true\n );\n } finally {\n await nodeFs\n .rm(stagedDir, { recursive: true, force: true })\n .catch(() => {});\n }\n scene = await explodeArchiveIntoWorkspace(eng, fs);\n } else {\n const content = new TextDecoder().decode(bytes).trim();\n await eng.scene.loadFromString(content, false, true);\n assets = classifyAssets(eng);\n scene = content;\n }\n const written = await store.write({ scene, parent: null, title });\n session.commit(slot, written.id);\n // Thumbnail block mirrors edit.ts — duplication is deliberate (KISS).\n if (renderThumbnail) {\n try {\n const thumb = await renderThumbnail({\n revision: written.id,\n engine: slot.engine\n });\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n await fs.writeFile(\n `exports/${shard}/${rest}/preview.webp`,\n thumb\n );\n } catch {\n // best-effort; thumbnail failure must not poison the load\n }\n }\n if (onLeafWritten) {\n // A new root is its own lineage root.\n await onLeafWritten({\n revision: written.id,\n rootRevision: written.id\n });\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({\n revision: written.id,\n title: await store.currentTitle(written.id),\n ...(assets !== undefined ? { assets } : {})\n })\n }\n ]\n };\n } catch (err) {\n session.markDirty(slot);\n return errorResult(\n `load: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`\n );\n }\n });\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/tools/load.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/tools/load.ts\n//\n// `load` — ingest a native CE.SDK `.scene` file or `.zip` scene archive as a\n// NEW design root revision (the inverse of `save`). Archives are \"exploded\":\n// after `loadFromArchiveURL` their bundled asset bytes live in the engine as\n// ephemeral `buffer:` resources (which scene-level `saveToString` rejects —\n// the CODE-3 bug); we extract each one into the content-addressed asset store\n// (`assets/<sha>.<ext>`), relocate it to a durable `workspace://` URI, and\n// only then serialize. Bare `.scene` files carry no asset bytes: they persist\n// as-is, plus a classification report of every referenced asset URI.\n//\n// Both source variants normalize to bytes up front (fetch for `url`, disk\n// read for `path` — Local only), then dispatch on the zip magic bytes.\n// Archive bytes are staged to a temp file and loaded via a `file://` URL:\n// scene-level CE.SDK v1.77 has no `loadFromArchive(path/bytes)`, only\n// `loadFromArchiveURL`, and `file` is a first-class engine scheme.\n\nimport { promises as nodeFs } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { Mutex } from '../engine/mutex.js';\nimport { SCENE_ALLOWED_RESOURCE_SCHEMES } from '../engine/scene-resource-schemes.js';\nimport type { EngineSession } from '../engine/session.js';\nimport type { RevisionStore } from '../revisions/store.js';\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport { shortHash } from '../workspace/content-hash.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\n\n/** The slice of CreativeEngine this tool touches — keeps it testable. */\ninterface MinimalLoadEngine {\n scene: {\n loadFromArchiveURL(\n url: string,\n overrideEditorConfig?: boolean,\n waitForResources?: boolean\n ): Promise<number>;\n loadFromString(\n content: string,\n overrideEditorConfig?: boolean,\n waitForResources?: boolean\n ): Promise<number>;\n saveToString(options?: {\n allowedResourceSchemes?: string[];\n onDisallowedResourceScheme?: (\n url: string,\n dataHash: string\n ) => Promise<string>;\n }): Promise<string>;\n };\n editor: {\n findAllTransientResources(): Array<{\n URL?: string;\n uri?: string;\n size: number;\n }>;\n findAllMediaURIs(): string[];\n getBufferLength(uri: string): number;\n getBufferData(uri: string, offset: number, length: number): Uint8Array;\n relocateResource(currentUrl: string, relocatedUrl: string): void;\n getMimeType(uri: string): Promise<string>;\n };\n}\n\ntype LoadSource = { url: string } | { path: string };\n\ninterface LoadArgs {\n source: LoadSource;\n title?: string;\n}\n\nconst DESCRIPTION = [\n 'Load a native CE.SDK design — a `.scene` file or a `.zip` scene archive (e.g. a template) — as a NEW design root revision. The inverse of `save`.',\n '',\n 'Source: `{ url }` (http/https) or `{ path }` (absolute local file path; Local only). Archives are ingested: bundled asset bytes are extracted into the workspace as content-addressed `assets/<sha>.<ext>` files, so the loaded design is durable and behaves exactly like a natively-built one.',\n '',\n 'Returns JSON `{ revision, title }` — continue with `edit({ parent: revision, … })`. Bare `.scene` loads additionally return `assets`: a classification of every referenced asset URI by scheme (`workspace` / `external` / `bundle` / `transient` / `other`). External URLs are NOT fetched.'\n].join('\\n');\n\n// The schema is built per-backend, like `save`'s outPath: the `{ path }`\n// source arm is advertised ONLY where local disk paths exist (Local).\n//\n// `source` is a FLAT object with optional `url`/`path` — deliberately NOT a\n// `oneOf` union. A `oneOf` here is the single construct most likely to break\n// tool calling: OpenAI bans it in strict mode, and weaker models (e.g.\n// openrouter:xiaomi/mimo-v2.5-pro, and Claude per samchon/openapi#88) serialize\n// the whole union value as an escaped JSON string, which then fails validation.\n// The \"exactly one of url/path\" invariant is enforced in `call` below, with a\n// clear error message instead of an opaque oneOf mismatch. This mirrors\n// `import`'s `source` shape. See test/tools/schema-best-practices.test.ts.\nfunction buildLoadInputSchema(allowPath: boolean) {\n const sourceProperties: Record<string, unknown> = {\n url: {\n type: 'string' as const,\n description: 'http(s) URL of a `.scene` file or `.zip` scene archive.'\n }\n };\n if (allowPath) {\n sourceProperties.path = {\n type: 'string' as const,\n description:\n 'Absolute local file path of a `.scene` file or `.zip` scene archive. Local-only.'\n };\n }\n return {\n type: 'object' as const,\n properties: {\n source: {\n type: 'object' as const,\n description: allowPath\n ? 'The design to load. Provide exactly one of `url` (http/https) or `path` (absolute local file path; Local only).'\n : 'The design to load. Provide `url` (http/https).',\n properties: sourceProperties,\n additionalProperties: false\n },\n title: {\n type: 'string' as const,\n description:\n \"Display name for the new design. Defaults to the source file's basename.\"\n }\n },\n required: ['source'],\n additionalProperties: false\n };\n}\n\n/** Standard zip local-file-header magic: PK\\x03\\x04. */\nfunction isZip(bytes: Uint8Array): boolean {\n return (\n bytes.length >= 4 &&\n bytes[0] === 0x50 &&\n bytes[1] === 0x4b &&\n bytes[2] === 0x03 &&\n bytes[3] === 0x04\n );\n}\n\n/** Basename of the source, minus a `.zip`/`.scene` extension. */\nfunction deriveTitle(source: LoadSource): string | undefined {\n let base: string;\n if ('url' in source) {\n const raw = new URL(source.url).pathname.split('/').pop() ?? '';\n try {\n base = decodeURIComponent(raw);\n } catch {\n base = raw; // malformed percent sequence — use the basename as-is\n }\n } else {\n // Local paths are literal filenames — never percent-decode them.\n base = source.path.split('/').pop() ?? '';\n }\n const name = base.replace(/\\.(zip|scene)$/i, '');\n return name === '' ? undefined : name;\n}\n\n// Broad enough for what CE.SDK archives actually bundle (images, fonts,\n// audio/video). Unknown types get a neutral `.bin` — the workspace store and\n// the /r/ route are extension-agnostic.\nconst MIME_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/svg+xml': 'svg',\n 'video/mp4': 'mp4',\n 'video/quicktime': 'mov',\n 'audio/mpeg': 'mp3',\n 'audio/wav': 'wav',\n 'audio/x-wav': 'wav',\n 'audio/mp4': 'm4a',\n 'font/ttf': 'ttf',\n 'font/otf': 'otf',\n 'font/woff': 'woff',\n 'font/woff2': 'woff2',\n 'application/x-font-ttf': 'ttf',\n 'application/font-sfnt': 'ttf'\n};\n\nfunction extForMime(mime: string): string {\n return MIME_EXT[mime] ?? 'bin';\n}\n\n/**\n * The buffer URI of a transient resource. @cesdk/node (WASM) exposes it as\n * `.URL`; @cesdk/node-native exposes it as `.uri`.\n */\nfunction transientResourceUri(resource: {\n URL?: string;\n uri?: string;\n}): string {\n const uri = resource.URL ?? resource.uri;\n if (uri === undefined) {\n throw new Error('load: transient resource has neither URL nor uri');\n }\n return uri;\n}\n\n/**\n * Persist one engine resource's bytes into the content-addressed asset store\n * and return its workspace-relative path. Identical bytes land at the\n * identical path regardless of caller (automatic dedup).\n */\nasync function persistResourceBytes(\n eng: MinimalLoadEngine,\n fs: WorkspaceFS,\n uri: string\n): Promise<string> {\n // getBufferLength + getBufferData is the engine-portable way to read a\n // transient (buffer:) resource's bytes: it exists on both @cesdk/node (WASM)\n // and @cesdk/node-native, whereas getResourceData is WASM-only. slice()\n // copies because the engine may reuse the returned buffer.\n const length = eng.editor.getBufferLength(uri);\n const bytes = eng.editor.getBufferData(uri, 0, length).slice();\n const sha = await shortHash(bytes);\n const ext = extForMime(await eng.editor.getMimeType(uri));\n const relPath = `assets/${sha}.${ext}`;\n if (!(await fs.exists(relPath))) {\n await fs.writeFile(relPath, bytes);\n }\n return relPath;\n}\n\n/** The engine's error code when a serialized scene still carries a scheme\n * outside `allowedResourceSchemes` (e.g. a lingering `buffer:`). */\nconst DISALLOWED_SCHEMES_CODE = 'SCENE.DISALLOWED_SCHEMES';\n\n// `@cesdk/node-native` (as of 1.79.0-nightly.20260708) does not set `err.code`\n// on engine errors, so this predicate never matches on native and the\n// duplicate-transient drain retry below is effectively WASM-only until the\n// upstream fix lands. The first-save path (which normally succeeds after\n// explode) is unaffected.\nfunction isDisallowedSchemesError(err: unknown): boolean {\n return (\n err instanceof Error &&\n (err as Error & { code?: string }).code === DISALLOWED_SCHEMES_CODE\n );\n}\n\n/**\n * Extract every transient (`buffer:`) resource into the content-addressed\n * asset store and relocate it to its durable `workspace://` URI, then\n * serialize with the shared scene-scheme allowlist (which permits `workspace`\n * but not `buffer` — see scene-resource-schemes.ts), so a missed transient\n * still fails loudly instead of silently persisting an unloadable scene.\n */\nasync function explodeArchiveIntoWorkspace(\n eng: MinimalLoadEngine,\n fs: WorkspaceFS\n): Promise<string> {\n // Persist + relocate every transient, remembering each URL's workspace path.\n const persistedByUrl = new Map<string, string>();\n for (const resource of eng.editor.findAllTransientResources()) {\n const url = transientResourceUri(resource);\n const relPath = await persistResourceBytes(eng, fs, url);\n persistedByUrl.set(url, relPath);\n eng.editor.relocateResource(url, `workspace://${relPath}`);\n }\n try {\n // Common case: every transient relocated cleanly, so no scheme is disallowed.\n return await eng.scene.saveToString({\n allowedResourceSchemes: SCENE_ALLOWED_RESOURCE_SCHEMES\n });\n } catch (err) {\n if (!isDisallowedSchemesError(err)) throw err;\n // A duplicate-content transient survived: relocateResource no-ops when two\n // transients share a content-addressed target (seen with duplicated fonts\n // in premium-template archives), so the engine still serializes that\n // straggler's buffer: URL. Retry with a drain callback that rewrites it in\n // the OUTPUT to its already-persisted workspace:// form.\n //\n // Two engine constraints shape this (CE.SDK v1.77): (1) the callback MUST\n // NOT re-enter the engine — getBufferData/getMimeType from inside it\n // deadlocks saveToString (it holds the engine while awaiting the callback),\n // so every straggler must be a transient we persisted above and the\n // callback is a pure lookup; (2) we attach the callback ONLY here, on the\n // failure path — passing onDisallowedResourceScheme when nothing is\n // disallowed hangs saveToString.\n const scene = await eng.scene.saveToString({\n allowedResourceSchemes: SCENE_ALLOWED_RESOURCE_SCHEMES,\n onDisallowedResourceScheme: async (url) => {\n const relPath = persistedByUrl.get(url);\n if (relPath === undefined) {\n // A buffer: URL we never enumerated as a transient — should not\n // happen; fail loudly rather than persist an unloadable scene.\n throw new Error(\n `load: cannot drain transient \"${url}\" — not persisted during explode.`\n );\n }\n return `workspace://${relPath}`;\n }\n });\n // The drain fixed the serialized OUTPUT, but the engine still holds the\n // un-relocatable straggler in memory — its live state now diverges from the\n // persisted scene. `load` commits this slot warm for the next edit, so\n // reload the drained scene to realign: reloading collapses the duplicate\n // typeface onto its single workspace:// URI, leaving no buffer: straggler.\n // Without this, the next edit reuses the warm slot and its plain\n // saveToString (no drain) fails on the lingering buffer: URL.\n await eng.scene.loadFromString(scene, false, true);\n return scene;\n }\n}\n\ntype AssetClass = 'workspace' | 'external' | 'bundle' | 'transient' | 'other';\n\ninterface AssetReportEntry {\n uri: string;\n class: AssetClass;\n}\n\n/**\n * Classify one asset URI by its scheme. Only a `buffer:` URI is genuinely\n * `transient` (in-memory, silently lost on the next save); every scheme in\n * SCENE_ALLOWED_RESOURCE_SCHEMES (workspace/http/https/bundle/file/…) is durable.\n */\nfunction classifyUri(uri: string): AssetClass {\n if (uri.startsWith('workspace://')) return 'workspace';\n if (uri.startsWith('http://') || uri.startsWith('https://'))\n return 'external';\n if (uri.startsWith('bundle://')) return 'bundle';\n if (uri.startsWith('buffer:')) return 'transient';\n return 'other';\n}\n\n/**\n * Classify every asset reference of a bare `.scene` (which carries no asset\n * bytes) by scheme. A referenced asset does NOT reliably surface in\n * `findAllMediaURIs()` alone: durable re-fetchable refs the engine keeps as\n * URLs (e.g. http(s)) come back there, while anything it had to load into\n * memory — `workspace://` (resolved via our URI resolver), `bundle://`\n * fallbacks, and genuine `buffer:` orphans — comes back from\n * `findAllTransientResources()` carrying its real URI. So we read BOTH lists\n * and classify by scheme, deduped by URI.\n *\n * Verified against the real engine (CODE-3): after a waitForResources load a\n * resolved `workspace://` asset appears ONLY as a transient resource (never in\n * findAllMediaURIs), and an UNRESOLVABLE `workspace://` ref is silently dropped\n * and substituted (e.g. a bundled fallback font) — so there is no reliable\n * \"dangling\" signal to report, and no such class exists. External URLs are\n * reported, never fetched.\n */\nfunction classifyAssets(eng: MinimalLoadEngine): AssetReportEntry[] {\n const uris = new Set<string>([\n ...eng.editor.findAllMediaURIs(),\n ...eng.editor.findAllTransientResources().map(transientResourceUri)\n ]);\n return [...uris].map((uri) => ({ uri, class: classifyUri(uri) }));\n}\n\nfunction errorResult(text: string): {\n isError: true;\n content: Array<{ type: 'text'; text: string }>;\n} {\n return { isError: true, content: [{ type: 'text' as const, text }] };\n}\n\nexport interface CreateLoadToolOptions<E = unknown> {\n fs: WorkspaceFS;\n store: RevisionStore;\n session: EngineSession<E>;\n /** Guards the shared engine — load-then-explode must not interleave with another engine-touching call. */\n engineLock: Mutex;\n /** Whether to honour a `{ path }` source (Local: yes; Hosted/sandbox: no). */\n allowPath?: boolean;\n /** Called after the new root revision is written (viewer SSE hook). */\n onLeafWritten?: (event: {\n revision: string;\n rootRevision: string;\n }) => void | Promise<void>;\n /** Render a thumbnail for the new revision. Best-effort: failures are swallowed. */\n renderThumbnail?: (event: {\n revision: string;\n engine: E;\n }) => Promise<Uint8Array>;\n}\n\nexport function createLoadTool<E = unknown>(\n opts: CreateLoadToolOptions<E>\n): CodesignMcpToolRegistration {\n const {\n fs,\n store,\n session,\n engineLock,\n allowPath = false,\n onLeafWritten,\n renderThumbnail\n } = opts;\n return {\n name: 'load',\n description: DESCRIPTION,\n inputSchema: buildLoadInputSchema(allowPath),\n call: async (rawArgs) => {\n const args = (rawArgs ?? {}) as Partial<LoadArgs>;\n const source = args.source;\n if (\n source === undefined ||\n typeof source !== 'object' ||\n 'url' in source === 'path' in source\n ) {\n return errorResult(\n 'load: `source` is required — exactly one of `{ url }` or `{ path }`.'\n );\n }\n if ('path' in source && !allowPath) {\n return errorResult(\n 'load: `source.path` is not supported on this (hosted) backend. Pass a `{ url }` source instead.'\n );\n }\n // Ingest bytes OUTSIDE the engine lock — network/disk I/O must not\n // block concurrent engine work.\n let bytes: Uint8Array;\n if ('url' in source) {\n let parsed: URL;\n try {\n parsed = new URL(source.url);\n } catch {\n return errorResult(`load: invalid URL \"${source.url}\".`);\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return errorResult('load: `source.url` must be http(s).');\n }\n let resp: Response;\n try {\n resp = await fetch(source.url);\n } catch (err) {\n return errorResult(\n `load: fetching \"${source.url}\" failed — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n if (!resp.ok) {\n return errorResult(\n `load: fetching \"${source.url}\" failed with HTTP ${resp.status}.`\n );\n }\n try {\n bytes = new Uint8Array(await resp.arrayBuffer());\n } catch (err) {\n return errorResult(\n `load: reading the response body of \"${source.url}\" failed — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n } else {\n try {\n bytes = new Uint8Array(await nodeFs.readFile(source.path));\n } catch (err) {\n return errorResult(\n `load: reading \"${source.path}\" failed — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n const title = args.title ?? deriveTitle(source);\n // The engine is loaded and mutated across several awaits — serialize\n // against any other engine-touching tool call.\n return engineLock.runExclusive(async () => {\n const slot = await session.acquire(null);\n const eng = slot.engine as MinimalLoadEngine;\n try {\n let scene: string;\n let assets: AssetReportEntry[] | undefined;\n if (isZip(bytes)) {\n // Stage to a temp file and load via file:// — scene-level CE.SDK\n // has no loadFromArchive(bytes/path), only loadFromArchiveURL.\n const stagedDir = await nodeFs.mkdtemp(\n join(tmpdir(), 'codesign-load-')\n );\n const staged = join(stagedDir, 'archive.zip');\n try {\n await nodeFs.writeFile(staged, bytes);\n // waitForResources: the archive's bundled bytes must be fully\n // materialized as buffer: resources before we enumerate them.\n await eng.scene.loadFromArchiveURL(\n pathToFileURL(staged).href,\n false,\n true\n );\n } finally {\n await nodeFs\n .rm(stagedDir, { recursive: true, force: true })\n .catch(() => {});\n }\n scene = await explodeArchiveIntoWorkspace(eng, fs);\n } else {\n const content = new TextDecoder().decode(bytes).trim();\n await eng.scene.loadFromString(content, false, true);\n assets = classifyAssets(eng);\n scene = content;\n }\n const written = await store.write({ scene, parent: null, title });\n session.commit(slot, written.id);\n // Thumbnail block mirrors edit.ts — duplication is deliberate (KISS).\n if (renderThumbnail) {\n try {\n const thumb = await renderThumbnail({\n revision: written.id,\n engine: slot.engine\n });\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n await fs.writeFile(\n `exports/${shard}/${rest}/preview.webp`,\n thumb\n );\n } catch {\n // best-effort; thumbnail failure must not poison the load\n }\n }\n if (onLeafWritten) {\n // A new root is its own lineage root.\n await onLeafWritten({\n revision: written.id,\n rootRevision: written.id\n });\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({\n revision: written.id,\n title: await store.currentTitle(written.id),\n ...(assets !== undefined ? { assets } : {})\n })\n }\n ]\n };\n } catch (err) {\n session.markDirty(slot);\n return errorResult(\n `load: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`\n );\n }\n });\n }\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/tools/preview.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/tools/preview.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AA8CpB,MAAM,WAAW,wBAAwB,CAAC,CAAC,GAAG,OAAO;IACnD,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,yHAAyH;IACzH,UAAU,EAAE,KAAK,CAAC;IAClB,2FAA2F;IAC3F,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAUD,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAC3C,IAAI,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAChC,2BAA2B,CA4H7B"}
|
package/dist/tools/preview.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// packages/codesign-mcp/src/tools/preview.ts
|
|
2
2
|
//
|
|
3
|
-
// `preview` — renders a block to an inline image.
|
|
4
|
-
// (
|
|
5
|
-
// the
|
|
6
|
-
//
|
|
3
|
+
// `preview` — renders a block to an inline image. `revision` is REQUIRED
|
|
4
|
+
// (CODE-76): the session loads that revision before exporting, so `blockId`
|
|
5
|
+
// always resolves against the scene it belongs to — never against a different
|
|
6
|
+
// design a concurrent agent may have loaded into the shared engine.
|
|
7
7
|
import { performance } from 'node:perf_hooks';
|
|
8
8
|
import { SpanRecorder, NOOP_TELEMETRY_SINK, summarizeResult, toErrorShape } from './spans.js';
|
|
9
9
|
const MAX_DIMENSION_PX = 1800;
|
|
@@ -11,9 +11,9 @@ const DEFAULT_TARGET_WIDTH = 512;
|
|
|
11
11
|
const DESCRIPTION = [
|
|
12
12
|
'Render a block (almost always a page) to an inline image so you can see the result.',
|
|
13
13
|
'',
|
|
14
|
-
"`blockId`
|
|
14
|
+
"`blockId` and `revision` are BOTH REQUIRED. Preview the page you're working on (not the scene root), and pass the `revision` the block belongs to — the id `edit` returned.",
|
|
15
15
|
'',
|
|
16
|
-
|
|
16
|
+
'The given `revision` is loaded before rendering, so `blockId` always resolves against the right scene — never against a different design another agent may have loaded on a shared server.',
|
|
17
17
|
'',
|
|
18
18
|
"Defaults: format='webp', width=512. Width/height clamped at 1800 px (Anthropic many-image limit)."
|
|
19
19
|
].join('\n');
|
|
@@ -23,13 +23,13 @@ const PREVIEW_INPUT_SCHEMA = {
|
|
|
23
23
|
blockId: { type: 'integer' },
|
|
24
24
|
revision: {
|
|
25
25
|
type: 'string',
|
|
26
|
-
description:
|
|
26
|
+
description: 'Revision id the block belongs to (from `edit`). Loaded before rendering so `blockId` resolves against the right scene.'
|
|
27
27
|
},
|
|
28
28
|
format: { enum: ['webp', 'png', 'jpeg'] },
|
|
29
29
|
width: { type: 'integer', minimum: 1 },
|
|
30
30
|
height: { type: 'integer', minimum: 1 }
|
|
31
31
|
},
|
|
32
|
-
required: ['blockId'],
|
|
32
|
+
required: ['blockId', 'revision'],
|
|
33
33
|
additionalProperties: false
|
|
34
34
|
};
|
|
35
35
|
function bytesToBase64(bytes) {
|
|
@@ -53,6 +53,7 @@ export function createPreviewTool(opts) {
|
|
|
53
53
|
let result;
|
|
54
54
|
let threw;
|
|
55
55
|
let revision;
|
|
56
|
+
let exportError;
|
|
56
57
|
try {
|
|
57
58
|
result = await (async () => {
|
|
58
59
|
const args = (rawArgs ?? {});
|
|
@@ -67,38 +68,30 @@ export function createPreviewTool(opts) {
|
|
|
67
68
|
]
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
if (typeof args.revision !== 'string') {
|
|
72
|
+
return {
|
|
73
|
+
isError: true,
|
|
74
|
+
content: [
|
|
75
|
+
{
|
|
76
|
+
type: 'text',
|
|
77
|
+
text: 'preview: `revision` (string) is required — pass the revision the block belongs to (the id `edit` returned). It is loaded before rendering so `blockId` always resolves against the right scene.'
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const revId = args.revision;
|
|
83
|
+
revision = revId;
|
|
84
|
+
// Load the requested revision, then export — both read the shared engine
|
|
85
|
+
// across awaits, so serialize against any concurrent engine-touching tool
|
|
86
|
+
// call (see engine/mutex.ts).
|
|
72
87
|
const beforeLock = performance.now();
|
|
73
88
|
return engineLock.runExclusive(async () => {
|
|
74
89
|
rec.add('lock_wait', performance.now() - beforeLock);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
slot = await rec.time('engine_scene_load', async () => {
|
|
81
|
-
await session.populate(revId, rev.scene);
|
|
82
|
-
return session.acquire(revId);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
// No revision: render the current engine state via the side-effect-free
|
|
87
|
-
// currentSlot accessor (does NOT reset). If no slot is warm, error.
|
|
88
|
-
const current = session.currentSlot();
|
|
89
|
-
if (!current) {
|
|
90
|
-
return {
|
|
91
|
-
isError: true,
|
|
92
|
-
content: [
|
|
93
|
-
{
|
|
94
|
-
type: 'text',
|
|
95
|
-
text: 'preview: no engine state to render. Run `edit` first, or pass `revision` to load a specific revision.'
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
slot = current;
|
|
101
|
-
}
|
|
90
|
+
const rev = await rec.time('workspace_read', () => store.read(revId));
|
|
91
|
+
const slot = await rec.time('engine_scene_load', async () => {
|
|
92
|
+
await session.populate(revId, rev.scene);
|
|
93
|
+
return session.acquire(revId);
|
|
94
|
+
});
|
|
102
95
|
const eng = slot.engine;
|
|
103
96
|
const format = args.format ?? 'webp';
|
|
104
97
|
const mimeType = `image/${format}`;
|
|
@@ -106,8 +99,31 @@ export function createPreviewTool(opts) {
|
|
|
106
99
|
const exportOpts = { targetWidth };
|
|
107
100
|
if (args.height !== undefined)
|
|
108
101
|
exportOpts.targetHeight = Math.min(args.height, MAX_DIMENSION_PX);
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
// We loaded `revId` under the lock immediately above, so the correct
|
|
103
|
+
// scene is guaranteed to be current here. If export still throws,
|
|
104
|
+
// `blockId` genuinely isn't in this revision's scene — a wrong id, or
|
|
105
|
+
// a block removed in this revision. Return a legible tool error the
|
|
106
|
+
// model can act on — mirroring `edit`'s dirty-on-throw path — instead
|
|
107
|
+
// of rethrowing into an opaque MCP -32603 the model can't reason about.
|
|
108
|
+
let bytes;
|
|
109
|
+
try {
|
|
110
|
+
const blob = await rec.time('engine_execute', () => eng.block.export(args.blockId, mimeType, exportOpts));
|
|
111
|
+
bytes = new Uint8Array(await blob.arrayBuffer());
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
exportError = err;
|
|
115
|
+
return {
|
|
116
|
+
isError: true,
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: `preview: could not render block ${args.blockId} in revision ${revId}. That block id isn't in this revision's scene — double-check the id (use \`inspect\` to see the revision's structure), or it may have been removed in this revision. ${err instanceof Error
|
|
121
|
+
? (err.stack ?? err.message)
|
|
122
|
+
: String(err)}`
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
};
|
|
126
|
+
}
|
|
111
127
|
return {
|
|
112
128
|
content: [
|
|
113
129
|
{ type: 'image', data: bytesToBase64(bytes), mimeType }
|
|
@@ -130,7 +146,11 @@ export function createPreviewTool(opts) {
|
|
|
130
146
|
args: rawArgs,
|
|
131
147
|
...(result !== undefined ? { result: summarizeResult(result) } : {}),
|
|
132
148
|
...(revision ? { revision } : {}),
|
|
133
|
-
...(threw
|
|
149
|
+
...(threw
|
|
150
|
+
? { error: toErrorShape(threw) }
|
|
151
|
+
: exportError
|
|
152
|
+
? { error: toErrorShape(exportError) }
|
|
153
|
+
: {})
|
|
134
154
|
});
|
|
135
155
|
}
|
|
136
156
|
}
|