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.
@@ -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
+ }
@@ -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 || undefined, () => {
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