@agimon-ai/po 0.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +755 -0
- package/assets/banner.png +0 -0
- package/assets/banner.svg +25 -0
- package/assets/favicon.svg +15 -0
- package/assets/logo-light.png +0 -0
- package/assets/logo-light.svg +25 -0
- package/assets/logo.png +0 -0
- package/assets/logo.svg +25 -0
- package/assets/mascot.png +0 -0
- package/assets/mascot.svg +25 -0
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/antigravity.cjs +5 -0
- package/dist/antigravity.d.mts +2 -0
- package/dist/antigravity.mjs +2 -0
- package/dist/apiContracts.cjs +11 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +52 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +7 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/baseCommand.cjs +3 -0
- package/dist/baseCommand.d.mts +2 -0
- package/dist/baseCommand.mjs +2 -0
- package/dist/bin/cli.cjs +24 -0
- package/dist/bin/cli.cjs.map +1 -0
- package/dist/bin/cli.d.mts +1 -0
- package/dist/bin/cli.mjs +25 -0
- package/dist/bin/cli.mjs.map +1 -0
- package/dist/bin/doomRunner.cjs +33 -0
- package/dist/bin/doomRunner.cjs.map +1 -0
- package/dist/bin/doomRunner.d.mts +4 -0
- package/dist/bin/doomRunner.d.mts.map +1 -0
- package/dist/bin/doomRunner.mjs +32 -0
- package/dist/bin/doomRunner.mjs.map +1 -0
- package/dist/bin/serve.cjs +30 -0
- package/dist/bin/serve.cjs.map +1 -0
- package/dist/bin/serve.d.mts +1 -0
- package/dist/bin/serve.mjs +31 -0
- package/dist/bin/serve.mjs.map +1 -0
- package/dist/builders/cli/extensionAssembler/index.cjs +342 -0
- package/dist/builders/cli/extensionAssembler/index.cjs.map +1 -0
- package/dist/builders/cli/extensionAssembler/index.d.mts +85 -0
- package/dist/builders/cli/extensionAssembler/index.d.mts.map +1 -0
- package/dist/builders/cli/extensionAssembler/index.mjs +335 -0
- package/dist/builders/cli/extensionAssembler/index.mjs.map +1 -0
- package/dist/catalog/index.json +430 -0
- package/dist/cli.cjs +28 -0
- package/dist/cli.d.mts +11 -0
- package/dist/cli.mjs +11 -0
- package/dist/cliApp.cjs +6 -0
- package/dist/cliApp.d.mts +2 -0
- package/dist/cliApp.mjs +2 -0
- package/dist/codex.cjs +8 -0
- package/dist/codex.d.mts +2 -0
- package/dist/codex.mjs +2 -0
- package/dist/commands.cjs +18 -0
- package/dist/commands.d.mts +9 -0
- package/dist/commands.mjs +9 -0
- package/dist/compatibility.cjs +27 -0
- package/dist/compatibility.d.mts +6 -0
- package/dist/compatibility.mjs +6 -0
- package/dist/compatibilityCommand.cjs +4 -0
- package/dist/compatibilityCommand.d.mts +2 -0
- package/dist/compatibilityCommand.mjs +2 -0
- package/dist/compatibilityContext.cjs +3 -0
- package/dist/compatibilityContext.d.mts +2 -0
- package/dist/compatibilityContext.mjs +2 -0
- package/dist/compatibilityOptions.cjs +5 -0
- package/dist/compatibilityOptions.d.mts +2 -0
- package/dist/compatibilityOptions.mjs +2 -0
- package/dist/config.cjs +163 -0
- package/dist/config.d.mts +7 -0
- package/dist/config.mjs +7 -0
- package/dist/doctorCommand.cjs +5 -0
- package/dist/doctorCommand.d.mts +2 -0
- package/dist/doctorCommand.mjs +2 -0
- package/dist/emitMcpCommand.cjs +4 -0
- package/dist/emitMcpCommand.d.mts +2 -0
- package/dist/emitMcpCommand.mjs +2 -0
- package/dist/explainCommand.cjs +5 -0
- package/dist/explainCommand.d.mts +2 -0
- package/dist/explainCommand.mjs +2 -0
- package/dist/extensions/server.cjs +39 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +34 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/harnessContext.cjs +5 -0
- package/dist/harnessContext.d.mts +2 -0
- package/dist/harnessContext.mjs +2 -0
- package/dist/harnessState.cjs +19 -0
- package/dist/harnessState.d.mts +2 -0
- package/dist/harnessState.mjs +2 -0
- package/dist/help.cjs +12 -0
- package/dist/help.d.mts +6 -0
- package/dist/help.mjs +6 -0
- package/dist/index.cjs +306 -0
- package/dist/index.d.mts +40 -0
- package/dist/index.mjs +37 -0
- package/dist/initCommand.cjs +4 -0
- package/dist/initCommand.d.mts +2 -0
- package/dist/initCommand.mjs +2 -0
- package/dist/matrixOptions.cjs +28 -0
- package/dist/matrixOptions.d.mts +2 -0
- package/dist/matrixOptions.mjs +2 -0
- package/dist/matrixSwitcher.cjs +11 -0
- package/dist/matrixSwitcher.d.mts +3 -0
- package/dist/matrixSwitcher.mjs +3 -0
- package/dist/mcpFilter.cjs +9 -0
- package/dist/mcpFilter.d.mts +1 -0
- package/dist/mcpFilter.mjs +2 -0
- package/dist/options.cjs +3 -0
- package/dist/options.d.mts +2 -0
- package/dist/options.mjs +2 -0
- package/dist/process.cjs +12 -0
- package/dist/process.d.mts +2 -0
- package/dist/process.mjs +2 -0
- package/dist/projectTrust.cjs +6 -0
- package/dist/projectTrust.d.mts +2 -0
- package/dist/projectTrust.mjs +2 -0
- package/dist/repository.cjs +5 -0
- package/dist/repository.d.mts +2 -0
- package/dist/repository.mjs +2 -0
- package/dist/resourceCollector.cjs +9 -0
- package/dist/resourceCollector.d.mts +1 -0
- package/dist/resourceCollector.mjs +2 -0
- package/dist/router.cjs +6 -0
- package/dist/router.d.mts +2 -0
- package/dist/router.mjs +2 -0
- package/dist/services.cjs +97 -0
- package/dist/services.d.mts +12 -0
- package/dist/services.mjs +12 -0
- package/dist/shared.cjs +18 -0
- package/dist/shared.d.mts +2 -0
- package/dist/shared.mjs +2 -0
- package/dist/src/bin/agentDirectory.cjs +27 -0
- package/dist/src/bin/agentDirectory.cjs.map +1 -0
- package/dist/src/bin/agentDirectory.mjs +26 -0
- package/dist/src/bin/agentDirectory.mjs.map +1 -0
- package/dist/src/builders/apiContractExport/index.cjs +126 -0
- package/dist/src/builders/apiContractExport/index.cjs.map +1 -0
- package/dist/src/builders/apiContractExport/index.mjs +123 -0
- package/dist/src/builders/apiContractExport/index.mjs.map +1 -0
- package/dist/src/builders/cli/entryResolution.cjs +39 -0
- package/dist/src/builders/cli/entryResolution.cjs.map +1 -0
- package/dist/src/builders/cli/entryResolution.mjs +35 -0
- package/dist/src/builders/cli/entryResolution.mjs.map +1 -0
- package/dist/src/builders/cli/harnessContext.cjs +175 -0
- package/dist/src/builders/cli/harnessContext.cjs.map +1 -0
- package/dist/src/builders/cli/harnessContext.d.mts +48 -0
- package/dist/src/builders/cli/harnessContext.d.mts.map +1 -0
- package/dist/src/builders/cli/harnessContext.mjs +167 -0
- package/dist/src/builders/cli/harnessContext.mjs.map +1 -0
- package/dist/src/builders/cli/sessionComposition.cjs +27 -0
- package/dist/src/builders/cli/sessionComposition.cjs.map +1 -0
- package/dist/src/builders/cli/sessionComposition.mjs +26 -0
- package/dist/src/builders/cli/sessionComposition.mjs.map +1 -0
- package/dist/src/builders/hooks/index.cjs +238 -0
- package/dist/src/builders/hooks/index.cjs.map +1 -0
- package/dist/src/builders/hooks/index.mjs +231 -0
- package/dist/src/builders/hooks/index.mjs.map +1 -0
- package/dist/src/builders/native/index.cjs +176 -0
- package/dist/src/builders/native/index.cjs.map +1 -0
- package/dist/src/builders/native/index.d.mts +36 -0
- package/dist/src/builders/native/index.d.mts.map +1 -0
- package/dist/src/builders/native/index.mjs +170 -0
- package/dist/src/builders/native/index.mjs.map +1 -0
- package/dist/src/builders/server/backgroundSync.cjs +333 -0
- package/dist/src/builders/server/backgroundSync.cjs.map +1 -0
- package/dist/src/builders/server/backgroundSync.mjs +320 -0
- package/dist/src/builders/server/backgroundSync.mjs.map +1 -0
- package/dist/src/builders/server/computerUseBinding.cjs +273 -0
- package/dist/src/builders/server/computerUseBinding.cjs.map +1 -0
- package/dist/src/builders/server/computerUseBinding.mjs +269 -0
- package/dist/src/builders/server/computerUseBinding.mjs.map +1 -0
- package/dist/src/builders/server/computerUseRecordings.cjs +188 -0
- package/dist/src/builders/server/computerUseRecordings.cjs.map +1 -0
- package/dist/src/builders/server/computerUseRecordings.mjs +184 -0
- package/dist/src/builders/server/computerUseRecordings.mjs.map +1 -0
- package/dist/src/builders/server/defaultWorkspace.cjs +38 -0
- package/dist/src/builders/server/defaultWorkspace.cjs.map +1 -0
- package/dist/src/builders/server/defaultWorkspace.mjs +35 -0
- package/dist/src/builders/server/defaultWorkspace.mjs.map +1 -0
- package/dist/src/builders/server/idleRestart.cjs +149 -0
- package/dist/src/builders/server/idleRestart.cjs.map +1 -0
- package/dist/src/builders/server/idleRestart.mjs +136 -0
- package/dist/src/builders/server/idleRestart.mjs.map +1 -0
- package/dist/src/builders/server/index.cjs +63 -0
- package/dist/src/builders/server/index.cjs.map +1 -0
- package/dist/src/builders/server/index.mjs +60 -0
- package/dist/src/builders/server/index.mjs.map +1 -0
- package/dist/src/builders/server/logSink.cjs +57 -0
- package/dist/src/builders/server/logSink.cjs.map +1 -0
- package/dist/src/builders/server/logSink.mjs +57 -0
- package/dist/src/builders/server/logSink.mjs.map +1 -0
- package/dist/src/builders/server/mcpApp.cjs +152 -0
- package/dist/src/builders/server/mcpApp.cjs.map +1 -0
- package/dist/src/builders/server/mcpApp.d.mts +18 -0
- package/dist/src/builders/server/mcpApp.d.mts.map +1 -0
- package/dist/src/builders/server/mcpApp.mjs +147 -0
- package/dist/src/builders/server/mcpApp.mjs.map +1 -0
- package/dist/src/builders/server/mcpAppBuild.cjs +111 -0
- package/dist/src/builders/server/mcpAppBuild.cjs.map +1 -0
- package/dist/src/builders/server/mcpAppBuild.d.mts +24 -0
- package/dist/src/builders/server/mcpAppBuild.d.mts.map +1 -0
- package/dist/src/builders/server/mcpAppBuild.mjs +105 -0
- package/dist/src/builders/server/mcpAppBuild.mjs.map +1 -0
- package/dist/src/builders/server/mcpBundle.cjs +87 -0
- package/dist/src/builders/server/mcpBundle.cjs.map +1 -0
- package/dist/src/builders/server/mcpBundle.d.mts +23 -0
- package/dist/src/builders/server/mcpBundle.d.mts.map +1 -0
- package/dist/src/builders/server/mcpBundle.mjs +84 -0
- package/dist/src/builders/server/mcpBundle.mjs.map +1 -0
- package/dist/src/builders/server/packageReloadCheck.cjs +62 -0
- package/dist/src/builders/server/packageReloadCheck.cjs.map +1 -0
- package/dist/src/builders/server/packageReloadCheck.mjs +61 -0
- package/dist/src/builders/server/packageReloadCheck.mjs.map +1 -0
- package/dist/src/builders/server/packageUrl.cjs +210 -0
- package/dist/src/builders/server/packageUrl.cjs.map +1 -0
- package/dist/src/builders/server/packageUrl.mjs +203 -0
- package/dist/src/builders/server/packageUrl.mjs.map +1 -0
- package/dist/src/builders/server/runtime.cjs +1428 -0
- package/dist/src/builders/server/runtime.cjs.map +1 -0
- package/dist/src/builders/server/runtime.mjs +1420 -0
- package/dist/src/builders/server/runtime.mjs.map +1 -0
- package/dist/src/builders/server/runtimeResources.cjs +72 -0
- package/dist/src/builders/server/runtimeResources.cjs.map +1 -0
- package/dist/src/builders/server/runtimeResources.mjs +71 -0
- package/dist/src/builders/server/runtimeResources.mjs.map +1 -0
- package/dist/src/builders/server/selectionStatus.cjs +12 -0
- package/dist/src/builders/server/selectionStatus.cjs.map +1 -0
- package/dist/src/builders/server/selectionStatus.mjs +12 -0
- package/dist/src/builders/server/selectionStatus.mjs.map +1 -0
- package/dist/src/builders/server/sessionArguments.cjs +63 -0
- package/dist/src/builders/server/sessionArguments.cjs.map +1 -0
- package/dist/src/builders/server/sessionArguments.mjs +62 -0
- package/dist/src/builders/server/sessionArguments.mjs.map +1 -0
- package/dist/src/builders/server/shutdown.cjs +16 -0
- package/dist/src/builders/server/shutdown.cjs.map +1 -0
- package/dist/src/builders/server/shutdown.mjs +16 -0
- package/dist/src/builders/server/shutdown.mjs.map +1 -0
- package/dist/src/builders/server/webTemplateDecision.cjs +34 -0
- package/dist/src/builders/server/webTemplateDecision.cjs.map +1 -0
- package/dist/src/builders/server/webTemplateDecision.mjs +32 -0
- package/dist/src/builders/server/webTemplateDecision.mjs.map +1 -0
- package/dist/src/builders/server/workspaceCheckout.cjs +31 -0
- package/dist/src/builders/server/workspaceCheckout.cjs.map +1 -0
- package/dist/src/builders/server/workspaceCheckout.mjs +29 -0
- package/dist/src/builders/server/workspaceCheckout.mjs.map +1 -0
- package/dist/src/builders/web/bundle.cjs +225 -0
- package/dist/src/builders/web/bundle.cjs.map +1 -0
- package/dist/src/builders/web/bundle.d.mts +65 -0
- package/dist/src/builders/web/bundle.d.mts.map +1 -0
- package/dist/src/builders/web/bundle.mjs +221 -0
- package/dist/src/builders/web/bundle.mjs.map +1 -0
- package/dist/src/builders/web/bundleAssetPolicy.cjs +96 -0
- package/dist/src/builders/web/bundleAssetPolicy.cjs.map +1 -0
- package/dist/src/builders/web/bundleAssetPolicy.d.mts +22 -0
- package/dist/src/builders/web/bundleAssetPolicy.d.mts.map +1 -0
- package/dist/src/builders/web/bundleAssetPolicy.mjs +95 -0
- package/dist/src/builders/web/bundleAssetPolicy.mjs.map +1 -0
- package/dist/src/builders/web/index.cjs +361 -0
- package/dist/src/builders/web/index.cjs.map +1 -0
- package/dist/src/builders/web/index.d.mts +109 -0
- package/dist/src/builders/web/index.d.mts.map +1 -0
- package/dist/src/builders/web/index.mjs +342 -0
- package/dist/src/builders/web/index.mjs.map +1 -0
- package/dist/src/builders/web/webDevRoots.cjs +27 -0
- package/dist/src/builders/web/webDevRoots.cjs.map +1 -0
- package/dist/src/builders/web/webDevRoots.d.mts +22 -0
- package/dist/src/builders/web/webDevRoots.d.mts.map +1 -0
- package/dist/src/builders/web/webDevRoots.mjs +25 -0
- package/dist/src/builders/web/webDevRoots.mjs.map +1 -0
- package/dist/src/builders/web/webPluginGenerate.cjs +188 -0
- package/dist/src/builders/web/webPluginGenerate.cjs.map +1 -0
- package/dist/src/builders/web/webPluginGenerate.d.mts +56 -0
- package/dist/src/builders/web/webPluginGenerate.d.mts.map +1 -0
- package/dist/src/builders/web/webPluginGenerate.mjs +178 -0
- package/dist/src/builders/web/webPluginGenerate.mjs.map +1 -0
- package/dist/src/builders/web/webPluginManifest.cjs +128 -0
- package/dist/src/builders/web/webPluginManifest.cjs.map +1 -0
- package/dist/src/builders/web/webPluginManifest.d.mts +64 -0
- package/dist/src/builders/web/webPluginManifest.d.mts.map +1 -0
- package/dist/src/builders/web/webPluginManifest.mjs +118 -0
- package/dist/src/builders/web/webPluginManifest.mjs.map +1 -0
- package/dist/src/builders/web/webPluginScan.cjs +57 -0
- package/dist/src/builders/web/webPluginScan.cjs.map +1 -0
- package/dist/src/builders/web/webPluginScan.d.mts +19 -0
- package/dist/src/builders/web/webPluginScan.d.mts.map +1 -0
- package/dist/src/builders/web/webPluginScan.mjs +54 -0
- package/dist/src/builders/web/webPluginScan.mjs.map +1 -0
- package/dist/src/builders/web/webPluginVite.cjs +255 -0
- package/dist/src/builders/web/webPluginVite.cjs.map +1 -0
- package/dist/src/builders/web/webPluginVite.d.mts +64 -0
- package/dist/src/builders/web/webPluginVite.d.mts.map +1 -0
- package/dist/src/builders/web/webPluginVite.mjs +242 -0
- package/dist/src/builders/web/webPluginVite.mjs.map +1 -0
- package/dist/src/cli/cliApp.cjs +187 -0
- package/dist/src/cli/cliApp.cjs.map +1 -0
- package/dist/src/cli/cliApp.d.mts +32 -0
- package/dist/src/cli/cliApp.d.mts.map +1 -0
- package/dist/src/cli/cliApp.mjs +184 -0
- package/dist/src/cli/cliApp.mjs.map +1 -0
- package/dist/src/cli/commands/api-export/index.cjs +66 -0
- package/dist/src/cli/commands/api-export/index.cjs.map +1 -0
- package/dist/src/cli/commands/api-export/index.mjs +62 -0
- package/dist/src/cli/commands/api-export/index.mjs.map +1 -0
- package/dist/src/cli/commands/baseCommand.cjs +16 -0
- package/dist/src/cli/commands/baseCommand.cjs.map +1 -0
- package/dist/src/cli/commands/baseCommand.d.mts +24 -0
- package/dist/src/cli/commands/baseCommand.d.mts.map +1 -0
- package/dist/src/cli/commands/baseCommand.mjs +16 -0
- package/dist/src/cli/commands/baseCommand.mjs.map +1 -0
- package/dist/src/cli/commands/compat/context.cjs +115 -0
- package/dist/src/cli/commands/compat/context.cjs.map +1 -0
- package/dist/src/cli/commands/compat/context.d.mts +22 -0
- package/dist/src/cli/commands/compat/context.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/context.mjs +110 -0
- package/dist/src/cli/commands/compat/context.mjs.map +1 -0
- package/dist/src/cli/commands/compat/help.cjs +26 -0
- package/dist/src/cli/commands/compat/help.cjs.map +1 -0
- package/dist/src/cli/commands/compat/help.d.mts +4 -0
- package/dist/src/cli/commands/compat/help.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/help.mjs +25 -0
- package/dist/src/cli/commands/compat/help.mjs.map +1 -0
- package/dist/src/cli/commands/compat/index.cjs +73 -0
- package/dist/src/cli/commands/compat/index.cjs.map +1 -0
- package/dist/src/cli/commands/compat/index.d.mts +14 -0
- package/dist/src/cli/commands/compat/index.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/index.mjs +70 -0
- package/dist/src/cli/commands/compat/index.mjs.map +1 -0
- package/dist/src/cli/commands/compat/options.cjs +88 -0
- package/dist/src/cli/commands/compat/options.cjs.map +1 -0
- package/dist/src/cli/commands/compat/options.d.mts +15 -0
- package/dist/src/cli/commands/compat/options.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/options.mjs +86 -0
- package/dist/src/cli/commands/compat/options.mjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/antigravity.cjs +294 -0
- package/dist/src/cli/commands/compat/providers/antigravity.cjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/antigravity.d.mts +9 -0
- package/dist/src/cli/commands/compat/providers/antigravity.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/providers/antigravity.mjs +289 -0
- package/dist/src/cli/commands/compat/providers/antigravity.mjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/codex.cjs +178 -0
- package/dist/src/cli/commands/compat/providers/codex.cjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/codex.d.mts +17 -0
- package/dist/src/cli/commands/compat/providers/codex.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/providers/codex.mjs +170 -0
- package/dist/src/cli/commands/compat/providers/codex.mjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/index.cjs +65 -0
- package/dist/src/cli/commands/compat/providers/index.cjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/index.d.mts +15 -0
- package/dist/src/cli/commands/compat/providers/index.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/providers/index.mjs +45 -0
- package/dist/src/cli/commands/compat/providers/index.mjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/process.cjs +172 -0
- package/dist/src/cli/commands/compat/providers/process.cjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/process.d.mts +33 -0
- package/dist/src/cli/commands/compat/providers/process.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/providers/process.mjs +159 -0
- package/dist/src/cli/commands/compat/providers/process.mjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/shared.cjs +76 -0
- package/dist/src/cli/commands/compat/providers/shared.cjs.map +1 -0
- package/dist/src/cli/commands/compat/providers/shared.d.mts +40 -0
- package/dist/src/cli/commands/compat/providers/shared.d.mts.map +1 -0
- package/dist/src/cli/commands/compat/providers/shared.mjs +61 -0
- package/dist/src/cli/commands/compat/providers/shared.mjs.map +1 -0
- package/dist/src/cli/commands/compat/types.d.mts +31 -0
- package/dist/src/cli/commands/compat/types.d.mts.map +1 -0
- package/dist/src/cli/commands/doctor/help.cjs +19 -0
- package/dist/src/cli/commands/doctor/help.cjs.map +1 -0
- package/dist/src/cli/commands/doctor/help.d.mts +4 -0
- package/dist/src/cli/commands/doctor/help.d.mts.map +1 -0
- package/dist/src/cli/commands/doctor/help.mjs +19 -0
- package/dist/src/cli/commands/doctor/help.mjs.map +1 -0
- package/dist/src/cli/commands/doctor/index.cjs +118 -0
- package/dist/src/cli/commands/doctor/index.cjs.map +1 -0
- package/dist/src/cli/commands/doctor/index.d.mts +33 -0
- package/dist/src/cli/commands/doctor/index.d.mts.map +1 -0
- package/dist/src/cli/commands/doctor/index.mjs +113 -0
- package/dist/src/cli/commands/doctor/index.mjs.map +1 -0
- package/dist/src/cli/commands/emit-mcp/index.cjs +32 -0
- package/dist/src/cli/commands/emit-mcp/index.cjs.map +1 -0
- package/dist/src/cli/commands/emit-mcp/index.d.mts +19 -0
- package/dist/src/cli/commands/emit-mcp/index.d.mts.map +1 -0
- package/dist/src/cli/commands/emit-mcp/index.mjs +31 -0
- package/dist/src/cli/commands/emit-mcp/index.mjs.map +1 -0
- package/dist/src/cli/commands/explain/index.cjs +155 -0
- package/dist/src/cli/commands/explain/index.cjs.map +1 -0
- package/dist/src/cli/commands/explain/index.d.mts +57 -0
- package/dist/src/cli/commands/explain/index.d.mts.map +1 -0
- package/dist/src/cli/commands/explain/index.mjs +150 -0
- package/dist/src/cli/commands/explain/index.mjs.map +1 -0
- package/dist/src/cli/commands/init/constants.cjs +11 -0
- package/dist/src/cli/commands/init/constants.cjs.map +1 -0
- package/dist/src/cli/commands/init/constants.mjs +11 -0
- package/dist/src/cli/commands/init/constants.mjs.map +1 -0
- package/dist/src/cli/commands/init/help.cjs +17 -0
- package/dist/src/cli/commands/init/help.cjs.map +1 -0
- package/dist/src/cli/commands/init/help.d.mts +4 -0
- package/dist/src/cli/commands/init/help.d.mts.map +1 -0
- package/dist/src/cli/commands/init/help.mjs +17 -0
- package/dist/src/cli/commands/init/help.mjs.map +1 -0
- package/dist/src/cli/commands/init/index.cjs +67 -0
- package/dist/src/cli/commands/init/index.cjs.map +1 -0
- package/dist/src/cli/commands/init/index.d.mts +12 -0
- package/dist/src/cli/commands/init/index.d.mts.map +1 -0
- package/dist/src/cli/commands/init/index.mjs +63 -0
- package/dist/src/cli/commands/init/index.mjs.map +1 -0
- package/dist/src/cli/commands/init/presenter.cjs +140 -0
- package/dist/src/cli/commands/init/presenter.cjs.map +1 -0
- package/dist/src/cli/commands/init/presenter.d.mts +8 -0
- package/dist/src/cli/commands/init/presenter.d.mts.map +1 -0
- package/dist/src/cli/commands/init/presenter.mjs +140 -0
- package/dist/src/cli/commands/init/presenter.mjs.map +1 -0
- package/dist/src/cli/commands/sync/help.cjs +34 -0
- package/dist/src/cli/commands/sync/help.cjs.map +1 -0
- package/dist/src/cli/commands/sync/help.d.mts +4 -0
- package/dist/src/cli/commands/sync/help.d.mts.map +1 -0
- package/dist/src/cli/commands/sync/help.mjs +34 -0
- package/dist/src/cli/commands/sync/help.mjs.map +1 -0
- package/dist/src/cli/commands/sync/index.cjs +215 -0
- package/dist/src/cli/commands/sync/index.cjs.map +1 -0
- package/dist/src/cli/commands/sync/index.d.mts +20 -0
- package/dist/src/cli/commands/sync/index.d.mts.map +1 -0
- package/dist/src/cli/commands/sync/index.mjs +206 -0
- package/dist/src/cli/commands/sync/index.mjs.map +1 -0
- package/dist/src/cli/commands/sync/presenter.cjs +58 -0
- package/dist/src/cli/commands/sync/presenter.cjs.map +1 -0
- package/dist/src/cli/commands/sync/presenter.d.mts +16 -0
- package/dist/src/cli/commands/sync/presenter.d.mts.map +1 -0
- package/dist/src/cli/commands/sync/presenter.mjs +57 -0
- package/dist/src/cli/commands/sync/presenter.mjs.map +1 -0
- package/dist/src/cli/commands/sync/workflow.cjs +16 -0
- package/dist/src/cli/commands/sync/workflow.cjs.map +1 -0
- package/dist/src/cli/commands/sync/workflow.mjs +16 -0
- package/dist/src/cli/commands/sync/workflow.mjs.map +1 -0
- package/dist/src/cli/commands/web/index.cjs +88 -0
- package/dist/src/cli/commands/web/index.cjs.map +1 -0
- package/dist/src/cli/commands/web/index.mjs +85 -0
- package/dist/src/cli/commands/web/index.mjs.map +1 -0
- package/dist/src/cli/harnessOptions.cjs +70 -0
- package/dist/src/cli/harnessOptions.cjs.map +1 -0
- package/dist/src/cli/harnessOptions.d.mts +26 -0
- package/dist/src/cli/harnessOptions.d.mts.map +1 -0
- package/dist/src/cli/harnessOptions.mjs +66 -0
- package/dist/src/cli/harnessOptions.mjs.map +1 -0
- package/dist/src/cli/help.cjs +82 -0
- package/dist/src/cli/help.cjs.map +1 -0
- package/dist/src/cli/help.d.mts +10 -0
- package/dist/src/cli/help.d.mts.map +1 -0
- package/dist/src/cli/help.mjs +78 -0
- package/dist/src/cli/help.mjs.map +1 -0
- package/dist/src/cli/matrixOptions.cjs +178 -0
- package/dist/src/cli/matrixOptions.cjs.map +1 -0
- package/dist/src/cli/matrixOptions.d.mts +90 -0
- package/dist/src/cli/matrixOptions.d.mts.map +1 -0
- package/dist/src/cli/matrixOptions.mjs +151 -0
- package/dist/src/cli/matrixOptions.mjs.map +1 -0
- package/dist/src/cli/options.cjs +281 -0
- package/dist/src/cli/options.cjs.map +1 -0
- package/dist/src/cli/options.d.mts +5 -0
- package/dist/src/cli/options.d.mts.map +1 -0
- package/dist/src/cli/options.mjs +279 -0
- package/dist/src/cli/options.mjs.map +1 -0
- package/dist/src/cli/router.cjs +57 -0
- package/dist/src/cli/router.cjs.map +1 -0
- package/dist/src/cli/router.d.mts +32 -0
- package/dist/src/cli/router.d.mts.map +1 -0
- package/dist/src/cli/router.mjs +54 -0
- package/dist/src/cli/router.mjs.map +1 -0
- package/dist/src/cli/server/options.cjs +71 -0
- package/dist/src/cli/server/options.cjs.map +1 -0
- package/dist/src/cli/server/options.mjs +70 -0
- package/dist/src/cli/server/options.mjs.map +1 -0
- package/dist/src/cli/server/processGuards.cjs +83 -0
- package/dist/src/cli/server/processGuards.cjs.map +1 -0
- package/dist/src/cli/server/processGuards.mjs +79 -0
- package/dist/src/cli/server/processGuards.mjs.map +1 -0
- package/dist/src/cli/server/run.cjs +69 -0
- package/dist/src/cli/server/run.cjs.map +1 -0
- package/dist/src/cli/server/run.mjs +66 -0
- package/dist/src/cli/server/run.mjs.map +1 -0
- package/dist/src/composition/harnessState/index.cjs +48 -0
- package/dist/src/composition/harnessState/index.cjs.map +1 -0
- package/dist/src/composition/harnessState/index.d.mts +19 -0
- package/dist/src/composition/harnessState/index.d.mts.map +1 -0
- package/dist/src/composition/harnessState/index.mjs +34 -0
- package/dist/src/composition/harnessState/index.mjs.map +1 -0
- package/dist/src/composition/layerPackageInstaller/index.cjs +651 -0
- package/dist/src/composition/layerPackageInstaller/index.cjs.map +1 -0
- package/dist/src/composition/layerPackageInstaller/index.d.mts +74 -0
- package/dist/src/composition/layerPackageInstaller/index.d.mts.map +1 -0
- package/dist/src/composition/layerPackageInstaller/index.mjs +637 -0
- package/dist/src/composition/layerPackageInstaller/index.mjs.map +1 -0
- package/dist/src/composition/poHomeMigration/index.cjs +234 -0
- package/dist/src/composition/poHomeMigration/index.cjs.map +1 -0
- package/dist/src/composition/poHomeMigration/index.d.mts +12 -0
- package/dist/src/composition/poHomeMigration/index.d.mts.map +1 -0
- package/dist/src/composition/poHomeMigration/index.mjs +226 -0
- package/dist/src/composition/poHomeMigration/index.mjs.map +1 -0
- package/dist/src/composition/projectTrust/index.cjs +51 -0
- package/dist/src/composition/projectTrust/index.cjs.map +1 -0
- package/dist/src/composition/projectTrust/index.d.mts +22 -0
- package/dist/src/composition/projectTrust/index.d.mts.map +1 -0
- package/dist/src/composition/projectTrust/index.mjs +48 -0
- package/dist/src/composition/projectTrust/index.mjs.map +1 -0
- package/dist/src/composition/repository/index.cjs +68 -0
- package/dist/src/composition/repository/index.cjs.map +1 -0
- package/dist/src/composition/repository/index.d.mts +22 -0
- package/dist/src/composition/repository/index.d.mts.map +1 -0
- package/dist/src/composition/repository/index.mjs +63 -0
- package/dist/src/composition/repository/index.mjs.map +1 -0
- package/dist/src/composition/syncPrune/index.cjs +85 -0
- package/dist/src/composition/syncPrune/index.cjs.map +1 -0
- package/dist/src/composition/syncPrune/index.d.mts +33 -0
- package/dist/src/composition/syncPrune/index.d.mts.map +1 -0
- package/dist/src/composition/syncPrune/index.mjs +82 -0
- package/dist/src/composition/syncPrune/index.mjs.map +1 -0
- package/dist/src/composition/syncService/index.cjs +808 -0
- package/dist/src/composition/syncService/index.cjs.map +1 -0
- package/dist/src/composition/syncService/index.d.mts +65 -0
- package/dist/src/composition/syncService/index.d.mts.map +1 -0
- package/dist/src/composition/syncService/index.mjs +792 -0
- package/dist/src/composition/syncService/index.mjs.map +1 -0
- package/dist/src/composition/syncService/localBuilds.cjs +367 -0
- package/dist/src/composition/syncService/localBuilds.cjs.map +1 -0
- package/dist/src/composition/syncService/localBuilds.mjs +358 -0
- package/dist/src/composition/syncService/localBuilds.mjs.map +1 -0
- package/dist/src/composition/transitionLayers.cjs +17 -0
- package/dist/src/composition/transitionLayers.cjs.map +1 -0
- package/dist/src/composition/transitionLayers.d.mts +6 -0
- package/dist/src/composition/transitionLayers.d.mts.map +1 -0
- package/dist/src/composition/transitionLayers.mjs +16 -0
- package/dist/src/composition/transitionLayers.mjs.map +1 -0
- package/dist/src/composition/types/harness.d.mts +52 -0
- package/dist/src/composition/types/harness.d.mts.map +1 -0
- package/dist/src/extensions/setupDiagnostics.cjs +65 -0
- package/dist/src/extensions/setupDiagnostics.cjs.map +1 -0
- package/dist/src/extensions/setupDiagnostics.mjs +64 -0
- package/dist/src/extensions/setupDiagnostics.mjs.map +1 -0
- package/dist/utils.cjs +4 -0
- package/dist/utils.d.mts +2 -0
- package/dist/utils.mjs +2 -0
- package/dist/webBuilder.cjs +59 -0
- package/dist/webBuilder.d.mts +9 -0
- package/dist/webBuilder.mjs +9 -0
- package/docs/server/api-export.md +86 -0
- package/docs/server/api.md +144 -0
- package/docs/server/getting-started.md +114 -0
- package/docs/server/index.md +67 -0
- package/docs/server/ipc.md +97 -0
- package/docs/server/lifecycle.md +108 -0
- package/docs/server/security.md +101 -0
- package/llms.txt +12 -0
- package/package.json +295 -0
- package/src/prompts/po-author-extension/SKILL.md +35 -0
- package/src/prompts/po-author-extension/references/extension-contract.md +134 -0
|
@@ -0,0 +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":"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"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { ComputerUseRecordingStore } from "./computerUseRecordings.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
5
|
+
import { doomApiCallerFrom } from "@agimon-ai/po-core/packageApi";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
//#region src/builders/server/computerUseBinding.ts
|
|
8
|
+
const VERSION = 1;
|
|
9
|
+
const MAX_BYTES = 8388608;
|
|
10
|
+
const DESKTOP_HEADER = "x-po-desktop";
|
|
11
|
+
/** The capability is discovered over the inherited Desktop IPC channel, never from environment flags. */
|
|
12
|
+
async function createComputerUseBinding(transport = process, remotePolicy = {}) {
|
|
13
|
+
if (!transport.send || !transport.connected) return void 0;
|
|
14
|
+
const recordings = new ComputerUseRecordingStore(await fs.mkdtemp(path.join(os.tmpdir(), "po-recordings-")));
|
|
15
|
+
let available = false;
|
|
16
|
+
let enabled = false;
|
|
17
|
+
let closed = false;
|
|
18
|
+
let token = "";
|
|
19
|
+
let generation;
|
|
20
|
+
const ownedSessions = /* @__PURE__ */ new Set();
|
|
21
|
+
const revokedSessions = /* @__PURE__ */ new Set();
|
|
22
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
23
|
+
const delegations = /* @__PURE__ */ new Map();
|
|
24
|
+
const nativeAuthorized = (headers) => {
|
|
25
|
+
const proof = headers.get(DESKTOP_HEADER) ?? "";
|
|
26
|
+
return available && /^[a-f0-9]{64}$/u.test(proof) && timingSafeEqual(Buffer.from(proof), Buffer.from(token));
|
|
27
|
+
};
|
|
28
|
+
const remoteCaller = (headers) => {
|
|
29
|
+
const caller = doomApiCallerFrom(headers);
|
|
30
|
+
return caller?.locality === "remote" && remotePolicy.isDeviceAuthorized?.(caller.deviceId) === true ? caller : void 0;
|
|
31
|
+
};
|
|
32
|
+
const notify = (sessionId) => {
|
|
33
|
+
for (const [listener, session] of listeners) if (sessionId === void 0 || session === sessionId) listener();
|
|
34
|
+
};
|
|
35
|
+
const revoke = (sessionId, retainRecording = false) => {
|
|
36
|
+
const delegated = delegations.get(sessionId);
|
|
37
|
+
if (delegated === void 0) return;
|
|
38
|
+
if (!retainRecording) delegations.delete(sessionId);
|
|
39
|
+
delegated.expiresAt = void 0;
|
|
40
|
+
delegated.abort.abort();
|
|
41
|
+
revokedSessions.add(sessionId);
|
|
42
|
+
if (delegated.grantId !== void 0 && available) exchange({
|
|
43
|
+
type: "po:computer-use:request",
|
|
44
|
+
sessionId,
|
|
45
|
+
operation: "stop",
|
|
46
|
+
payload: { grantId: delegated.grantId }
|
|
47
|
+
}, 3e4).then((result) => recordings.importStop(sessionId, delegated.grantId, result.result)).catch(() => close());
|
|
48
|
+
notify(sessionId);
|
|
49
|
+
};
|
|
50
|
+
const pending = /* @__PURE__ */ new Map();
|
|
51
|
+
const send = (value) => {
|
|
52
|
+
if (closed || !transport.connected) throw new Error("Desktop computer use is disconnected.");
|
|
53
|
+
transport.send(value, (error) => {
|
|
54
|
+
if (error) close();
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const close = () => {
|
|
58
|
+
if (closed) return;
|
|
59
|
+
closed = true;
|
|
60
|
+
available = false;
|
|
61
|
+
token = "";
|
|
62
|
+
transport.off("message", onMessage);
|
|
63
|
+
transport.off("disconnect", close);
|
|
64
|
+
transport.off("exit", close);
|
|
65
|
+
for (const request of pending.values()) request.finish(void 0, /* @__PURE__ */ new Error("Desktop computer use is disconnected."));
|
|
66
|
+
pending.clear();
|
|
67
|
+
for (const sessionId of delegations.keys()) revoke(sessionId);
|
|
68
|
+
notify();
|
|
69
|
+
listeners.clear();
|
|
70
|
+
recordings.close().catch(() => process.emitWarning("Computer-use recording cleanup failed."));
|
|
71
|
+
};
|
|
72
|
+
const onMessage = (value) => {
|
|
73
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
74
|
+
const message = value;
|
|
75
|
+
if (message.version !== VERSION) return;
|
|
76
|
+
if (message.type === "po:computer-use:availability" && typeof message.enabled === "boolean") {
|
|
77
|
+
enabled = message.enabled;
|
|
78
|
+
for (const sessionId of delegations.keys()) revoke(sessionId, true);
|
|
79
|
+
notify();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (typeof message.requestId !== "string") return;
|
|
83
|
+
if (message.type !== "po:computer-use:ready" && message.type !== "po:computer-use:response") return;
|
|
84
|
+
if (Buffer.byteLength(JSON.stringify(message)) > MAX_BYTES) {
|
|
85
|
+
close();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
pending.get(message.requestId)?.finish(message);
|
|
89
|
+
};
|
|
90
|
+
transport.on("message", onMessage);
|
|
91
|
+
transport.on("disconnect", close);
|
|
92
|
+
transport.on("exit", close);
|
|
93
|
+
const exchange = (value, timeoutMs, signal) => new Promise((resolve, reject) => {
|
|
94
|
+
signal?.throwIfAborted();
|
|
95
|
+
if (closed || pending.size >= 128) throw new Error("Desktop computer-use transport is unavailable or busy.");
|
|
96
|
+
const requestId = randomUUID();
|
|
97
|
+
const cancel = () => {
|
|
98
|
+
try {
|
|
99
|
+
send({
|
|
100
|
+
type: "po:computer-use:cancel",
|
|
101
|
+
version: VERSION,
|
|
102
|
+
requestId
|
|
103
|
+
});
|
|
104
|
+
} catch {}
|
|
105
|
+
};
|
|
106
|
+
const finish = (result, error) => {
|
|
107
|
+
if (!pending.delete(requestId)) return;
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
signal?.removeEventListener("abort", onAbort);
|
|
110
|
+
if (error) reject(error);
|
|
111
|
+
else resolve(result);
|
|
112
|
+
};
|
|
113
|
+
const onAbort = () => {
|
|
114
|
+
cancel();
|
|
115
|
+
finish(void 0, /* @__PURE__ */ new Error("Desktop computer-use request was cancelled."));
|
|
116
|
+
};
|
|
117
|
+
const timer = setTimeout(() => {
|
|
118
|
+
cancel();
|
|
119
|
+
finish(void 0, /* @__PURE__ */ new Error("Desktop computer-use request timed out."));
|
|
120
|
+
}, timeoutMs);
|
|
121
|
+
pending.set(requestId, { finish });
|
|
122
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
123
|
+
try {
|
|
124
|
+
const message = {
|
|
125
|
+
...value,
|
|
126
|
+
version: VERSION,
|
|
127
|
+
requestId
|
|
128
|
+
};
|
|
129
|
+
if (Buffer.byteLength(JSON.stringify(message)) > MAX_BYTES) throw new Error("Desktop computer-use request is too large.");
|
|
130
|
+
send(message);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
finish(void 0, error instanceof Error ? error : new Error(String(error)));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
const ready = await exchange({ type: "po:computer-use:hello" }, 1500);
|
|
137
|
+
if (ready.type !== "po:computer-use:ready" || typeof ready.token !== "string" || !/^[a-f0-9]{64}$/u.test(ready.token)) throw new Error("Invalid Desktop capability handshake.");
|
|
138
|
+
token = ready.token;
|
|
139
|
+
available = true;
|
|
140
|
+
enabled = ready.enabled === true;
|
|
141
|
+
} catch {
|
|
142
|
+
close();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
get available() {
|
|
147
|
+
return available;
|
|
148
|
+
},
|
|
149
|
+
get enabled() {
|
|
150
|
+
return available && enabled;
|
|
151
|
+
},
|
|
152
|
+
authorize: nativeAuthorized,
|
|
153
|
+
readRecording: (scope, artifactId, range) => recordings.read(scope.sessionId, artifactId, range),
|
|
154
|
+
authorizeActivation(headers) {
|
|
155
|
+
if (!available || !enabled) return false;
|
|
156
|
+
if (nativeAuthorized(headers)) return true;
|
|
157
|
+
const caller = remoteCaller(headers);
|
|
158
|
+
return caller !== void 0 && (remotePolicy.stepUpRequired?.() === true ? caller.stepUp === "verified" : caller.stepUp === "not-required");
|
|
159
|
+
},
|
|
160
|
+
authorizePending(sessionId, headers) {
|
|
161
|
+
if (nativeAuthorized(headers)) return true;
|
|
162
|
+
const caller = remoteCaller(headers);
|
|
163
|
+
const delegated = delegations.get(sessionId);
|
|
164
|
+
return available && enabled && caller !== void 0 && delegated !== void 0 && delegated.deviceId === caller.deviceId && delegated.expiresAt === void 0 && !delegated.abort.signal.aborted;
|
|
165
|
+
},
|
|
166
|
+
authorizeRecording(sessionId, headers) {
|
|
167
|
+
if (nativeAuthorized(headers)) return true;
|
|
168
|
+
const caller = remoteCaller(headers);
|
|
169
|
+
const delegated = delegations.get(sessionId);
|
|
170
|
+
return available && caller !== void 0 && delegated?.approved === true && delegated.deviceId === caller.deviceId;
|
|
171
|
+
},
|
|
172
|
+
authorizeSession(sessionId, headers) {
|
|
173
|
+
if (nativeAuthorized(headers)) return true;
|
|
174
|
+
const caller = remoteCaller(headers);
|
|
175
|
+
const delegated = delegations.get(sessionId);
|
|
176
|
+
if (available && enabled && caller !== void 0 && !ownedSessions.has(sessionId)) return true;
|
|
177
|
+
return available && enabled && caller !== void 0 && delegated?.approved === true && delegated.expiresAt !== void 0 && delegated.expiresAt > Date.now() && delegated.deviceId === caller.deviceId && !delegated.abort.signal.aborted;
|
|
178
|
+
},
|
|
179
|
+
claimSession(sessionId, headers) {
|
|
180
|
+
if (!available || !enabled) throw new Error("Desktop computer use is unavailable.");
|
|
181
|
+
const caller = headers === void 0 ? void 0 : remoteCaller(headers);
|
|
182
|
+
if (caller !== void 0) {
|
|
183
|
+
const previous = delegations.get(sessionId);
|
|
184
|
+
if (previous !== void 0 && previous.deviceId !== caller.deviceId) throw new Error("This session belongs to another paired device.");
|
|
185
|
+
if (previous?.abort.signal.aborted) {
|
|
186
|
+
previous.abort = new AbortController();
|
|
187
|
+
previous.grantId = void 0;
|
|
188
|
+
}
|
|
189
|
+
if (previous === void 0) delegations.set(sessionId, {
|
|
190
|
+
deviceId: caller.deviceId,
|
|
191
|
+
approved: false,
|
|
192
|
+
abort: new AbortController()
|
|
193
|
+
});
|
|
194
|
+
} else if (headers !== void 0 && !nativeAuthorized(headers)) throw new Error("Native or paired authorization is required.");
|
|
195
|
+
revokedSessions.delete(sessionId);
|
|
196
|
+
ownedSessions.add(sessionId);
|
|
197
|
+
},
|
|
198
|
+
revokeDevice(deviceId) {
|
|
199
|
+
for (const [sessionId, delegated] of delegations) if (delegated.deviceId === deviceId) revoke(sessionId);
|
|
200
|
+
},
|
|
201
|
+
ownsSession: (sessionId) => ownedSessions.has(sessionId),
|
|
202
|
+
forgetSession: (sessionId) => {
|
|
203
|
+
revoke(sessionId);
|
|
204
|
+
recordings.forgetSession(sessionId).catch(() => process.emitWarning("Computer-use recording cleanup failed."));
|
|
205
|
+
},
|
|
206
|
+
subscribe(listener, sessionId) {
|
|
207
|
+
listeners.set(listener, sessionId);
|
|
208
|
+
return () => {
|
|
209
|
+
listeners.delete(listener);
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
async request(scope, request) {
|
|
213
|
+
if (!available) throw new Error("Desktop computer use is unavailable.");
|
|
214
|
+
if (revokedSessions.has(scope.sessionId) && request.operation !== "stop" && request.operation !== "status") throw new Error("Computer-use delegation was revoked.");
|
|
215
|
+
if (request.operation !== "status" && request.operation !== "targets" && !ownedSessions.has(scope.sessionId)) throw new Error("This session has not been authorized by Desktop.");
|
|
216
|
+
const delegated = delegations.get(scope.sessionId);
|
|
217
|
+
if (delegated !== void 0 && remotePolicy.isDeviceAuthorized?.(delegated.deviceId) !== true) {
|
|
218
|
+
revoke(scope.sessionId);
|
|
219
|
+
if (request.operation !== "stop") throw new Error("The paired device was revoked.");
|
|
220
|
+
}
|
|
221
|
+
if (request.operation === "stop" && delegated !== void 0) {
|
|
222
|
+
delegated.expiresAt = void 0;
|
|
223
|
+
delegated.abort.abort();
|
|
224
|
+
}
|
|
225
|
+
const signal = delegated === void 0 || request.operation === "stop" ? request.signal : request.signal === void 0 ? delegated.abort.signal : AbortSignal.any([request.signal, delegated.abort.signal]);
|
|
226
|
+
const result = await exchange({
|
|
227
|
+
type: "po:computer-use:request",
|
|
228
|
+
sessionId: scope.sessionId,
|
|
229
|
+
operation: request.operation,
|
|
230
|
+
...request.payload === void 0 ? {} : { payload: request.payload }
|
|
231
|
+
}, request.operation === "activate" ? 12e4 : 3e4, signal);
|
|
232
|
+
if (typeof result.hostGeneration !== "string" || generation !== void 0 && generation !== result.hostGeneration) {
|
|
233
|
+
close();
|
|
234
|
+
throw new Error("Desktop generation changed. Authorize a new session.");
|
|
235
|
+
}
|
|
236
|
+
generation = result.hostGeneration;
|
|
237
|
+
if (result.ok !== true) throw new Error(typeof result.error === "string" ? result.error : "Desktop request failed.");
|
|
238
|
+
if (request.operation === "activate" && delegated !== void 0) {
|
|
239
|
+
if (delegations.get(scope.sessionId) !== delegated || delegated.abort.signal.aborted || remotePolicy.isDeviceAuthorized?.(delegated.deviceId) !== true) throw new Error("The paired activation was revoked.");
|
|
240
|
+
delegated.approved = true;
|
|
241
|
+
const grant = result.result;
|
|
242
|
+
if (typeof grant?.expiresAt === "number") delegated.expiresAt = grant.expiresAt;
|
|
243
|
+
if (typeof grant?.grantId === "string") delegated.grantId = grant.grantId;
|
|
244
|
+
}
|
|
245
|
+
if (request.operation === "stop") {
|
|
246
|
+
if (delegated !== void 0) delegated.expiresAt = void 0;
|
|
247
|
+
const grantId = request.payload?.grantId;
|
|
248
|
+
if (typeof grantId !== "string") throw new Error("Recording finalization requires its grant.");
|
|
249
|
+
return recordings.importStop(scope.sessionId, grantId, result.result);
|
|
250
|
+
}
|
|
251
|
+
return result.result;
|
|
252
|
+
},
|
|
253
|
+
close() {
|
|
254
|
+
if (closed) return;
|
|
255
|
+
try {
|
|
256
|
+
send({
|
|
257
|
+
type: "po:computer-use:close",
|
|
258
|
+
version: VERSION
|
|
259
|
+
});
|
|
260
|
+
} finally {
|
|
261
|
+
close();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
export { createComputerUseBinding };
|
|
268
|
+
|
|
269
|
+
//# sourceMappingURL=computerUseBinding.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computerUseBinding.mjs","names":[],"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,IAAI,0BAA0B,MAAM,GAAG,QAAQ,KAAK,KAAK,GAAG,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,KAAK,gBAAgB,OAAO,KAAK,KAAK,GAAG,OAAO,KAAK,KAAK,CAAC;CAC7G;CACA,MAAM,gBAAgB,YAAqB;EACzC,MAAM,SAAS,kBAAkB,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,YAAY,WAAW;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"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
5
|
+
let node_os = require("node:os");
|
|
6
|
+
node_os = require_runtime.__toESM(node_os, 1);
|
|
7
|
+
let node_crypto = require("node:crypto");
|
|
8
|
+
let node_fs_promises = require("node:fs/promises");
|
|
9
|
+
node_fs_promises = require_runtime.__toESM(node_fs_promises, 1);
|
|
10
|
+
//#region src/builders/server/computerUseRecordings.ts
|
|
11
|
+
const MAX_RECORDING_BYTES = 134217728;
|
|
12
|
+
const MAX_STORE_BYTES = 536870912;
|
|
13
|
+
const MAX_RECORDINGS = 8;
|
|
14
|
+
const RETENTION_MS = 864e5;
|
|
15
|
+
const CHUNK_BYTES = 1048576;
|
|
16
|
+
const SAFE_ID = /^[a-zA-Z0-9-]{1,128}$/u;
|
|
17
|
+
/** Imports only grant-bound native IPC receipts, never browser-provided paths. */
|
|
18
|
+
var ComputerUseRecordingStore = class {
|
|
19
|
+
directory;
|
|
20
|
+
stagingRoot;
|
|
21
|
+
recordings = /* @__PURE__ */ new Map();
|
|
22
|
+
imports = /* @__PURE__ */ new Map();
|
|
23
|
+
closed = false;
|
|
24
|
+
incarnations = /* @__PURE__ */ new Map();
|
|
25
|
+
constructor(directory, stagingRoot = node_path.default.join(node_os.default.tmpdir(), "po-computer-use")) {
|
|
26
|
+
this.directory = directory;
|
|
27
|
+
this.stagingRoot = stagingRoot;
|
|
28
|
+
}
|
|
29
|
+
async prune() {
|
|
30
|
+
let bytes = [...this.recordings.values()].reduce((total, item) => total + item.sizeBytes, 0);
|
|
31
|
+
for (const [id, item] of this.recordings) {
|
|
32
|
+
if (Date.now() - Date.parse(item.completedAt) < RETENTION_MS && this.recordings.size <= MAX_RECORDINGS && bytes <= MAX_STORE_BYTES) break;
|
|
33
|
+
this.recordings.delete(id);
|
|
34
|
+
this.imports.delete(`${item.sessionId}\0${item.grantId}`);
|
|
35
|
+
bytes -= item.sizeBytes;
|
|
36
|
+
await node_fs_promises.default.rm(item.file, { force: true });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async importStop(sessionId, grantId, value) {
|
|
40
|
+
if (this.closed) throw new Error("Recording storage is closed.");
|
|
41
|
+
const key = `${sessionId}\0${grantId}`;
|
|
42
|
+
const previous = this.imports.get(key);
|
|
43
|
+
if (previous) return previous;
|
|
44
|
+
const pending = this.importReceipt(sessionId, grantId, value);
|
|
45
|
+
this.imports.set(key, pending);
|
|
46
|
+
try {
|
|
47
|
+
const result = await pending;
|
|
48
|
+
if (!result?.artifact) this.imports.delete(key);
|
|
49
|
+
return result;
|
|
50
|
+
} catch {
|
|
51
|
+
this.imports.delete(key);
|
|
52
|
+
throw new Error("Native recording import failed.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async importReceipt(sessionId, grantId, value) {
|
|
56
|
+
const receipt = value;
|
|
57
|
+
if (receipt?.stopped !== true || receipt.artifact === void 0) return { stopped: receipt?.stopped === true };
|
|
58
|
+
const incarnation = this.incarnations.get(sessionId) ?? 0;
|
|
59
|
+
const artifact = receipt.artifact;
|
|
60
|
+
if (!SAFE_ID.test(grantId) || artifact.kind !== "screen_recording" || artifact.contentType !== "video/mp4" || artifact.audioScope !== "target_application") throw new Error("Invalid native recording receipt.");
|
|
61
|
+
const grantDirectory = node_path.default.join(this.stagingRoot, grantId);
|
|
62
|
+
const source = node_path.default.join(grantDirectory, "recording.mp4");
|
|
63
|
+
if (artifact.path !== source) throw new Error("Invalid native recording location.");
|
|
64
|
+
for (const directory of [this.stagingRoot, grantDirectory]) {
|
|
65
|
+
const stat = await node_fs_promises.default.lstat(directory);
|
|
66
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || (stat.mode & 63) !== 0) throw new Error("Recording staging is not private.");
|
|
67
|
+
}
|
|
68
|
+
const input = await node_fs_promises.default.open(source, node_fs.constants.O_RDONLY | node_fs.constants.O_NOFOLLOW);
|
|
69
|
+
const artifactId = (0, node_crypto.randomUUID)();
|
|
70
|
+
const destination = node_path.default.join(this.directory, `${artifactId}.mp4`);
|
|
71
|
+
const temporary = `${destination}.tmp`;
|
|
72
|
+
let output;
|
|
73
|
+
try {
|
|
74
|
+
const stat = await input.stat();
|
|
75
|
+
if (!stat.isFile() || stat.size < 1 || stat.size > MAX_RECORDING_BYTES) throw new Error("Recording exceeds the permitted size.");
|
|
76
|
+
await node_fs_promises.default.mkdir(this.directory, {
|
|
77
|
+
recursive: true,
|
|
78
|
+
mode: 448
|
|
79
|
+
});
|
|
80
|
+
const root = await node_fs_promises.default.lstat(this.directory);
|
|
81
|
+
if (!root.isDirectory() || root.isSymbolicLink() || (root.mode & 63) !== 0) throw new Error("Recording storage is not private.");
|
|
82
|
+
output = await node_fs_promises.default.open(temporary, "wx", 384);
|
|
83
|
+
const buffer = Buffer.alloc(CHUNK_BYTES);
|
|
84
|
+
let copied = 0;
|
|
85
|
+
while (true) {
|
|
86
|
+
const { bytesRead } = await input.read(buffer, 0, buffer.length, null);
|
|
87
|
+
if (bytesRead === 0) break;
|
|
88
|
+
copied += bytesRead;
|
|
89
|
+
if (copied > MAX_RECORDING_BYTES || copied > stat.size) throw new Error("Recording changed during import.");
|
|
90
|
+
await output.writeFile(buffer.subarray(0, bytesRead));
|
|
91
|
+
}
|
|
92
|
+
if (copied !== stat.size) throw new Error("Recording changed during import.");
|
|
93
|
+
await output.sync();
|
|
94
|
+
await output.close();
|
|
95
|
+
output = void 0;
|
|
96
|
+
if (this.closed || (this.incarnations.get(sessionId) ?? 0) !== incarnation) throw new Error("Recording storage is closed.");
|
|
97
|
+
await node_fs_promises.default.rename(temporary, destination);
|
|
98
|
+
if (this.closed || (this.incarnations.get(sessionId) ?? 0) !== incarnation) {
|
|
99
|
+
await node_fs_promises.default.rm(destination, { force: true });
|
|
100
|
+
throw new Error("Recording storage is closed.");
|
|
101
|
+
}
|
|
102
|
+
const item = {
|
|
103
|
+
sessionId,
|
|
104
|
+
grantId,
|
|
105
|
+
artifactId,
|
|
106
|
+
sizeBytes: copied,
|
|
107
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
108
|
+
file: destination
|
|
109
|
+
};
|
|
110
|
+
this.recordings.set(artifactId, item);
|
|
111
|
+
await this.prune();
|
|
112
|
+
await node_fs_promises.default.unlink(source);
|
|
113
|
+
await node_fs_promises.default.rmdir(grantDirectory);
|
|
114
|
+
return {
|
|
115
|
+
stopped: true,
|
|
116
|
+
artifact: {
|
|
117
|
+
artifactId,
|
|
118
|
+
status: "ready",
|
|
119
|
+
sizeBytes: copied,
|
|
120
|
+
completedAt: item.completedAt
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
} catch {
|
|
124
|
+
this.recordings.delete(artifactId);
|
|
125
|
+
await node_fs_promises.default.rm(temporary, { force: true });
|
|
126
|
+
await node_fs_promises.default.rm(destination, { force: true });
|
|
127
|
+
throw new Error("Native recording import failed.");
|
|
128
|
+
} finally {
|
|
129
|
+
await output?.close();
|
|
130
|
+
await input.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async forgetSession(sessionId) {
|
|
134
|
+
this.incarnations.set(sessionId, (this.incarnations.get(sessionId) ?? 0) + 1);
|
|
135
|
+
const files = [];
|
|
136
|
+
for (const [id, item] of this.recordings) {
|
|
137
|
+
if (item.sessionId !== sessionId) continue;
|
|
138
|
+
this.recordings.delete(id);
|
|
139
|
+
files.push(item.file);
|
|
140
|
+
}
|
|
141
|
+
for (const key of this.imports.keys()) if (key.startsWith(`${sessionId}\0`)) this.imports.delete(key);
|
|
142
|
+
await Promise.all(files.map((file) => node_fs_promises.default.rm(file, { force: true })));
|
|
143
|
+
}
|
|
144
|
+
async close() {
|
|
145
|
+
this.closed = true;
|
|
146
|
+
await Promise.allSettled(this.imports.values());
|
|
147
|
+
this.imports.clear();
|
|
148
|
+
this.recordings.clear();
|
|
149
|
+
await node_fs_promises.default.rm(this.directory, {
|
|
150
|
+
recursive: true,
|
|
151
|
+
force: true
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
async read(sessionId, artifactId, range) {
|
|
155
|
+
if (this.closed) return new Response(null, { status: 404 });
|
|
156
|
+
await this.prune();
|
|
157
|
+
const item = this.recordings.get(artifactId);
|
|
158
|
+
if (!item || item.sessionId !== sessionId) return new Response(null, { status: 404 });
|
|
159
|
+
const match = /^bytes=(\d+)-(\d+)$/u.exec(range ?? "");
|
|
160
|
+
const start = Number(match?.[1]);
|
|
161
|
+
const requestedEnd = Number(match?.[2]);
|
|
162
|
+
if (!match || !Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd) || start < 0 || start >= item.sizeBytes || requestedEnd < start || requestedEnd - start >= CHUNK_BYTES) return new Response(null, { status: 416 });
|
|
163
|
+
const end = Math.min(requestedEnd, item.sizeBytes - 1);
|
|
164
|
+
const input = await node_fs_promises.default.open(item.file, node_fs.constants.O_RDONLY | node_fs.constants.O_NOFOLLOW).catch(() => void 0);
|
|
165
|
+
if (input === void 0) return new Response(null, { status: 410 });
|
|
166
|
+
try {
|
|
167
|
+
const bytes = Buffer.alloc(end - start + 1);
|
|
168
|
+
const { bytesRead } = await input.read(bytes, 0, bytes.length, start);
|
|
169
|
+
if (bytesRead !== bytes.length) return new Response(null, { status: 410 });
|
|
170
|
+
return new Response(bytes, {
|
|
171
|
+
status: 206,
|
|
172
|
+
headers: {
|
|
173
|
+
"content-type": "video/mp4",
|
|
174
|
+
"content-range": `bytes ${start}-${end}/${item.sizeBytes}`,
|
|
175
|
+
"accept-ranges": "bytes",
|
|
176
|
+
"cache-control": "no-store",
|
|
177
|
+
"content-length": String(bytes.length)
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
} finally {
|
|
181
|
+
await input.close();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
//#endregion
|
|
186
|
+
exports.ComputerUseRecordingStore = ComputerUseRecordingStore;
|
|
187
|
+
|
|
188
|
+
//# sourceMappingURL=computerUseRecordings.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computerUseRecordings.cjs","names":["path","os","fs","constants","randomUUID"],"sources":["../../../../src/builders/server/computerUseRecordings.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst MAX_RECORDING_BYTES = 128 * 1024 * 1024;\nconst MAX_STORE_BYTES = 512 * 1024 * 1024;\nconst MAX_RECORDINGS = 8;\nconst RETENTION_MS = 24 * 60 * 60 * 1000;\nconst CHUNK_BYTES = 1024 * 1024;\nconst SAFE_ID = /^[a-zA-Z0-9-]{1,128}$/u;\n\ntype Recording = {\n sessionId: string;\n grantId: string;\n artifactId: string;\n sizeBytes: number;\n completedAt: string;\n file: string;\n};\n\n/** Imports only grant-bound native IPC receipts, never browser-provided paths. */\nexport class ComputerUseRecordingStore {\n private readonly recordings = new Map<string, Recording>();\n private readonly imports = new Map<string, Promise<unknown>>();\n private closed = false;\n private readonly incarnations = new Map<string, number>();\n constructor(\n private readonly directory: string,\n private readonly stagingRoot = path.join(os.tmpdir(), 'po-computer-use'),\n ) {}\n\n private async prune(): Promise<void> {\n let bytes = [...this.recordings.values()].reduce((total, item) => total + item.sizeBytes, 0);\n for (const [id, item] of this.recordings) {\n if (\n Date.now() - Date.parse(item.completedAt) < RETENTION_MS &&\n this.recordings.size <= MAX_RECORDINGS &&\n bytes <= MAX_STORE_BYTES\n )\n break;\n this.recordings.delete(id);\n this.imports.delete(`${item.sessionId}\\0${item.grantId}`);\n bytes -= item.sizeBytes;\n await fs.rm(item.file, { force: true });\n }\n }\n\n async importStop(sessionId: string, grantId: string, value: unknown): Promise<unknown> {\n if (this.closed) throw new Error('Recording storage is closed.');\n const key = `${sessionId}\\0${grantId}`;\n const previous = this.imports.get(key);\n if (previous) return previous;\n const pending = this.importReceipt(sessionId, grantId, value);\n this.imports.set(key, pending);\n try {\n const result = await pending;\n if (!(result as { artifact?: unknown } | undefined)?.artifact) this.imports.delete(key);\n return result;\n } catch {\n this.imports.delete(key);\n throw new Error('Native recording import failed.');\n }\n }\n\n private async importReceipt(sessionId: string, grantId: string, value: unknown): Promise<unknown> {\n const receipt = value as {\n stopped?: unknown;\n artifact?: { kind?: unknown; path?: unknown; contentType?: unknown; audioScope?: unknown };\n } | null;\n if (receipt?.stopped !== true || receipt.artifact === undefined) return { stopped: receipt?.stopped === true };\n const incarnation = this.incarnations.get(sessionId) ?? 0;\n const artifact = receipt.artifact;\n if (\n !SAFE_ID.test(grantId) ||\n artifact.kind !== 'screen_recording' ||\n artifact.contentType !== 'video/mp4' ||\n artifact.audioScope !== 'target_application'\n )\n throw new Error('Invalid native recording receipt.');\n const grantDirectory = path.join(this.stagingRoot, grantId);\n const source = path.join(grantDirectory, 'recording.mp4');\n if (artifact.path !== source) throw new Error('Invalid native recording location.');\n for (const directory of [this.stagingRoot, grantDirectory]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink() || (stat.mode & 0o077) !== 0)\n throw new Error('Recording staging is not private.');\n }\n const input = await fs.open(source, constants.O_RDONLY | constants.O_NOFOLLOW);\n const artifactId = randomUUID();\n const destination = path.join(this.directory, `${artifactId}.mp4`);\n const temporary = `${destination}.tmp`;\n let output: Awaited<ReturnType<typeof fs.open>> | undefined;\n try {\n const stat = await input.stat();\n if (!stat.isFile() || stat.size < 1 || stat.size > MAX_RECORDING_BYTES)\n throw new Error('Recording exceeds the permitted size.');\n await fs.mkdir(this.directory, { recursive: true, mode: 0o700 });\n const root = await fs.lstat(this.directory);\n if (!root.isDirectory() || root.isSymbolicLink() || (root.mode & 0o077) !== 0)\n throw new Error('Recording storage is not private.');\n output = await fs.open(temporary, 'wx', 0o600);\n const buffer = Buffer.alloc(CHUNK_BYTES);\n let copied = 0;\n while (true) {\n const { bytesRead } = await input.read(buffer, 0, buffer.length, null);\n if (bytesRead === 0) break;\n copied += bytesRead;\n if (copied > MAX_RECORDING_BYTES || copied > stat.size) throw new Error('Recording changed during import.');\n await output.writeFile(buffer.subarray(0, bytesRead));\n }\n if (copied !== stat.size) throw new Error('Recording changed during import.');\n await output.sync();\n await output.close();\n output = undefined;\n if (this.closed || (this.incarnations.get(sessionId) ?? 0) !== incarnation)\n throw new Error('Recording storage is closed.');\n await fs.rename(temporary, destination);\n if (this.closed || (this.incarnations.get(sessionId) ?? 0) !== incarnation) {\n await fs.rm(destination, { force: true });\n throw new Error('Recording storage is closed.');\n }\n const item = {\n sessionId,\n grantId,\n artifactId,\n sizeBytes: copied,\n completedAt: new Date().toISOString(),\n file: destination,\n };\n this.recordings.set(artifactId, item);\n await this.prune();\n await fs.unlink(source);\n await fs.rmdir(grantDirectory);\n return {\n stopped: true,\n artifact: { artifactId, status: 'ready', sizeBytes: copied, completedAt: item.completedAt },\n };\n } catch {\n this.recordings.delete(artifactId);\n await fs.rm(temporary, { force: true });\n await fs.rm(destination, { force: true });\n throw new Error('Native recording import failed.');\n } finally {\n await output?.close();\n await input.close();\n }\n }\n\n async forgetSession(sessionId: string): Promise<void> {\n this.incarnations.set(sessionId, (this.incarnations.get(sessionId) ?? 0) + 1);\n const files: string[] = [];\n for (const [id, item] of this.recordings) {\n if (item.sessionId !== sessionId) continue;\n this.recordings.delete(id);\n files.push(item.file);\n }\n for (const key of this.imports.keys()) if (key.startsWith(`${sessionId}\\0`)) this.imports.delete(key);\n await Promise.all(files.map((file) => fs.rm(file, { force: true })));\n }\n\n async close(): Promise<void> {\n this.closed = true;\n await Promise.allSettled(this.imports.values());\n this.imports.clear();\n this.recordings.clear();\n await fs.rm(this.directory, { recursive: true, force: true });\n }\n\n async read(sessionId: string, artifactId: string, range: string | undefined): Promise<Response> {\n if (this.closed) return new Response(null, { status: 404 });\n await this.prune();\n const item = this.recordings.get(artifactId);\n if (!item || item.sessionId !== sessionId) return new Response(null, { status: 404 });\n const match = /^bytes=(\\d+)-(\\d+)$/u.exec(range ?? '');\n const start = Number(match?.[1]);\n const requestedEnd = Number(match?.[2]);\n if (\n !match ||\n !Number.isSafeInteger(start) ||\n !Number.isSafeInteger(requestedEnd) ||\n start < 0 ||\n start >= item.sizeBytes ||\n requestedEnd < start ||\n requestedEnd - start >= CHUNK_BYTES\n )\n return new Response(null, { status: 416 });\n const end = Math.min(requestedEnd, item.sizeBytes - 1);\n const input = await fs.open(item.file, constants.O_RDONLY | constants.O_NOFOLLOW).catch(() => undefined);\n if (input === undefined) return new Response(null, { status: 410 });\n try {\n const bytes = Buffer.alloc(end - start + 1);\n const { bytesRead } = await input.read(bytes, 0, bytes.length, start);\n if (bytesRead !== bytes.length) return new Response(null, { status: 410 });\n return new Response(bytes, {\n status: 206,\n headers: {\n 'content-type': 'video/mp4',\n 'content-range': `bytes ${start}-${end}/${item.sizeBytes}`,\n 'accept-ranges': 'bytes',\n 'cache-control': 'no-store',\n 'content-length': String(bytes.length),\n },\n });\n } finally {\n await input.close();\n }\n }\n}\n"],"mappings":"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"}
|