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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/routes/system.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/routes/system.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /webdav/* routes (M3 W4 PR-3).
|
|
3
|
+
*
|
|
4
|
+
* Mounted as a Fastify plugin so the route-scoped octet-stream parser
|
|
5
|
+
* does not leak. Authentication runs as an onRequest hook upstream of
|
|
6
|
+
* the handlers — every request must carry Basic Auth resolving to a
|
|
7
|
+
* non-revoked app password.
|
|
8
|
+
*
|
|
9
|
+
* The mount path is fixed at /webdav (no instance-specific suffix);
|
|
10
|
+
* the root maps to ~/.jishushell/files/.
|
|
11
|
+
*/
|
|
12
|
+
import type { FastifyInstance } from "fastify";
|
|
13
|
+
import { FilesManager } from "../services/files-manager.js";
|
|
14
|
+
export interface WebdavRoutesDeps {
|
|
15
|
+
filesManager: FilesManager;
|
|
16
|
+
}
|
|
17
|
+
export declare function webdavRoutes(app: FastifyInstance, deps: WebdavRoutesDeps): Promise<void>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { parseBasicAuth, verifyAppPassword, } from "../services/app-passwords.js";
|
|
2
|
+
import { handleOptions, handlePropfind, handleGet, handlePut, handleDelete, handleMkcol, handleMove, handleCopy, } from "../services/webdav/server.js";
|
|
3
|
+
const MOUNT_PREFIX = "/webdav";
|
|
4
|
+
export async function webdavRoutes(app, deps) {
|
|
5
|
+
// ── Register WebDAV-specific HTTP methods ──────
|
|
6
|
+
// Fastify 5 supports any method via addHttpMethod. PROPFIND has a
|
|
7
|
+
// body (XML); MKCOL/MOVE/COPY don't.
|
|
8
|
+
app.addHttpMethod("PROPFIND", { hasBody: true });
|
|
9
|
+
app.addHttpMethod("MKCOL", { hasBody: false });
|
|
10
|
+
app.addHttpMethod("MOVE", { hasBody: false });
|
|
11
|
+
app.addHttpMethod("COPY", { hasBody: false });
|
|
12
|
+
app.addHttpMethod("PROPPATCH", { hasBody: true });
|
|
13
|
+
app.addHttpMethod("LOCK", { hasBody: true });
|
|
14
|
+
app.addHttpMethod("UNLOCK", { hasBody: false });
|
|
15
|
+
app.addHttpMethod("REPORT", { hasBody: true });
|
|
16
|
+
// Pass-through parser for octet-stream PUT bodies (avoid bufferization)
|
|
17
|
+
app.addContentTypeParser("application/octet-stream", (_req, payload, done) => done(null, payload));
|
|
18
|
+
// PROPFIND clients send XML — accept it as a stream too; we don't
|
|
19
|
+
// actually parse the request XML (Depth header is enough for the
|
|
20
|
+
// properties we serve), so just consume and ignore.
|
|
21
|
+
app.addContentTypeParser(["application/xml", "text/xml"], (_req, payload, done) => done(null, payload));
|
|
22
|
+
// ── Authentication: Basic + app password ───────
|
|
23
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
24
|
+
if (!req.url.startsWith(MOUNT_PREFIX))
|
|
25
|
+
return;
|
|
26
|
+
const auth = parseBasicAuth(req.headers.authorization);
|
|
27
|
+
if (!auth) {
|
|
28
|
+
reply
|
|
29
|
+
.code(401)
|
|
30
|
+
.header("WWW-Authenticate", 'Basic realm="JishuShell WebDAV"')
|
|
31
|
+
.send("authentication required");
|
|
32
|
+
return reply;
|
|
33
|
+
}
|
|
34
|
+
const [username, password] = auth;
|
|
35
|
+
const record = await verifyAppPassword(username, password);
|
|
36
|
+
if (!record) {
|
|
37
|
+
reply
|
|
38
|
+
.code(401)
|
|
39
|
+
.header("WWW-Authenticate", 'Basic realm="JishuShell WebDAV"')
|
|
40
|
+
.send("invalid credentials");
|
|
41
|
+
return reply;
|
|
42
|
+
}
|
|
43
|
+
req.appPassword = record;
|
|
44
|
+
});
|
|
45
|
+
// ── Method dispatcher ──────────────────────────
|
|
46
|
+
const dispatcher = async (req, reply) => {
|
|
47
|
+
const ctx = {
|
|
48
|
+
filesManager: deps.filesManager,
|
|
49
|
+
mountPrefix: MOUNT_PREFIX,
|
|
50
|
+
authedAs: req.appPassword,
|
|
51
|
+
};
|
|
52
|
+
const method = req.method.toUpperCase();
|
|
53
|
+
switch (method) {
|
|
54
|
+
case "OPTIONS":
|
|
55
|
+
return handleOptions(ctx, req, reply);
|
|
56
|
+
case "PROPFIND":
|
|
57
|
+
return handlePropfind(ctx, req, reply);
|
|
58
|
+
case "GET":
|
|
59
|
+
return handleGet(ctx, req, reply, false);
|
|
60
|
+
case "HEAD":
|
|
61
|
+
return handleGet(ctx, req, reply, true);
|
|
62
|
+
case "PUT":
|
|
63
|
+
return handlePut(ctx, req, reply);
|
|
64
|
+
case "DELETE":
|
|
65
|
+
return handleDelete(ctx, req, reply);
|
|
66
|
+
case "MKCOL":
|
|
67
|
+
return handleMkcol(ctx, req, reply);
|
|
68
|
+
case "MOVE":
|
|
69
|
+
return handleMove(ctx, req, reply);
|
|
70
|
+
case "COPY":
|
|
71
|
+
return handleCopy(ctx, req, reply);
|
|
72
|
+
case "PROPPATCH":
|
|
73
|
+
case "LOCK":
|
|
74
|
+
case "UNLOCK":
|
|
75
|
+
case "REPORT":
|
|
76
|
+
// Not implemented — return 501 so clients fall back gracefully
|
|
77
|
+
reply.code(501).send(`${method} not supported`);
|
|
78
|
+
return;
|
|
79
|
+
default:
|
|
80
|
+
reply.code(405).header("Allow", "OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MOVE, COPY").send();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const ALL_METHODS = [
|
|
85
|
+
"OPTIONS",
|
|
86
|
+
"GET",
|
|
87
|
+
"HEAD",
|
|
88
|
+
"PUT",
|
|
89
|
+
"DELETE",
|
|
90
|
+
"PROPFIND",
|
|
91
|
+
"MKCOL",
|
|
92
|
+
"MOVE",
|
|
93
|
+
"COPY",
|
|
94
|
+
"PROPPATCH",
|
|
95
|
+
"LOCK",
|
|
96
|
+
"UNLOCK",
|
|
97
|
+
"REPORT",
|
|
98
|
+
];
|
|
99
|
+
// 50MB body limit matches /api/files. WebDAV PUT on bigger files
|
|
100
|
+
// requires a separate "chunked upload" extension we'll add later.
|
|
101
|
+
app.route({
|
|
102
|
+
method: ALL_METHODS,
|
|
103
|
+
url: MOUNT_PREFIX,
|
|
104
|
+
bodyLimit: 50 * 1024 * 1024,
|
|
105
|
+
handler: dispatcher,
|
|
106
|
+
});
|
|
107
|
+
app.route({
|
|
108
|
+
method: ALL_METHODS,
|
|
109
|
+
url: `${MOUNT_PREFIX}/*`,
|
|
110
|
+
bodyLimit: 50 * 1024 * 1024,
|
|
111
|
+
handler: dispatcher,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=webdav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webdav.js","sourceRoot":"","sources":["../../src/routes/webdav.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -8,7 +8,8 @@ import { request as httpRequest } from "http";
|
|
|
8
8
|
import { request as httpsRequest } from "https";
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = dirname(__filename);
|
|
11
|
-
import { ensureDirs, ensurePanelConfig, getPanelConfig, migrateCatalogSchemaIfNeeded, migrateHermesShimOutIfNeeded, migrateOpenclawCatalogIfNeeded, migrateOpenclawImageTagIfNeeded, } from "./config.js";
|
|
11
|
+
import { ensureDirs, ensurePanelConfig, getPanelConfig, migrateCatalogSchemaIfNeeded, migrateHermesShimOutIfNeeded, migrateOpenclawCatalogIfNeeded, migrateOpenclawImageTagIfNeeded, getInternalMcpToken, } from "./config.js";
|
|
12
|
+
import { timingSafeEqual } from "node:crypto";
|
|
12
13
|
import { runStartupMigrations } from "./services/runtime/index.js";
|
|
13
14
|
import { verifyToken } from "./auth.js";
|
|
14
15
|
import { authRoutes } from "./routes/auth.js";
|
|
@@ -19,6 +20,13 @@ import { runtimeCatalogRoutes } from "./routes/runtime.js";
|
|
|
19
20
|
import { appRoutes } from "./routes/apps.js";
|
|
20
21
|
import { agentAppRoutes } from "./routes/agent-apps.js";
|
|
21
22
|
import { llmRoutes } from "./routes/llm.js";
|
|
23
|
+
import { filesRoutes } from "./routes/files.js";
|
|
24
|
+
import { internalRoutes } from "./routes/internal.js";
|
|
25
|
+
import { organizeRoutes } from "./routes/files-organize.js";
|
|
26
|
+
import { fileMountsRoutes } from "./routes/file-mounts.js";
|
|
27
|
+
import { webdavRoutes } from "./routes/webdav.js";
|
|
28
|
+
import { externalMountsRoutes } from "./routes/external-mounts.js";
|
|
29
|
+
import { FilesManager } from "./services/files-manager.js";
|
|
22
30
|
import * as appManager from "./services/app/app-manager.js";
|
|
23
31
|
import * as capabilityRegistry from "./services/capability-registry.js";
|
|
24
32
|
import backupRoutes from "./routes/backup.js";
|
|
@@ -31,6 +39,28 @@ import { checkUpdate } from "./services/panel-manager.js";
|
|
|
31
39
|
import { PROXY_IDENTITY_HEADERS } from "./constants.js";
|
|
32
40
|
import { supportsGatewayWebsocket } from "./services/runtime/instance.js";
|
|
33
41
|
const PUBLIC_PATHS = new Set(["/api/auth/status", "/api/auth/init", "/api/auth/login", "/api/setup/status", "/api/apps/validate-sudo-password"]);
|
|
42
|
+
/** Slug charset for /apps/:slug — same as instance ids. */
|
|
43
|
+
const SLUG_RE = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
44
|
+
/** Regex for top-level app WebSocket upgrades (AI-FS v1 W2.5 PR-8). */
|
|
45
|
+
const APP_SLUG_WS_RE = /^\/apps\/([a-z0-9][a-z0-9-]{0,62})(\/.*)?$/;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the upstream host/port for a top-level /apps/:slug request using
|
|
48
|
+
* the capability registry. Resolution order:
|
|
49
|
+
* 1. `<slug>-ui` — canonical convention (e.g. filebrowser-ui)
|
|
50
|
+
* 2. `web-<slug>` — web-* convention (e.g. web-filebrowser)
|
|
51
|
+
* Returns the first entry with status === "running", or null if none found.
|
|
52
|
+
*/
|
|
53
|
+
function resolveAppSlugUpstream(slug) {
|
|
54
|
+
const candidates = [`${slug}-ui`, `web-${slug}`];
|
|
55
|
+
for (const cap of candidates) {
|
|
56
|
+
const providers = capabilityRegistry.listProviders(cap);
|
|
57
|
+
const running = providers.find((p) => p.status === "running");
|
|
58
|
+
if (running) {
|
|
59
|
+
return { host: running.host, port: running.hostPort };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
34
64
|
function hasValidPanelUpgradeToken(req) {
|
|
35
65
|
const authHeader = req.headers.authorization || "";
|
|
36
66
|
const match = authHeader.match(/^Bearer\s+(\S+)$/);
|
|
@@ -123,7 +153,7 @@ export async function createServer(options = {}) {
|
|
|
123
153
|
bodyLimit: 1048576,
|
|
124
154
|
});
|
|
125
155
|
// Allow empty JSON body for POST requests
|
|
126
|
-
app.addContentTypeParser("application/json", { parseAs: "string" }, (
|
|
156
|
+
app.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
127
157
|
if (!body || body.length === 0) {
|
|
128
158
|
done(null, {});
|
|
129
159
|
}
|
|
@@ -137,9 +167,49 @@ export async function createServer(options = {}) {
|
|
|
137
167
|
}
|
|
138
168
|
});
|
|
139
169
|
// Parse text/plain and application/x-yaml as raw string (for app install endpoint)
|
|
140
|
-
app.addContentTypeParser(["text/plain", "application/x-yaml"], { parseAs: "string" }, (
|
|
170
|
+
app.addContentTypeParser(["text/plain", "application/x-yaml"], { parseAs: "string" }, (_req, body, done) => {
|
|
141
171
|
done(null, body);
|
|
142
172
|
});
|
|
173
|
+
// Fallback: pass any other content type through as the raw stream. Without
|
|
174
|
+
// this, /apps/:slug/* (W2.5 PR-8) would reject POST/PUT uploads with 415
|
|
175
|
+
// because Fastify's default behavior is to reject unparseable bodies.
|
|
176
|
+
// Specific routes that need typed bodies (octet-stream upload, etc.)
|
|
177
|
+
// register their own narrower parsers — those win over this catch-all.
|
|
178
|
+
app.addContentTypeParser("*", (_req, payload, done) => {
|
|
179
|
+
done(null, payload);
|
|
180
|
+
});
|
|
181
|
+
// Eager-init the internal MCP token so the file exists on disk before
|
|
182
|
+
// any agent container starts and tries to read it via env injection.
|
|
183
|
+
// Lazy-init would only fire on the first request that carries an
|
|
184
|
+
// X-Jishushell-Internal-Token header, which is too late for the
|
|
185
|
+
// adapter onBeforeStart that mounts the value into the container.
|
|
186
|
+
try {
|
|
187
|
+
getInternalMcpToken();
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
console.warn(`[server] internal MCP token init skipped: ${e?.message ?? e}`);
|
|
191
|
+
}
|
|
192
|
+
// ── Auth middleware for /apps/* (AI-FS v1 W2.5 PR-8) ──────────────────
|
|
193
|
+
// Top-level reverse proxy for installed apps. Added in AI-FS v1 W2.5 PR-8.
|
|
194
|
+
// Pairs with `/api/instances/:id/provides/:cap/*` which is instance-scoped.
|
|
195
|
+
// This hook is scoped to /apps/* only so it does not double-fire with the
|
|
196
|
+
// existing /api/* hook below.
|
|
197
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
198
|
+
const path = request.url.split("?")[0];
|
|
199
|
+
if (!path.startsWith("/apps/"))
|
|
200
|
+
return;
|
|
201
|
+
const authHeader = request.headers.authorization || "";
|
|
202
|
+
const match = authHeader.match(/^Bearer\s+(\S+)$/);
|
|
203
|
+
let token = match?.[1] || "";
|
|
204
|
+
if (!token) {
|
|
205
|
+
const cookie = request.headers.cookie || "";
|
|
206
|
+
const cookieMatch = cookie.match(/(?:^|;\s*)jishushell_session=([^;]*)/);
|
|
207
|
+
token = cookieMatch?.[1] || "";
|
|
208
|
+
}
|
|
209
|
+
if (!token || token.length > 4096 || !verifyToken(token)) {
|
|
210
|
+
return reply.status(401).send({ detail: "Unauthorized" });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
143
213
|
// Auth middleware
|
|
144
214
|
app.addHook("onRequest", async (request, reply) => {
|
|
145
215
|
const path = request.url.split("?")[0];
|
|
@@ -154,6 +224,35 @@ export async function createServer(options = {}) {
|
|
|
154
224
|
}
|
|
155
225
|
return;
|
|
156
226
|
}
|
|
227
|
+
// Internal token path — used by:
|
|
228
|
+
// (a) MCP shims spawned inside agent containers
|
|
229
|
+
// (need BOTH X-Jishushell-Internal-Token + X-Jishushell-Instance)
|
|
230
|
+
// (b) Panel app lifecycle scripts (post_start, etc.) calling back into
|
|
231
|
+
// /api/internal/* on the same host
|
|
232
|
+
// (need ONLY X-Jishushell-Internal-Token; no instance binding —
|
|
233
|
+
// apps aren't instances)
|
|
234
|
+
// Combined with the standard JWT path below; we never require both.
|
|
235
|
+
const internalToken = request.headers["x-jishushell-internal-token"];
|
|
236
|
+
if (typeof internalToken === "string" && internalToken.length >= 32) {
|
|
237
|
+
const expected = getInternalMcpToken();
|
|
238
|
+
if (internalToken.length === expected.length &&
|
|
239
|
+
timingSafeEqual(Buffer.from(internalToken), Buffer.from(expected))) {
|
|
240
|
+
const instanceId = request.headers["x-jishushell-instance"];
|
|
241
|
+
if (typeof instanceId === "string" && instanceManager.getInstance(instanceId)) {
|
|
242
|
+
request.internalCallerInstance = instanceId;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// Path (b): app-lifecycle / internal-only routes. Allowed without
|
|
246
|
+
// an instance header. The route is responsible for asserting the
|
|
247
|
+
// (request as any).internalCallerScope === "panel" tag before
|
|
248
|
+
// returning sensitive data.
|
|
249
|
+
if (path.startsWith("/api/internal/")) {
|
|
250
|
+
request.internalCallerScope = "panel";
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return reply.status(401).send({ detail: "Unauthorized" });
|
|
255
|
+
}
|
|
157
256
|
// Check Authorization header first, then fall back to httpOnly cookie
|
|
158
257
|
const authHeader = request.headers.authorization || "";
|
|
159
258
|
const match = authHeader.match(/^Bearer\s+(\S+)$/);
|
|
@@ -201,6 +300,122 @@ export async function createServer(options = {}) {
|
|
|
201
300
|
await app.register(agentAppRoutes);
|
|
202
301
|
await app.register(llmRoutes);
|
|
203
302
|
await app.register(backupRoutes);
|
|
303
|
+
await app.register(internalRoutes);
|
|
304
|
+
// Single FilesManager instance shared across /api/files, /webdav, and
|
|
305
|
+
// /api/files/external-mounts so a mutation on the mount list (PUT
|
|
306
|
+
// /external-mounts) is visible to every layer immediately.
|
|
307
|
+
const panelCfgForFiles = getPanelConfig();
|
|
308
|
+
const initialExternalMounts = Array.isArray(panelCfgForFiles.external_mounts)
|
|
309
|
+
? panelCfgForFiles.external_mounts
|
|
310
|
+
: [];
|
|
311
|
+
const sharedFilesManager = new FilesManager({
|
|
312
|
+
externalMounts: initialExternalMounts,
|
|
313
|
+
});
|
|
314
|
+
await app.register(async (scope) => {
|
|
315
|
+
// Encapsulated registration so the route-scoped octet-stream parser
|
|
316
|
+
// does not leak to other routes (matches plan G6/D6 — Files is the
|
|
317
|
+
// only route that needs raw-stream uploads at W1).
|
|
318
|
+
await filesRoutes(scope, { filesManager: sharedFilesManager });
|
|
319
|
+
await organizeRoutes(scope, { filesManager: sharedFilesManager });
|
|
320
|
+
await fileMountsRoutes(scope);
|
|
321
|
+
await externalMountsRoutes(scope, { filesManager: sharedFilesManager });
|
|
322
|
+
});
|
|
323
|
+
// WebDAV runs in its own scope — it has independent auth (app password
|
|
324
|
+
// Basic Auth, NOT the panel JWT) and registers extra HTTP methods that
|
|
325
|
+
// we don't want leaking to other routes.
|
|
326
|
+
await app.register(async (scope) => {
|
|
327
|
+
await webdavRoutes(scope, { filesManager: sharedFilesManager });
|
|
328
|
+
});
|
|
329
|
+
// ── Top-level /apps/:slug/* reverse proxy (AI-FS v1 W2.5 PR-8) ───────────
|
|
330
|
+
// Reverse-proxies HTTP requests for installed apps to their capability
|
|
331
|
+
// upstream. Auth is handled by the onRequest hook above. The /apps/:slug
|
|
332
|
+
// prefix is NOT stripped — apps like Filebrowser are configured with
|
|
333
|
+
// --baseURL=/apps/filebrowser and expect the full path.
|
|
334
|
+
//
|
|
335
|
+
// Resolution order: `<slug>-ui` → `web-<slug>` (first running entry wins).
|
|
336
|
+
// 503 if no running provider found. 400 if slug is malformed.
|
|
337
|
+
//
|
|
338
|
+
// Registered BEFORE the SPA static fallback so explicit routes win.
|
|
339
|
+
const appsProxyHandler = async (req, reply) => {
|
|
340
|
+
const rawSlug = req.params.slug ?? "";
|
|
341
|
+
if (!SLUG_RE.test(rawSlug)) {
|
|
342
|
+
return reply.status(400).send({ detail: "Invalid app slug", slug: rawSlug });
|
|
343
|
+
}
|
|
344
|
+
const upstream = resolveAppSlugUpstream(rawSlug);
|
|
345
|
+
if (!upstream) {
|
|
346
|
+
return reply.status(503).send({ detail: "app not running", slug: rawSlug });
|
|
347
|
+
}
|
|
348
|
+
const { host: upstreamHost, port: upstreamPort } = upstream;
|
|
349
|
+
// Pass the full original path unchanged (including /apps/<slug> prefix).
|
|
350
|
+
const rawUrl = req.raw.url ?? "/";
|
|
351
|
+
const targetPath = rawUrl;
|
|
352
|
+
const hostHeader = `${upstreamHost}:${upstreamPort}`;
|
|
353
|
+
// Build forwarded headers: strip cookie and authorization to avoid
|
|
354
|
+
// leaking panel session credentials to third-party apps.
|
|
355
|
+
const forwardHeaders = {};
|
|
356
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
357
|
+
if (value === undefined)
|
|
358
|
+
continue;
|
|
359
|
+
const lower = key.toLowerCase();
|
|
360
|
+
if (lower === "host")
|
|
361
|
+
continue;
|
|
362
|
+
if (lower === "cookie") {
|
|
363
|
+
const stripped = stripPanelSessionCookie(value);
|
|
364
|
+
if (stripped)
|
|
365
|
+
forwardHeaders[key] = stripped;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (lower === "authorization")
|
|
369
|
+
continue;
|
|
370
|
+
forwardHeaders[key] = value;
|
|
371
|
+
}
|
|
372
|
+
forwardHeaders["host"] = hostHeader;
|
|
373
|
+
// Hijack before writing to reply.raw so Fastify does not attempt its own
|
|
374
|
+
// lifecycle send after the handler returns.
|
|
375
|
+
reply.hijack();
|
|
376
|
+
await new Promise((resolve) => {
|
|
377
|
+
const proxyReq = httpRequest({
|
|
378
|
+
hostname: upstreamHost,
|
|
379
|
+
port: upstreamPort,
|
|
380
|
+
path: targetPath,
|
|
381
|
+
method: req.method,
|
|
382
|
+
headers: forwardHeaders,
|
|
383
|
+
}, (proxyRes) => {
|
|
384
|
+
// Stream the response back without buffering.
|
|
385
|
+
reply.raw.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
386
|
+
proxyRes.pipe(reply.raw, { end: true });
|
|
387
|
+
proxyRes.on("end", resolve);
|
|
388
|
+
proxyRes.on("error", () => {
|
|
389
|
+
try {
|
|
390
|
+
reply.raw.end();
|
|
391
|
+
}
|
|
392
|
+
catch { }
|
|
393
|
+
resolve();
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
proxyReq.on("error", (err) => {
|
|
397
|
+
console.error(`[apps-proxy] upstream error for slug=${rawSlug}:`, err.message);
|
|
398
|
+
if (!reply.raw.headersSent) {
|
|
399
|
+
reply.raw.writeHead(502, { "content-type": "application/json" });
|
|
400
|
+
reply.raw.end(JSON.stringify({ detail: "upstream error", slug: rawSlug }));
|
|
401
|
+
}
|
|
402
|
+
resolve();
|
|
403
|
+
});
|
|
404
|
+
// Forward request body if present (POST, PUT, PATCH, etc.)
|
|
405
|
+
if (req.rawBody) {
|
|
406
|
+
proxyReq.end(req.rawBody);
|
|
407
|
+
}
|
|
408
|
+
else if (req.raw.readable) {
|
|
409
|
+
req.raw.pipe(proxyReq, { end: true });
|
|
410
|
+
req.raw.on("end", () => { }); // ensure event fires
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
proxyReq.end();
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
};
|
|
417
|
+
app.all("/apps/:slug", appsProxyHandler);
|
|
418
|
+
app.all("/apps/:slug/*", appsProxyHandler);
|
|
204
419
|
// Serve frontend static files
|
|
205
420
|
// Look for frontend dist in multiple locations
|
|
206
421
|
const resolvedDir = existsSync(__dirname) ? realpathSync(__dirname) : __dirname;
|
|
@@ -408,6 +623,129 @@ export async function createServer(options = {}) {
|
|
|
408
623
|
const path = url.split("?")[0];
|
|
409
624
|
const gatewayMatch = path.match(GATEWAY_WS_RE);
|
|
410
625
|
const capabilityMatch = path.match(CAPABILITY_WS_RE);
|
|
626
|
+
// ── /apps/:slug/* WebSocket upgrade (AI-FS v1 W2.5 PR-8) ──────────────
|
|
627
|
+
// Proxy WS upgrades for top-level installed apps. Same auth gate as HTTP.
|
|
628
|
+
const appSlugWsMatch = path.match(APP_SLUG_WS_RE);
|
|
629
|
+
if (appSlugWsMatch && !gatewayMatch && !capabilityMatch) {
|
|
630
|
+
const slug = appSlugWsMatch[1];
|
|
631
|
+
// Auth gate: same JWT cookie/bearer check as the HTTP handler above.
|
|
632
|
+
if (!hasValidPanelUpgradeToken(req)) {
|
|
633
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
634
|
+
socket.destroy();
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const upstream = resolveAppSlugUpstream(slug);
|
|
638
|
+
if (!upstream) {
|
|
639
|
+
socket.write("HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n");
|
|
640
|
+
socket.destroy();
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const { host: upstreamHost, port: upstreamPort } = upstream;
|
|
644
|
+
const hostHeader = `${upstreamHost}:${upstreamPort}`;
|
|
645
|
+
const upstreamOrigin = `http://${hostHeader}`;
|
|
646
|
+
const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
|
|
647
|
+
const targetPath = `${path}${qs}`;
|
|
648
|
+
// Build upstream WS headers: strip cookie (panel session) and authorization.
|
|
649
|
+
const upstreamHeaders = buildCapabilityWebSocketHeaders(req.headers, upstreamOrigin);
|
|
650
|
+
socket.setTimeout?.(0);
|
|
651
|
+
socket.setNoDelay?.(true);
|
|
652
|
+
socket.setKeepAlive?.(true, 30000);
|
|
653
|
+
let proxyReq;
|
|
654
|
+
try {
|
|
655
|
+
proxyReq = httpRequest({
|
|
656
|
+
hostname: upstreamHost,
|
|
657
|
+
port: upstreamPort,
|
|
658
|
+
path: targetPath,
|
|
659
|
+
method: "GET",
|
|
660
|
+
timeout: 10_000,
|
|
661
|
+
headers: {
|
|
662
|
+
...upstreamHeaders,
|
|
663
|
+
host: hostHeader,
|
|
664
|
+
},
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
catch {
|
|
668
|
+
try {
|
|
669
|
+
socket.destroy();
|
|
670
|
+
}
|
|
671
|
+
catch { }
|
|
672
|
+
activeWsSockets.delete(socket);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
proxyReq.on("timeout", () => {
|
|
676
|
+
proxyReq.destroy();
|
|
677
|
+
socket.destroy();
|
|
678
|
+
});
|
|
679
|
+
proxyReq.on("upgrade", (_res, proxySocket, proxyHead) => {
|
|
680
|
+
proxySocket.setTimeout(0);
|
|
681
|
+
proxySocket.setNoDelay(true);
|
|
682
|
+
proxySocket.setKeepAlive(true, 30000);
|
|
683
|
+
proxySocket.pause();
|
|
684
|
+
let handshake = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n";
|
|
685
|
+
for (const [k, v] of Object.entries(_res.headers)) {
|
|
686
|
+
if (v !== undefined && k.toLowerCase().startsWith("sec-websocket-")) {
|
|
687
|
+
handshake += `${k}: ${v}\r\n`;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
handshake += "\r\n";
|
|
691
|
+
socket.write(handshake);
|
|
692
|
+
if (head.length > 0)
|
|
693
|
+
proxySocket.write(head);
|
|
694
|
+
if (proxyHead.length > 0)
|
|
695
|
+
socket.write(proxyHead);
|
|
696
|
+
proxySocket.pipe(socket);
|
|
697
|
+
socket.pipe(proxySocket);
|
|
698
|
+
proxySocket.resume();
|
|
699
|
+
activeWsSockets.add(socket);
|
|
700
|
+
activeWsSockets.add(proxySocket);
|
|
701
|
+
const release = (s) => { activeWsSockets.delete(s); };
|
|
702
|
+
const fail = () => {
|
|
703
|
+
try {
|
|
704
|
+
proxySocket.destroy();
|
|
705
|
+
}
|
|
706
|
+
catch { }
|
|
707
|
+
try {
|
|
708
|
+
socket.destroy();
|
|
709
|
+
}
|
|
710
|
+
catch { }
|
|
711
|
+
release(socket);
|
|
712
|
+
release(proxySocket);
|
|
713
|
+
};
|
|
714
|
+
proxySocket.on("close", () => {
|
|
715
|
+
release(proxySocket);
|
|
716
|
+
try {
|
|
717
|
+
if (!socket.destroyed && !socket.writableEnded)
|
|
718
|
+
socket.end();
|
|
719
|
+
}
|
|
720
|
+
catch { }
|
|
721
|
+
});
|
|
722
|
+
socket.on("close", () => {
|
|
723
|
+
release(socket);
|
|
724
|
+
try {
|
|
725
|
+
if (!proxySocket.destroyed && !proxySocket.writableEnded)
|
|
726
|
+
proxySocket.end();
|
|
727
|
+
}
|
|
728
|
+
catch { }
|
|
729
|
+
});
|
|
730
|
+
proxySocket.on("error", fail);
|
|
731
|
+
socket.on("error", fail);
|
|
732
|
+
});
|
|
733
|
+
proxyReq.on("response", (res) => {
|
|
734
|
+
forwardWebSocketRejection(socket, res);
|
|
735
|
+
activeWsSockets.delete(socket);
|
|
736
|
+
});
|
|
737
|
+
proxyReq.on("error", () => {
|
|
738
|
+
try {
|
|
739
|
+
socket.write("HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n");
|
|
740
|
+
}
|
|
741
|
+
catch { }
|
|
742
|
+
socket.destroy();
|
|
743
|
+
activeWsSockets.delete(socket);
|
|
744
|
+
});
|
|
745
|
+
proxyReq.end();
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
// ── end /apps/:slug/* WS upgrade ───────────────────────────────────────
|
|
411
749
|
// When an embedded page (e.g. Browserless debugger's puppeteer Worker)
|
|
412
750
|
// opens a WS to the panel origin at a non-API path (e.g. /?launch=...),
|
|
413
751
|
// resolve the target instance via the Referer header pointing at the
|