@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spa-assets.generated.js","sourceRoot":"","sources":["../../src/diagnostics/spa-assets.generated.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,UAAU,GAA2B;IACjD,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0sCd;CACA,CAAC","sourcesContent":["// AUTO-GENERATED by scripts/generate-spa-assets.mjs. Do not edit by hand.\nexport const SPA_ASSETS: Record<string, string> = {\n\t\"index.html\": `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>codesign diagnostics</title>\n <style>\n :root {\n color-scheme: light dark;\n --c-lock_wait: #e67e22;\n --c-workspace_read: #3498db;\n --c-engine_scene_load: #9b59b6;\n --c-engine_execute: #2ecc71;\n --c-engine_scene_save: #1abc9c;\n --c-engine_scene_thumbnail: #f39c12;\n --c-workspace_write: #e91e63;\n --line: #8884;\n --line-soft: #8882;\n }\n * {\n box-sizing: border-box;\n }\n body {\n font:\n 14px/1.5 system-ui,\n sans-serif;\n margin: 0 auto;\n padding: 1.5rem;\n max-width: 64rem;\n }\n h1 {\n font-size: 1.15rem;\n margin: 0 0 0.25rem;\n }\n h2 {\n font-size: 0.82rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n opacity: 0.7;\n margin: 1.6rem 0 0.6rem;\n }\n #status {\n font-size: 0.8rem;\n opacity: 0.7;\n margin-left: 0.5rem;\n font-weight: normal;\n }\n #meta {\n font-size: 0.8rem;\n opacity: 0.75;\n margin-bottom: 1.2rem;\n font-variant-numeric: tabular-nums;\n }\n #meta .sep {\n opacity: 0.4;\n margin: 0 0.5rem;\n }\n .muted {\n opacity: 0.6;\n }\n .err {\n color: #c0392b;\n }\n\n /* Drop zone */\n #drop-zone {\n border: 2px dashed var(--line);\n border-radius: 8px;\n padding: 2rem;\n text-align: center;\n margin: 1rem 0 1.5rem;\n cursor: pointer;\n }\n #drop-zone.over {\n background: #3498db22;\n border-color: #3498db;\n }\n #file-input {\n display: none;\n }\n\n /* Summary cards */\n .cards {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n margin-bottom: 0.5rem;\n }\n .card {\n border: 1px solid var(--line);\n border-radius: 8px;\n padding: 0.6rem 0.9rem;\n min-width: 8rem;\n flex: 1 1 8rem;\n }\n .card .label {\n font-size: 0.7rem;\n opacity: 0.65;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n }\n .card .value {\n font-size: 1.35rem;\n font-variant-numeric: tabular-nums;\n line-height: 1.3;\n }\n .card .sub {\n font-size: 0.75rem;\n opacity: 0.6;\n }\n\n /* Phase legend */\n .legend {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1rem;\n font-size: 0.75rem;\n opacity: 0.85;\n margin: 0 0 0.5rem;\n }\n .legend span {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n }\n .sw {\n width: 0.7rem;\n height: 0.7rem;\n border-radius: 2px;\n display: inline-block;\n flex: none;\n }\n\n /* Shared stacked phase bar */\n .phase-bar {\n display: flex;\n height: 1.1rem;\n border-radius: 4px;\n overflow: hidden;\n }\n .phase-seg {\n height: 100%;\n }\n\n /* Per-tool aggregate table */\n .table-wrap {\n overflow-x: auto;\n }\n table {\n border-collapse: collapse;\n width: 100%;\n }\n th,\n td {\n text-align: right;\n padding: 0.35rem 0.7rem;\n border-bottom: 1px solid var(--line-soft);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n th {\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n opacity: 0.6;\n font-weight: 600;\n }\n th:first-child,\n td:first-child {\n text-align: left;\n }\n td.bar-cell {\n width: 40%;\n min-width: 12rem;\n }\n\n /* Tabs */\n .tabs {\n display: flex;\n gap: 0.25rem;\n border-bottom: 1px solid var(--line);\n margin: 1.6rem 0 0.9rem;\n }\n .tab {\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n padding: 0.5rem 0.9rem;\n font: inherit;\n font-size: 0.85rem;\n color: inherit;\n opacity: 0.6;\n cursor: pointer;\n }\n .tab:hover {\n opacity: 0.9;\n }\n .tab.active {\n opacity: 1;\n font-weight: 600;\n border-bottom-color: currentColor;\n }\n .tabpanel[hidden] {\n display: none;\n }\n\n /* Per-call accordion */\n .calls-head,\n .call-row {\n display: grid;\n grid-template-columns: 5.5rem 7rem 1fr 5rem 4.5rem;\n gap: 0.6rem;\n align-items: center;\n }\n .calls-head {\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n opacity: 0.6;\n padding: 0 0.4rem 0.35rem;\n border-bottom: 1px solid var(--line);\n }\n .call {\n border-bottom: 1px solid var(--line-soft);\n }\n .call-row {\n cursor: pointer;\n list-style: none;\n padding: 0.4rem;\n font-size: 0.85rem;\n font-variant-numeric: tabular-nums;\n }\n .call-row::-webkit-details-marker {\n display: none;\n }\n .call-row:hover,\n .call[open] .call-row {\n background: #8881;\n }\n .c-time {\n opacity: 0.6;\n }\n .c-tool {\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .call-row .phase-bar {\n height: 0.9rem;\n }\n .num {\n text-align: right;\n }\n .c-status {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n justify-content: flex-end;\n }\n .chevron {\n opacity: 0.5;\n transition: transform 0.12s ease;\n }\n .call[open] .chevron {\n transform: rotate(90deg);\n }\n .ok-badge {\n font-size: 0.7rem;\n opacity: 0.5;\n }\n .err-badge {\n font-size: 0.7rem;\n background: #c0392b;\n color: #fff;\n border-radius: 3px;\n padding: 0 4px;\n }\n .call-body {\n padding: 0.3rem 0.6rem 0.9rem;\n }\n .kv-label {\n font-size: 0.68rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n opacity: 0.55;\n margin: 0.8rem 0 0.35rem;\n }\n .phase-list {\n display: flex;\n flex-direction: column;\n gap: 0.15rem;\n }\n .pl-row {\n display: grid;\n grid-template-columns: 11rem 1fr 5rem;\n gap: 0.6rem;\n align-items: center;\n font-size: 0.8rem;\n font-variant-numeric: tabular-nums;\n }\n .pl-name {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .pl-track {\n background: #8882;\n border-radius: 2px;\n height: 0.7rem;\n overflow: hidden;\n }\n .pl-fill {\n display: block;\n height: 100%;\n }\n .meta-kv {\n opacity: 0.7;\n font-size: 0.78rem;\n margin-top: 0.5rem;\n }\n .meta-kv code {\n font-family: monospace;\n }\n pre.args {\n margin: 0;\n padding: 0.5rem 0.6rem;\n background: #0002;\n border-radius: 6px;\n max-height: 18rem;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n font-size: 0.78rem;\n line-height: 1.45;\n }\n pre.args.err {\n color: #c0392b;\n }\n pre.code {\n margin: 0;\n padding: 0.6rem 0.7rem;\n background: #0003;\n border-left: 3px solid var(--c-engine_execute);\n border-radius: 4px;\n max-height: 26rem;\n overflow: auto;\n white-space: pre;\n font-size: 0.78rem;\n line-height: 1.5;\n tab-size: 2;\n }\n .result-img {\n display: block;\n max-width: 360px;\n max-height: 360px;\n margin-top: 0.5rem;\n border: 1px solid var(--line);\n border-radius: 6px;\n background: repeating-conic-gradient(#8882 0 25%, #0000 0 50%) 0 / 16px\n 16px;\n }\n\n /* Timeline */\n .timeline {\n list-style: none;\n padding: 0;\n margin: 0 0 1rem;\n max-height: 22rem;\n overflow: auto;\n font-variant-numeric: tabular-nums;\n }\n .timeline li {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n padding: 0.2rem 0;\n border-bottom: 1px solid var(--line-soft);\n font-size: 0.85rem;\n }\n .tl-ts {\n opacity: 0.6;\n min-width: 5rem;\n }\n .tl-start {\n font-weight: 600;\n }\n .tl-err {\n color: #c0392b;\n width: 100%;\n padding-left: 5.5rem;\n font-family: monospace;\n font-size: 0.8rem;\n }\n\n /* Engine log */\n .log-tail {\n font-family: monospace;\n font-size: 0.8rem;\n max-height: 24rem;\n overflow: auto;\n background: #0002;\n border-radius: 6px;\n padding: 0.5rem;\n margin-bottom: 1.5rem;\n }\n .log-line {\n display: flex;\n gap: 0.5rem;\n padding: 0.05rem 0;\n }\n .log-text {\n white-space: pre-wrap;\n word-break: break-all;\n }\n .lvl {\n min-width: 3.5rem;\n font-weight: bold;\n opacity: 0.8;\n }\n .lvl-warn {\n color: #e67e22;\n }\n .lvl-error {\n color: #c0392b;\n }\n .lvl-debug {\n opacity: 0.5;\n }\n .lvl-trace {\n opacity: 0.4;\n }\n </style>\n <!-- CODE-62: served-mode injection. The MCP router replaces the whole\n delimited placeholder with window.__DIAGNOSTICS_STREAM__/BUNDLE__\n assignments; the standalone serve.mjs blanks it. Unreplaced (raw\n file://) it is an empty comment — a valid no-op. -->\n <script>\n /*__DIAGNOSTICS_INJECT__*/\n </script>\n </head>\n <body>\n <h1>\n codesign diagnostics\n <span id=\"status\">drop a bundle (.zip) or .ndjson, or use ?file=</span>\n <a\n id=\"download-zip\"\n href=\"#\"\n download\n style=\"display: none; margin-left: 12px; font-weight: 600\"\n >⬇ Download ZIP (send to IMG.LY)</a\n >\n </h1>\n <div id=\"meta\"></div>\n\n <div id=\"drop-zone\">\n <label for=\"file-input\"\n >Drop a diagnostics <b>.zip</b> (or a raw <b>.ndjson</b>) here, or\n <u>click to browse</u></label\n >\n <input type=\"file\" id=\"file-input\" accept=\".zip,.ndjson,.txt,.json\" />\n </div>\n\n <div class=\"cards\" id=\"cards\"></div>\n <div class=\"legend\" id=\"legend\"></div>\n\n <h2>Per-tool</h2>\n <div class=\"table-wrap\">\n <table>\n <thead>\n <tr>\n <th>tool</th>\n <th>calls</th>\n <th>err%</th>\n <th>mean ms</th>\n <th>max ms</th>\n <th>phase breakdown</th>\n </tr>\n </thead>\n <tbody id=\"tool-agg\">\n <tr>\n <td colspan=\"6\" class=\"muted\">no events loaded</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <div class=\"tabs\" role=\"tablist\">\n <button\n class=\"tab active\"\n data-tab=\"calls\"\n role=\"tab\"\n aria-selected=\"true\"\n >\n Per-call detail\n </button>\n <button class=\"tab\" data-tab=\"timeline\" role=\"tab\" aria-selected=\"false\">\n Timeline\n </button>\n <button class=\"tab\" data-tab=\"log\" role=\"tab\" aria-selected=\"false\">\n Engine log\n </button>\n </div>\n\n <div class=\"tabpanel\" id=\"panel-calls\" role=\"tabpanel\">\n <div id=\"tool-calls\"><p class=\"muted\">no events loaded</p></div>\n </div>\n <div class=\"tabpanel\" id=\"panel-timeline\" role=\"tabpanel\" hidden>\n <ul class=\"timeline\" id=\"timeline\">\n <li class=\"muted\">no events loaded</li>\n </ul>\n </div>\n <div class=\"tabpanel\" id=\"panel-log\" role=\"tabpanel\" hidden>\n <div class=\"log-tail\" id=\"log-tail\">\n <span class=\"muted\">no events loaded</span>\n </div>\n </div>\n\n <script>\n // ---- Untrusted-text hardening: escape every event/meta-derived string\n // before it reaches innerHTML (bundles come from a user's machine).\n const esc = (s) =>\n String(s)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n const PHASE_ORDER = [\n 'lock_wait',\n 'workspace_read',\n 'engine_scene_load',\n 'engine_execute',\n 'engine_scene_save',\n 'engine_scene_thumbnail',\n 'workspace_write'\n ];\n const OS_NAMES = { darwin: 'macOS', linux: 'Linux', win32: 'Windows' };\n const TAB_LABELS = {\n calls: 'Per-call detail',\n timeline: 'Timeline',\n log: 'Engine log'\n };\n\n const safePhase = (p) => String(p).replace(/[^a-z0-9_]/gi, '_');\n const fmtTs = (n) => new Date(n).toLocaleTimeString();\n const round = (n) => Math.round(n).toLocaleString();\n\n // Humanize a millisecond duration: ms → s → m → h.\n function fmtDur(ms) {\n if (ms == null || !isFinite(ms)) return '—';\n if (ms < 1000) return Math.round(ms) + ' ms';\n const s = ms / 1000;\n if (s < 60) return s.toFixed(1) + ' s';\n const m = s / 60;\n if (m < 60) return m.toFixed(m < 10 ? 1 : 0) + ' m';\n return (m / 60).toFixed(1) + ' h';\n }\n\n function setTabCount(name, n) {\n const btn = document.querySelector('.tab[data-tab=\"' + name + '\"]');\n if (btn) btn.textContent = TAB_LABELS[name] + ' (' + n + ')';\n }\n\n function parseNdjson(text) {\n const events = [];\n for (const line of text.split('\\\\n')) {\n const t = line.trim();\n if (!t) continue;\n try {\n events.push(JSON.parse(t));\n } catch {\n /* skip malformed line */\n }\n }\n return events;\n }\n\n // ---- Minimal in-browser ZIP reader for the bundles \\`diagnose\\` writes.\n // Writer contract (src/diagnostics/bundle.ts): flags=0, method=8 (raw\n // deflate), sizes in each local header — so we walk local headers in order\n // and inflate with DecompressionStream('deflate-raw').\n async function unzip(arrayBuffer) {\n const dv = new DataView(arrayBuffer);\n const bytes = new Uint8Array(arrayBuffer);\n const td = new TextDecoder();\n const out = {};\n let pos = 0;\n while (\n pos + 4 <= dv.byteLength &&\n dv.getUint32(pos, true) === 0x04034b50\n ) {\n const method = dv.getUint16(pos + 8, true);\n const compSize = dv.getUint32(pos + 18, true);\n const nameLen = dv.getUint16(pos + 26, true);\n const extraLen = dv.getUint16(pos + 28, true);\n const nameStart = pos + 30;\n const name = td.decode(\n bytes.subarray(nameStart, nameStart + nameLen)\n );\n const dataStart = nameStart + nameLen + extraLen;\n const comp = bytes.subarray(dataStart, dataStart + compSize);\n let content;\n if (method === 0) {\n content = comp.slice();\n } else {\n const stream = new Response(comp).body.pipeThrough(\n new DecompressionStream('deflate-raw')\n );\n content = new Uint8Array(await new Response(stream).arrayBuffer());\n }\n out[name] = content;\n pos = dataStart + compSize;\n }\n return out;\n }\n\n // ---- Derived summaries -------------------------------------------------\n\n function engineVersion(meta, events) {\n if (meta && meta.cesdkEngine && meta.cesdkEngine !== 'unknown')\n return meta.cesdkEngine;\n const line = events.find(\n (e) =>\n e.kind === 'engine_log' && /\\\\[CreativeEngine\\\\]/.test(e.text || '')\n );\n const m = line && (line.text || '').match(/v?(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)/);\n return m ? m[1] : null;\n }\n\n // Engine init ≈ time from an \\`engine_init_start\\` to the first \"Engine has\n // been initialized\" log within that run's window. The NDJSON accumulates\n // across restarts, so we scan runs newest-first and return the most recent\n // one that actually completed (the newest start may have no captured\n // completion — e.g. the still-booting server).\n function engineInitMs(events) {\n const starts = events\n .filter(\n (e) => e.kind === 'lifecycle' && e.text === 'engine_init_start'\n )\n .map((e) => e.timestamp)\n .sort((a, b) => a - b);\n if (!starts.length) return null;\n const readies = events\n .filter(\n (e) =>\n e.kind === 'engine_log' &&\n /Engine has been initialized/i.test(e.text || '')\n )\n .map((e) => e.timestamp)\n .sort((a, b) => a - b);\n for (let i = starts.length - 1; i >= 0; i--) {\n const start = starts[i];\n const next = i + 1 < starts.length ? starts[i + 1] : Infinity;\n const ready = readies.find((t) => t >= start && t < next);\n if (ready != null) return ready - start;\n }\n return null;\n }\n\n function aggregateTools(events) {\n const map = new Map();\n for (const e of events) {\n if (e.kind !== 'tool_call') continue;\n let a = map.get(e.tool);\n if (!a) {\n a = {\n tool: e.tool,\n count: 0,\n errors: 0,\n sum: 0,\n max: 0,\n phases: {}\n };\n map.set(e.tool, a);\n }\n a.count++;\n a.sum += e.totalMs || 0;\n if ((e.totalMs || 0) > a.max) a.max = e.totalMs || 0;\n if (e.succeeded === false) a.errors++;\n for (const sp of e.spans || [])\n a.phases[sp.phase] = (a.phases[sp.phase] || 0) + (sp.ms || 0);\n }\n return [...map.values()].sort((x, y) => y.sum - x.sum);\n }\n\n // Stacked, normalized phase bar (used by the per-tool table and each\n // per-call summary row).\n function phaseSegs(phaseSums) {\n const names = Object.keys(phaseSums);\n if (!names.length)\n return '<div class=\"phase-seg\" style=\"width:100%;background:#8884\" title=\"no spans\"></div>';\n const ordered = PHASE_ORDER.filter((p) => phaseSums[p] != null).concat(\n names.filter((n) => !PHASE_ORDER.includes(n))\n );\n const total = ordered.reduce((s, n) => s + phaseSums[n], 0) || 1;\n return ordered\n .map((n) => {\n const pct = ((phaseSums[n] / total) * 100).toFixed(2);\n return (\n \\`<div class=\"phase-seg\" style=\"width:\\${pct}%;\\` +\n \\`background:var(--c-\\${safePhase(n)},#888)\" \\` +\n \\`title=\"\\${esc(n)}: \\${round(phaseSums[n])}ms\"></div>\\`\n );\n })\n .join('');\n }\n\n // Per-phase rows (one bar each) for an expanded call.\n function phaseList(spans) {\n if (!spans.length) return '<div class=\"muted\">no spans</div>';\n const total = spans.reduce((s, sp) => s + (sp.ms || 0), 0) || 1;\n return (\n '<div class=\"phase-list\">' +\n spans\n .map((sp) => {\n const pct = (((sp.ms || 0) / total) * 100).toFixed(1);\n const c = 'var(--c-' + safePhase(sp.phase) + ',#888)';\n return (\n '<div class=\"pl-row\">' +\n '<span class=\"pl-name\"><span class=\"sw\" style=\"background:' +\n c +\n '\"></span>' +\n esc(sp.phase) +\n '</span>' +\n '<span class=\"pl-track\"><span class=\"pl-fill\" style=\"width:' +\n pct +\n '%;background:' +\n c +\n '\"></span></span>' +\n '<span class=\"num\">' +\n round(sp.ms || 0) +\n ' ms</span>' +\n '</div>'\n );\n })\n .join('') +\n '</div>'\n );\n }\n\n // ---- Renderers ---------------------------------------------------------\n\n function renderMeta(meta, events) {\n const parts = [];\n if (meta && meta.buildId) parts.push('build ' + esc(meta.buildId));\n if (meta && meta.os)\n parts.push(\n esc(\n (OS_NAMES[meta.os] || meta.os) +\n (meta.arch ? ' ' + meta.arch : '')\n )\n );\n if (meta && meta.node) parts.push('node ' + esc(meta.node));\n const ev = engineVersion(meta, events);\n if (ev) parts.push('engine ' + esc(ev));\n document.getElementById('meta').innerHTML = parts.length\n ? parts.join('<span class=\"sep\">·</span>')\n : '<span class=\"muted\">no meta.json in this source</span>';\n }\n\n function card(label, value, sub) {\n return (\n '<div class=\"card\"><div class=\"label\">' +\n esc(label) +\n '</div><div class=\"value\">' +\n value +\n '</div>' +\n (sub ? '<div class=\"sub\">' + sub + '</div>' : '') +\n '</div>'\n );\n }\n\n function renderCards(events) {\n const calls = events.filter((e) => e.kind === 'tool_call');\n const errors = calls.filter((e) => e.succeeded === false).length;\n const starts = events.filter(\n (e) => e.kind === 'lifecycle' && e.text === 'engine_init_start'\n ).length;\n const stamps = events.map((e) => e.timestamp).filter((n) => n != null);\n const span = stamps.length\n ? Math.max(...stamps) - Math.min(...stamps)\n : null;\n const slowest = calls.reduce(\n (best, e) => ((e.totalMs || 0) > (best?.totalMs || 0) ? e : best),\n null\n );\n const init = engineInitMs(events);\n document.getElementById('cards').innerHTML = [\n card('tool calls', String(calls.length)),\n card(\n 'errors',\n errors ? '<span class=\"err\">' + errors + '</span>' : '0'\n ),\n card('server starts', String(starts)),\n card(\n 'log span',\n esc(fmtDur(span)),\n starts > 1 ? esc(starts + ' runs') : ''\n ),\n card(\n 'engine init',\n esc(fmtDur(init)),\n init != null ? 'last start' : ''\n ),\n card(\n 'slowest call',\n slowest ? esc(fmtDur(slowest.totalMs)) : '—',\n slowest ? esc(slowest.tool) : ''\n )\n ].join('');\n }\n\n function renderLegend() {\n document.getElementById('legend').innerHTML = PHASE_ORDER.map(\n (p) =>\n '<span><span class=\"sw\" style=\"background:var(--c-' +\n p +\n ')\"></span>' +\n esc(p) +\n '</span>'\n ).join('');\n }\n\n function renderToolAgg(events) {\n const rows = aggregateTools(events);\n const el = document.getElementById('tool-agg');\n if (!rows.length) {\n el.innerHTML =\n '<tr><td colspan=\"6\" class=\"muted\">no tool_call events</td></tr>';\n return;\n }\n el.innerHTML = rows\n .map((a) => {\n const errPct = a.count ? Math.round((a.errors / a.count) * 100) : 0;\n return (\n '<tr><td>' +\n esc(a.tool) +\n '</td><td>' +\n a.count +\n '</td><td' +\n (a.errors ? ' class=\"err\"' : '') +\n '>' +\n errPct +\n '%</td><td>' +\n round(a.sum / a.count) +\n '</td><td>' +\n round(a.max) +\n '</td><td class=\"bar-cell\"><div class=\"phase-bar\">' +\n phaseSegs(a.phases) +\n '</div></td></tr>'\n );\n })\n .join('');\n }\n\n // Only these mime types are rendered as inline images; a base64 \\`data\\`\n // is only trusted if it is pure base64 (defends the data: URI we build).\n const RESULT_IMG_MIME = /^image\\\\/(png|webp|jpe?g|gif|avif)$/i;\n const BASE64_RE = /^[A-Za-z0-9+/=\\\\s]+$/;\n\n // Result content parts that carry a real, renderable base64 image.\n function resultImages(result) {\n if (\n !result ||\n typeof result !== 'object' ||\n !Array.isArray(result.content)\n )\n return [];\n return result.content.filter(\n (p) =>\n p &&\n typeof p === 'object' &&\n typeof p.mimeType === 'string' &&\n RESULT_IMG_MIME.test(p.mimeType) &&\n typeof p.data === 'string' &&\n BASE64_RE.test(p.data)\n );\n }\n\n // A copy of the result with kept base64 blobs collapsed to a marker, so\n // the JSON dump stays readable — the image bytes render as <img> below.\n function markerizeResult(result) {\n if (\n !result ||\n typeof result !== 'object' ||\n !Array.isArray(result.content)\n )\n return result;\n const content = result.content.map((p) => {\n if (\n p &&\n typeof p === 'object' &&\n typeof p.data === 'string' &&\n p.data.length > 256 &&\n !/ omitted>$/.test(p.data)\n ) {\n const where = RESULT_IMG_MIME.test(p.mimeType || '')\n ? 'rendered below'\n : 'omitted';\n return { ...p, data: \\`<\\${p.data.length} base64 chars — \\${where}>\\` };\n }\n return p;\n });\n return { ...result, content };\n }\n\n // Chronological accordion: one <details> row per call; expand for the\n // per-phase breakdown, timestamp/revision, error, and raw arguments.\n function callRow(e) {\n const failed = e.succeeded === false;\n const spans = e.spans || [];\n const spanSums = {};\n for (const sp of spans)\n spanSums[sp.phase] = (spanSums[sp.phase] || 0) + (sp.ms || 0);\n const status = failed\n ? '<span class=\"err-badge\">FAIL</span>'\n : '<span class=\"ok-badge\">ok</span>';\n const rev = e.revision\n ? ' · revision <code>' + esc(e.revision) + '</code>'\n : '';\n const errBlock = e.error\n ? '<div class=\"kv-label err\">error</div><pre class=\"args err\">' +\n esc(\n (e.error.message || '') +\n (e.error.stack ? '\\\\n' + e.error.stack : '')\n ) +\n '</pre>'\n : '';\n // \\`code\\` is a long multiline string — noise as escaped JSON. Redact it\n // to a length marker in the arguments dump and render it properly in a\n // dedicated Code section below.\n const hasCode =\n e.args &&\n typeof e.args === 'object' &&\n typeof e.args.code === 'string' &&\n e.args.code.length > 0;\n const argsForJson = hasCode\n ? {\n ...e.args,\n code: '<' + e.args.code.length + ' chars — see Code below>'\n }\n : e.args;\n const argsBlock =\n e.args !== undefined\n ? '<div class=\"kv-label\">arguments</div><pre class=\"args\">' +\n esc(JSON.stringify(argsForJson, null, 2)) +\n '</pre>'\n : '<div class=\"kv-label\">arguments</div><div class=\"muted\">none recorded</div>';\n const codeBlock = hasCode\n ? '<div class=\"kv-label\">code</div><pre class=\"code\">' +\n esc(e.args.code) +\n '</pre>'\n : '';\n const resultImgs = resultImages(e.result)\n .map(\n (p) =>\n '<img class=\"result-img\" alt=\"result image\" src=\"data:' +\n esc(p.mimeType) +\n ';base64,' +\n esc(p.data.replace(/\\\\s+/g, '')) +\n '\">'\n )\n .join('');\n const resultBlock =\n e.result !== undefined\n ? '<div class=\"kv-label\">result</div><pre class=\"args\">' +\n esc(JSON.stringify(markerizeResult(e.result), null, 2)) +\n '</pre>' +\n resultImgs\n : '';\n return (\n '<details class=\"call' +\n (failed ? ' failed' : '') +\n '\">' +\n '<summary class=\"call-row\">' +\n '<span class=\"c-time\">' +\n esc(fmtTs(e.timestamp)) +\n '</span>' +\n '<span class=\"c-tool\">' +\n esc(e.tool) +\n '</span>' +\n '<span class=\"phase-bar\">' +\n phaseSegs(spanSums) +\n '</span>' +\n '<span class=\"num\">' +\n round(e.totalMs || 0) +\n ' ms</span>' +\n '<span class=\"c-status\">' +\n status +\n '<span class=\"chevron\">▸</span></span>' +\n '</summary>' +\n '<div class=\"call-body\">' +\n '<div class=\"kv-label\">phases</div>' +\n phaseList(spans) +\n '<div class=\"meta-kv\">' +\n esc(new Date(e.timestamp).toLocaleString()) +\n rev +\n '</div>' +\n errBlock +\n argsBlock +\n codeBlock +\n resultBlock +\n '</div>' +\n '</details>'\n );\n }\n\n function renderPerCall(events) {\n const calls = events\n .filter((e) => e.kind === 'tool_call')\n .slice()\n .sort((a, b) => a.timestamp - b.timestamp);\n setTabCount('calls', calls.length);\n const el = document.getElementById('tool-calls');\n if (!calls.length) {\n el.innerHTML = '<p class=\"muted\">no tool_call events</p>';\n return;\n }\n el.innerHTML =\n '<div class=\"calls-head\"><span>time</span><span>tool</span>' +\n '<span>phase breakdown</span><span class=\"num\">total</span>' +\n '<span></span></div>' +\n calls.map(callRow).join('');\n }\n\n function renderTimeline(events) {\n const el = document.getElementById('timeline');\n const entries = events.filter(\n (e) => e.kind === 'lifecycle' || (e.kind === 'tool_call' && e.error)\n );\n setTabCount('timeline', entries.length);\n if (!entries.length) {\n el.innerHTML = '<li class=\"muted\">no lifecycle or error events</li>';\n return;\n }\n el.innerHTML = entries\n .map((e) => {\n const ts = fmtTs(e.timestamp);\n const isStart = e.text === 'engine_init_start';\n const text =\n e.text != null\n ? e.text\n : e.kind === 'tool_call'\n ? 'tool error: ' + e.tool\n : e.kind;\n const errLine = e.error\n ? \\`<div class=\"tl-err\">\\${esc(e.error.message)}</div>\\`\n : '';\n return (\n \\`<li><span class=\"tl-ts\">\\${esc(ts)}</span>\\` +\n \\`<span\\${isStart ? ' class=\"tl-start\"' : ''}>\\${esc(text)}</span>\\${errLine}</li>\\`\n );\n })\n .join('');\n }\n\n function renderEngineLog(events) {\n const el = document.getElementById('log-tail');\n const logs = events.filter((e) => e.kind === 'engine_log');\n setTabCount('log', logs.length);\n const tail = logs.slice(-200);\n if (!tail.length) {\n el.innerHTML = '<span class=\"muted\">no engine_log events</span>';\n return;\n }\n el.innerHTML = tail\n .map((e) => {\n const lvl = /^[a-z]+$/i.test(String(e.level)) ? e.level : 'unknown';\n return \\`<div class=\"log-line\"><span class=\"lvl lvl-\\${lvl}\">\\${esc(e.level)}</span><span class=\"log-text\">\\${esc(e.text)}</span></div>\\`;\n })\n .join('');\n el.scrollTop = el.scrollHeight;\n }\n\n function render(events, meta) {\n renderMeta(meta, events);\n renderCards(events);\n renderLegend();\n renderToolAgg(events);\n renderPerCall(events);\n renderTimeline(events);\n renderEngineLog(events);\n document.getElementById('status').textContent =\n 'loaded ' + events.length + ' event(s)';\n document.getElementById('drop-zone').style.display = 'none';\n }\n\n // ---- Tabs --------------------------------------------------------------\n document.querySelectorAll('.tab').forEach((btn) => {\n btn.addEventListener('click', () => {\n const tab = btn.dataset.tab;\n document.querySelectorAll('.tab').forEach((b) => {\n const on = b === btn;\n b.classList.toggle('active', on);\n b.setAttribute('aria-selected', on ? 'true' : 'false');\n });\n document.querySelectorAll('.tabpanel').forEach((p) => {\n p.hidden = p.id !== 'panel-' + tab;\n });\n if (tab === 'log') {\n const el = document.getElementById('log-tail');\n el.scrollTop = el.scrollHeight;\n }\n });\n });\n\n // ---- Loading -----------------------------------------------------------\n\n function safeJson(text) {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n }\n\n async function loadArrayBuffer(buf) {\n const b = new Uint8Array(buf);\n // ZIP magic 'PK\\\\x03\\\\x04' → unzip the bundle; otherwise treat as NDJSON.\n if (b[0] === 0x50 && b[1] === 0x4b) {\n const files = await unzip(buf);\n const td = new TextDecoder();\n const ndName = Object.keys(files).find((n) => /\\\\.ndjson$/i.test(n));\n const text = ndName ? td.decode(files[ndName]) : '';\n const meta = files['meta.json']\n ? safeJson(td.decode(files['meta.json']))\n : null;\n render(parseNdjson(text), meta);\n } else {\n render(parseNdjson(new TextDecoder().decode(b)), null);\n }\n }\n\n function readFileAsBuffer(file) {\n const reader = new FileReader();\n reader.onload = (e) =>\n loadArrayBuffer(e.target.result).catch((err) => {\n document.getElementById('status').textContent =\n 'error: ' + err.message;\n });\n reader.readAsArrayBuffer(file);\n }\n\n document.getElementById('file-input').addEventListener('change', (ev) => {\n const file = ev.target.files[0];\n if (file) readFileAsBuffer(file);\n });\n\n const zone = document.getElementById('drop-zone');\n zone.addEventListener('dragover', (e) => {\n e.preventDefault();\n zone.classList.add('over');\n });\n zone.addEventListener('dragleave', () => zone.classList.remove('over'));\n zone.addEventListener('drop', (e) => {\n e.preventDefault();\n zone.classList.remove('over');\n const file = e.dataTransfer.files[0];\n if (file) readFileAsBuffer(file);\n });\n\n // Served mode (CODE-62): the MCP router injects an endpoint. Auto-load the\n // NDJSON snapshot over HTTP (no browser unzip) and reveal Download-ZIP.\n const streamUrl = window.__DIAGNOSTICS_STREAM__;\n const bundleUrl = window.__DIAGNOSTICS_BUNDLE__;\n const fileParam = new URLSearchParams(location.search).get('file');\n if (streamUrl) {\n const dl = document.getElementById('download-zip');\n if (bundleUrl && dl) {\n dl.href = bundleUrl;\n dl.style.display = '';\n }\n document.getElementById('status').textContent = 'loading…';\n fetch(streamUrl, { cache: 'no-store' })\n .then((r) => {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n })\n .then((text) => render(parseNdjson(text), null))\n .catch((e) => {\n document.getElementById('status').textContent =\n 'error: ' + e.message;\n });\n } else if (fileParam) {\n document.getElementById('status').textContent = 'loading…';\n const isZip = /\\\\.zip$/i.test(fileParam);\n fetch('/_file?path=' + encodeURIComponent(fileParam), {\n cache: 'no-store'\n })\n .then((r) => {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.arrayBuffer();\n })\n .then(async (buf) => {\n if (isZip) return loadArrayBuffer(buf);\n const text = new TextDecoder().decode(new Uint8Array(buf));\n const events = parseNdjson(text);\n const metaPath = fileParam.replace(/[^/\\\\\\\\]*$/, 'meta.json');\n let meta = null;\n if (metaPath !== fileParam) {\n meta = await fetch(\n '/_file?path=' + encodeURIComponent(metaPath),\n {\n cache: 'no-store'\n }\n )\n .then((r) => (r.ok ? r.text() : null))\n .then((t) => (t ? safeJson(t) : null))\n .catch(() => null);\n }\n render(events, meta);\n })\n .catch((e) => {\n document.getElementById('status').textContent =\n 'error: ' + e.message;\n });\n }\n </script>\n </body>\n</html>\n`,\n};\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Env } from '../config.js';
|
|
2
|
+
export interface StatusReport {
|
|
3
|
+
mcp: {
|
|
4
|
+
buildId: string;
|
|
5
|
+
node: string;
|
|
6
|
+
os: string;
|
|
7
|
+
arch: string;
|
|
8
|
+
};
|
|
9
|
+
cesdkEngine: string;
|
|
10
|
+
config: {
|
|
11
|
+
maxStdioEngines?: number;
|
|
12
|
+
maxHttpEngines?: number;
|
|
13
|
+
idleTimeoutSec: number;
|
|
14
|
+
workspaceMode: string;
|
|
15
|
+
workspaceBackend: string;
|
|
16
|
+
logLevel: string;
|
|
17
|
+
gatewayConfigured: boolean;
|
|
18
|
+
publicBaseUrl: string | null;
|
|
19
|
+
engine: string;
|
|
20
|
+
engineResolved: 'native' | 'wasm' | 'wasm-fallback' | null;
|
|
21
|
+
defaultAssetsBaseUrl: string;
|
|
22
|
+
};
|
|
23
|
+
diagnostics: {
|
|
24
|
+
toolEnabled: boolean;
|
|
25
|
+
fileLogging: boolean;
|
|
26
|
+
dir: string;
|
|
27
|
+
streamPresent: boolean;
|
|
28
|
+
lastActivity: number | null;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export declare function buildStatusReport(opts: {
|
|
32
|
+
env: Env;
|
|
33
|
+
transport: 'stdio' | 'http';
|
|
34
|
+
dir: string;
|
|
35
|
+
fileLogging: boolean;
|
|
36
|
+
toolEnabled: boolean;
|
|
37
|
+
}): Promise<StatusReport>;
|
|
38
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/diagnostics/status.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMxC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QACN,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,QAAQ,GAAG,MAAM,GAAG,eAAe,GAAG,IAAI,CAAC;QAC3D,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC;IACF,WAAW,EAAE;QACX,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;QACrB,GAAG,EAAE,MAAM,CAAC;QACZ,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,CAAC;CACH;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,YAAY,CAAC,CAgDxB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// packages/codesign-mcp/src/diagnostics/status.ts
|
|
2
|
+
//
|
|
3
|
+
// The `diagnostics` tool's `status` payload: what's running (versions) + a
|
|
4
|
+
// NON-SECRET config block. Assembled from direct env reads (never loadConfig,
|
|
5
|
+
// which requires a license and would throw), so status never fails on a partial
|
|
6
|
+
// config. Secrets appear only as a presence boolean (gatewayConfigured). See
|
|
7
|
+
// AGENTS.md — new config knobs should be surfaced here.
|
|
8
|
+
import { stat } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { resolveMaxStdioEngines, resolveSecret } from '../config.js';
|
|
11
|
+
import { DEFAULT_ASSETS_BASE_URL } from '../engine/asset-sources.js';
|
|
12
|
+
import { buildBundleMeta } from './bundle.js';
|
|
13
|
+
import { resolvedEngineFlavor } from '../tools/engine.js';
|
|
14
|
+
export async function buildStatusReport(opts) {
|
|
15
|
+
const { env, transport, dir, fileLogging, toolEnabled } = opts;
|
|
16
|
+
const meta = buildBundleMeta(env);
|
|
17
|
+
const idle = Number(env.IMGLY_SESSION_IDLE_TIMEOUT ?? 900);
|
|
18
|
+
const config = {
|
|
19
|
+
idleTimeoutSec: Number.isFinite(idle) ? idle : 900,
|
|
20
|
+
workspaceMode: env.IMGLY_WORKSPACE_MODE ?? 'header',
|
|
21
|
+
workspaceBackend: env.IMGLY_WORKSPACE_BACKEND ?? 'local',
|
|
22
|
+
logLevel: env.IMGLY_LOG_LEVEL ?? 'info',
|
|
23
|
+
gatewayConfigured: Boolean(env.API_KEY) ||
|
|
24
|
+
Boolean(resolveSecret('IMGLY_GATEWAY_API_KEY', env)),
|
|
25
|
+
publicBaseUrl: env.IMGLY_PUBLIC_BASE_URL ?? null,
|
|
26
|
+
engine: env.IMGLY_CESDK_ENGINE || 'default',
|
|
27
|
+
engineResolved: resolvedEngineFlavor() ?? null,
|
|
28
|
+
defaultAssetsBaseUrl: env.IMGLY_DEFAULT_ASSETS_BASE_URL ?? DEFAULT_ASSETS_BASE_URL
|
|
29
|
+
};
|
|
30
|
+
if (transport === 'stdio')
|
|
31
|
+
config.maxStdioEngines = resolveMaxStdioEngines(env);
|
|
32
|
+
else
|
|
33
|
+
config.maxHttpEngines = Number(env.IMGLY_MAX_HTTP_ENGINES ?? 8);
|
|
34
|
+
let streamPresent = false;
|
|
35
|
+
let lastActivity = null;
|
|
36
|
+
const s = await stat(join(dir, 'diagnostics.ndjson')).catch(() => null);
|
|
37
|
+
if (s) {
|
|
38
|
+
streamPresent = true;
|
|
39
|
+
lastActivity = Math.round(s.mtimeMs);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
mcp: {
|
|
43
|
+
buildId: meta.buildId,
|
|
44
|
+
node: meta.node,
|
|
45
|
+
os: meta.os,
|
|
46
|
+
arch: meta.arch
|
|
47
|
+
},
|
|
48
|
+
cesdkEngine: meta.cesdkEngine,
|
|
49
|
+
config,
|
|
50
|
+
diagnostics: { toolEnabled, fileLogging, dir, streamPresent, lastActivity }
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/diagnostics/status.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,2EAA2E;AAC3E,8EAA8E;AAC9E,gFAAgF;AAChF,6EAA6E;AAC7E,wDAAwD;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AA2B1D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAMvC;IACC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC/D,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAM/B,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,IAAI,GAAG,CAAC,CAAC;IAC3D,MAAM,MAAM,GAA2B;QACrC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG;QAClD,aAAa,EAAE,GAAG,CAAC,oBAAoB,IAAI,QAAQ;QACnD,gBAAgB,EAAE,GAAG,CAAC,uBAAuB,IAAI,OAAO;QACxD,QAAQ,EAAE,GAAG,CAAC,eAAe,IAAI,MAAM;QACvC,iBAAiB,EACf,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;YACpB,OAAO,CAAC,aAAa,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;QACtD,aAAa,EAAE,GAAG,CAAC,qBAAqB,IAAI,IAAI;QAChD,MAAM,EAAE,GAAG,CAAC,kBAAkB,IAAI,SAAS;QAC3C,cAAc,EAAE,oBAAoB,EAAE,IAAI,IAAI;QAC9C,oBAAoB,EAClB,GAAG,CAAC,6BAA6B,IAAI,uBAAuB;KAC/D,CAAC;IACF,IAAI,SAAS,KAAK,OAAO;QACvB,MAAM,CAAC,eAAe,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;;QAClD,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,CAAC,CAAC;IAErE,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACxE,IAAI,CAAC,EAAE,CAAC;QACN,aAAa,GAAG,IAAI,CAAC;QACrB,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,OAAO;QACL,GAAG,EAAE;YACH,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB;QACD,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,MAAM;QACN,WAAW,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,EAAE,YAAY,EAAE;KAC5E,CAAC;AACJ,CAAC","sourcesContent":["// packages/codesign-mcp/src/diagnostics/status.ts\n//\n// The `diagnostics` tool's `status` payload: what's running (versions) + a\n// NON-SECRET config block. Assembled from direct env reads (never loadConfig,\n// which requires a license and would throw), so status never fails on a partial\n// config. Secrets appear only as a presence boolean (gatewayConfigured). See\n// AGENTS.md — new config knobs should be surfaced here.\nimport { stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Env } from '../config.js';\nimport { resolveMaxStdioEngines, resolveSecret } from '../config.js';\nimport { DEFAULT_ASSETS_BASE_URL } from '../engine/asset-sources.js';\nimport { buildBundleMeta } from './bundle.js';\nimport { resolvedEngineFlavor } from '../tools/engine.js';\n\nexport interface StatusReport {\n mcp: { buildId: string; node: string; os: string; arch: string };\n cesdkEngine: string;\n config: {\n maxStdioEngines?: number;\n maxHttpEngines?: number;\n idleTimeoutSec: number;\n workspaceMode: string;\n workspaceBackend: string;\n logLevel: string;\n gatewayConfigured: boolean;\n publicBaseUrl: string | null;\n engine: string;\n engineResolved: 'native' | 'wasm' | 'wasm-fallback' | null;\n defaultAssetsBaseUrl: string;\n };\n diagnostics: {\n toolEnabled: boolean;\n fileLogging: boolean;\n dir: string;\n streamPresent: boolean;\n lastActivity: number | null;\n };\n}\n\nexport async function buildStatusReport(opts: {\n env: Env;\n transport: 'stdio' | 'http';\n dir: string;\n fileLogging: boolean;\n toolEnabled: boolean;\n}): Promise<StatusReport> {\n const { env, transport, dir, fileLogging, toolEnabled } = opts;\n const meta = buildBundleMeta(env) as {\n buildId: string;\n os: string;\n arch: string;\n node: string;\n cesdkEngine: string;\n };\n\n const idle = Number(env.IMGLY_SESSION_IDLE_TIMEOUT ?? 900);\n const config: StatusReport['config'] = {\n idleTimeoutSec: Number.isFinite(idle) ? idle : 900,\n workspaceMode: env.IMGLY_WORKSPACE_MODE ?? 'header',\n workspaceBackend: env.IMGLY_WORKSPACE_BACKEND ?? 'local',\n logLevel: env.IMGLY_LOG_LEVEL ?? 'info',\n gatewayConfigured:\n Boolean(env.API_KEY) ||\n Boolean(resolveSecret('IMGLY_GATEWAY_API_KEY', env)),\n publicBaseUrl: env.IMGLY_PUBLIC_BASE_URL ?? null,\n engine: env.IMGLY_CESDK_ENGINE || 'default',\n engineResolved: resolvedEngineFlavor() ?? null,\n defaultAssetsBaseUrl:\n env.IMGLY_DEFAULT_ASSETS_BASE_URL ?? DEFAULT_ASSETS_BASE_URL\n };\n if (transport === 'stdio')\n config.maxStdioEngines = resolveMaxStdioEngines(env);\n else config.maxHttpEngines = Number(env.IMGLY_MAX_HTTP_ENGINES ?? 8);\n\n let streamPresent = false;\n let lastActivity: number | null = null;\n const s = await stat(join(dir, 'diagnostics.ndjson')).catch(() => null);\n if (s) {\n streamPresent = true;\n lastActivity = Math.round(s.mtimeMs);\n }\n\n return {\n mcp: {\n buildId: meta.buildId,\n node: meta.node,\n os: meta.os,\n arch: meta.arch\n },\n cesdkEngine: meta.cesdkEngine,\n config,\n diagnostics: { toolEnabled, fileLogging, dir, streamPresent, lastActivity }\n };\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { DiagnosticEvent } from './events.js';
|
|
2
|
+
/** Parse NDJSON: complete lines only (drop an incomplete trailing line), skip bad JSON. */
|
|
3
|
+
export declare function parseEvents(text: string): DiagnosticEvent[];
|
|
4
|
+
export declare function readActiveSegment(dir: string, opts?: {
|
|
5
|
+
maxEvents?: number;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
events: DiagnosticEvent[];
|
|
8
|
+
truncated: boolean;
|
|
9
|
+
segment: string;
|
|
10
|
+
}>;
|
|
11
|
+
export interface DiagnosticSummary {
|
|
12
|
+
meta: Record<string, unknown>;
|
|
13
|
+
window: {
|
|
14
|
+
segment: string;
|
|
15
|
+
events: number;
|
|
16
|
+
from: number | null;
|
|
17
|
+
to: number | null;
|
|
18
|
+
truncated: boolean;
|
|
19
|
+
};
|
|
20
|
+
counts: {
|
|
21
|
+
toolCalls: number;
|
|
22
|
+
toolErrors: number;
|
|
23
|
+
engineLogErrors: number;
|
|
24
|
+
serverStarts: number;
|
|
25
|
+
};
|
|
26
|
+
recentErrors: Array<{
|
|
27
|
+
source: 'tool' | 'engine' | 'lifecycle';
|
|
28
|
+
tool?: string;
|
|
29
|
+
message: string;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
}>;
|
|
32
|
+
slowestCalls: Array<{
|
|
33
|
+
tool: string;
|
|
34
|
+
totalMs: number;
|
|
35
|
+
timestamp: number;
|
|
36
|
+
}>;
|
|
37
|
+
lastLifecycle: Array<{
|
|
38
|
+
text: string;
|
|
39
|
+
timestamp: number;
|
|
40
|
+
}>;
|
|
41
|
+
note?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function summarize(events: DiagnosticEvent[], meta: Record<string, unknown>, extra: {
|
|
44
|
+
segment: string;
|
|
45
|
+
truncated: boolean;
|
|
46
|
+
fileLogging: boolean;
|
|
47
|
+
}): DiagnosticSummary;
|
|
48
|
+
//# sourceMappingURL=summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../src/diagnostics/summary.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAMnD,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAe3D;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC;IAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAS7E;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,MAAM,EAAE;QACN,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,YAAY,EAAE,KAAK,CAAC;QAClB,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;QACxC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,eAAe,EAAE,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GACnE,iBAAiB,CAsEnB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// packages/codesign-mcp/src/diagnostics/summary.ts
|
|
2
|
+
//
|
|
3
|
+
// Reads the ACTIVE diagnostics segment (bounded ≤ 8 MB by rotation) and folds it
|
|
4
|
+
// into a small, bounded digest the `diagnostics` tool returns for the agent to
|
|
5
|
+
// reason over — counts + recent errors + slowest calls + last lifecycle. Never
|
|
6
|
+
// raw NDJSON. Events on disk are already redacted at write; the tool applies
|
|
7
|
+
// redactValue over the digest as a backstop.
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
const DEFAULT_MAX_EVENTS = 5000;
|
|
11
|
+
const RECENT = 5;
|
|
12
|
+
const SLOWEST = 5;
|
|
13
|
+
/** Parse NDJSON: complete lines only (drop an incomplete trailing line), skip bad JSON. */
|
|
14
|
+
export function parseEvents(text) {
|
|
15
|
+
if (text.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
const endsClean = text.endsWith('\n');
|
|
18
|
+
const lines = text.split('\n');
|
|
19
|
+
if (!endsClean)
|
|
20
|
+
lines.pop();
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
if (line.length === 0)
|
|
24
|
+
continue;
|
|
25
|
+
try {
|
|
26
|
+
out.push(JSON.parse(line));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// skip a corrupt line
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
export async function readActiveSegment(dir, opts = {}) {
|
|
35
|
+
const segment = 'diagnostics.ndjson';
|
|
36
|
+
const max = opts.maxEvents ?? DEFAULT_MAX_EVENTS;
|
|
37
|
+
const raw = await readFile(join(dir, segment), 'utf8').catch(() => '');
|
|
38
|
+
const all = parseEvents(raw);
|
|
39
|
+
if (all.length > max) {
|
|
40
|
+
return { events: all.slice(all.length - max), truncated: true, segment };
|
|
41
|
+
}
|
|
42
|
+
return { events: all, truncated: false, segment };
|
|
43
|
+
}
|
|
44
|
+
export function summarize(events, meta, extra) {
|
|
45
|
+
const counts = {
|
|
46
|
+
toolCalls: 0,
|
|
47
|
+
toolErrors: 0,
|
|
48
|
+
engineLogErrors: 0,
|
|
49
|
+
serverStarts: 0
|
|
50
|
+
};
|
|
51
|
+
const errors = [];
|
|
52
|
+
const calls = [];
|
|
53
|
+
const lifecycle = [];
|
|
54
|
+
let from = null;
|
|
55
|
+
let to = null;
|
|
56
|
+
for (const e of events) {
|
|
57
|
+
from = from === null ? e.timestamp : Math.min(from, e.timestamp);
|
|
58
|
+
to = to === null ? e.timestamp : Math.max(to, e.timestamp);
|
|
59
|
+
if (e.kind === 'tool_call') {
|
|
60
|
+
counts.toolCalls++;
|
|
61
|
+
calls.push({ tool: e.tool, totalMs: e.totalMs, timestamp: e.timestamp });
|
|
62
|
+
if (!e.succeeded) {
|
|
63
|
+
counts.toolErrors++;
|
|
64
|
+
errors.push({
|
|
65
|
+
source: 'tool',
|
|
66
|
+
tool: e.tool,
|
|
67
|
+
message: e.error?.message ?? 'tool call failed',
|
|
68
|
+
timestamp: e.timestamp
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (e.kind === 'engine_log') {
|
|
73
|
+
if (e.level === 'error') {
|
|
74
|
+
counts.engineLogErrors++;
|
|
75
|
+
errors.push({
|
|
76
|
+
source: 'engine',
|
|
77
|
+
message: e.text,
|
|
78
|
+
timestamp: e.timestamp
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// lifecycle
|
|
84
|
+
lifecycle.push({ text: e.text, timestamp: e.timestamp });
|
|
85
|
+
if (e.text === 'viewer_listening')
|
|
86
|
+
counts.serverStarts++;
|
|
87
|
+
if (e.error)
|
|
88
|
+
errors.push({
|
|
89
|
+
source: 'lifecycle',
|
|
90
|
+
message: e.error.message,
|
|
91
|
+
timestamp: e.timestamp
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const summary = {
|
|
96
|
+
meta,
|
|
97
|
+
window: {
|
|
98
|
+
segment: extra.segment,
|
|
99
|
+
events: events.length,
|
|
100
|
+
from,
|
|
101
|
+
to,
|
|
102
|
+
truncated: extra.truncated
|
|
103
|
+
},
|
|
104
|
+
counts,
|
|
105
|
+
recentErrors: errors.slice(-RECENT),
|
|
106
|
+
slowestCalls: [...calls]
|
|
107
|
+
.sort((a, b) => b.totalMs - a.totalMs)
|
|
108
|
+
.slice(0, SLOWEST),
|
|
109
|
+
lastLifecycle: lifecycle.slice(-RECENT)
|
|
110
|
+
};
|
|
111
|
+
if (!extra.fileLogging)
|
|
112
|
+
summary.note =
|
|
113
|
+
'diagnostics file logging is disabled (IMGLY_DIAGNOSTICS_FILE=false)';
|
|
114
|
+
return summary;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/diagnostics/summary.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/diagnostics/summary.ts\n//\n// Reads the ACTIVE diagnostics segment (bounded ≤ 8 MB by rotation) and folds it\n// into a small, bounded digest the `diagnostics` tool returns for the agent to\n// reason over — counts + recent errors + slowest calls + last lifecycle. Never\n// raw NDJSON. Events on disk are already redacted at write; the tool applies\n// redactValue over the digest as a backstop.\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DiagnosticEvent } from './events.js';\n\nconst DEFAULT_MAX_EVENTS = 5000;\nconst RECENT = 5;\nconst SLOWEST = 5;\n\n/** Parse NDJSON: complete lines only (drop an incomplete trailing line), skip bad JSON. */\nexport function parseEvents(text: string): DiagnosticEvent[] {\n if (text.length === 0) return [];\n const endsClean = text.endsWith('\\n');\n const lines = text.split('\\n');\n if (!endsClean) lines.pop();\n const out: DiagnosticEvent[] = [];\n for (const line of lines) {\n if (line.length === 0) continue;\n try {\n out.push(JSON.parse(line) as DiagnosticEvent);\n } catch {\n // skip a corrupt line\n }\n }\n return out;\n}\n\nexport async function readActiveSegment(\n dir: string,\n opts: { maxEvents?: number } = {}\n): Promise<{ events: DiagnosticEvent[]; truncated: boolean; segment: string }> {\n const segment = 'diagnostics.ndjson';\n const max = opts.maxEvents ?? DEFAULT_MAX_EVENTS;\n const raw = await readFile(join(dir, segment), 'utf8').catch(() => '');\n const all = parseEvents(raw);\n if (all.length > max) {\n return { events: all.slice(all.length - max), truncated: true, segment };\n }\n return { events: all, truncated: false, segment };\n}\n\nexport interface DiagnosticSummary {\n meta: Record<string, unknown>;\n window: {\n segment: string;\n events: number;\n from: number | null;\n to: number | null;\n truncated: boolean;\n };\n counts: {\n toolCalls: number;\n toolErrors: number;\n engineLogErrors: number;\n serverStarts: number;\n };\n recentErrors: Array<{\n source: 'tool' | 'engine' | 'lifecycle';\n tool?: string;\n message: string;\n timestamp: number;\n }>;\n slowestCalls: Array<{ tool: string; totalMs: number; timestamp: number }>;\n lastLifecycle: Array<{ text: string; timestamp: number }>;\n note?: string;\n}\n\nexport function summarize(\n events: DiagnosticEvent[],\n meta: Record<string, unknown>,\n extra: { segment: string; truncated: boolean; fileLogging: boolean }\n): DiagnosticSummary {\n const counts = {\n toolCalls: 0,\n toolErrors: 0,\n engineLogErrors: 0,\n serverStarts: 0\n };\n const errors: DiagnosticSummary['recentErrors'] = [];\n const calls: DiagnosticSummary['slowestCalls'] = [];\n const lifecycle: DiagnosticSummary['lastLifecycle'] = [];\n let from: number | null = null;\n let to: number | null = null;\n\n for (const e of events) {\n from = from === null ? e.timestamp : Math.min(from, e.timestamp);\n to = to === null ? e.timestamp : Math.max(to, e.timestamp);\n if (e.kind === 'tool_call') {\n counts.toolCalls++;\n calls.push({ tool: e.tool, totalMs: e.totalMs, timestamp: e.timestamp });\n if (!e.succeeded) {\n counts.toolErrors++;\n errors.push({\n source: 'tool',\n tool: e.tool,\n message: e.error?.message ?? 'tool call failed',\n timestamp: e.timestamp\n });\n }\n } else if (e.kind === 'engine_log') {\n if (e.level === 'error') {\n counts.engineLogErrors++;\n errors.push({\n source: 'engine',\n message: e.text,\n timestamp: e.timestamp\n });\n }\n } else {\n // lifecycle\n lifecycle.push({ text: e.text, timestamp: e.timestamp });\n if (e.text === 'viewer_listening') counts.serverStarts++;\n if (e.error)\n errors.push({\n source: 'lifecycle',\n message: e.error.message,\n timestamp: e.timestamp\n });\n }\n }\n\n const summary: DiagnosticSummary = {\n meta,\n window: {\n segment: extra.segment,\n events: events.length,\n from,\n to,\n truncated: extra.truncated\n },\n counts,\n recentErrors: errors.slice(-RECENT),\n slowestCalls: [...calls]\n .sort((a, b) => b.totalMs - a.totalMs)\n .slice(0, SLOWEST),\n lastLifecycle: lifecycle.slice(-RECENT)\n };\n if (!extra.fileLogging)\n summary.note =\n 'diagnostics file logging is disabled (IMGLY_DIAGNOSTICS_FILE=false)';\n return summary;\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CodesignMcpToolRegistration } from '../server.js';
|
|
2
|
+
import type { Env } from '../config.js';
|
|
3
|
+
export interface DiagnosticsToolOptions {
|
|
4
|
+
env: Env;
|
|
5
|
+
transport: 'stdio' | 'http';
|
|
6
|
+
/** Diagnostics dir (always resolved; may contain no files yet). */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** IMGLY_DIAGNOSTICS_FILE — false ⇒ nothing is being logged. */
|
|
9
|
+
fileLogging: boolean;
|
|
10
|
+
/** Await queued sink writes before a read. No-op when logging is off. */
|
|
11
|
+
flush: () => Promise<void>;
|
|
12
|
+
/** Served viewer URL — present only on stdio once routes are mounted. */
|
|
13
|
+
viewerUrl?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function createDiagnosticsTool(opts: DiagnosticsToolOptions): CodesignMcpToolRegistration;
|
|
16
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/diagnostics/tool.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMxC,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,WAAW,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,sBAAsB,GAC3B,2BAA2B,CAuF7B"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { buildBundleMeta } from './bundle.js';
|
|
2
|
+
import { redactText, redactValue } from './redact.js';
|
|
3
|
+
import { buildStatusReport } from './status.js';
|
|
4
|
+
import { readActiveSegment, summarize } from './summary.js';
|
|
5
|
+
const DESCRIPTION = 'Inspect this codesign MCP server: `status` (default) reports what is running ' +
|
|
6
|
+
'(build id, CE.SDK engine version, non-secret config); `summary` returns a bounded ' +
|
|
7
|
+
'digest of recent activity (counts, recent errors, slowest calls); `open` returns a ' +
|
|
8
|
+
'clickable local viewer URL with a Download-ZIP button to send diagnostics to IMG.LY.';
|
|
9
|
+
export function createDiagnosticsTool(opts) {
|
|
10
|
+
const { env, transport, dir, fileLogging, flush, viewerUrl } = opts;
|
|
11
|
+
const text = (t) => ({
|
|
12
|
+
content: [{ type: 'text', text: t }]
|
|
13
|
+
});
|
|
14
|
+
const json = (v) => text(JSON.stringify(redactValue(v), null, 2));
|
|
15
|
+
return {
|
|
16
|
+
name: 'diagnostics',
|
|
17
|
+
description: DESCRIPTION,
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
action: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
enum: ['status', 'summary', 'open'],
|
|
24
|
+
description: 'status (default) | summary | open'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false
|
|
28
|
+
},
|
|
29
|
+
call: async (rawArgs) => {
|
|
30
|
+
const action = (rawArgs ?? {}).action ?? 'status';
|
|
31
|
+
try {
|
|
32
|
+
if (action === 'status') {
|
|
33
|
+
return json(await buildStatusReport({
|
|
34
|
+
env,
|
|
35
|
+
transport,
|
|
36
|
+
dir,
|
|
37
|
+
fileLogging,
|
|
38
|
+
toolEnabled: true
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
if (action === 'summary') {
|
|
42
|
+
const meta = buildBundleMeta(env);
|
|
43
|
+
if (!fileLogging) {
|
|
44
|
+
return json(summarize([], meta, {
|
|
45
|
+
segment: 'diagnostics.ndjson',
|
|
46
|
+
truncated: false,
|
|
47
|
+
fileLogging: false
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
await flush();
|
|
51
|
+
const { events, truncated, segment } = await readActiveSegment(dir);
|
|
52
|
+
return json(summarize(events, meta, { segment, truncated, fileLogging: true }));
|
|
53
|
+
}
|
|
54
|
+
if (action === 'open') {
|
|
55
|
+
return viewerUrl
|
|
56
|
+
? json({
|
|
57
|
+
url: viewerUrl,
|
|
58
|
+
hint: 'Open this URL to view diagnostics; it has a Download-ZIP button to send to IMG.LY.'
|
|
59
|
+
})
|
|
60
|
+
: json({
|
|
61
|
+
url: null,
|
|
62
|
+
note: 'the diagnostics viewer is not available on this server; use action "status"/"summary", or run `codesign-mcp diagnose` locally.'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
isError: true,
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: 'text',
|
|
70
|
+
text: `diagnostics: unknown action "${action}" (use status|summary|open)`
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
77
|
+
return {
|
|
78
|
+
isError: true,
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: `diagnostics: ${redactText(message)}`
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/diagnostics/tool.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/diagnostics/tool.ts\n//\n// The `diagnostics` MCP tool (CODE-62). Reads the local diagnostics stream —\n// never the engine/store — so it is constructed at the CLI boundary and appended\n// AFTER the design-tool telemetry/capacity maps, which structurally excludes it\n// from wrapping (it must not record its own reads or acquire an engine slot).\nimport type { CodesignMcpToolRegistration } from '../server.js';\nimport type { Env } from '../config.js';\nimport { buildBundleMeta } from './bundle.js';\nimport { redactText, redactValue } from './redact.js';\nimport { buildStatusReport } from './status.js';\nimport { readActiveSegment, summarize } from './summary.js';\n\nexport interface DiagnosticsToolOptions {\n env: Env;\n transport: 'stdio' | 'http';\n /** Diagnostics dir (always resolved; may contain no files yet). */\n dir: string;\n /** IMGLY_DIAGNOSTICS_FILE — false ⇒ nothing is being logged. */\n fileLogging: boolean;\n /** Await queued sink writes before a read. No-op when logging is off. */\n flush: () => Promise<void>;\n /** Served viewer URL — present only on stdio once routes are mounted. */\n viewerUrl?: string;\n}\n\nconst DESCRIPTION =\n 'Inspect this codesign MCP server: `status` (default) reports what is running ' +\n '(build id, CE.SDK engine version, non-secret config); `summary` returns a bounded ' +\n 'digest of recent activity (counts, recent errors, slowest calls); `open` returns a ' +\n 'clickable local viewer URL with a Download-ZIP button to send diagnostics to IMG.LY.';\n\nexport function createDiagnosticsTool(\n opts: DiagnosticsToolOptions\n): CodesignMcpToolRegistration {\n const { env, transport, dir, fileLogging, flush, viewerUrl } = opts;\n const text = (t: string) => ({\n content: [{ type: 'text' as const, text: t }]\n });\n const json = (v: unknown) => text(JSON.stringify(redactValue(v), null, 2));\n\n return {\n name: 'diagnostics',\n description: DESCRIPTION,\n inputSchema: {\n type: 'object' as const,\n properties: {\n action: {\n type: 'string' as const,\n enum: ['status', 'summary', 'open'],\n description: 'status (default) | summary | open'\n }\n },\n additionalProperties: false\n },\n call: async (rawArgs) => {\n const action =\n ((rawArgs ?? {}) as { action?: string }).action ?? 'status';\n try {\n if (action === 'status') {\n return json(\n await buildStatusReport({\n env,\n transport,\n dir,\n fileLogging,\n toolEnabled: true\n })\n );\n }\n if (action === 'summary') {\n const meta = buildBundleMeta(env);\n if (!fileLogging) {\n return json(\n summarize([], meta, {\n segment: 'diagnostics.ndjson',\n truncated: false,\n fileLogging: false\n })\n );\n }\n await flush();\n const { events, truncated, segment } = await readActiveSegment(dir);\n return json(\n summarize(events, meta, { segment, truncated, fileLogging: true })\n );\n }\n if (action === 'open') {\n return viewerUrl\n ? json({\n url: viewerUrl,\n hint: 'Open this URL to view diagnostics; it has a Download-ZIP button to send to IMG.LY.'\n })\n : json({\n url: null,\n note: 'the diagnostics viewer is not available on this server; use action \"status\"/\"summary\", or run `codesign-mcp diagnose` locally.'\n });\n }\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `diagnostics: unknown action \"${action}\" (use status|summary|open)`\n }\n ]\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: `diagnostics: ${redactText(message)}`\n }\n ]\n };\n }\n }\n };\n}\n"]}
|
|
@@ -12,6 +12,14 @@ export interface AssetSourceEngine {
|
|
|
12
12
|
}): Promise<void>;
|
|
13
13
|
}
|
|
14
14
|
export declare function ensureEngineAssetSources(engine: AssetSourceEngine): Promise<void>;
|
|
15
|
+
export declare const DEFAULT_ASSETS_BASE_URL = "https://cdn.img.ly/assets/v3";
|
|
16
|
+
/**
|
|
17
|
+
* Manifest root for CE.SDK's default asset sources — env-overridable for
|
|
18
|
+
* self-hosted deployments. Exported because the viewer router forwards it
|
|
19
|
+
* to the browser editor via /api/config (browser and headless engine must
|
|
20
|
+
* resolve the same manifests).
|
|
21
|
+
*/
|
|
22
|
+
export declare function defaultAssetsBaseUrl(): string;
|
|
15
23
|
/** @internal test-only — resets the module-level in-flight guard. */
|
|
16
24
|
export declare function resetEngineAssetSourcesForTest(): void;
|
|
17
25
|
//# sourceMappingURL=asset-sources.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-sources.d.ts","sourceRoot":"","sources":["../../src/engine/asset-sources.ts"],"names":[],"mappings":"AAaA,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE;QAAE,cAAc,IAAI,MAAM,EAAE,CAAA;KAAE,CAAC;IACtC,sBAAsB,CAAC,IAAI,CAAC,EAAE;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;KAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,mBAAmB,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAeD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CAGf;
|
|
1
|
+
{"version":3,"file":"asset-sources.d.ts","sourceRoot":"","sources":["../../src/engine/asset-sources.ts"],"names":[],"mappings":"AAaA,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE;QAAE,cAAc,IAAI,MAAM,EAAE,CAAA;KAAE,CAAC;IACtC,sBAAsB,CAAC,IAAI,CAAC,EAAE;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;KAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,mBAAmB,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAeD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CAGf;AAQD,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AA6CD,qEAAqE;AACrE,wBAAgB,8BAA8B,IAAI,IAAI,CAErD"}
|
|
@@ -30,8 +30,15 @@ export function ensureEngineAssetSources(engine) {
|
|
|
30
30
|
// The IMG.LY CDN is the fallback; deployments that self-host their asset
|
|
31
31
|
// manifests override via env. These URLs are server configuration only —
|
|
32
32
|
// agents never see them and must always go through engine.asset/asset_search.
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
export const DEFAULT_ASSETS_BASE_URL = 'https://cdn.img.ly/assets/v3';
|
|
34
|
+
/**
|
|
35
|
+
* Manifest root for CE.SDK's default asset sources — env-overridable for
|
|
36
|
+
* self-hosted deployments. Exported because the viewer router forwards it
|
|
37
|
+
* to the browser editor via /api/config (browser and headless engine must
|
|
38
|
+
* resolve the same manifests).
|
|
39
|
+
*/
|
|
40
|
+
export function defaultAssetsBaseUrl() {
|
|
41
|
+
return process.env.IMGLY_DEFAULT_ASSETS_BASE_URL ?? DEFAULT_ASSETS_BASE_URL;
|
|
35
42
|
}
|
|
36
43
|
function demoAssetsBaseUrl() {
|
|
37
44
|
return (process.env.IMGLY_DEMO_ASSETS_BASE_URL ??
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-sources.js","sourceRoot":"","sources":["../../src/engine/asset-sources.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,4EAA4E;AAC5E,wEAAwE;AACxE,qEAAqE;AACrE,sEAAsE;AACtE,6EAA6E;AAC7E,8EAA8E;AAC9E,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,kEAAkE;AAYlE,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;AAC7E,8BAA8B;AAC9B,MAAM,uBAAuB,GAAG,CAAC,aAAa,CAAC,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,eAAe,GAAG,cAAc,CAAC;AAEvC,6EAA6E;AAC7E,yEAAyE;AACzE,IAAI,QAAmC,CAAC;AAExC,MAAM,UAAU,wBAAwB,CACtC,MAAyB;IAEzB,QAAQ,KAAK,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,yEAAyE;AACzE,8EAA8E;AAC9E,
|
|
1
|
+
{"version":3,"file":"asset-sources.js","sourceRoot":"","sources":["../../src/engine/asset-sources.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,4EAA4E;AAC5E,wEAAwE;AACxE,qEAAqE;AACrE,sEAAsE;AACtE,6EAA6E;AAC7E,8EAA8E;AAC9E,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,kEAAkE;AAYlE,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;AAC7E,8BAA8B;AAC9B,MAAM,uBAAuB,GAAG,CAAC,aAAa,CAAC,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,eAAe,GAAG,cAAc,CAAC;AAEvC,6EAA6E;AAC7E,yEAAyE;AACzE,IAAI,QAAmC,CAAC;AAExC,MAAM,UAAU,wBAAwB,CACtC,MAAyB;IAEzB,QAAQ,KAAK,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,yEAAyE;AACzE,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,uBAAuB,CAAC;AAC9E,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,0BAA0B;QACtC,mCAAmC,CACpC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,GAAY;IACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iBAAiB,KAAK,kDACpB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,IAAI,CACL,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,MAAyB;IACnD,IAAI,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;QAAE,OAAO;IAEpE,wEAAwE;IACxE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,sBAAsB,CAAC;YAClC,OAAO,EAAE,oBAAoB,EAAE;YAC/B,qBAAqB,EAAE,uBAAuB;SAC/C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,gBAAgB,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;QAChD,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,gBAAgB,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;QAC7C,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,wEAAwE;IACxE,wCAAwC;IACxC,IAAI,SAAS,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QAC1E,QAAQ,GAAG,SAAS,CAAC;IACvB,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,8BAA8B;IAC5C,QAAQ,GAAG,SAAS,CAAC;AACvB,CAAC","sourcesContent":["// packages/codesign-mcp/src/engine/asset-sources.ts\n//\n// Registers CE.SDK's default + demo asset sources on the headless engine so\n// `asset_search` has content to discover. Manifest roots default to the\n// IMG.LY CDN and are overridable via IMGLY_DEFAULT_ASSETS_BASE_URL /\n// IMGLY_DEMO_ASSETS_BASE_URL for self-hosted deployments. Idempotent:\n// skips when the demo image source is already present — ensureHeadlessEngine\n// is a process singleton, and the HTTP transport calls this once per session.\n// Best-effort: a CDN failure logs to stderr and leaves the engine usable.\n// Note: retries after total failure may re-add assets to already-populated\n// default sources (CE.SDK guards duplicate source ids, not duplicate assets);\n// partial failures after ly.img.image has landed are NOT retried.\n\n/** The slice of CreativeEngine this module needs — keeps it testable. */\nexport interface AssetSourceEngine {\n asset: { findAllSources(): string[] };\n addDefaultAssetSources(opts?: {\n baseURL?: string;\n excludeAssetSourceIds?: string[];\n }): Promise<void>;\n addDemoAssetSources(opts?: { baseURL?: string }): Promise<void>;\n}\n\n// `ly.img.text`'s manifest is not published on the CDN root (404), and the\n// SDK loads all manifests with Promise.all — one 404 rejects the whole call\n// and leaves WHICH sources registered up to fetch timing. Excluding it makes\n// registration deterministic.\nconst MISSING_DEFAULT_SOURCES = ['ly.img.text'];\n\n/** Registered by addDemoAssetSources — its presence means we already ran. */\nconst SENTINEL_SOURCE = 'ly.img.image';\n\n// Concurrent callers (HTTP createSession) share one in-flight registration —\n// the engine is a process singleton, so the guard lives at module level.\nlet inFlight: Promise<void> | undefined;\n\nexport function ensureEngineAssetSources(\n engine: AssetSourceEngine\n): Promise<void> {\n inFlight ??= registerOnce(engine);\n return inFlight;\n}\n\n// Manifest roots for the JSON-defined asset sources. The @cesdk/node npm\n// package only bundles the WASM core (no manifests), so without an explicit\n// baseURL both calls resolve against the engine's file:// baseURL and fail.\n// The IMG.LY CDN is the fallback; deployments that self-host their asset\n// manifests override via env. These URLs are server configuration only —\n// agents never see them and must always go through engine.asset/asset_search.\nexport const DEFAULT_ASSETS_BASE_URL = 'https://cdn.img.ly/assets/v3';\n\n/**\n * Manifest root for CE.SDK's default asset sources — env-overridable for\n * self-hosted deployments. Exported because the viewer router forwards it\n * to the browser editor via /api/config (browser and headless engine must\n * resolve the same manifests).\n */\nexport function defaultAssetsBaseUrl(): string {\n return process.env.IMGLY_DEFAULT_ASSETS_BASE_URL ?? DEFAULT_ASSETS_BASE_URL;\n}\n\nfunction demoAssetsBaseUrl(): string {\n return (\n process.env.IMGLY_DEMO_ASSETS_BASE_URL ??\n 'https://cdn.img.ly/assets/demo/v3'\n );\n}\n\nfunction logSourceFailure(label: string, err: unknown): void {\n process.stderr.write(\n `codesign-mcp: ${label} failed (asset_search will see fewer sources): ${\n err instanceof Error ? err.message : String(err)\n }\\n`\n );\n}\n\nasync function registerOnce(engine: AssetSourceEngine): Promise<void> {\n if (engine.asset.findAllSources().includes(SENTINEL_SOURCE)) return;\n\n // Each call is independent so a failure in one doesn't block the other.\n let anyFailed = false;\n try {\n await engine.addDefaultAssetSources({\n baseURL: defaultAssetsBaseUrl(),\n excludeAssetSourceIds: MISSING_DEFAULT_SOURCES\n });\n } catch (err) {\n logSourceFailure('addDefaultAssetSources', err);\n anyFailed = true;\n }\n try {\n await engine.addDemoAssetSources({ baseURL: demoAssetsBaseUrl() });\n } catch (err) {\n logSourceFailure('addDemoAssetSources', err);\n anyFailed = true;\n }\n\n // If the sentinel source still isn't present after both attempts, clear\n // the guard so the next call can retry.\n if (anyFailed && !engine.asset.findAllSources().includes(SENTINEL_SOURCE)) {\n inFlight = undefined;\n }\n}\n\n/** @internal test-only — resets the module-level in-flight guard. */\nexport function resetEngineAssetSourcesForTest(): void {\n inFlight = undefined;\n}\n"]}
|