sunpeak 0.16.28 → 0.17.1
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 +169 -342
- package/bin/commands/inspect.mjs +763 -0
- package/bin/commands/new.mjs +2 -2
- package/bin/lib/inspect/inspect-config.d.mts +20 -0
- package/bin/lib/inspect/inspect-config.mjs +76 -0
- package/bin/lib/live/global-setup.mjs +6 -1
- package/bin/sunpeak.js +11 -1
- package/dist/chatgpt/globals.css +35 -18
- package/dist/chatgpt/index.cjs +3 -11
- package/dist/chatgpt/index.cjs.map +1 -1
- package/dist/chatgpt/index.d.ts +2 -2
- package/dist/chatgpt/index.js +4 -8
- package/dist/chatgpt/index.js.map +1 -1
- package/dist/claude/index.cjs +1 -1
- package/dist/claude/index.js +1 -1
- package/dist/discovery-Cgoegt62.js +114 -0
- package/dist/discovery-Cgoegt62.js.map +1 -0
- package/dist/discovery-Clu4uHp1.cjs +161 -0
- package/dist/discovery-Clu4uHp1.cjs.map +1 -0
- package/dist/index.cjs +1 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/discovery-cli.cjs +1 -1
- package/dist/lib/discovery-cli.js +1 -1
- package/dist/lib/discovery.d.ts +7 -67
- package/dist/lib/index.d.ts +0 -1
- package/dist/mcp/index.cjs +34 -23
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +34 -23
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/types.d.ts +5 -0
- package/dist/simulator/index.cjs +5 -11
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.ts +4 -2
- package/dist/simulator/index.js +5 -8
- package/dist/simulator/index.js.map +1 -1
- package/dist/simulator/simple-sidebar.d.ts +7 -4
- package/dist/simulator/simulator-url.d.ts +8 -0
- package/dist/simulator/simulator.d.ts +15 -2
- package/dist/simulator/use-mcp-connection.d.ts +19 -0
- package/dist/{simulator-BYIH-xqQ.cjs → simulator-CH9hs0N6.cjs} +159 -52
- package/dist/simulator-CH9hs0N6.cjs.map +1 -0
- package/dist/{simulator-CmgNnWBO.js → simulator-Dl8B-Ljb.js} +154 -53
- package/dist/simulator-Dl8B-Ljb.js.map +1 -0
- package/dist/{simulator-url-BDGD4vZD.cjs → simulator-url-CozKF1jf.cjs} +3 -1
- package/dist/simulator-url-CozKF1jf.cjs.map +1 -0
- package/dist/{simulator-url-Bkxj43yT.js → simulator-url-KoS_ToP6.js} +3 -1
- package/dist/simulator-url-KoS_ToP6.js.map +1 -0
- package/dist/style.css +35 -18
- package/package.json +9 -1
- package/template/dist/albums/albums.html +105 -0
- package/template/dist/albums/albums.json +16 -0
- package/template/dist/carousel/carousel.html +105 -0
- package/template/dist/carousel/carousel.json +16 -0
- package/template/dist/map/map.html +3060 -0
- package/template/dist/map/map.json +22 -0
- package/template/dist/review/review.html +105 -0
- package/template/dist/review/review.json +16 -0
- package/template/dist/server.js +15 -0
- package/template/dist/tools/review-diff.js +50 -0
- package/template/dist/tools/review-post.js +50 -0
- package/template/dist/tools/review-purchase.js +61 -0
- package/template/dist/tools/review.js +31 -0
- package/template/dist/tools/show-albums.js +56 -0
- package/template/dist/tools/show-carousel.js +41 -0
- package/template/dist/tools/show-map.js +47 -0
- package/template/node_modules/.vite/deps/_metadata.json +8 -0
- package/template/node_modules/.vite/deps/package.json +3 -0
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +500 -0
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +563 -0
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +575 -0
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/@testing-library_react.js +11363 -0
- package/template/node_modules/.vite-mcp/deps/@testing-library_react.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/_metadata.json +130 -0
- package/template/node_modules/.vite-mcp/deps/chunk-BoAXSpZd.js +33 -0
- package/template/node_modules/.vite-mcp/deps/client-CU1wWud4.js +14385 -0
- package/template/node_modules/.vite-mcp/deps/client-CU1wWud4.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/clsx.js +18 -0
- package/template/node_modules/.vite-mcp/deps/clsx.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/dist-uWX8WbjY.js +505 -0
- package/template/node_modules/.vite-mcp/deps/dist-uWX8WbjY.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/embla-carousel-react.js +1461 -0
- package/template/node_modules/.vite-mcp/deps/embla-carousel-react.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/embla-carousel-wheel-gestures.js +536 -0
- package/template/node_modules/.vite-mcp/deps/embla-carousel-wheel-gestures.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/magic-string.es-Cklsmr-5.js +1013 -0
- package/template/node_modules/.vite-mcp/deps/magic-string.es-Cklsmr-5.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/mapbox-gl.js +46311 -0
- package/template/node_modules/.vite-mcp/deps/mapbox-gl.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/package.json +3 -0
- package/template/node_modules/.vite-mcp/deps/protocol-CTflwIfG.js +2090 -0
- package/template/node_modules/.vite-mcp/deps/protocol-CTflwIfG.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/react-dom.js +186 -0
- package/template/node_modules/.vite-mcp/deps/react-dom.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/react-dom_client.js +2 -0
- package/template/node_modules/.vite-mcp/deps/react.js +769 -0
- package/template/node_modules/.vite-mcp/deps/react.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/react_jsx-dev-runtime.js +205 -0
- package/template/node_modules/.vite-mcp/deps/react_jsx-dev-runtime.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/react_jsx-runtime.js +209 -0
- package/template/node_modules/.vite-mcp/deps/react_jsx-runtime.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/schemas-NsgmY9QV.js +12157 -0
- package/template/node_modules/.vite-mcp/deps/schemas-NsgmY9QV.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/tailwind-merge.js +2025 -0
- package/template/node_modules/.vite-mcp/deps/tailwind-merge.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/vitest.js +14021 -0
- package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/zod.js +624 -0
- package/template/node_modules/.vite-mcp/deps/zod.js.map +1 -0
- package/template/src/tools/review-diff.test.ts +5 -1
- package/template/src/tools/review-diff.ts +1 -1
- package/template/src/tools/review-post.test.ts +5 -1
- package/template/src/tools/review-post.ts +1 -1
- package/template/src/tools/review-purchase.test.ts +5 -1
- package/template/src/tools/review-purchase.ts +1 -1
- package/template/src/tools/review.test.ts +5 -1
- package/template/src/tools/review.ts +1 -1
- package/template/src/tools/show-albums.test.ts +5 -1
- package/template/src/tools/show-albums.ts +1 -1
- package/template/src/tools/show-carousel.test.ts +5 -1
- package/template/src/tools/show-carousel.ts +1 -1
- package/template/src/tools/show-map.test.ts +5 -1
- package/template/src/tools/show-map.ts +1 -1
- package/template/tests/e2e/map.spec.ts +4 -2
- package/dist/discovery-BxKCIgG5.cjs +0 -332
- package/dist/discovery-BxKCIgG5.cjs.map +0 -1
- package/dist/discovery-Du4LHrih.js +0 -261
- package/dist/discovery-Du4LHrih.js.map +0 -1
- package/dist/simulator-BYIH-xqQ.cjs.map +0 -1
- package/dist/simulator-CmgNnWBO.js.map +0 -1
- package/dist/simulator-url-BDGD4vZD.cjs.map +0 -1
- package/dist/simulator-url-Bkxj43yT.js.map +0 -1
- package/template/.sunpeak/dev.tsx +0 -79
- package/template/.sunpeak/resource-loader.html +0 -20
- package/template/.sunpeak/resource-loader.tsx +0 -57
- package/template/index.html +0 -14
- package/template/src/resources/index.ts +0 -17
package/bin/commands/new.mjs
CHANGED
|
@@ -170,8 +170,8 @@ export async function init(projectName, resourcesArg, deps = defaultDeps) {
|
|
|
170
170
|
filter: (src) => {
|
|
171
171
|
const name = basename(src);
|
|
172
172
|
|
|
173
|
-
// Skip node_modules and
|
|
174
|
-
if (name === 'node_modules' || name === 'pnpm-lock.yaml') {
|
|
173
|
+
// Skip node_modules, lock file, and legacy dev bootstrap files
|
|
174
|
+
if (name === 'node_modules' || name === 'pnpm-lock.yaml' || name === '.sunpeak' || name === 'index.html') {
|
|
175
175
|
return false;
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface InspectConfigOptions {
|
|
2
|
+
/** MCP server URL or stdio command string (required) */
|
|
3
|
+
server: string;
|
|
4
|
+
/** Test directory (default: 'tests/e2e') */
|
|
5
|
+
testDir?: string;
|
|
6
|
+
/** Simulation JSON directory (opt-in, fixtures loaded only when specified) */
|
|
7
|
+
simulationsDir?: string;
|
|
8
|
+
/** Host shells to test (default: ['chatgpt', 'claude']) */
|
|
9
|
+
hosts?: ('chatgpt' | 'claude')[];
|
|
10
|
+
/** App name in simulator chrome */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** Additional Playwright `use` options */
|
|
13
|
+
use?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a complete Playwright config for testing an external MCP server
|
|
18
|
+
* using the sunpeak simulator.
|
|
19
|
+
*/
|
|
20
|
+
export function defineInspectConfig(options: InspectConfigOptions): Record<string, unknown>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright config factory for inspect mode (BYOS — Bring Your Own Server).
|
|
3
|
+
*
|
|
4
|
+
* Generates a complete Playwright config that starts `sunpeak inspect` as the
|
|
5
|
+
* webServer and runs e2e tests against the simulator. Follows the same pattern
|
|
6
|
+
* as `defineLiveConfig` for live tests.
|
|
7
|
+
*
|
|
8
|
+
* Usage in playwright.config.ts:
|
|
9
|
+
* import { defineInspectConfig } from 'sunpeak/test/inspect/config';
|
|
10
|
+
* export default defineInspectConfig({
|
|
11
|
+
* server: 'http://localhost:8000/mcp',
|
|
12
|
+
* });
|
|
13
|
+
*/
|
|
14
|
+
import { getPortSync } from '../get-port.mjs';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a complete Playwright config for testing an external MCP server.
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} options
|
|
20
|
+
* @param {string} options.server - MCP server URL or stdio command (required)
|
|
21
|
+
* @param {string} [options.testDir='tests/e2e'] - Test directory
|
|
22
|
+
* @param {string} [options.simulationsDir='tests/simulations'] - Simulation JSON directory
|
|
23
|
+
* @param {string[]} [options.hosts=['chatgpt', 'claude']] - Host shells to test
|
|
24
|
+
* @param {string} [options.name] - App name in simulator chrome
|
|
25
|
+
* @param {Object} [options.use] - Additional Playwright `use` options
|
|
26
|
+
* @returns {import('@playwright/test').PlaywrightTestConfig}
|
|
27
|
+
*/
|
|
28
|
+
export function defineInspectConfig(options) {
|
|
29
|
+
const {
|
|
30
|
+
server,
|
|
31
|
+
testDir = 'tests/e2e',
|
|
32
|
+
simulationsDir,
|
|
33
|
+
hosts = ['chatgpt', 'claude'],
|
|
34
|
+
name,
|
|
35
|
+
use: userUse,
|
|
36
|
+
} = options;
|
|
37
|
+
|
|
38
|
+
if (!server) {
|
|
39
|
+
throw new Error('defineInspectConfig: `server` option is required');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const port = Number(process.env.SUNPEAK_TEST_PORT) || getPortSync(6776);
|
|
43
|
+
const sandboxPort = Number(process.env.SUNPEAK_SANDBOX_PORT) || getPortSync(24680);
|
|
44
|
+
|
|
45
|
+
// Build the sunpeak inspect command
|
|
46
|
+
const serverArg = server.includes(' ') ? `"${server}"` : server;
|
|
47
|
+
const command = [
|
|
48
|
+
'npx sunpeak inspect',
|
|
49
|
+
`--server ${serverArg}`,
|
|
50
|
+
...(simulationsDir ? [`--simulations ${simulationsDir}`] : []),
|
|
51
|
+
`--port ${port}`,
|
|
52
|
+
...(name ? [`--name "${name}"`] : []),
|
|
53
|
+
].join(' ');
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
testDir,
|
|
57
|
+
fullyParallel: true,
|
|
58
|
+
forbidOnly: !!process.env.CI,
|
|
59
|
+
retries: process.env.CI ? 2 : 1,
|
|
60
|
+
// Limit workers to avoid overwhelming the double-iframe sandbox proxy.
|
|
61
|
+
workers: process.env.CI ? 1 : 2,
|
|
62
|
+
reporter: 'list',
|
|
63
|
+
use: {
|
|
64
|
+
baseURL: `http://localhost:${port}`,
|
|
65
|
+
trace: 'on-first-retry',
|
|
66
|
+
...userUse,
|
|
67
|
+
},
|
|
68
|
+
projects: hosts.map((host) => ({ name: host })),
|
|
69
|
+
webServer: {
|
|
70
|
+
command: `SUNPEAK_SANDBOX_PORT=${sandboxPort} ${command}`,
|
|
71
|
+
url: `http://localhost:${port}/health`,
|
|
72
|
+
reuseExistingServer: !process.env.CI,
|
|
73
|
+
timeout: 60_000,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -23,7 +23,12 @@ import { dirname } from 'path';
|
|
|
23
23
|
import { ANTI_BOT_ARGS, CHROME_USER_AGENT, resolvePlaywright, getAppName } from './utils.mjs';
|
|
24
24
|
import { ChatGPTPage, CHATGPT_SELECTORS, CHATGPT_URLS } from './chatgpt-page.mjs';
|
|
25
25
|
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Auth file retention window. The file is kept for 24 hours, but sessions
|
|
28
|
+
* typically expire much sooner (a few hours) because Cloudflare's HttpOnly
|
|
29
|
+
* `cf_clearance` cookie cannot be captured by storageState(). When it
|
|
30
|
+
* expires, the next run will need re-authentication even if the file is fresh.
|
|
31
|
+
*/
|
|
27
32
|
const MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
28
33
|
|
|
29
34
|
const CHATGPT_URL = CHATGPT_URLS.base;
|
package/bin/sunpeak.js
CHANGED
|
@@ -37,7 +37,7 @@ function getVersion() {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// Commands that don't require a package.json
|
|
40
|
-
const standaloneCommands = ['new', 'upgrade', 'help', undefined];
|
|
40
|
+
const standaloneCommands = ['new', 'upgrade', 'inspect', 'help', undefined];
|
|
41
41
|
|
|
42
42
|
if (command && !standaloneCommands.includes(command)) {
|
|
43
43
|
checkPackageJson();
|
|
@@ -72,6 +72,13 @@ function getVersion() {
|
|
|
72
72
|
}
|
|
73
73
|
break;
|
|
74
74
|
|
|
75
|
+
case 'inspect':
|
|
76
|
+
{
|
|
77
|
+
const { inspect } = await import(join(COMMANDS_DIR, 'inspect.mjs'));
|
|
78
|
+
await inspect(args);
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
|
|
75
82
|
case 'upgrade':
|
|
76
83
|
{
|
|
77
84
|
const { upgrade } = await import(join(COMMANDS_DIR, 'upgrade.mjs'));
|
|
@@ -100,6 +107,9 @@ Usage:
|
|
|
100
107
|
sunpeak build Build resources + tools for production
|
|
101
108
|
sunpeak start Start production MCP server
|
|
102
109
|
--port, -p Server port (default: 8000, or PORT env)
|
|
110
|
+
sunpeak inspect Test an external MCP server in the simulator
|
|
111
|
+
--server, -s <url|cmd> MCP server URL or stdio command (required)
|
|
112
|
+
--simulations <dir> Simulation JSON directory
|
|
103
113
|
sunpeak upgrade Upgrade sunpeak to latest version
|
|
104
114
|
sunpeak --version Show version number
|
|
105
115
|
|
package/dist/chatgpt/globals.css
CHANGED
|
@@ -806,6 +806,10 @@
|
|
|
806
806
|
height: calc(var(--spacing) * 6);
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
+
.h-2 {
|
|
810
|
+
height: calc(var(--spacing) * 2);
|
|
811
|
+
}
|
|
812
|
+
|
|
809
813
|
.h-3 {
|
|
810
814
|
height: calc(var(--spacing) * 3);
|
|
811
815
|
}
|
|
@@ -898,6 +902,10 @@
|
|
|
898
902
|
width: calc(var(--spacing) * 1);
|
|
899
903
|
}
|
|
900
904
|
|
|
905
|
+
.w-2 {
|
|
906
|
+
width: calc(var(--spacing) * 2);
|
|
907
|
+
}
|
|
908
|
+
|
|
901
909
|
.w-3 {
|
|
902
910
|
width: calc(var(--spacing) * 3);
|
|
903
911
|
}
|
|
@@ -938,10 +946,6 @@
|
|
|
938
946
|
width: calc(var(--spacing) * 40);
|
|
939
947
|
}
|
|
940
948
|
|
|
941
|
-
.w-56 {
|
|
942
|
-
width: calc(var(--spacing) * 56);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
949
|
.w-\[18px\] {
|
|
946
950
|
width: 18px;
|
|
947
951
|
}
|
|
@@ -1056,10 +1060,6 @@
|
|
|
1056
1060
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
1057
1061
|
}
|
|
1058
1062
|
|
|
1059
|
-
.grid-cols-\[1fr_auto_1fr\] {
|
|
1060
|
-
grid-template-columns: 1fr auto 1fr;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
1063
|
.grid-cols-\[2fr_1fr\] {
|
|
1064
1064
|
grid-template-columns: 2fr 1fr;
|
|
1065
1065
|
}
|
|
@@ -1100,10 +1100,6 @@
|
|
|
1100
1100
|
justify-content: flex-end;
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
|
-
.justify-start {
|
|
1104
|
-
justify-content: flex-start;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
1103
|
.gap-0\.5 {
|
|
1108
1104
|
gap: calc(var(--spacing) * .5);
|
|
1109
1105
|
}
|
|
@@ -1224,8 +1220,12 @@
|
|
|
1224
1220
|
border-radius: 10px;
|
|
1225
1221
|
}
|
|
1226
1222
|
|
|
1227
|
-
.rounded-\[
|
|
1228
|
-
border-radius:
|
|
1223
|
+
.rounded-\[22px\] {
|
|
1224
|
+
border-radius: 22px;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
.rounded-\[28px\] {
|
|
1228
|
+
border-radius: 28px;
|
|
1229
1229
|
}
|
|
1230
1230
|
|
|
1231
1231
|
.rounded-full {
|
|
@@ -1470,10 +1470,18 @@
|
|
|
1470
1470
|
padding: calc(var(--spacing) * 1.5);
|
|
1471
1471
|
}
|
|
1472
1472
|
|
|
1473
|
+
.p-1\.25 {
|
|
1474
|
+
padding: calc(var(--spacing) * 1.25);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1473
1477
|
.p-2 {
|
|
1474
1478
|
padding: calc(var(--spacing) * 2);
|
|
1475
1479
|
}
|
|
1476
1480
|
|
|
1481
|
+
.p-2\.5 {
|
|
1482
|
+
padding: calc(var(--spacing) * 2.5);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1477
1485
|
.p-3 {
|
|
1478
1486
|
padding: calc(var(--spacing) * 3);
|
|
1479
1487
|
}
|
|
@@ -1640,6 +1648,11 @@
|
|
|
1640
1648
|
font-size: 11px;
|
|
1641
1649
|
}
|
|
1642
1650
|
|
|
1651
|
+
.leading-6 {
|
|
1652
|
+
--tw-leading: calc(var(--spacing) * 6);
|
|
1653
|
+
line-height: calc(var(--spacing) * 6);
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1643
1656
|
.leading-none {
|
|
1644
1657
|
--tw-leading: 1;
|
|
1645
1658
|
line-height: 1;
|
|
@@ -1750,13 +1763,13 @@
|
|
|
1750
1763
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1751
1764
|
}
|
|
1752
1765
|
|
|
1753
|
-
.shadow-\[
|
|
1754
|
-
--tw-shadow: 0px 0px 0px 1px var(--tw-shadow-color,
|
|
1766
|
+
.shadow-\[0px_0px_0px_1px_var\(--color-border-primary\)\,0px_4px_12px_rgba\(0\,0\,0\,0\.12\)\] {
|
|
1767
|
+
--tw-shadow: 0px 0px 0px 1px var(--tw-shadow-color, var(--color-border-primary)), 0px 4px 12px var(--tw-shadow-color, #0000001f);
|
|
1755
1768
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1756
1769
|
}
|
|
1757
1770
|
|
|
1758
|
-
.shadow-\[
|
|
1759
|
-
--tw-shadow: 0px 0px 0px 1px var(--tw-shadow-color,
|
|
1771
|
+
.shadow-\[0px_0px_0px_1px_var\(--color-border-primary\)\,0px_6px_20px_rgba\(0\,0\,0\,0\.1\)\] {
|
|
1772
|
+
--tw-shadow: 0px 0px 0px 1px var(--tw-shadow-color, var(--color-border-primary)), 0px 6px 20px var(--tw-shadow-color, #0000001a);
|
|
1760
1773
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1761
1774
|
}
|
|
1762
1775
|
|
|
@@ -2089,6 +2102,10 @@
|
|
|
2089
2102
|
}
|
|
2090
2103
|
|
|
2091
2104
|
@media (min-width: 48rem) {
|
|
2105
|
+
.md\:-start-6 {
|
|
2106
|
+
inset-inline-start: calc(var(--spacing) * -6);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2092
2109
|
.md\:z-20 {
|
|
2093
2110
|
z-index: 20;
|
|
2094
2111
|
}
|
package/dist/chatgpt/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../chunk-9hOWP6kD.cjs");
|
|
3
3
|
require("../protocol-jbxhzcnS.cjs");
|
|
4
|
-
const require_simulator = require("../simulator-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
4
|
+
const require_simulator = require("../simulator-CH9hs0N6.cjs");
|
|
5
|
+
const require_simulator_url = require("../simulator-url-CozKF1jf.cjs");
|
|
6
|
+
const require_discovery = require("../discovery-Clu4uHp1.cjs");
|
|
7
7
|
//#region src/chatgpt/index.ts
|
|
8
8
|
var chatgpt_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
9
9
|
IframeResource: () => require_simulator.IframeResource,
|
|
@@ -11,10 +11,6 @@ var chatgpt_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
|
11
11
|
SCREEN_WIDTHS: () => require_simulator.SCREEN_WIDTHS,
|
|
12
12
|
Simulator: () => require_simulator.Simulator,
|
|
13
13
|
ThemeProvider: () => require_simulator.ThemeProvider,
|
|
14
|
-
buildDevSimulations: () => require_discovery.buildDevSimulations,
|
|
15
|
-
buildResourceMap: () => require_discovery.buildResourceMap,
|
|
16
|
-
buildSimulations: () => require_discovery.buildSimulations,
|
|
17
|
-
createResourceExports: () => require_discovery.createResourceExports,
|
|
18
14
|
createSimulatorUrl: () => require_simulator_url.createSimulatorUrl,
|
|
19
15
|
extractResourceCSP: () => require_simulator.extractResourceCSP,
|
|
20
16
|
extractResourceKey: () => require_discovery.extractResourceKey,
|
|
@@ -32,16 +28,12 @@ exports.McpAppHost = require_simulator.McpAppHost;
|
|
|
32
28
|
exports.SCREEN_WIDTHS = require_simulator.SCREEN_WIDTHS;
|
|
33
29
|
exports.Simulator = require_simulator.Simulator;
|
|
34
30
|
exports.ThemeProvider = require_simulator.ThemeProvider;
|
|
35
|
-
exports.buildDevSimulations = require_discovery.buildDevSimulations;
|
|
36
|
-
exports.buildResourceMap = require_discovery.buildResourceMap;
|
|
37
|
-
exports.buildSimulations = require_discovery.buildSimulations;
|
|
38
31
|
Object.defineProperty(exports, "chatgpt_exports", {
|
|
39
32
|
enumerable: true,
|
|
40
33
|
get: function() {
|
|
41
34
|
return chatgpt_exports;
|
|
42
35
|
}
|
|
43
36
|
});
|
|
44
|
-
exports.createResourceExports = require_discovery.createResourceExports;
|
|
45
37
|
exports.createSimulatorUrl = require_simulator_url.createSimulatorUrl;
|
|
46
38
|
exports.extractResourceCSP = require_simulator.extractResourceCSP;
|
|
47
39
|
exports.extractResourceKey = require_discovery.extractResourceKey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities\nexport {\n toPascalCase,\n extractResourceKey,\n extractSimulationKey,\n findResourceKey,\n getComponentName,\n findResourceDirs,\n} from '../lib/discovery';\nexport type { ResourceDirInfo, FsOps } from '../lib/discovery';\n"],"mappings":""}
|
package/dist/chatgpt/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export type { ResourceCSP } from '../simulator/iframe-resource';
|
|
|
10
10
|
export * from '../simulator/theme-provider';
|
|
11
11
|
export { createSimulatorUrl } from '../simulator/simulator-url';
|
|
12
12
|
export type { SimulatorUrlParams } from '../simulator/simulator-url';
|
|
13
|
-
export {
|
|
14
|
-
export type {
|
|
13
|
+
export { toPascalCase, extractResourceKey, extractSimulationKey, findResourceKey, getComponentName, findResourceDirs, } from '../lib/discovery';
|
|
14
|
+
export type { ResourceDirInfo, FsOps } from '../lib/discovery';
|
package/dist/chatgpt/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
|
|
2
2
|
import "../protocol-DJmRaBzO.js";
|
|
3
|
-
import { _ as
|
|
4
|
-
import {
|
|
5
|
-
import { t as
|
|
3
|
+
import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Simulator, v as SCREEN_WIDTHS } from "../simulator-Dl8B-Ljb.js";
|
|
4
|
+
import { t as createSimulatorUrl } from "../simulator-url-KoS_ToP6.js";
|
|
5
|
+
import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
|
|
6
6
|
//#region src/chatgpt/index.ts
|
|
7
7
|
var chatgpt_exports = /* @__PURE__ */ __exportAll({
|
|
8
8
|
IframeResource: () => IframeResource,
|
|
@@ -10,10 +10,6 @@ var chatgpt_exports = /* @__PURE__ */ __exportAll({
|
|
|
10
10
|
SCREEN_WIDTHS: () => SCREEN_WIDTHS,
|
|
11
11
|
Simulator: () => Simulator,
|
|
12
12
|
ThemeProvider: () => ThemeProvider,
|
|
13
|
-
buildDevSimulations: () => buildDevSimulations,
|
|
14
|
-
buildResourceMap: () => buildResourceMap,
|
|
15
|
-
buildSimulations: () => buildSimulations,
|
|
16
|
-
createResourceExports: () => createResourceExports,
|
|
17
13
|
createSimulatorUrl: () => createSimulatorUrl,
|
|
18
14
|
extractResourceCSP: () => extractResourceCSP,
|
|
19
15
|
extractResourceKey: () => extractResourceKey,
|
|
@@ -26,6 +22,6 @@ var chatgpt_exports = /* @__PURE__ */ __exportAll({
|
|
|
26
22
|
useThemeContext: () => useThemeContext
|
|
27
23
|
});
|
|
28
24
|
//#endregion
|
|
29
|
-
export { IframeResource, McpAppHost, SCREEN_WIDTHS, Simulator, ThemeProvider,
|
|
25
|
+
export { IframeResource, McpAppHost, SCREEN_WIDTHS, Simulator, ThemeProvider, createSimulatorUrl, extractResourceCSP, extractResourceKey, extractSimulationKey, findResourceDirs, findResourceKey, getComponentName, resolveServerToolResult, chatgpt_exports as t, toPascalCase, useThemeContext };
|
|
30
26
|
|
|
31
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT-specific exports for the Sunpeak simulator.\n *\n * @module sunpeak/chatgpt\n */\n\n// Register ChatGPT host shell (side effect)\nimport './chatgpt-host';\n\n// Simulator\nexport { Simulator } from '../simulator/simulator';\n\n// Simulator types\nexport type { Simulation, ServerToolMock } from '../types/simulation';\nexport { resolveServerToolResult } from '../types/simulation';\nexport type { ScreenWidth, SimulatorConfig } from '../simulator/simulator-types';\nexport { SCREEN_WIDTHS } from '../simulator/simulator-types';\n\n// Host bridge (for building custom simulators or test harnesses)\nexport { McpAppHost } from '../simulator/mcp-app-host';\nexport type { McpAppHostOptions } from '../simulator/mcp-app-host';\n\n// Iframe rendering (used internally by simulator)\nexport { IframeResource, extractResourceCSP } from '../simulator/iframe-resource';\nexport type { ResourceCSP } from '../simulator/iframe-resource';\n\n// Theme provider\nexport * from '../simulator/theme-provider';\n\n// URL helpers\nexport { createSimulatorUrl } from '../simulator/simulator-url';\nexport type { SimulatorUrlParams } from '../simulator/simulator-url';\n\n// Discovery utilities\nexport {\n toPascalCase,\n extractResourceKey,\n extractSimulationKey,\n findResourceKey,\n getComponentName,\n findResourceDirs,\n} from '../lib/discovery';\nexport type { ResourceDirInfo, FsOps } from '../lib/discovery';\n"],"mappings":""}
|
package/dist/claude/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../chunk-9hOWP6kD.cjs");
|
|
3
3
|
require("../protocol-jbxhzcnS.cjs");
|
|
4
|
-
const require_simulator = require("../simulator-
|
|
4
|
+
const require_simulator = require("../simulator-CH9hs0N6.cjs");
|
|
5
5
|
exports.Simulator = require_simulator.Simulator;
|
package/dist/claude/index.js
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//#region src/lib/discovery.ts
|
|
2
|
+
/**
|
|
3
|
+
* Discovery utilities for resources, tools, and simulations.
|
|
4
|
+
*
|
|
5
|
+
* String helpers (toPascalCase, extractResourceKey, etc.) are used by both
|
|
6
|
+
* browser and Node.js code. Node.js utilities (findResourceDirs, findToolFiles,
|
|
7
|
+
* etc.) are used by CLI commands for build-time and runtime discovery.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Convert a kebab-case string to PascalCase
|
|
11
|
+
* @example toPascalCase('review') // 'Review'
|
|
12
|
+
* @example toPascalCase('album-art') // 'AlbumArt'
|
|
13
|
+
*/
|
|
14
|
+
function toPascalCase(str) {
|
|
15
|
+
return str.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract the resource key from a resource file path.
|
|
19
|
+
* Matches {name}.tsx (e.g., './albums/albums.tsx' → 'albums')
|
|
20
|
+
*/
|
|
21
|
+
function extractResourceKey(path) {
|
|
22
|
+
const match = path.match(/([^/]+)\.tsx$/);
|
|
23
|
+
return match ? match[1] : void 0;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract the simulation key from a simulation file path.
|
|
27
|
+
* Matches any *.json file (e.g., './show-albums.json' → 'show-albums')
|
|
28
|
+
*/
|
|
29
|
+
function extractSimulationKey(path) {
|
|
30
|
+
const match = path.match(/([^/]+)\.json$/);
|
|
31
|
+
return match ? match[1] : void 0;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Find the best matching resource key for a simulation key.
|
|
35
|
+
* Matches the longest resource name that is a prefix of the simulation key.
|
|
36
|
+
* @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'
|
|
37
|
+
* @example findResourceKey('albums', ['albums', 'review']) // 'albums'
|
|
38
|
+
*/
|
|
39
|
+
function findResourceKey(simulationKey, resourceKeys) {
|
|
40
|
+
const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);
|
|
41
|
+
for (const resourceKey of sorted) if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + "-")) return resourceKey;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the expected component export name for a resource
|
|
45
|
+
* @example getComponentName('review') // 'ReviewResource'
|
|
46
|
+
* @example getComponentName('album-art') // 'AlbumArtResource'
|
|
47
|
+
*/
|
|
48
|
+
function getComponentName(resourceKey) {
|
|
49
|
+
return `${toPascalCase(resourceKey)}Resource`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Find all resource directories in a base directory.
|
|
53
|
+
* Each valid resource directory contains a file matching the expected pattern.
|
|
54
|
+
*
|
|
55
|
+
* @param baseDir - Base directory to scan (e.g., 'src/resources' or 'dist')
|
|
56
|
+
* @param filePattern - Function to generate expected filename from resource key
|
|
57
|
+
* @param fs - File system operations (for testing)
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Find source resources (tsx files)
|
|
61
|
+
* const resources = findResourceDirs('src/resources', key => `${key}.tsx`);
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Find built resources (js files)
|
|
65
|
+
* const resources = findResourceDirs('dist', key => `${key}.js`);
|
|
66
|
+
*/
|
|
67
|
+
function findResourceDirs(baseDir, filePattern, fs) {
|
|
68
|
+
if (!fs.existsSync(baseDir)) return [];
|
|
69
|
+
return fs.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
|
|
70
|
+
const key = entry.name;
|
|
71
|
+
const dir = `${baseDir}/${key}`;
|
|
72
|
+
const resourcePath = `${dir}/${filePattern(key)}`;
|
|
73
|
+
if (!fs.existsSync(resourcePath)) return null;
|
|
74
|
+
return {
|
|
75
|
+
key,
|
|
76
|
+
dir,
|
|
77
|
+
resourcePath
|
|
78
|
+
};
|
|
79
|
+
}).filter((info) => info !== null);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Find all tool files in a tools directory.
|
|
83
|
+
* Matches *.ts files directly in the directory (not recursive).
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* findToolFiles('src/tools', fs)
|
|
87
|
+
* // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]
|
|
88
|
+
*/
|
|
89
|
+
function findToolFiles(toolsDir, fs) {
|
|
90
|
+
if (!fs.existsSync(toolsDir)) return [];
|
|
91
|
+
return fs.readdirSync(toolsDir, { withFileTypes: true }).filter((entry) => !entry.isDirectory() && entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts")).map((entry) => ({
|
|
92
|
+
name: entry.name.replace(/\.ts$/, ""),
|
|
93
|
+
path: `${toolsDir}/${entry.name}`
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Find all simulation JSON files in a flat simulations directory.
|
|
98
|
+
* Matches any *.json file directly in the directory.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* findSimulationFilesFlat('tests/simulations', fs)
|
|
102
|
+
* // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]
|
|
103
|
+
*/
|
|
104
|
+
function findSimulationFilesFlat(simulationsDir, fs) {
|
|
105
|
+
if (!fs.existsSync(simulationsDir)) return [];
|
|
106
|
+
return fs.readdirSync(simulationsDir, { withFileTypes: true }).filter((entry) => !entry.isDirectory() && entry.name.endsWith(".json")).map((entry) => ({
|
|
107
|
+
name: entry.name.replace(/\.json$/, ""),
|
|
108
|
+
path: `${simulationsDir}/${entry.name}`
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { findSimulationFilesFlat as a, toPascalCase as c, findResourceKey as i, extractSimulationKey as n, findToolFiles as o, findResourceDirs as r, getComponentName as s, extractResourceKey as t };
|
|
113
|
+
|
|
114
|
+
//# sourceMappingURL=discovery-Cgoegt62.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-Cgoegt62.js","names":[],"sources":["../src/lib/discovery.ts"],"sourcesContent":["/**\n * Discovery utilities for resources, tools, and simulations.\n *\n * String helpers (toPascalCase, extractResourceKey, etc.) are used by both\n * browser and Node.js code. Node.js utilities (findResourceDirs, findToolFiles,\n * etc.) are used by CLI commands for build-time and runtime discovery.\n */\n\n/**\n * Convert a kebab-case string to PascalCase\n * @example toPascalCase('review') // 'Review'\n * @example toPascalCase('album-art') // 'AlbumArt'\n */\nexport function toPascalCase(str: string): string {\n return str\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\n/**\n * Extract the resource key from a resource file path.\n * Matches {name}.tsx (e.g., './albums/albums.tsx' → 'albums')\n */\nexport function extractResourceKey(path: string): string | undefined {\n const match = path.match(/([^/]+)\\.tsx$/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Extract the simulation key from a simulation file path.\n * Matches any *.json file (e.g., './show-albums.json' → 'show-albums')\n */\nexport function extractSimulationKey(path: string): string | undefined {\n const match = path.match(/([^/]+)\\.json$/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * @example findResourceKey('review-diff', ['review', 'carousel']) // 'review'\n * @example findResourceKey('albums', ['albums', 'review']) // 'albums'\n */\nexport function findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\n/**\n * Get the expected component export name for a resource\n * @example getComponentName('review') // 'ReviewResource'\n * @example getComponentName('album-art') // 'AlbumArtResource'\n */\nexport function getComponentName(resourceKey: string): string {\n return `${toPascalCase(resourceKey)}Resource`;\n}\n\n// --- Node.js utilities for CLI commands ---\n// These utilities use standard Node.js APIs and can be imported by build/push/mcp commands.\n\n/**\n * Information about a discovered resource directory\n */\nexport interface ResourceDirInfo {\n /** Resource key (directory name), e.g., 'albums', 'carousel' */\n key: string;\n /** Full path to the resource directory */\n dir: string;\n /** Full path to the main resource file (tsx or json depending on context) */\n resourcePath: string;\n}\n\n/**\n * File system operations interface for dependency injection in tests\n */\nexport interface FsOps {\n readdirSync: (\n path: string,\n options: { withFileTypes: true }\n ) => Array<{ name: string; isDirectory: () => boolean }>;\n existsSync: (path: string) => boolean;\n}\n\n/**\n * Find all resource directories in a base directory.\n * Each valid resource directory contains a file matching the expected pattern.\n *\n * @param baseDir - Base directory to scan (e.g., 'src/resources' or 'dist')\n * @param filePattern - Function to generate expected filename from resource key\n * @param fs - File system operations (for testing)\n *\n * @example\n * // Find source resources (tsx files)\n * const resources = findResourceDirs('src/resources', key => `${key}.tsx`);\n *\n * @example\n * // Find built resources (js files)\n * const resources = findResourceDirs('dist', key => `${key}.js`);\n */\nexport function findResourceDirs(\n baseDir: string,\n filePattern: (key: string) => string,\n fs: FsOps\n): ResourceDirInfo[] {\n if (!fs.existsSync(baseDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(baseDir, { withFileTypes: true });\n\n return entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => {\n const key = entry.name;\n const dir = `${baseDir}/${key}`;\n const resourcePath = `${dir}/${filePattern(key)}`;\n\n if (!fs.existsSync(resourcePath)) {\n return null;\n }\n\n return { key, dir, resourcePath };\n })\n .filter((info): info is ResourceDirInfo => info !== null);\n}\n\n// --- Tool files + flat simulations discovery ---\n\n/**\n * Information about a discovered tool file\n */\nexport interface ToolFileInfo {\n /** Tool name derived from filename (e.g., 'show-albums') */\n name: string;\n /** Full path to the tool file */\n path: string;\n}\n\n/**\n * Find all tool files in a tools directory.\n * Matches *.ts files directly in the directory (not recursive).\n *\n * @example\n * findToolFiles('src/tools', fs)\n * // [{ name: 'show-albums', path: 'src/tools/show-albums.ts' }]\n */\nexport function findToolFiles(\n toolsDir: string,\n fs: Pick<FsOps, 'readdirSync' | 'existsSync'>\n): ToolFileInfo[] {\n if (!fs.existsSync(toolsDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(toolsDir, { withFileTypes: true });\n\n return entries\n .filter(\n (entry) =>\n !entry.isDirectory() && entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts')\n )\n .map((entry) => ({\n name: entry.name.replace(/\\.ts$/, ''),\n path: `${toolsDir}/${entry.name}`,\n }));\n}\n\n/**\n * Information about a discovered simulation file (flat convention)\n */\nexport interface SimulationFileInfo {\n /** Filename without extension (e.g., 'show-albums') */\n name: string;\n /** Full path to the simulation file */\n path: string;\n}\n\n/**\n * Find all simulation JSON files in a flat simulations directory.\n * Matches any *.json file directly in the directory.\n *\n * @example\n * findSimulationFilesFlat('tests/simulations', fs)\n * // [{ name: 'show-albums', path: 'tests/simulations/show-albums.json' }]\n */\nexport function findSimulationFilesFlat(\n simulationsDir: string,\n fs: Pick<FsOps, 'readdirSync' | 'existsSync'>\n): SimulationFileInfo[] {\n if (!fs.existsSync(simulationsDir)) {\n return [];\n }\n\n const entries = fs.readdirSync(simulationsDir, { withFileTypes: true });\n\n return entries\n .filter((entry) => !entry.isDirectory() && entry.name.endsWith('.json'))\n .map((entry) => ({\n name: entry.name.replace(/\\.json$/, ''),\n path: `${simulationsDir}/${entry.name}`,\n }));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,aAAa,KAAqB;AAChD,QAAO,IACJ,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;;;;;AAOb,SAAgB,mBAAmB,MAAkC;CACnE,MAAM,QAAQ,KAAK,MAAM,gBAAgB;AACzC,QAAO,QAAQ,MAAM,KAAK,KAAA;;;;;;AAO5B,SAAgB,qBAAqB,MAAkC;CACrE,MAAM,QAAQ,KAAK,MAAM,iBAAiB;AAC1C,QAAO,QAAQ,MAAM,KAAK,KAAA;;;;;;;;AAS5B,SAAgB,gBAAgB,eAAuB,cAA4C;CAEjG,MAAM,SAAS,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO;AACpE,MAAK,MAAM,eAAe,OACxB,KAAI,kBAAkB,eAAe,cAAc,WAAW,cAAc,IAAI,CAC9E,QAAO;;;;;;;AAWb,SAAgB,iBAAiB,aAA6B;AAC5D,QAAO,GAAG,aAAa,YAAY,CAAC;;;;;;;;;;;;;;;;;;AA6CtC,SAAgB,iBACd,SACA,aACA,IACmB;AACnB,KAAI,CAAC,GAAG,WAAW,QAAQ,CACzB,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,SAAS,EAAE,eAAe,MAAM,CAAC,CAG7D,QAAQ,UAAU,MAAM,aAAa,CAAC,CACtC,KAAK,UAAU;EACd,MAAM,MAAM,MAAM;EAClB,MAAM,MAAM,GAAG,QAAQ,GAAG;EAC1B,MAAM,eAAe,GAAG,IAAI,GAAG,YAAY,IAAI;AAE/C,MAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,QAAO;AAGT,SAAO;GAAE;GAAK;GAAK;GAAc;GACjC,CACD,QAAQ,SAAkC,SAAS,KAAK;;;;;;;;;;AAuB7D,SAAgB,cACd,UACA,IACgB;AAChB,KAAI,CAAC,GAAG,WAAW,SAAS,CAC1B,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,UAAU,EAAE,eAAe,MAAM,CAAC,CAG9D,QACE,UACC,CAAC,MAAM,aAAa,IAAI,MAAM,KAAK,SAAS,MAAM,IAAI,CAAC,MAAM,KAAK,SAAS,WAAW,CACzF,CACA,KAAK,WAAW;EACf,MAAM,MAAM,KAAK,QAAQ,SAAS,GAAG;EACrC,MAAM,GAAG,SAAS,GAAG,MAAM;EAC5B,EAAE;;;;;;;;;;AAqBP,SAAgB,wBACd,gBACA,IACsB;AACtB,KAAI,CAAC,GAAG,WAAW,eAAe,CAChC,QAAO,EAAE;AAKX,QAFgB,GAAG,YAAY,gBAAgB,EAAE,eAAe,MAAM,CAAC,CAGpE,QAAQ,UAAU,CAAC,MAAM,aAAa,IAAI,MAAM,KAAK,SAAS,QAAQ,CAAC,CACvE,KAAK,WAAW;EACf,MAAM,MAAM,KAAK,QAAQ,WAAW,GAAG;EACvC,MAAM,GAAG,eAAe,GAAG,MAAM;EAClC,EAAE"}
|