@portel/photon 1.4.1 → 1.6.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 (395) hide show
  1. package/README.md +326 -1177
  2. package/dist/auto-ui/beam.d.ts +14 -0
  3. package/dist/auto-ui/beam.d.ts.map +1 -0
  4. package/dist/auto-ui/beam.js +3057 -0
  5. package/dist/auto-ui/beam.js.map +1 -0
  6. package/dist/auto-ui/bridge/index.d.ts +37 -0
  7. package/dist/auto-ui/bridge/index.d.ts.map +1 -0
  8. package/dist/auto-ui/bridge/index.js +555 -0
  9. package/dist/auto-ui/bridge/index.js.map +1 -0
  10. package/dist/auto-ui/bridge/openai-shim.d.ts +20 -0
  11. package/dist/auto-ui/bridge/openai-shim.d.ts.map +1 -0
  12. package/dist/auto-ui/bridge/openai-shim.js +231 -0
  13. package/dist/auto-ui/bridge/openai-shim.js.map +1 -0
  14. package/dist/auto-ui/bridge/photon-app.d.ts +162 -0
  15. package/dist/auto-ui/bridge/photon-app.d.ts.map +1 -0
  16. package/dist/auto-ui/bridge/photon-app.js +460 -0
  17. package/dist/auto-ui/bridge/photon-app.js.map +1 -0
  18. package/dist/auto-ui/bridge/types.d.ts +128 -0
  19. package/dist/auto-ui/bridge/types.d.ts.map +1 -0
  20. package/dist/auto-ui/bridge/types.js +7 -0
  21. package/dist/auto-ui/bridge/types.js.map +1 -0
  22. package/dist/auto-ui/components/card.d.ts +13 -0
  23. package/dist/auto-ui/components/card.d.ts.map +1 -0
  24. package/dist/auto-ui/components/card.js +64 -0
  25. package/dist/auto-ui/components/card.js.map +1 -0
  26. package/dist/auto-ui/components/form.d.ts +15 -0
  27. package/dist/auto-ui/components/form.d.ts.map +1 -0
  28. package/dist/auto-ui/components/form.js +72 -0
  29. package/dist/auto-ui/components/form.js.map +1 -0
  30. package/dist/auto-ui/components/list.d.ts +13 -0
  31. package/dist/auto-ui/components/list.d.ts.map +1 -0
  32. package/dist/auto-ui/components/list.js +58 -0
  33. package/dist/auto-ui/components/list.js.map +1 -0
  34. package/dist/auto-ui/components/progress.d.ts +18 -0
  35. package/dist/auto-ui/components/progress.d.ts.map +1 -0
  36. package/dist/auto-ui/components/progress.js +125 -0
  37. package/dist/auto-ui/components/progress.js.map +1 -0
  38. package/dist/auto-ui/components/table.d.ts +13 -0
  39. package/dist/auto-ui/components/table.d.ts.map +1 -0
  40. package/dist/auto-ui/components/table.js +82 -0
  41. package/dist/auto-ui/components/table.js.map +1 -0
  42. package/dist/auto-ui/components/tree.d.ts +13 -0
  43. package/dist/auto-ui/components/tree.d.ts.map +1 -0
  44. package/dist/auto-ui/components/tree.js +61 -0
  45. package/dist/auto-ui/components/tree.js.map +1 -0
  46. package/dist/auto-ui/daemon-tools.d.ts +45 -0
  47. package/dist/auto-ui/daemon-tools.d.ts.map +1 -0
  48. package/dist/auto-ui/daemon-tools.js +580 -0
  49. package/dist/auto-ui/daemon-tools.js.map +1 -0
  50. package/dist/auto-ui/design-system/index.d.ts +21 -0
  51. package/dist/auto-ui/design-system/index.d.ts.map +1 -0
  52. package/dist/auto-ui/design-system/index.js +27 -0
  53. package/dist/auto-ui/design-system/index.js.map +1 -0
  54. package/dist/auto-ui/design-system/tokens.d.ts +9 -0
  55. package/dist/auto-ui/design-system/tokens.d.ts.map +1 -0
  56. package/dist/auto-ui/design-system/tokens.js +27 -0
  57. package/dist/auto-ui/design-system/tokens.js.map +1 -0
  58. package/dist/auto-ui/design-system/transaction-ui.d.ts +70 -0
  59. package/dist/auto-ui/design-system/transaction-ui.d.ts.map +1 -0
  60. package/dist/auto-ui/design-system/transaction-ui.js +982 -0
  61. package/dist/auto-ui/design-system/transaction-ui.js.map +1 -0
  62. package/dist/auto-ui/frontend/index.html +84 -0
  63. package/dist/auto-ui/index.d.ts +23 -0
  64. package/dist/auto-ui/index.d.ts.map +1 -0
  65. package/dist/auto-ui/index.js +28 -0
  66. package/dist/auto-ui/index.js.map +1 -0
  67. package/dist/auto-ui/openapi-generator.d.ts +71 -0
  68. package/dist/auto-ui/openapi-generator.d.ts.map +1 -0
  69. package/dist/auto-ui/openapi-generator.js +223 -0
  70. package/dist/auto-ui/openapi-generator.js.map +1 -0
  71. package/dist/auto-ui/photon-bridge.d.ts +159 -0
  72. package/dist/auto-ui/photon-bridge.d.ts.map +1 -0
  73. package/dist/auto-ui/photon-bridge.js +262 -0
  74. package/dist/auto-ui/photon-bridge.js.map +1 -0
  75. package/dist/auto-ui/photon-host.d.ts +113 -0
  76. package/dist/auto-ui/photon-host.d.ts.map +1 -0
  77. package/dist/auto-ui/photon-host.js +284 -0
  78. package/dist/auto-ui/photon-host.js.map +1 -0
  79. package/dist/auto-ui/platform-compat.d.ts +71 -0
  80. package/dist/auto-ui/platform-compat.d.ts.map +1 -0
  81. package/dist/auto-ui/platform-compat.js +628 -0
  82. package/dist/auto-ui/platform-compat.js.map +1 -0
  83. package/dist/auto-ui/playground-html.d.ts +15 -0
  84. package/dist/auto-ui/playground-html.d.ts.map +1 -0
  85. package/dist/auto-ui/playground-html.js +1113 -0
  86. package/dist/auto-ui/playground-html.js.map +1 -0
  87. package/dist/auto-ui/playground-server.d.ts +7 -0
  88. package/dist/auto-ui/playground-server.d.ts.map +1 -0
  89. package/dist/auto-ui/playground-server.js +840 -0
  90. package/dist/auto-ui/playground-server.js.map +1 -0
  91. package/dist/auto-ui/registry.d.ts +13 -0
  92. package/dist/auto-ui/registry.d.ts.map +1 -0
  93. package/dist/auto-ui/registry.js +62 -0
  94. package/dist/auto-ui/registry.js.map +1 -0
  95. package/dist/auto-ui/renderer.d.ts +14 -0
  96. package/dist/auto-ui/renderer.d.ts.map +1 -0
  97. package/dist/auto-ui/renderer.js +88 -0
  98. package/dist/auto-ui/renderer.js.map +1 -0
  99. package/dist/auto-ui/rendering/components.d.ts +29 -0
  100. package/dist/auto-ui/rendering/components.d.ts.map +1 -0
  101. package/dist/auto-ui/rendering/components.js +773 -0
  102. package/dist/auto-ui/rendering/components.js.map +1 -0
  103. package/dist/auto-ui/rendering/field-analyzer.d.ts +48 -0
  104. package/dist/auto-ui/rendering/field-analyzer.d.ts.map +1 -0
  105. package/dist/auto-ui/rendering/field-analyzer.js +270 -0
  106. package/dist/auto-ui/rendering/field-analyzer.js.map +1 -0
  107. package/dist/auto-ui/rendering/field-renderers.d.ts +64 -0
  108. package/dist/auto-ui/rendering/field-renderers.d.ts.map +1 -0
  109. package/dist/auto-ui/rendering/field-renderers.js +317 -0
  110. package/dist/auto-ui/rendering/field-renderers.js.map +1 -0
  111. package/dist/auto-ui/rendering/index.d.ts +28 -0
  112. package/dist/auto-ui/rendering/index.d.ts.map +1 -0
  113. package/dist/auto-ui/rendering/index.js +60 -0
  114. package/dist/auto-ui/rendering/index.js.map +1 -0
  115. package/dist/auto-ui/rendering/layout-selector.d.ts +48 -0
  116. package/dist/auto-ui/rendering/layout-selector.d.ts.map +1 -0
  117. package/dist/auto-ui/rendering/layout-selector.js +352 -0
  118. package/dist/auto-ui/rendering/layout-selector.js.map +1 -0
  119. package/dist/auto-ui/rendering/template-engine.d.ts +41 -0
  120. package/dist/auto-ui/rendering/template-engine.d.ts.map +1 -0
  121. package/dist/auto-ui/rendering/template-engine.js +238 -0
  122. package/dist/auto-ui/rendering/template-engine.js.map +1 -0
  123. package/dist/auto-ui/streamable-http-transport.d.ts +103 -0
  124. package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -0
  125. package/dist/auto-ui/streamable-http-transport.js +1875 -0
  126. package/dist/auto-ui/streamable-http-transport.js.map +1 -0
  127. package/dist/auto-ui/types.d.ts +384 -0
  128. package/dist/auto-ui/types.d.ts.map +1 -0
  129. package/dist/auto-ui/types.js +92 -0
  130. package/dist/auto-ui/types.js.map +1 -0
  131. package/dist/beam.bundle.js +63137 -0
  132. package/dist/beam.bundle.js.map +7 -0
  133. package/dist/claude-code-plugin.d.ts.map +1 -1
  134. package/dist/claude-code-plugin.js +30 -30
  135. package/dist/claude-code-plugin.js.map +1 -1
  136. package/dist/cli/commands/info.d.ts +11 -0
  137. package/dist/cli/commands/info.d.ts.map +1 -0
  138. package/dist/cli/commands/info.js +313 -0
  139. package/dist/cli/commands/info.js.map +1 -0
  140. package/dist/cli/commands/marketplace.d.ts +11 -0
  141. package/dist/cli/commands/marketplace.d.ts.map +1 -0
  142. package/dist/cli/commands/marketplace.js +198 -0
  143. package/dist/cli/commands/marketplace.js.map +1 -0
  144. package/dist/cli/commands/package-app.d.ts +9 -0
  145. package/dist/cli/commands/package-app.d.ts.map +1 -0
  146. package/dist/cli/commands/package-app.js +191 -0
  147. package/dist/cli/commands/package-app.js.map +1 -0
  148. package/dist/cli/commands/package.d.ts +11 -0
  149. package/dist/cli/commands/package.d.ts.map +1 -0
  150. package/dist/cli/commands/package.js +573 -0
  151. package/dist/cli/commands/package.js.map +1 -0
  152. package/dist/cli-alias.d.ts.map +1 -1
  153. package/dist/cli-alias.js +30 -28
  154. package/dist/cli-alias.js.map +1 -1
  155. package/dist/cli-formatter.d.ts +8 -24
  156. package/dist/cli-formatter.d.ts.map +1 -1
  157. package/dist/cli-formatter.js +8 -325
  158. package/dist/cli-formatter.js.map +1 -1
  159. package/dist/cli.d.ts +15 -1
  160. package/dist/cli.d.ts.map +1 -1
  161. package/dist/cli.js +1166 -1131
  162. package/dist/cli.js.map +1 -1
  163. package/dist/daemon/client.d.ts +84 -3
  164. package/dist/daemon/client.d.ts.map +1 -1
  165. package/dist/daemon/client.js +561 -11
  166. package/dist/daemon/client.js.map +1 -1
  167. package/dist/daemon/manager.d.ts +51 -12
  168. package/dist/daemon/manager.d.ts.map +1 -1
  169. package/dist/daemon/manager.js +122 -61
  170. package/dist/daemon/manager.js.map +1 -1
  171. package/dist/daemon/protocol.d.ts +62 -6
  172. package/dist/daemon/protocol.d.ts.map +1 -1
  173. package/dist/daemon/protocol.js +76 -1
  174. package/dist/daemon/protocol.js.map +1 -1
  175. package/dist/daemon/server.d.ts +6 -6
  176. package/dist/daemon/server.js +743 -133
  177. package/dist/daemon/server.js.map +1 -1
  178. package/dist/daemon/session-manager.d.ts +8 -1
  179. package/dist/daemon/session-manager.d.ts.map +1 -1
  180. package/dist/daemon/session-manager.js +32 -9
  181. package/dist/daemon/session-manager.js.map +1 -1
  182. package/dist/deploy/cloudflare.d.ts +12 -0
  183. package/dist/deploy/cloudflare.d.ts.map +1 -0
  184. package/dist/deploy/cloudflare.js +216 -0
  185. package/dist/deploy/cloudflare.js.map +1 -0
  186. package/dist/index.d.ts +1 -0
  187. package/dist/index.d.ts.map +1 -1
  188. package/dist/index.js +3 -0
  189. package/dist/index.js.map +1 -1
  190. package/dist/loader.d.ts +191 -21
  191. package/dist/loader.d.ts.map +1 -1
  192. package/dist/loader.js +1186 -319
  193. package/dist/loader.js.map +1 -1
  194. package/dist/markdown-utils.d.ts +8 -0
  195. package/dist/markdown-utils.d.ts.map +1 -0
  196. package/dist/markdown-utils.js +63 -0
  197. package/dist/markdown-utils.js.map +1 -0
  198. package/dist/marketplace-manager.d.ts +10 -0
  199. package/dist/marketplace-manager.d.ts.map +1 -1
  200. package/dist/marketplace-manager.js +112 -28
  201. package/dist/marketplace-manager.js.map +1 -1
  202. package/dist/mcp-client.d.ts +9 -0
  203. package/dist/mcp-client.d.ts.map +1 -0
  204. package/dist/mcp-client.js +11 -0
  205. package/dist/mcp-client.js.map +1 -0
  206. package/dist/mcp-elicitation.d.ts +32 -0
  207. package/dist/mcp-elicitation.d.ts.map +1 -0
  208. package/dist/mcp-elicitation.js +26 -0
  209. package/dist/mcp-elicitation.js.map +1 -0
  210. package/dist/path-resolver.d.ts +9 -12
  211. package/dist/path-resolver.d.ts.map +1 -1
  212. package/dist/path-resolver.js +13 -43
  213. package/dist/path-resolver.js.map +1 -1
  214. package/dist/photon-cli-runner.d.ts.map +1 -1
  215. package/dist/photon-cli-runner.js +204 -77
  216. package/dist/photon-cli-runner.js.map +1 -1
  217. package/dist/photon-doc-extractor.d.ts +89 -0
  218. package/dist/photon-doc-extractor.d.ts.map +1 -1
  219. package/dist/photon-doc-extractor.js +560 -32
  220. package/dist/photon-doc-extractor.js.map +1 -1
  221. package/dist/photons/maker.photon.d.ts +182 -0
  222. package/dist/photons/maker.photon.d.ts.map +1 -0
  223. package/dist/photons/maker.photon.js +504 -0
  224. package/dist/photons/maker.photon.js.map +1 -0
  225. package/dist/photons/maker.photon.ts +626 -0
  226. package/dist/photons/marketplace.photon.d.ts +110 -0
  227. package/dist/photons/marketplace.photon.d.ts.map +1 -0
  228. package/dist/photons/marketplace.photon.js +260 -0
  229. package/dist/photons/marketplace.photon.js.map +1 -0
  230. package/dist/photons/marketplace.photon.ts +378 -0
  231. package/dist/photons/tunnel.photon.d.ts +80 -0
  232. package/dist/photons/tunnel.photon.d.ts.map +1 -0
  233. package/dist/photons/tunnel.photon.js +269 -0
  234. package/dist/photons/tunnel.photon.js.map +1 -0
  235. package/dist/photons/tunnel.photon.ts +345 -0
  236. package/dist/security-scanner.d.ts.map +1 -1
  237. package/dist/security-scanner.js +18 -15
  238. package/dist/security-scanner.js.map +1 -1
  239. package/dist/serv/auth/jwt.d.ts +89 -0
  240. package/dist/serv/auth/jwt.d.ts.map +1 -0
  241. package/dist/serv/auth/jwt.js +239 -0
  242. package/dist/serv/auth/jwt.js.map +1 -0
  243. package/dist/serv/auth/oauth.d.ts +117 -0
  244. package/dist/serv/auth/oauth.d.ts.map +1 -0
  245. package/dist/serv/auth/oauth.js +395 -0
  246. package/dist/serv/auth/oauth.js.map +1 -0
  247. package/dist/serv/auth/well-known.d.ts +60 -0
  248. package/dist/serv/auth/well-known.d.ts.map +1 -0
  249. package/dist/serv/auth/well-known.js +154 -0
  250. package/dist/serv/auth/well-known.js.map +1 -0
  251. package/dist/serv/db/d1-client.d.ts +65 -0
  252. package/dist/serv/db/d1-client.d.ts.map +1 -0
  253. package/dist/serv/db/d1-client.js +137 -0
  254. package/dist/serv/db/d1-client.js.map +1 -0
  255. package/dist/serv/db/d1-stores.d.ts +62 -0
  256. package/dist/serv/db/d1-stores.d.ts.map +1 -0
  257. package/dist/serv/db/d1-stores.js +307 -0
  258. package/dist/serv/db/d1-stores.js.map +1 -0
  259. package/dist/serv/index.d.ts +114 -0
  260. package/dist/serv/index.d.ts.map +1 -0
  261. package/dist/serv/index.js +172 -0
  262. package/dist/serv/index.js.map +1 -0
  263. package/dist/serv/local.d.ts +118 -0
  264. package/dist/serv/local.d.ts.map +1 -0
  265. package/dist/serv/local.js +392 -0
  266. package/dist/serv/local.js.map +1 -0
  267. package/dist/serv/middleware/auth.d.ts +66 -0
  268. package/dist/serv/middleware/auth.d.ts.map +1 -0
  269. package/dist/serv/middleware/auth.js +178 -0
  270. package/dist/serv/middleware/auth.js.map +1 -0
  271. package/dist/serv/middleware/tenant.d.ts +94 -0
  272. package/dist/serv/middleware/tenant.d.ts.map +1 -0
  273. package/dist/serv/middleware/tenant.js +152 -0
  274. package/dist/serv/middleware/tenant.js.map +1 -0
  275. package/dist/serv/runtime/executor.d.ts +76 -0
  276. package/dist/serv/runtime/executor.d.ts.map +1 -0
  277. package/dist/serv/runtime/executor.js +105 -0
  278. package/dist/serv/runtime/executor.js.map +1 -0
  279. package/dist/serv/runtime/index.d.ts +8 -0
  280. package/dist/serv/runtime/index.d.ts.map +1 -0
  281. package/dist/serv/runtime/index.js +10 -0
  282. package/dist/serv/runtime/index.js.map +1 -0
  283. package/dist/serv/runtime/oauth-context.d.ts +121 -0
  284. package/dist/serv/runtime/oauth-context.d.ts.map +1 -0
  285. package/dist/serv/runtime/oauth-context.js +153 -0
  286. package/dist/serv/runtime/oauth-context.js.map +1 -0
  287. package/dist/serv/session/kv-store.d.ts +54 -0
  288. package/dist/serv/session/kv-store.d.ts.map +1 -0
  289. package/dist/serv/session/kv-store.js +149 -0
  290. package/dist/serv/session/kv-store.js.map +1 -0
  291. package/dist/serv/session/store.d.ts +113 -0
  292. package/dist/serv/session/store.d.ts.map +1 -0
  293. package/dist/serv/session/store.js +284 -0
  294. package/dist/serv/session/store.js.map +1 -0
  295. package/dist/serv/types/index.d.ts +147 -0
  296. package/dist/serv/types/index.d.ts.map +1 -0
  297. package/dist/serv/types/index.js +8 -0
  298. package/dist/serv/types/index.js.map +1 -0
  299. package/dist/serv/vault/token-vault.d.ts +102 -0
  300. package/dist/serv/vault/token-vault.d.ts.map +1 -0
  301. package/dist/serv/vault/token-vault.js +177 -0
  302. package/dist/serv/vault/token-vault.js.map +1 -0
  303. package/dist/server.d.ts +184 -0
  304. package/dist/server.d.ts.map +1 -1
  305. package/dist/server.js +1995 -86
  306. package/dist/server.js.map +1 -1
  307. package/dist/shared/cli-sections.d.ts +6 -0
  308. package/dist/shared/cli-sections.d.ts.map +1 -0
  309. package/dist/shared/cli-sections.js +16 -0
  310. package/dist/shared/cli-sections.js.map +1 -0
  311. package/dist/shared/cli-utils.d.ts +81 -0
  312. package/dist/shared/cli-utils.d.ts.map +1 -0
  313. package/dist/shared/cli-utils.js +174 -0
  314. package/dist/shared/cli-utils.js.map +1 -0
  315. package/dist/shared/config-docs.d.ts +6 -0
  316. package/dist/shared/config-docs.d.ts.map +1 -0
  317. package/dist/shared/config-docs.js +6 -0
  318. package/dist/shared/config-docs.js.map +1 -0
  319. package/dist/shared/error-handler.d.ts +128 -0
  320. package/dist/shared/error-handler.d.ts.map +1 -0
  321. package/dist/shared/error-handler.js +342 -0
  322. package/dist/shared/error-handler.js.map +1 -0
  323. package/dist/shared/logger.d.ts +42 -0
  324. package/dist/shared/logger.d.ts.map +1 -0
  325. package/dist/shared/logger.js +123 -0
  326. package/dist/shared/logger.js.map +1 -0
  327. package/dist/shared/performance.d.ts +65 -0
  328. package/dist/shared/performance.d.ts.map +1 -0
  329. package/dist/shared/performance.js +136 -0
  330. package/dist/shared/performance.js.map +1 -0
  331. package/dist/shared/task-runner.d.ts +2 -0
  332. package/dist/shared/task-runner.d.ts.map +1 -0
  333. package/dist/shared/task-runner.js +16 -0
  334. package/dist/shared/task-runner.js.map +1 -0
  335. package/dist/shared/validation.d.ts +6 -0
  336. package/dist/shared/validation.d.ts.map +1 -0
  337. package/dist/shared/validation.js +6 -0
  338. package/dist/shared/validation.js.map +1 -0
  339. package/dist/shared-utils.d.ts +63 -0
  340. package/dist/shared-utils.d.ts.map +1 -0
  341. package/dist/shared-utils.js +123 -0
  342. package/dist/shared-utils.js.map +1 -0
  343. package/dist/template-manager.d.ts +23 -2
  344. package/dist/template-manager.d.ts.map +1 -1
  345. package/dist/template-manager.js +176 -87
  346. package/dist/template-manager.js.map +1 -1
  347. package/dist/test-client.d.ts.map +1 -1
  348. package/dist/test-client.js +10 -8
  349. package/dist/test-client.js.map +1 -1
  350. package/dist/test-runner.d.ts +52 -0
  351. package/dist/test-runner.d.ts.map +1 -0
  352. package/dist/test-runner.js +785 -0
  353. package/dist/test-runner.js.map +1 -0
  354. package/dist/testing.d.ts +103 -0
  355. package/dist/testing.d.ts.map +1 -0
  356. package/dist/testing.js +163 -0
  357. package/dist/testing.js.map +1 -0
  358. package/dist/version-checker.d.ts.map +1 -1
  359. package/dist/version-checker.js +2 -2
  360. package/dist/version-checker.js.map +1 -1
  361. package/dist/version.d.ts +10 -0
  362. package/dist/version.d.ts.map +1 -0
  363. package/dist/version.js +21 -0
  364. package/dist/version.js.map +1 -0
  365. package/dist/watcher.d.ts +6 -3
  366. package/dist/watcher.d.ts.map +1 -1
  367. package/dist/watcher.js +49 -10
  368. package/dist/watcher.js.map +1 -1
  369. package/package.json +57 -7
  370. package/templates/cloudflare/worker.ts.template +381 -0
  371. package/templates/cloudflare/wrangler.toml.template +9 -0
  372. package/dist/base.d.ts +0 -58
  373. package/dist/base.d.ts.map +0 -1
  374. package/dist/base.js +0 -92
  375. package/dist/base.js.map +0 -1
  376. package/dist/dependency-manager.d.ts +0 -49
  377. package/dist/dependency-manager.d.ts.map +0 -1
  378. package/dist/dependency-manager.js +0 -165
  379. package/dist/dependency-manager.js.map +0 -1
  380. package/dist/registry-manager.d.ts +0 -76
  381. package/dist/registry-manager.d.ts.map +0 -1
  382. package/dist/registry-manager.js +0 -220
  383. package/dist/registry-manager.js.map +0 -1
  384. package/dist/schema-extractor.d.ts +0 -110
  385. package/dist/schema-extractor.d.ts.map +0 -1
  386. package/dist/schema-extractor.js +0 -727
  387. package/dist/schema-extractor.js.map +0 -1
  388. package/dist/test-marketplace-sources.d.ts +0 -5
  389. package/dist/test-marketplace-sources.d.ts.map +0 -1
  390. package/dist/test-marketplace-sources.js +0 -53
  391. package/dist/test-marketplace-sources.js.map +0 -1
  392. package/dist/types.d.ts +0 -109
  393. package/dist/types.d.ts.map +0 -1
  394. package/dist/types.js +0 -12
  395. package/dist/types.js.map +0 -1
@@ -1,96 +1,432 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Daemon Server
3
+ * Global Photon Daemon Server
4
4
  *
5
- * Runs as a background process for stateful photons
6
- * - Loads and initializes the photon
7
- * - Listens on Unix socket / named pipe
8
- * - Handles command requests
9
- * - Manages idle timeout and shutdown
5
+ * Single daemon process for all photons.
6
+ * - Listens on global Unix socket / named pipe (~/.photon/daemon.sock)
7
+ * - Handles requests for multiple photons via photonName field
8
+ * - Lazy-initializes SessionManagers per photon on first request
9
+ * - Provides pub/sub, locks, scheduled jobs, and webhooks
10
10
  */
11
11
  import * as net from 'net';
12
+ import * as http from 'http';
12
13
  import * as fs from 'fs';
13
14
  import { SessionManager } from './session-manager.js';
14
- import { setPromptHandler, } from '@portel/photon-core';
15
- // Command line args: photonName photonPath socketPath
16
- const photonName = process.argv[2];
17
- const photonPath = process.argv[3];
18
- const socketPath = process.argv[4];
19
- if (!photonName || !photonPath || !socketPath) {
20
- console.error('[daemon-server] Missing required arguments');
15
+ import { isValidDaemonRequest, } from './protocol.js';
16
+ import { setPromptHandler } from '@portel/photon-core';
17
+ import { createLogger } from '../shared/logger.js';
18
+ import { getErrorMessage } from '../shared/error-handler.js';
19
+ // Command line args: socketPath (global daemon only needs socket path)
20
+ const socketPath = process.argv[2];
21
+ const logger = createLogger({
22
+ component: 'daemon-server',
23
+ scope: 'global',
24
+ minimal: true,
25
+ });
26
+ if (!socketPath) {
27
+ logger.error('Missing required argument: socketPath');
21
28
  process.exit(1);
22
29
  }
23
- let sessionManager = null;
30
+ // Map of photonName -> SessionManager (lazy initialized)
31
+ const sessionManagers = new Map();
32
+ const photonPaths = new Map(); // photonName -> photonPath
24
33
  let idleTimeout = 600000; // 10 minutes default
25
34
  let idleTimer = null;
26
35
  // Track pending prompts waiting for user input
27
- // Map: requestId -> { resolve, reject } for resolving prompt responses
28
36
  const pendingPrompts = new Map();
29
- // Current socket for sending prompts (set per-request)
30
- let currentSocket = null;
31
- let currentRequestId = null;
32
- /**
33
- * Initialize session manager
34
- */
35
- async function initializeSessionManager() {
36
- try {
37
- console.error(`[daemon-server] Initializing session manager for: ${photonName}`);
38
- sessionManager = new SessionManager(photonPath, photonName, idleTimeout);
39
- console.error(`[daemon-server] Session manager initialized`);
40
- console.error(`[daemon-server] Session timeout: ${idleTimeout}ms`);
41
- // Start idle timer if timeout is set
42
- if (idleTimeout > 0) {
43
- startIdleTimer();
37
+ // Channel subscriptions for pub/sub
38
+ const channelSubscriptions = new Map();
39
+ const EVENT_BUFFER_SIZE = 30;
40
+ const channelEventBuffers = new Map();
41
+ function bufferEvent(channel, message) {
42
+ let buffer = channelEventBuffers.get(channel);
43
+ if (!buffer) {
44
+ buffer = { events: [], nextId: 1 };
45
+ channelEventBuffers.set(channel, buffer);
46
+ }
47
+ const eventId = buffer.nextId++;
48
+ const event = {
49
+ id: eventId,
50
+ channel,
51
+ message,
52
+ timestamp: Date.now(),
53
+ };
54
+ buffer.events.push(event);
55
+ // Keep only last N events (circular buffer)
56
+ if (buffer.events.length > EVENT_BUFFER_SIZE) {
57
+ buffer.events.shift();
58
+ }
59
+ return eventId;
60
+ }
61
+ function getEventsSince(channel, lastEventId) {
62
+ const buffer = channelEventBuffers.get(channel);
63
+ if (!buffer || buffer.events.length === 0) {
64
+ return { events: [], refreshNeeded: false };
65
+ }
66
+ const oldestEvent = buffer.events[0];
67
+ // If lastEventId is older than our oldest buffered event, refresh needed
68
+ if (lastEventId < oldestEvent.id) {
69
+ return { events: [], refreshNeeded: true };
70
+ }
71
+ // Find events to replay
72
+ const events = buffer.events.filter((e) => e.id > lastEventId);
73
+ return { events, refreshNeeded: false };
74
+ }
75
+ // ════════════════════════════════════════════════════════════════════════════════
76
+ // DISTRIBUTED LOCKS
77
+ // ════════════════════════════════════════════════════════════════════════════════
78
+ const activeLocks = new Map();
79
+ const DEFAULT_LOCK_TIMEOUT = 30000;
80
+ function acquireLock(lockName, holder, timeout = DEFAULT_LOCK_TIMEOUT) {
81
+ const now = Date.now();
82
+ const existing = activeLocks.get(lockName);
83
+ if (existing && existing.expiresAt > now) {
84
+ if (existing.holder !== holder) {
85
+ return false;
86
+ }
87
+ existing.expiresAt = now + timeout;
88
+ return true;
89
+ }
90
+ activeLocks.set(lockName, {
91
+ name: lockName,
92
+ holder,
93
+ acquiredAt: now,
94
+ expiresAt: now + timeout,
95
+ });
96
+ logger.info('Lock acquired', { lockName, holder, timeout });
97
+ return true;
98
+ }
99
+ function releaseLock(lockName, holder) {
100
+ const existing = activeLocks.get(lockName);
101
+ if (!existing)
102
+ return true;
103
+ if (existing.holder !== holder)
104
+ return false;
105
+ activeLocks.delete(lockName);
106
+ logger.info('Lock released', { lockName, holder });
107
+ return true;
108
+ }
109
+ function cleanupExpiredLocks() {
110
+ const now = Date.now();
111
+ for (const [name, lock] of activeLocks.entries()) {
112
+ if (lock.expiresAt <= now) {
113
+ activeLocks.delete(name);
114
+ logger.info('Lock expired', { lockName: name, holder: lock.holder });
115
+ }
116
+ }
117
+ }
118
+ setInterval(cleanupExpiredLocks, 10000);
119
+ // ════════════════════════════════════════════════════════════════════════════════
120
+ // SCHEDULED JOBS
121
+ // ════════════════════════════════════════════════════════════════════════════════
122
+ const scheduledJobs = new Map();
123
+ const jobTimers = new Map();
124
+ function parseCron(cron) {
125
+ const parts = cron.trim().split(/\s+/);
126
+ if (parts.length !== 5) {
127
+ return { isValid: false, nextRun: 0 };
128
+ }
129
+ const [minute, hour] = parts;
130
+ const now = new Date();
131
+ const nextDate = new Date(now);
132
+ nextDate.setSeconds(0);
133
+ nextDate.setMilliseconds(0);
134
+ if (minute === '*' && hour === '*') {
135
+ nextDate.setMinutes(nextDate.getMinutes() + 1);
136
+ }
137
+ else if (minute.startsWith('*/')) {
138
+ const interval = parseInt(minute.slice(2));
139
+ const currentMinute = nextDate.getMinutes();
140
+ const nextMinute = Math.ceil((currentMinute + 1) / interval) * interval;
141
+ nextDate.setMinutes(nextMinute);
142
+ }
143
+ else if (hour === '*') {
144
+ const targetMinute = parseInt(minute);
145
+ if (nextDate.getMinutes() >= targetMinute) {
146
+ nextDate.setHours(nextDate.getHours() + 1);
147
+ }
148
+ nextDate.setMinutes(targetMinute);
149
+ }
150
+ else {
151
+ const targetMinute = parseInt(minute);
152
+ const targetHour = parseInt(hour);
153
+ nextDate.setMinutes(targetMinute);
154
+ nextDate.setHours(targetHour);
155
+ if (nextDate <= now) {
156
+ nextDate.setDate(nextDate.getDate() + 1);
44
157
  }
45
158
  }
159
+ return { isValid: true, nextRun: nextDate.getTime() };
160
+ }
161
+ function scheduleJob(job) {
162
+ const { isValid, nextRun } = parseCron(job.cron);
163
+ if (!isValid) {
164
+ logger.error('Invalid cron expression', { jobId: job.id, cron: job.cron });
165
+ return false;
166
+ }
167
+ job.nextRun = nextRun;
168
+ scheduledJobs.set(job.id, job);
169
+ const existingTimer = jobTimers.get(job.id);
170
+ if (existingTimer) {
171
+ clearTimeout(existingTimer);
172
+ }
173
+ const delay = nextRun - Date.now();
174
+ const timer = setTimeout(() => runJob(job.id), delay);
175
+ jobTimers.set(job.id, timer);
176
+ logger.info('Job scheduled', {
177
+ jobId: job.id,
178
+ method: job.method,
179
+ photon: job.photonName,
180
+ nextRun: new Date(nextRun).toISOString(),
181
+ });
182
+ return true;
183
+ }
184
+ async function runJob(jobId) {
185
+ const job = scheduledJobs.get(jobId);
186
+ if (!job)
187
+ return;
188
+ const sessionManager = sessionManagers.get(job.photonName);
189
+ if (!sessionManager) {
190
+ logger.warn('Cannot run job - photon not initialized', { jobId, photon: job.photonName });
191
+ scheduleJob(job); // Reschedule anyway
192
+ return;
193
+ }
194
+ logger.info('Running scheduled job', { jobId, method: job.method, photon: job.photonName });
195
+ try {
196
+ const session = await sessionManager.getOrCreateSession('scheduler', 'scheduler');
197
+ await sessionManager.loader.executeTool(session.instance, job.method, job.args || {});
198
+ job.lastRun = Date.now();
199
+ job.runCount++;
200
+ publishToChannel(`jobs:${job.photonName}`, {
201
+ event: 'job-completed',
202
+ jobId,
203
+ method: job.method,
204
+ runCount: job.runCount,
205
+ });
206
+ logger.info('Job completed', { jobId, method: job.method, runCount: job.runCount });
207
+ }
46
208
  catch (error) {
47
- console.error(`[daemon-server] Failed to initialize session manager: ${error.message}`);
48
- process.exit(1);
209
+ logger.error('Job failed', { jobId, method: job.method, error: getErrorMessage(error) });
210
+ publishToChannel(`jobs:${job.photonName}`, {
211
+ event: 'job-failed',
212
+ jobId,
213
+ method: job.method,
214
+ error: getErrorMessage(error),
215
+ });
49
216
  }
217
+ scheduleJob(job);
50
218
  }
51
- /**
52
- * Start idle timer
53
- */
54
- function startIdleTimer() {
55
- if (idleTimer) {
56
- clearTimeout(idleTimer);
219
+ function unscheduleJob(jobId) {
220
+ const timer = jobTimers.get(jobId);
221
+ if (timer) {
222
+ clearTimeout(timer);
223
+ jobTimers.delete(jobId);
57
224
  }
58
- if (idleTimeout <= 0) {
59
- return; // Idle timeout disabled
225
+ const existed = scheduledJobs.delete(jobId);
226
+ if (existed) {
227
+ logger.info('Job unscheduled', { jobId });
60
228
  }
61
- idleTimer = setTimeout(() => {
62
- if (!sessionManager)
229
+ return existed;
230
+ }
231
+ // ════════════════════════════════════════════════════════════════════════════════
232
+ // WEBHOOK HTTP SERVER
233
+ // ════════════════════════════════════════════════════════════════════════════════
234
+ let webhookServer = null;
235
+ const WEBHOOK_PORT = parseInt(process.env.PHOTON_WEBHOOK_PORT || '0');
236
+ function startWebhookServer(port) {
237
+ if (port <= 0)
238
+ return;
239
+ webhookServer = http.createServer(async (req, res) => {
240
+ res.setHeader('Access-Control-Allow-Origin', '*');
241
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
242
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Webhook-Secret, X-Photon-Name');
243
+ if (req.method === 'OPTIONS') {
244
+ res.writeHead(204);
245
+ res.end();
63
246
  return;
64
- const lastActivity = sessionManager.getLastActivity();
65
- const idleTime = Date.now() - lastActivity;
66
- if (idleTime >= idleTimeout) {
67
- console.error(`[daemon-server] Idle timeout reached (${idleTime}ms). Shutting down.`);
68
- shutdown();
69
247
  }
70
- else {
71
- // Restart timer with remaining time
72
- startIdleTimer();
248
+ // Parse URL: /webhook/{photonName}/{method}
249
+ const url = new URL(req.url || '/', `http://localhost:${port}`);
250
+ const pathParts = url.pathname.split('/').filter(Boolean);
251
+ if (pathParts[0] !== 'webhook' || !pathParts[1] || !pathParts[2]) {
252
+ res.writeHead(404, { 'Content-Type': 'application/json' });
253
+ res.end(JSON.stringify({ error: 'Not found. Use /webhook/{photonName}/{method}' }));
254
+ return;
73
255
  }
74
- }, idleTimeout);
256
+ const photonName = pathParts[1];
257
+ const method = pathParts[2];
258
+ const expectedSecret = process.env.PHOTON_WEBHOOK_SECRET;
259
+ if (expectedSecret) {
260
+ const providedSecret = req.headers['x-webhook-secret'];
261
+ if (providedSecret !== expectedSecret) {
262
+ res.writeHead(401, { 'Content-Type': 'application/json' });
263
+ res.end(JSON.stringify({ error: 'Invalid webhook secret' }));
264
+ return;
265
+ }
266
+ }
267
+ let body = '';
268
+ req.on('data', (chunk) => {
269
+ body += chunk;
270
+ });
271
+ req.on('end', async () => {
272
+ let args = {};
273
+ try {
274
+ if (body) {
275
+ args = JSON.parse(body);
276
+ }
277
+ args._webhook = {
278
+ method: req.method,
279
+ headers: req.headers,
280
+ query: Object.fromEntries(url.searchParams),
281
+ timestamp: Date.now(),
282
+ };
283
+ }
284
+ catch {
285
+ res.writeHead(400, { 'Content-Type': 'application/json' });
286
+ res.end(JSON.stringify({ error: 'Invalid JSON body' }));
287
+ return;
288
+ }
289
+ const sessionManager = sessionManagers.get(photonName);
290
+ if (!sessionManager) {
291
+ res.writeHead(503, { 'Content-Type': 'application/json' });
292
+ res.end(JSON.stringify({ error: `Photon '${photonName}' not initialized` }));
293
+ return;
294
+ }
295
+ try {
296
+ const session = await sessionManager.getOrCreateSession('webhook', 'webhook');
297
+ const result = await sessionManager.loader.executeTool(session.instance, method, args);
298
+ logger.info('Webhook executed', { photon: photonName, method });
299
+ publishToChannel(`webhooks:${photonName}`, {
300
+ event: 'webhook-received',
301
+ method,
302
+ timestamp: Date.now(),
303
+ });
304
+ res.writeHead(200, { 'Content-Type': 'application/json' });
305
+ res.end(JSON.stringify({ success: true, data: result }));
306
+ }
307
+ catch (error) {
308
+ logger.error('Webhook execution failed', {
309
+ photon: photonName,
310
+ method,
311
+ error: getErrorMessage(error),
312
+ });
313
+ res.writeHead(500, { 'Content-Type': 'application/json' });
314
+ res.end(JSON.stringify({ error: getErrorMessage(error) }));
315
+ }
316
+ });
317
+ });
318
+ webhookServer.listen(port, () => {
319
+ logger.info('Webhook server started', { port });
320
+ });
321
+ webhookServer.on('error', (error) => {
322
+ logger.error('Webhook server error', { error: getErrorMessage(error) });
323
+ });
75
324
  }
76
- /**
77
- * Reset idle timer (called on each activity)
78
- */
79
- function resetIdleTimer() {
80
- startIdleTimer();
325
+ // ════════════════════════════════════════════════════════════════════════════════
326
+ // PUB/SUB
327
+ // ════════════════════════════════════════════════════════════════════════════════
328
+ function cleanupSocketSubscriptions(socket) {
329
+ for (const [channel, subs] of channelSubscriptions.entries()) {
330
+ subs.delete(socket);
331
+ if (subs.size === 0) {
332
+ channelSubscriptions.delete(channel);
333
+ }
334
+ }
81
335
  }
82
- /**
83
- * Create a prompt handler that sends prompts over the socket
84
- */
336
+ function publishToChannel(channel, message, excludeSocket) {
337
+ // Buffer the event for replay
338
+ const eventId = bufferEvent(channel, message);
339
+ const payload = JSON.stringify({
340
+ type: 'channel_message',
341
+ id: `ch_${eventId}`,
342
+ eventId,
343
+ channel,
344
+ message,
345
+ }) + '\n';
346
+ const sentSockets = new Set();
347
+ // Send to exact channel subscribers
348
+ const exactSubscribers = channelSubscriptions.get(channel);
349
+ if (exactSubscribers) {
350
+ for (const socket of exactSubscribers) {
351
+ if (socket !== excludeSocket && !socket.destroyed && !sentSockets.has(socket)) {
352
+ try {
353
+ socket.write(payload);
354
+ sentSockets.add(socket);
355
+ }
356
+ catch {
357
+ // Socket write failed
358
+ }
359
+ }
360
+ }
361
+ }
362
+ // Send to wildcard subscribers
363
+ const channelPrefix = channel.split(':')[0];
364
+ if (channelPrefix) {
365
+ const wildcardChannel = `${channelPrefix}:*`;
366
+ const wildcardSubscribers = channelSubscriptions.get(wildcardChannel);
367
+ if (wildcardSubscribers) {
368
+ for (const socket of wildcardSubscribers) {
369
+ if (socket !== excludeSocket && !socket.destroyed && !sentSockets.has(socket)) {
370
+ try {
371
+ socket.write(payload);
372
+ sentSockets.add(socket);
373
+ }
374
+ catch {
375
+ // Socket write failed
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ logger.debug('Published to channel', {
382
+ channel,
383
+ eventId,
384
+ exactSubs: exactSubscribers?.size || 0,
385
+ wildcardSubs: channelSubscriptions.get(`${channelPrefix}:*`)?.size || 0,
386
+ });
387
+ return eventId;
388
+ }
389
+ // ════════════════════════════════════════════════════════════════════════════════
390
+ // SESSION MANAGER (Lazy Initialization)
391
+ // ════════════════════════════════════════════════════════════════════════════════
392
+ async function getOrCreateSessionManager(photonName, photonPath) {
393
+ let manager = sessionManagers.get(photonName);
394
+ if (manager) {
395
+ return manager;
396
+ }
397
+ // Need photonPath to initialize
398
+ const storedPath = photonPaths.get(photonName);
399
+ const pathToUse = photonPath || storedPath;
400
+ if (!pathToUse) {
401
+ logger.warn('Cannot initialize photon - no path provided', { photonName });
402
+ return null;
403
+ }
404
+ try {
405
+ logger.info('Initializing session manager', { photonName, photonPath: pathToUse });
406
+ manager = new SessionManager(pathToUse, photonName, idleTimeout, logger.child({ scope: photonName }));
407
+ sessionManagers.set(photonName, manager);
408
+ photonPaths.set(photonName, pathToUse);
409
+ logger.info('Session manager initialized', { photonName });
410
+ return manager;
411
+ }
412
+ catch (error) {
413
+ logger.error('Failed to initialize session manager', {
414
+ photonName,
415
+ error: getErrorMessage(error),
416
+ });
417
+ return null;
418
+ }
419
+ }
420
+ // ════════════════════════════════════════════════════════════════════════════════
421
+ // PROMPT HANDLER
422
+ // ════════════════════════════════════════════════════════════════════════════════
85
423
  function createSocketPromptHandler(socket, requestId) {
86
424
  return async (message, defaultValue) => {
87
425
  return new Promise((resolve, reject) => {
88
- // Store the resolver for when we get the response
89
426
  pendingPrompts.set(requestId, {
90
427
  resolve: (value) => resolve(value),
91
428
  reject,
92
429
  });
93
- // Send prompt request to client
94
430
  const promptResponse = {
95
431
  type: 'prompt',
96
432
  id: requestId,
@@ -104,163 +440,437 @@ function createSocketPromptHandler(socket, requestId) {
104
440
  });
105
441
  };
106
442
  }
107
- /**
108
- * Handle incoming command request
109
- */
443
+ // ════════════════════════════════════════════════════════════════════════════════
444
+ // REQUEST HANDLER
445
+ // ════════════════════════════════════════════════════════════════════════════════
110
446
  async function handleRequest(request, socket) {
111
447
  resetIdleTimer();
112
448
  if (request.type === 'ping') {
113
- return {
114
- type: 'pong',
115
- id: request.id,
116
- };
449
+ return { type: 'pong', id: request.id };
117
450
  }
118
451
  if (request.type === 'shutdown') {
119
452
  shutdown();
453
+ return { type: 'result', id: request.id, success: true, data: { message: 'Shutting down' } };
454
+ }
455
+ // Handle hot-reload request
456
+ if (request.type === 'reload') {
457
+ const photonName = request.photonName;
458
+ const photonPath = request.photonPath;
459
+ if (!photonName || !photonPath) {
460
+ return {
461
+ type: 'error',
462
+ id: request.id,
463
+ error: 'photonName and photonPath required for reload',
464
+ };
465
+ }
466
+ const result = await reloadPhoton(photonName, photonPath);
120
467
  return {
121
468
  type: 'result',
122
469
  id: request.id,
123
- success: true,
124
- data: { message: 'Shutting down' },
470
+ success: result.success,
471
+ data: result.success
472
+ ? { message: 'Reload complete', sessionsUpdated: result.sessionsUpdated }
473
+ : { error: result.error },
125
474
  };
126
475
  }
127
- // Handle prompt response from client
476
+ // Handle prompt response
128
477
  if (request.type === 'prompt_response') {
129
478
  const pending = pendingPrompts.get(request.id);
130
479
  if (pending) {
131
480
  pendingPrompts.delete(request.id);
132
481
  pending.resolve(request.promptValue ?? null);
133
482
  }
134
- // Don't send a response back - the original command handler will respond
135
483
  return null;
136
484
  }
485
+ // Handle channel subscribe
486
+ if (request.type === 'subscribe') {
487
+ const channel = request.channel;
488
+ const lastEventId = request.lastEventId;
489
+ let subs = channelSubscriptions.get(channel);
490
+ if (!subs) {
491
+ subs = new Set();
492
+ channelSubscriptions.set(channel, subs);
493
+ }
494
+ subs.add(socket);
495
+ logger.info('Client subscribed to channel', { channel, subscribers: subs.size });
496
+ // Replay missed events if lastEventId provided
497
+ if (lastEventId !== undefined) {
498
+ const parsedLastEventId = parseInt(String(lastEventId), 10) || 0;
499
+ const { events, refreshNeeded } = getEventsSince(channel, parsedLastEventId);
500
+ if (refreshNeeded) {
501
+ // Send refresh-needed signal
502
+ socket.write(JSON.stringify({
503
+ type: 'refresh_needed',
504
+ id: request.id,
505
+ channel,
506
+ }) + '\n');
507
+ logger.info('Replay: refresh needed', { channel, lastEventId });
508
+ }
509
+ else if (events.length > 0) {
510
+ // Replay events
511
+ for (const event of events) {
512
+ socket.write(JSON.stringify({
513
+ type: 'channel_message',
514
+ id: `replay_${event.id}`,
515
+ eventId: event.id,
516
+ channel: event.channel,
517
+ message: event.message,
518
+ replay: true,
519
+ }) + '\n');
520
+ }
521
+ logger.info('Replayed events', { channel, count: events.length });
522
+ }
523
+ }
524
+ return {
525
+ type: 'result',
526
+ id: request.id,
527
+ success: true,
528
+ data: { subscribed: true, channel },
529
+ };
530
+ }
531
+ // Handle channel unsubscribe
532
+ if (request.type === 'unsubscribe') {
533
+ const channel = request.channel;
534
+ const subs = channelSubscriptions.get(channel);
535
+ if (subs) {
536
+ subs.delete(socket);
537
+ if (subs.size === 0) {
538
+ channelSubscriptions.delete(channel);
539
+ }
540
+ }
541
+ logger.info('Client unsubscribed from channel', { channel });
542
+ return { type: 'result', id: request.id, success: true, data: { unsubscribed: true, channel } };
543
+ }
544
+ // Handle channel publish
545
+ if (request.type === 'publish') {
546
+ const channel = request.channel;
547
+ const message = request.message;
548
+ const eventId = publishToChannel(channel, message, socket);
549
+ return {
550
+ type: 'result',
551
+ id: request.id,
552
+ success: true,
553
+ data: { published: true, channel, eventId },
554
+ };
555
+ }
556
+ // Handle get_events_since (for event replay)
557
+ if (request.type === 'get_events_since') {
558
+ const channel = request.channel;
559
+ const parsedLastEventId = parseInt(String(request.lastEventId || '0'), 10) || 0;
560
+ const { events, refreshNeeded } = getEventsSince(channel, parsedLastEventId);
561
+ return {
562
+ type: 'result',
563
+ id: request.id,
564
+ success: true,
565
+ data: { events, refreshNeeded },
566
+ };
567
+ }
568
+ // Handle lock acquisition
569
+ if (request.type === 'lock') {
570
+ const lockName = request.lockName;
571
+ const holder = request.sessionId || request.id;
572
+ const timeout = request.lockTimeout || DEFAULT_LOCK_TIMEOUT;
573
+ const acquired = acquireLock(lockName, holder, timeout);
574
+ return {
575
+ type: 'result',
576
+ id: request.id,
577
+ success: acquired,
578
+ data: {
579
+ acquired,
580
+ lockName,
581
+ holder,
582
+ ...(acquired ? {} : { reason: 'Lock held by another client' }),
583
+ },
584
+ };
585
+ }
586
+ // Handle lock release
587
+ if (request.type === 'unlock') {
588
+ const lockName = request.lockName;
589
+ const holder = request.sessionId || request.id;
590
+ const released = releaseLock(lockName, holder);
591
+ return {
592
+ type: 'result',
593
+ id: request.id,
594
+ success: released,
595
+ data: {
596
+ released,
597
+ lockName,
598
+ ...(released ? {} : { reason: 'Cannot release lock held by another client' }),
599
+ },
600
+ };
601
+ }
602
+ // Handle list locks
603
+ if (request.type === 'list_locks') {
604
+ const locks = Array.from(activeLocks.values());
605
+ return { type: 'result', id: request.id, success: true, data: { locks } };
606
+ }
607
+ // Handle job scheduling
608
+ if (request.type === 'schedule') {
609
+ const photonName = request.photonName;
610
+ if (!photonName) {
611
+ return { type: 'error', id: request.id, error: 'photonName required for scheduling' };
612
+ }
613
+ const job = {
614
+ id: request.jobId,
615
+ method: request.method,
616
+ args: request.args,
617
+ cron: request.cron,
618
+ runCount: 0,
619
+ createdAt: Date.now(),
620
+ createdBy: request.sessionId,
621
+ photonName,
622
+ };
623
+ const scheduled = scheduleJob(job);
624
+ return {
625
+ type: 'result',
626
+ id: request.id,
627
+ success: scheduled,
628
+ data: scheduled
629
+ ? { scheduled: true, jobId: job.id, nextRun: job.nextRun }
630
+ : { scheduled: false, reason: 'Invalid cron expression' },
631
+ };
632
+ }
633
+ // Handle job unscheduling
634
+ if (request.type === 'unschedule') {
635
+ const jobId = request.jobId;
636
+ const unscheduled = unscheduleJob(jobId);
637
+ return { type: 'result', id: request.id, success: true, data: { unscheduled, jobId } };
638
+ }
639
+ // Handle list jobs
640
+ if (request.type === 'list_jobs') {
641
+ const jobs = Array.from(scheduledJobs.values());
642
+ return { type: 'result', id: request.id, success: true, data: { jobs } };
643
+ }
644
+ // Handle command execution
137
645
  if (request.type === 'command') {
138
646
  if (!request.method) {
139
- return {
140
- type: 'error',
141
- id: request.id,
142
- error: 'Method name required',
143
- };
647
+ return { type: 'error', id: request.id, error: 'Method name required' };
648
+ }
649
+ const photonName = request.photonName;
650
+ if (!photonName) {
651
+ return { type: 'error', id: request.id, error: 'photonName required for commands' };
144
652
  }
653
+ const sessionManager = await getOrCreateSessionManager(photonName, request.photonPath);
145
654
  if (!sessionManager) {
146
655
  return {
147
656
  type: 'error',
148
657
  id: request.id,
149
- error: 'Session manager not initialized',
658
+ error: `Cannot initialize photon '${photonName}'. Provide photonPath in request.`,
150
659
  };
151
660
  }
152
661
  try {
153
- // Get or create session for this client
154
662
  const session = await sessionManager.getOrCreateSession(request.sessionId, request.clientType);
155
- console.error(`[daemon-server] Executing: ${request.method} (session: ${session.id})`);
156
- // Set up socket-based prompt handler for this request
663
+ logger.info('Executing request', {
664
+ method: request.method,
665
+ photon: photonName,
666
+ sessionId: session.id,
667
+ });
157
668
  setPromptHandler(createSocketPromptHandler(socket, request.id));
158
- // Execute method on session's photon instance using loader
159
- const result = await sessionManager.loader.executeTool(session.instance, request.method, request.args || {});
160
- // Clear prompt handler after execution
161
- setPromptHandler(null);
162
- return {
163
- type: 'result',
164
- id: request.id,
165
- success: true,
166
- data: result,
669
+ const outputHandler = (emit) => {
670
+ if (emit && typeof emit === 'object' && emit.channel) {
671
+ publishToChannel(emit.channel, emit, socket);
672
+ logger.debug('Published to channel', { channel: emit.channel });
673
+ }
167
674
  };
675
+ const result = await sessionManager.loader.executeTool(session.instance, request.method, request.args || {}, { outputHandler });
676
+ setPromptHandler(null);
677
+ return { type: 'result', id: request.id, success: true, data: result };
168
678
  }
169
679
  catch (error) {
170
- console.error(`[daemon-server] Error executing ${request.method}: ${error.message}`);
171
- // Clear prompt handler on error
680
+ logger.error('Error executing request', {
681
+ method: request.method,
682
+ error: getErrorMessage(error),
683
+ });
172
684
  setPromptHandler(null);
173
- return {
174
- type: 'error',
175
- id: request.id,
176
- error: error.message,
177
- };
685
+ return { type: 'error', id: request.id, error: getErrorMessage(error) };
178
686
  }
179
687
  }
180
- return {
181
- type: 'error',
182
- id: request.id,
183
- error: `Unknown request type: ${request.type}`,
184
- };
688
+ return { type: 'error', id: request.id, error: `Unknown request type: ${request.type}` };
185
689
  }
186
- /**
187
- * Start IPC server
188
- */
690
+ // ════════════════════════════════════════════════════════════════════════════════
691
+ // HOT RELOAD
692
+ // ════════════════════════════════════════════════════════════════════════════════
693
+ async function reloadPhoton(photonName, newPhotonPath) {
694
+ try {
695
+ logger.info('Hot-reloading photon', { photonName, path: newPhotonPath });
696
+ const sessionManager = sessionManagers.get(photonName);
697
+ if (!sessionManager) {
698
+ // First time - just register the path
699
+ photonPaths.set(photonName, newPhotonPath);
700
+ return { success: true, sessionsUpdated: 0 };
701
+ }
702
+ await sessionManager.loader.reloadFile(newPhotonPath);
703
+ const sessions = sessionManager.getSessions();
704
+ let updatedCount = 0;
705
+ for (const session of sessions) {
706
+ try {
707
+ const newInstance = await sessionManager.loader.loadFile(newPhotonPath);
708
+ const oldInstance = session.instance;
709
+ if (oldInstance && typeof oldInstance === 'object') {
710
+ for (const key of Object.keys(oldInstance)) {
711
+ const value = oldInstance[key];
712
+ if (typeof value !== 'function' && key !== 'constructor') {
713
+ try {
714
+ newInstance[key] = value;
715
+ }
716
+ catch {
717
+ // Some properties may be read-only
718
+ }
719
+ }
720
+ }
721
+ }
722
+ if (sessionManager.updateSessionInstance(session.id, newInstance)) {
723
+ updatedCount++;
724
+ }
725
+ }
726
+ catch (err) {
727
+ logger.error('Failed to update session instance', {
728
+ sessionId: session.id,
729
+ error: getErrorMessage(err),
730
+ });
731
+ }
732
+ }
733
+ publishToChannel(`system:${photonName}`, {
734
+ event: 'photon-reloaded',
735
+ timestamp: Date.now(),
736
+ sessionsUpdated: updatedCount,
737
+ });
738
+ logger.info('Photon reloaded successfully', { photonName, sessionsUpdated: updatedCount });
739
+ return { success: true, sessionsUpdated: updatedCount };
740
+ }
741
+ catch (error) {
742
+ const errorMessage = getErrorMessage(error);
743
+ logger.error('Photon reload failed', { photonName, error: errorMessage });
744
+ return { success: false, error: errorMessage };
745
+ }
746
+ }
747
+ // ════════════════════════════════════════════════════════════════════════════════
748
+ // IDLE TIMER
749
+ // ════════════════════════════════════════════════════════════════════════════════
750
+ function startIdleTimer() {
751
+ if (idleTimer) {
752
+ clearTimeout(idleTimer);
753
+ }
754
+ if (idleTimeout <= 0)
755
+ return;
756
+ idleTimer = setTimeout(() => {
757
+ let activeSubscribers = 0;
758
+ for (const subs of channelSubscriptions.values()) {
759
+ activeSubscribers += subs.size;
760
+ }
761
+ if (activeSubscribers > 0) {
762
+ logger.debug('Active channel subscribers, staying alive', { activeSubscribers });
763
+ startIdleTimer();
764
+ return;
765
+ }
766
+ // Check if any session manager has recent activity
767
+ let lastActivity = 0;
768
+ for (const manager of sessionManagers.values()) {
769
+ const activity = manager.getLastActivity();
770
+ if (activity > lastActivity) {
771
+ lastActivity = activity;
772
+ }
773
+ }
774
+ const idleTime = Date.now() - lastActivity;
775
+ if (idleTime >= idleTimeout && sessionManagers.size > 0) {
776
+ logger.warn('Idle timeout reached, shutting down', { idleTime });
777
+ shutdown();
778
+ }
779
+ else {
780
+ startIdleTimer();
781
+ }
782
+ }, idleTimeout);
783
+ }
784
+ function resetIdleTimer() {
785
+ startIdleTimer();
786
+ }
787
+ // ════════════════════════════════════════════════════════════════════════════════
788
+ // SERVER
789
+ // ════════════════════════════════════════════════════════════════════════════════
189
790
  function startServer() {
190
791
  const server = net.createServer((socket) => {
191
- console.error('[daemon-server] Client connected');
792
+ logger.info('Client connected');
192
793
  let buffer = '';
193
794
  socket.on('data', async (chunk) => {
194
795
  buffer += chunk.toString();
195
- // Process complete JSON messages (newline-delimited)
196
796
  const lines = buffer.split('\n');
197
- buffer = lines.pop() || ''; // Keep incomplete line in buffer
797
+ buffer = lines.pop() || '';
198
798
  for (const line of lines) {
199
799
  if (!line.trim())
200
800
  continue;
201
801
  try {
202
- const request = JSON.parse(line);
802
+ const parsed = JSON.parse(line);
803
+ if (!isValidDaemonRequest(parsed)) {
804
+ socket.write(JSON.stringify({ type: 'error', id: 'unknown', error: 'Invalid request format' }) +
805
+ '\n');
806
+ continue;
807
+ }
808
+ const request = parsed;
203
809
  const response = await handleRequest(request, socket);
204
- // Only send response if handler returned one (null for prompt_response)
205
810
  if (response !== null) {
206
811
  socket.write(JSON.stringify(response) + '\n');
207
812
  }
208
813
  }
209
814
  catch (error) {
210
- console.error(`[daemon-server] Error processing request: ${error.message}`);
211
- socket.write(JSON.stringify({
212
- type: 'error',
213
- id: 'unknown',
214
- error: error.message,
215
- }) + '\n');
815
+ logger.error('Error processing request', { error: getErrorMessage(error) });
816
+ socket.write(JSON.stringify({ type: 'error', id: 'unknown', error: getErrorMessage(error) }) + '\n');
216
817
  }
217
818
  }
218
819
  });
219
820
  socket.on('end', () => {
220
- console.error('[daemon-server] Client disconnected');
821
+ logger.info('Client disconnected');
822
+ cleanupSocketSubscriptions(socket);
221
823
  });
222
824
  socket.on('error', (error) => {
223
- console.error(`[daemon-server] Socket error: ${error.message}`);
825
+ logger.warn('Socket error', { error: getErrorMessage(error) });
826
+ cleanupSocketSubscriptions(socket);
827
+ });
828
+ socket.on('close', () => {
829
+ cleanupSocketSubscriptions(socket);
224
830
  });
225
831
  });
226
832
  server.listen(socketPath, () => {
227
- console.error(`[daemon-server] Listening on ${socketPath}`);
228
- console.error(`[daemon-server] PID: ${process.pid}`);
833
+ logger.info('Global Photon daemon listening', { socketPath, pid: process.pid });
229
834
  });
230
835
  server.on('error', (error) => {
231
- console.error(`[daemon-server] Server error: ${error.message}`);
836
+ logger.error('Server error', { error: getErrorMessage(error) });
232
837
  process.exit(1);
233
838
  });
234
- // Graceful shutdown handlers
235
839
  process.on('SIGTERM', shutdown);
236
840
  process.on('SIGINT', shutdown);
237
841
  }
238
- /**
239
- * Shutdown daemon
240
- */
241
842
  function shutdown() {
242
- console.error('[daemon-server] Shutting down...');
843
+ logger.info('Shutting down global daemon');
243
844
  if (idleTimer) {
244
845
  clearTimeout(idleTimer);
245
846
  }
246
- // Destroy session manager
247
- if (sessionManager) {
248
- sessionManager.destroy();
847
+ for (const timer of jobTimers.values()) {
848
+ clearTimeout(timer);
849
+ }
850
+ jobTimers.clear();
851
+ scheduledJobs.clear();
852
+ activeLocks.clear();
853
+ for (const manager of sessionManagers.values()) {
854
+ manager.destroy();
855
+ }
856
+ sessionManagers.clear();
857
+ if (webhookServer) {
858
+ webhookServer.close();
249
859
  }
250
- // Clean up socket file (Unix only)
251
860
  if (fs.existsSync(socketPath) && process.platform !== 'win32') {
252
861
  try {
253
862
  fs.unlinkSync(socketPath);
254
863
  }
255
- catch (error) {
864
+ catch {
256
865
  // Ignore cleanup errors
257
866
  }
258
867
  }
259
868
  process.exit(0);
260
869
  }
261
870
  // Main execution
262
- (async () => {
263
- await initializeSessionManager();
871
+ (() => {
264
872
  startServer();
873
+ startWebhookServer(WEBHOOK_PORT);
874
+ startIdleTimer();
265
875
  })();
266
876
  //# sourceMappingURL=server.js.map