@mrpatronz/nexusflow 2.9.0 → 2.10.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/README.md +172 -113
- package/dist/agent/AcpCliAdapter.d.ts +2 -0
- package/dist/agent/AcpCliAdapter.d.ts.map +1 -1
- package/dist/agent/AcpCliAdapter.js +50 -4
- package/dist/agent/AcpCliAdapter.js.map +1 -1
- package/dist/agent/AntigravityCliAdapter.d.ts +44 -2
- package/dist/agent/AntigravityCliAdapter.d.ts.map +1 -1
- package/dist/agent/AntigravityCliAdapter.js +363 -16
- package/dist/agent/AntigravityCliAdapter.js.map +1 -1
- package/dist/agent/ClaudeCliAdapter.d.ts +3 -0
- package/dist/agent/ClaudeCliAdapter.d.ts.map +1 -1
- package/dist/agent/ClaudeCliAdapter.js +37 -10
- package/dist/agent/ClaudeCliAdapter.js.map +1 -1
- package/dist/agent/ClaudeSdkAdapter.d.ts +1 -0
- package/dist/agent/ClaudeSdkAdapter.d.ts.map +1 -1
- package/dist/agent/ClaudeSdkAdapter.js +38 -11
- package/dist/agent/ClaudeSdkAdapter.js.map +1 -1
- package/dist/agent/CliAdapterBase.d.ts +2 -0
- package/dist/agent/CliAdapterBase.d.ts.map +1 -1
- package/dist/agent/CliAdapterBase.js +5 -1
- package/dist/agent/CliAdapterBase.js.map +1 -1
- package/dist/agent/CodexCliAdapter.d.ts +1 -1
- package/dist/agent/CodexCliAdapter.d.ts.map +1 -1
- package/dist/agent/CodexCliAdapter.js +5 -4
- package/dist/agent/CodexCliAdapter.js.map +1 -1
- package/dist/agent/CodexSdkAdapter.d.ts.map +1 -1
- package/dist/agent/CodexSdkAdapter.js +4 -1
- package/dist/agent/CodexSdkAdapter.js.map +1 -1
- package/dist/agent/CopilotAcpAdapter.d.ts.map +1 -1
- package/dist/agent/CopilotAcpAdapter.js +2 -2
- package/dist/agent/CopilotAcpAdapter.js.map +1 -1
- package/dist/agent/ProviderRegistry.d.ts +8 -1
- package/dist/agent/ProviderRegistry.d.ts.map +1 -1
- package/dist/agent/ProviderRegistry.js.map +1 -1
- package/dist/agent/TurnSessionManager.d.ts +65 -0
- package/dist/agent/TurnSessionManager.d.ts.map +1 -0
- package/dist/agent/TurnSessionManager.js +330 -0
- package/dist/agent/TurnSessionManager.js.map +1 -0
- package/dist/agent/adapters.d.ts.map +1 -1
- package/dist/agent/adapters.js +23 -6
- package/dist/agent/adapters.js.map +1 -1
- package/dist/agent/cliAvailability.d.ts.map +1 -1
- package/dist/agent/cliAvailability.js +12 -0
- package/dist/agent/cliAvailability.js.map +1 -1
- package/dist/agent/mcp-config.d.ts +0 -5
- package/dist/agent/mcp-config.d.ts.map +1 -1
- package/dist/agent/mcp-config.js +2 -1
- package/dist/agent/mcp-config.js.map +1 -1
- package/dist/agent/models.d.ts +13 -0
- package/dist/agent/models.d.ts.map +1 -1
- package/dist/agent/models.js +262 -8
- package/dist/agent/models.js.map +1 -1
- package/dist/agent/nativeTools.d.ts.map +1 -1
- package/dist/agent/nativeTools.js +1 -0
- package/dist/agent/nativeTools.js.map +1 -1
- package/dist/agent/orchestrator.d.ts.map +1 -1
- package/dist/agent/orchestrator.js +3 -2
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/session.d.ts +1 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js.map +1 -1
- package/dist/analyzers/detect-deps.js +16 -1
- package/dist/analyzers/detect-deps.js.map +1 -1
- package/dist/analyzers/run-analyzer.d.ts.map +1 -1
- package/dist/analyzers/run-analyzer.js.map +1 -1
- package/dist/commands/adapter.d.ts.map +1 -1
- package/dist/commands/adapter.js +15 -14
- package/dist/commands/adapter.js.map +1 -1
- package/dist/commands/add-repo.d.ts.map +1 -1
- package/dist/commands/add-repo.js +6 -5
- package/dist/commands/add-repo.js.map +1 -1
- package/dist/commands/commit.js +6 -8
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +2 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/create.d.ts +4 -4
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +8 -7
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/desktop.d.ts +44 -3
- package/dist/commands/desktop.d.ts.map +1 -1
- package/dist/commands/desktop.js +246 -7
- package/dist/commands/desktop.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +3 -2
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +3 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/finish.js +2 -1
- package/dist/commands/finish.js.map +1 -1
- package/dist/commands/handoff.d.ts.map +1 -1
- package/dist/commands/handoff.js +12 -12
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/isolate.d.ts.map +1 -1
- package/dist/commands/isolate.js +2 -1
- package/dist/commands/isolate.js.map +1 -1
- package/dist/commands/knowledge.d.ts.map +1 -1
- package/dist/commands/knowledge.js +3 -2
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +3 -2
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/logs.js +3 -3
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -2
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +19 -12
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/migrate.d.ts +12 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +128 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/open.d.ts.map +1 -1
- package/dist/commands/open.js +2 -1
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/progress.d.ts.map +1 -1
- package/dist/commands/progress.js +2 -1
- package/dist/commands/progress.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +7 -6
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/refresh.d.ts.map +1 -1
- package/dist/commands/refresh.js +8 -11
- package/dist/commands/refresh.js.map +1 -1
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +2 -1
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/schedule.js +16 -18
- package/dist/commands/schedule.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +9 -8
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/stop.d.ts.map +1 -1
- package/dist/commands/stop.js +2 -1
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/strategy.d.ts.map +1 -1
- package/dist/commands/strategy.js +6 -5
- package/dist/commands/strategy.js.map +1 -1
- package/dist/commands/sync.js +8 -10
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/commands/tui.js +11 -10
- package/dist/commands/tui.js.map +1 -1
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +4 -3
- package/dist/commands/ui.js.map +1 -1
- package/dist/core/adapters/local-storage.d.ts +3 -1
- package/dist/core/adapters/local-storage.d.ts.map +1 -1
- package/dist/core/adapters/local-storage.js +62 -4
- package/dist/core/adapters/local-storage.js.map +1 -1
- package/dist/core/analysis-cache.d.ts.map +1 -1
- package/dist/core/analysis-cache.js +8 -7
- package/dist/core/analysis-cache.js.map +1 -1
- package/dist/core/brand-config.d.ts +261 -0
- package/dist/core/brand-config.d.ts.map +1 -0
- package/dist/core/brand-config.js +514 -0
- package/dist/core/brand-config.js.map +1 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +18 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/constants.d.ts +88 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +103 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +11 -3
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/generation-lock.d.ts +2 -2
- package/dist/core/generation-lock.d.ts.map +1 -1
- package/dist/core/generation-lock.js +49 -22
- package/dist/core/generation-lock.js.map +1 -1
- package/dist/core/knowledge.d.ts +19 -0
- package/dist/core/knowledge.d.ts.map +1 -1
- package/dist/core/knowledge.js +150 -24
- package/dist/core/knowledge.js.map +1 -1
- package/dist/core/migrate.d.ts +53 -0
- package/dist/core/migrate.d.ts.map +1 -0
- package/dist/core/migrate.js +607 -0
- package/dist/core/migrate.js.map +1 -0
- package/dist/core/new-repo.js +1 -1
- package/dist/core/new-repo.js.map +1 -1
- package/dist/core/projects.d.ts.map +1 -1
- package/dist/core/projects.js +13 -4
- package/dist/core/projects.js.map +1 -1
- package/dist/core/refresh.d.ts.map +1 -1
- package/dist/core/refresh.js +6 -4
- package/dist/core/refresh.js.map +1 -1
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +11 -9
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/sync.js +1 -1
- package/dist/core/sync.js.map +1 -1
- package/dist/core/workspace-git.d.ts.map +1 -1
- package/dist/core/workspace-git.js +34 -10
- package/dist/core/workspace-git.js.map +1 -1
- package/dist/core/workspace-state.d.ts +4 -0
- package/dist/core/workspace-state.d.ts.map +1 -1
- package/dist/core/workspace-state.js +8 -8
- package/dist/core/workspace-state.js.map +1 -1
- package/dist/core/workspace.d.ts +3 -4
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +62 -30
- package/dist/core/workspace.js.map +1 -1
- package/dist/desktop-server.js +6 -3
- package/dist/desktop-server.js.map +1 -1
- package/dist/generators/base.d.ts.map +1 -1
- package/dist/generators/base.js +7 -5
- package/dist/generators/base.js.map +1 -1
- package/dist/generators/claude.d.ts.map +1 -1
- package/dist/generators/claude.js +2 -1
- package/dist/generators/claude.js.map +1 -1
- package/dist/generators/cursor.d.ts +1 -1
- package/dist/generators/cursor.d.ts.map +1 -1
- package/dist/generators/cursor.js +9 -4
- package/dist/generators/cursor.js.map +1 -1
- package/dist/generators/index.d.ts +4 -8
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +34 -69
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/plan-generator.d.ts.map +1 -1
- package/dist/generators/plan-generator.js +20 -11
- package/dist/generators/plan-generator.js.map +1 -1
- package/dist/generators/skills-generator.d.ts.map +1 -1
- package/dist/generators/skills-generator.js +26 -11
- package/dist/generators/skills-generator.js.map +1 -1
- package/dist/gui/app_logo.png +0 -0
- package/dist/gui/assets/AgentsPage-CnXzZFo1.js +3 -0
- package/dist/gui/assets/DashboardPage-rm3hKgsw.js +1 -0
- package/dist/gui/assets/GettingStartedPage-BKeuSHZQ.js +1 -0
- package/dist/gui/assets/NullStore-DuSDhGOe.js +1 -0
- package/dist/gui/assets/ProjectsPage-CioaA4uf.js +1 -0
- package/dist/gui/assets/ScaffoldRepoInline-DKZ1rPmE.js +1 -0
- package/dist/gui/assets/Separator-BgWyHDtg.js +1 -0
- package/dist/gui/assets/SettingsPage-BevO8oge.js +1 -0
- package/dist/gui/assets/SkillsPage-D2stJRWy.js +6 -0
- package/dist/gui/assets/StartWorkPage-C8a6eN90.js +1 -0
- package/dist/gui/assets/{StrategiesPage-C36TZgDX.js → StrategiesPage-BRQMly_d.js} +2 -2
- package/dist/gui/assets/WorkroomsPage-KqBkRD-x.js +4 -0
- package/dist/gui/assets/WorkspacesPage-DcA2i5HN.js +2 -0
- package/dist/gui/assets/alert-QnMXzwpU.js +1 -0
- package/dist/gui/assets/alert-dialog-BHEUQVyS.js +1 -0
- package/dist/gui/assets/badge-DTBzXOWn.js +1 -0
- package/dist/gui/assets/brand-hHByyyzq.js +1 -0
- package/dist/gui/assets/button-CREn1mup.js +1 -0
- package/dist/gui/assets/card-yrg6ov8B.js +1 -0
- package/dist/gui/assets/chevron-down-B_Mwbim8.js +1 -0
- package/dist/gui/assets/{circle-plus-lmU1yNJd.js → circle-plus--zM3-3r3.js} +1 -1
- package/dist/gui/assets/{code-xml-jFAa3-TT.js → code-xml-DH-38Tt7.js} +1 -1
- package/dist/gui/assets/{git-branch-Cfj9D8yW.js → git-branch-Ce0I2663.js} +1 -1
- package/dist/gui/assets/index-BB4BuHBQ.css +2 -0
- package/dist/gui/assets/index-BTe-_1Mu.js +57 -0
- package/dist/gui/assets/isElementDisabled-BrwmCB1d.js +1 -0
- package/dist/gui/assets/label-DC730X8s.js +1 -0
- package/dist/gui/assets/layers-j_brOuoa.js +1 -0
- package/dist/gui/assets/menu-d_G9km1-.js +1 -0
- package/dist/gui/assets/shield-check-Dd1Yv3l2.js +1 -0
- package/dist/gui/assets/square-check-big-BSIiYXPI.js +1 -0
- package/dist/gui/assets/tabs-BHwTt4t1.js +1 -0
- package/dist/gui/assets/{trash-2-CtR35_eF.js → trash-2-CMdv3eD3.js} +1 -1
- package/dist/gui/assets/useAriaLabelledBy-BmJErI5a.js +1 -0
- package/dist/gui/assets/useValueChanged-WS99hTmm.js +1 -0
- package/dist/gui/assets/{vsc-DFh0-53h.js → vsc-eMRaEwN3.js} +1 -1
- package/dist/gui/favicon-aurora.svg +8 -0
- package/dist/gui/favicon.ico +0 -0
- package/dist/gui/favicon.svg +8 -1
- package/dist/gui/index.html +21 -12
- package/dist/harness/claude.d.ts.map +1 -1
- package/dist/harness/claude.js +97 -7
- package/dist/harness/claude.js.map +1 -1
- package/dist/harness/codex.d.ts +1 -0
- package/dist/harness/codex.d.ts.map +1 -1
- package/dist/harness/codex.js +51 -34
- package/dist/harness/codex.js.map +1 -1
- package/dist/index.js +57 -27
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +12 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +24 -6
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +4 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +372 -17
- package/dist/mcp/tools.js.map +1 -1
- package/dist/orchestration/detect.d.ts.map +1 -1
- package/dist/orchestration/detect.js +12 -5
- package/dist/orchestration/detect.js.map +1 -1
- package/dist/orchestration/log-tail.js +17 -17
- package/dist/orchestration/log-tail.js.map +1 -1
- package/dist/orchestration/runner.d.ts +1 -1
- package/dist/orchestration/runner.d.ts.map +1 -1
- package/dist/orchestration/runner.js +13 -8
- package/dist/orchestration/runner.js.map +1 -1
- package/dist/resources/agents-catalog.d.ts +3 -1
- package/dist/resources/agents-catalog.d.ts.map +1 -1
- package/dist/resources/agents-catalog.js +12 -6
- package/dist/resources/agents-catalog.js.map +1 -1
- package/dist/resources/contracts.d.ts +7 -0
- package/dist/resources/contracts.d.ts.map +1 -1
- package/dist/resources/contracts.js +8 -1
- package/dist/resources/contracts.js.map +1 -1
- package/dist/resources/fs-safety.d.ts +7 -1
- package/dist/resources/fs-safety.d.ts.map +1 -1
- package/dist/resources/fs-safety.js +44 -1
- package/dist/resources/fs-safety.js.map +1 -1
- package/dist/resources/materializer.d.ts +1 -0
- package/dist/resources/materializer.d.ts.map +1 -1
- package/dist/resources/materializer.js +48 -17
- package/dist/resources/materializer.js.map +1 -1
- package/dist/resources/service.d.ts.map +1 -1
- package/dist/resources/service.js +3 -2
- package/dist/resources/service.js.map +1 -1
- package/dist/server.d.ts +2 -19
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1158 -318
- package/dist/server.js.map +1 -1
- package/dist/static-resources/workflows/cost-aware-sol-luna.md +95 -0
- package/dist/storage/db.d.ts +94 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/db.js +609 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/detect-ai.d.ts.map +1 -1
- package/dist/utils/detect-ai.js +1 -3
- package/dist/utils/detect-ai.js.map +1 -1
- package/dist/utils/multi-git.d.ts +0 -8
- package/dist/utils/multi-git.d.ts.map +1 -1
- package/dist/utils/multi-git.js +12 -4
- package/dist/utils/multi-git.js.map +1 -1
- package/dist/utils/pr.d.ts.map +1 -1
- package/dist/utils/pr.js +10 -5
- package/dist/utils/pr.js.map +1 -1
- package/dist/utils/resolve-workspace.d.ts +1 -1
- package/dist/utils/resolve-workspace.d.ts.map +1 -1
- package/dist/utils/resolve-workspace.js +10 -3
- package/dist/utils/resolve-workspace.js.map +1 -1
- package/dist/utils/session-finder.d.ts +12 -1
- package/dist/utils/session-finder.d.ts.map +1 -1
- package/dist/utils/session-finder.js +183 -0
- package/dist/utils/session-finder.js.map +1 -1
- package/dist/utils/skills-catalog.d.ts +5 -1
- package/dist/utils/skills-catalog.d.ts.map +1 -1
- package/dist/utils/skills-catalog.js +131 -75
- package/dist/utils/skills-catalog.js.map +1 -1
- package/dist/utils/terminal-launch.d.ts +4 -0
- package/dist/utils/terminal-launch.d.ts.map +1 -1
- package/dist/utils/terminal-launch.js +71 -12
- package/dist/utils/terminal-launch.js.map +1 -1
- package/dist/utils/update-check.d.ts +4 -1
- package/dist/utils/update-check.d.ts.map +1 -1
- package/dist/utils/update-check.js +66 -19
- package/dist/utils/update-check.js.map +1 -1
- package/dist/utils/workflows.d.ts +4 -1
- package/dist/utils/workflows.d.ts.map +1 -1
- package/dist/utils/workflows.js +51 -21
- package/dist/utils/workflows.js.map +1 -1
- package/dist/workrooms/client.d.ts +54 -0
- package/dist/workrooms/client.d.ts.map +1 -0
- package/dist/workrooms/client.js +191 -0
- package/dist/workrooms/client.js.map +1 -0
- package/dist/workrooms/contracts.d.ts +579 -0
- package/dist/workrooms/contracts.d.ts.map +1 -0
- package/dist/workrooms/contracts.js +231 -0
- package/dist/workrooms/contracts.js.map +1 -0
- package/dist/workrooms/crypto.d.ts +30 -0
- package/dist/workrooms/crypto.d.ts.map +1 -0
- package/dist/workrooms/crypto.js +72 -0
- package/dist/workrooms/crypto.js.map +1 -0
- package/dist/workrooms/host.d.ts +61 -0
- package/dist/workrooms/host.d.ts.map +1 -0
- package/dist/workrooms/host.js +747 -0
- package/dist/workrooms/host.js.map +1 -0
- package/dist/workrooms/local-resources.d.ts +26 -0
- package/dist/workrooms/local-resources.d.ts.map +1 -0
- package/dist/workrooms/local-resources.js +367 -0
- package/dist/workrooms/local-resources.js.map +1 -0
- package/dist/workrooms/manager.d.ts +228 -0
- package/dist/workrooms/manager.d.ts.map +1 -0
- package/dist/workrooms/manager.js +768 -0
- package/dist/workrooms/manager.js.map +1 -0
- package/dist/workrooms/milestones.d.ts +42 -0
- package/dist/workrooms/milestones.d.ts.map +1 -0
- package/dist/workrooms/milestones.js +94 -0
- package/dist/workrooms/milestones.js.map +1 -0
- package/dist/workrooms/portable.d.ts +21 -0
- package/dist/workrooms/portable.d.ts.map +1 -0
- package/dist/workrooms/portable.js +201 -0
- package/dist/workrooms/portable.js.map +1 -0
- package/dist/workrooms/resource-package.d.ts +7 -0
- package/dist/workrooms/resource-package.d.ts.map +1 -0
- package/dist/workrooms/resource-package.js +260 -0
- package/dist/workrooms/resource-package.js.map +1 -0
- package/dist/workrooms/service.d.ts +92 -0
- package/dist/workrooms/service.d.ts.map +1 -0
- package/dist/workrooms/service.js +788 -0
- package/dist/workrooms/service.js.map +1 -0
- package/dist/workrooms/sqlite-store.d.ts +31 -0
- package/dist/workrooms/sqlite-store.d.ts.map +1 -0
- package/dist/workrooms/sqlite-store.js +511 -0
- package/dist/workrooms/sqlite-store.js.map +1 -0
- package/dist/workrooms/store.d.ts +542 -0
- package/dist/workrooms/store.d.ts.map +1 -0
- package/dist/workrooms/store.js +71 -0
- package/dist/workrooms/store.js.map +1 -0
- package/package.json +16 -10
- package/resources/workflows/cost-aware-sol-luna.md +95 -0
- package/dist/agent/AcpCliAdapter.test.d.ts +0 -2
- package/dist/agent/AcpCliAdapter.test.d.ts.map +0 -1
- package/dist/agent/AcpCliAdapter.test.js +0 -235
- package/dist/agent/AcpCliAdapter.test.js.map +0 -1
- package/dist/agent/AntigravityCliAdapter.test.d.ts +0 -2
- package/dist/agent/AntigravityCliAdapter.test.d.ts.map +0 -1
- package/dist/agent/AntigravityCliAdapter.test.js +0 -222
- package/dist/agent/AntigravityCliAdapter.test.js.map +0 -1
- package/dist/agent/ClaudeCliAdapter.test.d.ts +0 -2
- package/dist/agent/ClaudeCliAdapter.test.d.ts.map +0 -1
- package/dist/agent/ClaudeCliAdapter.test.js +0 -320
- package/dist/agent/ClaudeCliAdapter.test.js.map +0 -1
- package/dist/agent/ClaudeSdkAdapter.test.d.ts +0 -2
- package/dist/agent/ClaudeSdkAdapter.test.d.ts.map +0 -1
- package/dist/agent/ClaudeSdkAdapter.test.js +0 -155
- package/dist/agent/ClaudeSdkAdapter.test.js.map +0 -1
- package/dist/agent/CliAdapterBase.test.d.ts +0 -2
- package/dist/agent/CliAdapterBase.test.d.ts.map +0 -1
- package/dist/agent/CliAdapterBase.test.js +0 -240
- package/dist/agent/CliAdapterBase.test.js.map +0 -1
- package/dist/agent/CodexCliAdapter.test.d.ts +0 -2
- package/dist/agent/CodexCliAdapter.test.d.ts.map +0 -1
- package/dist/agent/CodexCliAdapter.test.js +0 -331
- package/dist/agent/CodexCliAdapter.test.js.map +0 -1
- package/dist/agent/CodexSdkAdapter.test.d.ts +0 -2
- package/dist/agent/CodexSdkAdapter.test.d.ts.map +0 -1
- package/dist/agent/CodexSdkAdapter.test.js +0 -76
- package/dist/agent/CodexSdkAdapter.test.js.map +0 -1
- package/dist/agent/ProviderRegistry.test.d.ts +0 -2
- package/dist/agent/ProviderRegistry.test.d.ts.map +0 -1
- package/dist/agent/ProviderRegistry.test.js +0 -129
- package/dist/agent/ProviderRegistry.test.js.map +0 -1
- package/dist/agent/adapters.test.d.ts +0 -2
- package/dist/agent/adapters.test.d.ts.map +0 -1
- package/dist/agent/adapters.test.js +0 -71
- package/dist/agent/adapters.test.js.map +0 -1
- package/dist/agent/cliAvailability.test.d.ts +0 -2
- package/dist/agent/cliAvailability.test.d.ts.map +0 -1
- package/dist/agent/cliAvailability.test.js +0 -217
- package/dist/agent/cliAvailability.test.js.map +0 -1
- package/dist/agent/mcp-config.test.d.ts +0 -2
- package/dist/agent/mcp-config.test.d.ts.map +0 -1
- package/dist/agent/mcp-config.test.js +0 -22
- package/dist/agent/mcp-config.test.js.map +0 -1
- package/dist/agent/models.test.d.ts +0 -2
- package/dist/agent/models.test.d.ts.map +0 -1
- package/dist/agent/models.test.js +0 -40
- package/dist/agent/models.test.js.map +0 -1
- package/dist/agent/nativeAgent.test.d.ts +0 -2
- package/dist/agent/nativeAgent.test.d.ts.map +0 -1
- package/dist/agent/nativeAgent.test.js +0 -35
- package/dist/agent/nativeAgent.test.js.map +0 -1
- package/dist/agent/nativeTools.test.d.ts +0 -2
- package/dist/agent/nativeTools.test.d.ts.map +0 -1
- package/dist/agent/nativeTools.test.js +0 -67
- package/dist/agent/nativeTools.test.js.map +0 -1
- package/dist/agent/orchestrator.test.d.ts +0 -2
- package/dist/agent/orchestrator.test.d.ts.map +0 -1
- package/dist/agent/orchestrator.test.js +0 -284
- package/dist/agent/orchestrator.test.js.map +0 -1
- package/dist/agent/session.test.d.ts +0 -2
- package/dist/agent/session.test.d.ts.map +0 -1
- package/dist/agent/session.test.js +0 -74
- package/dist/agent/session.test.js.map +0 -1
- package/dist/agent/smoke-chat.test.d.ts +0 -2
- package/dist/agent/smoke-chat.test.d.ts.map +0 -1
- package/dist/agent/smoke-chat.test.js +0 -166
- package/dist/agent/smoke-chat.test.js.map +0 -1
- package/dist/commands/add-repo.test.d.ts +0 -2
- package/dist/commands/add-repo.test.d.ts.map +0 -1
- package/dist/commands/add-repo.test.js +0 -30
- package/dist/commands/add-repo.test.js.map +0 -1
- package/dist/commands/commands.test.d.ts +0 -2
- package/dist/commands/commands.test.d.ts.map +0 -1
- package/dist/commands/commands.test.js +0 -156
- package/dist/commands/commands.test.js.map +0 -1
- package/dist/commands/commit.test.d.ts +0 -2
- package/dist/commands/commit.test.d.ts.map +0 -1
- package/dist/commands/commit.test.js +0 -54
- package/dist/commands/commit.test.js.map +0 -1
- package/dist/commands/isolate.test.d.ts +0 -2
- package/dist/commands/isolate.test.d.ts.map +0 -1
- package/dist/commands/isolate.test.js +0 -59
- package/dist/commands/isolate.test.js.map +0 -1
- package/dist/commands/knowledge.test.d.ts +0 -2
- package/dist/commands/knowledge.test.d.ts.map +0 -1
- package/dist/commands/knowledge.test.js +0 -90
- package/dist/commands/knowledge.test.js.map +0 -1
- package/dist/commands/status.test.d.ts +0 -2
- package/dist/commands/status.test.d.ts.map +0 -1
- package/dist/commands/status.test.js +0 -31
- package/dist/commands/status.test.js.map +0 -1
- package/dist/commands/ui.test.d.ts +0 -2
- package/dist/commands/ui.test.d.ts.map +0 -1
- package/dist/commands/ui.test.js +0 -58
- package/dist/commands/ui.test.js.map +0 -1
- package/dist/core/adapters/storage.test.d.ts +0 -2
- package/dist/core/adapters/storage.test.d.ts.map +0 -1
- package/dist/core/adapters/storage.test.js +0 -65
- package/dist/core/adapters/storage.test.js.map +0 -1
- package/dist/core/analysis-cache.test.d.ts +0 -2
- package/dist/core/analysis-cache.test.d.ts.map +0 -1
- package/dist/core/analysis-cache.test.js +0 -163
- package/dist/core/analysis-cache.test.js.map +0 -1
- package/dist/core/config.test.d.ts +0 -2
- package/dist/core/config.test.d.ts.map +0 -1
- package/dist/core/config.test.js +0 -90
- package/dist/core/config.test.js.map +0 -1
- package/dist/core/finish.test.d.ts +0 -2
- package/dist/core/finish.test.d.ts.map +0 -1
- package/dist/core/finish.test.js +0 -110
- package/dist/core/finish.test.js.map +0 -1
- package/dist/core/generation-lock.test.d.ts +0 -2
- package/dist/core/generation-lock.test.d.ts.map +0 -1
- package/dist/core/generation-lock.test.js +0 -134
- package/dist/core/generation-lock.test.js.map +0 -1
- package/dist/core/isolate.test.d.ts +0 -2
- package/dist/core/isolate.test.d.ts.map +0 -1
- package/dist/core/isolate.test.js +0 -320
- package/dist/core/isolate.test.js.map +0 -1
- package/dist/core/knowledge.test.d.ts +0 -2
- package/dist/core/knowledge.test.d.ts.map +0 -1
- package/dist/core/knowledge.test.js +0 -287
- package/dist/core/knowledge.test.js.map +0 -1
- package/dist/core/locks.test.d.ts +0 -2
- package/dist/core/locks.test.d.ts.map +0 -1
- package/dist/core/locks.test.js +0 -143
- package/dist/core/locks.test.js.map +0 -1
- package/dist/core/new-repo.test.d.ts +0 -2
- package/dist/core/new-repo.test.d.ts.map +0 -1
- package/dist/core/new-repo.test.js +0 -78
- package/dist/core/new-repo.test.js.map +0 -1
- package/dist/core/progress.test.d.ts +0 -2
- package/dist/core/progress.test.d.ts.map +0 -1
- package/dist/core/progress.test.js +0 -52
- package/dist/core/progress.test.js.map +0 -1
- package/dist/core/projects.test.d.ts +0 -2
- package/dist/core/projects.test.d.ts.map +0 -1
- package/dist/core/projects.test.js +0 -165
- package/dist/core/projects.test.js.map +0 -1
- package/dist/core/refresh.test.d.ts +0 -2
- package/dist/core/refresh.test.d.ts.map +0 -1
- package/dist/core/refresh.test.js +0 -93
- package/dist/core/refresh.test.js.map +0 -1
- package/dist/core/scanner.test.d.ts +0 -2
- package/dist/core/scanner.test.d.ts.map +0 -1
- package/dist/core/scanner.test.js +0 -53
- package/dist/core/scanner.test.js.map +0 -1
- package/dist/core/scheduler.test.d.ts +0 -2
- package/dist/core/scheduler.test.d.ts.map +0 -1
- package/dist/core/scheduler.test.js +0 -169
- package/dist/core/scheduler.test.js.map +0 -1
- package/dist/core/sync.test.d.ts +0 -2
- package/dist/core/sync.test.d.ts.map +0 -1
- package/dist/core/sync.test.js +0 -128
- package/dist/core/sync.test.js.map +0 -1
- package/dist/core/workspace-git.test.d.ts +0 -2
- package/dist/core/workspace-git.test.d.ts.map +0 -1
- package/dist/core/workspace-git.test.js +0 -52
- package/dist/core/workspace-git.test.js.map +0 -1
- package/dist/core/workspace-skills-workflow.integration.test.d.ts +0 -2
- package/dist/core/workspace-skills-workflow.integration.test.d.ts.map +0 -1
- package/dist/core/workspace-skills-workflow.integration.test.js +0 -177
- package/dist/core/workspace-skills-workflow.integration.test.js.map +0 -1
- package/dist/core/workspace-state.test.d.ts +0 -2
- package/dist/core/workspace-state.test.d.ts.map +0 -1
- package/dist/core/workspace-state.test.js +0 -84
- package/dist/core/workspace-state.test.js.map +0 -1
- package/dist/core/workspace.create.test.d.ts +0 -2
- package/dist/core/workspace.create.test.d.ts.map +0 -1
- package/dist/core/workspace.create.test.js +0 -224
- package/dist/core/workspace.create.test.js.map +0 -1
- package/dist/core/workspace.test.d.ts +0 -2
- package/dist/core/workspace.test.d.ts.map +0 -1
- package/dist/core/workspace.test.js +0 -53
- package/dist/core/workspace.test.js.map +0 -1
- package/dist/core/worktree.test.d.ts +0 -2
- package/dist/core/worktree.test.d.ts.map +0 -1
- package/dist/core/worktree.test.js +0 -153
- package/dist/core/worktree.test.js.map +0 -1
- package/dist/generators/base.test.d.ts +0 -2
- package/dist/generators/base.test.d.ts.map +0 -1
- package/dist/generators/base.test.js +0 -287
- package/dist/generators/base.test.js.map +0 -1
- package/dist/generators/knowledge-migration.test.d.ts +0 -2
- package/dist/generators/knowledge-migration.test.d.ts.map +0 -1
- package/dist/generators/knowledge-migration.test.js +0 -22
- package/dist/generators/knowledge-migration.test.js.map +0 -1
- package/dist/generators/plan-generator.test.d.ts +0 -2
- package/dist/generators/plan-generator.test.d.ts.map +0 -1
- package/dist/generators/plan-generator.test.js +0 -220
- package/dist/generators/plan-generator.test.js.map +0 -1
- package/dist/generators/skills-generator.test.d.ts +0 -2
- package/dist/generators/skills-generator.test.d.ts.map +0 -1
- package/dist/generators/skills-generator.test.js +0 -79
- package/dist/generators/skills-generator.test.js.map +0 -1
- package/dist/gui/assets/AgentsPage-B4fx43rd.js +0 -3
- package/dist/gui/assets/CSPContext-DBUu9tE0.js +0 -1
- package/dist/gui/assets/DashboardPage-DH52G8IG.js +0 -1
- package/dist/gui/assets/GettingStartedPage-BlTrQhvi.js +0 -1
- package/dist/gui/assets/ProjectsPage-BPVSLPXv.js +0 -1
- package/dist/gui/assets/ScaffoldRepoInline-ywcpq9PX.js +0 -1
- package/dist/gui/assets/SettingsPage-RQr8SalV.js +0 -1
- package/dist/gui/assets/SkillsPage-CYoSRrwx.js +0 -6
- package/dist/gui/assets/StartWorkPage-DBKK4lFi.js +0 -1
- package/dist/gui/assets/WorkspacesPage-DxEONvSq.js +0 -6
- package/dist/gui/assets/alert-dialog-CQAX2yRx.js +0 -1
- package/dist/gui/assets/badge-CAohJGj6.js +0 -1
- package/dist/gui/assets/bot-C2HWrAft.js +0 -1
- package/dist/gui/assets/button-BDDZnIHx.js +0 -1
- package/dist/gui/assets/card-BKXzd_5L.js +0 -1
- package/dist/gui/assets/chevron-down-qTL8HPug.js +0 -1
- package/dist/gui/assets/cpu-Cdwd84Jz.js +0 -1
- package/dist/gui/assets/empty-BdlgbifD.js +0 -1
- package/dist/gui/assets/index-BF6pre6G.css +0 -2
- package/dist/gui/assets/index-C_rwnBzF.js +0 -52
- package/dist/gui/assets/isElementDisabled-uy_EOitH.js +0 -1
- package/dist/gui/assets/label-CG5exjag.js +0 -1
- package/dist/gui/assets/square-DlIYnlSq.js +0 -1
- package/dist/gui/assets/textarea-ByfoCb54.js +0 -1
- package/dist/gui/assets/useAriaLabelledBy-ClesgjMe.js +0 -1
- package/dist/gui/assets/useValueChanged-DeYvGnKs.js +0 -1
- package/dist/gui/assets/zap-CWG_Lvki.js +0 -1
- package/dist/harness/contract.test.d.ts +0 -2
- package/dist/harness/contract.test.d.ts.map +0 -1
- package/dist/harness/contract.test.js +0 -426
- package/dist/harness/contract.test.js.map +0 -1
- package/dist/harness/harness.test.d.ts +0 -2
- package/dist/harness/harness.test.d.ts.map +0 -1
- package/dist/harness/harness.test.js +0 -215
- package/dist/harness/harness.test.js.map +0 -1
- package/dist/mcp/tools.test.d.ts +0 -2
- package/dist/mcp/tools.test.d.ts.map +0 -1
- package/dist/mcp/tools.test.js +0 -194
- package/dist/mcp/tools.test.js.map +0 -1
- package/dist/orchestration/log-tail.test.d.ts +0 -2
- package/dist/orchestration/log-tail.test.d.ts.map +0 -1
- package/dist/orchestration/log-tail.test.js +0 -72
- package/dist/orchestration/log-tail.test.js.map +0 -1
- package/dist/orchestration/orchestrator.test.d.ts +0 -2
- package/dist/orchestration/orchestrator.test.d.ts.map +0 -1
- package/dist/orchestration/orchestrator.test.js +0 -85
- package/dist/orchestration/orchestrator.test.js.map +0 -1
- package/dist/orchestration/runner.test.d.ts +0 -2
- package/dist/orchestration/runner.test.d.ts.map +0 -1
- package/dist/orchestration/runner.test.js +0 -233
- package/dist/orchestration/runner.test.js.map +0 -1
- package/dist/resources/agents-catalog.test.d.ts +0 -2
- package/dist/resources/agents-catalog.test.d.ts.map +0 -1
- package/dist/resources/agents-catalog.test.js +0 -50
- package/dist/resources/agents-catalog.test.js.map +0 -1
- package/dist/resources/codex-agent.test.d.ts +0 -2
- package/dist/resources/codex-agent.test.d.ts.map +0 -1
- package/dist/resources/codex-agent.test.js +0 -27
- package/dist/resources/codex-agent.test.js.map +0 -1
- package/dist/resources/materializer.test.d.ts +0 -2
- package/dist/resources/materializer.test.d.ts.map +0 -1
- package/dist/resources/materializer.test.js +0 -149
- package/dist/resources/materializer.test.js.map +0 -1
- package/dist/resources/service.test.d.ts +0 -2
- package/dist/resources/service.test.d.ts.map +0 -1
- package/dist/resources/service.test.js +0 -58
- package/dist/resources/service.test.js.map +0 -1
- package/dist/server.test.d.ts +0 -2
- package/dist/server.test.d.ts.map +0 -1
- package/dist/server.test.js +0 -1697
- package/dist/server.test.js.map +0 -1
- package/dist/utils/detect-ai.test.d.ts +0 -2
- package/dist/utils/detect-ai.test.d.ts.map +0 -1
- package/dist/utils/detect-ai.test.js +0 -70
- package/dist/utils/detect-ai.test.js.map +0 -1
- package/dist/utils/detect-editors.test.d.ts +0 -2
- package/dist/utils/detect-editors.test.d.ts.map +0 -1
- package/dist/utils/detect-editors.test.js +0 -85
- package/dist/utils/detect-editors.test.js.map +0 -1
- package/dist/utils/git.test.d.ts +0 -2
- package/dist/utils/git.test.d.ts.map +0 -1
- package/dist/utils/git.test.js +0 -125
- package/dist/utils/git.test.js.map +0 -1
- package/dist/utils/multi-git.test.d.ts +0 -2
- package/dist/utils/multi-git.test.d.ts.map +0 -1
- package/dist/utils/multi-git.test.js +0 -330
- package/dist/utils/multi-git.test.js.map +0 -1
- package/dist/utils/pr.test.d.ts +0 -2
- package/dist/utils/pr.test.d.ts.map +0 -1
- package/dist/utils/pr.test.js +0 -47
- package/dist/utils/pr.test.js.map +0 -1
- package/dist/utils/session-finder.test.d.ts +0 -2
- package/dist/utils/session-finder.test.d.ts.map +0 -1
- package/dist/utils/session-finder.test.js +0 -304
- package/dist/utils/session-finder.test.js.map +0 -1
- package/dist/utils/skills-catalog.test.d.ts +0 -2
- package/dist/utils/skills-catalog.test.d.ts.map +0 -1
- package/dist/utils/skills-catalog.test.js +0 -281
- package/dist/utils/skills-catalog.test.js.map +0 -1
- package/dist/utils/system-scanner.test.d.ts +0 -2
- package/dist/utils/system-scanner.test.d.ts.map +0 -1
- package/dist/utils/system-scanner.test.js +0 -73
- package/dist/utils/system-scanner.test.js.map +0 -1
- package/dist/utils/terminal-launch.test.d.ts +0 -2
- package/dist/utils/terminal-launch.test.d.ts.map +0 -1
- package/dist/utils/terminal-launch.test.js +0 -117
- package/dist/utils/terminal-launch.test.js.map +0 -1
- package/dist/utils/update-check.test.d.ts +0 -2
- package/dist/utils/update-check.test.d.ts.map +0 -1
- package/dist/utils/update-check.test.js +0 -30
- package/dist/utils/update-check.test.js.map +0 -1
- package/dist/utils/workflows.test.d.ts +0 -2
- package/dist/utils/workflows.test.d.ts.map +0 -1
- package/dist/utils/workflows.test.js +0 -86
- package/dist/utils/workflows.test.js.map +0 -1
- package/dist/utils/workspace-launch.test.d.ts +0 -2
- package/dist/utils/workspace-launch.test.d.ts.map +0 -1
- package/dist/utils/workspace-launch.test.js +0 -128
- package/dist/utils/workspace-launch.test.js.map +0 -1
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { getConfigDir } from '../core/config.js';
|
|
5
|
+
import { acquireLock } from '../core/locks.js';
|
|
6
|
+
import { loadFeatureConfig } from '../core/workspace.js';
|
|
7
|
+
import { WORKROOM_MAX_DOCUMENT_BYTES, WORKROOM_MAX_RESOURCE_CATALOG_JSON_BYTES, WORKROOM_MAX_WORKFLOW_EVIDENCE_BYTES, WorkroomAuthorizationError, WorkroomValidationError, } from './contracts.js';
|
|
8
|
+
import { PinnedWorkroomClient, formatWorkroomInvite, parseWorkroomInvite } from './client.js';
|
|
9
|
+
import { decryptExport, encryptExport, randomToken, tokenDigest } from './crypto.js';
|
|
10
|
+
import { commitHostedWorkroomCredentialRotation, discardQuarantinedWorkroom, discardHostedWorkroomCredentialRotation, listQuarantinedWorkrooms, listPausedWorkrooms, prepareHostedWorkroomCredentialRotation, resumeHostedWorkroom, startHostedWorkroom, } from './host.js';
|
|
11
|
+
import { applyCachedResource, cacheResourcePackage, digestLocalResourceDefinition, getLocalResourceDefinition, listLocalShareableResources, packageLocalResource, } from './local-resources.js';
|
|
12
|
+
import { buildPublishedHandoff } from './portable.js';
|
|
13
|
+
import { parseResourceDefinition, validateResourcePackage } from './resource-package.js';
|
|
14
|
+
import { workroomExportPayloadSchema } from './store.js';
|
|
15
|
+
export function validateWorkroomExportPayload(input) {
|
|
16
|
+
const parsed = workroomExportPayloadSchema.safeParse(input);
|
|
17
|
+
if (!parsed.success)
|
|
18
|
+
throw new WorkroomValidationError('The Workroom export payload does not match schema version 1.');
|
|
19
|
+
const payload = parsed.data;
|
|
20
|
+
for (const name of ['plan', 'decisions', 'handoff']) {
|
|
21
|
+
const document = payload.room.documents[name];
|
|
22
|
+
if (document.name !== name || Buffer.byteLength(document.content, 'utf8') > WORKROOM_MAX_DOCUMENT_BYTES) {
|
|
23
|
+
throw new WorkroomValidationError(`The exported ${name} document is invalid or too large.`);
|
|
24
|
+
}
|
|
25
|
+
if (document.history.some((revision) => revision.revision >= document.revision || Buffer.byteLength(revision.content, 'utf8') > WORKROOM_MAX_DOCUMENT_BYTES)) {
|
|
26
|
+
throw new WorkroomValidationError(`The exported ${name} document history is invalid.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (let index = 1; index < payload.room.activity.length; index += 1) {
|
|
30
|
+
if (payload.room.activity[index].sequence <= payload.room.activity[index - 1].sequence) {
|
|
31
|
+
throw new WorkroomValidationError('The Workroom export activity sequence is invalid.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const workflowEvidenceBytes = payload.room.workflowProgress?.steps.reduce((total, step) => total + Buffer.byteLength(step.evidence ?? '', 'utf8'), 0) ?? 0;
|
|
35
|
+
if (workflowEvidenceBytes > WORKROOM_MAX_WORKFLOW_EVIDENCE_BYTES) {
|
|
36
|
+
throw new WorkroomValidationError('The Workroom export workflow evidence exceeds the 512 KiB room limit.');
|
|
37
|
+
}
|
|
38
|
+
const packages = payload.packages.map((pkg) => validateResourcePackage(pkg));
|
|
39
|
+
const packageStorageBytes = packages.reduce((total, pkg) => total + Buffer.byteLength(`${JSON.stringify(pkg, null, 2)}\n`, 'utf8'), 0);
|
|
40
|
+
if (packageStorageBytes > WORKROOM_MAX_RESOURCE_CATALOG_JSON_BYTES) {
|
|
41
|
+
throw new WorkroomValidationError('The Workroom export exceeds the 40 MiB shared resource storage limit.');
|
|
42
|
+
}
|
|
43
|
+
const keys = new Set();
|
|
44
|
+
for (const pkg of packages) {
|
|
45
|
+
const key = `${pkg.manifest.kind}:${pkg.manifest.id}:${pkg.manifest.version}`;
|
|
46
|
+
if (keys.has(key))
|
|
47
|
+
throw new WorkroomValidationError('The Workroom export contains duplicate immutable resource versions.');
|
|
48
|
+
keys.add(key);
|
|
49
|
+
}
|
|
50
|
+
const catalog = new Map(payload.room.resources.map((resource) => [resource.digest, resource]));
|
|
51
|
+
if (catalog.size !== packages.length || payload.room.resources.some((resource) => resource.quarantinedAt)) {
|
|
52
|
+
throw new WorkroomValidationError('The Workroom export resource catalog does not match its packages.');
|
|
53
|
+
}
|
|
54
|
+
for (const pkg of packages) {
|
|
55
|
+
const resource = catalog.get(pkg.manifest.digest);
|
|
56
|
+
if (!resource || resource.kind !== pkg.manifest.kind || resource.id !== pkg.manifest.id || resource.version !== pkg.manifest.version) {
|
|
57
|
+
throw new WorkroomValidationError('The Workroom export resource catalog does not match its packages.');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { payload, packages };
|
|
61
|
+
}
|
|
62
|
+
function activeCredentialPath(workspaceId, homeDir = getConfigDir()) {
|
|
63
|
+
const safePrefix = workspaceId.replace(/[^a-zA-Z0-9._-]/g, '-').slice(0, 64) || 'workspace';
|
|
64
|
+
const identity = createHash('sha256').update(workspaceId, 'utf8').digest('hex').slice(0, 16);
|
|
65
|
+
return path.join(homeDir, 'workrooms', 'active', `${safePrefix}-${identity}.json`);
|
|
66
|
+
}
|
|
67
|
+
function processIsAlive(pid) {
|
|
68
|
+
try {
|
|
69
|
+
process.kill(pid, 0);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'EPERM';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function writeActiveCredential(credential, homeDir = getConfigDir()) {
|
|
77
|
+
const destination = activeCredentialPath(credential.workspaceId, homeDir);
|
|
78
|
+
await fs.mkdir(path.dirname(destination), { recursive: true, mode: 0o700 });
|
|
79
|
+
const release = await acquireLock(`${destination}.lock`, {
|
|
80
|
+
staleMs: 60_000,
|
|
81
|
+
timeoutMs: 10_000,
|
|
82
|
+
timeoutMessage: 'Timed out waiting for the active Workroom credential lock.',
|
|
83
|
+
});
|
|
84
|
+
const ownershipId = randomUUID();
|
|
85
|
+
const temp = `${destination}.tmp-${process.pid}-${ownershipId}`;
|
|
86
|
+
try {
|
|
87
|
+
const existing = await fs.readFile(destination, 'utf8')
|
|
88
|
+
.then((raw) => JSON.parse(raw), () => undefined);
|
|
89
|
+
if (existing?.ownerPid && existing.ownershipId && processIsAlive(existing.ownerPid)) {
|
|
90
|
+
throw new WorkroomValidationError('Another running NexusFlow dashboard already owns the Workroom connection for this workspace.');
|
|
91
|
+
}
|
|
92
|
+
await fs.writeFile(temp, JSON.stringify({ ...credential, ownerPid: process.pid, ownershipId }, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
93
|
+
await fs.rename(temp, destination);
|
|
94
|
+
await fs.chmod(destination, 0o600).catch(() => { });
|
|
95
|
+
return ownershipId;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
await fs.unlink(temp).catch(() => { });
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await release();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function removeOwnedActiveCredential(workspaceId, roomId, ownershipId, homeDir = getConfigDir()) {
|
|
106
|
+
const destination = activeCredentialPath(workspaceId, homeDir);
|
|
107
|
+
const release = await acquireLock(`${destination}.lock`, {
|
|
108
|
+
staleMs: 60_000,
|
|
109
|
+
timeoutMs: 10_000,
|
|
110
|
+
timeoutMessage: 'Timed out waiting for the active Workroom credential lock.',
|
|
111
|
+
});
|
|
112
|
+
try {
|
|
113
|
+
const existing = await fs.readFile(destination, 'utf8')
|
|
114
|
+
.then((raw) => JSON.parse(raw), () => undefined);
|
|
115
|
+
if (existing?.roomId === roomId && existing.ownershipId === ownershipId && existing.ownerPid === process.pid) {
|
|
116
|
+
await fs.unlink(destination).catch(() => { });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
await release();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function loadPinnedWorkroomClientForWorkspace(workspaceId) {
|
|
124
|
+
let credential;
|
|
125
|
+
try {
|
|
126
|
+
credential = JSON.parse(await fs.readFile(activeCredentialPath(workspaceId), 'utf8'));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
throw new WorkroomValidationError('No active Workroom connection is registered for this workspace.');
|
|
130
|
+
}
|
|
131
|
+
if (credential.schemaVersion !== 1 || credential.workspaceId !== workspaceId || !credential.agentToken) {
|
|
132
|
+
throw new WorkroomValidationError('The active Workroom connection record is invalid.');
|
|
133
|
+
}
|
|
134
|
+
return new PinnedWorkroomClient(credential.url, credential.fingerprint, credential.agentToken);
|
|
135
|
+
}
|
|
136
|
+
export class WorkroomManager {
|
|
137
|
+
homeDir;
|
|
138
|
+
host;
|
|
139
|
+
guest;
|
|
140
|
+
humanSession;
|
|
141
|
+
activeGeneration = 0;
|
|
142
|
+
activeHumanOperations = 0;
|
|
143
|
+
lifecycleState = 'idle';
|
|
144
|
+
passwordRotationInProgress = false;
|
|
145
|
+
shutdownRequested = false;
|
|
146
|
+
stopPromise;
|
|
147
|
+
lifecycleWaiters = new Set();
|
|
148
|
+
credentialOwnership = new Map();
|
|
149
|
+
recoveryFailures = [];
|
|
150
|
+
constructor(homeDir = getConfigDir()) {
|
|
151
|
+
this.homeDir = homeDir;
|
|
152
|
+
}
|
|
153
|
+
hasActiveRoom() {
|
|
154
|
+
return Boolean(this.host || this.guest);
|
|
155
|
+
}
|
|
156
|
+
activeRoomType() {
|
|
157
|
+
if (this.host)
|
|
158
|
+
return 'host';
|
|
159
|
+
if (this.guest)
|
|
160
|
+
return 'guest';
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
activeRoomChanged() {
|
|
164
|
+
this.activeGeneration += 1;
|
|
165
|
+
this.humanSession = undefined;
|
|
166
|
+
}
|
|
167
|
+
signalLifecycleChange() {
|
|
168
|
+
const waiters = [...this.lifecycleWaiters];
|
|
169
|
+
this.lifecycleWaiters.clear();
|
|
170
|
+
for (const resolve of waiters)
|
|
171
|
+
resolve();
|
|
172
|
+
}
|
|
173
|
+
async waitForLifecycleIdle() {
|
|
174
|
+
while (this.lifecycleState !== 'idle' || this.passwordRotationInProgress || this.activeHumanOperations > 0) {
|
|
175
|
+
await new Promise((resolve) => this.lifecycleWaiters.add(resolve));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
beginLifecycle(next) {
|
|
179
|
+
if (this.shutdownRequested && next !== 'stopping') {
|
|
180
|
+
throw new WorkroomValidationError('The Workroom is shutting down.');
|
|
181
|
+
}
|
|
182
|
+
if (this.passwordRotationInProgress) {
|
|
183
|
+
throw new WorkroomValidationError('A Workroom password rotation is already in progress.');
|
|
184
|
+
}
|
|
185
|
+
if (this.lifecycleState !== 'idle') {
|
|
186
|
+
throw new WorkroomValidationError(`A Workroom lifecycle change is already ${this.lifecycleState}.`);
|
|
187
|
+
}
|
|
188
|
+
if (next !== 'stopping' && next !== 'polling' && (this.host || this.guest)) {
|
|
189
|
+
throw new WorkroomValidationError('Stop or leave the active Workroom first.');
|
|
190
|
+
}
|
|
191
|
+
if (next === 'polling' && !this.guest)
|
|
192
|
+
throw new WorkroomValidationError('No pending Workroom join exists.');
|
|
193
|
+
this.lifecycleState = next;
|
|
194
|
+
}
|
|
195
|
+
endLifecycle(expected) {
|
|
196
|
+
if (this.lifecycleState === expected)
|
|
197
|
+
this.lifecycleState = 'idle';
|
|
198
|
+
this.signalLifecycleChange();
|
|
199
|
+
}
|
|
200
|
+
beginHumanSession() {
|
|
201
|
+
if (!this.host && !this.guest)
|
|
202
|
+
throw new WorkroomValidationError('Join or start a Workroom first.');
|
|
203
|
+
if (this.shutdownRequested || this.lifecycleState === 'stopping') {
|
|
204
|
+
throw new WorkroomValidationError('The Workroom is shutting down.');
|
|
205
|
+
}
|
|
206
|
+
const token = randomToken();
|
|
207
|
+
this.humanSession = { digest: tokenDigest(token), generation: this.activeGeneration };
|
|
208
|
+
return token;
|
|
209
|
+
}
|
|
210
|
+
async reclaimHostHumanSession(password) {
|
|
211
|
+
if (!this.host)
|
|
212
|
+
throw new WorkroomValidationError('Guest connections must leave and rejoin after losing local human authority.');
|
|
213
|
+
const cutoff = Date.now() - 60_000;
|
|
214
|
+
this.recoveryFailures = this.recoveryFailures.filter((attempt) => attempt >= cutoff);
|
|
215
|
+
if (this.recoveryFailures.length >= 5) {
|
|
216
|
+
throw new WorkroomValidationError('Too many failed local recovery attempts. Try again in one minute.');
|
|
217
|
+
}
|
|
218
|
+
if (!(await this.host.service.verifyHostRecoveryPassword(password))) {
|
|
219
|
+
this.recoveryFailures.push(Date.now());
|
|
220
|
+
throw new WorkroomAuthorizationError('The Workroom password is incorrect.');
|
|
221
|
+
}
|
|
222
|
+
this.recoveryFailures = [];
|
|
223
|
+
return this.beginHumanSession();
|
|
224
|
+
}
|
|
225
|
+
abandonLockedGuest() {
|
|
226
|
+
if (!this.guest || this.host)
|
|
227
|
+
throw new WorkroomValidationError('Only a locked guest connection can be abandoned without a human session.');
|
|
228
|
+
return this.stopOrLeave();
|
|
229
|
+
}
|
|
230
|
+
assertHumanSession(token) {
|
|
231
|
+
if (!token || !this.humanSession || tokenDigest(token) !== this.humanSession.digest
|
|
232
|
+
|| this.humanSession.generation !== this.activeGeneration) {
|
|
233
|
+
throw new WorkroomAuthorizationError('A browser-established Workroom human session is required.');
|
|
234
|
+
}
|
|
235
|
+
return this.humanSession;
|
|
236
|
+
}
|
|
237
|
+
assertHumanAuthority(authority) {
|
|
238
|
+
if (!this.humanSession || authority.digest !== this.humanSession.digest
|
|
239
|
+
|| authority.generation !== this.humanSession.generation
|
|
240
|
+
|| authority.generation !== this.activeGeneration) {
|
|
241
|
+
throw new WorkroomAuthorizationError('The Workroom changed after this browser operation was authorized.');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async runWithHumanAuthority(authority, operation) {
|
|
245
|
+
this.assertHumanAuthority(authority);
|
|
246
|
+
if (this.shutdownRequested || this.lifecycleState === 'stopping') {
|
|
247
|
+
throw new WorkroomValidationError('The Workroom is shutting down.');
|
|
248
|
+
}
|
|
249
|
+
this.activeHumanOperations += 1;
|
|
250
|
+
try {
|
|
251
|
+
this.assertHumanAuthority(authority);
|
|
252
|
+
return await operation();
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
this.activeHumanOperations -= 1;
|
|
256
|
+
this.signalLifecycleChange();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async startHost(input) {
|
|
260
|
+
this.beginLifecycle('starting');
|
|
261
|
+
try {
|
|
262
|
+
return await this.startHostUnlocked(input);
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
this.endLifecycle('starting');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async startHostUnlocked(input) {
|
|
269
|
+
const hosted = await startHostedWorkroom({
|
|
270
|
+
homeDir: this.homeDir,
|
|
271
|
+
name: input.name,
|
|
272
|
+
workspaceId: input.workspaceId,
|
|
273
|
+
address: input.address,
|
|
274
|
+
port: input.port,
|
|
275
|
+
password: input.password,
|
|
276
|
+
hostDisplayName: input.hostDisplayName,
|
|
277
|
+
bundle: input.bundle,
|
|
278
|
+
documents: input.documents,
|
|
279
|
+
});
|
|
280
|
+
this.host = hosted;
|
|
281
|
+
this.activeRoomChanged();
|
|
282
|
+
try {
|
|
283
|
+
const ownershipId = await writeActiveCredential({
|
|
284
|
+
schemaVersion: 1,
|
|
285
|
+
workspaceId: input.workspaceId,
|
|
286
|
+
roomId: hosted.roomId,
|
|
287
|
+
url: hosted.url,
|
|
288
|
+
fingerprint: hosted.certificateFingerprint,
|
|
289
|
+
agentToken: hosted.hostAgentToken,
|
|
290
|
+
}, this.homeDir);
|
|
291
|
+
this.credentialOwnership.set(input.workspaceId, { roomId: hosted.roomId, ownershipId });
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
this.host = undefined;
|
|
295
|
+
this.activeRoomChanged();
|
|
296
|
+
this.recoveryFailures = [];
|
|
297
|
+
await hosted.stop().catch(() => { });
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
return this.status();
|
|
301
|
+
}
|
|
302
|
+
listPaused() {
|
|
303
|
+
return listPausedWorkrooms(this.homeDir);
|
|
304
|
+
}
|
|
305
|
+
listQuarantined() {
|
|
306
|
+
return listQuarantinedWorkrooms(this.homeDir);
|
|
307
|
+
}
|
|
308
|
+
discardQuarantined(roomId) {
|
|
309
|
+
if (this.host || this.guest)
|
|
310
|
+
throw new WorkroomValidationError('Stop or leave the active Workroom before discarding a failed import.');
|
|
311
|
+
return discardQuarantinedWorkroom(this.homeDir, roomId);
|
|
312
|
+
}
|
|
313
|
+
async resumeHost(roomId, password) {
|
|
314
|
+
this.beginLifecycle('resuming');
|
|
315
|
+
try {
|
|
316
|
+
const hosted = await resumeHostedWorkroom(this.homeDir, roomId, password);
|
|
317
|
+
this.host = hosted;
|
|
318
|
+
this.activeRoomChanged();
|
|
319
|
+
const state = await hosted.service.store.read();
|
|
320
|
+
try {
|
|
321
|
+
const ownershipId = await writeActiveCredential({
|
|
322
|
+
schemaVersion: 1,
|
|
323
|
+
workspaceId: state.workspaceId,
|
|
324
|
+
roomId: state.roomId,
|
|
325
|
+
url: hosted.url,
|
|
326
|
+
fingerprint: state.certificateFingerprint,
|
|
327
|
+
agentToken: hosted.hostAgentToken,
|
|
328
|
+
}, this.homeDir);
|
|
329
|
+
this.credentialOwnership.set(state.workspaceId, { roomId: state.roomId, ownershipId });
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
this.host = undefined;
|
|
333
|
+
this.activeRoomChanged();
|
|
334
|
+
await hosted.stop().catch(() => { });
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
return this.status();
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
this.endLifecycle('resuming');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
stopOrLeave(authority) {
|
|
344
|
+
if (this.stopPromise)
|
|
345
|
+
return this.stopPromise;
|
|
346
|
+
if (authority)
|
|
347
|
+
this.assertHumanAuthority(authority);
|
|
348
|
+
this.shutdownRequested = true;
|
|
349
|
+
const stopPromise = this.stopOrLeaveOnce();
|
|
350
|
+
this.stopPromise = stopPromise;
|
|
351
|
+
return stopPromise;
|
|
352
|
+
}
|
|
353
|
+
async stopOrLeaveOnce() {
|
|
354
|
+
try {
|
|
355
|
+
await this.waitForLifecycleIdle();
|
|
356
|
+
this.beginLifecycle('stopping');
|
|
357
|
+
const host = this.host;
|
|
358
|
+
const workspaceIds = [
|
|
359
|
+
host ? (await host.service.store.read()).workspaceId : undefined,
|
|
360
|
+
this.guest?.effectiveWorkspaceId ?? this.guest?.localWorkspaceId,
|
|
361
|
+
].filter((value) => Boolean(value));
|
|
362
|
+
const ownedCredentials = workspaceIds.flatMap((workspaceId) => {
|
|
363
|
+
const owned = this.credentialOwnership.get(workspaceId);
|
|
364
|
+
return owned ? [{ workspaceId, ...owned }] : [];
|
|
365
|
+
});
|
|
366
|
+
this.host = undefined;
|
|
367
|
+
this.guest = undefined;
|
|
368
|
+
this.activeRoomChanged();
|
|
369
|
+
if (host)
|
|
370
|
+
await host.stop();
|
|
371
|
+
await Promise.all(ownedCredentials.map((owned) => removeOwnedActiveCredential(owned.workspaceId, owned.roomId, owned.ownershipId, this.homeDir).catch(() => { })));
|
|
372
|
+
for (const workspaceId of workspaceIds)
|
|
373
|
+
this.credentialOwnership.delete(workspaceId);
|
|
374
|
+
}
|
|
375
|
+
finally {
|
|
376
|
+
this.endLifecycle('stopping');
|
|
377
|
+
this.shutdownRequested = false;
|
|
378
|
+
this.stopPromise = undefined;
|
|
379
|
+
this.signalLifecycleChange();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async status() {
|
|
383
|
+
if (this.host) {
|
|
384
|
+
const snapshot = await this.host.service.snapshot(this.host.hostAgentToken);
|
|
385
|
+
return {
|
|
386
|
+
mode: 'host',
|
|
387
|
+
roomId: this.host.roomId,
|
|
388
|
+
name: snapshot.name,
|
|
389
|
+
url: this.host.url,
|
|
390
|
+
localWorkspaceId: (await this.host.service.store.read()).workspaceId,
|
|
391
|
+
certificateFingerprint: this.host.certificateFingerprint,
|
|
392
|
+
snapshot,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (this.guest) {
|
|
396
|
+
let snapshot;
|
|
397
|
+
let connection;
|
|
398
|
+
if (this.guest.status === 'accepted') {
|
|
399
|
+
try {
|
|
400
|
+
snapshot = await this.guest.agentClient.snapshot();
|
|
401
|
+
connection = 'connected';
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
connection = error instanceof WorkroomAuthorizationError ? 'revoked' : 'disconnected';
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
mode: 'guest',
|
|
409
|
+
roomId: this.guest.invite.roomId,
|
|
410
|
+
name: snapshot?.name,
|
|
411
|
+
url: this.guest.invite.url,
|
|
412
|
+
status: this.guest.status,
|
|
413
|
+
connection,
|
|
414
|
+
memberId: this.guest.memberId,
|
|
415
|
+
localWorkspaceId: this.guest.effectiveWorkspaceId ?? this.guest.localWorkspaceId,
|
|
416
|
+
snapshot,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return { mode: 'idle' };
|
|
420
|
+
}
|
|
421
|
+
async createInvite() {
|
|
422
|
+
if (!this.host)
|
|
423
|
+
throw new WorkroomValidationError('Only the active host can create invitations.');
|
|
424
|
+
const created = await this.host.service.createInvite(this.host.hostToken);
|
|
425
|
+
return {
|
|
426
|
+
invite: formatWorkroomInvite({
|
|
427
|
+
schemaVersion: 1,
|
|
428
|
+
url: this.host.url,
|
|
429
|
+
roomId: this.host.roomId,
|
|
430
|
+
token: created.token,
|
|
431
|
+
fingerprint: this.host.certificateFingerprint,
|
|
432
|
+
}),
|
|
433
|
+
expiresAt: created.expiresAt,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async join(inviteText, password, displayName, localWorkspaceId) {
|
|
437
|
+
this.beginLifecycle('joining');
|
|
438
|
+
try {
|
|
439
|
+
const invite = parseWorkroomInvite(inviteText);
|
|
440
|
+
const deviceToken = randomToken();
|
|
441
|
+
const agentToken = randomToken();
|
|
442
|
+
const publicClient = new PinnedWorkroomClient(invite.url, invite.fingerprint);
|
|
443
|
+
const health = await publicClient.health();
|
|
444
|
+
if (health.roomId !== invite.roomId)
|
|
445
|
+
throw new WorkroomAuthorizationError('The invitation does not match this Workroom.');
|
|
446
|
+
const response = await publicClient.requestJoin({
|
|
447
|
+
roomId: invite.roomId,
|
|
448
|
+
inviteToken: invite.token,
|
|
449
|
+
password,
|
|
450
|
+
displayName,
|
|
451
|
+
deviceToken,
|
|
452
|
+
agentToken,
|
|
453
|
+
});
|
|
454
|
+
this.guest = {
|
|
455
|
+
invite,
|
|
456
|
+
displayName: displayName.trim(),
|
|
457
|
+
deviceToken,
|
|
458
|
+
agentToken,
|
|
459
|
+
requestId: response.requestId,
|
|
460
|
+
client: new PinnedWorkroomClient(invite.url, invite.fingerprint, deviceToken),
|
|
461
|
+
agentClient: new PinnedWorkroomClient(invite.url, invite.fingerprint, agentToken),
|
|
462
|
+
localWorkspaceId,
|
|
463
|
+
status: 'pending',
|
|
464
|
+
};
|
|
465
|
+
this.activeRoomChanged();
|
|
466
|
+
return this.status();
|
|
467
|
+
}
|
|
468
|
+
finally {
|
|
469
|
+
this.endLifecycle('joining');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async pollJoin() {
|
|
473
|
+
this.beginLifecycle('polling');
|
|
474
|
+
try {
|
|
475
|
+
const guest = this.guest;
|
|
476
|
+
const result = await guest.client.joinStatus(guest.requestId, guest.deviceToken);
|
|
477
|
+
guest.status = result.status;
|
|
478
|
+
guest.memberId = result.memberId;
|
|
479
|
+
if (result.status === 'accepted') {
|
|
480
|
+
const snapshot = await guest.agentClient.snapshot();
|
|
481
|
+
guest.effectiveWorkspaceId = guest.localWorkspaceId;
|
|
482
|
+
if (guest.effectiveWorkspaceId) {
|
|
483
|
+
const ownershipId = await writeActiveCredential({
|
|
484
|
+
schemaVersion: 1,
|
|
485
|
+
workspaceId: guest.effectiveWorkspaceId,
|
|
486
|
+
roomId: snapshot.roomId,
|
|
487
|
+
url: guest.invite.url,
|
|
488
|
+
fingerprint: guest.invite.fingerprint,
|
|
489
|
+
agentToken: guest.agentToken,
|
|
490
|
+
memberId: result.memberId,
|
|
491
|
+
}, this.homeDir);
|
|
492
|
+
this.credentialOwnership.set(guest.effectiveWorkspaceId, { roomId: snapshot.roomId, ownershipId });
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return this.status();
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
this.endLifecycle('polling');
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
async decideJoin(requestId, accept) {
|
|
502
|
+
if (!this.host)
|
|
503
|
+
throw new WorkroomValidationError('Only the active host can decide join requests.');
|
|
504
|
+
await this.host.service.decideJoin(this.host.hostToken, requestId, accept);
|
|
505
|
+
}
|
|
506
|
+
async setRole(memberId, role) {
|
|
507
|
+
if (!this.host)
|
|
508
|
+
throw new WorkroomValidationError('Only the active host can change member roles.');
|
|
509
|
+
await this.host.service.setParticipantRole(this.host.hostToken, memberId, role);
|
|
510
|
+
}
|
|
511
|
+
async revokeMember(memberId) {
|
|
512
|
+
if (!this.host)
|
|
513
|
+
throw new WorkroomValidationError('Only the active host can revoke members.');
|
|
514
|
+
await this.host.service.revokeParticipant(this.host.hostToken, memberId);
|
|
515
|
+
}
|
|
516
|
+
async rotatePassword(password, revokeDevices = true) {
|
|
517
|
+
const host = this.host;
|
|
518
|
+
if (!host)
|
|
519
|
+
throw new WorkroomValidationError('Only the active host can rotate the password.');
|
|
520
|
+
if (this.shutdownRequested)
|
|
521
|
+
throw new WorkroomValidationError('The Workroom is shutting down.');
|
|
522
|
+
if (this.lifecycleState !== 'idle')
|
|
523
|
+
throw new WorkroomValidationError(`A Workroom lifecycle change is already ${this.lifecycleState}.`);
|
|
524
|
+
if (this.passwordRotationInProgress)
|
|
525
|
+
throw new WorkroomValidationError('A Workroom password rotation is already in progress.');
|
|
526
|
+
this.passwordRotationInProgress = true;
|
|
527
|
+
let stateRotated = false;
|
|
528
|
+
try {
|
|
529
|
+
await prepareHostedWorkroomCredentialRotation(host.roomDir, host.hostToken, host.hostAgentToken, password);
|
|
530
|
+
await host.service.rotatePassword(host.hostToken, password, revokeDevices);
|
|
531
|
+
stateRotated = true;
|
|
532
|
+
await commitHostedWorkroomCredentialRotation(host.roomDir);
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
if (!stateRotated)
|
|
536
|
+
await discardHostedWorkroomCredentialRotation(host.roomDir).catch(() => { });
|
|
537
|
+
throw error;
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
this.passwordRotationInProgress = false;
|
|
541
|
+
this.signalLifecycleChange();
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
activeRoom() {
|
|
545
|
+
if (this.host)
|
|
546
|
+
return { roomId: this.host.roomId, service: this.host.service, token: this.host.hostToken };
|
|
547
|
+
if (this.guest?.status === 'accepted')
|
|
548
|
+
return { roomId: this.guest.invite.roomId, token: this.guest.deviceToken, client: this.guest.client };
|
|
549
|
+
throw new WorkroomValidationError('Join or start a Workroom first.');
|
|
550
|
+
}
|
|
551
|
+
async snapshot() {
|
|
552
|
+
if (this.host)
|
|
553
|
+
return this.host.service.snapshot(this.host.hostAgentToken);
|
|
554
|
+
if (this.guest?.status === 'accepted')
|
|
555
|
+
return this.guest.agentClient.snapshot();
|
|
556
|
+
throw new WorkroomValidationError('Join or start a Workroom first.');
|
|
557
|
+
}
|
|
558
|
+
async updateDocument(name, content, expectedRevision) {
|
|
559
|
+
const active = this.activeRoom();
|
|
560
|
+
return active.service
|
|
561
|
+
? active.service.updateDocument(active.token, name, content, expectedRevision)
|
|
562
|
+
: (await active.client.updateDocument(name, content, expectedRevision)).document;
|
|
563
|
+
}
|
|
564
|
+
async publishHandoff(workspacePath) {
|
|
565
|
+
const feature = await loadFeatureConfig(workspacePath);
|
|
566
|
+
if (!feature)
|
|
567
|
+
throw new WorkroomValidationError('Local workspace not found.');
|
|
568
|
+
const active = this.activeRoom();
|
|
569
|
+
const snapshot = active.service ? await active.service.snapshot(active.token) : await active.client.snapshot();
|
|
570
|
+
const actor = this.host
|
|
571
|
+
? snapshot.participants.find((participant) => participant.role === 'host')
|
|
572
|
+
: snapshot.participants.find((participant) => participant.id === this.guest?.memberId);
|
|
573
|
+
if (!actor)
|
|
574
|
+
throw new WorkroomAuthorizationError('The active Workroom member could not be identified.');
|
|
575
|
+
const handoff = await buildPublishedHandoff(feature, workspacePath, actor.id);
|
|
576
|
+
return active.service
|
|
577
|
+
? active.service.publishHandoff(active.token, handoff.markdown, handoff.repos, snapshot.documents.handoff.revision)
|
|
578
|
+
: (await active.client.publishHandoff(handoff.markdown, handoff.repos, snapshot.documents.handoff.revision)).document;
|
|
579
|
+
}
|
|
580
|
+
listLocalResources() {
|
|
581
|
+
return listLocalShareableResources();
|
|
582
|
+
}
|
|
583
|
+
async publishLocalResource(kind, id, version) {
|
|
584
|
+
const active = this.activeRoom();
|
|
585
|
+
const pkg = await packageLocalResource(kind, id, version);
|
|
586
|
+
return active.service
|
|
587
|
+
? active.service.publishResource(active.token, pkg)
|
|
588
|
+
: (await active.client.publishResource(pkg)).package;
|
|
589
|
+
}
|
|
590
|
+
async downloadResource(digest) {
|
|
591
|
+
const active = this.activeRoom();
|
|
592
|
+
const pkg = active.service
|
|
593
|
+
? await active.service.getResource(active.token, digest)
|
|
594
|
+
: (await active.client.getResource(digest)).package;
|
|
595
|
+
const existingDefinition = await getLocalResourceDefinition(pkg.manifest.kind, pkg.manifest.id);
|
|
596
|
+
return {
|
|
597
|
+
cachePath: await cacheResourcePackage(active.roomId, pkg),
|
|
598
|
+
preview: {
|
|
599
|
+
manifest: pkg.manifest,
|
|
600
|
+
action: existingDefinition ? 'update' : 'create',
|
|
601
|
+
localDigest: digestLocalResourceDefinition(existingDefinition),
|
|
602
|
+
incomingDefinition: JSON.stringify(parseResourceDefinition(pkg), null, 2),
|
|
603
|
+
existingDefinition: existingDefinition ? JSON.stringify(existingDefinition, null, 2) : undefined,
|
|
604
|
+
files: pkg.files.map((file) => {
|
|
605
|
+
const bytes = Buffer.from(file.contentBase64, 'base64');
|
|
606
|
+
let content;
|
|
607
|
+
let encoding;
|
|
608
|
+
try {
|
|
609
|
+
const decoded = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
610
|
+
if (/[^\t\n\r\x20-\x7e\u00a0-\uffff]/u.test(decoded))
|
|
611
|
+
throw new Error('binary');
|
|
612
|
+
content = decoded;
|
|
613
|
+
encoding = 'utf8';
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
content = bytes.toString('base64');
|
|
617
|
+
encoding = 'base64';
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
path: file.path,
|
|
621
|
+
bytes: bytes.length,
|
|
622
|
+
executable: Boolean((file.mode ?? 0) & 0o111),
|
|
623
|
+
encoding,
|
|
624
|
+
content,
|
|
625
|
+
};
|
|
626
|
+
}),
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
async applyResource(digest, approvedDigest, approvedLocalDigest) {
|
|
631
|
+
const active = this.activeRoom();
|
|
632
|
+
return applyCachedResource(active.roomId, digest, approvedDigest, approvedLocalDigest);
|
|
633
|
+
}
|
|
634
|
+
async quarantineResource(digest) {
|
|
635
|
+
if (!this.host)
|
|
636
|
+
throw new WorkroomValidationError('Only the active host can quarantine a shared resource version.');
|
|
637
|
+
await this.host.service.quarantineResource(this.host.hostToken, digest);
|
|
638
|
+
}
|
|
639
|
+
async purgeResource(digest) {
|
|
640
|
+
if (!this.host)
|
|
641
|
+
throw new WorkroomValidationError('Only the active host can purge a quarantined shared resource version.');
|
|
642
|
+
await this.host.service.purgeResource(this.host.hostToken, digest);
|
|
643
|
+
}
|
|
644
|
+
async selectWorkflow(workflow, expectedRevision) {
|
|
645
|
+
const active = this.activeRoom();
|
|
646
|
+
if (active.service)
|
|
647
|
+
await active.service.selectWorkflow(active.token, workflow, expectedRevision);
|
|
648
|
+
else
|
|
649
|
+
await active.client.selectWorkflow(workflow, expectedRevision);
|
|
650
|
+
}
|
|
651
|
+
async selectLocalWorkflow(workflowId, version, steps, expectedRevision) {
|
|
652
|
+
if (!this.host)
|
|
653
|
+
throw new WorkroomValidationError('Only the active host can select a shared workflow.');
|
|
654
|
+
const definition = await getLocalResourceDefinition('workflow', workflowId);
|
|
655
|
+
if (!definition || typeof definition.name !== 'string' || typeof definition.description !== 'string' || typeof definition.content !== 'string') {
|
|
656
|
+
throw new WorkroomValidationError('Select an available local workflow.');
|
|
657
|
+
}
|
|
658
|
+
await this.selectWorkflow({
|
|
659
|
+
schemaVersion: 1,
|
|
660
|
+
id: workflowId,
|
|
661
|
+
version,
|
|
662
|
+
name: definition.name,
|
|
663
|
+
description: definition.description,
|
|
664
|
+
markdown: definition.content,
|
|
665
|
+
steps: [...steps],
|
|
666
|
+
dependencies: [],
|
|
667
|
+
}, expectedRevision);
|
|
668
|
+
}
|
|
669
|
+
async transitionWorkflowStep(stepId, status, expectedRevision, evidence) {
|
|
670
|
+
const active = this.activeRoom();
|
|
671
|
+
return active.service
|
|
672
|
+
? active.service.transitionWorkflowStep(active.token, stepId, status, expectedRevision, evidence)
|
|
673
|
+
: (await active.client.transitionWorkflowStep(stepId, status, expectedRevision, evidence)).step;
|
|
674
|
+
}
|
|
675
|
+
async exportRoom(passphrase) {
|
|
676
|
+
if (!this.host)
|
|
677
|
+
throw new WorkroomValidationError('Only the active host can export the room.');
|
|
678
|
+
if (passphrase.length < 12)
|
|
679
|
+
throw new WorkroomValidationError('Export passphrases must contain at least 12 characters.');
|
|
680
|
+
return encryptExport(await this.host.service.store.createExportPayload(), passphrase);
|
|
681
|
+
}
|
|
682
|
+
async importRoom(envelope, exportPassphrase, host) {
|
|
683
|
+
this.beginLifecycle('importing');
|
|
684
|
+
try {
|
|
685
|
+
return await this.importRoomUnlocked(envelope, exportPassphrase, host);
|
|
686
|
+
}
|
|
687
|
+
finally {
|
|
688
|
+
this.endLifecycle('importing');
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
async importRoomUnlocked(envelope, exportPassphrase, host) {
|
|
692
|
+
let decrypted;
|
|
693
|
+
try {
|
|
694
|
+
decrypted = await decryptExport(envelope, exportPassphrase);
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
throw new WorkroomValidationError('The encrypted Workroom export is invalid or the passphrase is incorrect.');
|
|
698
|
+
}
|
|
699
|
+
const { payload, packages } = validateWorkroomExportPayload(decrypted);
|
|
700
|
+
const roomId = `room-${randomUUID().replace(/-/g, '')}`;
|
|
701
|
+
const roomDir = path.join(this.homeDir, 'workrooms', roomId);
|
|
702
|
+
const importMarker = {
|
|
703
|
+
schemaVersion: 1,
|
|
704
|
+
name: host.name || payload.room.name,
|
|
705
|
+
workspaceId: payload.room.workspaceId,
|
|
706
|
+
};
|
|
707
|
+
await fs.mkdir(roomDir, { recursive: true, mode: 0o700 });
|
|
708
|
+
await fs.writeFile(path.join(roomDir, 'import-in-progress.json'), JSON.stringify({ ...importMarker, startedAt: new Date().toISOString() }), { encoding: 'utf8', mode: 0o600 });
|
|
709
|
+
let importedHost;
|
|
710
|
+
try {
|
|
711
|
+
await this.startHostUnlocked({
|
|
712
|
+
...host,
|
|
713
|
+
roomId,
|
|
714
|
+
name: host.name || payload.room.name,
|
|
715
|
+
workspaceId: payload.room.workspaceId,
|
|
716
|
+
bundle: payload.room.bundle,
|
|
717
|
+
documents: {
|
|
718
|
+
plan: payload.room.documents.plan.content,
|
|
719
|
+
decisions: payload.room.documents.decisions.content,
|
|
720
|
+
handoff: payload.room.documents.handoff.content,
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
importedHost = this.host;
|
|
724
|
+
for (const pkg of packages)
|
|
725
|
+
await importedHost.service.publishResource(importedHost.hostToken, pkg);
|
|
726
|
+
await importedHost.service.store.mutate((state) => {
|
|
727
|
+
const importedActivity = payload.room.activity.slice(-999);
|
|
728
|
+
const sequence = (importedActivity.at(-1)?.sequence ?? 0) + 1;
|
|
729
|
+
const roomCreated = state.activity[0];
|
|
730
|
+
return {
|
|
731
|
+
state: {
|
|
732
|
+
...state,
|
|
733
|
+
revision: state.revision + 1,
|
|
734
|
+
documents: payload.room.documents,
|
|
735
|
+
workflowProgress: payload.room.workflowProgress,
|
|
736
|
+
activity: roomCreated ? [...importedActivity, {
|
|
737
|
+
...roomCreated,
|
|
738
|
+
sequence,
|
|
739
|
+
summary: 'Imported encrypted Workroom backup with new room credentials.',
|
|
740
|
+
}] : importedActivity,
|
|
741
|
+
},
|
|
742
|
+
result: undefined,
|
|
743
|
+
};
|
|
744
|
+
});
|
|
745
|
+
await fs.unlink(path.join(roomDir, 'import-in-progress.json'));
|
|
746
|
+
return this.status();
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
if (this.host) {
|
|
750
|
+
this.host = undefined;
|
|
751
|
+
this.activeRoomChanged();
|
|
752
|
+
}
|
|
753
|
+
await importedHost?.stop().catch(() => { });
|
|
754
|
+
const failureMarkerWritten = await fs.writeFile(path.join(roomDir, 'import-failed.json'), JSON.stringify({ ...importMarker, failedAt: new Date().toISOString() }), { encoding: 'utf8', mode: 0o600 }).then(() => true, () => false);
|
|
755
|
+
if (failureMarkerWritten) {
|
|
756
|
+
await fs.unlink(path.join(roomDir, 'import-in-progress.json')).catch(() => { });
|
|
757
|
+
}
|
|
758
|
+
const owned = this.credentialOwnership.get(payload.room.workspaceId);
|
|
759
|
+
if (owned) {
|
|
760
|
+
await removeOwnedActiveCredential(payload.room.workspaceId, owned.roomId, owned.ownershipId, this.homeDir).catch(() => { });
|
|
761
|
+
this.credentialOwnership.delete(payload.room.workspaceId);
|
|
762
|
+
}
|
|
763
|
+
throw error;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
export const workroomManager = new WorkroomManager();
|
|
768
|
+
//# sourceMappingURL=manager.js.map
|