@interf/compiler 0.9.5 → 0.16.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 (365) hide show
  1. package/README.md +135 -91
  2. package/TRADEMARKS.md +2 -13
  3. package/agent-skills/interf-actions/SKILL.md +143 -38
  4. package/agent-skills/interf-actions/references/cli.md +134 -67
  5. package/builtin-methods/interf-default/README.md +3 -4
  6. package/builtin-methods/interf-default/compile/stages/shape/SKILL.md +2 -2
  7. package/builtin-methods/interf-default/compile/stages/summarize/SKILL.md +2 -1
  8. package/builtin-methods/interf-default/improve/SKILL.md +1 -1
  9. package/builtin-methods/interf-default/method.json +10 -4
  10. package/builtin-methods/interf-default/method.schema.json +0 -9
  11. package/builtin-methods/interf-default/use/query/SKILL.md +5 -5
  12. package/dist/cli/commands/agents.d.ts +2 -0
  13. package/dist/cli/commands/agents.js +213 -0
  14. package/dist/cli/commands/compile.d.ts +8 -25
  15. package/dist/cli/commands/compile.js +83 -359
  16. package/dist/cli/commands/doctor.js +2 -2
  17. package/dist/cli/commands/login.d.ts +7 -0
  18. package/dist/cli/commands/login.js +39 -0
  19. package/dist/cli/commands/logout.d.ts +2 -0
  20. package/dist/cli/commands/logout.js +16 -0
  21. package/dist/cli/commands/mcp.d.ts +42 -0
  22. package/dist/cli/commands/mcp.js +239 -0
  23. package/dist/cli/commands/method.d.ts +2 -0
  24. package/dist/cli/commands/method.js +113 -0
  25. package/dist/cli/commands/prep.d.ts +2 -0
  26. package/dist/cli/commands/prep.js +152 -0
  27. package/dist/cli/commands/reset.d.ts +8 -1
  28. package/dist/cli/commands/reset.js +47 -26
  29. package/dist/cli/commands/runs.d.ts +2 -0
  30. package/dist/cli/commands/runs.js +120 -0
  31. package/dist/cli/commands/status.d.ts +6 -1
  32. package/dist/cli/commands/status.js +68 -111
  33. package/dist/cli/commands/verify.d.ts +7 -1
  34. package/dist/cli/commands/verify.js +69 -85
  35. package/dist/cli/commands/web.d.ts +0 -9
  36. package/dist/cli/commands/web.js +201 -121
  37. package/dist/cli/commands/wizard.d.ts +9 -0
  38. package/dist/cli/commands/wizard.js +689 -0
  39. package/dist/cli/index.d.ts +10 -7
  40. package/dist/cli/index.js +19 -12
  41. package/dist/compiler-ui/404.html +1 -1
  42. package/dist/compiler-ui/__next.__PAGE__.txt +2 -2
  43. package/dist/compiler-ui/__next._full.txt +3 -3
  44. package/dist/compiler-ui/__next._head.txt +1 -1
  45. package/dist/compiler-ui/__next._index.txt +2 -2
  46. package/dist/compiler-ui/__next._tree.txt +2 -2
  47. package/dist/compiler-ui/_next/static/chunks/{177mvn4rse235.js → 0jipmpez3_ehh.js} +16 -16
  48. package/dist/compiler-ui/_next/static/chunks/{18a8f2jkv3z.c.css → 13awzu4tooflw.css} +1 -1
  49. package/dist/compiler-ui/_not-found/__next._full.txt +2 -2
  50. package/dist/compiler-ui/_not-found/__next._head.txt +1 -1
  51. package/dist/compiler-ui/_not-found/__next._index.txt +2 -2
  52. package/dist/compiler-ui/_not-found/__next._not-found.__PAGE__.txt +1 -1
  53. package/dist/compiler-ui/_not-found/__next._not-found.txt +1 -1
  54. package/dist/compiler-ui/_not-found/__next._tree.txt +2 -2
  55. package/dist/compiler-ui/_not-found.html +1 -1
  56. package/dist/compiler-ui/_not-found.txt +2 -2
  57. package/dist/compiler-ui/index.html +1 -1
  58. package/dist/compiler-ui/index.txt +3 -3
  59. package/dist/index.d.ts +2 -2
  60. package/dist/index.js +2 -2
  61. package/dist/packages/contracts/index.d.ts +2 -1
  62. package/dist/packages/contracts/index.js +1 -0
  63. package/dist/packages/contracts/lib/preparation-paths.d.ts +117 -0
  64. package/dist/packages/contracts/lib/preparation-paths.js +177 -0
  65. package/dist/packages/contracts/lib/schema.d.ts +187 -13
  66. package/dist/packages/contracts/lib/schema.js +148 -3
  67. package/dist/packages/contracts/utils/filesystem.d.ts +9 -0
  68. package/dist/packages/contracts/utils/filesystem.js +142 -0
  69. package/dist/packages/{local-service → engine}/action-definitions.d.ts +14 -14
  70. package/dist/packages/{local-service → engine}/action-definitions.js +35 -29
  71. package/dist/packages/{local-service → engine}/action-planner.d.ts +1 -1
  72. package/dist/packages/{local-service → engine}/action-planner.js +3 -2
  73. package/dist/packages/{agents → engine/agents}/index.d.ts +3 -0
  74. package/dist/packages/{agents → engine/agents}/index.js +3 -0
  75. package/dist/packages/{agents → engine/agents}/lib/compiled-bootstrap.js +2 -2
  76. package/dist/packages/engine/agents/lib/detection.d.ts +13 -0
  77. package/dist/packages/{agents → engine/agents}/lib/detection.js +11 -0
  78. package/dist/packages/{agents → engine/agents}/lib/executors.d.ts +2 -2
  79. package/dist/packages/{agents → engine/agents}/lib/shells.d.ts +5 -5
  80. package/dist/packages/{agents → engine/agents}/lib/shells.js +117 -58
  81. package/dist/packages/{agents → engine/agents}/lib/user-config.d.ts +4 -2
  82. package/dist/packages/engine/agents/lib/user-config.js +24 -0
  83. package/dist/packages/engine/agents/registry.d.ts +91 -0
  84. package/dist/packages/engine/agents/registry.js +321 -0
  85. package/dist/packages/engine/agents/role-executors.d.ts +35 -0
  86. package/dist/packages/engine/agents/role-executors.js +88 -0
  87. package/dist/packages/engine/agents/role-router.d.ts +66 -0
  88. package/dist/packages/engine/agents/role-router.js +73 -0
  89. package/dist/packages/{local-service → engine}/client.d.ts +59 -60
  90. package/dist/packages/{local-service → engine}/client.js +137 -145
  91. package/dist/packages/{compiler → engine/compile}/artifact-counts.js +1 -1
  92. package/dist/packages/{compiler → engine/compile}/compiled-paths.d.ts +9 -2
  93. package/dist/packages/{compiler → engine/compile}/compiled-paths.js +30 -15
  94. package/dist/packages/{compiler → engine/compile}/compiled-pipeline.d.ts +12 -1
  95. package/dist/packages/{compiler → engine/compile}/compiled-pipeline.js +39 -9
  96. package/dist/packages/{compiler → engine/compile}/compiled-schema.d.ts +2 -2
  97. package/dist/packages/{compiler → engine/compile}/compiled-schema.js +4 -4
  98. package/dist/packages/{compiler → engine/compile}/compiled-stage-plan.d.ts +1 -1
  99. package/dist/packages/{compiler → engine/compile}/compiled-stage-plan.js +8 -4
  100. package/dist/packages/{compiler → engine/compile}/compiled-stage-runner.d.ts +1 -1
  101. package/dist/packages/{compiler → engine/compile}/compiled-stage-runner.js +3 -3
  102. package/dist/packages/{compiler → engine/compile}/compiled-target.d.ts +3 -3
  103. package/dist/packages/{compiler → engine/compile}/compiled-target.js +3 -3
  104. package/dist/packages/{compiler → engine/compile}/discovery.js +1 -1
  105. package/dist/packages/{compiler → engine/compile}/index.d.ts +1 -0
  106. package/dist/packages/{compiler → engine/compile}/index.js +1 -0
  107. package/dist/packages/{compiler → engine/compile}/lib/schema.d.ts +26 -31
  108. package/dist/packages/{compiler → engine/compile}/lib/schema.js +2 -13
  109. package/dist/packages/engine/compile/method-runs.d.ts +14 -0
  110. package/dist/packages/{compiler → engine/compile}/method-runs.js +5 -6
  111. package/dist/packages/{compiler → engine/compile}/reset.js +3 -1
  112. package/dist/packages/{compiler → engine/compile}/runtime-acceptance.js +17 -14
  113. package/dist/packages/{compiler → engine/compile}/runtime-contracts.js +0 -3
  114. package/dist/packages/{compiler → engine/compile}/runtime-prompt.js +1 -1
  115. package/dist/packages/{compiler → engine/compile}/runtime-reconcile.d.ts +1 -1
  116. package/dist/packages/{compiler → engine/compile}/runtime-reconcile.js +12 -10
  117. package/dist/packages/{compiler → engine/compile}/runtime-runs.d.ts +1 -2
  118. package/dist/packages/{compiler → engine/compile}/runtime-runs.js +3 -43
  119. package/dist/packages/{compiler → engine/compile}/runtime-types.d.ts +1 -5
  120. package/dist/packages/{compiler → engine/compile}/runtime.d.ts +2 -2
  121. package/dist/packages/{compiler → engine/compile}/runtime.js +1 -1
  122. package/dist/packages/engine/compile/source-files.d.ts +46 -0
  123. package/dist/packages/engine/compile/source-files.js +149 -0
  124. package/dist/packages/engine/compile/state-artifacts.d.ts +9 -0
  125. package/dist/packages/{compiler → engine/compile}/state-artifacts.js +4 -3
  126. package/dist/packages/{compiler → engine/compile}/state-health.js +2 -2
  127. package/dist/packages/{compiler → engine/compile}/state-io.d.ts +3 -2
  128. package/dist/packages/{compiler → engine/compile}/state-io.js +13 -7
  129. package/dist/packages/{compiler → engine/compile}/state-paths.d.ts +2 -1
  130. package/dist/packages/engine/compile/state-paths.js +16 -0
  131. package/dist/packages/engine/compile/state-view.d.ts +5 -0
  132. package/dist/packages/{compiler → engine/compile}/state-view.js +20 -30
  133. package/dist/packages/engine/compile/state.d.ts +7 -0
  134. package/dist/packages/{compiler → engine/compile}/state.js +3 -3
  135. package/dist/packages/{compiler → engine/compile}/validate-compiled.js +2 -2
  136. package/dist/packages/{compiler → engine/compile}/validate.d.ts +1 -1
  137. package/dist/packages/{compiler → engine/compile}/validate.js +3 -3
  138. package/dist/packages/engine/connection-config.d.ts +38 -0
  139. package/dist/packages/engine/connection-config.js +75 -0
  140. package/dist/packages/{execution → engine/execution}/lib/schema.d.ts +52 -72
  141. package/dist/packages/{execution → engine/execution}/lib/schema.js +3 -3
  142. package/dist/packages/engine/index.d.ts +22 -0
  143. package/dist/packages/engine/index.js +15 -0
  144. package/dist/packages/engine/instance-paths.d.ts +100 -0
  145. package/dist/packages/engine/instance-paths.js +165 -0
  146. package/dist/packages/{local-service → engine}/lib/schema.d.ts +392 -2408
  147. package/dist/packages/{local-service → engine}/lib/schema.js +164 -76
  148. package/dist/packages/{local-service → engine}/native-run-handlers.d.ts +7 -5
  149. package/dist/packages/{local-service → engine}/native-run-handlers.js +71 -27
  150. package/dist/packages/engine/preparation-store.d.ts +104 -0
  151. package/dist/packages/engine/preparation-store.js +194 -0
  152. package/dist/packages/{local-service → engine}/readiness-check-draft.d.ts +2 -2
  153. package/dist/packages/engine/routes.d.ts +78 -0
  154. package/dist/packages/engine/routes.js +92 -0
  155. package/dist/packages/{local-service → engine}/run-observability.d.ts +3 -3
  156. package/dist/packages/{local-service → engine}/run-observability.js +25 -24
  157. package/dist/packages/engine/runtime-caches.d.ts +76 -0
  158. package/dist/packages/engine/runtime-caches.js +191 -0
  159. package/dist/packages/engine/runtime-event-applier.d.ts +12 -0
  160. package/dist/packages/engine/runtime-event-applier.js +177 -0
  161. package/dist/packages/engine/runtime-persistence.d.ts +47 -0
  162. package/dist/packages/engine/runtime-persistence.js +137 -0
  163. package/dist/packages/engine/runtime-proposal-helpers.d.ts +35 -0
  164. package/dist/packages/engine/runtime-proposal-helpers.js +251 -0
  165. package/dist/packages/engine/runtime-resource-builders.d.ts +52 -0
  166. package/dist/packages/engine/runtime-resource-builders.js +149 -0
  167. package/dist/packages/engine/runtime.d.ts +318 -0
  168. package/dist/packages/{local-service → engine}/runtime.js +835 -1011
  169. package/dist/packages/{local-service → engine}/server.d.ts +15 -0
  170. package/dist/packages/engine/server.js +1257 -0
  171. package/dist/packages/engine/service-registry.d.ts +47 -0
  172. package/dist/packages/engine/service-registry.js +137 -0
  173. package/dist/packages/{testing → engine/verify}/lib/schema.d.ts +11 -11
  174. package/dist/packages/{testing → engine/verify}/lib/schema.js +3 -3
  175. package/dist/packages/{testing → engine/verify}/readiness-check-run.d.ts +9 -16
  176. package/dist/packages/{testing → engine/verify}/readiness-check-run.js +38 -94
  177. package/dist/packages/{testing → engine/verify}/test-execution.js +6 -6
  178. package/dist/packages/{testing → engine/verify}/test-paths.js +5 -4
  179. package/dist/packages/{testing → engine/verify}/test-sandbox.d.ts +0 -1
  180. package/dist/packages/{testing → engine/verify}/test-sandbox.js +17 -33
  181. package/dist/packages/{testing → engine/verify}/test-specs.js +1 -1
  182. package/dist/packages/{testing → engine/verify}/test-targets.d.ts +1 -1
  183. package/dist/packages/{testing → engine/verify}/test-targets.js +9 -9
  184. package/dist/packages/{testing → engine/verify}/test.d.ts +1 -1
  185. package/dist/packages/{testing → engine/verify}/test.js +1 -1
  186. package/dist/packages/{method-authoring → methods/authoring}/method-authoring.d.ts +12 -4
  187. package/dist/packages/{method-authoring → methods/authoring}/method-authoring.js +70 -7
  188. package/dist/packages/{method-authoring → methods/authoring}/method-edit-session.d.ts +2 -2
  189. package/dist/packages/{method-authoring → methods/authoring}/method-improvement.d.ts +4 -4
  190. package/dist/packages/{method-authoring → methods/authoring}/method-improvement.js +16 -10
  191. package/dist/packages/{method-package → methods/package}/builtin-compiled-method.d.ts +4 -5
  192. package/dist/packages/{method-package → methods/package}/builtin-compiled-method.js +10 -16
  193. package/dist/packages/{method-package → methods/package}/context-interface.d.ts +5 -41
  194. package/dist/packages/{method-package → methods/package}/context-interface.js +3 -25
  195. package/dist/packages/{method-package → methods/package}/interf-method-package.d.ts +4 -4
  196. package/dist/packages/{method-package → methods/package}/interf-method-package.js +24 -35
  197. package/dist/packages/{method-package → methods/package}/lib/package-root.js +2 -2
  198. package/dist/packages/{method-package → methods/package}/local-methods.d.ts +18 -8
  199. package/dist/packages/{method-package → methods/package}/local-methods.js +64 -45
  200. package/dist/packages/{method-package → methods/package}/method-definitions.d.ts +16 -36
  201. package/dist/packages/{method-package → methods/package}/method-definitions.js +53 -40
  202. package/dist/packages/{method-package → methods/package}/method-helpers.d.ts +2 -14
  203. package/dist/packages/{method-package → methods/package}/method-helpers.js +12 -46
  204. package/dist/packages/{method-package → methods/package}/method-review-paths.d.ts +1 -1
  205. package/dist/packages/{method-package → methods/package}/method-review-paths.js +1 -1
  206. package/dist/packages/{method-package → methods/package}/method-stage-runner.d.ts +4 -9
  207. package/dist/packages/{method-package → methods/package}/method-stage-runner.js +3 -31
  208. package/dist/packages/methods/package/user-methods.d.ts +17 -0
  209. package/dist/packages/methods/package/user-methods.js +77 -0
  210. package/dist/packages/{project-model → project}/index.d.ts +0 -1
  211. package/dist/packages/{project-model → project}/index.js +0 -1
  212. package/dist/packages/{project-model → project}/interf-bootstrap.d.ts +1 -1
  213. package/dist/packages/{project-model → project}/interf-bootstrap.js +1 -1
  214. package/dist/packages/{project-model → project}/interf-detect.d.ts +8 -3
  215. package/dist/packages/{project-model → project}/interf-detect.js +38 -38
  216. package/dist/packages/project/interf-scaffold.d.ts +3 -0
  217. package/dist/packages/{project-model → project}/interf-scaffold.js +30 -39
  218. package/dist/packages/{project-model → project}/lib/schema.d.ts +2 -2
  219. package/dist/packages/{project-model → project}/lib/schema.js +39 -2
  220. package/dist/packages/project/preparation-entries.d.ts +11 -0
  221. package/dist/packages/{project-model → project}/preparation-entries.js +14 -14
  222. package/dist/packages/{project-model → project}/source-config.d.ts +12 -12
  223. package/dist/packages/{project-model → project}/source-config.js +81 -53
  224. package/dist/packages/{project-model → project}/source-folders.d.ts +5 -5
  225. package/dist/packages/{project-model → project}/source-folders.js +16 -16
  226. package/package.json +8 -8
  227. package/CHANGELOG.md +0 -93
  228. package/LICENSE +0 -183
  229. package/dist/cli/commands/action-input-cli.d.ts +0 -25
  230. package/dist/cli/commands/action-input-cli.js +0 -73
  231. package/dist/cli/commands/control-path.d.ts +0 -11
  232. package/dist/cli/commands/control-path.js +0 -72
  233. package/dist/cli/commands/create-method-wizard.d.ts +0 -64
  234. package/dist/cli/commands/create-method-wizard.js +0 -434
  235. package/dist/cli/commands/create.d.ts +0 -6
  236. package/dist/cli/commands/create.js +0 -183
  237. package/dist/cli/commands/default.d.ts +0 -2
  238. package/dist/cli/commands/default.js +0 -39
  239. package/dist/cli/commands/executor-flow.d.ts +0 -29
  240. package/dist/cli/commands/executor-flow.js +0 -163
  241. package/dist/cli/commands/init.d.ts +0 -26
  242. package/dist/cli/commands/init.js +0 -771
  243. package/dist/cli/commands/list.d.ts +0 -2
  244. package/dist/cli/commands/list.js +0 -30
  245. package/dist/cli/commands/preparation-action.d.ts +0 -8
  246. package/dist/cli/commands/preparation-action.js +0 -29
  247. package/dist/cli/commands/preparation-picker.d.ts +0 -5
  248. package/dist/cli/commands/preparation-picker.js +0 -36
  249. package/dist/cli/commands/preparation-selection.d.ts +0 -6
  250. package/dist/cli/commands/preparation-selection.js +0 -11
  251. package/dist/cli/commands/service-action-flow.d.ts +0 -9
  252. package/dist/cli/commands/service-action-flow.js +0 -19
  253. package/dist/cli/commands/source-config-wizard.d.ts +0 -51
  254. package/dist/cli/commands/source-config-wizard.js +0 -670
  255. package/dist/cli/commands/test.d.ts +0 -17
  256. package/dist/cli/commands/test.js +0 -188
  257. package/dist/packages/agents/lib/detection.d.ts +0 -7
  258. package/dist/packages/agents/lib/user-config.js +0 -16
  259. package/dist/packages/compiler/method-runs.d.ts +0 -15
  260. package/dist/packages/compiler/raw-snapshot.d.ts +0 -49
  261. package/dist/packages/compiler/raw-snapshot.js +0 -101
  262. package/dist/packages/compiler/state-artifacts.d.ts +0 -8
  263. package/dist/packages/compiler/state-paths.js +0 -13
  264. package/dist/packages/compiler/state-view.d.ts +0 -4
  265. package/dist/packages/compiler/state.d.ts +0 -7
  266. package/dist/packages/local-service/index.d.ts +0 -18
  267. package/dist/packages/local-service/index.js +0 -13
  268. package/dist/packages/local-service/routes.d.ts +0 -32
  269. package/dist/packages/local-service/routes.js +0 -37
  270. package/dist/packages/local-service/runtime.d.ts +0 -133
  271. package/dist/packages/local-service/server.js +0 -627
  272. package/dist/packages/method-package/index.d.ts +0 -11
  273. package/dist/packages/method-package/index.js +0 -11
  274. package/dist/packages/method-package/method-stage-policy.d.ts +0 -5
  275. package/dist/packages/method-package/method-stage-policy.js +0 -31
  276. package/dist/packages/project-model/interf-scaffold.d.ts +0 -3
  277. package/dist/packages/project-model/preparation-entries.d.ts +0 -11
  278. package/dist/packages/project-model/project-paths.d.ts +0 -12
  279. package/dist/packages/project-model/project-paths.js +0 -33
  280. package/dist/packages/shared/filesystem.d.ts +0 -2
  281. package/dist/packages/shared/filesystem.js +0 -55
  282. /package/dist/compiler-ui/_next/static/{84FaeF3EzBF9kKTMjSEVN → a3UiUF0DiMEbfWy_0gihg}/_buildManifest.js +0 -0
  283. /package/dist/compiler-ui/_next/static/{84FaeF3EzBF9kKTMjSEVN → a3UiUF0DiMEbfWy_0gihg}/_clientMiddlewareManifest.js +0 -0
  284. /package/dist/compiler-ui/_next/static/{84FaeF3EzBF9kKTMjSEVN → a3UiUF0DiMEbfWy_0gihg}/_ssgManifest.js +0 -0
  285. /package/dist/packages/{shared → contracts/utils}/file-types.d.ts +0 -0
  286. /package/dist/packages/{shared → contracts/utils}/file-types.js +0 -0
  287. /package/dist/packages/{shared → contracts/utils}/logger.d.ts +0 -0
  288. /package/dist/packages/{shared → contracts/utils}/logger.js +0 -0
  289. /package/dist/packages/{shared → contracts/utils}/naming.d.ts +0 -0
  290. /package/dist/packages/{shared → contracts/utils}/naming.js +0 -0
  291. /package/dist/packages/{shared → contracts/utils}/parse.d.ts +0 -0
  292. /package/dist/packages/{shared → contracts/utils}/parse.js +0 -0
  293. /package/dist/packages/{shared → contracts/utils}/path-guards.d.ts +0 -0
  294. /package/dist/packages/{shared → contracts/utils}/path-guards.js +0 -0
  295. /package/dist/packages/{local-service → engine}/action-values.d.ts +0 -0
  296. /package/dist/packages/{local-service → engine}/action-values.js +0 -0
  297. /package/dist/packages/{agents → engine/agents}/lib/agents.d.ts +0 -0
  298. /package/dist/packages/{agents → engine/agents}/lib/agents.js +0 -0
  299. /package/dist/packages/{agents → engine/agents}/lib/args.d.ts +0 -0
  300. /package/dist/packages/{agents → engine/agents}/lib/args.js +0 -0
  301. /package/dist/packages/{agents → engine/agents}/lib/chart-guidance.d.ts +0 -0
  302. /package/dist/packages/{agents → engine/agents}/lib/chart-guidance.js +0 -0
  303. /package/dist/packages/{agents → engine/agents}/lib/compiled-bootstrap.d.ts +0 -0
  304. /package/dist/packages/{agents → engine/agents}/lib/constants.d.ts +0 -0
  305. /package/dist/packages/{agents → engine/agents}/lib/constants.js +0 -0
  306. /package/dist/packages/{agents → engine/agents}/lib/execution-profile.d.ts +0 -0
  307. /package/dist/packages/{agents → engine/agents}/lib/execution-profile.js +0 -0
  308. /package/dist/packages/{agents → engine/agents}/lib/execution.d.ts +0 -0
  309. /package/dist/packages/{agents → engine/agents}/lib/execution.js +0 -0
  310. /package/dist/packages/{agents → engine/agents}/lib/executors.js +0 -0
  311. /package/dist/packages/{agents → engine/agents}/lib/logs.d.ts +0 -0
  312. /package/dist/packages/{agents → engine/agents}/lib/logs.js +0 -0
  313. /package/dist/packages/{agents → engine/agents}/lib/preflight.d.ts +0 -0
  314. /package/dist/packages/{agents → engine/agents}/lib/preflight.js +0 -0
  315. /package/dist/packages/{agents → engine/agents}/lib/render.d.ts +0 -0
  316. /package/dist/packages/{agents → engine/agents}/lib/render.js +0 -0
  317. /package/dist/packages/{agents → engine/agents}/lib/schema.d.ts +0 -0
  318. /package/dist/packages/{agents → engine/agents}/lib/schema.js +0 -0
  319. /package/dist/packages/{agents → engine/agents}/lib/status.d.ts +0 -0
  320. /package/dist/packages/{agents → engine/agents}/lib/status.js +0 -0
  321. /package/dist/packages/{agents → engine/agents}/lib/types.d.ts +0 -0
  322. /package/dist/packages/{agents → engine/agents}/lib/types.js +0 -0
  323. /package/dist/packages/{compiler → engine/compile}/artifact-counts.d.ts +0 -0
  324. /package/dist/packages/{compiler → engine/compile}/compiled-compile.d.ts +0 -0
  325. /package/dist/packages/{compiler → engine/compile}/compiled-compile.js +0 -0
  326. /package/dist/packages/{compiler → engine/compile}/discovery.d.ts +0 -0
  327. /package/dist/packages/{compiler → engine/compile}/method-primitives.d.ts +0 -0
  328. /package/dist/packages/{compiler → engine/compile}/method-primitives.js +0 -0
  329. /package/dist/packages/{compiler → engine/compile}/reset.d.ts +0 -0
  330. /package/dist/packages/{compiler → engine/compile}/runtime-acceptance.d.ts +0 -0
  331. /package/dist/packages/{compiler → engine/compile}/runtime-contracts.d.ts +0 -0
  332. /package/dist/packages/{compiler → engine/compile}/runtime-inventory.d.ts +0 -0
  333. /package/dist/packages/{compiler → engine/compile}/runtime-inventory.js +0 -0
  334. /package/dist/packages/{compiler → engine/compile}/runtime-paths.d.ts +0 -0
  335. /package/dist/packages/{compiler → engine/compile}/runtime-paths.js +0 -0
  336. /package/dist/packages/{compiler → engine/compile}/runtime-prompt.d.ts +0 -0
  337. /package/dist/packages/{compiler → engine/compile}/runtime-types.js +0 -0
  338. /package/dist/packages/{compiler → engine/compile}/state-health.d.ts +0 -0
  339. /package/dist/packages/{compiler → engine/compile}/validate-compiled.d.ts +0 -0
  340. /package/dist/packages/{compiler → engine/compile}/validate-helpers.d.ts +0 -0
  341. /package/dist/packages/{compiler → engine/compile}/validate-helpers.js +0 -0
  342. /package/dist/packages/{execution → engine/execution}/adapters.d.ts +0 -0
  343. /package/dist/packages/{execution → engine/execution}/adapters.js +0 -0
  344. /package/dist/packages/{execution → engine/execution}/events.d.ts +0 -0
  345. /package/dist/packages/{execution → engine/execution}/events.js +0 -0
  346. /package/dist/packages/{execution → engine/execution}/index.d.ts +0 -0
  347. /package/dist/packages/{execution → engine/execution}/index.js +0 -0
  348. /package/dist/packages/{local-service → engine}/readiness-check-draft.js +0 -0
  349. /package/dist/packages/{testing → engine/verify}/index.d.ts +0 -0
  350. /package/dist/packages/{testing → engine/verify}/index.js +0 -0
  351. /package/dist/packages/{testing → engine/verify}/test-execution.d.ts +0 -0
  352. /package/dist/packages/{testing → engine/verify}/test-paths.d.ts +0 -0
  353. /package/dist/packages/{testing → engine/verify}/test-profile-presets.d.ts +0 -0
  354. /package/dist/packages/{testing → engine/verify}/test-profile-presets.js +0 -0
  355. /package/dist/packages/{testing → engine/verify}/test-specs.d.ts +0 -0
  356. /package/dist/packages/{testing → engine/verify}/test-types.d.ts +0 -0
  357. /package/dist/packages/{testing → engine/verify}/test-types.js +0 -0
  358. /package/dist/packages/{method-authoring → methods/authoring}/index.d.ts +0 -0
  359. /package/dist/packages/{method-authoring → methods/authoring}/index.js +0 -0
  360. /package/dist/packages/{method-authoring → methods/authoring}/lib/method-edit-utils.d.ts +0 -0
  361. /package/dist/packages/{method-authoring → methods/authoring}/lib/method-edit-utils.js +0 -0
  362. /package/dist/packages/{method-authoring → methods/authoring}/method-edit-session.js +0 -0
  363. /package/dist/packages/{method-package → methods/package}/lib/package-root.d.ts +0 -0
  364. /package/dist/packages/{project-model → project}/interf.d.ts +0 -0
  365. /package/dist/packages/{project-model → project}/interf.js +0 -0
@@ -0,0 +1,1257 @@
1
+ import { createServer } from "node:http";
2
+ import { spawn } from "node:child_process";
3
+ import { randomBytes } from "node:crypto";
4
+ import { existsSync, statSync, readFileSync } from "node:fs";
5
+ import { dirname, extname, join, normalize, resolve, sep } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { LOCAL_SERVICE_LOOPBACK_HOSTS, LocalServiceConfigSchema, LocalServiceDiscoverySchema, LocalServiceErrorSchema, OpenPathRequestSchema, ServiceRegistryEntrySchema, } from "./lib/schema.js";
8
+ import { assertPathWithinRoot, } from "../contracts/utils/path-guards.js";
9
+ import { createLocalServiceRuntime, } from "./runtime.js";
10
+ import { buildLocalServiceUrl, LOCAL_SERVICE_DEFAULT_HOST, LOCAL_SERVICE_DEFAULT_PORT, LOCAL_SERVICE_ROUTES, PREPARATION_SUBRESOURCES, } from "./routes.js";
11
+ import { registerServiceLocally, unregisterService, } from "./service-registry.js";
12
+ import { createStoredPreparation, deleteStoredPreparation, getStoredPreparation, listStoredPreparations, preparationWireShape, rehydratePreparations, updateStoredPreparation, } from "./preparation-store.js";
13
+ import { clearConnection, readActiveConnection, writeConnection, } from "./connection-config.js";
14
+ import { userMethodsRoot } from "./instance-paths.js";
15
+ import { builtinMethodPackagePath, methodDefinitionPath, } from "../methods/package/local-methods.js";
16
+ import { userMethodPath, userMethodExists } from "../methods/package/user-methods.js";
17
+ /** HTTP methods that require an authenticated bearer token + Origin guard. */
18
+ const MUTATING_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
19
+ /** Generate a fresh per-instance bearer token. */
20
+ export function createLocalServiceAuthToken() {
21
+ return randomBytes(32).toString("hex");
22
+ }
23
+ /**
24
+ * Build the per-instance CORS / Origin allowlist. Only the loopback
25
+ * hostnames bound to the active port count as same-origin. `null` and
26
+ * absent Origin are also accepted (Electron / file:// / CLI tools).
27
+ */
28
+ function buildAllowedOrigins(host, port) {
29
+ const hostnames = Array.from(new Set([host, ...LOCAL_SERVICE_LOOPBACK_HOSTS]));
30
+ return {
31
+ hostnames,
32
+ ports: [port],
33
+ hostUrls: hostnames.map((hostname) => {
34
+ const wrapped = hostname.includes(":") ? `[${hostname}]` : hostname;
35
+ return `http://${wrapped}:${port}`;
36
+ }),
37
+ };
38
+ }
39
+ function originHeaderValue(req) {
40
+ const raw = req.headers.origin;
41
+ if (raw === undefined)
42
+ return null;
43
+ const value = Array.isArray(raw) ? raw[0] : raw;
44
+ if (typeof value !== "string")
45
+ return null;
46
+ const trimmed = value.trim();
47
+ return trimmed.length > 0 ? trimmed : null;
48
+ }
49
+ function authorizationHeaderValue(req) {
50
+ const raw = req.headers.authorization;
51
+ if (!raw)
52
+ return null;
53
+ const value = Array.isArray(raw) ? raw[0] : raw;
54
+ if (typeof value !== "string")
55
+ return null;
56
+ const trimmed = value.trim();
57
+ return trimmed.length > 0 ? trimmed : null;
58
+ }
59
+ function isOriginAllowed(origin, allowed) {
60
+ if (origin === null)
61
+ return true; // CLI / Node fetch / native app — no Origin sent.
62
+ if (origin === "null")
63
+ return true; // Electron / sandboxed / file:// page.
64
+ let parsed;
65
+ try {
66
+ parsed = new URL(origin);
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
72
+ return false;
73
+ const hostname = parsed.hostname.replace(/^\[|\]$/g, "");
74
+ if (!allowed.hostnames.includes(hostname))
75
+ return false;
76
+ // Default ports (no explicit port in URL) cannot be the local service.
77
+ if (!parsed.port)
78
+ return false;
79
+ const portNumber = Number.parseInt(parsed.port, 10);
80
+ if (!Number.isInteger(portNumber))
81
+ return false;
82
+ return allowed.ports.includes(portNumber);
83
+ }
84
+ function corsHeadersFor(origin, allowed) {
85
+ if (!isOriginAllowed(origin, allowed))
86
+ return { vary: "origin" };
87
+ return {
88
+ "access-control-allow-origin": origin ?? allowed.hostUrls[0] ?? "",
89
+ "access-control-allow-credentials": "false",
90
+ "access-control-allow-methods": "GET,POST,PATCH,DELETE,OPTIONS",
91
+ "access-control-allow-headers": "content-type, authorization, x-interf-idempotency-key",
92
+ vary: "origin",
93
+ };
94
+ }
95
+ function isMutatingMethod(method) {
96
+ return method !== undefined && MUTATING_METHODS.has(method.toUpperCase());
97
+ }
98
+ /**
99
+ * Validate the bearer token on a mutating request. Returns true when:
100
+ * - the runtime has no token (test harness mode), or
101
+ * - the request includes `Authorization: Bearer <token>` matching the runtime token.
102
+ */
103
+ function isAuthorizedMutation(req, runtime) {
104
+ if (!runtime.authToken)
105
+ return true;
106
+ const authorization = authorizationHeaderValue(req);
107
+ if (!authorization)
108
+ return false;
109
+ const match = /^Bearer\s+([A-Za-z0-9._\-]+)$/.exec(authorization);
110
+ if (!match)
111
+ return false;
112
+ const presented = match[1];
113
+ if (!presented || presented.length !== runtime.authToken.length)
114
+ return false;
115
+ // Constant-time compare to avoid timing oracles.
116
+ let mismatch = 0;
117
+ for (let index = 0; index < runtime.authToken.length; index += 1) {
118
+ mismatch |= presented.charCodeAt(index) ^ runtime.authToken.charCodeAt(index);
119
+ }
120
+ return mismatch === 0;
121
+ }
122
+ function packageRoot() {
123
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
124
+ }
125
+ export function resolveCompilerUiStaticRoot(rootPath = packageRoot()) {
126
+ const distRoot = join(rootPath, "dist", "compiler-ui");
127
+ const sourceExportRoot = join(rootPath, "apps", "compiler-ui", "out");
128
+ if (existsSync(join(distRoot, "index.html")))
129
+ return distRoot;
130
+ if (existsSync(join(sourceExportRoot, "index.html")))
131
+ return sourceExportRoot;
132
+ return distRoot;
133
+ }
134
+ function compilerUiStaticRoot() {
135
+ const explicit = process.env.INTERF_COMPILER_UI_STATIC_ROOT?.trim();
136
+ if (explicit)
137
+ return resolve(explicit);
138
+ return resolveCompilerUiStaticRoot();
139
+ }
140
+ function contentType(filePath) {
141
+ switch (extname(filePath)) {
142
+ case ".html":
143
+ return "text/html; charset=utf-8";
144
+ case ".js":
145
+ return "text/javascript; charset=utf-8";
146
+ case ".css":
147
+ return "text/css; charset=utf-8";
148
+ case ".json":
149
+ return "application/json; charset=utf-8";
150
+ case ".svg":
151
+ return "image/svg+xml";
152
+ case ".png":
153
+ return "image/png";
154
+ case ".ico":
155
+ return "image/x-icon";
156
+ case ".txt":
157
+ return "text/plain; charset=utf-8";
158
+ case ".md":
159
+ return "text/markdown; charset=utf-8";
160
+ case ".yml":
161
+ case ".yaml":
162
+ return "application/yaml; charset=utf-8";
163
+ case ".map":
164
+ return "application/json; charset=utf-8";
165
+ case ".webmanifest":
166
+ return "application/manifest+json; charset=utf-8";
167
+ case ".woff2":
168
+ return "font/woff2";
169
+ default:
170
+ return "application/octet-stream";
171
+ }
172
+ }
173
+ const NO_ORIGIN_RESPONSE_CONTEXT = { cors: { vary: "origin" } };
174
+ /** Stash slot on the response object that holds the per-request CORS headers. */
175
+ const RESPONSE_CONTEXT_KEY = Symbol.for("interf.localService.responseContext");
176
+ function attachResponseContext(res, ctx) {
177
+ res[RESPONSE_CONTEXT_KEY] = ctx;
178
+ }
179
+ function activeResponseContext(res) {
180
+ const ctx = res[RESPONSE_CONTEXT_KEY];
181
+ return ctx ?? NO_ORIGIN_RESPONSE_CONTEXT;
182
+ }
183
+ function writeHeaders(res, statusCode, contextOrHeaders, headers = {}) {
184
+ // Compatibility shim while call sites migrate. Older callers pass a
185
+ // plain headers record as the third argument; new callers thread a
186
+ // `ResponseContext`. When neither is supplied (or only headers are),
187
+ // we read the per-request context the entry-point handler stashed on
188
+ // the response.
189
+ const isContext = contextOrHeaders !== undefined && "cors" in contextOrHeaders;
190
+ const context = isContext ? contextOrHeaders : activeResponseContext(res);
191
+ const extraHeaders = isContext ? headers : (contextOrHeaders ?? {});
192
+ res.writeHead(statusCode, {
193
+ ...context.cors,
194
+ ...extraHeaders,
195
+ });
196
+ }
197
+ function sendJson(res, statusCode, contextOrValue, maybeValue) {
198
+ const usingContext = contextOrValue !== null
199
+ && typeof contextOrValue === "object"
200
+ && "cors" in contextOrValue
201
+ && maybeValue !== undefined;
202
+ const context = usingContext ? contextOrValue : activeResponseContext(res);
203
+ const value = usingContext ? maybeValue : contextOrValue;
204
+ writeHeaders(res, statusCode, context, {
205
+ "content-type": "application/json; charset=utf-8",
206
+ });
207
+ res.end(`${JSON.stringify(value, null, 2)}\n`);
208
+ }
209
+ function sendText(res, statusCode, contextOrValue, maybeValue) {
210
+ const usingContext = typeof contextOrValue === "object" && contextOrValue !== null && "cors" in contextOrValue;
211
+ const context = usingContext ? contextOrValue : activeResponseContext(res);
212
+ const value = usingContext ? (maybeValue ?? "") : contextOrValue;
213
+ writeHeaders(res, statusCode, context, {
214
+ "content-type": "text/plain; charset=utf-8",
215
+ });
216
+ res.end(value);
217
+ }
218
+ function sendError(res, statusCode, contextOrMessage, maybeMessage) {
219
+ const usingContext = typeof contextOrMessage === "object" && contextOrMessage !== null && "cors" in contextOrMessage;
220
+ const context = usingContext ? contextOrMessage : activeResponseContext(res);
221
+ const message = usingContext ? (maybeMessage ?? "") : contextOrMessage;
222
+ sendJson(res, statusCode, context, LocalServiceErrorSchema.parse({
223
+ error: {
224
+ message,
225
+ },
226
+ }));
227
+ }
228
+ function resolveOpenPath(allowedRoots, pathValue) {
229
+ const roots = allowedRoots.map((root) => resolve(root));
230
+ const targetPath = resolve(pathValue);
231
+ for (const root of roots) {
232
+ try {
233
+ return assertPathWithinRoot(root, targetPath, "Open path");
234
+ }
235
+ catch {
236
+ continue;
237
+ }
238
+ }
239
+ throw new Error(`Open path must stay inside ${roots.join(" or ")}: ${targetPath}`);
240
+ }
241
+ async function openLocalPath(allowedRoots, pathValue) {
242
+ const targetPath = resolveOpenPath(allowedRoots, pathValue);
243
+ if (!existsSync(targetPath)) {
244
+ throw new Error(`Path does not exist: ${targetPath}`);
245
+ }
246
+ if (process.env.INTERF_OPEN_PATH_DRY_RUN === "1") {
247
+ return targetPath;
248
+ }
249
+ const isDirectory = statSync(targetPath).isDirectory();
250
+ const command = process.platform === "darwin"
251
+ ? "open"
252
+ : process.platform === "win32"
253
+ ? "explorer.exe"
254
+ : "xdg-open";
255
+ const args = process.platform === "darwin"
256
+ ? (isDirectory ? [targetPath] : ["-R", targetPath])
257
+ : process.platform === "win32"
258
+ ? (isDirectory ? [targetPath] : ["/select,", targetPath])
259
+ : [isDirectory ? targetPath : dirname(targetPath)];
260
+ await new Promise((resolveOpen, rejectOpen) => {
261
+ const child = spawn(command, args, {
262
+ detached: true,
263
+ stdio: "ignore",
264
+ });
265
+ child.once("error", rejectOpen);
266
+ child.once("spawn", () => {
267
+ child.unref();
268
+ resolveOpen();
269
+ });
270
+ });
271
+ return targetPath;
272
+ }
273
+ function parseRequestUrl(req) {
274
+ return new URL(req.url ?? "/", "http://127.0.0.1");
275
+ }
276
+ function safeStaticPath(root, relativePath) {
277
+ const normalized = normalize(relativePath).replace(/^(\.\.(?:\/|\\|$))+/, "");
278
+ const absolute = resolve(root, normalized);
279
+ const resolvedRoot = resolve(root);
280
+ if (absolute !== resolvedRoot && !absolute.startsWith(`${resolvedRoot}${sep}`)) {
281
+ return null;
282
+ }
283
+ return absolute;
284
+ }
285
+ /**
286
+ * Resolve the on-disk root path for a method id. Used by the
287
+ * `/v1/methods/<id>/files/<relpath>` route. Resolution order matches
288
+ * the runtime: preparation-draft (if a prep id is supplied) → user
289
+ * library → built-in. Returns `null` when the method id resolves
290
+ * nowhere.
291
+ */
292
+ function resolveMethodPackageRoot(methodId, prepDataDir) {
293
+ if (prepDataDir) {
294
+ const localPath = methodDefinitionPath(prepDataDir, methodId);
295
+ if (existsSync(join(localPath, "method.json")))
296
+ return localPath;
297
+ }
298
+ if (userMethodExists(methodId)) {
299
+ return userMethodPath(methodId);
300
+ }
301
+ const builtinPath = builtinMethodPackagePath(methodId);
302
+ if (existsSync(join(builtinPath, "method.json")))
303
+ return builtinPath;
304
+ return null;
305
+ }
306
+ function sendFile(res, filePath) {
307
+ if (!existsSync(filePath))
308
+ return false;
309
+ try {
310
+ if (!statSync(filePath).isFile())
311
+ return false;
312
+ writeHeaders(res, 200, {
313
+ "content-type": contentType(filePath),
314
+ });
315
+ res.end(readFileSync(filePath));
316
+ return true;
317
+ }
318
+ catch {
319
+ return false;
320
+ }
321
+ }
322
+ /**
323
+ * Reject any path that contains `..` or absolute segments — used by the
324
+ * 0.16 file-serving endpoints (`/v1/methods/<id>/files/...`,
325
+ * `/v1/preparations/<id>/files/...`). The `safeStaticPath` helper used
326
+ * by the static-asset path silently strips traversal segments; for the
327
+ * API endpoints we want a hard reject so the response is unambiguous.
328
+ */
329
+ function isTraversalRelativePath(relPath) {
330
+ if (relPath.length === 0)
331
+ return true;
332
+ const segments = relPath.split(/[\\/]+/);
333
+ for (const segment of segments) {
334
+ if (segment === ".." || segment === "")
335
+ return true;
336
+ }
337
+ if (relPath.startsWith("/") || /^[A-Za-z]:/.test(relPath))
338
+ return true;
339
+ return false;
340
+ }
341
+ function sendCompilerUiAsset(req, res, _runtime) {
342
+ const staticRoot = compilerUiStaticRoot();
343
+ const url = parseRequestUrl(req);
344
+ if (url.pathname === "/") {
345
+ const indexPath = join(staticRoot, "index.html");
346
+ if (sendFile(res, indexPath))
347
+ return true;
348
+ sendText(res, 503, "Interf UI assets are missing. Run `npm run web:build`, then restart `interf web`.\n");
349
+ return true;
350
+ }
351
+ if (url.pathname.startsWith("/_next/static/")) {
352
+ const relativePath = url.pathname.slice("/_next/static/".length);
353
+ const assetPath = safeStaticPath(join(staticRoot, "_next", "static"), relativePath);
354
+ if (assetPath && sendFile(res, assetPath))
355
+ return true;
356
+ }
357
+ const assetPath = safeStaticPath(staticRoot, url.pathname.slice(1));
358
+ if (assetPath && sendFile(res, assetPath))
359
+ return true;
360
+ return false;
361
+ }
362
+ async function readJsonBody(req) {
363
+ const chunks = [];
364
+ for await (const chunk of req) {
365
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
366
+ }
367
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
368
+ if (!raw)
369
+ return {};
370
+ return JSON.parse(raw);
371
+ }
372
+ async function routeApi(req, res, runtime) {
373
+ const url = parseRequestUrl(req);
374
+ const path = url.pathname;
375
+ const method = req.method ?? "GET";
376
+ const origin = originHeaderValue(req);
377
+ const allowed = buildAllowedOrigins(runtime.host, runtime.port);
378
+ // CORS preflight — answered for allowed origins, refused otherwise.
379
+ if (method === "OPTIONS") {
380
+ if (!isOriginAllowed(origin, allowed)) {
381
+ attachResponseContext(res, NO_ORIGIN_RESPONSE_CONTEXT);
382
+ sendError(res, 403, "Origin not allowed.");
383
+ return true;
384
+ }
385
+ attachResponseContext(res, { cors: corsHeadersFor(origin, allowed) });
386
+ writeHeaders(res, 204);
387
+ res.end();
388
+ return true;
389
+ }
390
+ // For non-OPTIONS, attach the CORS context once.
391
+ attachResponseContext(res, { cors: corsHeadersFor(origin, allowed) });
392
+ // Mutating requests must:
393
+ // 1. Have an Origin that's local OR no Origin at all (CLI),
394
+ // 2. Carry a valid Authorization: Bearer <token> if the runtime issued one.
395
+ // Read-only GETs stay open so dev tooling can still inspect state.
396
+ if (isMutatingMethod(method)) {
397
+ if (!isOriginAllowed(origin, allowed)) {
398
+ sendError(res, 403, "Origin not allowed.");
399
+ return true;
400
+ }
401
+ if (!isAuthorizedMutation(req, runtime)) {
402
+ writeHeaders(res, 401, { "www-authenticate": "Bearer realm=\"interf-local-service\"" });
403
+ res.end(`${JSON.stringify(LocalServiceErrorSchema.parse({
404
+ error: { message: "Missing or invalid bearer token." },
405
+ }), null, 2)}\n`);
406
+ return true;
407
+ }
408
+ }
409
+ // ─────────────────────────────────────────────────────────────────────────
410
+ // Top-level engine routes — instance metadata, health, discovery.
411
+ // ─────────────────────────────────────────────────────────────────────────
412
+ // GET /health — liveness probe used by the CLI bootstrap.
413
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.health) {
414
+ sendJson(res, 200, runtime.health());
415
+ return true;
416
+ }
417
+ // GET /v1 — discovery list of available resources.
418
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.api) {
419
+ sendJson(res, 200, LocalServiceDiscoverySchema.parse({
420
+ kind: "interf-local-service-discovery",
421
+ version: 1,
422
+ resources: {
423
+ instance: LOCAL_SERVICE_ROUTES.instance,
424
+ preparations: LOCAL_SERVICE_ROUTES.preparations,
425
+ methods: LOCAL_SERVICE_ROUTES.methods,
426
+ runs: LOCAL_SERVICE_ROUTES.runs,
427
+ action_proposals: LOCAL_SERVICE_ROUTES.actionProposals,
428
+ executor: LOCAL_SERVICE_ROUTES.executor,
429
+ agents: LOCAL_SERVICE_ROUTES.agents,
430
+ open_path: LOCAL_SERVICE_ROUTES.openPath,
431
+ },
432
+ }));
433
+ return true;
434
+ }
435
+ // GET /v1/instance — engine metadata.
436
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.instance) {
437
+ const startedAtIso = runtime.startedAt ?? new Date().toISOString();
438
+ const startedMs = Date.parse(startedAtIso);
439
+ const uptimeSeconds = Number.isFinite(startedMs)
440
+ ? Math.max(0, Math.floor((Date.now() - startedMs) / 1000))
441
+ : 0;
442
+ const agentSnapshot = runtime.getAgentsRegistry();
443
+ // 0.16 — `connection_kind` lets the UI know whether OS-open is even
444
+ // possible. A loopback bind means the engine has filesystem access
445
+ // to the same host as the user; non-loopback (cloud) returns
446
+ // "remote" so the UI routes to api-served / signed-URL paths
447
+ // instead. The current binary refuses non-loopback binds (see the
448
+ // host whitelist below), so the value is "local" today; the field
449
+ // is in place so the UI can read it without a CLI version sniff.
450
+ const isLoopback = LOCAL_SERVICE_LOOPBACK_HOSTS.includes(runtime.host);
451
+ sendJson(res, 200, {
452
+ kind: "interf-instance",
453
+ version: 1,
454
+ url: buildLocalServiceUrl({ host: runtime.host, port: runtime.port }),
455
+ host: runtime.host,
456
+ port: runtime.port,
457
+ pid: process.pid,
458
+ started_at: startedAtIso,
459
+ uptime_seconds: uptimeSeconds,
460
+ package_version: runtime.packageVersion ?? null,
461
+ preparation_count: listStoredPreparations().length,
462
+ auth_required: Boolean(runtime.authToken),
463
+ // 0.15 connected-agents fields:
464
+ agent_count: agentSnapshot.agents.length,
465
+ default_agent: agentSnapshot.active_agent?.name ?? null,
466
+ // 0.16 connection-mode flag:
467
+ connection_kind: isLoopback ? "local" : "remote",
468
+ });
469
+ return true;
470
+ }
471
+ // ─────────────────────────────────────────────────────────────────────────
472
+ // Preparation collection routes
473
+ // ─────────────────────────────────────────────────────────────────────────
474
+ // GET /v1/preparations — list every preparation on the instance.
475
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.preparations) {
476
+ const items = listStoredPreparations().map(preparationWireShape);
477
+ sendJson(res, 200, { preparations: items });
478
+ return true;
479
+ }
480
+ // POST /v1/preparations — create a new preparation.
481
+ if (method === "POST" && path === LOCAL_SERVICE_ROUTES.preparations) {
482
+ try {
483
+ const body = (await readJsonBody(req));
484
+ if (!body || typeof body !== "object") {
485
+ sendError(res, 400, "Request body must be a JSON object.");
486
+ return true;
487
+ }
488
+ if (!body.id || typeof body.id !== "string") {
489
+ sendError(res, 400, "Missing required field: id");
490
+ return true;
491
+ }
492
+ if (!body.method_id || typeof body.method_id !== "string") {
493
+ sendError(res, 400, "Missing required field: method_id");
494
+ return true;
495
+ }
496
+ if (!body.source || typeof body.source !== "object" || !body.source.locator) {
497
+ sendError(res, 400, "Missing required field: source.locator");
498
+ return true;
499
+ }
500
+ const stored = createStoredPreparation(runtime, {
501
+ id: body.id,
502
+ source: { kind: "local-folder", locator: body.source.locator },
503
+ method_id: body.method_id,
504
+ about: body.about,
505
+ checks: body.checks,
506
+ max_attempts: body.max_attempts,
507
+ max_loops: body.max_loops,
508
+ });
509
+ sendJson(res, 201, preparationWireShape(stored));
510
+ }
511
+ catch (error) {
512
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
513
+ }
514
+ return true;
515
+ }
516
+ // ─────────────────────────────────────────────────────────────────────────
517
+ // Per-preparation routes (under /v1/preparations/{id}/…)
518
+ // ─────────────────────────────────────────────────────────────────────────
519
+ if (path.startsWith(`${LOCAL_SERVICE_ROUTES.preparations}/`)) {
520
+ const tail = path.slice(LOCAL_SERVICE_ROUTES.preparations.length + 1);
521
+ const slashIndex = tail.indexOf("/");
522
+ const prepId = slashIndex === -1 ? tail : tail.slice(0, slashIndex);
523
+ const subPath = slashIndex === -1 ? "" : tail.slice(slashIndex + 1);
524
+ const decodedPrepId = decodeURIComponent(prepId);
525
+ const storedPrep = getStoredPreparation(decodedPrepId);
526
+ if (!storedPrep) {
527
+ sendError(res, 404, `Preparation not found: ${decodedPrepId}`);
528
+ return true;
529
+ }
530
+ runtime.touchPreparation(storedPrep.prepDataDir);
531
+ if (subPath === "") {
532
+ // Bare /v1/preparations/{id}
533
+ if (method === "GET") {
534
+ sendJson(res, 200, preparationWireShape(storedPrep));
535
+ return true;
536
+ }
537
+ if (method === "PATCH") {
538
+ try {
539
+ const body = (await readJsonBody(req));
540
+ if (!body || typeof body !== "object") {
541
+ sendError(res, 400, "Request body must be a JSON object.");
542
+ return true;
543
+ }
544
+ const updated = updateStoredPreparation(decodedPrepId, {
545
+ method_id: body.method_id,
546
+ about: body.about,
547
+ });
548
+ sendJson(res, 200, preparationWireShape(updated));
549
+ }
550
+ catch (error) {
551
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
552
+ }
553
+ return true;
554
+ }
555
+ if (method === "DELETE") {
556
+ deleteStoredPreparation(runtime, decodedPrepId);
557
+ sendJson(res, 200, { id: decodedPrepId, deleted: true });
558
+ return true;
559
+ }
560
+ }
561
+ else if (subPath === PREPARATION_SUBRESOURCES.compileRuns) {
562
+ if (method === "POST") {
563
+ if (!storedPrep.methodId) {
564
+ sendError(res, 400, `Preparation ${storedPrep.id} has no method bound. Set one via PATCH /v1/preparations/${storedPrep.id} { "method_id": "<id>" } before compiling.`);
565
+ return true;
566
+ }
567
+ try {
568
+ const body = (await readJsonBody(req));
569
+ const request = { preparation: storedPrep.id, ...(body ?? {}) };
570
+ const idempotencyKeyRaw = req.headers["x-interf-idempotency-key"];
571
+ const idempotencyKey = Array.isArray(idempotencyKeyRaw)
572
+ ? idempotencyKeyRaw[0]
573
+ : idempotencyKeyRaw;
574
+ const trimmedKey = typeof idempotencyKey === "string" ? idempotencyKey.trim() : "";
575
+ const dedupedRunId = trimmedKey
576
+ ? runtime.findIdempotentCompileRun(storedPrep.prepDataDir, trimmedKey)
577
+ : null;
578
+ if (dedupedRunId) {
579
+ const existing = runtime.getCompileRun(storedPrep.prepDataDir, dedupedRunId);
580
+ if (existing) {
581
+ sendJson(res, 200, existing);
582
+ return true;
583
+ }
584
+ }
585
+ const resource = await runtime.createCompileRun(storedPrep.prepDataDir, request);
586
+ if (trimmedKey) {
587
+ runtime.recordIdempotentCompileRun(storedPrep.prepDataDir, trimmedKey, resource.run.run_id);
588
+ }
589
+ sendJson(res, 201, resource);
590
+ }
591
+ catch (error) {
592
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
593
+ }
594
+ return true;
595
+ }
596
+ }
597
+ else if (subPath === PREPARATION_SUBRESOURCES.verifyRuns) {
598
+ if (method === "POST") {
599
+ if (!storedPrep.methodId) {
600
+ sendError(res, 400, `Preparation ${storedPrep.id} has no method bound. Set one via PATCH /v1/preparations/${storedPrep.id} { "method_id": "<id>" } before verifying.`);
601
+ return true;
602
+ }
603
+ try {
604
+ const body = (await readJsonBody(req));
605
+ const request = { preparation: storedPrep.id, ...(body ?? {}) };
606
+ const resource = await runtime.createVerifyRun(storedPrep.prepDataDir, request);
607
+ sendJson(res, 201, resource);
608
+ }
609
+ catch (error) {
610
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
611
+ }
612
+ return true;
613
+ }
614
+ }
615
+ else if (subPath === PREPARATION_SUBRESOURCES.methodAuthoringRuns) {
616
+ if (method === "POST") {
617
+ try {
618
+ const body = (await readJsonBody(req));
619
+ const job = await runtime.createMethodAuthoringRun(storedPrep.prepDataDir, body);
620
+ sendJson(res, 202, job);
621
+ }
622
+ catch (error) {
623
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
624
+ }
625
+ return true;
626
+ }
627
+ }
628
+ else if (subPath === PREPARATION_SUBRESOURCES.methodImprovementRuns) {
629
+ if (method === "POST") {
630
+ try {
631
+ const body = (await readJsonBody(req));
632
+ const job = await runtime.createMethodAuthoringRun(storedPrep.prepDataDir, body, "method-improvement");
633
+ sendJson(res, 202, job);
634
+ }
635
+ catch (error) {
636
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
637
+ }
638
+ return true;
639
+ }
640
+ }
641
+ else if (subPath === PREPARATION_SUBRESOURCES.readinessCheckDrafts) {
642
+ if (method === "POST") {
643
+ try {
644
+ const body = (await readJsonBody(req));
645
+ const job = await runtime.createReadinessCheckDraftRun(storedPrep.prepDataDir, body);
646
+ sendJson(res, 202, job);
647
+ }
648
+ catch (error) {
649
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
650
+ }
651
+ return true;
652
+ }
653
+ }
654
+ else if (subPath === PREPARATION_SUBRESOURCES.changes) {
655
+ if (method === "POST") {
656
+ try {
657
+ const body = (await readJsonBody(req));
658
+ sendJson(res, 202, runtime.applyPreparationChange(storedPrep.prepDataDir, body));
659
+ }
660
+ catch (error) {
661
+ sendError(res, 409, error instanceof Error ? error.message : String(error));
662
+ }
663
+ return true;
664
+ }
665
+ }
666
+ else if (subPath === PREPARATION_SUBRESOURCES.reset) {
667
+ if (method === "POST") {
668
+ try {
669
+ const body = (await readJsonBody(req));
670
+ const request = { preparation: storedPrep.id, scope: "compile", ...(body ?? {}) };
671
+ const result = runtime.applyReset(storedPrep.prepDataDir, request);
672
+ sendJson(res, 200, result);
673
+ }
674
+ catch (error) {
675
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
676
+ }
677
+ return true;
678
+ }
679
+ }
680
+ else if (subPath === PREPARATION_SUBRESOURCES.readiness) {
681
+ if (method === "GET") {
682
+ const readiness = runtime.getReadiness(storedPrep.prepDataDir, storedPrep.id);
683
+ sendJson(res, 200, readiness);
684
+ return true;
685
+ }
686
+ }
687
+ else if (subPath === PREPARATION_SUBRESOURCES.runs) {
688
+ if (method === "GET") {
689
+ const runs = runtime.listCompileRunsForPreparation(storedPrep.prepDataDir, storedPrep.id);
690
+ sendJson(res, 200, { runs });
691
+ return true;
692
+ }
693
+ }
694
+ else if (subPath === PREPARATION_SUBRESOURCES.sourceFiles) {
695
+ if (method === "GET") {
696
+ sendJson(res, 200, {
697
+ source_files: runtime.listSourceFiles(storedPrep.prepDataDir, storedPrep.id),
698
+ });
699
+ return true;
700
+ }
701
+ }
702
+ else if (subPath === PREPARATION_SUBRESOURCES.portableContext) {
703
+ if (method === "GET") {
704
+ const context = runtime.getPortableContext(storedPrep.prepDataDir, storedPrep.id);
705
+ if (!context)
706
+ sendError(res, 404, "Portable context not found.");
707
+ else
708
+ sendJson(res, 200, context);
709
+ return true;
710
+ }
711
+ }
712
+ else if (subPath.startsWith(`${PREPARATION_SUBRESOURCES.files}/`)) {
713
+ // GET /v1/preparations/<id>/files/<relpath> — read-only file
714
+ // serving inside the prep's portable-context root. Used by the
715
+ // locator pattern's `api-served` kind.
716
+ if (method === "GET") {
717
+ const rawRelPath = subPath.slice(PREPARATION_SUBRESOURCES.files.length + 1);
718
+ let relPath;
719
+ try {
720
+ relPath = decodeURIComponent(rawRelPath);
721
+ }
722
+ catch {
723
+ sendError(res, 400, "File path is not valid URI-encoded UTF-8.");
724
+ return true;
725
+ }
726
+ if (isTraversalRelativePath(relPath)) {
727
+ sendError(res, 400, "File path escapes portable-context root.");
728
+ return true;
729
+ }
730
+ const safePath = safeStaticPath(storedPrep.portableContextPath, relPath);
731
+ if (!safePath) {
732
+ sendError(res, 400, "File path escapes portable-context root.");
733
+ return true;
734
+ }
735
+ if (!sendFile(res, safePath)) {
736
+ sendError(res, 404, `File not found: ${relPath}`);
737
+ }
738
+ return true;
739
+ }
740
+ }
741
+ sendError(res, 404, `Unknown preparation sub-route: ${subPath}`);
742
+ return true;
743
+ }
744
+ // ─────────────────────────────────────────────────────────────────────────
745
+ // Method resources — preparation-independent (workspace draft + user lib + bundled).
746
+ // ─────────────────────────────────────────────────────────────────────────
747
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.methods) {
748
+ // The runtime needs SOME prep data dir to discover preparation-draft methods.
749
+ // Fall back to the first registered preparation (if any) so user-library
750
+ // and bundled methods still surface even when no preparation has drafts.
751
+ const firstPrep = listStoredPreparations()[0];
752
+ sendJson(res, 200, { methods: runtime.listMethods(firstPrep?.prepDataDir ?? runtime.rootPath) });
753
+ return true;
754
+ }
755
+ const methodMatch = path.match(/^\/v1\/methods\/([^/]+)$/);
756
+ if (method === "GET" && methodMatch?.[1]) {
757
+ const firstPrep = listStoredPreparations()[0];
758
+ const methodResource = runtime.getMethod(firstPrep?.prepDataDir ?? runtime.rootPath, decodeURIComponent(methodMatch[1]));
759
+ if (!methodResource)
760
+ sendError(res, 404, "Method not found.");
761
+ else
762
+ sendJson(res, 200, methodResource);
763
+ return true;
764
+ }
765
+ const methodRunsMatch = path.match(/^\/v1\/methods\/([^/]+)\/runs$/);
766
+ if (method === "GET" && methodRunsMatch?.[1]) {
767
+ const firstPrep = listStoredPreparations()[0];
768
+ const runs = runtime.listMethodRuns(firstPrep?.prepDataDir ?? runtime.rootPath, decodeURIComponent(methodRunsMatch[1]));
769
+ sendJson(res, 200, { runs });
770
+ return true;
771
+ }
772
+ // GET /v1/methods/<id>/files/<relpath> — read-only file serving inside
773
+ // the method root. Used by the locator pattern's `api-served` kind so
774
+ // the UI can render SKILL.md / contract files in a side drawer over a
775
+ // remote engine. Resolution: prep-draft (when first prep exists) →
776
+ // user library → built-in. Path-guard rejects any traversal outside
777
+ // the resolved root.
778
+ const methodFilesMatch = path.match(/^\/v1\/methods\/([^/]+)\/files\/(.+)$/);
779
+ if (method === "GET" && methodFilesMatch?.[1] && methodFilesMatch[2]) {
780
+ const methodId = decodeURIComponent(methodFilesMatch[1]);
781
+ let relPath;
782
+ try {
783
+ relPath = decodeURIComponent(methodFilesMatch[2]);
784
+ }
785
+ catch {
786
+ sendError(res, 400, "File path is not valid URI-encoded UTF-8.");
787
+ return true;
788
+ }
789
+ if (isTraversalRelativePath(relPath)) {
790
+ sendError(res, 400, "File path escapes method root.");
791
+ return true;
792
+ }
793
+ const firstPrep = listStoredPreparations()[0];
794
+ const root = resolveMethodPackageRoot(methodId, firstPrep?.prepDataDir);
795
+ if (!root) {
796
+ sendError(res, 404, `Method not found: ${methodId}`);
797
+ return true;
798
+ }
799
+ const safePath = safeStaticPath(root, relPath);
800
+ if (!safePath) {
801
+ sendError(res, 400, "File path escapes method root.");
802
+ return true;
803
+ }
804
+ if (!sendFile(res, safePath)) {
805
+ sendError(res, 404, `File not found: ${relPath}`);
806
+ }
807
+ return true;
808
+ }
809
+ // ─────────────────────────────────────────────────────────────────────────
810
+ // Run observability — instance-wide. Each run record carries a workspace,
811
+ // so the runtime takes a "first prep" hint to scan registered preparations.
812
+ // ─────────────────────────────────────────────────────────────────────────
813
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.runs) {
814
+ const firstPrep = listStoredPreparations()[0];
815
+ sendJson(res, 200, { runs: runtime.listRunObservability(firstPrep?.prepDataDir ?? runtime.rootPath) });
816
+ return true;
817
+ }
818
+ const observableRunMatch = path.match(/^\/v1\/runs\/([^/]+)$/);
819
+ if (method === "GET" && observableRunMatch?.[1]) {
820
+ const firstPrep = listStoredPreparations()[0];
821
+ const run = runtime.getRunObservability(firstPrep?.prepDataDir ?? runtime.rootPath, decodeURIComponent(observableRunMatch[1]));
822
+ if (!run)
823
+ sendError(res, 404, "Run not found.");
824
+ else
825
+ sendJson(res, 200, run);
826
+ return true;
827
+ }
828
+ // ─────────────────────────────────────────────────────────────────────────
829
+ // Action proposals (chat / wizard) — instance-wide.
830
+ // ─────────────────────────────────────────────────────────────────────────
831
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.actionProposals) {
832
+ const firstPrep = listStoredPreparations()[0];
833
+ sendJson(res, 200, {
834
+ action_proposals: runtime.listActionProposals(firstPrep?.prepDataDir ?? runtime.rootPath),
835
+ });
836
+ return true;
837
+ }
838
+ if (method === "POST" && path === LOCAL_SERVICE_ROUTES.actionProposals) {
839
+ const body = await readJsonBody(req);
840
+ const firstPrep = listStoredPreparations()[0];
841
+ sendJson(res, 202, await runtime.createActionProposal(firstPrep?.prepDataDir ?? runtime.rootPath, body));
842
+ return true;
843
+ }
844
+ const actionProposalMatch = path.match(/^\/v1\/action-proposals\/([^/]+)(?:\/([^/]+))?$/);
845
+ if (actionProposalMatch?.[1]) {
846
+ const proposalId = decodeURIComponent(actionProposalMatch[1]);
847
+ const child = actionProposalMatch[2];
848
+ const firstPrep = listStoredPreparations()[0];
849
+ const prepDataDir = firstPrep?.prepDataDir ?? runtime.rootPath;
850
+ if (method === "GET" && !child) {
851
+ const proposal = runtime.getActionProposal(prepDataDir, proposalId);
852
+ if (!proposal)
853
+ sendError(res, 404, "Action proposal not found.");
854
+ else
855
+ sendJson(res, 200, proposal);
856
+ return true;
857
+ }
858
+ if (method === "POST" && child === "decision") {
859
+ const body = await readJsonBody(req);
860
+ const proposal = await runtime.decideActionProposal(prepDataDir, proposalId, body);
861
+ if (!proposal)
862
+ sendError(res, 404, "Action proposal not found.");
863
+ else
864
+ sendJson(res, 202, proposal);
865
+ return true;
866
+ }
867
+ }
868
+ // ─────────────────────────────────────────────────────────────────────────
869
+ // Compile-run sub-resources (instance-wide; runs are addressable by run_id).
870
+ // ─────────────────────────────────────────────────────────────────────────
871
+ const compileRunMatch = path.match(/^\/v1\/compile-runs\/([^/]+)(?:\/([^/]+))?$/);
872
+ if (compileRunMatch?.[1]) {
873
+ const runId = decodeURIComponent(compileRunMatch[1]);
874
+ const child = compileRunMatch[2];
875
+ const firstPrep = listStoredPreparations()[0];
876
+ const prepDataDir = firstPrep?.prepDataDir ?? runtime.rootPath;
877
+ if (method === "GET" && !child) {
878
+ const run = runtime.getCompileRun(prepDataDir, runId);
879
+ if (!run)
880
+ sendError(res, 404, "Compile run not found.");
881
+ else
882
+ sendJson(res, 200, run);
883
+ return true;
884
+ }
885
+ if (method === "GET" && child === "events") {
886
+ const events = runtime.getCompileRunEvents(prepDataDir, runId);
887
+ if (!events)
888
+ sendError(res, 404, "Compile run not found.");
889
+ else
890
+ sendJson(res, 200, { events });
891
+ return true;
892
+ }
893
+ if (method === "GET" && child === "proof") {
894
+ const proof = runtime.getCompileRunProof(prepDataDir, runId);
895
+ if (!proof)
896
+ sendError(res, 404, "Compile run not found.");
897
+ else
898
+ sendJson(res, 200, { proof });
899
+ return true;
900
+ }
901
+ if (method === "GET" && child === "artifacts") {
902
+ const artifacts = runtime.getCompileRunArtifacts(prepDataDir, runId);
903
+ if (!artifacts)
904
+ sendError(res, 404, "Compile run not found.");
905
+ else
906
+ sendJson(res, 200, { artifacts });
907
+ return true;
908
+ }
909
+ if (method === "POST" && child === "cancel") {
910
+ const existing = runtime.getCompileRun(prepDataDir, runId);
911
+ if (!existing) {
912
+ sendError(res, 404, "Compile run not found.");
913
+ return true;
914
+ }
915
+ const result = runtime.cancelCompileRun(runId);
916
+ sendJson(res, 200, result);
917
+ return true;
918
+ }
919
+ }
920
+ const verifyRunMatch = path.match(/^\/v1\/verify-runs\/([^/]+)$/);
921
+ if (method === "GET" && verifyRunMatch?.[1]) {
922
+ const firstPrep = listStoredPreparations()[0];
923
+ const run = runtime.getVerifyRun(firstPrep?.prepDataDir ?? runtime.rootPath, decodeURIComponent(verifyRunMatch[1]));
924
+ if (!run)
925
+ sendError(res, 404, "Verify run not found.");
926
+ else
927
+ sendJson(res, 200, run);
928
+ return true;
929
+ }
930
+ const jobMatch = path.match(/^\/v1\/jobs\/([^/]+)(?:\/([^/]+))?$/);
931
+ if (jobMatch?.[1]) {
932
+ const runId = decodeURIComponent(jobMatch[1]);
933
+ const child = jobMatch[2];
934
+ const firstPrep = listStoredPreparations()[0];
935
+ const prepDataDir = firstPrep?.prepDataDir ?? runtime.rootPath;
936
+ if (method === "GET" && !child) {
937
+ const job = runtime.getJob(prepDataDir, runId);
938
+ if (!job)
939
+ sendError(res, 404, "Job run not found.");
940
+ else
941
+ sendJson(res, 200, job);
942
+ return true;
943
+ }
944
+ if (method === "GET" && child === "events") {
945
+ const events = runtime.getJobEvents(prepDataDir, runId);
946
+ if (!events)
947
+ sendError(res, 404, "Job run not found.");
948
+ else
949
+ sendJson(res, 200, { events });
950
+ return true;
951
+ }
952
+ }
953
+ // ─────────────────────────────────────────────────────────────────────────
954
+ // Executor + open-path — instance-wide.
955
+ // ─────────────────────────────────────────────────────────────────────────
956
+ if (method === "GET" && path === LOCAL_SERVICE_ROUTES.executor) {
957
+ sendJson(res, 200, runtime.getExecutorStatus());
958
+ return true;
959
+ }
960
+ if (method === "POST" && path === LOCAL_SERVICE_ROUTES.executor) {
961
+ const body = await readJsonBody(req);
962
+ sendJson(res, 202, runtime.selectExecutor(body));
963
+ return true;
964
+ }
965
+ // ─────────────────────────────────────────────────────────────────────────
966
+ // Connected agents (0.15) — registry, role-map, role list.
967
+ // ─────────────────────────────────────────────────────────────────────────
968
+ if (path === LOCAL_SERVICE_ROUTES.agents) {
969
+ if (method === "GET") {
970
+ sendJson(res, 200, runtime.getAgentsRegistry());
971
+ return true;
972
+ }
973
+ if (method === "POST") {
974
+ try {
975
+ const body = (await readJsonBody(req));
976
+ if (!body || typeof body !== "object") {
977
+ sendError(res, 400, "Request body must be a JSON object.");
978
+ return true;
979
+ }
980
+ if (!body.name || typeof body.name !== "string") {
981
+ sendError(res, 400, "Missing required field: name");
982
+ return true;
983
+ }
984
+ if (!body.display_name || typeof body.display_name !== "string") {
985
+ sendError(res, 400, "Missing required field: display_name");
986
+ return true;
987
+ }
988
+ if (!body.command || typeof body.command !== "string") {
989
+ sendError(res, 400, "Missing required field: command");
990
+ return true;
991
+ }
992
+ const updated = runtime.registerCustomAgent({
993
+ name: body.name,
994
+ display_name: body.display_name,
995
+ command: body.command,
996
+ });
997
+ sendJson(res, 201, updated);
998
+ }
999
+ catch (error) {
1000
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
1001
+ }
1002
+ return true;
1003
+ }
1004
+ }
1005
+ if (method === "GET" && path === `${LOCAL_SERVICE_ROUTES.agents}/roles`) {
1006
+ sendJson(res, 200, {
1007
+ kind: "interf-canonical-roles",
1008
+ version: 1,
1009
+ roles: ["extractor", "summarizer", "structurer", "verifier", "general"],
1010
+ });
1011
+ return true;
1012
+ }
1013
+ if (path === `${LOCAL_SERVICE_ROUTES.agents}/role-map`) {
1014
+ if (method === "GET") {
1015
+ const snapshot = runtime.getAgentsRegistry();
1016
+ sendJson(res, 200, {
1017
+ role_map: snapshot.role_map,
1018
+ active_agent: snapshot.active_agent,
1019
+ });
1020
+ return true;
1021
+ }
1022
+ if (method === "PATCH") {
1023
+ try {
1024
+ const body = (await readJsonBody(req));
1025
+ if (!body || typeof body !== "object") {
1026
+ sendError(res, 400, "Request body must be a JSON object.");
1027
+ return true;
1028
+ }
1029
+ const patch = {};
1030
+ for (const [role, value] of Object.entries(body)) {
1031
+ if (typeof value !== "string") {
1032
+ sendError(res, 400, `Role-map values must be strings (or "" to clear). Got ${typeof value} for role "${role}".`);
1033
+ return true;
1034
+ }
1035
+ patch[role] = value;
1036
+ }
1037
+ const updated = runtime.patchAgentsRoleMap(patch);
1038
+ sendJson(res, 200, updated);
1039
+ }
1040
+ catch (error) {
1041
+ sendError(res, 400, error instanceof Error ? error.message : String(error));
1042
+ }
1043
+ return true;
1044
+ }
1045
+ }
1046
+ // /v1/agents/<name> — only DELETE; built-in names return 400.
1047
+ const agentMatch = path.match(/^\/v1\/agents\/([^/]+)$/);
1048
+ if (agentMatch?.[1] && method === "DELETE") {
1049
+ const name = decodeURIComponent(agentMatch[1]);
1050
+ try {
1051
+ const updated = runtime.unregisterCustomAgent(name);
1052
+ sendJson(res, 200, updated);
1053
+ }
1054
+ catch (error) {
1055
+ const message = error instanceof Error ? error.message : String(error);
1056
+ // Rejecting a built-in unregister is a 400 (bad request, the
1057
+ // caller is asking for something the contract doesn't allow).
1058
+ const status = /built-in agent/.test(message) ? 400 : 404;
1059
+ sendError(res, status, message);
1060
+ }
1061
+ return true;
1062
+ }
1063
+ if (method === "POST" && path === LOCAL_SERVICE_ROUTES.openPath) {
1064
+ const body = OpenPathRequestSchema.parse(await readJsonBody(req));
1065
+ // Permit opening:
1066
+ // - any registered preparation root or its bound source folder
1067
+ // - the user method library at `~/.interf/methods/`
1068
+ // - the bundled built-in method root inside the installed package
1069
+ // (so SKILL.md / contract files in `interf-default` open correctly)
1070
+ const allowedRoots = [];
1071
+ for (const stored of listStoredPreparations()) {
1072
+ allowedRoots.push(stored.prepDataDir);
1073
+ if (stored.source.locator)
1074
+ allowedRoots.push(stored.source.locator);
1075
+ }
1076
+ allowedRoots.push(userMethodsRoot());
1077
+ allowedRoots.push(packageRoot()); // covers <pkg>/builtin-methods/...
1078
+ const openedPath = await openLocalPath(allowedRoots, body.path);
1079
+ sendJson(res, 202, { opened: true, path: openedPath });
1080
+ return true;
1081
+ }
1082
+ return false;
1083
+ }
1084
+ export function createLocalServiceServer(runtime) {
1085
+ return createServer((req, res) => {
1086
+ void (async () => {
1087
+ // Pre-attach a CORS context so static-asset GETs and the 404
1088
+ // fallback emit the right headers even before routeApi has a
1089
+ // chance to set its own. The OPTIONS preflight handling and the
1090
+ // mutating-method guards still happen inside routeApi.
1091
+ const origin = originHeaderValue(req);
1092
+ const allowed = buildAllowedOrigins(runtime.host, runtime.port);
1093
+ attachResponseContext(res, { cors: corsHeadersFor(origin, allowed) });
1094
+ try {
1095
+ const routed = await routeApi(req, res, runtime);
1096
+ if (routed)
1097
+ return;
1098
+ if (sendCompilerUiAsset(req, res, runtime))
1099
+ return;
1100
+ sendText(res, 404, "Not found.\n");
1101
+ }
1102
+ catch (error) {
1103
+ sendError(res, 500, error instanceof Error ? error.message : String(error));
1104
+ }
1105
+ })();
1106
+ });
1107
+ }
1108
+ function resolvePort(optionPort) {
1109
+ if (typeof optionPort === "number")
1110
+ return { port: optionPort, pinned: true };
1111
+ const envPort = Number.parseInt(process.env.INTERF_SERVICE_PORT ?? "", 10);
1112
+ if (Number.isInteger(envPort))
1113
+ return { port: envPort, pinned: true };
1114
+ return { port: LOCAL_SERVICE_DEFAULT_PORT, pinned: false };
1115
+ }
1116
+ const LOCAL_SERVICE_PORT_FALLBACK_RANGE = 50;
1117
+ async function listenWithFallback(server, host, startPort, pinned) {
1118
+ const maxPort = pinned ? startPort : startPort + LOCAL_SERVICE_PORT_FALLBACK_RANGE;
1119
+ for (let port = startPort; port <= maxPort; port += 1) {
1120
+ try {
1121
+ await new Promise((resolveListen, rejectListen) => {
1122
+ const onError = (error) => {
1123
+ server.off("listening", onListening);
1124
+ rejectListen(error);
1125
+ };
1126
+ const onListening = () => {
1127
+ server.off("error", onError);
1128
+ resolveListen();
1129
+ };
1130
+ server.once("error", onError);
1131
+ server.once("listening", onListening);
1132
+ server.listen(port, host);
1133
+ });
1134
+ return port;
1135
+ }
1136
+ catch (error) {
1137
+ const code = error?.code;
1138
+ if (code !== "EADDRINUSE" || port === maxPort)
1139
+ throw error;
1140
+ // Retry on the next port; the server will emit a fresh "listening" event on the next attempt.
1141
+ }
1142
+ }
1143
+ throw new Error(`Could not bind a free port between ${startPort} and ${maxPort}.`);
1144
+ }
1145
+ export async function startLocalService(options = {}) {
1146
+ const requestedPort = typeof options.searchFromPort === "number"
1147
+ ? { port: options.searchFromPort, pinned: false }
1148
+ : resolvePort(options.port);
1149
+ const requestedHost = options.host ?? process.env.INTERF_SERVICE_HOST ?? LOCAL_SERVICE_DEFAULT_HOST;
1150
+ // Reject non-loopback host bindings before we even configure anything.
1151
+ // This guards against `INTERF_SERVICE_HOST=0.0.0.0` env injection or a
1152
+ // call site that passes a LAN IP. The schema-level refinement also
1153
+ // rejects, but we want a clearer error message.
1154
+ if (!LOCAL_SERVICE_LOOPBACK_HOSTS.includes(requestedHost)) {
1155
+ throw new Error(`Refusing to bind local service to non-loopback host '${requestedHost}'. ` +
1156
+ `Use one of ${LOCAL_SERVICE_LOOPBACK_HOSTS.join(", ")} ` +
1157
+ `(adjust the --host flag or INTERF_SERVICE_HOST env var).`);
1158
+ }
1159
+ const config = LocalServiceConfigSchema.parse({
1160
+ host: requestedHost,
1161
+ port: requestedPort.port,
1162
+ });
1163
+ const rootPath = options.rootPath ?? process.cwd();
1164
+ const resolvedRoot = resolve(rootPath);
1165
+ // 0.13 auth-token policy: default to `null` (no token) on loopback. CORS
1166
+ // + loopback bind enforces the security boundary. Callers can opt in by
1167
+ // passing `"auto"` (generate fresh) or a pinned string (tests).
1168
+ const authToken = (() => {
1169
+ if (options.authToken === null)
1170
+ return null;
1171
+ if (options.authToken === "auto")
1172
+ return createLocalServiceAuthToken();
1173
+ if (typeof options.authToken === "string")
1174
+ return options.authToken;
1175
+ return null;
1176
+ })();
1177
+ const runtime = createLocalServiceRuntime({
1178
+ rootPath: resolvedRoot,
1179
+ host: config.host,
1180
+ port: config.port,
1181
+ packageVersion: options.packageVersion,
1182
+ handlers: options.handlers,
1183
+ ...(authToken ? { authToken } : {}),
1184
+ });
1185
+ // Rehydrate 0.13 preparations as synthetic workspaces so subsequent
1186
+ // compile / test / readiness calls find them after a service restart.
1187
+ try {
1188
+ rehydratePreparations(runtime);
1189
+ }
1190
+ catch {
1191
+ // best effort
1192
+ }
1193
+ const server = createLocalServiceServer(runtime);
1194
+ const boundPort = await listenWithFallback(server, config.host, config.port, requestedPort.pinned);
1195
+ runtime.setBoundPort(boundPort);
1196
+ const health = runtime.health();
1197
+ const url = health.service_url;
1198
+ const startedAt = health.instance_started_at ?? new Date().toISOString();
1199
+ // Central stop-lookup registry — one entry per running instance.
1200
+ // (Server-side write only; CLI no longer reads this.)
1201
+ const writeRegistryEntry = () => {
1202
+ try {
1203
+ registerServiceLocally(ServiceRegistryEntrySchema.parse({
1204
+ pid: process.pid,
1205
+ host: config.host,
1206
+ port: boundPort,
1207
+ url,
1208
+ started_at: startedAt,
1209
+ workspaces: runtime.registeredPreparationSnapshots(),
1210
+ ...(authToken ? { auth_token: authToken } : {}),
1211
+ }));
1212
+ }
1213
+ catch {
1214
+ // best effort: registry corruption shouldn't take the service down
1215
+ }
1216
+ };
1217
+ writeRegistryEntry();
1218
+ runtime.setOnRegistryChanged(writeRegistryEntry);
1219
+ // Write the 0.13 single-record CLI connection so `interf prep`,
1220
+ // `interf compile`, etc. can find this engine without a pointer file.
1221
+ try {
1222
+ writeConnection({ url, auth_token: authToken });
1223
+ }
1224
+ catch {
1225
+ // best effort: connection file is convenience, not correctness
1226
+ }
1227
+ return {
1228
+ runtime,
1229
+ server,
1230
+ url,
1231
+ close: () => new Promise((resolveClose, rejectClose) => {
1232
+ runtime.setOnRegistryChanged(null);
1233
+ server.close((error) => {
1234
+ if (error) {
1235
+ rejectClose(error);
1236
+ return;
1237
+ }
1238
+ try {
1239
+ unregisterService(process.pid);
1240
+ }
1241
+ catch {
1242
+ // best effort
1243
+ }
1244
+ try {
1245
+ // Clear connection record only if it still points at us.
1246
+ const current = readActiveConnection();
1247
+ if (current?.url === url)
1248
+ clearConnection();
1249
+ }
1250
+ catch {
1251
+ // best effort
1252
+ }
1253
+ resolveClose();
1254
+ });
1255
+ }),
1256
+ };
1257
+ }