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,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /api/files routes (M1 W1 PR-4).
|
|
3
|
+
*
|
|
4
|
+
* Mounted as a Fastify plugin so that the route-scoped octet-stream parser
|
|
5
|
+
* does not leak globally. The parser transparently passes the raw payload
|
|
6
|
+
* stream to handlers (no buffering), letting `FilesManager.writeStream`
|
|
7
|
+
* pipe straight to disk.
|
|
8
|
+
*
|
|
9
|
+
* Endpoints:
|
|
10
|
+
* GET /api/files list directory
|
|
11
|
+
* GET /api/files/raw stream file content
|
|
12
|
+
* GET /api/files/preview small text/image read (truncated)
|
|
13
|
+
* GET /api/files/quota quota info
|
|
14
|
+
* PUT /api/files upload (octet-stream only)
|
|
15
|
+
* POST /api/files/mkdir create directory
|
|
16
|
+
* POST /api/files/move rename / move
|
|
17
|
+
* DELETE /api/files soft-delete to .trash/
|
|
18
|
+
* GET /api/files/search full-text search index
|
|
19
|
+
* GET /api/files/meta read per-file sidecar metadata
|
|
20
|
+
* PUT /api/files/meta write/merge per-file sidecar metadata
|
|
21
|
+
* POST /api/files/reindex rebuild search index
|
|
22
|
+
* GET /api/files/resolve relative path → absolute host path
|
|
23
|
+
*
|
|
24
|
+
* Auth: covered by the global onRequest hook in server.ts.
|
|
25
|
+
*/
|
|
26
|
+
import type { FastifyInstance } from "fastify";
|
|
27
|
+
import { FilesManager } from "../services/files-manager.js";
|
|
28
|
+
export interface FilesRoutesDeps {
|
|
29
|
+
filesManager: FilesManager;
|
|
30
|
+
}
|
|
31
|
+
export declare function filesRoutes(app: FastifyInstance, deps: FilesRoutesDeps): Promise<void>;
|
|
@@ -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
|
@@ -16,6 +16,20 @@ export const HOP_BY_HOP = new Set([
|
|
|
16
16
|
"connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
|
|
17
17
|
"te", "trailer", "transfer-encoding", "upgrade",
|
|
18
18
|
]);
|
|
19
|
+
/**
|
|
20
|
+
* Strip the panel session cookie (`jishushell_session`) from a cookie header
|
|
21
|
+
* value, preserving any other cookies for upstream forwarding.
|
|
22
|
+
*/
|
|
23
|
+
function stripPanelSessionCookie(value) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
const cookie = Array.isArray(value) ? value.join("; ") : value;
|
|
27
|
+
const preserved = cookie
|
|
28
|
+
.split(";")
|
|
29
|
+
.map((part) => part.trim())
|
|
30
|
+
.filter((part) => part && !/^jishushell_session=/i.test(part));
|
|
31
|
+
return preserved.length ? preserved.join("; ") : undefined;
|
|
32
|
+
}
|
|
19
33
|
function readHeaderValue(value) {
|
|
20
34
|
const header = Array.isArray(value) ? value[0] : value;
|
|
21
35
|
return typeof header === "string" && header.trim() ? header.trim() : null;
|
|
@@ -269,6 +283,20 @@ function capabilityProxyBootstrap(proxyBasePath) {
|
|
|
269
283
|
"var _lr=location.replace.bind(location);",
|
|
270
284
|
"location.assign=function(u){return _la(rw(u));};",
|
|
271
285
|
"location.replace=function(u){return _lr(rw(u));};",
|
|
286
|
+
// --- frame-busting defense ---
|
|
287
|
+
// Embedded SPAs (e.g. WeKnora) frequently do
|
|
288
|
+
// window.top.location.href = '/login'
|
|
289
|
+
// when they see a 401, intending to log the user out. Inside our
|
|
290
|
+
// capability proxy iframe `top` is the panel's main window — that
|
|
291
|
+
// tears the user away from the instance detail page entirely.
|
|
292
|
+
// Redirect `top`/`parent` to the iframe's own window so the
|
|
293
|
+
// navigation stays inside the embed. Safe because the iframe IS
|
|
294
|
+
// same-origin as the panel (our reverse proxy serves it from the
|
|
295
|
+
// panel's host); cross-origin access would throw and fail closed.
|
|
296
|
+
"try{",
|
|
297
|
+
"Object.defineProperty(window,'top',{configurable:true,get:function(){return window;}});",
|
|
298
|
+
"Object.defineProperty(window,'parent',{configurable:true,get:function(){return window;}});",
|
|
299
|
+
"}catch(_e){}",
|
|
272
300
|
// --- dynamic property assignment: img.src = '/static/...' ---
|
|
273
301
|
"function patchProp(tag,prop){",
|
|
274
302
|
"var d=Object.getOwnPropertyDescriptor(tag.prototype,prop);",
|
|
@@ -507,11 +535,15 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
507
535
|
if (capability.protocol !== "http" && capability.protocol !== "https") {
|
|
508
536
|
return reply.status(400).send({ detail: `Capability '${req.params.capability}' does not use HTTP(S)` });
|
|
509
537
|
}
|
|
510
|
-
|
|
538
|
+
// Resolve the runtime port (handles port reallocation) instead of using
|
|
539
|
+
// the declared AppSpec port which may be stale.
|
|
540
|
+
const runtimePort = instanceManager.resolveRuntimeCapabilityPort(req.params.id, req.params.capability)
|
|
541
|
+
?? capability.port;
|
|
542
|
+
if (typeof runtimePort !== "number" || runtimePort < 1) {
|
|
511
543
|
return reply.status(500).send({ detail: `Capability '${req.params.capability}' has no resolved port` });
|
|
512
544
|
}
|
|
513
|
-
const upstreamHost = await instanceManager.getHostForAppPort(req.params.id,
|
|
514
|
-
const upstreamOrigin = `${capability.protocol}://${instanceManager.urlHost(upstreamHost)}:${
|
|
545
|
+
const upstreamHost = await instanceManager.getHostForAppPort(req.params.id, runtimePort);
|
|
546
|
+
const upstreamOrigin = `${capability.protocol}://${instanceManager.urlHost(upstreamHost)}:${runtimePort}`;
|
|
515
547
|
const wildcardSuffix = typeof req.params["*"] === "string" ? req.params["*"] : "";
|
|
516
548
|
const proxyBasePath = capabilityProxyPath(req.params.id, req.params.capability);
|
|
517
549
|
const querySuffix = req.raw.url?.includes("?") ? req.raw.url.slice(req.raw.url.indexOf("?")) : "";
|
|
@@ -531,6 +563,16 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
531
563
|
if (HOP_BY_HOP.has(normalizedKey) || normalizedKey === "host" || normalizedKey === "content-length" || normalizedKey === "accept-encoding") {
|
|
532
564
|
continue;
|
|
533
565
|
}
|
|
566
|
+
// Strip panel session credentials to avoid leaking them upstream
|
|
567
|
+
// (consistent with the WebSocket capability proxy in server.ts)
|
|
568
|
+
if (normalizedKey === "authorization")
|
|
569
|
+
continue;
|
|
570
|
+
if (normalizedKey === "cookie") {
|
|
571
|
+
const stripped = stripPanelSessionCookie(value);
|
|
572
|
+
if (stripped)
|
|
573
|
+
headers.set(key, stripped);
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
534
576
|
if (Array.isArray(value)) {
|
|
535
577
|
for (const item of value)
|
|
536
578
|
headers.append(key, item);
|
|
@@ -598,6 +640,21 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
598
640
|
if (HOP_BY_HOP.has(normalizedKey) || normalizedKey === "content-length" || normalizedKey === "content-encoding") {
|
|
599
641
|
return;
|
|
600
642
|
}
|
|
643
|
+
// When we rewrite the response body (HTML/CSS/JS), the upstream ETag /
|
|
644
|
+
// Cache-Control values describe the *original* upstream bytes — but the
|
|
645
|
+
// body the browser receives is post-rewrite (proxy-prefixed paths, JS
|
|
646
|
+
// hard-coded redirect targets, etc.). Honoring the upstream cache hints
|
|
647
|
+
// lets the browser pin a stale rewrite indefinitely: e.g. an early
|
|
648
|
+
// visit that pre-dated the JS rewrite gets cached and survives across
|
|
649
|
+
// panel restarts, breaking the auth redirect logic until a hard refresh.
|
|
650
|
+
// Strip cache validators and force revalidation on every load.
|
|
651
|
+
if (willRewriteBody && (normalizedKey === "cache-control" ||
|
|
652
|
+
normalizedKey === "etag" ||
|
|
653
|
+
normalizedKey === "last-modified" ||
|
|
654
|
+
normalizedKey === "expires" ||
|
|
655
|
+
normalizedKey === "pragma")) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
601
658
|
if (willInjectHtml && (normalizedKey === "content-security-policy" ||
|
|
602
659
|
normalizedKey === "content-security-policy-report-only" ||
|
|
603
660
|
normalizedKey === "x-frame-options")) {
|
|
@@ -627,6 +684,10 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
627
684
|
return reply.send();
|
|
628
685
|
}
|
|
629
686
|
if (willRewriteBody) {
|
|
687
|
+
// Pair with the cache-validator strip above.
|
|
688
|
+
reply.header("cache-control", "no-cache, no-store, must-revalidate");
|
|
689
|
+
reply.header("pragma", "no-cache");
|
|
690
|
+
reply.header("expires", "0");
|
|
630
691
|
let extraHeadHtml = "";
|
|
631
692
|
if (req.params.capability === "browserless-debugger") {
|
|
632
693
|
extraHeadHtml = browserlessDebuggerBootstrap(req.params.id);
|
|
@@ -645,11 +706,16 @@ async function proxyProvidedCapability(req, reply) {
|
|
|
645
706
|
// emit Clear-Site-Data so the browser drops the SW + its cache and
|
|
646
707
|
// reloads through the proxy. We mark the success with a long-lived
|
|
647
708
|
// cookie scoped to the proxy path to avoid a reload loop.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
709
|
+
// Gate to HTML only — JS/CSS sub-resources also flow through this branch
|
|
710
|
+
// now that we rewrite JS bundles, and emitting Clear-Site-Data on a JS
|
|
711
|
+
// response would clear storage mid-page-load.
|
|
712
|
+
if (willInjectHtml) {
|
|
713
|
+
const cookieHeader = (req.headers.cookie || "").toString();
|
|
714
|
+
const swCleaned = /(?:^|;\s*)cap_proxy_sw_clean=1(?:;|$)/.test(cookieHeader);
|
|
715
|
+
if (!swCleaned) {
|
|
716
|
+
reply.header("Clear-Site-Data", '"cache", "storage"');
|
|
717
|
+
reply.header("Set-Cookie", `cap_proxy_sw_clean=1; Path=${proxyBasePath}; Max-Age=2592000; SameSite=Lax`);
|
|
718
|
+
}
|
|
653
719
|
}
|
|
654
720
|
const rawBody = await upstream.text();
|
|
655
721
|
req.raw.off("close", onClientClose);
|
|
@@ -1074,10 +1140,19 @@ export async function instanceRoutes(app) {
|
|
|
1074
1140
|
const sudoPassword = typeof req.body?.sudoPassword === "string" && req.body.sudoPassword.trim()
|
|
1075
1141
|
? req.body.sudoPassword
|
|
1076
1142
|
: undefined;
|
|
1077
|
-
|
|
1143
|
+
const result = instanceManager.uninstallAppTask(req.params.id, sudoPassword ? { sudoPassword } : undefined);
|
|
1144
|
+
if (!result.ok) {
|
|
1145
|
+
const code = result.code;
|
|
1146
|
+
const status = code === "TASK_BUSY" ? 409 : code === "APP_NOT_FOUND" ? 404 : 400;
|
|
1147
|
+
return reply.status(status).send({ detail: result.error, code });
|
|
1148
|
+
}
|
|
1078
1149
|
statusCache.delete(req.params.id);
|
|
1079
1150
|
llmProxy.cleanupInstance(req.params.id);
|
|
1080
|
-
return {
|
|
1151
|
+
return reply.status(202).send({
|
|
1152
|
+
ok: true,
|
|
1153
|
+
taskId: result.taskId,
|
|
1154
|
+
...result.reused ? { reused: true } : {},
|
|
1155
|
+
});
|
|
1081
1156
|
}
|
|
1082
1157
|
const svc = await getSvc();
|
|
1083
1158
|
let stopFailed = false;
|
|
@@ -1625,7 +1700,7 @@ export async function instanceRoutes(app) {
|
|
|
1625
1700
|
return { lines: logLines };
|
|
1626
1701
|
});
|
|
1627
1702
|
// Admin: re-encrypt all instance secrets with current AES key
|
|
1628
|
-
app.post("/api/admin/migrate-secrets", async (_req,
|
|
1703
|
+
app.post("/api/admin/migrate-secrets", async (_req, _reply) => {
|
|
1629
1704
|
const { getAesKey, getJwtSecret } = await import("../config.js");
|
|
1630
1705
|
const { scryptSync, createDecipheriv, createCipheriv, randomBytes } = await import("crypto");
|
|
1631
1706
|
const { readFileSync, existsSync: fsExistsSync } = await import("fs");
|
|
@@ -1775,7 +1850,7 @@ export async function instanceRoutes(app) {
|
|
|
1775
1850
|
const consumerAgentType = String(meta?.agentType ?? "");
|
|
1776
1851
|
const consumerIsAgent = consumerAgentType === "hermes" || consumerAgentType === "openclaw";
|
|
1777
1852
|
const requires = (specInfo.spec.requires ?? []).map((r) => {
|
|
1778
|
-
const isCategoryToken = ["llm", "search", "browser", "mcp"].includes(r.capability);
|
|
1853
|
+
const isCategoryToken = ["llm", "search", "browser", "mcp", "files", "knowledge"].includes(r.capability);
|
|
1779
1854
|
const candidates = isCategoryToken
|
|
1780
1855
|
? Object.entries(capabilityRegistry.snapshot().providersByCapability ?? {})
|
|
1781
1856
|
.filter(([cap]) => cap.startsWith(r.capability + "-") || cap === r.capability)
|