jishushell 0.6.5 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1207) hide show
  1. package/apps/anythingllm-container.yaml +16 -170
  2. package/apps/browserless-chromium-container.yaml +16 -10
  3. package/apps/filebrowser-container.yaml +15 -9
  4. package/apps/hermes-container.yaml +20 -5
  5. package/apps/immich-container-lite.yaml +337 -0
  6. package/apps/immich-container.yaml +371 -0
  7. package/apps/jishu-kb-container.yaml +50 -177
  8. package/apps/ollama-binary.yaml +33 -28
  9. package/apps/ollama-cpu-container.yaml +6 -0
  10. package/apps/ollama-with-hollama-binary.yaml +35 -28
  11. package/apps/openclaw-binary.yaml +35 -15
  12. package/apps/openclaw-container.yaml +29 -11
  13. package/apps/openclaw-with-ollama-container.yaml +9 -2
  14. package/apps/openclaw-with-searxng-container.yaml +38 -6
  15. package/apps/searxng-container.yaml +31 -6
  16. package/apps/weknora-container.yaml +26 -21
  17. package/dependencies/jishushell-panel-0.7.3.tgz +0 -0
  18. package/dist/cli/app.js +244 -213
  19. package/dist/cli/app.js.map +1 -1
  20. package/dist/cli/backup.js +15 -12
  21. package/dist/cli/backup.js.map +1 -1
  22. package/dist/cli/core.d.ts +4 -3
  23. package/dist/cli/core.js +392 -227
  24. package/dist/cli/core.js.map +1 -1
  25. package/dist/cli/doctor.d.ts +1 -1
  26. package/dist/cli/doctor.js +113 -10
  27. package/dist/cli/doctor.js.map +1 -1
  28. package/dist/cli/job.js +62 -14
  29. package/dist/cli/job.js.map +1 -1
  30. package/dist/cli/llm.js +80 -11
  31. package/dist/cli/llm.js.map +1 -1
  32. package/dist/cli/managed-list.d.ts +1 -3
  33. package/dist/cli/managed-list.js +18 -16
  34. package/dist/cli/managed-list.js.map +1 -1
  35. package/dist/cli/migrate.d.ts +2 -0
  36. package/dist/cli/migrate.js +160 -0
  37. package/dist/cli/migrate.js.map +1 -0
  38. package/dist/cli.js +1 -0
  39. package/dist/cli.js.map +1 -1
  40. package/dist/config.d.ts +32 -20
  41. package/dist/config.js +132 -51
  42. package/dist/config.js.map +1 -1
  43. package/dist/control.d.ts +6 -6
  44. package/dist/control.js +31 -23
  45. package/dist/control.js.map +1 -1
  46. package/dist/core.d.ts +5 -5
  47. package/dist/core.js +5 -5
  48. package/dist/core.js.map +1 -1
  49. package/dist/install.d.ts +2 -2
  50. package/dist/install.js +78 -37
  51. package/dist/install.js.map +1 -1
  52. package/dist/routes/admin.d.ts +2 -0
  53. package/dist/routes/admin.js +72 -0
  54. package/dist/routes/admin.js.map +1 -0
  55. package/dist/routes/apps.d.ts +1 -1
  56. package/dist/routes/apps.js +101 -193
  57. package/dist/routes/apps.js.map +1 -1
  58. package/dist/routes/auth.js +1 -1
  59. package/dist/routes/auth.js.map +1 -1
  60. package/dist/routes/backup.js +1 -1
  61. package/dist/routes/backup.js.map +1 -1
  62. package/dist/routes/docker.d.ts +2 -0
  63. package/dist/routes/docker.js +58 -0
  64. package/dist/routes/docker.js.map +1 -0
  65. package/dist/routes/external-mounts.d.ts +1 -1
  66. package/dist/routes/external-mounts.js +1 -1
  67. package/dist/routes/external-mounts.js.map +1 -1
  68. package/dist/routes/file-mounts.d.ts +4 -3
  69. package/dist/routes/file-mounts.js +49 -31
  70. package/dist/routes/file-mounts.js.map +1 -1
  71. package/dist/routes/files-organize.d.ts +2 -2
  72. package/dist/routes/files-organize.js +5 -5
  73. package/dist/routes/files-organize.js.map +1 -1
  74. package/dist/routes/files.d.ts +1 -1
  75. package/dist/routes/files.js +1 -1
  76. package/dist/routes/files.js.map +1 -1
  77. package/dist/routes/instances.d.ts +0 -8
  78. package/dist/routes/instances.js +202 -1560
  79. package/dist/routes/instances.js.map +1 -1
  80. package/dist/routes/integration-apps.d.ts +14 -0
  81. package/dist/routes/integration-apps.js +81 -0
  82. package/dist/routes/integration-apps.js.map +1 -0
  83. package/dist/routes/integrations.d.ts +9 -0
  84. package/dist/routes/integrations.js +12 -0
  85. package/dist/routes/integrations.js.map +1 -0
  86. package/dist/routes/llm-proxy.js +26 -3
  87. package/dist/routes/llm-proxy.js.map +1 -1
  88. package/dist/routes/setup.js +53 -38
  89. package/dist/routes/setup.js.map +1 -1
  90. package/dist/routes/system.js +108 -68
  91. package/dist/routes/system.js.map +1 -1
  92. package/dist/routes/webdav.d.ts +1 -1
  93. package/dist/routes/webdav.js +2 -2
  94. package/dist/routes/webdav.js.map +1 -1
  95. package/dist/server.d.ts +6 -0
  96. package/dist/server.js +368 -233
  97. package/dist/server.js.map +1 -1
  98. package/dist/services/app-common/app-compiler.js +186 -0
  99. package/dist/services/app-common/app-compiler.js.map +1 -0
  100. package/dist/services/app-common/app-shared.d.ts +15 -0
  101. package/dist/services/app-common/app-shared.js +64 -0
  102. package/dist/services/app-common/app-shared.js.map +1 -0
  103. package/dist/services/app-common/capability-service.d.ts +45 -0
  104. package/dist/services/app-common/capability-service.js +331 -0
  105. package/dist/services/app-common/capability-service.js.map +1 -0
  106. package/dist/services/app-common/catalog-service.d.ts +59 -0
  107. package/dist/services/app-common/catalog-service.js +318 -0
  108. package/dist/services/app-common/catalog-service.js.map +1 -0
  109. package/dist/services/app-common/create-pipeline.d.ts +26 -0
  110. package/dist/services/app-common/create-pipeline.js +298 -0
  111. package/dist/services/app-common/create-pipeline.js.map +1 -0
  112. package/dist/services/app-common/delete-service.d.ts +5 -0
  113. package/dist/services/app-common/delete-service.js +109 -0
  114. package/dist/services/app-common/delete-service.js.map +1 -0
  115. package/dist/services/app-common/execution-owner.d.ts +23 -0
  116. package/dist/services/app-common/execution-owner.js +124 -0
  117. package/dist/services/app-common/execution-owner.js.map +1 -0
  118. package/dist/services/app-common/execution-service.d.ts +23 -0
  119. package/dist/services/app-common/execution-service.js +105 -0
  120. package/dist/services/app-common/execution-service.js.map +1 -0
  121. package/dist/services/app-common/id-normalizer.d.ts +31 -0
  122. package/dist/services/app-common/id-normalizer.js +83 -0
  123. package/dist/services/app-common/id-normalizer.js.map +1 -0
  124. package/dist/services/app-common/install-store.d.ts +34 -0
  125. package/dist/services/app-common/install-store.js +261 -0
  126. package/dist/services/app-common/install-store.js.map +1 -0
  127. package/dist/services/app-common/instance-store.d.ts +78 -0
  128. package/dist/services/app-common/instance-store.js +498 -0
  129. package/dist/services/app-common/instance-store.js.map +1 -0
  130. package/dist/services/app-common/integration-refs.d.ts +17 -0
  131. package/dist/services/app-common/integration-refs.js +47 -0
  132. package/dist/services/app-common/integration-refs.js.map +1 -0
  133. package/dist/services/app-common/lifecycle-pipeline.d.ts +62 -0
  134. package/dist/services/app-common/lifecycle-pipeline.js +317 -0
  135. package/dist/services/app-common/lifecycle-pipeline.js.map +1 -0
  136. package/dist/services/app-common/lifecycle-scripts.d.ts +38 -0
  137. package/dist/services/app-common/lifecycle-scripts.js +935 -0
  138. package/dist/services/app-common/lifecycle-scripts.js.map +1 -0
  139. package/dist/services/app-common/lifecycle-service.d.ts +68 -0
  140. package/dist/services/app-common/lifecycle-service.js +475 -0
  141. package/dist/services/app-common/lifecycle-service.js.map +1 -0
  142. package/dist/services/app-common/ownership.d.ts +3 -0
  143. package/dist/services/app-common/ownership.js +11 -0
  144. package/dist/services/app-common/ownership.js.map +1 -0
  145. package/dist/services/app-common/paths.d.ts +29 -0
  146. package/dist/services/app-common/paths.js +34 -0
  147. package/dist/services/app-common/paths.js.map +1 -0
  148. package/dist/services/app-common/platform-transform.d.ts +32 -0
  149. package/dist/services/app-common/platform-transform.js +65 -0
  150. package/dist/services/app-common/platform-transform.js.map +1 -0
  151. package/dist/services/app-common/provide-resolver.d.ts +29 -0
  152. package/dist/services/app-common/provide-resolver.js +129 -0
  153. package/dist/services/app-common/provide-resolver.js.map +1 -0
  154. package/dist/services/app-common/remote-spec.d.ts +14 -0
  155. package/dist/services/app-common/remote-spec.js +58 -0
  156. package/dist/services/app-common/remote-spec.js.map +1 -0
  157. package/dist/services/app-common/runtime-builder.d.ts +1 -0
  158. package/dist/services/app-common/runtime-builder.js +2 -0
  159. package/dist/services/app-common/runtime-builder.js.map +1 -0
  160. package/dist/services/app-common/runtime-facts.d.ts +19 -0
  161. package/dist/services/app-common/runtime-facts.js +128 -0
  162. package/dist/services/app-common/runtime-facts.js.map +1 -0
  163. package/dist/services/app-common/service.d.ts +9 -0
  164. package/dist/services/app-common/service.js +10 -0
  165. package/dist/services/app-common/service.js.map +1 -0
  166. package/dist/services/app-common/spec-materializer.d.ts +8 -0
  167. package/dist/services/app-common/spec-materializer.js +295 -0
  168. package/dist/services/app-common/spec-materializer.js.map +1 -0
  169. package/dist/services/app-common/status-refresh.d.ts +33 -0
  170. package/dist/services/app-common/status-refresh.js +771 -0
  171. package/dist/services/app-common/status-refresh.js.map +1 -0
  172. package/dist/services/app-common/task-service.d.ts +29 -0
  173. package/dist/services/app-common/task-service.js +93 -0
  174. package/dist/services/app-common/task-service.js.map +1 -0
  175. package/dist/services/app-common/terminal-session-manager.js +157 -0
  176. package/dist/services/app-common/terminal-session-manager.js.map +1 -0
  177. package/dist/services/app-modules/browserless/routes.d.ts +9 -0
  178. package/dist/services/app-modules/browserless/routes.js +519 -0
  179. package/dist/services/app-modules/browserless/routes.js.map +1 -0
  180. package/dist/services/app-modules/routes.d.ts +2 -0
  181. package/dist/services/app-modules/routes.js +5 -0
  182. package/dist/services/app-modules/routes.js.map +1 -0
  183. package/dist/services/backup/backup-admin.d.ts +95 -0
  184. package/dist/services/backup/backup-admin.js +246 -0
  185. package/dist/services/backup/backup-admin.js.map +1 -0
  186. package/dist/services/backup/backup-manager.d.ts +264 -0
  187. package/dist/services/backup/backup-manager.js +2318 -0
  188. package/dist/services/backup/backup-manager.js.map +1 -0
  189. package/dist/services/backup/backup-verify.js +240 -0
  190. package/dist/services/backup/backup-verify.js.map +1 -0
  191. package/dist/services/capabilities/browser-policy.d.ts +14 -0
  192. package/dist/services/capabilities/browser-policy.js +141 -0
  193. package/dist/services/capabilities/browser-policy.js.map +1 -0
  194. package/dist/services/capabilities/contract.d.ts +49 -0
  195. package/dist/services/capabilities/contract.js +119 -0
  196. package/dist/services/capabilities/contract.js.map +1 -0
  197. package/dist/services/capabilities/endpoint-validator.d.ts +42 -0
  198. package/dist/services/capabilities/endpoint-validator.js +113 -0
  199. package/dist/services/capabilities/endpoint-validator.js.map +1 -0
  200. package/dist/services/capabilities/health.d.ts +16 -0
  201. package/dist/services/capabilities/health.js +121 -0
  202. package/dist/services/capabilities/health.js.map +1 -0
  203. package/dist/services/capabilities/registry.d.ts +56 -0
  204. package/dist/services/capabilities/registry.js +222 -0
  205. package/dist/services/capabilities/registry.js.map +1 -0
  206. package/dist/services/capabilities/sync.d.ts +7 -0
  207. package/dist/services/capabilities/sync.js +223 -0
  208. package/dist/services/capabilities/sync.js.map +1 -0
  209. package/dist/services/capability-proxy/html-rewriters/browserless.d.ts +1 -0
  210. package/dist/services/capability-proxy/html-rewriters/browserless.js +83 -0
  211. package/dist/services/capability-proxy/html-rewriters/browserless.js.map +1 -0
  212. package/dist/services/capability-proxy/html-rewriters/index.d.ts +12 -0
  213. package/dist/services/capability-proxy/html-rewriters/index.js +25 -0
  214. package/dist/services/capability-proxy/html-rewriters/index.js.map +1 -0
  215. package/dist/services/capability-proxy/html-rewriters/jishukb.d.ts +1 -0
  216. package/dist/services/capability-proxy/html-rewriters/jishukb.js +161 -0
  217. package/dist/services/capability-proxy/html-rewriters/jishukb.js.map +1 -0
  218. package/dist/services/capability-proxy/http.d.ts +7 -0
  219. package/dist/services/capability-proxy/http.js +555 -0
  220. package/dist/services/capability-proxy/http.js.map +1 -0
  221. package/dist/services/capability-proxy/terminal.d.ts +4 -0
  222. package/dist/services/capability-proxy/terminal.js +179 -0
  223. package/dist/services/capability-proxy/terminal.js.map +1 -0
  224. package/dist/services/connections/admin.d.ts +80 -0
  225. package/dist/services/connections/admin.js +337 -0
  226. package/dist/services/connections/admin.js.map +1 -0
  227. package/dist/services/connections/apply.d.ts +104 -0
  228. package/dist/services/connections/apply.js +415 -0
  229. package/dist/services/connections/apply.js.map +1 -0
  230. package/dist/services/connections/resolver.d.ts +82 -0
  231. package/dist/services/connections/resolver.js +289 -0
  232. package/dist/services/connections/resolver.js.map +1 -0
  233. package/dist/services/connections/transactor.d.ts +39 -0
  234. package/dist/services/connections/transactor.js +307 -0
  235. package/dist/services/connections/transactor.js.map +1 -0
  236. package/dist/services/files/bootstrap.d.ts +7 -0
  237. package/dist/services/files/bootstrap.js +16 -0
  238. package/dist/services/files/bootstrap.js.map +1 -0
  239. package/dist/services/files/external-mounts.js +187 -0
  240. package/dist/services/files/external-mounts.js.map +1 -0
  241. package/dist/services/files/files-manager.d.ts +265 -0
  242. package/dist/services/files/files-manager.js +1189 -0
  243. package/dist/services/files/files-manager.js.map +1 -0
  244. package/dist/services/files/files-mounts.d.ts +42 -0
  245. package/dist/services/files/files-mounts.js +207 -0
  246. package/dist/services/files/files-mounts.js.map +1 -0
  247. package/dist/services/files/organize/applier.js +218 -0
  248. package/dist/services/files/organize/applier.js.map +1 -0
  249. package/dist/services/files/organize/rules.js +286 -0
  250. package/dist/services/files/organize/rules.js.map +1 -0
  251. package/dist/services/files/organize/scanner.js +366 -0
  252. package/dist/services/files/organize/scanner.js.map +1 -0
  253. package/dist/services/files/organize/store.js +82 -0
  254. package/dist/services/files/organize/store.js.map +1 -0
  255. package/dist/services/files/photos/upload-page.d.ts +2 -0
  256. package/dist/services/files/photos/upload-page.js +248 -0
  257. package/dist/services/files/photos/upload-page.js.map +1 -0
  258. package/dist/services/files/photos/upload-store.d.ts +74 -0
  259. package/dist/services/files/photos/upload-store.js +432 -0
  260. package/dist/services/files/photos/upload-store.js.map +1 -0
  261. package/dist/services/files/webdav/server.d.ts +47 -0
  262. package/dist/services/files/webdav/server.js +329 -0
  263. package/dist/services/files/webdav/server.js.map +1 -0
  264. package/dist/services/files/webdav/xml-builder.js.map +1 -0
  265. package/dist/services/http/proxy-utils.d.ts +7 -0
  266. package/dist/services/http/proxy-utils.js +29 -0
  267. package/dist/services/http/proxy-utils.js.map +1 -0
  268. package/dist/services/http/request-utils.d.ts +3 -0
  269. package/dist/services/http/request-utils.js +23 -0
  270. package/dist/services/http/request-utils.js.map +1 -0
  271. package/dist/services/instances/admin.d.ts +23 -0
  272. package/dist/services/instances/admin.js +218 -0
  273. package/dist/services/instances/admin.js.map +1 -0
  274. package/dist/services/instances/clone.d.ts +26 -0
  275. package/dist/services/instances/clone.js +78 -0
  276. package/dist/services/instances/clone.js.map +1 -0
  277. package/dist/services/instances/config-admin.d.ts +17 -0
  278. package/dist/services/instances/config-admin.js +181 -0
  279. package/dist/services/instances/config-admin.js.map +1 -0
  280. package/dist/services/instances/manager.d.ts +232 -0
  281. package/dist/services/instances/manager.js +1342 -0
  282. package/dist/services/instances/manager.js.map +1 -0
  283. package/dist/services/instances/pairing.d.ts +17 -0
  284. package/dist/services/instances/pairing.js +53 -0
  285. package/dist/services/instances/pairing.js.map +1 -0
  286. package/dist/services/instances/passwords.js +173 -0
  287. package/dist/services/instances/passwords.js.map +1 -0
  288. package/dist/services/instances/status.d.ts +2 -0
  289. package/dist/services/instances/status.js +11 -0
  290. package/dist/services/instances/status.js.map +1 -0
  291. package/dist/services/instances/types.d.ts +21 -0
  292. package/dist/services/instances/types.js +2 -0
  293. package/dist/services/instances/types.js.map +1 -0
  294. package/dist/services/integrations/anythingllm/integration.d.ts +25 -0
  295. package/dist/services/integrations/anythingllm/integration.js +251 -0
  296. package/dist/services/integrations/anythingllm/integration.js.map +1 -0
  297. package/dist/services/integrations/catalog.d.ts +3 -0
  298. package/dist/services/integrations/catalog.js +73 -0
  299. package/dist/services/integrations/catalog.js.map +1 -0
  300. package/dist/services/integrations/custom/integration.d.ts +28 -0
  301. package/dist/services/integrations/custom/integration.js +179 -0
  302. package/dist/services/integrations/custom/integration.js.map +1 -0
  303. package/dist/services/integrations/hermes/integration.d.ts +194 -0
  304. package/dist/services/integrations/hermes/integration.js +1668 -0
  305. package/dist/services/integrations/hermes/integration.js.map +1 -0
  306. package/dist/services/integrations/immich/client.d.ts +93 -0
  307. package/dist/services/integrations/immich/client.js +458 -0
  308. package/dist/services/integrations/immich/client.js.map +1 -0
  309. package/dist/services/integrations/immich/config.d.ts +15 -0
  310. package/dist/services/integrations/immich/config.js +178 -0
  311. package/dist/services/integrations/immich/config.js.map +1 -0
  312. package/dist/services/integrations/immich/discovery.d.ts +9 -0
  313. package/dist/services/integrations/immich/discovery.js +101 -0
  314. package/dist/services/integrations/immich/discovery.js.map +1 -0
  315. package/dist/services/integrations/immich/gallery-renderer.d.ts +5 -0
  316. package/dist/services/integrations/immich/gallery-renderer.js +150 -0
  317. package/dist/services/integrations/immich/gallery-renderer.js.map +1 -0
  318. package/dist/services/integrations/immich/immich-shim.d.ts +11 -0
  319. package/dist/services/integrations/immich/immich-shim.js +439 -0
  320. package/dist/services/integrations/immich/immich-shim.js.map +1 -0
  321. package/dist/services/integrations/immich/integration.d.ts +18 -0
  322. package/dist/services/integrations/immich/integration.js +64 -0
  323. package/dist/services/integrations/immich/integration.js.map +1 -0
  324. package/dist/services/integrations/immich/photo-library.d.ts +4 -0
  325. package/dist/services/integrations/immich/photo-library.js +63 -0
  326. package/dist/services/integrations/immich/photo-library.js.map +1 -0
  327. package/dist/services/integrations/immich/review-executor.d.ts +3 -0
  328. package/dist/services/integrations/immich/review-executor.js +41 -0
  329. package/dist/services/integrations/immich/review-executor.js.map +1 -0
  330. package/dist/services/integrations/immich/review-session-service.d.ts +27 -0
  331. package/dist/services/integrations/immich/review-session-service.js +206 -0
  332. package/dist/services/integrations/immich/review-session-service.js.map +1 -0
  333. package/dist/services/integrations/immich/review-store.d.ts +47 -0
  334. package/dist/services/integrations/immich/review-store.js +347 -0
  335. package/dist/services/integrations/immich/review-store.js.map +1 -0
  336. package/dist/services/integrations/immich/routes.d.ts +7 -0
  337. package/dist/services/integrations/immich/routes.js +363 -0
  338. package/dist/services/integrations/immich/routes.js.map +1 -0
  339. package/dist/services/integrations/immich/types.d.ts +186 -0
  340. package/dist/services/integrations/immich/types.js +2 -0
  341. package/dist/services/integrations/immich/types.js.map +1 -0
  342. package/dist/services/integrations/index.d.ts +41 -0
  343. package/dist/services/integrations/index.js +60 -0
  344. package/dist/services/integrations/index.js.map +1 -0
  345. package/dist/services/integrations/installable/catalog.d.ts +33 -0
  346. package/dist/services/integrations/installable/catalog.js +88 -0
  347. package/dist/services/integrations/installable/catalog.js.map +1 -0
  348. package/dist/services/integrations/installable/index.d.ts +35 -0
  349. package/dist/services/integrations/installable/index.js +170 -0
  350. package/dist/services/integrations/installable/index.js.map +1 -0
  351. package/dist/services/integrations/installable/installers/integration-probes.d.ts +50 -0
  352. package/dist/services/integrations/installable/installers/integration-probes.js +231 -0
  353. package/dist/services/integrations/installable/installers/integration-probes.js.map +1 -0
  354. package/dist/services/integrations/installable/installers/integration.d.ts +30 -0
  355. package/dist/services/integrations/installable/installers/integration.js +283 -0
  356. package/dist/services/integrations/installable/installers/integration.js.map +1 -0
  357. package/dist/services/integrations/installable/installers/registry-probe.js.map +1 -0
  358. package/dist/services/integrations/installable/installers/shell-script.d.ts +46 -0
  359. package/dist/services/integrations/installable/installers/shell-script.js +487 -0
  360. package/dist/services/integrations/installable/installers/shell-script.js.map +1 -0
  361. package/dist/services/integrations/installable/types.d.ts +130 -0
  362. package/dist/services/integrations/installable/types.js +19 -0
  363. package/dist/services/integrations/installable/types.js.map +1 -0
  364. package/dist/services/integrations/jishukb/integration.d.ts +24 -0
  365. package/dist/services/integrations/jishukb/integration.js +300 -0
  366. package/dist/services/integrations/jishukb/integration.js.map +1 -0
  367. package/dist/services/integrations/openclaw/anythingllm-shim.d.ts +46 -0
  368. package/dist/services/integrations/openclaw/anythingllm-shim.js +281 -0
  369. package/dist/services/integrations/openclaw/anythingllm-shim.js.map +1 -0
  370. package/dist/services/integrations/openclaw/drive-shim.js +490 -0
  371. package/dist/services/integrations/openclaw/drive-shim.js.map +1 -0
  372. package/dist/services/integrations/openclaw/integration.d.ts +438 -0
  373. package/dist/services/integrations/openclaw/integration.js +4629 -0
  374. package/dist/services/integrations/openclaw/integration.js.map +1 -0
  375. package/dist/services/integrations/openclaw/jishukb-native-mcp.d.ts +58 -0
  376. package/dist/services/integrations/openclaw/jishukb-native-mcp.js +373 -0
  377. package/dist/services/integrations/openclaw/jishukb-native-mcp.js.map +1 -0
  378. package/dist/services/integrations/openclaw/jishukb-shim.d.ts +52 -0
  379. package/dist/services/integrations/openclaw/jishukb-shim.js +1357 -0
  380. package/dist/services/integrations/openclaw/jishukb-shim.js.map +1 -0
  381. package/dist/services/integrations/openclaw/mcporter-lite.js +276 -0
  382. package/dist/services/integrations/openclaw/mcporter-lite.js.map +1 -0
  383. package/dist/services/integrations/openclaw/mcporter.d.ts +59 -0
  384. package/dist/services/integrations/openclaw/mcporter.js +143 -0
  385. package/dist/services/integrations/openclaw/mcporter.js.map +1 -0
  386. package/dist/services/integrations/openclaw/native-mcp.d.ts +48 -0
  387. package/dist/services/integrations/openclaw/native-mcp.js +125 -0
  388. package/dist/services/integrations/openclaw/native-mcp.js.map +1 -0
  389. package/dist/services/integrations/openclaw/routes.d.ts +21 -0
  390. package/dist/services/integrations/openclaw/routes.js +1194 -0
  391. package/dist/services/integrations/openclaw/routes.js.map +1 -0
  392. package/dist/services/integrations/registry.d.ts +17 -0
  393. package/dist/services/integrations/registry.js +36 -0
  394. package/dist/services/integrations/registry.js.map +1 -0
  395. package/dist/services/integrations/routes.d.ts +2 -0
  396. package/dist/services/integrations/routes.js +9 -0
  397. package/dist/services/integrations/routes.js.map +1 -0
  398. package/dist/services/integrations/types.d.ts +457 -0
  399. package/dist/services/integrations/types.js +2 -0
  400. package/dist/services/integrations/types.js.map +1 -0
  401. package/dist/services/legacy-migrator/classifier.d.ts +44 -0
  402. package/dist/services/legacy-migrator/classifier.js +309 -0
  403. package/dist/services/legacy-migrator/classifier.js.map +1 -0
  404. package/dist/services/legacy-migrator/executor.d.ts +42 -0
  405. package/dist/services/legacy-migrator/executor.js +637 -0
  406. package/dist/services/legacy-migrator/executor.js.map +1 -0
  407. package/dist/services/legacy-migrator/index.d.ts +31 -0
  408. package/dist/services/legacy-migrator/index.js +34 -0
  409. package/dist/services/legacy-migrator/index.js.map +1 -0
  410. package/dist/services/legacy-migrator/planner.d.ts +8 -0
  411. package/dist/services/legacy-migrator/planner.js +154 -0
  412. package/dist/services/legacy-migrator/planner.js.map +1 -0
  413. package/dist/services/legacy-migrator/provider-settings.d.ts +6 -0
  414. package/dist/services/legacy-migrator/provider-settings.js +72 -0
  415. package/dist/services/legacy-migrator/provider-settings.js.map +1 -0
  416. package/dist/services/legacy-migrator/report.d.ts +9 -0
  417. package/dist/services/legacy-migrator/report.js +99 -0
  418. package/dist/services/legacy-migrator/report.js.map +1 -0
  419. package/dist/services/legacy-migrator/scanner.d.ts +13 -0
  420. package/dist/services/legacy-migrator/scanner.js +157 -0
  421. package/dist/services/legacy-migrator/scanner.js.map +1 -0
  422. package/dist/services/legacy-migrator/types.d.ts +97 -0
  423. package/dist/services/legacy-migrator/types.js +23 -0
  424. package/dist/services/legacy-migrator/types.js.map +1 -0
  425. package/dist/services/llm-proxy/instance-proxy.d.ts +17 -1
  426. package/dist/services/llm-proxy/instance-proxy.js +171 -44
  427. package/dist/services/llm-proxy/instance-proxy.js.map +1 -1
  428. package/dist/services/llm-proxy/probe.js +5 -14
  429. package/dist/services/llm-proxy/probe.js.map +1 -1
  430. package/dist/services/llm-proxy/providers.js +23 -31
  431. package/dist/services/llm-proxy/providers.js.map +1 -1
  432. package/dist/services/llm-proxy/ssrf.d.ts +11 -4
  433. package/dist/services/llm-proxy/ssrf.js +45 -7
  434. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  435. package/dist/services/llm-proxy/validate-key.js +16 -37
  436. package/dist/services/llm-proxy/validate-key.js.map +1 -1
  437. package/dist/services/repair/runtime-repair.d.ts +22 -0
  438. package/dist/services/repair/runtime-repair.js +374 -0
  439. package/dist/services/repair/runtime-repair.js.map +1 -0
  440. package/dist/services/runtime/docker-network.d.ts +8 -0
  441. package/dist/services/runtime/docker-network.js +123 -0
  442. package/dist/services/runtime/docker-network.js.map +1 -0
  443. package/dist/services/runtime/driver-registry.d.ts +25 -0
  444. package/dist/services/runtime/driver-registry.js +22 -0
  445. package/dist/services/runtime/driver-registry.js.map +1 -0
  446. package/dist/services/runtime/drivers/nomad.d.ts +261 -0
  447. package/dist/services/runtime/drivers/nomad.js +3122 -0
  448. package/dist/services/runtime/drivers/nomad.js.map +1 -0
  449. package/dist/services/runtime/errors.d.ts +3 -3
  450. package/dist/services/runtime/errors.js +3 -3
  451. package/dist/services/runtime/instance.d.ts +14 -16
  452. package/dist/services/runtime/instance.js +93 -123
  453. package/dist/services/runtime/instance.js.map +1 -1
  454. package/dist/services/runtime/job-id.d.ts +1 -1
  455. package/dist/services/runtime/job-id.js +1 -1
  456. package/dist/services/runtime/mcp-shims/firewall.d.ts +2 -2
  457. package/dist/services/runtime/mcp-shims/firewall.js +2 -2
  458. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +3 -5
  459. package/dist/services/runtime/mcp-shims/searxng-shim.js +3 -5
  460. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -1
  461. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +20 -20
  462. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +16 -16
  463. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -1
  464. package/dist/services/runtime/ownership-marker.d.ts +83 -0
  465. package/dist/services/runtime/ownership-marker.js +109 -0
  466. package/dist/services/runtime/ownership-marker.js.map +1 -0
  467. package/dist/services/runtime/service-manager.d.ts +2 -0
  468. package/dist/services/runtime/service-manager.js +18 -0
  469. package/dist/services/runtime/service-manager.js.map +1 -0
  470. package/dist/services/runtime/types.d.ts +23 -501
  471. package/dist/services/runtime/types.js +0 -12
  472. package/dist/services/runtime/types.js.map +1 -1
  473. package/dist/services/runtime/workload-compiler.d.ts +17 -0
  474. package/dist/services/runtime/workload-compiler.js +550 -0
  475. package/dist/services/runtime/workload-compiler.js.map +1 -0
  476. package/dist/services/runtime/workload-types.d.ts +11 -0
  477. package/dist/services/runtime/workload-types.js +2 -0
  478. package/dist/services/runtime/workload-types.js.map +1 -0
  479. package/dist/services/setup/core-manager.d.ts +50 -0
  480. package/dist/services/setup/core-manager.js +456 -0
  481. package/dist/services/setup/core-manager.js.map +1 -0
  482. package/dist/services/setup/plugin-installer.js +136 -0
  483. package/dist/services/setup/plugin-installer.js.map +1 -0
  484. package/dist/services/setup/setup-manager.d.ts +158 -0
  485. package/dist/services/setup/setup-manager.js +2724 -0
  486. package/dist/services/setup/setup-manager.js.map +1 -0
  487. package/dist/services/system/cli-command.d.ts +5 -0
  488. package/dist/services/system/cli-command.js +18 -0
  489. package/dist/services/system/cli-command.js.map +1 -0
  490. package/dist/services/system/macos-launchd.js +312 -0
  491. package/dist/services/system/macos-launchd.js.map +1 -0
  492. package/dist/services/system/repair-orchestrator.d.ts +71 -0
  493. package/dist/services/system/repair-orchestrator.js +412 -0
  494. package/dist/services/system/repair-orchestrator.js.map +1 -0
  495. package/dist/services/system/runtime-ownership.d.ts +36 -0
  496. package/dist/services/system/runtime-ownership.js +250 -0
  497. package/dist/services/system/runtime-ownership.js.map +1 -0
  498. package/dist/services/system/system-monitor.js +96 -0
  499. package/dist/services/system/system-monitor.js.map +1 -0
  500. package/dist/services/system/system-ollama-provider.d.ts +14 -0
  501. package/dist/services/system/system-ollama-provider.js +129 -0
  502. package/dist/services/system/system-ollama-provider.js.map +1 -0
  503. package/dist/services/system/system-reconciler.d.ts +59 -0
  504. package/dist/services/system/system-reconciler.js +763 -0
  505. package/dist/services/system/system-reconciler.js.map +1 -0
  506. package/dist/services/system/update-manager.d.ts +43 -0
  507. package/dist/services/system/update-manager.js +315 -0
  508. package/dist/services/system/update-manager.js.map +1 -0
  509. package/dist/services/system/upgrade-finalize.d.ts +80 -0
  510. package/dist/services/system/upgrade-finalize.js +507 -0
  511. package/dist/services/system/upgrade-finalize.js.map +1 -0
  512. package/dist/services/tasks/registry.d.ts +44 -0
  513. package/dist/services/tasks/registry.js +90 -0
  514. package/dist/services/tasks/registry.js.map +1 -0
  515. package/dist/services/telemetry/activation.d.ts +6 -2
  516. package/dist/services/telemetry/activation.js +6 -2
  517. package/dist/services/telemetry/activation.js.map +1 -1
  518. package/dist/services/telemetry/heartbeat.d.ts +6 -2
  519. package/dist/services/telemetry/heartbeat.js +6 -2
  520. package/dist/services/telemetry/heartbeat.js.map +1 -1
  521. package/dist/services/workspaces/builder.d.ts +29 -0
  522. package/dist/services/workspaces/builder.js +186 -0
  523. package/dist/services/workspaces/builder.js.map +1 -0
  524. package/dist/types.d.ts +350 -48
  525. package/dist/utils/instance-lock.d.ts +2 -2
  526. package/dist/utils/instance-lock.js +2 -2
  527. package/dist/utils/path-safety.js +1 -1
  528. package/dist/utils/service-user.d.ts +13 -0
  529. package/dist/utils/service-user.js +129 -0
  530. package/dist/utils/service-user.js.map +1 -0
  531. package/install/jishu-install.sh +107 -27
  532. package/install/jishu-uninstall.sh +8 -0
  533. package/install/post-install.sh +162 -185
  534. package/install/post-uninstall.sh +6 -0
  535. package/node_modules/@fastify/static/.github/workflows/ci.yml +1 -1
  536. package/node_modules/@fastify/static/.github/workflows/lock-threads.yml +19 -0
  537. package/node_modules/@fastify/static/LICENSE +1 -3
  538. package/node_modules/@fastify/static/example/server-benchmark.js +39 -0
  539. package/node_modules/@fastify/static/index.js +169 -23
  540. package/node_modules/@fastify/static/lib/dirList.js +20 -6
  541. package/node_modules/@fastify/static/package.json +10 -8
  542. package/node_modules/@fastify/static/test/dir-list.test.js +82 -0
  543. package/node_modules/@fastify/static/test/static.test.js +326 -4
  544. package/node_modules/@fastify/static/types/index.d.ts +0 -4
  545. package/node_modules/@fastify/static/types/index.test-d.ts +1 -1
  546. package/node_modules/brace-expansion/dist/commonjs/index.js +24 -14
  547. package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
  548. package/node_modules/brace-expansion/dist/esm/index.js +24 -14
  549. package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
  550. package/node_modules/brace-expansion/package.json +2 -2
  551. package/node_modules/content-disposition/README.md +21 -22
  552. package/node_modules/content-disposition/index.js +122 -44
  553. package/node_modules/content-disposition/package.json +16 -20
  554. package/node_modules/fast-uri/index.js +1 -1
  555. package/node_modules/fast-uri/package.json +1 -1
  556. package/node_modules/fast-uri/test/security.test.js +28 -0
  557. package/node_modules/fastify/SECURITY.md +1 -1
  558. package/node_modules/fastify/SPONSORS.md +6 -4
  559. package/node_modules/fastify/docs/Guides/Database.md +0 -28
  560. package/node_modules/fastify/docs/Guides/Ecosystem.md +13 -2
  561. package/node_modules/fastify/docs/Guides/Serverless.md +2 -2
  562. package/node_modules/fastify/docs/Guides/Write-Plugin.md +1 -1
  563. package/node_modules/fastify/docs/Reference/Encapsulation.md +27 -26
  564. package/node_modules/fastify/docs/Reference/Errors.md +10 -4
  565. package/node_modules/fastify/docs/Reference/HTTP2.md +10 -10
  566. package/node_modules/fastify/docs/Reference/Hooks.md +4 -4
  567. package/node_modules/fastify/docs/Reference/Index.md +14 -16
  568. package/node_modules/fastify/docs/Reference/LTS.md +12 -13
  569. package/node_modules/fastify/docs/Reference/Lifecycle.md +9 -8
  570. package/node_modules/fastify/docs/Reference/Logging.md +44 -39
  571. package/node_modules/fastify/docs/Reference/Middleware.md +21 -25
  572. package/node_modules/fastify/docs/Reference/Principles.md +2 -2
  573. package/node_modules/fastify/docs/Reference/Reply.md +6 -1
  574. package/node_modules/fastify/docs/Reference/Request.md +27 -16
  575. package/node_modules/fastify/docs/Reference/Routes.md +5 -2
  576. package/node_modules/fastify/docs/Reference/Server.md +31 -3
  577. package/node_modules/fastify/docs/Reference/Type-Providers.md +29 -5
  578. package/node_modules/fastify/docs/Reference/Validation-and-Serialization.md +15 -2
  579. package/node_modules/fastify/docs/Reference/Warnings.md +7 -6
  580. package/node_modules/fastify/eslint.config.js +7 -2
  581. package/node_modules/fastify/fastify.d.ts +8 -3
  582. package/node_modules/fastify/fastify.js +43 -14
  583. package/node_modules/fastify/lib/content-type-parser.js +13 -1
  584. package/node_modules/fastify/lib/decorate.js +11 -3
  585. package/node_modules/fastify/lib/error-handler.js +4 -3
  586. package/node_modules/fastify/lib/error-serializer.js +59 -59
  587. package/node_modules/fastify/lib/errors.js +16 -1
  588. package/node_modules/fastify/lib/four-oh-four.js +14 -9
  589. package/node_modules/fastify/lib/handle-request.js +11 -5
  590. package/node_modules/fastify/lib/plugin-override.js +2 -1
  591. package/node_modules/fastify/lib/plugin-utils.js +5 -5
  592. package/node_modules/fastify/lib/reply.js +63 -8
  593. package/node_modules/fastify/lib/request.js +14 -4
  594. package/node_modules/fastify/lib/route.js +20 -6
  595. package/node_modules/fastify/lib/schema-controller.js +1 -1
  596. package/node_modules/fastify/lib/schemas.js +37 -30
  597. package/node_modules/fastify/lib/symbols.js +3 -1
  598. package/node_modules/fastify/lib/validation.js +1 -13
  599. package/node_modules/fastify/lib/warnings.js +3 -3
  600. package/node_modules/fastify/package.json +13 -15
  601. package/node_modules/fastify/scripts/validate-ecosystem-links.js +1 -0
  602. package/node_modules/fastify/test/bundler/esbuild/package.json +1 -1
  603. package/node_modules/fastify/test/close-pipelining.test.js +1 -2
  604. package/node_modules/fastify/test/custom-http-server.test.js +38 -0
  605. package/node_modules/fastify/test/decorator-instance-properties.test.js +63 -0
  606. package/node_modules/fastify/test/diagnostics-channel/async-error-handler.test.js +74 -0
  607. package/node_modules/fastify/test/hooks.test.js +23 -0
  608. package/node_modules/fastify/test/http-methods/get.test.js +1 -1
  609. package/node_modules/fastify/test/http2/plain.test.js +135 -0
  610. package/node_modules/fastify/test/http2/secure-with-fallback.test.js +1 -1
  611. package/node_modules/fastify/test/https/https.test.js +1 -2
  612. package/node_modules/fastify/test/internals/errors.test.js +31 -1
  613. package/node_modules/fastify/test/internals/plugin.test.js +3 -1
  614. package/node_modules/fastify/test/internals/request.test.js +27 -3
  615. package/node_modules/fastify/test/internals/schema-controller-perf.test.js +33 -0
  616. package/node_modules/fastify/test/logger/logging.test.js +18 -1
  617. package/node_modules/fastify/test/logger/options.test.js +38 -1
  618. package/node_modules/fastify/test/reply-error.test.js +1 -1
  619. package/node_modules/fastify/test/reply-trailers.test.js +70 -0
  620. package/node_modules/fastify/test/request-media-type.test.js +105 -0
  621. package/node_modules/fastify/test/route-prefix.test.js +34 -0
  622. package/node_modules/fastify/test/router-options.test.js +222 -11
  623. package/node_modules/fastify/test/schema-serialization.test.js +108 -0
  624. package/node_modules/fastify/test/schema-validation.test.js +24 -0
  625. package/node_modules/fastify/test/scripts/validate-ecosystem-links.test.js +40 -57
  626. package/node_modules/fastify/test/throw.test.js +14 -0
  627. package/node_modules/fastify/test/trust-proxy.test.js +21 -0
  628. package/node_modules/fastify/test/types/content-type-parser.tst.ts +70 -0
  629. package/node_modules/fastify/test/types/decorate-request-reply.tst.ts +18 -0
  630. package/node_modules/fastify/test/types/dummy-plugin.mts +9 -0
  631. package/node_modules/fastify/test/types/errors.tst.ts +91 -0
  632. package/node_modules/fastify/test/types/fastify.tst.ts +351 -0
  633. package/node_modules/fastify/test/types/hooks.tst.ts +578 -0
  634. package/node_modules/fastify/test/types/instance.tst.ts +597 -0
  635. package/node_modules/fastify/test/types/logger.tst.ts +276 -0
  636. package/node_modules/fastify/test/types/plugin.tst.ts +96 -0
  637. package/node_modules/fastify/test/types/register.tst.ts +245 -0
  638. package/node_modules/fastify/test/types/reply.tst.ts +297 -0
  639. package/node_modules/fastify/test/types/request.tst.ts +199 -0
  640. package/node_modules/fastify/test/types/route.tst.ts +576 -0
  641. package/node_modules/fastify/test/types/schema.tst.ts +135 -0
  642. package/node_modules/fastify/test/types/serverFactory.tst.ts +37 -0
  643. package/node_modules/fastify/test/types/tsconfig.json +9 -0
  644. package/node_modules/fastify/test/types/type-provider.tst.ts +1219 -0
  645. package/node_modules/fastify/test/types/using.tst.ts +14 -0
  646. package/node_modules/fastify/types/errors.d.ts +3 -0
  647. package/node_modules/fastify/types/request.d.ts +23 -2
  648. package/node_modules/glob/README.md +39 -130
  649. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  650. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  651. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  652. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  653. package/node_modules/glob/dist/commonjs/index.min.js +4 -0
  654. package/node_modules/glob/dist/commonjs/index.min.js.map +7 -0
  655. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  656. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  657. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  658. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  659. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  660. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  661. package/node_modules/glob/dist/esm/glob.js +2 -1
  662. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  663. package/node_modules/glob/dist/esm/index.min.js +4 -0
  664. package/node_modules/glob/dist/esm/index.min.js.map +7 -0
  665. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  666. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  667. package/node_modules/glob/dist/esm/pattern.js +4 -0
  668. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  669. package/node_modules/glob/package.json +38 -37
  670. package/node_modules/jishushell-panel/README.md +4 -4
  671. package/node_modules/jishushell-panel/output/dist/server.js +17 -6
  672. package/node_modules/jishushell-panel/output/dist/server.js.map +1 -1
  673. package/node_modules/jishushell-panel/output/public/assets/ApiKeyField-Ce5d1xna.js +1 -0
  674. package/node_modules/jishushell-panel/output/public/assets/Dashboard-BXame3yg.js +1 -0
  675. package/node_modules/jishushell-panel/output/public/assets/HermesChatPanel-BHZtPCJd.js +1 -0
  676. package/node_modules/jishushell-panel/output/public/assets/HermesConfigForm-CB3GbNX9.js +4 -0
  677. package/node_modules/jishushell-panel/output/public/assets/InitPassword-Boab9F6g.js +1 -0
  678. package/node_modules/jishushell-panel/output/public/assets/InstanceDetail-DrIWCqo-.js +14 -0
  679. package/node_modules/jishushell-panel/output/public/assets/Login-CzpOkNau.js +1 -0
  680. package/node_modules/jishushell-panel/output/public/assets/NewInstance-CANXyCcL.js +1 -0
  681. package/node_modules/jishushell-panel/output/public/assets/ProviderRecommendations-BABo9VOC.js +1 -0
  682. package/node_modules/jishushell-panel/output/public/assets/Settings-CKp5XxFh.js +1 -0
  683. package/node_modules/jishushell-panel/output/public/assets/Setup-C7xVDPow.js +1 -0
  684. package/node_modules/jishushell-panel/output/public/assets/WeixinLoginPanel-B765Xz4C.js +1 -0
  685. package/node_modules/jishushell-panel/output/public/assets/api-C70Gt678.js +4 -0
  686. package/node_modules/jishushell-panel/output/public/assets/index-Bs6DSbiR.js +23 -0
  687. package/node_modules/jishushell-panel/output/public/assets/index-DnnqTf7s.css +1 -0
  688. package/node_modules/jishushell-panel/output/public/assets/registry-sWIZsIEF.js +2 -0
  689. package/node_modules/jishushell-panel/output/public/assets/rolldown-runtime-QTnfLwEv.js +1 -0
  690. package/node_modules/jishushell-panel/output/public/assets/setup-task-q21GnI0E.js +1 -0
  691. package/node_modules/jishushell-panel/output/public/assets/usePolling-D4IDOQd_.js +1 -0
  692. package/node_modules/jishushell-panel/output/public/assets/vendor-i18n-Df8aUdv8.js +1 -0
  693. package/node_modules/jishushell-panel/output/public/assets/vendor-react-0L0rjmYG.js +8 -0
  694. package/node_modules/jishushell-panel/output/public/index.html +6 -4
  695. package/node_modules/jishushell-panel/package.json +2 -2
  696. package/node_modules/semver/classes/range.js +17 -4
  697. package/node_modules/semver/package.json +2 -2
  698. package/package.json +12 -64
  699. package/scripts/check-app-path-boundaries.mjs +121 -0
  700. package/scripts/check-app-spec.mjs +123 -29
  701. package/scripts/check-architecture-boundaries.mjs +178 -0
  702. package/scripts/check-colima-launchd.mjs +10 -8
  703. package/scripts/check-integration-isolation.ts +541 -0
  704. package/scripts/check-new-file-tests.mjs +11 -3
  705. package/scripts/check-open-core-boundaries.mjs +60 -10
  706. package/scripts/check-test-layering.sh +1 -1
  707. package/scripts/fixtures/instances/hermes-sample/instance.json +3 -2
  708. package/scripts/fixtures/instances/legacy-openclaw-sample/instance.json +1 -1
  709. package/scripts/local-web-upgrade-test.README +4 -3
  710. package/scripts/local-web-upgrade-test.example.env +2 -2
  711. package/scripts/local-web-upgrade-test.sh +14 -1
  712. package/scripts/pack-gui-and-send-pi.sh +43 -0
  713. package/scripts/perf/instances.js +1 -1
  714. package/scripts/prune-open-core-dist.mjs +89 -2
  715. package/scripts/smoke/hermes-bootstrap.sh +5 -5
  716. package/templates/hermes-entrypoint.sh +19 -29
  717. package/apps/openwebui-container.yaml +0 -97
  718. package/apps/playwright-container.yaml +0 -126
  719. package/dependencies/jishushell-panel-0.6.5.tgz +0 -0
  720. package/dist/crypto-shim.d.ts +0 -1
  721. package/dist/crypto-shim.js +0 -2
  722. package/dist/crypto-shim.js.map +0 -1
  723. package/dist/routes/agent-apps.d.ts +0 -14
  724. package/dist/routes/agent-apps.js +0 -77
  725. package/dist/routes/agent-apps.js.map +0 -1
  726. package/dist/routes/internal.d.ts +0 -2
  727. package/dist/routes/internal.js +0 -55
  728. package/dist/routes/internal.js.map +0 -1
  729. package/dist/routes/openclaw-routes.d.ts +0 -22
  730. package/dist/routes/openclaw-routes.js +0 -1020
  731. package/dist/routes/openclaw-routes.js.map +0 -1
  732. package/dist/routes/runtime.d.ts +0 -15
  733. package/dist/routes/runtime.js +0 -76
  734. package/dist/routes/runtime.js.map +0 -1
  735. package/dist/services/agent-apps/catalog.d.ts +0 -33
  736. package/dist/services/agent-apps/catalog.js +0 -88
  737. package/dist/services/agent-apps/catalog.js.map +0 -1
  738. package/dist/services/agent-apps/index.d.ts +0 -36
  739. package/dist/services/agent-apps/index.js +0 -171
  740. package/dist/services/agent-apps/index.js.map +0 -1
  741. package/dist/services/agent-apps/installers/adapter-probes.d.ts +0 -49
  742. package/dist/services/agent-apps/installers/adapter-probes.js +0 -230
  743. package/dist/services/agent-apps/installers/adapter-probes.js.map +0 -1
  744. package/dist/services/agent-apps/installers/adapter.d.ts +0 -30
  745. package/dist/services/agent-apps/installers/adapter.js +0 -171
  746. package/dist/services/agent-apps/installers/adapter.js.map +0 -1
  747. package/dist/services/agent-apps/installers/registry-probe.js.map +0 -1
  748. package/dist/services/agent-apps/installers/shell-script.d.ts +0 -47
  749. package/dist/services/agent-apps/installers/shell-script.js +0 -488
  750. package/dist/services/agent-apps/installers/shell-script.js.map +0 -1
  751. package/dist/services/agent-apps/types.d.ts +0 -128
  752. package/dist/services/agent-apps/types.js +0 -17
  753. package/dist/services/agent-apps/types.js.map +0 -1
  754. package/dist/services/app/app-compiler.js +0 -185
  755. package/dist/services/app/app-compiler.js.map +0 -1
  756. package/dist/services/app/app-manager.d.ts +0 -184
  757. package/dist/services/app/app-manager.js +0 -2933
  758. package/dist/services/app/app-manager.js.map +0 -1
  759. package/dist/services/app/custom-manager.d.ts +0 -27
  760. package/dist/services/app/custom-manager.js +0 -382
  761. package/dist/services/app/custom-manager.js.map +0 -1
  762. package/dist/services/app/hermes-agent-manager.d.ts +0 -20
  763. package/dist/services/app/hermes-agent-manager.js +0 -299
  764. package/dist/services/app/hermes-agent-manager.js.map +0 -1
  765. package/dist/services/app/id-normalizer.d.ts +0 -27
  766. package/dist/services/app/id-normalizer.js +0 -77
  767. package/dist/services/app/id-normalizer.js.map +0 -1
  768. package/dist/services/app/ollama-manager.d.ts +0 -18
  769. package/dist/services/app/ollama-manager.js +0 -224
  770. package/dist/services/app/ollama-manager.js.map +0 -1
  771. package/dist/services/app/openclaw-manager.d.ts +0 -63
  772. package/dist/services/app/openclaw-manager.js +0 -1215
  773. package/dist/services/app/openclaw-manager.js.map +0 -1
  774. package/dist/services/app/paths.d.ts +0 -27
  775. package/dist/services/app/paths.js +0 -40
  776. package/dist/services/app/paths.js.map +0 -1
  777. package/dist/services/app/platform-transform.d.ts +0 -32
  778. package/dist/services/app/platform-transform.js +0 -65
  779. package/dist/services/app/platform-transform.js.map +0 -1
  780. package/dist/services/app/provide-resolver.d.ts +0 -29
  781. package/dist/services/app/provide-resolver.js +0 -135
  782. package/dist/services/app/provide-resolver.js.map +0 -1
  783. package/dist/services/app/registry.d.ts +0 -17
  784. package/dist/services/app/registry.js +0 -31
  785. package/dist/services/app/registry.js.map +0 -1
  786. package/dist/services/app/remote-spec.d.ts +0 -14
  787. package/dist/services/app/remote-spec.js +0 -58
  788. package/dist/services/app/remote-spec.js.map +0 -1
  789. package/dist/services/app/terminal-session-manager.js +0 -157
  790. package/dist/services/app/terminal-session-manager.js.map +0 -1
  791. package/dist/services/app/types.d.ts +0 -74
  792. package/dist/services/app/types.js +0 -16
  793. package/dist/services/app/types.js.map +0 -1
  794. package/dist/services/app-config-admin.d.ts +0 -17
  795. package/dist/services/app-config-admin.js +0 -177
  796. package/dist/services/app-config-admin.js.map +0 -1
  797. package/dist/services/app-create-from-installed.d.ts +0 -23
  798. package/dist/services/app-create-from-installed.js +0 -75
  799. package/dist/services/app-create-from-installed.js.map +0 -1
  800. package/dist/services/app-passwords.js +0 -173
  801. package/dist/services/app-passwords.js.map +0 -1
  802. package/dist/services/backup-admin.d.ts +0 -101
  803. package/dist/services/backup-admin.js +0 -259
  804. package/dist/services/backup-admin.js.map +0 -1
  805. package/dist/services/backup-manager.d.ts +0 -264
  806. package/dist/services/backup-manager.js +0 -2263
  807. package/dist/services/backup-manager.js.map +0 -1
  808. package/dist/services/backup-verify.js +0 -240
  809. package/dist/services/backup-verify.js.map +0 -1
  810. package/dist/services/capability-endpoint-validator.d.ts +0 -41
  811. package/dist/services/capability-endpoint-validator.js +0 -114
  812. package/dist/services/capability-endpoint-validator.js.map +0 -1
  813. package/dist/services/capability-health.d.ts +0 -16
  814. package/dist/services/capability-health.js +0 -121
  815. package/dist/services/capability-health.js.map +0 -1
  816. package/dist/services/capability-registry.d.ts +0 -29
  817. package/dist/services/capability-registry.js +0 -176
  818. package/dist/services/capability-registry.js.map +0 -1
  819. package/dist/services/capability-sync.d.ts +0 -4
  820. package/dist/services/capability-sync.js +0 -220
  821. package/dist/services/capability-sync.js.map +0 -1
  822. package/dist/services/connection-admin.d.ts +0 -74
  823. package/dist/services/connection-admin.js +0 -287
  824. package/dist/services/connection-admin.js.map +0 -1
  825. package/dist/services/connection-apply.d.ts +0 -91
  826. package/dist/services/connection-apply.js +0 -471
  827. package/dist/services/connection-apply.js.map +0 -1
  828. package/dist/services/connection-resolver.d.ts +0 -65
  829. package/dist/services/connection-resolver.js +0 -281
  830. package/dist/services/connection-resolver.js.map +0 -1
  831. package/dist/services/connection-transactor.d.ts +0 -39
  832. package/dist/services/connection-transactor.js +0 -354
  833. package/dist/services/connection-transactor.js.map +0 -1
  834. package/dist/services/core-manager.d.ts +0 -50
  835. package/dist/services/core-manager.js +0 -411
  836. package/dist/services/core-manager.js.map +0 -1
  837. package/dist/services/external-mounts.js +0 -187
  838. package/dist/services/external-mounts.js.map +0 -1
  839. package/dist/services/files-manager.d.ts +0 -252
  840. package/dist/services/files-manager.js +0 -1156
  841. package/dist/services/files-manager.js.map +0 -1
  842. package/dist/services/files-mounts.d.ts +0 -42
  843. package/dist/services/files-mounts.js +0 -207
  844. package/dist/services/files-mounts.js.map +0 -1
  845. package/dist/services/instance-admin.d.ts +0 -26
  846. package/dist/services/instance-admin.js +0 -218
  847. package/dist/services/instance-admin.js.map +0 -1
  848. package/dist/services/instance-manager.d.ts +0 -192
  849. package/dist/services/instance-manager.js +0 -1289
  850. package/dist/services/instance-manager.js.map +0 -1
  851. package/dist/services/macos-launchd.js +0 -312
  852. package/dist/services/macos-launchd.js.map +0 -1
  853. package/dist/services/nomad-manager.d.ts +0 -307
  854. package/dist/services/nomad-manager.js +0 -3958
  855. package/dist/services/nomad-manager.js.map +0 -1
  856. package/dist/services/organize/applier.js +0 -218
  857. package/dist/services/organize/applier.js.map +0 -1
  858. package/dist/services/organize/rules.js +0 -286
  859. package/dist/services/organize/rules.js.map +0 -1
  860. package/dist/services/organize/scanner.js +0 -366
  861. package/dist/services/organize/scanner.js.map +0 -1
  862. package/dist/services/organize/store.js +0 -82
  863. package/dist/services/organize/store.js.map +0 -1
  864. package/dist/services/plugin-installer.js +0 -128
  865. package/dist/services/plugin-installer.js.map +0 -1
  866. package/dist/services/process-manager.d.ts +0 -25
  867. package/dist/services/process-manager.js +0 -568
  868. package/dist/services/process-manager.js.map +0 -1
  869. package/dist/services/runtime/adapters/custom.d.ts +0 -20
  870. package/dist/services/runtime/adapters/custom.js +0 -188
  871. package/dist/services/runtime/adapters/custom.js.map +0 -1
  872. package/dist/services/runtime/adapters/hermes.d.ts +0 -204
  873. package/dist/services/runtime/adapters/hermes.js +0 -1684
  874. package/dist/services/runtime/adapters/hermes.js.map +0 -1
  875. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +0 -45
  876. package/dist/services/runtime/adapters/openclaw-mcporter.js +0 -108
  877. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +0 -1
  878. package/dist/services/runtime/adapters/openclaw.d.ts +0 -426
  879. package/dist/services/runtime/adapters/openclaw.js +0 -3975
  880. package/dist/services/runtime/adapters/openclaw.js.map +0 -1
  881. package/dist/services/runtime/index.d.ts +0 -34
  882. package/dist/services/runtime/index.js +0 -51
  883. package/dist/services/runtime/index.js.map +0 -1
  884. package/dist/services/runtime/mcp-shims/anythingllm-shim.d.ts +0 -46
  885. package/dist/services/runtime/mcp-shims/anythingllm-shim.js +0 -281
  886. package/dist/services/runtime/mcp-shims/anythingllm-shim.js.map +0 -1
  887. package/dist/services/runtime/mcp-shims/drive-shim.js +0 -490
  888. package/dist/services/runtime/mcp-shims/drive-shim.js.map +0 -1
  889. package/dist/services/runtime/mcp-shims/jishukb-shim.d.ts +0 -48
  890. package/dist/services/runtime/mcp-shims/jishukb-shim.js +0 -723
  891. package/dist/services/runtime/mcp-shims/jishukb-shim.js.map +0 -1
  892. package/dist/services/runtime/mcp-shims/mcporter-lite.js +0 -276
  893. package/dist/services/runtime/mcp-shims/mcporter-lite.js.map +0 -1
  894. package/dist/services/runtime/migrations.d.ts +0 -23
  895. package/dist/services/runtime/migrations.js +0 -125
  896. package/dist/services/runtime/migrations.js.map +0 -1
  897. package/dist/services/runtime/registry.d.ts +0 -13
  898. package/dist/services/runtime/registry.js +0 -32
  899. package/dist/services/runtime/registry.js.map +0 -1
  900. package/dist/services/runtime-identity.d.ts +0 -13
  901. package/dist/services/runtime-identity.js +0 -166
  902. package/dist/services/runtime-identity.js.map +0 -1
  903. package/dist/services/runtime-repair.d.ts +0 -52
  904. package/dist/services/runtime-repair.js +0 -352
  905. package/dist/services/runtime-repair.js.map +0 -1
  906. package/dist/services/setup-manager.d.ts +0 -158
  907. package/dist/services/setup-manager.js +0 -2740
  908. package/dist/services/setup-manager.js.map +0 -1
  909. package/dist/services/suggestions.d.ts +0 -27
  910. package/dist/services/suggestions.js +0 -133
  911. package/dist/services/suggestions.js.map +0 -1
  912. package/dist/services/system-monitor.js +0 -79
  913. package/dist/services/system-monitor.js.map +0 -1
  914. package/dist/services/system-ollama-provider.d.ts +0 -14
  915. package/dist/services/system-ollama-provider.js +0 -125
  916. package/dist/services/system-ollama-provider.js.map +0 -1
  917. package/dist/services/system-reconciler.d.ts +0 -72
  918. package/dist/services/system-reconciler.js +0 -600
  919. package/dist/services/system-reconciler.js.map +0 -1
  920. package/dist/services/task-registry.d.ts +0 -44
  921. package/dist/services/task-registry.js +0 -76
  922. package/dist/services/task-registry.js.map +0 -1
  923. package/dist/services/types-shim.d.ts +0 -16
  924. package/dist/services/types-shim.js +0 -2
  925. package/dist/services/types-shim.js.map +0 -1
  926. package/dist/services/update-manager.d.ts +0 -47
  927. package/dist/services/update-manager.js +0 -351
  928. package/dist/services/update-manager.js.map +0 -1
  929. package/dist/services/webdav/server.d.ts +0 -24
  930. package/dist/services/webdav/server.js +0 -420
  931. package/dist/services/webdav/server.js.map +0 -1
  932. package/dist/services/webdav/xml-builder.js.map +0 -1
  933. package/dist/services/workspace-builder.d.ts +0 -29
  934. package/dist/services/workspace-builder.js +0 -188
  935. package/dist/services/workspace-builder.js.map +0 -1
  936. package/node_modules/@fastify/static/.github/stale.yml +0 -21
  937. package/node_modules/@isaacs/cliui/LICENSE.md +0 -63
  938. package/node_modules/@isaacs/cliui/README.md +0 -151
  939. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-regex/index.d.ts +0 -4
  940. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-regex/index.d.ts.map +0 -1
  941. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-regex/index.js +0 -16
  942. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-regex/index.js.map +0 -1
  943. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-styles/index.d.ts +0 -34
  944. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-styles/index.d.ts.map +0 -1
  945. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-styles/index.js +0 -170
  946. package/node_modules/@isaacs/cliui/dist/commonjs/ansi-styles/index.js.map +0 -1
  947. package/node_modules/@isaacs/cliui/dist/commonjs/eastasianwidth/index.d.ts +0 -6
  948. package/node_modules/@isaacs/cliui/dist/commonjs/eastasianwidth/index.d.ts.map +0 -1
  949. package/node_modules/@isaacs/cliui/dist/commonjs/eastasianwidth/index.js +0 -307
  950. package/node_modules/@isaacs/cliui/dist/commonjs/eastasianwidth/index.js.map +0 -1
  951. package/node_modules/@isaacs/cliui/dist/commonjs/emoji-regex/index.d.ts +0 -2
  952. package/node_modules/@isaacs/cliui/dist/commonjs/emoji-regex/index.d.ts.map +0 -1
  953. package/node_modules/@isaacs/cliui/dist/commonjs/emoji-regex/index.js +0 -7
  954. package/node_modules/@isaacs/cliui/dist/commonjs/emoji-regex/index.js.map +0 -1
  955. package/node_modules/@isaacs/cliui/dist/commonjs/index.d.ts +0 -41
  956. package/node_modules/@isaacs/cliui/dist/commonjs/index.d.ts.map +0 -1
  957. package/node_modules/@isaacs/cliui/dist/commonjs/index.js +0 -322
  958. package/node_modules/@isaacs/cliui/dist/commonjs/index.js.map +0 -1
  959. package/node_modules/@isaacs/cliui/dist/commonjs/index.min.js +0 -12
  960. package/node_modules/@isaacs/cliui/dist/commonjs/index.min.js.map +0 -7
  961. package/node_modules/@isaacs/cliui/dist/commonjs/package.json +0 -3
  962. package/node_modules/@isaacs/cliui/dist/commonjs/string-width/index.d.ts +0 -5
  963. package/node_modules/@isaacs/cliui/dist/commonjs/string-width/index.d.ts.map +0 -1
  964. package/node_modules/@isaacs/cliui/dist/commonjs/string-width/index.js +0 -49
  965. package/node_modules/@isaacs/cliui/dist/commonjs/string-width/index.js.map +0 -1
  966. package/node_modules/@isaacs/cliui/dist/commonjs/strip-ansi/index.d.ts +0 -2
  967. package/node_modules/@isaacs/cliui/dist/commonjs/strip-ansi/index.d.ts.map +0 -1
  968. package/node_modules/@isaacs/cliui/dist/commonjs/strip-ansi/index.js +0 -8
  969. package/node_modules/@isaacs/cliui/dist/commonjs/strip-ansi/index.js.map +0 -1
  970. package/node_modules/@isaacs/cliui/dist/commonjs/wrap-ansi/index.d.ts +0 -7
  971. package/node_modules/@isaacs/cliui/dist/commonjs/wrap-ansi/index.d.ts.map +0 -1
  972. package/node_modules/@isaacs/cliui/dist/commonjs/wrap-ansi/index.js +0 -176
  973. package/node_modules/@isaacs/cliui/dist/commonjs/wrap-ansi/index.js.map +0 -1
  974. package/node_modules/@isaacs/cliui/dist/esm/ansi-regex/index.d.ts +0 -4
  975. package/node_modules/@isaacs/cliui/dist/esm/ansi-regex/index.d.ts.map +0 -1
  976. package/node_modules/@isaacs/cliui/dist/esm/ansi-regex/index.js +0 -12
  977. package/node_modules/@isaacs/cliui/dist/esm/ansi-regex/index.js.map +0 -1
  978. package/node_modules/@isaacs/cliui/dist/esm/ansi-styles/index.d.ts +0 -34
  979. package/node_modules/@isaacs/cliui/dist/esm/ansi-styles/index.d.ts.map +0 -1
  980. package/node_modules/@isaacs/cliui/dist/esm/ansi-styles/index.js +0 -167
  981. package/node_modules/@isaacs/cliui/dist/esm/ansi-styles/index.js.map +0 -1
  982. package/node_modules/@isaacs/cliui/dist/esm/eastasianwidth/index.d.ts +0 -6
  983. package/node_modules/@isaacs/cliui/dist/esm/eastasianwidth/index.d.ts.map +0 -1
  984. package/node_modules/@isaacs/cliui/dist/esm/eastasianwidth/index.js +0 -299
  985. package/node_modules/@isaacs/cliui/dist/esm/eastasianwidth/index.js.map +0 -1
  986. package/node_modules/@isaacs/cliui/dist/esm/emoji-regex/index.d.ts +0 -2
  987. package/node_modules/@isaacs/cliui/dist/esm/emoji-regex/index.d.ts.map +0 -1
  988. package/node_modules/@isaacs/cliui/dist/esm/emoji-regex/index.js +0 -3
  989. package/node_modules/@isaacs/cliui/dist/esm/emoji-regex/index.js.map +0 -1
  990. package/node_modules/@isaacs/cliui/dist/esm/index.d.ts +0 -41
  991. package/node_modules/@isaacs/cliui/dist/esm/index.d.ts.map +0 -1
  992. package/node_modules/@isaacs/cliui/dist/esm/index.js +0 -317
  993. package/node_modules/@isaacs/cliui/dist/esm/index.js.map +0 -1
  994. package/node_modules/@isaacs/cliui/dist/esm/index.min.js +0 -12
  995. package/node_modules/@isaacs/cliui/dist/esm/index.min.js.map +0 -7
  996. package/node_modules/@isaacs/cliui/dist/esm/package.json +0 -3
  997. package/node_modules/@isaacs/cliui/dist/esm/string-width/index.d.ts +0 -5
  998. package/node_modules/@isaacs/cliui/dist/esm/string-width/index.d.ts.map +0 -1
  999. package/node_modules/@isaacs/cliui/dist/esm/string-width/index.js +0 -46
  1000. package/node_modules/@isaacs/cliui/dist/esm/string-width/index.js.map +0 -1
  1001. package/node_modules/@isaacs/cliui/dist/esm/strip-ansi/index.d.ts +0 -2
  1002. package/node_modules/@isaacs/cliui/dist/esm/strip-ansi/index.d.ts.map +0 -1
  1003. package/node_modules/@isaacs/cliui/dist/esm/strip-ansi/index.js +0 -4
  1004. package/node_modules/@isaacs/cliui/dist/esm/strip-ansi/index.js.map +0 -1
  1005. package/node_modules/@isaacs/cliui/dist/esm/wrap-ansi/index.d.ts +0 -7
  1006. package/node_modules/@isaacs/cliui/dist/esm/wrap-ansi/index.d.ts.map +0 -1
  1007. package/node_modules/@isaacs/cliui/dist/esm/wrap-ansi/index.js +0 -172
  1008. package/node_modules/@isaacs/cliui/dist/esm/wrap-ansi/index.js.map +0 -1
  1009. package/node_modules/@isaacs/cliui/package.json +0 -163
  1010. package/node_modules/content-disposition/HISTORY.md +0 -60
  1011. package/node_modules/cross-spawn/LICENSE +0 -21
  1012. package/node_modules/cross-spawn/README.md +0 -89
  1013. package/node_modules/cross-spawn/index.js +0 -39
  1014. package/node_modules/cross-spawn/lib/enoent.js +0 -59
  1015. package/node_modules/cross-spawn/lib/parse.js +0 -91
  1016. package/node_modules/cross-spawn/lib/util/escape.js +0 -47
  1017. package/node_modules/cross-spawn/lib/util/readShebang.js +0 -23
  1018. package/node_modules/cross-spawn/lib/util/resolveCommand.js +0 -52
  1019. package/node_modules/cross-spawn/package.json +0 -73
  1020. package/node_modules/fastify/test/types/content-type-parser.test-d.ts +0 -72
  1021. package/node_modules/fastify/test/types/decorate-request-reply.test-d.ts +0 -18
  1022. package/node_modules/fastify/test/types/dummy-plugin.ts +0 -9
  1023. package/node_modules/fastify/test/types/errors.test-d.ts +0 -90
  1024. package/node_modules/fastify/test/types/fastify.test-d.ts +0 -352
  1025. package/node_modules/fastify/test/types/hooks.test-d.ts +0 -550
  1026. package/node_modules/fastify/test/types/import.ts +0 -2
  1027. package/node_modules/fastify/test/types/instance.test-d.ts +0 -588
  1028. package/node_modules/fastify/test/types/logger.test-d.ts +0 -277
  1029. package/node_modules/fastify/test/types/plugin.test-d.ts +0 -97
  1030. package/node_modules/fastify/test/types/register.test-d.ts +0 -237
  1031. package/node_modules/fastify/test/types/reply.test-d.ts +0 -254
  1032. package/node_modules/fastify/test/types/request.test-d.ts +0 -188
  1033. package/node_modules/fastify/test/types/route.test-d.ts +0 -553
  1034. package/node_modules/fastify/test/types/schema.test-d.ts +0 -135
  1035. package/node_modules/fastify/test/types/serverFactory.test-d.ts +0 -37
  1036. package/node_modules/fastify/test/types/type-provider.test-d.ts +0 -1213
  1037. package/node_modules/fastify/test/types/using.test-d.ts +0 -17
  1038. package/node_modules/foreground-child/LICENSE +0 -15
  1039. package/node_modules/foreground-child/README.md +0 -128
  1040. package/node_modules/foreground-child/dist/commonjs/all-signals.d.ts +0 -2
  1041. package/node_modules/foreground-child/dist/commonjs/all-signals.d.ts.map +0 -1
  1042. package/node_modules/foreground-child/dist/commonjs/all-signals.js +0 -58
  1043. package/node_modules/foreground-child/dist/commonjs/all-signals.js.map +0 -1
  1044. package/node_modules/foreground-child/dist/commonjs/index.d.ts +0 -58
  1045. package/node_modules/foreground-child/dist/commonjs/index.d.ts.map +0 -1
  1046. package/node_modules/foreground-child/dist/commonjs/index.js +0 -123
  1047. package/node_modules/foreground-child/dist/commonjs/index.js.map +0 -1
  1048. package/node_modules/foreground-child/dist/commonjs/package.json +0 -3
  1049. package/node_modules/foreground-child/dist/commonjs/proxy-signals.d.ts +0 -6
  1050. package/node_modules/foreground-child/dist/commonjs/proxy-signals.d.ts.map +0 -1
  1051. package/node_modules/foreground-child/dist/commonjs/proxy-signals.js +0 -38
  1052. package/node_modules/foreground-child/dist/commonjs/proxy-signals.js.map +0 -1
  1053. package/node_modules/foreground-child/dist/commonjs/watchdog.d.ts +0 -10
  1054. package/node_modules/foreground-child/dist/commonjs/watchdog.d.ts.map +0 -1
  1055. package/node_modules/foreground-child/dist/commonjs/watchdog.js +0 -50
  1056. package/node_modules/foreground-child/dist/commonjs/watchdog.js.map +0 -1
  1057. package/node_modules/foreground-child/dist/esm/all-signals.d.ts +0 -2
  1058. package/node_modules/foreground-child/dist/esm/all-signals.d.ts.map +0 -1
  1059. package/node_modules/foreground-child/dist/esm/all-signals.js +0 -52
  1060. package/node_modules/foreground-child/dist/esm/all-signals.js.map +0 -1
  1061. package/node_modules/foreground-child/dist/esm/index.d.ts +0 -58
  1062. package/node_modules/foreground-child/dist/esm/index.d.ts.map +0 -1
  1063. package/node_modules/foreground-child/dist/esm/index.js +0 -115
  1064. package/node_modules/foreground-child/dist/esm/index.js.map +0 -1
  1065. package/node_modules/foreground-child/dist/esm/package.json +0 -3
  1066. package/node_modules/foreground-child/dist/esm/proxy-signals.d.ts +0 -6
  1067. package/node_modules/foreground-child/dist/esm/proxy-signals.d.ts.map +0 -1
  1068. package/node_modules/foreground-child/dist/esm/proxy-signals.js +0 -34
  1069. package/node_modules/foreground-child/dist/esm/proxy-signals.js.map +0 -1
  1070. package/node_modules/foreground-child/dist/esm/watchdog.d.ts +0 -10
  1071. package/node_modules/foreground-child/dist/esm/watchdog.d.ts.map +0 -1
  1072. package/node_modules/foreground-child/dist/esm/watchdog.js +0 -46
  1073. package/node_modules/foreground-child/dist/esm/watchdog.js.map +0 -1
  1074. package/node_modules/foreground-child/package.json +0 -106
  1075. package/node_modules/glob/dist/esm/bin.d.mts +0 -3
  1076. package/node_modules/glob/dist/esm/bin.d.mts.map +0 -1
  1077. package/node_modules/glob/dist/esm/bin.mjs +0 -346
  1078. package/node_modules/glob/dist/esm/bin.mjs.map +0 -1
  1079. package/node_modules/isexe/.npmignore +0 -2
  1080. package/node_modules/isexe/LICENSE +0 -15
  1081. package/node_modules/isexe/README.md +0 -51
  1082. package/node_modules/isexe/index.js +0 -57
  1083. package/node_modules/isexe/mode.js +0 -41
  1084. package/node_modules/isexe/package.json +0 -31
  1085. package/node_modules/isexe/test/basic.js +0 -221
  1086. package/node_modules/isexe/windows.js +0 -42
  1087. package/node_modules/jackspeak/LICENSE.md +0 -55
  1088. package/node_modules/jackspeak/README.md +0 -394
  1089. package/node_modules/jackspeak/dist/commonjs/index.d.ts +0 -323
  1090. package/node_modules/jackspeak/dist/commonjs/index.d.ts.map +0 -1
  1091. package/node_modules/jackspeak/dist/commonjs/index.js +0 -944
  1092. package/node_modules/jackspeak/dist/commonjs/index.js.map +0 -1
  1093. package/node_modules/jackspeak/dist/commonjs/index.min.js +0 -33
  1094. package/node_modules/jackspeak/dist/commonjs/index.min.js.map +0 -7
  1095. package/node_modules/jackspeak/dist/commonjs/package.json +0 -3
  1096. package/node_modules/jackspeak/dist/esm/index.d.ts +0 -323
  1097. package/node_modules/jackspeak/dist/esm/index.d.ts.map +0 -1
  1098. package/node_modules/jackspeak/dist/esm/index.js +0 -936
  1099. package/node_modules/jackspeak/dist/esm/index.js.map +0 -1
  1100. package/node_modules/jackspeak/dist/esm/index.min.js +0 -33
  1101. package/node_modules/jackspeak/dist/esm/index.min.js.map +0 -7
  1102. package/node_modules/jackspeak/dist/esm/package.json +0 -3
  1103. package/node_modules/jackspeak/package.json +0 -115
  1104. package/node_modules/jishushell-panel/output/public/assets/ApiKeyField-D1i7zWXR.js +0 -1
  1105. package/node_modules/jishushell-panel/output/public/assets/Dashboard-sWIvL43F.js +0 -1
  1106. package/node_modules/jishushell-panel/output/public/assets/HermesChatPanel-DQ8RyvQY.js +0 -1
  1107. package/node_modules/jishushell-panel/output/public/assets/HermesConfigForm-tIbPP1sB.js +0 -4
  1108. package/node_modules/jishushell-panel/output/public/assets/InitPassword-C3Slq3Dd.js +0 -1
  1109. package/node_modules/jishushell-panel/output/public/assets/InstanceDetail-7JqY9tq4.js +0 -92
  1110. package/node_modules/jishushell-panel/output/public/assets/Login-BXLDJlQN.js +0 -1
  1111. package/node_modules/jishushell-panel/output/public/assets/NewInstance-dLc5Xrpx.js +0 -1
  1112. package/node_modules/jishushell-panel/output/public/assets/ProviderRecommendations-DIAXxesl.js +0 -1
  1113. package/node_modules/jishushell-panel/output/public/assets/Settings-Bd5utbBh.js +0 -1
  1114. package/node_modules/jishushell-panel/output/public/assets/Setup-Yn9_20FL.js +0 -1
  1115. package/node_modules/jishushell-panel/output/public/assets/WeixinLoginPanel-C21doQTJ.js +0 -9
  1116. package/node_modules/jishushell-panel/output/public/assets/index-CCkaIEjn.js +0 -20
  1117. package/node_modules/jishushell-panel/output/public/assets/index-D7qxy-Vh.css +0 -1
  1118. package/node_modules/jishushell-panel/output/public/assets/registry-B2ZQZXWL.js +0 -2
  1119. package/node_modules/jishushell-panel/output/public/assets/usePolling-BFZm4do_.js +0 -1
  1120. package/node_modules/jishushell-panel/output/public/assets/vendor-i18n-DqPtOicc.js +0 -9
  1121. package/node_modules/jishushell-panel/output/public/assets/vendor-react-DW5juQin.js +0 -59
  1122. package/node_modules/package-json-from-dist/LICENSE.md +0 -63
  1123. package/node_modules/package-json-from-dist/README.md +0 -110
  1124. package/node_modules/package-json-from-dist/dist/commonjs/index.d.ts +0 -89
  1125. package/node_modules/package-json-from-dist/dist/commonjs/index.d.ts.map +0 -1
  1126. package/node_modules/package-json-from-dist/dist/commonjs/index.js +0 -134
  1127. package/node_modules/package-json-from-dist/dist/commonjs/index.js.map +0 -1
  1128. package/node_modules/package-json-from-dist/dist/commonjs/package.json +0 -3
  1129. package/node_modules/package-json-from-dist/dist/esm/index.d.ts +0 -89
  1130. package/node_modules/package-json-from-dist/dist/esm/index.d.ts.map +0 -1
  1131. package/node_modules/package-json-from-dist/dist/esm/index.js +0 -129
  1132. package/node_modules/package-json-from-dist/dist/esm/index.js.map +0 -1
  1133. package/node_modules/package-json-from-dist/dist/esm/package.json +0 -3
  1134. package/node_modules/package-json-from-dist/package.json +0 -68
  1135. package/node_modules/path-key/index.d.ts +0 -40
  1136. package/node_modules/path-key/index.js +0 -16
  1137. package/node_modules/path-key/license +0 -9
  1138. package/node_modules/path-key/package.json +0 -39
  1139. package/node_modules/path-key/readme.md +0 -61
  1140. package/node_modules/safe-buffer/LICENSE +0 -21
  1141. package/node_modules/safe-buffer/README.md +0 -584
  1142. package/node_modules/safe-buffer/index.d.ts +0 -187
  1143. package/node_modules/safe-buffer/index.js +0 -65
  1144. package/node_modules/safe-buffer/package.json +0 -51
  1145. package/node_modules/shebang-command/index.js +0 -19
  1146. package/node_modules/shebang-command/license +0 -9
  1147. package/node_modules/shebang-command/package.json +0 -34
  1148. package/node_modules/shebang-command/readme.md +0 -34
  1149. package/node_modules/shebang-regex/index.d.ts +0 -22
  1150. package/node_modules/shebang-regex/index.js +0 -2
  1151. package/node_modules/shebang-regex/license +0 -9
  1152. package/node_modules/shebang-regex/package.json +0 -35
  1153. package/node_modules/shebang-regex/readme.md +0 -33
  1154. package/node_modules/signal-exit/LICENSE.txt +0 -16
  1155. package/node_modules/signal-exit/README.md +0 -74
  1156. package/node_modules/signal-exit/dist/cjs/browser.d.ts +0 -12
  1157. package/node_modules/signal-exit/dist/cjs/browser.d.ts.map +0 -1
  1158. package/node_modules/signal-exit/dist/cjs/browser.js +0 -10
  1159. package/node_modules/signal-exit/dist/cjs/browser.js.map +0 -1
  1160. package/node_modules/signal-exit/dist/cjs/index.d.ts +0 -48
  1161. package/node_modules/signal-exit/dist/cjs/index.d.ts.map +0 -1
  1162. package/node_modules/signal-exit/dist/cjs/index.js +0 -279
  1163. package/node_modules/signal-exit/dist/cjs/index.js.map +0 -1
  1164. package/node_modules/signal-exit/dist/cjs/package.json +0 -3
  1165. package/node_modules/signal-exit/dist/cjs/signals.d.ts +0 -29
  1166. package/node_modules/signal-exit/dist/cjs/signals.d.ts.map +0 -1
  1167. package/node_modules/signal-exit/dist/cjs/signals.js +0 -42
  1168. package/node_modules/signal-exit/dist/cjs/signals.js.map +0 -1
  1169. package/node_modules/signal-exit/dist/mjs/browser.d.ts +0 -12
  1170. package/node_modules/signal-exit/dist/mjs/browser.d.ts.map +0 -1
  1171. package/node_modules/signal-exit/dist/mjs/browser.js +0 -4
  1172. package/node_modules/signal-exit/dist/mjs/browser.js.map +0 -1
  1173. package/node_modules/signal-exit/dist/mjs/index.d.ts +0 -48
  1174. package/node_modules/signal-exit/dist/mjs/index.d.ts.map +0 -1
  1175. package/node_modules/signal-exit/dist/mjs/index.js +0 -275
  1176. package/node_modules/signal-exit/dist/mjs/index.js.map +0 -1
  1177. package/node_modules/signal-exit/dist/mjs/package.json +0 -3
  1178. package/node_modules/signal-exit/dist/mjs/signals.d.ts +0 -29
  1179. package/node_modules/signal-exit/dist/mjs/signals.d.ts.map +0 -1
  1180. package/node_modules/signal-exit/dist/mjs/signals.js +0 -39
  1181. package/node_modules/signal-exit/dist/mjs/signals.js.map +0 -1
  1182. package/node_modules/signal-exit/package.json +0 -106
  1183. package/node_modules/which/CHANGELOG.md +0 -166
  1184. package/node_modules/which/LICENSE +0 -15
  1185. package/node_modules/which/README.md +0 -54
  1186. package/node_modules/which/bin/node-which +0 -52
  1187. package/node_modules/which/package.json +0 -43
  1188. package/node_modules/which/which.js +0 -125
  1189. package/scripts/check-adapter-isolation.ts +0 -293
  1190. /package/dist/services/{app → app-common}/app-compiler.d.ts +0 -0
  1191. /package/dist/services/{app → app-common}/terminal-session-manager.d.ts +0 -0
  1192. /package/dist/services/{backup-verify.d.ts → backup/backup-verify.d.ts} +0 -0
  1193. /package/dist/services/{external-mounts.d.ts → files/external-mounts.d.ts} +0 -0
  1194. /package/dist/services/{organize → files/organize}/applier.d.ts +0 -0
  1195. /package/dist/services/{organize → files/organize}/rules.d.ts +0 -0
  1196. /package/dist/services/{organize → files/organize}/scanner.d.ts +0 -0
  1197. /package/dist/services/{organize → files/organize}/store.d.ts +0 -0
  1198. /package/dist/services/{webdav → files/webdav}/xml-builder.d.ts +0 -0
  1199. /package/dist/services/{webdav → files/webdav}/xml-builder.js +0 -0
  1200. /package/dist/services/{app-passwords.d.ts → instances/passwords.d.ts} +0 -0
  1201. /package/dist/services/{agent-apps → integrations/installable}/installers/registry-probe.d.ts +0 -0
  1202. /package/dist/services/{agent-apps → integrations/installable}/installers/registry-probe.js +0 -0
  1203. /package/dist/services/{runtime/mcp-shims → integrations/openclaw}/drive-shim.d.ts +0 -0
  1204. /package/dist/services/{runtime/mcp-shims → integrations/openclaw}/mcporter-lite.d.ts +0 -0
  1205. /package/dist/services/{plugin-installer.d.ts → setup/plugin-installer.d.ts} +0 -0
  1206. /package/dist/services/{macos-launchd.d.ts → system/macos-launchd.d.ts} +0 -0
  1207. /package/dist/services/{system-monitor.d.ts → system/system-monitor.d.ts} +0 -0
@@ -1,2933 +0,0 @@
1
- import { createHash } from "crypto";
2
- import { existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, chmodSync, chownSync, lstatSync, } from "fs";
3
- import { homedir, tmpdir } from "os";
4
- import { basename, extname, join, dirname } from "path";
5
- import { spawn, spawnSync } from "child_process";
6
- import { fileURLToPath } from "url";
7
- import { parse, stringify } from "yaml";
8
- import * as config from "../../config.js";
9
- import { ensureDirHost } from "../../utils/fs.js";
10
- import { safeReadJson, safeWriteJson } from "../../utils/safe-json.js";
11
- import * as instanceServices from "../instance-manager.js";
12
- import { invalidateRuntimeIdentity, resolveRuntimeIdentity } from "../runtime-identity.js";
13
- import { withInstanceLock } from "../../utils/instance-lock.js";
14
- import { createTask, emitTask, getRunningTasks, getTask } from "../task-registry.js";
15
- import { compileTaskRuntime } from "./app-compiler.js";
16
- import * as capabilityRegistry from "../capability-registry.js";
17
- import { resolveProvideEndpoint } from "./provide-resolver.js";
18
- import { platformTransformSpec } from "./platform-transform.js";
19
- import { createInvalidSudoPasswordError, isSudoAuthenticationError, isSudoNoNewPrivilegesError, isSudoPasswordRequiredError, prepareSudoAskpassEnv, SUDO_PASSTHROUGH_ENV_KEYS, } from "../../utils/sudo-askpass.js";
20
- const DEFAULT_LIFECYCLE_PATH_ENTRIES = [
21
- "/opt/homebrew/bin",
22
- "/opt/homebrew/sbin",
23
- "/usr/local/bin",
24
- "/usr/local/sbin",
25
- "/usr/bin",
26
- "/bin",
27
- "/usr/sbin",
28
- "/sbin",
29
- ];
30
- const DEFAULT_LIFECYCLE_PATH = DEFAULT_LIFECYCLE_PATH_ENTRIES.join(":");
31
- const MACOS_LIFECYCLE_PATH_PROBES = [
32
- "/Applications/Docker.app/Contents/Resources/bin",
33
- ];
34
- const ANONYMOUS_DOWNLOAD_IMAGE_ALLOWLIST = new Set([
35
- "filebrowser/filebrowser:latest",
36
- "ghcr.io/browserless/chromium:latest",
37
- "ghcr.io/fmaclen/hollama:latest",
38
- "ghcr.io/open-webui/open-webui:main",
39
- "mcr.microsoft.com/playwright:v1.55.0-noble",
40
- "mintplexlabs/anythingllm:latest",
41
- "paradedb/paradedb:v0.22.2-pg17",
42
- "redis:7.0-alpine",
43
- "searxng/searxng:latest",
44
- "wechatopenai/weknora-app:latest",
45
- "wechatopenai/weknora-docreader:latest",
46
- "wechatopenai/weknora-ui:latest",
47
- ]);
48
- const ANONYMOUS_DOWNLOAD_IMAGE_REPOSITORY_ALLOWLIST = new Set([
49
- "ghcr.io/x-aijishu/jishu-kb",
50
- ]);
51
- function getConfigValue(name) {
52
- return name in config ? config[name] : undefined;
53
- }
54
- function resolveConfigPath(value, fallback) {
55
- return typeof value === "string" && value.trim() ? value : fallback;
56
- }
57
- const JISHUSHELL_HOME = resolveConfigPath(getConfigValue("JISHUSHELL_HOME"), join(process.env.HOME ?? homedir(), ".jishushell"));
58
- const APPS_DIR = resolveConfigPath(getConfigValue("APPS_DIR"), join(JISHUSHELL_HOME, "apps"));
59
- const INSTANCES_DIR = resolveConfigPath(getConfigValue("INSTANCES_DIR"), join(JISHUSHELL_HOME, "instances"));
60
- const CURRENT_JISHUSHELL_VERSION = (() => {
61
- try {
62
- const pkg = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf-8"));
63
- return String(pkg.version ?? "0.0.0");
64
- }
65
- catch {
66
- return "0.0.0";
67
- }
68
- })();
69
- const BUILTIN_SPEC_FILE_RE = /\.ya?ml$/i;
70
- function builtinAppsDirCandidates() {
71
- return [
72
- fileURLToPath(new URL("../../../apps/", import.meta.url)),
73
- join(process.cwd(), "apps"),
74
- ];
75
- }
76
- function resolveBuiltinAppsDir() {
77
- for (const candidate of builtinAppsDirCandidates()) {
78
- if (existsSync(candidate))
79
- return candidate;
80
- }
81
- return null;
82
- }
83
- function parseBuiltinTemplate(fileName, yamlText) {
84
- let parsed = {};
85
- try {
86
- parsed = parse(yamlText) ?? {};
87
- }
88
- catch {
89
- return null;
90
- }
91
- const tasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
92
- const serviceTasks = tasks.filter((task) => (task?.role ?? "service") === "service");
93
- const serviceTask = serviceTasks[0] ?? tasks[0] ?? null;
94
- const serviceRuntime = typeof serviceTask?.runtime === "string" && serviceTask.runtime.trim()
95
- ? serviceTask.runtime.trim()
96
- : null;
97
- const id = typeof parsed.id === "string" && parsed.id.trim()
98
- ? parsed.id.trim()
99
- : basename(fileName, extname(fileName));
100
- const name = typeof parsed.name === "string" && parsed.name.trim()
101
- ? parsed.name.trim()
102
- : id;
103
- const description = typeof parsed.description === "string" ? parsed.description : "";
104
- const isOllamaTemplate = /^ollama(?:[-_]|$)/i.test(basename(fileName, extname(fileName))) || /^ollama(?:[-_]|$)/i.test(id);
105
- const suggestedAppType = isOllamaTemplate ? "ollama" : "custom";
106
- return {
107
- id,
108
- fileName,
109
- name,
110
- description,
111
- serviceRuntime,
112
- instanceCompatible: serviceTasks.length === 1
113
- && tasks.every((task) => {
114
- const runtime = typeof task?.runtime === "string" ? task.runtime.trim() : "";
115
- return runtime === "container" || runtime === "process";
116
- })
117
- && (serviceRuntime === "container" || serviceRuntime === "process"),
118
- suggestedAppType,
119
- yaml: yamlText,
120
- };
121
- }
122
- export function listBuiltinAppSpecs() {
123
- const dir = resolveBuiltinAppsDir();
124
- if (!dir)
125
- return [];
126
- return readdirSync(dir)
127
- .filter((entry) => BUILTIN_SPEC_FILE_RE.test(entry))
128
- .sort((left, right) => left.localeCompare(right))
129
- .map((fileName) => parseBuiltinTemplate(fileName, readFileSync(join(dir, fileName), "utf-8")))
130
- .filter((entry) => entry != null);
131
- }
132
- function installedAppDir(instanceId) {
133
- for (const rootDir of [APPS_DIR]) {
134
- const dir = join(rootDir, instanceId);
135
- if (existsSync(join(dir, "app-spec.yaml")) && existsSync(join(dir, "manifest.json"))) {
136
- return dir;
137
- }
138
- }
139
- return null;
140
- }
141
- export function updateInstance(instanceId, name, description) {
142
- const updatedMeta = instanceServices.updateInstance(instanceId, name, description);
143
- const appData = getApp(instanceId);
144
- const appDir = installedAppDir(instanceId);
145
- if (appData && appDir) {
146
- const nextSpec = {
147
- ...appData.spec,
148
- ...(name != null ? { name } : {}),
149
- ...(description != null ? { description } : {}),
150
- };
151
- const yamlPath = join(appDir, "app-spec.yaml");
152
- const yamlTmp = yamlPath + ".tmp";
153
- writeFileSync(yamlTmp, stringify(nextSpec), { mode: 0o644 });
154
- renameSync(yamlTmp, yamlPath);
155
- }
156
- invalidateRuntimeIdentity(instanceId);
157
- return instanceServices.getInstance(instanceId) ?? updatedMeta;
158
- }
159
- function parseComparableVersion(version, label) {
160
- const normalized = version
161
- .trim()
162
- .replace(/^>=\s*/, "")
163
- .replace(/^v/i, "")
164
- .replace(/[-+].*$/, "");
165
- const match = normalized.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?$/);
166
- if (!match) {
167
- throw new Error(`${label} '${version}' 格式无效,应为 x.y.z`);
168
- }
169
- return [
170
- Number(match[1] ?? 0),
171
- Number(match[2] ?? 0),
172
- Number(match[3] ?? 0),
173
- ];
174
- }
175
- function compareVersions(left, right) {
176
- for (let index = 0; index < 3; index++) {
177
- if (left[index] > right[index])
178
- return 1;
179
- if (left[index] < right[index])
180
- return -1;
181
- }
182
- return 0;
183
- }
184
- function requiredJishuShellVersion(spec) {
185
- const required = spec.jishushell?.min_version?.trim();
186
- if (!required) {
187
- throw new Error(`App '${spec.id}' 缺少 jishushell.min_version,请先在 YAML 中声明支持的最低 JishuShell 版本`);
188
- }
189
- return required;
190
- }
191
- function ensureCompatibleJishuShellVersion(spec) {
192
- const required = requiredJishuShellVersion(spec);
193
- const current = parseComparableVersion(CURRENT_JISHUSHELL_VERSION, "当前 JishuShell 版本");
194
- const minimum = parseComparableVersion(required, "jishushell.min_version");
195
- if (compareVersions(current, minimum) < 0) {
196
- throw new Error(`当前 JishuShell 版本 ${CURRENT_JISHUSHELL_VERSION} 低于应用要求 ${required},请先升级 JishuShell`);
197
- }
198
- }
199
- function expandPath(p) {
200
- if (/^~\/\.jishushell(?:\/|$)/.test(p)) {
201
- return p.replace(/^~\/\.jishushell(?=\/|$)/, JISHUSHELL_HOME);
202
- }
203
- return p.replace(/^~(?=\/|$)/, process.env.HOME ?? homedir());
204
- }
205
- function buildDeterministicPath(basePath, extraPaths = []) {
206
- return [basePath ?? "", DEFAULT_LIFECYCLE_PATH, dirname(process.execPath), ...extraPaths]
207
- .flatMap((entry) => entry.split(":"))
208
- .map((entry) => entry.trim())
209
- .filter(Boolean)
210
- .filter((entry, index, entries) => entries.indexOf(entry) === index)
211
- .join(":");
212
- }
213
- function buildLifecycleEnv() {
214
- const extraPaths = process.platform === "darwin"
215
- ? MACOS_LIFECYCLE_PATH_PROBES.filter((entry) => existsSync(entry))
216
- : [];
217
- const mergedPath = buildDeterministicPath(process.env.PATH, extraPaths);
218
- // Surface core callback hooks to lifecycle scripts. post_start can curl
219
- // ${JISHUSHELL_CORE_URL}/api/internal/* with the internal token to read
220
- // core-managed state (default provider creds etc.) and self-configure
221
- // without going through user JWT. Best-effort: if the token file or
222
- // port lookup fails we just omit the vars and the script gets a clean
223
- // "missing env" failure path.
224
- const coreHooks = {};
225
- try {
226
- coreHooks.JISHUSHELL_CORE_URL = `http://127.0.0.1:${config.getCorePort()}`;
227
- coreHooks.JISHUSHELL_INTERNAL_TOKEN = config.getInternalMcpToken();
228
- // LAN host that *other* services (and the post_start script's curls into
229
- // its own service) can reach. AnythingLLM binds eth0 via Nomad's
230
- // `external` host_network, so the panel-host loopback (127.0.0.1) won't
231
- // reach 18097 — post_start needs to hit the LAN IP to check its own
232
- // health. getCoreLanHost() returns the same IP Nomad publishes ports on.
233
- coreHooks.JISHUSHELL_LAN_HOST = config.getCoreLanHost();
234
- }
235
- catch {
236
- // tolerate — only post_start cares
237
- }
238
- return {
239
- ...process.env,
240
- HOME: process.env.HOME ?? homedir(),
241
- PATH: mergedPath,
242
- ...coreHooks,
243
- };
244
- }
245
- function buildSudoWrappedCommand(cmd, args, env, execOptions) {
246
- const sudoArgs = execOptions?.sudoPassword ? ["-k", "-A"] : ["-n"];
247
- const envArgs = SUDO_PASSTHROUGH_ENV_KEYS.flatMap((key) => {
248
- const value = env[key];
249
- return typeof value === "string" && value.length > 0 ? [`${key}=${value}`] : [];
250
- });
251
- return {
252
- command: "sudo",
253
- args: [...sudoArgs, "--", "env", ...envArgs, cmd, ...args],
254
- };
255
- }
256
- function createLifecycleSudoError(stderr, fallbackDisplay, hasPassword) {
257
- const message = sanitizeTaskLine(stderr).trim();
258
- if (isSudoNoNewPrivilegesError(message)) {
259
- return createNoNewPrivilegesSudoError();
260
- }
261
- if (isSudoAuthenticationError(message)) {
262
- return createInvalidSudoPasswordError();
263
- }
264
- if (!hasPassword && isSudoPasswordRequiredError(message)) {
265
- return new Error("该生命周期步骤需要 sudo 密码;请在页面弹窗中输入后重试。");
266
- }
267
- if (message) {
268
- return new Error(message);
269
- }
270
- return new Error(`lifecycle sudo step failed: ${fallbackDisplay}`);
271
- }
272
- function coreSystemdServicePath() {
273
- const override = process.env.JISHUSHELL_CORE_SYSTEMD_SERVICE_PATH?.trim();
274
- return override || "/etc/systemd/system/jishushell.service";
275
- }
276
- function isLikelySystemdServiceProcess() {
277
- return Boolean(process.env.INVOCATION_ID
278
- || process.env.JOURNAL_STREAM
279
- || process.env.NOTIFY_SOCKET
280
- || process.env.JISHUSHELL_CORE_SYSTEMD_SERVICE_PATH?.trim());
281
- }
282
- function maybeRepairCoreAutostartNoNewPrivileges() {
283
- if (!isLikelySystemdServiceProcess())
284
- return null;
285
- const servicePath = coreSystemdServicePath();
286
- if (!existsSync(servicePath))
287
- return null;
288
- let unitText = "";
289
- try {
290
- unitText = readFileSync(servicePath, "utf-8");
291
- }
292
- catch {
293
- return { servicePath, detected: true, updated: false };
294
- }
295
- if (!/^\s*NoNewPrivileges\s*=\s*true\s*$/mi.test(unitText)) {
296
- return null;
297
- }
298
- const nextText = unitText.replace(/^\s*NoNewPrivileges\s*=\s*true\s*\n?/gim, "");
299
- if (nextText === unitText) {
300
- return { servicePath, detected: true, updated: false };
301
- }
302
- try {
303
- writeFileSync(servicePath, nextText);
304
- return { servicePath, detected: true, updated: true };
305
- }
306
- catch {
307
- return { servicePath, detected: true, updated: false };
308
- }
309
- }
310
- function manualInstallCommandForSpec(spec) {
311
- if (spec.id === "ollama-binary") {
312
- return "jishushell app install ollama";
313
- }
314
- const builtin = listBuiltinAppSpecs().find((entry) => entry.id === spec.id);
315
- if (!builtin)
316
- return null;
317
- return `jishushell app install ${spec.id}`;
318
- }
319
- function createNoNewPrivilegesSudoError(manualInstallCommand) {
320
- const repair = maybeRepairCoreAutostartNoNewPrivileges();
321
- const restartCommand = "sudo systemctl daemon-reload && sudo systemctl restart jishushell";
322
- const parts = ["当前运行环境禁止 sudo 提权(no new privileges),面板内无法继续后续安装。"];
323
- if (repair?.updated) {
324
- parts.push(`已从自启文件 ${repair.servicePath} 移除 NoNewPrivileges=true。请在系统终端执行以下命令后重试:\n${restartCommand}`);
325
- }
326
- else if (repair?.detected) {
327
- parts.push(`检测到自启文件 ${repair.servicePath} 仍包含 NoNewPrivileges=true。请在系统终端删除该行后执行:\n${restartCommand}`);
328
- }
329
- if (manualInstallCommand) {
330
- parts.push(`当前安装已停止。你也可以在系统终端手动执行 ${manualInstallCommand}。`);
331
- }
332
- return new Error(parts.join("\n"));
333
- }
334
- function decorateInstallError(error, spec) {
335
- const original = error instanceof Error ? error : new Error(String(error));
336
- if (!isSudoNoNewPrivilegesError(original.message) && !/NoNewPrivileges=true/i.test(original.message)) {
337
- return original;
338
- }
339
- return createNoNewPrivilegesSudoError(manualInstallCommandForSpec(spec) ?? undefined);
340
- }
341
- export async function validateSudoPassword(sudoPassword) {
342
- if (!sudoPassword) {
343
- throw new Error("请输入 sudo 密码");
344
- }
345
- if (typeof process.getuid === "function" && process.getuid() === 0) {
346
- return;
347
- }
348
- const preparedEnv = prepareLifecycleExecEnv({ sudoPassword });
349
- try {
350
- await new Promise((resolve, reject) => {
351
- const child = spawn("sudo", ["-k", "-A", "true"], {
352
- stdio: ["ignore", "ignore", "pipe"],
353
- env: preparedEnv.env,
354
- timeout: 15_000,
355
- });
356
- let stderr = "";
357
- child.stderr?.on("data", (chunk) => {
358
- stderr += chunk.toString("utf-8");
359
- });
360
- child.on("close", (code) => {
361
- if (code === 0) {
362
- resolve();
363
- return;
364
- }
365
- const message = sanitizeTaskLine(stderr).trim();
366
- if (isSudoNoNewPrivilegesError(message)) {
367
- reject(createNoNewPrivilegesSudoError());
368
- return;
369
- }
370
- if (isSudoAuthenticationError(message)) {
371
- reject(createInvalidSudoPasswordError());
372
- return;
373
- }
374
- resolve();
375
- });
376
- child.on("error", (_error) => {
377
- resolve();
378
- });
379
- });
380
- }
381
- finally {
382
- preparedEnv.cleanup();
383
- }
384
- }
385
- function prepareLifecycleExecEnv(execOptions, envOverrides) {
386
- const env = {
387
- ...buildLifecycleEnv(),
388
- ...(envOverrides ?? {}),
389
- };
390
- const sudoPassword = execOptions?.sudoPassword;
391
- if (!sudoPassword) {
392
- return { env, cleanup: () => undefined };
393
- }
394
- return prepareSudoAskpassEnv(sudoPassword, env);
395
- }
396
- function shouldBypassDockerCredentialHelperForDownloadImage(image) {
397
- const trimmedImage = image.trim();
398
- return ANONYMOUS_DOWNLOAD_IMAGE_ALLOWLIST.has(trimmedImage)
399
- || ANONYMOUS_DOWNLOAD_IMAGE_REPOSITORY_ALLOWLIST.has(normalizeDockerImageRepository(trimmedImage));
400
- }
401
- function normalizeDockerImageRepository(image) {
402
- const digestIndex = image.indexOf("@");
403
- const imageWithoutDigest = digestIndex >= 0 ? image.slice(0, digestIndex) : image;
404
- const lastSlashIndex = imageWithoutDigest.lastIndexOf("/");
405
- const lastColonIndex = imageWithoutDigest.lastIndexOf(":");
406
- return lastColonIndex > lastSlashIndex
407
- ? imageWithoutDigest.slice(0, lastColonIndex)
408
- : imageWithoutDigest;
409
- }
410
- function createAnonymousDockerConfig() {
411
- const dockerConfigDir = mkdtempSync(join(tmpdir(), "jishushell-docker-config-"));
412
- writeFileSync(join(dockerConfigDir, "config.json"), `${JSON.stringify({ auths: {} }, null, 2)}\n`, { mode: 0o600 });
413
- return {
414
- envOverrides: {
415
- DOCKER_CONFIG: dockerConfigDir,
416
- },
417
- cleanup: () => {
418
- try {
419
- rmSync(dockerConfigDir, { recursive: true, force: true });
420
- }
421
- catch {
422
- // best effort cleanup for one-shot anonymous docker config files
423
- }
424
- },
425
- };
426
- }
427
- const ANSI_ESCAPE_RE = /\u001B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
428
- function sanitizeTaskLine(line) {
429
- return line
430
- .replace(ANSI_ESCAPE_RE, "")
431
- .replace(/[\u0000-\u0008\u000B-\u001F\u007F]/g, "")
432
- .trimEnd();
433
- }
434
- function emitInstallTaskLog(task, message) {
435
- if (!task)
436
- return;
437
- const line = sanitizeTaskLine(message).trim();
438
- if (!line)
439
- return;
440
- emitTask(task, { type: "log", message: line });
441
- }
442
- function emitAppTaskProgress(task, message, progress) {
443
- if (!task)
444
- return;
445
- const line = sanitizeTaskLine(message).trim();
446
- if (!line)
447
- return;
448
- emitTask(task, { type: "progress", message: line, ...(typeof progress === "number" ? { progress } : {}) });
449
- }
450
- function normalizePortVisibility(visibility) {
451
- if (!visibility || visibility === "external" || visibility === "public") {
452
- return "external";
453
- }
454
- if (visibility === "internal") {
455
- return visibility;
456
- }
457
- throw new Error(`port visibility '${visibility}' 仅支持 external 或 internal`);
458
- }
459
- function normalizeAppSpec(spec) {
460
- const normalizedProvides = (spec.provides ?? []).map((provide) => {
461
- if (spec.id === "browserless-chromium-container"
462
- && provide.capability === BROWSERLESS_DEBUGGER_CAPABILITY
463
- && (provide.path === "/" || provide.path === "/debugger")) {
464
- return { ...provide, path: "/debugger/" };
465
- }
466
- if (spec.id === "browserless-chromium-container"
467
- && provide.capability === BROWSERLESS_DOCS_CAPABILITY
468
- && provide.path === "/docs") {
469
- return { ...provide, path: "/docs/" };
470
- }
471
- if (spec.id === "browserless-chromium-container"
472
- && provide.capability === BROWSERLESS_API_CAPABILITY
473
- && provide.path !== "/") {
474
- return { ...provide, path: "/" };
475
- }
476
- if (spec.id === "jishu-kb-container"
477
- && provide.capability === "web-jishukb"
478
- && provide.embedded !== "proxy") {
479
- return { ...provide, embedded: "proxy" };
480
- }
481
- return provide;
482
- });
483
- // Inject browserless-api capability for legacy installed specs that lack it.
484
- if (spec.id === "browserless-chromium-container"
485
- && normalizedProvides.length > 0
486
- && !normalizedProvides.some((p) => p.capability === BROWSERLESS_API_CAPABILITY)) {
487
- const debugger_ = normalizedProvides.find((p) => p.capability === BROWSERLESS_DEBUGGER_CAPABILITY);
488
- if (debugger_) {
489
- normalizedProvides.push({
490
- capability: BROWSERLESS_API_CAPABILITY,
491
- port: debugger_.port ?? 3000,
492
- path: "/",
493
- protocol: "http",
494
- description: "Browserless 根 API(供调试器页面连接 ws 与 sessions 接口)",
495
- });
496
- }
497
- }
498
- const normalizedTasks = (spec.tasks ?? []).map((task) => {
499
- const rawTask = { ...task };
500
- if (!rawTask.role) {
501
- rawTask.role = "service";
502
- }
503
- if (!rawTask.command && rawTask.binary) {
504
- rawTask.command = rawTask.binary;
505
- }
506
- if (Array.isArray(rawTask.ports)) {
507
- rawTask.ports = rawTask.ports.map((port) => ({
508
- ...port,
509
- visibility: normalizePortVisibility(port.visibility),
510
- }));
511
- }
512
- if (spec.id === "jishu-kb-container" && rawTask.name === "jishukb") {
513
- const env = rawTask.env && typeof rawTask.env === "object"
514
- ? { ...rawTask.env }
515
- : {};
516
- const frameAncestors = typeof env.JISHU_KB_FRAME_ANCESTORS === "string"
517
- ? env.JISHU_KB_FRAME_ANCESTORS.trim()
518
- : "";
519
- // Keep legacy KB specs iframe-safe even though the embedded UI now runs
520
- // through the capability proxy. Users can still open the direct port
521
- // manually, and older installs may still rely on permissive upstream
522
- // framing until they restart onto the proxied path.
523
- // Repair legacy installed specs here (not via Nomad extraEnv) because
524
- // task.env wins over extraEnv during job materialization.
525
- if (!frameAncestors || frameAncestors === "'self'") {
526
- env.JISHU_KB_FRAME_ANCESTORS = JISHU_KB_FRAME_ANCESTORS_ALLOW_ALL;
527
- }
528
- rawTask.env = env;
529
- }
530
- return rawTask;
531
- });
532
- let normalizedLifecycle = spec.lifecycle ? { ...spec.lifecycle } : undefined;
533
- if (spec.id === "jishu-kb-container" && normalizedLifecycle?.post_start?.length) {
534
- const postStart = normalizedLifecycle.post_start.map((step) => {
535
- if (typeof step?.run !== "string")
536
- return step;
537
- const run = normalizeJishuKbPostStartRun(step.run);
538
- return run === step.run
539
- ? step
540
- : { ...step, run };
541
- });
542
- normalizedLifecycle = {
543
- ...(normalizedLifecycle ?? {}),
544
- post_start: postStart,
545
- };
546
- }
547
- if (spec.id === "browserless-chromium-container") {
548
- const browserlessDataDir = `~/.jishushell/apps/${spec.app_id || spec.id}/data`;
549
- const browserlessDataDirTemplate = "~/.jishushell/apps/${app_id}/data";
550
- const browserlessTask = normalizedTasks.find((task) => task.name === "browserless");
551
- if (browserlessTask) {
552
- const dataDirTarget = "/tmp/browserless-data";
553
- const existingVolumes = Array.isArray(browserlessTask.volumes) ? [...browserlessTask.volumes] : [];
554
- const hasDataDirVolume = existingVolumes.some((volume) => typeof volume === "object" && volume !== null && volume.target === dataDirTarget);
555
- if (!hasDataDirVolume) {
556
- existingVolumes.push({ source: browserlessDataDir, target: dataDirTarget });
557
- }
558
- browserlessTask.volumes = existingVolumes;
559
- }
560
- const install = [...(normalizedLifecycle?.install ?? [])];
561
- if (!install.some((step) => "mkdir" in step && (step.mkdir === browserlessDataDir || step.mkdir === browserlessDataDirTemplate))) {
562
- install.push({ mkdir: browserlessDataDir });
563
- }
564
- const preStart = [...(normalizedLifecycle?.pre_start ?? [])];
565
- if (!preStart.some((step) => "mkdir" in step && (step.mkdir === browserlessDataDir || step.mkdir === browserlessDataDirTemplate))) {
566
- preStart.push({ mkdir: browserlessDataDir });
567
- }
568
- normalizedLifecycle = {
569
- ...(normalizedLifecycle ?? {}),
570
- install,
571
- pre_start: preStart,
572
- };
573
- }
574
- function normalizeJishuKbPostStartRun(run) {
575
- if (run.includes("fetch_default_provider_json()"))
576
- return run;
577
- const marker = 'echo "[post_start] fetching core default provider ..."';
578
- const readyLine = `READY=$(printf '%s' "$DP_JSON" | jget ready)`;
579
- if (!run.includes(marker)
580
- || !run.includes('"$JISHUSHELL_CORE_URL/api/internal/default-provider"')
581
- || !run.includes(`|| echo '{"ready":false,"reason":"core_unreachable"}'`)) {
582
- return run;
583
- }
584
- const start = run.indexOf(marker);
585
- const end = run.indexOf(readyLine, start);
586
- if (start < 0 || end < 0)
587
- return run;
588
- return `${run.slice(0, start)}${JISHU_KB_DEFAULT_PROVIDER_RETRY_BLOCK}${run.slice(end + readyLine.length)}`;
589
- }
590
- return {
591
- ...spec,
592
- ...(spec.provides ? { provides: normalizedProvides } : {}),
593
- tasks: normalizedTasks,
594
- ...(normalizedLifecycle ? { lifecycle: normalizedLifecycle } : {}),
595
- };
596
- }
597
- function imageReferencedByOtherInstalledApps(currentAppId, imagePath) {
598
- return listApps().some((app) => app.manifest.id !== currentAppId && (app.spec.tasks.some((task) => task.image === imagePath)
599
- || app.manifest.artifacts?.some((artifact) => artifact.type === "image" && artifact.path === imagePath)
600
- || app.spec.lifecycle?.install?.some((step) => "downloadImage" in step && step.downloadImage === imagePath)));
601
- }
602
- function shouldDeleteImageForApp(appData, imagePath) {
603
- if (appData.spec.singleInstance === false)
604
- return false;
605
- return !imageReferencedByOtherInstalledApps(appData.manifest.id, imagePath);
606
- }
607
- function selectUninstallLifecycleSteps(appData) {
608
- if (!appData?.spec.lifecycle?.uninstall?.length)
609
- return undefined;
610
- return appData.spec.lifecycle.uninstall.filter((step) => !("deleteImage" in step) || shouldDeleteImageForApp(appData, step.deleteImage));
611
- }
612
- function selectCleanupArtifacts(appData) {
613
- if (!appData?.manifest.artifacts?.length)
614
- return [];
615
- const lifecycleImageDeletes = new Set((selectUninstallLifecycleSteps(appData) ?? [])
616
- .flatMap((step) => ("deleteImage" in step ? [step.deleteImage] : [])));
617
- return appData.manifest.artifacts.filter((artifact) => {
618
- if (artifact.type !== "image")
619
- return true;
620
- if (lifecycleImageDeletes.has(artifact.path))
621
- return false;
622
- return shouldDeleteImageForApp(appData, artifact.path);
623
- });
624
- }
625
- async function deleteLinkedInstances(appId) {
626
- const warnings = [];
627
- const instanceManager = await import("../instance-manager.js");
628
- const linkedInstances = instanceManager
629
- .listInstances()
630
- .filter((instance) => instance?.id && instance.id !== appId && instance.app_id === appId);
631
- if (linkedInstances.length === 0)
632
- return warnings;
633
- const { stopNomadJobInstance: stopInstance } = await import("../nomad-manager.js");
634
- let processManager = null;
635
- try {
636
- processManager = await import("../process-manager.js");
637
- }
638
- catch {
639
- processManager = null;
640
- }
641
- for (const instance of linkedInstances) {
642
- const instanceId = String(instance.id);
643
- let stopFailed = false;
644
- try {
645
- const stopped = await stopInstance(instanceId, true);
646
- if (!stopped.ok) {
647
- const fallback = await stopInstance(instanceId);
648
- if (!fallback.ok)
649
- stopFailed = true;
650
- }
651
- }
652
- catch {
653
- stopFailed = true;
654
- }
655
- if (processManager) {
656
- try {
657
- if ((await processManager.getLegacyStatus(instanceId)).status === "running") {
658
- await processManager.stopInstance(instanceId);
659
- stopFailed = false;
660
- }
661
- }
662
- catch {
663
- // best effort
664
- }
665
- }
666
- const result = await instanceManager.deleteInstance(instanceId);
667
- if (!result.ok) {
668
- warnings.push(`关联实例 '${instanceId}' 删除失败`);
669
- }
670
- if (result.warnings?.length) {
671
- warnings.push(...result.warnings.map((warning) => `关联实例 '${instanceId}': ${warning}`));
672
- }
673
- if (stopFailed) {
674
- warnings.push(`关联实例 '${instanceId}' 停止失败,可能残留进程`);
675
- }
676
- }
677
- return warnings;
678
- }
679
- function getProvidePort(spec, provide) {
680
- if (typeof provide.url === "string" && provide.url.trim()) {
681
- return null;
682
- }
683
- if (typeof provide.port === "number") {
684
- return provide.port;
685
- }
686
- const firstPort = spec.tasks.find((task) => task.role === "service")?.ports?.[0];
687
- if (!firstPort)
688
- return null;
689
- const p = firstPort.host_port ?? firstPort.port;
690
- return typeof p === "number" && p > 0 ? p : null;
691
- }
692
- function getProvideUrl(provide) {
693
- const raw = typeof provide.url === "string" ? provide.url.trim() : "";
694
- if (!raw)
695
- return null;
696
- try {
697
- const parsed = new URL(raw);
698
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
699
- return null;
700
- }
701
- return parsed.toString();
702
- }
703
- catch {
704
- return null;
705
- }
706
- }
707
- function buildCapabilityAddress(port, path) {
708
- const host = port > 0 ? instanceServices.getAdvertisedHostForPort(port) : "127.0.0.1";
709
- if (!path) {
710
- return `${host}:${port}`;
711
- }
712
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
713
- return `${host}:${port}${normalizedPath}`;
714
- }
715
- // docker0 bridge gateway (host.docker.internal target). A port published only
716
- // on this address is reachable by peer containers but NOT by a remote browser,
717
- // so it must never be advertised as the host for an embedded UI iframe.
718
- function isDockerBridgeHost(host) {
719
- return host === "172.17.0.1";
720
- }
721
- function normalizeProvideProtocol(protocol) {
722
- const raw = typeof protocol === "string" ? protocol.trim().toLowerCase() : "";
723
- return raw || "http";
724
- }
725
- function resolveProvideProtocol(provide) {
726
- const url = getProvideUrl(provide);
727
- if (url) {
728
- try {
729
- return new URL(url).protocol.replace(/:$/, "").toLowerCase();
730
- }
731
- catch {
732
- return normalizeProvideProtocol(provide.protocol);
733
- }
734
- }
735
- return normalizeProvideProtocol(provide.protocol);
736
- }
737
- function collectDeclaredPorts(spec) {
738
- return spec.tasks.flatMap((task) => (task.ports ?? [])
739
- .map((port) => port.host_port ?? port.port)
740
- .filter((port) => Number.isInteger(port) && port > 0 && port <= 65535));
741
- }
742
- function buildPortShiftMap(spec, offset) {
743
- const portShiftMap = new Map();
744
- for (const port of collectDeclaredPorts(spec)) {
745
- const shiftedPort = port + offset;
746
- if (shiftedPort < 1 || shiftedPort > 65535) {
747
- throw new Error(`App '${spec.id}' 没有可用端口,端口递增已超出 65535`);
748
- }
749
- portShiftMap.set(port, shiftedPort);
750
- }
751
- return portShiftMap;
752
- }
753
- function replaceAppScopedPaths(value, baseId, appId) {
754
- if (baseId === appId)
755
- return value;
756
- const homeDir = homedir();
757
- const replacements = [
758
- [`~/.jishushell/apps/${baseId}`, `~/.jishushell/apps/${appId}`],
759
- [`$HOME/.jishushell/apps/${baseId}`, `$HOME/.jishushell/apps/${appId}`],
760
- [`${homeDir}/.jishushell/apps/${baseId}`, `${homeDir}/.jishushell/apps/${appId}`],
761
- [`\${HOME}/.jishushell/apps/${baseId}`, `\${HOME}/.jishushell/apps/${appId}`],
762
- ];
763
- let rewritten = value;
764
- for (const [from, to] of replacements) {
765
- rewritten = rewritten.split(from).join(to);
766
- }
767
- return rewritten;
768
- }
769
- function replaceAppIdTokens(value, appId) {
770
- return value
771
- .replace(/\$\{app_id\}/g, appId)
772
- .replace(/\$\{app\.id\}/g, appId);
773
- }
774
- function replacePortTokens(value, portShiftMap) {
775
- let rewritten = value;
776
- const replacements = [...portShiftMap.entries()].sort((left, right) => right[0] - left[0]);
777
- for (const [fromPort, toPort] of replacements) {
778
- const pattern = new RegExp(`(^|[^0-9A-Za-z])${fromPort}($|[^0-9A-Za-z])`, "g");
779
- rewritten = rewritten.replace(pattern, (_match, prefix, suffix) => `${prefix}${toPort}${suffix}`);
780
- }
781
- return rewritten;
782
- }
783
- function rewriteInstalledSpecValue(value, baseId, appId, portShiftMap) {
784
- if (typeof value === "string") {
785
- const hasAppIdToken = value.includes("${app_id}") || value.includes("${app.id}");
786
- return replacePortTokens(hasAppIdToken
787
- ? replaceAppIdTokens(value, appId)
788
- : replaceAppScopedPaths(value, baseId, appId), portShiftMap);
789
- }
790
- if (typeof value === "number") {
791
- return portShiftMap.get(value) ?? value;
792
- }
793
- if (Array.isArray(value)) {
794
- return value.map((entry) => rewriteInstalledSpecValue(entry, baseId, appId, portShiftMap));
795
- }
796
- if (value && typeof value === "object") {
797
- const rewritten = {};
798
- for (const [key, entry] of Object.entries(value)) {
799
- rewritten[key] = rewriteInstalledSpecValue(entry, baseId, appId, portShiftMap);
800
- }
801
- return rewritten;
802
- }
803
- return value;
804
- }
805
- function materializeInstalledSpec(spec, appId, offset) {
806
- const portShiftMap = buildPortShiftMap(spec, offset);
807
- const rewritten = rewriteInstalledSpecValue(spec, spec.id, appId, portShiftMap);
808
- const derivedName = deriveInstalledDisplayName(spec, appId);
809
- const normalized = normalizeAppSpec({
810
- ...rewritten,
811
- id: spec.id,
812
- ...(derivedName ? { name: derivedName } : {}),
813
- });
814
- // Final step: drop spec fields that work on the spec author's Linux
815
- // baseline but break on the host platform (e.g. host_network:
816
- // docker_bridge / user: "host" on darwin). Identity pass-through on
817
- // Linux — see platform-transform.ts for the rule list.
818
- return platformTransformSpec(normalized);
819
- }
820
- function deriveInstalledDisplayName(spec, appId) {
821
- const baseName = typeof spec.name === "string" && spec.name.trim() ? spec.name.trim() : spec.id;
822
- if (!baseName)
823
- return appId;
824
- if (appId === spec.id)
825
- return baseName;
826
- if (!appId.startsWith(`${spec.id}-`))
827
- return baseName;
828
- const suffix = appId.slice(spec.id.length + 1).trim();
829
- return suffix ? `${baseName}-${suffix}` : baseName;
830
- }
831
- function formatInstalledAppId(baseId, instancePrefix, offset) {
832
- if (offset === 0)
833
- return baseId;
834
- return `${baseId}-${instancePrefix}${offset}`;
835
- }
836
- // Delegate to the canonical multi-locus probe in instance-manager so macOS
837
- // loopback-only listeners (e.g. a natively-installed openclaw bound to
838
- // 127.0.0.1:18789) are visible to AppSpec port conflict detection as well.
839
- // Wrap in a thunk so the lookup happens at call time — some test suites
840
- // partially mock instance-manager without re-exporting this symbol.
841
- function isPortInUse(port) {
842
- return instanceServices.isPortInUse(port);
843
- }
844
- async function resolveInstallTarget(spec, originalSpecYaml, requestedAppId) {
845
- const baseId = spec.id;
846
- const multiInstance = spec.singleInstance === false;
847
- const explicitAppId = requestedAppId?.trim();
848
- if (explicitAppId && !APP_ID_RE.test(explicitAppId)) {
849
- throw new Error(`App id '${explicitAppId}' 格式无效,必须符合 /^[a-z0-9][a-z0-9-]{0,62}$/`);
850
- }
851
- const installedSameSpec = listApps().find((app) => app.spec.id === baseId);
852
- if (!multiInstance) {
853
- if (installedSameSpec) {
854
- const err = new Error(`App '${baseId}' 已安装为 '${installedSameSpec.manifest.id}',如需多实例请设置 singleInstance: false`);
855
- err.code = 409;
856
- throw err;
857
- }
858
- const appId = explicitAppId ?? baseId;
859
- if (appId !== baseId && resolveAppDir(appId)) {
860
- const err = new Error(`App '${appId}' 已安装`);
861
- err.code = 409;
862
- throw err;
863
- }
864
- const installedSpec = materializeInstalledSpec(spec, appId, 0);
865
- return {
866
- appId,
867
- installedSpec,
868
- installedSpecYaml: stringify(installedSpec),
869
- };
870
- }
871
- const usedPorts = new Set();
872
- for (const installedApp of listApps()) {
873
- for (const port of collectDeclaredPorts(installedApp.spec)) {
874
- usedPorts.add(port);
875
- }
876
- }
877
- const instancePrefix = spec.instancePrefix ?? "";
878
- const declaredPorts = collectDeclaredPorts(spec);
879
- const maxBasePort = declaredPorts.length > 0 ? Math.max(...declaredPorts) : 0;
880
- const maxOffset = declaredPorts.length > 0 ? 65535 - maxBasePort : 9999;
881
- if (explicitAppId) {
882
- if (resolveAppDir(explicitAppId)) {
883
- const err = new Error(`App '${explicitAppId}' 已安装`);
884
- err.code = 409;
885
- throw err;
886
- }
887
- for (let offset = 0; offset <= maxOffset; offset++) {
888
- const installedSpec = materializeInstalledSpec(spec, explicitAppId, offset);
889
- const candidatePorts = collectDeclaredPorts(installedSpec);
890
- let portConflict = false;
891
- for (const port of candidatePorts) {
892
- if (usedPorts.has(port) || await isPortInUse(port)) {
893
- portConflict = true;
894
- break;
895
- }
896
- }
897
- if (portConflict)
898
- continue;
899
- return {
900
- appId: explicitAppId,
901
- installedSpec,
902
- installedSpecYaml: stringify(installedSpec),
903
- };
904
- }
905
- throw new Error(`App '${baseId}' 使用指定 id '${explicitAppId}' 时没有可用端口槽位`);
906
- }
907
- for (let offset = 0; offset <= maxOffset; offset++) {
908
- const appId = formatInstalledAppId(baseId, instancePrefix, offset);
909
- if (!APP_ID_RE.test(appId)) {
910
- throw new Error(`自动生成的 App id '${appId}' 超出格式限制,请缩短 id 或 instancePrefix`);
911
- }
912
- if (existsSync(appDirForId(appId)))
913
- continue;
914
- const installedSpec = materializeInstalledSpec(spec, appId, offset);
915
- const candidatePorts = collectDeclaredPorts(installedSpec);
916
- let portConflict = false;
917
- for (const port of candidatePorts) {
918
- if (usedPorts.has(port) || await isPortInUse(port)) {
919
- portConflict = true;
920
- break;
921
- }
922
- }
923
- if (portConflict)
924
- continue;
925
- return {
926
- appId,
927
- installedSpec,
928
- // Always re-serialize from `installedSpec` so the cached yaml
929
- // reflects every transform `materializeInstalledSpec` ran —
930
- // including the platform pass that strips host_network/user fields
931
- // on darwin. Using `originalSpecYaml` for offset=0 (the previous
932
- // behavior) bypassed the transformer for multi-instance apps'
933
- // first install slot and re-introduced the Linux-only fields on
934
- // disk; subsequent core restarts then re-read the raw source via
935
- // `loadInstalledAppSpec` and broke macOS placement again.
936
- installedSpecYaml: stringify(installedSpec),
937
- };
938
- }
939
- throw new Error(`App '${baseId}' 没有可用安装槽位,目录名或端口已全部占用`);
940
- }
941
- function spawnStep(label, display, cmd, args, task) {
942
- return spawnStepWithTimeout(label, display, display, cmd, args, 300_000, task);
943
- }
944
- const DOCKER_PULL_RETRY_ATTEMPTS = 3;
945
- // spawnStep's 5-min default is fine for small CLI calls but starves real-world
946
- // image pulls on ARM + SD-card hardware. Playwright v1.55.0-noble is ~2.3 GB
947
- // compressed (bundles Chromium + Firefox + WebKit), Hermes ~2.4 GB — neither
948
- // extracts in 5 min on a Raspberry Pi. 30 min clears both with headroom while
949
- // still capping runaway failures (total retry budget 90 min).
950
- const DOCKER_PULL_TIMEOUT_MS = 1_800_000;
951
- // Separate from the total timeout above: if docker pull stops producing any
952
- // stdout/stderr for long enough, treat it as stalled and retry rather than
953
- // waiting the full 30 minutes.
954
- //
955
- // Why 600s (not 180s): on slow links (especially Docker Hub from China to
956
- // edge devices), large single layers — AnythingLLM ~500MB, Playwright/Hermes
957
- // ~2.3GB — can spend 5-8 minutes between progress lines without TTY. 180s
958
- // idle was killing pulls that were actually making progress, then rolling
959
- // back the partially-completed image. Layer cache is preserved across
960
- // retries (docker dedupes by layer sha), so a higher idle ceiling lets each
961
- // big layer finish without throwing away the layers already on disk.
962
- const DOCKER_PULL_IDLE_TIMEOUT_MS = 600_000;
963
- async function pullDockerImageStep(label, image, display, task, timeoutMs = DOCKER_PULL_TIMEOUT_MS, idleTimeoutMs) {
964
- if (await dockerImageExists(image)) {
965
- const skipMessage = `[lifecycle:${label}] docker image '${image}' already exists locally; skipping pull`;
966
- process.stdout.write(` ${skipMessage}\n`);
967
- emitInstallTaskLog(task, skipMessage);
968
- return;
969
- }
970
- const anonymousDockerConfig = shouldBypassDockerCredentialHelperForDownloadImage(image)
971
- ? createAnonymousDockerConfig()
972
- : null;
973
- let lastError;
974
- try {
975
- for (let attempt = 1; attempt <= DOCKER_PULL_RETRY_ATTEMPTS; attempt++) {
976
- try {
977
- await spawnStepWithTimeout(label, display, display, "docker", ["pull", image], timeoutMs, task, undefined, undefined, {
978
- idleTimeoutMs: idleTimeoutMs ?? Math.min(DOCKER_PULL_IDLE_TIMEOUT_MS, timeoutMs),
979
- envOverrides: anonymousDockerConfig?.envOverrides,
980
- stallMessageHint: "Docker credential resolution (for example docker-credential-desktop) may be involved.",
981
- });
982
- return;
983
- }
984
- catch (error) {
985
- if (await dockerImageExists(image)) {
986
- const recoveredMessage = `[lifecycle:${label}] docker image '${image}' is present locally after pull failure/timeout; treating step as successful`;
987
- process.stdout.write(` ${recoveredMessage}\n`);
988
- emitInstallTaskLog(task, recoveredMessage);
989
- return;
990
- }
991
- lastError = error;
992
- if (attempt === DOCKER_PULL_RETRY_ATTEMPTS) {
993
- break;
994
- }
995
- const reason = error instanceof Error ? error.message : String(error);
996
- const retryMessage = `[lifecycle:${label}] docker pull failed for ${image} (attempt ${attempt}/${DOCKER_PULL_RETRY_ATTEMPTS}): ${reason}; retrying`;
997
- process.stdout.write(` ${retryMessage}\n`);
998
- emitInstallTaskLog(task, retryMessage);
999
- }
1000
- }
1001
- }
1002
- finally {
1003
- anonymousDockerConfig?.cleanup();
1004
- }
1005
- throw (lastError instanceof Error ? lastError : new Error(String(lastError)));
1006
- }
1007
- /**
1008
- * In-flight background pulls keyed by image tag. Used by
1009
- * `pullDockerImageInBackground` to dedup concurrent requests so repeated
1010
- * UI clicks for the same not-yet-local image don't queue duplicate pulls.
1011
- */
1012
- const inFlightBackgroundPulls = new Map();
1013
- /**
1014
- * Fire-and-forget background docker pull. Used by `*-manager.ts`
1015
- * (custom / hermes / ollama) when an instance is requested with an image
1016
- * that's not yet local: kicks off a pull in the background and the caller
1017
- * asks the user to retry later (HTTP 202).
1018
- *
1019
- * Wraps the same `pullDockerImageStep` used by full installs, so it
1020
- * inherits the same protections — total timeout (30 min), idle timeout
1021
- * (10 min of silence on the pull's stdout/stderr terminates it), 3
1022
- * retries, and a post-failure `dockerImageExists` recovery check — that
1023
- * a bare `exec("docker pull ...", { timeout: 0 })` lacks. Docker Desktop
1024
- * on macOS can hang the CLI for tens of minutes after the image has been
1025
- * fully downloaded (the final manifest/tag write step occasionally
1026
- * deadlocks); the idle timer kills the hung CLI and the recovery check
1027
- * picks up the image if the tag did make it through.
1028
- *
1029
- * Concurrent calls for the same image dedup onto a single in-flight pull
1030
- * so repeated UI clicks don't waste retries.
1031
- *
1032
- * Always resolves — errors are logged via console.warn so the
1033
- * fire-and-forget caller doesn't have to attach a `.catch` handler.
1034
- */
1035
- export function pullDockerImageInBackground(label, image) {
1036
- const existing = inFlightBackgroundPulls.get(image);
1037
- if (existing)
1038
- return existing;
1039
- const promise = (async () => {
1040
- try {
1041
- await pullDockerImageStep(`${label}-background`, image, `docker pull ${image} (background)`);
1042
- }
1043
- catch (err) {
1044
- const reason = err instanceof Error ? err.message : String(err);
1045
- console.warn(`[${label}] Background pull of ${image} failed: ${reason}`);
1046
- }
1047
- finally {
1048
- inFlightBackgroundPulls.delete(image);
1049
- }
1050
- })();
1051
- inFlightBackgroundPulls.set(image, promise);
1052
- return promise;
1053
- }
1054
- async function dockerImageExists(image) {
1055
- return new Promise((resolve) => {
1056
- const child = spawn("docker", ["image", "inspect", image], {
1057
- stdio: "ignore",
1058
- env: buildLifecycleEnv(),
1059
- });
1060
- let settled = false;
1061
- const finish = (value) => {
1062
- if (settled)
1063
- return;
1064
- settled = true;
1065
- resolve(value);
1066
- };
1067
- // `docker image inspect` is a local-only daemon API call that should
1068
- // return in milliseconds. In practice, when dockerd itself wedges
1069
- // (the same failure mode that causes `docker pull` to hang), `docker
1070
- // image inspect` hangs too. Without this timeout the inspect call
1071
- // ties up the install lifecycle's post-failure recovery check, which
1072
- // in turn keeps the install lock held indefinitely. 10s is generous
1073
- // for a healthy daemon and short enough that a wedged daemon doesn't
1074
- // block install retry/recovery.
1075
- const watchdog = setTimeout(() => {
1076
- try {
1077
- child.kill("SIGKILL");
1078
- }
1079
- catch {
1080
- /* already exited */
1081
- }
1082
- finish(false);
1083
- }, 10_000);
1084
- watchdog.unref?.();
1085
- child.on("close", (code) => {
1086
- clearTimeout(watchdog);
1087
- finish(code === 0);
1088
- });
1089
- child.on("error", () => {
1090
- clearTimeout(watchdog);
1091
- finish(false);
1092
- });
1093
- });
1094
- }
1095
- /**
1096
- * Force-terminate a spawned child: send `SIGTERM`, then escalate to
1097
- * `SIGKILL` after a grace period if the process is still alive.
1098
- *
1099
- * Needed because some CLIs (notably `docker` on macOS when its daemon
1100
- * has wedged) block on a daemon round-trip and ignore `SIGTERM` until
1101
- * the daemon responds. Without `SIGKILL` escalation the parent
1102
- * `child.on("close")` handler never fires, the lifecycle promise never
1103
- * resolves, and the install task hangs forever holding its lock.
1104
- *
1105
- * The grace period must be long enough for well-behaved children to
1106
- * flush buffers and exit cleanly on SIGTERM (avoid corrupt artifacts in
1107
- * the common case) but short enough that a hung child doesn't waste a
1108
- * meaningful slice of the parent's overall budget. 30s is the sweet
1109
- * spot in practice for docker-class tooling.
1110
- */
1111
- function forceTerminateChild(child, label) {
1112
- try {
1113
- child.kill("SIGTERM");
1114
- }
1115
- catch {
1116
- /* already exited */
1117
- }
1118
- // Unref'd so it doesn't keep the event loop alive. If the child
1119
- // exited cleanly under SIGTERM before this fires, `kill("SIGKILL")`
1120
- // on a dead process is a harmless no-op (and we try/catch around it
1121
- // anyway). We deliberately do NOT register an extra `on("close")`
1122
- // listener to cancel this timer — some lifecycle paths use bare
1123
- // mock children whose `on` is single-slot and a second listener
1124
- // would clobber the parent's reject handler.
1125
- const killTimer = setTimeout(() => {
1126
- if (child.exitCode !== null || child.signalCode !== null)
1127
- return;
1128
- process.stdout.write(` [lifecycle:${label}] child ignored SIGTERM after 30s; escalating to SIGKILL\n`);
1129
- try {
1130
- child.kill("SIGKILL");
1131
- }
1132
- catch {
1133
- /* already exited */
1134
- }
1135
- }, 30_000);
1136
- killTimer.unref?.();
1137
- }
1138
- function spawnStepWithTimeout(label, display, taskDisplay, cmd, args, timeoutMs, task, execOptions, sudo, runOptions) {
1139
- process.stdout.write(` [lifecycle:${label}] ${display}\n`);
1140
- emitInstallTaskLog(task, `[lifecycle:${label}] ${taskDisplay}`);
1141
- return new Promise((resolve, reject) => {
1142
- const preparedEnv = prepareLifecycleExecEnv(sudo ? execOptions : undefined, runOptions?.envOverrides);
1143
- let cleaned = false;
1144
- let heartbeatTimer = null;
1145
- let idleTimer = null;
1146
- let stdoutPending = "";
1147
- let stderrPending = "";
1148
- let capturedStderr = "";
1149
- let forcedError = null;
1150
- let totalTimeoutTimer = null;
1151
- const cleanupPreparedEnv = () => {
1152
- if (cleaned)
1153
- return;
1154
- cleaned = true;
1155
- if (heartbeatTimer) {
1156
- clearInterval(heartbeatTimer);
1157
- heartbeatTimer = null;
1158
- }
1159
- if (idleTimer) {
1160
- clearTimeout(idleTimer);
1161
- idleTimer = null;
1162
- }
1163
- if (totalTimeoutTimer) {
1164
- clearTimeout(totalTimeoutTimer);
1165
- totalTimeoutTimer = null;
1166
- }
1167
- preparedEnv.cleanup();
1168
- };
1169
- const spawnTarget = sudo
1170
- ? buildSudoWrappedCommand(cmd, args, preparedEnv.env, execOptions)
1171
- : { command: cmd, args };
1172
- const captureOutput = Boolean(task) || Boolean(sudo) || Boolean(runOptions?.idleTimeoutMs);
1173
- const child = spawn(spawnTarget.command, spawnTarget.args, {
1174
- stdio: captureOutput ? ["ignore", "pipe", "pipe"] : "inherit",
1175
- // Node's spawn `timeout` sends SIGTERM at the deadline but has no
1176
- // SIGKILL escalation, so a child that ignores SIGTERM (docker CLI
1177
- // on macOS when its daemon is wedged) still blocks the parent's
1178
- // `child.on("close")`. We keep this here so callers / tests can
1179
- // observe the configured budget on the spawn options, and add the
1180
- // `totalKillEscalationTimer` below to SIGKILL after a grace.
1181
- timeout: timeoutMs,
1182
- env: preparedEnv.env,
1183
- });
1184
- // SIGKILL escalation for the total-timeout case. Node's `timeout`
1185
- // option above will have sent SIGTERM at `timeoutMs`; if the child
1186
- // hasn't exited within `timeoutMs + 30s` we force it. Independent
1187
- // of the idle timer's escalation (which `forceTerminateChild`
1188
- // handles inline) — covers the path where output keeps flowing but
1189
- // the total budget is blown.
1190
- totalTimeoutTimer = setTimeout(() => {
1191
- if (child.exitCode !== null || child.signalCode !== null)
1192
- return;
1193
- const totalSeconds = Math.max(1, Math.round(timeoutMs / 1000));
1194
- const timeoutMessage = `[lifecycle:${label}] child ignored spawn-timeout SIGTERM ${totalSeconds}s+30s ago; escalating to SIGKILL`;
1195
- process.stdout.write(` ${timeoutMessage}\n`);
1196
- emitInstallTaskLog(task, timeoutMessage);
1197
- if (!forcedError) {
1198
- forcedError = new Error(`lifecycle '${label}' step exceeded ${totalSeconds}s total budget: ${display}`);
1199
- }
1200
- try {
1201
- child.kill("SIGKILL");
1202
- }
1203
- catch {
1204
- /* already exited */
1205
- }
1206
- }, timeoutMs + 30_000);
1207
- totalTimeoutTimer.unref?.();
1208
- const resetIdleTimer = () => {
1209
- if (!runOptions?.idleTimeoutMs)
1210
- return;
1211
- if (idleTimer)
1212
- clearTimeout(idleTimer);
1213
- idleTimer = setTimeout(() => {
1214
- const idleSeconds = Math.max(1, Math.round(runOptions.idleTimeoutMs / 1000));
1215
- const stallMessageSuffix = runOptions.stallMessageHint ? ` ${runOptions.stallMessageHint}` : "";
1216
- const stallMessage = `[lifecycle:${label}] no output for ${idleSeconds}s; terminating stalled step: ${taskDisplay}${stallMessageSuffix}`;
1217
- process.stdout.write(` ${stallMessage}\n`);
1218
- emitInstallTaskLog(task, stallMessage);
1219
- forcedError = new Error(`lifecycle '${label}' step stalled after ${idleSeconds}s with no output: ${display}${stallMessageSuffix}`);
1220
- forceTerminateChild(child, label);
1221
- }, runOptions.idleTimeoutMs);
1222
- idleTimer.unref?.();
1223
- };
1224
- resetIdleTimer();
1225
- if (captureOutput) {
1226
- const startedAt = Date.now();
1227
- const flushPendingLine = (line) => {
1228
- if (!task)
1229
- return;
1230
- emitInstallTaskLog(task, line);
1231
- };
1232
- const handleChunk = (chunk, stream) => {
1233
- resetIdleTimer();
1234
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
1235
- if (stream === "stderr") {
1236
- capturedStderr += text;
1237
- }
1238
- if (!task) {
1239
- if (stream === "stdout")
1240
- process.stdout.write(text);
1241
- else
1242
- process.stderr.write(text);
1243
- return;
1244
- }
1245
- const normalized = `${stream === "stdout" ? stdoutPending : stderrPending}${text}`
1246
- .replace(/\r\n/g, "\n")
1247
- .replace(/\r/g, "\n");
1248
- const lines = normalized.split("\n");
1249
- const pending = lines.pop() ?? "";
1250
- if (stream === "stdout")
1251
- stdoutPending = pending;
1252
- else
1253
- stderrPending = pending;
1254
- for (const line of lines) {
1255
- flushPendingLine(line);
1256
- }
1257
- };
1258
- child.stdout?.on("data", (data) => handleChunk(data, "stdout"));
1259
- child.stderr?.on("data", (data) => handleChunk(data, "stderr"));
1260
- if (task) {
1261
- heartbeatTimer = setInterval(() => {
1262
- const elapsedSeconds = Math.max(1, Math.round((Date.now() - startedAt) / 1000));
1263
- emitInstallTaskLog(task, `[lifecycle:${label}] still running (${elapsedSeconds}s): ${taskDisplay}`);
1264
- }, 10_000);
1265
- child.on("close", () => {
1266
- flushPendingLine(stdoutPending);
1267
- flushPendingLine(stderrPending);
1268
- });
1269
- }
1270
- }
1271
- child.on("close", (code) => {
1272
- cleanupPreparedEnv();
1273
- if (forcedError)
1274
- reject(forcedError);
1275
- else if (code === 0)
1276
- resolve();
1277
- else if (sudo)
1278
- reject(createLifecycleSudoError(capturedStderr, display, Boolean(execOptions?.sudoPassword)));
1279
- else
1280
- reject(new Error(`lifecycle '${label}' step failed (exit ${code ?? 1}): ${display}`));
1281
- });
1282
- child.on("error", (err) => {
1283
- cleanupPreparedEnv();
1284
- if (forcedError) {
1285
- reject(forcedError);
1286
- return;
1287
- }
1288
- if (sudo && err.code === "ENOENT") {
1289
- reject(new Error("当前环境未检测到 sudo,无法执行需要 sudo 的生命周期步骤。请以 root 身份重试。"));
1290
- return;
1291
- }
1292
- reject(new Error(`lifecycle '${label}' step error: ${err.message}`));
1293
- });
1294
- });
1295
- }
1296
- function lifecycleRunStepDisplay(label, index) {
1297
- if (label === "pre_install") {
1298
- return `run step ${index + 1}`;
1299
- }
1300
- return "$";
1301
- }
1302
- async function commandExists(command) {
1303
- return new Promise((resolve) => {
1304
- const quoted = command.replace(/'/g, "'\\''");
1305
- const child = spawn("sh", ["-c", `command -v '${quoted}' > /dev/null 2>&1`], {
1306
- stdio: "ignore",
1307
- env: buildLifecycleEnv(),
1308
- });
1309
- child.on("close", (code) => resolve(code === 0));
1310
- child.on("error", () => resolve(false));
1311
- });
1312
- }
1313
- // Recursively chown a path. Owner format is "uid:gid" (numeric only, e.g.
1314
- // "0:0" or "1000:1000"). Used by container apps whose images run as a
1315
- // different uid than the service user — without this, bind-mounted data
1316
- // dirs end up unwritable for the in-container process and fail with
1317
- // SQLite "readonly database" or chroma init errors.
1318
- function parseOwnerSpec(owner) {
1319
- const m = /^(\d+):(\d+)$/.exec(owner);
1320
- if (!m)
1321
- throw new Error(`chown owner must be "uid:gid" (numeric), got "${owner}"`);
1322
- return { uid: Number(m[1]), gid: Number(m[2]) };
1323
- }
1324
- function chownRecursive(path, uid, gid) {
1325
- chownSync(path, uid, gid);
1326
- let stat;
1327
- try {
1328
- stat = lstatSync(path);
1329
- }
1330
- catch {
1331
- return;
1332
- }
1333
- if (!stat.isDirectory())
1334
- return;
1335
- for (const entry of readdirSync(path)) {
1336
- chownRecursive(join(path, entry), uid, gid);
1337
- }
1338
- }
1339
- /**
1340
- * Try chowning via direct fs syscall first; on EPERM (core runs as a
1341
- * non-root user with no CAP_CHOWN) fall back to `sudo -n chown`. The
1342
- * fallback only succeeds where passwordless sudo is configured for the
1343
- * service user (the canonical Pi setup); on other hosts the original
1344
- * EPERM bubbles up as a clear error.
1345
- */
1346
- function chownWithSudoFallback(path, uid, gid, recursive) {
1347
- try {
1348
- if (recursive)
1349
- chownRecursive(path, uid, gid);
1350
- else
1351
- chownSync(path, uid, gid);
1352
- return;
1353
- }
1354
- catch (e) {
1355
- if (e?.code !== "EPERM" && e?.code !== "EACCES")
1356
- throw e;
1357
- }
1358
- const args = [
1359
- "-n",
1360
- "chown",
1361
- ...(recursive ? ["-R"] : []),
1362
- `${uid}:${gid}`,
1363
- path,
1364
- ];
1365
- const r = spawnSync("sudo", args, { stdio: ["ignore", "ignore", "pipe"] });
1366
- if (r.status !== 0) {
1367
- const stderr = r.stderr ? r.stderr.toString().trim() : "";
1368
- throw new Error(`chown ${recursive ? "-R " : ""}${uid}:${gid} ${path} failed: service user lacks CAP_CHOWN and passwordless sudo also failed${stderr ? `: ${stderr}` : ""}`);
1369
- }
1370
- }
1371
- async function downloadBinaryStep(label, url, dest, chmod, task) {
1372
- const expanded = expandPath(dest);
1373
- process.stdout.write(` [lifecycle:${label}] downloadBinary: ${url} → ${expanded}\n`);
1374
- emitInstallTaskLog(task, `[lifecycle:${label}] downloadBinary: ${url} -> ${expanded}`);
1375
- mkdirSync(dirname(expanded), { recursive: true });
1376
- const tmp = expanded + ".tmp";
1377
- const res = await fetch(url);
1378
- if (!res.ok)
1379
- throw new Error(`downloadBinary: HTTP ${res.status} ${url}`);
1380
- const buf = await res.arrayBuffer();
1381
- writeFileSync(tmp, Buffer.from(buf), { mode: 0o644 });
1382
- renameSync(tmp, expanded);
1383
- if (chmod)
1384
- chmodSync(expanded, parseInt(chmod, 8));
1385
- }
1386
- async function runLifecycleSteps(steps, label, artifacts, task, execOptions) {
1387
- if (!steps || steps.length === 0)
1388
- return;
1389
- for (const [index, step] of steps.entries()) {
1390
- if ("run" in step) {
1391
- if (step.ifFileExists && !existsSync(expandPath(step.ifFileExists))) {
1392
- continue;
1393
- }
1394
- const timeoutMs = step.timeout_ms ?? 300_000;
1395
- const display = label === "pre_install"
1396
- ? lifecycleRunStepDisplay(label, index)
1397
- : `${lifecycleRunStepDisplay(label, index)} ${step.run}`;
1398
- const taskDisplay = `run step ${index + 1}`;
1399
- try {
1400
- await spawnStepWithTimeout(label, display, taskDisplay, "sh", ["-c", step.run], timeoutMs, task, execOptions, step.sudo === true);
1401
- }
1402
- catch (error) {
1403
- if (step.successIfCommandExists && await commandExists(step.successIfCommandExists)) {
1404
- process.stdout.write(` [lifecycle:${label}] command '${step.successIfCommandExists}' detected after failure/timeout; treating step as successful\n`);
1405
- emitInstallTaskLog(task, `[lifecycle:${label}] command '${step.successIfCommandExists}' detected after failure/timeout; treating step as successful`);
1406
- continue;
1407
- }
1408
- throw error;
1409
- }
1410
- }
1411
- else if ("checkNotCommand" in step) {
1412
- const cmd = step.checkNotCommand;
1413
- const found = await commandExists(cmd);
1414
- if (found) {
1415
- throw new Error(step.message ??
1416
- `系统中已检测到 '${cmd}',建议直接使用系统版本,无需重复安装。如需由 Nomad 统一管理,请先卸载系统版本`);
1417
- }
1418
- }
1419
- else if ("downloadImage" in step) {
1420
- await pullDockerImageStep(label, step.downloadImage, `downloadImage: ${step.downloadImage}`, task, step.timeout_ms, step.idle_timeout_ms);
1421
- artifacts?.push({ type: "image", path: step.downloadImage });
1422
- }
1423
- else if ("deleteImage" in step) {
1424
- await spawnStep(label, `deleteImage: ${step.deleteImage}`, "sh", [
1425
- "-c",
1426
- `docker rmi -f ${step.deleteImage} 2>/dev/null || true`,
1427
- ], task);
1428
- }
1429
- else if ("downloadBinary" in step) {
1430
- const { url, dest, chmod } = step.downloadBinary;
1431
- await downloadBinaryStep(label, url, dest, chmod, task);
1432
- artifacts?.push({ type: "binary", path: expandPath(dest) });
1433
- }
1434
- else if ("deleteBinary" in step) {
1435
- const p = expandPath(step.deleteBinary);
1436
- process.stdout.write(` [lifecycle:${label}] deleteBinary: ${p}\n`);
1437
- emitInstallTaskLog(task, `[lifecycle:${label}] deleteBinary: ${p}`);
1438
- try {
1439
- unlinkSync(p);
1440
- }
1441
- catch (e) {
1442
- if (e.code !== "ENOENT")
1443
- throw e;
1444
- }
1445
- }
1446
- else if ("mkdir" in step) {
1447
- const p = expandPath(step.mkdir);
1448
- process.stdout.write(` [lifecycle:${label}] mkdir: ${p}\n`);
1449
- emitInstallTaskLog(task, `[lifecycle:${label}] mkdir: ${p}`);
1450
- mkdirSync(p, { recursive: true });
1451
- artifacts?.push({ type: "dir", path: p });
1452
- }
1453
- else if ("chown" in step) {
1454
- const p = expandPath(step.chown.path);
1455
- const { uid, gid } = parseOwnerSpec(step.chown.owner);
1456
- const recursive = step.chown.recursive !== false;
1457
- const tag = recursive ? "chown -R" : "chown";
1458
- process.stdout.write(` [lifecycle:${label}] ${tag} ${uid}:${gid} ${p}\n`);
1459
- emitInstallTaskLog(task, `[lifecycle:${label}] ${tag} ${uid}:${gid} ${p}`);
1460
- if (!existsSync(p)) {
1461
- // chown only makes sense if the target exists; surface a clear
1462
- // error rather than letting fs throw an opaque ENOENT later.
1463
- throw new Error(`chown target does not exist: ${p}`);
1464
- }
1465
- try {
1466
- chownWithSudoFallback(p, uid, gid, recursive);
1467
- }
1468
- catch (chownErr) {
1469
- // In pre_start, chown failures are non-fatal: on macOS Docker/Colima
1470
- // the VM handles UID mapping for bind-mounts, so the container can
1471
- // write even without matching ownership. Failing fatally here blocks
1472
- // any non-root service user from starting the app.
1473
- if (label === "pre_start") {
1474
- const msg = `[lifecycle:${label}] ${tag} ${uid}:${gid} ${p} failed (non-fatal): ${chownErr.message}`;
1475
- process.stdout.write(` ${msg}\n`);
1476
- emitInstallTaskLog(task, msg);
1477
- }
1478
- else {
1479
- throw chownErr;
1480
- }
1481
- }
1482
- }
1483
- else if ("deleteDir" in step) {
1484
- const p = expandPath(step.deleteDir);
1485
- process.stdout.write(` [lifecycle:${label}] deleteDir: ${p}\n`);
1486
- emitInstallTaskLog(task, `[lifecycle:${label}] deleteDir: ${p}`);
1487
- rmSync(p, { recursive: true, force: true });
1488
- }
1489
- }
1490
- }
1491
- function cleanupArtifacts(artifacts, task) {
1492
- for (let i = artifacts.length - 1; i >= 0; i--) {
1493
- const a = artifacts[i];
1494
- try {
1495
- switch (a.type) {
1496
- case "image":
1497
- process.stdout.write(` [cleanup] removing image: ${a.path}\n`);
1498
- emitInstallTaskLog(task, `[cleanup] removing image: ${a.path}`);
1499
- spawn("docker", ["rmi", "-f", a.path], { stdio: "ignore" }).unref();
1500
- break;
1501
- case "binary":
1502
- process.stdout.write(` [cleanup] removing binary: ${a.path}\n`);
1503
- emitInstallTaskLog(task, `[cleanup] removing binary: ${a.path}`);
1504
- unlinkSync(a.path);
1505
- break;
1506
- case "dir":
1507
- process.stdout.write(` [cleanup] removing dir: ${a.path}\n`);
1508
- emitInstallTaskLog(task, `[cleanup] removing dir: ${a.path}`);
1509
- rmSync(a.path, { recursive: true, force: true });
1510
- break;
1511
- }
1512
- }
1513
- catch {
1514
- // best-effort
1515
- }
1516
- }
1517
- }
1518
- const DOCKER_IMAGE_RE = /^[a-zA-Z0-9][a-zA-Z0-9\-_.:/@]*$/;
1519
- const APP_ID_RE = /^[a-z0-9][a-z0-9-]{0,62}$/;
1520
- const _REGISTRY_PATH = join(APPS_DIR, "capability-registry.json");
1521
- const INSTALL_LOCK_FILENAME = "install.lock";
1522
- // ── Directory helpers ─────────────────────────────────────────────────────
1523
- function appDirForId(appId) {
1524
- return join(APPS_DIR, appId);
1525
- }
1526
- function installLockPathForDir(appDir) {
1527
- return join(appDir, INSTALL_LOCK_FILENAME);
1528
- }
1529
- function hasInstallLock(appDir) {
1530
- return existsSync(installLockPathForDir(appDir));
1531
- }
1532
- function readInstallLockMetadata(appDir) {
1533
- const lockPath = installLockPathForDir(appDir);
1534
- if (!existsSync(lockPath))
1535
- return null;
1536
- try {
1537
- const parsed = JSON.parse(readFileSync(lockPath, "utf-8"));
1538
- return parsed && typeof parsed === "object" ? parsed : null;
1539
- }
1540
- catch {
1541
- return null;
1542
- }
1543
- }
1544
- function installDirLooksComplete(appDir) {
1545
- return (existsSync(join(appDir, "app-spec.yaml"))
1546
- && existsSync(join(appDir, "manifest.json"))
1547
- && existsSync(join(appDir, "instance.json")));
1548
- }
1549
- function isProcessAlive(pid) {
1550
- if (!Number.isInteger(pid) || pid <= 0)
1551
- return false;
1552
- try {
1553
- process.kill(pid, 0);
1554
- return true;
1555
- }
1556
- catch {
1557
- return false;
1558
- }
1559
- }
1560
- function isStaleInstallLock(appDir) {
1561
- if (!hasInstallLock(appDir))
1562
- return false;
1563
- const metadata = readInstallLockMetadata(appDir);
1564
- if (!metadata)
1565
- return true;
1566
- const taskId = typeof metadata.taskId === "string" ? metadata.taskId.trim() : "";
1567
- if (taskId) {
1568
- return getTask(taskId)?.status !== "running";
1569
- }
1570
- if (typeof metadata.pid === "number") {
1571
- if (isProcessAlive(metadata.pid)) {
1572
- const specId = typeof metadata.specId === "string" ? metadata.specId.trim() : "";
1573
- if (installDirLooksComplete(appDir) && (!specId || getRunningTasks(`app-install-${specId}`).length === 0)) {
1574
- return true;
1575
- }
1576
- return false;
1577
- }
1578
- return !isProcessAlive(metadata.pid);
1579
- }
1580
- const specId = typeof metadata.specId === "string" ? metadata.specId.trim() : "";
1581
- return specId ? getRunningTasks(`app-install-${specId}`).length === 0 : true;
1582
- }
1583
- function cleanupStaleInstallDir(appDir) {
1584
- if (!isStaleInstallLock(appDir))
1585
- return false;
1586
- if (installDirLooksComplete(appDir)) {
1587
- process.stdout.write(` [app-manager] removing stale install lock: ${appDir}\n`);
1588
- removeInstallLock(appDir);
1589
- return false;
1590
- }
1591
- process.stdout.write(` [app-manager] removing stale install dir: ${appDir}\n`);
1592
- rmSync(appDir, { recursive: true, force: true });
1593
- return true;
1594
- }
1595
- function createInstallLock(appDir, appId, specId, task) {
1596
- writeFileSync(installLockPathForDir(appDir), JSON.stringify({ appId, specId, started_at: new Date().toISOString(), pid: process.pid, taskId: task?.id }, null, 2) + "\n", { mode: 0o644 });
1597
- }
1598
- function removeInstallLock(appDir) {
1599
- const lockPath = installLockPathForDir(appDir);
1600
- if (existsSync(lockPath)) {
1601
- unlinkSync(lockPath);
1602
- }
1603
- }
1604
- function appTaskName(kind, appId) {
1605
- return `app-${kind}:${appId}`;
1606
- }
1607
- function findRunningInstallTask(appDir) {
1608
- const metadata = readInstallLockMetadata(appDir);
1609
- const taskId = typeof metadata?.taskId === "string" ? metadata.taskId.trim() : "";
1610
- if (!taskId)
1611
- return undefined;
1612
- const status = getTask(taskId)?.status;
1613
- if (status !== "running")
1614
- return undefined;
1615
- return { id: taskId, kind: "install", status };
1616
- }
1617
- function findRunningNamedTask(taskName, kind) {
1618
- const running = getRunningTasks(taskName)[0];
1619
- if (!running)
1620
- return undefined;
1621
- const status = getTask(running.id)?.status;
1622
- if (status !== "running")
1623
- return undefined;
1624
- return { id: running.id, kind, status };
1625
- }
1626
- export function getRunningAppTask(appId, appDir) {
1627
- const resolvedAppDir = appDir ?? resolveAppLocation(appId)?.dir;
1628
- if (resolvedAppDir && hasInstallLock(resolvedAppDir)) {
1629
- const installTask = findRunningInstallTask(resolvedAppDir);
1630
- if (installTask)
1631
- return installTask;
1632
- }
1633
- for (const kind of ["uninstall", "restart", "stop", "start"]) {
1634
- const task = findRunningNamedTask(appTaskName(kind, appId), kind);
1635
- if (task)
1636
- return task;
1637
- }
1638
- return undefined;
1639
- }
1640
- function startAppLifecycleTask(appId, kind, startMessage, doneMessage, action) {
1641
- const currentTask = getRunningAppTask(appId);
1642
- if (currentTask) {
1643
- if (currentTask.kind === kind) {
1644
- return { ok: true, taskId: currentTask.id, reused: true, kind };
1645
- }
1646
- return {
1647
- ok: false,
1648
- error: `App '${appId}' 正在执行 ${currentTask.kind} 操作,请等待完成后再试`,
1649
- code: "TASK_BUSY",
1650
- kind,
1651
- };
1652
- }
1653
- const task = createTask(appTaskName(kind, appId));
1654
- emitAppTaskProgress(task, startMessage, 0);
1655
- void (async () => {
1656
- try {
1657
- await action();
1658
- task.status = "done";
1659
- emitTask(task, { type: "done", message: doneMessage, progress: 100 });
1660
- }
1661
- catch (e) {
1662
- task.status = "error";
1663
- emitTask(task, {
1664
- type: "error",
1665
- message: e?.message || `App '${appId}' ${kind} 失败`,
1666
- });
1667
- }
1668
- })();
1669
- return { ok: true, taskId: task.id, kind };
1670
- }
1671
- function instanceAppDirForId(appId) {
1672
- return join(INSTANCES_DIR, appId);
1673
- }
1674
- function resolveAppLocation(appId) {
1675
- const candidates = [
1676
- { dir: appDirForId(appId), installMode: "app-dir" },
1677
- { dir: instanceAppDirForId(appId), installMode: "instance-dir" },
1678
- ];
1679
- for (const candidate of candidates) {
1680
- cleanupStaleInstallDir(candidate.dir);
1681
- if (existsSync(join(candidate.dir, "app-spec.yaml"))
1682
- && existsSync(join(candidate.dir, "manifest.json"))) {
1683
- return candidate;
1684
- }
1685
- }
1686
- return null;
1687
- }
1688
- function isInstanceBackedApp(appData) {
1689
- return appData?.manifest.install_mode === "instance-dir";
1690
- }
1691
- function getSingleServiceTask(spec) {
1692
- const serviceTasks = spec.tasks.filter((task) => (task.role ?? "service") === "service");
1693
- if (spec.tasks.length !== 1 || serviceTasks.length !== 1)
1694
- return null;
1695
- return serviceTasks[0];
1696
- }
1697
- function inferLegacyAppType(spec) {
1698
- return /ollama/i.test(`${spec.id} ${spec.name ?? ""}`) ? "ollama" : "custom";
1699
- }
1700
- function rewriteInstanceScopedPathText(value, appId) {
1701
- return value
1702
- .split(`~/.jishushell/apps/${appId}`).join(`~/.jishushell/instances/${appId}`)
1703
- .split(`$HOME/.jishushell/apps/${appId}`).join(`$HOME/.jishushell/instances/${appId}`)
1704
- .split(`${homedir()}/.jishushell/apps/${appId}`).join(`${homedir()}/.jishushell/instances/${appId}`);
1705
- }
1706
- function rewriteInstanceScopedPaths(value, appId) {
1707
- if (typeof value === "string") {
1708
- return rewriteInstanceScopedPathText(value, appId);
1709
- }
1710
- if (Array.isArray(value)) {
1711
- return value.map((entry) => rewriteInstanceScopedPaths(entry, appId));
1712
- }
1713
- if (value && typeof value === "object") {
1714
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
1715
- key,
1716
- rewriteInstanceScopedPaths(entry, appId),
1717
- ]));
1718
- }
1719
- return value;
1720
- }
1721
- function buildGenericInstanceBackedMeta(appId, spec) {
1722
- return {
1723
- id: appId,
1724
- name: spec.name || appId,
1725
- description: spec.description || "",
1726
- // See buildGenericAppMeta for the dual-write rationale: legacy
1727
- // `app_type` keeps V1 dispatch alive, `app_spec_ref` is the V2
1728
- // discriminator recognized by resolveAgentType.
1729
- app_type: inferLegacyAppType(spec),
1730
- app_spec_ref: spec.id,
1731
- ...(spec.singleInstance ? { singleInstance: true } : {}),
1732
- runtime: {},
1733
- created_at: new Date().toISOString(),
1734
- app_id: spec.app_id ?? appId,
1735
- };
1736
- }
1737
- function explicitAgentTypeForSpec(spec) {
1738
- if (typeof spec.agentType === "string" && spec.agentType.trim()) {
1739
- return spec.agentType.trim();
1740
- }
1741
- if (typeof spec._engine?.agentType === "string" && spec._engine.agentType.trim()) {
1742
- return spec._engine.agentType.trim();
1743
- }
1744
- return "";
1745
- }
1746
- function buildGenericAppRuntime(spec, appId) {
1747
- const serviceTask = spec.tasks.find((task) => (task.role ?? "service") === "service");
1748
- if (!serviceTask)
1749
- return {};
1750
- const runtime = compileTaskRuntime(serviceTask, appId);
1751
- // Record EVERY external port across ALL tasks, each tagged with its owning
1752
- // taskName. Multi-service-task apps (e.g. WeKnora's `app` + `ui`, both with a
1753
- // port named "http") otherwise lose the secondary task's ports downstream,
1754
- // letting name-only matching collapse two distinct host ports into one.
1755
- // Ordering matters: the primary service task's ports come first so
1756
- // extractGatewayPort / single-service `runtime.ports[0]` semantics are
1757
- // byte-for-byte preserved.
1758
- const orderedTasks = [serviceTask, ...spec.tasks.filter((task) => task !== serviceTask)];
1759
- const allPorts = orderedTasks.flatMap((task) => (task.ports ?? [])
1760
- .filter((port) => (port.visibility ?? "external") !== "internal")
1761
- .map((port) => ({
1762
- taskName: task.name,
1763
- name: port.name,
1764
- containerPort: port.container_port ?? port.port,
1765
- hostPort: port.host_port ?? port.port,
1766
- visibility: port.visibility ?? "external",
1767
- ...(port.host_network ? { host_network: port.host_network } : {}),
1768
- })));
1769
- if (allPorts.length > 0) {
1770
- runtime.ports = allPorts;
1771
- }
1772
- if (serviceTask.health?.http) {
1773
- const healthPortEntry = serviceTask.ports?.find((port) => port.port === serviceTask.health?.http?.port
1774
- || port.host_port === serviceTask.health?.http?.port
1775
- || port.container_port === serviceTask.health?.http?.port);
1776
- runtime.health = {
1777
- type: "http",
1778
- path: serviceTask.health.http.path,
1779
- port: healthPortEntry?.host_port ?? healthPortEntry?.port ?? serviceTask.health.http.port,
1780
- interval: serviceTask.health.interval,
1781
- timeout: serviceTask.health.timeout,
1782
- retries: serviceTask.health.retries,
1783
- };
1784
- }
1785
- if (Array.isArray(serviceTask.volumes)) {
1786
- runtime.volumes = serviceTask.volumes.map((volume) => ({
1787
- hostPath: expandPath(String(volume.source ?? "")),
1788
- containerPath: String(volume.target ?? ""),
1789
- mode: volume.readonly ? "ro" : "rw",
1790
- }));
1791
- }
1792
- return runtime;
1793
- }
1794
- function buildGenericAppMeta(appId, spec, createdAt) {
1795
- const explicitAgentType = explicitAgentTypeForSpec(spec);
1796
- return {
1797
- id: appId,
1798
- name: spec.name || appId,
1799
- description: spec.description || "",
1800
- runtime: buildGenericAppRuntime(spec, appId),
1801
- created_at: createdAt,
1802
- app_id: spec.app_id ?? appId,
1803
- ...(explicitAgentType
1804
- ? { agentType: explicitAgentType }
1805
- : {
1806
- // `app_type: "custom"` is the V1 legacy marker; `app_spec_ref`
1807
- // is the V2 discriminator that resolveAgentType (runtime/
1808
- // instance.ts) uses to route to the custom adapter. Writing
1809
- // both makes dispatch survive the eventual removal of the
1810
- // resolveLegacyAppType bridge without touching each instance.
1811
- app_type: inferLegacyAppType(spec),
1812
- app_spec_ref: spec.id,
1813
- ...(spec.singleInstance ? { singleInstance: true } : {}),
1814
- }),
1815
- };
1816
- }
1817
- async function bootstrapAdapterManagedApp(appId, spec, options) {
1818
- const agentType = explicitAgentTypeForSpec(spec);
1819
- if (!agentType)
1820
- return false;
1821
- const { hasAdapter, getAdapter } = await import("../runtime/index.js");
1822
- if (!hasAdapter(agentType))
1823
- return false;
1824
- const adapter = getAdapter(agentType);
1825
- if (!adapter.createInstance)
1826
- return false;
1827
- await adapter.createInstance({
1828
- instanceId: appId,
1829
- name: options.bootstrap?.name ?? spec.name ?? appId,
1830
- description: options.bootstrap?.description ?? spec.description ?? "",
1831
- cloneFrom: options.bootstrap?.cloneFrom,
1832
- agentHome: options.bootstrap?.agentHome,
1833
- cloneOptions: options.bootstrap?.cloneOptions,
1834
- appSpec: spec,
1835
- });
1836
- return true;
1837
- }
1838
- function getAdapterManagedAgentType(appData) {
1839
- if (!appData || appData.manifest.install_mode !== "app-dir")
1840
- return null;
1841
- const identity = resolveRuntimeIdentity(appData.manifest.id);
1842
- return identity?.driver === "runtime-adapter" ? identity.agentType ?? null : null;
1843
- }
1844
- async function installIntoInstanceDir(spec, specYaml, requestedAppId, options = {}) {
1845
- if (options.preferInstanceDir !== true)
1846
- return null;
1847
- const serviceTask = getSingleServiceTask(spec);
1848
- if (!serviceTask)
1849
- return null;
1850
- const explicitAgentType = typeof spec.agentType === "string" && spec.agentType.trim()
1851
- ? spec.agentType.trim()
1852
- : typeof spec._engine?.agentType === "string" && spec._engine.agentType.trim()
1853
- ? spec._engine.agentType.trim()
1854
- : "";
1855
- const { hasAdapter, getAdapter } = await import("../runtime/index.js");
1856
- let installTarget = null;
1857
- if (explicitAgentType) {
1858
- if (hasAdapter(explicitAgentType) && getAdapter(explicitAgentType).createInstance) {
1859
- installTarget = { kind: "adapter", agentType: explicitAgentType };
1860
- }
1861
- }
1862
- else {
1863
- const hintText = `${spec.id} ${spec.name ?? ""}`.toLowerCase();
1864
- for (const hintedAgentType of ["openclaw", "hermes"]) {
1865
- if (hintText.includes(hintedAgentType) && hasAdapter(hintedAgentType) && getAdapter(hintedAgentType).createInstance) {
1866
- installTarget = { kind: "adapter", agentType: hintedAgentType };
1867
- break;
1868
- }
1869
- }
1870
- if (!installTarget && serviceTask.runtime === "container") {
1871
- installTarget = { kind: "legacy", appType: inferLegacyAppType(spec) };
1872
- }
1873
- }
1874
- if (!installTarget)
1875
- return null;
1876
- const { appId, installedSpec, } = await resolveInstallTarget(spec, specYaml, requestedAppId);
1877
- let instanceSpec = rewriteInstanceScopedPaths(installedSpec, appId);
1878
- // PR 3 sub-step 3c: switch from legacy `resolveRequires(spec)` to the new
1879
- // resolveConnections in preCreate mode. Legacy single-candidate fallback
1880
- // still materializes its env (so meta-apps stay "open the box and it
1881
- // works"); category-prefix requires + missing required producers fall
1882
- // into `pending` for UI display via the install task event (PR 4 wires
1883
- // task.event.affectedConsumers etc.). install never fails here — even if
1884
- // a required capability is unavailable; start time will surface the
1885
- // error. The previous `ensureRequiredCapabilitiesAvailable` block has
1886
- // been removed for the same reason.
1887
- const { resolveConnections, resolvedToLegacyEnv } = await import("../connection-resolver.js");
1888
- const { resolved, pending } = resolveConnections(installedSpec, { connections: {} }, "preCreate");
1889
- if (pending.length > 0) {
1890
- console.log(`[install] ${appId}: ${pending.length} pending connection(s): ` +
1891
- pending.map((p) => `${p.slot} (${p.capability}, ${p.reason})`).join(", "));
1892
- }
1893
- const resolvedRequires = resolvedToLegacyEnv(resolved);
1894
- if (Object.keys(resolvedRequires).length > 0) {
1895
- instanceSpec = {
1896
- ...installedSpec,
1897
- tasks: installedSpec.tasks.map((task) => (task.role ?? "service") === "service"
1898
- ? { ...task, env: { ...task.env, ...resolvedRequires } }
1899
- : task),
1900
- };
1901
- instanceSpec = rewriteInstanceScopedPaths(instanceSpec, appId);
1902
- }
1903
- const persistedSpecYaml = stringify(instanceSpec);
1904
- const instanceDir = instanceAppDirForId(appId);
1905
- const manifest = {
1906
- id: appId,
1907
- installed_at: new Date().toISOString(),
1908
- spec_hash: createHash("sha256").update(persistedSpecYaml).digest("hex"),
1909
- install_mode: "instance-dir",
1910
- };
1911
- const { getAppManager } = await import("./registry.js");
1912
- const instanceName = instanceSpec.name || appId;
1913
- const instanceDescription = instanceSpec.description || "";
1914
- const artifacts = [];
1915
- try {
1916
- if (installTarget?.kind === "adapter") {
1917
- const adapter = getAdapter(installTarget.agentType);
1918
- if (!adapter.createInstance)
1919
- return null;
1920
- await adapter.createInstance({
1921
- instanceId: appId,
1922
- name: instanceName,
1923
- description: instanceDescription,
1924
- appSpec: instanceSpec,
1925
- });
1926
- }
1927
- else if (installTarget?.kind === "legacy") {
1928
- await getAppManager(installTarget.appType).createInstance(appId, instanceName, instanceDescription, { appSpec: instanceSpec });
1929
- }
1930
- else {
1931
- ensureDirHost(instanceDir);
1932
- safeWriteJson(join(instanceDir, "instance.json"), buildGenericInstanceBackedMeta(appId, instanceSpec), true);
1933
- }
1934
- ensureDirHost(instanceDir);
1935
- const yamlPath = join(instanceDir, "app-spec.yaml");
1936
- const yamlTmp = yamlPath + ".tmp";
1937
- writeFileSync(yamlTmp, persistedSpecYaml, { mode: 0o644 });
1938
- renameSync(yamlTmp, yamlPath);
1939
- safeWriteJson(join(instanceDir, "manifest.json"), manifest, true);
1940
- await runLifecycleSteps(instanceSpec.lifecycle?.pre_install, "pre_install", artifacts, options.task, options.exec);
1941
- }
1942
- catch (e) {
1943
- cleanupArtifacts(artifacts, options.task);
1944
- try {
1945
- const instanceManager = await import("../instance-manager.js");
1946
- await instanceManager.deleteInstance(appId);
1947
- }
1948
- catch {
1949
- rmSync(instanceDir, { recursive: true, force: true });
1950
- }
1951
- throw decorateInstallError(e, instanceSpec);
1952
- }
1953
- try {
1954
- await runLifecycleSteps(instanceSpec.lifecycle?.install, "install", artifacts, options.task, options.exec);
1955
- const pulledImages = new Set(artifacts.filter((artifact) => artifact.type === "image").map((artifact) => artifact.path));
1956
- const imagesToPull = [...new Set(instanceSpec.tasks.filter((task) => task.image).map((task) => task.image))];
1957
- for (const image of imagesToPull) {
1958
- if (!pulledImages.has(image)) {
1959
- await pullDockerImageStep("install", image, `docker pull ${image}`, options.task);
1960
- artifacts.push({ type: "image", path: image });
1961
- }
1962
- }
1963
- }
1964
- catch (e) {
1965
- try {
1966
- await runLifecycleSteps(instanceSpec.lifecycle?.uninstall, "rollback_uninstall", undefined, options.task, options.exec);
1967
- }
1968
- catch (rollbackError) {
1969
- process.stdout.write(` [app-manager] rollback uninstall failed: ${rollbackError.message}\n`);
1970
- emitInstallTaskLog(options.task, `[app-manager] rollback uninstall failed: ${rollbackError.message}`);
1971
- }
1972
- cleanupArtifacts(artifacts, options.task);
1973
- try {
1974
- const instanceManager = await import("../instance-manager.js");
1975
- await instanceManager.deleteInstance(appId);
1976
- }
1977
- catch {
1978
- rmSync(instanceDir, { recursive: true, force: true });
1979
- }
1980
- throw decorateInstallError(e, instanceSpec);
1981
- }
1982
- if (artifacts.length > 0) {
1983
- manifest.artifacts = artifacts;
1984
- safeWriteJson(join(instanceDir, "manifest.json"), manifest, true);
1985
- }
1986
- invalidateRuntimeIdentity(appId);
1987
- return { spec: instanceSpec, manifest };
1988
- }
1989
- /**
1990
- * Check whether an app with this id is installed (has manifest.json + app-spec.yaml).
1991
- * Exported so nomad-manager can use it for routing without the old prefix convention.
1992
- */
1993
- export function isAppInstalled(appId) {
1994
- const location = resolveAppLocation(appId);
1995
- return location != null && !hasInstallLock(location.dir);
1996
- }
1997
- function resolveAppDir(appId) {
1998
- return resolveAppLocation(appId)?.dir ?? null;
1999
- }
2000
- function readAppFromDir(appDir, appId, installModeHint = "app-dir") {
2001
- if (cleanupStaleInstallDir(appDir))
2002
- return null;
2003
- const yamlPath = join(appDir, "app-spec.yaml");
2004
- const manifestPath = join(appDir, "manifest.json");
2005
- if (!existsSync(yamlPath) || !existsSync(manifestPath))
2006
- return null;
2007
- try {
2008
- const spec = normalizeAppSpec(parse(readFileSync(yamlPath, "utf-8")));
2009
- const manifest = safeReadJson(manifestPath, `app:${appId}`);
2010
- if (!manifest)
2011
- return null;
2012
- return {
2013
- spec,
2014
- manifest: {
2015
- ...manifest,
2016
- install_mode: manifest.install_mode ?? installModeHint,
2017
- },
2018
- install_state: hasInstallLock(appDir) ? "installing" : "installed",
2019
- ...(getRunningAppTask(appId, appDir) ? { current_task: getRunningAppTask(appId, appDir) } : {}),
2020
- };
2021
- }
2022
- catch {
2023
- return null;
2024
- }
2025
- }
2026
- export function getAppInstallState(appId) {
2027
- const location = resolveAppLocation(appId);
2028
- if (!location)
2029
- return null;
2030
- return hasInstallLock(location.dir) ? "installing" : "installed";
2031
- }
2032
- const BROWSERLESS_DEBUGGER_CAPABILITY = "browserless-debugger";
2033
- const BROWSERLESS_API_CAPABILITY = "browserless-api";
2034
- const BROWSERLESS_DOCS_CAPABILITY = "browserless-docs";
2035
- const JISHU_KB_FRAME_ANCESTORS_ALLOW_ALL = "*";
2036
- const JISHU_KB_DEFAULT_PROVIDER_RETRY_BLOCK = `echo "[post_start] fetching core default provider ..."
2037
- fetch_default_provider_json() {
2038
- curl -sf --max-time 8 \
2039
- -H "X-Jishushell-Internal-Token: $JISHUSHELL_INTERNAL_TOKEN" \
2040
- "$JISHUSHELL_CORE_URL/api/internal/default-provider"
2041
- }
2042
- DP_JSON=""
2043
- for i in $(seq 1 30); do
2044
- DP_JSON=$(fetch_default_provider_json || true)
2045
- READY=$(printf '%s' "$DP_JSON" | jget ready)
2046
- REASON=$(printf '%s' "$DP_JSON" | jget reason)
2047
- if [ "$READY" = "true" ]; then
2048
- if [ "$i" -gt 1 ]; then
2049
- echo "[post_start] core default provider became ready after $i attempts"
2050
- fi
2051
- break
2052
- fi
2053
- case "$REASON" in
2054
- no_provider|no_key)
2055
- break
2056
- ;;
2057
- esac
2058
- DP_JSON='{"ready":false,"reason":"core_unreachable"}'
2059
- if [ "$i" -lt 30 ]; then
2060
- echo "[post_start] core default provider not ready yet (attempt $i/30); retrying in 2s"
2061
- sleep 2
2062
- fi
2063
- done
2064
- READY=$(printf '%s' "$DP_JSON" | jget ready)`;
2065
- // `ensureRequiredCapabilitiesAvailable` was removed in PR 3 sub-step 3c.
2066
- // install never blocks on missing required providers any more —
2067
- // resolveConnections(..., "preCreate") collects them into the `pending`
2068
- // list and the UI surfaces them after install completes.
2069
- // `listProvidedCapabilities()` does a full filesystem app scan (listApps →
2070
- // readdir + per-app spec parse) plus a registry read. `augmentInstanceMetadata`
2071
- // calls it ~2× per instance (getProvidedCapabilitiesForApp +
2072
- // getEmbeddedUiHintForApp), so `GET /api/instances` over N instances did ~2N
2073
- // full disk scans per request — the dashboard warm-path cost.
2074
- //
2075
- // Cache the result, but key validity on a CHEAP fingerprint of the
2076
- // underlying state, not on time alone. A time-only TTL left a staleness
2077
- // window where a freshly installed/uninstalled app (or created/deleted
2078
- // instance — INSTANCES_DIR also feeds listApps) was missing from / lingered
2079
- // in the list until the TTL expired, which broke the Connections UI and the
2080
- // embedded-UI hint. The fingerprint (sorted dir entries of APPS_DIR +
2081
- // INSTANCES_DIR, plus the registry file mtime) changes the instant the app
2082
- // set or registry changes — including external mutations — so the cache
2083
- // self-busts without having to hook every mutation site. It costs two
2084
- // readdir + one stat, still vastly cheaper than re-parsing every app spec.
2085
- // The short TTL stays only as a safety net for in-place spec edits that
2086
- // don't change the dir set; the explicit register/unregister/markStopped
2087
- // invalidations remain as a precise fast-path for sub-millisecond registry
2088
- // writes that could share an mtime tick.
2089
- const PROVIDED_CAPS_TTL_MS = 5_000;
2090
- let _providedCapsEntry = null;
2091
- function providedCapabilitiesFingerprint() {
2092
- const dirEntries = (dir) => {
2093
- try {
2094
- return existsSync(dir) ? readdirSync(dir).sort().join(",") : "";
2095
- }
2096
- catch {
2097
- return "";
2098
- }
2099
- };
2100
- let registryMtime = 0;
2101
- try {
2102
- registryMtime = lstatSync(_REGISTRY_PATH).mtimeMs;
2103
- }
2104
- catch {
2105
- /* registry file not written yet → 0 */
2106
- }
2107
- return `${dirEntries(APPS_DIR)}|${registryMtime}`;
2108
- }
2109
- export function invalidateProvidedCapabilitiesCache() {
2110
- _providedCapsEntry = null;
2111
- }
2112
- export function listProvidedCapabilities() {
2113
- const fp = providedCapabilitiesFingerprint();
2114
- const now = Date.now();
2115
- if (_providedCapsEntry &&
2116
- _providedCapsEntry.fp === fp &&
2117
- now - _providedCapsEntry.ts < PROVIDED_CAPS_TTL_MS) {
2118
- return _providedCapsEntry.data;
2119
- }
2120
- const data = computeProvidedCapabilities();
2121
- _providedCapsEntry = { data, fp, ts: now };
2122
- return data;
2123
- }
2124
- function computeProvidedCapabilities() {
2125
- const reg = capabilityRegistry.snapshot();
2126
- return listApps().flatMap((app) => (app.spec.provides ?? []).map((provide) => {
2127
- const url = getProvideUrl(provide) ?? undefined;
2128
- // Report the RUNTIME port the capability actually listens on (the
2129
- // resolver consults the instance's recorded port allocation and only
2130
- // falls back to the declared spec port when no runtime exists yet).
2131
- // Declared spec ports go stale once a sibling task is remapped onto a
2132
- // different host port (multi-service-task apps like WeKnora).
2133
- const port = resolveRuntimeCapabilityPort(app.manifest.id, provide.capability) ?? undefined;
2134
- const address = !url && typeof port === "number" ? buildCapabilityAddress(port, provide.path) : undefined;
2135
- const providers = reg.providersByCapability?.[provide.capability] ?? [];
2136
- const registered = providers.find((e) => e.instanceId === app.manifest.id)
2137
- ?? providers.find((e) => e.status === "running")
2138
- ?? providers[0];
2139
- const protocol = resolveProvideProtocol(provide);
2140
- return {
2141
- appId: app.manifest.id,
2142
- capability: provide.capability,
2143
- ...(typeof port === "number" ? { port } : {}),
2144
- ...(provide.path ? { path: provide.path } : {}),
2145
- ...(url ? { url } : {}),
2146
- protocol,
2147
- ...(provide.visibility ? { visibility: provide.visibility } : {}),
2148
- ...(provide.description ? { description: provide.description } : {}),
2149
- ...(provide.terminal ? { terminal: provide.terminal } : {}),
2150
- ...(provide.embedded ? { embedded: provide.embedded } : {}),
2151
- ...(address ? { address } : {}),
2152
- registered: Boolean(registered),
2153
- ...(registered?.address ? { registeredAddress: registered.address } : {}),
2154
- ...(registered?.instanceId ? { providerInstanceId: registered.instanceId } : {}),
2155
- };
2156
- }));
2157
- }
2158
- export function getProvidedCapabilitiesForApp(appId) {
2159
- return listProvidedCapabilities().filter((entry) => entry.appId === appId);
2160
- }
2161
- /**
2162
- * Resolve the runtime endpoint (host + port) for a given app's capability.
2163
- * Returns the runtime-resolved port when available, falling back to the
2164
- * declared AppSpec port. This is used by the capability proxy to target the
2165
- * correct live endpoint after potential port reallocation.
2166
- */
2167
- export function resolveRuntimeCapabilityPort(appId, capabilityName) {
2168
- const app = getApp(appId);
2169
- if (!app)
2170
- return null;
2171
- const provide = (app.spec.provides ?? []).find((p) => p.capability === capabilityName);
2172
- if (!provide)
2173
- return null;
2174
- const resolved = resolveProvideEndpoint(appId, app.spec, provide);
2175
- return resolved?.hostPort ?? getProvidePort(app.spec, provide);
2176
- }
2177
- export function getEmbeddedUiHintForApp(appId) {
2178
- const app = getApp(appId);
2179
- if (!app)
2180
- return null;
2181
- // OpenClaw should always render through its dedicated gateway-launch iframe
2182
- // path, not through the generic app-provides embedded UI chooser. App-dir
2183
- // OpenClaw specs expose `openclaw-dashboard`, which would otherwise be
2184
- // advertised as a direct LAN URL (e.g. http://10.x.x.x:18793) and hijack the
2185
- // panel into a browser-direct iframe that bypasses gateway auth/proxy logic.
2186
- // Returning null here lets InstanceDetail fall back to
2187
- // `capabilities.gateway.chatPanel === "iframe"` → `/gateway-launch`.
2188
- if (app.spec?.agentType === "openclaw")
2189
- return null;
2190
- const provides = getProvidedCapabilitiesForApp(appId);
2191
- if (!provides.length)
2192
- return null;
2193
- const isUiCapability = (capability) => capability.endsWith("-ui") || capability.startsWith("web-") || capability.endsWith("-web");
2194
- // Selection priority for which provide becomes the embedded UI:
2195
- // 1. browserless-debugger (special — dev console, not the API)
2196
- // 2. any capability whose name looks like a Web UI slot:
2197
- // `*-ui`, `web-*`, or `*-web`. Apps like AnythingLLM provide both an
2198
- // API capability (`knowledge-anythingllm`, path `/api/v1`) AND a UI
2199
- // capability (`anythingllm-ui`, path `/`). Jishu KB uses `web-jishukb`
2200
- // while OpenWebUI/Hollama use `*-web`. Without this preference the
2201
- // first-iterated API provide can win and the iframe lands on JSON / a
2202
- // direct upstream URL instead of the intended Web UI.
2203
- // 3. fall back to natural order for legacy single-capability apps.
2204
- const browserlessPreferred = provides.find((provide) => provide.capability === BROWSERLESS_DEBUGGER_CAPABILITY);
2205
- const uiPreferred = provides.find((provide) => isUiCapability(provide.capability));
2206
- const preferred = browserlessPreferred ?? uiPreferred ?? null;
2207
- const orderedProvides = preferred
2208
- ? [preferred, ...provides.filter((provide) => provide !== preferred)]
2209
- : provides;
2210
- for (const provide of orderedProvides) {
2211
- const protocol = normalizeProvideProtocol(provide.protocol);
2212
- if (provide.visibility === "internal")
2213
- continue;
2214
- if (protocol !== "http" && protocol !== "https")
2215
- continue;
2216
- if (typeof provide.url === "string" && provide.url.trim()) {
2217
- const url = provide.url.trim();
2218
- return {
2219
- capability: provide.capability,
2220
- protocol,
2221
- url,
2222
- };
2223
- }
2224
- // Resolve the RUNTIME port for this capability, not the declared spec
2225
- // port. For multi-service-task apps the declared port can be a dead value
2226
- // once another task has been remapped onto a different host port; the
2227
- // runtime resolver returns the port the capability actually listens on
2228
- // (falling back to the declared port when no runtime is recorded yet).
2229
- const resolvedPort = resolveRuntimeCapabilityPort(appId, provide.capability) ?? provide.port;
2230
- if (typeof resolvedPort !== "number" || resolvedPort < 1)
2231
- continue;
2232
- // Honor explicit `embedded` opt-in/out on the provide before the
2233
- // auto-detection logic runs. `"proxy"` short-circuits to the
2234
- // same-origin proxy path (needed when upstream is firewall-blocked,
2235
- // emits X-Frame-Options, or otherwise can't be reached by the
2236
- // browser directly). `"direct"` forces the direct URL even when
2237
- // listening only on loopback — caller asserts they know what
2238
- // they're doing.
2239
- const embeddedMode = provide.embedded ?? "auto";
2240
- // Prefer a direct upstream URL when the container port is published to
2241
- // a LAN-reachable address (Pi with host_network "external", etc.). The
2242
- // same-origin reverse-proxy path is necessary only when the container
2243
- // is bound to 127.0.0.1 (macOS+Colima, dev laptops without LAN
2244
- // exposure) — there it's the only way for a remote browser to reach
2245
- // the iframe content. Going through the proxy when the upstream is
2246
- // already public causes path-collision bugs for apps that fetch
2247
- // absolute URLs starting with `/api/...` (e.g. OpenWebUI), because
2248
- // those calls bypass `<base href>` and hit the core API instead.
2249
- const listeningHost = instanceServices.getListeningHostForPort(resolvedPort);
2250
- const isLoopback = listeningHost === "127.0.0.1" || listeningHost === "::1";
2251
- const directlyReachable = embeddedMode !== "proxy"
2252
- && listeningHost
2253
- && (!isLoopback || embeddedMode === "direct");
2254
- if (directlyReachable) {
2255
- // Advertise a browser-reachable host. getAdvertisedHostForPort already
2256
- // prefers the external/LAN address and only ever falls back to the
2257
- // primary IPv4 — but when the port is published on the docker_bridge
2258
- // (docker0 IP 172.17.0.1) it is internal-only and unreachable from a
2259
- // remote browser, so override it with the LAN address for the embedded
2260
- // UI slot.
2261
- const rawAdvertised = instanceServices.getAdvertisedHostForPort(resolvedPort);
2262
- const advertised = isDockerBridgeHost(rawAdvertised)
2263
- ? instanceServices.getPrimaryIpv4Address()
2264
- : rawAdvertised;
2265
- const directUrl = `${protocol}://${advertised}:${resolvedPort}${provide.path ?? ""}`;
2266
- return {
2267
- capability: provide.capability,
2268
- protocol,
2269
- port: resolvedPort,
2270
- url: directUrl,
2271
- };
2272
- }
2273
- // Use same-origin reverse-proxy path so the frontend iframe works for
2274
- // remote browsers and macOS+Colima environments where the container
2275
- // port is only published to 127.0.0.1.
2276
- // Root-path UIs (path omitted) still need a trailing slash so the
2277
- // browser treats the iframe src as a directory URL. Without it, some
2278
- // SPA runtimes compute relative URLs from `/.../provides/<capability>`
2279
- // as if `<capability>` were a file segment, which breaks boot under the
2280
- // proxy even when the HTML/base rewrite succeeded.
2281
- const normalizedProvidePath = typeof provide.path === "string" ? provide.path.trim() : "";
2282
- const needsTrailingSlash = !normalizedProvidePath || normalizedProvidePath.endsWith("/");
2283
- const proxyPath = `/api/instances/${encodeURIComponent(appId)}/provides/${encodeURIComponent(provide.capability)}${needsTrailingSlash ? "/" : ""}`;
2284
- return {
2285
- capability: provide.capability,
2286
- protocol,
2287
- port: resolvedPort,
2288
- url: proxyPath,
2289
- };
2290
- }
2291
- return null;
2292
- }
2293
- export async function installApp(specYaml, requestedAppId, options = {}) {
2294
- let spec;
2295
- try {
2296
- spec = normalizeAppSpec(parse(specYaml));
2297
- }
2298
- catch (e) {
2299
- throw new Error(`YAML 解析失败: ${e.message}`);
2300
- }
2301
- if (!spec || !spec.id || !APP_ID_RE.test(spec.id)) {
2302
- throw new Error(`App id '${spec?.id}' 格式无效,必须符合 /^[a-z0-9][a-z0-9-]{0,62}$/`);
2303
- }
2304
- ensureCompatibleJishuShellVersion(spec);
2305
- if (!Array.isArray(spec.tasks) || spec.tasks.length === 0) {
2306
- throw new Error("tasks 不能为空");
2307
- }
2308
- const hasService = spec.tasks.some((t) => t.role === "service");
2309
- if (!hasService) {
2310
- throw new Error("tasks 中至少需要一个 role 为 'service' 的任务");
2311
- }
2312
- for (const task of spec.tasks) {
2313
- if (task.runtime === "vm") {
2314
- throw new Error(`runtime 'vm' 暂不支持,请使用 runtime: container 或 process`);
2315
- }
2316
- if (task.runtime === "container" && !task.image) {
2317
- throw new Error(`container task '${task.name}' 需要指定 image 字段`);
2318
- }
2319
- if (task.runtime === "process" && !task.command) {
2320
- throw new Error(`process task '${task.name}' 需要指定 command 或 binary 字段`);
2321
- }
2322
- if (task.image && !DOCKER_IMAGE_RE.test(task.image)) {
2323
- throw new Error(`task '${task.name}' 的 image '${task.image}' 格式无效`);
2324
- }
2325
- }
2326
- // PR 3 sub-step 3c: removed the legacy `ensureRequiredCapabilitiesAvailable`
2327
- // hard-stop. install now never blocks on missing required providers —
2328
- // resolveConnections(..., "preCreate") records them as `pending` on the
2329
- // install task event so the UI can prompt the user; start time surfaces
2330
- // the error if still unresolved.
2331
- const instanceBackedInstall = await installIntoInstanceDir(spec, specYaml, requestedAppId, options);
2332
- if (instanceBackedInstall) {
2333
- invalidateRuntimeIdentity(instanceBackedInstall.manifest.id);
2334
- return instanceBackedInstall;
2335
- }
2336
- const { appId, installedSpec, installedSpecYaml, } = await resolveInstallTarget(spec, specYaml, requestedAppId);
2337
- const appDir = appDirForId(appId);
2338
- ensureDirHost(appDir);
2339
- createInstallLock(appDir, appId, spec.id, options.task);
2340
- emitInstallTaskLog(options.task, `[app-manager] created ${INSTALL_LOCK_FILENAME} for ${appId}`);
2341
- const yamlPath = join(appDir, "app-spec.yaml");
2342
- const yamlTmp = yamlPath + ".tmp";
2343
- writeFileSync(yamlTmp, installedSpecYaml, { mode: 0o644 });
2344
- renameSync(yamlTmp, yamlPath);
2345
- const manifest = {
2346
- id: appId,
2347
- installed_at: new Date().toISOString(),
2348
- spec_hash: createHash("sha256").update(installedSpecYaml).digest("hex"),
2349
- install_mode: "app-dir",
2350
- };
2351
- safeWriteJson(join(appDir, "manifest.json"), manifest, true);
2352
- const artifacts = [];
2353
- try {
2354
- await runLifecycleSteps(installedSpec.lifecycle?.pre_install, "pre_install", artifacts, options.task, options.exec);
2355
- }
2356
- catch (e) {
2357
- cleanupArtifacts(artifacts, options.task);
2358
- rmSync(appDir, { recursive: true, force: true });
2359
- throw decorateInstallError(e, installedSpec);
2360
- }
2361
- try {
2362
- await runLifecycleSteps(installedSpec.lifecycle?.install, "install", artifacts, options.task, options.exec);
2363
- // Auto-pull docker images declared in tasks (deduplicated, skip already-pulled by lifecycle steps)
2364
- const pulledImages = new Set(artifacts.filter(a => a.type === "image").map(a => a.path));
2365
- const imagesToPull = [...new Set(installedSpec.tasks.filter(t => t.image).map(t => t.image))];
2366
- for (const image of imagesToPull) {
2367
- if (!pulledImages.has(image)) {
2368
- await pullDockerImageStep("install", image, `docker pull ${image}`, options.task);
2369
- artifacts.push({ type: "image", path: image });
2370
- }
2371
- }
2372
- const bootstrappedByAdapter = await bootstrapAdapterManagedApp(appId, installedSpec, options);
2373
- if (!bootstrappedByAdapter) {
2374
- safeWriteJson(join(appDir, "instance.json"), buildGenericAppMeta(appId, installedSpec, manifest.installed_at), true);
2375
- }
2376
- }
2377
- catch (e) {
2378
- try {
2379
- await runLifecycleSteps(installedSpec.lifecycle?.uninstall, "rollback_uninstall", undefined, options.task, options.exec);
2380
- }
2381
- catch (rollbackError) {
2382
- process.stdout.write(` [app-manager] rollback uninstall failed: ${rollbackError.message}\n`);
2383
- emitInstallTaskLog(options.task, `[app-manager] rollback uninstall failed: ${rollbackError.message}`);
2384
- }
2385
- cleanupArtifacts(artifacts, options.task);
2386
- rmSync(appDir, { recursive: true, force: true });
2387
- throw decorateInstallError(e, installedSpec);
2388
- }
2389
- if (artifacts.length > 0) {
2390
- manifest.artifacts = artifacts;
2391
- safeWriteJson(join(appDir, "manifest.json"), manifest, true);
2392
- }
2393
- removeInstallLock(appDir);
2394
- emitInstallTaskLog(options.task, `[app-manager] removed ${INSTALL_LOCK_FILENAME} for ${appId}`);
2395
- invalidateRuntimeIdentity(appId);
2396
- return { spec: installedSpec, manifest };
2397
- }
2398
- export function listApps() {
2399
- const results = [];
2400
- for (const { rootDir, installMode } of [
2401
- { rootDir: APPS_DIR, installMode: "app-dir" },
2402
- ]) {
2403
- if (!existsSync(rootDir))
2404
- continue;
2405
- let entries;
2406
- try {
2407
- entries = readdirSync(rootDir, { withFileTypes: true });
2408
- }
2409
- catch {
2410
- continue;
2411
- }
2412
- for (const entry of entries) {
2413
- if (!entry.isDirectory())
2414
- continue;
2415
- const appId = entry.name;
2416
- const appDir = join(rootDir, appId);
2417
- try {
2418
- const appData = readAppFromDir(appDir, appId, installMode);
2419
- if (!appData)
2420
- continue;
2421
- results.push(appData);
2422
- }
2423
- catch (e) {
2424
- console.warn(`[app-manager] Skipping app '${appId}': ${e.message}`);
2425
- }
2426
- }
2427
- }
2428
- const deduped = new Map();
2429
- for (const item of results) {
2430
- deduped.set(item.manifest.id, item);
2431
- }
2432
- return [...deduped.values()];
2433
- }
2434
- export function getApp(id) {
2435
- if (!APP_ID_RE.test(id))
2436
- return null;
2437
- const location = resolveAppLocation(id);
2438
- if (!location)
2439
- return null;
2440
- return readAppFromDir(location.dir, id, location.installMode);
2441
- }
2442
- function writeAppManagerWarnings(warnings) {
2443
- for (const warning of warnings) {
2444
- process.stdout.write(` [app-manager] ${warning}\n`);
2445
- }
2446
- }
2447
- export async function uninstallApp(id, options = {}) {
2448
- if (!APP_ID_RE.test(id))
2449
- return;
2450
- const appData = getApp(id);
2451
- const warnings = [];
2452
- if (isInstanceBackedApp(appData)) {
2453
- const { stopNomadJobInstance } = await import("../nomad-manager.js");
2454
- const stopResult = await stopNomadJobInstance(id, true);
2455
- if (!stopResult.ok && stopResult.error && !stopResult.error.includes("not found") && !stopResult.error.includes("not running")) {
2456
- warnings.push(`应用 '${id}' 停止失败: ${stopResult.error}`);
2457
- }
2458
- if (appData) {
2459
- // For multi-instance container apps the uninstalled app is the base
2460
- // template and may have spawned children via copyApp. Children carry
2461
- // `app_id === id`; stop + delete them here so they don't outlive the
2462
- // template that described their lifecycle.
2463
- if (appData.spec.singleInstance === false) {
2464
- warnings.push(...await deleteLinkedInstances(appData.manifest.id));
2465
- }
2466
- const uninstallSteps = selectUninstallLifecycleSteps(appData);
2467
- if (uninstallSteps?.length) {
2468
- try {
2469
- await runLifecycleSteps(uninstallSteps, "uninstall", undefined, undefined, options.exec);
2470
- }
2471
- catch (e) {
2472
- writeAppManagerWarnings(warnings);
2473
- throw new Error(`卸载生命周期执行失败: ${e.message}`);
2474
- }
2475
- }
2476
- const cleanupList = selectCleanupArtifacts(appData);
2477
- if (cleanupList.length > 0) {
2478
- cleanupArtifacts(cleanupList);
2479
- }
2480
- }
2481
- const instanceManager = await import("../instance-manager.js");
2482
- const deleteResult = await instanceManager.deleteInstance(id);
2483
- if (!deleteResult.ok && deleteResult.warnings?.length) {
2484
- warnings.push(...deleteResult.warnings);
2485
- }
2486
- await syncCapabilitiesForApp(id);
2487
- invalidateRuntimeIdentity(id);
2488
- writeAppManagerWarnings(warnings);
2489
- return;
2490
- }
2491
- const adapterManagedAgentType = getAdapterManagedAgentType(appData);
2492
- if (adapterManagedAgentType) {
2493
- const { stopNomadJobInstance } = await import("../nomad-manager.js");
2494
- const stopResult = await stopNomadJobInstance(id, true);
2495
- if (!stopResult.ok && stopResult.error && !stopResult.error.includes("not found") && !stopResult.error.includes("not running")) {
2496
- warnings.push(`应用 '${id}' 停止失败: ${stopResult.error}`);
2497
- }
2498
- if (appData) {
2499
- warnings.push(...await deleteLinkedInstances(appData.manifest.id));
2500
- const uninstallSteps = selectUninstallLifecycleSteps(appData);
2501
- if (uninstallSteps?.length) {
2502
- try {
2503
- await runLifecycleSteps(uninstallSteps, "uninstall", undefined, undefined, options.exec);
2504
- }
2505
- catch (e) {
2506
- writeAppManagerWarnings(warnings);
2507
- throw new Error(`卸载生命周期执行失败: ${e.message}`);
2508
- }
2509
- }
2510
- const cleanupList = selectCleanupArtifacts(appData);
2511
- if (cleanupList.length > 0) {
2512
- cleanupArtifacts(cleanupList);
2513
- }
2514
- }
2515
- const deleteResult = await instanceServices.deleteInstance(id);
2516
- if (!deleteResult.ok && deleteResult.warnings?.length) {
2517
- warnings.push(...deleteResult.warnings);
2518
- }
2519
- await syncCapabilitiesForApp(id);
2520
- invalidateRuntimeIdentity(id);
2521
- writeAppManagerWarnings(warnings);
2522
- return;
2523
- }
2524
- const stopResult = await stopApp(id, true);
2525
- if (stopResult.ok) {
2526
- process.stdout.write(` [app-manager] Stopped running job for "${id}"\n`);
2527
- }
2528
- else if (stopResult.error && !stopResult.error.includes("not found") && !stopResult.error.includes("not running")) {
2529
- warnings.push(`应用 '${id}' 停止失败: ${stopResult.error}`);
2530
- }
2531
- if (appData) {
2532
- warnings.push(...await deleteLinkedInstances(appData.manifest.id));
2533
- const uninstallSteps = selectUninstallLifecycleSteps(appData);
2534
- if (uninstallSteps?.length) {
2535
- try {
2536
- await runLifecycleSteps(uninstallSteps, "uninstall", undefined, undefined, options.exec);
2537
- }
2538
- catch (e) {
2539
- writeAppManagerWarnings(warnings);
2540
- throw new Error(`卸载生命周期执行失败: ${e.message}`);
2541
- }
2542
- }
2543
- const cleanupList = selectCleanupArtifacts(appData);
2544
- if (cleanupList.length > 0) {
2545
- cleanupArtifacts(cleanupList);
2546
- }
2547
- }
2548
- const appDir = resolveAppDir(id) ?? appDirForId(id);
2549
- rmSync(appDir, { recursive: true, force: true });
2550
- await syncCapabilitiesForApp(id);
2551
- invalidateRuntimeIdentity(id);
2552
- writeAppManagerWarnings(warnings);
2553
- }
2554
- export function uninstallAppTask(id, exec) {
2555
- if (!getApp(id)) {
2556
- return { ok: false, error: `App '${id}' not found`, kind: "uninstall" };
2557
- }
2558
- return startAppLifecycleTask(id, "uninstall", `开始卸载应用 ${id}...`, `应用 ${id} 卸载完成`, async () => {
2559
- await uninstallApp(id, { exec });
2560
- });
2561
- }
2562
- export async function runPostStartSteps(spec) {
2563
- await runLifecycleSteps(spec.lifecycle?.post_start, "post_start");
2564
- }
2565
- async function runPostStartStepsAfterStart(appId, spec) {
2566
- if (!spec.lifecycle?.post_start?.length)
2567
- return;
2568
- if (spec.provides?.length || spec.lifecycle.post_start.length) {
2569
- await waitForAppRuntimeRunning(appId, spec.lifecycle.post_start.length ? 120_000 : 30_000);
2570
- }
2571
- await runPostStartSteps(spec);
2572
- }
2573
- export async function waitForAppRuntimeRunning(appId, timeoutMs = 30_000, pollIntervalMs = 1_000) {
2574
- const deadline = Date.now() + timeoutMs;
2575
- while (Date.now() < deadline) {
2576
- const status = await getAppRuntimeStatus(appId);
2577
- if (status.status === "running")
2578
- return true;
2579
- if (status.status === "dead" || status.status === "failed")
2580
- return false;
2581
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
2582
- }
2583
- return false;
2584
- }
2585
- export async function syncCapabilitiesForApp(appId) {
2586
- const { syncCapabilitiesForApp: sync } = await import("../capability-sync.js");
2587
- await sync(appId);
2588
- }
2589
- export async function refreshCapabilityRegistry(options = {}) {
2590
- const { refreshCapabilityRegistry: refresh } = await import("../capability-sync.js");
2591
- await refresh(options);
2592
- }
2593
- export function resolveRequires(spec) {
2594
- if (!spec.requires || spec.requires.length === 0)
2595
- return {};
2596
- const result = {};
2597
- for (const req of spec.requires) {
2598
- const entry = capabilityRegistry
2599
- .listProviders(req.capability)
2600
- .find((provider) => provider.status === "running");
2601
- if (entry) {
2602
- result[req.inject_as] = entry.address;
2603
- }
2604
- else if (req.required !== false) {
2605
- throw new Error(`Required capability '${req.capability}' is not registered`);
2606
- }
2607
- }
2608
- return result;
2609
- }
2610
- // ── App Lifecycle (delegates to nomad-manager) ───────────────────
2611
- /**
2612
- * Read `instance.json` for a generic container app. Returns the parsed
2613
- * record or `null` if missing/unreadable. Adapter-managed consumers
2614
- * (OpenClaw, Hermes) keep their state elsewhere; this is the generic
2615
- * app-dir layout under `~/.jishushell/apps/<appId>/`.
2616
- */
2617
- function readAppInstanceJson(appId) {
2618
- try {
2619
- const path = join(APPS_DIR, appId, "instance.json");
2620
- return safeReadJson(path, `app-instance:${appId}`) ?? null;
2621
- }
2622
- catch (e) {
2623
- console.warn(`[app-instance] read failed for ${appId}: ${e?.message ?? e}`);
2624
- return null;
2625
- }
2626
- }
2627
- /**
2628
- * Read `instance.json["connections-env"]` for a generic container app and
2629
- * return a copy of the persisted env vars. Adapter-managed consumers
2630
- * (OpenClaw, Hermes) route connections through `applyConnectionEnv` and
2631
- * don't write to this field. Best-effort — failures return empty object
2632
- * so they never block startup.
2633
- */
2634
- function loadConnectionsEnv(appId) {
2635
- const inst = readAppInstanceJson(appId);
2636
- const env = inst?.["connections-env"];
2637
- if (env && typeof env === "object" && !Array.isArray(env)) {
2638
- const out = {};
2639
- for (const [k, v] of Object.entries(env)) {
2640
- if (typeof v === "string")
2641
- out[k] = v;
2642
- }
2643
- return out;
2644
- }
2645
- return {};
2646
- }
2647
- export async function startApp(appId) {
2648
- // Serialize against PUT /connections, stopApp and concurrent startApp on
2649
- // the same instance — see `utils/instance-lock.ts` and §10.3 of the
2650
- // app-interconnect design.
2651
- return withInstanceLock(appId, () => startAppImpl(appId));
2652
- }
2653
- async function startAppImpl(appId) {
2654
- const appData = getApp(appId);
2655
- if (!appData) {
2656
- return { ok: false, error: `App '${appId}' not found` };
2657
- }
2658
- if (appData.install_state === "installing") {
2659
- return { ok: false, error: `App '${appId}' is still installing` };
2660
- }
2661
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2662
- if (appData.spec.lifecycle?.pre_start?.length) {
2663
- await runLifecycleSteps(appData.spec.lifecycle.pre_start, "pre_start");
2664
- }
2665
- const { startNomadJobInstance } = await import("../nomad-manager.js");
2666
- const result = await startNomadJobInstance(appId);
2667
- if (!result.ok)
2668
- return result;
2669
- await runPostStartStepsAfterStart(appId, appData.spec);
2670
- await syncCapabilitiesForApp(appId);
2671
- return result;
2672
- }
2673
- // Resolve requires through the v3 connection-resolver in runtime mode so
2674
- // missing-required / ambiguous-prefix / invalid-binding all surface with
2675
- // the structured 412/409/400 codes (§6.4 Phase 4 of the app-interconnect
2676
- // design). The legacy `resolveRequires` path threw a bare Error which the
2677
- // route handler could only forward as a generic 400 — losing the bind-vs-
2678
- // start-vs-pick distinction the UI needs.
2679
- const instJson = readAppInstanceJson(appId);
2680
- const persistedEnv = loadConnectionsEnv(appId);
2681
- let extraEnv = {};
2682
- try {
2683
- await refreshCapabilityRegistry();
2684
- const { resolveConnections, resolvedToLegacyEnv } = await import("../connection-resolver.js");
2685
- const { renderRuntimeConnectionsEnv } = await import("../connection-apply.js");
2686
- const { resolved } = resolveConnections(appData.spec, { connections: instJson?.connections ?? {} }, "runtime");
2687
- const runtimeEnv = await renderRuntimeConnectionsEnv(appData.spec, {
2688
- id: appId,
2689
- connections: instJson?.connections,
2690
- });
2691
- // Frozen `connections-env` is the lowest-priority fallback for legacy
2692
- // apps that pre-date resolveConnections. Runtime-rendered env wins so
2693
- // provider port/IP changes propagate without re-binding.
2694
- extraEnv = { ...persistedEnv, ...resolvedToLegacyEnv(resolved), ...runtimeEnv };
2695
- }
2696
- catch (e) {
2697
- return {
2698
- ok: false,
2699
- error: e.message,
2700
- ...(e.code ? { code: e.code } : {}),
2701
- ...(typeof e.statusCode === "number" ? { statusCode: e.statusCode } : {}),
2702
- };
2703
- }
2704
- const { startAppJob: nomadStart, checkDependencies } = await import("../nomad-manager.js");
2705
- const depCheck = await checkDependencies(appData.spec);
2706
- if (!depCheck.ok) {
2707
- return { ok: false, error: depCheck.errors.join("; ") };
2708
- }
2709
- // Run pre_start steps right before submitting the Nomad job — gives
2710
- // apps a place to enforce per-start invariants (e.g. chown the
2711
- // bind-mount source so the container's runtime uid can write).
2712
- if (appData.spec.lifecycle?.pre_start?.length) {
2713
- await runLifecycleSteps(appData.spec.lifecycle.pre_start, "pre_start");
2714
- }
2715
- const result = await nomadStart(appData.spec, appId, extraEnv);
2716
- if (!result.ok) {
2717
- return result;
2718
- }
2719
- await runPostStartStepsAfterStart(appId, appData.spec);
2720
- await syncCapabilitiesForApp(appId);
2721
- return result;
2722
- }
2723
- export function startAppTask(appId) {
2724
- if (!getApp(appId)) {
2725
- return { ok: false, error: `App '${appId}' not found`, kind: "start" };
2726
- }
2727
- return startAppLifecycleTask(appId, "start", `开始启动应用 ${appId}...`, `应用 ${appId} 已启动`, async () => {
2728
- const result = await startApp(appId);
2729
- if (!result.ok) {
2730
- throw new Error(result.error || `App '${appId}' start failed`);
2731
- }
2732
- });
2733
- }
2734
- export async function stopApp(appId, purge = false) {
2735
- return withInstanceLock(appId, () => stopAppImpl(appId, purge));
2736
- }
2737
- async function stopAppImpl(appId, purge) {
2738
- const appData = getApp(appId);
2739
- if (appData?.install_state === "installing") {
2740
- return { ok: false, error: `App '${appId}' is still installing` };
2741
- }
2742
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2743
- const { stopNomadJobInstance } = await import("../nomad-manager.js");
2744
- const result = await stopNomadJobInstance(appId, purge);
2745
- if (result.ok || result.error?.includes("not running") || result.error?.includes("not found")) {
2746
- await syncCapabilitiesForApp(appId);
2747
- }
2748
- return result;
2749
- }
2750
- const { stopAppJob } = await import("../nomad-manager.js");
2751
- const result = await stopAppJob(appId, purge);
2752
- if (result.ok || result.error?.includes("not running") || result.error?.includes("not found")) {
2753
- await syncCapabilitiesForApp(appId);
2754
- }
2755
- return result;
2756
- }
2757
- export function stopAppTask(appId, purge = false) {
2758
- if (!getApp(appId)) {
2759
- return { ok: false, error: `App '${appId}' not found`, kind: "stop" };
2760
- }
2761
- return startAppLifecycleTask(appId, "stop", `开始停止应用 ${appId}${purge ? "(purge)" : ""}...`, `应用 ${appId} 已停止${purge ? "(purged)" : ""}`, async () => {
2762
- const result = await stopApp(appId, purge);
2763
- if (!result.ok) {
2764
- throw new Error(result.error || `App '${appId}' stop failed`);
2765
- }
2766
- });
2767
- }
2768
- export async function restartApp(appId) {
2769
- // Hold the instance lock for the entire restart sequence so a concurrent
2770
- // PUT /connections / startApp / stopApp on the same id can't observe a
2771
- // half-restarted state. Inner stop/start calls reuse the same lock id;
2772
- // we route them through the *Impl helpers to avoid re-acquiring.
2773
- return withInstanceLock(appId, async () => {
2774
- const appData = getApp(appId);
2775
- if (appData?.install_state === "installing") {
2776
- return { ok: false, error: `App '${appId}' is still installing` };
2777
- }
2778
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2779
- const { restartNomadJobInstance } = await import("../nomad-manager.js");
2780
- const result = await restartNomadJobInstance(appId);
2781
- if (!result.ok)
2782
- return result;
2783
- if (appData) {
2784
- await runPostStartStepsAfterStart(appId, appData.spec);
2785
- }
2786
- await syncCapabilitiesForApp(appId);
2787
- return result;
2788
- }
2789
- const stopResult = await stopAppImpl(appId, true);
2790
- if (!stopResult.ok && !stopResult.error?.includes("not found")) {
2791
- return stopResult;
2792
- }
2793
- return startAppImpl(appId);
2794
- });
2795
- }
2796
- export function restartAppTask(appId) {
2797
- if (!getApp(appId)) {
2798
- return { ok: false, error: `App '${appId}' not found`, kind: "restart" };
2799
- }
2800
- return startAppLifecycleTask(appId, "restart", `开始重启应用 ${appId}...`, `应用 ${appId} 已重启`, async () => {
2801
- const result = await restartApp(appId);
2802
- if (!result.ok) {
2803
- throw new Error(result.error || `App '${appId}' restart failed`);
2804
- }
2805
- });
2806
- }
2807
- export async function getAppRuntimeStatus(appId) {
2808
- const appData = getApp(appId);
2809
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2810
- const { getInstanceStatus } = await import("../nomad-manager.js");
2811
- const st = await getInstanceStatus(appId);
2812
- return {
2813
- status: st.status,
2814
- uptime: st.uptime ?? undefined,
2815
- memory_mb: st.memory_mb ?? undefined,
2816
- };
2817
- }
2818
- const { getAppStatus, isBinaryRunning } = await import("../nomad-manager.js");
2819
- const st = await getAppStatus(appId);
2820
- // For process-runtime apps: if Nomad can't place the job (pending/unplaced due
2821
- // to missing raw_exec driver) but the binary is already running on the host,
2822
- // report the real status as "running" so the UI doesn't show a failure.
2823
- if (st.status === "pending" || st.status === "stopped") {
2824
- const appData = getApp(appId);
2825
- if (appData) {
2826
- const processTask = appData.spec.tasks.find((t) => t.runtime === "process" && (t.role ?? "service") === "service" && t.command);
2827
- if (processTask) {
2828
- const running = await isBinaryRunning(processTask.command);
2829
- if (running) {
2830
- return { status: "running" };
2831
- }
2832
- }
2833
- }
2834
- }
2835
- return {
2836
- status: st.status,
2837
- uptime: st.uptime ?? undefined,
2838
- memory_mb: st.memory_mb ?? undefined,
2839
- tasks: st.tasks,
2840
- error: st.error,
2841
- };
2842
- }
2843
- export async function getAppStatus(appId) {
2844
- const appData = getApp(appId);
2845
- if (appData?.install_state === "installing") {
2846
- return {
2847
- status: "installing",
2848
- tasks: {},
2849
- pid: null,
2850
- uptime: null,
2851
- memory_mb: null,
2852
- cpu_percent: null,
2853
- restarts: 0,
2854
- error: undefined,
2855
- };
2856
- }
2857
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2858
- const { getInstanceStatus } = await import("../nomad-manager.js");
2859
- const st = await getInstanceStatus(appId);
2860
- return {
2861
- status: st.status,
2862
- tasks: {},
2863
- pid: null,
2864
- uptime: st.uptime,
2865
- memory_mb: st.memory_mb,
2866
- cpu_percent: st.cpu_percent,
2867
- restarts: 0,
2868
- error: undefined,
2869
- };
2870
- }
2871
- const { getAppStatus: nomadGetAppStatus } = await import("../nomad-manager.js");
2872
- return nomadGetAppStatus(appId);
2873
- }
2874
- export async function getAppLogs(appId, taskName, lines = 200, logType = "stderr") {
2875
- const appData = getApp(appId);
2876
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2877
- const { getInstanceLogs } = await import("../nomad-manager.js");
2878
- return getInstanceLogs(appId, lines, logType);
2879
- }
2880
- const { getAppLogs: nomadGetAppLogs } = await import("../nomad-manager.js");
2881
- return nomadGetAppLogs(appId, taskName, lines, logType);
2882
- }
2883
- export async function execInApp(appId, command, timeoutMs) {
2884
- const appData = getApp(appId);
2885
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2886
- const { execInInstance } = await import("../nomad-manager.js");
2887
- return execInInstance(appId, command, timeoutMs ?? 120_000);
2888
- }
2889
- const { execInApp: nomadExecInApp } = await import("../nomad-manager.js");
2890
- return nomadExecInApp(appId, "", command, timeoutMs ?? 120_000);
2891
- }
2892
- export async function streamExecInApp(appId, command, handlers, options) {
2893
- const timeoutMs = options?.timeoutMs ?? 120_000;
2894
- const taskName = options?.taskName ?? "";
2895
- const appData = getApp(appId);
2896
- if (isInstanceBackedApp(appData) || getAdapterManagedAgentType(appData)) {
2897
- const { streamExecInInstance } = await import("../nomad-manager.js");
2898
- return streamExecInInstance(appId, command, handlers, timeoutMs, taskName);
2899
- }
2900
- const { streamExecInApp: nomadStreamExecInApp } = await import("../nomad-manager.js");
2901
- return nomadStreamExecInApp(appId, taskName, command, handlers, timeoutMs);
2902
- }
2903
- export async function copyApp(sourceId) {
2904
- const source = getApp(sourceId);
2905
- if (!source)
2906
- throw new Error(`App '${sourceId}' not found`);
2907
- if (source.spec.singleInstance)
2908
- throw new Error(`App '${sourceId}' is singleInstance — cannot copy`);
2909
- // A faithful copy requires re-materialization: ${app_id} tokens, port
2910
- // shifts, and app-scoped paths all need to be recomputed for the new
2911
- // slot. Naively swapping the `id:` line leaves lifecycle scripts and
2912
- // volume sources pointing at the source's settings dir, which then
2913
- // collides on start and leaks state across instances.
2914
- //
2915
- // The base app's on-disk yaml for offset=0 is written as the pristine
2916
- // originalSpecYaml (see resolveInstallTarget → installedSpecYaml), so
2917
- // reading from the base dir recovers the template that installApp can
2918
- // re-materialize. The source's own yaml is already baked to its own
2919
- // offset when source is itself a copy, and installApp cannot cleanly
2920
- // un-do that substitution from a string diff alone.
2921
- const baseId = source.spec.id;
2922
- const baseDir = resolveAppDir(baseId);
2923
- if (!baseDir) {
2924
- throw new Error(`App '${sourceId}' base '${baseId}' is not installed; cannot copy without the pristine spec. Reinstall the base app first.`);
2925
- }
2926
- const baseYaml = readFileSync(join(baseDir, "app-spec.yaml"), "utf-8");
2927
- // Delegate: installApp runs resolveInstallTarget (which picks the next
2928
- // free <baseId>-<prefix><n> slot and applies a fresh port shift) and
2929
- // executes the install lifecycle in the new app dir.
2930
- return installApp(baseYaml);
2931
- }
2932
- export { onConfigChange, notifyConfigChange, instanceDir, instanceMetaPath, defaultModelEnvFile, normalizePath, extractGatewayPort, isPortInUse, allocateGatewayPort, releasePendingPort, getResolvedOpenclawBin, resolveServiceUser, chownToServiceUser, parseEnvFile, updateEnvFile, inferProviderApiKeyEnvName, listInstances, getInstance, updateInstanceMeta, deleteInstance, getConfig, getStoredConfig, saveConfig, CHANNEL_PLUGIN_MAP, isChannelPluginInstalled, createInstance, getOpenclawHome, saveFeishuCredentials, saveWeixinCredentials, getWeixinAccounts, getOpenclawConfigPath, getLegacyOpenclawConfigPath, getInstanceRuntime, getRuntimeEnvFiles, getGatewayPort, getGatewayHost, getListeningHostForPort, getHostForAppPort, urlHost, findInstancesSharingOpenclawHome, reallocateGatewayPort, findInstancesSharingGatewayPort, getRuntimeEnv, defaultGatewayPort, releasePort, } from "../instance-manager.js";
2933
- //# sourceMappingURL=app-manager.js.map