ape-claw 0.1.3 → 0.1.5
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 +36 -24
- package/docs/CLI_GUIDE.md +5 -0
- package/docs/GLOBAL_BACKEND.md +3 -1
- package/docs/PRODUCT_OVERVIEW.md +15 -1
- package/docs/STARTER_PACK.md +7 -5
- package/docs/SUPPORTED_NETWORKS.md +1 -1
- package/docs/operator/01-quickstart.md +100 -9
- package/docs/operator/02-dashboard.md +1 -1
- package/docs/operator/03-cli-reference.md +19 -7
- package/docs/operator/04-skills-library.md +13 -12
- package/docs/operator/06-deployment.md +60 -7
- package/docs/operator/09-env-reference.md +61 -0
- package/docs/social/STARTER_PACK_THREAD.md +3 -3
- package/package.json +1 -1
- package/src/cli.mjs +132 -16
- package/src/server/index.mjs +6 -2
- package/src/server/routes/forge-agent.mjs +820 -0
- package/src/server/routes/skills.mjs +39 -6
- package/src/server/storage/file-backend.mjs +31 -0
- package/src/telemetry-server.mjs +90 -4
- package/ui/forge/css/forge.css +30 -2
- package/ui/forge/index.html +20 -7
- package/ui/forge/js/forge-attachments.js +26 -38
- package/ui/forge/js/forge-chat.js +240 -20
- package/ui/forge/js/forge-data.js +76 -14
- package/ui/forge/js/forge-scene.js +1943 -391
- package/ui/index.html +53 -54
- package/ui/js/skills.js +57 -42
- package/ui/shared/sidebar-nav.js +1 -1
- package/ui/skills.html +80 -53
|
@@ -56,6 +56,39 @@ This document lists all environment variables used by ApeClaw, organized by comp
|
|
|
56
56
|
| `APE_CLAW_INVITE_MAX_USES` | Maximum uses per invite token | No | `5` |
|
|
57
57
|
| `APE_CLAW_POD_DIR` | Pod workspace directory path | No | Auto-detected |
|
|
58
58
|
|
|
59
|
+
## Forge Agent Variables
|
|
60
|
+
|
|
61
|
+
The forge agent auto-detects your LLM provider. Set **any one** of the API key variables below — the first one found is used.
|
|
62
|
+
|
|
63
|
+
**LLM Provider (set one):**
|
|
64
|
+
|
|
65
|
+
| Variable | Provider | Default Model |
|
|
66
|
+
|----------|----------|---------------|
|
|
67
|
+
| `PERPLEXITY_API_KEY` | Perplexity Sonar (web-grounded) | `sonar-pro` |
|
|
68
|
+
| `OPENAI_API_KEY` | OpenAI | `gpt-4o` |
|
|
69
|
+
| `ANTHROPIC_API_KEY` | Anthropic Claude | `claude-sonnet-4-20250514` |
|
|
70
|
+
| `GROQ_API_KEY` | Groq | `llama-3.3-70b-versatile` |
|
|
71
|
+
| `TOGETHER_API_KEY` | Together AI | `meta-llama/Llama-3.3-70B-Instruct-Turbo` |
|
|
72
|
+
| `OLLAMA_HOST` | Ollama (local, no key needed) | `llama3.2` |
|
|
73
|
+
|
|
74
|
+
**Explicit override (any OpenAI-compatible endpoint):**
|
|
75
|
+
|
|
76
|
+
| Variable | Description | Default |
|
|
77
|
+
|----------|-------------|---------|
|
|
78
|
+
| `FORGE_LLM_API_URL` | Full chat completions URL | Auto-detected from provider |
|
|
79
|
+
| `FORGE_LLM_API_KEY` | API key for custom endpoint | Auto-detected from provider |
|
|
80
|
+
| `FORGE_LLM_MODEL` | Model name override | Auto-detected from provider |
|
|
81
|
+
|
|
82
|
+
**Agent identity:**
|
|
83
|
+
|
|
84
|
+
| Variable | Description | Default |
|
|
85
|
+
|----------|-------------|---------|
|
|
86
|
+
| `FORGE_AGENT_ID` | ClawBot agent ID | `"the-clawllector"` |
|
|
87
|
+
| `FORGE_AGENT_TOKEN` | Pre-provisioned ClawBot token for verified identity | Auto-registers on startup |
|
|
88
|
+
| `FORGE_AGENT_NAME` | Display name shown in chat | `"The Clawllector"` |
|
|
89
|
+
|
|
90
|
+
Without any LLM key the endpoint returns 503 and the forge chat falls back to the basic `/api/chat` relay.
|
|
91
|
+
|
|
59
92
|
## External Service Variables
|
|
60
93
|
|
|
61
94
|
| Variable | Description | Required | Default |
|
|
@@ -117,6 +150,34 @@ export APE_CLAW_CORS_ORIGINS=https://apeclaw.ai
|
|
|
117
150
|
export APE_CLAW_STORAGE=file # or "sqlite"
|
|
118
151
|
```
|
|
119
152
|
|
|
153
|
+
### Forge Agent (Local) — pick any provider
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Option A: OpenAI
|
|
157
|
+
export OPENAI_API_KEY=sk-...
|
|
158
|
+
|
|
159
|
+
# Option B: Anthropic
|
|
160
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
161
|
+
|
|
162
|
+
# Option C: Perplexity
|
|
163
|
+
export PERPLEXITY_API_KEY=pplx-...
|
|
164
|
+
|
|
165
|
+
# Option D: Groq (free tier available)
|
|
166
|
+
export GROQ_API_KEY=gsk_...
|
|
167
|
+
|
|
168
|
+
# Option E: Local Ollama (no key needed)
|
|
169
|
+
export OLLAMA_HOST=http://localhost:11434
|
|
170
|
+
|
|
171
|
+
# Option F: Any OpenAI-compatible endpoint
|
|
172
|
+
export FORGE_LLM_API_URL=https://your-endpoint.com/v1/chat/completions
|
|
173
|
+
export FORGE_LLM_API_KEY=your-key
|
|
174
|
+
export FORGE_LLM_MODEL=your-model
|
|
175
|
+
|
|
176
|
+
# Optional identity overrides:
|
|
177
|
+
export FORGE_AGENT_NAME="My Bot"
|
|
178
|
+
export FORGE_AGENT_ID=my-forge-bot
|
|
179
|
+
```
|
|
180
|
+
|
|
120
181
|
### Pod Operations
|
|
121
182
|
|
|
122
183
|
```bash
|
|
@@ -15,7 +15,7 @@ Press Enter. 61 skills load in about four seconds.
|
|
|
15
15
|
That's it. You have a working agent. No browsing a marketplace for an hour.
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
npx ape-claw skill install
|
|
18
|
+
npx ape-claw skill install
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Here's what you actually get (thread)
|
|
@@ -152,8 +152,8 @@ Still in the library at apeclaw.ai/skills. Just not in the box you get on day on
|
|
|
152
152
|
|
|
153
153
|
The prompt defaults to yes, but you have options:
|
|
154
154
|
|
|
155
|
-
npx ape-claw skill install --
|
|
156
|
-
npx ape-claw skill install --
|
|
155
|
+
npx ape-claw skill install --starter-pack installs without asking
|
|
156
|
+
npx ape-claw skill install --no-starter-pack skips it
|
|
157
157
|
No flag means it asks you
|
|
158
158
|
|
|
159
159
|
You can install it later. You can also skip it entirely and pick from 10,000+ skills one at a time.
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -144,15 +144,15 @@ function installApeClawSkill(args) {
|
|
|
144
144
|
throw new Error(`Source skill missing at ${sourceSkillPath}`);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
const scope = String(args.scope || "
|
|
147
|
+
const scope = String(args.scope || "global").toLowerCase();
|
|
148
148
|
const explicitSkillsDir = args["skills-dir"] ? String(args["skills-dir"]) : "";
|
|
149
149
|
let skillsRoot;
|
|
150
150
|
if (explicitSkillsDir) {
|
|
151
151
|
skillsRoot = path.resolve(explicitSkillsDir);
|
|
152
152
|
if (skillsRoot.includes("\0")) throw new Error("Invalid skills-dir path");
|
|
153
153
|
}
|
|
154
|
-
else if (scope === "
|
|
155
|
-
else skillsRoot = path.join(
|
|
154
|
+
else if (scope === "local") skillsRoot = path.join(process.cwd(), ".cursor", "skills");
|
|
155
|
+
else skillsRoot = path.join(os.homedir(), ".openclaw", "skills");
|
|
156
156
|
|
|
157
157
|
const targetSkillDir = path.join(skillsRoot, "ape-claw");
|
|
158
158
|
const targetSkillPath = path.join(targetSkillDir, "SKILL.md");
|
|
@@ -211,6 +211,15 @@ function installApeClawSkill(args) {
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
function yamlSafe(val) {
|
|
215
|
+
const s = String(val);
|
|
216
|
+
if (!s) return '""';
|
|
217
|
+
if (/[:{}\[\]#&*!|>'"%@`,\n]/.test(s) || s.startsWith(" ") || s.endsWith(" ")) {
|
|
218
|
+
return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
219
|
+
}
|
|
220
|
+
return s;
|
|
221
|
+
}
|
|
222
|
+
|
|
214
223
|
function syncSkillToOpenClaw(cardObj, slug, skillsRoot) {
|
|
215
224
|
const s = String(slug || cardObj?.slug || cardObj?.name || "").toLowerCase().trim()
|
|
216
225
|
.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -218,11 +227,15 @@ function syncSkillToOpenClaw(cardObj, slug, skillsRoot) {
|
|
|
218
227
|
const rawDoc = String(cardObj?.documentation_md || "").trim();
|
|
219
228
|
const displayName = String(cardObj?.name || s).trim();
|
|
220
229
|
const versionValue = String(cardObj?.version || "1.0.0").trim();
|
|
221
|
-
|
|
230
|
+
let descriptionValue = String(cardObj?.description || "").trim();
|
|
231
|
+
// Strip embedded YAML frontmatter that some imported skills have in their description
|
|
232
|
+
if (descriptionValue.startsWith("---")) {
|
|
233
|
+
descriptionValue = descriptionValue.replace(/^---[\s\S]*?---\s*/, "").trim();
|
|
234
|
+
}
|
|
235
|
+
if (!descriptionValue) descriptionValue = String(cardObj?.desc || displayName);
|
|
222
236
|
const descOneLine = descriptionValue.replace(/\n/g, " ").slice(0, 300);
|
|
223
237
|
|
|
224
|
-
|
|
225
|
-
const openclawFrontmatter = `---\nname: ${s}\nversion: ${versionValue}\ndescription: ${descOneLine}\n---\n`;
|
|
238
|
+
const openclawFrontmatter = `---\nname: ${s}\nversion: ${yamlSafe(versionValue)}\ndescription: ${yamlSafe(descOneLine)}\n---\n`;
|
|
226
239
|
|
|
227
240
|
let content;
|
|
228
241
|
if (rawDoc) {
|
|
@@ -237,7 +250,8 @@ function syncSkillToOpenClaw(cardObj, slug, skillsRoot) {
|
|
|
237
250
|
fs.mkdirSync(skillDir, { recursive: true });
|
|
238
251
|
fs.writeFileSync(path.join(skillDir, "SKILL.md"), content, "utf8");
|
|
239
252
|
|
|
240
|
-
const
|
|
253
|
+
const homeDir = process.env.OPENCLAW_HOME || os.homedir();
|
|
254
|
+
const openclawWorkspaceSkills = path.join(homeDir, ".openclaw", "workspace", "skills", s);
|
|
241
255
|
try {
|
|
242
256
|
fs.mkdirSync(openclawWorkspaceSkills, { recursive: true });
|
|
243
257
|
fs.writeFileSync(path.join(openclawWorkspaceSkills, "SKILL.md"), content, "utf8");
|
|
@@ -365,6 +379,63 @@ function safeSkillVersion(v) {
|
|
|
365
379
|
return s;
|
|
366
380
|
}
|
|
367
381
|
|
|
382
|
+
const TRUSTED_SKILL_API_HOSTS = new Set(["apeclaw.ai", "www.apeclaw.ai", "api.apeclaw.ai"]);
|
|
383
|
+
|
|
384
|
+
function resolveSkillApiBase(args = {}) {
|
|
385
|
+
const explicit = String(args.api || "").trim();
|
|
386
|
+
const fromEnv = String(process.env.APE_CLAW_API_URL || "").trim();
|
|
387
|
+
const raw = explicit || fromEnv || "https://apeclaw.ai";
|
|
388
|
+
let u;
|
|
389
|
+
try {
|
|
390
|
+
u = new URL(raw);
|
|
391
|
+
} catch {
|
|
392
|
+
throw new Error(`Invalid APE_CLAW_API_URL: ${raw}`);
|
|
393
|
+
}
|
|
394
|
+
const isLoopback = u.hostname === "localhost" || u.hostname === "127.0.0.1";
|
|
395
|
+
if (u.protocol !== "https:" && !(isLoopback && Boolean(args["allow-insecure-api"]))) {
|
|
396
|
+
throw new Error("Remote skill API must use HTTPS. For local dev only, use --allow-insecure-api with localhost.");
|
|
397
|
+
}
|
|
398
|
+
if (!TRUSTED_SKILL_API_HOSTS.has(u.hostname) && !Boolean(args["allow-custom-api"])) {
|
|
399
|
+
throw new Error(`Untrusted skill API host: ${u.hostname}. Use --allow-custom-api to override.`);
|
|
400
|
+
}
|
|
401
|
+
return u.origin.replace(/\/+$/, "");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function assertRemoteSkillCardSafe({ requestedSlug, card, skillMeta = null, asJson = false, allowUnvetted = false, allowHighRisk = false }) {
|
|
405
|
+
if (!card || typeof card !== "object") throw new Error("Remote API returned invalid skill card object");
|
|
406
|
+
const normalizedRequested = toSlug(requestedSlug);
|
|
407
|
+
const normalizedCardSlug = toSlug(card.slug || card.name || "");
|
|
408
|
+
if (!normalizedCardSlug) throw new Error("Remote skill card missing slug/name");
|
|
409
|
+
if (normalizedCardSlug !== normalizedRequested) {
|
|
410
|
+
throw new Error(`Remote skill slug mismatch (requested=${normalizedRequested}, received=${normalizedCardSlug})`);
|
|
411
|
+
}
|
|
412
|
+
const version = safeSkillVersion(card.version || "1.0.0");
|
|
413
|
+
if (!version) throw new Error("Remote skill version is invalid");
|
|
414
|
+
const description = String(card.description || "").trim();
|
|
415
|
+
if (!description) throw new Error("Remote skill description is required");
|
|
416
|
+
const documentation = String(card.documentation_md || "");
|
|
417
|
+
if (Buffer.byteLength(documentation, "utf8") > 300_000) {
|
|
418
|
+
throw new Error("Remote skill documentation is too large (>300KB)");
|
|
419
|
+
}
|
|
420
|
+
if (/[\x00-\x08\x0B\x0C\x0E-\x1F]/.test(documentation)) {
|
|
421
|
+
throw new Error("Remote skill documentation contains control characters");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const metaRiskTierRaw = Number(skillMeta?.riskTier ?? card?.constraints?.riskTier ?? card?.riskTier ?? 2);
|
|
425
|
+
const metaRiskTier = Number.isFinite(metaRiskTierRaw) ? Math.max(1, Math.min(3, Math.round(metaRiskTierRaw))) : 2;
|
|
426
|
+
if (metaRiskTier >= 3 && !allowHighRisk) {
|
|
427
|
+
throw new Error(`Remote skill risk tier ${metaRiskTier} requires explicit --allow-high-risk`);
|
|
428
|
+
}
|
|
429
|
+
// If API metadata is present, require vetting by default.
|
|
430
|
+
if (skillMeta && skillMeta.vettedOk !== true && !allowUnvetted) {
|
|
431
|
+
throw new Error("Remote skill is not vetted. Use --allow-unvetted to install anyway.");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!asJson && skillMeta && skillMeta.vettedOk === true) {
|
|
435
|
+
console.log("\x1b[2m Security: vetted skill metadata confirmed by API.\x1b[0m");
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
368
439
|
function resolveBundledSkillFile(packageRoot, slug) {
|
|
369
440
|
const skillsDataDir = path.join(packageRoot, "data", "skills");
|
|
370
441
|
const target = path.join(skillsDataDir, `${slug}.json`);
|
|
@@ -389,16 +460,35 @@ function resolveBundledSkillFile(packageRoot, slug) {
|
|
|
389
460
|
return "";
|
|
390
461
|
}
|
|
391
462
|
|
|
392
|
-
async function fetchSkillFromApi(slug) {
|
|
393
|
-
const apiBase =
|
|
394
|
-
const url = `${apiBase}/api/skills
|
|
463
|
+
async function fetchSkillFromApi(slug, args = {}) {
|
|
464
|
+
const apiBase = resolveSkillApiBase(args);
|
|
465
|
+
const url = `${apiBase}/api/skills/get?slug=${encodeURIComponent(slug)}`;
|
|
395
466
|
try {
|
|
396
467
|
const res = await fetch(url, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(15000) });
|
|
397
|
-
if (!res.ok) return null;
|
|
468
|
+
if (!res.ok) return { card: null, skillMeta: null, url, apiBase };
|
|
398
469
|
const json = await res.json();
|
|
399
|
-
|
|
470
|
+
if (!json?.ok) return { card: null, skillMeta: null, url, apiBase };
|
|
471
|
+
|
|
472
|
+
const skillMeta = json?.skill && typeof json.skill === "object" ? json.skill : null;
|
|
473
|
+
// Prefer the full card JSON; fall back to synthesising a minimal card from index metadata
|
|
474
|
+
let card = json?.card && typeof json.card === "object" ? json.card : null;
|
|
475
|
+
if (!card && skillMeta) {
|
|
476
|
+
card = {
|
|
477
|
+
name: skillMeta.name || slug,
|
|
478
|
+
slug: skillMeta.slug || slug,
|
|
479
|
+
version: "1.0.0",
|
|
480
|
+
description: skillMeta.description || skillMeta.name || slug,
|
|
481
|
+
riskTier: skillMeta.riskTier ?? 2,
|
|
482
|
+
provenance: skillMeta.provenance || { publisher: "imported", signed: false },
|
|
483
|
+
constraints: { riskTier: skillMeta.riskTier ?? 2 },
|
|
484
|
+
documentation_md: skillMeta.description
|
|
485
|
+
? `# ${skillMeta.name || slug}\n\n${skillMeta.description}`
|
|
486
|
+
: `# ${skillMeta.name || slug}\n`,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return { card, skillMeta, url, apiBase };
|
|
400
490
|
} catch {
|
|
401
|
-
return null;
|
|
491
|
+
return { card: null, skillMeta: null, url, apiBase };
|
|
402
492
|
}
|
|
403
493
|
}
|
|
404
494
|
|
|
@@ -411,7 +501,8 @@ function resolveHumanizerDependencySlug(packageRoot) {
|
|
|
411
501
|
}
|
|
412
502
|
|
|
413
503
|
function installOpenClawSkillCard(cardObj, fallbackSlug = "") {
|
|
414
|
-
const
|
|
504
|
+
const homeDir = process.env.OPENCLAW_HOME || os.homedir();
|
|
505
|
+
const skillsRoot = path.join(homeDir, ".openclaw", "skills");
|
|
415
506
|
return syncSkillToOpenClaw(cardObj, fallbackSlug, skillsRoot);
|
|
416
507
|
}
|
|
417
508
|
|
|
@@ -607,7 +698,16 @@ async function main() {
|
|
|
607
698
|
} catch (bundledErr) {
|
|
608
699
|
// Not in bundled data — fetch from API
|
|
609
700
|
if (!asJson) console.log(`\x1b[2m Skill not bundled locally, fetching from API…\x1b[0m`);
|
|
610
|
-
|
|
701
|
+
let remote;
|
|
702
|
+
try {
|
|
703
|
+
remote = await fetchSkillFromApi(requestedSkillSlug, args);
|
|
704
|
+
} catch (apiErr) {
|
|
705
|
+
if (asJson) return print({ ok: false, error: `Skill "${requestedSkillSlug}" rejected: ${apiErr.message}` }, true);
|
|
706
|
+
console.error(`\x1b[31m ✗ ${apiErr.message}\x1b[0m`);
|
|
707
|
+
process.exitCode = 1;
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const card = remote?.card || null;
|
|
611
711
|
if (!card || typeof card !== "object" || (!card.name && !card.slug)) {
|
|
612
712
|
if (asJson) return print({ ok: false, error: `Skill "${requestedSkillSlug}" not found (bundled: ${bundledErr.message}, API: not found)` }, true);
|
|
613
713
|
console.error(`\x1b[31m ✗ Skill "${requestedSkillSlug}" not found in bundled library or API.\x1b[0m`);
|
|
@@ -615,6 +715,22 @@ async function main() {
|
|
|
615
715
|
process.exitCode = 1;
|
|
616
716
|
return;
|
|
617
717
|
}
|
|
718
|
+
try {
|
|
719
|
+
assertRemoteSkillCardSafe({
|
|
720
|
+
requestedSlug: requestedSkillSlug,
|
|
721
|
+
card,
|
|
722
|
+
skillMeta: remote?.skillMeta || null,
|
|
723
|
+
asJson,
|
|
724
|
+
allowUnvetted: Boolean(args["allow-unvetted"]),
|
|
725
|
+
allowHighRisk: Boolean(args["allow-high-risk"]),
|
|
726
|
+
});
|
|
727
|
+
} catch (safeErr) {
|
|
728
|
+
if (asJson) return print({ ok: false, error: safeErr.message }, true);
|
|
729
|
+
console.error(`\x1b[31m ✗ ${safeErr.message}\x1b[0m`);
|
|
730
|
+
console.error(`\x1b[2m Use --allow-unvetted and/or --allow-high-risk only if you trust this source.\x1b[0m`);
|
|
731
|
+
process.exitCode = 1;
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
618
734
|
// Write fetched card to a temp file and install it
|
|
619
735
|
const tmpDir = path.join(packageRoot, "data", "skills");
|
|
620
736
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
@@ -2184,7 +2300,7 @@ async function main() {
|
|
|
2184
2300
|
"bridge execute (autonomous)": "ape-claw bridge execute --request <requestId> --execute --autonomous --json",
|
|
2185
2301
|
"bridge status": "ape-claw bridge status --request <requestId> --json",
|
|
2186
2302
|
"allowlist audit": "ape-claw allowlist audit --json",
|
|
2187
|
-
"skill install": "ape-claw skill install [<slug>] [--scope local] [--starter-pack | --no-starter-pack] --json",
|
|
2303
|
+
"skill install": "ape-claw skill install [<slug>] [--scope local] [--starter-pack | --no-starter-pack] [--allow-unvetted] [--allow-high-risk] [--allow-custom-api] [--allow-insecure-api] --json",
|
|
2188
2304
|
"v2 skill mint": "ape-claw v2 skill mint --rpc <url> --privateKey 0x... --skillNft 0x... --registry 0x... [--parentId 0] [--royalty-receiver 0x... --royalty-bps 500] --json",
|
|
2189
2305
|
"v2 skill publish": "ape-claw v2 skill publish --rpc <url> --privateKey 0x... --registry 0x... --skillId <id> --file <skillcard.json> [--uri ipfs://...] [--riskTier 1] --json",
|
|
2190
2306
|
"v2 intent create": "ape-claw v2 intent create --rpc <url> --privateKey 0x... --intents 0x... --payload '{...}' [--expiresAt <unixSec>] --json",
|
package/src/server/index.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
handleChatStream, handleChatGet, handleChatRooms,
|
|
31
31
|
handleChatPost, handleChatReact,
|
|
32
32
|
} from "./routes/chat.mjs";
|
|
33
|
+
import { handleForgeChat, handleForgeStatus, initForgeAgent } from "./routes/forge-agent.mjs";
|
|
33
34
|
import { handleV2ReceiptGet, handleV2Config } from "./routes/v2.mjs";
|
|
34
35
|
import { handlePodStatus, handlePodStop, handlePodFiles, handleStarterPack } from "./routes/pod.mjs";
|
|
35
36
|
import {
|
|
@@ -41,7 +42,7 @@ import {
|
|
|
41
42
|
handleIndex, handleStaticFile,
|
|
42
43
|
} from "./routes/static.mjs";
|
|
43
44
|
|
|
44
|
-
const PORT = Number(process.env.APE_CLAW_UI_PORT || 8787);
|
|
45
|
+
const PORT = Number(process.env.PORT || process.env.APE_CLAW_UI_PORT || 8787);
|
|
45
46
|
const BIND_HOST = String(process.env.APE_CLAW_BIND_HOST || "").trim();
|
|
46
47
|
|
|
47
48
|
const RL_READ = { limit: 60, windowMs: 60_000, keyPrefix: "read" };
|
|
@@ -58,7 +59,8 @@ if (!process.env.OPENSEA_API_KEY) {
|
|
|
58
59
|
|
|
59
60
|
initStorage();
|
|
60
61
|
initSseBroadcast();
|
|
61
|
-
|
|
62
|
+
initForgeAgent();
|
|
63
|
+
logger.info("Storage initialized, SSE broadcast active, forge agent ready");
|
|
62
64
|
|
|
63
65
|
function safeHandler(fn) {
|
|
64
66
|
return (req, res, ...args) => {
|
|
@@ -122,6 +124,8 @@ const server = http.createServer((req, res) => {
|
|
|
122
124
|
if (pathname === "/api/invites/create" && req.method === "POST") return safeHandler(handleInviteCreate)(req, res);
|
|
123
125
|
if (pathname === "/api/clawbots/register" && req.method === "POST") return safeHandler(handleClawbotsRegister)(req, res);
|
|
124
126
|
if (pathname === "/api/events" && req.method === "POST") return safeHandler(handlePostEvent)(req, res);
|
|
127
|
+
if (pathname === "/api/forge/status" && req.method === "GET") return safeHandler(handleForgeStatus)(req, res);
|
|
128
|
+
if (pathname === "/api/forge/chat" && req.method === "POST") return safeHandler(handleForgeChat)(req, res);
|
|
125
129
|
if (pathname === "/api/chat/stream") return safeHandler(handleChatStream)(req, res, reqUrl);
|
|
126
130
|
if (pathname === "/api/chat" && req.method === "GET") return safeHandler(handleChatGet)(req, res, reqUrl);
|
|
127
131
|
if (pathname === "/api/chat/rooms" && req.method === "GET") return safeHandler(handleChatRooms)(req, res, reqUrl);
|