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