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/dist/mcp/index.js
CHANGED
|
@@ -9105,6 +9105,10 @@ function injectDefaultDomain(meta, clientName, serverUrl) {
|
|
|
9105
9105
|
//#region src/mcp/server.ts
|
|
9106
9106
|
var localDevServerUrl = "http://localhost:8000";
|
|
9107
9107
|
var localHmrWsUrl = "ws://localhost:24678";
|
|
9108
|
+
var lastToolTimingMs = null;
|
|
9109
|
+
function isDevOverlayEnabled() {
|
|
9110
|
+
return process.env.SUNPEAK_DEV_OVERLAY !== "false";
|
|
9111
|
+
}
|
|
9108
9112
|
/**
|
|
9109
9113
|
* Detect whether this request needs pre-built HTML (no Vite HMR).
|
|
9110
9114
|
*
|
|
@@ -9124,12 +9128,66 @@ function needsProdBuild(headers) {
|
|
|
9124
9128
|
* Read pre-built resource HTML (production mode).
|
|
9125
9129
|
* The HTML file is generated by `sunpeak build` with JS and CSS inlined.
|
|
9126
9130
|
*/
|
|
9127
|
-
function readResourceHtmlProd(distPath) {
|
|
9131
|
+
function readResourceHtmlProd(distPath, resourceName) {
|
|
9128
9132
|
const htmlPath = path.resolve(distPath);
|
|
9129
|
-
if (!fs.existsSync(htmlPath))
|
|
9133
|
+
if (!fs.existsSync(htmlPath)) {
|
|
9134
|
+
const label = resourceName ? `Resource "${resourceName}"` : "Resource";
|
|
9135
|
+
throw new Error(`${label}: built HTML not found at ${htmlPath}. Run "sunpeak build" to generate it.`);
|
|
9136
|
+
}
|
|
9130
9137
|
return fs.readFileSync(htmlPath, "utf8");
|
|
9131
9138
|
}
|
|
9132
9139
|
/**
|
|
9140
|
+
* Generate an inline script that shows a dev overlay with resource served timestamp
|
|
9141
|
+
* and tool call request timing.
|
|
9142
|
+
*
|
|
9143
|
+
* The resource timestamp is baked into the HTML at readResource time. Tool timing
|
|
9144
|
+
* arrives two ways:
|
|
9145
|
+
* 1. Baked-in `toolMs` from readResource time (works when the tool call precedes the
|
|
9146
|
+
* resource read, which is the case for Claude and the inspector's initial render).
|
|
9147
|
+
* 2. `_meta._sunpeak.requestTimeMs` on the tool result PostMessage (handles inspector
|
|
9148
|
+
* Re-run and hosts that pass `_meta` through to the resource iframe).
|
|
9149
|
+
*
|
|
9150
|
+
* NOTE: Keep in sync with bin/lib/dev-overlay.mjs (used by the standalone inspector).
|
|
9151
|
+
*
|
|
9152
|
+
* @param servedAt - Unix timestamp (ms) when the resource HTML was generated/served.
|
|
9153
|
+
* @param toolMs - Most recent tool call duration (ms), or null if no call yet.
|
|
9154
|
+
*/
|
|
9155
|
+
function getDevOverlayScript(servedAt, toolMs) {
|
|
9156
|
+
return `<script>
|
|
9157
|
+
(function(){
|
|
9158
|
+
var servedAt=${servedAt};
|
|
9159
|
+
var el=null,hidden=false,lastMs=${toolMs ?? "null"};
|
|
9160
|
+
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}
|
|
9161
|
+
function make(){
|
|
9162
|
+
var existing=document.getElementById('__sunpeak-dev-timing');
|
|
9163
|
+
if(existing)return existing;
|
|
9164
|
+
var b=document.createElement('button');b.id='__sunpeak-dev-timing';
|
|
9165
|
+
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;';
|
|
9166
|
+
b.onmouseenter=function(){b.style.opacity='1'};
|
|
9167
|
+
b.onmouseleave=function(){b.style.opacity='0.85'};
|
|
9168
|
+
b.onclick=function(){hidden=!hidden;upd()};
|
|
9169
|
+
document.body.appendChild(b);return b;
|
|
9170
|
+
}
|
|
9171
|
+
function upd(){
|
|
9172
|
+
if(!el)el=make();
|
|
9173
|
+
if(hidden){el.title='Show dev info';el.innerHTML='<span style="grid-column:1/-1;font-size:9px;text-align:center">DEV</span>';return}
|
|
9174
|
+
var h='';
|
|
9175
|
+
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>';
|
|
9176
|
+
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>';
|
|
9177
|
+
el.title='Hide dev info';el.innerHTML=h;
|
|
9178
|
+
}
|
|
9179
|
+
upd();
|
|
9180
|
+
window.addEventListener('message',function(e){
|
|
9181
|
+
var d=e.data;if(!d||typeof d!=='object')return;
|
|
9182
|
+
if(d.method!=='ui/notifications/tool-result')return;
|
|
9183
|
+
var p=d.params;if(!p)return;
|
|
9184
|
+
var ms=p._meta&&p._meta._sunpeak&&p._meta._sunpeak.requestTimeMs;
|
|
9185
|
+
if(typeof ms==='number'){lastMs=ms;upd()}
|
|
9186
|
+
});
|
|
9187
|
+
})();
|
|
9188
|
+
<\/script>`;
|
|
9189
|
+
}
|
|
9190
|
+
/**
|
|
9133
9191
|
* Generate HTML that loads from Vite dev server with HMR.
|
|
9134
9192
|
* Used for direct connections (e.g. ChatGPT connecting to localhost).
|
|
9135
9193
|
*/
|
|
@@ -9172,6 +9230,7 @@ function getViteResourceHtml(srcPath) {
|
|
|
9172
9230
|
src: srcPath,
|
|
9173
9231
|
component: componentName
|
|
9174
9232
|
}).toString()}`}"><\/script>
|
|
9233
|
+
${isDevOverlayEnabled() ? getDevOverlayScript(Date.now(), lastToolTimingMs) : ""}
|
|
9175
9234
|
</body>
|
|
9176
9235
|
</html>`;
|
|
9177
9236
|
}
|
|
@@ -9183,7 +9242,9 @@ function getViteResourceHtml(srcPath) {
|
|
|
9183
9242
|
*/
|
|
9184
9243
|
function getResourceHtml(simulation, viteMode, prodBuild) {
|
|
9185
9244
|
if (viteMode && simulation.srcPath && !prodBuild) return getViteResourceHtml(simulation.srcPath);
|
|
9186
|
-
|
|
9245
|
+
let html = readResourceHtmlProd(simulation.distPath, simulation.name);
|
|
9246
|
+
if (isDevOverlayEnabled()) html = html.replace("</body>", `${getDevOverlayScript(Date.now(), lastToolTimingMs)}\n</body>`);
|
|
9247
|
+
return html;
|
|
9187
9248
|
}
|
|
9188
9249
|
/**
|
|
9189
9250
|
* Inject localhost Vite dev server URLs into CSP metadata.
|
|
@@ -9304,14 +9365,29 @@ function createAppServer(config, simulations, viteMode) {
|
|
|
9304
9365
|
const realHandler = simulation.handler;
|
|
9305
9366
|
if (realHandler && (config.prodTools || !hasMockResult)) {
|
|
9306
9367
|
console.log(`[MCP] CallTool: ${tool.name}${argsStr} → live handler`);
|
|
9368
|
+
const startTime = performance.now();
|
|
9307
9369
|
try {
|
|
9308
9370
|
const result = await realHandler(args, extra);
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9371
|
+
const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
|
|
9372
|
+
lastToolTimingMs = durationMs;
|
|
9373
|
+
if (typeof result === "string") return {
|
|
9374
|
+
content: [{
|
|
9375
|
+
type: "text",
|
|
9376
|
+
text: result
|
|
9377
|
+
}],
|
|
9378
|
+
_meta: { _sunpeak: { requestTimeMs: durationMs } }
|
|
9379
|
+
};
|
|
9380
|
+
const typed = result;
|
|
9381
|
+
return {
|
|
9382
|
+
...typed,
|
|
9383
|
+
_meta: {
|
|
9384
|
+
...typed._meta,
|
|
9385
|
+
_sunpeak: { requestTimeMs: durationMs }
|
|
9386
|
+
}
|
|
9387
|
+
};
|
|
9314
9388
|
} catch (error) {
|
|
9389
|
+
const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
|
|
9390
|
+
lastToolTimingMs = durationMs;
|
|
9315
9391
|
const msg = error instanceof Error ? error.message : String(error);
|
|
9316
9392
|
console.error(`[MCP] CallTool error (${tool.name}): ${msg}`);
|
|
9317
9393
|
return {
|
|
@@ -9319,7 +9395,8 @@ function createAppServer(config, simulations, viteMode) {
|
|
|
9319
9395
|
type: "text",
|
|
9320
9396
|
text: `Error: ${msg}`
|
|
9321
9397
|
}],
|
|
9322
|
-
isError: true
|
|
9398
|
+
isError: true,
|
|
9399
|
+
_meta: { _sunpeak: { requestTimeMs: durationMs } }
|
|
9323
9400
|
};
|
|
9324
9401
|
}
|
|
9325
9402
|
}
|
|
@@ -9561,7 +9638,8 @@ function runMCPServer(config) {
|
|
|
9561
9638
|
viteServer.ws.handleUpgrade(request, socket, head);
|
|
9562
9639
|
});
|
|
9563
9640
|
httpServer.on("clientError", (err, socket) => {
|
|
9564
|
-
if (err.code
|
|
9641
|
+
if (err.code === "ECONNRESET") {} else if (err.code === "HPE_INVALID_METHOD" && "rawPacket" in err && Buffer.isBuffer(err.rawPacket) && err.rawPacket[0] === 22) console.error("Received HTTPS request on HTTP server. If you're using ngrok, make sure the upstream is http:// (not https://). Example: ngrok http 8000");
|
|
9642
|
+
else console.error("HTTP client error", err);
|
|
9565
9643
|
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
9566
9644
|
});
|
|
9567
9645
|
let resolveReady;
|
|
@@ -9571,14 +9649,16 @@ function runMCPServer(config) {
|
|
|
9571
9649
|
const onListening = () => {
|
|
9572
9650
|
const actualPort = httpServer.address().port;
|
|
9573
9651
|
localDevServerUrl = `http://localhost:${actualPort}`;
|
|
9574
|
-
console.log(`Sunpeak MCP server listening on http://localhost:${actualPort}`);
|
|
9652
|
+
if (actualPort !== requestedPort) console.log(`Sunpeak MCP server listening on http://localhost:${actualPort} (port ${requestedPort} was in use)`);
|
|
9653
|
+
else console.log(`Sunpeak MCP server listening on http://localhost:${actualPort}`);
|
|
9575
9654
|
console.log(` MCP endpoint: http://localhost:${actualPort}${MCP_PATH$1}`);
|
|
9576
9655
|
if (viteMode) console.log(` Vite HMR: enabled (source files served with hot reload)`);
|
|
9577
9656
|
resolveReady();
|
|
9578
9657
|
};
|
|
9658
|
+
const requestedPort = port;
|
|
9579
9659
|
httpServer.on("error", (err) => {
|
|
9580
9660
|
if (err.code === "EADDRINUSE") {
|
|
9581
|
-
console.warn(`Port ${
|
|
9661
|
+
console.warn(`Port ${requestedPort} is in use, trying another port...`);
|
|
9582
9662
|
httpServer.listen(0);
|
|
9583
9663
|
} else throw err;
|
|
9584
9664
|
});
|
|
@@ -10131,19 +10211,22 @@ function startProductionHttpServer(config, portOrOptions) {
|
|
|
10131
10211
|
}
|
|
10132
10212
|
});
|
|
10133
10213
|
httpServer.on("clientError", (err, socket) => {
|
|
10134
|
-
log("error", "HTTP
|
|
10214
|
+
if (err.code === "HPE_INVALID_METHOD" && "rawPacket" in err && Buffer.isBuffer(err.rawPacket) && err.rawPacket[0] === 22) log("error", "Received HTTPS request on HTTP server. If you're using ngrok, make sure the upstream is http:// (not https://). Example: ngrok http 8000");
|
|
10215
|
+
else log("error", "HTTP client error", { error: err.message });
|
|
10135
10216
|
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
10136
10217
|
});
|
|
10137
10218
|
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
10219
|
+
const requestedPort = port;
|
|
10138
10220
|
const onListening = () => {
|
|
10139
10221
|
const addr = httpServer.address();
|
|
10140
|
-
log("info", `Server listening on http://${displayHost}:${addr.port}`);
|
|
10222
|
+
if (addr.port !== requestedPort) log("info", `Server listening on http://${displayHost}:${addr.port} (port ${requestedPort} was in use)`);
|
|
10223
|
+
else log("info", `Server listening on http://${displayHost}:${addr.port}`);
|
|
10141
10224
|
log("info", `MCP endpoint: http://${displayHost}:${addr.port}${MCP_PATH}`);
|
|
10142
10225
|
log("info", `Health check: http://${displayHost}:${addr.port}/health`);
|
|
10143
10226
|
};
|
|
10144
10227
|
httpServer.on("error", (err) => {
|
|
10145
10228
|
if (err.code === "EADDRINUSE") {
|
|
10146
|
-
log("warn", `Port ${
|
|
10229
|
+
log("warn", `Port ${requestedPort} is in use, trying another port...`);
|
|
10147
10230
|
httpServer.listen(0, host);
|
|
10148
10231
|
} else throw err;
|
|
10149
10232
|
});
|