@zshuangmu/agenthub 0.4.14 → 0.4.16
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/LICENSE +21 -21
- package/README.md +268 -268
- package/package.json +41 -41
- package/src/api-server.js +518 -244
- package/src/cli.js +714 -671
- package/src/commands/api.js +9 -9
- package/src/commands/doctor.js +335 -335
- package/src/commands/info.js +15 -15
- package/src/commands/install.js +56 -56
- package/src/commands/list.js +78 -78
- package/src/commands/pack.js +249 -156
- package/src/commands/publish-remote.js +9 -9
- package/src/commands/publish.js +7 -7
- package/src/commands/rollback.js +59 -59
- package/src/commands/search.js +14 -14
- package/src/commands/serve.js +9 -9
- package/src/commands/stats.js +105 -105
- package/src/commands/uninstall.js +76 -76
- package/src/commands/update.js +54 -54
- package/src/commands/verify.js +133 -133
- package/src/commands/versions.js +75 -75
- package/src/commands/web.js +9 -9
- package/src/index.js +18 -18
- package/src/lib/auth.js +301 -0
- package/src/lib/bundle-transfer.js +58 -58
- package/src/lib/colors.js +60 -60
- package/src/lib/database.js +450 -244
- package/src/lib/debug.js +135 -135
- package/src/lib/fs-utils.js +107 -50
- package/src/lib/html.js +2163 -1824
- package/src/lib/http.js +168 -168
- package/src/lib/install.js +60 -60
- package/src/lib/manifest.js +124 -124
- package/src/lib/openclaw-config.js +40 -40
- package/src/lib/permissions.js +105 -0
- package/src/lib/privacy-engine.js +220 -0
- package/src/lib/registry.js +130 -130
- package/src/lib/remote.js +11 -11
- package/src/lib/security-scanner.js +233 -233
- package/src/lib/signing.js +158 -0
- package/src/lib/version-manager.js +77 -77
- package/src/server.js +176 -176
- package/src/web-server.js +135 -135
package/src/web-server.js
CHANGED
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
import http from "node:http";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { sendHtml, sendJson, notFound, fetchJsonWithFallback } from "./lib/http.js";
|
|
5
|
-
import { renderAgentListPage, renderAgentDetailPage, renderStatsPage } from "./lib/html.js";
|
|
6
|
-
|
|
7
|
-
const API_BASE = process.env.API_BASE || "http://127.0.0.1:3001";
|
|
8
|
-
|
|
9
|
-
async function fetchApi(endpoint, apiBaseUrl) {
|
|
10
|
-
const url = `${apiBaseUrl}${endpoint}`;
|
|
11
|
-
return fetchJsonWithFallback(url);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function createWebServer({ port = 3000, apiBase = null, host = "0.0.0.0" }) {
|
|
15
|
-
const apiBaseUrl = apiBase || API_BASE;
|
|
16
|
-
|
|
17
|
-
const server = http.createServer(async (request, response) => {
|
|
18
|
-
try {
|
|
19
|
-
const url = new URL(request.url, "http://127.0.0.1");
|
|
20
|
-
|
|
21
|
-
// 静态资源:CSS/JS
|
|
22
|
-
if (url.pathname.startsWith("/static/")) {
|
|
23
|
-
const filePath = path.join(process.cwd(), "public", url.pathname);
|
|
24
|
-
try {
|
|
25
|
-
const content = await readFile(filePath);
|
|
26
|
-
const ext = path.extname(filePath);
|
|
27
|
-
const contentTypes = {
|
|
28
|
-
".css": "text/css",
|
|
29
|
-
".js": "application/javascript",
|
|
30
|
-
".png": "image/png",
|
|
31
|
-
".svg": "image/svg+xml"
|
|
32
|
-
};
|
|
33
|
-
response.writeHead(200, { "Content-Type": contentTypes[ext] || "application/octet-stream" });
|
|
34
|
-
response.end(content);
|
|
35
|
-
} catch {
|
|
36
|
-
notFound(response);
|
|
37
|
-
}
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 首页
|
|
42
|
-
if (url.pathname === "/") {
|
|
43
|
-
const query = url.searchParams.get("q") ?? "";
|
|
44
|
-
const data = await fetchApi(`/api/agents?q=${encodeURIComponent(query)}`, apiBaseUrl);
|
|
45
|
-
const stats = await fetchApi("/api/stats", apiBaseUrl);
|
|
46
|
-
sendHtml(response, 200, renderAgentListPage({
|
|
47
|
-
query,
|
|
48
|
-
agents: data.agents,
|
|
49
|
-
totalDownloads: stats.stats.totalDownloads,
|
|
50
|
-
apiBase: apiBaseUrl
|
|
51
|
-
}));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Agent 详情页
|
|
56
|
-
if (url.pathname.startsWith("/agents/")) {
|
|
57
|
-
const slug = url.pathname.slice("/agents/".length);
|
|
58
|
-
const manifest = await fetchApi(`/api/agents/${slug}`, apiBaseUrl);
|
|
59
|
-
sendHtml(response, 200, renderAgentDetailPage({ ...manifest, apiBase: apiBaseUrl }));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// 统计页面
|
|
64
|
-
if (url.pathname === "/stats") {
|
|
65
|
-
const stats = await fetchApi("/api/stats", apiBaseUrl);
|
|
66
|
-
sendHtml(response, 200, renderStatsPage(stats));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 技能页面(重定向到 API)
|
|
71
|
-
if (url.pathname === "/skills/agenthub-discover" || url.pathname === "/skills/agenthub-discover/SKILL.md") {
|
|
72
|
-
response.writeHead(302, { "Location": `${apiBaseUrl}/api/skills/agenthub-discover` });
|
|
73
|
-
response.end();
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
notFound(response);
|
|
78
|
-
} catch (error) {
|
|
79
|
-
// 如果 API 不可用,显示错误页面
|
|
80
|
-
sendHtml(response, 503, renderErrorPage(error.message, apiBaseUrl));
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
85
|
-
const address = server.address();
|
|
86
|
-
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
server,
|
|
90
|
-
port: actualPort,
|
|
91
|
-
baseUrl: `http://127.0.0.1:${actualPort}`,
|
|
92
|
-
close: () => new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function renderErrorPage(message, apiBase) {
|
|
97
|
-
return `<!doctype html>
|
|
98
|
-
<html lang="zh-CN">
|
|
99
|
-
<head>
|
|
100
|
-
<meta charset="utf-8" />
|
|
101
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
102
|
-
<title>服务不可用 - AgentHub</title>
|
|
103
|
-
<style>
|
|
104
|
-
body {
|
|
105
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
106
|
-
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 50%, #a7f3d0 100%);
|
|
107
|
-
min-height: 100vh;
|
|
108
|
-
display: flex;
|
|
109
|
-
align-items: center;
|
|
110
|
-
justify-content: center;
|
|
111
|
-
color: #064e3b;
|
|
112
|
-
}
|
|
113
|
-
.error-box {
|
|
114
|
-
background: rgba(255,255,255,0.9);
|
|
115
|
-
padding: 48px;
|
|
116
|
-
border-radius: 24px;
|
|
117
|
-
text-align: center;
|
|
118
|
-
box-shadow: 0 8px 32px rgba(16, 185, 129, 0.15);
|
|
119
|
-
}
|
|
120
|
-
h1 { font-size: 48px; margin-bottom: 16px; }
|
|
121
|
-
p { color: #6b7280; margin-bottom: 24px; }
|
|
122
|
-
code { background: #f3f4f6; padding: 8px 16px; border-radius: 8px; }
|
|
123
|
-
</style>
|
|
124
|
-
</head>
|
|
125
|
-
<body>
|
|
126
|
-
<div class="error-box">
|
|
127
|
-
<h1>⚠️</h1>
|
|
128
|
-
<h2>后端服务不可用</h2>
|
|
129
|
-
<p>无法连接到 API 服务</p>
|
|
130
|
-
<code>API 地址: ${apiBase}</code>
|
|
131
|
-
<p style="margin-top: 16px; font-size: 14px;">错误: ${message}</p>
|
|
132
|
-
</div>
|
|
133
|
-
</body>
|
|
134
|
-
</html>`;
|
|
135
|
-
}
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { sendHtml, sendJson, notFound, fetchJsonWithFallback } from "./lib/http.js";
|
|
5
|
+
import { renderAgentListPage, renderAgentDetailPage, renderStatsPage } from "./lib/html.js";
|
|
6
|
+
|
|
7
|
+
const API_BASE = process.env.API_BASE || "http://127.0.0.1:3001";
|
|
8
|
+
|
|
9
|
+
async function fetchApi(endpoint, apiBaseUrl) {
|
|
10
|
+
const url = `${apiBaseUrl}${endpoint}`;
|
|
11
|
+
return fetchJsonWithFallback(url);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function createWebServer({ port = 3000, apiBase = null, host = "0.0.0.0" }) {
|
|
15
|
+
const apiBaseUrl = apiBase || API_BASE;
|
|
16
|
+
|
|
17
|
+
const server = http.createServer(async (request, response) => {
|
|
18
|
+
try {
|
|
19
|
+
const url = new URL(request.url, "http://127.0.0.1");
|
|
20
|
+
|
|
21
|
+
// 静态资源:CSS/JS
|
|
22
|
+
if (url.pathname.startsWith("/static/")) {
|
|
23
|
+
const filePath = path.join(process.cwd(), "public", url.pathname);
|
|
24
|
+
try {
|
|
25
|
+
const content = await readFile(filePath);
|
|
26
|
+
const ext = path.extname(filePath);
|
|
27
|
+
const contentTypes = {
|
|
28
|
+
".css": "text/css",
|
|
29
|
+
".js": "application/javascript",
|
|
30
|
+
".png": "image/png",
|
|
31
|
+
".svg": "image/svg+xml"
|
|
32
|
+
};
|
|
33
|
+
response.writeHead(200, { "Content-Type": contentTypes[ext] || "application/octet-stream" });
|
|
34
|
+
response.end(content);
|
|
35
|
+
} catch {
|
|
36
|
+
notFound(response);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 首页
|
|
42
|
+
if (url.pathname === "/") {
|
|
43
|
+
const query = url.searchParams.get("q") ?? "";
|
|
44
|
+
const data = await fetchApi(`/api/agents?q=${encodeURIComponent(query)}`, apiBaseUrl);
|
|
45
|
+
const stats = await fetchApi("/api/stats", apiBaseUrl);
|
|
46
|
+
sendHtml(response, 200, renderAgentListPage({
|
|
47
|
+
query,
|
|
48
|
+
agents: data.agents,
|
|
49
|
+
totalDownloads: stats.stats.totalDownloads,
|
|
50
|
+
apiBase: apiBaseUrl
|
|
51
|
+
}));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Agent 详情页
|
|
56
|
+
if (url.pathname.startsWith("/agents/")) {
|
|
57
|
+
const slug = url.pathname.slice("/agents/".length);
|
|
58
|
+
const manifest = await fetchApi(`/api/agents/${slug}`, apiBaseUrl);
|
|
59
|
+
sendHtml(response, 200, renderAgentDetailPage({ ...manifest, apiBase: apiBaseUrl }));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 统计页面
|
|
64
|
+
if (url.pathname === "/stats") {
|
|
65
|
+
const stats = await fetchApi("/api/stats", apiBaseUrl);
|
|
66
|
+
sendHtml(response, 200, renderStatsPage(stats));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 技能页面(重定向到 API)
|
|
71
|
+
if (url.pathname === "/skills/agenthub-discover" || url.pathname === "/skills/agenthub-discover/SKILL.md") {
|
|
72
|
+
response.writeHead(302, { "Location": `${apiBaseUrl}/api/skills/agenthub-discover` });
|
|
73
|
+
response.end();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
notFound(response);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// 如果 API 不可用,显示错误页面
|
|
80
|
+
sendHtml(response, 503, renderErrorPage(error.message, apiBaseUrl));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
85
|
+
const address = server.address();
|
|
86
|
+
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
server,
|
|
90
|
+
port: actualPort,
|
|
91
|
+
baseUrl: `http://127.0.0.1:${actualPort}`,
|
|
92
|
+
close: () => new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function renderErrorPage(message, apiBase) {
|
|
97
|
+
return `<!doctype html>
|
|
98
|
+
<html lang="zh-CN">
|
|
99
|
+
<head>
|
|
100
|
+
<meta charset="utf-8" />
|
|
101
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
102
|
+
<title>服务不可用 - AgentHub</title>
|
|
103
|
+
<style>
|
|
104
|
+
body {
|
|
105
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
106
|
+
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 50%, #a7f3d0 100%);
|
|
107
|
+
min-height: 100vh;
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
justify-content: center;
|
|
111
|
+
color: #064e3b;
|
|
112
|
+
}
|
|
113
|
+
.error-box {
|
|
114
|
+
background: rgba(255,255,255,0.9);
|
|
115
|
+
padding: 48px;
|
|
116
|
+
border-radius: 24px;
|
|
117
|
+
text-align: center;
|
|
118
|
+
box-shadow: 0 8px 32px rgba(16, 185, 129, 0.15);
|
|
119
|
+
}
|
|
120
|
+
h1 { font-size: 48px; margin-bottom: 16px; }
|
|
121
|
+
p { color: #6b7280; margin-bottom: 24px; }
|
|
122
|
+
code { background: #f3f4f6; padding: 8px 16px; border-radius: 8px; }
|
|
123
|
+
</style>
|
|
124
|
+
</head>
|
|
125
|
+
<body>
|
|
126
|
+
<div class="error-box">
|
|
127
|
+
<h1>⚠️</h1>
|
|
128
|
+
<h2>后端服务不可用</h2>
|
|
129
|
+
<p>无法连接到 API 服务</p>
|
|
130
|
+
<code>API 地址: ${apiBase}</code>
|
|
131
|
+
<p style="margin-top: 16px; font-size: 14px;">错误: ${message}</p>
|
|
132
|
+
</div>
|
|
133
|
+
</body>
|
|
134
|
+
</html>`;
|
|
135
|
+
}
|