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