hoi4-agent-tools 0.0.0-bootstrap.1 → 0.1.3
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 +50 -0
- package/LICENSE +201 -0
- package/README.md +140 -2
- package/SECURITY.md +17 -0
- package/dist/bin/http.d.ts +3 -0
- package/dist/bin/http.d.ts.map +1 -0
- package/dist/bin/http.js +27 -0
- package/dist/bin/http.js.map +1 -0
- package/dist/bin/setup.d.ts +3 -0
- package/dist/bin/setup.d.ts.map +1 -0
- package/dist/bin/setup.js +188 -0
- package/dist/bin/setup.js.map +1 -0
- package/dist/bin/stdio.d.ts +3 -0
- package/dist/bin/stdio.d.ts.map +1 -0
- package/dist/bin/stdio.js +36 -0
- package/dist/bin/stdio.js.map +1 -0
- package/dist/hoi4_agent_tools/core/artifacts.d.ts +117 -0
- package/dist/hoi4_agent_tools/core/artifacts.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +1047 -0
- package/dist/hoi4_agent_tools/core/artifacts.js.map +1 -0
- package/dist/hoi4_agent_tools/core/canonical.d.ts +13 -0
- package/dist/hoi4_agent_tools/core/canonical.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/canonical.js +41 -0
- package/dist/hoi4_agent_tools/core/canonical.js.map +1 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts +137 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/configuration.js +383 -0
- package/dist/hoi4_agent_tools/core/configuration.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts +18 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js +45 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts +26 -0
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostics.js +25 -0
- package/dist/hoi4_agent_tools/core/diagnostics.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diff.d.ts +22 -0
- package/dist/hoi4_agent_tools/core/diff.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diff.js +93 -0
- package/dist/hoi4_agent_tools/core/diff.js.map +1 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts +56 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/engine.js +319 -0
- package/dist/hoi4_agent_tools/core/engine.js.map +1 -0
- package/dist/hoi4_agent_tools/core/image-diff.d.ts +14 -0
- package/dist/hoi4_agent_tools/core/image-diff.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/image-diff.js +121 -0
- package/dist/hoi4_agent_tools/core/image-diff.js.map +1 -0
- package/dist/hoi4_agent_tools/core/index.d.ts +64 -0
- package/dist/hoi4_agent_tools/core/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/index.js +963 -0
- package/dist/hoi4_agent_tools/core/index.js.map +1 -0
- package/dist/hoi4_agent_tools/core/index.public.d.ts +16 -0
- package/dist/hoi4_agent_tools/core/index.public.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/index.public.js +16 -0
- package/dist/hoi4_agent_tools/core/index.public.js.map +1 -0
- package/dist/hoi4_agent_tools/core/render-budget.d.ts +51 -0
- package/dist/hoi4_agent_tools/core/render-budget.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/render-budget.js +109 -0
- package/dist/hoi4_agent_tools/core/render-budget.js.map +1 -0
- package/dist/hoi4_agent_tools/core/result.d.ts +45 -0
- package/dist/hoi4_agent_tools/core/result.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/result.js +26 -0
- package/dist/hoi4_agent_tools/core/result.js.map +1 -0
- package/dist/hoi4_agent_tools/core/scanner.d.ts +28 -0
- package/dist/hoi4_agent_tools/core/scanner.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/scanner.js +136 -0
- package/dist/hoi4_agent_tools/core/scanner.js.map +1 -0
- package/dist/hoi4_agent_tools/core/server-state.d.ts +31 -0
- package/dist/hoi4_agent_tools/core/server-state.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/server-state.js +413 -0
- package/dist/hoi4_agent_tools/core/server-state.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts +10 -0
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/encoding.js +54 -0
- package/dist/hoi4_agent_tools/core/source/encoding.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/index.d.ts +7 -0
- package/dist/hoi4_agent_tools/core/source/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/index.js +7 -0
- package/dist/hoi4_agent_tools/core/source/index.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts +30 -0
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/lexer.js +266 -0
- package/dist/hoi4_agent_tools/core/source/lexer.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/limits.d.ts +21 -0
- package/dist/hoi4_agent_tools/core/source/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/limits.js +49 -0
- package/dist/hoi4_agent_tools/core/source/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts +20 -0
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/localisation.js +163 -0
- package/dist/hoi4_agent_tools/core/source/localisation.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/parser.d.ts +44 -0
- package/dist/hoi4_agent_tools/core/source/parser.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/parser.js +334 -0
- package/dist/hoi4_agent_tools/core/source/parser.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts +10 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.js +28 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.js.map +1 -0
- package/dist/hoi4_agent_tools/core/svg-text.d.ts +34 -0
- package/dist/hoi4_agent_tools/core/svg-text.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/svg-text.js +127 -0
- package/dist/hoi4_agent_tools/core/svg-text.js.map +1 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts +9 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js +9 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js.map +1 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts +162 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/transactions.js +1181 -0
- package/dist/hoi4_agent_tools/core/transactions.js.map +1 -0
- package/dist/hoi4_agent_tools/core/workspace.d.ts +68 -0
- package/dist/hoi4_agent_tools/core/workspace.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/workspace.js +794 -0
- package/dist/hoi4_agent_tools/core/workspace.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/compiler.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/compiler.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/compiler.js +273 -0
- package/dist/hoi4_agent_tools/focus/compiler.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts +32 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.js +179 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/drift.d.ts +22 -0
- package/dist/hoi4_agent_tools/focus/drift.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/drift.js +124 -0
- package/dist/hoi4_agent_tools/focus/drift.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/geometry.d.ts +17 -0
- package/dist/hoi4_agent_tools/focus/geometry.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/geometry.js +35 -0
- package/dist/hoi4_agent_tools/focus/geometry.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/importer.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/importer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/importer.js +732 -0
- package/dist/hoi4_agent_tools/focus/importer.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/index.d.ts +14 -0
- package/dist/hoi4_agent_tools/focus/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/index.js +14 -0
- package/dist/hoi4_agent_tools/focus/index.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/layout.d.ts +5 -0
- package/dist/hoi4_agent_tools/focus/layout.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/layout.js +653 -0
- package/dist/hoi4_agent_tools/focus/layout.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/limits.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/limits.js +9 -0
- package/dist/hoi4_agent_tools/focus/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/lint.d.ts +14 -0
- package/dist/hoi4_agent_tools/focus/lint.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/lint.js +839 -0
- package/dist/hoi4_agent_tools/focus/lint.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/model.d.ts +303 -0
- package/dist/hoi4_agent_tools/focus/model.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/model.js +20 -0
- package/dist/hoi4_agent_tools/focus/model.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/native-effects.js +563 -0
- package/dist/hoi4_agent_tools/focus/native-effects.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/planning.d.ts +15 -0
- package/dist/hoi4_agent_tools/focus/planning.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/planning.js +241 -0
- package/dist/hoi4_agent_tools/focus/planning.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/presentation.d.ts +24 -0
- package/dist/hoi4_agent_tools/focus/presentation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/presentation.js +204 -0
- package/dist/hoi4_agent_tools/focus/presentation.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts +35 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/render.js +491 -0
- package/dist/hoi4_agent_tools/focus/render.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/service.d.ts +82 -0
- package/dist/hoi4_agent_tools/focus/service.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/service.js +492 -0
- package/dist/hoi4_agent_tools/focus/service.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/source-update.d.ts +6 -0
- package/dist/hoi4_agent_tools/focus/source-update.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/source-update.js +328 -0
- package/dist/hoi4_agent_tools/focus/source-update.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts +37 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.js +66 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/assets.d.ts +71 -0
- package/dist/hoi4_agent_tools/gui/assets.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/assets.js +629 -0
- package/dist/hoi4_agent_tools/gui/assets.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/dds.d.ts +17 -0
- package/dist/hoi4_agent_tools/gui/dds.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/dds.js +245 -0
- package/dist/hoi4_agent_tools/gui/dds.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/helpers.d.ts +101 -0
- package/dist/hoi4_agent_tools/gui/helpers.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/helpers.js +1072 -0
- package/dist/hoi4_agent_tools/gui/helpers.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/index.d.ts +15 -0
- package/dist/hoi4_agent_tools/gui/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/index.js +15 -0
- package/dist/hoi4_agent_tools/gui/index.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/layout.d.ts +6 -0
- package/dist/hoi4_agent_tools/gui/layout.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/layout.js +753 -0
- package/dist/hoi4_agent_tools/gui/layout.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/limits.d.ts +25 -0
- package/dist/hoi4_agent_tools/gui/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/limits.js +27 -0
- package/dist/hoi4_agent_tools/gui/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/renderer.d.ts +18 -0
- package/dist/hoi4_agent_tools/gui/renderer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/renderer.js +388 -0
- package/dist/hoi4_agent_tools/gui/renderer.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/scenario.d.ts +66 -0
- package/dist/hoi4_agent_tools/gui/scenario.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/scenario.js +191 -0
- package/dist/hoi4_agent_tools/gui/scenario.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts +7 -0
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js +1074 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts +13 -0
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-patch.js +90 -0
- package/dist/hoi4_agent_tools/gui/source-patch.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/studio.d.ts +86 -0
- package/dist/hoi4_agent_tools/gui/studio.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/studio.js +830 -0
- package/dist/hoi4_agent_tools/gui/studio.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/tga.d.ts +16 -0
- package/dist/hoi4_agent_tools/gui/tga.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/tga.js +157 -0
- package/dist/hoi4_agent_tools/gui/tga.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/types.d.ts +322 -0
- package/dist/hoi4_agent_tools/gui/types.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/types.js +19 -0
- package/dist/hoi4_agent_tools/gui/types.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/validators.d.ts +8 -0
- package/dist/hoi4_agent_tools/gui/validators.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/validators.js +951 -0
- package/dist/hoi4_agent_tools/gui/validators.js.map +1 -0
- package/dist/hoi4_agent_tools/map/bmp.d.ts +62 -0
- package/dist/hoi4_agent_tools/map/bmp.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/bmp.js +363 -0
- package/dist/hoi4_agent_tools/map/bmp.js.map +1 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts +5 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js +21 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js.map +1 -0
- package/dist/hoi4_agent_tools/map/index.d.ts +8 -0
- package/dist/hoi4_agent_tools/map/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/index.js +8 -0
- package/dist/hoi4_agent_tools/map/index.js.map +1 -0
- package/dist/hoi4_agent_tools/map/limits.d.ts +13 -0
- package/dist/hoi4_agent_tools/map/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/limits.js +13 -0
- package/dist/hoi4_agent_tools/map/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/map/model.d.ts +269 -0
- package/dist/hoi4_agent_tools/map/model.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/model.js +1114 -0
- package/dist/hoi4_agent_tools/map/model.js.map +1 -0
- package/dist/hoi4_agent_tools/map/operations.d.ts +401 -0
- package/dist/hoi4_agent_tools/map/operations.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/operations.js +2717 -0
- package/dist/hoi4_agent_tools/map/operations.js.map +1 -0
- package/dist/hoi4_agent_tools/map/render.d.ts +90 -0
- package/dist/hoi4_agent_tools/map/render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/render.js +759 -0
- package/dist/hoi4_agent_tools/map/render.js.map +1 -0
- package/dist/hoi4_agent_tools/map/service.d.ts +81 -0
- package/dist/hoi4_agent_tools/map/service.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/service.js +695 -0
- package/dist/hoi4_agent_tools/map/service.js.map +1 -0
- package/dist/hoi4_agent_tools/map/validation.d.ts +23 -0
- package/dist/hoi4_agent_tools/map/validation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/validation.js +1177 -0
- package/dist/hoi4_agent_tools/map/validation.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts +3 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.js +50 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts +9 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js +10 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js +278 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts +24 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.js +234 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts +35 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +835 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +26 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts +133 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js +127 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts +8 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js +21 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts +232 -0
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js +353 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +1059 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts +111 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +598 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +685 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts +58 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js +181 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts +26 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js +86 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts +28 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js +744 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts +15 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js +51 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js.map +1 -0
- package/dist/hoi4_agent_tools/runtime.d.ts +4 -0
- package/dist/hoi4_agent_tools/runtime.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/runtime.js +27 -0
- package/dist/hoi4_agent_tools/runtime.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/common.d.ts +74 -0
- package/dist/hoi4_agent_tools/schemas/common.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/common.js +51 -0
- package/dist/hoi4_agent_tools/schemas/common.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/focus.d.ts +784 -0
- package/dist/hoi4_agent_tools/schemas/focus.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/focus.js +330 -0
- package/dist/hoi4_agent_tools/schemas/focus.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/index.d.ts +5 -0
- package/dist/hoi4_agent_tools/schemas/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/index.js +5 -0
- package/dist/hoi4_agent_tools/schemas/index.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/map.d.ts +654 -0
- package/dist/hoi4_agent_tools/schemas/map.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/map.js +570 -0
- package/dist/hoi4_agent_tools/schemas/map.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts +259 -0
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/transaction.js +104 -0
- package/dist/hoi4_agent_tools/schemas/transaction.js.map +1 -0
- package/dist/hoi4_agent_tools/version.d.ts +6 -0
- package/dist/hoi4_agent_tools/version.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/version.js +7 -0
- package/dist/hoi4_agent_tools/version.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +22 -0
- package/docs/adr/0001-runtime-and-language.md +16 -0
- package/docs/adr/0002-lossless-source-model.md +18 -0
- package/docs/adr/0003-image-and-font-codecs.md +18 -0
- package/docs/adr/0004-focus-layout.md +16 -0
- package/docs/adr/0005-offline-rendering.md +18 -0
- package/docs/adr/0006-mcp-sdk-and-protocol.md +16 -0
- package/docs/adr/0007-storage-and-transactions.md +24 -0
- package/docs/adr/0008-test-strategy.md +18 -0
- package/docs/adr/0009-packaging-and-registry.md +16 -0
- package/docs/adr/0010-unsupported-and-refusal-policy.md +18 -0
- package/docs/adr/0011-http-security.md +16 -0
- package/docs/adr/README.md +13 -0
- package/docs/architecture.md +60 -0
- package/docs/artifacts.md +133 -0
- package/docs/compatibility.md +33 -0
- package/docs/completion-report.md +69 -0
- package/docs/configuration.md +285 -0
- package/docs/focus-workflow.md +68 -0
- package/docs/gui-workflow.md +121 -0
- package/docs/limitations.md +30 -0
- package/docs/map-workflow.md +89 -0
- package/docs/performance.md +117 -0
- package/docs/release.md +200 -0
- package/docs/research/mcp-2026-07-10.md +46 -0
- package/docs/research/source-ledger.md +68 -0
- package/docs/security.md +139 -0
- package/docs/self-hosting.md +168 -0
- package/docs/testing.md +43 -0
- package/docs/third-party-notices.md +11 -0
- package/docs/transactions.md +162 -0
- package/docs/troubleshooting.md +46 -0
- package/package.json +98 -5
- package/schemas/configuration.schema.json +794 -0
- package/schemas/continuous-focus-palette.schema.json +325 -0
- package/schemas/focus-plan.schema.json +902 -0
- package/schemas/focus-planning-sidecar.schema.json +364 -0
- package/schemas/gui-animation-source.schema.json +176 -0
- package/schemas/gui-helper.schema.json +301 -0
- package/schemas/gui-scenario.schema.json +298 -0
- package/schemas/map-operation.schema.json +2284 -0
- package/schemas/operation-result.schema.json +344 -0
- package/schemas/transaction-manifest.schema.json +685 -0
- package/server.json +32 -0
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ArtifactStore, boundedSourceHashEvidence, publicArtifactLink, } from '../core/artifacts.js';
|
|
5
|
+
import { compareCodeUnits, canonicalJson, deterministicId, sha256Bytes, } from '../core/canonical.js';
|
|
6
|
+
import { DiagnosticCollector } from '../core/diagnostic-collector.js';
|
|
7
|
+
import { sortDiagnostics } from '../core/diagnostics.js';
|
|
8
|
+
import { CoreEngine } from '../core/engine.js';
|
|
9
|
+
import { assertRenderDimensions, RenderBudget, RENDER_MAX_PIXELS } from '../core/render-budget.js';
|
|
10
|
+
import { ServiceError } from '../core/result.js';
|
|
11
|
+
import { WorkspaceScanner } from '../core/scanner.js';
|
|
12
|
+
import { applyReplacements, parseClausewitz, sourcePartialLimitDiagnostics, } from '../core/source/index.js';
|
|
13
|
+
import { readDependenciesFromScannedFiles, } from '../core/transactions.js';
|
|
14
|
+
import { PACKAGE_VERSION } from '../version.js';
|
|
15
|
+
import { isSafeAnimationSourcePath } from './animation-manifest.js';
|
|
16
|
+
import { GuiAssetCatalog, parseBmFont } from './assets.js';
|
|
17
|
+
import { planGuiHelperCompilation } from './helpers.js';
|
|
18
|
+
import { buildGuiScene } from './layout.js';
|
|
19
|
+
import { GUI_VALIDATION_MAX_DIAGNOSTICS } from './limits.js';
|
|
20
|
+
import { compareGuiImages, galleryDimensions, renderGallerySvg, renderGuiScene, } from './renderer.js';
|
|
21
|
+
import { parsePreviewScenario } from './scenario.js';
|
|
22
|
+
import { buildGuiSourceGraph } from './source-graph.js';
|
|
23
|
+
import { assertGuiSourcePatchesSafe } from './source-patch.js';
|
|
24
|
+
import { validateGuiScene, validateResolutionDrift, validateStateMatrix } from './validators.js';
|
|
25
|
+
const staticGuiDefinitionPatterns = [
|
|
26
|
+
'common/scripted_localisation/**/*.txt',
|
|
27
|
+
'common/decisions/**/*.txt',
|
|
28
|
+
'common/decision_categories/**/*.txt',
|
|
29
|
+
'fonts/**/*.{gfx,txt}',
|
|
30
|
+
'hoi4_agent/animation_sources/**/*.json',
|
|
31
|
+
];
|
|
32
|
+
function normalizeConfiguredRoot(value) {
|
|
33
|
+
const normalized = value.replaceAll('\\', '/').replace(/^\.\//u, '').replace(/\/$/u, '');
|
|
34
|
+
if (!isSafeAnimationSourcePath(normalized)) {
|
|
35
|
+
throw new ServiceError('GUI_CONFIGURED_ROOT_INVALID', `GUI source root must be a safe workspace-relative path: ${value}`);
|
|
36
|
+
}
|
|
37
|
+
return normalized;
|
|
38
|
+
}
|
|
39
|
+
function under(roots, suffixes) {
|
|
40
|
+
return roots.flatMap((root) => {
|
|
41
|
+
const normalized = normalizeConfiguredRoot(root);
|
|
42
|
+
return suffixes.map((suffix) => `${normalized}/${suffix}`);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function guiDefinitionPatterns(workspace, languages = ['l_english']) {
|
|
46
|
+
const roots = workspace.registration.roots;
|
|
47
|
+
const languageDirectories = [...new Set(languages)].map((language) => {
|
|
48
|
+
if (!/^l_[a-z_]+$/u.test(language)) {
|
|
49
|
+
throw new ServiceError('GUI_LANGUAGE_INVALID', `Invalid GUI preview language: ${language}`);
|
|
50
|
+
}
|
|
51
|
+
return language.slice(2);
|
|
52
|
+
});
|
|
53
|
+
return [
|
|
54
|
+
...new Set([
|
|
55
|
+
...under(roots.interface, ['**/*.gui', '**/*.gfx']),
|
|
56
|
+
...under(roots.gfx, ['**/*.gfx']),
|
|
57
|
+
...under(roots.scriptedGui, ['**/*.txt']),
|
|
58
|
+
...under(roots.localisation, [
|
|
59
|
+
'*.{yml,yaml}',
|
|
60
|
+
...languageDirectories.flatMap((directory) => [
|
|
61
|
+
`${directory}/**/*.yml`,
|
|
62
|
+
`${directory}/**/*.yaml`,
|
|
63
|
+
]),
|
|
64
|
+
]),
|
|
65
|
+
...staticGuiDefinitionPatterns,
|
|
66
|
+
]),
|
|
67
|
+
].sort((left, right) => compareCodeUnits(left, right));
|
|
68
|
+
}
|
|
69
|
+
function normalizedAssetReference(value) {
|
|
70
|
+
const normalized = value.replaceAll('\\', '/').replace(/^\/+|^\.\//u, '');
|
|
71
|
+
return isSafeAnimationSourcePath(normalized) ? normalized : undefined;
|
|
72
|
+
}
|
|
73
|
+
function referenceVariants(value) {
|
|
74
|
+
const normalized = normalizedAssetReference(value);
|
|
75
|
+
if (normalized === undefined)
|
|
76
|
+
return [];
|
|
77
|
+
const extension = path.posix.extname(normalized);
|
|
78
|
+
const candidates = extension.length === 0
|
|
79
|
+
? ['.fnt', '.ttf', '.otf', '.woff', '.woff2', '.png', '.bmp', '.tga', '.dds', '.svg'].map((suffix) => `${normalized}${suffix}`)
|
|
80
|
+
: [normalized];
|
|
81
|
+
return candidates;
|
|
82
|
+
}
|
|
83
|
+
function basenameFallbackPatterns(exactPaths, exactFiles) {
|
|
84
|
+
const resolved = new Set(exactFiles
|
|
85
|
+
.filter(({ shadowedBy }) => shadowedBy === undefined)
|
|
86
|
+
.map(({ relativePath }) => relativePath.replaceAll('\\', '/').toLowerCase()));
|
|
87
|
+
return [
|
|
88
|
+
...new Set(exactPaths
|
|
89
|
+
.filter((candidate) => !resolved.has(candidate.toLowerCase()))
|
|
90
|
+
.map((candidate) => `**/${path.posix.basename(candidate)}`)),
|
|
91
|
+
].sort((left, right) => compareCodeUnits(left, right));
|
|
92
|
+
}
|
|
93
|
+
function selectedElements(graph, windowName) {
|
|
94
|
+
const byId = new Map(graph.elements.map((element) => [element.id, element]));
|
|
95
|
+
const pending = graph.elements.filter(({ name }) => name === windowName).map(({ id }) => id);
|
|
96
|
+
const selected = new Map();
|
|
97
|
+
while (pending.length > 0) {
|
|
98
|
+
const id = pending.pop();
|
|
99
|
+
if (id === undefined || selected.has(id))
|
|
100
|
+
continue;
|
|
101
|
+
const element = byId.get(id);
|
|
102
|
+
if (element === undefined)
|
|
103
|
+
continue;
|
|
104
|
+
selected.set(id, element);
|
|
105
|
+
pending.push(...element.childIds);
|
|
106
|
+
}
|
|
107
|
+
return [...selected.values()];
|
|
108
|
+
}
|
|
109
|
+
function assertTargetWindowAvailable(graph, windowName) {
|
|
110
|
+
if (graph.elements.some(({ name }) => name === windowName))
|
|
111
|
+
return;
|
|
112
|
+
if (!graph.skippedPossibleSymbolKinds.includes('gui_element'))
|
|
113
|
+
return;
|
|
114
|
+
const possibleSources = graph.skippedSources.filter(({ possibleSymbolKinds }) => possibleSymbolKinds.includes('gui_element'));
|
|
115
|
+
throw new ServiceError('GUI_TARGET_SOURCE_SKIPPED_LIMIT', `The partial GUI inventory cannot determine whether skipped source defines window ${windowName}`, {
|
|
116
|
+
windowName,
|
|
117
|
+
skippedSourceCount: graph.skippedSourceCount,
|
|
118
|
+
skippedSourceSamplesRetained: possibleSources.length,
|
|
119
|
+
skippedSources: possibleSources.slice(0, 10).map(({ path, reasonCodes }) => ({
|
|
120
|
+
path,
|
|
121
|
+
reasonCodes,
|
|
122
|
+
})),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function collectNamedAttributes(value, keys, output) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
for (const entry of value)
|
|
128
|
+
collectNamedAttributes(entry, keys, output);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (typeof value !== 'object' || value === null)
|
|
132
|
+
return;
|
|
133
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
134
|
+
if (keys.has(key) && typeof entry === 'string')
|
|
135
|
+
output.add(entry);
|
|
136
|
+
collectNamedAttributes(entry, keys, output);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function referencedAssetPatternsForWindow(graph, windowName) {
|
|
140
|
+
const spriteNames = new Set();
|
|
141
|
+
const fontNames = new Set();
|
|
142
|
+
for (const element of selectedElements(graph, windowName)) {
|
|
143
|
+
collectNamedAttributes(element.attributes, new Set(['spriteType', 'quadTextureSprite']), spriteNames);
|
|
144
|
+
collectNamedAttributes(element.attributes, new Set(['font', 'buttonFont']), fontNames);
|
|
145
|
+
}
|
|
146
|
+
const spritesByName = new Map(graph.sprites.map((sprite) => [sprite.name, sprite]));
|
|
147
|
+
const selectedSprites = new Map();
|
|
148
|
+
const pendingSprites = [...spriteNames];
|
|
149
|
+
while (pendingSprites.length > 0) {
|
|
150
|
+
const name = pendingSprites.pop();
|
|
151
|
+
if (name === undefined || selectedSprites.has(name))
|
|
152
|
+
continue;
|
|
153
|
+
const sprite = spritesByName.get(name);
|
|
154
|
+
if (sprite === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
selectedSprites.set(name, sprite);
|
|
157
|
+
if (sprite.staticFallback !== undefined)
|
|
158
|
+
pendingSprites.push(sprite.staticFallback);
|
|
159
|
+
}
|
|
160
|
+
const selectedManifests = graph.animationSources.filter(({ sprite }) => selectedSprites.has(sprite));
|
|
161
|
+
const selectedFonts = graph.fonts.filter(({ name }) => fontNames.has(name));
|
|
162
|
+
const references = [
|
|
163
|
+
...[...selectedSprites.values()].flatMap(({ texturePath, texturePath2 }) => [texturePath, texturePath2].filter((value) => value !== undefined)),
|
|
164
|
+
...selectedFonts.flatMap(({ assetPaths }) => assetPaths),
|
|
165
|
+
...selectedManifests.flatMap((manifest) => [
|
|
166
|
+
manifest.sheet.path,
|
|
167
|
+
manifest.staticFallback.path,
|
|
168
|
+
...manifest.sourceFrames.map(({ path: framePath }) => framePath),
|
|
169
|
+
]),
|
|
170
|
+
];
|
|
171
|
+
return [...new Set(references.flatMap(referenceVariants))].sort((left, right) => compareCodeUnits(left, right));
|
|
172
|
+
}
|
|
173
|
+
function bmFontPagePatterns(files) {
|
|
174
|
+
const references = files.flatMap((file) => {
|
|
175
|
+
if (!file.relativePath.toLowerCase().endsWith('.fnt') || file.shadowedBy !== undefined)
|
|
176
|
+
return [];
|
|
177
|
+
const metrics = parseBmFont(file.bytes.toString('utf8'));
|
|
178
|
+
if (metrics === undefined)
|
|
179
|
+
return [];
|
|
180
|
+
return metrics.pages.map((page) => path.posix.join(path.posix.dirname(file.relativePath), page));
|
|
181
|
+
});
|
|
182
|
+
return [...new Set(references.flatMap(referenceVariants))].sort((left, right) => compareCodeUnits(left, right));
|
|
183
|
+
}
|
|
184
|
+
function mergeScannedFiles(...groups) {
|
|
185
|
+
const byDisplayPath = new Map();
|
|
186
|
+
for (const file of groups.flat())
|
|
187
|
+
byDisplayPath.set(file.displayPath, file);
|
|
188
|
+
return [...byDisplayPath.values()].sort((left, right) => left.loadOrder - right.loadOrder || compareCodeUnits(left.displayPath, right.displayPath));
|
|
189
|
+
}
|
|
190
|
+
function withGraphDiagnostics(graph, validation) {
|
|
191
|
+
const hard = graph.diagnostics.filter(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
192
|
+
const diagnostics = new DiagnosticCollector(GUI_VALIDATION_MAX_DIAGNOSTICS, {
|
|
193
|
+
code: 'GUI_VALIDATION_DIAGNOSTICS_TRUNCATED',
|
|
194
|
+
category: 'layout',
|
|
195
|
+
message: 'Combined GUI source and validation diagnostics exceeded the fixed result ceiling',
|
|
196
|
+
});
|
|
197
|
+
diagnostics.pushMany(graph.diagnostics);
|
|
198
|
+
diagnostics.pushMany(validation.diagnostics);
|
|
199
|
+
return {
|
|
200
|
+
diagnostics: sortDiagnostics(diagnostics.values()),
|
|
201
|
+
checks: [
|
|
202
|
+
{
|
|
203
|
+
id: 'gui-source-graph',
|
|
204
|
+
passed: hard.length === 0,
|
|
205
|
+
message: hard.length === 0
|
|
206
|
+
? 'GUI source graph parsed and linked without blocking diagnostics'
|
|
207
|
+
: `GUI source graph has ${hard.length} blocking diagnostic${hard.length === 1 ? '' : 's'}`,
|
|
208
|
+
},
|
|
209
|
+
...validation.checks,
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const galleryStates = [
|
|
214
|
+
'normal',
|
|
215
|
+
'hover',
|
|
216
|
+
'selected',
|
|
217
|
+
'locked',
|
|
218
|
+
'disabled',
|
|
219
|
+
'warning',
|
|
220
|
+
'active',
|
|
221
|
+
'completed',
|
|
222
|
+
'empty-list',
|
|
223
|
+
'full-list',
|
|
224
|
+
'minimum-value',
|
|
225
|
+
'maximum-value',
|
|
226
|
+
'long-text',
|
|
227
|
+
'missing-localisation',
|
|
228
|
+
];
|
|
229
|
+
function safeSlug(value) {
|
|
230
|
+
return (value
|
|
231
|
+
.replace(/[^A-Za-z0-9._-]+/gu, '-')
|
|
232
|
+
.replace(/^-+|-+$/gu, '')
|
|
233
|
+
.slice(0, 48) || 'gui');
|
|
234
|
+
}
|
|
235
|
+
function scenarioWith(base, patch) {
|
|
236
|
+
return parsePreviewScenario({ ...base, ...patch });
|
|
237
|
+
}
|
|
238
|
+
function fullImage(render) {
|
|
239
|
+
const image = render.images.find(({ variant }) => variant === 'full') ?? render.images[0];
|
|
240
|
+
if (image === undefined)
|
|
241
|
+
throw new Error('GUI render did not produce an image.');
|
|
242
|
+
return image;
|
|
243
|
+
}
|
|
244
|
+
export function guiArtifactProvenance(graph, kind, primaryScene, scenarioScenes = [primaryScene], metadata = {}) {
|
|
245
|
+
const { scenario } = primaryScene;
|
|
246
|
+
const sourceEvidence = boundedSourceHashEvidence(graph.sourceHashes);
|
|
247
|
+
const fidelitySummary = (report) => Object.fromEntries(Object.entries(report).map(([category, items]) => {
|
|
248
|
+
const fields = [...new Set(items.map(({ field }) => field))].sort((left, right) => compareCodeUnits(left, right));
|
|
249
|
+
return [
|
|
250
|
+
category,
|
|
251
|
+
{
|
|
252
|
+
count: items.length,
|
|
253
|
+
fields: fields.slice(0, 256),
|
|
254
|
+
fieldsTruncated: fields.length > 256,
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
}));
|
|
258
|
+
const scenarioSummary = (value) => ({
|
|
259
|
+
id: value.id,
|
|
260
|
+
resolution: value.resolution,
|
|
261
|
+
uiScale: value.uiScale,
|
|
262
|
+
state: value.state,
|
|
263
|
+
animationTimeSeconds: value.animationTimeSeconds,
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
kind,
|
|
267
|
+
toolVersion: PACKAGE_VERSION,
|
|
268
|
+
schemaVersion: 'gui-studio.v1',
|
|
269
|
+
sourceHashes: sourceEvidence.sourceHashes,
|
|
270
|
+
renderProfile: {
|
|
271
|
+
offline: true,
|
|
272
|
+
scenarioId: scenario.id,
|
|
273
|
+
sourceRevision: primaryScene.sourceRevision,
|
|
274
|
+
resolution: scenario.resolution,
|
|
275
|
+
uiScale: scenario.uiScale,
|
|
276
|
+
state: scenario.state,
|
|
277
|
+
animationTimeSeconds: scenario.animationTimeSeconds,
|
|
278
|
+
fidelity: fidelitySummary(primaryScene.fidelity),
|
|
279
|
+
scenarios: scenarioScenes.map((scene) => ({
|
|
280
|
+
scenario: scenarioSummary(scene.scenario),
|
|
281
|
+
sourceRevision: scene.sourceRevision,
|
|
282
|
+
fidelity: fidelitySummary(scene.fidelity),
|
|
283
|
+
})),
|
|
284
|
+
},
|
|
285
|
+
metadata: {
|
|
286
|
+
rendererLabel: 'OFFLINE APPROXIMATION · NOT HOI4',
|
|
287
|
+
...metadata,
|
|
288
|
+
sourceHashInventory: sourceEvidence.inventory,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
export class ScriptedGuiStudio {
|
|
293
|
+
engine;
|
|
294
|
+
resolver;
|
|
295
|
+
transactions;
|
|
296
|
+
scanner;
|
|
297
|
+
artifacts;
|
|
298
|
+
constructor(engineOrResolver, transactions, scanner = new WorkspaceScanner(), artifacts = new ArtifactStore()) {
|
|
299
|
+
this.engine =
|
|
300
|
+
engineOrResolver instanceof CoreEngine
|
|
301
|
+
? engineOrResolver
|
|
302
|
+
: new CoreEngine(engineOrResolver, {
|
|
303
|
+
scanner,
|
|
304
|
+
artifacts,
|
|
305
|
+
...(transactions === undefined ? {} : { transactions }),
|
|
306
|
+
});
|
|
307
|
+
this.resolver = this.engine.resolver;
|
|
308
|
+
this.transactions = this.engine.transactions;
|
|
309
|
+
this.scanner = this.engine.scanner;
|
|
310
|
+
this.artifacts = this.engine.artifacts;
|
|
311
|
+
}
|
|
312
|
+
async scan(workspaceId, principal, signal, languages = ['l_english']) {
|
|
313
|
+
signal?.throwIfAborted();
|
|
314
|
+
const workspace = this.resolver.get(workspaceId, principal);
|
|
315
|
+
const snapshot = await this.engine.scan(workspaceId, { patterns: guiDefinitionPatterns(workspace, languages) }, principal, signal);
|
|
316
|
+
signal?.throwIfAborted();
|
|
317
|
+
return {
|
|
318
|
+
files: snapshot.files,
|
|
319
|
+
graph: buildGuiSourceGraph(snapshot.files, snapshot.index),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
async scanWindow(workspaceId, windowName, principal, signal, languages = ['l_english']) {
|
|
323
|
+
const definitions = await this.scan(workspaceId, principal, signal, languages);
|
|
324
|
+
assertTargetWindowAvailable(definitions.graph, windowName);
|
|
325
|
+
const workspace = this.resolver.get(workspaceId, principal);
|
|
326
|
+
const referencedPatterns = referencedAssetPatternsForWindow(definitions.graph, windowName);
|
|
327
|
+
const exactReferenced = referencedPatterns.length === 0
|
|
328
|
+
? []
|
|
329
|
+
: await this.scanner.scan(workspace, {
|
|
330
|
+
patterns: referencedPatterns,
|
|
331
|
+
...(signal === undefined ? {} : { signal }),
|
|
332
|
+
});
|
|
333
|
+
signal?.throwIfAborted();
|
|
334
|
+
const fallbackPatterns = basenameFallbackPatterns(referencedPatterns, exactReferenced);
|
|
335
|
+
const fallbackReferenced = fallbackPatterns.length === 0
|
|
336
|
+
? []
|
|
337
|
+
: await this.scanner.scan(workspace, {
|
|
338
|
+
patterns: fallbackPatterns,
|
|
339
|
+
...(signal === undefined ? {} : { signal }),
|
|
340
|
+
});
|
|
341
|
+
const referenced = mergeScannedFiles(exactReferenced, fallbackReferenced);
|
|
342
|
+
const fontPagePatterns = bmFontPagePatterns(referenced);
|
|
343
|
+
const exactFontPages = fontPagePatterns.length === 0
|
|
344
|
+
? []
|
|
345
|
+
: await this.scanner.scan(workspace, {
|
|
346
|
+
patterns: fontPagePatterns,
|
|
347
|
+
...(signal === undefined ? {} : { signal }),
|
|
348
|
+
});
|
|
349
|
+
signal?.throwIfAborted();
|
|
350
|
+
const fontPageFallbackPatterns = basenameFallbackPatterns(fontPagePatterns, exactFontPages);
|
|
351
|
+
const fallbackFontPages = fontPageFallbackPatterns.length === 0
|
|
352
|
+
? []
|
|
353
|
+
: await this.scanner.scan(workspace, {
|
|
354
|
+
patterns: fontPageFallbackPatterns,
|
|
355
|
+
...(signal === undefined ? {} : { signal }),
|
|
356
|
+
});
|
|
357
|
+
const fontPages = mergeScannedFiles(exactFontPages, fallbackFontPages);
|
|
358
|
+
signal?.throwIfAborted();
|
|
359
|
+
const files = mergeScannedFiles(definitions.files, referenced, fontPages);
|
|
360
|
+
return { files, graph: buildGuiSourceGraph(files, this.engine.indexFiles(files)) };
|
|
361
|
+
}
|
|
362
|
+
async lint(input) {
|
|
363
|
+
input.signal?.throwIfAborted();
|
|
364
|
+
const scenario = parsePreviewScenario(input.scenario);
|
|
365
|
+
const relatedScenarios = (input.relatedScenarios ?? []).map(parsePreviewScenario);
|
|
366
|
+
const scanned = await this.scanWindow(input.workspaceId, input.windowName, input.principal, input.signal, [scenario.language, ...relatedScenarios.map(({ language }) => language)]);
|
|
367
|
+
const budget = new RenderBudget();
|
|
368
|
+
const catalog = new GuiAssetCatalog(scanned.graph, scanned.files, budget);
|
|
369
|
+
const scene = await buildGuiScene(scanned.graph, scanned.files, input.windowName, scenario, catalog);
|
|
370
|
+
const relatedScenes = [];
|
|
371
|
+
for (const related of relatedScenarios) {
|
|
372
|
+
input.signal?.throwIfAborted();
|
|
373
|
+
relatedScenes.push(await buildGuiScene(scanned.graph, scanned.files, input.windowName, related, catalog));
|
|
374
|
+
}
|
|
375
|
+
const validation = withGraphDiagnostics(scanned.graph, await validateGuiScene(scanned.graph, scene, scanned.files, relatedScenes, catalog));
|
|
376
|
+
return { scene, graph: scanned.graph, validation };
|
|
377
|
+
}
|
|
378
|
+
async compare(input) {
|
|
379
|
+
input.signal?.throwIfAborted();
|
|
380
|
+
const budget = new RenderBudget();
|
|
381
|
+
const beforeScenario = parsePreviewScenario(input.before);
|
|
382
|
+
const afterScenario = parsePreviewScenario(input.after);
|
|
383
|
+
const scanned = await this.scanWindow(input.workspaceId, input.windowName, input.principal, input.signal, [beforeScenario.language, afterScenario.language]);
|
|
384
|
+
const catalog = new GuiAssetCatalog(scanned.graph, scanned.files, budget);
|
|
385
|
+
const before = await buildGuiScene(scanned.graph, scanned.files, input.windowName, beforeScenario, catalog);
|
|
386
|
+
const after = await buildGuiScene(scanned.graph, scanned.files, input.windowName, afterScenario, catalog);
|
|
387
|
+
const [beforeRender, afterRender] = await Promise.all([
|
|
388
|
+
renderGuiScene(before, ['full'], input.signal, budget),
|
|
389
|
+
renderGuiScene(after, ['full'], input.signal, budget),
|
|
390
|
+
]);
|
|
391
|
+
const comparison = await compareGuiImages(fullImage(beforeRender).png, fullImage(afterRender).png, budget, input.signal);
|
|
392
|
+
const evidenceJson = `${canonicalJson({
|
|
393
|
+
offline: true,
|
|
394
|
+
kind: 'gui-comparison',
|
|
395
|
+
windowName: input.windowName,
|
|
396
|
+
sourceHashes: scanned.graph.sourceHashes,
|
|
397
|
+
before: {
|
|
398
|
+
scenario: before.scenario,
|
|
399
|
+
sourceRevision: before.sourceRevision,
|
|
400
|
+
fidelity: before.fidelity,
|
|
401
|
+
},
|
|
402
|
+
after: {
|
|
403
|
+
scenario: after.scenario,
|
|
404
|
+
sourceRevision: after.sourceRevision,
|
|
405
|
+
fidelity: after.fidelity,
|
|
406
|
+
},
|
|
407
|
+
comparison: JSON.parse(comparison.json),
|
|
408
|
+
})}\n`;
|
|
409
|
+
return { before, after, comparison, evidenceJson, graph: scanned.graph };
|
|
410
|
+
}
|
|
411
|
+
async renderAndStore(input) {
|
|
412
|
+
input.signal?.throwIfAborted();
|
|
413
|
+
const budget = new RenderBudget();
|
|
414
|
+
const workspace = this.resolver.get(input.workspaceId, input.principal);
|
|
415
|
+
const scenario = parsePreviewScenario(input.scenario);
|
|
416
|
+
const baselineScenario = parsePreviewScenario(input.comparisonScenario ?? { ...scenario, id: `${scenario.id}-comparison`, state: 'normal' });
|
|
417
|
+
const scanned = await this.scanWindow(input.workspaceId, input.windowName, input.principal, input.signal, [scenario.language, baselineScenario.language]);
|
|
418
|
+
const catalog = new GuiAssetCatalog(scanned.graph, scanned.files, budget);
|
|
419
|
+
const scene = await buildGuiScene(scanned.graph, scanned.files, input.windowName, scenario, catalog);
|
|
420
|
+
const render = await renderGuiScene(scene, undefined, input.signal, budget);
|
|
421
|
+
const stateScenes = [];
|
|
422
|
+
const stateItems = [];
|
|
423
|
+
for (const state of input.states ?? [...galleryStates]) {
|
|
424
|
+
input.signal?.throwIfAborted();
|
|
425
|
+
const stateScenario = scenarioWith(scenario, { id: `${scenario.id}-${state}`, state });
|
|
426
|
+
const stateScene = await buildGuiScene(scanned.graph, scanned.files, input.windowName, stateScenario, catalog);
|
|
427
|
+
stateScenes.push(stateScene);
|
|
428
|
+
const stateRender = await renderGuiScene(stateScene, ['full'], input.signal, budget);
|
|
429
|
+
const image = fullImage(stateRender);
|
|
430
|
+
stateItems.push({ label: state, png: image.png, width: image.width, height: image.height });
|
|
431
|
+
}
|
|
432
|
+
const resolutionScenes = [];
|
|
433
|
+
const resolutionItems = [];
|
|
434
|
+
const resolutions = input.resolutions ?? [
|
|
435
|
+
{ width: 1280, height: 720, uiScale: 1 },
|
|
436
|
+
{ width: 1920, height: 1080, uiScale: 1 },
|
|
437
|
+
{ width: 2560, height: 1440, uiScale: 1 },
|
|
438
|
+
{ width: 1920, height: 1080, uiScale: 1.25 },
|
|
439
|
+
];
|
|
440
|
+
for (const resolution of resolutions) {
|
|
441
|
+
input.signal?.throwIfAborted();
|
|
442
|
+
const uiScale = resolution.uiScale ?? scenario.uiScale;
|
|
443
|
+
const resolutionScenario = scenarioWith(scenario, {
|
|
444
|
+
id: `${scenario.id}-${resolution.width}x${resolution.height}-${uiScale}`,
|
|
445
|
+
resolution: { width: resolution.width, height: resolution.height },
|
|
446
|
+
uiScale,
|
|
447
|
+
});
|
|
448
|
+
const resolutionScene = await buildGuiScene(scanned.graph, scanned.files, input.windowName, resolutionScenario, catalog);
|
|
449
|
+
resolutionScenes.push(resolutionScene);
|
|
450
|
+
const resolutionRender = await renderGuiScene(resolutionScene, ['full'], input.signal, budget);
|
|
451
|
+
const image = fullImage(resolutionRender);
|
|
452
|
+
resolutionItems.push({
|
|
453
|
+
label: `${resolution.width}×${resolution.height} · UI ${uiScale}`,
|
|
454
|
+
png: image.png,
|
|
455
|
+
width: image.width,
|
|
456
|
+
height: image.height,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
const baselineScene = await buildGuiScene(scanned.graph, scanned.files, input.windowName, baselineScenario, catalog);
|
|
460
|
+
const baselineRender = await renderGuiScene(baselineScene, ['full'], input.signal, budget);
|
|
461
|
+
const comparison = await compareGuiImages(fullImage(baselineRender).png, fullImage(render).png, budget, input.signal);
|
|
462
|
+
const validation = await validateGuiScene(scanned.graph, scene, scanned.files, resolutionScenes, catalog);
|
|
463
|
+
const stateValidation = validateStateMatrix(stateScenes);
|
|
464
|
+
const resolutionValidation = validateResolutionDrift(resolutionScenes);
|
|
465
|
+
validation.diagnostics.push(...stateValidation.diagnostics, ...resolutionValidation.diagnostics);
|
|
466
|
+
validation.checks.push(...stateValidation.checks, ...resolutionValidation.checks);
|
|
467
|
+
const completeValidation = withGraphDiagnostics(scanned.graph, validation);
|
|
468
|
+
const stateGallerySvg = renderGallerySvg(`${input.windowName} state matrix`, stateItems);
|
|
469
|
+
const resolutionGallerySvg = renderGallerySvg(`${input.windowName} resolution and UI-scale matrix`, resolutionItems);
|
|
470
|
+
const stateGallerySize = galleryDimensions(stateItems);
|
|
471
|
+
const resolutionGallerySize = galleryDimensions(resolutionItems);
|
|
472
|
+
budget.reserve(stateGallerySize.width, stateGallerySize.height, 'GUI state gallery PNG');
|
|
473
|
+
budget.reserve(resolutionGallerySize.width, resolutionGallerySize.height, 'GUI resolution gallery PNG');
|
|
474
|
+
budget.reserveRasterOperation(`gui-gallery:${sha256Bytes(stateGallerySvg)}`, 'GUI state gallery SVG rasterization');
|
|
475
|
+
budget.reserveRasterOperation(`gui-gallery:${sha256Bytes(resolutionGallerySvg)}`, 'GUI resolution gallery SVG rasterization');
|
|
476
|
+
const [stateGalleryPng, resolutionGalleryPng] = await Promise.all([
|
|
477
|
+
sharp(Buffer.from(stateGallerySvg), { limitInputPixels: RENDER_MAX_PIXELS })
|
|
478
|
+
.png({ compressionLevel: 9, adaptiveFiltering: false, palette: false })
|
|
479
|
+
.toBuffer(),
|
|
480
|
+
sharp(Buffer.from(resolutionGallerySvg), { limitInputPixels: RENDER_MAX_PIXELS })
|
|
481
|
+
.png({ compressionLevel: 9, adaptiveFiltering: false, palette: false })
|
|
482
|
+
.toBuffer(),
|
|
483
|
+
]);
|
|
484
|
+
const slug = safeSlug(input.windowName);
|
|
485
|
+
const writes = [];
|
|
486
|
+
const add = (name, mimeType, content, kind, scenarioScenes = [scene], metadata = {}) => {
|
|
487
|
+
input.signal?.throwIfAborted();
|
|
488
|
+
writes.push({
|
|
489
|
+
name,
|
|
490
|
+
mimeType,
|
|
491
|
+
content,
|
|
492
|
+
provenance: guiArtifactProvenance(scanned.graph, kind, scenarioScenes[0] ?? scene, scenarioScenes, metadata),
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
for (const image of render.images) {
|
|
496
|
+
add(`${slug}-${image.variant}.svg`, 'image/svg+xml', image.svg, `gui-${image.variant}-svg`);
|
|
497
|
+
add(`${slug}-${image.variant}.png`, 'image/png', image.png, `gui-${image.variant}-png`);
|
|
498
|
+
}
|
|
499
|
+
add(`${slug}-hierarchy.svg`, 'image/svg+xml', render.hierarchySvg, 'gui-hierarchy');
|
|
500
|
+
add(`${slug}-layout.json`, 'application/json', render.layoutJson, 'gui-layout-json');
|
|
501
|
+
add(`${slug}-scenario.json`, 'application/json', render.scenarioJson, 'gui-preview-scenario');
|
|
502
|
+
add(`${slug}-fidelity.json`, 'application/json', `${canonicalJson({ offline: true, fidelity: render.fidelity })}\n`, 'gui-fidelity-report');
|
|
503
|
+
add(`${slug}-source-graph.json`, 'application/json', `${canonicalJson(scanned.graph)}\n`, 'gui-source-graph');
|
|
504
|
+
add(`${slug}-validation.json`, 'application/json', `${canonicalJson(completeValidation)}\n`, 'gui-validation-report', [scene, ...stateScenes, ...resolutionScenes]);
|
|
505
|
+
add(`${slug}-state-matrix.svg`, 'image/svg+xml', stateGallerySvg, 'gui-state-matrix', stateScenes);
|
|
506
|
+
add(`${slug}-state-matrix.png`, 'image/png', stateGalleryPng, 'gui-state-matrix', stateScenes);
|
|
507
|
+
add(`${slug}-state-matrix.json`, 'application/json', `${canonicalJson({ offline: true, scenarios: stateScenes.map(({ scenario: stateScenario, fidelity }) => ({ scenario: stateScenario, fidelity })) })}\n`, 'gui-state-matrix-json', stateScenes);
|
|
508
|
+
add(`${slug}-resolution-scale.svg`, 'image/svg+xml', resolutionGallerySvg, 'gui-resolution-scale-matrix', resolutionScenes);
|
|
509
|
+
add(`${slug}-resolution-scale.png`, 'image/png', resolutionGalleryPng, 'gui-resolution-scale-matrix', resolutionScenes);
|
|
510
|
+
add(`${slug}-resolution-scale.json`, 'application/json', `${canonicalJson({ offline: true, scenarios: resolutionScenes.map(({ scenario: resolutionScenario, fidelity }) => ({ scenario: resolutionScenario, fidelity })) })}\n`, 'gui-resolution-scale-json', resolutionScenes);
|
|
511
|
+
add(`${slug}-comparison.png`, 'image/png', comparison.png, 'gui-before-after-comparison', [
|
|
512
|
+
scene,
|
|
513
|
+
baselineScene,
|
|
514
|
+
]);
|
|
515
|
+
add(`${slug}-comparison.json`, 'application/json', comparison.json, 'gui-before-after-comparison', [scene, baselineScene]);
|
|
516
|
+
const stored = await this.artifacts.withAtomicChunkedWrites(workspace, writes, (artifacts) => Promise.resolve([...artifacts]), input.signal);
|
|
517
|
+
return {
|
|
518
|
+
artifacts: stored,
|
|
519
|
+
render,
|
|
520
|
+
stateScenes,
|
|
521
|
+
resolutionScenes,
|
|
522
|
+
comparison,
|
|
523
|
+
validation: completeValidation,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
planHelpers(input) {
|
|
527
|
+
return planGuiHelperCompilation(this.transactions, input);
|
|
528
|
+
}
|
|
529
|
+
async planSource(input) {
|
|
530
|
+
input.signal?.throwIfAborted();
|
|
531
|
+
if (!input.relativePath.replaceAll('\\', '/').toLowerCase().endsWith('.gui'))
|
|
532
|
+
throw new Error('GUI source plans must target a .gui file.');
|
|
533
|
+
if ((input.source === undefined) === (input.patches === undefined))
|
|
534
|
+
throw new ServiceError('GUI_SOURCE_MODE_INVALID', 'Provide either a new source file or targeted source patches');
|
|
535
|
+
let proposedBytes;
|
|
536
|
+
if (input.patches !== undefined) {
|
|
537
|
+
if (input.expectedSourceHash === undefined ||
|
|
538
|
+
!/^[a-f0-9]{64}$/u.test(input.expectedSourceHash))
|
|
539
|
+
throw new ServiceError('GUI_EXPECTED_SOURCE_HASH_REQUIRED', 'Targeted GUI patches require the exact expected source hash');
|
|
540
|
+
const existing = await this.resolver.resolvePath(input.workspaceId, input.relativePath, 'read', ['mod'], input.principal);
|
|
541
|
+
const before = await readFile(existing.path);
|
|
542
|
+
if (sha256Bytes(before) !== input.expectedSourceHash)
|
|
543
|
+
throw new ServiceError('GUI_SOURCE_STALE', 'GUI source changed after the patch was prepared');
|
|
544
|
+
const document = parseClausewitz(before, `mod:${input.relativePath}`);
|
|
545
|
+
const limitDiagnostics = sourcePartialLimitDiagnostics(document.diagnostics);
|
|
546
|
+
if (limitDiagnostics.length > 0) {
|
|
547
|
+
throw new ServiceError('GUI_TARGET_SOURCE_SKIPPED_LIMIT', 'The targeted GUI source exceeds a parser limit and cannot be patched safely', {
|
|
548
|
+
path: `mod:${input.relativePath}`,
|
|
549
|
+
reasonCodes: [...new Set(limitDiagnostics.map(({ code }) => code))],
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
for (const patch of input.patches) {
|
|
553
|
+
if (document.text.slice(patch.start, patch.end) !== patch.expectedText) {
|
|
554
|
+
throw new ServiceError('GUI_PATCH_PRECONDITION_FAILED', 'A targeted GUI patch no longer matches its expected token range', { start: patch.start, end: patch.end });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
assertGuiSourcePatchesSafe(document, input.patches);
|
|
558
|
+
proposedBytes = applyReplacements(document, input.patches);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
try {
|
|
562
|
+
await this.resolver.resolvePath(input.workspaceId, input.relativePath, 'read', ['mod'], input.principal);
|
|
563
|
+
throw new ServiceError('GUI_UNSAFE_WHOLE_FILE_REWRITE', 'Existing GUI files may be changed only with source-linked targeted patches');
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
if (error instanceof ServiceError && error.code !== 'PATH_NOT_FOUND_IN_ROOTS')
|
|
567
|
+
throw error;
|
|
568
|
+
}
|
|
569
|
+
if (input.source === undefined)
|
|
570
|
+
throw new ServiceError('GUI_SOURCE_REQUIRED', 'A new GUI source file requires source text');
|
|
571
|
+
proposedBytes = Buffer.from(input.source, 'utf8');
|
|
572
|
+
}
|
|
573
|
+
const proposedDocument = parseClausewitz(proposedBytes, `mod:${input.relativePath}`);
|
|
574
|
+
if (proposedDocument.diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker'))
|
|
575
|
+
throw new ServiceError('GUI_PROPOSED_SOURCE_INVALID', 'Proposed GUI source contains blocking parser diagnostics', { diagnostics: proposedDocument.diagnostics });
|
|
576
|
+
if ((input.windowName === undefined) !== (input.scenario === undefined))
|
|
577
|
+
throw new Error('GUI visual preflight requires both windowName and scenario.');
|
|
578
|
+
const workspace = this.resolver.get(input.workspaceId, input.principal);
|
|
579
|
+
const scanned = input.windowName === undefined
|
|
580
|
+
? await this.scan(input.workspaceId, input.principal, input.signal)
|
|
581
|
+
: await this.scanWindow(input.workspaceId, input.windowName, input.principal, input.signal, [parsePreviewScenario(input.scenario).language]);
|
|
582
|
+
const normalizedPath = input.relativePath.replaceAll('\\', '/');
|
|
583
|
+
if (input.patches === undefined &&
|
|
584
|
+
scanned.files.some(({ relativePath, rootKind }) => relativePath === normalizedPath && rootKind !== 'mod')) {
|
|
585
|
+
throw new ServiceError('GUI_UNSAFE_SHADOW_REWRITE', 'Creating this GUI file would replace a dependency or game file; use a distinct file or an explicit source-preserving patch in the owning workspace');
|
|
586
|
+
}
|
|
587
|
+
const preflightDiagnostics = [];
|
|
588
|
+
const preflightChecks = [];
|
|
589
|
+
const preflightArtifacts = [];
|
|
590
|
+
let dependencyFiles = scanned.files;
|
|
591
|
+
if (input.windowName !== undefined && input.scenario !== undefined) {
|
|
592
|
+
const resolved = await this.resolver.resolvePath(input.workspaceId, input.relativePath, 'write', ['mod'], input.principal);
|
|
593
|
+
const displayPath = `mod:${normalizedPath}`;
|
|
594
|
+
const replacesModFile = scanned.files.some(({ rootKind, relativePath }) => rootKind === 'mod' && relativePath === normalizedPath);
|
|
595
|
+
let proposedFiles = scanned.files.map((file) => {
|
|
596
|
+
if (file.rootKind === 'mod' && file.relativePath === normalizedPath) {
|
|
597
|
+
return {
|
|
598
|
+
...file,
|
|
599
|
+
bytes: proposedBytes,
|
|
600
|
+
size: proposedBytes.length,
|
|
601
|
+
modifiedMs: 0,
|
|
602
|
+
sha256: sha256Bytes(proposedBytes),
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
if (file.relativePath === normalizedPath)
|
|
606
|
+
return { ...file, shadowedBy: displayPath };
|
|
607
|
+
return file;
|
|
608
|
+
});
|
|
609
|
+
if (!replacesModFile) {
|
|
610
|
+
proposedFiles.push({
|
|
611
|
+
absolutePath: resolved.path,
|
|
612
|
+
displayPath,
|
|
613
|
+
relativePath: normalizedPath,
|
|
614
|
+
rootKind: 'mod',
|
|
615
|
+
loadOrder: workspace.dependencyRoots.length + 1,
|
|
616
|
+
size: proposedBytes.length,
|
|
617
|
+
modifiedMs: 0,
|
|
618
|
+
sha256: sha256Bytes(proposedBytes),
|
|
619
|
+
bytes: proposedBytes,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
const scenario = parsePreviewScenario(input.scenario);
|
|
623
|
+
const renderBudget = new RenderBudget();
|
|
624
|
+
let proposedGraph = buildGuiSourceGraph(proposedFiles, this.engine.indexFiles(proposedFiles));
|
|
625
|
+
const proposedAssetPatterns = referencedAssetPatternsForWindow(proposedGraph, input.windowName);
|
|
626
|
+
const exactProposedAssets = proposedAssetPatterns.length === 0
|
|
627
|
+
? []
|
|
628
|
+
: await this.scanner.scan(workspace, {
|
|
629
|
+
patterns: proposedAssetPatterns,
|
|
630
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
631
|
+
});
|
|
632
|
+
const proposedFallbackPatterns = basenameFallbackPatterns(proposedAssetPatterns, exactProposedAssets);
|
|
633
|
+
const fallbackProposedAssets = proposedFallbackPatterns.length === 0
|
|
634
|
+
? []
|
|
635
|
+
: await this.scanner.scan(workspace, {
|
|
636
|
+
patterns: proposedFallbackPatterns,
|
|
637
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
638
|
+
});
|
|
639
|
+
const proposedAssets = mergeScannedFiles(exactProposedAssets, fallbackProposedAssets);
|
|
640
|
+
const proposedFontPagePatterns = bmFontPagePatterns(proposedAssets);
|
|
641
|
+
const exactProposedFontPages = proposedFontPagePatterns.length === 0
|
|
642
|
+
? []
|
|
643
|
+
: await this.scanner.scan(workspace, {
|
|
644
|
+
patterns: proposedFontPagePatterns,
|
|
645
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
646
|
+
});
|
|
647
|
+
const proposedFontFallbackPatterns = basenameFallbackPatterns(proposedFontPagePatterns, exactProposedFontPages);
|
|
648
|
+
const fallbackProposedFontPages = proposedFontFallbackPatterns.length === 0
|
|
649
|
+
? []
|
|
650
|
+
: await this.scanner.scan(workspace, {
|
|
651
|
+
patterns: proposedFontFallbackPatterns,
|
|
652
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
653
|
+
});
|
|
654
|
+
const proposedFontPages = mergeScannedFiles(exactProposedFontPages, fallbackProposedFontPages);
|
|
655
|
+
input.signal?.throwIfAborted();
|
|
656
|
+
dependencyFiles = mergeScannedFiles(scanned.files, proposedAssets, proposedFontPages);
|
|
657
|
+
proposedFiles = mergeScannedFiles(proposedFiles, proposedAssets, proposedFontPages);
|
|
658
|
+
proposedGraph = buildGuiSourceGraph(proposedFiles, this.engine.indexFiles(proposedFiles));
|
|
659
|
+
const proposedCatalog = new GuiAssetCatalog(proposedGraph, proposedFiles, renderBudget);
|
|
660
|
+
const proposedScene = await buildGuiScene(proposedGraph, proposedFiles, input.windowName, scenario, proposedCatalog);
|
|
661
|
+
const proposedRender = await renderGuiScene(proposedScene, ['full'], input.signal, renderBudget);
|
|
662
|
+
const proposedImage = fullImage(proposedRender);
|
|
663
|
+
const proposedValidation = await validateGuiScene(proposedGraph, proposedScene, proposedFiles, [], proposedCatalog);
|
|
664
|
+
preflightDiagnostics.push(...proposedGraph.diagnostics, ...proposedScene.diagnostics, ...proposedValidation.diagnostics);
|
|
665
|
+
const visualPassed = proposedValidation.checks.every(({ passed }) => passed) &&
|
|
666
|
+
!preflightDiagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
667
|
+
preflightChecks.push(...proposedValidation.checks, {
|
|
668
|
+
id: 'gui-visual-preflight',
|
|
669
|
+
passed: visualPassed,
|
|
670
|
+
message: visualPassed
|
|
671
|
+
? 'Proposed GUI renders and validates offline'
|
|
672
|
+
: 'Proposed GUI has blocking offline render or validation findings',
|
|
673
|
+
});
|
|
674
|
+
const baselineScene = await buildGuiScene(scanned.graph, scanned.files, input.windowName, scenario, new GuiAssetCatalog(scanned.graph, scanned.files, renderBudget));
|
|
675
|
+
let baselinePng;
|
|
676
|
+
if (scanned.graph.elements.some(({ name }) => name === input.windowName)) {
|
|
677
|
+
baselinePng = fullImage(await renderGuiScene(baselineScene, ['full'], input.signal, renderBudget)).png;
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
renderBudget.reserve(proposedImage.width, proposedImage.height, 'GUI empty comparison baseline');
|
|
681
|
+
assertRenderDimensions(proposedImage.width, proposedImage.height, 'GUI empty comparison Sharp raster');
|
|
682
|
+
renderBudget.reserveRasterOperation(`gui-empty-baseline:${proposedImage.width}x${proposedImage.height}`, 'GUI empty comparison rasterization');
|
|
683
|
+
baselinePng = await sharp({
|
|
684
|
+
create: {
|
|
685
|
+
width: proposedImage.width,
|
|
686
|
+
height: proposedImage.height,
|
|
687
|
+
channels: 4,
|
|
688
|
+
background: '#00000000',
|
|
689
|
+
},
|
|
690
|
+
})
|
|
691
|
+
.png({ compressionLevel: 9, adaptiveFiltering: false, palette: false })
|
|
692
|
+
.toBuffer();
|
|
693
|
+
preflightDiagnostics.push({
|
|
694
|
+
code: 'GUI_COMPARISON_BASELINE_EMPTY',
|
|
695
|
+
severity: 'info',
|
|
696
|
+
category: 'rendering',
|
|
697
|
+
message: 'The proposed window has no renderable baseline; comparison uses an explicit empty canvas.',
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
const comparison = await compareGuiImages(baselinePng, proposedImage.png, renderBudget, input.signal);
|
|
701
|
+
const slug = safeSlug(input.windowName);
|
|
702
|
+
const metadata = { relativePath: normalizedPath };
|
|
703
|
+
const writes = [
|
|
704
|
+
{
|
|
705
|
+
name: `${slug}-before.png`,
|
|
706
|
+
mimeType: 'image/png',
|
|
707
|
+
content: baselinePng,
|
|
708
|
+
provenance: guiArtifactProvenance(scanned.graph, 'gui-before-render', baselineScene, [baselineScene], metadata),
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
name: `${slug}-proposed.png`,
|
|
712
|
+
mimeType: 'image/png',
|
|
713
|
+
content: proposedImage.png,
|
|
714
|
+
provenance: guiArtifactProvenance(proposedGraph, 'gui-proposed-render', proposedScene, [proposedScene], metadata),
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
name: `${slug}-visual-diff.png`,
|
|
718
|
+
mimeType: 'image/png',
|
|
719
|
+
content: comparison.png,
|
|
720
|
+
provenance: guiArtifactProvenance(proposedGraph, 'gui-visual-diff', proposedScene, [proposedScene, baselineScene], metadata),
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: `${slug}-visual-diff.json`,
|
|
724
|
+
mimeType: 'application/json',
|
|
725
|
+
content: comparison.json,
|
|
726
|
+
provenance: guiArtifactProvenance(proposedGraph, 'gui-visual-diff-json', proposedScene, [proposedScene, baselineScene], metadata),
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
name: `${slug}-proposed-fidelity.json`,
|
|
730
|
+
mimeType: 'application/json',
|
|
731
|
+
content: `${canonicalJson({
|
|
732
|
+
offline: true,
|
|
733
|
+
scenario,
|
|
734
|
+
fidelity: proposedScene.fidelity,
|
|
735
|
+
validation: proposedValidation,
|
|
736
|
+
})}\n`,
|
|
737
|
+
provenance: guiArtifactProvenance(proposedGraph, 'gui-proposed-fidelity', proposedScene, [proposedScene], metadata),
|
|
738
|
+
},
|
|
739
|
+
];
|
|
740
|
+
const stored = await this.artifacts.withAtomicWrites(workspace, writes, (artifacts) => Promise.resolve([...artifacts]), input.signal);
|
|
741
|
+
preflightArtifacts.push(...stored.map(publicArtifactLink));
|
|
742
|
+
}
|
|
743
|
+
const operationId = deterministicId('gui_source_change', {
|
|
744
|
+
relativePath: input.relativePath,
|
|
745
|
+
sourceHash: sha256Bytes(proposedBytes),
|
|
746
|
+
patchCount: input.patches?.length ?? 0,
|
|
747
|
+
});
|
|
748
|
+
return this.transactions.plan({
|
|
749
|
+
workspaceId: input.workspaceId,
|
|
750
|
+
...(input.principal === undefined ? {} : { principal: input.principal }),
|
|
751
|
+
operationKind: 'gui-source-change',
|
|
752
|
+
operations: [
|
|
753
|
+
{
|
|
754
|
+
id: operationId,
|
|
755
|
+
kind: 'replace-gui-source',
|
|
756
|
+
summary: `Plan GUI source ${input.relativePath}`,
|
|
757
|
+
data: { relativePath: input.relativePath },
|
|
758
|
+
},
|
|
759
|
+
],
|
|
760
|
+
changes: [
|
|
761
|
+
{
|
|
762
|
+
relativePath: input.relativePath,
|
|
763
|
+
content: proposedBytes,
|
|
764
|
+
operationIds: [operationId],
|
|
765
|
+
mediaType: 'text/plain',
|
|
766
|
+
},
|
|
767
|
+
],
|
|
768
|
+
readDependencies: readDependenciesFromScannedFiles(dependencyFiles),
|
|
769
|
+
artifacts: preflightArtifacts,
|
|
770
|
+
diagnostics: preflightDiagnostics,
|
|
771
|
+
validate: (proposed) => {
|
|
772
|
+
const bytes = proposed.get(input.relativePath);
|
|
773
|
+
if (bytes === undefined || bytes === null)
|
|
774
|
+
return Promise.resolve({
|
|
775
|
+
diagnostics: [
|
|
776
|
+
{
|
|
777
|
+
code: 'GUI_PROPOSED_SOURCE_MISSING',
|
|
778
|
+
severity: 'blocker',
|
|
779
|
+
category: 'transaction',
|
|
780
|
+
message: `Proposed source is missing ${input.relativePath}`,
|
|
781
|
+
operationId,
|
|
782
|
+
},
|
|
783
|
+
],
|
|
784
|
+
checks: [
|
|
785
|
+
...preflightChecks,
|
|
786
|
+
{
|
|
787
|
+
id: 'gui-source-present',
|
|
788
|
+
passed: false,
|
|
789
|
+
message: 'Proposed GUI source is present.',
|
|
790
|
+
},
|
|
791
|
+
],
|
|
792
|
+
});
|
|
793
|
+
const document = parseClausewitz(bytes, `mod:${input.relativePath}`);
|
|
794
|
+
const passed = !document.diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
795
|
+
return Promise.resolve({
|
|
796
|
+
diagnostics: document.diagnostics.map((diagnostic) => ({ ...diagnostic, operationId })),
|
|
797
|
+
checks: [
|
|
798
|
+
...preflightChecks,
|
|
799
|
+
{ id: 'gui-source-syntax', passed, message: 'Proposed GUI source parses safely.' },
|
|
800
|
+
],
|
|
801
|
+
});
|
|
802
|
+
},
|
|
803
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
applyPlannedSource(workspaceId, transactionId, planHash, principal, signal) {
|
|
807
|
+
return this.transactions.apply(workspaceId, transactionId, planHash, {
|
|
808
|
+
...(principal === undefined ? {} : { principal }),
|
|
809
|
+
...(signal === undefined ? {} : { signal }),
|
|
810
|
+
postValidate: async (_manifest, validationSignal) => {
|
|
811
|
+
this.engine.invalidate(workspaceId);
|
|
812
|
+
const scanned = await this.scan(workspaceId, principal, validationSignal);
|
|
813
|
+
const passed = !scanned.graph.diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
814
|
+
return {
|
|
815
|
+
diagnostics: scanned.graph.diagnostics,
|
|
816
|
+
checks: [
|
|
817
|
+
{
|
|
818
|
+
id: 'post-write-gui-graph',
|
|
819
|
+
passed,
|
|
820
|
+
message: passed
|
|
821
|
+
? 'Applied GUI source graph rebuilt successfully'
|
|
822
|
+
: 'Applied GUI source graph has blocking diagnostics',
|
|
823
|
+
},
|
|
824
|
+
],
|
|
825
|
+
};
|
|
826
|
+
},
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
//# sourceMappingURL=studio.js.map
|