jishushell 0.4.24 → 0.5.15

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 (281) hide show
  1. package/INSTALL-NOTICE +11 -0
  2. package/apps/anythingllm-container.yaml +287 -0
  3. package/apps/browserless-chromium-container.yaml +90 -0
  4. package/apps/filebrowser-container.yaml +163 -0
  5. package/apps/hermes-container.yaml +36 -2
  6. package/apps/ollama-binary.yaml +91 -90
  7. package/apps/ollama-cpu-container.yaml +8 -1
  8. package/apps/ollama-with-hollama-binary.yaml +91 -90
  9. package/apps/openclaw-binary.yaml +38 -1
  10. package/apps/openclaw-container.yaml +45 -2
  11. package/apps/openclaw-with-ollama-container.yaml +11 -2
  12. package/apps/openclaw-with-searxng-container.yaml +26 -2
  13. package/apps/openwebui-container.yaml +45 -1
  14. package/apps/playwright-container.yaml +7 -1
  15. package/apps/searxng-container.yaml +58 -7
  16. package/apps/weknora-container.yaml +471 -0
  17. package/dist/cli/app.js +79 -9
  18. package/dist/cli/app.js.map +1 -1
  19. package/dist/cli/doctor.d.ts +12 -12
  20. package/dist/cli/doctor.js +242 -55
  21. package/dist/cli/doctor.js.map +1 -1
  22. package/dist/cli/llm.d.ts +4 -3
  23. package/dist/cli/llm.js +4 -3
  24. package/dist/cli/llm.js.map +1 -1
  25. package/dist/cli/panel.d.ts +6 -5
  26. package/dist/cli/panel.js +10 -9
  27. package/dist/cli/panel.js.map +1 -1
  28. package/dist/config.d.ts +19 -0
  29. package/dist/config.js +99 -1
  30. package/dist/config.js.map +1 -1
  31. package/dist/control.d.ts +7 -6
  32. package/dist/control.js +7 -6
  33. package/dist/control.js.map +1 -1
  34. package/dist/install.js +3 -3
  35. package/dist/install.js.map +1 -1
  36. package/dist/routes/agent-apps.d.ts +1 -1
  37. package/dist/routes/agent-apps.js +1 -1
  38. package/dist/routes/apps.js +44 -11
  39. package/dist/routes/apps.js.map +1 -1
  40. package/dist/routes/auth.js +5 -2
  41. package/dist/routes/auth.js.map +1 -1
  42. package/dist/routes/backup.js +64 -11
  43. package/dist/routes/backup.js.map +1 -1
  44. package/dist/routes/external-mounts.d.ts +17 -0
  45. package/dist/routes/external-mounts.js +73 -0
  46. package/dist/routes/external-mounts.js.map +1 -0
  47. package/dist/routes/file-mounts.d.ts +13 -0
  48. package/dist/routes/file-mounts.js +90 -0
  49. package/dist/routes/file-mounts.js.map +1 -0
  50. package/dist/routes/files-organize.d.ts +28 -0
  51. package/dist/routes/files-organize.js +167 -0
  52. package/dist/routes/files-organize.js.map +1 -0
  53. package/dist/routes/files.d.ts +31 -0
  54. package/dist/routes/files.js +321 -0
  55. package/dist/routes/files.js.map +1 -0
  56. package/dist/routes/instances.js +826 -17
  57. package/dist/routes/instances.js.map +1 -1
  58. package/dist/routes/internal.d.ts +2 -0
  59. package/dist/routes/internal.js +59 -0
  60. package/dist/routes/internal.js.map +1 -0
  61. package/dist/routes/llm.js +24 -35
  62. package/dist/routes/llm.js.map +1 -1
  63. package/dist/routes/setup.js +10 -10
  64. package/dist/routes/setup.js.map +1 -1
  65. package/dist/routes/system.js +1 -1
  66. package/dist/routes/system.js.map +1 -1
  67. package/dist/routes/webdav.d.ts +17 -0
  68. package/dist/routes/webdav.js +114 -0
  69. package/dist/routes/webdav.js.map +1 -0
  70. package/dist/server.d.ts +9 -0
  71. package/dist/server.js +751 -20
  72. package/dist/server.js.map +1 -1
  73. package/dist/services/agent-apps/catalog.js +4 -3
  74. package/dist/services/agent-apps/catalog.js.map +1 -1
  75. package/dist/services/agent-apps/index.d.ts +1 -1
  76. package/dist/services/agent-apps/index.js +1 -1
  77. package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
  78. package/dist/services/agent-apps/installers/adapter.js +1 -1
  79. package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
  80. package/dist/services/agent-apps/installers/shell-script.js +3 -3
  81. package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
  82. package/dist/services/agent-apps/types.d.ts +2 -2
  83. package/dist/services/agent-apps/types.js +1 -1
  84. package/dist/services/app/app-compiler.d.ts +1 -1
  85. package/dist/services/app/app-compiler.js +5 -5
  86. package/dist/services/app/app-compiler.js.map +1 -1
  87. package/dist/services/app/app-manager.d.ts +25 -1
  88. package/dist/services/app/app-manager.js +829 -150
  89. package/dist/services/app/app-manager.js.map +1 -1
  90. package/dist/services/app/custom-manager.js.map +1 -1
  91. package/dist/services/app/hermes-agent-manager.js +7 -4
  92. package/dist/services/app/hermes-agent-manager.js.map +1 -1
  93. package/dist/services/app/ollama-manager.js +1 -1
  94. package/dist/services/app/ollama-manager.js.map +1 -1
  95. package/dist/services/app/openclaw-manager.js +20 -3
  96. package/dist/services/app/openclaw-manager.js.map +1 -1
  97. package/dist/services/app/platform-transform.d.ts +32 -0
  98. package/dist/services/app/platform-transform.js +65 -0
  99. package/dist/services/app/platform-transform.js.map +1 -0
  100. package/dist/services/app/provide-resolver.d.ts +29 -0
  101. package/dist/services/app/provide-resolver.js +112 -0
  102. package/dist/services/app/provide-resolver.js.map +1 -0
  103. package/dist/services/app-passwords.d.ts +61 -0
  104. package/dist/services/app-passwords.js +173 -0
  105. package/dist/services/app-passwords.js.map +1 -0
  106. package/dist/services/backup-manager.d.ts +11 -0
  107. package/dist/services/backup-manager.js +177 -4
  108. package/dist/services/backup-manager.js.map +1 -1
  109. package/dist/services/capability-endpoint-validator.d.ts +41 -0
  110. package/dist/services/capability-endpoint-validator.js +104 -0
  111. package/dist/services/capability-endpoint-validator.js.map +1 -0
  112. package/dist/services/capability-health.d.ts +16 -0
  113. package/dist/services/capability-health.js +121 -0
  114. package/dist/services/capability-health.js.map +1 -0
  115. package/dist/services/capability-registry.d.ts +106 -0
  116. package/dist/services/capability-registry.js +313 -0
  117. package/dist/services/capability-registry.js.map +1 -0
  118. package/dist/services/connection-apply.d.ts +91 -0
  119. package/dist/services/connection-apply.js +475 -0
  120. package/dist/services/connection-apply.js.map +1 -0
  121. package/dist/services/connection-resolver.d.ts +65 -0
  122. package/dist/services/connection-resolver.js +281 -0
  123. package/dist/services/connection-resolver.js.map +1 -0
  124. package/dist/services/connection-transactor.d.ts +39 -0
  125. package/dist/services/connection-transactor.js +351 -0
  126. package/dist/services/connection-transactor.js.map +1 -0
  127. package/dist/services/external-mounts.d.ts +40 -0
  128. package/dist/services/external-mounts.js +187 -0
  129. package/dist/services/external-mounts.js.map +1 -0
  130. package/dist/services/files-manager.d.ts +252 -0
  131. package/dist/services/files-manager.js +1075 -0
  132. package/dist/services/files-manager.js.map +1 -0
  133. package/dist/services/files-mounts.d.ts +42 -0
  134. package/dist/services/files-mounts.js +207 -0
  135. package/dist/services/files-mounts.js.map +1 -0
  136. package/dist/services/instance-manager.d.ts +13 -0
  137. package/dist/services/instance-manager.js +138 -46
  138. package/dist/services/instance-manager.js.map +1 -1
  139. package/dist/services/llm-proxy/index.d.ts +16 -2
  140. package/dist/services/llm-proxy/index.js +48 -44
  141. package/dist/services/llm-proxy/index.js.map +1 -1
  142. package/dist/services/llm-proxy/probe.d.ts +6 -0
  143. package/dist/services/llm-proxy/probe.js +85 -0
  144. package/dist/services/llm-proxy/probe.js.map +1 -0
  145. package/dist/services/llm-proxy/ssrf.d.ts +1 -0
  146. package/dist/services/llm-proxy/ssrf.js +24 -9
  147. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  148. package/dist/services/nomad-manager.d.ts +4 -0
  149. package/dist/services/nomad-manager.js +428 -35
  150. package/dist/services/nomad-manager.js.map +1 -1
  151. package/dist/services/organize/applier.d.ts +46 -0
  152. package/dist/services/organize/applier.js +218 -0
  153. package/dist/services/organize/applier.js.map +1 -0
  154. package/dist/services/organize/rules.d.ts +57 -0
  155. package/dist/services/organize/rules.js +286 -0
  156. package/dist/services/organize/rules.js.map +1 -0
  157. package/dist/services/organize/scanner.d.ts +50 -0
  158. package/dist/services/organize/scanner.js +366 -0
  159. package/dist/services/organize/scanner.js.map +1 -0
  160. package/dist/services/organize/store.d.ts +14 -0
  161. package/dist/services/organize/store.js +82 -0
  162. package/dist/services/organize/store.js.map +1 -0
  163. package/dist/services/panel-manager.js +20 -1
  164. package/dist/services/panel-manager.js.map +1 -1
  165. package/dist/services/process-manager.js +4 -3
  166. package/dist/services/process-manager.js.map +1 -1
  167. package/dist/services/runtime/adapters/hermes.d.ts +30 -1
  168. package/dist/services/runtime/adapters/hermes.js +219 -6
  169. package/dist/services/runtime/adapters/hermes.js.map +1 -1
  170. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
  171. package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
  172. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
  173. package/dist/services/runtime/adapters/openclaw-routes.d.ts +8 -2
  174. package/dist/services/runtime/adapters/openclaw-routes.js +68 -0
  175. package/dist/services/runtime/adapters/openclaw-routes.js.map +1 -1
  176. package/dist/services/runtime/adapters/openclaw.d.ts +177 -0
  177. package/dist/services/runtime/adapters/openclaw.js +1171 -11
  178. package/dist/services/runtime/adapters/openclaw.js.map +1 -1
  179. package/dist/services/runtime/instance.d.ts +1 -1
  180. package/dist/services/runtime/instance.js +1 -1
  181. package/dist/services/runtime/instance.js.map +1 -1
  182. package/dist/services/runtime/mcp-shims/anythingllm-shim.d.ts +46 -0
  183. package/dist/services/runtime/mcp-shims/anythingllm-shim.js +281 -0
  184. package/dist/services/runtime/mcp-shims/anythingllm-shim.js.map +1 -0
  185. package/dist/services/runtime/mcp-shims/drive-shim.d.ts +54 -0
  186. package/dist/services/runtime/mcp-shims/drive-shim.js +489 -0
  187. package/dist/services/runtime/mcp-shims/drive-shim.js.map +1 -0
  188. package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
  189. package/dist/services/runtime/mcp-shims/firewall.js +129 -0
  190. package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
  191. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
  192. package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
  193. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
  194. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
  195. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
  196. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
  197. package/dist/services/runtime/migrations.d.ts +8 -0
  198. package/dist/services/runtime/migrations.js +100 -0
  199. package/dist/services/runtime/migrations.js.map +1 -1
  200. package/dist/services/runtime/types.d.ts +46 -0
  201. package/dist/services/setup-manager.js +99 -24
  202. package/dist/services/setup-manager.js.map +1 -1
  203. package/dist/services/suggestions.d.ts +27 -0
  204. package/dist/services/suggestions.js +133 -0
  205. package/dist/services/suggestions.js.map +1 -0
  206. package/dist/services/task-registry.js +4 -2
  207. package/dist/services/task-registry.js.map +1 -1
  208. package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
  209. package/dist/services/telemetry/device-fingerprint.js +1 -1
  210. package/dist/services/types-shim.d.ts +16 -0
  211. package/dist/services/types-shim.js +2 -0
  212. package/dist/services/types-shim.js.map +1 -0
  213. package/dist/services/webdav/server.d.ts +24 -0
  214. package/dist/services/webdav/server.js +420 -0
  215. package/dist/services/webdav/server.js.map +1 -0
  216. package/dist/services/webdav/xml-builder.d.ts +73 -0
  217. package/dist/services/webdav/xml-builder.js +156 -0
  218. package/dist/services/webdav/xml-builder.js.map +1 -0
  219. package/dist/services/workspace-builder.d.ts +29 -0
  220. package/dist/services/workspace-builder.js +188 -0
  221. package/dist/services/workspace-builder.js.map +1 -0
  222. package/dist/types.d.ts +231 -1
  223. package/dist/utils/instance-lock.d.ts +22 -0
  224. package/dist/utils/instance-lock.js +48 -0
  225. package/dist/utils/instance-lock.js.map +1 -0
  226. package/dist/utils/path-locks.d.ts +30 -0
  227. package/dist/utils/path-locks.js +63 -0
  228. package/dist/utils/path-locks.js.map +1 -0
  229. package/dist/utils/path-safety.d.ts +41 -0
  230. package/dist/utils/path-safety.js +119 -0
  231. package/dist/utils/path-safety.js.map +1 -0
  232. package/dist/utils/safe-json.js +55 -22
  233. package/dist/utils/safe-json.js.map +1 -1
  234. package/dist/utils/safe-write.d.ts +24 -0
  235. package/dist/utils/safe-write.js +82 -0
  236. package/dist/utils/safe-write.js.map +1 -0
  237. package/install/jishu-install.sh +323 -27
  238. package/install/jishu-uninstall.sh +353 -20
  239. package/package.json +18 -1
  240. package/public/assets/Dashboard-BdWPtroF.js +1 -0
  241. package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-B_2HlVBQ.js} +1 -1
  242. package/public/assets/HermesConfigForm-DVlhg3WV.js +4 -0
  243. package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-D7glTExX.js} +1 -1
  244. package/public/assets/InstanceDetail-CxSy2cpe.js +92 -0
  245. package/public/assets/{Login-BWsZH2mu.js → Login-Cfr5c2sv.js} +1 -1
  246. package/public/assets/NewInstance-BIYDmJis.js +1 -0
  247. package/public/assets/ProviderRecommendations-BuRnvRcI.js +1 -0
  248. package/public/assets/Settings-Cc-tYBil.js +1 -0
  249. package/public/assets/Setup-lGZEk5jq.js +1 -0
  250. package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-CoGqzxeV.js} +2 -2
  251. package/public/assets/index-87IJXG-w.css +1 -0
  252. package/public/assets/index-BZc5zH7u.js +19 -0
  253. package/public/assets/providers-DtNXh9JD.js +1 -0
  254. package/public/assets/registry-BWnkJgZ1.js +2 -0
  255. package/public/assets/{usePolling-Do5Erqm_.js → usePolling-CwwT9KrC.js} +1 -1
  256. package/public/assets/{vendor-i18n-ucpM0OR0.js → vendor-i18n-y9V7Sfuu.js} +1 -1
  257. package/public/assets/{vendor-react-Bk1hRGiY.js → vendor-react-BWrEVJVb.js} +6 -6
  258. package/public/index.html +4 -4
  259. package/scripts/check-app-spec.mjs +457 -0
  260. package/scripts/check-i18n.mjs +154 -0
  261. package/scripts/check-new-file-tests.mjs +230 -0
  262. package/scripts/check-quarantine-expiry.mjs +105 -0
  263. package/scripts/perf/README.md +49 -0
  264. package/scripts/perf/auth.js +99 -0
  265. package/scripts/perf/config.js +63 -0
  266. package/scripts/perf/instances.js +143 -0
  267. package/scripts/perf/proxy.js +96 -0
  268. package/scripts/run.sh +4 -4
  269. package/scripts/smoke/files-w1.sh +142 -0
  270. package/scripts/smoke-backend.mjs +122 -0
  271. package/scripts/smoke-post-publish.mjs +346 -0
  272. package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
  273. package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
  274. package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
  275. package/public/assets/NewInstance-BCIrAd86.js +0 -1
  276. package/public/assets/Settings-xkDcduFz.js +0 -1
  277. package/public/assets/Setup-Cfuwj4gV.js +0 -1
  278. package/public/assets/index-CPhVFEsx.css +0 -1
  279. package/public/assets/index-DQsM6Joa.js +0 -19
  280. package/public/assets/providers-V-vwrExZ.js +0 -1
  281. package/public/assets/registry-B4UFJdpA.js +0 -2
@@ -0,0 +1,471 @@
1
+ id: weknora-container
2
+ name: WeKnora
3
+ version: "1.0.0"
4
+ jishushell:
5
+ min_version: "0.5.15"
6
+ description: "Tencent 开源 RAG 知识库:文档解析 + 向量检索 + 对话式问答(5 容器单 Nomad TaskGroup)"
7
+ singleInstance: true
8
+
9
+ # 拓扑(每个 task 独立 docker netns,跨 task 经 host.docker.internal:<host-port>):
10
+ # 注:原始设计想用 Nomad group bridge mode 让 task 共享 netns 走 127.0.0.1,
11
+ # 但需要 /opt/cni/bin/ 下的 containernetworking 插件(Pi 默认没装)。退而用
12
+ # host 端口发布 + host.docker.internal 路由(panel `extra_hosts:host-gateway`
13
+ # 已 mapping 到 172.17.0.1 = docker0 bridge gateway)。
14
+ # 关键:5 个发布端口都设 `host_network: docker_bridge` 让 panel/Nomad 把
15
+ # docker-proxy 绑到 docker0 IP(172.17.0.1),peer 容器走 host.docker.internal
16
+ # 即 172.17.0.1 时打得通。要求 nomad.hcl 声明 `host_network "docker_bridge"
17
+ # { cidr = "172.17.0.0/16" }`(panel doctor 会自动写入)。
18
+ #
19
+ # weknora-ui (nginx :80) ─proxy_pass─► weknora-app :8080
20
+ # │ │
21
+ # │ ├─► paradedb :5432 (PG17 + pgvector + FTS5;同时承担 vector store)
22
+ # │ ├─► redis :6379 (任务队列 / 流式响应缓存)
23
+ # │ └─► docreader:50051 (gRPC:PDF / Word / Markdown 解析)
24
+ # │
25
+ # host :18092 ◄── published (visibility: external)
26
+ #
27
+ # 关键裁剪(相对官方 docker-compose):
28
+ # - RETRIEVE_DRIVER=postgres:复用 paradedb 的 pgvector,**不装 Qdrant / Milvus / Weaviate**(省 1 task + ~70MB 下载)
29
+ # - WEKNORA_SANDBOX_MODE=disabled:不装 weknora-sandbox(省 ~80MB;阉割 agent 工具沙箱执行,RAG 主流程不受影响)
30
+ # - 不装 Neo4j / MinIO / Langfuse / Jaeger / SearXNG / Doris / ClickHouse / Dex(都是 profile 才启的)
31
+ #
32
+ # 镜像总下载:~2.8 GB (arm64) / ~3.0 GB (amd64);解压磁盘 ~6.5 GB
33
+ # 稳态内存预算:~1.5–2.0 GB(4 GB Pi 紧张,建议 8 GB Pi 5 / x86 节点)
34
+
35
+ tasks:
36
+ # ────────────────────────────────────────────────────────────────
37
+ # 1) PostgreSQL(ParadeDB:PG17 + pgvector + ParadeDB BM25 全文)
38
+ # role: sidecar → prestart 起来并持续运行;service tasks 在所有
39
+ # sidecar Running 后再启动(但 Running ≠ Ready,weknora-app 自己
40
+ # 要靠连接重试兜住 DB init 的 30s 窗口)。
41
+ # ────────────────────────────────────────────────────────────────
42
+ - name: paradedb
43
+ role: sidecar
44
+ runtime: container
45
+ image: "paradedb/paradedb:v0.22.2-pg17"
46
+ user: "host"
47
+ env:
48
+ POSTGRES_USER: "weknora"
49
+ # ⚠️ 开发默认值。生产请改:
50
+ # docker exec -it <container> psql -U weknora -c "ALTER USER weknora WITH PASSWORD '<new>'"
51
+ # 并同步更新 weknora-app 的 DB_PASSWORD。
52
+ POSTGRES_PASSWORD: "weknora-dev-pg-pw"
53
+ POSTGRES_DB: "WeKnora"
54
+ PGDATA: "/var/lib/postgresql/data/pgdata"
55
+ resources:
56
+ cpu: "500m"
57
+ memory: "384Mi"
58
+ ports:
59
+ - name: pg
60
+ port: 18093
61
+ container_port: 5432
62
+ visibility: external
63
+ host_network: docker_bridge
64
+ volumes:
65
+ - source: "~/.jishushell/apps/${app_id}/postgres"
66
+ target: "/var/lib/postgresql/data"
67
+
68
+ # ────────────────────────────────────────────────────────────────
69
+ # 2) Redis(任务队列 + Stream Manager 状态)
70
+ # ────────────────────────────────────────────────────────────────
71
+ - name: redis
72
+ role: sidecar
73
+ runtime: container
74
+ image: "redis:7.0-alpine"
75
+ user: "host"
76
+ args:
77
+ - "redis-server"
78
+ - "--appendonly"
79
+ - "yes"
80
+ # 无密码:仅 group 内 127.0.0.1 可达,未发布到宿主。
81
+ # 如需密码,加 --requirepass <pw> 并同步设 weknora-app 的 REDIS_PASSWORD。
82
+ resources:
83
+ cpu: "100m"
84
+ memory: "96Mi"
85
+ ports:
86
+ - name: redis
87
+ port: 18094
88
+ container_port: 6379
89
+ visibility: external
90
+ host_network: docker_bridge
91
+ volumes:
92
+ - source: "~/.jishushell/apps/${app_id}/redis"
93
+ target: "/data"
94
+
95
+ # ────────────────────────────────────────────────────────────────
96
+ # 3) docreader(最大头:1.7 GB;Python + markitdown + PDF 渲染)
97
+ # weknora-app `depends_on: docreader (service_healthy)` 是 core
98
+ # 依赖,**不能砍**。
99
+ # ────────────────────────────────────────────────────────────────
100
+ - name: docreader
101
+ role: sidecar
102
+ runtime: container
103
+ image: "wechatopenai/weknora-docreader:latest"
104
+ # Image entrypoint uses `uv` package manager which writes to /.cache/uv;
105
+ # forcing host uid (1000:1000) gets Permission denied on the root-owned
106
+ # filesystem and the container exits 2. Run as root — image is read-only
107
+ # in the layers that matter; runtime writes go to /tmp/docreader (bind
108
+ # mount, panel-user owned).
109
+ user: "0:0"
110
+ env:
111
+ DOCREADER_IMAGE_OUTPUT_DIR: "/tmp/docreader"
112
+ MAX_FILE_SIZE_MB: "50"
113
+ DOCREADER_MARKITDOWN_MAX_WORKERS: "1"
114
+ DOCREADER_PDF_RENDER_MAX_WORKERS: "1"
115
+ DOCREADER_PDF_RENDER_DPI: "200"
116
+ DOCREADER_PDF_JPEG_QUALITY: "90"
117
+ resources:
118
+ cpu: "800m"
119
+ memory: "768Mi"
120
+ ports:
121
+ - name: grpc
122
+ port: 18095
123
+ container_port: 50051
124
+ visibility: external
125
+ host_network: docker_bridge
126
+ volumes:
127
+ # tmp 目录与 weknora-app 共享,docreader 写 / app 读(图片提取场景)。
128
+ - source: "~/.jishushell/apps/${app_id}/docreader-tmp"
129
+ target: "/tmp/docreader"
130
+
131
+ # ────────────────────────────────────────────────────────────────
132
+ # 4) WeKnora 后端 app(Go)
133
+ # ────────────────────────────────────────────────────────────────
134
+ - name: app
135
+ role: service
136
+ runtime: container
137
+ image: "wechatopenai/weknora-app:latest"
138
+ # Image entrypoint chowns /app/skills/preloaded; with host uid the
139
+ # chown fails with "Operation not permitted" and the container crashes.
140
+ # Bind-mounted /data/files is panel-user owned (1000) — Go binary opens
141
+ # it as root without issue.
142
+ user: "0:0"
143
+ # panel default is cap_drop:ALL; weknora-app entrypoint does
144
+ # `chown -R appuser /app/skills/preloaded` at startup → needs CHOWN.
145
+ # It then `su appuser` to drop to non-root → needs SETUID/SETGID/SETPCAP.
146
+ cap_add: ["CHOWN", "FOWNER", "DAC_OVERRIDE", "SETUID", "SETGID", "SETPCAP"]
147
+ env:
148
+ # ── 基础运行时 ──
149
+ GIN_MODE: "release"
150
+ LOG_LEVEL: "info"
151
+ TZ: "Asia/Shanghai"
152
+ WEKNORA_LANGUAGE: "zh-CN"
153
+ DISABLE_REGISTRATION: "false"
154
+
155
+ # ── 数据库(host_network: docker_bridge → 绑 docker0 IP,
156
+ # host.docker.internal=172.17.0.1 容器内可达) ──
157
+ DB_DRIVER: "postgres"
158
+ DB_HOST: "host.docker.internal"
159
+ DB_PORT: "18093"
160
+ DB_USER: "weknora"
161
+ DB_PASSWORD: "weknora-dev-pg-pw"
162
+ DB_NAME: "WeKnora"
163
+
164
+ # ── 向量检索:走 paradedb 的 pgvector(砍 Qdrant / Milvus / Weaviate) ──
165
+ RETRIEVE_DRIVER: "postgres"
166
+
167
+ # ── Redis ──
168
+ REDIS_ADDR: "host.docker.internal:18094"
169
+ REDIS_DB: "0"
170
+ REDIS_PREFIX: "stream:"
171
+ STREAM_MANAGER_TYPE: "redis"
172
+
173
+ # ── docreader gRPC ──
174
+ DOCREADER_ADDR: "host.docker.internal:18095"
175
+ DOCREADER_TRANSPORT: "grpc"
176
+
177
+ # ── 文件存储:local 模式,写到 bind-mounted /data/files ──
178
+ STORAGE_TYPE: "local"
179
+ LOCAL_STORAGE_BASE_DIR: "/data/files"
180
+ MAX_FILE_SIZE_MB: "50"
181
+
182
+ # ── Sandbox:关。Agent 工具调用执行能力不可用,RAG 主流程不受影响。 ──
183
+ # 如需开启:(a) 把 weknora-sandbox 也加进 tasks[];(b) 改为
184
+ # WEKNORA_SANDBOX_MODE=docker;(c) 注意 sandbox 需要 docker.sock 挂载,
185
+ # Nomad 调度的容器内不一定有权限——可能需要额外设计。
186
+ WEKNORA_SANDBOX_MODE: "disabled"
187
+
188
+ # ── 安全密钥 ⚠️ 开发默认值,生产前必改 ──
189
+ # 推荐:openssl rand -hex 32 重新生成;同步改前端 NEXTAUTH_SECRET 之类。
190
+ JWT_SECRET: "weknora-dev-jwt-secret-CHANGE-ME-IN-PROD"
191
+ TENANT_AES_KEY: "weknora-dev-tenant-key-32bytes!!"
192
+ SYSTEM_AES_KEY: "weknora-dev-system-key-32bytes!!"
193
+
194
+ # ── LLM provider:留空,让用户在 WeKnora UI 里手动配。 ──
195
+ # 想用 panel 内嵌 LLM proxy(推荐)的话,UI 里填:
196
+ # base_url: http://host.docker.internal:8090/proxy/v1
197
+ # api_key: <从 panel /instances/:id 拿到的 sk-js-*>
198
+ # 这样审计 / 限流 / 上游切换都收敛在 panel 一层。
199
+ OLLAMA_BASE_URL: ""
200
+
201
+ # ── Tracing / Observability:全关 ──
202
+ LANGFUSE_ENABLED: "false"
203
+ OTEL_TRACES_EXPORTER: "none"
204
+ OTEL_METRICS_EXPORTER: "none"
205
+ OTEL_LOGS_EXPORTER: "none"
206
+ ENABLE_GRAPH_RAG: "false"
207
+ NEO4J_ENABLE: "false"
208
+
209
+ # ── 并发 ──
210
+ CONCURRENCY_POOL_SIZE: "5"
211
+
212
+ # ── SSRF 白名单:默认走 panel 反代,无需对外。 ──
213
+ SSRF_WHITELIST: ""
214
+ SSRF_WHITELIST_EXTRA: ""
215
+ resources:
216
+ cpu: "600m"
217
+ memory: "640Mi"
218
+ ports:
219
+ - name: http
220
+ # Published on docker0 so the ui task can reach backend via
221
+ # host.docker.internal:18096 (no shared netns on Pi w/o CNI).
222
+ port: 18096
223
+ container_port: 8080
224
+ visibility: external
225
+ host_network: docker_bridge
226
+ volumes:
227
+ # 知识库附件落盘。与 ~/.jishushell/files/ 故意分离 ——
228
+ # WeKnora 自己管文件元数据 / RAG 索引,避免与 panel W3 FTS5 打架。
229
+ - source: "~/.jishushell/apps/${app_id}/files"
230
+ target: "/data/files"
231
+
232
+ # ────────────────────────────────────────────────────────────────
233
+ # 5) WeKnora 前端 UI(nginx + Vue dist)
234
+ # ⚠️ 镜像不支持 BASE_URL 子路径 → 不走 panel /apps/weknora/* 反代,
235
+ # 用端口直连 18092。子路径要走需 fork 镜像(vite.config base + nginx
236
+ # location prefix),暂列为后续工作。
237
+ # ────────────────────────────────────────────────────────────────
238
+ - name: ui
239
+ role: service
240
+ runtime: container
241
+ image: "wechatopenai/weknora-ui:latest"
242
+ # nginx entrypoint needs to mkdir /var/cache/nginx/client_temp and
243
+ # rewrite /etc/nginx/conf.d/default.conf via envsubst — both require root.
244
+ # Host uid breaks both steps and the container crashloops.
245
+ user: "0:0"
246
+ # nginx master spawns workers as uid 101 (nginx user) and chowns the
247
+ # cache dir to it — needs CHOWN to do so even when master is root.
248
+ cap_add: ["CHOWN", "SETGID", "SETUID"]
249
+ env:
250
+ MAX_FILE_SIZE_MB: "50"
251
+ APP_HOST: "host.docker.internal"
252
+ APP_PORT: "18096"
253
+ APP_SCHEME: "http"
254
+ resources:
255
+ cpu: "100m"
256
+ memory: "48Mi"
257
+ ports:
258
+ - name: http
259
+ # 18092 在 panel 18xxx 范围内(filebrowser 18088 / openclaw-gw 18790)。
260
+ # 避开 OpenWebUI 3000、SearXNG 8080、panel 8090。
261
+ port: 18092
262
+ container_port: 80
263
+ visibility: external
264
+ volumes:
265
+ # 覆盖镜像内的 nginx 模板:上游模板带 `add_header X-Frame-Options
266
+ # SAMEORIGIN`,panel 实例详情页 iframe (origin=panel:8090) 嵌
267
+ # WeKnora UI (origin=panel:18092) 会被浏览器拒绝。我们这份完全
268
+ # 照搬上游 location 块,只去掉 X-Frame-Options/CSP 限制,保留
269
+ # ${APP_HOST}/${APP_PORT}/${APP_SCHEME}/${MAX_FILE_SIZE} 由镜像
270
+ # docker-entrypoint.sh envsubst。
271
+ - source: "~/.jishushell/apps/${app_id}/nginx/default.conf.template"
272
+ target: "/etc/nginx/templates/default.conf.template"
273
+ readonly: true
274
+ health:
275
+ http:
276
+ path: /
277
+ port: 80
278
+ interval: "15s"
279
+ timeout: "5s"
280
+ retries: 6
281
+ start_period: "60s" # 等 paradedb 初始化 + weknora-app boot + nginx 上线
282
+
283
+ provides:
284
+ # ⚠️ Capability 命名特意避开 `web-<slug>` 和 `<slug>-ui` 这两种 panel 顶层
285
+ # `/apps/<slug>/*` 反代会命中的模式(详见 src/server.ts:69 resolveAppSlugUpstream)。
286
+ # 原因:panel 反代**不去掉** `/apps/<slug>` 前缀(server.ts:382 注释),原样
287
+ # 转发给上游;而 WeKnora 前端镜像的 nginx 只 listen `/`、vite build 也没设
288
+ # `base`——任何 `/apps/weknora/*` 请求到了 ui 容器都会 404。
289
+ #
290
+ # 因此用 `knowledge-weknora`(业务命名、非通配前缀),让它**只**进 capability
291
+ # registry 供未来 agent `requires:` 注入使用,**不**触发 panel 顶层反代。
292
+ # 用户访问 UI 走端口直连:http://<host>:18092/。
293
+ - capability: "knowledge-weknora"
294
+ task: "ui"
295
+ port: 18092
296
+ path: "/"
297
+ protocol: "http"
298
+ visibility: "external"
299
+ # 强制 iframe 走 panel 反代而不是端口直连。原因:
300
+ # 1. 用户从 panel 8090 同源加载详情页,corp 防火墙 / VPN / 混合内容
301
+ # 策略可能让 8090 通但 18092 不通(典型:"refused to connect")。
302
+ # 2. nginx SAMEORIGIN/CSP 即便去掉了,浏览器 Private Network Access
303
+ # 仍可能拒绝跨网段 iframe;走 panel 反代后 iframe src 与 panel
304
+ # 同源,绕开所有边界检查。
305
+ # 3. WeKnora 前端 vite build 没设 `base`,绝对路径 `/api/...` 直接
306
+ # 打到 panel API → 但反代会把 base 改成 /api/instances/.../provides/
307
+ # /knowledge-weknora/,后续相对 fetch 正确(panel 反代有 base 重写)。
308
+ # Direct mode: iframe loads http://<host>:18092/ instead of the panel
309
+ # capability proxy. The proxy path approach kept tripping over WeKnora's
310
+ # axios 401 interceptor, which hard-codes `window.location.href = '/login'`
311
+ # and bounces the iframe to the panel root login page. Rewriting that
312
+ # literal at the proxy works for the assignment but the bundle has
313
+ # additional indirect navigations (vue-router fallback when pushState is
314
+ # blocked, internal pinia store actions) that aren't reachable from a
315
+ # text rewrite — same-origin iframe vs. direct iframe tradeoff favors
316
+ # direct here because the dev/internal browser already reaches 18092
317
+ # successfully (popout link confirmed working).
318
+ embedded: "direct"
319
+ description: "WeKnora 知识库 Web UI"
320
+
321
+ lifecycle:
322
+ install:
323
+ # 镜像按尺寸升序下载:小的先稳,大的(docreader 1.7GB)放最后,
324
+ # 万一失败下次重试时小的全部 skip,从大的断点续。
325
+ - downloadImage: "redis:7.0-alpine"
326
+ - downloadImage: "wechatopenai/weknora-ui:latest"
327
+ - downloadImage: "wechatopenai/weknora-app:latest"
328
+ - downloadImage: "paradedb/paradedb:v0.22.2-pg17"
329
+ # docreader 单独给 30 分钟总超时 + 30 分钟 idle 超时(1.7GB 内含 400MB+ 的
330
+ # 大 layer;Pi 带宽 ~2 MB/s 下单 layer 下载耗时可超 panel 默认 180s idle
331
+ # → 误判 stalled。设 30 分钟 idle 给慢网充足时间)。
332
+ - downloadImage: "wechatopenai/weknora-docreader:latest"
333
+ timeout_ms: 1800000
334
+ idle_timeout_ms: 1800000
335
+
336
+ - mkdir: "~/.jishushell/apps/${app_id}/postgres"
337
+ - mkdir: "~/.jishushell/apps/${app_id}/redis"
338
+ - mkdir: "~/.jishushell/apps/${app_id}/docreader-tmp"
339
+ - mkdir: "~/.jishushell/apps/${app_id}/files"
340
+ - mkdir: "~/.jishushell/apps/${app_id}/nginx"
341
+ # 写自定义 nginx 模板(去掉 X-Frame-Options 让 panel 实例详情页能
342
+ # iframe 嵌入)。单引号 heredoc 关掉 shell 展开,保留 ${APP_HOST}/
343
+ # ${APP_PORT}/${APP_SCHEME}/${MAX_FILE_SIZE} 由镜像 envsubst 替换。
344
+ - run: |
345
+ set -e
346
+ TPL="$HOME/.jishushell/apps/${app_id}/nginx/default.conf.template"
347
+ cat > "$TPL" <<'NGX'
348
+ server {
349
+ listen 80;
350
+ server_name localhost;
351
+
352
+ client_max_body_size ${MAX_FILE_SIZE};
353
+
354
+ gzip on;
355
+ gzip_min_length 1024;
356
+ gzip_comp_level 6;
357
+ gzip_types text/plain text/css application/json application/javascript
358
+ application/xml text/xml application/x-javascript;
359
+
360
+ # X-Frame-Options 故意不发:panel 实例详情页 (origin=panel:8090)
361
+ # 要 iframe 嵌入本服务 (origin=panel:18092),原版 SAMEORIGIN
362
+ # 会被浏览器拒。其余安全头照旧。
363
+ add_header X-Content-Type-Options nosniff always;
364
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
365
+
366
+ location /assets/ {
367
+ root /usr/share/nginx/html;
368
+ expires max;
369
+ add_header Cache-Control "public, max-age=31536000, immutable";
370
+ }
371
+
372
+ location = /files {
373
+ proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/files;
374
+ proxy_http_version 1.1;
375
+ proxy_set_header Host $host;
376
+ proxy_set_header X-Real-IP $remote_addr;
377
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
378
+ }
379
+
380
+ location /api/ {
381
+ proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/api/;
382
+ proxy_http_version 1.1;
383
+ proxy_set_header Host $host;
384
+ proxy_set_header X-Real-IP $remote_addr;
385
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
386
+ proxy_set_header Connection "";
387
+ proxy_buffering off;
388
+ proxy_cache off;
389
+ proxy_read_timeout 3600s;
390
+ proxy_send_timeout 3600s;
391
+ proxy_connect_timeout 30s;
392
+ proxy_next_upstream error timeout http_502 http_503 http_504;
393
+ proxy_next_upstream_tries 3;
394
+ }
395
+
396
+ location / {
397
+ root /usr/share/nginx/html;
398
+ try_files $uri $uri/ /index.html;
399
+ add_header Cache-Control "no-cache, must-revalidate";
400
+ }
401
+ }
402
+ NGX
403
+ chmod 644 "$TPL"
404
+ echo "[install] wrote nginx template (no X-Frame-Options) → $TPL"
405
+
406
+ pre_start:
407
+ # 幂等:start/restart 都过这条路径,目录被误删可自愈。
408
+ - mkdir: "~/.jishushell/apps/${app_id}/postgres"
409
+ - mkdir: "~/.jishushell/apps/${app_id}/redis"
410
+ - mkdir: "~/.jishushell/apps/${app_id}/docreader-tmp"
411
+ - mkdir: "~/.jishushell/apps/${app_id}/files"
412
+ - mkdir: "~/.jishushell/apps/${app_id}/nginx"
413
+ - run: |
414
+ set -e
415
+ TPL="$HOME/.jishushell/apps/${app_id}/nginx/default.conf.template"
416
+ if [ ! -f "$TPL" ]; then
417
+ cat > "$TPL" <<'NGX'
418
+ server {
419
+ listen 80;
420
+ server_name localhost;
421
+ client_max_body_size ${MAX_FILE_SIZE};
422
+ gzip on;
423
+ gzip_min_length 1024;
424
+ gzip_comp_level 6;
425
+ gzip_types text/plain text/css application/json application/javascript
426
+ application/xml text/xml application/x-javascript;
427
+ add_header X-Content-Type-Options nosniff always;
428
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
429
+ location /assets/ {
430
+ root /usr/share/nginx/html;
431
+ expires max;
432
+ add_header Cache-Control "public, max-age=31536000, immutable";
433
+ }
434
+ location = /files {
435
+ proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/files;
436
+ proxy_http_version 1.1;
437
+ proxy_set_header Host $host;
438
+ proxy_set_header X-Real-IP $remote_addr;
439
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
440
+ }
441
+ location /api/ {
442
+ proxy_pass ${APP_SCHEME}://${APP_HOST}:${APP_PORT}/api/;
443
+ proxy_http_version 1.1;
444
+ proxy_set_header Host $host;
445
+ proxy_set_header X-Real-IP $remote_addr;
446
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
447
+ proxy_set_header Connection "";
448
+ proxy_buffering off;
449
+ proxy_cache off;
450
+ proxy_read_timeout 3600s;
451
+ proxy_send_timeout 3600s;
452
+ proxy_connect_timeout 30s;
453
+ proxy_next_upstream error timeout http_502 http_503 http_504;
454
+ proxy_next_upstream_tries 3;
455
+ }
456
+ location / {
457
+ root /usr/share/nginx/html;
458
+ try_files $uri $uri/ /index.html;
459
+ add_header Cache-Control "no-cache, must-revalidate";
460
+ }
461
+ }
462
+ NGX
463
+ chmod 644 "$TPL"
464
+ fi
465
+
466
+ uninstall:
467
+ - deleteImage: "wechatopenai/weknora-docreader:latest"
468
+ - deleteImage: "paradedb/paradedb:v0.22.2-pg17"
469
+ - deleteImage: "wechatopenai/weknora-app:latest"
470
+ - deleteImage: "wechatopenai/weknora-ui:latest"
471
+ - deleteImage: "redis:7.0-alpine"
package/dist/cli/app.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync } from "fs";
2
+ import { basename, extname } from "path";
2
3
  import { stringify } from "yaml";
3
4
  import { parseFlag } from "./helpers.js";
4
5
  import { loadManagedListEntries, printManagedList } from "./managed-list.js";
@@ -328,6 +329,30 @@ function parseRemoteYamlUrl(value) {
328
329
  throw e;
329
330
  }
330
331
  }
332
+ const BUILTIN_INSTALL_ALIASES = {
333
+ ollama: "ollama-binary.yaml",
334
+ };
335
+ async function loadBuiltinInstallYaml(source) {
336
+ const normalizedSource = source.trim();
337
+ if (!normalizedSource)
338
+ return null;
339
+ const { listBuiltinAppSpecs } = await import("../services/app/app-manager.js");
340
+ const templates = listBuiltinAppSpecs();
341
+ const normalizedWithoutExt = normalizedSource.replace(/\.ya?ml$/i, "");
342
+ const aliasTarget = BUILTIN_INSTALL_ALIASES[normalizedWithoutExt] ?? BUILTIN_INSTALL_ALIASES[normalizedSource];
343
+ const template = templates.find((entry) => {
344
+ if (aliasTarget && entry.fileName === aliasTarget)
345
+ return true;
346
+ if (entry.fileName === normalizedSource)
347
+ return true;
348
+ if (basename(entry.fileName, extname(entry.fileName)) === normalizedWithoutExt)
349
+ return true;
350
+ if (entry.id === normalizedWithoutExt || entry.id === normalizedSource)
351
+ return true;
352
+ return false;
353
+ });
354
+ return template?.yaml ?? null;
355
+ }
331
356
  async function loadInstallYaml(source) {
332
357
  const remoteUrl = parseRemoteYamlUrl(source);
333
358
  if (remoteUrl) {
@@ -347,6 +372,10 @@ async function loadInstallYaml(source) {
347
372
  }
348
373
  return body;
349
374
  }
375
+ const builtinYaml = await loadBuiltinInstallYaml(source);
376
+ if (builtinYaml) {
377
+ return builtinYaml;
378
+ }
350
379
  if (!existsSync(source)) {
351
380
  throw new Error(`File not found: ${source}`);
352
381
  }
@@ -377,12 +406,41 @@ async function cmdShow(id) {
377
406
  }
378
407
  console.log(JSON.stringify(instance, null, 2));
379
408
  }
380
- async function cmdInstall(yamlPath, requestedAppId) {
381
- const yamlText = await loadInstallYaml(yamlPath);
409
+ async function cmdInstall(args) {
410
+ const installSource = args[0];
411
+ let requestedAppId;
412
+ for (let index = 1; index < args.length; index += 1) {
413
+ const value = args[index];
414
+ if (value === "--sudo-password") {
415
+ index += 1;
416
+ continue;
417
+ }
418
+ if (value.startsWith("--"))
419
+ continue;
420
+ requestedAppId = value;
421
+ break;
422
+ }
423
+ const yamlText = await loadInstallYaml(installSource);
382
424
  const { installApp } = await import("../services/app/app-manager.js");
383
- const result = await installApp(yamlText, requestedAppId);
384
- const installModeLabel = result.manifest.install_mode === "instance-dir" ? "instance" : "app";
385
- log(c.green(` ✓ Installed: ${result.manifest.id} (${result.spec.name || ""} v${result.spec.version || "?"}, ${installModeLabel})`));
425
+ const sudoState = getCliSudoState(args);
426
+ while (true) {
427
+ try {
428
+ const installOptions = sudoState.password ? { exec: { sudoPassword: sudoState.password } } : undefined;
429
+ const result = installOptions
430
+ ? await installApp(yamlText, requestedAppId, installOptions)
431
+ : await installApp(yamlText, requestedAppId);
432
+ const installModeLabel = result.manifest.install_mode === "instance-dir" ? "instance" : "app";
433
+ log(c.green(` ✓ Installed: ${result.manifest.id} (${result.spec.name || ""} v${result.spec.version || "?"}, ${installModeLabel})`));
434
+ return;
435
+ }
436
+ catch (err) {
437
+ const message = err?.message || "Install failed";
438
+ if (isSudoPasswordError(message) && await promptForCliSudoPassword(sudoState)) {
439
+ continue;
440
+ }
441
+ throw err;
442
+ }
443
+ }
386
444
  }
387
445
  async function cmdProvides(args) {
388
446
  const { listProvidedCapabilities } = await import("../services/app/app-manager.js");
@@ -504,16 +562,26 @@ async function cmdUninstallAll(args = []) {
504
562
  }
505
563
  }
506
564
  async function cmdCreateInstance(appId, instanceId, name) {
507
- const { getApp, installApp, resolveRequires, updateInstance } = await import("../services/app/app-manager.js");
565
+ const { getApp, installApp, updateInstance } = await import("../services/app/app-manager.js");
566
+ const { resolveConnections, resolvedToLegacyEnv } = await import("../services/connection-resolver.js");
508
567
  const appData = getApp(appId);
509
568
  if (!appData) {
510
569
  log(c.red(` ✗ App "${appId}" not found.`));
511
570
  process.exitCode = 1;
512
571
  return;
513
572
  }
573
+ // PR 3 sub-step 3e: switch CLI to resolveConnections (preCreate). Pending
574
+ // requires are printed so the user can see what still needs binding.
514
575
  let resolvedEnv = {};
515
576
  try {
516
- resolvedEnv = resolveRequires(appData.spec);
577
+ const { resolved, pending } = resolveConnections(appData.spec, { connections: {} }, "preCreate");
578
+ resolvedEnv = resolvedToLegacyEnv(resolved);
579
+ if (pending.length > 0) {
580
+ log(c.yellow(` ⚠ ${pending.length} pending connection(s):`));
581
+ for (const p of pending) {
582
+ log(c.yellow(` - ${p.slot} (${p.capability}): ${p.reason}`));
583
+ }
584
+ }
517
585
  }
518
586
  catch (e) {
519
587
  log(c.red(` ✗ ${e.message}`));
@@ -705,7 +773,7 @@ export async function run(rest) {
705
773
  await cmdShow(rest[1]);
706
774
  }
707
775
  else if (appCmd === "install" && rest[1]) {
708
- await cmdInstall(rest[1], rest[2]);
776
+ await cmdInstall(rest.slice(1));
709
777
  }
710
778
  else if (appCmd === "uninstall" && rest[1] === "--all") {
711
779
  await cmdUninstallAll(rest.slice(2));
@@ -781,7 +849,8 @@ Commands:
781
849
  list [--json] 列出统一受管列表(apps 下已安装应用 + instances 下遗留实例)
782
850
  provides [--json] 列出已安装 App 声明/注册的能力
783
851
  show <id> 查看 App 或实例详情(JSON)
784
- install <yaml-file|https-url> [app-id] 从本地或 HTTPS YAML 安装 App,可选指定安装 id
852
+ install <yaml-file|https-url|builtin-id> [app-id] [--sudo-password <password>]
853
+ 从本地、HTTPS 或内置模板安装 App,可选指定安装 id
785
854
  uninstall <app-id> [--sudo-password <password>] 卸载单个 App 或实例(停止 + 删除目录)
786
855
  uninstall --all [--sudo-password <password>] 卸载全部 App 与实例
787
856
  create-instance <app-id> <inst-id> [name] 从 App 创建实例(Agent 通道由 spec.agentType 决定)
@@ -798,6 +867,7 @@ Examples:
798
867
  jishushell app install ./apps/ollama-with-hollama-binary.yaml
799
868
  jishushell app install ./apps/ollama-with-hollama-binary.yaml ollama-local
800
869
  jishushell app install https://example.com/apps/ollama-with-hollama-binary.yaml
870
+ jishushell app install ollama
801
871
  jishushell app list
802
872
  jishushell app provides
803
873
  jishushell app copy ollama-1