forbocai 0.1.0 → 0.1.1
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 +29 -0
- package/dist/cli.js +288 -7
- package/dist/cli.mjs +288 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,35 @@ Autonomous AI for game NPCs.
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
## 🚀 Publishing to NPM (Quick Reference)
|
|
26
|
+
|
|
27
|
+
`Públish_Séquence // NPM_Deploý`
|
|
28
|
+
|
|
29
|
+
To publish a new version to NPM:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 1. Bump version
|
|
33
|
+
npm version <major|minor|patch> --no-git-tag-version
|
|
34
|
+
|
|
35
|
+
# 2. Build
|
|
36
|
+
npm run build
|
|
37
|
+
|
|
38
|
+
# 3. Publish (use token from api/NPM_TOKEN.md)
|
|
39
|
+
echo "//registry.npmjs.org/:_authToken=<TOKEN_FROM_API_NPM_TOKEN_MD>" > .npmrc
|
|
40
|
+
npm publish --access public
|
|
41
|
+
|
|
42
|
+
# 4. Clean up (IMPORTANT: don't commit .npmrc)
|
|
43
|
+
rm .npmrc
|
|
44
|
+
|
|
45
|
+
# 5. Commit and push
|
|
46
|
+
git add . && git commit -m "chore: release vX.X.X" && git push
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **Token Location**: The NPM token is stored in `../api/NPM_TOKEN.md`
|
|
50
|
+
> **Current Version**: Check `package.json` for the current version
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
25
54
|
## Overview
|
|
26
55
|
|
|
27
56
|
`Córe_Módules // SDK_Init`
|
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
// src/cli.ts
|
|
27
27
|
var import_http = __toESM(require("http"));
|
|
28
28
|
var import_https = __toESM(require("https"));
|
|
29
|
+
var readline = __toESM(require("readline"));
|
|
29
30
|
var DEFAULT_API_URL = "https://forbocai-api.onrender.com";
|
|
30
31
|
var API_URL = process.env.FORBOC_API_URL || DEFAULT_API_URL;
|
|
31
32
|
var args = process.argv.slice(2);
|
|
@@ -36,15 +37,32 @@ if (command === "api" && subcommand === "status") {
|
|
|
36
37
|
checkApiStatus();
|
|
37
38
|
} else if (command === "agent" && subcommand === "create") {
|
|
38
39
|
createAgent(subcommand2);
|
|
40
|
+
} else if (command === "agent" && subcommand === "list") {
|
|
41
|
+
listAgents();
|
|
42
|
+
} else if (command === "agent" && subcommand === "chat") {
|
|
43
|
+
chatWithAgent(subcommand2);
|
|
44
|
+
} else if (command === "agent" && subcommand === "delete") {
|
|
45
|
+
deleteAgent(subcommand2);
|
|
39
46
|
} else if (command === "soul" && subcommand === "export") {
|
|
40
47
|
exportSoul(subcommand2);
|
|
41
48
|
} else if (command === "soul" && subcommand === "import") {
|
|
42
49
|
importSoul(subcommand2);
|
|
50
|
+
} else if (command === "soul" && subcommand === "chat") {
|
|
51
|
+
chatWithSoul(subcommand2);
|
|
52
|
+
} else if (command === "ghost" && subcommand === "run") {
|
|
53
|
+
runGhost(subcommand2);
|
|
54
|
+
} else if (command === "ghost" && subcommand === "status") {
|
|
55
|
+
ghostStatus(subcommand2);
|
|
56
|
+
} else if (command === "ghost" && subcommand === "results") {
|
|
57
|
+
ghostResults(subcommand2);
|
|
58
|
+
} else if (command === "cortex" && subcommand === "models") {
|
|
59
|
+
listModels();
|
|
43
60
|
} else {
|
|
44
61
|
printUsage();
|
|
45
62
|
}
|
|
46
63
|
function checkApiStatus() {
|
|
47
64
|
console.log(`> Connecting to Neuro-Symbolic Grid...`);
|
|
65
|
+
console.log(`> API: ${API_URL}`);
|
|
48
66
|
const client = API_URL.startsWith("https") ? import_https.default : import_http.default;
|
|
49
67
|
client.get(`${API_URL}/status`, (res) => {
|
|
50
68
|
let data = "";
|
|
@@ -86,6 +104,95 @@ async function createAgent(persona) {
|
|
|
86
104
|
console.error(`> Failed to create agent:`, e);
|
|
87
105
|
}
|
|
88
106
|
}
|
|
107
|
+
async function listAgents() {
|
|
108
|
+
console.log(`> Listing agents...`);
|
|
109
|
+
try {
|
|
110
|
+
const res = await fetch(`${API_URL}/agents`, {
|
|
111
|
+
method: "GET",
|
|
112
|
+
headers: { "Content-Type": "application/json" }
|
|
113
|
+
});
|
|
114
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
115
|
+
const data = await res.json();
|
|
116
|
+
if (data.length === 0) {
|
|
117
|
+
console.log(`> No agents found.`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
console.log(`> Found ${data.length} agents:
|
|
121
|
+
`);
|
|
122
|
+
data.forEach((agent, i) => {
|
|
123
|
+
console.log(` ${i + 1}. \x1B[32m${agent.agentId}\x1B[0m`);
|
|
124
|
+
console.log(` Persona: ${agent.persona?.substring(0, 50)}...`);
|
|
125
|
+
console.log(` Mood: ${agent.mood || "unknown"}`);
|
|
126
|
+
console.log("");
|
|
127
|
+
});
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.error(`> Failed to list agents:`, e);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function deleteAgent(agentId) {
|
|
133
|
+
if (!agentId) {
|
|
134
|
+
console.error("Error: Agent ID required");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
console.log(`> Deleting Agent: ${agentId}...`);
|
|
138
|
+
try {
|
|
139
|
+
const res = await fetch(`${API_URL}/agents/${agentId}`, {
|
|
140
|
+
method: "DELETE",
|
|
141
|
+
headers: { "Content-Type": "application/json" }
|
|
142
|
+
});
|
|
143
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
144
|
+
console.log(`> Agent \x1B[31mdeleted\x1B[0m: ${agentId}`);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
console.error(`> Failed to delete agent:`, e);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function chatWithAgent(agentId) {
|
|
150
|
+
if (!agentId) {
|
|
151
|
+
console.error("Error: Agent ID required");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.log(`
|
|
155
|
+
\x1B[36m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\x1B[0m`);
|
|
156
|
+
console.log(`\x1B[36m\u2551 ForbocAI Agent Chat \u2551\x1B[0m`);
|
|
157
|
+
console.log(`\x1B[36m\u2551 Agent: ${agentId.substring(0, 28).padEnd(28)} \u2551\x1B[0m`);
|
|
158
|
+
console.log(`\x1B[36m\u2551 Type 'exit' to quit \u2551\x1B[0m`);
|
|
159
|
+
console.log(`\x1B[36m\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\x1B[0m
|
|
160
|
+
`);
|
|
161
|
+
const rl = readline.createInterface({
|
|
162
|
+
input: process.stdin,
|
|
163
|
+
output: process.stdout
|
|
164
|
+
});
|
|
165
|
+
const promptUser = () => {
|
|
166
|
+
rl.question("\x1B[33m> You: \x1B[0m", async (input) => {
|
|
167
|
+
if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
|
|
168
|
+
console.log("\n> Disconnecting from agent...");
|
|
169
|
+
rl.close();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
|
|
174
|
+
method: "POST",
|
|
175
|
+
headers: { "Content-Type": "application/json" },
|
|
176
|
+
body: JSON.stringify({
|
|
177
|
+
inputText: input,
|
|
178
|
+
context: { source: "cli" }
|
|
179
|
+
})
|
|
180
|
+
});
|
|
181
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
182
|
+
const data = await res.json();
|
|
183
|
+
console.log(`\x1B[32m> Agent: \x1B[0m${data.dialogue || data.response || "No response"}`);
|
|
184
|
+
if (data.actions && data.actions.length > 0) {
|
|
185
|
+
console.log(`\x1B[2m> Actions: ${data.actions.map((a) => a.type).join(", ")}\x1B[0m`);
|
|
186
|
+
}
|
|
187
|
+
console.log("");
|
|
188
|
+
} catch (e) {
|
|
189
|
+
console.error(`> Error: ${e}`);
|
|
190
|
+
}
|
|
191
|
+
promptUser();
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
promptUser();
|
|
195
|
+
}
|
|
89
196
|
async function exportSoul(agentId) {
|
|
90
197
|
if (!agentId) {
|
|
91
198
|
console.error("Error: Agent ID required");
|
|
@@ -128,12 +235,186 @@ async function importSoul(cid) {
|
|
|
128
235
|
console.error(`> Failed to import soul:`, e);
|
|
129
236
|
}
|
|
130
237
|
}
|
|
238
|
+
async function chatWithSoul(cid) {
|
|
239
|
+
if (!cid) {
|
|
240
|
+
console.error("Error: CID required");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
console.log(`> Waking Soul from cryo: ${cid}...`);
|
|
244
|
+
try {
|
|
245
|
+
const res = await fetch(`${API_URL}/agents/import`, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
headers: { "Content-Type": "application/json" },
|
|
248
|
+
body: JSON.stringify({ cidRef: cid })
|
|
249
|
+
});
|
|
250
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
251
|
+
const data = await res.json();
|
|
252
|
+
console.log(`> Soul awakened! Temporary Agent: ${data.agentId}`);
|
|
253
|
+
console.log(`> Persona: ${data.persona}
|
|
254
|
+
`);
|
|
255
|
+
await chatWithAgent(data.agentId);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
console.error(`> Failed to wake soul:`, e);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async function runGhost(suite) {
|
|
261
|
+
const testSuite = suite || "exploration";
|
|
262
|
+
console.log(`> Starting Ghost QA session...`);
|
|
263
|
+
console.log(`> Test Suite: ${testSuite}`);
|
|
264
|
+
try {
|
|
265
|
+
const res = await fetch(`${API_URL}/ghost/run`, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: { "Content-Type": "application/json" },
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
testSuite,
|
|
270
|
+
duration: 300
|
|
271
|
+
})
|
|
272
|
+
});
|
|
273
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
274
|
+
const data = await res.json();
|
|
275
|
+
console.log(`> Session Started!`);
|
|
276
|
+
console.log(`> Session ID: \x1B[36m${data.sessionId}\x1B[0m`);
|
|
277
|
+
console.log(`> Status: ${data.runStatus || "running"}`);
|
|
278
|
+
console.log(`
|
|
279
|
+
> To check status: forbocai ghost status ${data.sessionId}`);
|
|
280
|
+
console.log(`> To get results: forbocai ghost results ${data.sessionId}`);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
console.error(`> Failed to start Ghost session:`, e);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async function ghostStatus(sessionId) {
|
|
286
|
+
if (!sessionId) {
|
|
287
|
+
console.error("Error: Session ID required");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
console.log(`> Checking Ghost session: ${sessionId}...`);
|
|
291
|
+
try {
|
|
292
|
+
const res = await fetch(`${API_URL}/ghost/${sessionId}/status`, {
|
|
293
|
+
method: "GET",
|
|
294
|
+
headers: { "Content-Type": "application/json" }
|
|
295
|
+
});
|
|
296
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
297
|
+
const data = await res.json();
|
|
298
|
+
const statusColor = data.ghostStatus === "completed" ? "\x1B[32m" : data.ghostStatus === "failed" ? "\x1B[31m" : "\x1B[33m";
|
|
299
|
+
console.log(`> Status: ${statusColor}${data.ghostStatus?.toUpperCase()}\x1B[0m`);
|
|
300
|
+
console.log(`> Progress: ${data.ghostProgress || 0}%`);
|
|
301
|
+
console.log(`> Duration: ${data.ghostDuration || 0}s`);
|
|
302
|
+
console.log(`> Errors: ${data.ghostErrors || 0}`);
|
|
303
|
+
} catch (e) {
|
|
304
|
+
console.error(`> Failed to get Ghost status:`, e);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function ghostResults(sessionId) {
|
|
308
|
+
if (!sessionId) {
|
|
309
|
+
console.error("Error: Session ID required");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
console.log(`> Fetching Ghost results: ${sessionId}...`);
|
|
313
|
+
try {
|
|
314
|
+
const res = await fetch(`${API_URL}/ghost/${sessionId}/results`, {
|
|
315
|
+
method: "GET",
|
|
316
|
+
headers: { "Content-Type": "application/json" }
|
|
317
|
+
});
|
|
318
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
319
|
+
const data = await res.json();
|
|
320
|
+
console.log(`
|
|
321
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`);
|
|
322
|
+
console.log(`\u2551 Ghost QA Results \u2551`);
|
|
323
|
+
console.log(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
324
|
+
`);
|
|
325
|
+
const passRate = data.resultsTotalTests > 0 ? (data.resultsPassed / data.resultsTotalTests * 100).toFixed(1) : 0;
|
|
326
|
+
console.log(` Total Tests: ${data.resultsTotalTests}`);
|
|
327
|
+
console.log(` \x1B[32mPassed: ${data.resultsPassed}\x1B[0m`);
|
|
328
|
+
console.log(` \x1B[31mFailed: ${data.resultsFailed}\x1B[0m`);
|
|
329
|
+
console.log(` Pass Rate: ${passRate}%`);
|
|
330
|
+
console.log(` Duration: ${data.resultsDuration}ms`);
|
|
331
|
+
console.log(` Coverage: ${((data.resultsCoverage || 0) * 100).toFixed(1)}%`);
|
|
332
|
+
if (data.resultsMetrics) {
|
|
333
|
+
console.log(`
|
|
334
|
+
Metrics:`);
|
|
335
|
+
for (const [key, value] of data.resultsMetrics) {
|
|
336
|
+
console.log(` ${key}: ${value}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (data.resultsTests && data.resultsTests.length > 0) {
|
|
340
|
+
console.log(`
|
|
341
|
+
Tests:`);
|
|
342
|
+
data.resultsTests.forEach((test) => {
|
|
343
|
+
const icon = test.testPassed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
344
|
+
console.log(` ${icon} ${test.testName} (${test.testDuration}ms)`);
|
|
345
|
+
if (test.testError) {
|
|
346
|
+
console.log(` \x1B[31mError: ${test.testError}\x1B[0m`);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
} catch (e) {
|
|
351
|
+
console.error(`> Failed to get Ghost results:`, e);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function listModels() {
|
|
355
|
+
console.log(`> Fetching available models...`);
|
|
356
|
+
try {
|
|
357
|
+
const res = await fetch(`${API_URL}/cortex/models`, {
|
|
358
|
+
method: "GET",
|
|
359
|
+
headers: { "Content-Type": "application/json" }
|
|
360
|
+
});
|
|
361
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
362
|
+
const data = await res.json();
|
|
363
|
+
console.log(`
|
|
364
|
+
Available Models:
|
|
365
|
+
`);
|
|
366
|
+
data.forEach((model) => {
|
|
367
|
+
console.log(` \x1B[36m${model.id}\x1B[0m`);
|
|
368
|
+
console.log(` Name: ${model.name}`);
|
|
369
|
+
console.log(` Parameters: ${(model.parameters / 1e6).toFixed(0)}M`);
|
|
370
|
+
console.log(` Size: ${model.downloadSize}`);
|
|
371
|
+
console.log(` Capabilities: ${model.capabilities?.join(", ") || "N/A"}`);
|
|
372
|
+
console.log("");
|
|
373
|
+
});
|
|
374
|
+
} catch (e) {
|
|
375
|
+
console.error(`> Failed to list models:`, e);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
131
378
|
function printUsage() {
|
|
132
|
-
console.log(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
379
|
+
console.log(`
|
|
380
|
+
\x1B[36m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
381
|
+
\u2551 ForbocAI CLI v0.1.0 \u2551
|
|
382
|
+
\u2551 The Neuro-Symbolic Grid Interface \u2551
|
|
383
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\x1B[0m
|
|
384
|
+
|
|
385
|
+
\x1B[1mUsage:\x1B[0m
|
|
386
|
+
forbocai <command> <subcommand> [options]
|
|
387
|
+
|
|
388
|
+
\x1B[1mAPI Commands:\x1B[0m
|
|
389
|
+
api status Check API connection and version
|
|
390
|
+
|
|
391
|
+
\x1B[1mAgent Commands:\x1B[0m
|
|
392
|
+
agent create [persona] Create a new agent
|
|
393
|
+
agent list List all agents
|
|
394
|
+
agent chat <agentId> Interactive chat with an agent
|
|
395
|
+
agent delete <agentId> Delete an agent
|
|
396
|
+
|
|
397
|
+
\x1B[1mSoul Commands:\x1B[0m
|
|
398
|
+
soul export <agentId> Export agent to IPFS
|
|
399
|
+
soul import <cid> Import agent from IPFS
|
|
400
|
+
soul chat <cid> Wake and chat with a Soul
|
|
401
|
+
|
|
402
|
+
\x1B[1mGhost Commands:\x1B[0m
|
|
403
|
+
ghost run [suite] Start QA test session
|
|
404
|
+
ghost status <id> Check session progress
|
|
405
|
+
ghost results <id> Get test results
|
|
406
|
+
|
|
407
|
+
\x1B[1mCortex Commands:\x1B[0m
|
|
408
|
+
cortex models List available SLM models
|
|
409
|
+
|
|
410
|
+
\x1B[1mEnvironment:\x1B[0m
|
|
411
|
+
FORBOC_API_URL Override API URL (default: render)
|
|
412
|
+
|
|
413
|
+
\x1B[1mExamples:\x1B[0m
|
|
414
|
+
forbocai api status
|
|
415
|
+
forbocai agent create "A wise old wizard"
|
|
416
|
+
forbocai agent chat agent_abc123
|
|
417
|
+
forbocai ghost run exploration
|
|
418
|
+
forbocai soul chat QmXyz...
|
|
419
|
+
`);
|
|
139
420
|
}
|
package/dist/cli.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import http from "http";
|
|
5
5
|
import https from "https";
|
|
6
|
+
import * as readline from "readline";
|
|
6
7
|
var DEFAULT_API_URL = "https://forbocai-api.onrender.com";
|
|
7
8
|
var API_URL = process.env.FORBOC_API_URL || DEFAULT_API_URL;
|
|
8
9
|
var args = process.argv.slice(2);
|
|
@@ -13,15 +14,32 @@ if (command === "api" && subcommand === "status") {
|
|
|
13
14
|
checkApiStatus();
|
|
14
15
|
} else if (command === "agent" && subcommand === "create") {
|
|
15
16
|
createAgent(subcommand2);
|
|
17
|
+
} else if (command === "agent" && subcommand === "list") {
|
|
18
|
+
listAgents();
|
|
19
|
+
} else if (command === "agent" && subcommand === "chat") {
|
|
20
|
+
chatWithAgent(subcommand2);
|
|
21
|
+
} else if (command === "agent" && subcommand === "delete") {
|
|
22
|
+
deleteAgent(subcommand2);
|
|
16
23
|
} else if (command === "soul" && subcommand === "export") {
|
|
17
24
|
exportSoul(subcommand2);
|
|
18
25
|
} else if (command === "soul" && subcommand === "import") {
|
|
19
26
|
importSoul(subcommand2);
|
|
27
|
+
} else if (command === "soul" && subcommand === "chat") {
|
|
28
|
+
chatWithSoul(subcommand2);
|
|
29
|
+
} else if (command === "ghost" && subcommand === "run") {
|
|
30
|
+
runGhost(subcommand2);
|
|
31
|
+
} else if (command === "ghost" && subcommand === "status") {
|
|
32
|
+
ghostStatus(subcommand2);
|
|
33
|
+
} else if (command === "ghost" && subcommand === "results") {
|
|
34
|
+
ghostResults(subcommand2);
|
|
35
|
+
} else if (command === "cortex" && subcommand === "models") {
|
|
36
|
+
listModels();
|
|
20
37
|
} else {
|
|
21
38
|
printUsage();
|
|
22
39
|
}
|
|
23
40
|
function checkApiStatus() {
|
|
24
41
|
console.log(`> Connecting to Neuro-Symbolic Grid...`);
|
|
42
|
+
console.log(`> API: ${API_URL}`);
|
|
25
43
|
const client = API_URL.startsWith("https") ? https : http;
|
|
26
44
|
client.get(`${API_URL}/status`, (res) => {
|
|
27
45
|
let data = "";
|
|
@@ -63,6 +81,95 @@ async function createAgent(persona) {
|
|
|
63
81
|
console.error(`> Failed to create agent:`, e);
|
|
64
82
|
}
|
|
65
83
|
}
|
|
84
|
+
async function listAgents() {
|
|
85
|
+
console.log(`> Listing agents...`);
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(`${API_URL}/agents`, {
|
|
88
|
+
method: "GET",
|
|
89
|
+
headers: { "Content-Type": "application/json" }
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
if (data.length === 0) {
|
|
94
|
+
console.log(`> No agents found.`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log(`> Found ${data.length} agents:
|
|
98
|
+
`);
|
|
99
|
+
data.forEach((agent, i) => {
|
|
100
|
+
console.log(` ${i + 1}. \x1B[32m${agent.agentId}\x1B[0m`);
|
|
101
|
+
console.log(` Persona: ${agent.persona?.substring(0, 50)}...`);
|
|
102
|
+
console.log(` Mood: ${agent.mood || "unknown"}`);
|
|
103
|
+
console.log("");
|
|
104
|
+
});
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.error(`> Failed to list agents:`, e);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function deleteAgent(agentId) {
|
|
110
|
+
if (!agentId) {
|
|
111
|
+
console.error("Error: Agent ID required");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log(`> Deleting Agent: ${agentId}...`);
|
|
115
|
+
try {
|
|
116
|
+
const res = await fetch(`${API_URL}/agents/${agentId}`, {
|
|
117
|
+
method: "DELETE",
|
|
118
|
+
headers: { "Content-Type": "application/json" }
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
121
|
+
console.log(`> Agent \x1B[31mdeleted\x1B[0m: ${agentId}`);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(`> Failed to delete agent:`, e);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function chatWithAgent(agentId) {
|
|
127
|
+
if (!agentId) {
|
|
128
|
+
console.error("Error: Agent ID required");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.log(`
|
|
132
|
+
\x1B[36m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\x1B[0m`);
|
|
133
|
+
console.log(`\x1B[36m\u2551 ForbocAI Agent Chat \u2551\x1B[0m`);
|
|
134
|
+
console.log(`\x1B[36m\u2551 Agent: ${agentId.substring(0, 28).padEnd(28)} \u2551\x1B[0m`);
|
|
135
|
+
console.log(`\x1B[36m\u2551 Type 'exit' to quit \u2551\x1B[0m`);
|
|
136
|
+
console.log(`\x1B[36m\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\x1B[0m
|
|
137
|
+
`);
|
|
138
|
+
const rl = readline.createInterface({
|
|
139
|
+
input: process.stdin,
|
|
140
|
+
output: process.stdout
|
|
141
|
+
});
|
|
142
|
+
const promptUser = () => {
|
|
143
|
+
rl.question("\x1B[33m> You: \x1B[0m", async (input) => {
|
|
144
|
+
if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
|
|
145
|
+
console.log("\n> Disconnecting from agent...");
|
|
146
|
+
rl.close();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: { "Content-Type": "application/json" },
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
inputText: input,
|
|
155
|
+
context: { source: "cli" }
|
|
156
|
+
})
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
159
|
+
const data = await res.json();
|
|
160
|
+
console.log(`\x1B[32m> Agent: \x1B[0m${data.dialogue || data.response || "No response"}`);
|
|
161
|
+
if (data.actions && data.actions.length > 0) {
|
|
162
|
+
console.log(`\x1B[2m> Actions: ${data.actions.map((a) => a.type).join(", ")}\x1B[0m`);
|
|
163
|
+
}
|
|
164
|
+
console.log("");
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.error(`> Error: ${e}`);
|
|
167
|
+
}
|
|
168
|
+
promptUser();
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
promptUser();
|
|
172
|
+
}
|
|
66
173
|
async function exportSoul(agentId) {
|
|
67
174
|
if (!agentId) {
|
|
68
175
|
console.error("Error: Agent ID required");
|
|
@@ -105,12 +212,186 @@ async function importSoul(cid) {
|
|
|
105
212
|
console.error(`> Failed to import soul:`, e);
|
|
106
213
|
}
|
|
107
214
|
}
|
|
215
|
+
async function chatWithSoul(cid) {
|
|
216
|
+
if (!cid) {
|
|
217
|
+
console.error("Error: CID required");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
console.log(`> Waking Soul from cryo: ${cid}...`);
|
|
221
|
+
try {
|
|
222
|
+
const res = await fetch(`${API_URL}/agents/import`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: { "Content-Type": "application/json" },
|
|
225
|
+
body: JSON.stringify({ cidRef: cid })
|
|
226
|
+
});
|
|
227
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
228
|
+
const data = await res.json();
|
|
229
|
+
console.log(`> Soul awakened! Temporary Agent: ${data.agentId}`);
|
|
230
|
+
console.log(`> Persona: ${data.persona}
|
|
231
|
+
`);
|
|
232
|
+
await chatWithAgent(data.agentId);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
console.error(`> Failed to wake soul:`, e);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function runGhost(suite) {
|
|
238
|
+
const testSuite = suite || "exploration";
|
|
239
|
+
console.log(`> Starting Ghost QA session...`);
|
|
240
|
+
console.log(`> Test Suite: ${testSuite}`);
|
|
241
|
+
try {
|
|
242
|
+
const res = await fetch(`${API_URL}/ghost/run`, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: { "Content-Type": "application/json" },
|
|
245
|
+
body: JSON.stringify({
|
|
246
|
+
testSuite,
|
|
247
|
+
duration: 300
|
|
248
|
+
})
|
|
249
|
+
});
|
|
250
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
251
|
+
const data = await res.json();
|
|
252
|
+
console.log(`> Session Started!`);
|
|
253
|
+
console.log(`> Session ID: \x1B[36m${data.sessionId}\x1B[0m`);
|
|
254
|
+
console.log(`> Status: ${data.runStatus || "running"}`);
|
|
255
|
+
console.log(`
|
|
256
|
+
> To check status: forbocai ghost status ${data.sessionId}`);
|
|
257
|
+
console.log(`> To get results: forbocai ghost results ${data.sessionId}`);
|
|
258
|
+
} catch (e) {
|
|
259
|
+
console.error(`> Failed to start Ghost session:`, e);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function ghostStatus(sessionId) {
|
|
263
|
+
if (!sessionId) {
|
|
264
|
+
console.error("Error: Session ID required");
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
console.log(`> Checking Ghost session: ${sessionId}...`);
|
|
268
|
+
try {
|
|
269
|
+
const res = await fetch(`${API_URL}/ghost/${sessionId}/status`, {
|
|
270
|
+
method: "GET",
|
|
271
|
+
headers: { "Content-Type": "application/json" }
|
|
272
|
+
});
|
|
273
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
274
|
+
const data = await res.json();
|
|
275
|
+
const statusColor = data.ghostStatus === "completed" ? "\x1B[32m" : data.ghostStatus === "failed" ? "\x1B[31m" : "\x1B[33m";
|
|
276
|
+
console.log(`> Status: ${statusColor}${data.ghostStatus?.toUpperCase()}\x1B[0m`);
|
|
277
|
+
console.log(`> Progress: ${data.ghostProgress || 0}%`);
|
|
278
|
+
console.log(`> Duration: ${data.ghostDuration || 0}s`);
|
|
279
|
+
console.log(`> Errors: ${data.ghostErrors || 0}`);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
console.error(`> Failed to get Ghost status:`, e);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async function ghostResults(sessionId) {
|
|
285
|
+
if (!sessionId) {
|
|
286
|
+
console.error("Error: Session ID required");
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
console.log(`> Fetching Ghost results: ${sessionId}...`);
|
|
290
|
+
try {
|
|
291
|
+
const res = await fetch(`${API_URL}/ghost/${sessionId}/results`, {
|
|
292
|
+
method: "GET",
|
|
293
|
+
headers: { "Content-Type": "application/json" }
|
|
294
|
+
});
|
|
295
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
296
|
+
const data = await res.json();
|
|
297
|
+
console.log(`
|
|
298
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`);
|
|
299
|
+
console.log(`\u2551 Ghost QA Results \u2551`);
|
|
300
|
+
console.log(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
301
|
+
`);
|
|
302
|
+
const passRate = data.resultsTotalTests > 0 ? (data.resultsPassed / data.resultsTotalTests * 100).toFixed(1) : 0;
|
|
303
|
+
console.log(` Total Tests: ${data.resultsTotalTests}`);
|
|
304
|
+
console.log(` \x1B[32mPassed: ${data.resultsPassed}\x1B[0m`);
|
|
305
|
+
console.log(` \x1B[31mFailed: ${data.resultsFailed}\x1B[0m`);
|
|
306
|
+
console.log(` Pass Rate: ${passRate}%`);
|
|
307
|
+
console.log(` Duration: ${data.resultsDuration}ms`);
|
|
308
|
+
console.log(` Coverage: ${((data.resultsCoverage || 0) * 100).toFixed(1)}%`);
|
|
309
|
+
if (data.resultsMetrics) {
|
|
310
|
+
console.log(`
|
|
311
|
+
Metrics:`);
|
|
312
|
+
for (const [key, value] of data.resultsMetrics) {
|
|
313
|
+
console.log(` ${key}: ${value}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (data.resultsTests && data.resultsTests.length > 0) {
|
|
317
|
+
console.log(`
|
|
318
|
+
Tests:`);
|
|
319
|
+
data.resultsTests.forEach((test) => {
|
|
320
|
+
const icon = test.testPassed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
321
|
+
console.log(` ${icon} ${test.testName} (${test.testDuration}ms)`);
|
|
322
|
+
if (test.testError) {
|
|
323
|
+
console.log(` \x1B[31mError: ${test.testError}\x1B[0m`);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
} catch (e) {
|
|
328
|
+
console.error(`> Failed to get Ghost results:`, e);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async function listModels() {
|
|
332
|
+
console.log(`> Fetching available models...`);
|
|
333
|
+
try {
|
|
334
|
+
const res = await fetch(`${API_URL}/cortex/models`, {
|
|
335
|
+
method: "GET",
|
|
336
|
+
headers: { "Content-Type": "application/json" }
|
|
337
|
+
});
|
|
338
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
339
|
+
const data = await res.json();
|
|
340
|
+
console.log(`
|
|
341
|
+
Available Models:
|
|
342
|
+
`);
|
|
343
|
+
data.forEach((model) => {
|
|
344
|
+
console.log(` \x1B[36m${model.id}\x1B[0m`);
|
|
345
|
+
console.log(` Name: ${model.name}`);
|
|
346
|
+
console.log(` Parameters: ${(model.parameters / 1e6).toFixed(0)}M`);
|
|
347
|
+
console.log(` Size: ${model.downloadSize}`);
|
|
348
|
+
console.log(` Capabilities: ${model.capabilities?.join(", ") || "N/A"}`);
|
|
349
|
+
console.log("");
|
|
350
|
+
});
|
|
351
|
+
} catch (e) {
|
|
352
|
+
console.error(`> Failed to list models:`, e);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
108
355
|
function printUsage() {
|
|
109
|
-
console.log(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
356
|
+
console.log(`
|
|
357
|
+
\x1B[36m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
358
|
+
\u2551 ForbocAI CLI v0.1.0 \u2551
|
|
359
|
+
\u2551 The Neuro-Symbolic Grid Interface \u2551
|
|
360
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\x1B[0m
|
|
361
|
+
|
|
362
|
+
\x1B[1mUsage:\x1B[0m
|
|
363
|
+
forbocai <command> <subcommand> [options]
|
|
364
|
+
|
|
365
|
+
\x1B[1mAPI Commands:\x1B[0m
|
|
366
|
+
api status Check API connection and version
|
|
367
|
+
|
|
368
|
+
\x1B[1mAgent Commands:\x1B[0m
|
|
369
|
+
agent create [persona] Create a new agent
|
|
370
|
+
agent list List all agents
|
|
371
|
+
agent chat <agentId> Interactive chat with an agent
|
|
372
|
+
agent delete <agentId> Delete an agent
|
|
373
|
+
|
|
374
|
+
\x1B[1mSoul Commands:\x1B[0m
|
|
375
|
+
soul export <agentId> Export agent to IPFS
|
|
376
|
+
soul import <cid> Import agent from IPFS
|
|
377
|
+
soul chat <cid> Wake and chat with a Soul
|
|
378
|
+
|
|
379
|
+
\x1B[1mGhost Commands:\x1B[0m
|
|
380
|
+
ghost run [suite] Start QA test session
|
|
381
|
+
ghost status <id> Check session progress
|
|
382
|
+
ghost results <id> Get test results
|
|
383
|
+
|
|
384
|
+
\x1B[1mCortex Commands:\x1B[0m
|
|
385
|
+
cortex models List available SLM models
|
|
386
|
+
|
|
387
|
+
\x1B[1mEnvironment:\x1B[0m
|
|
388
|
+
FORBOC_API_URL Override API URL (default: render)
|
|
389
|
+
|
|
390
|
+
\x1B[1mExamples:\x1B[0m
|
|
391
|
+
forbocai api status
|
|
392
|
+
forbocai agent create "A wise old wizard"
|
|
393
|
+
forbocai agent chat agent_abc123
|
|
394
|
+
forbocai ghost run exploration
|
|
395
|
+
forbocai soul chat QmXyz...
|
|
396
|
+
`);
|
|
116
397
|
}
|