@portel/photon 1.11.0 → 1.13.0

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 (87) hide show
  1. package/README.md +81 -72
  2. package/dist/auto-ui/beam/photon-management.d.ts.map +1 -1
  3. package/dist/auto-ui/beam/photon-management.js +5 -0
  4. package/dist/auto-ui/beam/photon-management.js.map +1 -1
  5. package/dist/auto-ui/beam/routes/api-browse.d.ts +1 -2
  6. package/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -1
  7. package/dist/auto-ui/beam/routes/api-browse.js +140 -191
  8. package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
  9. package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -1
  10. package/dist/auto-ui/beam/routes/api-config.js +44 -1
  11. package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
  12. package/dist/auto-ui/beam.d.ts.map +1 -1
  13. package/dist/auto-ui/beam.js +994 -34
  14. package/dist/auto-ui/beam.js.map +1 -1
  15. package/dist/auto-ui/frontend/index.html +83 -60
  16. package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
  17. package/dist/auto-ui/streamable-http-transport.js +53 -12
  18. package/dist/auto-ui/streamable-http-transport.js.map +1 -1
  19. package/dist/auto-ui/types.d.ts +28 -1
  20. package/dist/auto-ui/types.d.ts.map +1 -1
  21. package/dist/auto-ui/types.js +23 -0
  22. package/dist/auto-ui/types.js.map +1 -1
  23. package/dist/beam.bundle.js +2894 -329
  24. package/dist/beam.bundle.js.map +4 -4
  25. package/dist/cli/commands/build.d.ts +3 -0
  26. package/dist/cli/commands/build.d.ts.map +1 -0
  27. package/dist/cli/commands/build.js +339 -0
  28. package/dist/cli/commands/build.js.map +1 -0
  29. package/dist/cli/commands/package-app.d.ts.map +1 -1
  30. package/dist/cli/commands/package-app.js +116 -35
  31. package/dist/cli/commands/package-app.js.map +1 -1
  32. package/dist/cli/commands/run.d.ts.map +1 -1
  33. package/dist/cli/commands/run.js +2 -0
  34. package/dist/cli/commands/run.js.map +1 -1
  35. package/dist/cli/index.d.ts.map +1 -1
  36. package/dist/cli/index.js +2 -0
  37. package/dist/cli/index.js.map +1 -1
  38. package/dist/context-store.d.ts +5 -0
  39. package/dist/context-store.d.ts.map +1 -1
  40. package/dist/context-store.js +9 -0
  41. package/dist/context-store.js.map +1 -1
  42. package/dist/daemon/client.d.ts.map +1 -1
  43. package/dist/daemon/client.js +81 -0
  44. package/dist/daemon/client.js.map +1 -1
  45. package/dist/daemon/protocol.d.ts +3 -1
  46. package/dist/daemon/protocol.d.ts.map +1 -1
  47. package/dist/daemon/protocol.js +1 -1
  48. package/dist/daemon/protocol.js.map +1 -1
  49. package/dist/daemon/server.js +513 -18
  50. package/dist/daemon/server.js.map +1 -1
  51. package/dist/embedded-runtime.d.ts +38 -0
  52. package/dist/embedded-runtime.d.ts.map +1 -0
  53. package/dist/embedded-runtime.js +326 -0
  54. package/dist/embedded-runtime.js.map +1 -0
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -0
  58. package/dist/index.js.map +1 -1
  59. package/dist/loader.d.ts +38 -1
  60. package/dist/loader.d.ts.map +1 -1
  61. package/dist/loader.js +455 -15
  62. package/dist/loader.js.map +1 -1
  63. package/dist/photon-cli-runner.d.ts +22 -0
  64. package/dist/photon-cli-runner.d.ts.map +1 -1
  65. package/dist/photon-cli-runner.js +244 -12
  66. package/dist/photon-cli-runner.js.map +1 -1
  67. package/dist/photon-doc-extractor.d.ts +6 -0
  68. package/dist/photon-doc-extractor.d.ts.map +1 -1
  69. package/dist/photon-doc-extractor.js +22 -0
  70. package/dist/photon-doc-extractor.js.map +1 -1
  71. package/dist/photons/tunnel.photon.d.ts +5 -9
  72. package/dist/photons/tunnel.photon.d.ts.map +1 -1
  73. package/dist/photons/tunnel.photon.js +36 -96
  74. package/dist/photons/tunnel.photon.js.map +1 -1
  75. package/dist/photons/tunnel.photon.ts +40 -112
  76. package/dist/server.d.ts +30 -0
  77. package/dist/server.d.ts.map +1 -1
  78. package/dist/server.js +155 -10
  79. package/dist/server.js.map +1 -1
  80. package/dist/test-runner.d.ts +13 -1
  81. package/dist/test-runner.d.ts.map +1 -1
  82. package/dist/test-runner.js +529 -122
  83. package/dist/test-runner.js.map +1 -1
  84. package/dist/version.d.ts.map +1 -1
  85. package/dist/version.js +10 -2
  86. package/dist/version.js.map +1 -1
  87. package/package.json +23 -6
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerBuildCommand(program: Command): void;
3
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuGpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA8TpD"}
@@ -0,0 +1,339 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import { spawn } from 'child_process';
4
+ import ora from 'ora';
5
+ import chalk from 'chalk';
6
+ import { printError } from '../../cli-formatter.js';
7
+ import { fileURLToPath } from 'url';
8
+ import { SchemaExtractor } from '@portel/photon-core';
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ /**
11
+ * Recursively resolve all @photon dependencies starting from a root source file.
12
+ * Returns a flat list of unique dependencies (by file path).
13
+ */
14
+ function resolvePhotonDeps(sourceCode, sourceFilePath, baseDir, visited = new Set()) {
15
+ const extractor = new SchemaExtractor();
16
+ const deps = extractor.extractPhotonDependencies(sourceCode);
17
+ const result = [];
18
+ for (const dep of deps) {
19
+ // Resolve the file path based on source type
20
+ let resolvedPath = null;
21
+ if (dep.sourceType === 'local') {
22
+ // Relative or absolute path
23
+ if (dep.source.startsWith('./') || dep.source.startsWith('../')) {
24
+ resolvedPath = path.resolve(path.dirname(sourceFilePath), dep.source);
25
+ }
26
+ else {
27
+ resolvedPath = path.resolve(dep.source);
28
+ }
29
+ // Ensure .photon.ts extension
30
+ if (!resolvedPath.endsWith('.photon.ts') && !resolvedPath.endsWith('.ts')) {
31
+ resolvedPath += '.photon.ts';
32
+ }
33
+ }
34
+ else if (dep.sourceType === 'marketplace') {
35
+ // Search common locations for marketplace photons
36
+ const slug = dep.source
37
+ .replace(/\.photon\.ts$/, '')
38
+ .replace(/\.photon$/, '')
39
+ .replace(/\.ts$/, '');
40
+ const fileName = `${slug}.photon.ts`;
41
+ const candidates = [
42
+ path.resolve(path.dirname(sourceFilePath), fileName),
43
+ path.resolve(path.dirname(sourceFilePath), 'photons', fileName),
44
+ path.join(baseDir, fileName),
45
+ path.join(baseDir, 'photons', fileName),
46
+ path.join(baseDir, 'marketplace', fileName),
47
+ ];
48
+ for (const candidate of candidates) {
49
+ if (fs.existsSync(candidate)) {
50
+ resolvedPath = candidate;
51
+ break;
52
+ }
53
+ }
54
+ }
55
+ if (!resolvedPath || !fs.existsSync(resolvedPath)) {
56
+ // Can't resolve — will be warned about separately
57
+ continue;
58
+ }
59
+ resolvedPath = fs.realpathSync(resolvedPath);
60
+ if (visited.has(resolvedPath)) {
61
+ continue; // Already processed (circular dep protection)
62
+ }
63
+ visited.add(resolvedPath);
64
+ const depSource = fs.readFileSync(resolvedPath, 'utf-8');
65
+ result.push({
66
+ name: dep.name,
67
+ source: dep.source,
68
+ filePath: resolvedPath,
69
+ sourceCode: depSource,
70
+ });
71
+ // Recurse into transitive dependencies
72
+ const transitive = resolvePhotonDeps(depSource, resolvedPath, baseDir, visited);
73
+ result.push(...transitive);
74
+ }
75
+ return result;
76
+ }
77
+ export function registerBuildCommand(program) {
78
+ program
79
+ .command('build')
80
+ .description('Compile a photon into a standalone executable binary')
81
+ .argument('<file>', 'Path to the .photon.ts file')
82
+ .option('-o, --outfile <name>', 'Name of the output binary')
83
+ .option('-t, --target <target>', 'Bun compilation target (e.g. bun-linux-x64, bun-darwin-arm64, bun-windows-x64)')
84
+ .action(async (file, options) => {
85
+ const spinner = ora(`Preparing standalone build for ${file}...`).start();
86
+ const workingDir = process.cwd();
87
+ const photonPath = path.resolve(workingDir, file);
88
+ if (!fs.existsSync(photonPath)) {
89
+ spinner.fail(`File not found: ${photonPath}`);
90
+ process.exit(1);
91
+ }
92
+ // Check that bun is available
93
+ try {
94
+ const { execSync } = await import('child_process');
95
+ execSync('bun --version', { stdio: 'ignore' });
96
+ }
97
+ catch {
98
+ spinner.fail('Bun is required for compilation. Install it: https://bun.sh');
99
+ process.exit(1);
100
+ }
101
+ // Read the source code to embed in the binary
102
+ const sourceCode = fs.readFileSync(photonPath, 'utf-8');
103
+ // Resolve @photon dependencies recursively
104
+ const baseDir = path.join(process.env.HOME || '~', '.photon');
105
+ const photonDeps = resolvePhotonDeps(sourceCode, photonPath, baseDir);
106
+ if (photonDeps.length > 0) {
107
+ spinner.info(`Bundling ${photonDeps.length} @photon dependenc${photonDeps.length === 1 ? 'y' : 'ies'}:`);
108
+ for (const dep of photonDeps) {
109
+ console.log(chalk.cyan(` 📦 ${dep.name} → ${path.basename(dep.filePath)}`));
110
+ }
111
+ console.log();
112
+ spinner.start('Continuing build...');
113
+ }
114
+ // Detect unbundleable dependencies and warn
115
+ const warnings = [];
116
+ // Check all sources (main + deps) for @mcp and @cli tags
117
+ const allSources = [sourceCode, ...photonDeps.map((d) => d.sourceCode)];
118
+ for (const src of allSources) {
119
+ for (const match of src.matchAll(/@mcp\s+(\S+)/g)) {
120
+ warnings.push(`@mcp dependency "${match[1]}" — external MCP server, must be installed separately`);
121
+ }
122
+ for (const match of src.matchAll(/@cli\s+(\S+)/g)) {
123
+ warnings.push(`@cli dependency "${match[1]}" — external CLI tool, must be available on target system`);
124
+ }
125
+ }
126
+ // Check for unresolved @photon deps (ones we couldn't find on disk)
127
+ const extractor = new SchemaExtractor();
128
+ const declaredDeps = extractor.extractPhotonDependencies(sourceCode);
129
+ for (const dep of declaredDeps) {
130
+ const resolved = photonDeps.find((d) => d.name === dep.name);
131
+ if (!resolved) {
132
+ warnings.push(`@photon dependency "${dep.name}" (from ${dep.source}) — could not be resolved, must be available at runtime`);
133
+ }
134
+ }
135
+ if (warnings.length > 0) {
136
+ spinner.warn('External dependencies detected (not bundled):');
137
+ for (const w of warnings) {
138
+ console.log(chalk.yellow(` ⚠ ${w}`));
139
+ }
140
+ console.log();
141
+ spinner.start('Continuing build...');
142
+ }
143
+ // Generate a unique temporary entrypoint file
144
+ const tempEntrypointName = `.photon-build-${Date.now()}.ts`;
145
+ const tempEntrypointPath = path.join(workingDir, tempEntrypointName);
146
+ // Determine outfile name
147
+ let outfile = options.outfile;
148
+ if (!outfile) {
149
+ const basename = path.basename(file, '.photon.ts');
150
+ outfile = basename.endsWith('.photon.js') ? path.basename(file, '.photon.js') : basename;
151
+ }
152
+ try {
153
+ // Relative import path for the photon module
154
+ let relativePhotonPath = path.relative(workingDir, photonPath);
155
+ if (!relativePhotonPath.startsWith('.')) {
156
+ relativePhotonPath = `./${relativePhotonPath}`;
157
+ }
158
+ // Escape source for embedding as a template literal
159
+ const escapeForTemplateLiteral = (src) => src.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
160
+ const escapedSource = escapeForTemplateLiteral(sourceCode);
161
+ // Resolve the photon runtime package path for the entrypoint import.
162
+ const photonPkgDir = path.resolve(__dirname, '..', '..'); // dist/ -> package root
163
+ let photonImportPath = path.relative(workingDir, path.join(photonPkgDir, 'index.js'));
164
+ if (!photonImportPath.startsWith('.')) {
165
+ photonImportPath = `./${photonImportPath}`;
166
+ }
167
+ // Generate import statements for each @photon dependency
168
+ const depImports = [];
169
+ const depMapEntries = [];
170
+ for (let i = 0; i < photonDeps.length; i++) {
171
+ const dep = photonDeps[i];
172
+ const varName = `__dep${i}`;
173
+ let depImportPath = path.relative(workingDir, dep.filePath);
174
+ if (!depImportPath.startsWith('.')) {
175
+ depImportPath = `./${depImportPath}`;
176
+ }
177
+ depImports.push(`import * as ${varName} from '${depImportPath}';`);
178
+ const escapedDepSource = escapeForTemplateLiteral(dep.sourceCode);
179
+ depMapEntries.push(` deps.set('${dep.name}', { module: ${varName}, source: \`${escapedDepSource}\`, filePath: ${JSON.stringify(dep.filePath)} });`);
180
+ // Also register by source name for fallback matching
181
+ if (dep.source !== dep.name) {
182
+ depMapEntries.push(` deps.set('${dep.source}', { module: ${varName}, source: \`${escapedDepSource}\`, filePath: ${JSON.stringify(dep.filePath)} });`);
183
+ }
184
+ }
185
+ const depImportsBlock = depImports.length > 0 ? depImports.join('\n') + '\n' : '';
186
+ const depMapBlock = depMapEntries.length > 0
187
+ ? `\nfunction buildDependencyMap() {\n const deps = new Map();\n${depMapEntries.join('\n')}\n return deps;\n}\n`
188
+ : '';
189
+ const depMapArg = depMapEntries.length > 0 ? `\n preloadedDependencies: buildDependencyMap(),` : '';
190
+ // Build a symlink routing map for the entrypoint:
191
+ // When invoked via a symlink named after a bundled dep, serve that dep instead.
192
+ const symlinkEntries = [];
193
+ for (let i = 0; i < photonDeps.length; i++) {
194
+ const dep = photonDeps[i];
195
+ const varName = `__dep${i}`;
196
+ const escapedDepSource = escapeForTemplateLiteral(dep.sourceCode);
197
+ // Extract the photon name from the file (e.g., "whatsapp" from "whatsapp.photon.ts")
198
+ const depPhotonName = path.basename(dep.filePath, '.photon.ts').replace('.photon', '');
199
+ symlinkEntries.push(` '${depPhotonName}': { module: ${varName}, source: \`${escapedDepSource}\`, filePath: ${JSON.stringify(dep.filePath)} },`);
200
+ // Also map by the dependency variable name if different
201
+ if (dep.name !== depPhotonName) {
202
+ symlinkEntries.push(` '${dep.name}': { module: ${varName}, source: \`${escapedDepSource}\`, filePath: ${JSON.stringify(dep.filePath)} },`);
203
+ }
204
+ }
205
+ const symlinkMapBlock = symlinkEntries.length > 0
206
+ ? `\nconst BUNDLED_PHOTONS: Record<string, { module: any; source: string; filePath: string }> = {\n${symlinkEntries.join('\n')}\n};\n`
207
+ : '\nconst BUNDLED_PHOTONS: Record<string, { module: any; source: string; filePath: string }> = {};\n';
208
+ // Symlink routing logic: detect which photon to serve based on executable name
209
+ const symlinkRoutingCode = `
210
+ // Detect if invoked via a symlink to serve a bundled dependency.
211
+ // Bun compiled binaries don't preserve argv[0] for symlinks, but
212
+ // $_ (set by shells) contains the actual invocation path including symlink name.
213
+ const __path = await import('path');
214
+ const invokedAs = __path.default.basename(process.env._ || process.execPath);
215
+ const bundled = BUNDLED_PHOTONS[invokedAs];
216
+ const activeModule = bundled ? bundled.module : photonModule;
217
+ const activeSource = bundled ? bundled.source : EMBEDDED_SOURCE;
218
+ const activeFilePath = bundled ? bundled.filePath : ${JSON.stringify(photonPath)};
219
+ `;
220
+ const entrypointCode = `import { PhotonServer, EmbeddedRuntime } from '${photonImportPath}';
221
+ import * as photonModule from '${relativePhotonPath}';
222
+ ${depImportsBlock}
223
+ const EMBEDDED_SOURCE = \`${escapedSource}\`;
224
+ ${depMapBlock}${symlinkMapBlock}
225
+ async function main() {
226
+ const args = process.argv.slice(2);
227
+ const command = args[0] || 'mcp';
228
+
229
+ let port: number | undefined;
230
+ if (command === 'sse') {
231
+ port = args[1] ? parseInt(args[1], 10) : undefined;
232
+ }
233
+ ${symlinkRoutingCode}
234
+ // Start embedded runtime (in-process broker + scheduler)
235
+ const runtime = new EmbeddedRuntime();
236
+ runtime.start();
237
+
238
+ const server = new PhotonServer({
239
+ filePath: activeFilePath,
240
+ transport: command === 'sse' ? 'sse' : 'stdio',
241
+ port,
242
+ preloadedModule: activeModule,
243
+ embeddedSource: activeSource,${depMapArg}
244
+ });
245
+
246
+ await server.start();
247
+
248
+ // Register @scheduled/@cron jobs from the loaded photon
249
+ if (server.getLoadedPhoton()) {
250
+ runtime.registerScheduledJobs(server.getLoadedPhoton()!, server.getLoader());
251
+ }
252
+ }
253
+
254
+ main().catch(err => {
255
+ console.error('Fatal error:', err.message || err);
256
+ process.exit(1);
257
+ });
258
+ `;
259
+ fs.writeFileSync(tempEntrypointPath, entrypointCode, 'utf-8');
260
+ spinner.text = 'Compiling executable with Bun...';
261
+ // Prepare bun build arguments
262
+ const buildArgs = ['build', tempEntrypointPath, '--compile', '--outfile', outfile];
263
+ if (options.target) {
264
+ buildArgs.push('--target', options.target);
265
+ }
266
+ const buildProcess = spawn('bun', buildArgs, {
267
+ cwd: workingDir,
268
+ stdio: 'pipe',
269
+ });
270
+ let stdoutData = '';
271
+ let stderrData = '';
272
+ buildProcess.stdout.on('data', (data) => {
273
+ stdoutData += data.toString();
274
+ });
275
+ buildProcess.stderr.on('data', (data) => {
276
+ stderrData += data.toString();
277
+ });
278
+ await new Promise((resolve, reject) => {
279
+ buildProcess.on('close', (code) => {
280
+ if (code === 0) {
281
+ resolve();
282
+ }
283
+ else {
284
+ reject(new Error(`Bun compilation failed with exit code ${code}\n\n${stderrData || stdoutData}`));
285
+ }
286
+ });
287
+ buildProcess.on('error', reject);
288
+ });
289
+ // Get file size for display
290
+ const stat = fs.statSync(outfile);
291
+ const sizeMB = (stat.size / 1024 / 1024).toFixed(1);
292
+ spinner.succeed(`Compiled: ${chalk.green(chalk.bold(outfile))} (${sizeMB} MB)`);
293
+ // Create symlinks for bundled @photon dependencies
294
+ if (photonDeps.length > 0) {
295
+ const outDir = path.dirname(path.resolve(outfile));
296
+ const outBase = path.resolve(outfile);
297
+ const createdLinks = [];
298
+ const seenNames = new Set();
299
+ for (const dep of photonDeps) {
300
+ const depName = path.basename(dep.filePath, '.photon.ts').replace('.photon', '');
301
+ if (seenNames.has(depName) || depName === path.basename(outfile))
302
+ continue;
303
+ seenNames.add(depName);
304
+ const linkPath = path.join(outDir, depName);
305
+ try {
306
+ if (fs.existsSync(linkPath))
307
+ fs.unlinkSync(linkPath);
308
+ fs.symlinkSync(outBase, linkPath);
309
+ createdLinks.push(depName);
310
+ }
311
+ catch {
312
+ // Symlink creation may fail on some filesystems — non-fatal
313
+ }
314
+ }
315
+ if (createdLinks.length > 0) {
316
+ console.log(`\n${chalk.dim('Symlinks (each serves the bundled photon as its own MCP server):')}`);
317
+ for (const name of createdLinks) {
318
+ console.log(` ${chalk.cyan(name)} → ${outfile}`);
319
+ }
320
+ }
321
+ }
322
+ console.log(`\nUsage:`);
323
+ console.log(` ./${outfile} MCP server (stdio)`);
324
+ console.log(` ./${outfile} sse MCP server (HTTP/SSE)`);
325
+ }
326
+ catch (err) {
327
+ spinner.fail('Build failed');
328
+ printError(err instanceof Error ? err.message : String(err));
329
+ process.exit(1);
330
+ }
331
+ finally {
332
+ // Clean up the temporary entrypoint file
333
+ if (fs.existsSync(tempEntrypointPath)) {
334
+ fs.unlinkSync(tempEntrypointPath);
335
+ }
336
+ }
337
+ });
338
+ }
339
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"package-app.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/package-app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8LpC,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwChE"}
1
+ {"version":3,"file":"package-app.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/package-app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuOpC,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiGhE"}
@@ -14,44 +14,56 @@ function toClassName(name) {
14
14
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
15
15
  .join('');
16
16
  }
17
- function generateBashLauncherScript(name, photonDir, startPort, openCmd) {
17
+ function generateBashLauncherScript(name, version, startPort, openCmd) {
18
18
  const endPort = startPort + 9;
19
19
  return `#!/usr/bin/env bash
20
- # Launcher for ${name} — auto-starts beam and opens PWA
20
+ # Launcher for ${name} — auto-starts beam and opens as PWA
21
21
  set -e
22
22
 
23
- PHOTON_DIR="${photonDir}"
23
+ PHOTON="${name}"
24
+ VERSION="${version}"
24
25
  START_PORT=${startPort}
25
26
  END_PORT=${endPort}
26
27
  OPEN_CMD="${openCmd}"
27
28
 
28
- # Scan ports for an existing beam serving this directory
29
+ # Detect package runner: bunx > pnpx > npx
30
+ if command -v bunx &>/dev/null; then
31
+ RUNNER="bunx @portel/photon@$VERSION"
32
+ elif command -v pnpx &>/dev/null; then
33
+ RUNNER="pnpx @portel/photon@$VERSION"
34
+ elif command -v npx &>/dev/null; then
35
+ RUNNER="npx -y @portel/photon@$VERSION"
36
+ else
37
+ echo "Error: no package runner found (npx, pnpx, or bunx required)"
38
+ exit 1
39
+ fi
40
+
41
+ # Scan ports for an existing beam already serving this photon
29
42
  for port in $(seq $START_PORT $END_PORT); do
30
43
  RESP=$(curl -s --max-time 2 "http://localhost:\${port}/api/diagnostics" 2>/dev/null || true)
31
44
  if [ -n "$RESP" ]; then
32
- DIR=$(echo "$RESP" | grep -o '"workingDir":"[^"]*"' | cut -d'"' -f4)
33
- if [ "$DIR" = "$PHOTON_DIR" ]; then
34
- $OPEN_CMD "http://localhost:\${port}"
45
+ # Check if this beam has our photon loaded
46
+ echo "$RESP" | grep -q "\\"name\\":\\"$PHOTON\\"" 2>/dev/null && {
47
+ # Beam already running with this photon — skip browser open (client likely connected)
35
48
  exit 0
36
- fi
49
+ }
37
50
  fi
38
51
  done
39
52
 
40
- # No existing beam found — start one
41
- npx @portel/photon beam --port=$START_PORT &
53
+ # No existing beam found — start one (--no-open: we handle browser launch ourselves)
54
+ $RUNNER $PHOTON --no-open --port=$START_PORT &
42
55
  BEAM_PID=$!
43
56
 
44
- # Poll until beam is ready (up to 30s)
57
+ # Poll until beam is ready (up to 30s), then open browser
45
58
  WAITED=0
46
59
  while [ $WAITED -lt 30 ]; do
47
60
  for port in $(seq $START_PORT $END_PORT); do
48
61
  RESP=$(curl -s --max-time 1 "http://localhost:\${port}/api/diagnostics" 2>/dev/null || true)
49
62
  if [ -n "$RESP" ]; then
50
- DIR=$(echo "$RESP" | grep -o '"workingDir":"[^"]*"' | cut -d'"' -f4)
51
- if [ "$DIR" = "$PHOTON_DIR" ]; then
52
- $OPEN_CMD "http://localhost:\${port}"
63
+ echo "$RESP" | grep -q "\\"name\\":\\"$PHOTON\\"" 2>/dev/null && {
64
+ $OPEN_CMD "http://localhost:\${port}/#$PHOTON?focus=1"
53
65
  exit 0
54
- fi
66
+ }
55
67
  fi
56
68
  done
57
69
  sleep 1
@@ -84,31 +96,40 @@ function generateInfoPlist(name, className) {
84
96
  </dict>
85
97
  </plist>`;
86
98
  }
87
- function generateWindowsBat(name, photonDir, startPort) {
99
+ function generateWindowsBat(name, version, startPort) {
88
100
  const endPort = startPort + 9;
89
101
  return `@echo off
90
- REM Launcher for ${name} — auto-starts beam and opens PWA
102
+ REM Launcher for ${name} — auto-starts beam and opens as PWA
91
103
  setlocal enabledelayedexpansion
92
104
 
93
- set "PHOTON_DIR=${photonDir}"
105
+ set "PHOTON=${name}"
106
+ set "VERSION=${version}"
94
107
  set START_PORT=${startPort}
95
108
  set END_PORT=${endPort}
96
109
 
97
- REM Scan ports for an existing beam serving this directory
110
+ REM Detect package runner: bunx > pnpx > npx
111
+ where bunx >nul 2>&1 && set "RUNNER=bunx @portel/photon@%VERSION%" && goto :found_runner
112
+ where pnpx >nul 2>&1 && set "RUNNER=pnpx @portel/photon@%VERSION%" && goto :found_runner
113
+ where npx >nul 2>&1 && set "RUNNER=npx -y @portel/photon@%VERSION%" && goto :found_runner
114
+ echo Error: no package runner found (npx, pnpx, or bunx required)
115
+ exit /b 1
116
+ :found_runner
117
+
118
+ REM Scan ports for an existing beam serving this photon
98
119
  for /L %%p in (%START_PORT%,1,%END_PORT%) do (
99
120
  for /f "delims=" %%r in ('curl -s --max-time 2 "http://localhost:%%p/api/diagnostics" 2^>nul') do (
100
- echo %%r | findstr /c:"%PHOTON_DIR%" >nul 2>&1
121
+ echo %%r | findstr /c:"%PHOTON%" >nul 2>&1
101
122
  if !errorlevel! equ 0 (
102
- start "" "http://localhost:%%p"
123
+ REM Beam already running with this photon — skip browser open
103
124
  exit /b 0
104
125
  )
105
126
  )
106
127
  )
107
128
 
108
129
  REM No existing beam found — start one
109
- start /b npx @portel/photon beam --port=%START_PORT%
130
+ start /b %RUNNER% %PHOTON% --no-open --port=%START_PORT%
110
131
 
111
- REM Poll until beam is ready (up to 30s)
132
+ REM Poll until beam is ready (up to 30s), then open browser
112
133
  set WAITED=0
113
134
  :poll
114
135
  if %WAITED% geq 30 (
@@ -117,9 +138,9 @@ if %WAITED% geq 30 (
117
138
  )
118
139
  for /L %%p in (%START_PORT%,1,%END_PORT%) do (
119
140
  for /f "delims=" %%r in ('curl -s --max-time 1 "http://localhost:%%p/api/diagnostics" 2^>nul') do (
120
- echo %%r | findstr /c:"%PHOTON_DIR%" >nul 2>&1
141
+ echo %%r | findstr /c:"%PHOTON%" >nul 2>&1
121
142
  if !errorlevel! equ 0 (
122
- start "" "http://localhost:%%p"
143
+ start "" "http://localhost:%%p/#%PHOTON%?focus=1"
123
144
  exit /b 0
124
145
  )
125
146
  )
@@ -129,20 +150,20 @@ set /a WAITED+=1
129
150
  goto poll
130
151
  `;
131
152
  }
132
- async function generateMacOSApp(name, className, photonDir, startPort, outputDir) {
153
+ async function generateMacOSApp(name, className, version, startPort, outputDir) {
133
154
  const appDir = path.join(outputDir, `${className}.app`);
134
155
  const contentsDir = path.join(appDir, 'Contents');
135
156
  const macosDir = path.join(contentsDir, 'MacOS');
136
157
  await fs.mkdir(macosDir, { recursive: true });
137
158
  await fs.writeFile(path.join(contentsDir, 'Info.plist'), generateInfoPlist(name, className));
138
- const script = generateBashLauncherScript(name, photonDir, startPort, 'open');
159
+ const script = generateBashLauncherScript(name, version, startPort, 'open');
139
160
  await fs.writeFile(path.join(macosDir, 'launch'), script, { mode: 0o755 });
140
161
  return appDir;
141
162
  }
142
- async function generateLinuxLauncher(name, photonDir, startPort, outputDir) {
163
+ async function generateLinuxLauncher(name, version, startPort, outputDir) {
143
164
  const shPath = path.join(outputDir, `${name}.sh`);
144
165
  const desktopPath = path.join(outputDir, `${name}.desktop`);
145
- const script = generateBashLauncherScript(name, photonDir, startPort, 'xdg-open');
166
+ const script = generateBashLauncherScript(name, version, startPort, 'xdg-open');
146
167
  await fs.writeFile(shPath, script, { mode: 0o755 });
147
168
  const desktop = `[Desktop Entry]
148
169
  Type=Application
@@ -154,11 +175,30 @@ Categories=Utility;
154
175
  await fs.writeFile(desktopPath, desktop);
155
176
  return [shPath, desktopPath];
156
177
  }
157
- async function generateWindowsLauncher(name, photonDir, startPort, outputDir) {
178
+ async function generateWindowsLauncher(name, version, startPort, outputDir) {
158
179
  const batPath = path.join(outputDir, `${name}.bat`);
159
- await fs.writeFile(batPath, generateWindowsBat(name, photonDir, startPort));
180
+ await fs.writeFile(batPath, generateWindowsBat(name, version, startPort));
160
181
  return batPath;
161
182
  }
183
+ /**
184
+ * Check if a photon directory contains an app photon (main() + @ui).
185
+ * Reads .ts/.js source files and looks for class-level @ui asset declaration
186
+ * and a main() method linked to that UI.
187
+ */
188
+ async function checkIsAppPhoton(photonDir) {
189
+ const entries = await fs.readdir(photonDir);
190
+ const sourceFiles = entries.filter((f) => /\.(ts|js)$/.test(f) && !f.endsWith('.d.ts'));
191
+ for (const file of sourceFiles) {
192
+ const content = await fs.readFile(path.join(photonDir, file), 'utf-8');
193
+ // Check for class-level @ui asset declaration (e.g. @ui my-view ./ui/view.html)
194
+ const hasClassUi = /@ui\s+\S+\s+\S+/.test(content);
195
+ // Check for a main method
196
+ const hasMain = /(?:async\s+)?main\s*\(/.test(content);
197
+ if (hasClassUi && hasMain)
198
+ return true;
199
+ }
200
+ return false;
201
+ }
162
202
  export function registerPackageAppCommand(program) {
163
203
  program
164
204
  .command('package')
@@ -176,16 +216,57 @@ export function registerPackageAppCommand(program) {
176
216
  console.error(`Error: photon directory not found: ${photonDir}`);
177
217
  process.exit(1);
178
218
  }
219
+ // Validate that the photon is an app (has main() method + @ui)
220
+ const isApp = await checkIsAppPhoton(photonDir);
221
+ if (!isApp) {
222
+ console.error(`Error: "${name}" is not an app photon.\n` +
223
+ `Only photons with a main() method and a linked @ui can be packaged as PWA apps.`);
224
+ process.exit(1);
225
+ }
226
+ // Get the current photon runtime version to pin in launchers
227
+ const { PHOTON_VERSION } = await import('../../version.js');
179
228
  const className = toClassName(name);
180
- console.log(`\n📦 Packaging ${name}...\n`);
229
+ console.log(`\n📦 Packaging ${name} (runtime v${PHOTON_VERSION})...\n`);
181
230
  await fs.mkdir(outputDir, { recursive: true });
182
231
  // Generate all platform launchers
183
- const macAppPath = await generateMacOSApp(name, className, photonDir, startPort, outputDir);
184
- const [linuxShPath, linuxDesktopPath] = await generateLinuxLauncher(name, photonDir, startPort, outputDir);
185
- const winBatPath = await generateWindowsLauncher(name, photonDir, startPort, outputDir);
232
+ const macAppPath = await generateMacOSApp(name, className, PHOTON_VERSION, startPort, outputDir);
233
+ const [linuxShPath, linuxDesktopPath] = await generateLinuxLauncher(name, PHOTON_VERSION, startPort, outputDir);
234
+ const winBatPath = await generateWindowsLauncher(name, PHOTON_VERSION, startPort, outputDir);
186
235
  console.log(` ✓ macOS ${macAppPath}`);
187
236
  console.log(` ✓ Linux ${linuxShPath}, ${path.basename(linuxDesktopPath)}`);
188
237
  console.log(` ✓ Windows ${winBatPath}`);
238
+ // Write pwa.json to record this PWA instance configuration
239
+ try {
240
+ const pwaConfigPath = path.join(workingDir, 'pwa.json');
241
+ let config = { instances: [] };
242
+ try {
243
+ const raw = await fs.readFile(pwaConfigPath, 'utf-8');
244
+ config = JSON.parse(raw);
245
+ if (!Array.isArray(config.instances))
246
+ config.instances = [];
247
+ }
248
+ catch {
249
+ // File doesn't exist yet — use default
250
+ }
251
+ // Deduplicate by port + photon name
252
+ const exists = config.instances.some((i) => i.port === startPort && i.photon === name);
253
+ if (!exists) {
254
+ config.instances.push({
255
+ port: startPort,
256
+ photon: name,
257
+ version: PHOTON_VERSION,
258
+ createdAt: new Date().toISOString(),
259
+ });
260
+ await fs.writeFile(pwaConfigPath, JSON.stringify(config, null, 2));
261
+ console.log(` ✓ pwa.json PWA configured (port ${startPort})`);
262
+ }
263
+ else {
264
+ console.log(` ✓ pwa.json Already configured`);
265
+ }
266
+ }
267
+ catch (err) {
268
+ console.error(` ⚠ pwa.json ${err.message || 'Failed to write'}`);
269
+ }
189
270
  console.log();
190
271
  });
191
272
  }