@portel/photon 1.16.0 → 1.17.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.
- package/dist/auto-ui/beam/external-mcp.d.ts.map +1 -1
- package/dist/auto-ui/beam/photon-management.d.ts +1 -1
- package/dist/auto-ui/beam/photon-management.d.ts.map +1 -1
- package/dist/auto-ui/beam/photon-management.js +5 -1
- package/dist/auto-ui/beam/photon-management.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.js +28 -7
- package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.js +2 -1
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
- package/dist/auto-ui/beam/types.d.ts.map +1 -1
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +74 -51
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/bridge/index.d.ts.map +1 -1
- package/dist/auto-ui/bridge/renderers.d.ts.map +1 -1
- package/dist/auto-ui/bridge/renderers.js +202 -13
- package/dist/auto-ui/bridge/renderers.js.map +1 -1
- package/dist/auto-ui/components/checklist.d.ts +13 -0
- package/dist/auto-ui/components/checklist.d.ts.map +1 -0
- package/dist/auto-ui/components/checklist.js +48 -0
- package/dist/auto-ui/components/checklist.js.map +1 -0
- package/dist/auto-ui/photon-host.d.ts +0 -1
- package/dist/auto-ui/photon-host.d.ts.map +1 -1
- package/dist/auto-ui/photon-host.js +0 -3
- package/dist/auto-ui/photon-host.js.map +1 -1
- package/dist/auto-ui/platform-compat.js.map +1 -1
- package/dist/auto-ui/playground-html.js +1 -1
- package/dist/auto-ui/playground-html.js.map +1 -1
- package/dist/auto-ui/registry.d.ts.map +1 -1
- package/dist/auto-ui/registry.js +2 -0
- package/dist/auto-ui/registry.js.map +1 -1
- package/dist/auto-ui/rendering/template-engine.d.ts.map +1 -1
- package/dist/auto-ui/rendering/template-engine.js +0 -3
- package/dist/auto-ui/rendering/template-engine.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/auto-ui/types.d.ts +1 -1
- package/dist/auto-ui/types.d.ts.map +1 -1
- package/dist/auto-ui/types.js.map +1 -1
- package/dist/beam-form.bundle.js +131 -0
- package/dist/beam-form.bundle.js.map +2 -2
- package/dist/beam.bundle.js +4946 -498
- package/dist/beam.bundle.js.map +4 -4
- package/dist/claude-code-plugin.js +11 -3
- package/dist/claude-code-plugin.js.map +1 -1
- package/dist/cli/commands/build.js +1 -1
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/config.js +2 -2
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/daemon.js +4 -4
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +7 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +3 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +0 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +2 -2
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/package.d.ts.map +1 -1
- package/dist/cli/commands/package.js +10 -3
- package/dist/cli/commands/package.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +5 -2
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli-alias.d.ts.map +1 -1
- package/dist/cli-alias.js +3 -2
- package/dist/cli-alias.js.map +1 -1
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +0 -2
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/server.js +321 -22
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/worker-manager.js +1 -1
- package/dist/daemon/worker-manager.js.map +1 -1
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +10 -16
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/loader.d.ts +0 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +18 -36
- package/dist/loader.js.map +1 -1
- package/dist/marketplace-manager.d.ts +14 -0
- package/dist/marketplace-manager.d.ts.map +1 -1
- package/dist/marketplace-manager.js +146 -26
- package/dist/marketplace-manager.js.map +1 -1
- package/dist/namespace-migration.d.ts.map +1 -1
- package/dist/namespace-migration.js +0 -12
- package/dist/namespace-migration.js.map +1 -1
- package/dist/path-resolver.js.map +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +18 -24
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +0 -1
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/photons/builder-compass.photon.d.ts +167 -0
- package/dist/photons/builder-compass.photon.d.ts.map +1 -0
- package/dist/photons/builder-compass.photon.js +816 -0
- package/dist/photons/builder-compass.photon.js.map +1 -0
- package/dist/photons/builder-compass.photon.ts +1129 -0
- package/dist/photons/maker.photon.d.ts +0 -1
- package/dist/photons/maker.photon.d.ts.map +1 -1
- package/dist/photons/maker.photon.js +19 -5
- package/dist/photons/maker.photon.js.map +1 -1
- package/dist/photons/maker.photon.ts +18 -6
- package/dist/photons/marketplace.photon.d.ts +1 -2
- package/dist/photons/marketplace.photon.d.ts.map +1 -1
- package/dist/photons/marketplace.photon.js +2 -3
- package/dist/photons/marketplace.photon.js.map +1 -1
- package/dist/photons/marketplace.photon.ts +2 -5
- package/dist/photons/ui/builder-compass.html +1199 -0
- package/dist/photons/ui/builder-compass.photon.html +380 -0
- package/dist/serv/auth/oauth.d.ts.map +1 -1
- package/dist/serv/auth/oauth.js.map +1 -1
- package/dist/serv/local.d.ts.map +1 -1
- package/dist/serv/local.js +1 -1
- package/dist/serv/local.js.map +1 -1
- package/dist/serv/middleware/auth.d.ts.map +1 -1
- package/dist/serv/middleware/auth.js.map +1 -1
- package/dist/serv/runtime/oauth-context.d.ts +0 -1
- package/dist/serv/runtime/oauth-context.d.ts.map +1 -1
- package/dist/serv/runtime/oauth-context.js +0 -2
- package/dist/serv/runtime/oauth-context.js.map +1 -1
- package/dist/serv/vault/token-vault.d.ts +0 -1
- package/dist/serv/vault/token-vault.d.ts.map +1 -1
- package/dist/serv/vault/token-vault.js +0 -2
- package/dist/serv/vault/token-vault.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +20 -6
- package/dist/server.js.map +1 -1
- package/dist/shared/io.d.ts +33 -0
- package/dist/shared/io.d.ts.map +1 -0
- package/dist/shared/io.js +88 -0
- package/dist/shared/io.js.map +1 -0
- package/dist/shared-utils.d.ts +13 -0
- package/dist/shared-utils.d.ts.map +1 -1
- package/dist/shared-utils.js +33 -0
- package/dist/shared-utils.js.map +1 -1
- package/dist/tasks/store.d.ts.map +1 -1
- package/dist/tasks/store.js +7 -6
- package/dist/tasks/store.js.map +1 -1
- package/dist/template-manager.d.ts +0 -1
- package/dist/template-manager.d.ts.map +1 -1
- package/dist/template-manager.js +2 -3
- package/dist/template-manager.js.map +1 -1
- package/package.json +3 -2
package/dist/auto-ui/beam.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import * as http from 'http';
|
|
9
9
|
import * as net from 'net';
|
|
10
10
|
import * as fs from 'fs/promises';
|
|
11
|
+
import { readText, readJSON as readJSONFile, writeText } from '../shared/io.js';
|
|
11
12
|
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, watch, } from 'fs';
|
|
12
13
|
import * as path from 'path';
|
|
13
14
|
import * as os from 'os';
|
|
@@ -97,14 +98,14 @@ import { MarketplaceManager } from '../marketplace-manager.js';
|
|
|
97
98
|
import { subscribeChannel, reloadDaemonPhoton } from '../daemon/client.js';
|
|
98
99
|
import { ensurePhotonEditorDeclaration, writePhotonEditorDeclaration, } from '../photon-editor-declarations.js';
|
|
99
100
|
import { ensureDaemon } from '../daemon/manager.js';
|
|
100
|
-
import { SchemaExtractor
|
|
101
|
+
import { SchemaExtractor } from '@portel/photon-core';
|
|
101
102
|
import { generateServerCard } from '../server-card.js';
|
|
102
103
|
import { handleStreamableHTTP, broadcastNotification, broadcastToBeam, } from './streamable-http-transport.js';
|
|
103
104
|
import { getBundledPhotonPath, BEAM_BUNDLED_PHOTONS } from '../shared-utils.js';
|
|
104
105
|
// BUNDLED_PHOTONS and getBundledPhotonPath are imported from shared-utils.js
|
|
105
106
|
// Extracted modules (Phase 5)
|
|
106
107
|
import { loadConfig as loadConfigFromModule, saveConfig as saveConfigFromModule, migrateConfig as migrateConfigFromModule, getConfigFilePath as getConfigFilePathFromModule, } from './beam/config.js';
|
|
107
|
-
import { extractClassMetadataFromSource as extractClassMetadataFromModule, applyMethodVisibility as applyMethodVisibilityFromModule, extractCspFromSource as extractCspFromModule, prettifyName as prettifyNameFromModule,
|
|
108
|
+
import { extractClassMetadataFromSource as extractClassMetadataFromModule, applyMethodVisibility as applyMethodVisibilityFromModule, extractCspFromSource as extractCspFromModule, prettifyName as prettifyNameFromModule, backfillEnvDefaults as backfillEnvDefaultsFromModule, } from './beam/class-metadata.js';
|
|
108
109
|
import { StartupSequencer } from './beam/startup.js';
|
|
109
110
|
import { SubscriptionManager } from './beam/subscription.js';
|
|
110
111
|
import { handleMarketplaceRoutes } from './beam/routes/api-marketplace.js';
|
|
@@ -119,8 +120,6 @@ const getConfigFilePath = getConfigFilePathFromModule;
|
|
|
119
120
|
const externalMCPs = [];
|
|
120
121
|
const externalMCPClients = new Map();
|
|
121
122
|
const externalMCPSDKClients = new Map();
|
|
122
|
-
// Delegate to extracted module
|
|
123
|
-
const prettifyToolName = prettifyToolNameFromModule;
|
|
124
123
|
// Delegates — external MCP management now in beam/external-mcp.ts
|
|
125
124
|
const externalMCPState = { externalMCPs, externalMCPClients, externalMCPSDKClients };
|
|
126
125
|
const loadExternalMCPs = (config) => loadExternalMCPsFromModule(config, externalMCPState);
|
|
@@ -485,18 +484,43 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
485
484
|
logger.warn(`Asset repair check failed: ${getErrorMessage(error)}`);
|
|
486
485
|
}
|
|
487
486
|
// Discover all photons with namespace metadata (user photons + bundled photons)
|
|
488
|
-
const
|
|
489
|
-
//
|
|
487
|
+
const scannedPhotonList = await listPhotonFilesWithNamespace(workingDir);
|
|
488
|
+
// Deduplicate aliases that resolve to the same underlying file (for example,
|
|
489
|
+
// a marketplace photon and a local alias/symlink that both point at the same
|
|
490
|
+
// source). Beam should not show both copies in the sidebar.
|
|
491
|
+
const userPhotonListDetailed = [];
|
|
492
|
+
const seenPhotonKeys = new Set();
|
|
493
|
+
for (const photon of scannedPhotonList) {
|
|
494
|
+
let realPath = photon.filePath;
|
|
495
|
+
try {
|
|
496
|
+
realPath = realpathSync(photon.filePath);
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
// Fall back to the discovered path if realpath resolution fails.
|
|
500
|
+
}
|
|
501
|
+
const dedupeKey = `${photon.name}::${realPath}`;
|
|
502
|
+
if (seenPhotonKeys.has(dedupeKey))
|
|
503
|
+
continue;
|
|
504
|
+
seenPhotonKeys.add(dedupeKey);
|
|
505
|
+
userPhotonListDetailed.push(photon);
|
|
506
|
+
}
|
|
507
|
+
// Detect name collisions and generate friendly duplicate labels. We should
|
|
508
|
+
// never leak namespace/owner names into the sidebar just because there are
|
|
509
|
+
// multiple copies of a photon with the same short name.
|
|
490
510
|
const nameOccurrences = new Map();
|
|
491
511
|
for (const p of userPhotonListDetailed) {
|
|
492
512
|
nameOccurrences.set(p.name, (nameOccurrences.get(p.name) || 0) + 1);
|
|
493
513
|
}
|
|
494
|
-
// Build photon list
|
|
495
|
-
// Also track resolved paths from namespace scan
|
|
514
|
+
// Build photon list with short names plus a numeric suffix for duplicates.
|
|
515
|
+
// Also track resolved paths from namespace scan.
|
|
496
516
|
const namespacePaths = new Map(); // displayName → filePath
|
|
497
517
|
const userPhotonList = [];
|
|
518
|
+
const duplicateIndex = new Map();
|
|
498
519
|
for (const p of userPhotonListDetailed) {
|
|
499
|
-
const
|
|
520
|
+
const duplicateCount = nameOccurrences.get(p.name) || 0;
|
|
521
|
+
const nextIndex = (duplicateIndex.get(p.name) || 0) + 1;
|
|
522
|
+
duplicateIndex.set(p.name, nextIndex);
|
|
523
|
+
const displayName = duplicateCount > 1 ? `${p.name} (${nextIndex})` : p.name;
|
|
500
524
|
userPhotonList.push(displayName);
|
|
501
525
|
namespacePaths.set(displayName, p.filePath);
|
|
502
526
|
}
|
|
@@ -527,9 +551,6 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
527
551
|
// Beam handles config errors gracefully via UI forms, but we still want to see actual errors
|
|
528
552
|
const errorOnlyLogger = createLogger({ level: 'error' });
|
|
529
553
|
const loader = new PhotonLoader(false, errorOnlyLogger, workingDir);
|
|
530
|
-
// Counts updated after photon loading
|
|
531
|
-
let configuredCount = 0;
|
|
532
|
-
let unconfiguredCount = 0;
|
|
533
554
|
// Check for placeholder defaults or localhost URLs (which need local services running)
|
|
534
555
|
const isPlaceholderOrLocalDefault = (value) => {
|
|
535
556
|
if (value.includes('<') || value.includes('your-'))
|
|
@@ -558,7 +579,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
558
579
|
let source;
|
|
559
580
|
let isInternal;
|
|
560
581
|
try {
|
|
561
|
-
source = await
|
|
582
|
+
source = await readText(photonPath);
|
|
562
583
|
await ensurePhotonEditorDeclaration(photonPath, source, workingDir).catch(() => { });
|
|
563
584
|
}
|
|
564
585
|
catch {
|
|
@@ -634,7 +655,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
634
655
|
photonMCPs.set(name, mcp);
|
|
635
656
|
backfillEnvDefaults(instance, constructorParams);
|
|
636
657
|
// Extract schema for UI — reuse source read from above
|
|
637
|
-
const schemaSource = source || (await
|
|
658
|
+
const schemaSource = source || (await readText(photonPath));
|
|
638
659
|
const metadata = extractor.extractAllFromSource(schemaSource);
|
|
639
660
|
const schemas = metadata.tools;
|
|
640
661
|
const templates = metadata.templates;
|
|
@@ -652,8 +673,6 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
652
673
|
// If loader didn't resolve UI assets but source has @ui tags,
|
|
653
674
|
// extract them from source to populate linkedUi for sidebar
|
|
654
675
|
if (uiAssets.length === 0 && schemaSource) {
|
|
655
|
-
const uiTagRegex = /^\s*\*\s*@ui\s+(\S+)(?:\s+(\S+))?/gm;
|
|
656
|
-
let uiMatch;
|
|
657
676
|
const classUiMatch = schemaSource.match(/^\s*\*\s*@ui\s+(\S+)\s+(\.\/\S+)/m);
|
|
658
677
|
if (classUiMatch) {
|
|
659
678
|
// Class-level @ui tag with file path: @ui dashboard ./ui/dashboard.html
|
|
@@ -777,8 +796,10 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
777
796
|
catch {
|
|
778
797
|
// No install metadata - that's fine
|
|
779
798
|
}
|
|
780
|
-
|
|
781
|
-
const
|
|
799
|
+
// Extract class-level JSDoc block to avoid matching @tags inside template literals
|
|
800
|
+
const classDocblock = schemaSource?.match(/\/\*\*[\s\S]*?\*\/\s*(?:export\s+)?(?:default\s+)?class\b/)?.[0] ?? '';
|
|
801
|
+
const isStateful = classDocblock ? /@stateful\b/.test(classDocblock) : false;
|
|
802
|
+
const authMatch = classDocblock?.match(/@auth\b(?:\s+(\S+))?/i);
|
|
782
803
|
const authValue = authMatch ? authMatch[1]?.trim() || 'required' : undefined;
|
|
783
804
|
return {
|
|
784
805
|
id: generatePhotonId(photonPath),
|
|
@@ -844,19 +865,26 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
844
865
|
uiPath = asset.resolvedPath;
|
|
845
866
|
}
|
|
846
867
|
else {
|
|
847
|
-
// Prefer .photon.html
|
|
868
|
+
// Prefer .photon.html, then .photon.md, fall back to .html
|
|
848
869
|
const photonHtmlPath = path.join(photonDir, photonName, 'ui', `${uiId}.photon.html`);
|
|
870
|
+
const photonMdPath = path.join(photonDir, photonName, 'ui', `${uiId}.photon.md`);
|
|
849
871
|
try {
|
|
850
872
|
await fs.access(photonHtmlPath);
|
|
851
873
|
uiPath = photonHtmlPath;
|
|
852
874
|
}
|
|
853
875
|
catch {
|
|
854
|
-
|
|
876
|
+
try {
|
|
877
|
+
await fs.access(photonMdPath);
|
|
878
|
+
uiPath = photonMdPath;
|
|
879
|
+
}
|
|
880
|
+
catch {
|
|
881
|
+
uiPath = path.join(photonDir, photonName, 'ui', `${uiId}.html`);
|
|
882
|
+
}
|
|
855
883
|
}
|
|
856
884
|
}
|
|
857
|
-
const isPhotonTemplate = uiPath.endsWith('.photon.html');
|
|
885
|
+
const isPhotonTemplate = uiPath.endsWith('.photon.html') || uiPath.endsWith('.photon.md');
|
|
858
886
|
try {
|
|
859
|
-
const content = await
|
|
887
|
+
const content = await readText(uiPath);
|
|
860
888
|
return { content, isPhotonTemplate };
|
|
861
889
|
}
|
|
862
890
|
catch {
|
|
@@ -868,7 +896,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
868
896
|
const formatName = uiId.slice('format-'.length);
|
|
869
897
|
const formatPath = path.join(photonDir, photonName, 'assets', 'formats', `${formatName}.html`);
|
|
870
898
|
try {
|
|
871
|
-
const content = await
|
|
899
|
+
const content = await readText(formatPath);
|
|
872
900
|
return { content, isPhotonTemplate: false };
|
|
873
901
|
}
|
|
874
902
|
catch {
|
|
@@ -1019,7 +1047,12 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1019
1047
|
return configurePhotonViaMCP(photonName, config, photons, photonMCPs, loader, savedConfig, workingDir, activeLoads);
|
|
1020
1048
|
},
|
|
1021
1049
|
reloadPhoton: async (photonName) => {
|
|
1022
|
-
return reloadPhotonViaMCP(photonName, photons, photonMCPs, loader, savedConfig, broadcastPhotonChange, activeLoads)
|
|
1050
|
+
return reloadPhotonViaMCP(photonName, photons, photonMCPs, loader, savedConfig, broadcastPhotonChange, activeLoads, (name, path, isStateful) => {
|
|
1051
|
+
if (isStateful) {
|
|
1052
|
+
subscribeStatefulPhoton(name).catch(() => { });
|
|
1053
|
+
reloadDaemonPhoton(name, path, workingDir).catch(() => { });
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1023
1056
|
},
|
|
1024
1057
|
removePhoton: async (photonName) => {
|
|
1025
1058
|
return removePhotonViaMCP(photonName, photons, photonMCPs, savedConfig, broadcastPhotonChange, workingDir);
|
|
@@ -1190,7 +1223,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1190
1223
|
if (url.pathname === '/beam.bundle.js') {
|
|
1191
1224
|
try {
|
|
1192
1225
|
const bundlePath = path.join(__dirname, '../../dist/beam.bundle.js');
|
|
1193
|
-
const content = await
|
|
1226
|
+
const content = await readText(bundlePath);
|
|
1194
1227
|
res.writeHead(200, {
|
|
1195
1228
|
'Content-Type': 'text/javascript',
|
|
1196
1229
|
'Cache-Control': 'no-cache',
|
|
@@ -1199,7 +1232,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1199
1232
|
}
|
|
1200
1233
|
catch {
|
|
1201
1234
|
res.writeHead(404);
|
|
1202
|
-
res.end('Bundle not found. Run
|
|
1235
|
+
res.end('Bundle not found. Run the beam build script first.');
|
|
1203
1236
|
}
|
|
1204
1237
|
return;
|
|
1205
1238
|
}
|
|
@@ -1207,7 +1240,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1207
1240
|
if (url.pathname === '/beam-form.bundle.js') {
|
|
1208
1241
|
try {
|
|
1209
1242
|
const formBundlePath = path.join(__dirname, '../../dist/beam-form.bundle.js');
|
|
1210
|
-
const content = await
|
|
1243
|
+
const content = await readText(formBundlePath);
|
|
1211
1244
|
res.writeHead(200, {
|
|
1212
1245
|
'Content-Type': 'text/javascript',
|
|
1213
1246
|
'Cache-Control': 'no-cache',
|
|
@@ -1216,14 +1249,14 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1216
1249
|
}
|
|
1217
1250
|
catch {
|
|
1218
1251
|
res.writeHead(404);
|
|
1219
|
-
res.end('Form bundle not found. Run
|
|
1252
|
+
res.end('Form bundle not found. Run the beam build script first.');
|
|
1220
1253
|
}
|
|
1221
1254
|
return;
|
|
1222
1255
|
}
|
|
1223
1256
|
if (url.pathname === '/beam-ts-worker.js') {
|
|
1224
1257
|
try {
|
|
1225
1258
|
const workerPath = path.join(__dirname, '../../dist/beam-ts-worker.js');
|
|
1226
|
-
const content = await
|
|
1259
|
+
const content = await readText(workerPath);
|
|
1227
1260
|
res.writeHead(200, {
|
|
1228
1261
|
'Content-Type': 'text/javascript',
|
|
1229
1262
|
'Cache-Control': 'no-cache',
|
|
@@ -1232,7 +1265,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1232
1265
|
}
|
|
1233
1266
|
catch {
|
|
1234
1267
|
res.writeHead(404);
|
|
1235
|
-
res.end('TS worker not found. Run
|
|
1268
|
+
res.end('TS worker not found. Run the beam build script first.');
|
|
1236
1269
|
}
|
|
1237
1270
|
return;
|
|
1238
1271
|
}
|
|
@@ -1696,7 +1729,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1696
1729
|
params[k] = v;
|
|
1697
1730
|
}
|
|
1698
1731
|
const pureViewPath = path.join(__dirname, 'frontend/pure-view.html');
|
|
1699
|
-
let html = await
|
|
1732
|
+
let html = await readText(pureViewPath);
|
|
1700
1733
|
// Replace placeholders
|
|
1701
1734
|
const argsJson = escapeHtml(JSON.stringify(params));
|
|
1702
1735
|
html = html
|
|
@@ -1720,7 +1753,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1720
1753
|
if (url.pathname === '/' || !url.pathname.startsWith('/api')) {
|
|
1721
1754
|
try {
|
|
1722
1755
|
const indexPath = path.join(__dirname, 'frontend/index.html');
|
|
1723
|
-
let content = await
|
|
1756
|
+
let content = await readText(indexPath);
|
|
1724
1757
|
// Inject shell integration flag so frontend can strip CLI prefix
|
|
1725
1758
|
if (_shellIntegrationInstalled) {
|
|
1726
1759
|
content = content.replace('</head>', '<script>window.__PHOTON_SHELL_INIT=true</script></head>');
|
|
@@ -1905,14 +1938,14 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1905
1938
|
// Auto-scaffold empty photon files with a starter template
|
|
1906
1939
|
if (isNewPhoton) {
|
|
1907
1940
|
try {
|
|
1908
|
-
const rawContent = await
|
|
1941
|
+
const rawContent = await readText(photonPath);
|
|
1909
1942
|
if (rawContent.trim().length === 0) {
|
|
1910
1943
|
const className = photonName
|
|
1911
1944
|
.split(/[-_]/)
|
|
1912
1945
|
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
1913
1946
|
.join('');
|
|
1914
1947
|
const scaffold = `/**\n * ${className} Photon\n */\n\nexport default class ${className} {\n /**\n * Example tool\n * @param message Message to echo\n */\n async echo(params: { message: string }) {\n return \`Echo: \${params.message}\`;\n }\n}\n`;
|
|
1915
|
-
await
|
|
1948
|
+
await writeText(photonPath, scaffold);
|
|
1916
1949
|
logger.info(`📝 Scaffolded empty file: ${photonName}.photon.ts`);
|
|
1917
1950
|
// The write triggers another watcher event which will load the scaffolded photon
|
|
1918
1951
|
return;
|
|
@@ -1927,7 +1960,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1927
1960
|
const extractor = new SchemaExtractor();
|
|
1928
1961
|
let constructorParams = [];
|
|
1929
1962
|
try {
|
|
1930
|
-
const source = await
|
|
1963
|
+
const source = await readText(photonPath);
|
|
1931
1964
|
await writePhotonEditorDeclaration(photonPath, source, workingDir).catch(() => { });
|
|
1932
1965
|
const params = extractor.extractConstructorParams(source);
|
|
1933
1966
|
constructorParams = params
|
|
@@ -1975,7 +2008,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
1975
2008
|
photonMCPs.set(photonName, mcp);
|
|
1976
2009
|
// Re-extract schema - use extractAllFromSource to get both tools and templates
|
|
1977
2010
|
const extractor = new SchemaExtractor();
|
|
1978
|
-
const reloadSource = await
|
|
2011
|
+
const reloadSource = await readText(photonPath);
|
|
1979
2012
|
await writePhotonEditorDeclaration(photonPath, reloadSource, workingDir).catch(() => { });
|
|
1980
2013
|
const reloadMetadata = extractor.extractAllFromSource(reloadSource);
|
|
1981
2014
|
const schemas = reloadMetadata.tools;
|
|
@@ -2153,7 +2186,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
2153
2186
|
const extractor = new SchemaExtractor();
|
|
2154
2187
|
let constructorParams = [];
|
|
2155
2188
|
try {
|
|
2156
|
-
const source = await
|
|
2189
|
+
const source = await readText(photonPath);
|
|
2157
2190
|
const params = extractor.extractConstructorParams(source);
|
|
2158
2191
|
constructorParams = params
|
|
2159
2192
|
.filter((p) => p.isPrimitive)
|
|
@@ -2340,18 +2373,9 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
2340
2373
|
}
|
|
2341
2374
|
}
|
|
2342
2375
|
}
|
|
2343
|
-
configuredCount = photons.filter((p) => p.configured).length;
|
|
2344
|
-
unconfiguredCount = photons.filter((p) => !p.configured).length;
|
|
2345
2376
|
// Load external MCPs from config
|
|
2346
2377
|
const externalMCPList = await loadExternalMCPs(savedConfig);
|
|
2347
2378
|
externalMCPs.push(...externalMCPList);
|
|
2348
|
-
const connectedMCPs = externalMCPList.filter((m) => m.connected).length;
|
|
2349
|
-
const failedMCPs = externalMCPList.length - connectedMCPs;
|
|
2350
|
-
const photonStatus = unconfiguredCount > 0
|
|
2351
|
-
? `${configuredCount} ready, ${unconfiguredCount} need setup`
|
|
2352
|
-
: `${configuredCount} photon${configuredCount !== 1 ? 's' : ''} ready`;
|
|
2353
|
-
const mcpStatus = externalMCPList.length > 0 ? `, ${connectedMCPs}/${externalMCPList.length} MCPs` : '';
|
|
2354
|
-
const url = `http://localhost:${process.env.BEAM_PORT || port}`;
|
|
2355
2379
|
// Mark startup complete — flushes queued output and restores console
|
|
2356
2380
|
startup.ready();
|
|
2357
2381
|
// Notify connected clients that photon list is now available
|
|
@@ -2376,8 +2400,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
2376
2400
|
const instName = instanceName || 'default';
|
|
2377
2401
|
const stateFile = path.join(workingDir, 'state', photonName, `${instName}.json`);
|
|
2378
2402
|
try {
|
|
2379
|
-
const
|
|
2380
|
-
const state = JSON.parse(json);
|
|
2403
|
+
const state = await readJSONFile(stateFile);
|
|
2381
2404
|
for (const [key, value] of Object.entries(state)) {
|
|
2382
2405
|
const target = mcp.instance[key];
|
|
2383
2406
|
if (target && typeof target.splice === 'function' && Array.isArray(value)) {
|
|
@@ -2619,8 +2642,8 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
2619
2642
|
configDebounce = null;
|
|
2620
2643
|
let newConfig;
|
|
2621
2644
|
try {
|
|
2622
|
-
const data = await
|
|
2623
|
-
newConfig = migrateConfig(
|
|
2645
|
+
const data = await readJSONFile(configFile);
|
|
2646
|
+
newConfig = migrateConfig(data);
|
|
2624
2647
|
}
|
|
2625
2648
|
catch (err) {
|
|
2626
2649
|
logger.warn(`⚠️ Failed to parse config.json: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2654,7 +2677,7 @@ export async function startBeam(rawWorkingDir, port) {
|
|
|
2654
2677
|
logger.info(`🔌 Removed external MCP: ${name}`);
|
|
2655
2678
|
}
|
|
2656
2679
|
// Close SDK clients after all Maps are consistent
|
|
2657
|
-
for (const { name, client } of removedSdkClients) {
|
|
2680
|
+
for (const { name: _name, client } of removedSdkClients) {
|
|
2658
2681
|
try {
|
|
2659
2682
|
await client.close();
|
|
2660
2683
|
}
|