sunpeak 0.20.22 → 0.20.28
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/README.md +29 -4
- package/bin/commands/inspect.mjs +18 -0
- package/dist/chatgpt/index.cjs +1 -1
- package/dist/chatgpt/index.js +1 -1
- package/dist/claude/index.cjs +1 -1
- package/dist/claude/index.js +1 -1
- package/dist/embed.css +2 -0
- package/dist/host/chatgpt/index.cjs +1 -1
- package/dist/host/chatgpt/index.js +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/inspector/app-flatten.d.ts +7 -0
- package/dist/inspector/app-types.d.ts +83 -0
- package/dist/inspector/iframe-resource.d.ts +19 -1
- package/dist/inspector/index.cjs +3 -1
- package/dist/inspector/index.cjs.map +1 -1
- package/dist/inspector/index.d.ts +2 -0
- package/dist/inspector/index.js +3 -2
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/inline-helper-script.d.ts +22 -0
- package/dist/inspector/inspector.d.ts +14 -1
- package/dist/inspector/simple-sidebar.d.ts +13 -1
- package/dist/inspector/use-inspector-state.d.ts +1 -0
- package/dist/{inspector-DtEighD9.cjs → inspector-DSX76Z-W.cjs} +340 -28
- package/dist/inspector-DSX76Z-W.cjs.map +1 -0
- package/dist/{inspector-CJNvLoHo.js → inspector-tIphAHtK.js} +336 -30
- package/dist/inspector-tIphAHtK.js.map +1 -0
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/sandbox-proxy.html +173 -0
- package/dist/style.css +9 -1
- package/dist/types/simulation.d.ts +3 -8
- package/dist/{use-app-cSBm5Pjl.js → use-app-C2pGHlnF.js} +2 -2
- package/dist/{use-app-cSBm5Pjl.js.map → use-app-C2pGHlnF.js.map} +1 -1
- package/dist/{use-app-CxtSfkSF.cjs → use-app-DIWh7-3f.cjs} +2 -2
- package/dist/{use-app-CxtSfkSF.cjs.map → use-app-DIWh7-3f.cjs.map} +1 -1
- package/package.json +14 -11
- package/template/dist/albums/albums.html +1 -1
- package/template/dist/albums/albums.json +1 -1
- package/template/dist/carousel/carousel.html +1 -1
- package/template/dist/carousel/carousel.json +1 -1
- package/template/dist/map/map.html +1 -1
- package/template/dist/map/map.json +1 -1
- package/template/dist/review/review.html +1 -1
- package/template/dist/review/review.json +1 -1
- package/template/node_modules/.bin/playwright +2 -2
- package/template/node_modules/.bin/vite +2 -2
- package/template/node_modules/.bin/vitest +2 -2
- package/template/node_modules/.vite/deps/_metadata.json +4 -4
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/_metadata.json +24 -24
- package/template/node_modules/.vite-mcp/deps/vitest.js +8 -8
- package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -1
- package/template/package.json +4 -4
- package/template/tests/e2e/visual.spec.ts-snapshots/albums-page-light-claude-linux.png +0 -0
- package/dist/inspector-CJNvLoHo.js.map +0 -1
- package/dist/inspector-DtEighD9.cjs.map +0 -1
|
@@ -4224,7 +4224,7 @@ registerHostShell({
|
|
|
4224
4224
|
}`
|
|
4225
4225
|
});
|
|
4226
4226
|
//#endregion
|
|
4227
|
-
//#region ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.
|
|
4227
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.7.2_@modelcontextprotocol+sdk@1.29.0_zod@4.4.3__react-_f5b843da9146ebea748e10ad8dfce46a/node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js
|
|
4228
4228
|
((X) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(X, { get: (Y, Z) => (typeof require < "u" ? require : Y)[Z] }) : X)(function(X) {
|
|
4229
4229
|
if (typeof require < "u") return require.apply(this, arguments);
|
|
4230
4230
|
throw Error("Dynamic require of \"" + X + "\" is not supported");
|
|
@@ -5546,6 +5546,145 @@ iframe { border: none; width: 100%; height: 100%; display: block; }
|
|
|
5546
5546
|
</body>
|
|
5547
5547
|
</html>`;
|
|
5548
5548
|
}
|
|
5549
|
+
var SUNPEAK_INLINE_HELPER_SCRIPT = `
|
|
5550
|
+
(function() {
|
|
5551
|
+
if (window.sunpeak) return; // already installed (e.g. parent hot-reload)
|
|
5552
|
+
// Opt-out: resource HTML using the real MCP Apps SDK can suppress the
|
|
5553
|
+
// helper to avoid sending two ui/initialize requests. Manual lowercase
|
|
5554
|
+
// comparison so content="off" / "OFF" / "Off" all match (the Selectors
|
|
5555
|
+
// L4 case-insensitive flag isn't supported in all test environments).
|
|
5556
|
+
try {
|
|
5557
|
+
var metas = document.querySelectorAll('meta[name="sunpeak-helper"]');
|
|
5558
|
+
for (var mi = 0; mi < metas.length; mi++) {
|
|
5559
|
+
var contentAttr = metas[mi].getAttribute('content');
|
|
5560
|
+
if (contentAttr && contentAttr.toLowerCase() === 'off') return;
|
|
5561
|
+
}
|
|
5562
|
+
} catch (e) { /* document not ready or querySelector missing */ }
|
|
5563
|
+
|
|
5564
|
+
var listeners = {
|
|
5565
|
+
toolInput: [],
|
|
5566
|
+
toolInputPartial: [],
|
|
5567
|
+
toolResult: [],
|
|
5568
|
+
toolCancelled: [],
|
|
5569
|
+
hostContext: []
|
|
5570
|
+
};
|
|
5571
|
+
var last = {
|
|
5572
|
+
toolInput: undefined,
|
|
5573
|
+
toolInputPartial: undefined,
|
|
5574
|
+
toolResult: undefined,
|
|
5575
|
+
toolCancelled: undefined,
|
|
5576
|
+
hostContext: undefined
|
|
5577
|
+
};
|
|
5578
|
+
function dispatch(channel, value) {
|
|
5579
|
+
last[channel] = value;
|
|
5580
|
+
// Iterate over a SNAPSHOT — callbacks that unsubscribe themselves
|
|
5581
|
+
// mutate the live list, which would skip subsequent callbacks under a
|
|
5582
|
+
// for-by-index loop on the original array.
|
|
5583
|
+
var list = listeners[channel].slice();
|
|
5584
|
+
for (var i = 0; i < list.length; i++) {
|
|
5585
|
+
try { list[i](value); } catch (e) { console.error('[sunpeak] callback error:', e); }
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
// Map channel names to the public method name for error messages.
|
|
5589
|
+
var channelMethodName = {
|
|
5590
|
+
toolInput: 'onToolInput',
|
|
5591
|
+
toolInputPartial: 'onToolInputPartial',
|
|
5592
|
+
toolResult: 'onToolResult',
|
|
5593
|
+
toolCancelled: 'onToolCancelled',
|
|
5594
|
+
hostContext: 'onHostContextChange'
|
|
5595
|
+
};
|
|
5596
|
+
function subscribe(channel) {
|
|
5597
|
+
return function(cb) {
|
|
5598
|
+
if (typeof cb !== 'function') {
|
|
5599
|
+
throw new TypeError('window.sunpeak.' + channelMethodName[channel] + ' expects a function');
|
|
5600
|
+
}
|
|
5601
|
+
listeners[channel].push(cb);
|
|
5602
|
+
if (last[channel] !== undefined) {
|
|
5603
|
+
try { cb(last[channel]); } catch (e) { console.error('[sunpeak] callback error:', e); }
|
|
5604
|
+
}
|
|
5605
|
+
return function unsubscribe() {
|
|
5606
|
+
var idx = listeners[channel].indexOf(cb);
|
|
5607
|
+
if (idx >= 0) listeners[channel].splice(idx, 1);
|
|
5608
|
+
};
|
|
5609
|
+
};
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
window.sunpeak = {
|
|
5613
|
+
onToolInput: subscribe('toolInput'),
|
|
5614
|
+
onToolInputPartial: subscribe('toolInputPartial'),
|
|
5615
|
+
onToolResult: subscribe('toolResult'),
|
|
5616
|
+
onToolCancelled: subscribe('toolCancelled'),
|
|
5617
|
+
onHostContextChange: subscribe('hostContext')
|
|
5618
|
+
};
|
|
5619
|
+
|
|
5620
|
+
var nextId = 1;
|
|
5621
|
+
var pending = {};
|
|
5622
|
+
|
|
5623
|
+
function sendRequest(method, params) {
|
|
5624
|
+
var id = nextId++;
|
|
5625
|
+
return new Promise(function(resolve, reject) {
|
|
5626
|
+
pending[id] = { resolve: resolve, reject: reject };
|
|
5627
|
+
try {
|
|
5628
|
+
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params }, '*');
|
|
5629
|
+
} catch (e) {
|
|
5630
|
+
delete pending[id];
|
|
5631
|
+
reject(e);
|
|
5632
|
+
}
|
|
5633
|
+
});
|
|
5634
|
+
}
|
|
5635
|
+
function sendNotification(method, params) {
|
|
5636
|
+
try {
|
|
5637
|
+
window.parent.postMessage({ jsonrpc: '2.0', method: method, params: params || {} }, '*');
|
|
5638
|
+
} catch (e) { /* parent detached */ }
|
|
5639
|
+
}
|
|
5640
|
+
|
|
5641
|
+
window.addEventListener('message', function(ev) {
|
|
5642
|
+
// Only trust messages from the actual parent (the sandbox proxy).
|
|
5643
|
+
// Without this guard, a sibling iframe in the same browsing context or
|
|
5644
|
+
// a browser extension content script could forge JSON-RPC notifications
|
|
5645
|
+
// and drive the embedder's onToolResult/onToolInput callbacks with
|
|
5646
|
+
// attacker-controlled data.
|
|
5647
|
+
if (ev.source !== window.parent) return;
|
|
5648
|
+
var msg = ev.data;
|
|
5649
|
+
if (!msg || typeof msg !== 'object' || msg.jsonrpc !== '2.0') return;
|
|
5650
|
+
if (typeof msg.id !== 'undefined' && pending[msg.id]) {
|
|
5651
|
+
var p = pending[msg.id];
|
|
5652
|
+
delete pending[msg.id];
|
|
5653
|
+
if (msg.error) p.reject(new Error((msg.error && msg.error.message) || 'request error'));
|
|
5654
|
+
else p.resolve(msg.result);
|
|
5655
|
+
return;
|
|
5656
|
+
}
|
|
5657
|
+
if (msg.method === 'ui/notifications/tool-input') {
|
|
5658
|
+
dispatch('toolInput', (msg.params && msg.params.arguments) || {});
|
|
5659
|
+
} else if (msg.method === 'ui/notifications/tool-input-partial') {
|
|
5660
|
+
dispatch('toolInputPartial', (msg.params && msg.params.arguments) || {});
|
|
5661
|
+
} else if (msg.method === 'ui/notifications/tool-result') {
|
|
5662
|
+
// tool-result params are the CallToolResult directly per the spec.
|
|
5663
|
+
dispatch('toolResult', msg.params || {});
|
|
5664
|
+
} else if (msg.method === 'ui/notifications/tool-cancelled') {
|
|
5665
|
+
// Clear cached input / partial / result so a late onToolResult
|
|
5666
|
+
// subscriber doesn't get the cancelled-then-stale value replayed.
|
|
5667
|
+
// hostContext stays — the environment hasn't changed.
|
|
5668
|
+
last.toolInput = undefined;
|
|
5669
|
+
last.toolInputPartial = undefined;
|
|
5670
|
+
last.toolResult = undefined;
|
|
5671
|
+
dispatch('toolCancelled', msg.params || {});
|
|
5672
|
+
} else if (msg.method === 'ui/notifications/host-context-changed') {
|
|
5673
|
+
dispatch('hostContext', msg.params || {});
|
|
5674
|
+
}
|
|
5675
|
+
});
|
|
5676
|
+
|
|
5677
|
+
sendRequest('ui/initialize', {
|
|
5678
|
+
appInfo: { name: 'sunpeak-inline-helper', version: '1.0.0' },
|
|
5679
|
+
appCapabilities: {},
|
|
5680
|
+
protocolVersion: '2026-01-26'
|
|
5681
|
+
}).then(function() {
|
|
5682
|
+
sendNotification('ui/notifications/initialized');
|
|
5683
|
+
}).catch(function(err) {
|
|
5684
|
+
console.warn('[sunpeak] ui/initialize failed:', err && err.message);
|
|
5685
|
+
});
|
|
5686
|
+
})();
|
|
5687
|
+
`.trim();
|
|
5549
5688
|
//#endregion
|
|
5550
5689
|
//#region src/inspector/iframe-resource.tsx
|
|
5551
5690
|
/**
|
|
@@ -5684,6 +5823,23 @@ requestAnimationFrame(function(){
|
|
|
5684
5823
|
e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");
|
|
5685
5824
|
});}});`;
|
|
5686
5825
|
/**
|
|
5826
|
+
* Inject the paint-fence responder and (optionally) a platform runtime script
|
|
5827
|
+
* into a user-provided HTML document. Used for the `html` prop mode where the
|
|
5828
|
+
* embedder hands us a complete document from `mcpClient.readResource(...)` and
|
|
5829
|
+
* we need to splice in the same infrastructure that the `scriptSrc` wrapper
|
|
5830
|
+
* provides. The injection is placed before `</head>` when present, falling
|
|
5831
|
+
* back to the start of `<body>` or the document start.
|
|
5832
|
+
*/
|
|
5833
|
+
function injectInfraScripts(html, platformScript) {
|
|
5834
|
+
const fenceTag = `<script data-sunpeak-fence>${PAINT_FENCE_SCRIPT}<\/script>`;
|
|
5835
|
+
const injection = `${platformScript ? `<script>${platformScript}<\/script>` : ""}${fenceTag}${`<script data-sunpeak-helper>${SUNPEAK_INLINE_HELPER_SCRIPT}<\/script>`}`;
|
|
5836
|
+
const headMatch = html.match(/<\/head\s*>/i);
|
|
5837
|
+
if (headMatch) return html.replace(headMatch[0], `${injection}${headMatch[0]}`);
|
|
5838
|
+
const bodyMatch = html.match(/<body([^>]*)>/i);
|
|
5839
|
+
if (bodyMatch) return html.replace(bodyMatch[0], `${bodyMatch[0]}${injection}`);
|
|
5840
|
+
return injection + html;
|
|
5841
|
+
}
|
|
5842
|
+
/**
|
|
5687
5843
|
* Generates HTML wrapper for a script URL.
|
|
5688
5844
|
* The MCP Apps SDK in the loaded script handles communication via PostMessageTransport.
|
|
5689
5845
|
*/
|
|
@@ -5747,7 +5903,7 @@ function buildIframeAllow(permissions) {
|
|
|
5747
5903
|
* connects via PostMessageTransport to window.parent. The parent side uses
|
|
5748
5904
|
* McpAppHost (wrapping AppBridge) to communicate.
|
|
5749
5905
|
*/
|
|
5750
|
-
function IframeResource({ src, scriptSrc, hostContext, toolInput, toolInputPartial, toolResult, hostOptions, csp, permissions, prefersBorder, className, style, onDisplayModeReady, debugInjectState, injectOpenAIRuntime, sandboxUrl }) {
|
|
5906
|
+
function IframeResource({ src, scriptSrc, html, hostContext, toolInput, toolInputPartial, toolResult, hostOptions, csp, permissions, prefersBorder, className, style, onDisplayModeReady, debugInjectState, injectOpenAIRuntime, sandboxUrl }) {
|
|
5751
5907
|
const iframeRef = (0, react.useRef)(null);
|
|
5752
5908
|
const hostRef = (0, react.useRef)(null);
|
|
5753
5909
|
const resourceUrl = src ?? scriptSrc;
|
|
@@ -5755,6 +5911,8 @@ function IframeResource({ src, scriptSrc, hostContext, toolInput, toolInputParti
|
|
|
5755
5911
|
srcRef.current = src;
|
|
5756
5912
|
const scriptSrcRef = (0, react.useRef)(scriptSrc);
|
|
5757
5913
|
scriptSrcRef.current = scriptSrc;
|
|
5914
|
+
const htmlRef = (0, react.useRef)(html);
|
|
5915
|
+
htmlRef.current = html;
|
|
5758
5916
|
const cspRef = (0, react.useRef)(csp);
|
|
5759
5917
|
cspRef.current = csp;
|
|
5760
5918
|
const hostContextRef = (0, react.useRef)(hostContext);
|
|
@@ -5804,9 +5962,20 @@ function IframeResource({ src, scriptSrc, hostContext, toolInput, toolInputParti
|
|
|
5804
5962
|
onSandboxReady: () => {
|
|
5805
5963
|
const currentSrc = srcRef.current;
|
|
5806
5964
|
const currentScriptSrc = scriptSrcRef.current;
|
|
5965
|
+
const currentHtml = htmlRef.current;
|
|
5807
5966
|
const currentHost = hostRef.current;
|
|
5808
5967
|
if (!currentHost) return;
|
|
5809
|
-
if (
|
|
5968
|
+
if (currentHtml) {
|
|
5969
|
+
const finalHtml = injectInfraScripts(currentHtml, injectOpenAIRuntimeRef.current ? MOCK_OPENAI_RUNTIME_SCRIPT : void 0);
|
|
5970
|
+
currentHost.sendSandboxResourceReady({
|
|
5971
|
+
html: finalHtml,
|
|
5972
|
+
sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
|
|
5973
|
+
});
|
|
5974
|
+
if (iframeRef.current) {
|
|
5975
|
+
iframeRef.current.style.opacity = "1";
|
|
5976
|
+
iframeRef.current.style.transition = "opacity 100ms";
|
|
5977
|
+
}
|
|
5978
|
+
} else if (currentScriptSrc) {
|
|
5810
5979
|
const absoluteScriptSrc = currentScriptSrc.startsWith("/") ? `${window.location.origin}${currentScriptSrc}` : currentScriptSrc;
|
|
5811
5980
|
const cspPolicy = generateCSP(cspRef.current, absoluteScriptSrc);
|
|
5812
5981
|
const appHtml = generateScriptHtml(absoluteScriptSrc, hostContextRef.current?.theme ?? "dark", cspPolicy, injectOpenAIRuntimeRef.current ? MOCK_OPENAI_RUNTIME_SCRIPT : void 0);
|
|
@@ -5877,7 +6046,17 @@ function IframeResource({ src, scriptSrc, hostContext, toolInput, toolInputParti
|
|
|
5877
6046
|
const borderStyle = prefersBorder ? { border: "1px solid var(--color-border-primary, #e5e7eb)" } : { border: "none" };
|
|
5878
6047
|
const sandboxSrc = (0, react.useMemo)(() => {
|
|
5879
6048
|
if (!sandboxUrl) return void 0;
|
|
5880
|
-
|
|
6049
|
+
let url;
|
|
6050
|
+
try {
|
|
6051
|
+
const parsed = new URL(sandboxUrl);
|
|
6052
|
+
url = parsed.pathname && parsed.pathname !== "/" ? parsed : new URL("/proxy", sandboxUrl);
|
|
6053
|
+
} catch {
|
|
6054
|
+
return;
|
|
6055
|
+
}
|
|
6056
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
6057
|
+
console.warn("[IframeResource] Ignoring non-http(s) sandboxUrl:", sandboxUrl);
|
|
6058
|
+
return;
|
|
6059
|
+
}
|
|
5881
6060
|
if (injectOpenAIRuntime) url.searchParams.set("platform", "chatgpt");
|
|
5882
6061
|
return url.toString();
|
|
5883
6062
|
}, [sandboxUrl, injectOpenAIRuntime]);
|
|
@@ -6317,6 +6496,7 @@ function useInspectorState({ simulations, defaultHost = "chatgpt" }) {
|
|
|
6317
6496
|
}, [toolResult, modelContext]);
|
|
6318
6497
|
const resourceUrl = selectedSim?.resourceUrl;
|
|
6319
6498
|
const resourceScript = selectedSim?.resourceScript;
|
|
6499
|
+
const resourceHtml = selectedSim?.resourceHtml;
|
|
6320
6500
|
const csp = selectedSim?.resource ? extractResourceCSP(selectedSim.resource) : void 0;
|
|
6321
6501
|
const resourceMeta = (selectedSim?.resource?._meta)?.ui;
|
|
6322
6502
|
return {
|
|
@@ -6383,6 +6563,7 @@ function useInspectorState({ simulations, defaultHost = "chatgpt" }) {
|
|
|
6383
6563
|
handleUpdateModelContext,
|
|
6384
6564
|
resourceUrl,
|
|
6385
6565
|
resourceScript,
|
|
6566
|
+
resourceHtml,
|
|
6386
6567
|
csp,
|
|
6387
6568
|
permissions: resourceMeta?.permissions,
|
|
6388
6569
|
prefersBorder: resourceMeta?.prefersBorder ?? false,
|
|
@@ -6485,7 +6666,14 @@ function useMcpConnection(initialServerUrl) {
|
|
|
6485
6666
|
//#endregion
|
|
6486
6667
|
//#region src/inspector/theme-provider.tsx
|
|
6487
6668
|
var ThemeProviderContext = react.createContext(void 0);
|
|
6488
|
-
/**
|
|
6669
|
+
/**
|
|
6670
|
+
* Default theme applier: sets `data-theme` on `document.documentElement`.
|
|
6671
|
+
* Kept for callers using `ThemeProvider` outside the bundled `<Inspector />`
|
|
6672
|
+
* (e.g. custom-inspector builds composed from this package's primitives).
|
|
6673
|
+
* The bundled Inspector overrides this with a no-op applier and applies
|
|
6674
|
+
* theme to its own root element, so embedding it inside a host React app
|
|
6675
|
+
* leaves the host's document attributes untouched.
|
|
6676
|
+
*/
|
|
6489
6677
|
function defaultApplyTheme(theme) {
|
|
6490
6678
|
if (typeof document !== "undefined") document.documentElement.setAttribute("data-theme", theme);
|
|
6491
6679
|
}
|
|
@@ -6528,7 +6716,7 @@ function ChevronRightIcon() {
|
|
|
6528
6716
|
})
|
|
6529
6717
|
});
|
|
6530
6718
|
}
|
|
6531
|
-
function SimpleSidebar({ children, controls, headerRight }) {
|
|
6719
|
+
function SimpleSidebar({ children, controls, headerRight, rootRef, fillParent = false }) {
|
|
6532
6720
|
const [isDrawerOpen, setIsDrawerOpen] = react.useState(false);
|
|
6533
6721
|
const [sidebarWidth, setSidebarWidth] = react.useState(DEFAULT_SIDEBAR_WIDTH);
|
|
6534
6722
|
const [isResizing, setIsResizing] = react.useState(false);
|
|
@@ -6553,7 +6741,8 @@ function SimpleSidebar({ children, controls, headerRight }) {
|
|
|
6553
6741
|
};
|
|
6554
6742
|
}, [isResizing]);
|
|
6555
6743
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6556
|
-
|
|
6744
|
+
ref: rootRef,
|
|
6745
|
+
className: `sunpeak-inspector-root flex ${fillParent ? "h-full w-full" : "h-screen w-full"} overflow-hidden relative`,
|
|
6557
6746
|
children: [
|
|
6558
6747
|
isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "fixed inset-0 z-50 cursor-col-resize" }),
|
|
6559
6748
|
isDrawerOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -6883,13 +7072,85 @@ function resolveServerToolResult(mock, args) {
|
|
|
6883
7072
|
for (const entry of mock) if (Object.entries(entry.when).every(([key, value]) => args != null && args[key] === value)) return entry.result;
|
|
6884
7073
|
}
|
|
6885
7074
|
//#endregion
|
|
7075
|
+
//#region src/inspector/app-flatten.ts
|
|
7076
|
+
/** Pull the output-template URI off a tool's _meta, if present. */
|
|
7077
|
+
function getOutputTemplate(toolMeta) {
|
|
7078
|
+
if (!toolMeta || typeof toolMeta !== "object") return void 0;
|
|
7079
|
+
const openai = toolMeta.openai;
|
|
7080
|
+
if (!openai || typeof openai !== "object") return void 0;
|
|
7081
|
+
const template = openai.outputTemplate;
|
|
7082
|
+
return typeof template === "string" ? template : void 0;
|
|
7083
|
+
}
|
|
7084
|
+
/** Build an MCP-shaped `Resource` from the embedder's input. Used purely for
|
|
7085
|
+
* sidebar metadata (CSP, permissions, prefersBorder); the actual HTML render
|
|
7086
|
+
* goes through `resourceHtml` on the resulting Simulation. */
|
|
7087
|
+
function toMcpResource(r) {
|
|
7088
|
+
return {
|
|
7089
|
+
uri: r.uri,
|
|
7090
|
+
mimeType: r.mimeType ?? "text/html",
|
|
7091
|
+
name: r.uri,
|
|
7092
|
+
...r._meta ? { _meta: r._meta } : {}
|
|
7093
|
+
};
|
|
7094
|
+
}
|
|
7095
|
+
/**
|
|
7096
|
+
* Flatten an `InspectorApp` to the `Record<string, Simulation>` shape the
|
|
7097
|
+
* Inspector consumes internally. Returns an empty map if `app` is missing.
|
|
7098
|
+
*/
|
|
7099
|
+
function flattenAppToSimulations(app) {
|
|
7100
|
+
if (!app) return {};
|
|
7101
|
+
const result = {};
|
|
7102
|
+
const resourcesByUri = /* @__PURE__ */ new Map();
|
|
7103
|
+
for (const r of app.resources) {
|
|
7104
|
+
if (resourcesByUri.has(r.uri)) console.warn(`[Inspector] Duplicate resource URI '${r.uri}' in app.resources — the second entry replaces the first.`);
|
|
7105
|
+
resourcesByUri.set(r.uri, r);
|
|
7106
|
+
}
|
|
7107
|
+
for (const appTool of app.tools) {
|
|
7108
|
+
const uri = getOutputTemplate(appTool.tool._meta);
|
|
7109
|
+
if (!uri) continue;
|
|
7110
|
+
const resource = resourcesByUri.get(uri);
|
|
7111
|
+
if (!resource) continue;
|
|
7112
|
+
const mcpResource = toMcpResource(resource);
|
|
7113
|
+
const sims = appTool.simulations && appTool.simulations.length > 0 ? appTool.simulations : [{ name: appTool.tool.name }];
|
|
7114
|
+
for (const sim of sims) {
|
|
7115
|
+
const key = `${appTool.tool.name}__${sim.name}`;
|
|
7116
|
+
if (key in result) console.warn(`[Inspector] Duplicate simulation name '${sim.name}' under tool '${appTool.tool.name}' — the second entry replaces the first.`);
|
|
7117
|
+
result[key] = {
|
|
7118
|
+
name: key,
|
|
7119
|
+
displayName: sim.name,
|
|
7120
|
+
resourceHtml: resource.html,
|
|
7121
|
+
userMessage: sim.userMessage,
|
|
7122
|
+
tool: appTool.tool,
|
|
7123
|
+
resource: mcpResource,
|
|
7124
|
+
toolInput: sim.toolInput,
|
|
7125
|
+
toolResult: sim.toolResult,
|
|
7126
|
+
serverTools: sim.serverTools
|
|
7127
|
+
};
|
|
7128
|
+
}
|
|
7129
|
+
}
|
|
7130
|
+
return result;
|
|
7131
|
+
}
|
|
7132
|
+
//#endregion
|
|
6886
7133
|
//#region src/inspector/inspector.tsx
|
|
6887
7134
|
var DOCS_BASE_URL = "https://sunpeak.ai/docs";
|
|
6888
7135
|
/** Check whether a simulation has user-authored fixture data. */
|
|
6889
7136
|
function hasFixtureData(sim) {
|
|
6890
7137
|
return sim.toolResult != null || sim.toolInput != null || sim.serverTools != null;
|
|
6891
7138
|
}
|
|
6892
|
-
|
|
7139
|
+
var EMPTY_SIMULATIONS = Object.freeze({});
|
|
7140
|
+
function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_SIMULATIONS, appName: appNameProp, appIcon: appIconProp, defaultHost = "chatgpt", onCallTool, onCallToolDirect, defaultProdResources = false, hideInspectorModes = false, demoMode = false, sandboxUrl, mcpServerUrl }) {
|
|
7141
|
+
const initialSimulations = react.useMemo(() => app ? flattenAppToSimulations(app) : initialSimulationsProp, [app, initialSimulationsProp]);
|
|
7142
|
+
const appName = app?.name ?? appNameProp ?? "Sunpeak";
|
|
7143
|
+
const appIcon = app?.icon ?? appIconProp;
|
|
7144
|
+
const isEmbedded = !!app;
|
|
7145
|
+
const conflictWarnedRef = react.useRef(false);
|
|
7146
|
+
react.useEffect(() => {
|
|
7147
|
+
if (conflictWarnedRef.current) return;
|
|
7148
|
+
if (app && initialSimulationsProp && Object.keys(initialSimulationsProp).length > 0) {
|
|
7149
|
+
conflictWarnedRef.current = true;
|
|
7150
|
+
console.warn("[Inspector] Both `app` and `simulations` were provided. `app` takes precedence; `simulations` is ignored.");
|
|
7151
|
+
}
|
|
7152
|
+
}, [app, initialSimulationsProp]);
|
|
7153
|
+
const rootRef = react.useRef(null);
|
|
6893
7154
|
const [simulations, setSimulations] = react.useState(initialSimulations);
|
|
6894
7155
|
react.useEffect(() => {
|
|
6895
7156
|
setSimulations(initialSimulations);
|
|
@@ -6972,7 +7233,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
6972
7233
|
const [oauthClientSecret, setOauthClientSecret] = react.useState("");
|
|
6973
7234
|
const [oauthStatus, setOauthStatus] = react.useState("none");
|
|
6974
7235
|
const [oauthError, setOauthError] = react.useState();
|
|
6975
|
-
const connection = useMcpConnection(mcpServerUrl || void 0);
|
|
7236
|
+
const connection = useMcpConnection(isEmbedded ? void 0 : mcpServerUrl || void 0);
|
|
6976
7237
|
const [prodResources, setProdResources] = react.useState(state.urlProdResources ?? defaultProdResources);
|
|
6977
7238
|
const showSidebar = state.urlSidebar !== false;
|
|
6978
7239
|
const showDevOverlay = state.urlDevOverlay !== false;
|
|
@@ -7046,12 +7307,22 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7046
7307
|
return;
|
|
7047
7308
|
}
|
|
7048
7309
|
if (data.status === "redirect" && data.authUrl) {
|
|
7310
|
+
let parsedAuthUrl = null;
|
|
7311
|
+
try {
|
|
7312
|
+
parsedAuthUrl = new URL(data.authUrl);
|
|
7313
|
+
} catch {}
|
|
7314
|
+
if (!parsedAuthUrl || parsedAuthUrl.protocol !== "http:" && parsedAuthUrl.protocol !== "https:") {
|
|
7315
|
+
popup?.close();
|
|
7316
|
+
setOauthError("OAuth authorization URL is not a valid http(s) URL.");
|
|
7317
|
+
setOauthStatus("error");
|
|
7318
|
+
return;
|
|
7319
|
+
}
|
|
7049
7320
|
if (!popup || popup.closed) {
|
|
7050
7321
|
setOauthError("Popup was blocked. Allow popups for this site and try again.");
|
|
7051
7322
|
setOauthStatus("error");
|
|
7052
7323
|
return;
|
|
7053
7324
|
}
|
|
7054
|
-
popup.location.href =
|
|
7325
|
+
popup.location.href = parsedAuthUrl.toString();
|
|
7055
7326
|
let checkClosed;
|
|
7056
7327
|
let bc;
|
|
7057
7328
|
const cleanup = () => {
|
|
@@ -7240,15 +7511,23 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7240
7511
|
displayMode,
|
|
7241
7512
|
setDisplayMode
|
|
7242
7513
|
]);
|
|
7243
|
-
react.
|
|
7244
|
-
const vars = activeShell?.styleVariables;
|
|
7245
|
-
if (!vars) return;
|
|
7246
|
-
const root = document.documentElement;
|
|
7247
|
-
for (const [key, value] of Object.entries(vars)) if (value) root.style.setProperty(key, value);
|
|
7248
|
-
}, [activeShell]);
|
|
7514
|
+
const prevStyleVarKeysRef = react.useRef([]);
|
|
7249
7515
|
const prevPageStyleKeysRef = react.useRef([]);
|
|
7250
7516
|
react.useLayoutEffect(() => {
|
|
7251
|
-
const root =
|
|
7517
|
+
const root = rootRef.current;
|
|
7518
|
+
if (!root) return;
|
|
7519
|
+
root.setAttribute("data-theme", state.theme);
|
|
7520
|
+
root.style.colorScheme = state.theme;
|
|
7521
|
+
for (const key of prevStyleVarKeysRef.current) root.style.removeProperty(key);
|
|
7522
|
+
const vars = activeShell?.styleVariables;
|
|
7523
|
+
if (vars) {
|
|
7524
|
+
const keys = [];
|
|
7525
|
+
for (const [key, value] of Object.entries(vars)) if (value) {
|
|
7526
|
+
root.style.setProperty(key, value);
|
|
7527
|
+
keys.push(key);
|
|
7528
|
+
}
|
|
7529
|
+
prevStyleVarKeysRef.current = keys;
|
|
7530
|
+
} else prevStyleVarKeysRef.current = [];
|
|
7252
7531
|
for (const key of prevPageStyleKeysRef.current) root.style.removeProperty(key);
|
|
7253
7532
|
const pageStyles = activeShell?.pageStyles;
|
|
7254
7533
|
if (pageStyles) {
|
|
@@ -7259,7 +7538,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7259
7538
|
}
|
|
7260
7539
|
prevPageStyleKeysRef.current = keys;
|
|
7261
7540
|
} else prevPageStyleKeysRef.current = [];
|
|
7262
|
-
}, [activeShell]);
|
|
7541
|
+
}, [activeShell, state.theme]);
|
|
7263
7542
|
react.useLayoutEffect(() => {
|
|
7264
7543
|
const fontCss = activeShell?.fontCss;
|
|
7265
7544
|
const id = "sunpeak-host-fonts";
|
|
@@ -7350,7 +7629,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7350
7629
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7351
7630
|
className: "text-sm text-center max-w-xs",
|
|
7352
7631
|
style: { color: "var(--color-text-secondary)" },
|
|
7353
|
-
children: isError ? "Could not connect to MCP server" : isConnected ? "No tools with UI resources found on this server" : serverUrl ? "Connecting…" : "Enter an MCP server URL to get started"
|
|
7632
|
+
children: isEmbedded ? "No tools with UI resources in this app" : isError ? "Could not connect to MCP server" : isConnected ? "No tools with UI resources found on this server" : serverUrl ? "Connecting…" : "Enter an MCP server URL to get started"
|
|
7354
7633
|
})
|
|
7355
7634
|
});
|
|
7356
7635
|
} else if (showEmptyState) content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -7375,6 +7654,30 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7375
7654
|
children: "Building…"
|
|
7376
7655
|
})
|
|
7377
7656
|
});
|
|
7657
|
+
else if (state.resourceHtml) content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7658
|
+
className: "h-full w-full",
|
|
7659
|
+
style: { background: iframeBg },
|
|
7660
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IframeResource, {
|
|
7661
|
+
html: state.resourceHtml,
|
|
7662
|
+
hostContext,
|
|
7663
|
+
toolInput: state.toolInput,
|
|
7664
|
+
toolResult: state.effectiveToolResult,
|
|
7665
|
+
hostOptions: {
|
|
7666
|
+
hostInfo: activeShell?.hostInfo,
|
|
7667
|
+
hostCapabilities: activeShell?.hostCapabilities,
|
|
7668
|
+
onDisplayModeChange: state.handleDisplayModeChange,
|
|
7669
|
+
onUpdateModelContext: state.handleUpdateModelContext,
|
|
7670
|
+
onCallTool: handleCallTool
|
|
7671
|
+
},
|
|
7672
|
+
permissions: state.permissions,
|
|
7673
|
+
prefersBorder: state.prefersBorder,
|
|
7674
|
+
onDisplayModeReady: state.handleDisplayModeReady,
|
|
7675
|
+
debugInjectState: state.modelContext,
|
|
7676
|
+
injectOpenAIRuntime: state.activeHost === "chatgpt",
|
|
7677
|
+
sandboxUrl,
|
|
7678
|
+
className: "h-full w-full"
|
|
7679
|
+
}, `${state.activeHost}-${state.selectedSimulationName}-html`)
|
|
7680
|
+
});
|
|
7378
7681
|
else if (effectiveResourceUrl) content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7379
7682
|
className: "h-full w-full",
|
|
7380
7683
|
style: { background: iframeBg },
|
|
@@ -7425,7 +7728,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7425
7728
|
}, `${state.activeHost}-${state.selectedSimulationName}-${state.resourceScript}`)
|
|
7426
7729
|
});
|
|
7427
7730
|
else content = children;
|
|
7428
|
-
const applyTheme =
|
|
7731
|
+
const applyTheme = react.useCallback((_theme) => {}, []);
|
|
7429
7732
|
const runButton = !demoMode && onCallTool && currentSim && activeSimulationName === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7430
7733
|
type: "button",
|
|
7431
7734
|
onClick: handleRun,
|
|
@@ -7465,11 +7768,13 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7465
7768
|
headerAction: runButton,
|
|
7466
7769
|
children: content
|
|
7467
7770
|
}) : content;
|
|
7771
|
+
const rootSizing = isEmbedded ? "h-full w-full" : "h-screen w-screen";
|
|
7468
7772
|
if (!showSidebar) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeProvider, {
|
|
7469
7773
|
theme: state.theme,
|
|
7470
7774
|
applyTheme,
|
|
7471
7775
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7472
|
-
|
|
7776
|
+
ref: rootRef,
|
|
7777
|
+
className: `sunpeak-inspector-root flex ${rootSizing}`,
|
|
7473
7778
|
children: conversationContent
|
|
7474
7779
|
})
|
|
7475
7780
|
});
|
|
@@ -7477,10 +7782,12 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7477
7782
|
theme: state.theme,
|
|
7478
7783
|
applyTheme,
|
|
7479
7784
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SimpleSidebar, {
|
|
7785
|
+
rootRef,
|
|
7786
|
+
fillParent: isEmbedded,
|
|
7480
7787
|
controls: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7481
7788
|
className: "space-y-1",
|
|
7482
7789
|
children: [
|
|
7483
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarControl, {
|
|
7790
|
+
!isEmbedded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarControl, {
|
|
7484
7791
|
label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7485
7792
|
className: "flex items-center gap-1.5",
|
|
7486
7793
|
children: ["MCP Server", serverUrl && !demoMode && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -7499,8 +7806,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7499
7806
|
placeholder: "http://localhost:8000/mcp",
|
|
7500
7807
|
disabled: demoMode
|
|
7501
7808
|
})
|
|
7502
|
-
}),
|
|
7503
|
-
!demoMode && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCollapsibleControl, {
|
|
7809
|
+
}), !demoMode && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCollapsibleControl, {
|
|
7504
7810
|
label: "Authentication",
|
|
7505
7811
|
defaultCollapsed: authType === "none",
|
|
7506
7812
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -7585,8 +7891,8 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7585
7891
|
})
|
|
7586
7892
|
]
|
|
7587
7893
|
})
|
|
7588
|
-
}, `auth-${authType === "none" ? "none" : "active"}`),
|
|
7589
|
-
!hideInspectorModes && !demoMode && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCheckbox, {
|
|
7894
|
+
}, `auth-${authType === "none" ? "none" : "active"}`)] }),
|
|
7895
|
+
!hideInspectorModes && !demoMode && !isEmbedded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCheckbox, {
|
|
7590
7896
|
checked: prodResources,
|
|
7591
7897
|
onChange: setProdResources,
|
|
7592
7898
|
label: "Prod Resources",
|
|
@@ -7637,7 +7943,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
7637
7943
|
label: selectedToolInfo && selectedToolInfo.fixtureSimNames.length > 0 ? "None (call server)" : "None"
|
|
7638
7944
|
}], ...(selectedToolInfo?.fixtureSimNames ?? []).map((simName) => ({
|
|
7639
7945
|
value: simName,
|
|
7640
|
-
label: simName
|
|
7946
|
+
label: simulations[simName]?.displayName ?? simName
|
|
7641
7947
|
}))]
|
|
7642
7948
|
})
|
|
7643
7949
|
})]
|
|
@@ -8113,6 +8419,12 @@ Object.defineProperty(exports, "extractResourceCSP", {
|
|
|
8113
8419
|
return extractResourceCSP;
|
|
8114
8420
|
}
|
|
8115
8421
|
});
|
|
8422
|
+
Object.defineProperty(exports, "flattenAppToSimulations", {
|
|
8423
|
+
enumerable: true,
|
|
8424
|
+
get: function() {
|
|
8425
|
+
return flattenAppToSimulations;
|
|
8426
|
+
}
|
|
8427
|
+
});
|
|
8116
8428
|
Object.defineProperty(exports, "getHostShell", {
|
|
8117
8429
|
enumerable: true,
|
|
8118
8430
|
get: function() {
|
|
@@ -8156,4 +8468,4 @@ Object.defineProperty(exports, "useThemeContext", {
|
|
|
8156
8468
|
}
|
|
8157
8469
|
});
|
|
8158
8470
|
|
|
8159
|
-
//# sourceMappingURL=inspector-
|
|
8471
|
+
//# sourceMappingURL=inspector-DSX76Z-W.cjs.map
|