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.
- package/INSTALL-NOTICE +11 -0
- package/apps/anythingllm-container.yaml +287 -0
- package/apps/browserless-chromium-container.yaml +90 -0
- package/apps/filebrowser-container.yaml +163 -0
- package/apps/hermes-container.yaml +36 -2
- package/apps/ollama-binary.yaml +91 -90
- package/apps/ollama-cpu-container.yaml +8 -1
- package/apps/ollama-with-hollama-binary.yaml +91 -90
- package/apps/openclaw-binary.yaml +38 -1
- package/apps/openclaw-container.yaml +45 -2
- package/apps/openclaw-with-ollama-container.yaml +11 -2
- package/apps/openclaw-with-searxng-container.yaml +26 -2
- package/apps/openwebui-container.yaml +45 -1
- package/apps/playwright-container.yaml +7 -1
- package/apps/searxng-container.yaml +58 -7
- package/apps/weknora-container.yaml +471 -0
- package/dist/cli/app.js +79 -9
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/doctor.d.ts +12 -12
- package/dist/cli/doctor.js +242 -55
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/llm.d.ts +4 -3
- package/dist/cli/llm.js +4 -3
- package/dist/cli/llm.js.map +1 -1
- package/dist/cli/panel.d.ts +6 -5
- package/dist/cli/panel.js +10 -9
- package/dist/cli/panel.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +99 -1
- package/dist/config.js.map +1 -1
- package/dist/control.d.ts +7 -6
- package/dist/control.js +7 -6
- package/dist/control.js.map +1 -1
- package/dist/install.js +3 -3
- package/dist/install.js.map +1 -1
- package/dist/routes/agent-apps.d.ts +1 -1
- package/dist/routes/agent-apps.js +1 -1
- package/dist/routes/apps.js +44 -11
- package/dist/routes/apps.js.map +1 -1
- package/dist/routes/auth.js +5 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/backup.js +64 -11
- package/dist/routes/backup.js.map +1 -1
- package/dist/routes/external-mounts.d.ts +17 -0
- package/dist/routes/external-mounts.js +73 -0
- package/dist/routes/external-mounts.js.map +1 -0
- package/dist/routes/file-mounts.d.ts +13 -0
- package/dist/routes/file-mounts.js +90 -0
- package/dist/routes/file-mounts.js.map +1 -0
- package/dist/routes/files-organize.d.ts +28 -0
- package/dist/routes/files-organize.js +167 -0
- package/dist/routes/files-organize.js.map +1 -0
- package/dist/routes/files.d.ts +31 -0
- package/dist/routes/files.js +321 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/instances.js +826 -17
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/internal.d.ts +2 -0
- package/dist/routes/internal.js +59 -0
- package/dist/routes/internal.js.map +1 -0
- package/dist/routes/llm.js +24 -35
- package/dist/routes/llm.js.map +1 -1
- package/dist/routes/setup.js +10 -10
- package/dist/routes/setup.js.map +1 -1
- package/dist/routes/system.js +1 -1
- package/dist/routes/system.js.map +1 -1
- package/dist/routes/webdav.d.ts +17 -0
- package/dist/routes/webdav.js +114 -0
- package/dist/routes/webdav.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +751 -20
- package/dist/server.js.map +1 -1
- package/dist/services/agent-apps/catalog.js +4 -3
- package/dist/services/agent-apps/catalog.js.map +1 -1
- package/dist/services/agent-apps/index.d.ts +1 -1
- package/dist/services/agent-apps/index.js +1 -1
- package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
- package/dist/services/agent-apps/installers/adapter.js +1 -1
- package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
- package/dist/services/agent-apps/installers/shell-script.js +3 -3
- package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
- package/dist/services/agent-apps/types.d.ts +2 -2
- package/dist/services/agent-apps/types.js +1 -1
- package/dist/services/app/app-compiler.d.ts +1 -1
- package/dist/services/app/app-compiler.js +5 -5
- package/dist/services/app/app-compiler.js.map +1 -1
- package/dist/services/app/app-manager.d.ts +25 -1
- package/dist/services/app/app-manager.js +829 -150
- package/dist/services/app/app-manager.js.map +1 -1
- package/dist/services/app/custom-manager.js.map +1 -1
- package/dist/services/app/hermes-agent-manager.js +7 -4
- package/dist/services/app/hermes-agent-manager.js.map +1 -1
- package/dist/services/app/ollama-manager.js +1 -1
- package/dist/services/app/ollama-manager.js.map +1 -1
- package/dist/services/app/openclaw-manager.js +20 -3
- package/dist/services/app/openclaw-manager.js.map +1 -1
- package/dist/services/app/platform-transform.d.ts +32 -0
- package/dist/services/app/platform-transform.js +65 -0
- package/dist/services/app/platform-transform.js.map +1 -0
- package/dist/services/app/provide-resolver.d.ts +29 -0
- package/dist/services/app/provide-resolver.js +112 -0
- package/dist/services/app/provide-resolver.js.map +1 -0
- package/dist/services/app-passwords.d.ts +61 -0
- package/dist/services/app-passwords.js +173 -0
- package/dist/services/app-passwords.js.map +1 -0
- package/dist/services/backup-manager.d.ts +11 -0
- package/dist/services/backup-manager.js +177 -4
- package/dist/services/backup-manager.js.map +1 -1
- package/dist/services/capability-endpoint-validator.d.ts +41 -0
- package/dist/services/capability-endpoint-validator.js +104 -0
- package/dist/services/capability-endpoint-validator.js.map +1 -0
- package/dist/services/capability-health.d.ts +16 -0
- package/dist/services/capability-health.js +121 -0
- package/dist/services/capability-health.js.map +1 -0
- package/dist/services/capability-registry.d.ts +106 -0
- package/dist/services/capability-registry.js +313 -0
- package/dist/services/capability-registry.js.map +1 -0
- package/dist/services/connection-apply.d.ts +91 -0
- package/dist/services/connection-apply.js +475 -0
- package/dist/services/connection-apply.js.map +1 -0
- package/dist/services/connection-resolver.d.ts +65 -0
- package/dist/services/connection-resolver.js +281 -0
- package/dist/services/connection-resolver.js.map +1 -0
- package/dist/services/connection-transactor.d.ts +39 -0
- package/dist/services/connection-transactor.js +351 -0
- package/dist/services/connection-transactor.js.map +1 -0
- package/dist/services/external-mounts.d.ts +40 -0
- package/dist/services/external-mounts.js +187 -0
- package/dist/services/external-mounts.js.map +1 -0
- package/dist/services/files-manager.d.ts +252 -0
- package/dist/services/files-manager.js +1075 -0
- package/dist/services/files-manager.js.map +1 -0
- package/dist/services/files-mounts.d.ts +42 -0
- package/dist/services/files-mounts.js +207 -0
- package/dist/services/files-mounts.js.map +1 -0
- package/dist/services/instance-manager.d.ts +13 -0
- package/dist/services/instance-manager.js +138 -46
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +16 -2
- package/dist/services/llm-proxy/index.js +48 -44
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/probe.d.ts +6 -0
- package/dist/services/llm-proxy/probe.js +85 -0
- package/dist/services/llm-proxy/probe.js.map +1 -0
- package/dist/services/llm-proxy/ssrf.d.ts +1 -0
- package/dist/services/llm-proxy/ssrf.js +24 -9
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.d.ts +4 -0
- package/dist/services/nomad-manager.js +428 -35
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/organize/applier.d.ts +46 -0
- package/dist/services/organize/applier.js +218 -0
- package/dist/services/organize/applier.js.map +1 -0
- package/dist/services/organize/rules.d.ts +57 -0
- package/dist/services/organize/rules.js +286 -0
- package/dist/services/organize/rules.js.map +1 -0
- package/dist/services/organize/scanner.d.ts +50 -0
- package/dist/services/organize/scanner.js +366 -0
- package/dist/services/organize/scanner.js.map +1 -0
- package/dist/services/organize/store.d.ts +14 -0
- package/dist/services/organize/store.js +82 -0
- package/dist/services/organize/store.js.map +1 -0
- package/dist/services/panel-manager.js +20 -1
- package/dist/services/panel-manager.js.map +1 -1
- package/dist/services/process-manager.js +4 -3
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/runtime/adapters/hermes.d.ts +30 -1
- package/dist/services/runtime/adapters/hermes.js +219 -6
- package/dist/services/runtime/adapters/hermes.js.map +1 -1
- package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
- package/dist/services/runtime/adapters/openclaw-routes.d.ts +8 -2
- package/dist/services/runtime/adapters/openclaw-routes.js +68 -0
- package/dist/services/runtime/adapters/openclaw-routes.js.map +1 -1
- package/dist/services/runtime/adapters/openclaw.d.ts +177 -0
- package/dist/services/runtime/adapters/openclaw.js +1171 -11
- package/dist/services/runtime/adapters/openclaw.js.map +1 -1
- package/dist/services/runtime/instance.d.ts +1 -1
- package/dist/services/runtime/instance.js +1 -1
- package/dist/services/runtime/instance.js.map +1 -1
- package/dist/services/runtime/mcp-shims/anythingllm-shim.d.ts +46 -0
- package/dist/services/runtime/mcp-shims/anythingllm-shim.js +281 -0
- package/dist/services/runtime/mcp-shims/anythingllm-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/drive-shim.d.ts +54 -0
- package/dist/services/runtime/mcp-shims/drive-shim.js +489 -0
- package/dist/services/runtime/mcp-shims/drive-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
- package/dist/services/runtime/mcp-shims/firewall.js +129 -0
- package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
- package/dist/services/runtime/migrations.d.ts +8 -0
- package/dist/services/runtime/migrations.js +100 -0
- package/dist/services/runtime/migrations.js.map +1 -1
- package/dist/services/runtime/types.d.ts +46 -0
- package/dist/services/setup-manager.js +99 -24
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/suggestions.d.ts +27 -0
- package/dist/services/suggestions.js +133 -0
- package/dist/services/suggestions.js.map +1 -0
- package/dist/services/task-registry.js +4 -2
- package/dist/services/task-registry.js.map +1 -1
- package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
- package/dist/services/telemetry/device-fingerprint.js +1 -1
- package/dist/services/types-shim.d.ts +16 -0
- package/dist/services/types-shim.js +2 -0
- package/dist/services/types-shim.js.map +1 -0
- package/dist/services/webdav/server.d.ts +24 -0
- package/dist/services/webdav/server.js +420 -0
- package/dist/services/webdav/server.js.map +1 -0
- package/dist/services/webdav/xml-builder.d.ts +73 -0
- package/dist/services/webdav/xml-builder.js +156 -0
- package/dist/services/webdav/xml-builder.js.map +1 -0
- package/dist/services/workspace-builder.d.ts +29 -0
- package/dist/services/workspace-builder.js +188 -0
- package/dist/services/workspace-builder.js.map +1 -0
- package/dist/types.d.ts +231 -1
- package/dist/utils/instance-lock.d.ts +22 -0
- package/dist/utils/instance-lock.js +48 -0
- package/dist/utils/instance-lock.js.map +1 -0
- package/dist/utils/path-locks.d.ts +30 -0
- package/dist/utils/path-locks.js +63 -0
- package/dist/utils/path-locks.js.map +1 -0
- package/dist/utils/path-safety.d.ts +41 -0
- package/dist/utils/path-safety.js +119 -0
- package/dist/utils/path-safety.js.map +1 -0
- package/dist/utils/safe-json.js +55 -22
- package/dist/utils/safe-json.js.map +1 -1
- package/dist/utils/safe-write.d.ts +24 -0
- package/dist/utils/safe-write.js +82 -0
- package/dist/utils/safe-write.js.map +1 -0
- package/install/jishu-install.sh +323 -27
- package/install/jishu-uninstall.sh +353 -20
- package/package.json +18 -1
- package/public/assets/Dashboard-BdWPtroF.js +1 -0
- package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-B_2HlVBQ.js} +1 -1
- package/public/assets/HermesConfigForm-DVlhg3WV.js +4 -0
- package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-D7glTExX.js} +1 -1
- package/public/assets/InstanceDetail-CxSy2cpe.js +92 -0
- package/public/assets/{Login-BWsZH2mu.js → Login-Cfr5c2sv.js} +1 -1
- package/public/assets/NewInstance-BIYDmJis.js +1 -0
- package/public/assets/ProviderRecommendations-BuRnvRcI.js +1 -0
- package/public/assets/Settings-Cc-tYBil.js +1 -0
- package/public/assets/Setup-lGZEk5jq.js +1 -0
- package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-CoGqzxeV.js} +2 -2
- package/public/assets/index-87IJXG-w.css +1 -0
- package/public/assets/index-BZc5zH7u.js +19 -0
- package/public/assets/providers-DtNXh9JD.js +1 -0
- package/public/assets/registry-BWnkJgZ1.js +2 -0
- package/public/assets/{usePolling-Do5Erqm_.js → usePolling-CwwT9KrC.js} +1 -1
- package/public/assets/{vendor-i18n-ucpM0OR0.js → vendor-i18n-y9V7Sfuu.js} +1 -1
- package/public/assets/{vendor-react-Bk1hRGiY.js → vendor-react-BWrEVJVb.js} +6 -6
- package/public/index.html +4 -4
- package/scripts/check-app-spec.mjs +457 -0
- package/scripts/check-i18n.mjs +154 -0
- package/scripts/check-new-file-tests.mjs +230 -0
- package/scripts/check-quarantine-expiry.mjs +105 -0
- package/scripts/perf/README.md +49 -0
- package/scripts/perf/auth.js +99 -0
- package/scripts/perf/config.js +63 -0
- package/scripts/perf/instances.js +143 -0
- package/scripts/perf/proxy.js +96 -0
- package/scripts/run.sh +4 -4
- package/scripts/smoke/files-w1.sh +142 -0
- package/scripts/smoke-backend.mjs +122 -0
- package/scripts/smoke-post-publish.mjs +346 -0
- package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
- package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
- package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
- package/public/assets/NewInstance-BCIrAd86.js +0 -1
- package/public/assets/Settings-xkDcduFz.js +0 -1
- package/public/assets/Setup-Cfuwj4gV.js +0 -1
- package/public/assets/index-CPhVFEsx.css +0 -1
- package/public/assets/index-DQsM6Joa.js +0 -19
- package/public/assets/providers-V-vwrExZ.js +0 -1
- 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
|
-
|
|
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
|
-
|
|
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
|