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.
- package/README.md +53 -49
- package/bin/commands/build.mjs +119 -6
- package/bin/commands/dev.mjs +168 -27
- package/bin/commands/new.mjs +13 -3
- package/bin/commands/start.mjs +215 -0
- package/bin/lib/extract-resource.mjs +1 -1
- package/bin/lib/extract-tool.mjs +78 -0
- package/bin/lib/patterns.mjs +2 -26
- package/bin/sunpeak.js +11 -1
- package/dist/chatgpt/index.cjs +3 -6
- package/dist/chatgpt/index.cjs.map +1 -1
- package/dist/chatgpt/index.d.ts +1 -1
- package/dist/chatgpt/index.js +6 -9
- package/dist/claude/index.cjs +1 -1
- package/dist/claude/index.js +1 -1
- package/dist/discovery-CH80W5l9.js +217 -0
- package/dist/discovery-CH80W5l9.js.map +1 -0
- package/dist/discovery-DmB8_4QL.cjs +216 -0
- package/dist/discovery-DmB8_4QL.cjs.map +1 -0
- package/dist/{index-Cngntkp2.cjs → index-Bll1bszc.cjs} +3 -6
- package/dist/{index-Cngntkp2.cjs.map → index-Bll1bszc.cjs.map} +1 -1
- package/dist/{index-Ce_5ZIdJ.js → index-CACtnwu2.js} +3 -6
- package/dist/{index-Ce_5ZIdJ.js.map → index-CACtnwu2.js.map} +1 -1
- package/dist/{index-CutQgPzR.js → index-CLcr8IyR.js} +3 -6
- package/dist/index-CLcr8IyR.js.map +1 -0
- package/dist/{index-B0dxRJvS.cjs → index-CaQmwZJc.cjs} +3 -6
- package/dist/index-CaQmwZJc.cjs.map +1 -0
- package/dist/index.cjs +49 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3405 -3362
- package/dist/index.js.map +1 -1
- package/dist/lib/discovery-cli.cjs +58 -5
- package/dist/lib/discovery-cli.cjs.map +1 -1
- package/dist/lib/discovery-cli.d.ts +3 -2
- package/dist/lib/discovery-cli.js +61 -8
- package/dist/lib/discovery-cli.js.map +1 -1
- package/dist/lib/discovery.d.ts +42 -43
- package/dist/lib/extract-tool.d.ts +12 -0
- package/dist/mcp/favicon.d.ts +1 -1
- package/dist/mcp/index.cjs +1582 -5
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.d.ts +3 -1
- package/dist/mcp/index.js +1583 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/production-server.d.ts +156 -0
- package/dist/mcp/types.d.ts +24 -1
- package/dist/platform/chatgpt/index.cjs +1 -1
- package/dist/platform/chatgpt/index.js +1 -1
- package/dist/{protocol-DFbsCx7E.js → protocol-BD5jDQEx.js} +8 -1
- package/dist/{protocol-DFbsCx7E.js.map → protocol-BD5jDQEx.js.map} +1 -1
- package/dist/{protocol-CL4_Npj5.cjs → protocol-BOjXuK6l.cjs} +8 -1
- package/dist/{protocol-CL4_Npj5.cjs.map → protocol-BOjXuK6l.cjs.map} +1 -1
- package/dist/simulator/index.cjs +2 -5
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.ts +1 -1
- package/dist/simulator/index.js +5 -8
- package/dist/simulator/simulator-url.d.ts +9 -9
- package/dist/{simulator-CxrtnguM.js → simulator-B7rw83zP.js} +9 -3
- package/dist/{simulator-CxrtnguM.js.map → simulator-B7rw83zP.js.map} +1 -1
- package/dist/{simulator-DcfQBRXE.cjs → simulator-DjZNa1MI.cjs} +9 -3
- package/dist/{simulator-DcfQBRXE.cjs.map → simulator-DjZNa1MI.cjs.map} +1 -1
- package/dist/simulator-url-CuLqtnSS.js.map +1 -1
- package/dist/simulator-url-rgg_KYOg.cjs.map +1 -1
- package/dist/types/resource-config.d.ts +7 -5
- package/dist/{use-app-BnoSPiUT.cjs → use-app-BpAJqzdE.cjs} +50 -21
- package/dist/{use-app-BnoSPiUT.cjs.map → use-app-BpAJqzdE.cjs.map} +1 -1
- package/dist/{use-app-D_TeaMFG.js → use-app-WOUdh1PR.js} +52 -23
- package/dist/{use-app-D_TeaMFG.js.map → use-app-WOUdh1PR.js.map} +1 -1
- package/package.json +1 -1
- package/template/.sunpeak/dev.tsx +8 -4
- package/template/.sunpeak/resource-loader.tsx +2 -1
- package/template/README.md +27 -22
- package/template/package.json +3 -1
- package/template/src/resources/albums/{albums-resource.test.tsx → albums.test.tsx} +1 -1
- package/template/src/resources/albums/{albums-resource.tsx → albums.tsx} +0 -1
- package/template/src/resources/carousel/{carousel-resource.test.tsx → carousel.test.tsx} +1 -1
- package/template/src/resources/carousel/{carousel-resource.tsx → carousel.tsx} +0 -1
- package/template/src/resources/index.ts +4 -4
- package/template/src/resources/map/{map-resource.test.tsx → map.test.tsx} +1 -1
- package/template/src/resources/map/{map-resource.tsx → map.tsx} +0 -1
- package/template/src/resources/review/{review-resource.test.tsx → review.test.tsx} +1 -1
- package/template/src/resources/review/{review-resource.tsx → review.tsx} +1 -2
- package/template/src/server.ts +17 -0
- package/template/src/tools/review-diff.ts +24 -0
- package/template/src/tools/review-post.ts +26 -0
- package/template/src/tools/review-purchase.ts +31 -0
- package/template/src/tools/show-albums.ts +22 -0
- package/template/src/tools/show-carousel.ts +25 -0
- package/template/src/tools/show-map.ts +29 -0
- package/template/tests/e2e/albums.spec.ts +6 -6
- package/template/tests/e2e/carousel.spec.ts +6 -6
- package/template/tests/e2e/map.spec.ts +11 -11
- package/template/tests/simulations/{review/review-diff-simulation.json → review-diff.json} +1 -31
- package/template/tests/simulations/{review/review-post-simulation.json → review-post.json} +1 -37
- package/template/tests/simulations/{review/review-purchase-simulation.json → review-purchase.json} +1 -38
- package/template/tests/simulations/{albums/albums-show-simulation.json → show-albums.json} +1 -24
- package/template/tests/simulations/{carousel/carousel-show-simulation.json → show-carousel.json} +1 -24
- package/template/tests/simulations/{map/map-show-simulation.json → show-map.json} +1 -35
- package/dist/discovery-CRR3SlyI.cjs +0 -156
- package/dist/discovery-CRR3SlyI.cjs.map +0 -1
- package/dist/discovery-DzV3HLXs.js +0 -157
- package/dist/discovery-DzV3HLXs.js.map +0 -1
- package/dist/index-B0dxRJvS.cjs.map +0 -1
- 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 = {
|
|
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
|
+
}
|
package/bin/lib/patterns.mjs
CHANGED
|
@@ -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}
|
|
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
|
-
|
|
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
|
|
package/dist/chatgpt/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const simulator = require("../simulator-
|
|
4
|
-
const chatgpt_index = require("../index-
|
|
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-
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;"}
|
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 { buildDevSimulations, buildSimulations, buildResourceMap, createResourceExports, toPascalCase, extractResourceKey, extractSimulationKey, findResourceKey, getComponentName, findResourceDirs,
|
|
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';
|
package/dist/chatgpt/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { I, M, a, S, T, j, m } from "../simulator-
|
|
2
|
-
import { C } from "../index-
|
|
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,
|
|
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
|
|
21
|
-
h as
|
|
22
|
-
i as
|
|
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
|
};
|
package/dist/claude/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const simulator = require("../simulator-
|
|
3
|
+
const simulator = require("../simulator-DjZNa1MI.cjs");
|
|
4
4
|
exports.ClaudeSimulator = simulator.Simulator;
|
|
5
5
|
//# sourceMappingURL=index.cjs.map
|
package/dist/claude/index.js
CHANGED
|
@@ -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
|