@agimon-ai/po 0.0.0-alpha.2 → 0.0.0-alpha.21

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 (255) hide show
  1. package/README.md +14 -11
  2. package/dist/antigravity.mjs +1 -2
  3. package/dist/baseCommand.mjs +1 -2
  4. package/dist/bin/cli.cjs +37 -14
  5. package/dist/bin/cli.cjs.map +1 -1
  6. package/dist/bin/cli.mjs +34 -14
  7. package/dist/bin/cli.mjs.map +1 -1
  8. package/dist/bin/{doomRunner.cjs → poRunner.cjs} +5 -5
  9. package/dist/bin/poRunner.cjs.map +1 -0
  10. package/dist/bin/{doomRunner.d.mts → poRunner.d.mts} +2 -2
  11. package/dist/bin/poRunner.d.mts.map +1 -0
  12. package/dist/bin/{doomRunner.mjs → poRunner.mjs} +5 -5
  13. package/dist/bin/poRunner.mjs.map +1 -0
  14. package/dist/builders/cli/extensionAssembler/index.cjs +14 -12
  15. package/dist/builders/cli/extensionAssembler/index.cjs.map +1 -1
  16. package/dist/builders/cli/extensionAssembler/index.d.mts +2 -1
  17. package/dist/builders/cli/extensionAssembler/index.d.mts.map +1 -1
  18. package/dist/builders/cli/extensionAssembler/index.mjs +14 -12
  19. package/dist/builders/cli/extensionAssembler/index.mjs.map +1 -1
  20. package/dist/catalog/index.json +68 -51
  21. package/dist/cliApp.mjs +1 -2
  22. package/dist/codex.mjs +1 -2
  23. package/dist/compatibilityCommand.mjs +1 -2
  24. package/dist/compatibilityContext.mjs +1 -2
  25. package/dist/compatibilityOptions.mjs +1 -2
  26. package/dist/config.cjs +57 -12
  27. package/dist/config.d.mts +3 -3
  28. package/dist/config.mjs +3 -3
  29. package/dist/doctorCommand.mjs +1 -2
  30. package/dist/emitMcpCommand.mjs +1 -2
  31. package/dist/explainCommand.mjs +1 -2
  32. package/dist/extensions/server.cjs +11 -7
  33. package/dist/extensions/server.cjs.map +1 -1
  34. package/dist/extensions/server.d.mts +2 -2
  35. package/dist/extensions/server.d.mts.map +1 -1
  36. package/dist/extensions/server.mjs +12 -8
  37. package/dist/extensions/server.mjs.map +1 -1
  38. package/dist/harnessContext.mjs +1 -2
  39. package/dist/harnessState.cjs +66 -11
  40. package/dist/harnessState.d.mts +2 -2
  41. package/dist/harnessState.mjs +1 -2
  42. package/dist/index.cjs +60 -15
  43. package/dist/index.d.mts +3 -3
  44. package/dist/index.mjs +3 -3
  45. package/dist/initCommand.mjs +1 -2
  46. package/dist/matrixOptions.mjs +1 -2
  47. package/dist/options.mjs +1 -2
  48. package/dist/process.mjs +1 -2
  49. package/dist/projectTrust.cjs +2 -2
  50. package/dist/projectTrust.d.mts +2 -2
  51. package/dist/projectTrust.mjs +1 -2
  52. package/dist/repository.cjs +1 -1
  53. package/dist/repository.d.mts +2 -2
  54. package/dist/repository.mjs +1 -2
  55. package/dist/router.mjs +1 -2
  56. package/dist/shared.mjs +1 -2
  57. package/dist/src/bin/agentDirectory.cjs +18 -12
  58. package/dist/src/bin/agentDirectory.cjs.map +1 -1
  59. package/dist/src/bin/agentDirectory.mjs +15 -12
  60. package/dist/src/bin/agentDirectory.mjs.map +1 -1
  61. package/dist/src/builders/apiContractExport/index.cjs +6 -6
  62. package/dist/src/builders/apiContractExport/index.cjs.map +1 -1
  63. package/dist/src/builders/apiContractExport/index.mjs +9 -9
  64. package/dist/src/builders/apiContractExport/index.mjs.map +1 -1
  65. package/dist/src/builders/cli/entryResolution.cjs +3 -3
  66. package/dist/src/builders/cli/entryResolution.cjs.map +1 -1
  67. package/dist/src/builders/cli/entryResolution.mjs +3 -3
  68. package/dist/src/builders/cli/entryResolution.mjs.map +1 -1
  69. package/dist/src/builders/cli/harnessContext.cjs +5 -5
  70. package/dist/src/builders/cli/harnessContext.cjs.map +1 -1
  71. package/dist/src/builders/cli/harnessContext.mjs +7 -7
  72. package/dist/src/builders/cli/harnessContext.mjs.map +1 -1
  73. package/dist/src/builders/hooks/index.cjs +1 -1
  74. package/dist/src/builders/hooks/index.cjs.map +1 -1
  75. package/dist/src/builders/hooks/index.mjs +2 -2
  76. package/dist/src/builders/hooks/index.mjs.map +1 -1
  77. package/dist/src/builders/server/backgroundSync.cjs +13 -1
  78. package/dist/src/builders/server/backgroundSync.cjs.map +1 -1
  79. package/dist/src/builders/server/backgroundSync.mjs +13 -1
  80. package/dist/src/builders/server/backgroundSync.mjs.map +1 -1
  81. package/dist/src/builders/server/bundlerService.cjs +151 -0
  82. package/dist/src/builders/server/bundlerService.cjs.map +1 -0
  83. package/dist/src/builders/server/bundlerService.mjs +151 -0
  84. package/dist/src/builders/server/bundlerService.mjs.map +1 -0
  85. package/dist/src/builders/server/computerUseBinding.cjs +2 -2
  86. package/dist/src/builders/server/computerUseBinding.cjs.map +1 -1
  87. package/dist/src/builders/server/computerUseBinding.mjs +3 -3
  88. package/dist/src/builders/server/computerUseBinding.mjs.map +1 -1
  89. package/dist/src/builders/server/defaultWorkspace.cjs +1 -1
  90. package/dist/src/builders/server/defaultWorkspace.cjs.map +1 -1
  91. package/dist/src/builders/server/defaultWorkspace.mjs +2 -2
  92. package/dist/src/builders/server/defaultWorkspace.mjs.map +1 -1
  93. package/dist/src/builders/server/idleRestart.cjs +3 -3
  94. package/dist/src/builders/server/idleRestart.cjs.map +1 -1
  95. package/dist/src/builders/server/idleRestart.mjs +3 -3
  96. package/dist/src/builders/server/idleRestart.mjs.map +1 -1
  97. package/dist/src/builders/server/index.cjs +6 -6
  98. package/dist/src/builders/server/index.cjs.map +1 -1
  99. package/dist/src/builders/server/index.mjs +6 -6
  100. package/dist/src/builders/server/index.mjs.map +1 -1
  101. package/dist/src/builders/server/logSink.cjs +3 -3
  102. package/dist/src/builders/server/logSink.cjs.map +1 -1
  103. package/dist/src/builders/server/logSink.mjs +2 -2
  104. package/dist/src/builders/server/logSink.mjs.map +1 -1
  105. package/dist/src/builders/server/mcpBundle.cjs.map +1 -1
  106. package/dist/src/builders/server/mcpBundle.d.mts +2 -2
  107. package/dist/src/builders/server/mcpBundle.mjs.map +1 -1
  108. package/dist/src/builders/server/packageUrl.cjs +25 -18
  109. package/dist/src/builders/server/packageUrl.cjs.map +1 -1
  110. package/dist/src/builders/server/packageUrl.mjs +25 -18
  111. package/dist/src/builders/server/packageUrl.mjs.map +1 -1
  112. package/dist/src/builders/server/runtime.cjs +157 -168
  113. package/dist/src/builders/server/runtime.cjs.map +1 -1
  114. package/dist/src/builders/server/runtime.mjs +161 -171
  115. package/dist/src/builders/server/runtime.mjs.map +1 -1
  116. package/dist/src/builders/server/selectionStatus.cjs +3 -3
  117. package/dist/src/builders/server/selectionStatus.cjs.map +1 -1
  118. package/dist/src/builders/server/selectionStatus.mjs +3 -3
  119. package/dist/src/builders/server/selectionStatus.mjs.map +1 -1
  120. package/dist/src/builders/server/sessionArguments.cjs.map +1 -1
  121. package/dist/src/builders/server/sessionArguments.mjs.map +1 -1
  122. package/dist/src/builders/server/watcherService.cjs +65 -0
  123. package/dist/src/builders/server/watcherService.cjs.map +1 -0
  124. package/dist/src/builders/server/watcherService.mjs +61 -0
  125. package/dist/src/builders/server/watcherService.mjs.map +1 -0
  126. package/dist/src/builders/server/webTemplateDecision.cjs +1 -1
  127. package/dist/src/builders/server/webTemplateDecision.cjs.map +1 -1
  128. package/dist/src/builders/server/webTemplateDecision.mjs +2 -2
  129. package/dist/src/builders/server/webTemplateDecision.mjs.map +1 -1
  130. package/dist/src/builders/web/bundle.cjs.map +1 -1
  131. package/dist/src/builders/web/bundle.d.mts +4 -3
  132. package/dist/src/builders/web/bundle.mjs.map +1 -1
  133. package/dist/src/builders/web/bundleAssetPolicy.d.mts +1 -0
  134. package/dist/src/builders/web/index.cjs +1 -1
  135. package/dist/src/builders/web/index.cjs.map +1 -1
  136. package/dist/src/builders/web/index.d.mts +1 -0
  137. package/dist/src/builders/web/index.mjs +1 -1
  138. package/dist/src/builders/web/index.mjs.map +1 -1
  139. package/dist/src/builders/web/webPluginGenerate.cjs +4 -15
  140. package/dist/src/builders/web/webPluginGenerate.cjs.map +1 -1
  141. package/dist/src/builders/web/webPluginGenerate.d.mts +5 -9
  142. package/dist/src/builders/web/webPluginGenerate.d.mts.map +1 -1
  143. package/dist/src/builders/web/webPluginGenerate.mjs +4 -15
  144. package/dist/src/builders/web/webPluginGenerate.mjs.map +1 -1
  145. package/dist/src/builders/web/webPluginVite.d.mts +1 -0
  146. package/dist/src/cli/cliApp.cjs +1 -1
  147. package/dist/src/cli/cliApp.cjs.map +1 -1
  148. package/dist/src/cli/cliApp.mjs +1 -1
  149. package/dist/src/cli/cliApp.mjs.map +1 -1
  150. package/dist/src/cli/commands/compat/index.cjs +1 -1
  151. package/dist/src/cli/commands/compat/index.cjs.map +1 -1
  152. package/dist/src/cli/commands/compat/index.mjs +1 -1
  153. package/dist/src/cli/commands/compat/index.mjs.map +1 -1
  154. package/dist/src/cli/commands/doctor/index.cjs +2 -2
  155. package/dist/src/cli/commands/doctor/index.cjs.map +1 -1
  156. package/dist/src/cli/commands/doctor/index.d.mts +1 -0
  157. package/dist/src/cli/commands/doctor/index.d.mts.map +1 -1
  158. package/dist/src/cli/commands/doctor/index.mjs +4 -4
  159. package/dist/src/cli/commands/doctor/index.mjs.map +1 -1
  160. package/dist/src/cli/commands/explain/index.cjs +3 -1
  161. package/dist/src/cli/commands/explain/index.cjs.map +1 -1
  162. package/dist/src/cli/commands/explain/index.d.mts.map +1 -1
  163. package/dist/src/cli/commands/explain/index.mjs +4 -2
  164. package/dist/src/cli/commands/explain/index.mjs.map +1 -1
  165. package/dist/src/cli/commands/init/index.cjs +2 -2
  166. package/dist/src/cli/commands/init/index.cjs.map +1 -1
  167. package/dist/src/cli/commands/init/index.mjs +3 -3
  168. package/dist/src/cli/commands/init/index.mjs.map +1 -1
  169. package/dist/src/cli/commands/init/presenter.cjs +1 -1
  170. package/dist/src/cli/commands/init/presenter.cjs.map +1 -1
  171. package/dist/src/cli/commands/init/presenter.mjs +2 -2
  172. package/dist/src/cli/commands/init/presenter.mjs.map +1 -1
  173. package/dist/src/cli/commands/sync/index.cjs +4 -4
  174. package/dist/src/cli/commands/sync/index.cjs.map +1 -1
  175. package/dist/src/cli/commands/sync/index.d.mts +1 -0
  176. package/dist/src/cli/commands/sync/index.mjs +7 -7
  177. package/dist/src/cli/commands/sync/index.mjs.map +1 -1
  178. package/dist/src/cli/commands/sync/presenter.cjs +1 -1
  179. package/dist/src/cli/commands/sync/presenter.cjs.map +1 -1
  180. package/dist/src/cli/commands/sync/presenter.d.mts +1 -1
  181. package/dist/src/cli/commands/sync/presenter.mjs +1 -1
  182. package/dist/src/cli/commands/sync/presenter.mjs.map +1 -1
  183. package/dist/src/cli/commands/web/index.cjs +2 -1
  184. package/dist/src/cli/commands/web/index.cjs.map +1 -1
  185. package/dist/src/cli/commands/web/index.mjs +2 -1
  186. package/dist/src/cli/commands/web/index.mjs.map +1 -1
  187. package/dist/src/cli/harnessOptions.cjs +1 -1
  188. package/dist/src/cli/harnessOptions.cjs.map +1 -1
  189. package/dist/src/cli/harnessOptions.mjs +2 -2
  190. package/dist/src/cli/harnessOptions.mjs.map +1 -1
  191. package/dist/src/cli/help.cjs +2 -0
  192. package/dist/src/cli/help.cjs.map +1 -1
  193. package/dist/src/cli/help.mjs +2 -0
  194. package/dist/src/cli/help.mjs.map +1 -1
  195. package/dist/src/cli/matrixOptions.cjs +1 -1
  196. package/dist/src/cli/matrixOptions.cjs.map +1 -1
  197. package/dist/src/cli/matrixOptions.d.mts +1 -1
  198. package/dist/src/cli/matrixOptions.mjs +1 -1
  199. package/dist/src/cli/matrixOptions.mjs.map +1 -1
  200. package/dist/src/cli/server/run.cjs +1 -1
  201. package/dist/src/cli/server/run.cjs.map +1 -1
  202. package/dist/src/cli/server/run.mjs +2 -2
  203. package/dist/src/cli/server/run.mjs.map +1 -1
  204. package/dist/src/composition/harnessState/index.cjs +66 -11
  205. package/dist/src/composition/harnessState/index.cjs.map +1 -1
  206. package/dist/src/composition/harnessState/index.d.mts +3 -3
  207. package/dist/src/composition/harnessState/index.mjs +2 -2
  208. package/dist/src/composition/harnessState/index.mjs.map +1 -1
  209. package/dist/src/composition/layerPackageInstaller/index.cjs +5 -4
  210. package/dist/src/composition/layerPackageInstaller/index.cjs.map +1 -1
  211. package/dist/src/composition/layerPackageInstaller/index.mjs +6 -5
  212. package/dist/src/composition/layerPackageInstaller/index.mjs.map +1 -1
  213. package/dist/src/composition/poHomeMigration/index.cjs +1 -1
  214. package/dist/src/composition/poHomeMigration/index.cjs.map +1 -1
  215. package/dist/src/composition/poHomeMigration/index.mjs +1 -1
  216. package/dist/src/composition/poHomeMigration/index.mjs.map +1 -1
  217. package/dist/src/composition/projectTrust/index.cjs +6 -6
  218. package/dist/src/composition/projectTrust/index.cjs.map +1 -1
  219. package/dist/src/composition/projectTrust/index.d.mts +7 -7
  220. package/dist/src/composition/projectTrust/index.d.mts.map +1 -1
  221. package/dist/src/composition/projectTrust/index.mjs +6 -6
  222. package/dist/src/composition/projectTrust/index.mjs.map +1 -1
  223. package/dist/src/composition/repository/index.cjs +8 -8
  224. package/dist/src/composition/repository/index.cjs.map +1 -1
  225. package/dist/src/composition/repository/index.d.mts +5 -5
  226. package/dist/src/composition/repository/index.d.mts.map +1 -1
  227. package/dist/src/composition/repository/index.mjs +10 -10
  228. package/dist/src/composition/repository/index.mjs.map +1 -1
  229. package/dist/src/composition/syncService/index.cjs +29 -9
  230. package/dist/src/composition/syncService/index.cjs.map +1 -1
  231. package/dist/src/composition/syncService/index.d.mts.map +1 -1
  232. package/dist/src/composition/syncService/index.mjs +31 -11
  233. package/dist/src/composition/syncService/index.mjs.map +1 -1
  234. package/dist/src/composition/syncService/localBuilds.cjs +7 -2
  235. package/dist/src/composition/syncService/localBuilds.cjs.map +1 -1
  236. package/dist/src/composition/syncService/localBuilds.mjs +7 -2
  237. package/dist/src/composition/syncService/localBuilds.mjs.map +1 -1
  238. package/dist/src/extensions/setupDiagnostics.cjs +3 -3
  239. package/dist/src/extensions/setupDiagnostics.cjs.map +1 -1
  240. package/dist/src/extensions/setupDiagnostics.mjs +3 -3
  241. package/dist/src/extensions/setupDiagnostics.mjs.map +1 -1
  242. package/dist/utils.mjs +1 -2
  243. package/docs/server/api-export.md +3 -3
  244. package/docs/server/api.md +23 -22
  245. package/docs/server/getting-started.md +7 -7
  246. package/docs/server/index.md +4 -4
  247. package/docs/server/ipc.md +7 -11
  248. package/docs/server/lifecycle.md +17 -17
  249. package/docs/server/security.md +8 -8
  250. package/package.json +42 -45
  251. package/src/prompts/po-author-extension/SKILL.md +1 -1
  252. package/src/prompts/po-author-extension/references/extension-contract.md +4 -4
  253. package/dist/bin/doomRunner.cjs.map +0 -1
  254. package/dist/bin/doomRunner.d.mts.map +0 -1
  255. package/dist/bin/doomRunner.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"backgroundSync.mjs","names":["spawnProcess"],"sources":["../../../../src/builders/server/backgroundSync.ts"],"sourcesContent":["import { type ChildProcess, spawn as spawnProcess, type SpawnOptions } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { HARNESS_STATE_KEYS, HARNESS_STATE_POINTER } from '../../composition/harnessState';\nimport { packageCatalogHash } from '../../composition/layerPackageInstaller';\nimport {\n readSyncLastAttempt,\n readSyncPointer,\n rootSelectionFingerprint,\n SYNC_EXIT_BUSY,\n SYNC_EXIT_OK,\n type SyncPointer,\n syncHostIdentity,\n} from '../../composition/syncService';\nimport { poPackageRoot } from '../cli/entryResolution';\n\n/**\n * Background sync: hosts that only read what sync prepared start `po sync --json --global` as\n * a child process when a root is out of date, and pick its artifacts up when it exits. The\n * terminal app instead syncs from its repository without `--global`, which registers it.\n *\n * At most one job runs per host. Requests that arrive while it runs merge into one rerun, and\n * nothing waits on a job, so admission and session open never block on sync.\n *\n * ponytail: there is no fs.watch on `.po`, so a config edit syncs at the next checkpoint\n * (server start, admission, session open). Add a watcher if edits must apply sooner.\n */\n\n/** Set on every background sync child to the pid of the host that started it. */\nexport const SYNC_PARENT_PID_ENV = 'PO_SYNC_PARENT_PID';\n/** `PO_AUTO_SYNC=0` turns background sync off, for CI and the system test. */\nexport const AUTO_SYNC_ENV = 'PO_AUTO_SYNC';\nconst AUTO_SYNC_OFF = '0';\nconst ROOT_ENV = 'PO_ROOT';\nconst SYNC_ARGS = ['sync', '--json'] as const;\nconst GLOBAL_OPTION = '--global';\nconst CLI_ENTRY_SEGMENTS = ['dist', 'bin', 'cli.mjs'] as const;\n\n/** The host pid a sync child was started by, or undefined when this process is not one. */\nexport function syncParentPid(environment: NodeJS.ProcessEnv): number | undefined {\n const pid = Number(environment[SYNC_PARENT_PID_ENV]);\n return Number.isInteger(pid) && pid > 0 ? pid : undefined;\n}\n\n/**\n * The environment a sync child runs under, and the one a host compares root fingerprints\n * with: no harness state and no root of the host's own, so both resolve every root alike.\n */\nexport function syncEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const scoped = { ...environment };\n for (const key of [ROOT_ENV, HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete scoped[key];\n return scoped;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport interface BackgroundSyncOptions {\n environment: NodeJS.ProcessEnv;\n onNotice: (message: string) => void;\n /** Runs after each job's child exits, before a merged rerun starts. */\n onExit?: (code: number | null) => void | Promise<void>;\n /** Lets the child outlive this process, for a terminal app that exits before sync finishes. */\n detached?: boolean;\n /**\n * The repository the child syncs from, without `--global`, so sync registers it as a workspace.\n * Without one the child runs with `--global` and registers nothing.\n */\n repoRoot?: string;\n /** Test seams. */\n spawn?: (command: string, args: readonly string[], options: SpawnOptions) => ChildProcess;\n cliEntry?: string;\n parentPid?: number;\n}\n\nexport interface BackgroundSync {\n /** Starts a job, or merges into the rerun of the running one. False when background sync is off. */\n request(reason: string): boolean;\n /** Whether a job runs or a rerun is queued. */\n readonly syncing: boolean;\n /** Resolves once no job runs and none is queued. */\n idle(): Promise<void>;\n}\n\n/** Whether this host may start background sync: on unless `PO_AUTO_SYNC=0`, and never inside a sync child. */\nexport function autoSyncEnabled(environment: NodeJS.ProcessEnv): boolean {\n return environment[AUTO_SYNC_ENV] !== AUTO_SYNC_OFF && syncParentPid(environment) === undefined;\n}\n\nexport function createBackgroundSync(options: BackgroundSyncOptions): BackgroundSync {\n const enabled = autoSyncEnabled(options.environment);\n let running = false;\n let rerun = false;\n const waiters: (() => void)[] = [];\n const settle = (): void => {\n if (!running) for (const resolve of waiters.splice(0)) resolve();\n };\n const start = (): boolean => {\n const entry = options.cliEntry ?? path.join(poPackageRoot(), ...CLI_ENTRY_SEGMENTS);\n if (!fs.existsSync(entry)) {\n options.onNotice(`Background sync is unavailable because '${entry}' is missing; run po sync.`);\n return false;\n }\n let child: ChildProcess;\n try {\n // On desktop the child inherits ELECTRON_RUN_AS_NODE, so the app binary runs it as node.\n const scope = options.repoRoot === undefined ? [GLOBAL_OPTION] : [];\n child = (options.spawn ?? spawnProcess)(process.execPath, [...process.execArgv, entry, ...SYNC_ARGS, ...scope], {\n ...(options.repoRoot === undefined ? {} : { cwd: options.repoRoot }),\n env: {\n ...syncEnvironment(options.environment),\n [SYNC_PARENT_PID_ENV]: String(options.parentPid ?? process.pid),\n },\n stdio: 'ignore',\n detached: options.detached === true,\n windowsHide: true,\n });\n } catch (error) {\n options.onNotice(`Background sync could not start: ${errorMessage(error)}`);\n return false;\n }\n running = true;\n if (options.detached === true) child.unref();\n let finished = false;\n const finish = (code: number | null): void => {\n if (finished) return;\n finished = true;\n if (code !== null && code !== SYNC_EXIT_OK && code !== SYNC_EXIT_BUSY)\n options.onNotice(`Background sync exited with code ${String(code)}.`);\n void Promise.resolve()\n .then(() => options.onExit?.(code))\n .catch((error: unknown) => options.onNotice(`Picking up background sync failed: ${errorMessage(error)}`))\n .finally(() => {\n running = false;\n if (rerun) {\n rerun = false;\n start();\n }\n settle();\n });\n };\n child.once('error', (error) => {\n options.onNotice(`Background sync could not start: ${error.message}`);\n finish(null);\n });\n child.once('exit', (code) => finish(code));\n return true;\n };\n return {\n request(): boolean {\n if (!enabled) return false;\n if (running) {\n rerun = true;\n return true;\n }\n return start();\n },\n get syncing() {\n return running || rerun;\n },\n idle() {\n return new Promise<void>((resolve) => {\n waiters.push(resolve);\n settle();\n });\n },\n };\n}\n\n/**\n * Publishes a build only when the key it is given differs from the last one published, so a\n * sync that changed nothing republishes nothing. A key whose build cannot be read is skipped.\n */\nexport function createKeyedPickup<T>(\n read: (key: string) => T | undefined,\n publish: (build: T, key: string) => void,\n): (key: string | null | undefined) => 'published' | 'unchanged' | 'missing' {\n let current: string | undefined;\n return (key) => {\n if (key === null || key === undefined || key === current) return 'unchanged';\n const build = read(key);\n if (build === undefined) return 'missing';\n current = key;\n publish(build, key);\n return 'published';\n };\n}\n\nexport interface SyncedBuildKeys {\n webKey: string | null | undefined;\n nativeKey: string | null | undefined;\n}\n\nexport interface SyncedBuildKeysInput {\n pointer: SyncPointer | undefined;\n /** Whether this host's own Po install wrote `pointer`. */\n ownsPointer: boolean;\n /** The kinds this host already publishes. */\n published: { web: boolean; native: boolean };\n /** The keys this install derives the way sync does, each only when its build exists. */\n ownKeys: () => { webKey?: string; nativeKey?: string };\n /** The newest web build in the Po home, whichever install made it. */\n newestWebKey: () => string | undefined;\n}\n\n/**\n * The build keys a host publishes. Its own pointer names them. Before sync ever wrote one (the\n * first start after an upgrade), this install's derived keys, else the newest web build. A\n * pointer another Po install wrote is refused: what the host already publishes stays (an\n * undefined key changes nothing), and a kind it has not published yet takes only this\n * install's own build while its own sync runs, never the other install's.\n */\nexport function syncedBuildKeys(input: SyncedBuildKeysInput): SyncedBuildKeys {\n const { pointer, published } = input;\n if (input.ownsPointer && pointer !== undefined) return { webKey: pointer.webKey, nativeKey: pointer.nativeKey };\n if (pointer === undefined) {\n const own = input.ownKeys();\n return { webKey: own.webKey ?? input.newestWebKey(), nativeKey: own.nativeKey };\n }\n if (published.web && published.native) return { webKey: undefined, nativeKey: undefined };\n const own = input.ownKeys();\n return {\n webKey: published.web ? undefined : own.webKey,\n nativeKey: published.native ? undefined : own.nativeKey,\n };\n}\n\nexport interface SyncCheckpointsInput {\n globalRoot: string;\n homeDirectory: string;\n environment: NodeJS.ProcessEnv;\n background: BackgroundSync;\n onNotice: (message: string) => void;\n}\n\nexport interface RootSyncCheck {\n root: string;\n /** How the root is named in a warning, such as `Workspace 'po'`. */\n label: string;\n /** The selectors this host left out because they are not synced. */\n unsynced: readonly string[];\n}\n\nexport interface RootSyncStatus {\n syncing: boolean;\n /** One line per unsynced selector: why sync could not prepare it, or that it is still syncing. */\n reasons: string[];\n}\n\n/**\n * The checkpoints a host runs when it admits a root or opens a session: request a background\n * job when the root changed since the last sync, and warn about what it opened without. Each\n * root's warning is emitted once until it changes.\n */\nexport function createSyncCheckpoints(input: SyncCheckpointsInput) {\n const fingerprintEnvironment = syncEnvironment(input.environment);\n const warned = new Map<string, string>();\n /** The state each root last requested a sync for: pointer generation, catalogue and selection. */\n const requested = new Map<string, string>();\n /** This host's install, compared with the one that wrote the pointer. */\n const host = (): string => syncHostIdentity(input.globalRoot, fingerprintEnvironment);\n /** The last foreign pointer (install and generation) this host warned about. */\n let warnedTakeover: string | undefined;\n /**\n * The root's state when it is stale (no pointer, a pointer another Po install wrote, a changed\n * catalogue, or a selection that differs from the pointer's), else undefined.\n *\n * A foreign pointer's state includes its generation, which every sync moves, so each takeover\n * of the shared runtime is one new stale state and gets one resync. That settles: the resync\n * writes this host's own pointer, which is never stale for being foreign.\n */\n const staleState = (root: string): string | undefined => {\n const pointer = readSyncPointer(input.globalRoot);\n const catalogueHash = packageCatalogHash(input.environment);\n const fingerprint = rootSelectionFingerprint(root, input.homeDirectory, fingerprintEnvironment);\n if (pointer !== undefined && pointer.host !== host())\n return JSON.stringify(['host', pointer.host ?? null, pointer.generation, catalogueHash, fingerprint]);\n if (pointer?.catalogueHash === catalogueHash && pointer.fingerprints?.[root] === fingerprint) return undefined;\n return JSON.stringify([pointer?.generation ?? null, catalogueHash, fingerprint]);\n };\n const stale = (root: string): boolean => staleState(root) !== undefined;\n /**\n * Requests one sync per stale state of a root. A sync that ran and could not change the\n * pointer (it failed) is not requested again until the root, the catalogue or the pointer\n * moves, so a broken root never makes every checkpoint start another full sync.\n */\n const requestIfStale = (check: RootSyncCheck): boolean => {\n const state = staleState(check.root);\n if (state === undefined) return false;\n if (requested.get(check.root) === state) return true;\n if (!input.background.request(`${check.label} changed since the last sync`)) return false;\n requested.set(check.root, state);\n return true;\n };\n /** The missing artifacts a sync was requested for, each with the pointer state it was missing under. */\n const requestedMissing = new Set<string>();\n /** Why the last attempt, or failing that the pointer, could not prepare each selector. */\n const failures = (root: string): Map<string, string> => {\n const reports = [readSyncLastAttempt(input.globalRoot), readSyncPointer(input.globalRoot)];\n const reasons = new Map<string, string>();\n for (const report of reports)\n for (const entry of report?.roots.find((candidate) => candidate.root === root)?.unsynced ?? [])\n if (!reasons.has(entry.selector)) reasons.set(entry.selector, entry.reason);\n return reasons;\n };\n return {\n stale,\n /**\n * Whether this host may publish the builds `pointer` names: only when its own Po install wrote\n * it. Every pointer another install writes (each sync moves its generation) is refused, and\n * gets one background sync of the global root and one warning, so this host keeps its previous\n * publication until its own sync replaces the pointer.\n */\n ownsPointer(pointer: SyncPointer | undefined): pointer is SyncPointer {\n if (pointer === undefined) return false;\n const own = host();\n if (pointer.host === own) return true;\n const foreign = pointer.host ?? 'an older Po';\n const resyncing = requestIfStale({ root: input.globalRoot, label: 'The global scope', unsynced: [] });\n const takeover = JSON.stringify([foreign, pointer.generation]);\n if (warnedTakeover !== takeover) {\n warnedTakeover = takeover;\n input.onNotice(\n `Another Po install (${foreign}) replaced the shared packages; ${resyncing ? `resyncing with this install (${own})` : 'run po sync from this install to resync'}. Use one Po install so they stop replacing each other.`,\n );\n }\n return false;\n },\n /**\n * Requests one sync per missing artifact (such as an MCP app build key) and pointer state,\n * the same limit stale roots get: a sync that ran and still did not produce it is not\n * requested again until the pointer or the catalogue moves.\n */\n requestMissing(artifact: string, reason: string): boolean {\n const pointer = readSyncPointer(input.globalRoot);\n const state = JSON.stringify([artifact, pointer?.generation ?? null, packageCatalogHash(input.environment)]);\n if (requestedMissing.has(state) || !input.background.request(reason)) return false;\n requestedMissing.add(state);\n return true;\n },\n /** Requests a job when `request` allows and the root is stale, then warns about `unsynced`. */\n check(check: RootSyncCheck, request = true): RootSyncStatus {\n if (request) requestIfStale(check);\n const syncing = input.background.syncing;\n if (check.unsynced.length === 0) {\n warned.delete(check.root);\n return { syncing, reasons: [] };\n }\n const known = failures(check.root);\n const count = check.unsynced.length;\n const noun = count === 1 ? 'extension' : 'extensions';\n const reasons = check.unsynced.map((selector) => {\n const reason = known.get(selector);\n if (syncing || reason === undefined) return `${selector} is not synced yet`;\n return `Sync could not prepare ${selector}: ${reason}.`;\n });\n const message = syncing\n ? `${check.label} opened without ${String(count)} ${noun} that ${count === 1 ? 'is' : 'are'} still syncing: ${check.unsynced.join(', ')}. ${count === 1 ? 'It loads' : 'They load'} when sync finishes.`\n : check.unsynced.every((selector) => known.has(selector))\n ? reasons.join(' ')\n : `${check.label} opened without ${String(count)} ${noun} that ${count === 1 ? 'is' : 'are'} not synced: ${check.unsynced.join(', ')}. Run po sync to prepare them.`;\n if (warned.get(check.root) !== message) {\n warned.set(check.root, message);\n input.onNotice(message);\n }\n return { syncing, reasons };\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,sBAAsB;;AAEnC,MAAa,gBAAgB;AAC7B,MAAM,gBAAgB;AACtB,MAAM,WAAW;AACjB,MAAM,YAAY,CAAC,QAAQ,QAAQ;AACnC,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;CAAC;CAAQ;CAAO;AAAS;;AAGpD,SAAgB,cAAc,aAAoD;CAChF,MAAM,MAAM,OAAO,YAAY,oBAAoB;CACnD,OAAO,OAAO,UAAU,GAAG,KAAK,MAAM,IAAI,MAAM,KAAA;AAClD;;;;;AAMA,SAAgB,gBAAgB,aAAmD;CACjF,MAAM,SAAS,EAAE,GAAG,YAAY;CAChC,KAAK,MAAM,OAAO;EAAC;EAAU;EAAuB,GAAG,OAAO,OAAO,kBAAkB;CAAC,GAAG,OAAO,OAAO;CACzG,OAAO;AACT;AAEA,SAAS,aAAa,OAAwB;CAC5C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;;AA8BA,SAAgB,gBAAgB,aAAyC;CACvE,OAAO,YAAA,oBAA+B,iBAAiB,cAAc,WAAW,MAAM,KAAA;AACxF;AAEA,SAAgB,qBAAqB,SAAgD;CACnF,MAAM,UAAU,gBAAgB,QAAQ,WAAW;CACnD,IAAI,UAAU;CACd,IAAI,QAAQ;CACZ,MAAM,UAA0B,CAAC;CACjC,MAAM,eAAqB;EACzB,IAAI,CAAC,SAAS,KAAK,MAAM,WAAW,QAAQ,OAAO,CAAC,GAAG,QAAQ;CACjE;CACA,MAAM,cAAuB;EAC3B,MAAM,QAAQ,QAAQ,YAAY,KAAK,KAAK,cAAc,GAAG,GAAG,kBAAkB;EAClF,IAAI,CAAC,GAAG,WAAW,KAAK,GAAG;GACzB,QAAQ,SAAS,2CAA2C,MAAM,2BAA2B;GAC7F,OAAO;EACT;EACA,IAAI;EACJ,IAAI;GAEF,MAAM,QAAQ,QAAQ,aAAa,KAAA,IAAY,CAAC,aAAa,IAAI,CAAC;GAClE,SAAS,QAAQ,SAASA,MAAAA,CAAc,QAAQ,UAAU;IAAC,GAAG,QAAQ;IAAU;IAAO,GAAG;IAAW,GAAG;GAAK,GAAG;IAC9G,GAAI,QAAQ,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK,QAAQ,SAAS;IAClE,KAAK;KACH,GAAG,gBAAgB,QAAQ,WAAW;MACrC,sBAAsB,OAAO,QAAQ,aAAa,QAAQ,GAAG;IAChE;IACA,OAAO;IACP,UAAU,QAAQ,aAAa;IAC/B,aAAa;GACf,CAAC;EACH,SAAS,OAAO;GACd,QAAQ,SAAS,oCAAoC,aAAa,KAAK,GAAG;GAC1E,OAAO;EACT;EACA,UAAU;EACV,IAAI,QAAQ,aAAa,MAAM,MAAM,MAAM;EAC3C,IAAI,WAAW;EACf,MAAM,UAAU,SAA8B;GAC5C,IAAI,UAAU;GACd,WAAW;GACX,IAAI,SAAS,QAAQ,SAAA,KAAyB,SAAA,IAC5C,QAAQ,SAAS,oCAAoC,OAAO,IAAI,EAAE,EAAE;GACtE,QAAa,QAAQ,CAAC,CACnB,WAAW,QAAQ,SAAS,IAAI,CAAC,CAAC,CAClC,OAAO,UAAmB,QAAQ,SAAS,sCAAsC,aAAa,KAAK,GAAG,CAAC,CAAC,CACxG,cAAc;IACb,UAAU;IACV,IAAI,OAAO;KACT,QAAQ;KACR,MAAM;IACR;IACA,OAAO;GACT,CAAC;EACL;EACA,MAAM,KAAK,UAAU,UAAU;GAC7B,QAAQ,SAAS,oCAAoC,MAAM,SAAS;GACpE,OAAO,IAAI;EACb,CAAC;EACD,MAAM,KAAK,SAAS,SAAS,OAAO,IAAI,CAAC;EACzC,OAAO;CACT;CACA,OAAO;EACL,UAAmB;GACjB,IAAI,CAAC,SAAS,OAAO;GACrB,IAAI,SAAS;IACX,QAAQ;IACR,OAAO;GACT;GACA,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,WAAW;EACpB;EACA,OAAO;GACL,OAAO,IAAI,SAAe,YAAY;IACpC,QAAQ,KAAK,OAAO;IACpB,OAAO;GACT,CAAC;EACH;CACF;AACF;;;;;AAMA,SAAgB,kBACd,MACA,SAC2E;CAC3E,IAAI;CACJ,QAAQ,QAAQ;EACd,IAAI,QAAQ,QAAQ,QAAQ,KAAA,KAAa,QAAQ,SAAS,OAAO;EACjE,MAAM,QAAQ,KAAK,GAAG;EACtB,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,UAAU;EACV,QAAQ,OAAO,GAAG;EAClB,OAAO;CACT;AACF;;;;;;;;AA0BA,SAAgB,gBAAgB,OAA8C;CAC5E,MAAM,EAAE,SAAS,cAAc;CAC/B,IAAI,MAAM,eAAe,YAAY,KAAA,GAAW,OAAO;EAAE,QAAQ,QAAQ;EAAQ,WAAW,QAAQ;CAAU;CAC9G,IAAI,YAAY,KAAA,GAAW;EACzB,MAAM,MAAM,MAAM,QAAQ;EAC1B,OAAO;GAAE,QAAQ,IAAI,UAAU,MAAM,aAAa;GAAG,WAAW,IAAI;EAAU;CAChF;CACA,IAAI,UAAU,OAAO,UAAU,QAAQ,OAAO;EAAE,QAAQ,KAAA;EAAW,WAAW,KAAA;CAAU;CACxF,MAAM,MAAM,MAAM,QAAQ;CAC1B,OAAO;EACL,QAAQ,UAAU,MAAM,KAAA,IAAY,IAAI;EACxC,WAAW,UAAU,SAAS,KAAA,IAAY,IAAI;CAChD;AACF;;;;;;AA6BA,SAAgB,sBAAsB,OAA6B;CACjE,MAAM,yBAAyB,gBAAgB,MAAM,WAAW;CAChE,MAAM,yBAAS,IAAI,IAAoB;;CAEvC,MAAM,4BAAY,IAAI,IAAoB;;CAE1C,MAAM,aAAqB,iBAAiB,MAAM,YAAY,sBAAsB;;CAEpF,IAAI;;;;;;;;;CASJ,MAAM,cAAc,SAAqC;EACvD,MAAM,UAAU,gBAAgB,MAAM,UAAU;EAChD,MAAM,gBAAgB,mBAAmB,MAAM,WAAW;EAC1D,MAAM,cAAc,yBAAyB,MAAM,MAAM,eAAe,sBAAsB;EAC9F,IAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,KAAK,GACjD,OAAO,KAAK,UAAU;GAAC;GAAQ,QAAQ,QAAQ;GAAM,QAAQ;GAAY;GAAe;EAAW,CAAC;EACtG,IAAI,SAAS,kBAAkB,iBAAiB,QAAQ,eAAe,UAAU,aAAa,OAAO,KAAA;EACrG,OAAO,KAAK,UAAU;GAAC,SAAS,cAAc;GAAM;GAAe;EAAW,CAAC;CACjF;CACA,MAAM,SAAS,SAA0B,WAAW,IAAI,MAAM,KAAA;;;;;;CAM9D,MAAM,kBAAkB,UAAkC;EACxD,MAAM,QAAQ,WAAW,MAAM,IAAI;EACnC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,IAAI,UAAU,IAAI,MAAM,IAAI,MAAM,OAAO,OAAO;EAChD,IAAI,CAAC,MAAM,WAAW,QAAQ,GAAG,MAAM,MAAM,6BAA6B,GAAG,OAAO;EACpF,UAAU,IAAI,MAAM,MAAM,KAAK;EAC/B,OAAO;CACT;;CAEA,MAAM,mCAAmB,IAAI,IAAY;;CAEzC,MAAM,YAAY,SAAsC;EACtD,MAAM,UAAU,CAAC,oBAAoB,MAAM,UAAU,GAAG,gBAAgB,MAAM,UAAU,CAAC;EACzF,MAAM,0BAAU,IAAI,IAAoB;EACxC,KAAK,MAAM,UAAU,SACnB,KAAK,MAAM,SAAS,QAAQ,MAAM,MAAM,cAAc,UAAU,SAAS,IAAI,CAAC,EAAE,YAAY,CAAC,GAC3F,IAAI,CAAC,QAAQ,IAAI,MAAM,QAAQ,GAAG,QAAQ,IAAI,MAAM,UAAU,MAAM,MAAM;EAC9E,OAAO;CACT;CACA,OAAO;EACL;;;;;;;EAOA,YAAY,SAA0D;GACpE,IAAI,YAAY,KAAA,GAAW,OAAO;GAClC,MAAM,MAAM,KAAK;GACjB,IAAI,QAAQ,SAAS,KAAK,OAAO;GACjC,MAAM,UAAU,QAAQ,QAAQ;GAChC,MAAM,YAAY,eAAe;IAAE,MAAM,MAAM;IAAY,OAAO;IAAoB,UAAU,CAAC;GAAE,CAAC;GACpG,MAAM,WAAW,KAAK,UAAU,CAAC,SAAS,QAAQ,UAAU,CAAC;GAC7D,IAAI,mBAAmB,UAAU;IAC/B,iBAAiB;IACjB,MAAM,SACJ,uBAAuB,QAAQ,kCAAkC,YAAY,gCAAgC,IAAI,KAAK,0CAA0C,wDAClK;GACF;GACA,OAAO;EACT;;;;;;EAMA,eAAe,UAAkB,QAAyB;GACxD,MAAM,UAAU,gBAAgB,MAAM,UAAU;GAChD,MAAM,QAAQ,KAAK,UAAU;IAAC;IAAU,SAAS,cAAc;IAAM,mBAAmB,MAAM,WAAW;GAAC,CAAC;GAC3G,IAAI,iBAAiB,IAAI,KAAK,KAAK,CAAC,MAAM,WAAW,QAAQ,MAAM,GAAG,OAAO;GAC7E,iBAAiB,IAAI,KAAK;GAC1B,OAAO;EACT;;EAEA,MAAM,OAAsB,UAAU,MAAsB;GAC1D,IAAI,SAAS,eAAe,KAAK;GACjC,MAAM,UAAU,MAAM,WAAW;GACjC,IAAI,MAAM,SAAS,WAAW,GAAG;IAC/B,OAAO,OAAO,MAAM,IAAI;IACxB,OAAO;KAAE;KAAS,SAAS,CAAC;IAAE;GAChC;GACA,MAAM,QAAQ,SAAS,MAAM,IAAI;GACjC,MAAM,QAAQ,MAAM,SAAS;GAC7B,MAAM,OAAO,UAAU,IAAI,cAAc;GACzC,MAAM,UAAU,MAAM,SAAS,KAAK,aAAa;IAC/C,MAAM,SAAS,MAAM,IAAI,QAAQ;IACjC,IAAI,WAAW,WAAW,KAAA,GAAW,OAAO,GAAG,SAAS;IACxD,OAAO,0BAA0B,SAAS,IAAI,OAAO;GACvD,CAAC;GACD,MAAM,UAAU,UACZ,GAAG,MAAM,MAAM,kBAAkB,OAAO,KAAK,EAAE,GAAG,KAAK,QAAQ,UAAU,IAAI,OAAO,MAAM,kBAAkB,MAAM,SAAS,KAAK,IAAI,EAAE,IAAI,UAAU,IAAI,aAAa,YAAY,wBACjL,MAAM,SAAS,OAAO,aAAa,MAAM,IAAI,QAAQ,CAAC,IACpD,QAAQ,KAAK,GAAG,IAChB,GAAG,MAAM,MAAM,kBAAkB,OAAO,KAAK,EAAE,GAAG,KAAK,QAAQ,UAAU,IAAI,OAAO,MAAM,eAAe,MAAM,SAAS,KAAK,IAAI,EAAE;GACzI,IAAI,OAAO,IAAI,MAAM,IAAI,MAAM,SAAS;IACtC,OAAO,IAAI,MAAM,MAAM,OAAO;IAC9B,MAAM,SAAS,OAAO;GACxB;GACA,OAAO;IAAE;IAAS;GAAQ;EAC5B;CACF;AACF"}
1
+ {"version":3,"file":"backgroundSync.mjs","names":["spawnProcess"],"sources":["../../../../src/builders/server/backgroundSync.ts"],"sourcesContent":["import { type ChildProcess, spawn as spawnProcess, type SpawnOptions } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { HARNESS_STATE_KEYS, HARNESS_STATE_POINTER } from '../../composition/harnessState';\nimport { packageCatalogHash } from '../../composition/layerPackageInstaller';\nimport {\n readSyncLastAttempt,\n readSyncPointer,\n rootSelectionFingerprint,\n SYNC_EXIT_BUSY,\n SYNC_EXIT_OK,\n type SyncPointer,\n syncHostIdentity,\n} from '../../composition/syncService';\nimport { poPackageRoot } from '../cli/entryResolution';\n\n/**\n * Background sync: hosts that only read what sync prepared start `po sync --json --global` as\n * a child process when a root is out of date, and pick its artifacts up when it exits. The\n * terminal app instead syncs from its repository without `--global`, which registers it.\n *\n * At most one job runs per host. Requests that arrive while it runs merge into one rerun, and\n * nothing waits on a job, so admission and session open never block on sync.\n *\n * ponytail: there is no fs.watch on `.po`, so a config edit syncs at the next checkpoint\n * (server start, admission, session open). Add a watcher if edits must apply sooner.\n */\n\n/** Set on every background sync child to the pid of the host that started it. */\nexport const SYNC_PARENT_PID_ENV = 'PO_SYNC_PARENT_PID';\n/** `PO_AUTO_SYNC=0` turns background sync off, for CI and the system test. */\nexport const AUTO_SYNC_ENV = 'PO_AUTO_SYNC';\nconst AUTO_SYNC_OFF = '0';\nconst ROOT_ENV = 'PO_ROOT';\nconst SYNC_ARGS = ['sync', '--json'] as const;\nconst GLOBAL_OPTION = '--global';\nconst CLI_ENTRY_SEGMENTS = ['dist', 'bin', 'cli.mjs'] as const;\n\n/** The host pid a sync child was started by, or undefined when this process is not one. */\nexport function syncParentPid(environment: NodeJS.ProcessEnv): number | undefined {\n const pid = Number(environment[SYNC_PARENT_PID_ENV]);\n return Number.isInteger(pid) && pid > 0 ? pid : undefined;\n}\n\n/**\n * The environment a sync child runs under, and the one a host compares root fingerprints\n * with: no harness state and no root of the host's own, so both resolve every root alike.\n */\nexport function syncEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const scoped = { ...environment };\n for (const key of [ROOT_ENV, HARNESS_STATE_POINTER, ...Object.values(HARNESS_STATE_KEYS)]) delete scoped[key];\n return scoped;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport interface BackgroundSyncOptions {\n environment: NodeJS.ProcessEnv;\n onNotice: (message: string) => void;\n /** Runs after each job's child exits, before a merged rerun starts. */\n onExit?: (code: number | null) => void | Promise<void>;\n /** Lets the child outlive this process, for a terminal app that exits before sync finishes. */\n detached?: boolean;\n /**\n * The repository the child syncs from, without `--global`, so sync registers it as a workspace.\n * Without one the child runs with `--global` and registers nothing.\n */\n repoRoot?: string;\n /** Test seams. */\n spawn?: (command: string, args: readonly string[], options: SpawnOptions) => ChildProcess;\n cliEntry?: string;\n parentPid?: number;\n}\n\nexport interface BackgroundSync {\n /** Starts a job, or merges into the rerun of the running one. False when background sync is off. */\n request(reason: string): boolean;\n /** Whether a job runs or a rerun is queued. */\n readonly syncing: boolean;\n /** Resolves once no job runs and none is queued. */\n idle(): Promise<void>;\n}\n\n/** Whether this host may start background sync: on unless `PO_AUTO_SYNC=0`, and never inside a sync child. */\nexport function autoSyncEnabled(environment: NodeJS.ProcessEnv): boolean {\n return environment[AUTO_SYNC_ENV] !== AUTO_SYNC_OFF && syncParentPid(environment) === undefined;\n}\n\nexport function createBackgroundSync(options: BackgroundSyncOptions): BackgroundSync {\n const enabled = autoSyncEnabled(options.environment);\n let running = false;\n let rerun = false;\n const waiters: (() => void)[] = [];\n const settle = (): void => {\n if (!running) for (const resolve of waiters.splice(0)) resolve();\n };\n const start = (): boolean => {\n const entry = options.cliEntry ?? path.join(poPackageRoot(), ...CLI_ENTRY_SEGMENTS);\n if (!fs.existsSync(entry)) {\n options.onNotice(`Background sync is unavailable because '${entry}' is missing; run po sync.`);\n return false;\n }\n let child: ChildProcess;\n try {\n // On desktop the child inherits ELECTRON_RUN_AS_NODE, so the app binary runs it as node.\n const scope = options.repoRoot === undefined ? [GLOBAL_OPTION] : [];\n child = (options.spawn ?? spawnProcess)(process.execPath, [...process.execArgv, entry, ...SYNC_ARGS, ...scope], {\n ...(options.repoRoot === undefined ? {} : { cwd: options.repoRoot }),\n env: {\n ...syncEnvironment(options.environment),\n [SYNC_PARENT_PID_ENV]: String(options.parentPid ?? process.pid),\n },\n stdio: 'ignore',\n detached: options.detached === true,\n windowsHide: true,\n });\n } catch (error) {\n options.onNotice(`Background sync could not start: ${errorMessage(error)}`);\n return false;\n }\n running = true;\n if (options.detached === true) child.unref();\n let finished = false;\n const finish = (code: number | null): void => {\n if (finished) return;\n finished = true;\n if (code !== null && code !== SYNC_EXIT_OK && code !== SYNC_EXIT_BUSY)\n options.onNotice(`Background sync exited with code ${String(code)}.`);\n void Promise.resolve()\n .then(() => options.onExit?.(code))\n .catch((error: unknown) => options.onNotice(`Picking up background sync failed: ${errorMessage(error)}`))\n .finally(() => {\n running = false;\n if (rerun) {\n rerun = false;\n start();\n }\n settle();\n });\n };\n child.once('error', (error) => {\n options.onNotice(`Background sync could not start: ${error.message}`);\n finish(null);\n });\n child.once('exit', (code) => finish(code));\n return true;\n };\n return {\n request(): boolean {\n if (!enabled) return false;\n if (running) {\n rerun = true;\n return true;\n }\n return start();\n },\n get syncing() {\n return running || rerun;\n },\n idle() {\n return new Promise<void>((resolve) => {\n waiters.push(resolve);\n settle();\n });\n },\n };\n}\n\n/**\n * Publishes a build only when the key it is given differs from the last one published, so a\n * sync that changed nothing republishes nothing. A key whose build cannot be read is skipped.\n */\nexport function createKeyedPickup<T>(\n read: (key: string) => T | undefined,\n publish: (build: T, key: string) => void,\n): (key: string | null | undefined) => 'published' | 'unchanged' | 'missing' {\n let current: string | undefined;\n return (key) => {\n if (key === null || key === undefined || key === current) return 'unchanged';\n const build = read(key);\n if (build === undefined) return 'missing';\n current = key;\n publish(build, key);\n return 'published';\n };\n}\n\nexport interface SyncedBuildKeys {\n webKey: string | null | undefined;\n nativeKey: string | null | undefined;\n}\n\nexport interface SyncedBuildKeysInput {\n pointer: SyncPointer | undefined;\n /** Whether this host's own Po install wrote `pointer`. */\n ownsPointer: boolean;\n /** The kinds this host already publishes. */\n published: { web: boolean; native: boolean };\n /** The keys this install derives the way sync does, each only when its build exists. */\n ownKeys: () => { webKey?: string; nativeKey?: string };\n /** The newest web build in the Po home, whichever install made it. */\n newestWebKey: () => string | undefined;\n}\n\n/**\n * The build keys a host publishes. Its own pointer names them. Before sync ever wrote one (the\n * first start after an upgrade), this install's derived keys, else the newest web build. A\n * pointer another Po install wrote is refused: what the host already publishes stays (an\n * undefined key changes nothing), and a kind it has not published yet takes only this\n * install's own build while its own sync runs, never the other install's.\n */\nexport function syncedBuildKeys(input: SyncedBuildKeysInput): SyncedBuildKeys {\n const { pointer, published } = input;\n if (input.ownsPointer && pointer !== undefined) return { webKey: pointer.webKey, nativeKey: pointer.nativeKey };\n if (pointer === undefined) {\n const own = input.ownKeys();\n return { webKey: own.webKey ?? input.newestWebKey(), nativeKey: own.nativeKey };\n }\n if (published.web && published.native) return { webKey: undefined, nativeKey: undefined };\n const own = input.ownKeys();\n return {\n webKey: published.web ? undefined : own.webKey,\n nativeKey: published.native ? undefined : own.nativeKey,\n };\n}\n\nexport interface SyncCheckpointsInput {\n globalRoot: string;\n homeDirectory: string;\n environment: NodeJS.ProcessEnv;\n background: BackgroundSync;\n onNotice: (message: string) => void;\n}\n\nexport interface RootSyncCheck {\n root: string;\n /** How the root is named in a warning, such as `Workspace 'po'`. */\n label: string;\n /** The selectors this host left out because they are not synced. */\n unsynced: readonly string[];\n}\n\nexport interface RootSyncStatus {\n syncing: boolean;\n /** One line per unsynced selector: why sync could not prepare it, or that it is still syncing. */\n reasons: string[];\n}\n\n/**\n * The checkpoints a host runs when it admits a root or opens a session: request a background\n * job when the root changed since the last sync, and warn about what it opened without. Each\n * root's warning is emitted once until it changes.\n */\nexport function createSyncCheckpoints(input: SyncCheckpointsInput) {\n const fingerprintEnvironment = syncEnvironment(input.environment);\n const warned = new Map<string, string>();\n /** The state each root last requested a sync for: pointer generation, catalogue and selection. */\n const requested = new Map<string, string>();\n /** This host's install, compared with the one that wrote the pointer. */\n const host = (): string => syncHostIdentity(input.globalRoot, fingerprintEnvironment);\n /** The last foreign pointer (install and generation) this host warned about. */\n let warnedTakeover: string | undefined;\n /** Each root's selection fingerprint, kept until `invalidate`: resolving it composes every mode. */\n const fingerprints = new Map<string, string>();\n const fingerprintFor = (root: string): string => {\n let fingerprint = fingerprints.get(root);\n if (fingerprint === undefined) {\n fingerprint = rootSelectionFingerprint(root, input.homeDirectory, fingerprintEnvironment);\n fingerprints.set(root, fingerprint);\n }\n return fingerprint;\n };\n /**\n * The root's state when it is stale (no pointer, a pointer another Po install wrote, a changed\n * catalogue, or a selection that differs from the pointer's), else undefined.\n *\n * A foreign pointer's state includes its generation, which every sync moves, so each takeover\n * of the shared runtime is one new stale state and gets one resync. That settles: the resync\n * writes this host's own pointer, which is never stale for being foreign.\n */\n const staleState = (root: string): string | undefined => {\n const pointer = readSyncPointer(input.globalRoot);\n const catalogueHash = packageCatalogHash(input.environment);\n const fingerprint = fingerprintFor(root);\n if (pointer !== undefined && pointer.host !== host())\n return JSON.stringify(['host', pointer.host ?? null, pointer.generation, catalogueHash, fingerprint]);\n if (pointer?.catalogueHash === catalogueHash && pointer.fingerprints?.[root] === fingerprint) return undefined;\n return JSON.stringify([pointer?.generation ?? null, catalogueHash, fingerprint]);\n };\n const stale = (root: string): boolean => staleState(root) !== undefined;\n /**\n * Requests one sync per stale state of a root. A sync that ran and could not change the\n * pointer (it failed) is not requested again until the root, the catalogue or the pointer\n * moves, so a broken root never makes every checkpoint start another full sync.\n */\n const requestIfStale = (check: RootSyncCheck): boolean => {\n const state = staleState(check.root);\n if (state === undefined) return false;\n if (requested.get(check.root) === state) return true;\n if (!input.background.request(`${check.label} changed since the last sync`)) return false;\n requested.set(check.root, state);\n return true;\n };\n /** The missing artifacts a sync was requested for, each with the pointer state it was missing under. */\n const requestedMissing = new Set<string>();\n /** Why the last attempt, or failing that the pointer, could not prepare each selector. */\n const failures = (root: string): Map<string, string> => {\n const reports = [readSyncLastAttempt(input.globalRoot), readSyncPointer(input.globalRoot)];\n const reasons = new Map<string, string>();\n for (const report of reports)\n for (const entry of report?.roots.find((candidate) => candidate.root === root)?.unsynced ?? [])\n if (!reasons.has(entry.selector)) reasons.set(entry.selector, entry.reason);\n return reasons;\n };\n return {\n stale,\n /** Forgets every root's fingerprint; the caller runs it when a root's selection may have moved. */\n invalidate: (): void => fingerprints.clear(),\n /**\n * Whether this host may publish the builds `pointer` names: only when its own Po install wrote\n * it. Every pointer another install writes (each sync moves its generation) is refused, and\n * gets one background sync of the global root and one warning, so this host keeps its previous\n * publication until its own sync replaces the pointer.\n */\n ownsPointer(pointer: SyncPointer | undefined): pointer is SyncPointer {\n if (pointer === undefined) return false;\n const own = host();\n if (pointer.host === own) return true;\n const foreign = pointer.host ?? 'an older Po';\n const resyncing = requestIfStale({ root: input.globalRoot, label: 'The global scope', unsynced: [] });\n const takeover = JSON.stringify([foreign, pointer.generation]);\n if (warnedTakeover !== takeover) {\n warnedTakeover = takeover;\n input.onNotice(\n `Another Po install (${foreign}) replaced the shared packages; ${resyncing ? `resyncing with this install (${own})` : 'run po sync from this install to resync'}. Use one Po install so they stop replacing each other.`,\n );\n }\n return false;\n },\n /**\n * Requests one sync per missing artifact (such as an MCP app build key) and pointer state,\n * the same limit stale roots get: a sync that ran and still did not produce it is not\n * requested again until the pointer or the catalogue moves.\n */\n requestMissing(artifact: string, reason: string): boolean {\n const pointer = readSyncPointer(input.globalRoot);\n const state = JSON.stringify([artifact, pointer?.generation ?? null, packageCatalogHash(input.environment)]);\n if (requestedMissing.has(state) || !input.background.request(reason)) return false;\n requestedMissing.add(state);\n return true;\n },\n /** Requests a job when `request` allows and the root is stale, then warns about `unsynced`. */\n check(check: RootSyncCheck, request = true): RootSyncStatus {\n if (request) requestIfStale(check);\n const syncing = input.background.syncing;\n if (check.unsynced.length === 0) {\n warned.delete(check.root);\n return { syncing, reasons: [] };\n }\n const known = failures(check.root);\n const count = check.unsynced.length;\n const noun = count === 1 ? 'extension' : 'extensions';\n const reasons = check.unsynced.map((selector) => {\n const reason = known.get(selector);\n if (syncing || reason === undefined) return `${selector} is not synced yet`;\n return `Sync could not prepare ${selector}: ${reason}.`;\n });\n const message = syncing\n ? `${check.label} opened without ${String(count)} ${noun} that ${count === 1 ? 'is' : 'are'} still syncing: ${check.unsynced.join(', ')}. ${count === 1 ? 'It loads' : 'They load'} when sync finishes.`\n : check.unsynced.every((selector) => known.has(selector))\n ? reasons.join(' ')\n : `${check.label} opened without ${String(count)} ${noun} that ${count === 1 ? 'is' : 'are'} not synced: ${check.unsynced.join(', ')}. Run po sync to prepare them.`;\n if (warned.get(check.root) !== message) {\n warned.set(check.root, message);\n input.onNotice(message);\n }\n return { syncing, reasons };\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ const require_packageUrl = require("./packageUrl.cjs");
2
+ const require_index = require("../web/index.cjs");
3
+ const require_sessionComposition = require("../cli/sessionComposition.cjs");
4
+ const require_mcpBundle = require("./mcpBundle.cjs");
5
+ const require_index$1 = require("./index.cjs");
6
+ const require_packageReloadCheck = require("./packageReloadCheck.cjs");
7
+ let _agimon_ai_po_config_majorModes = require("@agimon-ai/po-config/majorModes");
8
+ //#region src/builders/server/bundlerService.ts
9
+ /**
10
+ * Process-level owner of what Po compiles and loads: content-addressed package URLs, the
11
+ * live module cache, the poison set, per-scope reload tracking, and a per-root memo of the
12
+ * compositions and entries a root selects. Every reader shares one computation until
13
+ * `invalidate`; the caller decides when (a finished sync or a watched change).
14
+ */
15
+ function createBundlerService(options) {
16
+ /** Content-addressed import URLs for every Po package entry this server loads. */
17
+ const packageUrls = require_packageUrl.createPackageUrls();
18
+ /** URLs whose live import timed out or threw; skipped by every loader until the content changes. */
19
+ const poisoned = /* @__PURE__ */ new Set();
20
+ /** Imports each content-addressed URL once, so a reload imports only what changed. */
21
+ const importer = require_packageReloadCheck.createLiveModuleCache();
22
+ /** The package identities each mounted global and workspace scope last loaded. */
23
+ const reloadCheck = require_packageReloadCheck.createPackageReloadCheck();
24
+ const memos = /* @__PURE__ */ new Map();
25
+ const memoFor = (root) => {
26
+ let memo = memos.get(root);
27
+ if (memo === void 0) {
28
+ memo = {};
29
+ memos.set(root, memo);
30
+ }
31
+ return memo;
32
+ };
33
+ /** A pending load shared by every reader; a failed one is dropped so the next reader retries. */
34
+ const shared = (read, write, load) => {
35
+ const existing = read();
36
+ if (existing !== void 0) return existing;
37
+ const loading = load();
38
+ write(loading);
39
+ loading.catch(() => {
40
+ if (read() === loading) write(void 0);
41
+ });
42
+ return loading;
43
+ };
44
+ /**
45
+ * Every composition the root's config can select, one per major mode, resolved live.
46
+ * A package sync has not prepared yet is left out with a diagnostic.
47
+ */
48
+ const compositions = (root) => {
49
+ const memo = memoFor(root);
50
+ if (memo.compositions === void 0) {
51
+ memo.compositions = require_sessionComposition.rootCompositions({
52
+ root,
53
+ homeDirectory: options.homeDirectory,
54
+ environment: options.environment,
55
+ tolerateUnsynced: true
56
+ });
57
+ options.onCompositions?.(root, memo.compositions);
58
+ }
59
+ return memo.compositions;
60
+ };
61
+ const defaultSelection = (root) => {
62
+ const memo = memoFor(root);
63
+ if (memo.selection === void 0) {
64
+ const config = (0, _agimon_ai_po_config_majorModes.loadMajorModesConfig)(root, options.homeDirectory, options.environment);
65
+ const majorMode = config.defaultMajorMode;
66
+ memo.selection = {
67
+ root,
68
+ majorMode,
69
+ activeLayers: (0, _agimon_ai_po_config_majorModes.resolveLayers)(config, majorMode)
70
+ };
71
+ }
72
+ return memo.selection;
73
+ };
74
+ /** The root's server package entries, each with the URL its current package content imports from. */
75
+ const packageEntries = (root) => {
76
+ const memo = memoFor(root);
77
+ return shared(() => memo.packageEntries, (value) => memo.packageEntries = value, async () => {
78
+ const entries = [];
79
+ for (const { declaration, module, owners } of require_index$1.serverPackageCandidates(compositions(root))) entries.push({
80
+ packageName: declaration.packageName,
81
+ entry: declaration.entry,
82
+ module,
83
+ url: await packageUrls.urlFor(module),
84
+ scopes: declaration.scopes,
85
+ owners,
86
+ required: declaration.required === true
87
+ });
88
+ return entries;
89
+ });
90
+ };
91
+ /** The root's MCP plugin candidates and their entries with current URLs. */
92
+ const mcpEntries = (root) => {
93
+ const memo = memoFor(root);
94
+ return shared(() => memo.mcp, (value) => memo.mcp = value, async () => {
95
+ const candidates = require_mcpBundle.mcpPluginCandidates(compositions(root));
96
+ const entries = [];
97
+ for (const { declaration, module, owners } of candidates) entries.push({
98
+ packageName: declaration.packageName,
99
+ ...declaration.ui === void 0 ? {} : { widgets: declaration.ui.widgets },
100
+ entry: declaration.entry,
101
+ module,
102
+ url: await packageUrls.urlFor(module),
103
+ owners
104
+ });
105
+ return {
106
+ candidates,
107
+ entries
108
+ };
109
+ });
110
+ };
111
+ /**
112
+ * What a global or workspace scope loads: one identity per server package entry in that scope.
113
+ * The URL changes with package content; `selection` carries ownership and the root's
114
+ * default mode, so a modes.yaml change that selects a package in or out is reloaded too.
115
+ */
116
+ const scopeIdentities = (root, scope, entries) => {
117
+ const { majorMode, activeLayers } = defaultSelection(root);
118
+ return entries.filter((entry) => entry.scopes.includes(scope)).map((entry) => ({
119
+ module: entry.module,
120
+ url: entry.url,
121
+ selection: JSON.stringify([
122
+ entry.owners,
123
+ entry.required === true,
124
+ majorMode,
125
+ activeLayers
126
+ ])
127
+ }));
128
+ };
129
+ return {
130
+ packageUrls,
131
+ poisoned,
132
+ importer,
133
+ compositions,
134
+ defaultSelection,
135
+ packageEntries,
136
+ mcpEntries,
137
+ /** Every package root the root's config can select, over all its major modes or only `majorMode`'s. */
138
+ packageRoots: (root, majorMode) => require_index.compositionPackageRoots(compositions(root).filter((composition) => majorMode === void 0 || composition.majorMode?.name === majorMode)),
139
+ /** How the scope's current entries differ from what it last committed. */
140
+ diffScope: async (key, root, scope) => reloadCheck.diff(key, scopeIdentities(root, scope, await packageEntries(root))),
141
+ /** Records what a scope installed, so the next diff compares against it. */
142
+ commitScope: (key, root, scope, entries) => reloadCheck.commit(key, scopeIdentities(root, scope, entries)),
143
+ forgetScope: (key) => reloadCheck.forget(key),
144
+ /** Drops every root's memo; the next reader recomputes from disk. */
145
+ invalidate: () => memos.clear()
146
+ };
147
+ }
148
+ //#endregion
149
+ exports.createBundlerService = createBundlerService;
150
+
151
+ //# sourceMappingURL=bundlerService.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundlerService.cjs","names":["createPackageUrls","createLiveModuleCache","createPackageReloadCheck","rootCompositions","loadMajorModesConfig","resolveLayers","serverPackageCandidates","mcpPluginCandidates","compositionPackageRoots"],"sources":["../../../../src/builders/server/bundlerService.ts"],"sourcesContent":["import { loadMajorModesConfig, resolveLayers } from '@agimon-ai/po-config/majorModes';\nimport type { LiveMcpPluginEntry } from '@agimon-ai/po-core/mcpFacet';\nimport type { PoApiScope } from '@agimon-ai/po-core/packageApi';\nimport type { LiveServerPackageEntry } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { ExtensionComposition } from '../cli/extensionAssembler';\nimport { rootCompositions } from '../cli/sessionComposition';\nimport { compositionPackageRoots } from '../web';\nimport { serverPackageCandidates } from './index';\nimport { type McpPluginCandidate, mcpPluginCandidates } from './mcpBundle';\nimport { createLiveModuleCache, createPackageReloadCheck, type ScopeChange } from './packageReloadCheck';\nimport { createPackageUrls } from './packageUrl';\n\nexport interface BundlerServiceOptions {\n readonly homeDirectory: string;\n readonly environment: NodeJS.ProcessEnv;\n /** Called after a root's compositions are resolved, so the caller can watch what they read. */\n readonly onCompositions?: (root: string, compositions: readonly ExtensionComposition[]) => void;\n}\n\nexport interface RootSelection {\n readonly root: string;\n readonly majorMode: string;\n readonly activeLayers: string[];\n}\n\ninterface RootMcp {\n readonly candidates: McpPluginCandidate[];\n readonly entries: LiveMcpPluginEntry[];\n}\n\ninterface RootMemo {\n compositions?: ExtensionComposition[];\n selection?: RootSelection;\n packageEntries?: Promise<LiveServerPackageEntry[]>;\n mcp?: Promise<RootMcp>;\n}\n\n/**\n * Process-level owner of what Po compiles and loads: content-addressed package URLs, the\n * live module cache, the poison set, per-scope reload tracking, and a per-root memo of the\n * compositions and entries a root selects. Every reader shares one computation until\n * `invalidate`; the caller decides when (a finished sync or a watched change).\n */\nexport function createBundlerService(options: BundlerServiceOptions) {\n /** Content-addressed import URLs for every Po package entry this server loads. */\n const packageUrls = createPackageUrls();\n /** URLs whose live import timed out or threw; skipped by every loader until the content changes. */\n const poisoned = new Set<string>();\n /** Imports each content-addressed URL once, so a reload imports only what changed. */\n const importer = createLiveModuleCache();\n /** The package identities each mounted global and workspace scope last loaded. */\n const reloadCheck = createPackageReloadCheck();\n const memos = new Map<string, RootMemo>();\n const memoFor = (root: string): RootMemo => {\n let memo = memos.get(root);\n if (memo === undefined) {\n memo = {};\n memos.set(root, memo);\n }\n return memo;\n };\n /** A pending load shared by every reader; a failed one is dropped so the next reader retries. */\n const shared = <T>(\n read: () => Promise<T> | undefined,\n write: (value: Promise<T> | undefined) => void,\n load: () => Promise<T>,\n ) => {\n const existing = read();\n if (existing !== undefined) return existing;\n const loading = load();\n write(loading);\n loading.catch(() => {\n if (read() === loading) write(undefined);\n });\n return loading;\n };\n\n /**\n * Every composition the root's config can select, one per major mode, resolved live.\n * A package sync has not prepared yet is left out with a diagnostic.\n */\n const compositions = (root: string): ExtensionComposition[] => {\n const memo = memoFor(root);\n if (memo.compositions === undefined) {\n memo.compositions = rootCompositions({\n root,\n homeDirectory: options.homeDirectory,\n environment: options.environment,\n tolerateUnsynced: true,\n });\n options.onCompositions?.(root, memo.compositions);\n }\n return memo.compositions;\n };\n\n const defaultSelection = (root: string): RootSelection => {\n const memo = memoFor(root);\n if (memo.selection === undefined) {\n const config = loadMajorModesConfig(root, options.homeDirectory, options.environment);\n const majorMode = config.defaultMajorMode;\n memo.selection = { root, majorMode, activeLayers: resolveLayers(config, majorMode) };\n }\n return memo.selection;\n };\n\n /** The root's server package entries, each with the URL its current package content imports from. */\n const packageEntries = (root: string): Promise<LiveServerPackageEntry[]> => {\n const memo = memoFor(root);\n return shared(\n () => memo.packageEntries,\n (value) => (memo.packageEntries = value),\n async () => {\n const entries: LiveServerPackageEntry[] = [];\n for (const { declaration, module, owners } of serverPackageCandidates(compositions(root)))\n entries.push({\n packageName: declaration.packageName,\n entry: declaration.entry,\n module,\n url: await packageUrls.urlFor(module),\n scopes: declaration.scopes,\n owners,\n required: declaration.required === true,\n });\n return entries;\n },\n );\n };\n\n /** The root's MCP plugin candidates and their entries with current URLs. */\n const mcpEntries = (root: string): Promise<RootMcp> => {\n const memo = memoFor(root);\n return shared(\n () => memo.mcp,\n (value) => (memo.mcp = value),\n async () => {\n const candidates = mcpPluginCandidates(compositions(root));\n const entries: LiveMcpPluginEntry[] = [];\n for (const { declaration, module, owners } of candidates)\n entries.push({\n packageName: declaration.packageName,\n ...(declaration.ui === undefined ? {} : { widgets: declaration.ui.widgets }),\n entry: declaration.entry,\n module,\n url: await packageUrls.urlFor(module),\n owners,\n });\n return { candidates, entries };\n },\n );\n };\n\n /**\n * What a global or workspace scope loads: one identity per server package entry in that scope.\n * The URL changes with package content; `selection` carries ownership and the root's\n * default mode, so a modes.yaml change that selects a package in or out is reloaded too.\n */\n const scopeIdentities = (root: string, scope: PoApiScope, entries: readonly LiveServerPackageEntry[]) => {\n const { majorMode, activeLayers } = defaultSelection(root);\n return entries\n .filter((entry) => entry.scopes.includes(scope))\n .map((entry) => ({\n module: entry.module,\n url: entry.url,\n selection: JSON.stringify([entry.owners, entry.required === true, majorMode, activeLayers]),\n }));\n };\n\n return {\n packageUrls,\n poisoned,\n importer,\n compositions,\n defaultSelection,\n packageEntries,\n mcpEntries,\n /** Every package root the root's config can select, over all its major modes or only `majorMode`'s. */\n packageRoots: (root: string, majorMode?: string): string[] =>\n compositionPackageRoots(\n compositions(root).filter(\n (composition) => majorMode === undefined || composition.majorMode?.name === majorMode,\n ),\n ),\n /** How the scope's current entries differ from what it last committed. */\n diffScope: async (key: string, root: string, scope: PoApiScope): Promise<ScopeChange> =>\n reloadCheck.diff(key, scopeIdentities(root, scope, await packageEntries(root))),\n /** Records what a scope installed, so the next diff compares against it. */\n commitScope: (key: string, root: string, scope: PoApiScope, entries: readonly LiveServerPackageEntry[]): void =>\n reloadCheck.commit(key, scopeIdentities(root, scope, entries)),\n forgetScope: (key: string): void => reloadCheck.forget(key),\n /** Drops every root's memo; the next reader recomputes from disk. */\n invalidate: (): void => memos.clear(),\n };\n}\n\nexport type BundlerService = ReturnType<typeof createBundlerService>;\n"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ import { createPackageUrls } from "./packageUrl.mjs";
2
+ import { compositionPackageRoots } from "../web/index.mjs";
3
+ import { rootCompositions } from "../cli/sessionComposition.mjs";
4
+ import { mcpPluginCandidates } from "./mcpBundle.mjs";
5
+ import { serverPackageCandidates } from "./index.mjs";
6
+ import { createLiveModuleCache, createPackageReloadCheck } from "./packageReloadCheck.mjs";
7
+ import { loadMajorModesConfig, resolveLayers } from "@agimon-ai/po-config/majorModes";
8
+ //#region src/builders/server/bundlerService.ts
9
+ /**
10
+ * Process-level owner of what Po compiles and loads: content-addressed package URLs, the
11
+ * live module cache, the poison set, per-scope reload tracking, and a per-root memo of the
12
+ * compositions and entries a root selects. Every reader shares one computation until
13
+ * `invalidate`; the caller decides when (a finished sync or a watched change).
14
+ */
15
+ function createBundlerService(options) {
16
+ /** Content-addressed import URLs for every Po package entry this server loads. */
17
+ const packageUrls = createPackageUrls();
18
+ /** URLs whose live import timed out or threw; skipped by every loader until the content changes. */
19
+ const poisoned = /* @__PURE__ */ new Set();
20
+ /** Imports each content-addressed URL once, so a reload imports only what changed. */
21
+ const importer = createLiveModuleCache();
22
+ /** The package identities each mounted global and workspace scope last loaded. */
23
+ const reloadCheck = createPackageReloadCheck();
24
+ const memos = /* @__PURE__ */ new Map();
25
+ const memoFor = (root) => {
26
+ let memo = memos.get(root);
27
+ if (memo === void 0) {
28
+ memo = {};
29
+ memos.set(root, memo);
30
+ }
31
+ return memo;
32
+ };
33
+ /** A pending load shared by every reader; a failed one is dropped so the next reader retries. */
34
+ const shared = (read, write, load) => {
35
+ const existing = read();
36
+ if (existing !== void 0) return existing;
37
+ const loading = load();
38
+ write(loading);
39
+ loading.catch(() => {
40
+ if (read() === loading) write(void 0);
41
+ });
42
+ return loading;
43
+ };
44
+ /**
45
+ * Every composition the root's config can select, one per major mode, resolved live.
46
+ * A package sync has not prepared yet is left out with a diagnostic.
47
+ */
48
+ const compositions = (root) => {
49
+ const memo = memoFor(root);
50
+ if (memo.compositions === void 0) {
51
+ memo.compositions = rootCompositions({
52
+ root,
53
+ homeDirectory: options.homeDirectory,
54
+ environment: options.environment,
55
+ tolerateUnsynced: true
56
+ });
57
+ options.onCompositions?.(root, memo.compositions);
58
+ }
59
+ return memo.compositions;
60
+ };
61
+ const defaultSelection = (root) => {
62
+ const memo = memoFor(root);
63
+ if (memo.selection === void 0) {
64
+ const config = loadMajorModesConfig(root, options.homeDirectory, options.environment);
65
+ const majorMode = config.defaultMajorMode;
66
+ memo.selection = {
67
+ root,
68
+ majorMode,
69
+ activeLayers: resolveLayers(config, majorMode)
70
+ };
71
+ }
72
+ return memo.selection;
73
+ };
74
+ /** The root's server package entries, each with the URL its current package content imports from. */
75
+ const packageEntries = (root) => {
76
+ const memo = memoFor(root);
77
+ return shared(() => memo.packageEntries, (value) => memo.packageEntries = value, async () => {
78
+ const entries = [];
79
+ for (const { declaration, module, owners } of serverPackageCandidates(compositions(root))) entries.push({
80
+ packageName: declaration.packageName,
81
+ entry: declaration.entry,
82
+ module,
83
+ url: await packageUrls.urlFor(module),
84
+ scopes: declaration.scopes,
85
+ owners,
86
+ required: declaration.required === true
87
+ });
88
+ return entries;
89
+ });
90
+ };
91
+ /** The root's MCP plugin candidates and their entries with current URLs. */
92
+ const mcpEntries = (root) => {
93
+ const memo = memoFor(root);
94
+ return shared(() => memo.mcp, (value) => memo.mcp = value, async () => {
95
+ const candidates = mcpPluginCandidates(compositions(root));
96
+ const entries = [];
97
+ for (const { declaration, module, owners } of candidates) entries.push({
98
+ packageName: declaration.packageName,
99
+ ...declaration.ui === void 0 ? {} : { widgets: declaration.ui.widgets },
100
+ entry: declaration.entry,
101
+ module,
102
+ url: await packageUrls.urlFor(module),
103
+ owners
104
+ });
105
+ return {
106
+ candidates,
107
+ entries
108
+ };
109
+ });
110
+ };
111
+ /**
112
+ * What a global or workspace scope loads: one identity per server package entry in that scope.
113
+ * The URL changes with package content; `selection` carries ownership and the root's
114
+ * default mode, so a modes.yaml change that selects a package in or out is reloaded too.
115
+ */
116
+ const scopeIdentities = (root, scope, entries) => {
117
+ const { majorMode, activeLayers } = defaultSelection(root);
118
+ return entries.filter((entry) => entry.scopes.includes(scope)).map((entry) => ({
119
+ module: entry.module,
120
+ url: entry.url,
121
+ selection: JSON.stringify([
122
+ entry.owners,
123
+ entry.required === true,
124
+ majorMode,
125
+ activeLayers
126
+ ])
127
+ }));
128
+ };
129
+ return {
130
+ packageUrls,
131
+ poisoned,
132
+ importer,
133
+ compositions,
134
+ defaultSelection,
135
+ packageEntries,
136
+ mcpEntries,
137
+ /** Every package root the root's config can select, over all its major modes or only `majorMode`'s. */
138
+ packageRoots: (root, majorMode) => compositionPackageRoots(compositions(root).filter((composition) => majorMode === void 0 || composition.majorMode?.name === majorMode)),
139
+ /** How the scope's current entries differ from what it last committed. */
140
+ diffScope: async (key, root, scope) => reloadCheck.diff(key, scopeIdentities(root, scope, await packageEntries(root))),
141
+ /** Records what a scope installed, so the next diff compares against it. */
142
+ commitScope: (key, root, scope, entries) => reloadCheck.commit(key, scopeIdentities(root, scope, entries)),
143
+ forgetScope: (key) => reloadCheck.forget(key),
144
+ /** Drops every root's memo; the next reader recomputes from disk. */
145
+ invalidate: () => memos.clear()
146
+ };
147
+ }
148
+ //#endregion
149
+ export { createBundlerService };
150
+
151
+ //# sourceMappingURL=bundlerService.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundlerService.mjs","names":[],"sources":["../../../../src/builders/server/bundlerService.ts"],"sourcesContent":["import { loadMajorModesConfig, resolveLayers } from '@agimon-ai/po-config/majorModes';\nimport type { LiveMcpPluginEntry } from '@agimon-ai/po-core/mcpFacet';\nimport type { PoApiScope } from '@agimon-ai/po-core/packageApi';\nimport type { LiveServerPackageEntry } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { ExtensionComposition } from '../cli/extensionAssembler';\nimport { rootCompositions } from '../cli/sessionComposition';\nimport { compositionPackageRoots } from '../web';\nimport { serverPackageCandidates } from './index';\nimport { type McpPluginCandidate, mcpPluginCandidates } from './mcpBundle';\nimport { createLiveModuleCache, createPackageReloadCheck, type ScopeChange } from './packageReloadCheck';\nimport { createPackageUrls } from './packageUrl';\n\nexport interface BundlerServiceOptions {\n readonly homeDirectory: string;\n readonly environment: NodeJS.ProcessEnv;\n /** Called after a root's compositions are resolved, so the caller can watch what they read. */\n readonly onCompositions?: (root: string, compositions: readonly ExtensionComposition[]) => void;\n}\n\nexport interface RootSelection {\n readonly root: string;\n readonly majorMode: string;\n readonly activeLayers: string[];\n}\n\ninterface RootMcp {\n readonly candidates: McpPluginCandidate[];\n readonly entries: LiveMcpPluginEntry[];\n}\n\ninterface RootMemo {\n compositions?: ExtensionComposition[];\n selection?: RootSelection;\n packageEntries?: Promise<LiveServerPackageEntry[]>;\n mcp?: Promise<RootMcp>;\n}\n\n/**\n * Process-level owner of what Po compiles and loads: content-addressed package URLs, the\n * live module cache, the poison set, per-scope reload tracking, and a per-root memo of the\n * compositions and entries a root selects. Every reader shares one computation until\n * `invalidate`; the caller decides when (a finished sync or a watched change).\n */\nexport function createBundlerService(options: BundlerServiceOptions) {\n /** Content-addressed import URLs for every Po package entry this server loads. */\n const packageUrls = createPackageUrls();\n /** URLs whose live import timed out or threw; skipped by every loader until the content changes. */\n const poisoned = new Set<string>();\n /** Imports each content-addressed URL once, so a reload imports only what changed. */\n const importer = createLiveModuleCache();\n /** The package identities each mounted global and workspace scope last loaded. */\n const reloadCheck = createPackageReloadCheck();\n const memos = new Map<string, RootMemo>();\n const memoFor = (root: string): RootMemo => {\n let memo = memos.get(root);\n if (memo === undefined) {\n memo = {};\n memos.set(root, memo);\n }\n return memo;\n };\n /** A pending load shared by every reader; a failed one is dropped so the next reader retries. */\n const shared = <T>(\n read: () => Promise<T> | undefined,\n write: (value: Promise<T> | undefined) => void,\n load: () => Promise<T>,\n ) => {\n const existing = read();\n if (existing !== undefined) return existing;\n const loading = load();\n write(loading);\n loading.catch(() => {\n if (read() === loading) write(undefined);\n });\n return loading;\n };\n\n /**\n * Every composition the root's config can select, one per major mode, resolved live.\n * A package sync has not prepared yet is left out with a diagnostic.\n */\n const compositions = (root: string): ExtensionComposition[] => {\n const memo = memoFor(root);\n if (memo.compositions === undefined) {\n memo.compositions = rootCompositions({\n root,\n homeDirectory: options.homeDirectory,\n environment: options.environment,\n tolerateUnsynced: true,\n });\n options.onCompositions?.(root, memo.compositions);\n }\n return memo.compositions;\n };\n\n const defaultSelection = (root: string): RootSelection => {\n const memo = memoFor(root);\n if (memo.selection === undefined) {\n const config = loadMajorModesConfig(root, options.homeDirectory, options.environment);\n const majorMode = config.defaultMajorMode;\n memo.selection = { root, majorMode, activeLayers: resolveLayers(config, majorMode) };\n }\n return memo.selection;\n };\n\n /** The root's server package entries, each with the URL its current package content imports from. */\n const packageEntries = (root: string): Promise<LiveServerPackageEntry[]> => {\n const memo = memoFor(root);\n return shared(\n () => memo.packageEntries,\n (value) => (memo.packageEntries = value),\n async () => {\n const entries: LiveServerPackageEntry[] = [];\n for (const { declaration, module, owners } of serverPackageCandidates(compositions(root)))\n entries.push({\n packageName: declaration.packageName,\n entry: declaration.entry,\n module,\n url: await packageUrls.urlFor(module),\n scopes: declaration.scopes,\n owners,\n required: declaration.required === true,\n });\n return entries;\n },\n );\n };\n\n /** The root's MCP plugin candidates and their entries with current URLs. */\n const mcpEntries = (root: string): Promise<RootMcp> => {\n const memo = memoFor(root);\n return shared(\n () => memo.mcp,\n (value) => (memo.mcp = value),\n async () => {\n const candidates = mcpPluginCandidates(compositions(root));\n const entries: LiveMcpPluginEntry[] = [];\n for (const { declaration, module, owners } of candidates)\n entries.push({\n packageName: declaration.packageName,\n ...(declaration.ui === undefined ? {} : { widgets: declaration.ui.widgets }),\n entry: declaration.entry,\n module,\n url: await packageUrls.urlFor(module),\n owners,\n });\n return { candidates, entries };\n },\n );\n };\n\n /**\n * What a global or workspace scope loads: one identity per server package entry in that scope.\n * The URL changes with package content; `selection` carries ownership and the root's\n * default mode, so a modes.yaml change that selects a package in or out is reloaded too.\n */\n const scopeIdentities = (root: string, scope: PoApiScope, entries: readonly LiveServerPackageEntry[]) => {\n const { majorMode, activeLayers } = defaultSelection(root);\n return entries\n .filter((entry) => entry.scopes.includes(scope))\n .map((entry) => ({\n module: entry.module,\n url: entry.url,\n selection: JSON.stringify([entry.owners, entry.required === true, majorMode, activeLayers]),\n }));\n };\n\n return {\n packageUrls,\n poisoned,\n importer,\n compositions,\n defaultSelection,\n packageEntries,\n mcpEntries,\n /** Every package root the root's config can select, over all its major modes or only `majorMode`'s. */\n packageRoots: (root: string, majorMode?: string): string[] =>\n compositionPackageRoots(\n compositions(root).filter(\n (composition) => majorMode === undefined || composition.majorMode?.name === majorMode,\n ),\n ),\n /** How the scope's current entries differ from what it last committed. */\n diffScope: async (key: string, root: string, scope: PoApiScope): Promise<ScopeChange> =>\n reloadCheck.diff(key, scopeIdentities(root, scope, await packageEntries(root))),\n /** Records what a scope installed, so the next diff compares against it. */\n commitScope: (key: string, root: string, scope: PoApiScope, entries: readonly LiveServerPackageEntry[]): void =>\n reloadCheck.commit(key, scopeIdentities(root, scope, entries)),\n forgetScope: (key: string): void => reloadCheck.forget(key),\n /** Drops every root's memo; the next reader recomputes from disk. */\n invalidate: (): void => memos.clear(),\n };\n}\n\nexport type BundlerService = ReturnType<typeof createBundlerService>;\n"],"mappings":"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"}
@@ -30,11 +30,11 @@ async function createComputerUseBinding(transport = process, remotePolicy = {})
30
30
  return available && /^[a-f0-9]{64}$/u.test(proof) && (0, node_crypto.timingSafeEqual)(Buffer.from(proof), Buffer.from(token));
31
31
  };
32
32
  const remoteCaller = (headers) => {
33
- const caller = (0, _agimon_ai_po_core_packageApi.doomApiCallerFrom)(headers);
33
+ const caller = (0, _agimon_ai_po_core_packageApi.poApiCallerFrom)(headers);
34
34
  return caller?.locality === "remote" && remotePolicy.isDeviceAuthorized?.(caller.deviceId) === true ? caller : void 0;
35
35
  };
36
36
  const notify = (sessionId) => {
37
- for (const [listener, session] of listeners) if (sessionId === void 0 || session === sessionId) listener();
37
+ for (const [listener, session] of listeners) if (sessionId === void 0 || session === void 0 || session === sessionId) listener();
38
38
  };
39
39
  const revoke = (sessionId, retainRecording = false) => {
40
40
  const delegated = delegations.get(sessionId);
@@ -1 +1 @@
1
- {"version":3,"file":"computerUseBinding.cjs","names":["ComputerUseRecordingStore","fs","path","os","timingSafeEqual","doomApiCallerFrom","randomUUID"],"sources":["../../../../src/builders/server/computerUseBinding.ts"],"sourcesContent":["import { randomUUID, timingSafeEqual } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { DoomComputerUseHostBinding } from '@agimon-ai/po-core/hubChannel';\nimport { doomApiCallerFrom } from '@agimon-ai/po-core/packageApi';\n\nimport { ComputerUseRecordingStore } from './computerUseRecordings';\n\nconst VERSION = 1;\nconst MAX_BYTES = 8 * 1024 * 1024;\nconst DESKTOP_HEADER = 'x-po-desktop';\ntype Transport = Pick<NodeJS.Process, 'send' | 'connected' | 'on' | 'off'>;\n\n/** The capability is discovered over the inherited Desktop IPC channel, never from environment flags. */\nexport async function createComputerUseBinding(\n transport: Transport = process,\n remotePolicy: {\n isDeviceAuthorized?: (deviceId: string) => boolean;\n stepUpRequired?: () => boolean;\n } = {},\n): Promise<DoomComputerUseHostBinding | undefined> {\n if (!transport.send || !transport.connected) return undefined;\n const recordings = new ComputerUseRecordingStore(await fs.mkdtemp(path.join(os.tmpdir(), 'po-recordings-')));\n let available = false;\n let enabled = false;\n let closed = false;\n let token = '';\n let generation: string | undefined;\n const ownedSessions = new Set<string>();\n const revokedSessions = new Set<string>();\n const listeners = new Map<() => void, string | undefined>();\n const delegations = new Map<\n string,\n { deviceId: string; approved: boolean; abort: AbortController; grantId?: string; expiresAt?: number }\n >();\n const nativeAuthorized = (headers: Headers): boolean => {\n const proof = headers.get(DESKTOP_HEADER) ?? '';\n return available && /^[a-f0-9]{64}$/u.test(proof) && timingSafeEqual(Buffer.from(proof), Buffer.from(token));\n };\n const remoteCaller = (headers: Headers) => {\n const caller = doomApiCallerFrom(headers);\n return caller?.locality === 'remote' && remotePolicy.isDeviceAuthorized?.(caller.deviceId) === true\n ? caller\n : undefined;\n };\n const notify = (sessionId?: string): void => {\n for (const [listener, session] of listeners) if (sessionId === undefined || session === sessionId) listener();\n };\n const revoke = (sessionId: string, retainRecording = false): void => {\n const delegated = delegations.get(sessionId);\n if (delegated === undefined) return;\n if (!retainRecording) delegations.delete(sessionId);\n delegated.expiresAt = undefined;\n delegated.abort.abort();\n revokedSessions.add(sessionId);\n if (delegated.grantId !== undefined && available) {\n void exchange(\n { type: 'po:computer-use:request', sessionId, operation: 'stop', payload: { grantId: delegated.grantId } },\n 30_000,\n )\n .then((result) => recordings.importStop(sessionId, delegated.grantId!, result.result))\n .catch(() => close());\n }\n notify(sessionId);\n };\n const pending = new Map<string, { finish: (value?: Record<string, unknown>, error?: Error) => void }>();\n const send = (value: object): void => {\n if (closed || !transport.connected) throw new Error('Desktop computer use is disconnected.');\n transport.send!(value, (error: Error | null) => {\n if (error) close();\n });\n };\n const close = (): void => {\n if (closed) return;\n closed = true;\n available = false;\n token = '';\n transport.off('message', onMessage);\n transport.off('disconnect', close);\n transport.off('exit', close);\n for (const request of pending.values())\n request.finish(undefined, new Error('Desktop computer use is disconnected.'));\n pending.clear();\n for (const sessionId of delegations.keys()) revoke(sessionId);\n notify();\n listeners.clear();\n void recordings.close().catch(() => process.emitWarning('Computer-use recording cleanup failed.'));\n // Keep ownership pinned while the server is alive, including after Desktop disconnects.\n };\n const onMessage = (value: unknown): void => {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return;\n const message = value as Record<string, unknown>;\n if (message.version !== VERSION) return;\n if (message.type === 'po:computer-use:availability' && typeof message.enabled === 'boolean') {\n enabled = message.enabled;\n // Native stop/expiry also notifies availability without changing the global setting.\n for (const sessionId of delegations.keys()) revoke(sessionId, true);\n notify();\n return;\n }\n if (typeof message.requestId !== 'string') return;\n if (message.type !== 'po:computer-use:ready' && message.type !== 'po:computer-use:response') return;\n if (Buffer.byteLength(JSON.stringify(message)) > MAX_BYTES) {\n close();\n return;\n }\n pending.get(message.requestId)?.finish(message);\n };\n transport.on('message', onMessage);\n transport.on('disconnect', close);\n transport.on('exit', close);\n\n const exchange = (\n value: Record<string, unknown>,\n timeoutMs: number,\n signal?: AbortSignal,\n ): Promise<Record<string, unknown>> =>\n new Promise((resolve, reject) => {\n signal?.throwIfAborted();\n if (closed || pending.size >= 128) throw new Error('Desktop computer-use transport is unavailable or busy.');\n const requestId = randomUUID();\n const cancel = (): void => {\n try {\n send({ type: 'po:computer-use:cancel', version: VERSION, requestId });\n } catch {\n /* Already disconnected. */\n }\n };\n const finish = (result?: Record<string, unknown>, error?: Error): void => {\n if (!pending.delete(requestId)) return;\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n if (error) reject(error);\n else resolve(result!);\n };\n const onAbort = (): void => {\n cancel();\n finish(undefined, new Error('Desktop computer-use request was cancelled.'));\n };\n const timer = setTimeout(() => {\n cancel();\n finish(undefined, new Error('Desktop computer-use request timed out.'));\n }, timeoutMs);\n pending.set(requestId, { finish });\n signal?.addEventListener('abort', onAbort, { once: true });\n try {\n const message = { ...value, version: VERSION, requestId };\n if (Buffer.byteLength(JSON.stringify(message)) > MAX_BYTES)\n throw new Error('Desktop computer-use request is too large.');\n send(message);\n } catch (error) {\n finish(undefined, error instanceof Error ? error : new Error(String(error)));\n }\n });\n\n try {\n const ready = await exchange({ type: 'po:computer-use:hello' }, 1_500);\n if (\n ready.type !== 'po:computer-use:ready' ||\n typeof ready.token !== 'string' ||\n !/^[a-f0-9]{64}$/u.test(ready.token)\n ) {\n throw new Error('Invalid Desktop capability handshake.');\n }\n token = ready.token;\n available = true;\n enabled = ready.enabled === true;\n } catch {\n close();\n return undefined;\n }\n\n return {\n get available() {\n return available;\n },\n get enabled() {\n return available && enabled;\n },\n authorize: nativeAuthorized,\n readRecording: (scope, artifactId, range) => recordings.read(scope.sessionId, artifactId, range),\n authorizeActivation(headers) {\n if (!available || !enabled) return false;\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n return (\n caller !== undefined &&\n (remotePolicy.stepUpRequired?.() === true ? caller.stepUp === 'verified' : caller.stepUp === 'not-required')\n );\n },\n authorizePending(sessionId, headers) {\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n const delegated = delegations.get(sessionId);\n return (\n available &&\n enabled &&\n caller !== undefined &&\n delegated !== undefined &&\n delegated.deviceId === caller.deviceId &&\n delegated.expiresAt === undefined &&\n !delegated.abort.signal.aborted\n );\n },\n authorizeRecording(sessionId, headers) {\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n const delegated = delegations.get(sessionId);\n return (\n available && caller !== undefined && delegated?.approved === true && delegated.deviceId === caller.deviceId\n );\n },\n authorizeSession(sessionId, headers) {\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n const delegated = delegations.get(sessionId);\n if (available && enabled && caller !== undefined && !ownedSessions.has(sessionId)) return true;\n return (\n available &&\n enabled &&\n caller !== undefined &&\n delegated?.approved === true &&\n delegated.expiresAt !== undefined &&\n delegated.expiresAt > Date.now() &&\n delegated.deviceId === caller.deviceId &&\n !delegated.abort.signal.aborted\n );\n },\n claimSession(sessionId, headers) {\n if (!available || !enabled) throw new Error('Desktop computer use is unavailable.');\n const caller = headers === undefined ? undefined : remoteCaller(headers);\n if (caller !== undefined) {\n const previous = delegations.get(sessionId);\n if (previous !== undefined && previous.deviceId !== caller.deviceId)\n throw new Error('This session belongs to another paired device.');\n if (previous?.abort.signal.aborted) {\n previous.abort = new AbortController();\n previous.grantId = undefined;\n }\n if (previous === undefined)\n delegations.set(sessionId, {\n deviceId: caller.deviceId,\n approved: false,\n abort: new AbortController(),\n });\n } else if (headers !== undefined && !nativeAuthorized(headers)) {\n throw new Error('Native or paired authorization is required.');\n }\n revokedSessions.delete(sessionId);\n ownedSessions.add(sessionId);\n },\n revokeDevice(deviceId) {\n for (const [sessionId, delegated] of delegations) if (delegated.deviceId === deviceId) revoke(sessionId);\n },\n ownsSession: (sessionId) => ownedSessions.has(sessionId),\n forgetSession: (sessionId) => {\n revoke(sessionId);\n void recordings\n .forgetSession(sessionId)\n .catch(() => process.emitWarning('Computer-use recording cleanup failed.'));\n // Keep private history pinned to Desktop, but never replay device approval after restart.\n },\n subscribe(listener, sessionId) {\n listeners.set(listener, sessionId);\n return () => {\n listeners.delete(listener);\n };\n },\n async request(scope, request) {\n if (!available) throw new Error('Desktop computer use is unavailable.');\n if (revokedSessions.has(scope.sessionId) && request.operation !== 'stop' && request.operation !== 'status')\n throw new Error('Computer-use delegation was revoked.');\n if (request.operation !== 'status' && request.operation !== 'targets' && !ownedSessions.has(scope.sessionId)) {\n throw new Error('This session has not been authorized by Desktop.');\n }\n const delegated = delegations.get(scope.sessionId);\n if (delegated !== undefined && remotePolicy.isDeviceAuthorized?.(delegated.deviceId) !== true) {\n revoke(scope.sessionId);\n if (request.operation !== 'stop') throw new Error('The paired device was revoked.');\n }\n if (request.operation === 'stop' && delegated !== undefined) {\n delegated.expiresAt = undefined;\n delegated.abort.abort();\n }\n const signal =\n delegated === undefined || request.operation === 'stop'\n ? request.signal\n : request.signal === undefined\n ? delegated.abort.signal\n : AbortSignal.any([request.signal, delegated.abort.signal]);\n const result = await exchange(\n {\n type: 'po:computer-use:request',\n sessionId: scope.sessionId,\n operation: request.operation,\n ...(request.payload === undefined ? {} : { payload: request.payload }),\n },\n request.operation === 'activate' ? 120_000 : 30_000,\n signal,\n );\n if (\n typeof result.hostGeneration !== 'string' ||\n (generation !== undefined && generation !== result.hostGeneration)\n ) {\n close();\n throw new Error('Desktop generation changed. Authorize a new session.');\n }\n generation = result.hostGeneration;\n if (result.ok !== true)\n throw new Error(typeof result.error === 'string' ? result.error : 'Desktop request failed.');\n if (request.operation === 'activate' && delegated !== undefined) {\n if (\n delegations.get(scope.sessionId) !== delegated ||\n delegated.abort.signal.aborted ||\n remotePolicy.isDeviceAuthorized?.(delegated.deviceId) !== true\n )\n throw new Error('The paired activation was revoked.');\n delegated.approved = true;\n const grant = result.result as { grantId?: unknown; expiresAt?: unknown } | undefined;\n if (typeof grant?.expiresAt === 'number') delegated.expiresAt = grant.expiresAt;\n if (typeof grant?.grantId === 'string') delegated.grantId = grant.grantId;\n }\n if (request.operation === 'stop') {\n if (delegated !== undefined) delegated.expiresAt = undefined;\n const grantId = (request.payload as { grantId?: unknown } | undefined)?.grantId;\n if (typeof grantId !== 'string') throw new Error('Recording finalization requires its grant.');\n return recordings.importStop(scope.sessionId, grantId, result.result);\n }\n return result.result;\n },\n close() {\n if (closed) return;\n try {\n send({ type: 'po:computer-use:close', version: VERSION });\n } finally {\n close();\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AAUA,MAAM,UAAU;AAChB,MAAM,YAAY;AAClB,MAAM,iBAAiB;;AAIvB,eAAsB,yBACpB,YAAuB,SACvB,eAGI,CAAC,GAC4C;CACjD,IAAI,CAAC,UAAU,QAAQ,CAAC,UAAU,WAAW,OAAO,KAAA;CACpD,MAAM,aAAa,IAAIA,8BAAAA,0BAA0B,MAAMC,iBAAAA,QAAG,QAAQC,UAAAA,QAAK,KAAKC,QAAAA,QAAG,OAAO,GAAG,gBAAgB,CAAC,CAAC;CAC3G,IAAI,YAAY;CAChB,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI,QAAQ;CACZ,IAAI;CACJ,MAAM,gCAAgB,IAAI,IAAY;CACtC,MAAM,kCAAkB,IAAI,IAAY;CACxC,MAAM,4BAAY,IAAI,IAAoC;CAC1D,MAAM,8BAAc,IAAI,IAGtB;CACF,MAAM,oBAAoB,YAA8B;EACtD,MAAM,QAAQ,QAAQ,IAAI,cAAc,KAAK;EAC7C,OAAO,aAAa,kBAAkB,KAAK,KAAK,MAAA,GAAKC,YAAAA,gBAAAA,CAAgB,OAAO,KAAK,KAAK,GAAG,OAAO,KAAK,KAAK,CAAC;CAC7G;CACA,MAAM,gBAAgB,YAAqB;EACzC,MAAM,UAAA,GAASC,8BAAAA,kBAAAA,CAAkB,OAAO;EACxC,OAAO,QAAQ,aAAa,YAAY,aAAa,qBAAqB,OAAO,QAAQ,MAAM,OAC3F,SACA,KAAA;CACN;CACA,MAAM,UAAU,cAA6B;EAC3C,KAAK,MAAM,CAAC,UAAU,YAAY,WAAW,IAAI,cAAc,KAAA,KAAa,YAAY,WAAW,SAAS;CAC9G;CACA,MAAM,UAAU,WAAmB,kBAAkB,UAAgB;EACnE,MAAM,YAAY,YAAY,IAAI,SAAS;EAC3C,IAAI,cAAc,KAAA,GAAW;EAC7B,IAAI,CAAC,iBAAiB,YAAY,OAAO,SAAS;EAClD,UAAU,YAAY,KAAA;EACtB,UAAU,MAAM,MAAM;EACtB,gBAAgB,IAAI,SAAS;EAC7B,IAAI,UAAU,YAAY,KAAA,KAAa,WACrC,SACE;GAAE,MAAM;GAA2B;GAAW,WAAW;GAAQ,SAAS,EAAE,SAAS,UAAU,QAAQ;EAAE,GACzG,GACF,CAAC,CACE,MAAM,WAAW,WAAW,WAAW,WAAW,UAAU,SAAU,OAAO,MAAM,CAAC,CAAC,CACrF,YAAY,MAAM,CAAC;EAExB,OAAO,SAAS;CAClB;CACA,MAAM,0BAAU,IAAI,IAAkF;CACtG,MAAM,QAAQ,UAAwB;EACpC,IAAI,UAAU,CAAC,UAAU,WAAW,MAAM,IAAI,MAAM,uCAAuC;EAC3F,UAAU,KAAM,QAAQ,UAAwB;GAC9C,IAAI,OAAO,MAAM;EACnB,CAAC;CACH;CACA,MAAM,cAAoB;EACxB,IAAI,QAAQ;EACZ,SAAS;EACT,YAAY;EACZ,QAAQ;EACR,UAAU,IAAI,WAAW,SAAS;EAClC,UAAU,IAAI,cAAc,KAAK;EACjC,UAAU,IAAI,QAAQ,KAAK;EAC3B,KAAK,MAAM,WAAW,QAAQ,OAAO,GACnC,QAAQ,OAAO,KAAA,mBAAW,IAAI,MAAM,uCAAuC,CAAC;EAC9E,QAAQ,MAAM;EACd,KAAK,MAAM,aAAa,YAAY,KAAK,GAAG,OAAO,SAAS;EAC5D,OAAO;EACP,UAAU,MAAM;EAChB,WAAgB,MAAM,CAAC,CAAC,YAAY,QAAQ,YAAY,wCAAwC,CAAC;CAEnG;CACA,MAAM,aAAa,UAAyB;EAC1C,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EACjE,MAAM,UAAU;EAChB,IAAI,QAAQ,YAAY,SAAS;EACjC,IAAI,QAAQ,SAAS,kCAAkC,OAAO,QAAQ,YAAY,WAAW;GAC3F,UAAU,QAAQ;GAElB,KAAK,MAAM,aAAa,YAAY,KAAK,GAAG,OAAO,WAAW,IAAI;GAClE,OAAO;GACP;EACF;EACA,IAAI,OAAO,QAAQ,cAAc,UAAU;EAC3C,IAAI,QAAQ,SAAS,2BAA2B,QAAQ,SAAS,4BAA4B;EAC7F,IAAI,OAAO,WAAW,KAAK,UAAU,OAAO,CAAC,IAAI,WAAW;GAC1D,MAAM;GACN;EACF;EACA,QAAQ,IAAI,QAAQ,SAAS,CAAC,EAAE,OAAO,OAAO;CAChD;CACA,UAAU,GAAG,WAAW,SAAS;CACjC,UAAU,GAAG,cAAc,KAAK;CAChC,UAAU,GAAG,QAAQ,KAAK;CAE1B,MAAM,YACJ,OACA,WACA,WAEA,IAAI,SAAS,SAAS,WAAW;EAC/B,QAAQ,eAAe;EACvB,IAAI,UAAU,QAAQ,QAAQ,KAAK,MAAM,IAAI,MAAM,wDAAwD;EAC3G,MAAM,aAAA,GAAYC,YAAAA,WAAAA,CAAW;EAC7B,MAAM,eAAqB;GACzB,IAAI;IACF,KAAK;KAAE,MAAM;KAA0B,SAAS;KAAS;IAAU,CAAC;GACtE,QAAQ,CAER;EACF;EACA,MAAM,UAAU,QAAkC,UAAwB;GACxE,IAAI,CAAC,QAAQ,OAAO,SAAS,GAAG;GAChC,aAAa,KAAK;GAClB,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,IAAI,OAAO,OAAO,KAAK;QAClB,QAAQ,MAAO;EACtB;EACA,MAAM,gBAAsB;GAC1B,OAAO;GACP,OAAO,KAAA,mBAAW,IAAI,MAAM,6CAA6C,CAAC;EAC5E;EACA,MAAM,QAAQ,iBAAiB;GAC7B,OAAO;GACP,OAAO,KAAA,mBAAW,IAAI,MAAM,yCAAyC,CAAC;EACxE,GAAG,SAAS;EACZ,QAAQ,IAAI,WAAW,EAAE,OAAO,CAAC;EACjC,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACzD,IAAI;GACF,MAAM,UAAU;IAAE,GAAG;IAAO,SAAS;IAAS;GAAU;GACxD,IAAI,OAAO,WAAW,KAAK,UAAU,OAAO,CAAC,IAAI,WAC/C,MAAM,IAAI,MAAM,4CAA4C;GAC9D,KAAK,OAAO;EACd,SAAS,OAAO;GACd,OAAO,KAAA,GAAW,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EAC7E;CACF,CAAC;CAEH,IAAI;EACF,MAAM,QAAQ,MAAM,SAAS,EAAE,MAAM,wBAAwB,GAAG,IAAK;EACrE,IACE,MAAM,SAAS,2BACf,OAAO,MAAM,UAAU,YACvB,CAAC,kBAAkB,KAAK,MAAM,KAAK,GAEnC,MAAM,IAAI,MAAM,uCAAuC;EAEzD,QAAQ,MAAM;EACd,YAAY;EACZ,UAAU,MAAM,YAAY;CAC9B,QAAQ;EACN,MAAM;EACN;CACF;CAEA,OAAO;EACL,IAAI,YAAY;GACd,OAAO;EACT;EACA,IAAI,UAAU;GACZ,OAAO,aAAa;EACtB;EACA,WAAW;EACX,gBAAgB,OAAO,YAAY,UAAU,WAAW,KAAK,MAAM,WAAW,YAAY,KAAK;EAC/F,oBAAoB,SAAS;GAC3B,IAAI,CAAC,aAAa,CAAC,SAAS,OAAO;GACnC,IAAI,iBAAiB,OAAO,GAAG,OAAO;GACtC,MAAM,SAAS,aAAa,OAAO;GACnC,OACE,WAAW,KAAA,MACV,aAAa,iBAAiB,MAAM,OAAO,OAAO,WAAW,aAAa,OAAO,WAAW;EAEjG;EACA,iBAAiB,WAAW,SAAS;GACnC,IAAI,iBAAiB,OAAO,GAAG,OAAO;GACtC,MAAM,SAAS,aAAa,OAAO;GACnC,MAAM,YAAY,YAAY,IAAI,SAAS;GAC3C,OACE,aACA,WACA,WAAW,KAAA,KACX,cAAc,KAAA,KACd,UAAU,aAAa,OAAO,YAC9B,UAAU,cAAc,KAAA,KACxB,CAAC,UAAU,MAAM,OAAO;EAE5B;EACA,mBAAmB,WAAW,SAAS;GACrC,IAAI,iBAAiB,OAAO,GAAG,OAAO;GACtC,MAAM,SAAS,aAAa,OAAO;GACnC,MAAM,YAAY,YAAY,IAAI,SAAS;GAC3C,OACE,aAAa,WAAW,KAAA,KAAa,WAAW,aAAa,QAAQ,UAAU,aAAa,OAAO;EAEvG;EACA,iBAAiB,WAAW,SAAS;GACnC,IAAI,iBAAiB,OAAO,GAAG,OAAO;GACtC,MAAM,SAAS,aAAa,OAAO;GACnC,MAAM,YAAY,YAAY,IAAI,SAAS;GAC3C,IAAI,aAAa,WAAW,WAAW,KAAA,KAAa,CAAC,cAAc,IAAI,SAAS,GAAG,OAAO;GAC1F,OACE,aACA,WACA,WAAW,KAAA,KACX,WAAW,aAAa,QACxB,UAAU,cAAc,KAAA,KACxB,UAAU,YAAY,KAAK,IAAI,KAC/B,UAAU,aAAa,OAAO,YAC9B,CAAC,UAAU,MAAM,OAAO;EAE5B;EACA,aAAa,WAAW,SAAS;GAC/B,IAAI,CAAC,aAAa,CAAC,SAAS,MAAM,IAAI,MAAM,sCAAsC;GAClF,MAAM,SAAS,YAAY,KAAA,IAAY,KAAA,IAAY,aAAa,OAAO;GACvE,IAAI,WAAW,KAAA,GAAW;IACxB,MAAM,WAAW,YAAY,IAAI,SAAS;IAC1C,IAAI,aAAa,KAAA,KAAa,SAAS,aAAa,OAAO,UACzD,MAAM,IAAI,MAAM,gDAAgD;IAClE,IAAI,UAAU,MAAM,OAAO,SAAS;KAClC,SAAS,QAAQ,IAAI,gBAAgB;KACrC,SAAS,UAAU,KAAA;IACrB;IACA,IAAI,aAAa,KAAA,GACf,YAAY,IAAI,WAAW;KACzB,UAAU,OAAO;KACjB,UAAU;KACV,OAAO,IAAI,gBAAgB;IAC7B,CAAC;GACL,OAAO,IAAI,YAAY,KAAA,KAAa,CAAC,iBAAiB,OAAO,GAC3D,MAAM,IAAI,MAAM,6CAA6C;GAE/D,gBAAgB,OAAO,SAAS;GAChC,cAAc,IAAI,SAAS;EAC7B;EACA,aAAa,UAAU;GACrB,KAAK,MAAM,CAAC,WAAW,cAAc,aAAa,IAAI,UAAU,aAAa,UAAU,OAAO,SAAS;EACzG;EACA,cAAc,cAAc,cAAc,IAAI,SAAS;EACvD,gBAAgB,cAAc;GAC5B,OAAO,SAAS;GAChB,WACG,cAAc,SAAS,CAAC,CACxB,YAAY,QAAQ,YAAY,wCAAwC,CAAC;EAE9E;EACA,UAAU,UAAU,WAAW;GAC7B,UAAU,IAAI,UAAU,SAAS;GACjC,aAAa;IACX,UAAU,OAAO,QAAQ;GAC3B;EACF;EACA,MAAM,QAAQ,OAAO,SAAS;GAC5B,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,sCAAsC;GACtE,IAAI,gBAAgB,IAAI,MAAM,SAAS,KAAK,QAAQ,cAAc,UAAU,QAAQ,cAAc,UAChG,MAAM,IAAI,MAAM,sCAAsC;GACxD,IAAI,QAAQ,cAAc,YAAY,QAAQ,cAAc,aAAa,CAAC,cAAc,IAAI,MAAM,SAAS,GACzG,MAAM,IAAI,MAAM,kDAAkD;GAEpE,MAAM,YAAY,YAAY,IAAI,MAAM,SAAS;GACjD,IAAI,cAAc,KAAA,KAAa,aAAa,qBAAqB,UAAU,QAAQ,MAAM,MAAM;IAC7F,OAAO,MAAM,SAAS;IACtB,IAAI,QAAQ,cAAc,QAAQ,MAAM,IAAI,MAAM,gCAAgC;GACpF;GACA,IAAI,QAAQ,cAAc,UAAU,cAAc,KAAA,GAAW;IAC3D,UAAU,YAAY,KAAA;IACtB,UAAU,MAAM,MAAM;GACxB;GACA,MAAM,SACJ,cAAc,KAAA,KAAa,QAAQ,cAAc,SAC7C,QAAQ,SACR,QAAQ,WAAW,KAAA,IACjB,UAAU,MAAM,SAChB,YAAY,IAAI,CAAC,QAAQ,QAAQ,UAAU,MAAM,MAAM,CAAC;GAChE,MAAM,SAAS,MAAM,SACnB;IACE,MAAM;IACN,WAAW,MAAM;IACjB,WAAW,QAAQ;IACnB,GAAI,QAAQ,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;GACtE,GACA,QAAQ,cAAc,aAAa,OAAU,KAC7C,MACF;GACA,IACE,OAAO,OAAO,mBAAmB,YAChC,eAAe,KAAA,KAAa,eAAe,OAAO,gBACnD;IACA,MAAM;IACN,MAAM,IAAI,MAAM,sDAAsD;GACxE;GACA,aAAa,OAAO;GACpB,IAAI,OAAO,OAAO,MAChB,MAAM,IAAI,MAAM,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ,yBAAyB;GAC7F,IAAI,QAAQ,cAAc,cAAc,cAAc,KAAA,GAAW;IAC/D,IACE,YAAY,IAAI,MAAM,SAAS,MAAM,aACrC,UAAU,MAAM,OAAO,WACvB,aAAa,qBAAqB,UAAU,QAAQ,MAAM,MAE1D,MAAM,IAAI,MAAM,oCAAoC;IACtD,UAAU,WAAW;IACrB,MAAM,QAAQ,OAAO;IACrB,IAAI,OAAO,OAAO,cAAc,UAAU,UAAU,YAAY,MAAM;IACtE,IAAI,OAAO,OAAO,YAAY,UAAU,UAAU,UAAU,MAAM;GACpE;GACA,IAAI,QAAQ,cAAc,QAAQ;IAChC,IAAI,cAAc,KAAA,GAAW,UAAU,YAAY,KAAA;IACnD,MAAM,UAAW,QAAQ,SAA+C;IACxE,IAAI,OAAO,YAAY,UAAU,MAAM,IAAI,MAAM,4CAA4C;IAC7F,OAAO,WAAW,WAAW,MAAM,WAAW,SAAS,OAAO,MAAM;GACtE;GACA,OAAO,OAAO;EAChB;EACA,QAAQ;GACN,IAAI,QAAQ;GACZ,IAAI;IACF,KAAK;KAAE,MAAM;KAAyB,SAAS;IAAQ,CAAC;GAC1D,UAAU;IACR,MAAM;GACR;EACF;CACF;AACF"}
1
+ {"version":3,"file":"computerUseBinding.cjs","names":["ComputerUseRecordingStore","fs","path","os","timingSafeEqual","poApiCallerFrom","randomUUID"],"sources":["../../../../src/builders/server/computerUseBinding.ts"],"sourcesContent":["import { randomUUID, timingSafeEqual } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { PoComputerUseHostBinding } from '@agimon-ai/po-core/hubChannel';\nimport { poApiCallerFrom } from '@agimon-ai/po-core/packageApi';\n\nimport { ComputerUseRecordingStore } from './computerUseRecordings';\n\nconst VERSION = 1;\nconst MAX_BYTES = 8 * 1024 * 1024;\nconst DESKTOP_HEADER = 'x-po-desktop';\ntype Transport = Pick<NodeJS.Process, 'send' | 'connected' | 'on' | 'off'>;\n\n/** The capability is discovered over the inherited Desktop IPC channel, never from environment flags. */\nexport async function createComputerUseBinding(\n transport: Transport = process,\n remotePolicy: {\n isDeviceAuthorized?: (deviceId: string) => boolean;\n stepUpRequired?: () => boolean;\n } = {},\n): Promise<PoComputerUseHostBinding | undefined> {\n if (!transport.send || !transport.connected) return undefined;\n const recordings = new ComputerUseRecordingStore(await fs.mkdtemp(path.join(os.tmpdir(), 'po-recordings-')));\n let available = false;\n let enabled = false;\n let closed = false;\n let token = '';\n let generation: string | undefined;\n const ownedSessions = new Set<string>();\n const revokedSessions = new Set<string>();\n const listeners = new Map<() => void, string | undefined>();\n const delegations = new Map<\n string,\n { deviceId: string; approved: boolean; abort: AbortController; grantId?: string; expiresAt?: number }\n >();\n const nativeAuthorized = (headers: Headers): boolean => {\n const proof = headers.get(DESKTOP_HEADER) ?? '';\n return available && /^[a-f0-9]{64}$/u.test(proof) && timingSafeEqual(Buffer.from(proof), Buffer.from(token));\n };\n const remoteCaller = (headers: Headers) => {\n const caller = poApiCallerFrom(headers);\n return caller?.locality === 'remote' && remotePolicy.isDeviceAuthorized?.(caller.deviceId) === true\n ? caller\n : undefined;\n };\n const notify = (sessionId?: string): void => {\n for (const [listener, session] of listeners)\n if (sessionId === undefined || session === undefined || session === sessionId) listener();\n };\n const revoke = (sessionId: string, retainRecording = false): void => {\n const delegated = delegations.get(sessionId);\n if (delegated === undefined) return;\n if (!retainRecording) delegations.delete(sessionId);\n delegated.expiresAt = undefined;\n delegated.abort.abort();\n revokedSessions.add(sessionId);\n if (delegated.grantId !== undefined && available) {\n void exchange(\n { type: 'po:computer-use:request', sessionId, operation: 'stop', payload: { grantId: delegated.grantId } },\n 30_000,\n )\n .then((result) => recordings.importStop(sessionId, delegated.grantId!, result.result))\n .catch(() => close());\n }\n notify(sessionId);\n };\n const pending = new Map<string, { finish: (value?: Record<string, unknown>, error?: Error) => void }>();\n const send = (value: object): void => {\n if (closed || !transport.connected) throw new Error('Desktop computer use is disconnected.');\n transport.send!(value, (error: Error | null) => {\n if (error) close();\n });\n };\n const close = (): void => {\n if (closed) return;\n closed = true;\n available = false;\n token = '';\n transport.off('message', onMessage);\n transport.off('disconnect', close);\n transport.off('exit', close);\n for (const request of pending.values())\n request.finish(undefined, new Error('Desktop computer use is disconnected.'));\n pending.clear();\n for (const sessionId of delegations.keys()) revoke(sessionId);\n notify();\n listeners.clear();\n void recordings.close().catch(() => process.emitWarning('Computer-use recording cleanup failed.'));\n // Keep ownership pinned while the server is alive, including after Desktop disconnects.\n };\n const onMessage = (value: unknown): void => {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return;\n const message = value as Record<string, unknown>;\n if (message.version !== VERSION) return;\n if (message.type === 'po:computer-use:availability' && typeof message.enabled === 'boolean') {\n enabled = message.enabled;\n // Native stop/expiry also notifies availability without changing the global setting.\n for (const sessionId of delegations.keys()) revoke(sessionId, true);\n notify();\n return;\n }\n if (typeof message.requestId !== 'string') return;\n if (message.type !== 'po:computer-use:ready' && message.type !== 'po:computer-use:response') return;\n if (Buffer.byteLength(JSON.stringify(message)) > MAX_BYTES) {\n close();\n return;\n }\n pending.get(message.requestId)?.finish(message);\n };\n transport.on('message', onMessage);\n transport.on('disconnect', close);\n transport.on('exit', close);\n\n const exchange = (\n value: Record<string, unknown>,\n timeoutMs: number,\n signal?: AbortSignal,\n ): Promise<Record<string, unknown>> =>\n new Promise((resolve, reject) => {\n signal?.throwIfAborted();\n if (closed || pending.size >= 128) throw new Error('Desktop computer-use transport is unavailable or busy.');\n const requestId = randomUUID();\n const cancel = (): void => {\n try {\n send({ type: 'po:computer-use:cancel', version: VERSION, requestId });\n } catch {\n /* Already disconnected. */\n }\n };\n const finish = (result?: Record<string, unknown>, error?: Error): void => {\n if (!pending.delete(requestId)) return;\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n if (error) reject(error);\n else resolve(result!);\n };\n const onAbort = (): void => {\n cancel();\n finish(undefined, new Error('Desktop computer-use request was cancelled.'));\n };\n const timer = setTimeout(() => {\n cancel();\n finish(undefined, new Error('Desktop computer-use request timed out.'));\n }, timeoutMs);\n pending.set(requestId, { finish });\n signal?.addEventListener('abort', onAbort, { once: true });\n try {\n const message = { ...value, version: VERSION, requestId };\n if (Buffer.byteLength(JSON.stringify(message)) > MAX_BYTES)\n throw new Error('Desktop computer-use request is too large.');\n send(message);\n } catch (error) {\n finish(undefined, error instanceof Error ? error : new Error(String(error)));\n }\n });\n\n try {\n const ready = await exchange({ type: 'po:computer-use:hello' }, 1_500);\n if (\n ready.type !== 'po:computer-use:ready' ||\n typeof ready.token !== 'string' ||\n !/^[a-f0-9]{64}$/u.test(ready.token)\n ) {\n throw new Error('Invalid Desktop capability handshake.');\n }\n token = ready.token;\n available = true;\n enabled = ready.enabled === true;\n } catch {\n close();\n return undefined;\n }\n\n return {\n get available() {\n return available;\n },\n get enabled() {\n return available && enabled;\n },\n authorize: nativeAuthorized,\n readRecording: (scope, artifactId, range) => recordings.read(scope.sessionId, artifactId, range),\n authorizeActivation(headers) {\n if (!available || !enabled) return false;\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n return (\n caller !== undefined &&\n (remotePolicy.stepUpRequired?.() === true ? caller.stepUp === 'verified' : caller.stepUp === 'not-required')\n );\n },\n authorizePending(sessionId, headers) {\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n const delegated = delegations.get(sessionId);\n return (\n available &&\n enabled &&\n caller !== undefined &&\n delegated !== undefined &&\n delegated.deviceId === caller.deviceId &&\n delegated.expiresAt === undefined &&\n !delegated.abort.signal.aborted\n );\n },\n authorizeRecording(sessionId, headers) {\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n const delegated = delegations.get(sessionId);\n return (\n available && caller !== undefined && delegated?.approved === true && delegated.deviceId === caller.deviceId\n );\n },\n authorizeSession(sessionId, headers) {\n if (nativeAuthorized(headers)) return true;\n const caller = remoteCaller(headers);\n const delegated = delegations.get(sessionId);\n if (available && enabled && caller !== undefined && !ownedSessions.has(sessionId)) return true;\n return (\n available &&\n enabled &&\n caller !== undefined &&\n delegated?.approved === true &&\n delegated.expiresAt !== undefined &&\n delegated.expiresAt > Date.now() &&\n delegated.deviceId === caller.deviceId &&\n !delegated.abort.signal.aborted\n );\n },\n claimSession(sessionId, headers) {\n if (!available || !enabled) throw new Error('Desktop computer use is unavailable.');\n const caller = headers === undefined ? undefined : remoteCaller(headers);\n if (caller !== undefined) {\n const previous = delegations.get(sessionId);\n if (previous !== undefined && previous.deviceId !== caller.deviceId)\n throw new Error('This session belongs to another paired device.');\n if (previous?.abort.signal.aborted) {\n previous.abort = new AbortController();\n previous.grantId = undefined;\n }\n if (previous === undefined)\n delegations.set(sessionId, {\n deviceId: caller.deviceId,\n approved: false,\n abort: new AbortController(),\n });\n } else if (headers !== undefined && !nativeAuthorized(headers)) {\n throw new Error('Native or paired authorization is required.');\n }\n revokedSessions.delete(sessionId);\n ownedSessions.add(sessionId);\n },\n revokeDevice(deviceId) {\n for (const [sessionId, delegated] of delegations) if (delegated.deviceId === deviceId) revoke(sessionId);\n },\n ownsSession: (sessionId) => ownedSessions.has(sessionId),\n forgetSession: (sessionId) => {\n revoke(sessionId);\n void recordings\n .forgetSession(sessionId)\n .catch(() => process.emitWarning('Computer-use recording cleanup failed.'));\n // Keep private history pinned to Desktop, but never replay device approval after restart.\n },\n subscribe(listener, sessionId) {\n listeners.set(listener, sessionId);\n return () => {\n listeners.delete(listener);\n };\n },\n async request(scope, request) {\n if (!available) throw new Error('Desktop computer use is unavailable.');\n if (revokedSessions.has(scope.sessionId) && request.operation !== 'stop' && request.operation !== 'status')\n throw new Error('Computer-use delegation was revoked.');\n if (request.operation !== 'status' && request.operation !== 'targets' && !ownedSessions.has(scope.sessionId)) {\n throw new Error('This session has not been authorized by Desktop.');\n }\n const delegated = delegations.get(scope.sessionId);\n if (delegated !== undefined && remotePolicy.isDeviceAuthorized?.(delegated.deviceId) !== true) {\n revoke(scope.sessionId);\n if (request.operation !== 'stop') throw new Error('The paired device was revoked.');\n }\n if (request.operation === 'stop' && delegated !== undefined) {\n delegated.expiresAt = undefined;\n delegated.abort.abort();\n }\n const signal =\n delegated === undefined || request.operation === 'stop'\n ? request.signal\n : request.signal === undefined\n ? delegated.abort.signal\n : AbortSignal.any([request.signal, delegated.abort.signal]);\n const result = await exchange(\n {\n type: 'po:computer-use:request',\n sessionId: scope.sessionId,\n operation: request.operation,\n ...(request.payload === undefined ? {} : { payload: request.payload }),\n },\n request.operation === 'activate' ? 120_000 : 30_000,\n signal,\n );\n if (\n typeof result.hostGeneration !== 'string' ||\n (generation !== undefined && generation !== result.hostGeneration)\n ) {\n close();\n throw new Error('Desktop generation changed. Authorize a new session.');\n }\n generation = result.hostGeneration;\n if (result.ok !== true)\n throw new Error(typeof result.error === 'string' ? result.error : 'Desktop request failed.');\n if (request.operation === 'activate' && delegated !== undefined) {\n if (\n delegations.get(scope.sessionId) !== delegated ||\n delegated.abort.signal.aborted ||\n remotePolicy.isDeviceAuthorized?.(delegated.deviceId) !== true\n )\n throw new Error('The paired activation was revoked.');\n delegated.approved = true;\n const grant = result.result as { grantId?: unknown; expiresAt?: unknown } | undefined;\n if (typeof grant?.expiresAt === 'number') delegated.expiresAt = grant.expiresAt;\n if (typeof grant?.grantId === 'string') delegated.grantId = grant.grantId;\n }\n if (request.operation === 'stop') {\n if (delegated !== undefined) delegated.expiresAt = undefined;\n const grantId = (request.payload as { grantId?: unknown } | undefined)?.grantId;\n if (typeof grantId !== 'string') throw new Error('Recording finalization requires its grant.');\n return recordings.importStop(scope.sessionId, grantId, result.result);\n }\n return result.result;\n },\n close() {\n if (closed) return;\n try {\n send({ type: 'po:computer-use:close', version: VERSION });\n } finally {\n close();\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { ComputerUseRecordingStore } from "./computerUseRecordings.mjs";
2
2
  import path from "node:path";
3
3
  import os from "node:os";
4
4
  import { randomUUID, timingSafeEqual } from "node:crypto";
5
- import { doomApiCallerFrom } from "@agimon-ai/po-core/packageApi";
5
+ import { poApiCallerFrom } from "@agimon-ai/po-core/packageApi";
6
6
  import fs from "node:fs/promises";
7
7
  //#region src/builders/server/computerUseBinding.ts
8
8
  const VERSION = 1;
@@ -26,11 +26,11 @@ async function createComputerUseBinding(transport = process, remotePolicy = {})
26
26
  return available && /^[a-f0-9]{64}$/u.test(proof) && timingSafeEqual(Buffer.from(proof), Buffer.from(token));
27
27
  };
28
28
  const remoteCaller = (headers) => {
29
- const caller = doomApiCallerFrom(headers);
29
+ const caller = poApiCallerFrom(headers);
30
30
  return caller?.locality === "remote" && remotePolicy.isDeviceAuthorized?.(caller.deviceId) === true ? caller : void 0;
31
31
  };
32
32
  const notify = (sessionId) => {
33
- for (const [listener, session] of listeners) if (sessionId === void 0 || session === sessionId) listener();
33
+ for (const [listener, session] of listeners) if (sessionId === void 0 || session === void 0 || session === sessionId) listener();
34
34
  };
35
35
  const revoke = (sessionId, retainRecording = false) => {
36
36
  const delegated = delegations.get(sessionId);