jishushell 0.4.24 → 0.4.30

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 (167) hide show
  1. package/INSTALL-NOTICE +11 -0
  2. package/apps/browserless-chromium-container.yaml +78 -0
  3. package/apps/hermes-container.yaml +36 -2
  4. package/apps/ollama-binary.yaml +91 -90
  5. package/apps/ollama-cpu-container.yaml +8 -1
  6. package/apps/ollama-with-hollama-binary.yaml +91 -90
  7. package/apps/openclaw-binary.yaml +30 -1
  8. package/apps/openclaw-container.yaml +37 -2
  9. package/apps/openclaw-with-ollama-container.yaml +11 -2
  10. package/apps/openclaw-with-searxng-container.yaml +22 -2
  11. package/apps/openwebui-container.yaml +45 -1
  12. package/apps/playwright-container.yaml +7 -1
  13. package/apps/searxng-container.yaml +54 -4
  14. package/dist/cli/app.js +79 -9
  15. package/dist/cli/app.js.map +1 -1
  16. package/dist/cli/doctor.d.ts +12 -12
  17. package/dist/cli/doctor.js +242 -55
  18. package/dist/cli/doctor.js.map +1 -1
  19. package/dist/cli/llm.d.ts +4 -3
  20. package/dist/cli/llm.js +4 -3
  21. package/dist/cli/llm.js.map +1 -1
  22. package/dist/cli/panel.d.ts +6 -5
  23. package/dist/cli/panel.js +10 -9
  24. package/dist/cli/panel.js.map +1 -1
  25. package/dist/control.d.ts +7 -6
  26. package/dist/control.js +7 -6
  27. package/dist/control.js.map +1 -1
  28. package/dist/routes/agent-apps.d.ts +1 -1
  29. package/dist/routes/agent-apps.js +1 -1
  30. package/dist/routes/apps.js +44 -11
  31. package/dist/routes/apps.js.map +1 -1
  32. package/dist/routes/auth.js +3 -0
  33. package/dist/routes/auth.js.map +1 -1
  34. package/dist/routes/instances.js +787 -16
  35. package/dist/routes/instances.js.map +1 -1
  36. package/dist/routes/llm.js +24 -35
  37. package/dist/routes/llm.js.map +1 -1
  38. package/dist/routes/setup.js +1 -1
  39. package/dist/routes/setup.js.map +1 -1
  40. package/dist/server.d.ts +9 -0
  41. package/dist/server.js +410 -17
  42. package/dist/server.js.map +1 -1
  43. package/dist/services/agent-apps/catalog.js +4 -3
  44. package/dist/services/agent-apps/catalog.js.map +1 -1
  45. package/dist/services/agent-apps/index.d.ts +1 -1
  46. package/dist/services/agent-apps/index.js +1 -1
  47. package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
  48. package/dist/services/agent-apps/installers/adapter.js +1 -1
  49. package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
  50. package/dist/services/agent-apps/installers/shell-script.js +3 -3
  51. package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
  52. package/dist/services/agent-apps/types.d.ts +2 -2
  53. package/dist/services/agent-apps/types.js +1 -1
  54. package/dist/services/app/app-manager.d.ts +24 -1
  55. package/dist/services/app/app-manager.js +664 -116
  56. package/dist/services/app/app-manager.js.map +1 -1
  57. package/dist/services/app/hermes-agent-manager.js +6 -4
  58. package/dist/services/app/hermes-agent-manager.js.map +1 -1
  59. package/dist/services/app/provide-resolver.d.ts +29 -0
  60. package/dist/services/app/provide-resolver.js +112 -0
  61. package/dist/services/app/provide-resolver.js.map +1 -0
  62. package/dist/services/capability-endpoint-validator.d.ts +41 -0
  63. package/dist/services/capability-endpoint-validator.js +104 -0
  64. package/dist/services/capability-endpoint-validator.js.map +1 -0
  65. package/dist/services/capability-health.d.ts +16 -0
  66. package/dist/services/capability-health.js +121 -0
  67. package/dist/services/capability-health.js.map +1 -0
  68. package/dist/services/capability-registry.d.ts +106 -0
  69. package/dist/services/capability-registry.js +313 -0
  70. package/dist/services/capability-registry.js.map +1 -0
  71. package/dist/services/connection-apply.d.ts +89 -0
  72. package/dist/services/connection-apply.js +421 -0
  73. package/dist/services/connection-apply.js.map +1 -0
  74. package/dist/services/connection-resolver.d.ts +65 -0
  75. package/dist/services/connection-resolver.js +281 -0
  76. package/dist/services/connection-resolver.js.map +1 -0
  77. package/dist/services/connection-transactor.d.ts +37 -0
  78. package/dist/services/connection-transactor.js +341 -0
  79. package/dist/services/connection-transactor.js.map +1 -0
  80. package/dist/services/instance-manager.d.ts +13 -0
  81. package/dist/services/instance-manager.js +137 -23
  82. package/dist/services/instance-manager.js.map +1 -1
  83. package/dist/services/llm-proxy/index.d.ts +16 -2
  84. package/dist/services/llm-proxy/index.js +48 -44
  85. package/dist/services/llm-proxy/index.js.map +1 -1
  86. package/dist/services/llm-proxy/probe.d.ts +6 -0
  87. package/dist/services/llm-proxy/probe.js +85 -0
  88. package/dist/services/llm-proxy/probe.js.map +1 -0
  89. package/dist/services/llm-proxy/ssrf.d.ts +1 -0
  90. package/dist/services/llm-proxy/ssrf.js +18 -7
  91. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  92. package/dist/services/nomad-manager.js +375 -16
  93. package/dist/services/nomad-manager.js.map +1 -1
  94. package/dist/services/process-manager.js +1 -1
  95. package/dist/services/process-manager.js.map +1 -1
  96. package/dist/services/runtime/adapters/hermes.d.ts +30 -1
  97. package/dist/services/runtime/adapters/hermes.js +218 -5
  98. package/dist/services/runtime/adapters/hermes.js.map +1 -1
  99. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
  100. package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
  101. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
  102. package/dist/services/runtime/adapters/openclaw.d.ts +87 -0
  103. package/dist/services/runtime/adapters/openclaw.js +250 -2
  104. package/dist/services/runtime/adapters/openclaw.js.map +1 -1
  105. package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
  106. package/dist/services/runtime/mcp-shims/firewall.js +129 -0
  107. package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
  108. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
  109. package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
  110. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
  111. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
  112. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
  113. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
  114. package/dist/services/runtime/migrations.d.ts +8 -0
  115. package/dist/services/runtime/migrations.js +100 -0
  116. package/dist/services/runtime/migrations.js.map +1 -1
  117. package/dist/services/runtime/types.d.ts +15 -0
  118. package/dist/services/setup-manager.js +6 -6
  119. package/dist/services/setup-manager.js.map +1 -1
  120. package/dist/services/suggestions.d.ts +27 -0
  121. package/dist/services/suggestions.js +133 -0
  122. package/dist/services/suggestions.js.map +1 -0
  123. package/dist/services/task-registry.js +4 -2
  124. package/dist/services/task-registry.js.map +1 -1
  125. package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
  126. package/dist/services/telemetry/device-fingerprint.js +1 -1
  127. package/dist/services/types-shim.d.ts +16 -0
  128. package/dist/services/types-shim.js +2 -0
  129. package/dist/services/types-shim.js.map +1 -0
  130. package/dist/types.d.ts +171 -1
  131. package/dist/utils/instance-lock.d.ts +22 -0
  132. package/dist/utils/instance-lock.js +48 -0
  133. package/dist/utils/instance-lock.js.map +1 -0
  134. package/dist/utils/safe-json.js +55 -22
  135. package/dist/utils/safe-json.js.map +1 -1
  136. package/install/jishu-install.sh +323 -27
  137. package/install/jishu-uninstall.sh +353 -20
  138. package/package.json +3 -1
  139. package/public/assets/Dashboard-rkWp-CXd.js +1 -0
  140. package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-_GHoklgo.js} +1 -1
  141. package/public/assets/HermesConfigForm-anDnwUp_.js +4 -0
  142. package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-ZU9_-hDr.js} +1 -1
  143. package/public/assets/InstanceDetail-CN0FH1aw.js +92 -0
  144. package/public/assets/{Login-BWsZH2mu.js → Login-BItXqYAJ.js} +1 -1
  145. package/public/assets/NewInstance-BousE6kY.js +1 -0
  146. package/public/assets/ProviderRecommendations-DFYj7Fb6.js +1 -0
  147. package/public/assets/Settings-Bttc6QmM.js +1 -0
  148. package/public/assets/Setup-Bsxx1zgj.js +1 -0
  149. package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-DPZpAKgO.js} +2 -2
  150. package/public/assets/index-8xZy1z5k.css +1 -0
  151. package/public/assets/index-Dw3HhUYE.js +19 -0
  152. package/public/assets/providers-DtNXh9JD.js +1 -0
  153. package/public/assets/registry-5s2UB6is.js +2 -0
  154. package/public/index.html +2 -2
  155. package/scripts/check-app-spec.mjs +443 -0
  156. package/scripts/check-i18n.mjs +154 -0
  157. package/scripts/run.sh +4 -4
  158. package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
  159. package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
  160. package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
  161. package/public/assets/NewInstance-BCIrAd86.js +0 -1
  162. package/public/assets/Settings-xkDcduFz.js +0 -1
  163. package/public/assets/Setup-Cfuwj4gV.js +0 -1
  164. package/public/assets/index-CPhVFEsx.css +0 -1
  165. package/public/assets/index-DQsM6Joa.js +0 -19
  166. package/public/assets/providers-V-vwrExZ.js +0 -1
  167. package/public/assets/registry-B4UFJdpA.js +0 -2
@@ -2,7 +2,7 @@ id: openclaw-binary
2
2
  name: OpenClaw Binary
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
5
+ min_version: "0.4.30"
6
6
  agentType: openclaw
7
7
  description: "将 OpenClaw 安装到应用目录内的 npm prefix,并以 process runtime 运行网关"
8
8
  singleInstance: false
@@ -33,8 +33,37 @@ tasks:
33
33
 
34
34
  provides:
35
35
  - capability: "openclaw-dashboard"
36
+ task: "gateway"
36
37
  port: 18789
38
+ protocol: "http"
37
39
  description: "OpenClaw Dashboard"
40
+ - capability: "llm-agent"
41
+ task: "gateway"
42
+ port: 18789
43
+ protocol: "http"
44
+ path: "/v1"
45
+ description: "OpenAI-compatible agent endpoint backed by OpenClaw"
46
+ auth:
47
+ kind: "bearer"
48
+ tokenSource: "instance.config.gateway.auth.token"
49
+
50
+ requires:
51
+ - capability: "llm"
52
+ inject_as: "LLM_ENDPOINT"
53
+ required: false
54
+ cardinality: "one"
55
+ - capability: "search"
56
+ inject_as: "SEARCH_API_BASE_URL"
57
+ required: false
58
+ cardinality: "one"
59
+ - capability: "browser"
60
+ inject_as: "BROWSER_CDP_URL"
61
+ required: false
62
+ cardinality: "one"
63
+ - capability: "mcp"
64
+ inject_as: "MCP_SERVERS"
65
+ required: false
66
+ cardinality: "many"
38
67
 
39
68
  lifecycle:
40
69
  install:
@@ -2,7 +2,7 @@ id: openclaw-container
2
2
  name: OpenClaw Container
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
5
+ min_version: "0.4.30"
6
6
  agentType: openclaw
7
7
  description: "基于 openclaw-runtime 容器运行的 OpenClaw 网关"
8
8
  singleInstance: false
@@ -33,5 +33,40 @@ tasks:
33
33
 
34
34
  provides:
35
35
  - capability: "openclaw-dashboard"
36
+ task: "gateway"
36
37
  port: 18789
37
- description: "OpenClaw Dashboard"
38
+ protocol: "http"
39
+ description: "OpenClaw Dashboard"
40
+ # OpenClaw exposes OpenAI-compatible chat/completions when its config has
41
+ # `gateway.http.endpoints.chatCompletions.enabled = true`. The runtime
42
+ # adapter's startInstance hook patches openclaw.json on every start to
43
+ # ensure that flag is on, so any consumer binding this provider gets a
44
+ # working /v1/chat/completions endpoint regardless of when the instance
45
+ # was originally created. Auth uses the gateway token from openclaw.json.
46
+ - capability: "llm-agent"
47
+ task: "gateway"
48
+ port: 18789
49
+ protocol: "http"
50
+ path: "/v1"
51
+ description: "OpenAI-compatible agent endpoint backed by OpenClaw"
52
+ auth:
53
+ kind: "bearer"
54
+ tokenSource: "instance.config.gateway.auth.token"
55
+
56
+ requires:
57
+ - capability: "llm"
58
+ inject_as: "LLM_ENDPOINT"
59
+ required: false
60
+ cardinality: "one"
61
+ - capability: "search"
62
+ inject_as: "SEARCH_API_BASE_URL"
63
+ required: false
64
+ cardinality: "one"
65
+ - capability: "browser"
66
+ inject_as: "BROWSER_CDP_URL"
67
+ required: false
68
+ cardinality: "one"
69
+ - capability: "mcp"
70
+ inject_as: "MCP_SERVERS"
71
+ required: false
72
+ cardinality: "many"
@@ -2,7 +2,7 @@ id: openclaw-with-ollama-container
2
2
  name: OpenClaw With Ollama
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
5
+ min_version: "0.4.30"
6
6
  agentType: openclaw
7
7
  description: "通过 requires 消费 ollama-api 能力,并将解析地址注入 OpenClaw 运行环境"
8
8
  singleInstance: false
@@ -39,4 +39,13 @@ tasks:
39
39
  provides:
40
40
  - capability: "openclaw-dashboard"
41
41
  port: 18789
42
- description: "OpenClaw Dashboard"
42
+ description: "OpenClaw Dashboard"
43
+ - capability: "llm-agent"
44
+ task: "gateway"
45
+ port: 18789
46
+ protocol: "http"
47
+ path: "/v1"
48
+ description: "OpenAI-compatible agent endpoint backed by OpenClaw"
49
+ auth:
50
+ kind: "bearer"
51
+ tokenSource: "instance.config.gateway.auth.token"
@@ -2,7 +2,7 @@ id: openclaw-with-searxng-container
2
2
  name: OpenClaw With SearXNG Container
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
5
+ min_version: "0.4.30"
6
6
  agentType: openclaw
7
7
  description: "将 SearXNG 作为 sidecar 子 task 安装并运行,并注入 OpenClaw 搜索环境"
8
8
  singleInstance: false
@@ -46,7 +46,11 @@ tasks:
46
46
  args: ["gateway", "run", "--port", "18789", "--allow-unconfigured"]
47
47
  env:
48
48
  NODE_ENV: production
49
- SEARCH_API_BASE_URL: "http://127.0.0.1:8080/search"
49
+ # SEARCH_API_BASE_URL injected by Connection apply hook (search category, §13).
50
+ # Single-candidate fallback (`requires: search-searxng`) auto-binds the
51
+ # sidecar SearXNG below on first start, so meta-app UX stays "open the
52
+ # box and it works".
53
+ SEARCH_API_BASE_URL: "${requires.SEARCH_API_BASE_URL}"
50
54
  resources:
51
55
  cpu: "1000m"
52
56
  memory: "1024Mi"
@@ -65,8 +69,24 @@ tasks:
65
69
 
66
70
  provides:
67
71
  - capability: "openclaw-dashboard"
72
+ task: "gateway"
68
73
  port: 18789
74
+ protocol: "http"
69
75
  description: "OpenClaw Dashboard"
76
+ - capability: "llm-agent"
77
+ task: "gateway"
78
+ port: 18789
79
+ protocol: "http"
80
+ path: "/v1"
81
+ description: "OpenAI-compatible agent endpoint backed by OpenClaw"
82
+ auth:
83
+ kind: "bearer"
84
+ tokenSource: "instance.config.gateway.auth.token"
85
+
86
+ requires:
87
+ - capability: "search-searxng"
88
+ inject_as: "SEARCH_API_BASE_URL"
89
+ required: false
70
90
 
71
91
  lifecycle:
72
92
  install:
@@ -2,7 +2,7 @@ id: openwebui-container
2
2
  name: Open WebUI
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
5
+ min_version: "0.4.30"
6
6
  description: "使用 Open WebUI 提供自托管的大模型 Web 界面,容器模式由 Nomad 管理生命周期"
7
7
  singleInstance: false
8
8
 
@@ -11,6 +11,11 @@ tasks:
11
11
  role: service
12
12
  runtime: container
13
13
  image: "ghcr.io/open-webui/open-webui:main"
14
+ # Run as the panel user's uid:gid (typically pi=1000:1000). Bind-mount
15
+ # source `~/.jishushell/apps/${app_id}/data` is owned by the panel user,
16
+ # so the container can read/write it directly — no chown, no
17
+ # CAP_DAC_OVERRIDE, no DB readonly errors. Mirrors hermes / openclaw.
18
+ user: "host"
14
19
  env:
15
20
  HOME: "/app/backend/data"
16
21
  PORT: "8080"
@@ -19,6 +24,32 @@ tasks:
19
24
  HF_HOME: "/app/backend/data/.cache/huggingface"
20
25
  TRANSFORMERS_CACHE: "/app/backend/data/.cache/huggingface/transformers"
21
26
  SENTENCE_TRANSFORMERS_HOME: "/app/backend/data/.cache/sentence-transformers"
27
+ # OpenWebUI's start.sh writes a generated WEBUI_SECRET_KEY to a key
28
+ # file. The default path is `.webui_secret_key` in the image's
29
+ # /app/backend WORKDIR, which is owned by root and unwritable when
30
+ # running as uid 1000. Redirect to the bind-mounted (and writable)
31
+ # data dir so the key persists across restarts and survives image
32
+ # upgrades.
33
+ WEBUI_SECRET_KEY_FILE: "/app/backend/data/.webui_secret_key"
34
+ # Open WebUI marks OPENAI_API_BASE_URL / OPENAI_API_KEY as PersistentConfig:
35
+ # without this flag, the first start writes them to the internal DB and
36
+ # subsequent restarts ignore env, defeating Connections "Save & Restart"
37
+ # rebinds. Disabling persistent config means the Admin UI's manual
38
+ # OpenAI config doesn't survive restarts — that's the trade-off
39
+ # JishuShell takes to own LLM configuration via Connections (§13.3.2.1).
40
+ ENABLE_PERSISTENT_CONFIG: "False"
41
+ # JishuShell ships Open WebUI as a chat front-end only — RAG /
42
+ # reranker / Whisper STT all need ML model downloads from
43
+ # HuggingFace at first boot (~500 MB – 1.5 GB). On slow outbound
44
+ # networks (Raspberry Pi behind throttled NAT) this blocks
45
+ # health checks for hours. Bypassing them keeps the boot path
46
+ # to "FastAPI up in <30s". Users who want RAG/STT can flip these
47
+ # back from Admin UI and accept the one-time download.
48
+ BYPASS_EMBEDDING_AND_RETRIEVAL: "True"
49
+ RAG_EMBEDDING_MODEL: ""
50
+ RAG_RERANKING_MODEL: ""
51
+ WHISPER_MODEL: ""
52
+ AUDIO_STT_ENGINE: ""
22
53
  resources:
23
54
  cpu: "1000m"
24
55
  memory: "1024Mi"
@@ -41,10 +72,23 @@ tasks:
41
72
 
42
73
  provides:
43
74
  - capability: "openwebui-web"
75
+ task: "openwebui"
76
+ port: 3000
77
+ protocol: "http"
78
+ description: "Open WebUI Web 界面(legacy capability,请改用 web-openwebui)"
79
+ - capability: "web-openwebui"
80
+ task: "openwebui"
44
81
  port: 3000
45
82
  protocol: "http"
46
83
  description: "Open WebUI Web 界面"
47
84
 
85
+ requires:
86
+ - capability: "llm"
87
+ inject_as: "OPENAI_API_BASE_URL"
88
+ apply: "openai-env"
89
+ cardinality: "one"
90
+ required: false
91
+
48
92
  lifecycle:
49
93
  install:
50
94
  - downloadImage: "ghcr.io/open-webui/open-webui:main"
@@ -2,7 +2,7 @@ id: playwright-container
2
2
  name: Playwright
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
5
+ min_version: "0.4.30"
6
6
  description: "在容器中启动 Playwright UI,通过浏览器查看和管理测试运行"
7
7
  singleInstance: false
8
8
 
@@ -52,9 +52,15 @@ tasks:
52
52
 
53
53
  provides:
54
54
  - capability: "playwright-ui"
55
+ task: "playwright-ui"
55
56
  port: 14567
56
57
  protocol: "http"
57
58
  description: "Playwright UI Web 界面"
59
+ # browser-playwright (CDP endpoint) is TODO — needs a sidecar task that runs
60
+ # `npx playwright launch-server --browser chromium --hostname 0.0.0.0`,
61
+ # captures the token-bearing ws URL into a file/env, and registers it as a
62
+ # capability with protocol=ws. Until the sidecar lands, OpenClaw / Hermes
63
+ # browser slots cannot bind to this app via the Connections UI.
58
64
 
59
65
  lifecycle:
60
66
  install:
@@ -2,9 +2,9 @@ id: searxng-container
2
2
  name: SearXNG
3
3
  version: "1.0.0"
4
4
  jishushell:
5
- min_version: "0.4.24"
6
- description: "隐私优先的元搜索引擎,Docker 容器模式由 Nomad 管理生命周期"
7
- singleInstance: false
5
+ min_version: "0.4.30"
6
+ description: "隐私优先的元搜索引擎"
7
+ singleInstance: true
8
8
 
9
9
  tasks:
10
10
  - name: searxng
@@ -40,12 +40,62 @@ tasks:
40
40
 
41
41
  provides:
42
42
  - capability: "web-ui"
43
+ task: "searxng"
43
44
  port: 8080
45
+ protocol: "http"
46
+ description: "SearXNG Web UI(legacy capability,请改用 web-searxng)"
47
+ - capability: "web-searxng"
48
+ task: "searxng"
49
+ port: 8080
50
+ protocol: "http"
44
51
  description: "SearXNG Web UI"
45
- - capability: "search-api"
52
+ - capability: "search-searxng"
53
+ task: "searxng"
46
54
  port: 8080
47
55
  path: "/search"
56
+ protocol: "http"
48
57
  description: "SearXNG 搜索 API,支持 JSON 格式 (/search?q=xxx&format=json)"
58
+ # PR 8 / docs §17 — canonical MCP surface that LLM-binding adapters
59
+ # (Hermes, future MCP-capable runtimes) expose via the in-tree MCP
60
+ # firewall. Description is deliberately minimal so MiniMax-class
61
+ # models don't get prompt-injected by upstream wording into adding
62
+ # dates / translating queries / auto-filling time_range filters.
63
+ # Mirrors OpenClaw's built-in searxng plugin description verbatim.
64
+ tool_schema:
65
+ name: "searxng_web_search"
66
+ description: "Search the web using a self-hosted SearXNG instance. Returns titles, URLs, and snippets."
67
+ parameters:
68
+ type: object
69
+ properties:
70
+ query:
71
+ type: string
72
+ description: "Search query string."
73
+ count:
74
+ type: number
75
+ description: "Number of results to return (1-10)."
76
+ minimum: 1
77
+ maximum: 10
78
+ categories:
79
+ type: string
80
+ description: "Optional comma-separated categories such as general, news, or science."
81
+ language:
82
+ type: string
83
+ description: "Optional language code such as en, de, or fr."
84
+ required:
85
+ - query
86
+ upstream:
87
+ command: npx
88
+ args:
89
+ - "-y"
90
+ - "mcp-searxng"
91
+ env_template:
92
+ # ${SEARCH_API_BASE_URL_ORIGIN} = parsed URL with /search stripped.
93
+ # The URL stays at the registry-resolved host:port at PUT
94
+ # /connections time; on host IP changes, the framework re-renders
95
+ # this env on next instance start (PR 9 phaseRefreshConnections),
96
+ # so users only need to restart the consuming agent — no re-bind.
97
+ SEARXNG_URL: "${SEARCH_API_BASE_URL_ORIGIN}"
98
+ wrap_outputs: true
49
99
 
50
100
  lifecycle:
51
101
  install:
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