jishushell 0.4.30 → 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/apps/anythingllm-container.yaml +287 -0
- package/apps/browserless-chromium-container.yaml +18 -6
- package/apps/filebrowser-container.yaml +163 -0
- 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/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 +3 -3
- 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 +45 -7
- 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/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 +341 -3
- package/dist/server.js.map +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 +1 -0
- package/dist/services/app/app-manager.js +172 -41
- 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 +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-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/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 +1 -23
- package/dist/services/instance-manager.js.map +1 -1
- 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/nomad-manager.d.ts +4 -0
- package/dist/services/nomad-manager.js +53 -19
- 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 +3 -2
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/runtime/adapters/hermes.js +1 -1
- 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 +90 -0
- package/dist/services/runtime/adapters/openclaw.js +957 -45
- 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 +93 -18
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/suggestions.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 +60 -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/package.json +16 -1
- package/public/assets/Dashboard-BdWPtroF.js +1 -0
- package/public/assets/{HermesChatPanel-_GHoklgo.js → HermesChatPanel-B_2HlVBQ.js} +1 -1
- package/public/assets/{HermesConfigForm-anDnwUp_.js → HermesConfigForm-DVlhg3WV.js} +2 -2
- package/public/assets/{InitPassword-ZU9_-hDr.js → InitPassword-D7glTExX.js} +1 -1
- package/public/assets/InstanceDetail-CxSy2cpe.js +92 -0
- package/public/assets/{Login-BItXqYAJ.js → Login-Cfr5c2sv.js} +1 -1
- package/public/assets/NewInstance-BIYDmJis.js +1 -0
- package/public/assets/{ProviderRecommendations-DFYj7Fb6.js → ProviderRecommendations-BuRnvRcI.js} +1 -1
- package/public/assets/{Settings-Bttc6QmM.js → Settings-Cc-tYBil.js} +1 -1
- package/public/assets/{Setup-Bsxx1zgj.js → Setup-lGZEk5jq.js} +1 -1
- package/public/assets/{WeixinLoginPanel-DPZpAKgO.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/{registry-5s2UB6is.js → registry-BWnkJgZ1.js} +2 -2
- 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 +18 -4
- 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/InstanceDetail-CN0FH1aw.js +0 -92
- package/public/assets/NewInstance-BousE6kY.js +0 -1
- package/public/assets/index-8xZy1z5k.css +0 -1
- package/public/assets/index-Dw3HhUYE.js +0 -19
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { FilesError, } from "../services/files-manager.js";
|
|
2
|
+
const MAX_UPLOAD_BYTES = 50 * 1024 * 1024; // matches FilesManager default
|
|
3
|
+
export async function filesRoutes(app, deps) {
|
|
4
|
+
const { filesManager } = deps;
|
|
5
|
+
// ── Route-scoped parser ───────────────────────────
|
|
6
|
+
// Pass-through: hand the raw payload stream straight to the handler.
|
|
7
|
+
// Do NOT use parseAs:'buffer' — that would read the whole body into memory.
|
|
8
|
+
app.addContentTypeParser("application/octet-stream", (_req, payload, done) => done(null, payload));
|
|
9
|
+
// W2 PR-3: /api/files/* is the panel-internal substrate for the drive_*
|
|
10
|
+
// MCP shim only. Browser users now manage files via the Filebrowser app
|
|
11
|
+
// mounted at /apps/filebrowser/*. Reject any request that didn't pass
|
|
12
|
+
// the X-Jishushell-Internal-Token check in server.ts auth middleware.
|
|
13
|
+
// Return 404 rather than 401 so unauthenticated probes can't fingerprint
|
|
14
|
+
// the surface.
|
|
15
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
16
|
+
if (!request.internalCallerInstance) {
|
|
17
|
+
return reply.status(404).send();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
// ── GET /api/files (list) ─────────────────────────
|
|
21
|
+
app.get("/api/files", async (req, reply) => {
|
|
22
|
+
const path = decodeOrEmpty(req.query.path);
|
|
23
|
+
const showHidden = req.query.show_hidden === "true";
|
|
24
|
+
try {
|
|
25
|
+
const result = await filesManager.list(path, { showHidden });
|
|
26
|
+
reply.send(result);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
sendFilesError(reply, e);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// ── GET /api/files/raw ────────────────────────────
|
|
33
|
+
app.get("/api/files/raw", async (req, reply) => {
|
|
34
|
+
const path = decodeOrEmpty(req.query.path);
|
|
35
|
+
try {
|
|
36
|
+
// readStream returns a lazy stream factory — no fd opened yet
|
|
37
|
+
const r = await filesManager.readStream(path);
|
|
38
|
+
// Conditional GET (weak ETag) — answer without opening the file
|
|
39
|
+
const ifNoneMatch = req.headers["if-none-match"];
|
|
40
|
+
if (typeof ifNoneMatch === "string" && weakETagMatch(ifNoneMatch, r.etag)) {
|
|
41
|
+
reply.code(304);
|
|
42
|
+
reply.header("ETag", r.etag);
|
|
43
|
+
return reply.send();
|
|
44
|
+
}
|
|
45
|
+
reply.header("Content-Type", r.mime);
|
|
46
|
+
reply.header("Content-Length", String(r.size));
|
|
47
|
+
reply.header("ETag", r.etag);
|
|
48
|
+
reply.header("Last-Modified", new Date(r.mtime * 1000).toUTCString());
|
|
49
|
+
reply.header("Cache-Control", "private, max-age=0");
|
|
50
|
+
reply.header("Content-Disposition", `inline; filename*=UTF-8''${encodeRfc5987(basename(path))}`);
|
|
51
|
+
// Open the fd only now, just before piping
|
|
52
|
+
return reply.send(r.openStream());
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
sendFilesError(reply, e);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
// ── GET /api/files/preview ────────────────────────
|
|
59
|
+
app.get("/api/files/preview", async (req, reply) => {
|
|
60
|
+
const path = decodeOrEmpty(req.query.path);
|
|
61
|
+
const maxKb = parsePositiveInt(req.query.max_kb, 256) ?? 256;
|
|
62
|
+
const maxBytes = maxKb * 1024;
|
|
63
|
+
try {
|
|
64
|
+
const r = await filesManager.readSmall(path, maxBytes);
|
|
65
|
+
// Response is a JSON envelope; the underlying file's mime is in the body.
|
|
66
|
+
reply.header("ETag", r.etag);
|
|
67
|
+
reply.send({
|
|
68
|
+
path,
|
|
69
|
+
mime: r.mime,
|
|
70
|
+
size: r.size,
|
|
71
|
+
truncated: r.truncated,
|
|
72
|
+
content: r.buf.toString("utf8"),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
sendFilesError(reply, e);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// ── GET /api/files/quota ──────────────────────────
|
|
80
|
+
app.get("/api/files/quota", async (_req, reply) => {
|
|
81
|
+
try {
|
|
82
|
+
const q = await filesManager.quota();
|
|
83
|
+
reply.send(q);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
sendFilesError(reply, e);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// ── PUT /api/files (upload) ───────────────────────
|
|
90
|
+
app.put("/api/files", { bodyLimit: MAX_UPLOAD_BYTES }, async (req, reply) => {
|
|
91
|
+
// Reject non-octet-stream up front for a clean 415
|
|
92
|
+
const ct = (req.headers["content-type"] || "")
|
|
93
|
+
.split(";")[0]
|
|
94
|
+
.trim()
|
|
95
|
+
.toLowerCase();
|
|
96
|
+
if (ct !== "application/octet-stream") {
|
|
97
|
+
return reply
|
|
98
|
+
.code(415)
|
|
99
|
+
.send({
|
|
100
|
+
error: "Content-Type must be application/octet-stream",
|
|
101
|
+
code: "unsupported-media-type",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const path = decodeOrEmpty(req.query.path);
|
|
105
|
+
const overwrite = req.query.overwrite === "true";
|
|
106
|
+
const ifMatch = req.headers["if-match"];
|
|
107
|
+
// Content-Length is mandatory: chunked uploads (no length) would let
|
|
108
|
+
// a client stream gigabytes before we hit the in-flight size guard,
|
|
109
|
+
// and the resulting "write past limit then soft-delete" path is
|
|
110
|
+
// dangerous when disk is near-full. Reject up front per spec.
|
|
111
|
+
const cl = req.headers["content-length"];
|
|
112
|
+
const clNum = typeof cl === "string" ? Number.parseInt(cl, 10) : Number.NaN;
|
|
113
|
+
if (!Number.isFinite(clNum) || clNum < 0) {
|
|
114
|
+
return reply.code(411).send({
|
|
115
|
+
error: "Content-Length header is required",
|
|
116
|
+
code: "length-required",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (clNum > MAX_UPLOAD_BYTES) {
|
|
120
|
+
return reply.code(413).send({
|
|
121
|
+
error: `Content-Length ${clNum} exceeds max ${MAX_UPLOAD_BYTES}`,
|
|
122
|
+
code: "too-large",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// The route-scoped parser passes payload as the request body (Readable)
|
|
126
|
+
const body = req.body;
|
|
127
|
+
if (!body) {
|
|
128
|
+
return reply
|
|
129
|
+
.code(400)
|
|
130
|
+
.send({ error: "missing request body", code: "no-body" });
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const result = await filesManager.writeStream(path, body, {
|
|
134
|
+
overwrite,
|
|
135
|
+
ifMatch: typeof ifMatch === "string" ? ifMatch : undefined,
|
|
136
|
+
expectedSize: clNum,
|
|
137
|
+
});
|
|
138
|
+
reply.code(overwrite ? 200 : 201).send({
|
|
139
|
+
path,
|
|
140
|
+
etag: result.etag,
|
|
141
|
+
size: result.size,
|
|
142
|
+
mime: result.mime,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
sendFilesError(reply, e);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
// ── POST /api/files/mkdir ─────────────────────────
|
|
150
|
+
app.post("/api/files/mkdir", async (req, reply) => {
|
|
151
|
+
const p = req.body?.path ?? "";
|
|
152
|
+
try {
|
|
153
|
+
await filesManager.mkdir(p);
|
|
154
|
+
reply.code(201).send({ path: p });
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
sendFilesError(reply, e);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
// ── POST /api/files/move ──────────────────────────
|
|
161
|
+
app.post("/api/files/move", async (req, reply) => {
|
|
162
|
+
const from = req.body?.from ?? "";
|
|
163
|
+
const to = req.body?.to ?? "";
|
|
164
|
+
const overwrite = req.body?.overwrite === true;
|
|
165
|
+
try {
|
|
166
|
+
await filesManager.move(from, to, overwrite);
|
|
167
|
+
reply.send({ from, to });
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
sendFilesError(reply, e);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// ── DELETE /api/files ─────────────────────────────
|
|
174
|
+
app.delete("/api/files", async (req, reply) => {
|
|
175
|
+
const path = decodeOrEmpty(req.query.path);
|
|
176
|
+
try {
|
|
177
|
+
await filesManager.remove(path);
|
|
178
|
+
reply.code(204).send();
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
sendFilesError(reply, e);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
// ── GET /api/files/search ─────────────────────────
|
|
185
|
+
app.get("/api/files/search", async (req, reply) => {
|
|
186
|
+
const q = (req.query.q ?? "").trim();
|
|
187
|
+
if (!q) {
|
|
188
|
+
return reply.code(400).send({ detail: "q required" });
|
|
189
|
+
}
|
|
190
|
+
const rawLimit = parsePositiveInt(req.query.limit, 20) ?? 20;
|
|
191
|
+
const limit = Math.min(Math.max(rawLimit, 1), 100);
|
|
192
|
+
const pathPrefix = decodeOrEmpty(req.query.path) || undefined;
|
|
193
|
+
try {
|
|
194
|
+
const hits = await filesManager.searchIndex(q, { limit, pathPrefix });
|
|
195
|
+
reply.send({ hits });
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
sendFilesError(reply, e);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
// ── GET /api/files/meta ───────────────────────────
|
|
202
|
+
app.get("/api/files/meta", async (req, reply) => {
|
|
203
|
+
const { sha, path } = req.query;
|
|
204
|
+
if (sha && path) {
|
|
205
|
+
return reply.code(400).send({ detail: "provide sha or path, not both" });
|
|
206
|
+
}
|
|
207
|
+
if (!sha && !path) {
|
|
208
|
+
return reply.code(400).send({ detail: "sha or path required" });
|
|
209
|
+
}
|
|
210
|
+
if (sha && !/^[a-f0-9]{64}$/i.test(sha)) {
|
|
211
|
+
return reply.code(400).send({ detail: "sha must be a 64-char hex string" });
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const meta = await filesManager.getMeta({
|
|
215
|
+
sha256: sha,
|
|
216
|
+
path: path ? decodeOrEmpty(path) : undefined,
|
|
217
|
+
});
|
|
218
|
+
reply.send(meta);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
sendFilesError(reply, e);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
// ── PUT /api/files/meta ───────────────────────────
|
|
225
|
+
// 96KB body cap: Agent A enforces 64KB on the serialized result;
|
|
226
|
+
// the slack covers merged result growth.
|
|
227
|
+
app.put("/api/files/meta", { bodyLimit: 96 * 1024 }, async (req, reply) => {
|
|
228
|
+
const { sha } = req.query;
|
|
229
|
+
if (!sha) {
|
|
230
|
+
return reply.code(400).send({ detail: "sha required" });
|
|
231
|
+
}
|
|
232
|
+
if (!/^[a-f0-9]{64}$/i.test(sha)) {
|
|
233
|
+
return reply.code(400).send({ detail: "sha must be a 64-char hex string" });
|
|
234
|
+
}
|
|
235
|
+
const merge = (req.query.merge ?? "").toLowerCase() !== "false";
|
|
236
|
+
const body = req.body;
|
|
237
|
+
if (body === null ||
|
|
238
|
+
body === undefined ||
|
|
239
|
+
typeof body !== "object" ||
|
|
240
|
+
Array.isArray(body)) {
|
|
241
|
+
return reply.code(400).send({ detail: "body must be a JSON object" });
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const result = await filesManager.setMeta(sha, body, { merge });
|
|
245
|
+
reply.send(result);
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
if (e instanceof FilesError && e.httpStatus === 413) {
|
|
249
|
+
return reply.code(413).send({ error: e.message, code: e.code });
|
|
250
|
+
}
|
|
251
|
+
sendFilesError(reply, e);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
// ── POST /api/files/reindex ───────────────────────
|
|
255
|
+
// Potentially long-running — no timeout; let it finish.
|
|
256
|
+
app.post("/api/files/reindex", async (req, reply) => {
|
|
257
|
+
const pathPrefix = decodeOrEmpty(req.query.path) || undefined;
|
|
258
|
+
try {
|
|
259
|
+
const result = await filesManager.reindex({ pathPrefix });
|
|
260
|
+
reply.send(result);
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
sendFilesError(reply, e);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
// ── GET /api/files/resolve ────────────────────────
|
|
267
|
+
// Returns the absolute host filesystem path for a files/-relative path,
|
|
268
|
+
// plus existence + stat. Used by the drive MCP shim so agents can hand
|
|
269
|
+
// a real filesystem path to IM channel plugins (Feishu / WeChat / ...)
|
|
270
|
+
// that send files. Host==container by buildVolumes(), so the same
|
|
271
|
+
// string is valid inside the OpenClaw container.
|
|
272
|
+
app.get("/api/files/resolve", async (req, reply) => {
|
|
273
|
+
const rel = decodeOrEmpty(req.query.path);
|
|
274
|
+
try {
|
|
275
|
+
const info = await filesManager.resolveLocalPath(rel);
|
|
276
|
+
reply.send(info);
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
sendFilesError(reply, e);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
// ── Helpers ──────────────────────────────────────────
|
|
284
|
+
function decodeOrEmpty(input) {
|
|
285
|
+
if (!input)
|
|
286
|
+
return "";
|
|
287
|
+
// Fastify already URL-decodes query strings; this is just a defensive type guard.
|
|
288
|
+
return input;
|
|
289
|
+
}
|
|
290
|
+
function parsePositiveInt(s, def) {
|
|
291
|
+
if (s === undefined || s === null)
|
|
292
|
+
return def;
|
|
293
|
+
const n = Number.parseInt(s, 10);
|
|
294
|
+
if (Number.isFinite(n) && n >= 0)
|
|
295
|
+
return n;
|
|
296
|
+
return def;
|
|
297
|
+
}
|
|
298
|
+
function basename(p) {
|
|
299
|
+
if (!p)
|
|
300
|
+
return "";
|
|
301
|
+
const idx = p.lastIndexOf("/");
|
|
302
|
+
return idx === -1 ? p : p.slice(idx + 1);
|
|
303
|
+
}
|
|
304
|
+
function encodeRfc5987(s) {
|
|
305
|
+
// RFC 5987 encoding for filename* parameter
|
|
306
|
+
return encodeURIComponent(s).replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
307
|
+
}
|
|
308
|
+
function weakETagMatch(provided, current) {
|
|
309
|
+
const strip = (x) => x.replace(/^W\//, "");
|
|
310
|
+
return strip(provided) === strip(current);
|
|
311
|
+
}
|
|
312
|
+
function sendFilesError(reply, e) {
|
|
313
|
+
if (e instanceof FilesError) {
|
|
314
|
+
reply.code(e.httpStatus).send({ error: e.message, code: e.code });
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const err = e;
|
|
318
|
+
console.error("[files-routes] unexpected:", err.message);
|
|
319
|
+
reply.code(500).send({ error: "internal error", code: err.code ?? "internal" });
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/instances.js
CHANGED
|
@@ -269,6 +269,20 @@ function capabilityProxyBootstrap(proxyBasePath) {
|
|
|
269
269
|
"var _lr=location.replace.bind(location);",
|
|
270
270
|
"location.assign=function(u){return _la(rw(u));};",
|
|
271
271
|
"location.replace=function(u){return _lr(rw(u));};",
|
|
272
|
+
// --- frame-busting defense ---
|
|
273
|
+
// Embedded SPAs (e.g. WeKnora) frequently do
|
|
274
|
+
// window.top.location.href = '/login'
|
|
275
|
+
// when they see a 401, intending to log the user out. Inside our
|
|
276
|
+
// capability proxy iframe `top` is the panel's main window — that
|
|
277
|
+
// tears the user away from the instance detail page entirely.
|
|
278
|
+
// Redirect `top`/`parent` to the iframe's own window so the
|
|
279
|
+
// navigation stays inside the embed. Safe because the iframe IS
|
|
280
|
+
// same-origin as the panel (our reverse proxy serves it from the
|
|
281
|
+
// panel's host); cross-origin access would throw and fail closed.
|
|
282
|
+
"try{",
|
|
283
|
+
"Object.defineProperty(window,'top',{configurable:true,get:function(){return window;}});",
|
|
284
|
+
"Object.defineProperty(window,'parent',{configurable:true,get:function(){return window;}});",
|
|
285
|
+
"}catch(_e){}",
|
|
272
286
|
// --- dynamic property assignment: img.src = '/static/...' ---
|
|
273
287
|
"function patchProp(tag,prop){",
|
|
274
288
|
"var d=Object.getOwnPropertyDescriptor(tag.prototype,prop);",
|
|
@@ -598,6 +612,21 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
598
612
|
if (HOP_BY_HOP.has(normalizedKey) || normalizedKey === "content-length" || normalizedKey === "content-encoding") {
|
|
599
613
|
return;
|
|
600
614
|
}
|
|
615
|
+
// When we rewrite the response body (HTML/CSS/JS), the upstream ETag /
|
|
616
|
+
// Cache-Control values describe the *original* upstream bytes — but the
|
|
617
|
+
// body the browser receives is post-rewrite (proxy-prefixed paths, JS
|
|
618
|
+
// hard-coded redirect targets, etc.). Honoring the upstream cache hints
|
|
619
|
+
// lets the browser pin a stale rewrite indefinitely: e.g. an early
|
|
620
|
+
// visit that pre-dated the JS rewrite gets cached and survives across
|
|
621
|
+
// panel restarts, breaking the auth redirect logic until a hard refresh.
|
|
622
|
+
// Strip cache validators and force revalidation on every load.
|
|
623
|
+
if (willRewriteBody && (normalizedKey === "cache-control" ||
|
|
624
|
+
normalizedKey === "etag" ||
|
|
625
|
+
normalizedKey === "last-modified" ||
|
|
626
|
+
normalizedKey === "expires" ||
|
|
627
|
+
normalizedKey === "pragma")) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
601
630
|
if (willInjectHtml && (normalizedKey === "content-security-policy" ||
|
|
602
631
|
normalizedKey === "content-security-policy-report-only" ||
|
|
603
632
|
normalizedKey === "x-frame-options")) {
|
|
@@ -627,6 +656,10 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
627
656
|
return reply.send();
|
|
628
657
|
}
|
|
629
658
|
if (willRewriteBody) {
|
|
659
|
+
// Pair with the cache-validator strip above.
|
|
660
|
+
reply.header("cache-control", "no-cache, no-store, must-revalidate");
|
|
661
|
+
reply.header("pragma", "no-cache");
|
|
662
|
+
reply.header("expires", "0");
|
|
630
663
|
let extraHeadHtml = "";
|
|
631
664
|
if (req.params.capability === "browserless-debugger") {
|
|
632
665
|
extraHeadHtml = browserlessDebuggerBootstrap(req.params.id);
|
|
@@ -645,11 +678,16 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
645
678
|
// emit Clear-Site-Data so the browser drops the SW + its cache and
|
|
646
679
|
// reloads through the proxy. We mark the success with a long-lived
|
|
647
680
|
// cookie scoped to the proxy path to avoid a reload loop.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
681
|
+
// Gate to HTML only — JS/CSS sub-resources also flow through this branch
|
|
682
|
+
// now that we rewrite JS bundles, and emitting Clear-Site-Data on a JS
|
|
683
|
+
// response would clear storage mid-page-load.
|
|
684
|
+
if (willInjectHtml) {
|
|
685
|
+
const cookieHeader = (req.headers.cookie || "").toString();
|
|
686
|
+
const swCleaned = /(?:^|;\s*)cap_proxy_sw_clean=1(?:;|$)/.test(cookieHeader);
|
|
687
|
+
if (!swCleaned) {
|
|
688
|
+
reply.header("Clear-Site-Data", '"cache", "storage"');
|
|
689
|
+
reply.header("Set-Cookie", `cap_proxy_sw_clean=1; Path=${proxyBasePath}; Max-Age=2592000; SameSite=Lax`);
|
|
690
|
+
}
|
|
653
691
|
}
|
|
654
692
|
const rawBody = await upstream.text();
|
|
655
693
|
req.raw.off("close", onClientClose);
|
|
@@ -1625,7 +1663,7 @@ export async function instanceRoutes(app) {
|
|
|
1625
1663
|
return { lines: logLines };
|
|
1626
1664
|
});
|
|
1627
1665
|
// Admin: re-encrypt all instance secrets with current AES key
|
|
1628
|
-
app.post("/api/admin/migrate-secrets", async (_req,
|
|
1666
|
+
app.post("/api/admin/migrate-secrets", async (_req, _reply) => {
|
|
1629
1667
|
const { getAesKey, getJwtSecret } = await import("../config.js");
|
|
1630
1668
|
const { scryptSync, createDecipheriv, createCipheriv, randomBytes } = await import("crypto");
|
|
1631
1669
|
const { readFileSync, existsSync: fsExistsSync } = await import("fs");
|
|
@@ -1775,7 +1813,7 @@ export async function instanceRoutes(app) {
|
|
|
1775
1813
|
const consumerAgentType = String(meta?.agentType ?? "");
|
|
1776
1814
|
const consumerIsAgent = consumerAgentType === "hermes" || consumerAgentType === "openclaw";
|
|
1777
1815
|
const requires = (specInfo.spec.requires ?? []).map((r) => {
|
|
1778
|
-
const isCategoryToken = ["llm", "search", "browser", "mcp"].includes(r.capability);
|
|
1816
|
+
const isCategoryToken = ["llm", "search", "browser", "mcp", "files", "knowledge"].includes(r.capability);
|
|
1779
1817
|
const candidates = isCategoryToken
|
|
1780
1818
|
? Object.entries(capabilityRegistry.snapshot().providersByCapability ?? {})
|
|
1781
1819
|
.filter(([cap]) => cap.startsWith(r.capability + "-") || cap === r.capability)
|