@forbocai/node 0.4.8 → 0.5.0

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/dist/cli.js ADDED
@@ -0,0 +1,932 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_http = __toESM(require("http"));
28
+ var import_https = __toESM(require("https"));
29
+ var readline = __toESM(require("readline"));
30
+ var fs = __toESM(require("fs"));
31
+ var path = __toESM(require("path"));
32
+ var VERSION = "0.4.10";
33
+ var DEFAULT_API_URL = "https://api.forboc.ai";
34
+ var CONFIG_PATH = path.join(process.env.HOME || ".", ".forbocai.json");
35
+ var config = {};
36
+ try {
37
+ if (fs.existsSync(CONFIG_PATH)) {
38
+ config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
39
+ }
40
+ } catch {
41
+ }
42
+ var API_URL = process.env.FORBOC_API_URL || config.apiUrl || DEFAULT_API_URL;
43
+ var args = process.argv.slice(2);
44
+ var command = args[0];
45
+ var subcommand = args[1];
46
+ var arg3 = args[2];
47
+ var arg4 = args[3];
48
+ var routes = {
49
+ // Meta
50
+ "version": () => console.log(`ForbocAI SDK v${VERSION}`),
51
+ "doctor": doctor,
52
+ // Config
53
+ "config_set": () => configSet(subcommand, arg3),
54
+ "config_get": () => configGet(subcommand),
55
+ "config_list": configList,
56
+ // API
57
+ "api_status": checkApiStatus,
58
+ // Cortex
59
+ "cortex_models": listModels,
60
+ "cortex_init": () => cortexInit(arg3),
61
+ // Agent
62
+ "agent_create": () => createAgent(arg3),
63
+ "agent_list": listAgents,
64
+ "agent_chat": () => chatWithAgent(arg3),
65
+ "agent_delete": () => deleteAgent(arg3),
66
+ "agent_state": () => agentState(arg3),
67
+ "agent_process": () => agentProcess(arg3, arg4),
68
+ "agent_update": () => agentUpdate(arg3, args.slice(3)),
69
+ // Soul
70
+ "soul_export": () => exportSoul(arg3),
71
+ "soul_import": () => importSoul(arg3),
72
+ "soul_chat": () => chatWithSoul(arg3),
73
+ "soul_list": soulList,
74
+ "soul_verify": () => soulVerify(arg3),
75
+ // Ghost
76
+ "ghost_run": () => runGhost(arg3),
77
+ "ghost_status": () => ghostStatus(arg3),
78
+ "ghost_results": () => ghostResults(arg3),
79
+ "ghost_stop": () => ghostStop(arg3),
80
+ "ghost_history": ghostHistory,
81
+ // Memory
82
+ "memory_list": () => memoryList(arg3),
83
+ "memory_recall": () => memoryRecall(arg3, arg4),
84
+ "memory_store": () => memoryStore(arg3, args.slice(3).join(" ")),
85
+ "memory_clear": () => memoryClear(arg3),
86
+ "memory_export": () => memoryExport(arg3),
87
+ // Bridge
88
+ "bridge_validate": () => bridgeValidate(arg3),
89
+ "bridge_rules": bridgeRules
90
+ };
91
+ var routeKey = subcommand ? `${command}_${subcommand}` : command;
92
+ if (routes[routeKey]) {
93
+ routes[routeKey]();
94
+ } else if (command === "config" && subcommand === "set") {
95
+ configSet(arg3, arg4);
96
+ } else if (command === "config" && subcommand === "get") {
97
+ configGet(arg3);
98
+ } else {
99
+ printUsage();
100
+ }
101
+ function doctor() {
102
+ console.log(`
103
+ \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`);
104
+ console.log(`\u2551 ForbocAI Doctor \u2551`);
105
+ 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\x1B[0m
106
+ `);
107
+ console.log(`Checking system...
108
+ `);
109
+ const nodeVersion = process.version;
110
+ const nodeMajor = parseInt(nodeVersion.slice(1).split(".")[0]);
111
+ const nodeOk = nodeMajor >= 18;
112
+ console.log(`${nodeOk ? "\u2713" : "\u2717"} Node.js: ${nodeVersion} ${nodeOk ? "" : "(requires 18+)"}`);
113
+ console.log(`\u2713 API URL: ${API_URL}`);
114
+ const configExists = fs.existsSync(CONFIG_PATH);
115
+ console.log(`${configExists ? "\u2713" : "\u25CB"} Config: ${configExists ? CONFIG_PATH : "Not configured (using defaults)"}`);
116
+ console.log(`
117
+ Testing API connection...`);
118
+ const client = API_URL.startsWith("https") ? import_https.default : import_http.default;
119
+ client.get(`${API_URL}/status`, (res) => {
120
+ if (res.statusCode === 200) {
121
+ console.log(`\x1B[32m\u2713 API: Online\x1B[0m`);
122
+ } else {
123
+ console.log(`\x1B[31m\u2717 API: Returned ${res.statusCode}\x1B[0m`);
124
+ }
125
+ console.log(`
126
+ \x1B[32mDiagnosis complete.\x1B[0m`);
127
+ }).on("error", (e) => {
128
+ console.log(`\x1B[31m\u2717 API: ${e.message}\x1B[0m`);
129
+ console.log(`
130
+ \x1B[33mTip: Check your internet connection or API URL\x1B[0m`);
131
+ });
132
+ }
133
+ function configSet(key, value) {
134
+ if (!key || !value) {
135
+ console.error("Usage: forbocai config set <key> <value>");
136
+ return;
137
+ }
138
+ config[key] = value;
139
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
140
+ console.log(`> Set ${key} = ${value}`);
141
+ }
142
+ function configGet(key) {
143
+ if (!key) {
144
+ console.error("Usage: forbocai config get <key>");
145
+ return;
146
+ }
147
+ console.log(config[key] || `(not set)`);
148
+ }
149
+ function configList() {
150
+ console.log(`
151
+ Configuration (${CONFIG_PATH}):
152
+ `);
153
+ if (Object.keys(config).length === 0) {
154
+ console.log(" (no configuration set)");
155
+ } else {
156
+ for (const [key, value] of Object.entries(config)) {
157
+ console.log(` ${key}: ${value}`);
158
+ }
159
+ }
160
+ console.log(`
161
+ Defaults:`);
162
+ console.log(` apiUrl: ${DEFAULT_API_URL}`);
163
+ }
164
+ function checkApiStatus() {
165
+ console.log(`> Connecting to Neuro-Symbolic Grid...`);
166
+ console.log(`> API: ${API_URL}`);
167
+ const client = API_URL.startsWith("https") ? import_https.default : import_http.default;
168
+ client.get(`${API_URL}/status`, (res) => {
169
+ let data = "";
170
+ res.on("data", (chunk) => data += chunk);
171
+ res.on("end", () => {
172
+ try {
173
+ if (res.statusCode !== 200) {
174
+ console.error(`> Error: Server returned ${res.statusCode}`);
175
+ process.exit(1);
176
+ }
177
+ const json = JSON.parse(data);
178
+ console.log(`> Status: \x1B[32m${json.status.toUpperCase()}\x1B[0m`);
179
+ console.log(`> Message: ${json.message}`);
180
+ console.log(`> Version: ${json.version}`);
181
+ } catch (e) {
182
+ console.error("> Error: Invalid JSON response from server.");
183
+ }
184
+ });
185
+ }).on("error", (e) => {
186
+ console.error(`> Error: ${e.message}`);
187
+ console.log(`> Hint: Run 'forbocai doctor' to diagnose`);
188
+ });
189
+ }
190
+ async function listModels() {
191
+ console.log(`> Fetching available models...`);
192
+ try {
193
+ const res = await fetch(`${API_URL}/cortex/models`);
194
+ if (!res.ok) throw new Error(res.statusText);
195
+ const data = await res.json();
196
+ console.log(`
197
+ Available Models:
198
+ `);
199
+ data.forEach((model) => {
200
+ console.log(` \x1B[36m${model.id || model.modelId}\x1B[0m`);
201
+ console.log(` Name: ${model.name || model.modelName}`);
202
+ console.log(` Parameters: ${((model.parameters || 0) / 1e6).toFixed(0)}M`);
203
+ console.log(` Size: ${model.downloadSize || "N/A"}`);
204
+ console.log(` Capabilities: ${model.capabilities?.join(", ") || "N/A"}`);
205
+ console.log("");
206
+ });
207
+ } catch (e) {
208
+ console.error(`> Failed to list models:`, e);
209
+ }
210
+ }
211
+ async function cortexInit(model) {
212
+ const modelId = model || "smollm2-135m";
213
+ console.log(`> Initializing Cortex with model: ${modelId}...`);
214
+ try {
215
+ const res = await fetch(`${API_URL}/cortex/init`, {
216
+ method: "POST",
217
+ headers: { "Content-Type": "application/json" },
218
+ body: JSON.stringify({ requestedModel: modelId })
219
+ });
220
+ if (!res.ok) throw new Error(res.statusText);
221
+ const data = await res.json();
222
+ console.log(`> Cortex Initialized!`);
223
+ console.log(`> Instance ID: \x1B[32m${data.cortexId || data.id}\x1B[0m`);
224
+ console.log(`> Model: ${data.model || modelId}`);
225
+ console.log(`> Status: ${data.status || "ready"}`);
226
+ } catch (e) {
227
+ console.error(`> Failed to initialize Cortex:`, e);
228
+ }
229
+ }
230
+ async function createAgent(persona) {
231
+ const p = persona || "Default Persona";
232
+ console.log(`> Creating Agent with persona: "${p}"...`);
233
+ try {
234
+ const res = await fetch(`${API_URL}/agents`, {
235
+ method: "POST",
236
+ headers: { "Content-Type": "application/json" },
237
+ body: JSON.stringify({ createPersona: p, cortexRef: "cli-user" })
238
+ });
239
+ if (!res.ok) throw new Error(res.statusText);
240
+ const data = await res.json();
241
+ console.log(`> Agent Created: \x1B[32m${data.agentId}\x1B[0m`);
242
+ console.log(`> Mood: ${data.mood}`);
243
+ } catch (e) {
244
+ console.error(`> Failed to create agent:`, e);
245
+ }
246
+ }
247
+ async function listAgents() {
248
+ console.log(`> Listing agents...`);
249
+ try {
250
+ const res = await fetch(`${API_URL}/agents`);
251
+ if (!res.ok) throw new Error(res.statusText);
252
+ const data = await res.json();
253
+ if (!data || data.length === 0) {
254
+ console.log(`> No agents found.`);
255
+ return;
256
+ }
257
+ console.log(`> Found ${data.length} agents:
258
+ `);
259
+ data.forEach((agent, i) => {
260
+ console.log(` ${i + 1}. \x1B[32m${agent.agentId}\x1B[0m`);
261
+ console.log(` Persona: ${(agent.persona || "").substring(0, 50)}...`);
262
+ console.log(` Mood: ${agent.mood || "unknown"}`);
263
+ console.log("");
264
+ });
265
+ } catch (e) {
266
+ console.error(`> Failed to list agents:`, e);
267
+ }
268
+ }
269
+ async function deleteAgent(agentId) {
270
+ if (!agentId) {
271
+ console.error("Error: Agent ID required");
272
+ return;
273
+ }
274
+ console.log(`> Deleting Agent: ${agentId}...`);
275
+ try {
276
+ const res = await fetch(`${API_URL}/agents/${agentId}`, { method: "DELETE" });
277
+ if (!res.ok) throw new Error(res.statusText);
278
+ console.log(`> Agent \x1B[31mdeleted\x1B[0m: ${agentId}`);
279
+ } catch (e) {
280
+ console.error(`> Failed to delete agent:`, e);
281
+ }
282
+ }
283
+ async function agentState(agentId) {
284
+ if (!agentId) {
285
+ console.error("Error: Agent ID required");
286
+ return;
287
+ }
288
+ console.log(`> Fetching state for Agent: ${agentId}...`);
289
+ try {
290
+ const res = await fetch(`${API_URL}/agents/${agentId}/state`);
291
+ if (!res.ok) throw new Error(res.statusText);
292
+ const data = await res.json();
293
+ console.log(`
294
+ Agent State:
295
+ `);
296
+ const mood = data.mood || data.stateMood || "unknown";
297
+ console.log(` Mood: \x1B[33m${mood}\x1B[0m`);
298
+ const inventory = data.inventory || data.stateInventory;
299
+ if (Array.isArray(inventory)) {
300
+ console.log(` Inventory: ${inventory.length > 0 ? inventory.join(", ") : "(empty)"}`);
301
+ } else if (inventory) {
302
+ console.log(` Inventory: ${JSON.stringify(inventory)}`);
303
+ }
304
+ const skills = data.skills || data.stateSkills;
305
+ if (skills && typeof skills === "object") {
306
+ console.log(` Skills:`);
307
+ for (const [skill, level] of Object.entries(skills)) {
308
+ console.log(` ${skill}: ${level}`);
309
+ }
310
+ }
311
+ const relationships = data.relationships || data.stateRelationships;
312
+ if (relationships && typeof relationships === "object") {
313
+ console.log(` Relationships:`);
314
+ for (const [entity, value] of Object.entries(relationships)) {
315
+ const indicator = value > 0 ? "\x1B[32m+" : "\x1B[31m";
316
+ console.log(` ${entity}: ${indicator}${value}\x1B[0m`);
317
+ }
318
+ }
319
+ } catch (e) {
320
+ console.error(`> Failed to get agent state:`, e);
321
+ }
322
+ }
323
+ async function agentProcess(agentId, input) {
324
+ if (!agentId) {
325
+ console.error("Error: Agent ID required");
326
+ return;
327
+ }
328
+ if (!input) {
329
+ console.error("Error: Input text required");
330
+ return;
331
+ }
332
+ try {
333
+ const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
334
+ method: "POST",
335
+ headers: { "Content-Type": "application/json" },
336
+ body: JSON.stringify({ input, context: [["source", "cli"]] })
337
+ });
338
+ if (!res.ok) throw new Error(res.statusText);
339
+ const data = await res.json();
340
+ console.log(`\x1B[32m${data.directive || data.dialogue || data.response || "No response"}\x1B[0m`);
341
+ if (data.instruction) {
342
+ console.log(`\x1B[2mInstruction: ${data.instruction}\x1B[0m`);
343
+ } else if (data.actions && data.actions.length > 0) {
344
+ console.log(`\x1B[2mActions: ${data.actions.map((a) => a.type).join(", ")}\x1B[0m`);
345
+ }
346
+ } catch (e) {
347
+ console.error(`> Error:`, e);
348
+ }
349
+ }
350
+ async function agentUpdate(agentId, updateArgs) {
351
+ if (!agentId) {
352
+ console.error("Error: Agent ID required");
353
+ return;
354
+ }
355
+ const updates = {};
356
+ for (let i = 0; i < updateArgs.length; i++) {
357
+ if (updateArgs[i] === "--mood" && updateArgs[i + 1]) {
358
+ updates.mood = updateArgs[++i];
359
+ } else if (updateArgs[i] === "--inventory" && updateArgs[i + 1]) {
360
+ updates.inventory = updateArgs[++i].split(",");
361
+ }
362
+ }
363
+ if (Object.keys(updates).length === 0) {
364
+ console.error("Usage: forbocai agent update <id> --mood <mood> [--inventory <item1,item2>]");
365
+ return;
366
+ }
367
+ console.log(`> Updating Agent: ${agentId}...`);
368
+ try {
369
+ const res = await fetch(`${API_URL}/agents/${agentId}/state`, {
370
+ method: "PATCH",
371
+ headers: { "Content-Type": "application/json" },
372
+ body: JSON.stringify({ stateUpdate: updates })
373
+ });
374
+ if (!res.ok) throw new Error(res.statusText);
375
+ console.log(`> Agent updated!`);
376
+ for (const [key, value] of Object.entries(updates)) {
377
+ console.log(` ${key}: ${value}`);
378
+ }
379
+ } catch (e) {
380
+ console.error(`> Failed to update agent:`, e);
381
+ }
382
+ }
383
+ async function chatWithAgent(agentId) {
384
+ if (!agentId) {
385
+ console.error("Error: Agent ID required");
386
+ return;
387
+ }
388
+ console.log(`
389
+ \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`);
390
+ console.log(`\x1B[36m\u2551 ForbocAI Agent Chat \u2551\x1B[0m`);
391
+ console.log(`\x1B[36m\u2551 Agent: ${agentId.substring(0, 28).padEnd(28)} \u2551\x1B[0m`);
392
+ console.log(`\x1B[36m\u2551 Type 'exit' to quit \u2551\x1B[0m`);
393
+ 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
394
+ `);
395
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
396
+ const promptUser = () => {
397
+ rl.question("\x1B[33m> You: \x1B[0m", async (input) => {
398
+ if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
399
+ console.log("\n> Disconnecting from agent...");
400
+ rl.close();
401
+ return;
402
+ }
403
+ try {
404
+ const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
405
+ method: "POST",
406
+ headers: { "Content-Type": "application/json" },
407
+ body: JSON.stringify({ input, context: [["source", "cli"]] })
408
+ });
409
+ if (!res.ok) throw new Error(res.statusText);
410
+ const data = await res.json();
411
+ console.log(`\x1B[32m> Agent: \x1B[0m${data.dialogue || data.response || "No response"}`);
412
+ if (data.actions && data.actions.length > 0) {
413
+ console.log(`\x1B[2m> Actions: ${data.actions.map((a) => a.type).join(", ")}\x1B[0m`);
414
+ }
415
+ console.log("");
416
+ } catch (e) {
417
+ console.error(`> Error: ${e}`);
418
+ }
419
+ promptUser();
420
+ });
421
+ };
422
+ promptUser();
423
+ }
424
+ async function exportSoul(agentId) {
425
+ if (!agentId) {
426
+ console.error("Error: Agent ID required");
427
+ return;
428
+ }
429
+ console.log(`> Exporting Soul for Agent: ${agentId}...`);
430
+ try {
431
+ const res = await fetch(`${API_URL}/agents/${agentId}/soul/export`, {
432
+ method: "POST",
433
+ headers: { "Content-Type": "application/json" },
434
+ body: JSON.stringify({ agentIdRef: agentId })
435
+ });
436
+ if (!res.ok) throw new Error(res.statusText);
437
+ const data = await res.json();
438
+ console.log(`> Soul Exported!`);
439
+ console.log(`> CID: \x1B[36m${data.cid}\x1B[0m`);
440
+ console.log(`> IPFS: ${data.ipfsUrl}`);
441
+ } catch (e) {
442
+ console.error(`> Failed to export soul:`, e);
443
+ }
444
+ }
445
+ async function importSoul(cid) {
446
+ if (!cid) {
447
+ console.error("Error: CID required");
448
+ return;
449
+ }
450
+ console.log(`> Importing Soul from CID: ${cid}...`);
451
+ try {
452
+ const res = await fetch(`${API_URL}/agents/import`, {
453
+ method: "POST",
454
+ headers: { "Content-Type": "application/json" },
455
+ body: JSON.stringify({ cidRef: cid })
456
+ });
457
+ if (!res.ok) throw new Error(res.statusText);
458
+ const data = await res.json();
459
+ console.log(`> Soul Imported!`);
460
+ console.log(`> New Agent ID: \x1B[32m${data.agentId}\x1B[0m`);
461
+ console.log(`> Persona: ${data.persona}`);
462
+ } catch (e) {
463
+ console.error(`> Failed to import soul:`, e);
464
+ }
465
+ }
466
+ async function chatWithSoul(cid) {
467
+ if (!cid) {
468
+ console.error("Error: CID required");
469
+ return;
470
+ }
471
+ console.log(`> Waking Soul from cryo: ${cid}...`);
472
+ try {
473
+ const res = await fetch(`${API_URL}/agents/import`, {
474
+ method: "POST",
475
+ headers: { "Content-Type": "application/json" },
476
+ body: JSON.stringify({ cidRef: cid })
477
+ });
478
+ if (!res.ok) throw new Error(res.statusText);
479
+ const data = await res.json();
480
+ console.log(`> Soul awakened! Temporary Agent: ${data.agentId}`);
481
+ console.log(`> Persona: ${data.persona}
482
+ `);
483
+ await chatWithAgent(data.agentId);
484
+ } catch (e) {
485
+ console.error(`> Failed to wake soul:`, e);
486
+ }
487
+ }
488
+ async function soulList() {
489
+ console.log(`> Listing exported Souls...`);
490
+ try {
491
+ const res = await fetch(`${API_URL}/souls?limit=50`);
492
+ if (!res.ok) throw new Error(res.statusText);
493
+ const data = await res.json();
494
+ const souls = data.souls || data;
495
+ if (!souls || souls.length === 0) {
496
+ console.log(`
497
+ No exported Souls found.`);
498
+ console.log(` Use 'soul export <agentId>' to create a new Soul`);
499
+ return;
500
+ }
501
+ console.log(`
502
+ Exported Souls (${souls.length}):
503
+ `);
504
+ souls.forEach((soul, i) => {
505
+ console.log(` ${i + 1}. ${soul.soulName || soul.name || "Unknown"}`);
506
+ console.log(` CID: ${soul.soulId || soul.cid}`);
507
+ console.log(` IPFS: ipfs://${soul.soulId || soul.cid}`);
508
+ console.log(``);
509
+ });
510
+ } catch (e) {
511
+ console.log(`
512
+ Exported Souls:
513
+ `);
514
+ console.log(` (Soul listing requires API endpoint)`);
515
+ console.log(` Use 'soul export <agentId>' to create a new Soul`);
516
+ }
517
+ }
518
+ async function soulVerify(cid) {
519
+ if (!cid) {
520
+ console.error("Error: CID required");
521
+ return;
522
+ }
523
+ console.log(`> Verifying Soul: ${cid}...`);
524
+ try {
525
+ const res = await fetch(`${API_URL}/souls/${cid}`);
526
+ if (!res.ok) throw new Error(res.statusText);
527
+ const data = await res.json();
528
+ console.log(`> Soul Verified!`);
529
+ console.log(`> Name: ${data.soulName}`);
530
+ console.log(`> DNA: ${(data.dna || "").substring(0, 50)}...`);
531
+ console.log(`> Signature: ${data.signature ? "\x1B[32m\u2713 Valid\x1B[0m" : "\x1B[33m\u25CB Not signed\x1B[0m"}`);
532
+ } catch (e) {
533
+ console.error(`> Failed to verify soul:`, e);
534
+ }
535
+ }
536
+ async function runGhost(suite) {
537
+ const testSuite = suite || "exploration";
538
+ console.log(`> Starting Ghost QA session...`);
539
+ console.log(`> Test Suite: ${testSuite}`);
540
+ try {
541
+ const res = await fetch(`${API_URL}/ghost/run`, {
542
+ method: "POST",
543
+ headers: { "Content-Type": "application/json" },
544
+ body: JSON.stringify({ testSuite, duration: 300 })
545
+ });
546
+ if (!res.ok) throw new Error(res.statusText);
547
+ const data = await res.json();
548
+ console.log(`> Session Started!`);
549
+ console.log(`> Session ID: \x1B[36m${data.sessionId}\x1B[0m`);
550
+ console.log(`> Status: ${data.runStatus || "running"}`);
551
+ console.log(`
552
+ > To check status: forbocai ghost status ${data.sessionId}`);
553
+ console.log(`> To get results: forbocai ghost results ${data.sessionId}`);
554
+ } catch (e) {
555
+ console.error(`> Failed to start Ghost session:`, e);
556
+ }
557
+ }
558
+ async function ghostStatus(sessionId) {
559
+ if (!sessionId) {
560
+ console.error("Error: Session ID required");
561
+ return;
562
+ }
563
+ console.log(`> Checking Ghost session: ${sessionId}...`);
564
+ try {
565
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/status`);
566
+ if (!res.ok) throw new Error(res.statusText);
567
+ const data = await res.json();
568
+ const statusColor = data.ghostStatus === "completed" ? "\x1B[32m" : data.ghostStatus === "failed" ? "\x1B[31m" : "\x1B[33m";
569
+ console.log(`> Status: ${statusColor}${(data.ghostStatus || "unknown").toUpperCase()}\x1B[0m`);
570
+ console.log(`> Progress: ${data.ghostProgress || 0}%`);
571
+ console.log(`> Duration: ${data.ghostDuration || 0}s`);
572
+ console.log(`> Errors: ${data.ghostErrors || 0}`);
573
+ } catch (e) {
574
+ console.error(`> Failed to get Ghost status:`, e);
575
+ }
576
+ }
577
+ async function ghostResults(sessionId) {
578
+ if (!sessionId) {
579
+ console.error("Error: Session ID required");
580
+ return;
581
+ }
582
+ console.log(`> Fetching Ghost results: ${sessionId}...`);
583
+ try {
584
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/results`);
585
+ if (!res.ok) throw new Error(res.statusText);
586
+ const data = await res.json();
587
+ console.log(`
588
+ \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`);
589
+ console.log(`\u2551 Ghost QA Results \u2551`);
590
+ 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
591
+ `);
592
+ const passRate = data.resultsTotalTests > 0 ? (data.resultsPassed / data.resultsTotalTests * 100).toFixed(1) : 0;
593
+ console.log(` Total Tests: ${data.resultsTotalTests}`);
594
+ console.log(` \x1B[32mPassed: ${data.resultsPassed}\x1B[0m`);
595
+ console.log(` \x1B[31mFailed: ${data.resultsFailed}\x1B[0m`);
596
+ console.log(` Pass Rate: ${passRate}%`);
597
+ console.log(` Duration: ${data.resultsDuration}ms`);
598
+ console.log(` Coverage: ${((data.resultsCoverage || 0) * 100).toFixed(1)}%`);
599
+ if (data.resultsMetrics) {
600
+ console.log(`
601
+ Metrics:`);
602
+ for (const [key, value] of data.resultsMetrics) {
603
+ console.log(` ${key}: ${value}`);
604
+ }
605
+ }
606
+ if (data.resultsTests && data.resultsTests.length > 0) {
607
+ console.log(`
608
+ Tests:`);
609
+ data.resultsTests.forEach((test) => {
610
+ const icon = test.testPassed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
611
+ console.log(` ${icon} ${test.testName} (${test.testDuration}ms)`);
612
+ if (test.testError) {
613
+ console.log(` \x1B[31mError: ${test.testError}\x1B[0m`);
614
+ }
615
+ });
616
+ }
617
+ } catch (e) {
618
+ console.error(`> Failed to get Ghost results:`, e);
619
+ }
620
+ }
621
+ async function ghostStop(sessionId) {
622
+ if (!sessionId) {
623
+ console.error("Error: Session ID required");
624
+ return;
625
+ }
626
+ console.log(`> Stopping Ghost session: ${sessionId}...`);
627
+ try {
628
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/stop`, {
629
+ method: "POST",
630
+ headers: { "Content-Type": "application/json" }
631
+ });
632
+ if (!res.ok) throw new Error(res.statusText);
633
+ const data = await res.json();
634
+ console.log(`> Session stop requested`);
635
+ console.log(`> Status: \x1B[33m${data.status || "Stopped"}\x1B[0m`);
636
+ } catch (e) {
637
+ console.log(`> Session stop requested`);
638
+ console.log(`> Status: \x1B[33mStopped\x1B[0m`);
639
+ }
640
+ }
641
+ async function ghostHistory() {
642
+ console.log(`> Fetching Ghost session history...`);
643
+ try {
644
+ const res = await fetch(`${API_URL}/ghost/history?limit=10`);
645
+ if (!res.ok) throw new Error(res.statusText);
646
+ const data = await res.json();
647
+ const sessions = data.sessions || data;
648
+ if (!sessions || sessions.length === 0) {
649
+ console.log(`
650
+ No session history found.`);
651
+ console.log(` Use 'ghost run <suite>' to start a new session`);
652
+ return;
653
+ }
654
+ console.log(`
655
+ Recent Sessions:
656
+ `);
657
+ sessions.forEach((s, i) => {
658
+ const statusColor = s.status === "completed" ? "\x1B[32m" : s.status === "failed" ? "\x1B[31m" : "\x1B[33m";
659
+ console.log(` ${i + 1}. ${s.sessionId}`);
660
+ console.log(` Suite: ${s.testSuite}`);
661
+ console.log(` Status: ${statusColor}${s.status}\x1B[0m`);
662
+ if (s.passRate !== void 0) {
663
+ console.log(` Pass Rate: ${(s.passRate * 100).toFixed(1)}%`);
664
+ }
665
+ console.log(``);
666
+ });
667
+ } catch (e) {
668
+ console.log(`
669
+ Recent Sessions:
670
+ `);
671
+ console.log(` (Session history requires API endpoint)`);
672
+ console.log(` Use 'ghost run <suite>' to start a new session`);
673
+ }
674
+ }
675
+ async function memoryList(agentId) {
676
+ if (!agentId) {
677
+ console.error("Error: Agent ID required");
678
+ return;
679
+ }
680
+ console.log(`> Listing memories for Agent: ${agentId}...`);
681
+ try {
682
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory`);
683
+ if (!res.ok) throw new Error(res.statusText);
684
+ const data = await res.json();
685
+ const memories = data.memories || data;
686
+ if (!memories || memories.length === 0) {
687
+ console.log(`> No memories found.`);
688
+ return;
689
+ }
690
+ console.log(`
691
+ Memories (${memories.length}):
692
+ `);
693
+ memories.slice(0, 20).forEach((mem, i) => {
694
+ const importance = mem.importance || mem.memImportance || 0.5;
695
+ const bar = "\u2588".repeat(Math.floor(importance * 10));
696
+ console.log(` ${i + 1}. [${bar.padEnd(10)}] ${(mem.text || mem.memText || "").substring(0, 50)}...`);
697
+ });
698
+ if (memories.length > 20) {
699
+ console.log(`
700
+ ... and ${memories.length - 20} more`);
701
+ }
702
+ } catch (e) {
703
+ console.error(`> Failed to list memories:`, e);
704
+ }
705
+ }
706
+ async function memoryRecall(agentId, query) {
707
+ if (!agentId) {
708
+ console.error("Error: Agent ID required");
709
+ return;
710
+ }
711
+ if (!query) {
712
+ console.error("Error: Query required");
713
+ return;
714
+ }
715
+ console.log(`> Recalling memories for: "${query}"...`);
716
+ try {
717
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory/recall`, {
718
+ method: "POST",
719
+ headers: { "Content-Type": "application/json" },
720
+ body: JSON.stringify({ query, similarity: 0.8 })
721
+ });
722
+ if (!res.ok) throw new Error(res.statusText);
723
+ const data = await res.json();
724
+ const memories = data.recalledMemories || data;
725
+ if (!memories || memories.length === 0) {
726
+ console.log(`> No relevant memories found.`);
727
+ return;
728
+ }
729
+ console.log(`
730
+ Relevant Memories:
731
+ `);
732
+ memories.forEach((mem, i) => {
733
+ const score = mem.score || mem.relevance || "N/A";
734
+ console.log(` ${i + 1}. [Score: ${score}] ${(mem.content || mem.text || mem.memText || "").substring(0, 60)}...`);
735
+ });
736
+ } catch (e) {
737
+ console.error(`> Failed to recall memories:`, e);
738
+ }
739
+ }
740
+ async function memoryStore(agentId, text) {
741
+ if (!agentId) {
742
+ console.error("Error: Agent ID required");
743
+ return;
744
+ }
745
+ if (!text) {
746
+ console.error("Error: Memory text required");
747
+ return;
748
+ }
749
+ console.log(`> Storing memory...`);
750
+ try {
751
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory`, {
752
+ method: "POST",
753
+ headers: { "Content-Type": "application/json" },
754
+ body: JSON.stringify({ observation: text, importance: 0.5 })
755
+ });
756
+ if (!res.ok) throw new Error(res.statusText);
757
+ const data = await res.json();
758
+ console.log(`> Memory stored!`);
759
+ console.log(`> ID: ${data.memoryId || data.storedId || data.id}`);
760
+ } catch (e) {
761
+ console.error(`> Failed to store memory:`, e);
762
+ }
763
+ }
764
+ async function memoryClear(agentId) {
765
+ if (!agentId) {
766
+ console.error("Error: Agent ID required");
767
+ return;
768
+ }
769
+ console.log(`> Clearing memories for Agent: ${agentId}...`);
770
+ console.log(`> \x1B[31mThis action cannot be undone!\x1B[0m`);
771
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
772
+ rl.question('Type "CONFIRM" to proceed: ', async (answer) => {
773
+ if (answer !== "CONFIRM") {
774
+ console.log("> Aborted.");
775
+ rl.close();
776
+ return;
777
+ }
778
+ try {
779
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory/clear`, {
780
+ method: "DELETE",
781
+ headers: { "Content-Type": "application/json" }
782
+ });
783
+ if (res.ok) {
784
+ console.log(`> Memories cleared`);
785
+ } else {
786
+ console.log(`> Memories cleared (local)`);
787
+ }
788
+ } catch (e) {
789
+ console.log(`> Memories cleared (local)`);
790
+ }
791
+ rl.close();
792
+ });
793
+ }
794
+ async function memoryExport(agentId) {
795
+ if (!agentId) {
796
+ console.error("Error: Agent ID required");
797
+ return;
798
+ }
799
+ console.log(`> Exporting memories for Agent: ${agentId}...`);
800
+ try {
801
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory`);
802
+ if (!res.ok) throw new Error(res.statusText);
803
+ const data = await res.json();
804
+ const filename = `memories_${agentId}_${Date.now()}.json`;
805
+ fs.writeFileSync(filename, JSON.stringify(data, null, 2));
806
+ console.log(`> Memories exported to: ${filename}`);
807
+ } catch (e) {
808
+ console.error(`> Failed to export memories:`, e);
809
+ }
810
+ }
811
+ async function bridgeValidate(actionFile) {
812
+ if (!actionFile) {
813
+ console.error("Error: Action file (JSON) required");
814
+ return;
815
+ }
816
+ console.log(`> Validating action from: ${actionFile}...`);
817
+ try {
818
+ const actionJson = fs.readFileSync(actionFile, "utf-8");
819
+ const action = JSON.parse(actionJson);
820
+ const res = await fetch(`${API_URL}/bridge/validate`, {
821
+ method: "POST",
822
+ headers: { "Content-Type": "application/json" },
823
+ body: JSON.stringify({ actionType: action.type, payload: JSON.stringify(action.payload || {}) })
824
+ });
825
+ if (!res.ok) throw new Error(res.statusText);
826
+ const data = await res.json();
827
+ if (data.validationIsValid || data.valid) {
828
+ console.log(`> \x1B[32m\u2713 Action is VALID\x1B[0m`);
829
+ } else {
830
+ console.log(`> \x1B[31m\u2717 Action is INVALID\x1B[0m`);
831
+ console.log(`> Reason: ${data.validationReason || data.reason}`);
832
+ }
833
+ } catch (e) {
834
+ console.error(`> Failed to validate action:`, e);
835
+ }
836
+ }
837
+ async function bridgeRules() {
838
+ console.log(`> Fetching validation rules...`);
839
+ console.log(`
840
+ Built-in Rules:
841
+ `);
842
+ console.log(` \x1B[36mcore.movement\x1B[0m`);
843
+ console.log(` Actions: MOVE, WALK, RUN`);
844
+ console.log(` Validates: Coordinates, world bounds`);
845
+ console.log("");
846
+ console.log(` \x1B[36mcore.attack\x1B[0m`);
847
+ console.log(` Actions: ATTACK, HIT, STRIKE`);
848
+ console.log(` Validates: Target exists`);
849
+ console.log("");
850
+ console.log(` \x1B[36mcore.interact\x1B[0m`);
851
+ console.log(` Actions: INTERACT, USE, PICKUP`);
852
+ console.log(` Validates: Object specified`);
853
+ console.log("");
854
+ console.log(` \x1B[36mcore.speak\x1B[0m`);
855
+ console.log(` Actions: SPEAK, SAY, SHOUT`);
856
+ console.log(` Validates: Text not empty`);
857
+ console.log("");
858
+ console.log(` \x1B[36mcore.resources\x1B[0m`);
859
+ console.log(` Actions: (all)`);
860
+ console.log(` Validates: HP > 0, sufficient mana`);
861
+ }
862
+ function printUsage() {
863
+ console.log(`
864
+ \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
865
+ \u2551 ForbocAI CLI v${VERSION} \u2551
866
+ \u2551 The Neuro-Symbolic Grid Interface \u2551
867
+ \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
868
+
869
+ \x1B[1mUsage:\x1B[0m
870
+ forbocai <command> <subcommand> [options]
871
+
872
+ \x1B[1mMeta:\x1B[0m
873
+ version Show SDK version
874
+ doctor Diagnose SDK/API issues
875
+
876
+ \x1B[1mConfig:\x1B[0m
877
+ config set <k> <v> Set configuration value
878
+ config get <key> Get configuration value
879
+ config list List all configuration
880
+
881
+ \x1B[1mAPI:\x1B[0m
882
+ api status Check API connection
883
+
884
+ \x1B[1mCortex:\x1B[0m
885
+ cortex models List available SLM models
886
+ cortex init [model] Initialize Cortex with model
887
+
888
+ \x1B[1mAgent:\x1B[0m
889
+ agent create [persona] Create a new agent
890
+ agent list List all agents
891
+ agent chat <id> Interactive chat with agent
892
+ agent state <id> View agent's current state
893
+ agent process <id> <t> One-shot process input
894
+ agent update <id> ... Update agent state
895
+ agent delete <id> Delete an agent
896
+
897
+ \x1B[1mMemory:\x1B[0m
898
+ memory list <agentId> List agent's memories
899
+ memory recall <id> <q> Semantic search memories
900
+ memory store <id> <t> Store new observation
901
+ memory clear <id> Clear all memories
902
+ memory export <id> Export memories to JSON
903
+
904
+ \x1B[1mBridge:\x1B[0m
905
+ bridge validate <file> Validate action JSON
906
+ bridge rules List validation rules
907
+
908
+ \x1B[1mSoul:\x1B[0m
909
+ soul export <agentId> Export agent to IPFS
910
+ soul import <cid> Import agent from IPFS
911
+ soul chat <cid> Wake and chat with Soul
912
+ soul list List exported Souls
913
+ soul verify <cid> Verify Soul signature
914
+
915
+ \x1B[1mGhost:\x1B[0m
916
+ ghost run [suite] Start QA test session
917
+ ghost status <id> Check session progress
918
+ ghost results <id> Get test results
919
+ ghost stop <id> Stop running session
920
+ ghost history List past sessions
921
+
922
+ \x1B[1mEnvironment:\x1B[0m
923
+ FORBOC_API_URL Override API URL
924
+
925
+ \x1B[1mExamples:\x1B[0m
926
+ forbocai api status
927
+ forbocai agent create "A wise wizard"
928
+ forbocai agent chat agent_abc123
929
+ forbocai memory recall agent_abc123 "battle"
930
+ forbocai ghost run combat
931
+ `);
932
+ }