hoi4-agent-tools 0.2.0 → 1.0.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/CHANGELOG.md +12 -118
- package/README.md +35 -127
- package/SECURITY.md +5 -7
- package/dist/bin/http.js +0 -1
- package/dist/bin/setup.js +141 -92
- package/dist/bin/stdio.js +0 -1
- package/dist/hoi4_agent_tools/core/artifacts.js +0 -1
- package/dist/hoi4_agent_tools/core/canonical.js +0 -1
- package/dist/hoi4_agent_tools/core/configuration.js +28 -28
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js +0 -1
- package/dist/hoi4_agent_tools/core/diagnostics.js +0 -1
- package/dist/hoi4_agent_tools/core/diff.js +0 -1
- package/dist/hoi4_agent_tools/core/engine.js +6 -21
- package/dist/hoi4_agent_tools/core/image-diff.js +0 -1
- package/dist/hoi4_agent_tools/core/index.js +0 -1
- package/dist/hoi4_agent_tools/core/render-budget.js +0 -1
- package/dist/hoi4_agent_tools/core/result.js +0 -1
- package/dist/hoi4_agent_tools/core/scanner.js +0 -1
- package/dist/hoi4_agent_tools/core/server-state.js +0 -1
- package/dist/hoi4_agent_tools/core/source/encoding.js +0 -1
- package/dist/hoi4_agent_tools/core/source/index.js +0 -1
- package/dist/hoi4_agent_tools/core/source/lexer.js +0 -1
- package/dist/hoi4_agent_tools/core/source/limits.js +0 -1
- package/dist/hoi4_agent_tools/core/source/localisation.js +0 -1
- package/dist/hoi4_agent_tools/core/source/parser.js +0 -1
- package/dist/hoi4_agent_tools/core/source/rewrite.js +0 -1
- package/dist/hoi4_agent_tools/core/svg-text.js +0 -1
- package/dist/hoi4_agent_tools/core/transaction-limits.js +0 -1
- package/dist/hoi4_agent_tools/core/transactions.js +2 -30
- package/dist/hoi4_agent_tools/core/workspace.js +112 -396
- package/dist/hoi4_agent_tools/focus/compiler.js +0 -1
- package/dist/hoi4_agent_tools/focus/continuous-render.js +0 -1
- package/dist/hoi4_agent_tools/focus/drift.js +0 -1
- package/dist/hoi4_agent_tools/focus/geometry.js +0 -1
- package/dist/hoi4_agent_tools/focus/importer.js +0 -1
- package/dist/hoi4_agent_tools/focus/index.js +0 -1
- package/dist/hoi4_agent_tools/focus/layout.js +0 -1
- package/dist/hoi4_agent_tools/focus/limits.js +0 -1
- package/dist/hoi4_agent_tools/focus/lint.js +0 -1
- package/dist/hoi4_agent_tools/focus/model.js +0 -1
- package/dist/hoi4_agent_tools/focus/native-effects.js +0 -1
- package/dist/hoi4_agent_tools/focus/planning.js +0 -1
- package/dist/hoi4_agent_tools/focus/presentation.js +0 -1
- package/dist/hoi4_agent_tools/focus/render.js +0 -1
- package/dist/hoi4_agent_tools/focus/service.js +7 -8
- package/dist/hoi4_agent_tools/focus/source-update.js +1 -2
- package/dist/hoi4_agent_tools/gui/animation-manifest.js +0 -1
- package/dist/hoi4_agent_tools/gui/assets.js +0 -1
- package/dist/hoi4_agent_tools/gui/dds.js +0 -1
- package/dist/hoi4_agent_tools/gui/helpers.js +1 -2
- package/dist/hoi4_agent_tools/gui/index.js +0 -1
- package/dist/hoi4_agent_tools/gui/layout.js +0 -1
- package/dist/hoi4_agent_tools/gui/limits.js +0 -1
- package/dist/hoi4_agent_tools/gui/renderer.js +0 -1
- package/dist/hoi4_agent_tools/gui/scenario.js +0 -1
- package/dist/hoi4_agent_tools/gui/source-graph.js +0 -1
- package/dist/hoi4_agent_tools/gui/source-patch.js +1 -2
- package/dist/hoi4_agent_tools/gui/studio.js +247 -104
- package/dist/hoi4_agent_tools/gui/tga.js +0 -1
- package/dist/hoi4_agent_tools/gui/types.js +0 -1
- package/dist/hoi4_agent_tools/gui/validators.js +0 -1
- package/dist/hoi4_agent_tools/map/bmp.js +0 -1
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js +0 -1
- package/dist/hoi4_agent_tools/map/index.js +0 -1
- package/dist/hoi4_agent_tools/map/limits.js +2 -1
- package/dist/hoi4_agent_tools/map/model.js +0 -1
- package/dist/hoi4_agent_tools/map/operations.js +0 -1
- package/dist/hoi4_agent_tools/map/render.js +0 -1
- package/dist/hoi4_agent_tools/map/service.js +171 -33
- package/dist/hoi4_agent_tools/map/validation.js +0 -1
- package/dist/hoi4_agent_tools/mcp/resources/register.js +61 -187
- package/dist/hoi4_agent_tools/mcp/security/auth.js +1 -2
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +24 -750
- package/dist/hoi4_agent_tools/mcp/server/create.js +3 -12
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js +0 -71
- package/dist/hoi4_agent_tools/mcp/server/progress.js +0 -1
- package/dist/hoi4_agent_tools/mcp/server/result.js +24 -30
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +10 -14
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +219 -275
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +149 -250
- package/dist/hoi4_agent_tools/mcp/tools/map.js +256 -337
- package/dist/hoi4_agent_tools/mcp/tools/mods.js +54 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/http.js +1 -12
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js +0 -1
- package/dist/hoi4_agent_tools/runtime.js +5 -2
- package/dist/hoi4_agent_tools/schemas/common.js +1 -2
- package/dist/hoi4_agent_tools/schemas/focus.js +0 -1
- package/dist/hoi4_agent_tools/schemas/map.js +0 -1
- package/dist/hoi4_agent_tools/schemas/transaction.js +0 -1
- package/dist/hoi4_agent_tools/version.js +1 -2
- package/docs/README.md +8 -21
- package/docs/development.md +22 -0
- package/docs/focus.md +40 -0
- package/docs/gui.md +37 -0
- package/docs/http.md +53 -0
- package/docs/map.md +39 -0
- package/docs/setup.md +39 -0
- package/package.json +10 -11
- package/server.json +3 -12
- package/dist/bin/http.d.ts +0 -3
- package/dist/bin/http.d.ts.map +0 -1
- package/dist/bin/http.js.map +0 -1
- package/dist/bin/setup.d.ts +0 -3
- package/dist/bin/setup.d.ts.map +0 -1
- package/dist/bin/setup.js.map +0 -1
- package/dist/bin/stdio.d.ts +0 -3
- package/dist/bin/stdio.d.ts.map +0 -1
- package/dist/bin/stdio.js.map +0 -1
- package/dist/hoi4_agent_tools/core/artifacts.d.ts +0 -117
- package/dist/hoi4_agent_tools/core/artifacts.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/artifacts.js.map +0 -1
- package/dist/hoi4_agent_tools/core/canonical.d.ts +0 -13
- package/dist/hoi4_agent_tools/core/canonical.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/canonical.js.map +0 -1
- package/dist/hoi4_agent_tools/core/configuration.d.ts +0 -138
- package/dist/hoi4_agent_tools/core/configuration.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/configuration.js.map +0 -1
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts +0 -18
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js.map +0 -1
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts +0 -26
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/diagnostics.js.map +0 -1
- package/dist/hoi4_agent_tools/core/diff.d.ts +0 -22
- package/dist/hoi4_agent_tools/core/diff.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/diff.js.map +0 -1
- package/dist/hoi4_agent_tools/core/engine.d.ts +0 -57
- package/dist/hoi4_agent_tools/core/engine.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/engine.js.map +0 -1
- package/dist/hoi4_agent_tools/core/image-diff.d.ts +0 -14
- package/dist/hoi4_agent_tools/core/image-diff.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/image-diff.js.map +0 -1
- package/dist/hoi4_agent_tools/core/index.d.ts +0 -64
- package/dist/hoi4_agent_tools/core/index.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/index.js.map +0 -1
- package/dist/hoi4_agent_tools/core/index.public.d.ts +0 -16
- package/dist/hoi4_agent_tools/core/index.public.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/index.public.js +0 -16
- package/dist/hoi4_agent_tools/core/index.public.js.map +0 -1
- package/dist/hoi4_agent_tools/core/render-budget.d.ts +0 -51
- package/dist/hoi4_agent_tools/core/render-budget.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/render-budget.js.map +0 -1
- package/dist/hoi4_agent_tools/core/result.d.ts +0 -45
- package/dist/hoi4_agent_tools/core/result.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/result.js.map +0 -1
- package/dist/hoi4_agent_tools/core/scanner.d.ts +0 -28
- package/dist/hoi4_agent_tools/core/scanner.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/scanner.js.map +0 -1
- package/dist/hoi4_agent_tools/core/server-state.d.ts +0 -31
- package/dist/hoi4_agent_tools/core/server-state.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/server-state.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts +0 -10
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/encoding.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/index.d.ts +0 -7
- package/dist/hoi4_agent_tools/core/source/index.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/index.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts +0 -30
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/lexer.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/limits.d.ts +0 -21
- package/dist/hoi4_agent_tools/core/source/limits.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/limits.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts +0 -20
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/localisation.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/parser.d.ts +0 -44
- package/dist/hoi4_agent_tools/core/source/parser.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/parser.js.map +0 -1
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts +0 -10
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/source/rewrite.js.map +0 -1
- package/dist/hoi4_agent_tools/core/svg-text.d.ts +0 -34
- package/dist/hoi4_agent_tools/core/svg-text.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/svg-text.js.map +0 -1
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts +0 -9
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/transaction-limits.js.map +0 -1
- package/dist/hoi4_agent_tools/core/transactions.d.ts +0 -164
- package/dist/hoi4_agent_tools/core/transactions.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/transactions.js.map +0 -1
- package/dist/hoi4_agent_tools/core/workspace.d.ts +0 -68
- package/dist/hoi4_agent_tools/core/workspace.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/core/workspace.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/compiler.d.ts +0 -10
- package/dist/hoi4_agent_tools/focus/compiler.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/compiler.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts +0 -32
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/continuous-render.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/drift.d.ts +0 -22
- package/dist/hoi4_agent_tools/focus/drift.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/drift.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/geometry.d.ts +0 -17
- package/dist/hoi4_agent_tools/focus/geometry.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/geometry.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/importer.d.ts +0 -9
- package/dist/hoi4_agent_tools/focus/importer.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/importer.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/index.d.ts +0 -14
- package/dist/hoi4_agent_tools/focus/index.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/index.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/layout.d.ts +0 -5
- package/dist/hoi4_agent_tools/focus/layout.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/layout.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/limits.d.ts +0 -9
- package/dist/hoi4_agent_tools/focus/limits.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/limits.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/lint.d.ts +0 -14
- package/dist/hoi4_agent_tools/focus/lint.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/lint.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/model.d.ts +0 -307
- package/dist/hoi4_agent_tools/focus/model.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/model.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts +0 -9
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/native-effects.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/planning.d.ts +0 -15
- package/dist/hoi4_agent_tools/focus/planning.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/planning.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/presentation.d.ts +0 -24
- package/dist/hoi4_agent_tools/focus/presentation.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/presentation.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/render.d.ts +0 -39
- package/dist/hoi4_agent_tools/focus/render.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/render.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/service.d.ts +0 -82
- package/dist/hoi4_agent_tools/focus/service.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/service.js.map +0 -1
- package/dist/hoi4_agent_tools/focus/source-update.d.ts +0 -6
- package/dist/hoi4_agent_tools/focus/source-update.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/focus/source-update.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts +0 -37
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/animation-manifest.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/assets.d.ts +0 -71
- package/dist/hoi4_agent_tools/gui/assets.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/assets.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/dds.d.ts +0 -17
- package/dist/hoi4_agent_tools/gui/dds.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/dds.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/helpers.d.ts +0 -101
- package/dist/hoi4_agent_tools/gui/helpers.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/helpers.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/index.d.ts +0 -15
- package/dist/hoi4_agent_tools/gui/index.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/index.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/layout.d.ts +0 -6
- package/dist/hoi4_agent_tools/gui/layout.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/layout.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/limits.d.ts +0 -25
- package/dist/hoi4_agent_tools/gui/limits.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/limits.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/renderer.d.ts +0 -18
- package/dist/hoi4_agent_tools/gui/renderer.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/renderer.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/scenario.d.ts +0 -66
- package/dist/hoi4_agent_tools/gui/scenario.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/scenario.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts +0 -7
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/source-graph.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts +0 -13
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/source-patch.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/studio.d.ts +0 -86
- package/dist/hoi4_agent_tools/gui/studio.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/studio.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/tga.d.ts +0 -16
- package/dist/hoi4_agent_tools/gui/tga.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/tga.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/types.d.ts +0 -322
- package/dist/hoi4_agent_tools/gui/types.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/types.js.map +0 -1
- package/dist/hoi4_agent_tools/gui/validators.d.ts +0 -8
- package/dist/hoi4_agent_tools/gui/validators.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/gui/validators.js.map +0 -1
- package/dist/hoi4_agent_tools/map/bmp.d.ts +0 -62
- package/dist/hoi4_agent_tools/map/bmp.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/bmp.js.map +0 -1
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts +0 -5
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js.map +0 -1
- package/dist/hoi4_agent_tools/map/index.d.ts +0 -8
- package/dist/hoi4_agent_tools/map/index.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/index.js.map +0 -1
- package/dist/hoi4_agent_tools/map/limits.d.ts +0 -13
- package/dist/hoi4_agent_tools/map/limits.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/limits.js.map +0 -1
- package/dist/hoi4_agent_tools/map/model.d.ts +0 -269
- package/dist/hoi4_agent_tools/map/model.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/model.js.map +0 -1
- package/dist/hoi4_agent_tools/map/operations.d.ts +0 -401
- package/dist/hoi4_agent_tools/map/operations.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/operations.js.map +0 -1
- package/dist/hoi4_agent_tools/map/render.d.ts +0 -90
- package/dist/hoi4_agent_tools/map/render.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/render.js.map +0 -1
- package/dist/hoi4_agent_tools/map/service.d.ts +0 -81
- package/dist/hoi4_agent_tools/map/service.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/service.js.map +0 -1
- package/dist/hoi4_agent_tools/map/validation.d.ts +0 -23
- package/dist/hoi4_agent_tools/map/validation.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/map/validation.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts +0 -4
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/prompts/register.js +0 -72
- package/dist/hoi4_agent_tools/mcp/prompts/register.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts +0 -9
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js +0 -10
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts +0 -5
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/resources/register.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts +0 -24
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/security/auth.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts +0 -47
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts +0 -5
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/create.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts +0 -138
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts +0 -8
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/progress.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts +0 -232
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/result.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.d.ts +0 -31
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts +0 -5
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/tools/focus.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts +0 -117
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/tools/gui.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts +0 -5
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/tools/map.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts +0 -58
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts +0 -26
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts +0 -28
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/http.js.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts +0 -15
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js.map +0 -1
- package/dist/hoi4_agent_tools/runtime.d.ts +0 -4
- package/dist/hoi4_agent_tools/runtime.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/runtime.js.map +0 -1
- package/dist/hoi4_agent_tools/schemas/common.d.ts +0 -74
- package/dist/hoi4_agent_tools/schemas/common.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/schemas/common.js.map +0 -1
- package/dist/hoi4_agent_tools/schemas/focus.d.ts +0 -784
- package/dist/hoi4_agent_tools/schemas/focus.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/schemas/focus.js.map +0 -1
- package/dist/hoi4_agent_tools/schemas/index.d.ts +0 -5
- package/dist/hoi4_agent_tools/schemas/index.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/schemas/index.js +0 -5
- package/dist/hoi4_agent_tools/schemas/index.js.map +0 -1
- package/dist/hoi4_agent_tools/schemas/map.d.ts +0 -654
- package/dist/hoi4_agent_tools/schemas/map.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/schemas/map.js.map +0 -1
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts +0 -259
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/schemas/transaction.js.map +0 -1
- package/dist/hoi4_agent_tools/version.d.ts +0 -6
- package/dist/hoi4_agent_tools/version.d.ts.map +0 -1
- package/dist/hoi4_agent_tools/version.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +0 -1
- package/docs/adr/0001-runtime-and-language.md +0 -16
- package/docs/adr/0002-lossless-source-model.md +0 -18
- package/docs/adr/0003-image-and-font-codecs.md +0 -18
- package/docs/adr/0004-focus-layout.md +0 -16
- package/docs/adr/0005-offline-rendering.md +0 -18
- package/docs/adr/0006-mcp-sdk-and-protocol.md +0 -16
- package/docs/adr/0007-storage-and-transactions.md +0 -24
- package/docs/adr/0008-test-strategy.md +0 -18
- package/docs/adr/0009-packaging-and-registry.md +0 -16
- package/docs/adr/0010-unsupported-and-refusal-policy.md +0 -18
- package/docs/adr/0011-http-security.md +0 -16
- package/docs/adr/0012-autonomous-rewrites.md +0 -55
- package/docs/adr/README.md +0 -14
- package/docs/agent-integration.md +0 -146
- package/docs/architecture.md +0 -76
- package/docs/artifacts.md +0 -136
- package/docs/compatibility.md +0 -33
- package/docs/completion-report.md +0 -122
- package/docs/configuration.md +0 -305
- package/docs/focus-workflow.md +0 -197
- package/docs/gui-workflow.md +0 -125
- package/docs/limitations.md +0 -32
- package/docs/map-workflow.md +0 -95
- package/docs/performance.md +0 -117
- package/docs/release.md +0 -222
- package/docs/research/mcp-2026-07-10.md +0 -52
- package/docs/research/source-ledger.md +0 -68
- package/docs/security.md +0 -145
- package/docs/self-hosting.md +0 -170
- package/docs/testing.md +0 -43
- package/docs/third-party-notices.md +0 -11
- package/docs/transactions.md +0 -180
- package/docs/troubleshooting.md +0 -54
- package/schemas/configuration.schema.json +0 -795
- package/schemas/continuous-focus-palette.schema.json +0 -325
- package/schemas/focus-plan.schema.json +0 -927
- package/schemas/focus-planning-sidecar.schema.json +0 -379
- package/schemas/gui-animation-source.schema.json +0 -176
- package/schemas/gui-helper.schema.json +0 -301
- package/schemas/gui-scenario.schema.json +0 -298
- package/schemas/map-operation.schema.json +0 -2284
- package/schemas/operation-result.schema.json +0 -344
- package/schemas/transaction-manifest.schema.json +0 -685
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { lstat, mkdir, opendir, realpath } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { compareCodeUnits, canonicalJson, hashCanonical, secureId, sha256Bytes, } from './canonical.js';
|
|
3
|
+
import { WORKSPACE_MAX_REGISTRATIONS, workspaceRegistrationSchema, } from './configuration.js';
|
|
4
|
+
import { compareCodeUnits, hashCanonical, sha256Bytes } from './canonical.js';
|
|
6
5
|
import { ServiceError } from './result.js';
|
|
7
6
|
import { ServerState } from './server-state.js';
|
|
8
|
-
const runtimeRegistrationClaimFile = '.runtime-registration-owner.json';
|
|
9
|
-
const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
10
|
-
const runtimeRegistrationClaimSchema = z
|
|
11
|
-
.object({
|
|
12
|
-
version: z.literal(1),
|
|
13
|
-
workspaceIdentity: sha256Schema,
|
|
14
|
-
ownerIdentity: sha256Schema,
|
|
15
|
-
claimHash: sha256Schema,
|
|
16
|
-
})
|
|
17
|
-
.strict();
|
|
18
7
|
async function exists(value, signal) {
|
|
19
8
|
signal?.throwIfAborted();
|
|
20
9
|
try {
|
|
@@ -59,6 +48,17 @@ export function isWithin(root, candidate) {
|
|
|
59
48
|
function pathsOverlap(left, right) {
|
|
60
49
|
return isWithin(left, right) || isWithin(right, left);
|
|
61
50
|
}
|
|
51
|
+
function discoveredWorkspaceId(rootPath) {
|
|
52
|
+
const basename = path.basename(rootPath);
|
|
53
|
+
const slug = basename
|
|
54
|
+
.normalize('NFKD')
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/[^a-z0-9]+/gu, '_')
|
|
57
|
+
.replace(/^_+|_+$/gu, '')
|
|
58
|
+
.slice(0, 40);
|
|
59
|
+
const identityPath = process.platform === 'win32' ? rootPath.toLowerCase() : rootPath;
|
|
60
|
+
return `mod_${slug.length === 0 ? 'workspace' : slug}_${sha256Bytes(identityPath).slice(0, 12)}`;
|
|
61
|
+
}
|
|
62
62
|
/**
|
|
63
63
|
* Hash the resolved root topology without persisting canonical host paths. Canonicalization happens
|
|
64
64
|
* before this function is called, so filesystem aliases converge on the same stable identity.
|
|
@@ -86,121 +86,6 @@ function configuredOwnerIdentity(workspaceIdentity) {
|
|
|
86
86
|
workspaceIdentity,
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
function runtimeOwnerIdentity(workspaceIdentity, principal) {
|
|
90
|
-
return hashCanonical({
|
|
91
|
-
domain: 'hoi4-agent-runtime-workspace-owner-v1',
|
|
92
|
-
workspaceIdentity,
|
|
93
|
-
principal: principal ?? null,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
function runtimeClaimHash(claim) {
|
|
97
|
-
return hashCanonical(claim);
|
|
98
|
-
}
|
|
99
|
-
function registrationClaimConflict() {
|
|
100
|
-
return new ServiceError('WORKSPACE_REGISTRATION_CONFLICT', 'Runtime workspace registration conflicts with an existing registration');
|
|
101
|
-
}
|
|
102
|
-
async function readRuntimeRegistrationClaim(claimPath, signal) {
|
|
103
|
-
signal?.throwIfAborted();
|
|
104
|
-
let claimStatus;
|
|
105
|
-
try {
|
|
106
|
-
claimStatus = await lstat(claimPath);
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
if (error.code === 'ENOENT')
|
|
110
|
-
return undefined;
|
|
111
|
-
throw registrationClaimConflict();
|
|
112
|
-
}
|
|
113
|
-
signal?.throwIfAborted();
|
|
114
|
-
if (claimStatus.isSymbolicLink() || !claimStatus.isFile() || claimStatus.size > 4_096) {
|
|
115
|
-
throw registrationClaimConflict();
|
|
116
|
-
}
|
|
117
|
-
let parsed;
|
|
118
|
-
try {
|
|
119
|
-
const text = signal === undefined
|
|
120
|
-
? await readFile(claimPath, 'utf8')
|
|
121
|
-
: await readFile(claimPath, { encoding: 'utf8', signal });
|
|
122
|
-
signal?.throwIfAborted();
|
|
123
|
-
parsed = JSON.parse(text);
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
if (error.name === 'AbortError')
|
|
127
|
-
throw error;
|
|
128
|
-
throw registrationClaimConflict();
|
|
129
|
-
}
|
|
130
|
-
signal?.throwIfAborted();
|
|
131
|
-
const validated = runtimeRegistrationClaimSchema.safeParse(parsed);
|
|
132
|
-
if (!validated.success)
|
|
133
|
-
throw registrationClaimConflict();
|
|
134
|
-
const claim = validated.data;
|
|
135
|
-
if (runtimeClaimHash({
|
|
136
|
-
version: claim.version,
|
|
137
|
-
workspaceIdentity: claim.workspaceIdentity,
|
|
138
|
-
ownerIdentity: claim.ownerIdentity,
|
|
139
|
-
}) !== claim.claimHash) {
|
|
140
|
-
throw registrationClaimConflict();
|
|
141
|
-
}
|
|
142
|
-
return claim;
|
|
143
|
-
}
|
|
144
|
-
async function assertRuntimeRegistrationClaim(workspace, signal) {
|
|
145
|
-
signal?.throwIfAborted();
|
|
146
|
-
const claimRoot = await containedGeneratedPath(workspace.artifactRoot);
|
|
147
|
-
signal?.throwIfAborted();
|
|
148
|
-
const claimPath = path.join(claimRoot, runtimeRegistrationClaimFile);
|
|
149
|
-
const expectedWithoutHash = {
|
|
150
|
-
version: 1,
|
|
151
|
-
workspaceIdentity: workspace.workspaceIdentity,
|
|
152
|
-
ownerIdentity: workspace.ownerIdentity,
|
|
153
|
-
};
|
|
154
|
-
const expected = {
|
|
155
|
-
...expectedWithoutHash,
|
|
156
|
-
claimHash: runtimeClaimHash(expectedWithoutHash),
|
|
157
|
-
};
|
|
158
|
-
const assertMatches = (claim) => {
|
|
159
|
-
if (claim.workspaceIdentity !== expected.workspaceIdentity ||
|
|
160
|
-
claim.ownerIdentity !== expected.ownerIdentity ||
|
|
161
|
-
claim.claimHash !== expected.claimHash) {
|
|
162
|
-
throw registrationClaimConflict();
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
const existing = await readRuntimeRegistrationClaim(claimPath, signal);
|
|
166
|
-
if (existing !== undefined) {
|
|
167
|
-
assertMatches(existing);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
signal?.throwIfAborted();
|
|
171
|
-
const temporaryPath = path.join(claimRoot, `.runtime-registration-${secureId('claim')}.tmp`);
|
|
172
|
-
let temporaryHandle;
|
|
173
|
-
try {
|
|
174
|
-
temporaryHandle = await open(temporaryPath, 'wx', 0o600);
|
|
175
|
-
await temporaryHandle.writeFile(`${canonicalJson(expected)}\n`, 'utf8');
|
|
176
|
-
await temporaryHandle.sync();
|
|
177
|
-
await temporaryHandle.close();
|
|
178
|
-
temporaryHandle = undefined;
|
|
179
|
-
signal?.throwIfAborted();
|
|
180
|
-
try {
|
|
181
|
-
// A hard link publishes fully flushed bytes without replacing a competing claim.
|
|
182
|
-
await link(temporaryPath, claimPath);
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
if (error.code !== 'EEXIST') {
|
|
186
|
-
throw registrationClaimConflict();
|
|
187
|
-
}
|
|
188
|
-
const racedClaim = await readRuntimeRegistrationClaim(claimPath, signal);
|
|
189
|
-
if (racedClaim === undefined)
|
|
190
|
-
throw registrationClaimConflict();
|
|
191
|
-
assertMatches(racedClaim);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
if (error instanceof ServiceError || error.name === 'AbortError')
|
|
196
|
-
throw error;
|
|
197
|
-
throw registrationClaimConflict();
|
|
198
|
-
}
|
|
199
|
-
finally {
|
|
200
|
-
await temporaryHandle?.close().catch(() => undefined);
|
|
201
|
-
await unlink(temporaryPath).catch(() => undefined);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
89
|
function containsAsciiControlCharacter(value) {
|
|
205
90
|
for (let index = 0; index < value.length; index += 1) {
|
|
206
91
|
if (value.charCodeAt(index) <= 0x1f)
|
|
@@ -252,18 +137,12 @@ function assertRelative(relativePath) {
|
|
|
252
137
|
export class WorkspaceResolver {
|
|
253
138
|
configuration;
|
|
254
139
|
#byId = new Map();
|
|
255
|
-
#
|
|
256
|
-
#runtimeWorkspaceIds = new Set();
|
|
257
|
-
#runtimeWorkspaceOwners = new Map();
|
|
258
|
-
#registrationRoots = [];
|
|
259
|
-
#registrationLexicalRoots = [];
|
|
260
|
-
#writableRegistrationRoots = [];
|
|
261
|
-
#writableRegistrationLexicalRoots = [];
|
|
140
|
+
#discoveredWorkspaceIds = new Set();
|
|
262
141
|
#storageRoots = [];
|
|
263
|
-
#
|
|
142
|
+
#modRoots = [];
|
|
143
|
+
#workspaceStorageRoot;
|
|
264
144
|
#serverStateRoot;
|
|
265
145
|
#serverState;
|
|
266
|
-
#registrationQueue = Promise.resolve();
|
|
267
146
|
constructor(configuration) {
|
|
268
147
|
this.configuration = configuration;
|
|
269
148
|
}
|
|
@@ -272,34 +151,41 @@ export class WorkspaceResolver {
|
|
|
272
151
|
if (configuration.serverStateRoot !== undefined) {
|
|
273
152
|
resolver.#serverStateRoot = await canonicalPath(configuration.serverStateRoot);
|
|
274
153
|
}
|
|
275
|
-
resolver.#
|
|
276
|
-
resolver.#
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
154
|
+
resolver.#storageRoots = await Promise.all(configuration.storageRoots.map((root) => canonicalPath(root)));
|
|
155
|
+
resolver.#modRoots = await Promise.all(configuration.modRoots.map(async (root) => {
|
|
156
|
+
const status = await lstat(root).catch(() => undefined);
|
|
157
|
+
if (status === undefined || !status.isDirectory() || status.isSymbolicLink()) {
|
|
158
|
+
throw new ServiceError('WORKSPACE_MOD_ROOT_UNSAFE', 'Configured mod roots must be existing real directories');
|
|
159
|
+
}
|
|
160
|
+
return canonicalPath(root);
|
|
161
|
+
}));
|
|
162
|
+
for (const [index, candidate] of resolver.#modRoots.entries()) {
|
|
163
|
+
if (resolver.#modRoots.slice(0, index).some((root) => pathsOverlap(root, candidate))) {
|
|
164
|
+
throw new ServiceError('WORKSPACE_MOD_ROOT_OVERLAP', 'Configured mod roots must be distinct and non-overlapping');
|
|
282
165
|
}
|
|
283
166
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
167
|
+
if (configuration.workspaceStorageRoot !== undefined) {
|
|
168
|
+
resolver.#workspaceStorageRoot = await canonicalPath(configuration.workspaceStorageRoot);
|
|
169
|
+
if (resolver.#modRoots.some((root) => pathsOverlap(root, resolver.#workspaceStorageRoot))) {
|
|
170
|
+
throw new ServiceError('WORKSPACE_STORAGE_ROOT_OVERLAP', 'Workspace storage root must not overlap a configured mod root');
|
|
287
171
|
}
|
|
172
|
+
resolver.#storageRoots.push(resolver.#workspaceStorageRoot);
|
|
288
173
|
}
|
|
289
|
-
resolver.#storageLexicalRoots = configuration.storageRoots.map((root) => path.normalize(path.resolve(root)));
|
|
290
|
-
resolver.#storageRoots = await Promise.all(configuration.storageRoots.map((root) => canonicalPath(root)));
|
|
291
174
|
if (resolver.#serverStateRoot !== undefined) {
|
|
292
175
|
for (const root of [
|
|
293
|
-
...resolver.#registrationRoots,
|
|
294
|
-
...resolver.#writableRegistrationRoots,
|
|
295
176
|
...resolver.#storageRoots,
|
|
177
|
+
...resolver.#modRoots,
|
|
178
|
+
...(configuration.gameRoot === undefined
|
|
179
|
+
? []
|
|
180
|
+
: [await canonicalPath(configuration.gameRoot)]),
|
|
296
181
|
]) {
|
|
297
182
|
if (pathsOverlap(resolver.#serverStateRoot, root)) {
|
|
298
183
|
throw new ServiceError('SERVER_STATE_ROOT_OVERLAP', 'Server state root must not overlap a registration or generated-storage capability root');
|
|
299
184
|
}
|
|
300
185
|
}
|
|
301
186
|
}
|
|
302
|
-
for (const
|
|
187
|
+
for (const configured of configuration.workspaces) {
|
|
188
|
+
const registration = resolver.withGlobalWorkspaceDefaults(configured);
|
|
303
189
|
const workspace = await resolver.resolveRegistration(registration);
|
|
304
190
|
if (resolver.#byId.has(workspace.id)) {
|
|
305
191
|
throw new ServiceError('WORKSPACE_DUPLICATE', `Duplicate workspace ID: ${workspace.id}`);
|
|
@@ -307,6 +193,7 @@ export class WorkspaceResolver {
|
|
|
307
193
|
resolver.assertWorkspaceIsolation(workspace);
|
|
308
194
|
resolver.#byId.set(workspace.id, workspace);
|
|
309
195
|
}
|
|
196
|
+
await resolver.discoverModWorkspaces();
|
|
310
197
|
if (configuration.serverStateRoot !== undefined) {
|
|
311
198
|
resolver.#serverState = await ServerState.create(configuration.serverStateRoot);
|
|
312
199
|
resolver.#serverStateRoot = resolver.#serverState.root;
|
|
@@ -319,179 +206,67 @@ export class WorkspaceResolver {
|
|
|
319
206
|
serverState() {
|
|
320
207
|
return this.#serverState;
|
|
321
208
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
209
|
+
withGlobalWorkspaceDefaults(registration) {
|
|
210
|
+
const gameRoot = registration.kind === 'mod' && registration.gameRoot === undefined
|
|
211
|
+
? this.configuration.gameRoot
|
|
212
|
+
: registration.gameRoot;
|
|
213
|
+
const storageBase = this.#workspaceStorageRoot === undefined
|
|
214
|
+
? undefined
|
|
215
|
+
: path.join(this.#workspaceStorageRoot, registration.id);
|
|
216
|
+
return {
|
|
217
|
+
...registration,
|
|
218
|
+
...(gameRoot === undefined ? {} : { gameRoot }),
|
|
219
|
+
...(registration.artifactRoot !== undefined || storageBase === undefined
|
|
220
|
+
? {}
|
|
221
|
+
: { artifactRoot: path.join(storageBase, 'artifacts') }),
|
|
222
|
+
...(registration.cacheRoot !== undefined || storageBase === undefined
|
|
223
|
+
? {}
|
|
224
|
+
: { cacheRoot: path.join(storageBase, 'cache') }),
|
|
225
|
+
};
|
|
337
226
|
}
|
|
338
|
-
async
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
registration.fixtureRoot,
|
|
371
|
-
].filter((value) => value !== undefined);
|
|
372
|
-
const canonicalReadRoots = [];
|
|
373
|
-
for (const requestedRoot of readRoots) {
|
|
374
|
-
signal?.throwIfAborted();
|
|
375
|
-
this.assertLexicalRegistrationPath(requestedRoot, 'workspace source root', 'WORKSPACE_REGISTRATION_SOURCE_ROOT_FORBIDDEN');
|
|
376
|
-
const candidate = await canonicalPath(requestedRoot, signal);
|
|
377
|
-
if (!this.#registrationRoots.some((root) => isWithin(root, candidate))) {
|
|
378
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_SOURCE_ROOT_FORBIDDEN', 'A requested game, dependency, or fixture root is outside configured registration roots', { workspaceId: registration.id });
|
|
379
|
-
}
|
|
380
|
-
canonicalReadRoots.push(candidate);
|
|
381
|
-
}
|
|
382
|
-
for (const existing of this.#byId.values()) {
|
|
383
|
-
signal?.throwIfAborted();
|
|
384
|
-
if (existing.roots.some((root) => pathsOverlap(candidateRoot, root.path))) {
|
|
385
|
-
throw registrationConflict('WORKSPACE_REGISTRATION_ROOT_OVERLAP', 'Runtime workspace root overlaps an existing registered workspace root');
|
|
386
|
-
}
|
|
387
|
-
const protectedRoots = existing.roots.filter((root) => ['mod', 'artifact', 'cache'].includes(root.kind));
|
|
388
|
-
if (canonicalReadRoots.some((requestedRoot) => protectedRoots.some((root) => pathsOverlap(requestedRoot, root.path)))) {
|
|
389
|
-
throw registrationConflict('WORKSPACE_REGISTRATION_SOURCE_OVERLAP', 'Runtime source root overlaps an existing workspace-owned root');
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
for (const [index, requestedRoot] of canonicalReadRoots.entries()) {
|
|
393
|
-
signal?.throwIfAborted();
|
|
394
|
-
if (pathsOverlap(candidateRoot, requestedRoot) ||
|
|
395
|
-
canonicalReadRoots
|
|
396
|
-
.slice(0, index)
|
|
397
|
-
.some((existingRequestedRoot) => pathsOverlap(requestedRoot, existingRequestedRoot))) {
|
|
398
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_INTERNAL_OVERLAP', 'Runtime workspace roots must be distinct and non-overlapping', { workspaceId: registration.id });
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const generatedCandidates = new Map();
|
|
402
|
-
for (const [generatedKind, configuredRoot] of [
|
|
403
|
-
['artifacts', registration.artifactRoot],
|
|
404
|
-
['cache', registration.cacheRoot],
|
|
405
|
-
]) {
|
|
406
|
-
signal?.throwIfAborted();
|
|
407
|
-
const generatedRoot = configuredRoot ??
|
|
408
|
-
(registration.kind === 'mod'
|
|
409
|
-
? path.join(registration.root, '.hoi4-agent', generatedKind)
|
|
410
|
-
: undefined);
|
|
411
|
-
if (generatedRoot === undefined)
|
|
412
|
-
continue;
|
|
413
|
-
this.assertLexicalGeneratedPath(registration, generatedRoot, generatedKind);
|
|
414
|
-
const candidate = await canonicalPath(generatedRoot, signal);
|
|
415
|
-
const relativeToPrimary = path
|
|
416
|
-
.relative(candidateRoot, candidate)
|
|
417
|
-
.replaceAll('\\', '/')
|
|
418
|
-
.toLowerCase();
|
|
419
|
-
const allowed = (registration.kind === 'mod' &&
|
|
420
|
-
isWithin(candidateRoot, candidate) &&
|
|
421
|
-
(relativeToPrimary === `.hoi4-agent/${generatedKind}` ||
|
|
422
|
-
relativeToPrimary.startsWith(`.hoi4-agent/${generatedKind}/`))) ||
|
|
423
|
-
this.#storageRoots.some((root) => isWithin(root, candidate));
|
|
424
|
-
if (!allowed) {
|
|
425
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_GENERATED_ROOT_FORBIDDEN', 'Runtime artifact and cache roots must remain inside the registered workspace', { workspaceId: registration.id });
|
|
227
|
+
async discoverModWorkspaces() {
|
|
228
|
+
const registrations = [];
|
|
229
|
+
for (const modRoot of this.#modRoots) {
|
|
230
|
+
const directory = await opendir(modRoot);
|
|
231
|
+
for await (const entry of directory) {
|
|
232
|
+
if (entry.name.startsWith('.') || entry.isSymbolicLink() || !entry.isDirectory())
|
|
233
|
+
continue;
|
|
234
|
+
const lexicalCandidate = path.normalize(path.join(modRoot, entry.name));
|
|
235
|
+
if (!isWithin(modRoot, lexicalCandidate))
|
|
236
|
+
continue;
|
|
237
|
+
const candidateStatus = await lstat(lexicalCandidate).catch(() => undefined);
|
|
238
|
+
if (candidateStatus === undefined ||
|
|
239
|
+
candidateStatus.isSymbolicLink() ||
|
|
240
|
+
!candidateStatus.isDirectory()) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const candidate = await canonicalPath(lexicalCandidate);
|
|
244
|
+
if (!isWithin(modRoot, candidate))
|
|
245
|
+
continue;
|
|
246
|
+
if ([...this.#byId.values()].some((workspace) => workspace.modRoot === candidate))
|
|
247
|
+
continue;
|
|
248
|
+
if (this.#byId.size + registrations.length >= WORKSPACE_MAX_REGISTRATIONS) {
|
|
249
|
+
throw new ServiceError('WORKSPACE_REGISTRATION_LIMIT', 'Workspace registration limit has been reached');
|
|
250
|
+
}
|
|
251
|
+
const id = discoveredWorkspaceId(candidate);
|
|
252
|
+
const registration = workspaceRegistrationSchema.parse({
|
|
253
|
+
id,
|
|
254
|
+
name: entry.name.slice(0, 200),
|
|
255
|
+
root: candidate,
|
|
256
|
+
kind: 'mod',
|
|
257
|
+
});
|
|
258
|
+
registrations.push(this.withGlobalWorkspaceDefaults(registration));
|
|
426
259
|
}
|
|
427
|
-
generatedCandidates.set(generatedKind, candidate);
|
|
428
|
-
}
|
|
429
|
-
const artifactCandidate = generatedCandidates.get('artifacts');
|
|
430
|
-
const cacheCandidate = generatedCandidates.get('cache');
|
|
431
|
-
if (artifactCandidate !== undefined &&
|
|
432
|
-
cacheCandidate !== undefined &&
|
|
433
|
-
pathsOverlap(artifactCandidate, cacheCandidate)) {
|
|
434
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_GENERATED_ROOT_FORBIDDEN', 'Runtime artifact and cache roots must be distinct and non-overlapping', { workspaceId: registration.id });
|
|
435
260
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
!isWithin(candidateRoot, candidate) &&
|
|
441
|
-
pathsOverlap(candidateRoot, candidate)) ||
|
|
442
|
-
canonicalReadRoots.some((sourceRoot) => pathsOverlap(sourceRoot, candidate));
|
|
443
|
-
if (overlapsOwnSources) {
|
|
444
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_GENERATED_ROOT_FORBIDDEN', 'Runtime generated storage must not overlap a source root', { workspaceId: registration.id });
|
|
261
|
+
registrations.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
262
|
+
for (const registration of registrations) {
|
|
263
|
+
if (this.#byId.has(registration.id)) {
|
|
264
|
+
throw new ServiceError('WORKSPACE_DUPLICATE', `Duplicate workspace ID: ${registration.id}`);
|
|
445
265
|
}
|
|
446
|
-
|
|
447
|
-
throw registrationConflict('WORKSPACE_REGISTRATION_GENERATED_ROOT_OVERLAP', 'Runtime generated storage overlaps an existing workspace root');
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
const workspace = await this.resolveRegistration(registration, {
|
|
451
|
-
principal,
|
|
452
|
-
...(signal === undefined ? {} : { signal }),
|
|
453
|
-
});
|
|
454
|
-
signal?.throwIfAborted();
|
|
455
|
-
if (this.#byId.has(registration.id)) {
|
|
456
|
-
throw registrationConflict('WORKSPACE_DUPLICATE', `Workspace ID is already registered: ${registration.id}`);
|
|
457
|
-
}
|
|
458
|
-
this.assertResolvedRuntimeRegistration(workspace);
|
|
459
|
-
try {
|
|
266
|
+
const workspace = await this.resolveRegistration(registration);
|
|
460
267
|
this.assertWorkspaceIsolation(workspace);
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (principal !== undefined && error instanceof ServiceError) {
|
|
464
|
-
throw registrationConflict(error.code, error.message);
|
|
465
|
-
}
|
|
466
|
-
throw error;
|
|
467
|
-
}
|
|
468
|
-
signal?.throwIfAborted();
|
|
469
|
-
await assertRuntimeRegistrationClaim(workspace, signal);
|
|
470
|
-
signal?.throwIfAborted();
|
|
471
|
-
this.#byId.set(workspace.id, workspace);
|
|
472
|
-
this.#runtimeWorkspaceIds.add(workspace.id);
|
|
473
|
-
this.#runtimeWorkspaceOwners.set(workspace.id, principal);
|
|
474
|
-
if (principal !== undefined) {
|
|
475
|
-
const ids = this.#runtimePrincipalWorkspaces.get(principal) ?? new Set();
|
|
476
|
-
ids.add(workspace.id);
|
|
477
|
-
this.#runtimePrincipalWorkspaces.set(principal, ids);
|
|
478
|
-
}
|
|
479
|
-
return workspace;
|
|
480
|
-
}
|
|
481
|
-
unregisterRuntime(workspaceId, principal) {
|
|
482
|
-
if (this.#runtimeWorkspaceIds.has(workspaceId) &&
|
|
483
|
-
this.#runtimeWorkspaceOwners.get(workspaceId) !== principal) {
|
|
484
|
-
throw new ServiceError('WORKSPACE_INACCESSIBLE', 'Workspace is unavailable to the authenticated principal');
|
|
485
|
-
}
|
|
486
|
-
if (!this.#runtimeWorkspaceIds.delete(workspaceId))
|
|
487
|
-
return;
|
|
488
|
-
this.#runtimeWorkspaceOwners.delete(workspaceId);
|
|
489
|
-
this.#byId.delete(workspaceId);
|
|
490
|
-
if (principal !== undefined) {
|
|
491
|
-
const ids = this.#runtimePrincipalWorkspaces.get(principal);
|
|
492
|
-
ids?.delete(workspaceId);
|
|
493
|
-
if (ids?.size === 0)
|
|
494
|
-
this.#runtimePrincipalWorkspaces.delete(principal);
|
|
268
|
+
this.#byId.set(workspace.id, workspace);
|
|
269
|
+
this.#discoveredWorkspaceIds.add(workspace.id);
|
|
495
270
|
}
|
|
496
271
|
}
|
|
497
272
|
list(principal) {
|
|
@@ -574,56 +349,12 @@ export class WorkspaceResolver {
|
|
|
574
349
|
return new Set([
|
|
575
350
|
...(token?.workspaceIds ?? []),
|
|
576
351
|
...(oauth?.workspaceIds ?? []),
|
|
577
|
-
...(this
|
|
352
|
+
...(this.discoveredModsAllowed(principal) ? this.#discoveredWorkspaceIds : []),
|
|
578
353
|
]);
|
|
579
354
|
}
|
|
580
|
-
|
|
581
|
-
return (this.configuration.http.tokens.some((entry) => entry.principal === principal && entry.
|
|
582
|
-
this.configuration.http.principals.some((entry) => entry.principal === principal && entry.
|
|
583
|
-
}
|
|
584
|
-
assertLexicalRegistrationPath(value, label, code) {
|
|
585
|
-
const candidate = path.normalize(path.resolve(value));
|
|
586
|
-
if (!this.#registrationLexicalRoots.some((root) => isWithin(root, candidate))) {
|
|
587
|
-
throw new ServiceError(code, `Runtime ${label} is lexically outside configured registration roots`);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
assertLexicalWritableRegistrationPath(value) {
|
|
591
|
-
const candidate = path.normalize(path.resolve(value));
|
|
592
|
-
if (!this.#writableRegistrationLexicalRoots.some((root) => isWithin(root, candidate))) {
|
|
593
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_ROOT_FORBIDDEN', 'Runtime mod roots are lexically outside operator-approved writable registration roots');
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
assertLexicalGeneratedPath(registration, value, generatedKind) {
|
|
597
|
-
const candidate = path.normalize(path.resolve(value));
|
|
598
|
-
const primary = path.normalize(path.resolve(registration.root));
|
|
599
|
-
const expectedRoot = path.join(primary, '.hoi4-agent', generatedKind);
|
|
600
|
-
const insideWritableMod = registration.kind === 'mod' && isWithin(expectedRoot, candidate);
|
|
601
|
-
const insideStorage = this.#storageLexicalRoots.some((root) => isWithin(root, candidate));
|
|
602
|
-
if (!insideWritableMod && !insideStorage) {
|
|
603
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_GENERATED_ROOT_FORBIDDEN', 'Runtime generated storage is outside the writable mod or configured storage roots');
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
assertResolvedRuntimeRegistration(workspace) {
|
|
607
|
-
const allowed = (candidate) => this.#registrationRoots.some((root) => isWithin(root, candidate));
|
|
608
|
-
if (!allowed(workspace.modRoot)) {
|
|
609
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_ROOT_FORBIDDEN', 'Resolved runtime workspace is outside configured registration roots');
|
|
610
|
-
}
|
|
611
|
-
if (workspace.registration.kind === 'mod' &&
|
|
612
|
-
!this.#writableRegistrationRoots.some((root) => isWithin(root, workspace.modRoot))) {
|
|
613
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_ROOT_FORBIDDEN', 'Resolved runtime mod root is outside operator-approved writable registration roots');
|
|
614
|
-
}
|
|
615
|
-
for (const root of workspace.roots.filter((candidate) => ['game', 'dependency', 'fixture'].includes(candidate.kind))) {
|
|
616
|
-
if (!allowed(root.path)) {
|
|
617
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_SOURCE_ROOT_FORBIDDEN', 'Resolved runtime source is outside configured registration roots');
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
const generatedAllowed = (candidate, generatedKind) => (workspace.registration.kind === 'mod' &&
|
|
621
|
-
isWithin(path.join(workspace.modRoot, '.hoi4-agent', generatedKind), candidate)) ||
|
|
622
|
-
this.#storageRoots.some((root) => isWithin(root, candidate));
|
|
623
|
-
if (!generatedAllowed(workspace.artifactRoot, 'artifacts') ||
|
|
624
|
-
!generatedAllowed(workspace.cacheRoot, 'cache')) {
|
|
625
|
-
throw new ServiceError('WORKSPACE_REGISTRATION_GENERATED_ROOT_FORBIDDEN', 'Resolved runtime generated storage must remain inside its workspace');
|
|
626
|
-
}
|
|
355
|
+
discoveredModsAllowed(principal) {
|
|
356
|
+
return (this.configuration.http.tokens.some((entry) => entry.principal === principal && entry.allowDiscoveredMods) ||
|
|
357
|
+
this.configuration.http.principals.some((entry) => entry.principal === principal && entry.allowDiscoveredMods));
|
|
627
358
|
}
|
|
628
359
|
assertWorkspaceIsolation(workspace) {
|
|
629
360
|
const readRoots = workspace.roots.filter((root) => ['game', 'dependency', 'fixture'].includes(root.kind));
|
|
@@ -641,31 +372,25 @@ export class WorkspaceResolver {
|
|
|
641
372
|
}
|
|
642
373
|
}
|
|
643
374
|
}
|
|
644
|
-
async resolveRegistration(registration
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
const modRoot = await canonicalPath(registration.root, signal);
|
|
648
|
-
if (!(await exists(modRoot, signal))) {
|
|
375
|
+
async resolveRegistration(registration) {
|
|
376
|
+
const modRoot = await canonicalPath(registration.root);
|
|
377
|
+
if (!(await exists(modRoot))) {
|
|
649
378
|
throw new ServiceError('WORKSPACE_ROOT_MISSING', `Workspace root does not exist: ${registration.root}`);
|
|
650
379
|
}
|
|
651
|
-
const gameRoot = registration.gameRoot === undefined
|
|
652
|
-
? undefined
|
|
653
|
-
: await canonicalPath(registration.gameRoot, signal);
|
|
380
|
+
const gameRoot = registration.gameRoot === undefined ? undefined : await canonicalPath(registration.gameRoot);
|
|
654
381
|
const dependencyRegistrations = registration.dependencies.length > 0
|
|
655
382
|
? registration.dependencies
|
|
656
383
|
: registration.dependencyRoots.map((root) => ({ root, replacePaths: [] }));
|
|
657
|
-
const dependencyRoots = await Promise.all(dependencyRegistrations.map(({ root }) => canonicalPath(root
|
|
658
|
-
signal?.throwIfAborted();
|
|
384
|
+
const dependencyRoots = await Promise.all(dependencyRegistrations.map(({ root }) => canonicalPath(root)));
|
|
659
385
|
if (registration.kind !== 'mod' &&
|
|
660
386
|
(registration.artifactRoot === undefined || registration.cacheRoot === undefined)) {
|
|
661
387
|
throw new ServiceError('WORKSPACE_GENERATED_ROOT_REQUIRED', 'Read-only game and dependency workspaces require explicit operator-owned storage roots', { workspaceId: registration.id });
|
|
662
388
|
}
|
|
663
|
-
const artifactRoot = await canonicalPath(registration.artifactRoot ?? path.join(modRoot, '.hoi4-agent', 'artifacts')
|
|
664
|
-
const cacheRoot = await canonicalPath(registration.cacheRoot ?? path.join(modRoot, '.hoi4-agent', 'cache')
|
|
389
|
+
const artifactRoot = await canonicalPath(registration.artifactRoot ?? path.join(modRoot, '.hoi4-agent', 'artifacts'));
|
|
390
|
+
const cacheRoot = await canonicalPath(registration.cacheRoot ?? path.join(modRoot, '.hoi4-agent', 'cache'));
|
|
665
391
|
const fixtureRoot = registration.fixtureRoot === undefined
|
|
666
392
|
? undefined
|
|
667
|
-
: await canonicalPath(registration.fixtureRoot
|
|
668
|
-
signal?.throwIfAborted();
|
|
393
|
+
: await canonicalPath(registration.fixtureRoot);
|
|
669
394
|
const generatedAllowed = (candidate, generatedKind) => (registration.kind === 'mod' &&
|
|
670
395
|
isWithin(path.join(modRoot, '.hoi4-agent', generatedKind), candidate)) ||
|
|
671
396
|
this.#storageRoots.some((root) => isWithin(root, candidate));
|
|
@@ -679,8 +404,7 @@ export class WorkspaceResolver {
|
|
|
679
404
|
throw new ServiceError('WORKSPACE_GENERATED_ROOT_OVERLAP', 'Artifact and cache storage roots must be distinct and non-overlapping', { workspaceId: registration.id });
|
|
680
405
|
}
|
|
681
406
|
for (const sourceRoot of [gameRoot, ...dependencyRoots, fixtureRoot].filter((value) => value !== undefined)) {
|
|
682
|
-
|
|
683
|
-
if (!(await exists(sourceRoot, signal))) {
|
|
407
|
+
if (!(await exists(sourceRoot))) {
|
|
684
408
|
throw new ServiceError('WORKSPACE_SOURCE_ROOT_MISSING', 'Configured game, dependency, or fixture root does not exist', { workspaceId: registration.id });
|
|
685
409
|
}
|
|
686
410
|
}
|
|
@@ -707,9 +431,7 @@ export class WorkspaceResolver {
|
|
|
707
431
|
throw new ServiceError('SERVER_STATE_ROOT_OVERLAP', 'Server state root must not overlap a workspace source or generated-storage root', { workspaceId: registration.id });
|
|
708
432
|
}
|
|
709
433
|
await mkdir(artifactRoot, { recursive: true });
|
|
710
|
-
signal?.throwIfAborted();
|
|
711
434
|
await mkdir(cacheRoot, { recursive: true });
|
|
712
|
-
signal?.throwIfAborted();
|
|
713
435
|
const roots = [
|
|
714
436
|
{
|
|
715
437
|
kind: registration.kind,
|
|
@@ -763,8 +485,7 @@ export class WorkspaceResolver {
|
|
|
763
485
|
]),
|
|
764
486
|
];
|
|
765
487
|
for (const root of roots) {
|
|
766
|
-
|
|
767
|
-
root.path = await canonicalPath(root.path, signal);
|
|
488
|
+
root.path = await canonicalPath(root.path);
|
|
768
489
|
}
|
|
769
490
|
const workspaceBase = {
|
|
770
491
|
id: registration.id,
|
|
@@ -777,18 +498,13 @@ export class WorkspaceResolver {
|
|
|
777
498
|
artifactRoot,
|
|
778
499
|
cacheRoot,
|
|
779
500
|
...(fixtureRoot === undefined ? {} : { fixtureRoot }),
|
|
780
|
-
writeEnabled: registration.kind === 'mod'
|
|
781
|
-
this.configuration.writePolicy !== 'read-only' &&
|
|
782
|
-
registration.writeEnabled,
|
|
501
|
+
writeEnabled: registration.kind === 'mod',
|
|
783
502
|
};
|
|
784
503
|
const workspaceIdentity = resolvedWorkspaceIdentity(workspaceBase);
|
|
785
504
|
return {
|
|
786
505
|
...workspaceBase,
|
|
787
506
|
workspaceIdentity,
|
|
788
|
-
ownerIdentity:
|
|
789
|
-
? configuredOwnerIdentity(workspaceIdentity)
|
|
790
|
-
: runtimeOwnerIdentity(workspaceIdentity, runtimeOwner.principal),
|
|
507
|
+
ownerIdentity: configuredOwnerIdentity(workspaceIdentity),
|
|
791
508
|
};
|
|
792
509
|
}
|
|
793
510
|
}
|
|
794
|
-
//# sourceMappingURL=workspace.js.map
|