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.
Files changed (428) hide show
  1. package/CHANGELOG.md +12 -118
  2. package/README.md +35 -127
  3. package/SECURITY.md +5 -7
  4. package/dist/bin/http.js +0 -1
  5. package/dist/bin/setup.js +141 -92
  6. package/dist/bin/stdio.js +0 -1
  7. package/dist/hoi4_agent_tools/core/artifacts.js +0 -1
  8. package/dist/hoi4_agent_tools/core/canonical.js +0 -1
  9. package/dist/hoi4_agent_tools/core/configuration.js +28 -28
  10. package/dist/hoi4_agent_tools/core/diagnostic-collector.js +0 -1
  11. package/dist/hoi4_agent_tools/core/diagnostics.js +0 -1
  12. package/dist/hoi4_agent_tools/core/diff.js +0 -1
  13. package/dist/hoi4_agent_tools/core/engine.js +6 -21
  14. package/dist/hoi4_agent_tools/core/image-diff.js +0 -1
  15. package/dist/hoi4_agent_tools/core/index.js +0 -1
  16. package/dist/hoi4_agent_tools/core/render-budget.js +0 -1
  17. package/dist/hoi4_agent_tools/core/result.js +0 -1
  18. package/dist/hoi4_agent_tools/core/scanner.js +0 -1
  19. package/dist/hoi4_agent_tools/core/server-state.js +0 -1
  20. package/dist/hoi4_agent_tools/core/source/encoding.js +0 -1
  21. package/dist/hoi4_agent_tools/core/source/index.js +0 -1
  22. package/dist/hoi4_agent_tools/core/source/lexer.js +0 -1
  23. package/dist/hoi4_agent_tools/core/source/limits.js +0 -1
  24. package/dist/hoi4_agent_tools/core/source/localisation.js +0 -1
  25. package/dist/hoi4_agent_tools/core/source/parser.js +0 -1
  26. package/dist/hoi4_agent_tools/core/source/rewrite.js +0 -1
  27. package/dist/hoi4_agent_tools/core/svg-text.js +0 -1
  28. package/dist/hoi4_agent_tools/core/transaction-limits.js +0 -1
  29. package/dist/hoi4_agent_tools/core/transactions.js +2 -30
  30. package/dist/hoi4_agent_tools/core/workspace.js +112 -396
  31. package/dist/hoi4_agent_tools/focus/compiler.js +0 -1
  32. package/dist/hoi4_agent_tools/focus/continuous-render.js +0 -1
  33. package/dist/hoi4_agent_tools/focus/drift.js +0 -1
  34. package/dist/hoi4_agent_tools/focus/geometry.js +0 -1
  35. package/dist/hoi4_agent_tools/focus/importer.js +0 -1
  36. package/dist/hoi4_agent_tools/focus/index.js +0 -1
  37. package/dist/hoi4_agent_tools/focus/layout.js +0 -1
  38. package/dist/hoi4_agent_tools/focus/limits.js +0 -1
  39. package/dist/hoi4_agent_tools/focus/lint.js +0 -1
  40. package/dist/hoi4_agent_tools/focus/model.js +0 -1
  41. package/dist/hoi4_agent_tools/focus/native-effects.js +0 -1
  42. package/dist/hoi4_agent_tools/focus/planning.js +0 -1
  43. package/dist/hoi4_agent_tools/focus/presentation.js +0 -1
  44. package/dist/hoi4_agent_tools/focus/render.js +0 -1
  45. package/dist/hoi4_agent_tools/focus/service.js +7 -8
  46. package/dist/hoi4_agent_tools/focus/source-update.js +1 -2
  47. package/dist/hoi4_agent_tools/gui/animation-manifest.js +0 -1
  48. package/dist/hoi4_agent_tools/gui/assets.js +0 -1
  49. package/dist/hoi4_agent_tools/gui/dds.js +0 -1
  50. package/dist/hoi4_agent_tools/gui/helpers.js +1 -2
  51. package/dist/hoi4_agent_tools/gui/index.js +0 -1
  52. package/dist/hoi4_agent_tools/gui/layout.js +0 -1
  53. package/dist/hoi4_agent_tools/gui/limits.js +0 -1
  54. package/dist/hoi4_agent_tools/gui/renderer.js +0 -1
  55. package/dist/hoi4_agent_tools/gui/scenario.js +0 -1
  56. package/dist/hoi4_agent_tools/gui/source-graph.js +0 -1
  57. package/dist/hoi4_agent_tools/gui/source-patch.js +1 -2
  58. package/dist/hoi4_agent_tools/gui/studio.js +247 -104
  59. package/dist/hoi4_agent_tools/gui/tga.js +0 -1
  60. package/dist/hoi4_agent_tools/gui/types.js +0 -1
  61. package/dist/hoi4_agent_tools/gui/validators.js +0 -1
  62. package/dist/hoi4_agent_tools/map/bmp.js +0 -1
  63. package/dist/hoi4_agent_tools/map/diagnostic-limit.js +0 -1
  64. package/dist/hoi4_agent_tools/map/index.js +0 -1
  65. package/dist/hoi4_agent_tools/map/limits.js +2 -1
  66. package/dist/hoi4_agent_tools/map/model.js +0 -1
  67. package/dist/hoi4_agent_tools/map/operations.js +0 -1
  68. package/dist/hoi4_agent_tools/map/render.js +0 -1
  69. package/dist/hoi4_agent_tools/map/service.js +171 -33
  70. package/dist/hoi4_agent_tools/map/validation.js +0 -1
  71. package/dist/hoi4_agent_tools/mcp/resources/register.js +61 -187
  72. package/dist/hoi4_agent_tools/mcp/security/auth.js +1 -2
  73. package/dist/hoi4_agent_tools/mcp/server/base-tools.js +24 -750
  74. package/dist/hoi4_agent_tools/mcp/server/create.js +3 -12
  75. package/dist/hoi4_agent_tools/mcp/server/output-schemas.js +0 -71
  76. package/dist/hoi4_agent_tools/mcp/server/progress.js +0 -1
  77. package/dist/hoi4_agent_tools/mcp/server/result.js +24 -30
  78. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +10 -14
  79. package/dist/hoi4_agent_tools/mcp/tools/focus.js +219 -275
  80. package/dist/hoi4_agent_tools/mcp/tools/gui.js +149 -250
  81. package/dist/hoi4_agent_tools/mcp/tools/map.js +256 -337
  82. package/dist/hoi4_agent_tools/mcp/tools/mods.js +54 -0
  83. package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js +0 -1
  84. package/dist/hoi4_agent_tools/mcp/transports/event-store.js +0 -1
  85. package/dist/hoi4_agent_tools/mcp/transports/http.js +1 -12
  86. package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js +0 -1
  87. package/dist/hoi4_agent_tools/runtime.js +5 -2
  88. package/dist/hoi4_agent_tools/schemas/common.js +1 -2
  89. package/dist/hoi4_agent_tools/schemas/focus.js +0 -1
  90. package/dist/hoi4_agent_tools/schemas/map.js +0 -1
  91. package/dist/hoi4_agent_tools/schemas/transaction.js +0 -1
  92. package/dist/hoi4_agent_tools/version.js +1 -2
  93. package/docs/README.md +8 -21
  94. package/docs/development.md +22 -0
  95. package/docs/focus.md +40 -0
  96. package/docs/gui.md +37 -0
  97. package/docs/http.md +53 -0
  98. package/docs/map.md +39 -0
  99. package/docs/setup.md +39 -0
  100. package/package.json +10 -11
  101. package/server.json +3 -12
  102. package/dist/bin/http.d.ts +0 -3
  103. package/dist/bin/http.d.ts.map +0 -1
  104. package/dist/bin/http.js.map +0 -1
  105. package/dist/bin/setup.d.ts +0 -3
  106. package/dist/bin/setup.d.ts.map +0 -1
  107. package/dist/bin/setup.js.map +0 -1
  108. package/dist/bin/stdio.d.ts +0 -3
  109. package/dist/bin/stdio.d.ts.map +0 -1
  110. package/dist/bin/stdio.js.map +0 -1
  111. package/dist/hoi4_agent_tools/core/artifacts.d.ts +0 -117
  112. package/dist/hoi4_agent_tools/core/artifacts.d.ts.map +0 -1
  113. package/dist/hoi4_agent_tools/core/artifacts.js.map +0 -1
  114. package/dist/hoi4_agent_tools/core/canonical.d.ts +0 -13
  115. package/dist/hoi4_agent_tools/core/canonical.d.ts.map +0 -1
  116. package/dist/hoi4_agent_tools/core/canonical.js.map +0 -1
  117. package/dist/hoi4_agent_tools/core/configuration.d.ts +0 -138
  118. package/dist/hoi4_agent_tools/core/configuration.d.ts.map +0 -1
  119. package/dist/hoi4_agent_tools/core/configuration.js.map +0 -1
  120. package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts +0 -18
  121. package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts.map +0 -1
  122. package/dist/hoi4_agent_tools/core/diagnostic-collector.js.map +0 -1
  123. package/dist/hoi4_agent_tools/core/diagnostics.d.ts +0 -26
  124. package/dist/hoi4_agent_tools/core/diagnostics.d.ts.map +0 -1
  125. package/dist/hoi4_agent_tools/core/diagnostics.js.map +0 -1
  126. package/dist/hoi4_agent_tools/core/diff.d.ts +0 -22
  127. package/dist/hoi4_agent_tools/core/diff.d.ts.map +0 -1
  128. package/dist/hoi4_agent_tools/core/diff.js.map +0 -1
  129. package/dist/hoi4_agent_tools/core/engine.d.ts +0 -57
  130. package/dist/hoi4_agent_tools/core/engine.d.ts.map +0 -1
  131. package/dist/hoi4_agent_tools/core/engine.js.map +0 -1
  132. package/dist/hoi4_agent_tools/core/image-diff.d.ts +0 -14
  133. package/dist/hoi4_agent_tools/core/image-diff.d.ts.map +0 -1
  134. package/dist/hoi4_agent_tools/core/image-diff.js.map +0 -1
  135. package/dist/hoi4_agent_tools/core/index.d.ts +0 -64
  136. package/dist/hoi4_agent_tools/core/index.d.ts.map +0 -1
  137. package/dist/hoi4_agent_tools/core/index.js.map +0 -1
  138. package/dist/hoi4_agent_tools/core/index.public.d.ts +0 -16
  139. package/dist/hoi4_agent_tools/core/index.public.d.ts.map +0 -1
  140. package/dist/hoi4_agent_tools/core/index.public.js +0 -16
  141. package/dist/hoi4_agent_tools/core/index.public.js.map +0 -1
  142. package/dist/hoi4_agent_tools/core/render-budget.d.ts +0 -51
  143. package/dist/hoi4_agent_tools/core/render-budget.d.ts.map +0 -1
  144. package/dist/hoi4_agent_tools/core/render-budget.js.map +0 -1
  145. package/dist/hoi4_agent_tools/core/result.d.ts +0 -45
  146. package/dist/hoi4_agent_tools/core/result.d.ts.map +0 -1
  147. package/dist/hoi4_agent_tools/core/result.js.map +0 -1
  148. package/dist/hoi4_agent_tools/core/scanner.d.ts +0 -28
  149. package/dist/hoi4_agent_tools/core/scanner.d.ts.map +0 -1
  150. package/dist/hoi4_agent_tools/core/scanner.js.map +0 -1
  151. package/dist/hoi4_agent_tools/core/server-state.d.ts +0 -31
  152. package/dist/hoi4_agent_tools/core/server-state.d.ts.map +0 -1
  153. package/dist/hoi4_agent_tools/core/server-state.js.map +0 -1
  154. package/dist/hoi4_agent_tools/core/source/encoding.d.ts +0 -10
  155. package/dist/hoi4_agent_tools/core/source/encoding.d.ts.map +0 -1
  156. package/dist/hoi4_agent_tools/core/source/encoding.js.map +0 -1
  157. package/dist/hoi4_agent_tools/core/source/index.d.ts +0 -7
  158. package/dist/hoi4_agent_tools/core/source/index.d.ts.map +0 -1
  159. package/dist/hoi4_agent_tools/core/source/index.js.map +0 -1
  160. package/dist/hoi4_agent_tools/core/source/lexer.d.ts +0 -30
  161. package/dist/hoi4_agent_tools/core/source/lexer.d.ts.map +0 -1
  162. package/dist/hoi4_agent_tools/core/source/lexer.js.map +0 -1
  163. package/dist/hoi4_agent_tools/core/source/limits.d.ts +0 -21
  164. package/dist/hoi4_agent_tools/core/source/limits.d.ts.map +0 -1
  165. package/dist/hoi4_agent_tools/core/source/limits.js.map +0 -1
  166. package/dist/hoi4_agent_tools/core/source/localisation.d.ts +0 -20
  167. package/dist/hoi4_agent_tools/core/source/localisation.d.ts.map +0 -1
  168. package/dist/hoi4_agent_tools/core/source/localisation.js.map +0 -1
  169. package/dist/hoi4_agent_tools/core/source/parser.d.ts +0 -44
  170. package/dist/hoi4_agent_tools/core/source/parser.d.ts.map +0 -1
  171. package/dist/hoi4_agent_tools/core/source/parser.js.map +0 -1
  172. package/dist/hoi4_agent_tools/core/source/rewrite.d.ts +0 -10
  173. package/dist/hoi4_agent_tools/core/source/rewrite.d.ts.map +0 -1
  174. package/dist/hoi4_agent_tools/core/source/rewrite.js.map +0 -1
  175. package/dist/hoi4_agent_tools/core/svg-text.d.ts +0 -34
  176. package/dist/hoi4_agent_tools/core/svg-text.d.ts.map +0 -1
  177. package/dist/hoi4_agent_tools/core/svg-text.js.map +0 -1
  178. package/dist/hoi4_agent_tools/core/transaction-limits.d.ts +0 -9
  179. package/dist/hoi4_agent_tools/core/transaction-limits.d.ts.map +0 -1
  180. package/dist/hoi4_agent_tools/core/transaction-limits.js.map +0 -1
  181. package/dist/hoi4_agent_tools/core/transactions.d.ts +0 -164
  182. package/dist/hoi4_agent_tools/core/transactions.d.ts.map +0 -1
  183. package/dist/hoi4_agent_tools/core/transactions.js.map +0 -1
  184. package/dist/hoi4_agent_tools/core/workspace.d.ts +0 -68
  185. package/dist/hoi4_agent_tools/core/workspace.d.ts.map +0 -1
  186. package/dist/hoi4_agent_tools/core/workspace.js.map +0 -1
  187. package/dist/hoi4_agent_tools/focus/compiler.d.ts +0 -10
  188. package/dist/hoi4_agent_tools/focus/compiler.d.ts.map +0 -1
  189. package/dist/hoi4_agent_tools/focus/compiler.js.map +0 -1
  190. package/dist/hoi4_agent_tools/focus/continuous-render.d.ts +0 -32
  191. package/dist/hoi4_agent_tools/focus/continuous-render.d.ts.map +0 -1
  192. package/dist/hoi4_agent_tools/focus/continuous-render.js.map +0 -1
  193. package/dist/hoi4_agent_tools/focus/drift.d.ts +0 -22
  194. package/dist/hoi4_agent_tools/focus/drift.d.ts.map +0 -1
  195. package/dist/hoi4_agent_tools/focus/drift.js.map +0 -1
  196. package/dist/hoi4_agent_tools/focus/geometry.d.ts +0 -17
  197. package/dist/hoi4_agent_tools/focus/geometry.d.ts.map +0 -1
  198. package/dist/hoi4_agent_tools/focus/geometry.js.map +0 -1
  199. package/dist/hoi4_agent_tools/focus/importer.d.ts +0 -9
  200. package/dist/hoi4_agent_tools/focus/importer.d.ts.map +0 -1
  201. package/dist/hoi4_agent_tools/focus/importer.js.map +0 -1
  202. package/dist/hoi4_agent_tools/focus/index.d.ts +0 -14
  203. package/dist/hoi4_agent_tools/focus/index.d.ts.map +0 -1
  204. package/dist/hoi4_agent_tools/focus/index.js.map +0 -1
  205. package/dist/hoi4_agent_tools/focus/layout.d.ts +0 -5
  206. package/dist/hoi4_agent_tools/focus/layout.d.ts.map +0 -1
  207. package/dist/hoi4_agent_tools/focus/layout.js.map +0 -1
  208. package/dist/hoi4_agent_tools/focus/limits.d.ts +0 -9
  209. package/dist/hoi4_agent_tools/focus/limits.d.ts.map +0 -1
  210. package/dist/hoi4_agent_tools/focus/limits.js.map +0 -1
  211. package/dist/hoi4_agent_tools/focus/lint.d.ts +0 -14
  212. package/dist/hoi4_agent_tools/focus/lint.d.ts.map +0 -1
  213. package/dist/hoi4_agent_tools/focus/lint.js.map +0 -1
  214. package/dist/hoi4_agent_tools/focus/model.d.ts +0 -307
  215. package/dist/hoi4_agent_tools/focus/model.d.ts.map +0 -1
  216. package/dist/hoi4_agent_tools/focus/model.js.map +0 -1
  217. package/dist/hoi4_agent_tools/focus/native-effects.d.ts +0 -9
  218. package/dist/hoi4_agent_tools/focus/native-effects.d.ts.map +0 -1
  219. package/dist/hoi4_agent_tools/focus/native-effects.js.map +0 -1
  220. package/dist/hoi4_agent_tools/focus/planning.d.ts +0 -15
  221. package/dist/hoi4_agent_tools/focus/planning.d.ts.map +0 -1
  222. package/dist/hoi4_agent_tools/focus/planning.js.map +0 -1
  223. package/dist/hoi4_agent_tools/focus/presentation.d.ts +0 -24
  224. package/dist/hoi4_agent_tools/focus/presentation.d.ts.map +0 -1
  225. package/dist/hoi4_agent_tools/focus/presentation.js.map +0 -1
  226. package/dist/hoi4_agent_tools/focus/render.d.ts +0 -39
  227. package/dist/hoi4_agent_tools/focus/render.d.ts.map +0 -1
  228. package/dist/hoi4_agent_tools/focus/render.js.map +0 -1
  229. package/dist/hoi4_agent_tools/focus/service.d.ts +0 -82
  230. package/dist/hoi4_agent_tools/focus/service.d.ts.map +0 -1
  231. package/dist/hoi4_agent_tools/focus/service.js.map +0 -1
  232. package/dist/hoi4_agent_tools/focus/source-update.d.ts +0 -6
  233. package/dist/hoi4_agent_tools/focus/source-update.d.ts.map +0 -1
  234. package/dist/hoi4_agent_tools/focus/source-update.js.map +0 -1
  235. package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts +0 -37
  236. package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts.map +0 -1
  237. package/dist/hoi4_agent_tools/gui/animation-manifest.js.map +0 -1
  238. package/dist/hoi4_agent_tools/gui/assets.d.ts +0 -71
  239. package/dist/hoi4_agent_tools/gui/assets.d.ts.map +0 -1
  240. package/dist/hoi4_agent_tools/gui/assets.js.map +0 -1
  241. package/dist/hoi4_agent_tools/gui/dds.d.ts +0 -17
  242. package/dist/hoi4_agent_tools/gui/dds.d.ts.map +0 -1
  243. package/dist/hoi4_agent_tools/gui/dds.js.map +0 -1
  244. package/dist/hoi4_agent_tools/gui/helpers.d.ts +0 -101
  245. package/dist/hoi4_agent_tools/gui/helpers.d.ts.map +0 -1
  246. package/dist/hoi4_agent_tools/gui/helpers.js.map +0 -1
  247. package/dist/hoi4_agent_tools/gui/index.d.ts +0 -15
  248. package/dist/hoi4_agent_tools/gui/index.d.ts.map +0 -1
  249. package/dist/hoi4_agent_tools/gui/index.js.map +0 -1
  250. package/dist/hoi4_agent_tools/gui/layout.d.ts +0 -6
  251. package/dist/hoi4_agent_tools/gui/layout.d.ts.map +0 -1
  252. package/dist/hoi4_agent_tools/gui/layout.js.map +0 -1
  253. package/dist/hoi4_agent_tools/gui/limits.d.ts +0 -25
  254. package/dist/hoi4_agent_tools/gui/limits.d.ts.map +0 -1
  255. package/dist/hoi4_agent_tools/gui/limits.js.map +0 -1
  256. package/dist/hoi4_agent_tools/gui/renderer.d.ts +0 -18
  257. package/dist/hoi4_agent_tools/gui/renderer.d.ts.map +0 -1
  258. package/dist/hoi4_agent_tools/gui/renderer.js.map +0 -1
  259. package/dist/hoi4_agent_tools/gui/scenario.d.ts +0 -66
  260. package/dist/hoi4_agent_tools/gui/scenario.d.ts.map +0 -1
  261. package/dist/hoi4_agent_tools/gui/scenario.js.map +0 -1
  262. package/dist/hoi4_agent_tools/gui/source-graph.d.ts +0 -7
  263. package/dist/hoi4_agent_tools/gui/source-graph.d.ts.map +0 -1
  264. package/dist/hoi4_agent_tools/gui/source-graph.js.map +0 -1
  265. package/dist/hoi4_agent_tools/gui/source-patch.d.ts +0 -13
  266. package/dist/hoi4_agent_tools/gui/source-patch.d.ts.map +0 -1
  267. package/dist/hoi4_agent_tools/gui/source-patch.js.map +0 -1
  268. package/dist/hoi4_agent_tools/gui/studio.d.ts +0 -86
  269. package/dist/hoi4_agent_tools/gui/studio.d.ts.map +0 -1
  270. package/dist/hoi4_agent_tools/gui/studio.js.map +0 -1
  271. package/dist/hoi4_agent_tools/gui/tga.d.ts +0 -16
  272. package/dist/hoi4_agent_tools/gui/tga.d.ts.map +0 -1
  273. package/dist/hoi4_agent_tools/gui/tga.js.map +0 -1
  274. package/dist/hoi4_agent_tools/gui/types.d.ts +0 -322
  275. package/dist/hoi4_agent_tools/gui/types.d.ts.map +0 -1
  276. package/dist/hoi4_agent_tools/gui/types.js.map +0 -1
  277. package/dist/hoi4_agent_tools/gui/validators.d.ts +0 -8
  278. package/dist/hoi4_agent_tools/gui/validators.d.ts.map +0 -1
  279. package/dist/hoi4_agent_tools/gui/validators.js.map +0 -1
  280. package/dist/hoi4_agent_tools/map/bmp.d.ts +0 -62
  281. package/dist/hoi4_agent_tools/map/bmp.d.ts.map +0 -1
  282. package/dist/hoi4_agent_tools/map/bmp.js.map +0 -1
  283. package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts +0 -5
  284. package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts.map +0 -1
  285. package/dist/hoi4_agent_tools/map/diagnostic-limit.js.map +0 -1
  286. package/dist/hoi4_agent_tools/map/index.d.ts +0 -8
  287. package/dist/hoi4_agent_tools/map/index.d.ts.map +0 -1
  288. package/dist/hoi4_agent_tools/map/index.js.map +0 -1
  289. package/dist/hoi4_agent_tools/map/limits.d.ts +0 -13
  290. package/dist/hoi4_agent_tools/map/limits.d.ts.map +0 -1
  291. package/dist/hoi4_agent_tools/map/limits.js.map +0 -1
  292. package/dist/hoi4_agent_tools/map/model.d.ts +0 -269
  293. package/dist/hoi4_agent_tools/map/model.d.ts.map +0 -1
  294. package/dist/hoi4_agent_tools/map/model.js.map +0 -1
  295. package/dist/hoi4_agent_tools/map/operations.d.ts +0 -401
  296. package/dist/hoi4_agent_tools/map/operations.d.ts.map +0 -1
  297. package/dist/hoi4_agent_tools/map/operations.js.map +0 -1
  298. package/dist/hoi4_agent_tools/map/render.d.ts +0 -90
  299. package/dist/hoi4_agent_tools/map/render.d.ts.map +0 -1
  300. package/dist/hoi4_agent_tools/map/render.js.map +0 -1
  301. package/dist/hoi4_agent_tools/map/service.d.ts +0 -81
  302. package/dist/hoi4_agent_tools/map/service.d.ts.map +0 -1
  303. package/dist/hoi4_agent_tools/map/service.js.map +0 -1
  304. package/dist/hoi4_agent_tools/map/validation.d.ts +0 -23
  305. package/dist/hoi4_agent_tools/map/validation.d.ts.map +0 -1
  306. package/dist/hoi4_agent_tools/map/validation.js.map +0 -1
  307. package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts +0 -4
  308. package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts.map +0 -1
  309. package/dist/hoi4_agent_tools/mcp/prompts/register.js +0 -72
  310. package/dist/hoi4_agent_tools/mcp/prompts/register.js.map +0 -1
  311. package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts +0 -9
  312. package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts.map +0 -1
  313. package/dist/hoi4_agent_tools/mcp/registry/metadata.js +0 -10
  314. package/dist/hoi4_agent_tools/mcp/registry/metadata.js.map +0 -1
  315. package/dist/hoi4_agent_tools/mcp/resources/register.d.ts +0 -5
  316. package/dist/hoi4_agent_tools/mcp/resources/register.d.ts.map +0 -1
  317. package/dist/hoi4_agent_tools/mcp/resources/register.js.map +0 -1
  318. package/dist/hoi4_agent_tools/mcp/security/auth.d.ts +0 -24
  319. package/dist/hoi4_agent_tools/mcp/security/auth.d.ts.map +0 -1
  320. package/dist/hoi4_agent_tools/mcp/security/auth.js.map +0 -1
  321. package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts +0 -47
  322. package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts.map +0 -1
  323. package/dist/hoi4_agent_tools/mcp/server/base-tools.js.map +0 -1
  324. package/dist/hoi4_agent_tools/mcp/server/create.d.ts +0 -5
  325. package/dist/hoi4_agent_tools/mcp/server/create.d.ts.map +0 -1
  326. package/dist/hoi4_agent_tools/mcp/server/create.js.map +0 -1
  327. package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts +0 -138
  328. package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts.map +0 -1
  329. package/dist/hoi4_agent_tools/mcp/server/output-schemas.js.map +0 -1
  330. package/dist/hoi4_agent_tools/mcp/server/progress.d.ts +0 -8
  331. package/dist/hoi4_agent_tools/mcp/server/progress.d.ts.map +0 -1
  332. package/dist/hoi4_agent_tools/mcp/server/progress.js.map +0 -1
  333. package/dist/hoi4_agent_tools/mcp/server/result.d.ts +0 -232
  334. package/dist/hoi4_agent_tools/mcp/server/result.d.ts.map +0 -1
  335. package/dist/hoi4_agent_tools/mcp/server/result.js.map +0 -1
  336. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.d.ts +0 -31
  337. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.d.ts.map +0 -1
  338. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js.map +0 -1
  339. package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts +0 -5
  340. package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts.map +0 -1
  341. package/dist/hoi4_agent_tools/mcp/tools/focus.js.map +0 -1
  342. package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts +0 -117
  343. package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts.map +0 -1
  344. package/dist/hoi4_agent_tools/mcp/tools/gui.js.map +0 -1
  345. package/dist/hoi4_agent_tools/mcp/tools/map.d.ts +0 -5
  346. package/dist/hoi4_agent_tools/mcp/tools/map.d.ts.map +0 -1
  347. package/dist/hoi4_agent_tools/mcp/tools/map.js.map +0 -1
  348. package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts +0 -58
  349. package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts.map +0 -1
  350. package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js.map +0 -1
  351. package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts +0 -26
  352. package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts.map +0 -1
  353. package/dist/hoi4_agent_tools/mcp/transports/event-store.js.map +0 -1
  354. package/dist/hoi4_agent_tools/mcp/transports/http.d.ts +0 -28
  355. package/dist/hoi4_agent_tools/mcp/transports/http.d.ts.map +0 -1
  356. package/dist/hoi4_agent_tools/mcp/transports/http.js.map +0 -1
  357. package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts +0 -15
  358. package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts.map +0 -1
  359. package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js.map +0 -1
  360. package/dist/hoi4_agent_tools/runtime.d.ts +0 -4
  361. package/dist/hoi4_agent_tools/runtime.d.ts.map +0 -1
  362. package/dist/hoi4_agent_tools/runtime.js.map +0 -1
  363. package/dist/hoi4_agent_tools/schemas/common.d.ts +0 -74
  364. package/dist/hoi4_agent_tools/schemas/common.d.ts.map +0 -1
  365. package/dist/hoi4_agent_tools/schemas/common.js.map +0 -1
  366. package/dist/hoi4_agent_tools/schemas/focus.d.ts +0 -784
  367. package/dist/hoi4_agent_tools/schemas/focus.d.ts.map +0 -1
  368. package/dist/hoi4_agent_tools/schemas/focus.js.map +0 -1
  369. package/dist/hoi4_agent_tools/schemas/index.d.ts +0 -5
  370. package/dist/hoi4_agent_tools/schemas/index.d.ts.map +0 -1
  371. package/dist/hoi4_agent_tools/schemas/index.js +0 -5
  372. package/dist/hoi4_agent_tools/schemas/index.js.map +0 -1
  373. package/dist/hoi4_agent_tools/schemas/map.d.ts +0 -654
  374. package/dist/hoi4_agent_tools/schemas/map.d.ts.map +0 -1
  375. package/dist/hoi4_agent_tools/schemas/map.js.map +0 -1
  376. package/dist/hoi4_agent_tools/schemas/transaction.d.ts +0 -259
  377. package/dist/hoi4_agent_tools/schemas/transaction.d.ts.map +0 -1
  378. package/dist/hoi4_agent_tools/schemas/transaction.js.map +0 -1
  379. package/dist/hoi4_agent_tools/version.d.ts +0 -6
  380. package/dist/hoi4_agent_tools/version.d.ts.map +0 -1
  381. package/dist/hoi4_agent_tools/version.js.map +0 -1
  382. package/dist/index.d.ts +0 -3
  383. package/dist/index.d.ts.map +0 -1
  384. package/dist/index.js +0 -3
  385. package/dist/index.js.map +0 -1
  386. package/docs/adr/0001-runtime-and-language.md +0 -16
  387. package/docs/adr/0002-lossless-source-model.md +0 -18
  388. package/docs/adr/0003-image-and-font-codecs.md +0 -18
  389. package/docs/adr/0004-focus-layout.md +0 -16
  390. package/docs/adr/0005-offline-rendering.md +0 -18
  391. package/docs/adr/0006-mcp-sdk-and-protocol.md +0 -16
  392. package/docs/adr/0007-storage-and-transactions.md +0 -24
  393. package/docs/adr/0008-test-strategy.md +0 -18
  394. package/docs/adr/0009-packaging-and-registry.md +0 -16
  395. package/docs/adr/0010-unsupported-and-refusal-policy.md +0 -18
  396. package/docs/adr/0011-http-security.md +0 -16
  397. package/docs/adr/0012-autonomous-rewrites.md +0 -55
  398. package/docs/adr/README.md +0 -14
  399. package/docs/agent-integration.md +0 -146
  400. package/docs/architecture.md +0 -76
  401. package/docs/artifacts.md +0 -136
  402. package/docs/compatibility.md +0 -33
  403. package/docs/completion-report.md +0 -122
  404. package/docs/configuration.md +0 -305
  405. package/docs/focus-workflow.md +0 -197
  406. package/docs/gui-workflow.md +0 -125
  407. package/docs/limitations.md +0 -32
  408. package/docs/map-workflow.md +0 -95
  409. package/docs/performance.md +0 -117
  410. package/docs/release.md +0 -222
  411. package/docs/research/mcp-2026-07-10.md +0 -52
  412. package/docs/research/source-ledger.md +0 -68
  413. package/docs/security.md +0 -145
  414. package/docs/self-hosting.md +0 -170
  415. package/docs/testing.md +0 -43
  416. package/docs/third-party-notices.md +0 -11
  417. package/docs/transactions.md +0 -180
  418. package/docs/troubleshooting.md +0 -54
  419. package/schemas/configuration.schema.json +0 -795
  420. package/schemas/continuous-focus-palette.schema.json +0 -325
  421. package/schemas/focus-plan.schema.json +0 -927
  422. package/schemas/focus-planning-sidecar.schema.json +0 -379
  423. package/schemas/gui-animation-source.schema.json +0 -176
  424. package/schemas/gui-helper.schema.json +0 -301
  425. package/schemas/gui-scenario.schema.json +0 -298
  426. package/schemas/map-operation.schema.json +0 -2284
  427. package/schemas/operation-result.schema.json +0 -344
  428. package/schemas/transaction-manifest.schema.json +0 -685
@@ -1,20 +1,9 @@
1
- import { link, lstat, mkdir, open, readFile, realpath, unlink } from 'node:fs/promises';
1
+ import { lstat, mkdir, opendir, realpath } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { z } from 'zod/v4';
4
- import { WORKSPACE_MAX_REGISTRATIONS, } from './configuration.js';
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
- #runtimePrincipalWorkspaces = new Map();
256
- #runtimeWorkspaceIds = new Set();
257
- #runtimeWorkspaceOwners = new Map();
258
- #registrationRoots = [];
259
- #registrationLexicalRoots = [];
260
- #writableRegistrationRoots = [];
261
- #writableRegistrationLexicalRoots = [];
140
+ #discoveredWorkspaceIds = new Set();
262
141
  #storageRoots = [];
263
- #storageLexicalRoots = [];
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.#registrationLexicalRoots = configuration.registrationRoots.map((root) => path.normalize(path.resolve(root)));
276
- resolver.#registrationRoots = await Promise.all(configuration.registrationRoots.map((root) => canonicalPath(root)));
277
- resolver.#writableRegistrationLexicalRoots = configuration.writableRegistrationRoots.map((root) => path.normalize(path.resolve(root)));
278
- resolver.#writableRegistrationRoots = await Promise.all(configuration.writableRegistrationRoots.map((root) => canonicalPath(root)));
279
- for (const candidate of resolver.#writableRegistrationLexicalRoots) {
280
- if (!resolver.#registrationLexicalRoots.some((root) => isWithin(root, candidate))) {
281
- throw new ServiceError('WORKSPACE_WRITABLE_REGISTRATION_ROOT_FORBIDDEN', 'Writable runtime registration roots must be lexical descendants of registration roots');
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
- for (const candidate of resolver.#writableRegistrationRoots) {
285
- if (!resolver.#registrationRoots.some((root) => isWithin(root, candidate))) {
286
- throw new ServiceError('WORKSPACE_WRITABLE_REGISTRATION_ROOT_FORBIDDEN', 'Writable runtime registration roots must resolve beneath registration roots');
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 registration of configuration.workspaces) {
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
- async register(registration, principal, signal) {
323
- signal?.throwIfAborted();
324
- let release;
325
- const previous = this.#registrationQueue;
326
- this.#registrationQueue = new Promise((resolve) => {
327
- release = resolve;
328
- });
329
- await previous;
330
- signal?.throwIfAborted();
331
- try {
332
- return await this.registerExclusive(registration, principal, signal);
333
- }
334
- finally {
335
- release();
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 registerExclusive(registration, principal, signal) {
339
- signal?.throwIfAborted();
340
- if (principal !== undefined && !this.registrationAllowed(principal)) {
341
- throw new ServiceError('WORKSPACE_REGISTRATION_FORBIDDEN', 'Principal cannot register workspaces');
342
- }
343
- const registrationConflict = (code, message) => principal === undefined
344
- ? new ServiceError(code, message)
345
- : new ServiceError('WORKSPACE_REGISTRATION_CONFLICT', 'Runtime workspace registration conflicts with an existing registration');
346
- if (this.#byId.has(registration.id)) {
347
- throw registrationConflict('WORKSPACE_DUPLICATE', `Workspace ID is already registered: ${registration.id}`);
348
- }
349
- if (this.#byId.size >= WORKSPACE_MAX_REGISTRATIONS) {
350
- throw registrationConflict('WORKSPACE_REGISTRATION_LIMIT', 'Workspace registration limit has been reached');
351
- }
352
- this.assertLexicalRegistrationPath(registration.root, 'workspace root', 'WORKSPACE_REGISTRATION_ROOT_FORBIDDEN');
353
- if (registration.kind === 'mod')
354
- this.assertLexicalWritableRegistrationPath(registration.root);
355
- const candidateRoot = await canonicalPath(registration.root, signal);
356
- signal?.throwIfAborted();
357
- if (!this.#registrationRoots.some((root) => isWithin(root, candidateRoot))) {
358
- throw new ServiceError('WORKSPACE_REGISTRATION_ROOT_FORBIDDEN', 'Workspace is outside configured registration roots', {
359
- workspaceId: registration.id,
360
- });
361
- }
362
- if (registration.kind === 'mod' &&
363
- !this.#writableRegistrationRoots.some((root) => isWithin(root, candidateRoot))) {
364
- throw new ServiceError('WORKSPACE_REGISTRATION_ROOT_FORBIDDEN', 'Runtime mod roots require an operator-approved writable registration root');
365
- }
366
- const readRoots = [
367
- registration.gameRoot,
368
- ...registration.dependencyRoots,
369
- ...registration.dependencies.map(({ root }) => root),
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
- for (const candidate of generatedCandidates.values()) {
437
- signal?.throwIfAborted();
438
- const overlapsOwnSources = (registration.kind !== 'mod' && pathsOverlap(candidateRoot, candidate)) ||
439
- (registration.kind === 'mod' &&
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
- if ([...this.#byId.values()].some((existing) => existing.roots.some((root) => pathsOverlap(candidate, root.path)))) {
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
- catch (error) {
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.#runtimePrincipalWorkspaces.get(principal) ?? []),
352
+ ...(this.discoveredModsAllowed(principal) ? this.#discoveredWorkspaceIds : []),
578
353
  ]);
579
354
  }
580
- registrationAllowed(principal) {
581
- return (this.configuration.http.tokens.some((entry) => entry.principal === principal && entry.allowRegistration) ||
582
- this.configuration.http.principals.some((entry) => entry.principal === principal && entry.allowRegistration));
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, runtimeOwner) {
645
- const signal = runtimeOwner?.signal;
646
- signal?.throwIfAborted();
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, signal)));
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'), signal);
664
- const cacheRoot = await canonicalPath(registration.cacheRoot ?? path.join(modRoot, '.hoi4-agent', 'cache'), signal);
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, signal);
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
- signal?.throwIfAborted();
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
- signal?.throwIfAborted();
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: runtimeOwner === undefined
789
- ? configuredOwnerIdentity(workspaceIdentity)
790
- : runtimeOwnerIdentity(workspaceIdentity, runtimeOwner.principal),
507
+ ownerIdentity: configuredOwnerIdentity(workspaceIdentity),
791
508
  };
792
509
  }
793
510
  }
794
- //# sourceMappingURL=workspace.js.map
@@ -280,4 +280,3 @@ export function compileContinuousFocusPaletteWithSourceMap(plan, generatedPath =
280
280
  },
281
281
  };
282
282
  }
283
- //# sourceMappingURL=compiler.js.map
@@ -176,4 +176,3 @@ export async function storeContinuousFocusRenderArtifacts(workspace, store, plan
176
176
  }));
177
177
  return store.withAtomicChunkedWrites(workspace, writes, (stored) => Promise.resolve([...stored]), options.signal);
178
178
  }
179
- //# sourceMappingURL=continuous-render.js.map
@@ -121,4 +121,3 @@ export function detectContinuousFocusDrift(savedPlan, currentDocument) {
121
121
  currentSourcePlan,
122
122
  };
123
123
  }
124
- //# sourceMappingURL=drift.js.map