sunpeak 0.18.6 → 0.18.9
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/bin/commands/dev.mjs +9 -5
- package/bin/commands/inspect.mjs +13 -1
- package/bin/commands/new.mjs +5 -0
- package/bin/lib/dev-overlay.mjs +50 -0
- package/bin/lib/live/live-config.d.mts +3 -0
- package/bin/lib/live/live-config.mjs +3 -1
- package/bin/lib/sandbox-server.mjs +19 -0
- package/dist/chatgpt/index.cjs +2 -3
- package/dist/chatgpt/index.js +2 -3
- package/dist/claude/index.cjs +1 -2
- package/dist/claude/index.js +1 -2
- package/dist/host/chatgpt/index.cjs +0 -1
- package/dist/host/chatgpt/index.cjs.map +1 -1
- package/dist/host/chatgpt/index.js +0 -1
- package/dist/host/chatgpt/index.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/inspector/index.cjs +2 -3
- package/dist/inspector/index.js +2 -3
- package/dist/inspector/inspector-url.d.ts +13 -0
- package/dist/inspector/use-inspector-state.d.ts +2 -0
- package/dist/{inspector-DRD_Q66E.cjs → inspector-CTMccsz9.cjs} +71 -22
- package/dist/inspector-CTMccsz9.cjs.map +1 -0
- package/dist/{inspector-CjSoXm6N.js → inspector-DkS75JCk.js} +71 -22
- package/dist/inspector-DkS75JCk.js.map +1 -0
- package/dist/{inspector-url-7qhtJwY6.cjs → inspector-url-C3LTKgXt.cjs} +3 -1
- package/dist/inspector-url-C3LTKgXt.cjs.map +1 -0
- package/dist/{inspector-url-DuEFmxLP.js → inspector-url-CyQcuBI9.js} +3 -1
- package/dist/inspector-url-CyQcuBI9.js.map +1 -0
- package/dist/mcp/index.cjs +98 -15
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +98 -15
- package/dist/mcp/index.js.map +1 -1
- package/dist/style.css +4 -0
- package/package.json +5 -5
- 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/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/@testing-library_react.js +9 -6
- package/template/node_modules/.vite-mcp/deps/@testing-library_react.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/_metadata.json +21 -21
- package/template/node_modules/.vite-mcp/deps/vitest.js +366 -128
- package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -1
- package/template/package.json +2 -2
- package/template/tests/e2e/albums.spec.ts +1 -1
- package/template/tests/e2e/carousel.spec.ts +1 -1
- package/template/tests/e2e/dev-overlay.spec.ts +118 -0
- package/template/tests/e2e/helpers.ts +13 -0
- package/template/tests/e2e/map.spec.ts +1 -1
- package/template/tests/e2e/review.spec.ts +1 -1
- package/template/tests/live/playwright.config.ts +1 -1
- package/dist/inspector-CjSoXm6N.js.map +0 -1
- package/dist/inspector-DRD_Q66E.cjs.map +0 -1
- package/dist/inspector-url-7qhtJwY6.cjs.map +0 -1
- package/dist/inspector-url-DuEFmxLP.js.map +0 -1
package/bin/commands/dev.mjs
CHANGED
|
@@ -239,7 +239,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
|
|
|
239
239
|
const { tool } = await extractToolExport(toolPath);
|
|
240
240
|
toolMap.set(toolName, { tool, path: toolPath });
|
|
241
241
|
} catch (err) {
|
|
242
|
-
console.warn(`Warning: Could not extract metadata from tool ${toolName}
|
|
242
|
+
console.warn(`Warning: Could not extract metadata from tool "${toolName}" (${toolPath}):\n ${err.message}\n Expected: export const tool: AppToolConfig = { ... }`);
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
@@ -271,7 +271,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
|
|
|
271
271
|
toolHandlerMap.set(toolName, { handler: mod.default, outputSchema: mod.outputSchema });
|
|
272
272
|
}
|
|
273
273
|
} catch (err) {
|
|
274
|
-
console.warn(`Warning: Could not load handler for tool "${toolName}"
|
|
274
|
+
console.warn(`Warning: Could not load handler for tool "${toolName}" (${relativePath}):\n ${err.message}`);
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -287,7 +287,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
|
|
|
287
287
|
const toolEntry = toolMap.get(toolName);
|
|
288
288
|
const tool = toolEntry?.tool;
|
|
289
289
|
if (!tool) {
|
|
290
|
-
console.warn(`Warning: Tool "${toolName}" not found for simulation "${simName}".
|
|
290
|
+
console.warn(`Warning: Tool "${toolName}" not found for simulation "${simName}". Expected file: src/tools/${toolName}.ts`);
|
|
291
291
|
continue;
|
|
292
292
|
}
|
|
293
293
|
|
|
@@ -412,6 +412,7 @@ if (!Component) {
|
|
|
412
412
|
if (import.meta.hot) {
|
|
413
413
|
import.meta.hot.accept();
|
|
414
414
|
}
|
|
415
|
+
|
|
415
416
|
`;
|
|
416
417
|
}
|
|
417
418
|
},
|
|
@@ -547,11 +548,14 @@ if (import.meta.hot) {
|
|
|
547
548
|
if (typeof mod.default !== 'function') {
|
|
548
549
|
throw new Error(`Tool "${name}" has no default export handler`);
|
|
549
550
|
}
|
|
551
|
+
const startTime = performance.now();
|
|
550
552
|
const result = await mod.default(args, {});
|
|
553
|
+
const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
|
|
551
554
|
if (typeof result === 'string') {
|
|
552
|
-
return { content: [{ type: 'text', text: result }] };
|
|
555
|
+
return { content: [{ type: 'text', text: result }], _meta: { _sunpeak: { requestTimeMs: durationMs } } };
|
|
553
556
|
}
|
|
554
|
-
|
|
557
|
+
const typed = result ?? {};
|
|
558
|
+
return { ...typed, _meta: { ...typed._meta, _sunpeak: { requestTimeMs: durationMs } } };
|
|
555
559
|
}
|
|
556
560
|
throw new Error(`Tool "${name}" not found`);
|
|
557
561
|
},
|
package/bin/commands/inspect.mjs
CHANGED
|
@@ -20,6 +20,7 @@ const { join, resolve, dirname } = path;
|
|
|
20
20
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
21
21
|
import { getPort } from '../lib/get-port.mjs';
|
|
22
22
|
import { startSandboxServer } from '../lib/sandbox-server.mjs';
|
|
23
|
+
import { getDevOverlayScript } from '../lib/dev-overlay.mjs';
|
|
23
24
|
|
|
24
25
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
26
|
const SUNPEAK_PKG_DIR = resolve(__dirname, '..', '..');
|
|
@@ -850,7 +851,18 @@ function sunpeakInspectEndpointsPlugin(getClient, setClient, pluginOpts = {}) {
|
|
|
850
851
|
'X-Content-Type-Options': 'nosniff',
|
|
851
852
|
});
|
|
852
853
|
if (typeof content.text === 'string') {
|
|
853
|
-
|
|
854
|
+
const stripOverlay = url.searchParams.get('devOverlay') === 'false';
|
|
855
|
+
let text = content.text;
|
|
856
|
+
if (stripOverlay) {
|
|
857
|
+
// Strip dev overlay (e.g., for e2e tests)
|
|
858
|
+
text = text.replace(/<script>(?:(?!<\/script>)[\s\S])*?__sunpeak-dev-timing(?:(?!<\/script>)[\s\S])*?<\/script>/g, '');
|
|
859
|
+
} else if (process.env.SUNPEAK_DEV_OVERLAY !== 'false' && !text.includes('__sunpeak-dev-timing') && text.includes('</body>')) {
|
|
860
|
+
// Inject dev overlay into resources from non-sunpeak servers.
|
|
861
|
+
// The overlay shows resource served timestamp and tool timing (from
|
|
862
|
+
// _meta._sunpeak.requestTimeMs on the PostMessage tool-result notification).
|
|
863
|
+
text = text.replace('</body>', `${getDevOverlayScript(Date.now(), null)}\n</body>`);
|
|
864
|
+
}
|
|
865
|
+
res.end(text);
|
|
854
866
|
} else if (content.blob) {
|
|
855
867
|
res.end(Buffer.from(content.blob, 'base64'));
|
|
856
868
|
} else {
|
package/bin/commands/new.mjs
CHANGED
|
@@ -175,6 +175,11 @@ export async function init(projectName, resourcesArg, deps = defaultDeps) {
|
|
|
175
175
|
return false;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
// Skip framework-internal test files (dev overlay tests are for sunpeak development, not user projects)
|
|
179
|
+
if ((src.includes('/tests/e2e/') || src.includes('/tests/live/')) && name.startsWith('dev-')) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
178
183
|
// Skip deps.json files (build-time metadata, not needed in scaffolded projects)
|
|
179
184
|
if (name === 'deps.json' && src.includes('/resources/')) {
|
|
180
185
|
return false;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate an inline script that shows a dev overlay with resource served timestamp
|
|
3
|
+
* and tool call request timing.
|
|
4
|
+
*
|
|
5
|
+
* The resource timestamp is baked into the HTML at readResource time. Tool timing
|
|
6
|
+
* arrives two ways:
|
|
7
|
+
* 1. Baked-in `toolMs` from readResource time (works when the tool call precedes the
|
|
8
|
+
* resource read, which is the case for Claude and the inspector's initial render).
|
|
9
|
+
* 2. `_meta._sunpeak.requestTimeMs` on the tool result PostMessage (handles inspector
|
|
10
|
+
* Re-run and hosts that pass `_meta` through to the resource iframe).
|
|
11
|
+
*
|
|
12
|
+
* @param {number} servedAt - Unix timestamp (ms) when the resource HTML was generated/served.
|
|
13
|
+
* @param {number | null} toolMs - Most recent tool call duration (ms), or null if no call yet.
|
|
14
|
+
* @returns {string} HTML script tag with the dev overlay.
|
|
15
|
+
*/
|
|
16
|
+
export function getDevOverlayScript(servedAt, toolMs) {
|
|
17
|
+
return `<script>
|
|
18
|
+
(function(){
|
|
19
|
+
var servedAt=${servedAt};
|
|
20
|
+
var el=null,hidden=false,lastMs=${toolMs ?? 'null'};
|
|
21
|
+
function fmt(ts){var d=new Date(ts);var h=d.getHours(),m=d.getMinutes(),s=d.getSeconds();return (h<10?'0':'')+h+':'+(m<10?'0':'')+m+':'+(s<10?'0':'')+s}
|
|
22
|
+
function make(){
|
|
23
|
+
var existing=document.getElementById('__sunpeak-dev-timing');
|
|
24
|
+
if(existing)return existing;
|
|
25
|
+
var b=document.createElement('button');b.id='__sunpeak-dev-timing';
|
|
26
|
+
b.style.cssText='position:fixed;bottom:8px;right:8px;z-index:2147483647;display:grid;grid-template-columns:auto auto;gap:0 6px;align-items:baseline;padding:5px 8px;border-radius:6px;border:1px solid rgba(128,128,128,0.25);background:rgba(0,0,0,0.75);backdrop-filter:blur(8px);color:#e5e5e5;font-size:11px;font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;line-height:1.4;cursor:pointer;user-select:none;opacity:0.85;transition:opacity 150ms;';
|
|
27
|
+
b.onmouseenter=function(){b.style.opacity='1'};
|
|
28
|
+
b.onmouseleave=function(){b.style.opacity='0.85'};
|
|
29
|
+
b.onclick=function(){hidden=!hidden;upd()};
|
|
30
|
+
document.body.appendChild(b);return b;
|
|
31
|
+
}
|
|
32
|
+
function upd(){
|
|
33
|
+
if(!el)el=make();
|
|
34
|
+
if(hidden){el.title='Show dev info';el.innerHTML='<span style="grid-column:1/-1;font-size:9px;text-align:center">DEV</span>';return}
|
|
35
|
+
var h='';
|
|
36
|
+
h+='<span style="text-align:right;color:rgba(255,255,255,0.5);white-space:nowrap">Resource:</span><span style="white-space:nowrap">'+fmt(servedAt)+'</span>';
|
|
37
|
+
if(lastMs!=null)h+='<span style="text-align:right;color:rgba(255,255,255,0.5);white-space:nowrap">Tool:</span><span style="white-space:nowrap">'+(lastMs%1===0?lastMs:lastMs.toFixed(1))+'ms</span>';
|
|
38
|
+
el.title='Hide dev info';el.innerHTML=h;
|
|
39
|
+
}
|
|
40
|
+
upd();
|
|
41
|
+
window.addEventListener('message',function(e){
|
|
42
|
+
var d=e.data;if(!d||typeof d!=='object')return;
|
|
43
|
+
if(d.method!=='ui/notifications/tool-result')return;
|
|
44
|
+
var p=d.params;if(!p)return;
|
|
45
|
+
var ms=p._meta&&p._meta._sunpeak&&p._meta._sunpeak.requestTimeMs;
|
|
46
|
+
if(typeof ms==='number'){lastMs=ms;upd()}
|
|
47
|
+
});
|
|
48
|
+
})();
|
|
49
|
+
</script>`;
|
|
50
|
+
}
|
|
@@ -23,6 +23,9 @@ export interface LiveConfigOptions {
|
|
|
23
23
|
/** Browser permissions to grant (e.g., ['geolocation']). */
|
|
24
24
|
permissions?: string[];
|
|
25
25
|
|
|
26
|
+
/** Show the dev overlay (resource timestamp + tool timing) in resources. Default: true */
|
|
27
|
+
devOverlay?: boolean;
|
|
28
|
+
|
|
26
29
|
/** Additional Playwright `use` options, merged with defaults. */
|
|
27
30
|
use?: Record<string, unknown>;
|
|
28
31
|
}
|
|
@@ -32,6 +32,7 @@ const GLOBAL_SETUP_PATH = join(__dirname, 'global-setup.mjs');
|
|
|
32
32
|
* @param {string} [options.timezoneId] - Timezone (e.g., 'America/New_York')
|
|
33
33
|
* @param {{ latitude: number, longitude: number }} [options.geolocation] - Geolocation coordinates
|
|
34
34
|
* @param {string[]} [options.permissions] - Browser permissions to grant (e.g., ['geolocation'])
|
|
35
|
+
* @param {boolean} [options.devOverlay=true] - Show the dev overlay (resource timestamp + tool timing) in resources
|
|
35
36
|
* @param {Object} [options.use] - Additional Playwright `use` options (merged with defaults)
|
|
36
37
|
*/
|
|
37
38
|
export function createLiveConfig(hostOptions, options = {}) {
|
|
@@ -40,6 +41,7 @@ export function createLiveConfig(hostOptions, options = {}) {
|
|
|
40
41
|
testDir = '.',
|
|
41
42
|
authDir,
|
|
42
43
|
vitePort = getPortSync(3456),
|
|
44
|
+
devOverlay = true,
|
|
43
45
|
colorScheme,
|
|
44
46
|
viewport,
|
|
45
47
|
locale,
|
|
@@ -89,7 +91,7 @@ export function createLiveConfig(hostOptions, options = {}) {
|
|
|
89
91
|
},
|
|
90
92
|
],
|
|
91
93
|
webServer: {
|
|
92
|
-
command: `SUNPEAK_LIVE_TEST=1 SUNPEAK_SANDBOX_PORT=${getPortSync(24680)} pnpm dev -- --prod-resources --port ${vitePort}`,
|
|
94
|
+
command: `SUNPEAK_LIVE_TEST=1 SUNPEAK_SANDBOX_PORT=${getPortSync(24680)}${devOverlay ? '' : ' SUNPEAK_DEV_OVERLAY=false'} pnpm dev -- --prod-resources --port ${vitePort}`,
|
|
93
95
|
url: `http://localhost:${vitePort}/health`,
|
|
94
96
|
reuseExistingServer: !process.env.CI,
|
|
95
97
|
timeout: 60_000,
|
|
@@ -59,6 +59,25 @@ export async function startSandboxServer({ preferredPort = 24680 } = {}) {
|
|
|
59
59
|
res.end('Not found');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
server.on('clientError', (err, socket) => {
|
|
63
|
+
if (err.code === 'ECONNRESET') {
|
|
64
|
+
// Normal when browser tabs close abruptly
|
|
65
|
+
} else if (
|
|
66
|
+
err.code === 'HPE_INVALID_METHOD' &&
|
|
67
|
+
err.rawPacket instanceof Buffer &&
|
|
68
|
+
err.rawPacket[0] === 0x16
|
|
69
|
+
) {
|
|
70
|
+
console.error(
|
|
71
|
+
'Received HTTPS request on sandbox server (port ' + port + '). ' +
|
|
72
|
+
'If you\'re using ngrok, make sure the upstream is http:// (not https://). ' +
|
|
73
|
+
'Example: ngrok http 8000'
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
console.error('Sandbox server client error', err);
|
|
77
|
+
}
|
|
78
|
+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
79
|
+
});
|
|
80
|
+
|
|
62
81
|
await new Promise((resolve, reject) => {
|
|
63
82
|
server.listen(port, () => resolve());
|
|
64
83
|
server.on('error', reject);
|
package/dist/chatgpt/index.cjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../
|
|
4
|
-
const
|
|
5
|
-
const require_inspector_url = require("../inspector-url-7qhtJwY6.cjs");
|
|
3
|
+
const require_inspector = require("../inspector-CTMccsz9.cjs");
|
|
4
|
+
const require_inspector_url = require("../inspector-url-C3LTKgXt.cjs");
|
|
6
5
|
const require_discovery = require("../discovery-Clu4uHp1.cjs");
|
|
7
6
|
//#region src/chatgpt/index.ts
|
|
8
7
|
var chatgpt_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
package/dist/chatgpt/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
|
|
2
|
-
import "../
|
|
3
|
-
import {
|
|
4
|
-
import { t as createInspectorUrl } from "../inspector-url-DuEFmxLP.js";
|
|
2
|
+
import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Inspector, v as SCREEN_WIDTHS } from "../inspector-DkS75JCk.js";
|
|
3
|
+
import { t as createInspectorUrl } from "../inspector-url-CyQcuBI9.js";
|
|
5
4
|
import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
|
|
6
5
|
//#region src/chatgpt/index.ts
|
|
7
6
|
var chatgpt_exports = /* @__PURE__ */ __exportAll({
|
package/dist/claude/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../
|
|
4
|
-
const require_inspector = require("../inspector-DRD_Q66E.cjs");
|
|
3
|
+
const require_inspector = require("../inspector-CTMccsz9.cjs");
|
|
5
4
|
exports.Inspector = require_inspector.Inspector;
|
package/dist/claude/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../../protocol-jbxhzcnS.cjs");
|
|
4
3
|
const require_use_app = require("../../use-app-Dqh20JPP.cjs");
|
|
5
4
|
let react = require("react");
|
|
6
5
|
//#region src/host/chatgpt/openai-types.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":";;;;;;;;;AAuFA,SAAgB,mBAA8C;AAC5D,KAAI,OAAO,WAAW,eAAe,YAAY,OAC/C,QAAQ,OAAgD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzD5D,SAAgB,gBAAiE;CAC/E,MAAM,MAAM,gBAAA,QAAQ;AACpB,SAAA,GAAA,MAAA,aACE,OAAO,SAAe;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,oCAAoC;EAEtD,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,WACZ,OAAM,IAAI,MAAM,yDAAyD;AAE3E,SAAO,QAAQ,WAAW,KAAK;IAEjC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBH,SAAgB,kBAAuE;CACrF,MAAM,MAAM,gBAAA,QAAQ;AACpB,SAAA,GAAA,MAAA,aACE,OAAO,WAAqC;AAC1C,MAAI,CAAC,KAAK;AACR,WAAQ,KAAK,sCAAsC;AACnD;;EAEF,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,aACZ,OAAM,IAAI,MAAM,6DAA6D;AAE/E,SAAO,QAAQ,aAAa,OAAO;IAErC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOH,SAAgB,qBAEkB;CAChC,MAAM,MAAM,gBAAA,QAAQ;AACpB,SAAA,GAAA,MAAA,aACE,OAAO,YAAmC;AACxC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,yCAAyC;EAE3D,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,gBACZ,OAAM,IAAI,MAAM,mEAAmE;AAErF,SAAO,QAAQ,gBAAgB,QAAQ;IAEzC,CAAC,IAAI,CACN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":";;;;;;;AAuFA,SAAgB,mBAA8C;AAC5D,KAAI,OAAO,WAAW,eAAe,YAAY,OAC/C,QAAQ,OAAgD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzD5D,SAAgB,gBAAiE;CAC/E,MAAM,MAAM,QAAQ;AACpB,QAAO,YACL,OAAO,SAAe;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,oCAAoC;EAEtD,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,WACZ,OAAM,IAAI,MAAM,yDAAyD;AAE3E,SAAO,QAAQ,WAAW,KAAK;IAEjC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBH,SAAgB,kBAAuE;CACrF,MAAM,MAAM,QAAQ;AACpB,QAAO,YACL,OAAO,WAAqC;AAC1C,MAAI,CAAC,KAAK;AACR,WAAQ,KAAK,sCAAsC;AACnD;;EAEF,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,aACZ,OAAM,IAAI,MAAM,6DAA6D;AAE/E,SAAO,QAAQ,aAAa,OAAO;IAErC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOH,SAAgB,qBAEkB;CAChC,MAAM,MAAM,QAAQ;AACpB,QAAO,YACL,OAAO,YAAmC;AACxC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,yCAAyC;EAE3D,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,gBACZ,OAAM,IAAI,MAAM,mEAAmE;AAErF,SAAO,QAAQ,gBAAgB,QAAQ;IAEzC,CAAC,IAAI,CACN"}
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_chunk = require("./chunk-9hOWP6kD.cjs");
|
|
3
3
|
const require_protocol = require("./protocol-jbxhzcnS.cjs");
|
|
4
4
|
const require_use_app = require("./use-app-Dqh20JPP.cjs");
|
|
5
|
-
const require_inspector = require("./inspector-
|
|
5
|
+
const require_inspector = require("./inspector-CTMccsz9.cjs");
|
|
6
6
|
const require_host_index = require("./host/index.cjs");
|
|
7
7
|
const require_inspector_index = require("./inspector/index.cjs");
|
|
8
8
|
const require_chatgpt_index = require("./chatgpt/index.cjs");
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $ as literal, L as RequestIdSchema, Q as boolean, R as ResourceLinkSchema, U as ToolSchema, X as _undefined, Z as array, _ as ImplementationSchema, a as CallToolResultSchema, at as unknown, et as number, it as union, nt as record, p as EmbeddedResourceSchema, rt as string, s as ContentBlockSchema, tt as object } from "./protocol-DJmRaBzO.js";
|
|
2
2
|
import { $ as s, A as V, B as e, C as QQ, D as T, E as S, F as _Q, G as k, H as gQ, I as a, J as o, K as m, L as b, M as W, N as WQ, O as TQ, P as _, Q as r, R as c, S as PQ, T as RQ, U as h, V as g, W as jQ, X as q, Y as p, Z as qQ, _ as LQ, a as B, at as zQ, b as OQ, c as EQ, d as G, et as t, f as I, g as L, h as K, i as AQ, it as z, j as VQ, k as U, l as F, m as J, n as AppProvider, nt as w, o as BQ, p as IQ, q as n, r as A, rt as x, s as C, t as useApp, tt as vX, u as FQ, v as MX, w as R, x as P, y as O, z as d } from "./use-app-BNbz1uzj.js";
|
|
3
|
-
import { S as DEFAULT_STYLE_VARIABLES } from "./inspector-
|
|
3
|
+
import { S as DEFAULT_STYLE_VARIABLES } from "./inspector-DkS75JCk.js";
|
|
4
4
|
import { detectHost, isChatGPT, isClaude } from "./host/index.js";
|
|
5
5
|
import { t as inspector_exports } from "./inspector/index.js";
|
|
6
6
|
import { t as chatgpt_exports } from "./chatgpt/index.js";
|
package/dist/inspector/index.cjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../
|
|
4
|
-
const
|
|
5
|
-
const require_inspector_url = require("../inspector-url-7qhtJwY6.cjs");
|
|
3
|
+
const require_inspector = require("../inspector-CTMccsz9.cjs");
|
|
4
|
+
const require_inspector_url = require("../inspector-url-C3LTKgXt.cjs");
|
|
6
5
|
const require_discovery = require("../discovery-Clu4uHp1.cjs");
|
|
7
6
|
//#region src/inspector/index.ts
|
|
8
7
|
var inspector_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
package/dist/inspector/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
|
|
2
|
-
import "../
|
|
3
|
-
import {
|
|
4
|
-
import { t as createInspectorUrl } from "../inspector-url-DuEFmxLP.js";
|
|
2
|
+
import { _ as McpAppHost, a as SidebarControl, b as getRegisteredHosts, c as SidebarTextarea, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, i as SidebarCollapsibleControl, l as SidebarToggle, m as useInspectorState, n as resolveServerToolResult, o as SidebarInput, p as useMcpConnection, r as SidebarCheckbox, s as SidebarSelect, t as Inspector, u as SimpleSidebar, v as SCREEN_WIDTHS, x as registerHostShell, y as getHostShell } from "../inspector-DkS75JCk.js";
|
|
3
|
+
import { t as createInspectorUrl } from "../inspector-url-CyQcuBI9.js";
|
|
5
4
|
import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
|
|
6
5
|
//#region src/inspector/index.ts
|
|
7
6
|
var inspector_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -109,6 +109,19 @@ export interface InspectorUrlParams {
|
|
|
109
109
|
* Enable Prod Resources mode (production dist/ bundles instead of HMR).
|
|
110
110
|
*/
|
|
111
111
|
prodResources?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Show the inspector sidebar. Useful for headless testing or embedding
|
|
114
|
+
* the inspector as a pure resource viewer.
|
|
115
|
+
* @default true
|
|
116
|
+
*/
|
|
117
|
+
sidebar?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Show the dev overlay (resource timestamp + tool timing) inside resources.
|
|
120
|
+
* Set to false to hide it during e2e tests so it doesn't interfere with
|
|
121
|
+
* element assertions.
|
|
122
|
+
* @default true
|
|
123
|
+
*/
|
|
124
|
+
devOverlay?: boolean;
|
|
112
125
|
}
|
|
113
126
|
/**
|
|
114
127
|
* Creates a URL path with query parameters for the Inspector.
|
|
@@ -88,6 +88,8 @@ export interface InspectorState {
|
|
|
88
88
|
prefersBorder: boolean;
|
|
89
89
|
urlTool: string | undefined;
|
|
90
90
|
urlProdResources: boolean | undefined;
|
|
91
|
+
urlSidebar: boolean | undefined;
|
|
92
|
+
urlDevOverlay: boolean | undefined;
|
|
91
93
|
}
|
|
92
94
|
export declare function useInspectorState({ simulations, defaultHost, }: UseInspectorStateOptions): InspectorState;
|
|
93
95
|
export {};
|
|
@@ -2338,6 +2338,10 @@ function parseUrlParams() {
|
|
|
2338
2338
|
const host = params.get("host") ?? void 0;
|
|
2339
2339
|
const prodResourcesParam = params.get("prodResources");
|
|
2340
2340
|
const prodResources = prodResourcesParam === "true" ? true : prodResourcesParam === "false" ? false : void 0;
|
|
2341
|
+
const sidebarParam = params.get("sidebar");
|
|
2342
|
+
const sidebar = sidebarParam === "false" ? false : sidebarParam === "true" ? true : void 0;
|
|
2343
|
+
const devOverlayParam = params.get("devOverlay");
|
|
2344
|
+
const devOverlay = devOverlayParam === "false" ? false : devOverlayParam === "true" ? true : void 0;
|
|
2341
2345
|
const deviceType = params.get("deviceType");
|
|
2342
2346
|
let platform;
|
|
2343
2347
|
if (deviceType === "mobile" || deviceType === "tablet") platform = "mobile";
|
|
@@ -2370,7 +2374,9 @@ function parseUrlParams() {
|
|
|
2370
2374
|
deviceCapabilities,
|
|
2371
2375
|
safeAreaInsets,
|
|
2372
2376
|
host: host ?? void 0,
|
|
2373
|
-
prodResources
|
|
2377
|
+
prodResources,
|
|
2378
|
+
sidebar,
|
|
2379
|
+
devOverlay
|
|
2374
2380
|
};
|
|
2375
2381
|
}
|
|
2376
2382
|
function useInspectorState({ simulations, defaultHost = "chatgpt" }) {
|
|
@@ -2621,7 +2627,9 @@ function useInspectorState({ simulations, defaultHost = "chatgpt" }) {
|
|
|
2621
2627
|
permissions: resourceMeta?.permissions,
|
|
2622
2628
|
prefersBorder: resourceMeta?.prefersBorder ?? false,
|
|
2623
2629
|
urlTool: urlParams.tool,
|
|
2624
|
-
urlProdResources: urlParams.prodResources
|
|
2630
|
+
urlProdResources: urlParams.prodResources,
|
|
2631
|
+
urlSidebar: urlParams.sidebar,
|
|
2632
|
+
urlDevOverlay: urlParams.devOverlay
|
|
2625
2633
|
};
|
|
2626
2634
|
}
|
|
2627
2635
|
//#endregion
|
|
@@ -2655,18 +2663,23 @@ function useMcpConnection(initialServerUrl) {
|
|
|
2655
2663
|
body: JSON.stringify(body)
|
|
2656
2664
|
});
|
|
2657
2665
|
if (!res.ok) {
|
|
2658
|
-
let message
|
|
2666
|
+
let message;
|
|
2659
2667
|
try {
|
|
2660
2668
|
const json = await res.json();
|
|
2661
2669
|
if (json.error) message = json.error;
|
|
2662
2670
|
} catch {}
|
|
2671
|
+
if (!message) if (res.status === 404) message = "Server not found at this URL. Check the URL and make sure the server is running.";
|
|
2672
|
+
else if (res.status >= 500) message = `Server error (${res.status}). Check the MCP server logs for details.`;
|
|
2673
|
+
else message = `Connection failed (${res.status})`;
|
|
2663
2674
|
throw new Error(message);
|
|
2664
2675
|
}
|
|
2665
2676
|
const data = await res.json();
|
|
2666
2677
|
setStatus("connected");
|
|
2667
2678
|
setSimulations(data.simulations ?? void 0);
|
|
2668
2679
|
} catch (err) {
|
|
2669
|
-
|
|
2680
|
+
let message = err instanceof Error ? err.message : String(err);
|
|
2681
|
+
if (err instanceof TypeError && message === "Failed to fetch") message = "Cannot reach MCP server. Is it running?";
|
|
2682
|
+
setError(message);
|
|
2670
2683
|
setStatus("error");
|
|
2671
2684
|
setSimulations(void 0);
|
|
2672
2685
|
}
|
|
@@ -2685,7 +2698,10 @@ function useMcpConnection(initialServerUrl) {
|
|
|
2685
2698
|
try {
|
|
2686
2699
|
const res = await fetch("/__sunpeak/list-tools");
|
|
2687
2700
|
if (cancelled) return;
|
|
2688
|
-
if (!res.ok)
|
|
2701
|
+
if (!res.ok) {
|
|
2702
|
+
const msg = res.status === 404 ? "MCP server not reachable. Is it running?" : `Health check failed (${res.status}). Check the MCP server logs.`;
|
|
2703
|
+
throw new Error(msg);
|
|
2704
|
+
}
|
|
2689
2705
|
setStatus("connected");
|
|
2690
2706
|
} catch (err) {
|
|
2691
2707
|
if (cancelled) return;
|
|
@@ -3196,6 +3212,8 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
3196
3212
|
const [oauthError, setOauthError] = react.useState();
|
|
3197
3213
|
const connection = useMcpConnection(mcpServerUrl || void 0);
|
|
3198
3214
|
const [prodResources, setProdResources] = react.useState(state.urlProdResources ?? defaultProdResources);
|
|
3215
|
+
const showSidebar = state.urlSidebar !== false;
|
|
3216
|
+
const showDevOverlay = state.urlDevOverlay !== false;
|
|
3199
3217
|
const [isRunning, setIsRunning] = react.useState(false);
|
|
3200
3218
|
const [hasRun, setHasRun] = react.useState(false);
|
|
3201
3219
|
const [showCheck, setShowCheck] = react.useState(false);
|
|
@@ -3366,26 +3384,47 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
3366
3384
|
if (!caller || !sim) return;
|
|
3367
3385
|
const toolName = sim.tool.name;
|
|
3368
3386
|
setIsRunning(true);
|
|
3387
|
+
const startTime = performance.now();
|
|
3369
3388
|
try {
|
|
3370
3389
|
const result = await caller({
|
|
3371
3390
|
name: toolName,
|
|
3372
3391
|
arguments: state.toolInput
|
|
3373
3392
|
});
|
|
3374
|
-
|
|
3375
|
-
|
|
3393
|
+
const clientMs = Math.round((performance.now() - startTime) * 10) / 10;
|
|
3394
|
+
const resultMeta = result?._meta;
|
|
3395
|
+
const serverMs = (resultMeta?._sunpeak)?.requestTimeMs;
|
|
3396
|
+
const durationMs = typeof serverMs === "number" ? serverMs : clientMs;
|
|
3397
|
+
const resultWithTiming = {
|
|
3398
|
+
...result,
|
|
3399
|
+
_meta: {
|
|
3400
|
+
...resultMeta,
|
|
3401
|
+
_sunpeak: { requestTimeMs: durationMs }
|
|
3402
|
+
}
|
|
3403
|
+
};
|
|
3404
|
+
state.setToolResult(resultWithTiming);
|
|
3405
|
+
const displayResult = resultMeta?._sunpeak ? (() => {
|
|
3406
|
+
const { _sunpeak: _, ...cleanMeta } = resultMeta;
|
|
3407
|
+
const clean = { ...result };
|
|
3408
|
+
clean._meta = Object.keys(cleanMeta).length > 0 ? cleanMeta : void 0;
|
|
3409
|
+
if (clean._meta === void 0) delete clean._meta;
|
|
3410
|
+
return clean;
|
|
3411
|
+
})() : result;
|
|
3412
|
+
state.setToolResultJson(JSON.stringify(displayResult, null, 2));
|
|
3376
3413
|
state.setToolResultError("");
|
|
3377
3414
|
setHasRun(true);
|
|
3378
3415
|
setShowCheck(true);
|
|
3379
3416
|
clearTimeout(checkTimerRef.current);
|
|
3380
3417
|
checkTimerRef.current = setTimeout(() => setShowCheck(false), 2e3);
|
|
3381
3418
|
} catch (err) {
|
|
3419
|
+
const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
|
|
3382
3420
|
const message = err instanceof Error ? err.message : String(err);
|
|
3383
3421
|
state.setToolResult({
|
|
3384
3422
|
content: [{
|
|
3385
3423
|
type: "text",
|
|
3386
3424
|
text: `Error: ${message}`
|
|
3387
3425
|
}],
|
|
3388
|
-
isError: true
|
|
3426
|
+
isError: true,
|
|
3427
|
+
_meta: { _sunpeak: { requestTimeMs: durationMs } }
|
|
3389
3428
|
});
|
|
3390
3429
|
state.setToolResultJson(JSON.stringify({
|
|
3391
3430
|
content: [{
|
|
@@ -3520,7 +3559,8 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
3520
3559
|
clearTimeout(timer);
|
|
3521
3560
|
};
|
|
3522
3561
|
}, [prodResourcesPath]);
|
|
3523
|
-
const
|
|
3562
|
+
const baseResourceUrl = (prodResourcesPath && prodResourcesReady ? prodResourcesPath : void 0) ?? state.resourceUrl;
|
|
3563
|
+
const effectiveResourceUrl = baseResourceUrl && !showDevOverlay ? `${baseResourceUrl}${baseResourceUrl.includes("?") ? "&" : "?"}devOverlay=false` : baseResourceUrl;
|
|
3524
3564
|
const prodResourcesLoading = !!prodResourcesPath && !prodResourcesReady;
|
|
3525
3565
|
const hasTools = toolNames.length > 0;
|
|
3526
3566
|
const showEmptyState = !(activeSimulationName !== null && currentSim?.toolResult != null) && !hasRun;
|
|
@@ -3638,6 +3678,26 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
3638
3678
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M0 0L10 6L0 12V0Z" })
|
|
3639
3679
|
}), "Run"]
|
|
3640
3680
|
}) : void 0;
|
|
3681
|
+
const conversationContent = ShellConversation ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellConversation, {
|
|
3682
|
+
screenWidth: state.screenWidth,
|
|
3683
|
+
displayMode: state.displayMode,
|
|
3684
|
+
platform: state.platform,
|
|
3685
|
+
onRequestDisplayMode: state.handleDisplayModeChange,
|
|
3686
|
+
appName,
|
|
3687
|
+
appIcon,
|
|
3688
|
+
userMessage,
|
|
3689
|
+
onContentWidthChange: state.handleContentWidthChange,
|
|
3690
|
+
headerAction: runButton,
|
|
3691
|
+
children: content
|
|
3692
|
+
}) : content;
|
|
3693
|
+
if (!showSidebar) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeProvider, {
|
|
3694
|
+
theme: state.theme,
|
|
3695
|
+
applyTheme,
|
|
3696
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3697
|
+
className: "flex h-screen w-screen",
|
|
3698
|
+
children: conversationContent
|
|
3699
|
+
})
|
|
3700
|
+
});
|
|
3641
3701
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeProvider, {
|
|
3642
3702
|
theme: state.theme,
|
|
3643
3703
|
applyTheme,
|
|
@@ -4170,18 +4230,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
|
|
|
4170
4230
|
})
|
|
4171
4231
|
]
|
|
4172
4232
|
}),
|
|
4173
|
-
children:
|
|
4174
|
-
screenWidth: state.screenWidth,
|
|
4175
|
-
displayMode: state.displayMode,
|
|
4176
|
-
platform: state.platform,
|
|
4177
|
-
onRequestDisplayMode: state.handleDisplayModeChange,
|
|
4178
|
-
appName,
|
|
4179
|
-
appIcon,
|
|
4180
|
-
userMessage,
|
|
4181
|
-
onContentWidthChange: state.handleContentWidthChange,
|
|
4182
|
-
headerAction: runButton,
|
|
4183
|
-
children: content
|
|
4184
|
-
}) : content
|
|
4233
|
+
children: conversationContent
|
|
4185
4234
|
})
|
|
4186
4235
|
});
|
|
4187
4236
|
}
|
|
@@ -4319,4 +4368,4 @@ Object.defineProperty(exports, "useThemeContext", {
|
|
|
4319
4368
|
}
|
|
4320
4369
|
});
|
|
4321
4370
|
|
|
4322
|
-
//# sourceMappingURL=inspector-
|
|
4371
|
+
//# sourceMappingURL=inspector-CTMccsz9.cjs.map
|