jishushell 0.4.10 → 0.4.24-beta.2

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 (248) hide show
  1. package/Dockerfile.hermes-slim +193 -0
  2. package/INSTALL-NOTICE +10 -12
  3. package/apps/hermes-container.yaml +35 -0
  4. package/apps/ollama-binary.yaml +164 -0
  5. package/apps/ollama-cpu-container.yaml +37 -0
  6. package/apps/ollama-with-hollama-binary.yaml +159 -0
  7. package/apps/openclaw-binary.yaml +69 -0
  8. package/apps/openclaw-container.yaml +37 -0
  9. package/apps/openclaw-with-ollama-container.yaml +42 -0
  10. package/apps/openclaw-with-searxng-container.yaml +136 -0
  11. package/apps/openwebui-container.yaml +53 -0
  12. package/apps/playwright-container.yaml +120 -0
  13. package/apps/searxng-container.yaml +115 -0
  14. package/dist/auth.d.ts +1 -0
  15. package/dist/auth.js +15 -14
  16. package/dist/auth.js.map +1 -1
  17. package/dist/cli/app.d.ts +4 -0
  18. package/dist/cli/app.js +874 -0
  19. package/dist/cli/app.js.map +1 -0
  20. package/dist/cli/backup.d.ts +3 -0
  21. package/dist/cli/backup.js +434 -0
  22. package/dist/cli/backup.js.map +1 -0
  23. package/dist/{doctor.d.ts → cli/doctor.d.ts} +7 -1
  24. package/dist/{doctor.js → cli/doctor.js} +377 -22
  25. package/dist/cli/doctor.js.map +1 -0
  26. package/dist/cli/helpers.d.ts +4 -0
  27. package/dist/cli/helpers.js +32 -0
  28. package/dist/cli/helpers.js.map +1 -0
  29. package/dist/cli/job.d.ts +4 -0
  30. package/dist/cli/job.js +198 -0
  31. package/dist/cli/job.js.map +1 -0
  32. package/dist/cli/llm.d.ts +25 -0
  33. package/dist/cli/llm.js +599 -0
  34. package/dist/cli/llm.js.map +1 -0
  35. package/dist/cli/managed-list.d.ts +30 -0
  36. package/dist/cli/managed-list.js +129 -0
  37. package/dist/cli/managed-list.js.map +1 -0
  38. package/dist/cli/panel.d.ts +26 -0
  39. package/dist/cli/panel.js +804 -0
  40. package/dist/cli/panel.js.map +1 -0
  41. package/dist/cli/version.d.ts +1 -0
  42. package/dist/cli/version.js +12 -0
  43. package/dist/cli/version.js.map +1 -0
  44. package/dist/cli.js +48 -776
  45. package/dist/cli.js.map +1 -1
  46. package/dist/config.d.ts +69 -0
  47. package/dist/config.js +268 -7
  48. package/dist/config.js.map +1 -1
  49. package/dist/control.d.ts +17 -41
  50. package/dist/control.js +61 -1323
  51. package/dist/control.js.map +1 -1
  52. package/dist/install.d.ts +16 -0
  53. package/dist/install.js +75 -26
  54. package/dist/install.js.map +1 -1
  55. package/dist/routes/agent-apps.d.ts +15 -0
  56. package/dist/routes/agent-apps.js +78 -0
  57. package/dist/routes/agent-apps.js.map +1 -0
  58. package/dist/routes/apps.d.ts +3 -0
  59. package/dist/routes/apps.js +278 -0
  60. package/dist/routes/apps.js.map +1 -0
  61. package/dist/routes/backup.js +3 -3
  62. package/dist/routes/backup.js.map +1 -1
  63. package/dist/routes/instances.d.ts +6 -0
  64. package/dist/routes/instances.js +863 -874
  65. package/dist/routes/instances.js.map +1 -1
  66. package/dist/routes/llm.d.ts +15 -0
  67. package/dist/routes/llm.js +247 -0
  68. package/dist/routes/llm.js.map +1 -0
  69. package/dist/routes/runtime.d.ts +15 -0
  70. package/dist/routes/runtime.js +69 -0
  71. package/dist/routes/runtime.js.map +1 -0
  72. package/dist/routes/setup.js +131 -9
  73. package/dist/routes/setup.js.map +1 -1
  74. package/dist/routes/system.js +56 -9
  75. package/dist/routes/system.js.map +1 -1
  76. package/dist/server.js +107 -7
  77. package/dist/server.js.map +1 -1
  78. package/dist/services/agent-apps/catalog.d.ts +30 -0
  79. package/dist/services/agent-apps/catalog.js +60 -0
  80. package/dist/services/agent-apps/catalog.js.map +1 -0
  81. package/dist/services/agent-apps/index.d.ts +36 -0
  82. package/dist/services/agent-apps/index.js +171 -0
  83. package/dist/services/agent-apps/index.js.map +1 -0
  84. package/dist/services/agent-apps/installers/adapter-probes.d.ts +49 -0
  85. package/dist/services/agent-apps/installers/adapter-probes.js +223 -0
  86. package/dist/services/agent-apps/installers/adapter-probes.js.map +1 -0
  87. package/dist/services/agent-apps/installers/adapter.d.ts +30 -0
  88. package/dist/services/agent-apps/installers/adapter.js +171 -0
  89. package/dist/services/agent-apps/installers/adapter.js.map +1 -0
  90. package/dist/services/agent-apps/installers/registry-probe.d.ts +38 -0
  91. package/dist/services/agent-apps/installers/registry-probe.js +183 -0
  92. package/dist/services/agent-apps/installers/registry-probe.js.map +1 -0
  93. package/dist/services/agent-apps/installers/shell-script.d.ts +47 -0
  94. package/dist/services/agent-apps/installers/shell-script.js +471 -0
  95. package/dist/services/agent-apps/installers/shell-script.js.map +1 -0
  96. package/dist/services/agent-apps/types.d.ts +125 -0
  97. package/dist/services/agent-apps/types.js +17 -0
  98. package/dist/services/agent-apps/types.js.map +1 -0
  99. package/dist/services/app/app-compiler.d.ts +15 -0
  100. package/dist/services/app/app-compiler.js +172 -0
  101. package/dist/services/app/app-compiler.js.map +1 -0
  102. package/dist/services/app/app-manager.d.ts +142 -0
  103. package/dist/services/app/app-manager.js +2148 -0
  104. package/dist/services/app/app-manager.js.map +1 -0
  105. package/dist/services/app/custom-manager.d.ts +27 -0
  106. package/dist/services/app/custom-manager.js +285 -0
  107. package/dist/services/app/custom-manager.js.map +1 -0
  108. package/dist/services/app/hermes-agent-manager.d.ts +20 -0
  109. package/dist/services/app/hermes-agent-manager.js +289 -0
  110. package/dist/services/app/hermes-agent-manager.js.map +1 -0
  111. package/dist/services/app/id-normalizer.d.ts +27 -0
  112. package/dist/services/app/id-normalizer.js +77 -0
  113. package/dist/services/app/id-normalizer.js.map +1 -0
  114. package/dist/services/app/ollama-manager.d.ts +18 -0
  115. package/dist/services/app/ollama-manager.js +207 -0
  116. package/dist/services/app/ollama-manager.js.map +1 -0
  117. package/dist/services/app/openclaw-manager.d.ts +63 -0
  118. package/dist/services/app/openclaw-manager.js +1178 -0
  119. package/dist/services/app/openclaw-manager.js.map +1 -0
  120. package/dist/services/app/paths.d.ts +47 -0
  121. package/dist/services/app/paths.js +68 -0
  122. package/dist/services/app/paths.js.map +1 -0
  123. package/dist/services/app/registry.d.ts +17 -0
  124. package/dist/services/app/registry.js +31 -0
  125. package/dist/services/app/registry.js.map +1 -0
  126. package/dist/services/app/remote-spec.d.ts +14 -0
  127. package/dist/services/app/remote-spec.js +58 -0
  128. package/dist/services/app/remote-spec.js.map +1 -0
  129. package/dist/services/app/terminal-session-manager.d.ts +27 -0
  130. package/dist/services/app/terminal-session-manager.js +157 -0
  131. package/dist/services/app/terminal-session-manager.js.map +1 -0
  132. package/dist/services/app/types.d.ts +72 -0
  133. package/dist/services/app/types.js +16 -0
  134. package/dist/services/app/types.js.map +1 -0
  135. package/dist/services/backup-manager.js +60 -22
  136. package/dist/services/backup-manager.js.map +1 -1
  137. package/dist/services/instance-manager.d.ts +125 -34
  138. package/dist/services/instance-manager.js +679 -1043
  139. package/dist/services/instance-manager.js.map +1 -1
  140. package/dist/services/llm-proxy/adapters.js +5 -1
  141. package/dist/services/llm-proxy/adapters.js.map +1 -1
  142. package/dist/services/llm-proxy/circuit-breaker.js +10 -2
  143. package/dist/services/llm-proxy/circuit-breaker.js.map +1 -1
  144. package/dist/services/llm-proxy/index.d.ts +43 -0
  145. package/dist/services/llm-proxy/index.js +120 -5
  146. package/dist/services/llm-proxy/index.js.map +1 -1
  147. package/dist/services/llm-proxy/ssrf.js +1 -1
  148. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  149. package/dist/services/nomad-manager.d.ts +260 -3
  150. package/dist/services/nomad-manager.js +2921 -341
  151. package/dist/services/nomad-manager.js.map +1 -1
  152. package/dist/services/panel-manager.d.ts +50 -0
  153. package/dist/services/panel-manager.js +443 -0
  154. package/dist/services/panel-manager.js.map +1 -0
  155. package/dist/services/plugin-installer.js +28 -2
  156. package/dist/services/plugin-installer.js.map +1 -1
  157. package/dist/services/process-manager.js +42 -7
  158. package/dist/services/process-manager.js.map +1 -1
  159. package/dist/services/runtime/adapters/custom.d.ts +20 -0
  160. package/dist/services/runtime/adapters/custom.js +90 -0
  161. package/dist/services/runtime/adapters/custom.js.map +1 -0
  162. package/dist/services/runtime/adapters/hermes.d.ts +174 -0
  163. package/dist/services/runtime/adapters/hermes.js +1316 -0
  164. package/dist/services/runtime/adapters/hermes.js.map +1 -0
  165. package/dist/services/runtime/adapters/openclaw-routes.d.ts +17 -0
  166. package/dist/services/runtime/adapters/openclaw-routes.js +946 -0
  167. package/dist/services/runtime/adapters/openclaw-routes.js.map +1 -0
  168. package/dist/services/runtime/adapters/openclaw.d.ts +188 -0
  169. package/dist/services/runtime/adapters/openclaw.js +2195 -0
  170. package/dist/services/runtime/adapters/openclaw.js.map +1 -0
  171. package/dist/services/runtime/errors.d.ts +28 -0
  172. package/dist/services/runtime/errors.js +31 -0
  173. package/dist/services/runtime/errors.js.map +1 -0
  174. package/dist/services/runtime/index.d.ts +34 -0
  175. package/dist/services/runtime/index.js +51 -0
  176. package/dist/services/runtime/index.js.map +1 -0
  177. package/dist/services/runtime/instance.d.ts +24 -0
  178. package/dist/services/runtime/instance.js +143 -0
  179. package/dist/services/runtime/instance.js.map +1 -0
  180. package/dist/services/runtime/migrations.d.ts +15 -0
  181. package/dist/services/runtime/migrations.js +25 -0
  182. package/dist/services/runtime/migrations.js.map +1 -0
  183. package/dist/services/runtime/registry.d.ts +13 -0
  184. package/dist/services/runtime/registry.js +32 -0
  185. package/dist/services/runtime/registry.js.map +1 -0
  186. package/dist/services/runtime/types.d.ts +545 -0
  187. package/dist/services/runtime/types.js +14 -0
  188. package/dist/services/runtime/types.js.map +1 -0
  189. package/dist/services/setup-manager.d.ts +70 -29
  190. package/dist/services/setup-manager.js +591 -625
  191. package/dist/services/setup-manager.js.map +1 -1
  192. package/dist/services/task-registry.d.ts +44 -0
  193. package/dist/services/task-registry.js +74 -0
  194. package/dist/services/task-registry.js.map +1 -0
  195. package/dist/services/telemetry/heartbeat.d.ts +6 -6
  196. package/dist/services/telemetry/heartbeat.js +29 -30
  197. package/dist/services/telemetry/heartbeat.js.map +1 -1
  198. package/dist/services/update-manager.d.ts +47 -0
  199. package/dist/services/update-manager.js +305 -0
  200. package/dist/services/update-manager.js.map +1 -0
  201. package/dist/types.d.ts +224 -0
  202. package/dist/utils/docker-host.d.ts +15 -0
  203. package/dist/utils/docker-host.js +64 -0
  204. package/dist/utils/docker-host.js.map +1 -0
  205. package/install/jishu-install.sh +303 -38
  206. package/install/post-install.sh +64 -5
  207. package/package.json +19 -5
  208. package/public/assets/Dashboard-rh9qpYRR.js +1 -0
  209. package/public/assets/HermesChatPanel-D6JI6lLY.js +1 -0
  210. package/public/assets/HermesConfigForm-DcbSemaj.js +4 -0
  211. package/public/assets/InitPassword-CFTKsED4.js +1 -0
  212. package/public/assets/InstanceDetail-BhNIKA6Z.js +91 -0
  213. package/public/assets/{Login-CUoEZOWR.js → Login-KB9qrtM0.js} +1 -1
  214. package/public/assets/NewInstance-CxkO8Hlq.js +1 -0
  215. package/public/assets/Settings-BVWJvOkU.js +1 -0
  216. package/public/assets/Setup-X-lzuaUT.js +1 -0
  217. package/public/assets/WeixinLoginPanel-gca0QTic.js +9 -0
  218. package/public/assets/index-C8B0cFJM.js +19 -0
  219. package/public/assets/index-CPhVFEsx.css +1 -0
  220. package/public/assets/input-paste-CrNVAyOy.js +1 -0
  221. package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
  222. package/public/assets/registry-fVUSujib.js +2 -0
  223. package/public/assets/{usePolling-CK0DfI4h.js → usePolling-Do5Erqm_.js} +1 -1
  224. package/public/assets/vendor-i18n-ucpM0OR0.js +9 -0
  225. package/public/assets/{vendor-react-B1-3Yrt-.js → vendor-react-Bk1hRGiY.js} +1 -1
  226. package/public/favicon.png +0 -0
  227. package/public/index.html +9 -4
  228. package/public/logos/hermes.png +0 -0
  229. package/public/logos/ollama.png +0 -0
  230. package/public/logos/openclaw.svg +60 -0
  231. package/scripts/build-hermes-image.sh +21 -0
  232. package/scripts/build-local.sh +54 -0
  233. package/scripts/check-adapter-isolation.ts +293 -0
  234. package/scripts/fixtures/instances/hermes-sample/instance.json +37 -0
  235. package/scripts/fixtures/instances/legacy-openclaw-sample/instance.json +7 -0
  236. package/scripts/smoke/hermes-bootstrap.sh +195 -0
  237. package/templates/hermes-entrypoint.sh +154 -0
  238. package/dist/doctor.js.map +0 -1
  239. package/install/jishu-install-china.sh +0 -3092
  240. package/public/assets/Dashboard-DhsrzJ4F.js +0 -1
  241. package/public/assets/InitPassword-BjubiVdd.js +0 -1
  242. package/public/assets/InstanceDetail-DMcywsof.js +0 -17
  243. package/public/assets/NewInstance-Bk0G4EiJ.js +0 -1
  244. package/public/assets/Settings-D5tHL_h5.js +0 -1
  245. package/public/assets/Setup-4t6E3Rut.js +0 -1
  246. package/public/assets/index-BJ47MWpF.css +0 -1
  247. package/public/assets/index-DbX85irc.js +0 -16
  248. package/public/assets/vendor-i18n-CfW0RvgE.js +0 -9
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Probe functions used by `buildCardView` to populate an adapter-backed
3
+ * `AgentAppCardView` — install status, disk size, and update availability.
4
+ *
5
+ * The update probe is the expensive one (needs to reach the upstream
6
+ * registry) so it runs behind a stale-while-revalidate cache: callers see
7
+ * whatever is cached immediately, and a background refresh kicks off when
8
+ * the cache is stale. `forceRefreshAllAdapterUpdates` is the explicit
9
+ * "check updates now" path used by the UI button; it awaits in-flight
10
+ * refreshes instead of skipping them so the button's result reflects the
11
+ * newest state, not the value that was cached at click time.
12
+ */
13
+ import { execFile } from "child_process";
14
+ import { promisify } from "util";
15
+ import { HERMES_DEFAULT_IMAGE, getOpenclawDockerImage, getRuntimeCatalogEntry, } from "../../../config.js";
16
+ import { getAdapter, hasAdapter } from "../../runtime/index.js";
17
+ import { fetchRemoteDigest, parseImageRef } from "./registry-probe.js";
18
+ const execFileAsync = promisify(execFile);
19
+ // ── Image resolution ────────────────────────────────────────────────────
20
+ /**
21
+ * Resolve the docker image an adapter's instances currently run on. Prefers
22
+ * the installed record in panel.json.runtime_catalog; falls back to the
23
+ * adapter's declared default so update checks still work even when the app
24
+ * has been uninstalled (or was never installed through the new flow).
25
+ */
26
+ export function resolveAdapterImage(agentType) {
27
+ if (agentType === "openclaw")
28
+ return getOpenclawDockerImage();
29
+ const entry = getRuntimeCatalogEntry(agentType);
30
+ const fromCatalog = entry?.defaultImage;
31
+ if (typeof fromCatalog === "string" && fromCatalog.trim())
32
+ return fromCatalog;
33
+ // Fallback: hardcoded defaults per adapter. Adapters that want to
34
+ // participate in update checks without a runtime_catalog entry (yet)
35
+ // advertise their default here.
36
+ if (agentType === "hermes")
37
+ return HERMES_DEFAULT_IMAGE;
38
+ return null;
39
+ }
40
+ // ── Status probe ────────────────────────────────────────────────────────
41
+ export async function probeAdapterStatus(agentType) {
42
+ if (!hasAdapter(agentType))
43
+ return null;
44
+ const adapter = getAdapter(agentType);
45
+ if (!adapter.getInstallStatus)
46
+ return null;
47
+ try {
48
+ return (await Promise.race([
49
+ Promise.resolve(adapter.getInstallStatus()),
50
+ new Promise((_, reject) => setTimeout(() => reject(new Error("install-status probe timed out")), 2500)),
51
+ ]));
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ // ── Image size probe ────────────────────────────────────────────────────
58
+ /**
59
+ * Read the disk size (MB, decimal) of the adapter's current docker image.
60
+ * Uses `docker image ls --format {{.Size}}` (same cumulative-layer total
61
+ * admins see in `docker images`) instead of `docker image inspect .Size`
62
+ * which reports dedup'd unique bytes and doesn't match operator
63
+ * expectation.
64
+ */
65
+ export async function probeAdapterImageSizeMB(agentType) {
66
+ const image = resolveAdapterImage(agentType);
67
+ if (!image)
68
+ return null;
69
+ try {
70
+ const { stdout } = await execFileAsync("docker", ["image", "ls", "--format", "{{.Size}}", image], { timeout: 2500, encoding: "utf-8" });
71
+ const firstLine = stdout.split("\n")[0]?.trim();
72
+ if (!firstLine)
73
+ return null;
74
+ return parseDockerSizeToMB(firstLine);
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ /**
81
+ * Parse docker's human-readable size strings ("2.26GB", "593MB", "12.5kB")
82
+ * into megabytes. Docker uses decimal (SI) units for these displays.
83
+ */
84
+ function parseDockerSizeToMB(raw) {
85
+ const m = raw.trim().match(/^(\d+(?:\.\d+)?)\s*(B|kB|MB|GB|TB)$/i);
86
+ if (!m)
87
+ return null;
88
+ const value = parseFloat(m[1]);
89
+ if (!Number.isFinite(value))
90
+ return null;
91
+ const unit = m[2].toLowerCase();
92
+ const mb = unit === "b" ? value / 1_000_000 :
93
+ unit === "kb" ? value / 1_000 :
94
+ unit === "mb" ? value :
95
+ unit === "gb" ? value * 1_000 :
96
+ unit === "tb" ? value * 1_000_000 : NaN;
97
+ return Number.isFinite(mb) ? Math.round(mb) : null;
98
+ }
99
+ const updateCache = new Map();
100
+ const UPDATE_CACHE_TTL_OK_MS = 10 * 60 * 1000;
101
+ const UPDATE_CACHE_TTL_NULL_MS = 60 * 1000;
102
+ /**
103
+ * Read the local RepoDigest sha for the given image — this is what docker
104
+ * recorded when it pulled the tag (the manifest-list's digest for
105
+ * multi-arch images), so it lines up with what the registry HEAD returns.
106
+ */
107
+ async function readLocalDigest(image) {
108
+ try {
109
+ const { stdout } = await execFileAsync("docker", ["image", "inspect", "--format", "{{range .RepoDigests}}{{.}}{{println}}{{end}}", image], { timeout: 5_000, encoding: "utf-8" });
110
+ const line = stdout.split("\n").find((l) => l.includes("@sha256:"));
111
+ const m = line?.match(/@sha256:([a-f0-9]{64})/);
112
+ return m ? m[1] : null;
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ async function readRemoteDigest(image) {
119
+ const ref = parseImageRef(image);
120
+ if (!ref)
121
+ return null;
122
+ return fetchRemoteDigest(ref);
123
+ }
124
+ /**
125
+ * Refresh the update-cache entry for one agentType. Safe to call while a
126
+ * refresh is already in flight — the second caller awaits the same
127
+ * promise instead of starting a duplicate probe.
128
+ */
129
+ function refreshUpdateCache(agentType, image) {
130
+ const cached = updateCache.get(agentType);
131
+ if (cached?.inflight)
132
+ return cached.inflight;
133
+ const t0 = Date.now();
134
+ console.log(`[agent-apps] ${agentType}: probing digest for ${image}`);
135
+ const inflight = (async () => {
136
+ try {
137
+ const [local, remote] = await Promise.all([
138
+ readLocalDigest(image),
139
+ readRemoteDigest(image),
140
+ ]);
141
+ const value = !local || !remote ? null : local !== remote;
142
+ console.log(`[agent-apps] ${agentType}: local=${local?.slice(0, 12) ?? "null"} ` +
143
+ `remote=${remote?.slice(0, 12) ?? "null"} result=${value} (+${Date.now() - t0}ms)`);
144
+ updateCache.set(agentType, { value, at: Date.now() });
145
+ }
146
+ catch (e) {
147
+ console.log(`[agent-apps] ${agentType}: probe error ${e?.message || e} (+${Date.now() - t0}ms)`);
148
+ updateCache.set(agentType, { value: null, at: Date.now() });
149
+ }
150
+ })();
151
+ updateCache.set(agentType, {
152
+ value: cached?.value ?? null,
153
+ at: cached?.at ?? 0,
154
+ inflight,
155
+ });
156
+ // When the async IIFE above writes the final value, the new entry will
157
+ // not carry `inflight`, so subsequent calls will see the fresh state.
158
+ inflight.finally(() => {
159
+ const final = updateCache.get(agentType);
160
+ if (final?.inflight === inflight) {
161
+ updateCache.set(agentType, { value: final.value, at: final.at });
162
+ }
163
+ });
164
+ return inflight;
165
+ }
166
+ /**
167
+ * Lazy background probe: returns whatever is currently cached, and kicks
168
+ * off a refresh in the background if the cache is stale. First cold call
169
+ * returns `null`; the next catalog poll after the refresh lands will flip
170
+ * to the real value.
171
+ */
172
+ export async function probeAdapterUpdateAvailable(agentType) {
173
+ const image = resolveAdapterImage(agentType);
174
+ if (!image)
175
+ return null;
176
+ const cached = updateCache.get(agentType);
177
+ const now = Date.now();
178
+ const ttl = cached?.value == null ? UPDATE_CACHE_TTL_NULL_MS : UPDATE_CACHE_TTL_OK_MS;
179
+ const stale = !cached || now - cached.at > ttl;
180
+ if (stale && !cached?.inflight) {
181
+ refreshUpdateCache(agentType, image).catch(() => {
182
+ // swallowed — refreshUpdateCache writes a null entry on its own
183
+ });
184
+ }
185
+ return cached?.value ?? null;
186
+ }
187
+ /** Timestamp (ms since epoch) when the freshest cached probe completed. */
188
+ export function getUpdateCacheLastCheckedAt() {
189
+ let latest = 0;
190
+ for (const entry of updateCache.values()) {
191
+ if (entry.at > latest)
192
+ latest = entry.at;
193
+ }
194
+ return latest > 0 ? latest : null;
195
+ }
196
+ /**
197
+ * Explicit "check updates now" path — backs `POST /api/agent-apps/check-
198
+ * updates`. Bypasses the TTL entirely: if a refresh is already running it
199
+ * awaits it instead of skipping (so the response carries the newest value,
200
+ * not a stale cached null). Runs all targets in parallel.
201
+ */
202
+ export async function forceRefreshAllAdapterUpdates(agentTypes) {
203
+ const targets = [];
204
+ for (const agentType of agentTypes) {
205
+ const image = resolveAdapterImage(agentType);
206
+ if (image)
207
+ targets.push({ agentType, image });
208
+ }
209
+ await Promise.all(targets.map(({ agentType, image }) => {
210
+ const cached = updateCache.get(agentType);
211
+ // Already refreshing → wait for it. Otherwise start a fresh probe
212
+ // even if the TTL hasn't expired (user explicitly asked for a check).
213
+ if (cached?.inflight)
214
+ return cached.inflight;
215
+ return refreshUpdateCache(agentType, image);
216
+ }));
217
+ const out = {};
218
+ for (const { agentType } of targets) {
219
+ out[agentType] = updateCache.get(agentType)?.value ?? null;
220
+ }
221
+ return out;
222
+ }
223
+ //# sourceMappingURL=adapter-probes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-probes.js","sourceRoot":"","sources":["../../../../src/services/agent-apps/installers/adapter-probes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEvE,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,2EAA2E;AAE3E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,IAAI,SAAS,KAAK,UAAU;QAAE,OAAO,sBAAsB,EAAE,CAAC;IAE9D,MAAM,KAAK,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,KAAK,EAAE,YAAY,CAAC;IACxC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,IAAI,EAAE;QAAE,OAAO,WAAW,CAAC;IAE9E,kEAAkE;IAClE,qEAAqE;IACrE,gCAAgC;IAChC,IAAI,SAAS,KAAK,QAAQ;QAAE,OAAO,oBAAoB,CAAC;IAExD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2EAA2E;AAE3E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB;IAEjB,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC;YACzB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC3C,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC9B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,EAAE,IAAI,CAAC,CAC5E;SACF,CAAC,CAEM,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,2EAA2E;AAE3E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,SAAiB;IAC7D,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,QAAQ,EACR,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,CAAC,EAC/C,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CACrC,CAAC;QACF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAC5B,OAAO,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,GAAW;IACtC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACnE,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,EAAE,GACN,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;QAClC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;YAC/B,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBACvB,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;oBAC/B,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1C,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAWD,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;AACxD,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC9C,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C;;;;GAIG;AACH,KAAK,UAAU,eAAe,CAAC,KAAa;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,QAAQ,EACR,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,+CAA+C,EAAE,KAAK,CAAC,EACxF,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CACtC,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QACpE,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAChD,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,KAAa;IAC3C,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,SAAiB,EAAE,KAAa;IAC1D,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,MAAM,EAAE,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IAE7C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,wBAAwB,KAAK,EAAE,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACxC,eAAe,CAAC,KAAK,CAAC;gBACtB,gBAAgB,CAAC,KAAK,CAAC;aACxB,CAAC,CAAC;YACH,MAAM,KAAK,GACT,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC;YAC9C,OAAO,CAAC,GAAG,CACT,gBAAgB,SAAS,WAAW,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,GAAG;gBACpE,UAAU,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CACnF,CAAC;YACF,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,iBAAiB,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACjG,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE;QACzB,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,IAAI;QAC5B,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC;QACnB,QAAQ;KACT,CAAC,CAAC;IACH,uEAAuE;IACvE,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;QACpB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,KAAK,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,SAAiB;IAEjB,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,GAAG,GAAG,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,sBAAsB,CAAC;IACtF,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC;IAE/C,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;QAC/B,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC9C,gEAAgE;QAClE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,2BAA2B;IACzC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,KAAK,CAAC,EAAE,GAAG,MAAM;YAAE,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACpC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,UAAoB;IAEpB,MAAM,OAAO,GAAgD,EAAE,CAAC;IAChE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;QACnC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,kEAAkE;QAClE,sEAAsE;QACtE,IAAI,MAAM,EAAE,QAAQ;YAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;QAC7C,OAAO,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,GAAG,GAAmC,EAAE,CAAC;IAC/C,KAAK,MAAM,EAAE,SAAS,EAAE,IAAI,OAAO,EAAE,CAAC;QACpC,GAAG,CAAC,SAAS,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Installer strategy for adapter-backed agent apps (hermes, openclaw).
3
+ *
4
+ * Delegates install to `adapter.startBuildRuntimeImage` (preferred when
5
+ * present — openclaw builds its image locally) or
6
+ * `adapter.startInstallRuntime` (hermes pulls from GHCR). Uninstall runs
7
+ * locally here (docker image rm + clear runtime_catalog + drop shim).
8
+ *
9
+ * Probe + cache logic lives in `adapter-probes.ts`; this file re-exports
10
+ * the probes so callers import one entry point.
11
+ */
12
+ import type { InstallStartResult } from "../types.js";
13
+ export { forceRefreshAllAdapterUpdates, getUpdateCacheLastCheckedAt, probeAdapterImageSizeMB, probeAdapterStatus, probeAdapterUpdateAvailable, resolveAdapterImage, } from "./adapter-probes.js";
14
+ export declare function startAdapterInstall(agentType: string, options?: Record<string, any>): Promise<InstallStartResult>;
15
+ export declare function startAdapterUninstall(agentType: string): InstallStartResult;
16
+ /**
17
+ * Current running task for this agentType. Returns both the id and the
18
+ * kind so the UI can render "安装中" vs "卸载中" from one field instead
19
+ * of having to guess from task name shape.
20
+ *
21
+ * Install tasks match by prefix (`openclaw`, `openclaw-docker-pull`,
22
+ * `hermes`). Uninstall tasks use the disjoint `uninstall-${agentType}`
23
+ * name so no prefix collision is possible.
24
+ */
25
+ export declare function findRunningAdapterTask(agentType: string): {
26
+ id: string;
27
+ kind: "install" | "uninstall";
28
+ } | undefined;
29
+ /** Count live instances bound to this agentType — used to block uninstall. */
30
+ export declare function countInstancesUsingAgentType(agentType: string): number;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Installer strategy for adapter-backed agent apps (hermes, openclaw).
3
+ *
4
+ * Delegates install to `adapter.startBuildRuntimeImage` (preferred when
5
+ * present — openclaw builds its image locally) or
6
+ * `adapter.startInstallRuntime` (hermes pulls from GHCR). Uninstall runs
7
+ * locally here (docker image rm + clear runtime_catalog + drop shim).
8
+ *
9
+ * Probe + cache logic lives in `adapter-probes.ts`; this file re-exports
10
+ * the probes so callers import one entry point.
11
+ */
12
+ import { execFile } from "child_process";
13
+ import { existsSync, rmSync } from "fs";
14
+ import { promisify } from "util";
15
+ import { HERMES_RUNTIME_DIR, getPanelConfig, savePanelConfig, } from "../../../config.js";
16
+ import { listInstances } from "../../app/app-manager.js";
17
+ import { getAdapter, hasAdapter, resolveAgentType } from "../../runtime/index.js";
18
+ import { createTask, emitTask, getRunningTasks, } from "../../task-registry.js";
19
+ import { resolveAdapterImage } from "./adapter-probes.js";
20
+ const execFileAsync = promisify(execFile);
21
+ export { forceRefreshAllAdapterUpdates, getUpdateCacheLastCheckedAt, probeAdapterImageSizeMB, probeAdapterStatus, probeAdapterUpdateAvailable, resolveAdapterImage, } from "./adapter-probes.js";
22
+ // ── Dispatch: install ───────────────────────────────────────────────────
23
+ export async function startAdapterInstall(agentType, options = {}) {
24
+ if (!hasAdapter(agentType)) {
25
+ return { ok: false, error: `Unknown agentType "${agentType}"` };
26
+ }
27
+ // Reject install while uninstall is running — they are destructive of
28
+ // each other. Names are disjoint (`uninstall-${agentType}` vs
29
+ // `${agentType}` / `${agentType}-*`), so this check can't match an
30
+ // install task.
31
+ if (getRunningTasks(`uninstall-${agentType}`).length > 0) {
32
+ return {
33
+ ok: false,
34
+ error: `${agentType} 正在卸载,请等待卸载完成后再安装`,
35
+ };
36
+ }
37
+ // Dedup: if a task for this agent is already running (Setup wizard, or
38
+ // the user clicked install twice), join it instead of starting a second.
39
+ // Adapters use prefix-based names (`openclaw`, `openclaw-docker-pull`,
40
+ // `hermes`), so a prefix match matches install variants but — by design
41
+ // — cannot match `uninstall-${agentType}`.
42
+ const existing = getRunningTasks(agentType);
43
+ if (existing.length > 0) {
44
+ return {
45
+ ok: true,
46
+ taskId: existing[0].id,
47
+ reused: true,
48
+ message: `${agentType} install already in progress`,
49
+ };
50
+ }
51
+ const adapter = getAdapter(agentType);
52
+ let result;
53
+ if (typeof adapter.startBuildRuntimeImage === "function") {
54
+ result = await adapter.startBuildRuntimeImage(options);
55
+ }
56
+ else if (typeof adapter.startInstallRuntime === "function") {
57
+ result = adapter.startInstallRuntime(options);
58
+ }
59
+ else {
60
+ return { ok: false, error: `Runtime "${agentType}" does not implement install` };
61
+ }
62
+ if (result && typeof result === "object") {
63
+ return {
64
+ ok: result.ok !== false,
65
+ taskId: result.taskId,
66
+ message: result.message,
67
+ error: result.ok === false ? (result.error || result.message) : undefined,
68
+ };
69
+ }
70
+ return { ok: false, error: "Adapter returned an unexpected install result" };
71
+ }
72
+ // ── Dispatch: uninstall ─────────────────────────────────────────────────
73
+ export function startAdapterUninstall(agentType) {
74
+ if (!hasAdapter(agentType)) {
75
+ return { ok: false, error: `Unknown agentType "${agentType}"` };
76
+ }
77
+ // Reject uninstall while install/update is in flight — we don't want to
78
+ // yank the docker image out from under a running docker pull.
79
+ if (getRunningTasks(agentType).length > 0) {
80
+ return {
81
+ ok: false,
82
+ error: `${agentType} 正在安装/更新,请等待完成后再卸载`,
83
+ };
84
+ }
85
+ // Uninstall dedup: second uninstall clicks within the window join nothing
86
+ // — we just tell the user it's already running.
87
+ if (getRunningTasks(`uninstall-${agentType}`).length > 0) {
88
+ return {
89
+ ok: false,
90
+ error: `${agentType} 卸载已在进行中`,
91
+ };
92
+ }
93
+ const image = resolveAdapterImage(agentType);
94
+ const task = createTask(`uninstall-${agentType}`);
95
+ (async () => {
96
+ try {
97
+ emitTask(task, { type: "progress", message: `准备卸载 ${agentType}...`, progress: 0 });
98
+ if (image) {
99
+ emitTask(task, { type: "progress", message: `删除 docker 镜像: ${image}`, progress: 30 });
100
+ try {
101
+ await execFileAsync("docker", ["image", "rm", "-f", image], {
102
+ timeout: 30_000, encoding: "utf-8",
103
+ });
104
+ emitTask(task, { type: "log", message: "docker image rm 成功" });
105
+ }
106
+ catch (e) {
107
+ emitTask(task, {
108
+ type: "log",
109
+ message: `docker image rm 失败(可能镜像不存在): ${e.message || e}`,
110
+ });
111
+ }
112
+ }
113
+ else {
114
+ emitTask(task, { type: "log", message: "未找到镜像引用,跳过 docker image rm" });
115
+ }
116
+ emitTask(task, { type: "progress", message: "清理 runtime_catalog...", progress: 70 });
117
+ const config = getPanelConfig();
118
+ if (config.runtime_catalog && typeof config.runtime_catalog === "object") {
119
+ delete config.runtime_catalog[agentType];
120
+ savePanelConfig(config);
121
+ }
122
+ if (agentType === "hermes") {
123
+ emitTask(task, { type: "progress", message: "清理 Hermes shim 目录...", progress: 85 });
124
+ try {
125
+ if (existsSync(HERMES_RUNTIME_DIR)) {
126
+ rmSync(HERMES_RUNTIME_DIR, { recursive: true, force: true });
127
+ }
128
+ }
129
+ catch (e) {
130
+ emitTask(task, { type: "log", message: `shim 目录清理失败: ${e.message || e}` });
131
+ }
132
+ }
133
+ emitTask(task, { type: "done", message: `${agentType} 卸载完成`, progress: 100 });
134
+ task.status = "done";
135
+ }
136
+ catch (e) {
137
+ emitTask(task, { type: "error", message: `卸载失败: ${e?.message || e}` });
138
+ task.status = "error";
139
+ }
140
+ })();
141
+ return { ok: true, taskId: task.id };
142
+ }
143
+ // ── Helpers consumed by routes/index layer ──────────────────────────────
144
+ /**
145
+ * Current running task for this agentType. Returns both the id and the
146
+ * kind so the UI can render "安装中" vs "卸载中" from one field instead
147
+ * of having to guess from task name shape.
148
+ *
149
+ * Install tasks match by prefix (`openclaw`, `openclaw-docker-pull`,
150
+ * `hermes`). Uninstall tasks use the disjoint `uninstall-${agentType}`
151
+ * name so no prefix collision is possible.
152
+ */
153
+ export function findRunningAdapterTask(agentType) {
154
+ const install = getRunningTasks(agentType)[0];
155
+ if (install)
156
+ return { id: install.id, kind: "install" };
157
+ const uninstall = getRunningTasks(`uninstall-${agentType}`)[0];
158
+ if (uninstall)
159
+ return { id: uninstall.id, kind: "uninstall" };
160
+ return undefined;
161
+ }
162
+ /** Count live instances bound to this agentType — used to block uninstall. */
163
+ export function countInstancesUsingAgentType(agentType) {
164
+ let count = 0;
165
+ for (const meta of listInstances()) {
166
+ if (resolveAgentType(meta) === agentType)
167
+ count += 1;
168
+ }
169
+ return count;
170
+ }
171
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../../src/services/agent-apps/installers/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * OCI / Docker Registry v2 digest lookups via curl.
3
+ *
4
+ * We shell out to curl instead of using Node's built-in `fetch` because:
5
+ * - Node's fetch (undici) ships a 10 s TCP connect timeout that isn't
6
+ * configurable without pulling undici in as a direct dep, and the TLS
7
+ * handshake to ghcr.io from edge devices on slow international links
8
+ * routinely exceeds that (9–15 s measured from a Pi in mainland China).
9
+ * - curl's `--max-time` is wall-clock and reliable.
10
+ * - curl is already present on every target platform.
11
+ *
12
+ * The call chain implements the standard OCI distribution bearer-token flow:
13
+ * 1. GET <realm>?service=...&scope=... → bearer token
14
+ * 2. HEAD /v2/<repository>/manifests/<reference> → docker-content-digest
15
+ *
16
+ * Well-known public registries (GHCR, Docker Hub, Quay) always require a
17
+ * token, so we skip the throwaway unauthenticated probe for them — that
18
+ * alone saves ~10 s per image on slow WAN links.
19
+ */
20
+ export interface ImageRef {
21
+ registry: string;
22
+ repository: string;
23
+ reference: string;
24
+ }
25
+ /**
26
+ * Parse a docker image reference into `{ registry, repository, reference }`.
27
+ * Handles: "ghcr.io/owner/name:tag", "owner/name:tag" (defaults to Docker
28
+ * Hub), "name" (defaults to "library/<name>:latest"). Returns null for
29
+ * digest-pinned refs (no lookup is needed) or malformed strings.
30
+ */
31
+ export declare function parseImageRef(image: string): ImageRef | null;
32
+ /**
33
+ * Fetch the current remote digest (sha256 hex, without the "sha256:" prefix)
34
+ * for a fully-resolved image reference. Returns null when the registry is
35
+ * unreachable, the image is private without an auth token we can obtain, or
36
+ * any other probe failure.
37
+ */
38
+ export declare function fetchRemoteDigest(ref: ImageRef): Promise<string | null>;