forbocai 0.1.0 → 0.2.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.
Files changed (4) hide show
  1. package/README.md +29 -0
  2. package/dist/cli.js +744 -19
  3. package/dist/cli.mjs +744 -19
  4. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -26,25 +26,144 @@ 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"));
30
+ var fs = __toESM(require("fs"));
31
+ var path = __toESM(require("path"));
32
+ var VERSION = "0.2.0";
29
33
  var DEFAULT_API_URL = "https://forbocai-api.onrender.com";
30
- var API_URL = process.env.FORBOC_API_URL || DEFAULT_API_URL;
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;
31
43
  var args = process.argv.slice(2);
32
44
  var command = args[0];
33
45
  var subcommand = args[1];
34
- var subcommand2 = args[2];
35
- if (command === "api" && subcommand === "status") {
36
- checkApiStatus();
37
- } else if (command === "agent" && subcommand === "create") {
38
- createAgent(subcommand2);
39
- } else if (command === "soul" && subcommand === "export") {
40
- exportSoul(subcommand2);
41
- } else if (command === "soul" && subcommand === "import") {
42
- importSoul(subcommand2);
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);
43
98
  } else {
44
99
  printUsage();
45
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
+ }
46
164
  function checkApiStatus() {
47
165
  console.log(`> Connecting to Neuro-Symbolic Grid...`);
166
+ console.log(`> API: ${API_URL}`);
48
167
  const client = API_URL.startsWith("https") ? import_https.default : import_http.default;
49
168
  client.get(`${API_URL}/status`, (res) => {
50
169
  let data = "";
@@ -61,14 +180,53 @@ function checkApiStatus() {
61
180
  console.log(`> Version: ${json.version}`);
62
181
  } catch (e) {
63
182
  console.error("> Error: Invalid JSON response from server.");
64
- console.log(data.substring(0, 100) + "...");
65
183
  }
66
184
  });
67
185
  }).on("error", (e) => {
68
186
  console.error(`> Error: ${e.message}`);
69
- console.log(`> Hint: Is the local Haskell API running? (cd api/hs && cabal run)`);
187
+ console.log(`> Hint: Run 'forbocai doctor' to diagnose`);
70
188
  });
71
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({ model: 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
+ }
72
230
  async function createAgent(persona) {
73
231
  const p = persona || "Default Persona";
74
232
  console.log(`> Creating Agent with persona: "${p}"...`);
@@ -86,6 +244,178 @@ async function createAgent(persona) {
86
244
  console.error(`> Failed to create agent:`, e);
87
245
  }
88
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
+ console.log(` Mood: \x1B[33m${data.stateMood || data.mood || "unknown"}\x1B[0m`);
297
+ if (data.stateInventory || data.inventory) {
298
+ const inv = data.stateInventory || data.inventory;
299
+ console.log(` Inventory: ${inv.length > 0 ? inv.join(", ") : "(empty)"}`);
300
+ }
301
+ if (data.stateSkills || data.skills) {
302
+ console.log(` Skills:`);
303
+ const skills = data.stateSkills || data.skills || {};
304
+ for (const [skill, level] of Object.entries(skills)) {
305
+ console.log(` ${skill}: ${level}`);
306
+ }
307
+ }
308
+ if (data.stateRelationships || data.relationships) {
309
+ console.log(` Relationships:`);
310
+ const rels = data.stateRelationships || data.relationships || {};
311
+ for (const [entity, value] of Object.entries(rels)) {
312
+ const indicator = value > 0 ? "\x1B[32m+" : "\x1B[31m";
313
+ console.log(` ${entity}: ${indicator}${value}\x1B[0m`);
314
+ }
315
+ }
316
+ } catch (e) {
317
+ console.error(`> Failed to get agent state:`, e);
318
+ }
319
+ }
320
+ async function agentProcess(agentId, input) {
321
+ if (!agentId) {
322
+ console.error("Error: Agent ID required");
323
+ return;
324
+ }
325
+ if (!input) {
326
+ console.error("Error: Input text required");
327
+ return;
328
+ }
329
+ try {
330
+ const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
331
+ method: "POST",
332
+ headers: { "Content-Type": "application/json" },
333
+ body: JSON.stringify({ inputText: input, context: { source: "cli" } })
334
+ });
335
+ if (!res.ok) throw new Error(res.statusText);
336
+ const data = await res.json();
337
+ console.log(`\x1B[32m${data.dialogue || data.response || "No response"}\x1B[0m`);
338
+ if (data.actions && data.actions.length > 0) {
339
+ console.log(`\x1B[2mActions: ${data.actions.map((a) => a.type).join(", ")}\x1B[0m`);
340
+ }
341
+ } catch (e) {
342
+ console.error(`> Error:`, e);
343
+ }
344
+ }
345
+ async function agentUpdate(agentId, updateArgs) {
346
+ if (!agentId) {
347
+ console.error("Error: Agent ID required");
348
+ return;
349
+ }
350
+ const updates = {};
351
+ for (let i = 0; i < updateArgs.length; i++) {
352
+ if (updateArgs[i] === "--mood" && updateArgs[i + 1]) {
353
+ updates.mood = updateArgs[++i];
354
+ } else if (updateArgs[i] === "--inventory" && updateArgs[i + 1]) {
355
+ updates.inventory = updateArgs[++i].split(",");
356
+ }
357
+ }
358
+ if (Object.keys(updates).length === 0) {
359
+ console.error("Usage: forbocai agent update <id> --mood <mood> [--inventory <item1,item2>]");
360
+ return;
361
+ }
362
+ console.log(`> Updating Agent: ${agentId}...`);
363
+ try {
364
+ const res = await fetch(`${API_URL}/agents/${agentId}/state`, {
365
+ method: "PATCH",
366
+ headers: { "Content-Type": "application/json" },
367
+ body: JSON.stringify({ stateUpdate: updates })
368
+ });
369
+ if (!res.ok) throw new Error(res.statusText);
370
+ console.log(`> Agent updated!`);
371
+ for (const [key, value] of Object.entries(updates)) {
372
+ console.log(` ${key}: ${value}`);
373
+ }
374
+ } catch (e) {
375
+ console.error(`> Failed to update agent:`, e);
376
+ }
377
+ }
378
+ async function chatWithAgent(agentId) {
379
+ if (!agentId) {
380
+ console.error("Error: Agent ID required");
381
+ return;
382
+ }
383
+ console.log(`
384
+ \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`);
385
+ console.log(`\x1B[36m\u2551 ForbocAI Agent Chat \u2551\x1B[0m`);
386
+ console.log(`\x1B[36m\u2551 Agent: ${agentId.substring(0, 28).padEnd(28)} \u2551\x1B[0m`);
387
+ console.log(`\x1B[36m\u2551 Type 'exit' to quit \u2551\x1B[0m`);
388
+ 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
389
+ `);
390
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
391
+ const promptUser = () => {
392
+ rl.question("\x1B[33m> You: \x1B[0m", async (input) => {
393
+ if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
394
+ console.log("\n> Disconnecting from agent...");
395
+ rl.close();
396
+ return;
397
+ }
398
+ try {
399
+ const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
400
+ method: "POST",
401
+ headers: { "Content-Type": "application/json" },
402
+ body: JSON.stringify({ inputText: input, context: { source: "cli" } })
403
+ });
404
+ if (!res.ok) throw new Error(res.statusText);
405
+ const data = await res.json();
406
+ console.log(`\x1B[32m> Agent: \x1B[0m${data.dialogue || data.response || "No response"}`);
407
+ if (data.actions && data.actions.length > 0) {
408
+ console.log(`\x1B[2m> Actions: ${data.actions.map((a) => a.type).join(", ")}\x1B[0m`);
409
+ }
410
+ console.log("");
411
+ } catch (e) {
412
+ console.error(`> Error: ${e}`);
413
+ }
414
+ promptUser();
415
+ });
416
+ };
417
+ promptUser();
418
+ }
89
419
  async function exportSoul(agentId) {
90
420
  if (!agentId) {
91
421
  console.error("Error: Agent ID required");
@@ -128,12 +458,407 @@ async function importSoul(cid) {
128
458
  console.error(`> Failed to import soul:`, e);
129
459
  }
130
460
  }
461
+ async function chatWithSoul(cid) {
462
+ if (!cid) {
463
+ console.error("Error: CID required");
464
+ return;
465
+ }
466
+ console.log(`> Waking Soul from cryo: ${cid}...`);
467
+ try {
468
+ const res = await fetch(`${API_URL}/agents/import`, {
469
+ method: "POST",
470
+ headers: { "Content-Type": "application/json" },
471
+ body: JSON.stringify({ cidRef: cid })
472
+ });
473
+ if (!res.ok) throw new Error(res.statusText);
474
+ const data = await res.json();
475
+ console.log(`> Soul awakened! Temporary Agent: ${data.agentId}`);
476
+ console.log(`> Persona: ${data.persona}
477
+ `);
478
+ await chatWithAgent(data.agentId);
479
+ } catch (e) {
480
+ console.error(`> Failed to wake soul:`, e);
481
+ }
482
+ }
483
+ async function soulList() {
484
+ console.log(`> Listing exported Souls...`);
485
+ try {
486
+ console.log(`
487
+ Exported Souls:
488
+ `);
489
+ console.log(` (Soul listing requires local storage or API endpoint)`);
490
+ console.log(` Use 'soul export <agentId>' to create a new Soul`);
491
+ } catch (e) {
492
+ console.error(`> Failed to list souls:`, e);
493
+ }
494
+ }
495
+ async function soulVerify(cid) {
496
+ if (!cid) {
497
+ console.error("Error: CID required");
498
+ return;
499
+ }
500
+ console.log(`> Verifying Soul: ${cid}...`);
501
+ try {
502
+ const res = await fetch(`${API_URL}/souls/${cid}`);
503
+ if (!res.ok) throw new Error(res.statusText);
504
+ const data = await res.json();
505
+ console.log(`> Soul Verified!`);
506
+ console.log(`> Name: ${data.soulName}`);
507
+ console.log(`> DNA: ${(data.dna || "").substring(0, 50)}...`);
508
+ console.log(`> Signature: ${data.signature ? "\x1B[32m\u2713 Valid\x1B[0m" : "\x1B[33m\u25CB Not signed\x1B[0m"}`);
509
+ } catch (e) {
510
+ console.error(`> Failed to verify soul:`, e);
511
+ }
512
+ }
513
+ async function runGhost(suite) {
514
+ const testSuite = suite || "exploration";
515
+ console.log(`> Starting Ghost QA session...`);
516
+ console.log(`> Test Suite: ${testSuite}`);
517
+ try {
518
+ const res = await fetch(`${API_URL}/ghost/run`, {
519
+ method: "POST",
520
+ headers: { "Content-Type": "application/json" },
521
+ body: JSON.stringify({ testSuite, duration: 300 })
522
+ });
523
+ if (!res.ok) throw new Error(res.statusText);
524
+ const data = await res.json();
525
+ console.log(`> Session Started!`);
526
+ console.log(`> Session ID: \x1B[36m${data.sessionId}\x1B[0m`);
527
+ console.log(`> Status: ${data.runStatus || "running"}`);
528
+ console.log(`
529
+ > To check status: forbocai ghost status ${data.sessionId}`);
530
+ console.log(`> To get results: forbocai ghost results ${data.sessionId}`);
531
+ } catch (e) {
532
+ console.error(`> Failed to start Ghost session:`, e);
533
+ }
534
+ }
535
+ async function ghostStatus(sessionId) {
536
+ if (!sessionId) {
537
+ console.error("Error: Session ID required");
538
+ return;
539
+ }
540
+ console.log(`> Checking Ghost session: ${sessionId}...`);
541
+ try {
542
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/status`);
543
+ if (!res.ok) throw new Error(res.statusText);
544
+ const data = await res.json();
545
+ const statusColor = data.ghostStatus === "completed" ? "\x1B[32m" : data.ghostStatus === "failed" ? "\x1B[31m" : "\x1B[33m";
546
+ console.log(`> Status: ${statusColor}${(data.ghostStatus || "unknown").toUpperCase()}\x1B[0m`);
547
+ console.log(`> Progress: ${data.ghostProgress || 0}%`);
548
+ console.log(`> Duration: ${data.ghostDuration || 0}s`);
549
+ console.log(`> Errors: ${data.ghostErrors || 0}`);
550
+ } catch (e) {
551
+ console.error(`> Failed to get Ghost status:`, e);
552
+ }
553
+ }
554
+ async function ghostResults(sessionId) {
555
+ if (!sessionId) {
556
+ console.error("Error: Session ID required");
557
+ return;
558
+ }
559
+ console.log(`> Fetching Ghost results: ${sessionId}...`);
560
+ try {
561
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/results`);
562
+ if (!res.ok) throw new Error(res.statusText);
563
+ const data = await res.json();
564
+ console.log(`
565
+ \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`);
566
+ console.log(`\u2551 Ghost QA Results \u2551`);
567
+ 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
568
+ `);
569
+ const passRate = data.resultsTotalTests > 0 ? (data.resultsPassed / data.resultsTotalTests * 100).toFixed(1) : 0;
570
+ console.log(` Total Tests: ${data.resultsTotalTests}`);
571
+ console.log(` \x1B[32mPassed: ${data.resultsPassed}\x1B[0m`);
572
+ console.log(` \x1B[31mFailed: ${data.resultsFailed}\x1B[0m`);
573
+ console.log(` Pass Rate: ${passRate}%`);
574
+ console.log(` Duration: ${data.resultsDuration}ms`);
575
+ console.log(` Coverage: ${((data.resultsCoverage || 0) * 100).toFixed(1)}%`);
576
+ if (data.resultsMetrics) {
577
+ console.log(`
578
+ Metrics:`);
579
+ for (const [key, value] of data.resultsMetrics) {
580
+ console.log(` ${key}: ${value}`);
581
+ }
582
+ }
583
+ if (data.resultsTests && data.resultsTests.length > 0) {
584
+ console.log(`
585
+ Tests:`);
586
+ data.resultsTests.forEach((test) => {
587
+ const icon = test.testPassed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
588
+ console.log(` ${icon} ${test.testName} (${test.testDuration}ms)`);
589
+ if (test.testError) {
590
+ console.log(` \x1B[31mError: ${test.testError}\x1B[0m`);
591
+ }
592
+ });
593
+ }
594
+ } catch (e) {
595
+ console.error(`> Failed to get Ghost results:`, e);
596
+ }
597
+ }
598
+ async function ghostStop(sessionId) {
599
+ if (!sessionId) {
600
+ console.error("Error: Session ID required");
601
+ return;
602
+ }
603
+ console.log(`> Stopping Ghost session: ${sessionId}...`);
604
+ console.log(`> Session stop requested (mock)`);
605
+ console.log(`> Status: \x1B[33mStopped\x1B[0m`);
606
+ }
607
+ async function ghostHistory() {
608
+ console.log(`> Fetching Ghost session history...`);
609
+ console.log(`
610
+ Recent Sessions:
611
+ `);
612
+ console.log(` (Session history requires persistent storage)`);
613
+ console.log(` Use 'ghost run <suite>' to start a new session`);
614
+ }
615
+ async function memoryList(agentId) {
616
+ if (!agentId) {
617
+ console.error("Error: Agent ID required");
618
+ return;
619
+ }
620
+ console.log(`> Listing memories for Agent: ${agentId}...`);
621
+ try {
622
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory`);
623
+ if (!res.ok) throw new Error(res.statusText);
624
+ const data = await res.json();
625
+ const memories = data.memories || data;
626
+ if (!memories || memories.length === 0) {
627
+ console.log(`> No memories found.`);
628
+ return;
629
+ }
630
+ console.log(`
631
+ Memories (${memories.length}):
632
+ `);
633
+ memories.slice(0, 20).forEach((mem, i) => {
634
+ const importance = mem.importance || mem.memImportance || 0.5;
635
+ const bar = "\u2588".repeat(Math.floor(importance * 10));
636
+ console.log(` ${i + 1}. [${bar.padEnd(10)}] ${(mem.text || mem.memText || "").substring(0, 50)}...`);
637
+ });
638
+ if (memories.length > 20) {
639
+ console.log(`
640
+ ... and ${memories.length - 20} more`);
641
+ }
642
+ } catch (e) {
643
+ console.error(`> Failed to list memories:`, e);
644
+ }
645
+ }
646
+ async function memoryRecall(agentId, query) {
647
+ if (!agentId) {
648
+ console.error("Error: Agent ID required");
649
+ return;
650
+ }
651
+ if (!query) {
652
+ console.error("Error: Query required");
653
+ return;
654
+ }
655
+ console.log(`> Recalling memories for: "${query}"...`);
656
+ try {
657
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory/recall`, {
658
+ method: "POST",
659
+ headers: { "Content-Type": "application/json" },
660
+ body: JSON.stringify({ recallQuery: query, recallLimit: 5 })
661
+ });
662
+ if (!res.ok) throw new Error(res.statusText);
663
+ const data = await res.json();
664
+ const memories = data.recalledMemories || data;
665
+ if (!memories || memories.length === 0) {
666
+ console.log(`> No relevant memories found.`);
667
+ return;
668
+ }
669
+ console.log(`
670
+ Relevant Memories:
671
+ `);
672
+ memories.forEach((mem, i) => {
673
+ const score = mem.score || mem.relevance || "?";
674
+ console.log(` ${i + 1}. [Score: ${score}] ${(mem.text || mem.memText || "").substring(0, 60)}...`);
675
+ });
676
+ } catch (e) {
677
+ console.error(`> Failed to recall memories:`, e);
678
+ }
679
+ }
680
+ async function memoryStore(agentId, text) {
681
+ if (!agentId) {
682
+ console.error("Error: Agent ID required");
683
+ return;
684
+ }
685
+ if (!text) {
686
+ console.error("Error: Memory text required");
687
+ return;
688
+ }
689
+ console.log(`> Storing memory...`);
690
+ try {
691
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory`, {
692
+ method: "POST",
693
+ headers: { "Content-Type": "application/json" },
694
+ body: JSON.stringify({ storeText: text, storeType: "observation" })
695
+ });
696
+ if (!res.ok) throw new Error(res.statusText);
697
+ const data = await res.json();
698
+ console.log(`> Memory stored!`);
699
+ console.log(`> ID: ${data.storedId || data.id}`);
700
+ } catch (e) {
701
+ console.error(`> Failed to store memory:`, e);
702
+ }
703
+ }
704
+ async function memoryClear(agentId) {
705
+ if (!agentId) {
706
+ console.error("Error: Agent ID required");
707
+ return;
708
+ }
709
+ console.log(`> Clearing memories for Agent: ${agentId}...`);
710
+ console.log(`> \x1B[31mThis action cannot be undone!\x1B[0m`);
711
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
712
+ rl.question('Type "CONFIRM" to proceed: ', async (answer) => {
713
+ if (answer !== "CONFIRM") {
714
+ console.log("> Aborted.");
715
+ rl.close();
716
+ return;
717
+ }
718
+ try {
719
+ console.log(`> Memories cleared (mock)`);
720
+ } catch (e) {
721
+ console.error(`> Failed to clear memories:`, e);
722
+ }
723
+ rl.close();
724
+ });
725
+ }
726
+ async function memoryExport(agentId) {
727
+ if (!agentId) {
728
+ console.error("Error: Agent ID required");
729
+ return;
730
+ }
731
+ console.log(`> Exporting memories for Agent: ${agentId}...`);
732
+ try {
733
+ const res = await fetch(`${API_URL}/agents/${agentId}/memory`);
734
+ if (!res.ok) throw new Error(res.statusText);
735
+ const data = await res.json();
736
+ const filename = `memories_${agentId}_${Date.now()}.json`;
737
+ fs.writeFileSync(filename, JSON.stringify(data, null, 2));
738
+ console.log(`> Memories exported to: ${filename}`);
739
+ } catch (e) {
740
+ console.error(`> Failed to export memories:`, e);
741
+ }
742
+ }
743
+ async function bridgeValidate(actionFile) {
744
+ if (!actionFile) {
745
+ console.error("Error: Action file (JSON) required");
746
+ return;
747
+ }
748
+ console.log(`> Validating action from: ${actionFile}...`);
749
+ try {
750
+ const actionJson = fs.readFileSync(actionFile, "utf-8");
751
+ const action = JSON.parse(actionJson);
752
+ const res = await fetch(`${API_URL}/bridge/validate`, {
753
+ method: "POST",
754
+ headers: { "Content-Type": "application/json" },
755
+ body: JSON.stringify({ validateAction: action })
756
+ });
757
+ if (!res.ok) throw new Error(res.statusText);
758
+ const data = await res.json();
759
+ if (data.validationIsValid || data.valid) {
760
+ console.log(`> \x1B[32m\u2713 Action is VALID\x1B[0m`);
761
+ } else {
762
+ console.log(`> \x1B[31m\u2717 Action is INVALID\x1B[0m`);
763
+ console.log(`> Reason: ${data.validationReason || data.reason}`);
764
+ }
765
+ } catch (e) {
766
+ console.error(`> Failed to validate action:`, e);
767
+ }
768
+ }
769
+ async function bridgeRules() {
770
+ console.log(`> Fetching validation rules...`);
771
+ console.log(`
772
+ Built-in Rules:
773
+ `);
774
+ console.log(` \x1B[36mcore.movement\x1B[0m`);
775
+ console.log(` Actions: MOVE, WALK, RUN`);
776
+ console.log(` Validates: Coordinates, world bounds`);
777
+ console.log("");
778
+ console.log(` \x1B[36mcore.attack\x1B[0m`);
779
+ console.log(` Actions: ATTACK, HIT, STRIKE`);
780
+ console.log(` Validates: Target exists`);
781
+ console.log("");
782
+ console.log(` \x1B[36mcore.interact\x1B[0m`);
783
+ console.log(` Actions: INTERACT, USE, PICKUP`);
784
+ console.log(` Validates: Object specified`);
785
+ console.log("");
786
+ console.log(` \x1B[36mcore.speak\x1B[0m`);
787
+ console.log(` Actions: SPEAK, SAY, SHOUT`);
788
+ console.log(` Validates: Text not empty`);
789
+ console.log("");
790
+ console.log(` \x1B[36mcore.resources\x1B[0m`);
791
+ console.log(` Actions: (all)`);
792
+ console.log(` Validates: HP > 0, sufficient mana`);
793
+ }
131
794
  function printUsage() {
132
- console.log("Usage:");
133
- console.log(" forbocai api status");
134
- console.log(" forbocai agent create [<persona>]");
135
- console.log(" forbocai soul export <agentId>");
136
- console.log(" forbocai soul import <cid>");
137
- console.log("\nEnvironment:");
138
- console.log(" FORBOC_API_URL Override API URL (default: render)");
795
+ console.log(`
796
+ \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
797
+ \u2551 ForbocAI CLI v${VERSION} \u2551
798
+ \u2551 The Neuro-Symbolic Grid Interface \u2551
799
+ \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
800
+
801
+ \x1B[1mUsage:\x1B[0m
802
+ forbocai <command> <subcommand> [options]
803
+
804
+ \x1B[1mMeta:\x1B[0m
805
+ version Show SDK version
806
+ doctor Diagnose SDK/API issues
807
+
808
+ \x1B[1mConfig:\x1B[0m
809
+ config set <k> <v> Set configuration value
810
+ config get <key> Get configuration value
811
+ config list List all configuration
812
+
813
+ \x1B[1mAPI:\x1B[0m
814
+ api status Check API connection
815
+
816
+ \x1B[1mCortex:\x1B[0m
817
+ cortex models List available SLM models
818
+ cortex init [model] Initialize Cortex with model
819
+
820
+ \x1B[1mAgent:\x1B[0m
821
+ agent create [persona] Create a new agent
822
+ agent list List all agents
823
+ agent chat <id> Interactive chat with agent
824
+ agent state <id> View agent's current state
825
+ agent process <id> <t> One-shot process input
826
+ agent update <id> ... Update agent state
827
+ agent delete <id> Delete an agent
828
+
829
+ \x1B[1mMemory:\x1B[0m
830
+ memory list <agentId> List agent's memories
831
+ memory recall <id> <q> Semantic search memories
832
+ memory store <id> <t> Store new observation
833
+ memory clear <id> Clear all memories
834
+ memory export <id> Export memories to JSON
835
+
836
+ \x1B[1mBridge:\x1B[0m
837
+ bridge validate <file> Validate action JSON
838
+ bridge rules List validation rules
839
+
840
+ \x1B[1mSoul:\x1B[0m
841
+ soul export <agentId> Export agent to IPFS
842
+ soul import <cid> Import agent from IPFS
843
+ soul chat <cid> Wake and chat with Soul
844
+ soul list List exported Souls
845
+ soul verify <cid> Verify Soul signature
846
+
847
+ \x1B[1mGhost:\x1B[0m
848
+ ghost run [suite] Start QA test session
849
+ ghost status <id> Check session progress
850
+ ghost results <id> Get test results
851
+ ghost stop <id> Stop running session
852
+ ghost history List past sessions
853
+
854
+ \x1B[1mEnvironment:\x1B[0m
855
+ FORBOC_API_URL Override API URL
856
+
857
+ \x1B[1mExamples:\x1B[0m
858
+ forbocai api status
859
+ forbocai agent create "A wise wizard"
860
+ forbocai agent chat agent_abc123
861
+ forbocai memory recall agent_abc123 "battle"
862
+ forbocai ghost run combat
863
+ `);
139
864
  }