ape-claw 0.1.6 → 0.1.8
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/README.md +157 -8
- package/docs/operator/01-quickstart.md +81 -72
- package/docs/operator/03-cli-reference.md +46 -5
- package/package.json +1 -1
- package/src/cli.mjs +551 -15
- package/src/lib/openclaw-paths.mjs +65 -0
- package/src/server/index.mjs +8 -2
- package/src/server/middleware/auth.mjs +10 -0
- package/src/server/routes/forge-agent.mjs +438 -58
- package/src/server/routes/openclaw-env.mjs +206 -0
- package/src/server/routes/skills.mjs +80 -3
- package/ui/forge/css/forge.css +153 -2
- package/ui/forge/index.html +40 -6
- package/ui/forge/js/forge-attachments.js +616 -377
- package/ui/forge/js/forge-chat.js +201 -90
- package/ui/forge/js/forge-data.js +305 -11
- package/ui/forge/js/forge-scene.js +178 -18
- package/ui/index.html +3 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function clean(v) {
|
|
5
|
+
return String(v || "").trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function uniq(list) {
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
const out = [];
|
|
11
|
+
for (const item of list) {
|
|
12
|
+
const v = clean(item);
|
|
13
|
+
if (!v || seen.has(v)) continue;
|
|
14
|
+
seen.add(v);
|
|
15
|
+
out.push(v);
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function openClawHomeDir() {
|
|
21
|
+
return clean(process.env.OPENCLAW_HOME) || os.homedir();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function openClawRootCandidates() {
|
|
25
|
+
const home = openClawHomeDir();
|
|
26
|
+
const stateDir = clean(process.env.OPENCLAW_STATE_DIR);
|
|
27
|
+
const configPath = clean(process.env.OPENCLAW_CONFIG_PATH);
|
|
28
|
+
const configDir = configPath ? path.dirname(configPath) : "";
|
|
29
|
+
return uniq([
|
|
30
|
+
path.join(home, ".openclaw"),
|
|
31
|
+
stateDir,
|
|
32
|
+
configDir,
|
|
33
|
+
"/data/.clawdbot",
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function openClawSkillsDirCandidates() {
|
|
38
|
+
const explicitSkillsDir = clean(process.env.OPENCLAW_SKILLS_DIR);
|
|
39
|
+
const roots = openClawRootCandidates();
|
|
40
|
+
return uniq([
|
|
41
|
+
explicitSkillsDir,
|
|
42
|
+
...roots.map((r) => path.join(r, "skills")),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function openClawWorkspaceSkillsDirCandidates() {
|
|
47
|
+
return uniq(openClawRootCandidates().map((r) => path.join(r, "workspace", "skills")));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function openClawConfigCandidates() {
|
|
51
|
+
const explicit = clean(process.env.OPENCLAW_CONFIG_PATH);
|
|
52
|
+
const roots = openClawRootCandidates();
|
|
53
|
+
return uniq([
|
|
54
|
+
explicit,
|
|
55
|
+
...roots.map((r) => path.join(r, "openclaw.json")),
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function openClawEnvFileCandidates() {
|
|
60
|
+
return uniq(openClawRootCandidates().map((r) => path.join(r, ".env")));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function openClawControlUiIndexCandidates() {
|
|
64
|
+
return uniq(openClawRootCandidates().map((r) => path.join(r, "dist", "control-ui", "index.html")));
|
|
65
|
+
}
|
package/src/server/index.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { handleHealth } from "./routes/health.mjs";
|
|
|
18
18
|
import { handleEventsSse, handleEventsBacklog, handlePostEvent } from "./routes/events.mjs";
|
|
19
19
|
import {
|
|
20
20
|
handleSkillsSearch, handleSkillsGet, handleSkillsStats,
|
|
21
|
+
handleOpenClawInstalledSkills,
|
|
21
22
|
handleSkillcardsUserGet, handleSkillcardsAuthCheck,
|
|
22
23
|
handleSkillcardsUserAdd, handleSkillcardsUserDelete,
|
|
23
24
|
handleSkillcardsUserMarkOnchain, handleSkillcardFile,
|
|
@@ -30,7 +31,8 @@ import {
|
|
|
30
31
|
handleChatStream, handleChatGet, handleChatRooms,
|
|
31
32
|
handleChatPost, handleChatReact,
|
|
32
33
|
} from "./routes/chat.mjs";
|
|
33
|
-
import { handleForgeChat, handleForgeStatus, initForgeAgent } from "./routes/forge-agent.mjs";
|
|
34
|
+
import { handleForgeChat, handleForgeStatus, handleForgeGatewayControl, initForgeAgent } from "./routes/forge-agent.mjs";
|
|
35
|
+
import { handleOpenClawEnvGet, handleOpenClawEnvSet } from "./routes/openclaw-env.mjs";
|
|
34
36
|
import { handleV2ReceiptGet, handleV2Config } from "./routes/v2.mjs";
|
|
35
37
|
import { handlePodStatus, handlePodStop, handlePodFiles, handleStarterPack } from "./routes/pod.mjs";
|
|
36
38
|
import {
|
|
@@ -114,6 +116,7 @@ const server = http.createServer((req, res) => {
|
|
|
114
116
|
if (pathname === "/api/skills/search" && req.method === "GET") return safeHandler(handleSkillsSearch)(req, res, reqUrl);
|
|
115
117
|
if (pathname === "/api/skills/get" && req.method === "GET") return safeHandler(handleSkillsGet)(req, res, reqUrl);
|
|
116
118
|
if (pathname === "/api/skills/stats" && req.method === "GET") return safeHandler(handleSkillsStats)(req, res);
|
|
119
|
+
if (pathname === "/api/skills/openclaw-installed" && req.method === "GET") return safeHandler(handleOpenClawInstalledSkills)(req, res, reqUrl);
|
|
117
120
|
if (pathname === "/api/skillcards/user/auth-check" && req.method === "GET") return safeHandler(handleSkillcardsAuthCheck)(req, res);
|
|
118
121
|
if (pathname === "/api/skillcards/user/add" && req.method === "POST") return safeHandler(handleSkillcardsUserAdd)(req, res);
|
|
119
122
|
if (pathname === "/api/skillcards/user/delete" && req.method === "POST") return safeHandler(handleSkillcardsUserDelete)(req, res);
|
|
@@ -126,6 +129,9 @@ const server = http.createServer((req, res) => {
|
|
|
126
129
|
if (pathname === "/api/events" && req.method === "POST") return safeHandler(handlePostEvent)(req, res);
|
|
127
130
|
if (pathname === "/api/forge/status" && req.method === "GET") return safeHandler(handleForgeStatus)(req, res);
|
|
128
131
|
if (pathname === "/api/forge/chat" && req.method === "POST") return safeHandler(handleForgeChat)(req, res);
|
|
132
|
+
if (pathname === "/api/forge/gateway/control" && req.method === "POST") return safeHandler(handleForgeGatewayControl)(req, res);
|
|
133
|
+
if (pathname === "/api/openclaw/env" && req.method === "GET") return safeHandler(handleOpenClawEnvGet)(req, res);
|
|
134
|
+
if (pathname === "/api/openclaw/env" && req.method === "POST") return safeHandler(handleOpenClawEnvSet)(req, res);
|
|
129
135
|
if (pathname === "/api/chat/stream") return safeHandler(handleChatStream)(req, res, reqUrl);
|
|
130
136
|
if (pathname === "/api/chat" && req.method === "GET") return safeHandler(handleChatGet)(req, res, reqUrl);
|
|
131
137
|
if (pathname === "/api/chat/rooms" && req.method === "GET") return safeHandler(handleChatRooms)(req, res, reqUrl);
|
|
@@ -173,7 +179,7 @@ function shutdown(signal) {
|
|
|
173
179
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
174
180
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
175
181
|
|
|
176
|
-
server.listen(PORT, BIND_HOST ||
|
|
182
|
+
server.listen(PORT, BIND_HOST || "0.0.0.0", () => {
|
|
177
183
|
logger.info({ port: PORT, bind: BIND_HOST || "0.0.0.0", corsOrigins: process.env.APE_CLAW_CORS_ORIGINS || "(default)" }, "Server listening");
|
|
178
184
|
console.log(`ape-claw telemetry server listening on http://localhost:${PORT}`);
|
|
179
185
|
console.log(`SSE stream: http://localhost:${PORT}/events`);
|
|
@@ -16,6 +16,11 @@ export function getRegistrationKey() { return REGISTRATION_KEY; }
|
|
|
16
16
|
export function getMoltbookAppKey() { return MOLTBOOK_APP_KEY; }
|
|
17
17
|
export function getMoltbookApiBase() { return MOLTBOOK_API_BASE; }
|
|
18
18
|
|
|
19
|
+
function isLocalRequest(req) {
|
|
20
|
+
const ip = String(req?.socket?.remoteAddress || "");
|
|
21
|
+
return ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1";
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
export function requireSkillWriteAuth(req) {
|
|
20
25
|
const adminKey = String(req.headers["x-registration-key"] || "").trim();
|
|
21
26
|
if (adminKey && REGISTRATION_KEY && adminKey === REGISTRATION_KEY) {
|
|
@@ -29,6 +34,11 @@ export function requireSkillWriteAuth(req) {
|
|
|
29
34
|
if (v?.verified) return { ok: true, mode: "agent", agentId };
|
|
30
35
|
} catch {}
|
|
31
36
|
}
|
|
37
|
+
// Local Forge installs are allowed without clawbot credentials.
|
|
38
|
+
// Credentials remain optional for users who want global telemetry/dashboard posting.
|
|
39
|
+
if (isLocalRequest(req)) {
|
|
40
|
+
return { ok: true, mode: "local", agentId: "local-forge" };
|
|
41
|
+
}
|
|
32
42
|
return { ok: false, mode: "none", agentId: null };
|
|
33
43
|
}
|
|
34
44
|
|