sunpeak 0.15.4 → 0.16.2

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.
Files changed (105) hide show
  1. package/README.md +53 -49
  2. package/bin/commands/build.mjs +119 -6
  3. package/bin/commands/dev.mjs +168 -27
  4. package/bin/commands/new.mjs +13 -3
  5. package/bin/commands/start.mjs +215 -0
  6. package/bin/lib/extract-resource.mjs +1 -1
  7. package/bin/lib/extract-tool.mjs +78 -0
  8. package/bin/lib/patterns.mjs +2 -26
  9. package/bin/sunpeak.js +11 -1
  10. package/dist/chatgpt/index.cjs +3 -6
  11. package/dist/chatgpt/index.cjs.map +1 -1
  12. package/dist/chatgpt/index.d.ts +1 -1
  13. package/dist/chatgpt/index.js +6 -9
  14. package/dist/claude/index.cjs +1 -1
  15. package/dist/claude/index.js +1 -1
  16. package/dist/discovery-CH80W5l9.js +217 -0
  17. package/dist/discovery-CH80W5l9.js.map +1 -0
  18. package/dist/discovery-DmB8_4QL.cjs +216 -0
  19. package/dist/discovery-DmB8_4QL.cjs.map +1 -0
  20. package/dist/{index-Cngntkp2.cjs → index-Bll1bszc.cjs} +3 -6
  21. package/dist/{index-Cngntkp2.cjs.map → index-Bll1bszc.cjs.map} +1 -1
  22. package/dist/{index-Ce_5ZIdJ.js → index-CACtnwu2.js} +3 -6
  23. package/dist/{index-Ce_5ZIdJ.js.map → index-CACtnwu2.js.map} +1 -1
  24. package/dist/{index-CutQgPzR.js → index-CLcr8IyR.js} +3 -6
  25. package/dist/index-CLcr8IyR.js.map +1 -0
  26. package/dist/{index-B0dxRJvS.cjs → index-CaQmwZJc.cjs} +3 -6
  27. package/dist/index-CaQmwZJc.cjs.map +1 -0
  28. package/dist/index.cjs +49 -6
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.js +3405 -3362
  32. package/dist/index.js.map +1 -1
  33. package/dist/lib/discovery-cli.cjs +58 -5
  34. package/dist/lib/discovery-cli.cjs.map +1 -1
  35. package/dist/lib/discovery-cli.d.ts +3 -2
  36. package/dist/lib/discovery-cli.js +61 -8
  37. package/dist/lib/discovery-cli.js.map +1 -1
  38. package/dist/lib/discovery.d.ts +42 -43
  39. package/dist/lib/extract-tool.d.ts +12 -0
  40. package/dist/mcp/favicon.d.ts +1 -1
  41. package/dist/mcp/index.cjs +1582 -5
  42. package/dist/mcp/index.cjs.map +1 -1
  43. package/dist/mcp/index.d.ts +3 -1
  44. package/dist/mcp/index.js +1583 -6
  45. package/dist/mcp/index.js.map +1 -1
  46. package/dist/mcp/production-server.d.ts +156 -0
  47. package/dist/mcp/types.d.ts +24 -1
  48. package/dist/platform/chatgpt/index.cjs +1 -1
  49. package/dist/platform/chatgpt/index.js +1 -1
  50. package/dist/{protocol-DFbsCx7E.js → protocol-BD5jDQEx.js} +8 -1
  51. package/dist/{protocol-DFbsCx7E.js.map → protocol-BD5jDQEx.js.map} +1 -1
  52. package/dist/{protocol-CL4_Npj5.cjs → protocol-BOjXuK6l.cjs} +8 -1
  53. package/dist/{protocol-CL4_Npj5.cjs.map → protocol-BOjXuK6l.cjs.map} +1 -1
  54. package/dist/simulator/index.cjs +2 -5
  55. package/dist/simulator/index.cjs.map +1 -1
  56. package/dist/simulator/index.d.ts +1 -1
  57. package/dist/simulator/index.js +5 -8
  58. package/dist/simulator/simulator-url.d.ts +9 -9
  59. package/dist/{simulator-CxrtnguM.js → simulator-B7rw83zP.js} +9 -3
  60. package/dist/{simulator-CxrtnguM.js.map → simulator-B7rw83zP.js.map} +1 -1
  61. package/dist/{simulator-DcfQBRXE.cjs → simulator-DjZNa1MI.cjs} +9 -3
  62. package/dist/{simulator-DcfQBRXE.cjs.map → simulator-DjZNa1MI.cjs.map} +1 -1
  63. package/dist/simulator-url-CuLqtnSS.js.map +1 -1
  64. package/dist/simulator-url-rgg_KYOg.cjs.map +1 -1
  65. package/dist/types/resource-config.d.ts +7 -5
  66. package/dist/{use-app-BnoSPiUT.cjs → use-app-BpAJqzdE.cjs} +50 -21
  67. package/dist/{use-app-BnoSPiUT.cjs.map → use-app-BpAJqzdE.cjs.map} +1 -1
  68. package/dist/{use-app-D_TeaMFG.js → use-app-WOUdh1PR.js} +52 -23
  69. package/dist/{use-app-D_TeaMFG.js.map → use-app-WOUdh1PR.js.map} +1 -1
  70. package/package.json +1 -1
  71. package/template/.sunpeak/dev.tsx +8 -4
  72. package/template/.sunpeak/resource-loader.tsx +2 -1
  73. package/template/README.md +27 -22
  74. package/template/package.json +3 -1
  75. package/template/src/resources/albums/{albums-resource.test.tsx → albums.test.tsx} +1 -1
  76. package/template/src/resources/albums/{albums-resource.tsx → albums.tsx} +0 -1
  77. package/template/src/resources/carousel/{carousel-resource.test.tsx → carousel.test.tsx} +1 -1
  78. package/template/src/resources/carousel/{carousel-resource.tsx → carousel.tsx} +0 -1
  79. package/template/src/resources/index.ts +4 -4
  80. package/template/src/resources/map/{map-resource.test.tsx → map.test.tsx} +1 -1
  81. package/template/src/resources/map/{map-resource.tsx → map.tsx} +0 -1
  82. package/template/src/resources/review/{review-resource.test.tsx → review.test.tsx} +1 -1
  83. package/template/src/resources/review/{review-resource.tsx → review.tsx} +1 -2
  84. package/template/src/server.ts +17 -0
  85. package/template/src/tools/review-diff.ts +24 -0
  86. package/template/src/tools/review-post.ts +26 -0
  87. package/template/src/tools/review-purchase.ts +31 -0
  88. package/template/src/tools/show-albums.ts +22 -0
  89. package/template/src/tools/show-carousel.ts +25 -0
  90. package/template/src/tools/show-map.ts +29 -0
  91. package/template/tests/e2e/albums.spec.ts +6 -6
  92. package/template/tests/e2e/carousel.spec.ts +6 -6
  93. package/template/tests/e2e/map.spec.ts +11 -11
  94. package/template/tests/simulations/{review/review-diff-simulation.json → review-diff.json} +1 -31
  95. package/template/tests/simulations/{review/review-post-simulation.json → review-post.json} +1 -37
  96. package/template/tests/simulations/{review/review-purchase-simulation.json → review-purchase.json} +1 -38
  97. package/template/tests/simulations/{albums/albums-show-simulation.json → show-albums.json} +1 -24
  98. package/template/tests/simulations/{carousel/carousel-show-simulation.json → show-carousel.json} +1 -24
  99. package/template/tests/simulations/{map/map-show-simulation.json → show-map.json} +1 -35
  100. package/dist/discovery-CRR3SlyI.cjs +0 -156
  101. package/dist/discovery-CRR3SlyI.cjs.map +0 -1
  102. package/dist/discovery-DzV3HLXs.js +0 -157
  103. package/dist/discovery-DzV3HLXs.js.map +0 -1
  104. package/dist/index-B0dxRJvS.cjs.map +0 -1
  105. package/dist/index-CutQgPzR.js.map +0 -1
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, readdirSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { createRequire } from 'module';
5
+ import { pathToFileURL } from 'url';
6
+
7
+ /**
8
+ * Start a production MCP server from built artifacts.
9
+ *
10
+ * Discovers compiled tools from dist/tools/, resources from dist/{name}/,
11
+ * and optional server entry from dist/server.js. Registers tools with
12
+ * real handlers and Zod schemas, serves pre-built resource HTML.
13
+ *
14
+ * Run `sunpeak build` before `sunpeak start`.
15
+ */
16
+ export async function start(projectRoot = process.cwd(), args = []) {
17
+ const pkgJsonPath = join(projectRoot, 'package.json');
18
+ if (!existsSync(pkgJsonPath)) {
19
+ console.error('Error: No package.json found in current directory');
20
+ console.error('Make sure you are in a Sunpeak project directory');
21
+ process.exit(1);
22
+ }
23
+
24
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
25
+ const distDir = join(projectRoot, 'dist');
26
+
27
+ if (!existsSync(distDir)) {
28
+ console.error('Error: No dist/ directory found. Run `sunpeak build` first.');
29
+ process.exit(1);
30
+ }
31
+
32
+ // Parse port from args or env
33
+ let port = parseInt(process.env.PORT || '8000');
34
+ const portArgIndex = args.findIndex(arg => arg === '--port' || arg === '-p');
35
+ if (portArgIndex !== -1 && args[portArgIndex + 1]) {
36
+ port = parseInt(args[portArgIndex + 1]);
37
+ }
38
+
39
+ // Import production server from sunpeak
40
+ const isTemplate = projectRoot.endsWith('/template') || projectRoot.endsWith('\\template');
41
+ let sunpeakMcp;
42
+ if (isTemplate) {
43
+ // In workspace dev mode — import from TypeScript source via dynamic import
44
+ // We compile on the fly using the parent's Vite (same pattern as dev.mjs)
45
+ const parentSrc = join(projectRoot, '../src');
46
+ const require = createRequire(pkgJsonPath);
47
+ const vite = await import(findEsmEntry(require, 'vite'));
48
+ const loaderServer = await vite.createServer({
49
+ root: join(projectRoot, '..'),
50
+ server: { middlewareMode: true },
51
+ appType: 'custom',
52
+ logLevel: 'silent',
53
+ });
54
+ sunpeakMcp = await loaderServer.ssrLoadModule('./src/mcp/index.ts');
55
+ await loaderServer.close();
56
+ } else {
57
+ const require = createRequire(pkgJsonPath);
58
+ const sunpeakBase = require.resolve('sunpeak').replace(/dist\/index\.(c)?js$/, '');
59
+ sunpeakMcp = await import(pathToFileURL(join(sunpeakBase, 'dist/mcp/index.js')).href);
60
+ }
61
+
62
+ const { startProductionHttpServer } = sunpeakMcp;
63
+
64
+ // ========================================================================
65
+ // Discover built resources
66
+ // ========================================================================
67
+
68
+ const resources = [];
69
+ const entries = readdirSync(distDir, { withFileTypes: true });
70
+
71
+ for (const entry of entries) {
72
+ if (!entry.isDirectory() || entry.name === 'tools' || entry.name === 'build-output') continue;
73
+
74
+ const name = entry.name;
75
+ const jsonPath = join(distDir, name, `${name}.json`);
76
+ const htmlPath = join(distDir, name, `${name}.html`);
77
+
78
+ if (!existsSync(jsonPath) || !existsSync(htmlPath)) continue;
79
+
80
+ const meta = JSON.parse(readFileSync(jsonPath, 'utf-8'));
81
+ const html = readFileSync(htmlPath, 'utf-8');
82
+
83
+ resources.push({
84
+ name: meta.name ?? name,
85
+ uri: meta.uri ?? `ui://${name}`,
86
+ html,
87
+ description: meta.description,
88
+ _meta: meta._meta,
89
+ });
90
+ }
91
+
92
+ if (resources.length === 0) {
93
+ console.error('Error: No built resources found in dist/. Run `sunpeak build` first.');
94
+ process.exit(1);
95
+ }
96
+
97
+ console.log(`Found ${resources.length} resource(s): ${resources.map(r => r.name).join(', ')}`);
98
+
99
+ // ========================================================================
100
+ // Load compiled tool modules
101
+ // ========================================================================
102
+
103
+ const toolsDir = join(distDir, 'tools');
104
+ const tools = [];
105
+
106
+ if (existsSync(toolsDir)) {
107
+ const toolFiles = readdirSync(toolsDir).filter(f => f.endsWith('.js'));
108
+
109
+ for (const file of toolFiles) {
110
+ const toolName = file.replace(/\.js$/, '');
111
+ const toolPath = pathToFileURL(join(toolsDir, file)).href;
112
+
113
+ try {
114
+ const mod = await import(toolPath);
115
+ const tool = mod.tool;
116
+ const schema = mod.schema;
117
+ const handler = mod.default;
118
+
119
+ if (!tool) {
120
+ console.warn(`Warning: No "tool" export in ${file}. Skipping.`);
121
+ continue;
122
+ }
123
+ if (!handler || typeof handler !== 'function') {
124
+ console.warn(`Warning: No default handler export in ${file}. Skipping.`);
125
+ continue;
126
+ }
127
+
128
+ tools.push({ name: toolName, tool, schema, handler });
129
+ } catch (err) {
130
+ console.error(`Failed to load tool ${toolName}:`, err.message);
131
+ process.exit(1);
132
+ }
133
+ }
134
+ }
135
+
136
+ if (tools.length === 0) {
137
+ console.error('Error: No compiled tools found in dist/tools/. Run `sunpeak build` first.');
138
+ process.exit(1);
139
+ }
140
+
141
+ console.log(`Found ${tools.length} tool(s): ${tools.map(t => t.name).join(', ')}`);
142
+
143
+ // ========================================================================
144
+ // Load server entry (optional)
145
+ // ========================================================================
146
+
147
+ const serverEntryPath = join(distDir, 'server.js');
148
+ let auth = undefined;
149
+ let serverConfig = {};
150
+
151
+ if (existsSync(serverEntryPath)) {
152
+ try {
153
+ const serverEntry = await import(pathToFileURL(serverEntryPath).href);
154
+ if (typeof serverEntry.auth === 'function') {
155
+ auth = serverEntry.auth;
156
+ console.log('Loaded auth from server entry');
157
+ }
158
+ if (serverEntry.server) {
159
+ serverConfig = serverEntry.server;
160
+ }
161
+ } catch (err) {
162
+ console.error('Failed to load server entry:', err.message);
163
+ process.exit(1);
164
+ }
165
+ }
166
+
167
+ // ========================================================================
168
+ // Start production MCP server
169
+ // ========================================================================
170
+
171
+ const name = serverConfig.name ?? pkg.name ?? 'sunpeak-app';
172
+ const version = serverConfig.version ?? pkg.version ?? '0.1.0';
173
+
174
+ console.log(`\nStarting ${name} v${version} on port ${port}...`);
175
+
176
+ startProductionHttpServer(
177
+ { name, version, tools, resources, auth },
178
+ port
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Resolve ESM entry point for a package (same utility as build.mjs)
184
+ */
185
+ function findEsmEntry(require, packageName) {
186
+ const resolvedPath = require.resolve(packageName);
187
+ let dir = dirname(resolvedPath);
188
+ while (dir !== dirname(dir)) {
189
+ const pkgPath = join(dir, 'package.json');
190
+ if (existsSync(pkgPath)) {
191
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
192
+ if (pkg.name === packageName) {
193
+ const exports = pkg.exports;
194
+ if (exports?.['.']?.import) {
195
+ const importEntry = exports['.'].import;
196
+ const esmPath = typeof importEntry === 'string' ? importEntry : importEntry.default;
197
+ if (esmPath) return pathToFileURL(join(dir, esmPath)).href;
198
+ }
199
+ if (pkg.module) return pathToFileURL(join(dir, pkg.module)).href;
200
+ break;
201
+ }
202
+ }
203
+ dir = dirname(dir);
204
+ }
205
+ return pathToFileURL(resolvedPath).href;
206
+ }
207
+
208
+ // Allow running directly
209
+ if (import.meta.url === `file://${process.argv[1]}`) {
210
+ const args = process.argv.slice(2);
211
+ start(process.cwd(), args).catch(error => {
212
+ console.error('Error:', error.message);
213
+ process.exit(1);
214
+ });
215
+ }
@@ -55,7 +55,7 @@ export async function extractResourceExport(tsxPath) {
55
55
  if (!resource) {
56
56
  throw new Error(
57
57
  `No "resource" export found in ${tsxPath}. ` +
58
- `Add: export const resource = { name: '...', ... };`
58
+ `Add: export const resource: ResourceConfig = { title: '...', ... };`
59
59
  );
60
60
  }
61
61
 
@@ -0,0 +1,78 @@
1
+ import path from 'path';
2
+
3
+ /**
4
+ * Extract the `tool` named export from a tool .ts file.
5
+ *
6
+ * Uses esbuild in ESM mode to compile TypeScript and tree-shake to just the
7
+ * `tool` export. ESM tree-shaking drops unused exports (schema, handler) so
8
+ * their dependencies (zod, etc.) are never evaluated.
9
+ *
10
+ * `schema` and `default` handler are loaded at runtime via Vite SSR.
11
+ */
12
+ export async function extractToolExport(tsPath) {
13
+ const esbuild = await import('esbuild');
14
+ const absolutePath = path.resolve(tsPath);
15
+ const dir = path.dirname(absolutePath);
16
+ const base = path.basename(absolutePath);
17
+
18
+ const result = await esbuild.build({
19
+ stdin: {
20
+ contents: `export { tool } from './${base}';`,
21
+ resolveDir: dir,
22
+ loader: 'ts',
23
+ },
24
+ bundle: true,
25
+ write: false,
26
+ format: 'esm',
27
+ treeShaking: true,
28
+ loader: { '.tsx': 'tsx', '.ts': 'ts', '.jsx': 'jsx' },
29
+ logLevel: 'silent',
30
+ plugins: [
31
+ {
32
+ name: 'externalize-node-modules',
33
+ setup(build) {
34
+ build.onResolve({ filter: /.*/ }, (args) => {
35
+ if (args.kind !== 'import-statement') return;
36
+ if (!args.path.startsWith('.') && !args.path.startsWith('/')) {
37
+ return { external: true };
38
+ }
39
+ return undefined;
40
+ });
41
+ },
42
+ },
43
+ ],
44
+ });
45
+
46
+ if (!result.outputFiles?.length) {
47
+ throw new Error(`Failed to extract tool from ${tsPath}`);
48
+ }
49
+
50
+ // Strip import statements and export block so we can eval as plain JS.
51
+ // `tool` is pure data (no dependencies), so stripping imports is safe.
52
+ // Other top-level code (schema, etc.) may reference stripped imports but
53
+ // we only need the `tool` variable — errors in other code are caught and ignored.
54
+ const code = result.outputFiles[0].text
55
+ .replace(/^import\s+.*$/gm, '')
56
+ .replace(/^export\s*\{[^}]*\}\s*;?\s*$/m, '');
57
+ let tool;
58
+ try {
59
+ const fn = new Function(code + '\nreturn tool;');
60
+ tool = fn();
61
+ } catch {
62
+ // If other top-level code crashes (e.g. schema using stripped zod),
63
+ // extract just the tool variable declaration and eval that alone.
64
+ const toolMatch = result.outputFiles[0].text.match(/var tool\s*=\s*(\{[\s\S]*?\n\});/);
65
+ if (toolMatch) {
66
+ tool = new Function('return ' + toolMatch[1])();
67
+ }
68
+ }
69
+
70
+ if (!tool) {
71
+ throw new Error(
72
+ `No "tool" export found in ${tsPath}. ` +
73
+ `Add: export const tool: AppToolConfig = { resource, ... };`
74
+ );
75
+ }
76
+
77
+ return { tool };
78
+ }
@@ -11,7 +11,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
11
11
 
12
12
  /**
13
13
  * Auto-discover available resources from template/src/resources directories.
14
- * Each subdirectory containing a {name}-resource.tsx file is a valid resource.
14
+ * Each subdirectory containing a {name}.tsx file is a valid resource.
15
15
  * @returns {string[]} Array of resource names
16
16
  */
17
17
  export function discoverResources() {
@@ -21,10 +21,7 @@ export function discoverResources() {
21
21
  }
22
22
  return readdirSync(resourcesDir, { withFileTypes: true })
23
23
  .filter((entry) => entry.isDirectory())
24
- .filter((entry) => {
25
- const resourceFile = join(resourcesDir, entry.name, `${entry.name}-resource.tsx`);
26
- return existsSync(resourceFile);
27
- })
24
+ .filter((entry) => existsSync(join(resourcesDir, entry.name, `${entry.name}.tsx`)))
28
25
  .map((entry) => entry.name);
29
26
  }
30
27
 
@@ -42,24 +39,3 @@ export function toPascalCase(str) {
42
39
  .join('');
43
40
  }
44
41
 
45
- /**
46
- * Check if a filename is a simulation file for a given resource.
47
- * @param {string} filename
48
- * @param {string} resourceKey
49
- * @returns {boolean}
50
- * @example isSimulationFile('albums-show-simulation.json', 'albums') // true
51
- */
52
- export function isSimulationFile(filename, resourceKey) {
53
- return filename.startsWith(`${resourceKey}-`) && filename.endsWith('-simulation.json');
54
- }
55
-
56
- /**
57
- * Extract the simulation name from a simulation filename.
58
- * @param {string} filename
59
- * @param {string} resourceKey
60
- * @returns {string}
61
- * @example extractSimulationName('albums-show-simulation.json', 'albums') // 'show'
62
- */
63
- export function extractSimulationName(filename, resourceKey) {
64
- return filename.replace(`${resourceKey}-`, '').replace('-simulation.json', '');
65
- }
package/bin/sunpeak.js CHANGED
@@ -65,6 +65,13 @@ function getVersion() {
65
65
  }
66
66
  break;
67
67
 
68
+ case 'start':
69
+ {
70
+ const { start } = await import(join(COMMANDS_DIR, 'start.mjs'));
71
+ await start(process.cwd(), args);
72
+ }
73
+ break;
74
+
68
75
  case 'upgrade':
69
76
  {
70
77
  const { upgrade } = await import(join(COMMANDS_DIR, 'upgrade.mjs'));
@@ -90,7 +97,10 @@ Usage:
90
97
  sunpeak new [name] [resources] Create a new project
91
98
  sunpeak dev Start dev server + MCP endpoint
92
99
  --no-begging Suppress GitHub star message
93
- sunpeak build Build resources
100
+ --prod-mcp Use real tool handlers (not fixtures)
101
+ sunpeak build Build resources + tools for production
102
+ sunpeak start Start production MCP server
103
+ --port, -p Server port (default: 8000, or PORT env)
94
104
  sunpeak upgrade Upgrade sunpeak to latest version
95
105
  sunpeak --version Show version number
96
106
 
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const simulator = require("../simulator-DcfQBRXE.cjs");
4
- const chatgpt_index = require("../index-Cngntkp2.cjs");
3
+ const simulator = require("../simulator-DjZNa1MI.cjs");
4
+ const chatgpt_index = require("../index-Bll1bszc.cjs");
5
5
  const simulatorUrl = require("../simulator-url-rgg_KYOg.cjs");
6
- const discovery = require("../discovery-CRR3SlyI.cjs");
6
+ const discovery = require("../discovery-DmB8_4QL.cjs");
7
7
  exports.IframeResource = simulator.IframeResource;
8
8
  exports.McpAppHost = simulator.McpAppHost;
9
9
  exports.SCREEN_WIDTHS = simulator.SCREEN_WIDTHS;
@@ -19,11 +19,8 @@ exports.buildSimulations = discovery.buildSimulations;
19
19
  exports.createResourceExports = discovery.createResourceExports;
20
20
  exports.extractResourceKey = discovery.extractResourceKey;
21
21
  exports.extractSimulationKey = discovery.extractSimulationKey;
22
- exports.extractSimulationName = discovery.extractSimulationName;
23
22
  exports.findResourceDirs = discovery.findResourceDirs;
24
23
  exports.findResourceKey = discovery.findResourceKey;
25
- exports.findSimulationFiles = discovery.findSimulationFiles;
26
24
  exports.getComponentName = discovery.getComponentName;
27
- exports.isSimulationFile = discovery.isSimulationFile;
28
25
  exports.toPascalCase = discovery.toPascalCase;
29
26
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -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 { buildDevSimulations, buildSimulations, buildResourceMap, createResourceExports, toPascalCase, extractResourceKey, extractSimulationKey, findResourceKey, getComponentName, findResourceDirs, isSimulationFile, extractSimulationName, findSimulationFiles, } from '../lib/discovery';
13
+ export { buildDevSimulations, buildSimulations, buildResourceMap, createResourceExports, toPascalCase, extractResourceKey, extractSimulationKey, findResourceKey, getComponentName, findResourceDirs, } from '../lib/discovery';
14
14
  export type { BuildSimulationsOptions, BuildDevSimulationsOptions, ResourceMetadata, ResourceDirInfo, FsOps, } from '../lib/discovery';
@@ -1,7 +1,7 @@
1
- import { I, M, a, S, T, j, m } from "../simulator-CxrtnguM.js";
2
- import { C } from "../index-Ce_5ZIdJ.js";
1
+ import { I, M, a, S, T, j, m } from "../simulator-B7rw83zP.js";
2
+ import { C } from "../index-CACtnwu2.js";
3
3
  import { c } from "../simulator-url-CuLqtnSS.js";
4
- import { b, a as a2, c as c2, d, e, f, g, h, i, j as j2, k, l, t } from "../discovery-DzV3HLXs.js";
4
+ import { b, a as a2, c as c2, d, e, f, g, h, i, t } from "../discovery-CH80W5l9.js";
5
5
  export {
6
6
  C as ChatGPTSimulator,
7
7
  I as IframeResource,
@@ -17,12 +17,9 @@ export {
17
17
  j as extractResourceCSP,
18
18
  e as extractResourceKey,
19
19
  f as extractSimulationKey,
20
- g as extractSimulationName,
21
- h as findResourceDirs,
22
- i as findResourceKey,
23
- j2 as findSimulationFiles,
24
- k as getComponentName,
25
- l as isSimulationFile,
20
+ g as findResourceDirs,
21
+ h as findResourceKey,
22
+ i as getComponentName,
26
23
  t as toPascalCase,
27
24
  m as useThemeContext
28
25
  };
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const simulator = require("../simulator-DcfQBRXE.cjs");
3
+ const simulator = require("../simulator-DjZNa1MI.cjs");
4
4
  exports.ClaudeSimulator = simulator.Simulator;
5
5
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- import { S } from "../simulator-CxrtnguM.js";
1
+ import { S } from "../simulator-B7rw83zP.js";
2
2
  export {
3
3
  S as ClaudeSimulator
4
4
  };
@@ -0,0 +1,217 @@
1
+ function toPascalCase(str) {
2
+ return str.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
3
+ }
4
+ function extractResourceKey(path) {
5
+ const match = path.match(/([^/]+)\.tsx$/);
6
+ return match ? match[1] : void 0;
7
+ }
8
+ function extractSimulationKey(path) {
9
+ const match = path.match(/([^/]+)\.json$/);
10
+ return match ? match[1] : void 0;
11
+ }
12
+ function findResourceKey(simulationKey, resourceKeys) {
13
+ const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);
14
+ for (const resourceKey of sorted) {
15
+ if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + "-")) {
16
+ return resourceKey;
17
+ }
18
+ }
19
+ return void 0;
20
+ }
21
+ function getComponentName(resourceKey) {
22
+ return `${toPascalCase(resourceKey)}Resource`;
23
+ }
24
+ function createResourceExports(modules) {
25
+ const resources = {};
26
+ for (const [path, module] of Object.entries(modules)) {
27
+ const key = extractResourceKey(path);
28
+ if (!key) continue;
29
+ const exportName = getComponentName(key);
30
+ const mod = module;
31
+ const component = mod.default ?? mod[exportName];
32
+ if (component && (typeof component === "function" || typeof component === "object")) {
33
+ resources[exportName] = component;
34
+ }
35
+ }
36
+ return resources;
37
+ }
38
+ function buildResourceMap(modules) {
39
+ const map = /* @__PURE__ */ new Map();
40
+ for (const [path, module] of Object.entries(modules)) {
41
+ const key = extractResourceKey(path);
42
+ if (key) {
43
+ map.set(key, module.resource);
44
+ }
45
+ }
46
+ return map;
47
+ }
48
+ function buildSimulations(options) {
49
+ const {
50
+ simulationModules,
51
+ resourcesMap,
52
+ resourceComponents,
53
+ createSimulation,
54
+ onMissingResource = (key, prefix) => console.warn(
55
+ `No matching resource found for simulation "${key}". Expected a resource file like src/resources/${prefix}/${prefix}.tsx`
56
+ )
57
+ } = options;
58
+ const resourceKeys = Array.from(resourcesMap.keys());
59
+ const simulations = {};
60
+ for (const [path, module] of Object.entries(simulationModules)) {
61
+ const simulationKey = extractSimulationKey(path);
62
+ if (!simulationKey) continue;
63
+ const simulationData = module.default;
64
+ const resourceKey = findResourceKey(simulationKey, resourceKeys);
65
+ if (!resourceKey) {
66
+ onMissingResource(simulationKey, simulationKey.split("-")[0]);
67
+ continue;
68
+ }
69
+ const resource = resourcesMap.get(resourceKey);
70
+ const componentName = getComponentName(resourceKey);
71
+ const resourceComponent = resourceComponents[componentName];
72
+ if (!resourceComponent) {
73
+ console.warn(
74
+ `Resource component "${componentName}" not found for resource "${resourceKey}". Make sure src/resources/${resourceKey}/${resourceKey}.tsx exists with a default export.`
75
+ );
76
+ continue;
77
+ }
78
+ simulations[simulationKey] = createSimulation(
79
+ simulationKey,
80
+ simulationData,
81
+ resource,
82
+ resourceComponent
83
+ );
84
+ }
85
+ return simulations;
86
+ }
87
+ function buildDevSimulations(options) {
88
+ const { simulationModules, resourceComponents, toolModules, resourceModules } = options;
89
+ const resourceMetaByName = /* @__PURE__ */ new Map();
90
+ const resourceKeyByName = /* @__PURE__ */ new Map();
91
+ for (const [path, module] of Object.entries(resourceModules)) {
92
+ const key = extractResourceKey(path);
93
+ if (!key) continue;
94
+ const mod = module;
95
+ if (mod.resource) {
96
+ const name = mod.resource.name ?? key;
97
+ resourceMetaByName.set(name, { ...mod.resource, name });
98
+ resourceKeyByName.set(name, key);
99
+ }
100
+ }
101
+ const toolsMap = /* @__PURE__ */ new Map();
102
+ if (toolModules) {
103
+ for (const [path, module] of Object.entries(toolModules)) {
104
+ const nameMatch = path.match(/([^/]+)\.ts$/);
105
+ if (!nameMatch) continue;
106
+ const mod = module;
107
+ if (mod.tool) {
108
+ const resourceName = mod.tool.resource;
109
+ if (resourceName) {
110
+ toolsMap.set(nameMatch[1], { tool: mod.tool, resourceName });
111
+ }
112
+ }
113
+ }
114
+ }
115
+ const simulations = {};
116
+ for (const [path, module] of Object.entries(simulationModules)) {
117
+ const simKey = extractSimulationKey(path);
118
+ if (!simKey) continue;
119
+ const simulationData = module.default;
120
+ const toolName = typeof simulationData.tool === "string" ? simulationData.tool : simKey;
121
+ const toolInfo = toolsMap.get(toolName);
122
+ if (!toolInfo) {
123
+ console.warn(
124
+ `Tool "${toolName}" not found for simulation "${simKey}". Make sure src/tools/${toolName}.ts exists.`
125
+ );
126
+ continue;
127
+ }
128
+ const resourceMeta = resourceMetaByName.get(toolInfo.resourceName);
129
+ const resourceKey = resourceKeyByName.get(toolInfo.resourceName);
130
+ if (!resourceMeta || !resourceKey) {
131
+ console.warn(
132
+ `Resource "${toolInfo.resourceName}" not found for tool "${toolName}". Make sure a resource with name "${toolInfo.resourceName}" exists in src/resources/.`
133
+ );
134
+ continue;
135
+ }
136
+ const componentName = getComponentName(resourceKey);
137
+ const resourceComponent = resourceComponents[componentName];
138
+ if (!resourceComponent) {
139
+ console.warn(`Resource component "${componentName}" not found for tool "${toolName}".`);
140
+ continue;
141
+ }
142
+ simulations[simKey] = {
143
+ name: simKey,
144
+ userMessage: simulationData.userMessage,
145
+ tool: {
146
+ name: toolName,
147
+ description: toolInfo.tool.description ?? "",
148
+ inputSchema: { type: "object" },
149
+ ...toolInfo.tool.title != null ? { title: toolInfo.tool.title } : {},
150
+ ...toolInfo.tool.annotations != null ? { annotations: toolInfo.tool.annotations } : {},
151
+ ...toolInfo.tool._meta != null ? { _meta: toolInfo.tool._meta } : {}
152
+ },
153
+ resource: {
154
+ uri: `ui://${resourceKey}`,
155
+ name: resourceKey,
156
+ ...resourceMeta.title != null ? { title: resourceMeta.title } : {},
157
+ ...resourceMeta.description != null ? { description: resourceMeta.description } : {},
158
+ ...resourceMeta.mimeType != null ? { mimeType: resourceMeta.mimeType } : {},
159
+ ...resourceMeta._meta != null ? { _meta: resourceMeta._meta } : {}
160
+ },
161
+ toolInput: simulationData.toolInput,
162
+ toolResult: simulationData.toolResult,
163
+ resourceUrl: `/.sunpeak/resource-loader.html?component=${componentName}`
164
+ };
165
+ }
166
+ return simulations;
167
+ }
168
+ function findResourceDirs(baseDir, filePattern, fs) {
169
+ if (!fs.existsSync(baseDir)) {
170
+ return [];
171
+ }
172
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
173
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => {
174
+ const key = entry.name;
175
+ const dir = `${baseDir}/${key}`;
176
+ const resourcePath = `${dir}/${filePattern(key)}`;
177
+ if (!fs.existsSync(resourcePath)) {
178
+ return null;
179
+ }
180
+ return { key, dir, resourcePath };
181
+ }).filter((info) => info !== null);
182
+ }
183
+ function findToolFiles(toolsDir, fs) {
184
+ if (!fs.existsSync(toolsDir)) {
185
+ return [];
186
+ }
187
+ const entries = fs.readdirSync(toolsDir, { withFileTypes: true });
188
+ return entries.filter((entry) => !entry.isDirectory() && entry.name.endsWith(".ts")).map((entry) => ({
189
+ name: entry.name.replace(/\.ts$/, ""),
190
+ path: `${toolsDir}/${entry.name}`
191
+ }));
192
+ }
193
+ function findSimulationFilesFlat(simulationsDir, fs) {
194
+ if (!fs.existsSync(simulationsDir)) {
195
+ return [];
196
+ }
197
+ const entries = fs.readdirSync(simulationsDir, { withFileTypes: true });
198
+ return entries.filter((entry) => !entry.isDirectory() && entry.name.endsWith(".json")).map((entry) => ({
199
+ name: entry.name.replace(/\.json$/, ""),
200
+ path: `${simulationsDir}/${entry.name}`
201
+ }));
202
+ }
203
+ export {
204
+ buildResourceMap as a,
205
+ buildDevSimulations as b,
206
+ buildSimulations as c,
207
+ createResourceExports as d,
208
+ extractResourceKey as e,
209
+ extractSimulationKey as f,
210
+ findResourceDirs as g,
211
+ findResourceKey as h,
212
+ getComponentName as i,
213
+ findSimulationFilesFlat as j,
214
+ findToolFiles as k,
215
+ toPascalCase as t
216
+ };
217
+ //# sourceMappingURL=discovery-CH80W5l9.js.map