jishushell 0.4.24 → 0.4.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL-NOTICE +11 -0
- package/apps/browserless-chromium-container.yaml +78 -0
- package/apps/hermes-container.yaml +36 -2
- package/apps/ollama-binary.yaml +91 -90
- package/apps/ollama-cpu-container.yaml +8 -1
- package/apps/ollama-with-hollama-binary.yaml +91 -90
- package/apps/openclaw-binary.yaml +30 -1
- package/apps/openclaw-container.yaml +37 -2
- package/apps/openclaw-with-ollama-container.yaml +11 -2
- package/apps/openclaw-with-searxng-container.yaml +22 -2
- package/apps/openwebui-container.yaml +45 -1
- package/apps/playwright-container.yaml +7 -1
- package/apps/searxng-container.yaml +54 -4
- package/dist/cli/app.js +79 -9
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/doctor.d.ts +12 -12
- package/dist/cli/doctor.js +242 -55
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/llm.d.ts +4 -3
- package/dist/cli/llm.js +4 -3
- package/dist/cli/llm.js.map +1 -1
- package/dist/cli/panel.d.ts +6 -5
- package/dist/cli/panel.js +10 -9
- package/dist/cli/panel.js.map +1 -1
- package/dist/control.d.ts +7 -6
- package/dist/control.js +7 -6
- package/dist/control.js.map +1 -1
- package/dist/routes/agent-apps.d.ts +1 -1
- package/dist/routes/agent-apps.js +1 -1
- package/dist/routes/apps.js +44 -11
- package/dist/routes/apps.js.map +1 -1
- package/dist/routes/auth.js +3 -0
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/instances.js +787 -16
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/llm.js +24 -35
- package/dist/routes/llm.js.map +1 -1
- package/dist/routes/setup.js +1 -1
- package/dist/routes/setup.js.map +1 -1
- package/dist/server.d.ts +9 -0
- package/dist/server.js +410 -17
- package/dist/server.js.map +1 -1
- package/dist/services/agent-apps/catalog.js +4 -3
- package/dist/services/agent-apps/catalog.js.map +1 -1
- package/dist/services/agent-apps/index.d.ts +1 -1
- package/dist/services/agent-apps/index.js +1 -1
- package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
- package/dist/services/agent-apps/installers/adapter.js +1 -1
- package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
- package/dist/services/agent-apps/installers/shell-script.js +3 -3
- package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
- package/dist/services/agent-apps/types.d.ts +2 -2
- package/dist/services/agent-apps/types.js +1 -1
- package/dist/services/app/app-manager.d.ts +24 -1
- package/dist/services/app/app-manager.js +664 -116
- package/dist/services/app/app-manager.js.map +1 -1
- package/dist/services/app/hermes-agent-manager.js +6 -4
- package/dist/services/app/hermes-agent-manager.js.map +1 -1
- package/dist/services/app/provide-resolver.d.ts +29 -0
- package/dist/services/app/provide-resolver.js +112 -0
- package/dist/services/app/provide-resolver.js.map +1 -0
- package/dist/services/capability-endpoint-validator.d.ts +41 -0
- package/dist/services/capability-endpoint-validator.js +104 -0
- package/dist/services/capability-endpoint-validator.js.map +1 -0
- package/dist/services/capability-health.d.ts +16 -0
- package/dist/services/capability-health.js +121 -0
- package/dist/services/capability-health.js.map +1 -0
- package/dist/services/capability-registry.d.ts +106 -0
- package/dist/services/capability-registry.js +313 -0
- package/dist/services/capability-registry.js.map +1 -0
- package/dist/services/connection-apply.d.ts +89 -0
- package/dist/services/connection-apply.js +421 -0
- package/dist/services/connection-apply.js.map +1 -0
- package/dist/services/connection-resolver.d.ts +65 -0
- package/dist/services/connection-resolver.js +281 -0
- package/dist/services/connection-resolver.js.map +1 -0
- package/dist/services/connection-transactor.d.ts +37 -0
- package/dist/services/connection-transactor.js +341 -0
- package/dist/services/connection-transactor.js.map +1 -0
- package/dist/services/instance-manager.d.ts +13 -0
- package/dist/services/instance-manager.js +137 -23
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +16 -2
- package/dist/services/llm-proxy/index.js +48 -44
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/probe.d.ts +6 -0
- package/dist/services/llm-proxy/probe.js +85 -0
- package/dist/services/llm-proxy/probe.js.map +1 -0
- package/dist/services/llm-proxy/ssrf.d.ts +1 -0
- package/dist/services/llm-proxy/ssrf.js +18 -7
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.js +375 -16
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/process-manager.js +1 -1
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/runtime/adapters/hermes.d.ts +30 -1
- package/dist/services/runtime/adapters/hermes.js +218 -5
- package/dist/services/runtime/adapters/hermes.js.map +1 -1
- package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
- package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
- package/dist/services/runtime/adapters/openclaw.d.ts +87 -0
- package/dist/services/runtime/adapters/openclaw.js +250 -2
- package/dist/services/runtime/adapters/openclaw.js.map +1 -1
- package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
- package/dist/services/runtime/mcp-shims/firewall.js +129 -0
- package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
- package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
- package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
- package/dist/services/runtime/migrations.d.ts +8 -0
- package/dist/services/runtime/migrations.js +100 -0
- package/dist/services/runtime/migrations.js.map +1 -1
- package/dist/services/runtime/types.d.ts +15 -0
- package/dist/services/setup-manager.js +6 -6
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/suggestions.d.ts +27 -0
- package/dist/services/suggestions.js +133 -0
- package/dist/services/suggestions.js.map +1 -0
- package/dist/services/task-registry.js +4 -2
- package/dist/services/task-registry.js.map +1 -1
- package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
- package/dist/services/telemetry/device-fingerprint.js +1 -1
- package/dist/services/types-shim.d.ts +16 -0
- package/dist/services/types-shim.js +2 -0
- package/dist/services/types-shim.js.map +1 -0
- package/dist/types.d.ts +171 -1
- package/dist/utils/instance-lock.d.ts +22 -0
- package/dist/utils/instance-lock.js +48 -0
- package/dist/utils/instance-lock.js.map +1 -0
- package/dist/utils/safe-json.js +55 -22
- package/dist/utils/safe-json.js.map +1 -1
- package/install/jishu-install.sh +323 -27
- package/install/jishu-uninstall.sh +353 -20
- package/package.json +3 -1
- package/public/assets/Dashboard-rkWp-CXd.js +1 -0
- package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-_GHoklgo.js} +1 -1
- package/public/assets/HermesConfigForm-anDnwUp_.js +4 -0
- package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-ZU9_-hDr.js} +1 -1
- package/public/assets/InstanceDetail-CN0FH1aw.js +92 -0
- package/public/assets/{Login-BWsZH2mu.js → Login-BItXqYAJ.js} +1 -1
- package/public/assets/NewInstance-BousE6kY.js +1 -0
- package/public/assets/ProviderRecommendations-DFYj7Fb6.js +1 -0
- package/public/assets/Settings-Bttc6QmM.js +1 -0
- package/public/assets/Setup-Bsxx1zgj.js +1 -0
- package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-DPZpAKgO.js} +2 -2
- package/public/assets/index-8xZy1z5k.css +1 -0
- package/public/assets/index-Dw3HhUYE.js +19 -0
- package/public/assets/providers-DtNXh9JD.js +1 -0
- package/public/assets/registry-5s2UB6is.js +2 -0
- package/public/index.html +2 -2
- package/scripts/check-app-spec.mjs +443 -0
- package/scripts/check-i18n.mjs +154 -0
- package/scripts/run.sh +4 -4
- package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
- package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
- package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
- package/public/assets/NewInstance-BCIrAd86.js +0 -1
- package/public/assets/Settings-xkDcduFz.js +0 -1
- package/public/assets/Setup-Cfuwj4gV.js +0 -1
- package/public/assets/index-CPhVFEsx.css +0 -1
- package/public/assets/index-DQsM6Joa.js +0 -19
- package/public/assets/providers-V-vwrExZ.js +0 -1
- package/public/assets/registry-B4UFJdpA.js +0 -2
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SearXNG MCP shim — exported as a string for runtime drop into the
|
|
3
|
+
* Hermes container's agent-home.
|
|
4
|
+
*
|
|
5
|
+
* Why a shim instead of upstream `mcp-searxng`?
|
|
6
|
+
*
|
|
7
|
+
* The upstream `mcp-searxng` npm package's tool description and parameter
|
|
8
|
+
* surface (`Performs a web search using the SearXNG API, ideal for general
|
|
9
|
+
* queries, news, articles, and online content. Use this for broad
|
|
10
|
+
* information gathering, recent events, or when you need diverse web
|
|
11
|
+
* sources.` + `time_range: day|month|year` + `language` slot) acts as a
|
|
12
|
+
* prompt injection that nudges some LLMs (observed: MiniMax-M2.7) to:
|
|
13
|
+
* - inject the current date (or a hallucinated past date) into the query,
|
|
14
|
+
* - translate the query to English,
|
|
15
|
+
* - auto-fill `time_range=day`.
|
|
16
|
+
*
|
|
17
|
+
* OpenClaw's built-in `searxng` web-search tool exposes a deliberately
|
|
18
|
+
* minimal description (`Search the web using a self-hosted SearXNG
|
|
19
|
+
* instance. Returns titles, URLs, and snippets.`) and the same model
|
|
20
|
+
* leaves the user query untouched. We mirror OpenClaw's surface here so
|
|
21
|
+
* Hermes' MCP-bound search behaves identically to OpenClaw's plugin path.
|
|
22
|
+
*
|
|
23
|
+
* The shim is pure stdio MCP (JSON-RPC 2.0 over NDJSON) implemented
|
|
24
|
+
* against Node built-ins — no npm install needed inside the runtime.
|
|
25
|
+
*/
|
|
26
|
+
export const SEARXNG_MCP_SHIM_VERSION = "0.1.0";
|
|
27
|
+
export const SEARXNG_MCP_SHIM_SOURCE = `#!/usr/bin/env node
|
|
28
|
+
// jishushell-searxng-shim — generated, do not edit by hand.
|
|
29
|
+
// Source: src/services/runtime/mcp-shims/searxng-shim.ts
|
|
30
|
+
import { createInterface } from "node:readline";
|
|
31
|
+
|
|
32
|
+
const SEARXNG_URL = (process.env.SEARXNG_URL || "").replace(/\\/+$/, "");
|
|
33
|
+
|
|
34
|
+
const TOOL = {
|
|
35
|
+
name: "searxng_web_search",
|
|
36
|
+
description: "Search the web using a self-hosted SearXNG instance. Returns titles, URLs, and snippets.",
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
query: { type: "string", description: "Search query string." },
|
|
41
|
+
count: { type: "number", description: "Number of results to return (1-10).", minimum: 1, maximum: 10 },
|
|
42
|
+
categories: { type: "string", description: "Optional comma-separated categories such as general, news, or science." },
|
|
43
|
+
language: { type: "string", description: "Optional language code such as en, de, or fr." }
|
|
44
|
+
},
|
|
45
|
+
required: ["query"]
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function send(msg) { process.stdout.write(JSON.stringify(msg) + "\\n"); }
|
|
50
|
+
function reply(id, result) { if (id != null) send({ jsonrpc: "2.0", id, result }); }
|
|
51
|
+
function replyError(id, code, message) { if (id != null) send({ jsonrpc: "2.0", id, error: { code, message } }); }
|
|
52
|
+
|
|
53
|
+
async function callSearch(args) {
|
|
54
|
+
if (!SEARXNG_URL) throw new Error("SEARXNG_URL env not set");
|
|
55
|
+
const query = String(args?.query ?? "").trim();
|
|
56
|
+
if (!query) throw new Error("query is required");
|
|
57
|
+
const count = Math.max(1, Math.min(10, Number(args?.count) || 5));
|
|
58
|
+
const url = new URL("/search", SEARXNG_URL);
|
|
59
|
+
url.searchParams.set("q", query);
|
|
60
|
+
url.searchParams.set("format", "json");
|
|
61
|
+
if (args?.categories) url.searchParams.set("categories", String(args.categories));
|
|
62
|
+
if (args?.language) url.searchParams.set("language", String(args.language));
|
|
63
|
+
const ctrl = new AbortController();
|
|
64
|
+
const t = setTimeout(() => ctrl.abort(), 20000);
|
|
65
|
+
let resp;
|
|
66
|
+
try {
|
|
67
|
+
resp = await fetch(url.toString(), {
|
|
68
|
+
method: "GET",
|
|
69
|
+
headers: { Accept: "application/json" },
|
|
70
|
+
signal: ctrl.signal
|
|
71
|
+
});
|
|
72
|
+
} finally {
|
|
73
|
+
clearTimeout(t);
|
|
74
|
+
}
|
|
75
|
+
if (!resp.ok) throw new Error("SearXNG " + resp.status + " " + resp.statusText);
|
|
76
|
+
const body = await resp.json();
|
|
77
|
+
const raw = Array.isArray(body?.results) ? body.results : [];
|
|
78
|
+
const results = raw.slice(0, count).map((r) => ({
|
|
79
|
+
title: typeof r?.title === "string" ? r.title : "",
|
|
80
|
+
url: typeof r?.url === "string" ? r.url : "",
|
|
81
|
+
snippet: typeof r?.content === "string" ? r.content : ""
|
|
82
|
+
}));
|
|
83
|
+
return { query, count: results.length, results };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function handle(req) {
|
|
87
|
+
const { id, method, params } = req || {};
|
|
88
|
+
try {
|
|
89
|
+
if (method === "initialize") {
|
|
90
|
+
reply(id, {
|
|
91
|
+
protocolVersion: "2024-11-05",
|
|
92
|
+
capabilities: { tools: {} },
|
|
93
|
+
serverInfo: { name: "jishushell-searxng-shim", version: "${SEARXNG_MCP_SHIM_VERSION}" }
|
|
94
|
+
});
|
|
95
|
+
} else if (method === "notifications/initialized") {
|
|
96
|
+
// notification, no reply
|
|
97
|
+
} else if (method === "tools/list") {
|
|
98
|
+
reply(id, { tools: [TOOL] });
|
|
99
|
+
} else if (method === "tools/call") {
|
|
100
|
+
if (params?.name !== TOOL.name) throw new Error("unknown tool: " + params?.name);
|
|
101
|
+
const data = await callSearch(params?.arguments ?? {});
|
|
102
|
+
reply(id, { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] });
|
|
103
|
+
} else if (method === "ping") {
|
|
104
|
+
reply(id, {});
|
|
105
|
+
} else {
|
|
106
|
+
replyError(id, -32601, "method not found: " + method);
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
replyError(id, -32000, (e && e.message) ? e.message : String(e));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const rl = createInterface({ input: process.stdin });
|
|
114
|
+
rl.on("line", (line) => {
|
|
115
|
+
const t = line.trim();
|
|
116
|
+
if (!t) return;
|
|
117
|
+
let req;
|
|
118
|
+
try { req = JSON.parse(t); } catch { return; }
|
|
119
|
+
handle(req);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
123
|
+
process.on("SIGINT", () => process.exit(0));
|
|
124
|
+
`;
|
|
125
|
+
//# sourceMappingURL=searxng-shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searxng-shim.js","sourceRoot":"","sources":["../../../../src/services/runtime/mcp-shims/searxng-shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAkE4B,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+B1F,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* writeMcpEntry — adapter helper that materializes the MCP firewall
|
|
3
|
+
* (see firewall.ts) into an instance's agent-home and returns the
|
|
4
|
+
* `mcp_servers.<X>` block the adapter should write into its own config.
|
|
5
|
+
*
|
|
6
|
+
* Called by adapters from `applyConnectionEnv` (PR 8 / docs §17.3.3).
|
|
7
|
+
*
|
|
8
|
+
* Side effects on the host filesystem:
|
|
9
|
+
* - <agentHomeDir>/__mcp_shims__/<capabilityId>/firewall.mjs
|
|
10
|
+
* - <agentHomeDir>/__mcp_shims__/<capabilityId>/config.json
|
|
11
|
+
* - chown both to serviceUser if provided (best-effort)
|
|
12
|
+
*
|
|
13
|
+
* Pure return value (the adapter applies it to its own config schema):
|
|
14
|
+
* {
|
|
15
|
+
* command: "node",
|
|
16
|
+
* args: ["<containerHomeDir>/__mcp_shims__/<id>/firewall.mjs",
|
|
17
|
+
* "--config",
|
|
18
|
+
* "<containerHomeDir>/__mcp_shims__/<id>/config.json"],
|
|
19
|
+
* env: {} // upstream env is encoded in config.json — keep adapter env clean
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* The adapter is responsible for:
|
|
23
|
+
* - Adding `enabled: true` (or whatever flag its config schema needs).
|
|
24
|
+
* - Picking the `mcp_servers` map key (typically capabilityId).
|
|
25
|
+
*/
|
|
26
|
+
import type { ToolSchema } from "../../../types.js";
|
|
27
|
+
export interface WriteMcpEntryArgs {
|
|
28
|
+
/** Host-side path where the agent's home directory lives. */
|
|
29
|
+
agentHomeDir: string;
|
|
30
|
+
/**
|
|
31
|
+
* Container-side equivalent of agentHomeDir — used to compose the
|
|
32
|
+
* absolute path the agent process will exec. For Hermes that's
|
|
33
|
+
* `/opt/data` (HERMES_CONTAINER_HOME).
|
|
34
|
+
*/
|
|
35
|
+
containerHomeDir: string;
|
|
36
|
+
/** Capability ID (e.g. "search-searxng"); also used as the on-disk subdir. */
|
|
37
|
+
capabilityId: string;
|
|
38
|
+
/** Canonical tool surface — copied into config.json verbatim. */
|
|
39
|
+
toolSchema: ToolSchema;
|
|
40
|
+
/**
|
|
41
|
+
* Resolved upstream env for this binding — values are already substituted
|
|
42
|
+
* from connection-resolver outputs (no `${...}` placeholders left).
|
|
43
|
+
*/
|
|
44
|
+
upstreamEnv: Record<string, string>;
|
|
45
|
+
/** Best-effort chown so the in-container process can read the files. */
|
|
46
|
+
serviceUser?: {
|
|
47
|
+
uid: number;
|
|
48
|
+
gid: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export interface WriteMcpEntryResult {
|
|
52
|
+
/** Command to put under `mcp_servers.<key>.command`. */
|
|
53
|
+
command: string;
|
|
54
|
+
/** Args to put under `mcp_servers.<key>.args`. */
|
|
55
|
+
args: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Env to put under `mcp_servers.<key>.env`. Always empty — upstream
|
|
58
|
+
* env is funneled through firewall config.json so adapter config
|
|
59
|
+
* stays minimal. Adapters should still write this key so any prior
|
|
60
|
+
* env entries get cleared on overwrite.
|
|
61
|
+
*/
|
|
62
|
+
env: Record<string, string>;
|
|
63
|
+
}
|
|
64
|
+
export declare function writeMcpEntry(args: WriteMcpEntryArgs): WriteMcpEntryResult;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve `${SLOT}` placeholders in an `env_template` against a flat
|
|
67
|
+
* env map (typically the env that connection-resolver returned).
|
|
68
|
+
*
|
|
69
|
+
* Suffix forms:
|
|
70
|
+
* ${NAME_ORIGIN} → parsed URL with /search stripped, returns
|
|
71
|
+
* scheme://host:port (no path, no trailing slash).
|
|
72
|
+
* ${NAME} → verbatim env value.
|
|
73
|
+
*
|
|
74
|
+
* Note: this returns the host:port as the registry resolved at the time
|
|
75
|
+
* the consumer's PUT /connections was processed. To keep this fresh
|
|
76
|
+
* across host IP changes, the framework re-runs adapter.applyConnectionEnv
|
|
77
|
+
* before each instance start (see nomad-manager.phaseRefreshConnections,
|
|
78
|
+
* §17/PR 9). That re-render path passes a fresh env map here.
|
|
79
|
+
*
|
|
80
|
+
* Unresolved placeholders are left as-is (caller can decide whether to
|
|
81
|
+
* fail or pass through).
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolveEnvTemplate(template: Record<string, string> | undefined, envSource: Record<string, string>): Record<string, string>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* writeMcpEntry — adapter helper that materializes the MCP firewall
|
|
3
|
+
* (see firewall.ts) into an instance's agent-home and returns the
|
|
4
|
+
* `mcp_servers.<X>` block the adapter should write into its own config.
|
|
5
|
+
*
|
|
6
|
+
* Called by adapters from `applyConnectionEnv` (PR 8 / docs §17.3.3).
|
|
7
|
+
*
|
|
8
|
+
* Side effects on the host filesystem:
|
|
9
|
+
* - <agentHomeDir>/__mcp_shims__/<capabilityId>/firewall.mjs
|
|
10
|
+
* - <agentHomeDir>/__mcp_shims__/<capabilityId>/config.json
|
|
11
|
+
* - chown both to serviceUser if provided (best-effort)
|
|
12
|
+
*
|
|
13
|
+
* Pure return value (the adapter applies it to its own config schema):
|
|
14
|
+
* {
|
|
15
|
+
* command: "node",
|
|
16
|
+
* args: ["<containerHomeDir>/__mcp_shims__/<id>/firewall.mjs",
|
|
17
|
+
* "--config",
|
|
18
|
+
* "<containerHomeDir>/__mcp_shims__/<id>/config.json"],
|
|
19
|
+
* env: {} // upstream env is encoded in config.json — keep adapter env clean
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* The adapter is responsible for:
|
|
23
|
+
* - Adding `enabled: true` (or whatever flag its config schema needs).
|
|
24
|
+
* - Picking the `mcp_servers` map key (typically capabilityId).
|
|
25
|
+
*/
|
|
26
|
+
import { chownSync, mkdirSync, writeFileSync } from "fs";
|
|
27
|
+
import { dirname, join, posix } from "path";
|
|
28
|
+
import { MCP_FIREWALL_SOURCE } from "./firewall.js";
|
|
29
|
+
const SHIM_ROOT = "__mcp_shims__";
|
|
30
|
+
const FIREWALL_FILENAME = "firewall.mjs";
|
|
31
|
+
const CONFIG_FILENAME = "config.json";
|
|
32
|
+
export function writeMcpEntry(args) {
|
|
33
|
+
const { agentHomeDir, containerHomeDir, capabilityId, toolSchema, upstreamEnv, serviceUser, } = args;
|
|
34
|
+
const hostShimDir = join(agentHomeDir, SHIM_ROOT, capabilityId);
|
|
35
|
+
const hostFirewall = join(hostShimDir, FIREWALL_FILENAME);
|
|
36
|
+
const hostConfig = join(hostShimDir, CONFIG_FILENAME);
|
|
37
|
+
mkdirSync(hostShimDir, { recursive: true });
|
|
38
|
+
// Drop firewall source. Mode 0o755 so the in-container service user
|
|
39
|
+
// (which may not match host UID via every chown path) can still read+exec.
|
|
40
|
+
writeFileSync(hostFirewall, MCP_FIREWALL_SOURCE, { mode: 0o755 });
|
|
41
|
+
// Build firewall config
|
|
42
|
+
const config = {
|
|
43
|
+
capability_id: capabilityId,
|
|
44
|
+
tool_schema: {
|
|
45
|
+
name: toolSchema.name,
|
|
46
|
+
description: toolSchema.description,
|
|
47
|
+
// Stored under `inputSchema` so firewall doesn't have to alias.
|
|
48
|
+
inputSchema: toolSchema.parameters,
|
|
49
|
+
},
|
|
50
|
+
upstream: {
|
|
51
|
+
command: toolSchema.upstream.command,
|
|
52
|
+
args: toolSchema.upstream.args ?? [],
|
|
53
|
+
env: upstreamEnv,
|
|
54
|
+
},
|
|
55
|
+
wrap_outputs: toolSchema.wrap_outputs !== false,
|
|
56
|
+
};
|
|
57
|
+
writeFileSync(hostConfig, JSON.stringify(config, null, 2), { mode: 0o644 });
|
|
58
|
+
if (serviceUser) {
|
|
59
|
+
try {
|
|
60
|
+
chownSync(dirname(hostFirewall), serviceUser.uid, serviceUser.gid);
|
|
61
|
+
chownSync(hostFirewall, serviceUser.uid, serviceUser.gid);
|
|
62
|
+
chownSync(hostConfig, serviceUser.uid, serviceUser.gid);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* best-effort — files are world-readable */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Build container-side absolute path. Always POSIX-style — container
|
|
69
|
+
// OSes are Linux even when host is not.
|
|
70
|
+
const containerShimDir = posix.join(containerHomeDir, SHIM_ROOT, capabilityId);
|
|
71
|
+
return {
|
|
72
|
+
command: "node",
|
|
73
|
+
args: [
|
|
74
|
+
posix.join(containerShimDir, FIREWALL_FILENAME),
|
|
75
|
+
"--config",
|
|
76
|
+
posix.join(containerShimDir, CONFIG_FILENAME),
|
|
77
|
+
],
|
|
78
|
+
env: {},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve `${SLOT}` placeholders in an `env_template` against a flat
|
|
83
|
+
* env map (typically the env that connection-resolver returned).
|
|
84
|
+
*
|
|
85
|
+
* Suffix forms:
|
|
86
|
+
* ${NAME_ORIGIN} → parsed URL with /search stripped, returns
|
|
87
|
+
* scheme://host:port (no path, no trailing slash).
|
|
88
|
+
* ${NAME} → verbatim env value.
|
|
89
|
+
*
|
|
90
|
+
* Note: this returns the host:port as the registry resolved at the time
|
|
91
|
+
* the consumer's PUT /connections was processed. To keep this fresh
|
|
92
|
+
* across host IP changes, the framework re-runs adapter.applyConnectionEnv
|
|
93
|
+
* before each instance start (see nomad-manager.phaseRefreshConnections,
|
|
94
|
+
* §17/PR 9). That re-render path passes a fresh env map here.
|
|
95
|
+
*
|
|
96
|
+
* Unresolved placeholders are left as-is (caller can decide whether to
|
|
97
|
+
* fail or pass through).
|
|
98
|
+
*/
|
|
99
|
+
export function resolveEnvTemplate(template, envSource) {
|
|
100
|
+
if (!template)
|
|
101
|
+
return {};
|
|
102
|
+
const out = {};
|
|
103
|
+
for (const [key, raw] of Object.entries(template)) {
|
|
104
|
+
out[key] = raw.replace(/\$\{([A-Z0-9_]+)\}/g, (_m, name) => {
|
|
105
|
+
if (name.endsWith("_ORIGIN")) {
|
|
106
|
+
const baseName = name.slice(0, -"_ORIGIN".length);
|
|
107
|
+
const baseVal = envSource[baseName];
|
|
108
|
+
if (typeof baseVal === "string" && baseVal) {
|
|
109
|
+
try {
|
|
110
|
+
const u = new URL(baseVal);
|
|
111
|
+
if (u.pathname === "/search" || u.pathname === "/search/")
|
|
112
|
+
u.pathname = "";
|
|
113
|
+
return u.toString().replace(/\/$/, "");
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return baseVal;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return _m;
|
|
120
|
+
}
|
|
121
|
+
const val = envSource[name];
|
|
122
|
+
return typeof val === "string" ? val : _m;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=write-mcp-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-mcp-entry.js","sourceRoot":"","sources":["../../../../src/services/runtime/mcp-shims/write-mcp-entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,4 +12,12 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This file is framework-level and must never import adapter code.
|
|
14
14
|
*/
|
|
15
|
+
import type { AppSpec } from "../../types.js";
|
|
15
16
|
export declare function backfillInstanceMeta<T extends Record<string, any>>(raw: T | null | undefined): T | null;
|
|
17
|
+
/**
|
|
18
|
+
* Return a synthetic capability-only spec for a legacy instance based on
|
|
19
|
+
* its agentType. Returns null when the agent type has no known template
|
|
20
|
+
* (e.g. raw `custom` agents). Cached per-agentType — templates ship with
|
|
21
|
+
* the panel and don't change at runtime.
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadCapabilitySpecForLegacyInstance(meta: Record<string, any> | null): AppSpec | null;
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This file is framework-level and must never import adapter code.
|
|
14
14
|
*/
|
|
15
|
+
import { existsSync, readFileSync } from "fs";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { parse } from "yaml";
|
|
15
19
|
import { resolveAgentType } from "./instance.js";
|
|
16
20
|
export function backfillInstanceMeta(raw) {
|
|
17
21
|
if (!raw)
|
|
@@ -22,4 +26,100 @@ export function backfillInstanceMeta(raw) {
|
|
|
22
26
|
mut.agentType = agentType;
|
|
23
27
|
return raw;
|
|
24
28
|
}
|
|
29
|
+
// ── Capability discovery for legacy instances ──────────────────────────────
|
|
30
|
+
//
|
|
31
|
+
// PR 1+2 introduced the capability registry / app-spec contract. Legacy
|
|
32
|
+
// hermes/openclaw instances created via the old "新建实例" flow only carry
|
|
33
|
+
// `instance.json` — they have no `app-spec.yaml`, no `manifest.json`, and
|
|
34
|
+
// no `app_id`. The Connections endpoint then early-returns
|
|
35
|
+
// `{requires:[], connections:{}, pending:[]}` and the UI shows "此应用没
|
|
36
|
+
// 有需要绑定的能力。" even though every hermes/openclaw runtime *should*
|
|
37
|
+
// expose the standard llm/search/browser/mcp slots.
|
|
38
|
+
//
|
|
39
|
+
// We deliberately do NOT write any files for these instances:
|
|
40
|
+
//
|
|
41
|
+
// - The shipped templates (`apps/openclaw-container.yaml`,
|
|
42
|
+
// `apps/hermes-container.yaml`) describe a *container* runtime
|
|
43
|
+
// (image / args / ports). Legacy instances may run as raw_exec on a
|
|
44
|
+
// local binary instead, with their real runtime in
|
|
45
|
+
// `instance.json#runtime`. Persisting the container spec would
|
|
46
|
+
// cause `nomad-manager.startAppJob(spec, ...)` to rebuild the job
|
|
47
|
+
// from the wrong runtime shape on next start, losing volume mounts
|
|
48
|
+
// and per-instance customisation.
|
|
49
|
+
//
|
|
50
|
+
// - The Connections feature only needs `spec.requires` (and a stable
|
|
51
|
+
// `spec.id`) to populate the UI. We can synthesize that in-memory
|
|
52
|
+
// from the matching template every time the Connections route is
|
|
53
|
+
// hit. There is no state to drift, nothing to migrate, nothing to
|
|
54
|
+
// undo.
|
|
55
|
+
//
|
|
56
|
+
// The synthetic spec returned by `loadCapabilitySpecForLegacyInstance`
|
|
57
|
+
// is therefore pruned to capability metadata only. Service routes
|
|
58
|
+
// (start / stop / restart) keep going through the legacy
|
|
59
|
+
// `svc.startInstance` path because `meta.app_id` stays unset — the
|
|
60
|
+
// runtime continues to come from `instance.json`.
|
|
61
|
+
const TEMPLATE_BY_AGENT_TYPE = {
|
|
62
|
+
hermes: "hermes-container.yaml",
|
|
63
|
+
openclaw: "openclaw-container.yaml",
|
|
64
|
+
};
|
|
65
|
+
function resolveTemplatePath(templateName) {
|
|
66
|
+
const candidates = [
|
|
67
|
+
fileURLToPath(new URL(`../../../apps/${templateName}`, import.meta.url)),
|
|
68
|
+
join(process.cwd(), "apps", templateName),
|
|
69
|
+
];
|
|
70
|
+
for (const candidate of candidates) {
|
|
71
|
+
if (existsSync(candidate))
|
|
72
|
+
return candidate;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const specCache = new Map();
|
|
77
|
+
/**
|
|
78
|
+
* Return a synthetic capability-only spec for a legacy instance based on
|
|
79
|
+
* its agentType. Returns null when the agent type has no known template
|
|
80
|
+
* (e.g. raw `custom` agents). Cached per-agentType — templates ship with
|
|
81
|
+
* the panel and don't change at runtime.
|
|
82
|
+
*/
|
|
83
|
+
export function loadCapabilitySpecForLegacyInstance(meta) {
|
|
84
|
+
if (!meta)
|
|
85
|
+
return null;
|
|
86
|
+
const agentType = typeof meta.agentType === "string" && meta.agentType
|
|
87
|
+
? meta.agentType
|
|
88
|
+
: resolveAgentType(meta);
|
|
89
|
+
const templateName = TEMPLATE_BY_AGENT_TYPE[agentType];
|
|
90
|
+
if (!templateName)
|
|
91
|
+
return null;
|
|
92
|
+
if (specCache.has(agentType)) {
|
|
93
|
+
return specCache.get(agentType) ?? null;
|
|
94
|
+
}
|
|
95
|
+
const templatePath = resolveTemplatePath(templateName);
|
|
96
|
+
if (!templatePath) {
|
|
97
|
+
specCache.set(agentType, null);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const raw = parse(readFileSync(templatePath, "utf-8"));
|
|
102
|
+
// Strip runtime — Connections only needs id/name/requires/provides.
|
|
103
|
+
// Leaving `tasks` in would tempt callers to (re)build a nomad job
|
|
104
|
+
// from a spec that doesn't match the instance's actual runtime.
|
|
105
|
+
const synthetic = {
|
|
106
|
+
id: raw.id,
|
|
107
|
+
name: raw.name,
|
|
108
|
+
version: raw.version,
|
|
109
|
+
agentType: raw.agentType,
|
|
110
|
+
description: raw.description,
|
|
111
|
+
singleInstance: raw.singleInstance,
|
|
112
|
+
tasks: [],
|
|
113
|
+
provides: raw.provides ?? [],
|
|
114
|
+
requires: raw.requires ?? [],
|
|
115
|
+
};
|
|
116
|
+
specCache.set(agentType, synthetic);
|
|
117
|
+
return synthetic;
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
console.warn(`[migration] cannot load capability template '${templateName}': ${e.message}`);
|
|
121
|
+
specCache.set(agentType, null);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
25
125
|
//# sourceMappingURL=migrations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/services/runtime/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/services/runtime/migrations.ts"],"names":[],"mappings":"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"}
|
|
@@ -354,6 +354,21 @@ export interface RuntimeAdapter {
|
|
|
354
354
|
}>;
|
|
355
355
|
/** Return the list of other instance IDs that share this one's agent-home. */
|
|
356
356
|
findInstancesSharingHome?(instanceId: string): string[];
|
|
357
|
+
/**
|
|
358
|
+
* Translate a flat env map (`{SEARCH_API_BASE_URL, OPENAI_API_BASE_URL,
|
|
359
|
+
* MCP_SERVERS, ...}`) — produced by `connection-resolver` from the
|
|
360
|
+
* user's persisted bindings — into adapter-native config writes
|
|
361
|
+
* (Hermes' `mcp_servers` in config.yaml, OpenClaw's
|
|
362
|
+
* `plugins.entries.searxng.config.webSearch.baseUrl` in openclaw.json,
|
|
363
|
+
* etc.). Called both at PUT /connections time AND on every instance
|
|
364
|
+
* start (PR 9 phaseRefreshConnections), so host IP changes /
|
|
365
|
+
* provider redeployments propagate without a manual re-bind.
|
|
366
|
+
*
|
|
367
|
+
* Adapters are free to no-op for env keys they don't recognize.
|
|
368
|
+
* Failures should be logged but NOT thrown — start should proceed
|
|
369
|
+
* with whatever config is on disk.
|
|
370
|
+
*/
|
|
371
|
+
applyConnectionEnv?(instanceId: string, env: Record<string, string>): Promise<void>;
|
|
357
372
|
/** Resolve the canonical agent binary path (OpenClaw only). */
|
|
358
373
|
resolveBin?(): string;
|
|
359
374
|
/** Resolve the instance's on-disk config file path. */
|
|
@@ -579,7 +579,7 @@ async function installDockerWithTask(task) {
|
|
|
579
579
|
try {
|
|
580
580
|
emitTask(task, { type: "progress", message: "开始安装 Docker...", progress: 0 });
|
|
581
581
|
const user = execSync("whoami", { encoding: "utf-8", timeout: 5000 }).trim();
|
|
582
|
-
// ──
|
|
582
|
+
// ── Try the get.docker.com convenience script ─────────────────
|
|
583
583
|
emitTask(task, { type: "log", message: "下载 Docker 安装脚本..." });
|
|
584
584
|
emitTask(task, { type: "progress", message: "下载中...", progress: 5 });
|
|
585
585
|
// Unique temp path prevents TOCTOU: another user can't swap a fixed /tmp/get-docker.sh
|
|
@@ -617,10 +617,10 @@ async function installDockerWithTask(task) {
|
|
|
617
617
|
else {
|
|
618
618
|
emitTask(task, { type: "log", message: "下载便捷脚本失败,尝试 apt 仓库方式..." });
|
|
619
619
|
}
|
|
620
|
-
// ──
|
|
620
|
+
// ── Fallback: apt-repo manual install (handles Debian trixie pre-releases) ─
|
|
621
621
|
if (!scriptOk) {
|
|
622
622
|
emitTask(task, { type: "progress", message: "通过 apt 仓库安装 Docker...", progress: 15 });
|
|
623
|
-
//
|
|
623
|
+
// Detect system ID + codename; downgrade trixie/sid to bookworm.
|
|
624
624
|
let codename = "bookworm";
|
|
625
625
|
let repoOs = "debian";
|
|
626
626
|
try {
|
|
@@ -629,7 +629,7 @@ async function installDockerWithTask(task) {
|
|
|
629
629
|
const distroId = (osRelease.match(/^ID=(.+)$/m)?.[1] || "debian").trim();
|
|
630
630
|
const raw = (codenameMatch?.[1] || "").trim();
|
|
631
631
|
repoOs = (distroId === "ubuntu") ? "ubuntu" : "debian";
|
|
632
|
-
// Debian testing/unstable
|
|
632
|
+
// Debian testing/unstable (trixie, sid) → use the previous stable repo.
|
|
633
633
|
const unstableCodenames = new Set(["trixie", "forky", "sid", "unstable", "testing"]);
|
|
634
634
|
const ubuntuFallback = "noble";
|
|
635
635
|
if (distroId === "ubuntu" && unstableCodenames.has(raw)) {
|
|
@@ -671,7 +671,7 @@ async function installDockerWithTask(task) {
|
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
}
|
|
674
|
-
// ──
|
|
674
|
+
// ── Wrap up ────────────────────────────────────────────────────
|
|
675
675
|
emitTask(task, { type: "progress", message: "配置用户权限...", progress: 90 });
|
|
676
676
|
try {
|
|
677
677
|
execFileSync("sudo", ["groupadd", "docker"], { timeout: 10000 });
|
|
@@ -693,7 +693,7 @@ async function installDockerWithTask(task) {
|
|
|
693
693
|
emitTask(task, { type: "log", message: "已启用内存 cgroup(需要重启生效,Docker 内存统计才会正常)" });
|
|
694
694
|
}
|
|
695
695
|
const version = getDockerVersionLine(10000);
|
|
696
|
-
//
|
|
696
|
+
// Verify the daemon via `sudo docker` (needed when the current user is not in the docker group yet).
|
|
697
697
|
const daemonOk = canAccessDockerDaemon(10000);
|
|
698
698
|
if (daemonOk) {
|
|
699
699
|
const doneMsg = needsReboot
|