forbocai 0.1.1 → 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 (3) hide show
  1. package/dist/cli.js +533 -89
  2. package/dist/cli.mjs +533 -89
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -27,39 +27,140 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_http = __toESM(require("http"));
28
28
  var import_https = __toESM(require("https"));
29
29
  var readline = __toESM(require("readline"));
30
+ var fs = __toESM(require("fs"));
31
+ var path = __toESM(require("path"));
32
+ var VERSION = "0.2.0";
30
33
  var DEFAULT_API_URL = "https://forbocai-api.onrender.com";
31
- 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;
32
43
  var args = process.argv.slice(2);
33
44
  var command = args[0];
34
45
  var subcommand = args[1];
35
- var subcommand2 = args[2];
36
- if (command === "api" && subcommand === "status") {
37
- checkApiStatus();
38
- } else if (command === "agent" && subcommand === "create") {
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);
46
- } else if (command === "soul" && subcommand === "export") {
47
- exportSoul(subcommand2);
48
- } else if (command === "soul" && subcommand === "import") {
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();
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);
60
98
  } else {
61
99
  printUsage();
62
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
+ }
63
164
  function checkApiStatus() {
64
165
  console.log(`> Connecting to Neuro-Symbolic Grid...`);
65
166
  console.log(`> API: ${API_URL}`);
@@ -79,14 +180,53 @@ function checkApiStatus() {
79
180
  console.log(`> Version: ${json.version}`);
80
181
  } catch (e) {
81
182
  console.error("> Error: Invalid JSON response from server.");
82
- console.log(data.substring(0, 100) + "...");
83
183
  }
84
184
  });
85
185
  }).on("error", (e) => {
86
186
  console.error(`> Error: ${e.message}`);
87
- console.log(`> Hint: Is the local Haskell API running? (cd api/hs && cabal run)`);
187
+ console.log(`> Hint: Run 'forbocai doctor' to diagnose`);
88
188
  });
89
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
+ }
90
230
  async function createAgent(persona) {
91
231
  const p = persona || "Default Persona";
92
232
  console.log(`> Creating Agent with persona: "${p}"...`);
@@ -107,13 +247,10 @@ async function createAgent(persona) {
107
247
  async function listAgents() {
108
248
  console.log(`> Listing agents...`);
109
249
  try {
110
- const res = await fetch(`${API_URL}/agents`, {
111
- method: "GET",
112
- headers: { "Content-Type": "application/json" }
113
- });
250
+ const res = await fetch(`${API_URL}/agents`);
114
251
  if (!res.ok) throw new Error(res.statusText);
115
252
  const data = await res.json();
116
- if (data.length === 0) {
253
+ if (!data || data.length === 0) {
117
254
  console.log(`> No agents found.`);
118
255
  return;
119
256
  }
@@ -121,7 +258,7 @@ async function listAgents() {
121
258
  `);
122
259
  data.forEach((agent, i) => {
123
260
  console.log(` ${i + 1}. \x1B[32m${agent.agentId}\x1B[0m`);
124
- console.log(` Persona: ${agent.persona?.substring(0, 50)}...`);
261
+ console.log(` Persona: ${(agent.persona || "").substring(0, 50)}...`);
125
262
  console.log(` Mood: ${agent.mood || "unknown"}`);
126
263
  console.log("");
127
264
  });
@@ -136,16 +273,108 @@ async function deleteAgent(agentId) {
136
273
  }
137
274
  console.log(`> Deleting Agent: ${agentId}...`);
138
275
  try {
139
- const res = await fetch(`${API_URL}/agents/${agentId}`, {
140
- method: "DELETE",
141
- headers: { "Content-Type": "application/json" }
142
- });
276
+ const res = await fetch(`${API_URL}/agents/${agentId}`, { method: "DELETE" });
143
277
  if (!res.ok) throw new Error(res.statusText);
144
278
  console.log(`> Agent \x1B[31mdeleted\x1B[0m: ${agentId}`);
145
279
  } catch (e) {
146
280
  console.error(`> Failed to delete agent:`, e);
147
281
  }
148
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
+ }
149
378
  async function chatWithAgent(agentId) {
150
379
  if (!agentId) {
151
380
  console.error("Error: Agent ID required");
@@ -158,10 +387,7 @@ async function chatWithAgent(agentId) {
158
387
  console.log(`\x1B[36m\u2551 Type 'exit' to quit \u2551\x1B[0m`);
159
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
160
389
  `);
161
- const rl = readline.createInterface({
162
- input: process.stdin,
163
- output: process.stdout
164
- });
390
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
165
391
  const promptUser = () => {
166
392
  rl.question("\x1B[33m> You: \x1B[0m", async (input) => {
167
393
  if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
@@ -173,10 +399,7 @@ async function chatWithAgent(agentId) {
173
399
  const res = await fetch(`${API_URL}/agents/${agentId}/process`, {
174
400
  method: "POST",
175
401
  headers: { "Content-Type": "application/json" },
176
- body: JSON.stringify({
177
- inputText: input,
178
- context: { source: "cli" }
179
- })
402
+ body: JSON.stringify({ inputText: input, context: { source: "cli" } })
180
403
  });
181
404
  if (!res.ok) throw new Error(res.statusText);
182
405
  const data = await res.json();
@@ -257,6 +480,36 @@ async function chatWithSoul(cid) {
257
480
  console.error(`> Failed to wake soul:`, e);
258
481
  }
259
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
+ }
260
513
  async function runGhost(suite) {
261
514
  const testSuite = suite || "exploration";
262
515
  console.log(`> Starting Ghost QA session...`);
@@ -265,10 +518,7 @@ async function runGhost(suite) {
265
518
  const res = await fetch(`${API_URL}/ghost/run`, {
266
519
  method: "POST",
267
520
  headers: { "Content-Type": "application/json" },
268
- body: JSON.stringify({
269
- testSuite,
270
- duration: 300
271
- })
521
+ body: JSON.stringify({ testSuite, duration: 300 })
272
522
  });
273
523
  if (!res.ok) throw new Error(res.statusText);
274
524
  const data = await res.json();
@@ -289,14 +539,11 @@ async function ghostStatus(sessionId) {
289
539
  }
290
540
  console.log(`> Checking Ghost session: ${sessionId}...`);
291
541
  try {
292
- const res = await fetch(`${API_URL}/ghost/${sessionId}/status`, {
293
- method: "GET",
294
- headers: { "Content-Type": "application/json" }
295
- });
542
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/status`);
296
543
  if (!res.ok) throw new Error(res.statusText);
297
544
  const data = await res.json();
298
545
  const statusColor = data.ghostStatus === "completed" ? "\x1B[32m" : data.ghostStatus === "failed" ? "\x1B[31m" : "\x1B[33m";
299
- console.log(`> Status: ${statusColor}${data.ghostStatus?.toUpperCase()}\x1B[0m`);
546
+ console.log(`> Status: ${statusColor}${(data.ghostStatus || "unknown").toUpperCase()}\x1B[0m`);
300
547
  console.log(`> Progress: ${data.ghostProgress || 0}%`);
301
548
  console.log(`> Duration: ${data.ghostDuration || 0}s`);
302
549
  console.log(`> Errors: ${data.ghostErrors || 0}`);
@@ -311,10 +558,7 @@ async function ghostResults(sessionId) {
311
558
  }
312
559
  console.log(`> Fetching Ghost results: ${sessionId}...`);
313
560
  try {
314
- const res = await fetch(`${API_URL}/ghost/${sessionId}/results`, {
315
- method: "GET",
316
- headers: { "Content-Type": "application/json" }
317
- });
561
+ const res = await fetch(`${API_URL}/ghost/${sessionId}/results`);
318
562
  if (!res.ok) throw new Error(res.statusText);
319
563
  const data = await res.json();
320
564
  console.log(`
@@ -351,70 +595,270 @@ async function ghostResults(sessionId) {
351
595
  console.error(`> Failed to get Ghost results:`, e);
352
596
  }
353
597
  }
354
- async function listModels() {
355
- console.log(`> Fetching available models...`);
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}"...`);
356
656
  try {
357
- const res = await fetch(`${API_URL}/cortex/models`, {
358
- method: "GET",
359
- headers: { "Content-Type": "application/json" }
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 })
360
661
  });
361
662
  if (!res.ok) throw new Error(res.statusText);
362
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
+ }
363
669
  console.log(`
364
- Available Models:
670
+ Relevant Memories:
365
671
  `);
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("");
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)}...`);
373
675
  });
374
676
  } catch (e) {
375
- console.error(`> Failed to list models:`, e);
677
+ console.error(`> Failed to recall memories:`, e);
376
678
  }
377
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
+ }
378
794
  function printUsage() {
379
795
  console.log(`
380
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
381
- \u2551 ForbocAI CLI v0.1.0 \u2551
797
+ \u2551 ForbocAI CLI v${VERSION} \u2551
382
798
  \u2551 The Neuro-Symbolic Grid Interface \u2551
383
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
384
800
 
385
801
  \x1B[1mUsage:\x1B[0m
386
802
  forbocai <command> <subcommand> [options]
387
803
 
388
- \x1B[1mAPI Commands:\x1B[0m
389
- api status Check API connection and version
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
390
815
 
391
- \x1B[1mAgent Commands:\x1B[0m
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
392
821
  agent create [persona] Create a new agent
393
822
  agent list List all agents
394
- agent chat <agentId> Interactive chat with an agent
395
- agent delete <agentId> Delete an agent
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
396
835
 
397
- \x1B[1mSoul Commands:\x1B[0m
836
+ \x1B[1mBridge:\x1B[0m
837
+ bridge validate <file> Validate action JSON
838
+ bridge rules List validation rules
839
+
840
+ \x1B[1mSoul:\x1B[0m
398
841
  soul export <agentId> Export agent to IPFS
399
842
  soul import <cid> Import agent from IPFS
400
- soul chat <cid> Wake and chat with a Soul
843
+ soul chat <cid> Wake and chat with Soul
844
+ soul list List exported Souls
845
+ soul verify <cid> Verify Soul signature
401
846
 
402
- \x1B[1mGhost Commands:\x1B[0m
847
+ \x1B[1mGhost:\x1B[0m
403
848
  ghost run [suite] Start QA test session
404
849
  ghost status <id> Check session progress
405
850
  ghost results <id> Get test results
406
-
407
- \x1B[1mCortex Commands:\x1B[0m
408
- cortex models List available SLM models
851
+ ghost stop <id> Stop running session
852
+ ghost history List past sessions
409
853
 
410
854
  \x1B[1mEnvironment:\x1B[0m
411
- FORBOC_API_URL Override API URL (default: render)
855
+ FORBOC_API_URL Override API URL
412
856
 
413
857
  \x1B[1mExamples:\x1B[0m
414
858
  forbocai api status
415
- forbocai agent create "A wise old wizard"
859
+ forbocai agent create "A wise wizard"
416
860
  forbocai agent chat agent_abc123
417
- forbocai ghost run exploration
418
- forbocai soul chat QmXyz...
861
+ forbocai memory recall agent_abc123 "battle"
862
+ forbocai ghost run combat
419
863
  `);
420
864
  }