@portel/photon 1.28.2 → 1.31.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 (183) hide show
  1. package/README.md +42 -11
  2. package/dist/asset-resolver.d.ts +44 -0
  3. package/dist/asset-resolver.d.ts.map +1 -0
  4. package/dist/asset-resolver.js +105 -0
  5. package/dist/asset-resolver.js.map +1 -0
  6. package/dist/auto-ui/beam/external-mcp-manager.d.ts +73 -0
  7. package/dist/auto-ui/beam/external-mcp-manager.d.ts.map +1 -0
  8. package/dist/auto-ui/beam/external-mcp-manager.js +65 -0
  9. package/dist/auto-ui/beam/external-mcp-manager.js.map +1 -0
  10. package/dist/auto-ui/beam/external-mcp.d.ts.map +1 -1
  11. package/dist/auto-ui/beam/external-mcp.js +25 -1
  12. package/dist/auto-ui/beam/external-mcp.js.map +1 -1
  13. package/dist/auto-ui/beam/photon-management.d.ts.map +1 -1
  14. package/dist/auto-ui/beam/photon-management.js +11 -8
  15. package/dist/auto-ui/beam/photon-management.js.map +1 -1
  16. package/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -1
  17. package/dist/auto-ui/beam/routes/api-browse.js +7 -4
  18. package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
  19. package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -1
  20. package/dist/auto-ui/beam/routes/api-config.js +3 -2
  21. package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
  22. package/dist/auto-ui/beam/routes/api-marketplace.d.ts.map +1 -1
  23. package/dist/auto-ui/beam/routes/api-marketplace.js +6 -2
  24. package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -1
  25. package/dist/auto-ui/beam/startup.js.map +1 -1
  26. package/dist/auto-ui/beam/types.d.ts +5 -2
  27. package/dist/auto-ui/beam/types.d.ts.map +1 -1
  28. package/dist/auto-ui/beam.d.ts.map +1 -1
  29. package/dist/auto-ui/beam.js +239 -88
  30. package/dist/auto-ui/beam.js.map +1 -1
  31. package/dist/auto-ui/bridge/index.d.ts.map +1 -1
  32. package/dist/auto-ui/bridge/index.js +11 -0
  33. package/dist/auto-ui/bridge/index.js.map +1 -1
  34. package/dist/auto-ui/bridge/types.d.ts +2 -0
  35. package/dist/auto-ui/bridge/types.d.ts.map +1 -1
  36. package/dist/auto-ui/openapi-generator.js +1 -4
  37. package/dist/auto-ui/openapi-generator.js.map +1 -1
  38. package/dist/auto-ui/photon-bridge.d.ts +4 -0
  39. package/dist/auto-ui/photon-bridge.d.ts.map +1 -1
  40. package/dist/auto-ui/photon-bridge.js.map +1 -1
  41. package/dist/auto-ui/photon-host.js.map +1 -1
  42. package/dist/auto-ui/streamable-http-transport.d.ts +7 -0
  43. package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
  44. package/dist/auto-ui/streamable-http-transport.js +252 -43
  45. package/dist/auto-ui/streamable-http-transport.js.map +1 -1
  46. package/dist/auto-ui/types.d.ts +24 -2
  47. package/dist/auto-ui/types.d.ts.map +1 -1
  48. package/dist/auto-ui/types.js.map +1 -1
  49. package/dist/beam.bundle.js +202 -24
  50. package/dist/beam.bundle.js.map +3 -3
  51. package/dist/capability-negotiator.d.ts +39 -1
  52. package/dist/capability-negotiator.d.ts.map +1 -1
  53. package/dist/capability-negotiator.js +5 -0
  54. package/dist/capability-negotiator.js.map +1 -1
  55. package/dist/cf-bindings-parser.d.ts +15 -0
  56. package/dist/cf-bindings-parser.d.ts.map +1 -0
  57. package/dist/cf-bindings-parser.js +98 -0
  58. package/dist/cf-bindings-parser.js.map +1 -0
  59. package/dist/cf-usage-scanner.d.ts +76 -0
  60. package/dist/cf-usage-scanner.d.ts.map +1 -0
  61. package/dist/cf-usage-scanner.js +179 -0
  62. package/dist/cf-usage-scanner.js.map +1 -0
  63. package/dist/cli/commands/build.d.ts.map +1 -1
  64. package/dist/cli/commands/build.js +124 -16
  65. package/dist/cli/commands/build.js.map +1 -1
  66. package/dist/cli/commands/cf.d.ts +18 -0
  67. package/dist/cli/commands/cf.d.ts.map +1 -0
  68. package/dist/cli/commands/cf.js +207 -0
  69. package/dist/cli/commands/cf.js.map +1 -0
  70. package/dist/cli/commands/info.js +1 -1
  71. package/dist/cli/commands/info.js.map +1 -1
  72. package/dist/cli/commands/init.d.ts.map +1 -1
  73. package/dist/cli/commands/init.js +59 -46
  74. package/dist/cli/commands/init.js.map +1 -1
  75. package/dist/cli/commands/run.d.ts.map +1 -1
  76. package/dist/cli/commands/run.js +3 -0
  77. package/dist/cli/commands/run.js.map +1 -1
  78. package/dist/cli/index.d.ts.map +1 -1
  79. package/dist/cli/index.js +43 -6
  80. package/dist/cli/index.js.map +1 -1
  81. package/dist/daemon/client.d.ts.map +1 -1
  82. package/dist/daemon/client.js +40 -33
  83. package/dist/daemon/client.js.map +1 -1
  84. package/dist/daemon/manager.d.ts +6 -2
  85. package/dist/daemon/manager.d.ts.map +1 -1
  86. package/dist/daemon/manager.js +75 -20
  87. package/dist/daemon/manager.js.map +1 -1
  88. package/dist/daemon/server.js +69 -11
  89. package/dist/daemon/server.js.map +1 -1
  90. package/dist/daemon/worker-host.js.map +1 -1
  91. package/dist/deploy/cloudflare.d.ts +27 -0
  92. package/dist/deploy/cloudflare.d.ts.map +1 -1
  93. package/dist/deploy/cloudflare.js +210 -3
  94. package/dist/deploy/cloudflare.js.map +1 -1
  95. package/dist/editor-support/docblock-tag-catalog.d.ts.map +1 -1
  96. package/dist/editor-support/docblock-tag-catalog.js +32 -2
  97. package/dist/editor-support/docblock-tag-catalog.js.map +1 -1
  98. package/dist/embedded-runtime.js.map +1 -1
  99. package/dist/format/registry.d.ts +83 -0
  100. package/dist/format/registry.d.ts.map +1 -0
  101. package/dist/format/registry.js +139 -0
  102. package/dist/format/registry.js.map +1 -0
  103. package/dist/format/seed.d.ts +18 -0
  104. package/dist/format/seed.d.ts.map +1 -0
  105. package/dist/format/seed.js +246 -0
  106. package/dist/format/seed.js.map +1 -0
  107. package/dist/loader.d.ts +61 -66
  108. package/dist/loader.d.ts.map +1 -1
  109. package/dist/loader.js +315 -327
  110. package/dist/loader.js.map +1 -1
  111. package/dist/photon-cli-runner.d.ts.map +1 -1
  112. package/dist/photon-cli-runner.js +20 -11
  113. package/dist/photon-cli-runner.js.map +1 -1
  114. package/dist/photons/maker.photon.d.ts +2 -2
  115. package/dist/photons/maker.photon.d.ts.map +1 -1
  116. package/dist/photons/maker.photon.js +5 -6
  117. package/dist/photons/maker.photon.js.map +1 -1
  118. package/dist/photons/maker.photon.ts +5 -6
  119. package/dist/resource-server.d.ts +55 -15
  120. package/dist/resource-server.d.ts.map +1 -1
  121. package/dist/resource-server.js +205 -50
  122. package/dist/resource-server.js.map +1 -1
  123. package/dist/runtime/cf-local.d.ts +157 -0
  124. package/dist/runtime/cf-local.d.ts.map +1 -0
  125. package/dist/runtime/cf-local.js +406 -0
  126. package/dist/runtime/cf-local.js.map +1 -0
  127. package/dist/server.d.ts +117 -2
  128. package/dist/server.d.ts.map +1 -1
  129. package/dist/server.js +681 -67
  130. package/dist/server.js.map +1 -1
  131. package/dist/settings-persistence.d.ts +50 -0
  132. package/dist/settings-persistence.d.ts.map +1 -0
  133. package/dist/settings-persistence.js +188 -0
  134. package/dist/settings-persistence.js.map +1 -0
  135. package/dist/shared/asset-encoding.d.ts +30 -0
  136. package/dist/shared/asset-encoding.d.ts.map +1 -0
  137. package/dist/shared/asset-encoding.js +0 -0
  138. package/dist/shared/asset-encoding.js.map +1 -0
  139. package/dist/shared/audit-sqlite.d.ts.map +1 -1
  140. package/dist/shared/audit-sqlite.js +0 -1
  141. package/dist/shared/audit-sqlite.js.map +1 -1
  142. package/dist/shared/cross-origin-headers.d.ts +47 -0
  143. package/dist/shared/cross-origin-headers.d.ts.map +1 -0
  144. package/dist/shared/cross-origin-headers.js +61 -0
  145. package/dist/shared/cross-origin-headers.js.map +1 -0
  146. package/dist/shared/error-handler.d.ts.map +1 -1
  147. package/dist/shared/error-handler.js +3 -1
  148. package/dist/shared/error-handler.js.map +1 -1
  149. package/dist/shared/expose-route-extractor.d.ts +36 -0
  150. package/dist/shared/expose-route-extractor.d.ts.map +1 -0
  151. package/dist/shared/expose-route-extractor.js +64 -0
  152. package/dist/shared/expose-route-extractor.js.map +1 -0
  153. package/dist/shared/extract-claims.d.ts +33 -0
  154. package/dist/shared/extract-claims.d.ts.map +1 -0
  155. package/dist/shared/extract-claims.js +60 -0
  156. package/dist/shared/extract-claims.js.map +1 -0
  157. package/dist/shared/http-route-extractor.d.ts +6 -0
  158. package/dist/shared/http-route-extractor.d.ts.map +1 -1
  159. package/dist/shared/http-route-extractor.js +29 -5
  160. package/dist/shared/http-route-extractor.js.map +1 -1
  161. package/dist/shared/instance-binding.d.ts +53 -0
  162. package/dist/shared/instance-binding.d.ts.map +1 -0
  163. package/dist/shared/instance-binding.js +85 -0
  164. package/dist/shared/instance-binding.js.map +1 -0
  165. package/dist/shared/io.d.ts.map +1 -1
  166. package/dist/shared/io.js +5 -2
  167. package/dist/shared/io.js.map +1 -1
  168. package/dist/shared/logger.js.map +1 -1
  169. package/dist/shared/sqlite-runtime.d.ts.map +1 -1
  170. package/dist/shared/sqlite-runtime.js +0 -1
  171. package/dist/shared/sqlite-runtime.js.map +1 -1
  172. package/dist/task-executor.js.map +1 -1
  173. package/dist/telemetry/sdk.d.ts.map +1 -1
  174. package/dist/telemetry/sdk.js +0 -1
  175. package/dist/telemetry/sdk.js.map +1 -1
  176. package/dist/test-runner.d.ts.map +1 -1
  177. package/dist/test-runner.js.map +1 -1
  178. package/dist/types/server-types.d.ts +16 -7
  179. package/dist/types/server-types.d.ts.map +1 -1
  180. package/package.json +14 -4
  181. package/templates/cloudflare/worker.ts.template +428 -14
  182. package/templates/cloudflare/wrangler.toml.template +2 -7
  183. package/templates/photon.template.ts +13 -0
@@ -144,8 +144,10 @@ setBroker(inProcessBroker);
144
144
  const workerManager = new WorkerManager(logger);
145
145
  // Wire worker manager pub/sub bridge to main broker
146
146
  workerManager.onPublish = (channel, message) => {
147
+ // Worker pub/sub bridge: the worker emits a ChannelMessage-shaped envelope;
148
+ // unknown → ChannelMessage cast is the trust boundary between the worker
149
+ // protocol and the in-process broker.
147
150
  void inProcessBroker.publish(message);
148
- // Also forward to other workers
149
151
  workerManager.dispatchToWorkers(channel, message);
150
152
  };
151
153
  // Track connected sockets for graceful shutdown broadcast
@@ -311,6 +313,8 @@ const pendingPrompts = new Map();
311
313
  const channelSubscriptions = new Map();
312
314
  /** Buffer retention window — events older than this are purged */
313
315
  const EVENT_BUFFER_DURATION_MS = 5 * 60 * 1000; // 5 minutes
316
+ /** Hard cap per channel — prevents unbounded growth on high-frequency channels */
317
+ const MAX_BUFFER_EVENTS_PER_CHANNEL = 500;
314
318
  const channelEventBuffers = new Map();
315
319
  function bufferEvent(channel, message) {
316
320
  let buffer = channelEventBuffers.get(channel);
@@ -338,6 +342,13 @@ function bufferEvent(channel, message) {
338
342
  buffer.events.splice(0, firstValid);
339
343
  }
340
344
  }
345
+ // Hard cap: trim oldest when a high-frequency channel exceeds the count limit.
346
+ // A reconnecting subscriber whose lastEventId predates the oldest buffered event
347
+ // will receive refreshNeeded:true via getEventsSince — same handling as the
348
+ // time-based purge above.
349
+ if (buffer.events.length > MAX_BUFFER_EVENTS_PER_CHANNEL) {
350
+ buffer.events.splice(0, buffer.events.length - MAX_BUFFER_EVENTS_PER_CHANNEL);
351
+ }
341
352
  return now;
342
353
  }
343
354
  function getEventsSince(channel, lastTimestamp) {
@@ -1346,6 +1357,8 @@ async function scanOneForProactiveMetadata(photonName, filePath, workingDir) {
1346
1357
  return dropProactiveMetadataFor(photonName, workingDir);
1347
1358
  }
1348
1359
  let changed = false;
1360
+ // ExtractedMetadata isn't part of photon-core's public surface; type the
1361
+ // bits we read structurally so we get checking without re-exporting.
1349
1362
  let meta;
1350
1363
  try {
1351
1364
  const extractor = new SchemaExtractor();
@@ -1366,8 +1379,7 @@ async function scanOneForProactiveMetadata(photonName, filePath, workingDir) {
1366
1379
  // See src/daemon/registry-keys.ts for the branded-key rationale.
1367
1380
  const nextSchedules = new Map();
1368
1381
  const nextRoutes = new Map(); // routePath → methodName
1369
- for (const rawTool of meta.tools) {
1370
- const tool = rawTool;
1382
+ for (const tool of meta.tools) {
1371
1383
  if (tool.scheduled) {
1372
1384
  nextSchedules.set(declaredKey(photonName, tool.name, workingDir), {
1373
1385
  photon: photonName,
@@ -3703,7 +3715,8 @@ async function handleRequest(request, socket) {
3703
3715
  // Worker photons don't have a session manager — handle runtime tools directly
3704
3716
  if (workerManager.has(cmdKey) && runtimeTools.includes(request.method)) {
3705
3717
  if (request.method === '_use') {
3706
- const instanceName = String(request.args?.name ?? '');
3718
+ const rawName = request.args?.name;
3719
+ const instanceName = typeof rawName === 'string' ? rawName : '';
3707
3720
  const label = instanceName || 'default';
3708
3721
  return {
3709
3722
  type: 'result',
@@ -3760,7 +3773,8 @@ async function handleRequest(request, socket) {
3760
3773
  }
3761
3774
  // ── Runtime-injected instance tools ──────────────────────────
3762
3775
  if (request.method === '_use') {
3763
- const instanceName = String(request.args?.name ?? '');
3776
+ const rawName = request.args?.name;
3777
+ const instanceName = typeof rawName === 'string' ? rawName : '';
3764
3778
  await sessionManager.switchInstance(session.id, instanceName);
3765
3779
  const label = instanceName || 'default';
3766
3780
  // Ensure a state file exists so _instances can discover this instance
@@ -4252,7 +4266,10 @@ async function appendEventLog(photonName, instanceName, entry, workingDir) {
4252
4266
  const stat = await fsPromises.stat(logPath);
4253
4267
  if (stat.size >= EVENT_LOG_MAX_SIZE) {
4254
4268
  const rotatedPath = logPath + '.1';
4255
- await fsPromises.rename(logPath, rotatedPath).catch(() => { });
4269
+ await fsPromises.rename(logPath, rotatedPath).catch((err) => logger.debug('Event log rotation rename failed', {
4270
+ photon: photonName,
4271
+ error: String(err),
4272
+ }));
4256
4273
  logger.debug('Rotated event log', { photon: photonName, instance: instanceName });
4257
4274
  }
4258
4275
  }
@@ -4559,7 +4576,7 @@ function watchPhotonFile(photonName, photonPath) {
4559
4576
  }, 100);
4560
4577
  watchDebounce.set(watchPath, timer);
4561
4578
  });
4562
- if ('on' in watcher && typeof watcher.on === 'function') {
4579
+ if (typeof watcher.on === 'function') {
4563
4580
  watcher.on('error', (err) => {
4564
4581
  logger.warn('File watcher error', { photonName, error: getErrorMessage(err) });
4565
4582
  unwatchPhotonFile(watchPath);
@@ -4723,7 +4740,7 @@ function watchWorkingDir(workingDir) {
4723
4740
  }, 150);
4724
4741
  watchDebounce.set(debounceKey, timer);
4725
4742
  });
4726
- if ('on' in watcher && typeof watcher.on === 'function') {
4743
+ if (typeof watcher.on === 'function') {
4727
4744
  watcher.on('error', (err) => {
4728
4745
  logger.warn('workingDir parent watcher error', { parentDir, error: getErrorMessage(err) });
4729
4746
  parentDirWatchers.delete(parentDir);
@@ -4785,7 +4802,7 @@ function watchStateDir(workingDir) {
4785
4802
  }, 150);
4786
4803
  watchDebounce.set(debounceKey, timer);
4787
4804
  });
4788
- if ('on' in watcher && typeof watcher.on === 'function') {
4805
+ if (typeof watcher.on === 'function') {
4789
4806
  watcher.on('error', (err) => {
4790
4807
  logger.warn('State dir watcher error', { stateDir, error: getErrorMessage(err) });
4791
4808
  stateDirWatchers.delete(workingDir);
@@ -5188,7 +5205,7 @@ function startupWatchPhotons() {
5188
5205
  });
5189
5206
  return;
5190
5207
  }
5191
- eagerLoad().catch(() => { });
5208
+ eagerLoad().catch((err) => logger.debug('Eager load failed', { error: String(err) }));
5192
5209
  }, 1000);
5193
5210
  }
5194
5211
  // Watch the directory itself for new photon files added after startup
@@ -5220,7 +5237,7 @@ function startupWatchPhotons() {
5220
5237
  logger.info('Photon file removed', { photonName, path: filePath });
5221
5238
  }
5222
5239
  });
5223
- if ('on' in dirWatcher && typeof dirWatcher.on === 'function') {
5240
+ if (typeof dirWatcher.on === 'function') {
5224
5241
  dirWatcher.on('error', () => { }); // Non-fatal
5225
5242
  }
5226
5243
  }
@@ -5345,6 +5362,47 @@ function startServer() {
5345
5362
  stack: reason instanceof Error ? reason.stack : undefined,
5346
5363
  });
5347
5364
  });
5365
+ // Periodic self-check: log once when the daemon binary on disk is newer
5366
+ // than this running daemon. The DaemonManager.isBinaryStale path already
5367
+ // restarts the daemon when a CLI command runs after `npm install`, so
5368
+ // the staleness window for interactive users is short. This nudge
5369
+ // covers the *non-interactive* case: a developer who upgrades photon
5370
+ // and then walks away leaving only scheduled jobs running. The daemon
5371
+ // does NOT auto-exit — schedules need continuity, and the next CLI
5372
+ // invocation triggers the same restart anyway. The warning is logged
5373
+ // at most once per detected upgrade so it doesn't spam the log when
5374
+ // running for hours after an upgrade.
5375
+ // Default 60s interval; tests can override via env var to assert the
5376
+ // log warning fires without waiting a full minute.
5377
+ const STALE_CHECK_INTERVAL_MS = Math.max(100, Number.parseInt(process.env.PHOTON_STALE_CHECK_INTERVAL_MS || '', 10) || 60_000);
5378
+ const daemonStartedAtMs = Date.now();
5379
+ let lastWarnedBinaryMtimeMs = 0;
5380
+ const myScript = process.argv[1];
5381
+ if (myScript) {
5382
+ const staleCheckTimer = setInterval(() => {
5383
+ try {
5384
+ const mtime = fs.statSync(myScript).mtimeMs;
5385
+ if (mtime > daemonStartedAtMs && mtime !== lastWarnedBinaryMtimeMs) {
5386
+ lastWarnedBinaryMtimeMs = mtime;
5387
+ logger.warn('Daemon binary on disk is newer than this running daemon. ' +
5388
+ 'Run any photon CLI command (e.g. `photon ps`) to bounce the daemon to the new build, ' +
5389
+ 'or restart it manually with `pkill -9 -f daemon/server.js`.', {
5390
+ pid: process.pid,
5391
+ scriptPath: myScript,
5392
+ binaryMtimeMs: mtime,
5393
+ daemonStartedAtMs,
5394
+ ageSec: Math.round((mtime - daemonStartedAtMs) / 1000),
5395
+ });
5396
+ }
5397
+ }
5398
+ catch {
5399
+ // stat raced with a write — try again next tick.
5400
+ }
5401
+ }, STALE_CHECK_INTERVAL_MS);
5402
+ // Don't keep the event loop alive on this watchdog alone — the
5403
+ // listening socket is the canonical liveness anchor.
5404
+ staleCheckTimer.unref?.();
5405
+ }
5348
5406
  }
5349
5407
  /**
5350
5408
  * Scan the OS process table for any other process whose argv looks like