jishushell 0.4.30 → 0.5.22
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/Dockerfile.hermes-slim +2 -5
- package/apps/anythingllm-container.yaml +287 -0
- package/apps/browserless-chromium-container.yaml +18 -6
- package/apps/filebrowser-container.yaml +164 -0
- package/apps/ollama-binary.yaml +44 -0
- package/apps/ollama-with-hollama-binary.yaml +45 -1
- package/apps/openclaw-binary.yaml +8 -0
- package/apps/openclaw-container.yaml +9 -1
- package/apps/openclaw-with-searxng-container.yaml +4 -0
- package/apps/searxng-container.yaml +5 -4
- package/apps/weknora-container.yaml +471 -0
- package/dist/cli/doctor.js +144 -16
- package/dist/cli/doctor.js.map +1 -1
- 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/install.js +4 -4
- package/dist/install.js.map +1 -1
- package/dist/routes/auth.js +2 -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 +87 -12
- 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 +29 -0
- package/dist/routes/llm.js.map +1 -1
- package/dist/routes/setup.js +9 -9
- 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.js +358 -6
- package/dist/server.js.map +1 -1
- package/dist/services/agent-apps/catalog.d.ts +3 -0
- package/dist/services/agent-apps/catalog.js +40 -13
- package/dist/services/agent-apps/catalog.js.map +1 -1
- package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
- package/dist/services/agent-apps/installers/shell-script.js +19 -2
- package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
- package/dist/services/agent-apps/types.d.ts +3 -0
- 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 +9 -0
- package/dist/services/app/app-manager.js +248 -43
- 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 +1 -0
- 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 +37 -5
- 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-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 +220 -8
- package/dist/services/backup-manager.js.map +1 -1
- package/dist/services/capability-endpoint-validator.js +26 -7
- package/dist/services/capability-endpoint-validator.js.map +1 -1
- package/dist/services/connection-apply.d.ts +2 -0
- package/dist/services/connection-apply.js +55 -1
- package/dist/services/connection-apply.js.map +1 -1
- package/dist/services/connection-resolver.js +1 -1
- package/dist/services/connection-resolver.js.map +1 -1
- package/dist/services/connection-transactor.d.ts +2 -0
- package/dist/services/connection-transactor.js +12 -2
- package/dist/services/connection-transactor.js.map +1 -1
- 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.js +90 -32
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +28 -0
- package/dist/services/llm-proxy/index.js +76 -3
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/ssrf.js +6 -2
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/llm-proxy/validate-key.d.ts +41 -0
- package/dist/services/llm-proxy/validate-key.js +672 -0
- package/dist/services/llm-proxy/validate-key.js.map +1 -0
- package/dist/services/macos-launchd.d.ts +89 -0
- package/dist/services/macos-launchd.js +273 -0
- package/dist/services/macos-launchd.js.map +1 -0
- package/dist/services/nomad-manager.d.ts +11 -0
- package/dist/services/nomad-manager.js +343 -98
- 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 +40 -11
- package/dist/services/panel-manager.js.map +1 -1
- package/dist/services/process-manager.js +3 -2
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/runtime/adapters/custom.js +56 -0
- package/dist/services/runtime/adapters/custom.js.map +1 -1
- package/dist/services/runtime/adapters/hermes.d.ts +4 -3
- package/dist/services/runtime/adapters/hermes.js +166 -64
- package/dist/services/runtime/adapters/hermes.js.map +1 -1
- 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 +118 -0
- package/dist/services/runtime/adapters/openclaw.js +1459 -49
- 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/types.d.ts +31 -0
- package/dist/services/setup-manager.js +190 -68
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/suggestions.js.map +1 -1
- package/dist/services/update-manager.js +32 -14
- package/dist/services/update-manager.js.map +1 -1
- 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 +61 -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-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 +247 -35
- package/install/jishu-uninstall.sh +45 -5
- package/package.json +20 -2
- package/public/assets/ApiKeyField-CvyAOcJS.js +1 -0
- package/public/assets/Dashboard-AuJESBlJ.js +1 -0
- package/public/assets/{HermesChatPanel-_GHoklgo.js → HermesChatPanel-CByPREwb.js} +1 -1
- package/public/assets/HermesConfigForm-DRda8FKX.js +4 -0
- package/public/assets/InitPassword-ka4wNpM5.js +1 -0
- package/public/assets/InstanceDetail-Cg1nS8HX.js +92 -0
- package/public/assets/Login-aPajuQzf.js +1 -0
- package/public/assets/NewInstance-Dd1ebNIx.js +1 -0
- package/public/assets/ProviderRecommendations-DFmADQ7V.js +1 -0
- package/public/assets/Settings-BYQnbLYL.js +1 -0
- package/public/assets/Setup-D05lwDOV.js +1 -0
- package/public/assets/WeixinLoginPanel-D89kdhP4.js +9 -0
- package/public/assets/index-HSXCsceK.css +1 -0
- package/public/assets/index-bnBu0nlQ.js +19 -0
- package/public/assets/registry-C_qeFTkZ.js +2 -0
- package/public/assets/usePolling-Bn93fe7M.js +1 -0
- package/public/assets/{vendor-i18n-ucpM0OR0.js → vendor-i18n-flxcMVeP.js} +2 -2
- package/public/assets/{vendor-react-Bk1hRGiY.js → vendor-react-ZC5T_huj.js} +7 -7
- package/public/index.html +4 -4
- package/scripts/check-app-spec.mjs +18 -4
- package/scripts/check-colima-launchd.mjs +230 -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/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-rkWp-CXd.js +0 -1
- package/public/assets/HermesConfigForm-anDnwUp_.js +0 -4
- package/public/assets/InitPassword-ZU9_-hDr.js +0 -1
- package/public/assets/InstanceDetail-CN0FH1aw.js +0 -92
- package/public/assets/Login-BItXqYAJ.js +0 -1
- package/public/assets/NewInstance-BousE6kY.js +0 -1
- package/public/assets/ProviderRecommendations-DFYj7Fb6.js +0 -1
- package/public/assets/Settings-Bttc6QmM.js +0 -1
- package/public/assets/Setup-Bsxx1zgj.js +0 -1
- package/public/assets/WeixinLoginPanel-DPZpAKgO.js +0 -9
- package/public/assets/index-8xZy1z5k.css +0 -1
- package/public/assets/index-Dw3HhUYE.js +0 -19
- package/public/assets/input-paste-CrNVAyOy.js +0 -1
- package/public/assets/providers-DtNXh9JD.js +0 -1
- package/public/assets/registry-5s2UB6is.js +0 -2
- package/public/assets/usePolling-Do5Erqm_.js +0 -1
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drive MCP shim — exposes the panel's file + organize API to the
|
|
3
|
+
* agent inside the OpenClaw container (M1 W1.6).
|
|
4
|
+
*
|
|
5
|
+
* Why a shim, not a direct MCP server in panel:
|
|
6
|
+
* - Panel and agent run in different processes (and often different
|
|
7
|
+
* containers). Stdio MCP is the cheapest contract — the container
|
|
8
|
+
* already runs node, and `mcporter.json` already wires shim
|
|
9
|
+
* stdio servers via `command/args`.
|
|
10
|
+
* - The shim translates JSON-RPC tool calls into HTTP calls back
|
|
11
|
+
* to the panel via `host.docker.internal:8090`, authenticated
|
|
12
|
+
* with the per-host internal token (`X-Jishushell-Internal-Token`)
|
|
13
|
+
* plus the per-instance id (`X-Jishushell-Instance`).
|
|
14
|
+
*
|
|
15
|
+
* Tool surface (matches panel REST one-to-one):
|
|
16
|
+
* drive_list → GET /api/files
|
|
17
|
+
* drive_read_preview → GET /api/files/preview
|
|
18
|
+
* drive_quota → GET /api/files/quota
|
|
19
|
+
* drive_mkdir → POST /api/files/mkdir
|
|
20
|
+
* drive_organize_scan → POST /api/files/organize/scan
|
|
21
|
+
* drive_organize_apply → PUT /api/files/organize/batches/:id/apply
|
|
22
|
+
* drive_organize_revert → POST /api/files/organize/batches/:id/revert
|
|
23
|
+
* drive_search → GET /api/files/search
|
|
24
|
+
* drive_get_meta → GET /api/files/meta
|
|
25
|
+
* drive_set_meta → PUT /api/files/meta
|
|
26
|
+
* drive_reindex → POST /api/files/reindex
|
|
27
|
+
* drive_resolve_local_path → GET /api/files/resolve (v0.4 — IM-plugin bridge)
|
|
28
|
+
* drive_write_binary → PUT /api/files (v0.4 — base64 body)
|
|
29
|
+
*
|
|
30
|
+
* The user-facing chat experience this enables:
|
|
31
|
+
* user: "整理一下 inbox 里的发票"
|
|
32
|
+
* agent → drive_organize_scan({ path: "inbox" })
|
|
33
|
+
* agent → drive_organize_apply({ batch_id, selected_ids: [...] })
|
|
34
|
+
* agent: "✅ 整理了 8 张发票到 finance/2026/05/, 30 秒内可撤销"
|
|
35
|
+
*
|
|
36
|
+
* Stdio framing: NDJSON JSON-RPC 2.0 (MCP standard). Pure Node
|
|
37
|
+
* builtins — no npm install needed inside the runtime image.
|
|
38
|
+
*/
|
|
39
|
+
export const DRIVE_MCP_SHIM_VERSION = "0.4.0";
|
|
40
|
+
export const DRIVE_MCP_SHIM_SOURCE = `#!/usr/bin/env node
|
|
41
|
+
// jishushell-drive-shim — generated, do not edit by hand.
|
|
42
|
+
// Source: src/services/runtime/mcp-shims/drive-shim.ts
|
|
43
|
+
import { createInterface } from "node:readline";
|
|
44
|
+
|
|
45
|
+
// Per-instance values are baked in by the adapter at install time via
|
|
46
|
+
// substituteDriveShimPlaceholders(). OpenClaw scrubs env when spawning MCP
|
|
47
|
+
// subprocesses, so the env-based fallback is unreliable in production —
|
|
48
|
+
// the literal substitution path below is the source of truth. Env vars
|
|
49
|
+
// are kept as a backstop for hand-running the shim during development.
|
|
50
|
+
const PANEL_URL_LIT = "__JS_PANEL_URL__";
|
|
51
|
+
const TOKEN_LIT = "__JS_TOKEN__";
|
|
52
|
+
const INSTANCE_ID_LIT = "__JS_INSTANCE_ID__";
|
|
53
|
+
const PANEL_URL = (PANEL_URL_LIT.startsWith("__JS_") ? (process.env.JISHUSHELL_PANEL_URL || "http://host.docker.internal:8090") : PANEL_URL_LIT).replace(/\\/+$/, "");
|
|
54
|
+
const TOKEN = TOKEN_LIT.startsWith("__JS_") ? (process.env.JISHUSHELL_INTERNAL_TOKEN || "") : TOKEN_LIT;
|
|
55
|
+
const INSTANCE_ID = INSTANCE_ID_LIT.startsWith("__JS_") ? (process.env.JISHUSHELL_INSTANCE_ID || "") : INSTANCE_ID_LIT;
|
|
56
|
+
|
|
57
|
+
if (!TOKEN || !INSTANCE_ID) {
|
|
58
|
+
process.stderr.write("[drive-shim] missing JISHUSHELL_INTERNAL_TOKEN or JISHUSHELL_INSTANCE_ID env\\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function authedHeaders(extra) {
|
|
62
|
+
return Object.assign({
|
|
63
|
+
"X-Jishushell-Internal-Token": TOKEN,
|
|
64
|
+
"X-Jishushell-Instance": INSTANCE_ID,
|
|
65
|
+
}, extra || {});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function panelGet(path, query) {
|
|
69
|
+
const url = new URL(PANEL_URL + path);
|
|
70
|
+
for (const [k, v] of Object.entries(query || {})) {
|
|
71
|
+
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
|
|
72
|
+
}
|
|
73
|
+
const res = await fetch(url, { headers: authedHeaders() });
|
|
74
|
+
const body = await res.text();
|
|
75
|
+
let parsed;
|
|
76
|
+
try { parsed = body ? JSON.parse(body) : null; } catch { parsed = body; }
|
|
77
|
+
return { status: res.status, body: parsed };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function panelSend(method, path, query, jsonBody) {
|
|
81
|
+
const url = new URL(PANEL_URL + path);
|
|
82
|
+
for (const [k, v] of Object.entries(query || {})) {
|
|
83
|
+
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
|
|
84
|
+
}
|
|
85
|
+
const res = await fetch(url, {
|
|
86
|
+
method,
|
|
87
|
+
headers: authedHeaders({ "Content-Type": "application/json" }),
|
|
88
|
+
body: jsonBody !== undefined ? JSON.stringify(jsonBody) : undefined,
|
|
89
|
+
});
|
|
90
|
+
const body = await res.text();
|
|
91
|
+
let parsed;
|
|
92
|
+
try { parsed = body ? JSON.parse(body) : null; } catch { parsed = body; }
|
|
93
|
+
return { status: res.status, body: parsed };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function panelPutBinary(path, query, buf) {
|
|
97
|
+
const url = new URL(PANEL_URL + path);
|
|
98
|
+
for (const [k, v] of Object.entries(query || {})) {
|
|
99
|
+
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
|
|
100
|
+
}
|
|
101
|
+
const res = await fetch(url, {
|
|
102
|
+
method: "PUT",
|
|
103
|
+
headers: authedHeaders({
|
|
104
|
+
"Content-Type": "application/octet-stream",
|
|
105
|
+
"Content-Length": String(buf.length),
|
|
106
|
+
}),
|
|
107
|
+
body: buf,
|
|
108
|
+
});
|
|
109
|
+
const body = await res.text();
|
|
110
|
+
let parsed;
|
|
111
|
+
try { parsed = body ? JSON.parse(body) : null; } catch { parsed = body; }
|
|
112
|
+
return { status: res.status, body: parsed };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const TOOLS = [
|
|
116
|
+
{
|
|
117
|
+
name: "drive_list",
|
|
118
|
+
description:
|
|
119
|
+
"List a directory in the user's NAS (~/.jishushell/files/). Returns entries with name, is_dir, size, mtime, mime, etag. These paths are also valid host filesystem paths inside your container — call drive_resolve_local_path to get an absolute path you can hand to native IM channel send_file tools (Feishu / WeChat / ...).",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: "object",
|
|
122
|
+
properties: {
|
|
123
|
+
path: { type: "string", description: "directory path relative to files/ root; \\"\\" for root" },
|
|
124
|
+
show_hidden: { type: "boolean" },
|
|
125
|
+
},
|
|
126
|
+
required: [],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "drive_read_preview",
|
|
131
|
+
description:
|
|
132
|
+
"Read a small text/JSON file preview (up to max_kb KB, default 256). Returns content + truncated flag.",
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
path: { type: "string" },
|
|
137
|
+
max_kb: { type: "integer", description: "max kilobytes to read; default 256" },
|
|
138
|
+
},
|
|
139
|
+
required: ["path"],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "drive_quota",
|
|
144
|
+
description: "Get NAS storage quota: quota_mb, used_mb, available_mb, trash_mb.",
|
|
145
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "drive_mkdir",
|
|
149
|
+
description: "Create a new directory in the user's NAS.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
type: "object",
|
|
152
|
+
properties: { path: { type: "string" } },
|
|
153
|
+
required: ["path"],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "drive_organize_scan",
|
|
158
|
+
description:
|
|
159
|
+
"Scan a directory and propose organize moves based on user-defined rules. Returns batch.id and suggestions[].",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
path: { type: "string", description: "subdirectory to scan; default scans the entire root" },
|
|
164
|
+
},
|
|
165
|
+
required: [],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: "drive_organize_apply",
|
|
170
|
+
description:
|
|
171
|
+
"Apply previously-scanned organize suggestions. selected_ids defaults to all suggestions in the batch. Returns report.applied/skipped/failed and an undo window of 30 seconds.",
|
|
172
|
+
inputSchema: {
|
|
173
|
+
type: "object",
|
|
174
|
+
properties: {
|
|
175
|
+
batch_id: { type: "string" },
|
|
176
|
+
selected_ids: { type: "array", items: { type: "string" } },
|
|
177
|
+
},
|
|
178
|
+
required: ["batch_id"],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "drive_organize_revert",
|
|
183
|
+
description:
|
|
184
|
+
"Revert an applied organize batch. Only valid within the 30-second undo window; returns 410 expired afterwards.",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: { batch_id: { type: "string" } },
|
|
188
|
+
required: ["batch_id"],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "drive_read_full",
|
|
193
|
+
description:
|
|
194
|
+
"Read a TEXT file in full (up to 1MB). Use for summarizing PDFs/notes/code; for binary files this returns a hint to use the file URL instead. Always check the truncated flag.",
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: "object",
|
|
197
|
+
properties: {
|
|
198
|
+
path: { type: "string" },
|
|
199
|
+
max_kb: {
|
|
200
|
+
type: "integer",
|
|
201
|
+
description: "max kilobytes (default 1024 = 1MB; hard ceiling 4096)",
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
required: ["path"],
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "drive_write_text",
|
|
209
|
+
description:
|
|
210
|
+
"Write a text file (overwrites if it exists). Use this when the agent generates a report / summary / draft and wants to persist it. Path is files/-relative; agents typically write under agent-data/{instance}/. To then SEND the file via an IM channel, call drive_resolve_local_path on the same path and pass the abs_path to the channel plugin's send_file tool.",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
path: { type: "string" },
|
|
215
|
+
content: { type: "string" },
|
|
216
|
+
},
|
|
217
|
+
required: ["path", "content"],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "drive_move",
|
|
222
|
+
description:
|
|
223
|
+
"Move or rename a single file/directory. Both paths are files/-relative. Default does NOT overwrite — pass overwrite=true to replace.",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {
|
|
227
|
+
from: { type: "string" },
|
|
228
|
+
to: { type: "string" },
|
|
229
|
+
overwrite: { type: "boolean" },
|
|
230
|
+
},
|
|
231
|
+
required: ["from", "to"],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "drive_delete",
|
|
236
|
+
description:
|
|
237
|
+
"Soft-delete a file/directory (moves it to .trash/{date}/, recoverable for 30 days). Files-relative path; do not include leading slash.",
|
|
238
|
+
inputSchema: {
|
|
239
|
+
type: "object",
|
|
240
|
+
properties: { path: { type: "string" } },
|
|
241
|
+
required: ["path"],
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "drive_search",
|
|
246
|
+
description: "Search indexed file content via FTS5; returns relative paths + snippets + sha256.",
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
query: { type: "string", description: "FTS5 query string (supports operators: \\"AND\\", \\"OR\\", quoted phrases)." },
|
|
251
|
+
limit: { type: "number", description: "Max hits (default 20, max 100).", minimum: 1, maximum: 100 },
|
|
252
|
+
path: { type: "string", description: "Optional relative-path prefix to scope the search." },
|
|
253
|
+
},
|
|
254
|
+
required: ["query"],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "drive_get_meta",
|
|
259
|
+
description: "Read the sidecar metadata (tags/summary/links/notes) for a file by sha256 or by path. Returns null if no sidecar exists yet.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
sha256: { type: "string", pattern: "^[a-fA-F0-9]{64}$", description: "64-hex content hash." },
|
|
264
|
+
path: { type: "string", description: "Relative path under the user file root." },
|
|
265
|
+
},
|
|
266
|
+
oneOf: [{ required: ["sha256"] }, { required: ["path"] }],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "drive_set_meta",
|
|
271
|
+
description: "Write or merge sidecar metadata for a file. payload is free-form JSON; merge=true (default) shallow-merges with the existing sidecar.",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
sha256: { type: "string", pattern: "^[a-fA-F0-9]{64}$" },
|
|
276
|
+
payload: { type: "object", description: "Sidecar fields to write. Schema: { tags?: string[], summary?: string, links?: [{path, rel}], agent_notes?: string, ... }." },
|
|
277
|
+
merge: { type: "boolean", description: "When true (default), shallow-merge with existing sidecar; when false, replace." },
|
|
278
|
+
},
|
|
279
|
+
required: ["sha256", "payload"],
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "drive_reindex",
|
|
284
|
+
description: "Rescan the file tree (or a sub-path) and rebuild FTS5 entries for text/markdown/json/yaml/code files. Explicit trigger only.",
|
|
285
|
+
inputSchema: {
|
|
286
|
+
type: "object",
|
|
287
|
+
properties: {
|
|
288
|
+
path: { type: "string", description: "Optional relative prefix to limit the rescan." },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
name: "drive_resolve_local_path",
|
|
294
|
+
description:
|
|
295
|
+
"Resolve a files/-relative path to its ABSOLUTE host filesystem path. Use this whenever you need to hand a path to a native IM channel plugin (Feishu / WeChat / Telegram / ... send_file tool) — those plugins read bytes from the local filesystem, not via HTTP. The returned abs_path is valid inside your container (panel binds host==container for files mounts) and on the host (raw_exec / process service managers run the agent natively on the host). Returns { abs_path, exists, is_dir?, size?, mtime?, external_mount?, external_mount_mode? }. Check exists before sending; check external_mount_mode='ro' before trying to overwrite.",
|
|
296
|
+
inputSchema: {
|
|
297
|
+
type: "object",
|
|
298
|
+
properties: {
|
|
299
|
+
path: { type: "string", description: "files/-relative path, e.g. \\"reports/q4.pdf\\" or \\"agent-data/<instance>/outbox/summary.pdf\\"." },
|
|
300
|
+
},
|
|
301
|
+
required: ["path"],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "drive_write_binary",
|
|
306
|
+
description:
|
|
307
|
+
"Write a binary file (overwrites if it exists). Use this when the agent generates a PDF / image / audio / archive that needs to be persisted or handed to an IM channel for sending. content_base64 is standard base64 of the file bytes (no data: URL prefix). Path is files/-relative; agents typically write under agent-data/{instance}/outbox/ so the file is clearly outbound. After writing, call drive_resolve_local_path on the same path to get the absolute path to pass to the IM channel's send_file tool. Soft cap: 10 MB raw (~13.5 MB base64) per call.",
|
|
308
|
+
inputSchema: {
|
|
309
|
+
type: "object",
|
|
310
|
+
properties: {
|
|
311
|
+
path: { type: "string" },
|
|
312
|
+
content_base64: { type: "string", description: "Standard base64 (RFC 4648). No data: URL prefix." },
|
|
313
|
+
},
|
|
314
|
+
required: ["path", "content_base64"],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
async function callTool(name, args) {
|
|
320
|
+
switch (name) {
|
|
321
|
+
case "drive_list": {
|
|
322
|
+
const r = await panelGet("/api/files", { path: args.path || "", show_hidden: args.show_hidden });
|
|
323
|
+
return r;
|
|
324
|
+
}
|
|
325
|
+
case "drive_read_preview": {
|
|
326
|
+
const r = await panelGet("/api/files/preview", { path: args.path, max_kb: args.max_kb });
|
|
327
|
+
return r;
|
|
328
|
+
}
|
|
329
|
+
case "drive_quota": {
|
|
330
|
+
return panelGet("/api/files/quota");
|
|
331
|
+
}
|
|
332
|
+
case "drive_mkdir": {
|
|
333
|
+
return panelSend("POST", "/api/files/mkdir", null, { path: args.path });
|
|
334
|
+
}
|
|
335
|
+
case "drive_organize_scan": {
|
|
336
|
+
return panelSend("POST", "/api/files/organize/scan", null, { path: args.path || "" });
|
|
337
|
+
}
|
|
338
|
+
case "drive_organize_apply": {
|
|
339
|
+
// If selected_ids is missing or empty, fetch the batch and apply ALL suggestions.
|
|
340
|
+
let selected = Array.isArray(args.selected_ids) ? args.selected_ids : null;
|
|
341
|
+
if (!selected || selected.length === 0) {
|
|
342
|
+
const got = await panelGet("/api/files/organize/batches/" + encodeURIComponent(args.batch_id));
|
|
343
|
+
if (got.status === 200 && got.body && Array.isArray(got.body.suggestions)) {
|
|
344
|
+
selected = got.body.suggestions.map((s) => s.id);
|
|
345
|
+
} else {
|
|
346
|
+
return got;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return panelSend("PUT", "/api/files/organize/batches/" + encodeURIComponent(args.batch_id) + "/apply", null, { selected_ids: selected });
|
|
350
|
+
}
|
|
351
|
+
case "drive_organize_revert": {
|
|
352
|
+
return panelSend("POST", "/api/files/organize/batches/" + encodeURIComponent(args.batch_id) + "/revert", null);
|
|
353
|
+
}
|
|
354
|
+
case "drive_read_full": {
|
|
355
|
+
const ceiling = 4096;
|
|
356
|
+
const wanted = Math.min(Number.isFinite(+args.max_kb) ? +args.max_kb : 1024, ceiling);
|
|
357
|
+
return panelGet("/api/files/preview", { path: args.path, max_kb: wanted });
|
|
358
|
+
}
|
|
359
|
+
case "drive_write_text": {
|
|
360
|
+
const text = String(args.content == null ? "" : args.content);
|
|
361
|
+
const buf = Buffer.from(text, "utf8");
|
|
362
|
+
return panelPutBinary("/api/files", { path: args.path, overwrite: "true", client_mime: "text/plain" }, buf);
|
|
363
|
+
}
|
|
364
|
+
case "drive_move": {
|
|
365
|
+
const overwrite = args.overwrite === true;
|
|
366
|
+
return panelSend("POST", "/api/files/move", null, {
|
|
367
|
+
from: args.from,
|
|
368
|
+
to: args.to,
|
|
369
|
+
overwrite,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
case "drive_delete": {
|
|
373
|
+
return panelSend("DELETE", "/api/files", { path: args.path }, undefined);
|
|
374
|
+
}
|
|
375
|
+
case "drive_search": {
|
|
376
|
+
const qs = new URLSearchParams({ q: args.query });
|
|
377
|
+
if (args.limit != null) qs.set("limit", String(args.limit));
|
|
378
|
+
if (args.path) qs.set("path", args.path);
|
|
379
|
+
return panelGet("/api/files/search?" + qs.toString(), null);
|
|
380
|
+
}
|
|
381
|
+
case "drive_get_meta": {
|
|
382
|
+
const qs = new URLSearchParams();
|
|
383
|
+
if (args.sha256) qs.set("sha", args.sha256);
|
|
384
|
+
else if (args.path) qs.set("path", args.path);
|
|
385
|
+
return panelGet("/api/files/meta?" + qs.toString(), null);
|
|
386
|
+
}
|
|
387
|
+
case "drive_set_meta": {
|
|
388
|
+
const qs = new URLSearchParams({ sha: args.sha256 });
|
|
389
|
+
if (args.merge === false) qs.set("merge", "false");
|
|
390
|
+
return panelSend("PUT", "/api/files/meta?" + qs.toString(), null, args.payload);
|
|
391
|
+
}
|
|
392
|
+
case "drive_reindex": {
|
|
393
|
+
const qs = args.path ? "?path=" + encodeURIComponent(args.path) : "";
|
|
394
|
+
return panelSend("POST", "/api/files/reindex" + qs, null, undefined);
|
|
395
|
+
}
|
|
396
|
+
case "drive_resolve_local_path": {
|
|
397
|
+
return panelGet("/api/files/resolve", { path: args.path });
|
|
398
|
+
}
|
|
399
|
+
case "drive_write_binary": {
|
|
400
|
+
const b64 = String(args.content_base64 == null ? "" : args.content_base64);
|
|
401
|
+
if (!b64) {
|
|
402
|
+
return { status: 400, body: { detail: "content_base64 required" } };
|
|
403
|
+
}
|
|
404
|
+
let buf;
|
|
405
|
+
try { buf = Buffer.from(b64, "base64"); } catch (e) {
|
|
406
|
+
return { status: 400, body: { detail: "content_base64 not valid base64" } };
|
|
407
|
+
}
|
|
408
|
+
// Soft cap so MCP stdio framing (NDJSON) stays healthy. Bigger files
|
|
409
|
+
// should be staged via Filebrowser / WebDAV directly into files/.
|
|
410
|
+
if (buf.length > 10 * 1024 * 1024) {
|
|
411
|
+
return { status: 413, body: { detail: "content_base64 exceeds 10MB soft cap; split into smaller writes or stage the file via Filebrowser/WebDAV" } };
|
|
412
|
+
}
|
|
413
|
+
return panelPutBinary("/api/files", { path: args.path, overwrite: "true" }, buf);
|
|
414
|
+
}
|
|
415
|
+
default:
|
|
416
|
+
throw new Error("unknown tool: " + name);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function send(msg) {
|
|
421
|
+
process.stdout.write(JSON.stringify(msg) + "\\n");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function ok(id, result) { send({ jsonrpc: "2.0", id, result }); }
|
|
425
|
+
function err(id, code, message, data) {
|
|
426
|
+
send({ jsonrpc: "2.0", id, error: { code, message, data } });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function dispatch(msg) {
|
|
430
|
+
if (msg.method === "initialize") {
|
|
431
|
+
return ok(msg.id, {
|
|
432
|
+
protocolVersion: "2024-11-05",
|
|
433
|
+
capabilities: { tools: {} },
|
|
434
|
+
serverInfo: { name: "jishushell-drive-shim", version: ${JSON.stringify(DRIVE_MCP_SHIM_VERSION)} },
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if (msg.method === "tools/list") {
|
|
438
|
+
return ok(msg.id, { tools: TOOLS });
|
|
439
|
+
}
|
|
440
|
+
if (msg.method === "tools/call") {
|
|
441
|
+
const name = msg.params && msg.params.name;
|
|
442
|
+
const args = (msg.params && msg.params.arguments) || {};
|
|
443
|
+
try {
|
|
444
|
+
const r = await callTool(name, args);
|
|
445
|
+
const text = r.status >= 200 && r.status < 300
|
|
446
|
+
? "ok " + r.status + ": " + JSON.stringify(r.body)
|
|
447
|
+
: "error " + r.status + ": " + JSON.stringify(r.body);
|
|
448
|
+
return ok(msg.id, {
|
|
449
|
+
content: [{ type: "text", text: text }],
|
|
450
|
+
isError: !(r.status >= 200 && r.status < 300),
|
|
451
|
+
});
|
|
452
|
+
} catch (e) {
|
|
453
|
+
return err(msg.id, -32000, "tool execution failed", { error: String(e && e.message || e) });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (msg.method === "ping") return ok(msg.id, {});
|
|
457
|
+
if (msg.method && msg.method.startsWith("notifications/")) return; // no reply
|
|
458
|
+
if (msg.id !== undefined) err(msg.id, -32601, "method not found: " + msg.method);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
462
|
+
rl.on("line", (line) => {
|
|
463
|
+
const trimmed = line.trim();
|
|
464
|
+
if (!trimmed) return;
|
|
465
|
+
let msg;
|
|
466
|
+
try { msg = JSON.parse(trimmed); } catch { return; }
|
|
467
|
+
Promise.resolve(dispatch(msg)).catch((e) => {
|
|
468
|
+
process.stderr.write("[drive-shim] dispatch crashed: " + (e && e.stack || e) + "\\n");
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
process.stdin.on("end", () => process.exit(0));
|
|
472
|
+
`;
|
|
473
|
+
/**
|
|
474
|
+
* Bake per-instance values into the shim template. Returns the source to
|
|
475
|
+
* write to disk. Must be called before writing because OpenClaw scrubs
|
|
476
|
+
* env when spawning MCP subprocesses, so the env-based fallback in the
|
|
477
|
+
* template is unreliable in production.
|
|
478
|
+
*
|
|
479
|
+
* Placeholders use non-JS-identifier marker `__JS_*__` so they grep cleanly
|
|
480
|
+
* and never collide with real code paths.
|
|
481
|
+
*/
|
|
482
|
+
export function substituteDriveShimPlaceholders(params) {
|
|
483
|
+
const escapeStr = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
484
|
+
return DRIVE_MCP_SHIM_SOURCE
|
|
485
|
+
.replace(/__JS_PANEL_URL__/g, escapeStr(params.panelUrl))
|
|
486
|
+
.replace(/__JS_TOKEN__/g, escapeStr(params.token))
|
|
487
|
+
.replace(/__JS_INSTANCE_ID__/g, escapeStr(params.instanceId));
|
|
488
|
+
}
|
|
489
|
+
//# sourceMappingURL=drive-shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drive-shim.js","sourceRoot":"","sources":["../../../../src/services/runtime/mcp-shims/drive-shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAE9C,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8DA0YyB,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCnG,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,+BAA+B,CAC7C,MAA+D;IAE/D,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/E,OAAO,qBAAqB;SACzB,OAAO,CAAC,mBAAmB,EAAE,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;SACxD,OAAO,CAAC,eAAe,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;SACjD,OAAO,CAAC,qBAAqB,EAAE,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -55,6 +55,37 @@ export interface RuntimeSpec {
|
|
|
55
55
|
ports?: RuntimePortSpec[];
|
|
56
56
|
volumes?: RuntimeVolumeSpec[];
|
|
57
57
|
health?: RuntimeHealthSpec | null;
|
|
58
|
+
/**
|
|
59
|
+
* Per-instance mounts of subtrees from `~/.jishushell/files/`.
|
|
60
|
+
*
|
|
61
|
+
* Each entry exposes one user file-tree subdirectory inside the
|
|
62
|
+
* container under a stable alias, optionally read-only.
|
|
63
|
+
*
|
|
64
|
+
* - Docker mode: enforced by Linux mount options (ro is real)
|
|
65
|
+
* - raw_exec / process mode: configuration only — the agent runs as
|
|
66
|
+
* the panel user and can ignore the ro flag (see vision G8)
|
|
67
|
+
*
|
|
68
|
+
* The structural defaults and built-in `agent-data/{id}` mount are
|
|
69
|
+
* applied by `services/instance-manager.ts` at create time.
|
|
70
|
+
*/
|
|
71
|
+
fileMounts?: FileMount[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* One subtree of `~/.jishushell/files/` exposed to a single instance.
|
|
75
|
+
*
|
|
76
|
+
* - `path` is files/-relative; validated by path-safety
|
|
77
|
+
* - `mode` is the mount mode (ro/rw)
|
|
78
|
+
* - `alias` controls how the mount appears inside the container's
|
|
79
|
+
* workspace:
|
|
80
|
+
* - non-empty string → workspace/{alias} symlink to this subtree
|
|
81
|
+
* - "" or null → workspace IS this subtree (root mount;
|
|
82
|
+
* exclusive — at most one per instance)
|
|
83
|
+
* - undefined → defaults to basename(path)
|
|
84
|
+
*/
|
|
85
|
+
export interface FileMount {
|
|
86
|
+
path: string;
|
|
87
|
+
mode: "ro" | "rw";
|
|
88
|
+
alias?: string | null;
|
|
58
89
|
}
|
|
59
90
|
export interface CapabilityProfile {
|
|
60
91
|
gateway: {
|