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,173 @@
1
+ /**
2
+ * App passwords for WebDAV / external clients (M3 W4).
3
+ *
4
+ * The panel's master password is too valuable to hand to iOS Files,
5
+ * macOS Finder, etc. — those clients persist credentials and have a
6
+ * long blast radius if leaked. App passwords are independent tokens:
7
+ * - bcrypt hashed
8
+ * - per-token scope (path prefix) and mode (ro/rw)
9
+ * - revocable individually without affecting other clients
10
+ * - last_used_at tracked so suspicious tokens can be spotted
11
+ *
12
+ * Stored under ~/.jishushell/app-passwords.json (atomic safe write).
13
+ *
14
+ * Vision: G7 — WebDAV uses app-passwords, NOT the panel password.
15
+ */
16
+ import * as fs from "node:fs";
17
+ import { randomBytes, timingSafeEqual } from "node:crypto";
18
+ import bcrypt from "bcryptjs";
19
+ import { JISHUSHELL_HOME } from "../config.js";
20
+ import { join } from "node:path";
21
+ import { safeWriteJson } from "../utils/safe-json.js";
22
+ const STORE_PATH = join(JISHUSHELL_HOME, "app-passwords.json");
23
+ function readStore() {
24
+ if (!fs.existsSync(STORE_PATH))
25
+ return { version: 1, passwords: [] };
26
+ try {
27
+ const raw = JSON.parse(fs.readFileSync(STORE_PATH, "utf8"));
28
+ if (raw && Array.isArray(raw.passwords))
29
+ return raw;
30
+ }
31
+ catch (e) {
32
+ console.warn(`[app-passwords] failed to load: ${e.message}`);
33
+ }
34
+ return { version: 1, passwords: [] };
35
+ }
36
+ function writeStore(data) {
37
+ fs.mkdirSync(JISHUSHELL_HOME, { recursive: true });
38
+ safeWriteJson(STORE_PATH, data);
39
+ }
40
+ /**
41
+ * Generate a new app password. Returns the plaintext token exactly
42
+ * once — caller MUST surface it to the user immediately, after which
43
+ * only the hash is recoverable.
44
+ */
45
+ export async function createAppPassword(opts) {
46
+ const trimmedName = (opts.name ?? "").trim();
47
+ if (!trimmedName)
48
+ throw new Error("name is required");
49
+ if (trimmedName.length > 64)
50
+ throw new Error("name too long");
51
+ const id = randomBytes(8).toString("hex"); // 16 hex chars
52
+ const token = randomBytes(32).toString("hex"); // 64 hex chars (~256 bits)
53
+ const hash = await bcrypt.hash(token, 10);
54
+ const record = {
55
+ id,
56
+ name: trimmedName,
57
+ hash,
58
+ scope: opts.scope ?? { kind: "all" },
59
+ mode: opts.mode ?? "rw",
60
+ created_at: Math.floor(Date.now() / 1000),
61
+ revoked: false,
62
+ };
63
+ const store = readStore();
64
+ store.passwords.push(record);
65
+ writeStore(store);
66
+ return { id, token, record };
67
+ }
68
+ /**
69
+ * Verify a Basic Auth credential pair against the store. Returns the
70
+ * matching record when valid, null otherwise. Updates last_used_at on
71
+ * success.
72
+ *
73
+ * Username is the password id (16 hex chars from createAppPassword).
74
+ * Password is the plaintext token.
75
+ */
76
+ export async function verifyAppPassword(username, password) {
77
+ if (!username || !password)
78
+ return null;
79
+ const store = readStore();
80
+ const record = store.passwords.find((p) => p.id === username);
81
+ if (!record || record.revoked) {
82
+ // Run a dummy bcrypt to keep timing constant whether or not the id exists
83
+ await bcrypt.compare(password, "$2a$10$0123456789012345678901abcdefghijklmnopqrstuvwxyz");
84
+ return null;
85
+ }
86
+ const ok = await bcrypt.compare(password, record.hash);
87
+ if (!ok)
88
+ return null;
89
+ record.last_used_at = Math.floor(Date.now() / 1000);
90
+ writeStore(store);
91
+ return record;
92
+ }
93
+ /**
94
+ * Returns the public view of all stored passwords (no hashes / tokens).
95
+ */
96
+ export function listAppPasswords() {
97
+ const store = readStore();
98
+ return store.passwords.map(({ hash: _ignored, ...rest }) => rest);
99
+ }
100
+ export function revokeAppPassword(id) {
101
+ const store = readStore();
102
+ const record = store.passwords.find((p) => p.id === id);
103
+ if (!record)
104
+ return false;
105
+ record.revoked = true;
106
+ writeStore(store);
107
+ return true;
108
+ }
109
+ export function deleteAppPassword(id) {
110
+ const store = readStore();
111
+ const before = store.passwords.length;
112
+ store.passwords = store.passwords.filter((p) => p.id !== id);
113
+ if (store.passwords.length === before)
114
+ return false;
115
+ writeStore(store);
116
+ return true;
117
+ }
118
+ /**
119
+ * Check whether `relPath` is allowed by `record.scope` and `record.mode`.
120
+ * `relPath` is files/-relative.
121
+ */
122
+ export function isAllowed(record, relPath, needsWrite) {
123
+ if (record.revoked)
124
+ return { allowed: false, reason: "revoked" };
125
+ if (needsWrite && record.mode === "ro") {
126
+ return { allowed: false, reason: "read-only credential" };
127
+ }
128
+ if (record.scope.kind === "all")
129
+ return { allowed: true };
130
+ // path scope: relPath must equal prefix or start with prefix + '/'
131
+ const prefix = record.scope.prefix;
132
+ if (!prefix)
133
+ return { allowed: true };
134
+ if (relPath === prefix)
135
+ return { allowed: true };
136
+ if (relPath.startsWith(prefix + "/"))
137
+ return { allowed: true };
138
+ if (prefix === "")
139
+ return { allowed: true };
140
+ return { allowed: false, reason: "outside scope" };
141
+ }
142
+ /**
143
+ * Parse a Basic Auth header into [username, password] or null.
144
+ */
145
+ export function parseBasicAuth(header) {
146
+ if (!header || typeof header !== "string")
147
+ return null;
148
+ const [scheme, value] = header.split(" ", 2);
149
+ if (!scheme || scheme.toLowerCase() !== "basic" || !value)
150
+ return null;
151
+ let decoded;
152
+ try {
153
+ decoded = Buffer.from(value, "base64").toString("utf8");
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ const idx = decoded.indexOf(":");
159
+ if (idx < 0)
160
+ return null;
161
+ const username = decoded.slice(0, idx);
162
+ const password = decoded.slice(idx + 1);
163
+ return [username, password];
164
+ }
165
+ /**
166
+ * Constant-time compare helper for callers that need it (token == token).
167
+ */
168
+ export function tokenEquals(a, b) {
169
+ if (a.length !== b.length)
170
+ return false;
171
+ return timingSafeEqual(Buffer.from(a), Buffer.from(b));
172
+ }
173
+ //# sourceMappingURL=app-passwords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-passwords.js","sourceRoot":"","sources":["../../src/services/app-passwords.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,17 @@
3
3
  * Ported from OpenClaw official: encodeAbsolutePathForBackupArchive.
4
4
  */
5
5
  export declare function encodeAbsolutePathForArchive(sourcePath: string): string;
6
+ /**
7
+ * Decode a path produced by {@link encodeAbsolutePathForArchive} back to
8
+ * the original absolute filesystem path.
9
+ *
10
+ * - `"posix/Users/arm/foo"` → `"/Users/arm/foo"`
11
+ * - `"windows/C/Users/foo"` → `"C:\\Users\\foo"`
12
+ * - `"relative/some/path"` → `"some/path"`
13
+ *
14
+ * Returns the input unchanged when the prefix is unrecognised.
15
+ */
16
+ export declare function decodeArchivePath(encodedPath: string): string;
6
17
  export interface CallOpenclawBackupResult {
7
18
  ok: boolean;
8
19
  archivePath?: string;
@@ -1,7 +1,7 @@
1
1
  import { execFileSync, spawn as spawnChild } from "child_process";
2
2
  import { createHash, randomUUID } from "crypto";
3
- import { copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "fs";
4
- import { dirname, join } from "path";
3
+ import { copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "fs";
4
+ import { dirname, join, relative } from "path";
5
5
  import { posix as pathPosix } from "path";
6
6
  import { BACKUPS_DIR, INSTANCES_DIR, TMP_DIR } from "../config.js";
7
7
  /**
@@ -20,6 +20,156 @@ export function encodeAbsolutePathForArchive(sourcePath) {
20
20
  return pathPosix.join("posix", normalized.slice(1));
21
21
  return pathPosix.join("relative", normalized);
22
22
  }
23
+ /**
24
+ * Decode a path produced by {@link encodeAbsolutePathForArchive} back to
25
+ * the original absolute filesystem path.
26
+ *
27
+ * - `"posix/Users/arm/foo"` → `"/Users/arm/foo"`
28
+ * - `"windows/C/Users/foo"` → `"C:\\Users\\foo"`
29
+ * - `"relative/some/path"` → `"some/path"`
30
+ *
31
+ * Returns the input unchanged when the prefix is unrecognised.
32
+ */
33
+ export function decodeArchivePath(encodedPath) {
34
+ const normalized = encodedPath.replaceAll("\\", "/");
35
+ if (normalized.startsWith("posix/")) {
36
+ return "/" + normalized.slice("posix/".length);
37
+ }
38
+ if (normalized.startsWith("windows/")) {
39
+ const rest = normalized.slice("windows/".length);
40
+ const slashIdx = rest.indexOf("/");
41
+ const drive = slashIdx >= 0 ? rest.slice(0, slashIdx) : rest;
42
+ // Drive letter must be a single alphabetic character
43
+ if (!/^[A-Za-z]$/.test(drive))
44
+ return encodedPath;
45
+ const tail = slashIdx >= 0 ? rest.slice(slashIdx + 1) : "";
46
+ return `${drive}:\\${tail.replaceAll("/", "\\")}`;
47
+ }
48
+ if (normalized.startsWith("relative/")) {
49
+ return normalized.slice("relative/".length);
50
+ }
51
+ return encodedPath;
52
+ }
53
+ /**
54
+ * Validate that a decoded `oldHome` path is safe to use with `replaceAll`.
55
+ *
56
+ * Guards against catastrophic replacements when the inferred path is empty,
57
+ * a filesystem root, or relative (which would match too broadly).
58
+ */
59
+ function isValidOldHome(path) {
60
+ if (!path || path === "." || path === "..")
61
+ return false;
62
+ // POSIX: must start with / and not be root itself
63
+ if (path.startsWith("/"))
64
+ return path.length > 1;
65
+ // Windows: must be drive:\something (at least one segment after drive)
66
+ const winMatch = path.match(/^[A-Za-z]:[/\\](.+)$/);
67
+ if (winMatch)
68
+ return true;
69
+ return false;
70
+ }
71
+ const MAX_REWRITE_FILE_SIZE = 100 * 1024 * 1024; // 100 MB
72
+ /**
73
+ * Rewrite absolute paths in session data and symlinks after copying from a
74
+ * backup archive. Best-effort per file: individual failures produce warnings
75
+ * but do not block the import or affect other files.
76
+ *
77
+ * Walks all `.json` and `.jsonl` files under {@link targetDir} recursively
78
+ * and replaces occurrences of {@link oldHome} with {@link newHome}. Also
79
+ * rewrites symlink targets that contain {@link oldHome}.
80
+ */
81
+ function rewriteInstancePaths(targetDir, oldHome, newHome, warnings) {
82
+ if (oldHome === newHome)
83
+ return;
84
+ if (!isValidOldHome(oldHome)) {
85
+ warnings.push(`Path rewrite skipped: inferred source path "${oldHome}" is invalid or too broad`);
86
+ return;
87
+ }
88
+ const walkDir = (dir) => {
89
+ if (!existsSync(dir))
90
+ return;
91
+ let entries;
92
+ try {
93
+ entries = readdirSync(dir);
94
+ }
95
+ catch {
96
+ return;
97
+ }
98
+ for (const entry of entries) {
99
+ const fullPath = join(dir, entry);
100
+ let s;
101
+ try {
102
+ s = lstatSync(fullPath);
103
+ }
104
+ catch {
105
+ continue;
106
+ }
107
+ // Rewrite symlink targets
108
+ if (s.isSymbolicLink()) {
109
+ try {
110
+ const target = readlinkSync(fullPath);
111
+ if (target.includes(oldHome)) {
112
+ unlinkSync(fullPath);
113
+ symlinkSync(target.replaceAll(oldHome, newHome), fullPath);
114
+ }
115
+ }
116
+ catch (e) {
117
+ warnings.push(`Symlink rewrite failed for ${entry}: ${e.message}`);
118
+ }
119
+ continue;
120
+ }
121
+ if (s.isDirectory()) {
122
+ walkDir(fullPath);
123
+ continue;
124
+ }
125
+ // Only process .json and .jsonl regular files
126
+ if (!s.isFile())
127
+ continue;
128
+ if (!entry.endsWith(".json") && !entry.endsWith(".jsonl"))
129
+ continue;
130
+ if (s.size > MAX_REWRITE_FILE_SIZE) {
131
+ warnings.push(`Skipped path rewrite for large file: ${entry} (${s.size} bytes)`);
132
+ continue;
133
+ }
134
+ try {
135
+ const content = readFileSync(fullPath, "utf-8");
136
+ if (content.includes(oldHome)) {
137
+ writeFileSync(fullPath, content.replaceAll(oldHome, newHome));
138
+ }
139
+ }
140
+ catch (e) {
141
+ warnings.push(`Path rewrite failed for ${entry}: ${e.message}`);
142
+ }
143
+ }
144
+ };
145
+ walkDir(targetDir);
146
+ }
147
+ /**
148
+ * Detect the original `openclawHome` path that was used when the backup
149
+ * archive was created.
150
+ *
151
+ * Priority:
152
+ * 1. `manifest.source_home` (written by newer selfPackOfficialFormat)
153
+ * 2. Fallback: decode the encoded path from the payload/ directory structure
154
+ */
155
+ function detectOldHome(manifest, payloadDir, extractedStateDir) {
156
+ // Priority 1: manifest metadata
157
+ if (manifest?.source_home && typeof manifest.source_home === "string") {
158
+ return manifest.source_home;
159
+ }
160
+ // Priority 2: decode from payload/ path structure
161
+ try {
162
+ const extractedHomeDir = dirname(extractedStateDir);
163
+ const relativeToPayload = relative(payloadDir, extractedHomeDir);
164
+ // Reject if relative() produced a `..` traversal (unexpected layout)
165
+ if (relativeToPayload.startsWith(".."))
166
+ return null;
167
+ return decodeArchivePath(relativeToPayload);
168
+ }
169
+ catch {
170
+ return null;
171
+ }
172
+ }
23
173
  /**
24
174
  * Try to create a backup by calling the official `openclaw backup create` CLI.
25
175
  * Returns ok:false if the binary is missing or the command fails.
@@ -795,6 +945,7 @@ export async function selfPackOfficialFormat(instanceId, outputPath, opts) {
795
945
  paths: {
796
946
  stateDir: `payload/${encodedPath}/.openclaw`,
797
947
  },
948
+ source_home: openclawHome,
798
949
  has_sessions: hasSessions,
799
950
  checksum: `sha256:${checksum}`,
800
951
  checksum_scope: "content-excluding-manifest",
@@ -996,6 +1147,12 @@ export async function restoreInstance(instanceId, backupFilePath) {
996
1147
  }
997
1148
  }
998
1149
  touchInstanceLock(instanceId);
1150
+ // Rewrite absolute paths in session data and symlinks when the backup
1151
+ // originates from a different machine or instance (cross-machine restore).
1152
+ const oldHome = detectOldHome(manifest, payloadDir, extractedStateDir);
1153
+ if (oldHome) {
1154
+ rewriteInstancePaths(effectiveScope === "home" ? openclawHome : stateDir, oldHome, openclawHome, warnings);
1155
+ }
999
1156
  // Step 10: Rebuild runtime state
1000
1157
  try {
1001
1158
  const { bootstrapInstanceProxy } = await import("./llm-proxy/index.js");
@@ -1071,9 +1228,10 @@ export async function createFromBackup(backupFilePath, opts) {
1071
1228
  // openclaw-home tree, not just .openclaw (see importInstance for the
1072
1229
  // rationale).
1073
1230
  let archiveScope = "state";
1231
+ let manifest = null;
1074
1232
  const manifestPath = join(archiveRoot, "manifest.json");
1075
1233
  if (existsSync(manifestPath)) {
1076
- const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
1234
+ manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
1077
1235
  if (manifest.has_api_keys === false || !manifest.has_api_keys) {
1078
1236
  warnings.push("No API Key in this backup. You will need to configure one.");
1079
1237
  }
@@ -1126,6 +1284,13 @@ export async function createFromBackup(backupFilePath, opts) {
1126
1284
  return true;
1127
1285
  });
1128
1286
  }
1287
+ // Rewrite absolute paths in session data and symlinks from the source
1288
+ // instance to the new instance (fixes ENOENT crash on Docker import
1289
+ // and silent cross-instance data leakage on local import).
1290
+ const oldHome = detectOldHome(manifest, payloadDir, extractedStateDir);
1291
+ if (oldHome) {
1292
+ rewriteInstancePaths(archiveScope === "home" ? newOpenclawHome : newStateDir, oldHome, newOpenclawHome, warnings);
1293
+ }
1129
1294
  // Scrub channel credentials from copied openclaw.json (new instance should not inherit IM bindings)
1130
1295
  await scrubNewInstanceConfig(join(newStateDir, "openclaw.json"));
1131
1296
  // DO NOT copy model.env — bootstrapInstanceProxy will generate a new proxy token
@@ -1490,10 +1655,11 @@ export async function importInstance(tempId, opts) {
1490
1655
  // backup silently drops the runtime/app tree that motivated the
1491
1656
  // home-scope in the first place.
1492
1657
  let archiveScope = "state";
1658
+ let manifest = null;
1493
1659
  const manifestPath = join(archiveRoot, "manifest.json");
1494
1660
  if (existsSync(manifestPath)) {
1495
1661
  try {
1496
- const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
1662
+ manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
1497
1663
  if (manifest?.scope === "home")
1498
1664
  archiveScope = "home";
1499
1665
  }
@@ -1541,6 +1707,13 @@ export async function importInstance(tempId, opts) {
1541
1707
  return true;
1542
1708
  });
1543
1709
  }
1710
+ // Rewrite absolute paths in session data and symlinks from the source
1711
+ // instance to the new instance (fixes ENOENT crash on Docker import
1712
+ // and silent cross-instance data leakage on local import).
1713
+ const oldHome = detectOldHome(manifest, payloadDir, extractedStateDir);
1714
+ if (oldHome) {
1715
+ rewriteInstancePaths(archiveScope === "home" ? newOpenclawHome : newStateDir, oldHome, newOpenclawHome, warnings);
1716
+ }
1544
1717
  // Scrub channel credentials from copied openclaw.json (new instance should not inherit IM bindings)
1545
1718
  await scrubNewInstanceConfig(join(newStateDir, "openclaw.json"));
1546
1719
  // DO NOT copy model.env, provider.env, instance.json