@prismer/sdk 1.8.0 → 1.8.2
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/chunk-BWZXMXL7.mjs +4762 -0
- package/dist/chunk-VSAVCMMZ.mjs +4761 -0
- package/dist/cli.d.mts +15 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +570 -284
- package/dist/cli.mjs +3838 -0
- package/dist/index.d.mts +60 -5
- package/dist/index.d.ts +60 -5
- package/dist/index.js +32 -3
- package/dist/index.mjs +50 -4681
- package/icon +21 -0
- package/package.json +9 -4
- package/dist/chunk-Y6FXYEAI.mjs +0 -10
- package/dist/webhook.d.mts +0 -114
- package/dist/webhook.d.ts +0 -114
- package/dist/webhook.js +0 -200
- package/dist/webhook.mjs +0 -175
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,3838 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PrismerClient,
|
|
3
|
+
__require
|
|
4
|
+
} from "./chunk-VSAVCMMZ.mjs";
|
|
5
|
+
|
|
6
|
+
// src/cli.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import * as fs3 from "fs";
|
|
9
|
+
import * as path3 from "path";
|
|
10
|
+
import * as os2 from "os";
|
|
11
|
+
import * as TOML2 from "@iarna/toml";
|
|
12
|
+
|
|
13
|
+
// src/ui.ts
|
|
14
|
+
import * as pc from "picocolors";
|
|
15
|
+
import * as clack from "@clack/prompts";
|
|
16
|
+
import * as fs from "fs";
|
|
17
|
+
import * as path from "path";
|
|
18
|
+
var isTTY = !!process.stdout.isTTY || !!process.env.FORCE_COLOR;
|
|
19
|
+
function displayBanner() {
|
|
20
|
+
if (!isTTY) return;
|
|
21
|
+
let iconPath;
|
|
22
|
+
try {
|
|
23
|
+
iconPath = path.resolve(__dirname, "..", "icon");
|
|
24
|
+
if (!fs.existsSync(iconPath)) {
|
|
25
|
+
iconPath = path.resolve(__dirname, "..", "..", "icon");
|
|
26
|
+
}
|
|
27
|
+
if (!fs.existsSync(iconPath)) return;
|
|
28
|
+
const raw = fs.readFileSync(iconPath, "utf-8");
|
|
29
|
+
const lines = raw.split("\n");
|
|
30
|
+
const termWidth = process.stdout.columns || 80;
|
|
31
|
+
const colorized = lines.map((line) => {
|
|
32
|
+
const trimmed = line.trimEnd();
|
|
33
|
+
if (!trimmed) return "";
|
|
34
|
+
let result = "";
|
|
35
|
+
let visibleLen = 0;
|
|
36
|
+
for (const ch of trimmed) {
|
|
37
|
+
if (visibleLen >= termWidth - 1) break;
|
|
38
|
+
if (ch === "\u2592") {
|
|
39
|
+
result += pc.cyan(ch);
|
|
40
|
+
} else if (ch === "\u2593") {
|
|
41
|
+
result += pc.white(ch);
|
|
42
|
+
} else if (ch === "\u2588") {
|
|
43
|
+
result += pc.white(ch);
|
|
44
|
+
} else {
|
|
45
|
+
result += ch;
|
|
46
|
+
}
|
|
47
|
+
visibleLen++;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
});
|
|
51
|
+
while (colorized.length > 0 && colorized[colorized.length - 1].trim() === "") {
|
|
52
|
+
colorized.pop();
|
|
53
|
+
}
|
|
54
|
+
console.log(colorized.join("\n"));
|
|
55
|
+
console.log();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var SYMBOLS = {
|
|
60
|
+
success: isTTY ? "\u2713" : "[ok]",
|
|
61
|
+
// ✓
|
|
62
|
+
error: isTTY ? "\u2717" : "[error]",
|
|
63
|
+
// ✗
|
|
64
|
+
warn: isTTY ? "\u26A0" : "[warn]",
|
|
65
|
+
// ⚠
|
|
66
|
+
info: isTTY ? "\u2139" : "[info]"
|
|
67
|
+
// ℹ
|
|
68
|
+
};
|
|
69
|
+
function success(msg) {
|
|
70
|
+
console.log(pc.green(`${SYMBOLS.success} ${msg}`));
|
|
71
|
+
}
|
|
72
|
+
function error(msg) {
|
|
73
|
+
console.error(pc.red(`${SYMBOLS.error} ${msg}`));
|
|
74
|
+
}
|
|
75
|
+
function warn(msg) {
|
|
76
|
+
console.warn(pc.yellow(`${SYMBOLS.warn} ${msg}`));
|
|
77
|
+
}
|
|
78
|
+
function info(msg) {
|
|
79
|
+
console.log(pc.blue(`${SYMBOLS.info} ${msg}`));
|
|
80
|
+
}
|
|
81
|
+
function dim2(msg) {
|
|
82
|
+
console.log(pc.dim(msg));
|
|
83
|
+
}
|
|
84
|
+
async function withSpinner(message, fn) {
|
|
85
|
+
if (!isTTY) {
|
|
86
|
+
return fn();
|
|
87
|
+
}
|
|
88
|
+
const s = clack.spinner();
|
|
89
|
+
s.start(message);
|
|
90
|
+
try {
|
|
91
|
+
const result = await fn();
|
|
92
|
+
s.stop(pc.green(`${SYMBOLS.success} ${message}`));
|
|
93
|
+
return result;
|
|
94
|
+
} catch (err) {
|
|
95
|
+
s.stop(pc.red(`${SYMBOLS.error} ${message}`));
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function table(headers, rows) {
|
|
100
|
+
if (headers.length === 0) return;
|
|
101
|
+
const widths = headers.map((h, i) => {
|
|
102
|
+
const dataMax = rows.reduce((max, row) => Math.max(max, (row[i] || "").length), 0);
|
|
103
|
+
return Math.max(h.length, dataMax);
|
|
104
|
+
});
|
|
105
|
+
const PAD = 2;
|
|
106
|
+
const headerLine = headers.map((h, i) => h.padEnd(widths[i] + PAD)).join("");
|
|
107
|
+
if (isTTY) {
|
|
108
|
+
console.log(pc.bold(headerLine));
|
|
109
|
+
const separator = widths.map((w) => "\u2500".repeat(w)).join(" ");
|
|
110
|
+
console.log(pc.dim(separator));
|
|
111
|
+
} else {
|
|
112
|
+
console.log(headerLine);
|
|
113
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
114
|
+
console.log(separator);
|
|
115
|
+
}
|
|
116
|
+
for (const row of rows) {
|
|
117
|
+
const line = headers.map((_, i) => (row[i] || "").padEnd(widths[i] + PAD)).join("");
|
|
118
|
+
console.log(line);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function keyValue(pairs) {
|
|
122
|
+
const keys = Object.keys(pairs);
|
|
123
|
+
if (keys.length === 0) return;
|
|
124
|
+
const maxKeyLen = keys.reduce((max, k) => Math.max(max, k.length), 0);
|
|
125
|
+
for (const key of keys) {
|
|
126
|
+
const label = isTTY ? pc.bold(key.padEnd(maxKeyLen)) : key.padEnd(maxKeyLen);
|
|
127
|
+
const value = pairs[key];
|
|
128
|
+
console.log(` ${label} ${value}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/commands/im.ts
|
|
133
|
+
function register(parent, getIMClient2, _getAPIClient) {
|
|
134
|
+
const im = parent.command("im").description("IM messaging, groups, conversations, and credits");
|
|
135
|
+
im.command("send <user-id> <message>").description("Send a direct message to a user").option("-t, --type <type>", "Message type: text, markdown, code, file, etc.", "text").option("--reply-to <msg-id>", "Reply to a specific message ID (parentId)").option("--json", "Output raw JSON response").action(async (userId, message, opts) => {
|
|
136
|
+
const client = getIMClient2();
|
|
137
|
+
try {
|
|
138
|
+
const sendOpts = {
|
|
139
|
+
type: opts.type
|
|
140
|
+
};
|
|
141
|
+
if (opts.replyTo) sendOpts.parentId = opts.replyTo;
|
|
142
|
+
const res = await client.im.direct.send(userId, message, sendOpts);
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
145
|
+
`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
if (opts.json) {
|
|
149
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
process.stdout.write(`Message sent (conversationId: ${res.data?.conversationId})
|
|
153
|
+
`);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
156
|
+
`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
im.command("messages <user-id>").description("View direct message history with a user").option("-n, --limit <n>", "Max number of messages to fetch", "20").option("--json", "Output raw JSON response").action(async (userId, opts) => {
|
|
161
|
+
const client = getIMClient2();
|
|
162
|
+
try {
|
|
163
|
+
const res = await client.im.direct.getMessages(userId, { limit: parseInt(opts.limit, 10) });
|
|
164
|
+
if (!res.ok) {
|
|
165
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
166
|
+
`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
const msgs = res.data || [];
|
|
170
|
+
if (opts.json) {
|
|
171
|
+
process.stdout.write(JSON.stringify(msgs, null, 2) + "\n");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (msgs.length === 0) {
|
|
175
|
+
process.stdout.write("No messages.\n");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
for (const m of msgs) {
|
|
179
|
+
const ts = m.createdAt ? new Date(m.createdAt).toLocaleString() : "";
|
|
180
|
+
process.stdout.write(`[${ts}] ${m.senderId || "?"}: ${m.content}
|
|
181
|
+
`);
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
185
|
+
`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
im.command("edit <conversation-id> <message-id> <content>").description("Edit an existing message").option("--json", "Output raw JSON response").action(async (convId, msgId, content, opts) => {
|
|
190
|
+
const client = getIMClient2();
|
|
191
|
+
try {
|
|
192
|
+
const res = await client.im.messages.edit(convId, msgId, content);
|
|
193
|
+
if (!res.ok) {
|
|
194
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
195
|
+
`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
if (opts.json) {
|
|
199
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
process.stdout.write(`Message ${msgId} updated.
|
|
203
|
+
`);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
206
|
+
`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
im.command("delete <conversation-id> <message-id>").description("Delete a message").option("--json", "Output raw JSON response").action(async (convId, msgId, opts) => {
|
|
211
|
+
const client = getIMClient2();
|
|
212
|
+
try {
|
|
213
|
+
const res = await client.im.messages.delete(convId, msgId);
|
|
214
|
+
if (!res.ok) {
|
|
215
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
216
|
+
`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
if (opts.json) {
|
|
220
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
process.stdout.write(`Message ${msgId} deleted.
|
|
224
|
+
`);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
227
|
+
`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
im.command("discover").description("Discover available agents").option("--type <type>", "Filter by agent type").option("--capability <cap>", "Filter by capability").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
232
|
+
const client = getIMClient2();
|
|
233
|
+
try {
|
|
234
|
+
const discoverOpts = {};
|
|
235
|
+
if (opts.type) discoverOpts.type = opts.type;
|
|
236
|
+
if (opts.capability) discoverOpts.capability = opts.capability;
|
|
237
|
+
const res = await client.im.contacts.discover(Object.keys(discoverOpts).length ? discoverOpts : void 0);
|
|
238
|
+
if (!res.ok) {
|
|
239
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
240
|
+
`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
const agents = res.data || [];
|
|
244
|
+
if (opts.json) {
|
|
245
|
+
process.stdout.write(JSON.stringify(agents, null, 2) + "\n");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (agents.length === 0) {
|
|
249
|
+
process.stdout.write("No agents found.\n");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
process.stdout.write(
|
|
253
|
+
"Username".padEnd(20) + "Type".padEnd(14) + "Status".padEnd(10) + "Display Name\n"
|
|
254
|
+
);
|
|
255
|
+
for (const a of agents) {
|
|
256
|
+
process.stdout.write(
|
|
257
|
+
`${(a.username || "").padEnd(20)}${(a.agentType || "").padEnd(14)}${(a.status || "").padEnd(10)}${a.displayName || ""}
|
|
258
|
+
`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
} catch (err) {
|
|
262
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
263
|
+
`);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
im.command("contacts").description("List contacts").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
268
|
+
const client = getIMClient2();
|
|
269
|
+
try {
|
|
270
|
+
const res = await client.im.contacts.list();
|
|
271
|
+
if (!res.ok) {
|
|
272
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
273
|
+
`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
const contacts = res.data || [];
|
|
277
|
+
if (opts.json) {
|
|
278
|
+
process.stdout.write(JSON.stringify(contacts, null, 2) + "\n");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (contacts.length === 0) {
|
|
282
|
+
process.stdout.write("No contacts.\n");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
process.stdout.write(
|
|
286
|
+
"Username".padEnd(20) + "Role".padEnd(10) + "Unread".padEnd(8) + "Display Name\n"
|
|
287
|
+
);
|
|
288
|
+
for (const c of contacts) {
|
|
289
|
+
process.stdout.write(
|
|
290
|
+
`${(c.username || "").padEnd(20)}${(c.role || "").padEnd(10)}${String(c.unreadCount ?? 0).padEnd(8)}${c.displayName || ""}
|
|
291
|
+
`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
296
|
+
`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
im.command("conversations").description("List conversations").option("--unread", "Show only conversations with unread messages").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
301
|
+
const client = getIMClient2();
|
|
302
|
+
try {
|
|
303
|
+
const listOpts = {};
|
|
304
|
+
if (opts.unread) {
|
|
305
|
+
listOpts.withUnread = true;
|
|
306
|
+
listOpts.unreadOnly = true;
|
|
307
|
+
}
|
|
308
|
+
const res = await client.im.conversations.list(listOpts);
|
|
309
|
+
if (!res.ok) {
|
|
310
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
311
|
+
`);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
const list = res.data || [];
|
|
315
|
+
if (opts.json) {
|
|
316
|
+
process.stdout.write(JSON.stringify(list, null, 2) + "\n");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (list.length === 0) {
|
|
320
|
+
process.stdout.write("No conversations.\n");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
for (const c of list) {
|
|
324
|
+
const unread = c.unreadCount ? ` (${c.unreadCount} unread)` : "";
|
|
325
|
+
process.stdout.write(`${c.id || ""} ${c.type || ""} ${c.title || ""}${unread}
|
|
326
|
+
`);
|
|
327
|
+
}
|
|
328
|
+
} catch (err) {
|
|
329
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
330
|
+
`);
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
im.command("read <conversation-id>").description("Mark a conversation as read").action(async (convId) => {
|
|
335
|
+
const client = getIMClient2();
|
|
336
|
+
try {
|
|
337
|
+
const res = await client.im.conversations.markAsRead(convId);
|
|
338
|
+
if (!res.ok) {
|
|
339
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
340
|
+
`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
process.stdout.write("Marked as read.\n");
|
|
344
|
+
} catch (err) {
|
|
345
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
346
|
+
`);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
const groups = im.command("groups").description("Group chat management");
|
|
351
|
+
groups.command("create <title>").description("Create a new group").option("-m, --members <ids>", "Comma-separated member user IDs to add").option("--json", "Output raw JSON response").action(async (title, opts) => {
|
|
352
|
+
const client = getIMClient2();
|
|
353
|
+
try {
|
|
354
|
+
const members = opts.members ? opts.members.split(",").map((s) => s.trim()) : [];
|
|
355
|
+
const res = await client.im.groups.create({ title, members });
|
|
356
|
+
if (!res.ok) {
|
|
357
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
358
|
+
`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
if (opts.json) {
|
|
362
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
process.stdout.write(`Group created (groupId: ${res.data?.groupId})
|
|
366
|
+
`);
|
|
367
|
+
} catch (err) {
|
|
368
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
369
|
+
`);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
groups.command("list").description("List groups you belong to").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
374
|
+
const client = getIMClient2();
|
|
375
|
+
try {
|
|
376
|
+
const res = await client.im.groups.list();
|
|
377
|
+
if (!res.ok) {
|
|
378
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
379
|
+
`);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
const list = res.data || [];
|
|
383
|
+
if (opts.json) {
|
|
384
|
+
process.stdout.write(JSON.stringify(list, null, 2) + "\n");
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (list.length === 0) {
|
|
388
|
+
process.stdout.write("No groups.\n");
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
for (const g of list) {
|
|
392
|
+
process.stdout.write(`${g.groupId || ""} ${g.title || ""} (${g.members?.length || "?"} members)
|
|
393
|
+
`);
|
|
394
|
+
}
|
|
395
|
+
} catch (err) {
|
|
396
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
397
|
+
`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
groups.command("send <group-id> <message>").description("Send a message to a group").option("--json", "Output raw JSON response").action(async (groupId, message, opts) => {
|
|
402
|
+
const client = getIMClient2();
|
|
403
|
+
try {
|
|
404
|
+
const res = await client.im.groups.send(groupId, message);
|
|
405
|
+
if (!res.ok) {
|
|
406
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
407
|
+
`);
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
if (opts.json) {
|
|
411
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
process.stdout.write("Message sent to group.\n");
|
|
415
|
+
} catch (err) {
|
|
416
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
417
|
+
`);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
groups.command("messages <group-id>").description("View group message history").option("-n, --limit <n>", "Max number of messages to fetch", "20").option("--json", "Output raw JSON response").action(async (groupId, opts) => {
|
|
422
|
+
const client = getIMClient2();
|
|
423
|
+
try {
|
|
424
|
+
const res = await client.im.groups.getMessages(groupId, { limit: parseInt(opts.limit, 10) });
|
|
425
|
+
if (!res.ok) {
|
|
426
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
427
|
+
`);
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
const msgs = res.data || [];
|
|
431
|
+
if (opts.json) {
|
|
432
|
+
process.stdout.write(JSON.stringify(msgs, null, 2) + "\n");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (msgs.length === 0) {
|
|
436
|
+
process.stdout.write("No messages.\n");
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
for (const m of msgs) {
|
|
440
|
+
const ts = m.createdAt ? new Date(m.createdAt).toLocaleString() : "";
|
|
441
|
+
process.stdout.write(`[${ts}] ${m.senderId || "?"}: ${m.content}
|
|
442
|
+
`);
|
|
443
|
+
}
|
|
444
|
+
} catch (err) {
|
|
445
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
446
|
+
`);
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
im.command("me").description("Show current identity, agent card, credits, and stats").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
451
|
+
const client = getIMClient2();
|
|
452
|
+
try {
|
|
453
|
+
const res = await client.im.account.me();
|
|
454
|
+
if (!res.ok) {
|
|
455
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
456
|
+
`);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
const d = res.data;
|
|
460
|
+
if (opts.json) {
|
|
461
|
+
process.stdout.write(JSON.stringify(d, null, 2) + "\n");
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
process.stdout.write(`Display Name: ${d?.user?.displayName || "-"}
|
|
465
|
+
`);
|
|
466
|
+
process.stdout.write(`Username: ${d?.user?.username || "-"}
|
|
467
|
+
`);
|
|
468
|
+
process.stdout.write(`Role: ${d?.user?.role || "-"}
|
|
469
|
+
`);
|
|
470
|
+
process.stdout.write(`Agent Type: ${d?.agentCard?.agentType || "-"}
|
|
471
|
+
`);
|
|
472
|
+
process.stdout.write(`Credits: ${d?.credits?.balance ?? "-"}
|
|
473
|
+
`);
|
|
474
|
+
process.stdout.write(`Messages: ${d?.stats?.messagesSent ?? "-"}
|
|
475
|
+
`);
|
|
476
|
+
process.stdout.write(`Unread: ${d?.stats?.unreadCount ?? "-"}
|
|
477
|
+
`);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
480
|
+
`);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
im.command("credits").description("Show credits balance").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
485
|
+
const client = getIMClient2();
|
|
486
|
+
try {
|
|
487
|
+
const res = await client.im.credits.get();
|
|
488
|
+
if (!res.ok) {
|
|
489
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
490
|
+
`);
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
if (opts.json) {
|
|
494
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
process.stdout.write(`Balance: ${res.data?.balance ?? "-"}
|
|
498
|
+
`);
|
|
499
|
+
} catch (err) {
|
|
500
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
501
|
+
`);
|
|
502
|
+
process.exit(1);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
im.command("transactions").description("Show credit transaction history").option("-n, --limit <n>", "Max number of transactions to fetch", "20").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
506
|
+
const client = getIMClient2();
|
|
507
|
+
try {
|
|
508
|
+
const res = await client.im.credits.transactions({ limit: parseInt(opts.limit, 10) });
|
|
509
|
+
if (!res.ok) {
|
|
510
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
511
|
+
`);
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
const txns = res.data || [];
|
|
515
|
+
if (opts.json) {
|
|
516
|
+
process.stdout.write(JSON.stringify(txns, null, 2) + "\n");
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (txns.length === 0) {
|
|
520
|
+
process.stdout.write("No transactions.\n");
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
process.stdout.write(
|
|
524
|
+
"Date".padEnd(24) + "Type".padEnd(20) + "Amount".padEnd(12) + "Description\n"
|
|
525
|
+
);
|
|
526
|
+
for (const t of txns) {
|
|
527
|
+
const date = t.createdAt ? new Date(t.createdAt).toLocaleString() : "";
|
|
528
|
+
process.stdout.write(
|
|
529
|
+
`${date.padEnd(24)}${(t.type || "").padEnd(20)}${String(t.amount ?? "").padEnd(12)}${t.description || ""}
|
|
530
|
+
`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
} catch (err) {
|
|
534
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
535
|
+
`);
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
im.command("heartbeat").description("Send agent heartbeat (online/busy/offline) with optional load").option("--status <status>", "Presence status: online, busy, or offline", "online").option("--load <n>", "Current load factor (0.0 to 1.0)").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
540
|
+
const client = getIMClient2();
|
|
541
|
+
try {
|
|
542
|
+
const body = { status: opts.status };
|
|
543
|
+
if (opts.load !== void 0) {
|
|
544
|
+
const load = parseFloat(opts.load);
|
|
545
|
+
if (!isNaN(load)) body.load = load;
|
|
546
|
+
}
|
|
547
|
+
const res = await client.im.account._r("POST", "/api/im/agents/heartbeat", body);
|
|
548
|
+
if (!res.ok) {
|
|
549
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
550
|
+
`);
|
|
551
|
+
process.exit(1);
|
|
552
|
+
}
|
|
553
|
+
if (opts.json) {
|
|
554
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
process.stdout.write(`Heartbeat sent (status: ${opts.status}${opts.load !== void 0 ? `, load: ${opts.load}` : ""}).
|
|
558
|
+
`);
|
|
559
|
+
} catch (err) {
|
|
560
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
561
|
+
`);
|
|
562
|
+
process.exit(1);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
im.command("health").description("Check IM service health").action(async () => {
|
|
566
|
+
const client = getIMClient2();
|
|
567
|
+
try {
|
|
568
|
+
const res = await client.im.health();
|
|
569
|
+
if (!res.ok) {
|
|
570
|
+
process.stderr.write(`IM Service: ERROR
|
|
571
|
+
`);
|
|
572
|
+
process.stderr.write(`${JSON.stringify(res.error)}
|
|
573
|
+
`);
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
576
|
+
process.stdout.write("IM Service: OK\n");
|
|
577
|
+
} catch (err) {
|
|
578
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
579
|
+
`);
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/commands/context.ts
|
|
586
|
+
function register2(parent, _getIMClient, getAPIClient2) {
|
|
587
|
+
const ctx = parent.command("context").description("Context loading, searching, and caching");
|
|
588
|
+
ctx.command("load <urls...>").description("Load one or more URLs into context").option("-f, --format <fmt>", "output format: hqcc, raw, or both", "hqcc").option("--json", "output raw JSON response").action(async (urls, opts) => {
|
|
589
|
+
const client = getAPIClient2();
|
|
590
|
+
try {
|
|
591
|
+
const input = urls.length === 1 ? urls[0] : urls;
|
|
592
|
+
const format = opts.format;
|
|
593
|
+
const res = await client.load(input, {
|
|
594
|
+
return: { format }
|
|
595
|
+
});
|
|
596
|
+
if (opts.json) {
|
|
597
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (!res.success) {
|
|
601
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
602
|
+
`);
|
|
603
|
+
process.exit(1);
|
|
604
|
+
}
|
|
605
|
+
const results = res.results ?? (res.result ? [res.result] : []);
|
|
606
|
+
if (results.length === 0) {
|
|
607
|
+
process.stdout.write("No results returned.\n");
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
for (const item of results) {
|
|
611
|
+
process.stdout.write(`
|
|
612
|
+
--- ${item.url ?? item.input ?? "result"} ---
|
|
613
|
+
`);
|
|
614
|
+
const hqcc = item.hqcc ?? item.content ?? "";
|
|
615
|
+
if (hqcc) {
|
|
616
|
+
const truncated = hqcc.length > 2e3 ? hqcc.slice(0, 2e3) + "... [truncated]" : hqcc;
|
|
617
|
+
process.stdout.write(truncated + "\n");
|
|
618
|
+
}
|
|
619
|
+
if (item.cached !== void 0) {
|
|
620
|
+
process.stdout.write(`[cached: ${item.cached}]
|
|
621
|
+
`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
} catch (err) {
|
|
625
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
626
|
+
process.stderr.write(`Error: ${message}
|
|
627
|
+
`);
|
|
628
|
+
process.exit(1);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
ctx.command("search <query>").description("Search for content using a natural language query").option("-k, --top-k <n>", "number of results to return", "5").option("--json", "output raw JSON response").action(async (query, opts) => {
|
|
632
|
+
const client = getAPIClient2();
|
|
633
|
+
try {
|
|
634
|
+
const topK = parseInt(opts.topK, 10);
|
|
635
|
+
const res = await client.search(query, { topK });
|
|
636
|
+
if (opts.json) {
|
|
637
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (!res.success) {
|
|
641
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
642
|
+
`);
|
|
643
|
+
process.exit(1);
|
|
644
|
+
}
|
|
645
|
+
const results = res.results ?? (res.result ? [res.result] : []);
|
|
646
|
+
if (results.length === 0) {
|
|
647
|
+
process.stdout.write("No results found.\n");
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
process.stdout.write(`Search results for: "${query}"
|
|
651
|
+
|
|
652
|
+
`);
|
|
653
|
+
results.forEach((item, i) => {
|
|
654
|
+
process.stdout.write(`[${i + 1}] ${item.url ?? item.input ?? "result"}
|
|
655
|
+
`);
|
|
656
|
+
const hqcc = item.hqcc ?? item.content ?? "";
|
|
657
|
+
if (hqcc) {
|
|
658
|
+
const truncated = hqcc.length > 2e3 ? hqcc.slice(0, 2e3) + "... [truncated]" : hqcc;
|
|
659
|
+
process.stdout.write(truncated + "\n");
|
|
660
|
+
}
|
|
661
|
+
process.stdout.write("\n");
|
|
662
|
+
});
|
|
663
|
+
} catch (err) {
|
|
664
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
665
|
+
process.stderr.write(`Error: ${message}
|
|
666
|
+
`);
|
|
667
|
+
process.exit(1);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
ctx.command("save <url> <hqcc>").description("Save a URL and its HQCC content to the context cache").option("--json", "output raw JSON response").action(async (url, hqcc, opts) => {
|
|
671
|
+
const client = getAPIClient2();
|
|
672
|
+
try {
|
|
673
|
+
const res = await client.save({ url, hqcc });
|
|
674
|
+
if (opts.json) {
|
|
675
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (!res.success) {
|
|
679
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
680
|
+
`);
|
|
681
|
+
process.exit(1);
|
|
682
|
+
}
|
|
683
|
+
process.stdout.write(`Saved: ${url}
|
|
684
|
+
`);
|
|
685
|
+
} catch (err) {
|
|
686
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
687
|
+
process.stderr.write(`Error: ${message}
|
|
688
|
+
`);
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/commands/evolve.ts
|
|
695
|
+
function parseSignals(raw) {
|
|
696
|
+
if (!raw) return void 0;
|
|
697
|
+
const trimmed = raw.trim();
|
|
698
|
+
if (trimmed.startsWith("[")) {
|
|
699
|
+
try {
|
|
700
|
+
const parsed = JSON.parse(trimmed);
|
|
701
|
+
if (Array.isArray(parsed)) return parsed.map(String);
|
|
702
|
+
} catch {
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return trimmed.split(",").map((s) => s.trim()).filter(Boolean);
|
|
706
|
+
}
|
|
707
|
+
function handleError(err) {
|
|
708
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
709
|
+
process.stderr.write(`Error: ${message}
|
|
710
|
+
`);
|
|
711
|
+
process.exit(1);
|
|
712
|
+
}
|
|
713
|
+
function printResult(res, label) {
|
|
714
|
+
if (!res.ok) {
|
|
715
|
+
const errMsg = res.error && typeof res.error === "object" && "message" in res.error ? res.error.message : JSON.stringify(res.error);
|
|
716
|
+
process.stderr.write(`Error: ${errMsg || "Unknown error"}
|
|
717
|
+
`);
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
if (label) {
|
|
721
|
+
process.stdout.write(`${label}
|
|
722
|
+
`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function register3(parent, getIMClient2, _getAPIClient) {
|
|
726
|
+
const evolve = parent.command("evolve").description("Evolution engine \u2014 analyze signals, manage genes, track learning");
|
|
727
|
+
evolve.command("analyze").description("Analyze signals to find matching evolution strategies").option("-e, --error <msg>", "error message to analyze").option("-s, --signals <signals>", "signals as JSON array or comma-separated list").option("--task-status <status>", "task status (e.g. failed, timeout)").option("--provider <name>", "provider name (e.g. openai, exa)").option("--stage <stage>", "pipeline stage").option("--severity <level>", "severity level (low, medium, high, critical)").option("--tags <tags>", "comma-separated tags").option("--scope <scope>", "evolution scope (default: global)").option("--json", "output raw JSON response").action(async (opts) => {
|
|
728
|
+
const client = getIMClient2();
|
|
729
|
+
try {
|
|
730
|
+
const signals = parseSignals(opts.signals);
|
|
731
|
+
const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean) : void 0;
|
|
732
|
+
const res = await client.im.evolution.analyze({
|
|
733
|
+
signals,
|
|
734
|
+
error: opts.error,
|
|
735
|
+
task_status: opts.taskStatus,
|
|
736
|
+
provider: opts.provider,
|
|
737
|
+
stage: opts.stage,
|
|
738
|
+
severity: opts.severity,
|
|
739
|
+
tags,
|
|
740
|
+
scope: opts.scope
|
|
741
|
+
});
|
|
742
|
+
if (opts.json) {
|
|
743
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
printResult(res);
|
|
747
|
+
const data = res.data;
|
|
748
|
+
if (data) {
|
|
749
|
+
const matches = data.matches;
|
|
750
|
+
const count = matches?.length ?? 0;
|
|
751
|
+
process.stdout.write(`Matched ${count} gene(s)
|
|
752
|
+
`);
|
|
753
|
+
if (matches && count > 0) {
|
|
754
|
+
for (const m of matches) {
|
|
755
|
+
const id = m.gene_id ?? m.id ?? "?";
|
|
756
|
+
const title = m.title ?? m.name ?? "";
|
|
757
|
+
const score = m.score !== void 0 ? ` (score: ${m.score})` : "";
|
|
758
|
+
process.stdout.write(` \u2022 ${id}${title ? ` \u2014 ${title}` : ""}${score}
|
|
759
|
+
`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
} catch (err) {
|
|
764
|
+
handleError(err);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
evolve.command("record").description("Record an outcome against an evolution gene").requiredOption("-g, --gene <id>", "gene ID to record against").requiredOption("-o, --outcome <outcome>", "outcome: success, failure, partial").option("-s, --signals <signals>", "signals as JSON array or comma-separated list").option("--score <n>", "outcome score (0-1)").option("--summary <text>", "brief summary of the outcome").option("--scope <scope>", "evolution scope (default: global)").option("--json", "output raw JSON response").action(async (opts) => {
|
|
768
|
+
const client = getIMClient2();
|
|
769
|
+
try {
|
|
770
|
+
const signals = parseSignals(opts.signals);
|
|
771
|
+
const score = opts.score !== void 0 ? parseFloat(opts.score) : void 0;
|
|
772
|
+
const res = await client.im.evolution.record({
|
|
773
|
+
gene_id: opts.gene,
|
|
774
|
+
signals,
|
|
775
|
+
outcome: opts.outcome,
|
|
776
|
+
score,
|
|
777
|
+
summary: opts.summary,
|
|
778
|
+
scope: opts.scope
|
|
779
|
+
});
|
|
780
|
+
if (opts.json) {
|
|
781
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
printResult(res, `Recorded outcome "${opts.outcome}" for gene ${opts.gene}`);
|
|
785
|
+
} catch (err) {
|
|
786
|
+
handleError(err);
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
evolve.command("report").description("Submit a full evolution report (error + status context)").requiredOption("-e, --error <msg>", "raw error message or context").requiredOption("--status <outcome>", "final task outcome (success, failure, partial)").option("--task <context>", "task context description").option("--wait", "poll for report completion (max 60s)").option("--json", "output raw JSON response").action(async (opts) => {
|
|
790
|
+
const client = getIMClient2();
|
|
791
|
+
try {
|
|
792
|
+
const res = await client.im.evolution.submitReport({
|
|
793
|
+
rawContext: opts.error,
|
|
794
|
+
outcome: opts.status,
|
|
795
|
+
taskContext: opts.task
|
|
796
|
+
});
|
|
797
|
+
if (opts.json && !opts.wait) {
|
|
798
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (!res.ok) {
|
|
802
|
+
const errMsg = res.error && typeof res.error === "object" && "message" in res.error ? res.error.message : JSON.stringify(res.error);
|
|
803
|
+
process.stderr.write(`Error: ${errMsg || "Unknown error"}
|
|
804
|
+
`);
|
|
805
|
+
process.exit(1);
|
|
806
|
+
}
|
|
807
|
+
const submitData = res.data;
|
|
808
|
+
const traceId = submitData?.trace_id;
|
|
809
|
+
if (!opts.wait || !traceId) {
|
|
810
|
+
if (opts.json) {
|
|
811
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
812
|
+
} else {
|
|
813
|
+
process.stdout.write(`Report submitted. trace_id: ${traceId ?? "unknown"}
|
|
814
|
+
`);
|
|
815
|
+
if (submitData?.fast_signals) {
|
|
816
|
+
process.stdout.write(`Fast signals: ${JSON.stringify(submitData.fast_signals)}
|
|
817
|
+
`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (!opts.json) {
|
|
823
|
+
process.stdout.write(`Waiting for report ${traceId} `);
|
|
824
|
+
}
|
|
825
|
+
const maxIterations = 30;
|
|
826
|
+
let lastStatus;
|
|
827
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
828
|
+
await new Promise((resolve2) => setTimeout(resolve2, 2e3));
|
|
829
|
+
if (!opts.json) process.stdout.write(".");
|
|
830
|
+
const statusRes = await client.im.evolution.getReportStatus(traceId);
|
|
831
|
+
if (!statusRes.ok) break;
|
|
832
|
+
const statusData = statusRes.data;
|
|
833
|
+
lastStatus = statusData;
|
|
834
|
+
if (statusData?.status === "done" || statusData?.status === "complete" || statusData?.status === "completed") {
|
|
835
|
+
if (!opts.json) {
|
|
836
|
+
process.stdout.write("\n");
|
|
837
|
+
process.stdout.write(`Status: ${statusData.status}
|
|
838
|
+
`);
|
|
839
|
+
if (statusData.root_cause) process.stdout.write(`Root cause: ${statusData.root_cause}
|
|
840
|
+
`);
|
|
841
|
+
if (statusData.extracted_signals) process.stdout.write(`Extracted signals: ${JSON.stringify(statusData.extracted_signals)}
|
|
842
|
+
`);
|
|
843
|
+
} else {
|
|
844
|
+
process.stdout.write(JSON.stringify({ trace_id: traceId, ...statusData }, null, 2) + "\n");
|
|
845
|
+
}
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (!opts.json) {
|
|
850
|
+
process.stdout.write("\n");
|
|
851
|
+
process.stdout.write(`Timed out waiting for report. Last status: ${JSON.stringify(lastStatus)}
|
|
852
|
+
`);
|
|
853
|
+
} else {
|
|
854
|
+
process.stdout.write(JSON.stringify({ trace_id: traceId, status: "timeout", last: lastStatus }, null, 2) + "\n");
|
|
855
|
+
}
|
|
856
|
+
process.exit(1);
|
|
857
|
+
} catch (err) {
|
|
858
|
+
handleError(err);
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
evolve.command("report-status <trace-id>").description("Check the status of a submitted evolution report").option("--json", "output raw JSON response").action(async (traceId, opts) => {
|
|
862
|
+
const client = getIMClient2();
|
|
863
|
+
try {
|
|
864
|
+
const res = await client.im.evolution.getReportStatus(traceId);
|
|
865
|
+
if (opts.json) {
|
|
866
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
printResult(res);
|
|
870
|
+
const data = res.data;
|
|
871
|
+
process.stdout.write(`trace_id: ${traceId}
|
|
872
|
+
`);
|
|
873
|
+
process.stdout.write(`status: ${data?.status ?? "unknown"}
|
|
874
|
+
`);
|
|
875
|
+
if (data?.root_cause) process.stdout.write(`root_cause: ${data.root_cause}
|
|
876
|
+
`);
|
|
877
|
+
if (data?.extracted_signals) process.stdout.write(`extracted_signals: ${JSON.stringify(data.extracted_signals)}
|
|
878
|
+
`);
|
|
879
|
+
} catch (err) {
|
|
880
|
+
handleError(err);
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
evolve.command("create").description("Create a new evolution gene").requiredOption("-c, --category <cat>", "gene category").requiredOption("-s, --signals <signals>", "trigger signals as JSON array or comma-separated list").requiredOption("--strategy <steps...>", "strategy steps (variadic)").option("-n, --name <title>", "gene title / display name").option("--scope <scope>", "evolution scope (default: global)").option("--json", "output raw JSON response").action(async (opts) => {
|
|
884
|
+
const client = getIMClient2();
|
|
885
|
+
try {
|
|
886
|
+
const signals_match = parseSignals(opts.signals) ?? [];
|
|
887
|
+
const res = await client.im.evolution.createGene({
|
|
888
|
+
category: opts.category,
|
|
889
|
+
signals_match,
|
|
890
|
+
strategy: opts.strategy,
|
|
891
|
+
title: opts.name,
|
|
892
|
+
scope: opts.scope
|
|
893
|
+
});
|
|
894
|
+
if (opts.json) {
|
|
895
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
printResult(res);
|
|
899
|
+
const data = res.data;
|
|
900
|
+
const id = data?.gene_id ?? data?.id ?? "unknown";
|
|
901
|
+
process.stdout.write(`Gene created: ${id}
|
|
902
|
+
`);
|
|
903
|
+
if (opts.name) process.stdout.write(`Title: ${opts.name}
|
|
904
|
+
`);
|
|
905
|
+
process.stdout.write(`Category: ${opts.category}
|
|
906
|
+
`);
|
|
907
|
+
} catch (err) {
|
|
908
|
+
handleError(err);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
evolve.command("genes").description("List your own evolution genes").option("--scope <scope>", "filter by evolution scope").option("--json", "output raw JSON response").action(async (opts) => {
|
|
912
|
+
const client = getIMClient2();
|
|
913
|
+
try {
|
|
914
|
+
const res = await client.im.evolution.listGenes(void 0, opts.scope);
|
|
915
|
+
if (opts.json) {
|
|
916
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
printResult(res);
|
|
920
|
+
const data = res.data;
|
|
921
|
+
const genes = Array.isArray(data) ? data : data?.genes ?? data?.items ?? [];
|
|
922
|
+
if (genes.length === 0) {
|
|
923
|
+
process.stdout.write("No genes found.\n");
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
process.stdout.write(`${genes.length} gene(s):
|
|
927
|
+
`);
|
|
928
|
+
for (const g of genes) {
|
|
929
|
+
const id = g.gene_id ?? g.id ?? "?";
|
|
930
|
+
const title = g.title ?? g.name ?? "";
|
|
931
|
+
const category = g.category ? ` [${g.category}]` : "";
|
|
932
|
+
const scope = g.scope ? ` (${g.scope})` : "";
|
|
933
|
+
process.stdout.write(` \u2022 ${id}${title ? ` \u2014 ${title}` : ""}${category}${scope}
|
|
934
|
+
`);
|
|
935
|
+
}
|
|
936
|
+
} catch (err) {
|
|
937
|
+
handleError(err);
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
evolve.command("stats").description("Show public evolution statistics").option("--json", "output raw JSON response").action(async (opts) => {
|
|
941
|
+
const client = getIMClient2();
|
|
942
|
+
try {
|
|
943
|
+
const res = await client.im.evolution.getStats();
|
|
944
|
+
if (opts.json) {
|
|
945
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
printResult(res);
|
|
949
|
+
const data = res.data;
|
|
950
|
+
if (data) {
|
|
951
|
+
for (const [key, val] of Object.entries(data)) {
|
|
952
|
+
process.stdout.write(`${key}: ${JSON.stringify(val)}
|
|
953
|
+
`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
} catch (err) {
|
|
957
|
+
handleError(err);
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
evolve.command("metrics").description("Show A/B experiment metrics").option("--json", "output raw JSON response").action(async (opts) => {
|
|
961
|
+
const client = getIMClient2();
|
|
962
|
+
try {
|
|
963
|
+
const res = await client.im.evolution.getMetrics();
|
|
964
|
+
if (opts.json) {
|
|
965
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
printResult(res);
|
|
969
|
+
const data = res.data;
|
|
970
|
+
if (data) {
|
|
971
|
+
for (const [key, val] of Object.entries(data)) {
|
|
972
|
+
process.stdout.write(`${key}: ${JSON.stringify(val)}
|
|
973
|
+
`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
} catch (err) {
|
|
977
|
+
handleError(err);
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
evolve.command("achievements").description("Show your evolution achievements").option("--json", "output raw JSON response").action(async (opts) => {
|
|
981
|
+
const client = getIMClient2();
|
|
982
|
+
try {
|
|
983
|
+
const res = await client.im.evolution.getAchievements();
|
|
984
|
+
if (opts.json) {
|
|
985
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
printResult(res);
|
|
989
|
+
const data = res.data;
|
|
990
|
+
const achievements = Array.isArray(data) ? data : data?.achievements ?? data?.items ?? [];
|
|
991
|
+
if (achievements.length === 0) {
|
|
992
|
+
process.stdout.write("No achievements yet.\n");
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
process.stdout.write(`${achievements.length} achievement(s):
|
|
996
|
+
`);
|
|
997
|
+
for (const a of achievements) {
|
|
998
|
+
const id = a.id ?? "?";
|
|
999
|
+
const title = a.title ?? a.name ?? "";
|
|
1000
|
+
const desc = a.description ? ` \u2014 ${a.description}` : "";
|
|
1001
|
+
process.stdout.write(` \u2022 ${id}${title ? ` ${title}` : ""}${desc}
|
|
1002
|
+
`);
|
|
1003
|
+
}
|
|
1004
|
+
} catch (err) {
|
|
1005
|
+
handleError(err);
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
evolve.command("sync").description("Get a sync snapshot of recent evolution data").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1009
|
+
const client = getIMClient2();
|
|
1010
|
+
try {
|
|
1011
|
+
const res = await client.im.evolution.getSyncSnapshot();
|
|
1012
|
+
if (opts.json) {
|
|
1013
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
printResult(res);
|
|
1017
|
+
const data = res.data;
|
|
1018
|
+
if (data) {
|
|
1019
|
+
const since = data.since ?? data.timestamp ?? data.generated_at;
|
|
1020
|
+
if (since) process.stdout.write(`Snapshot since: ${since}
|
|
1021
|
+
`);
|
|
1022
|
+
const genes = data.genes;
|
|
1023
|
+
const signals = data.signals;
|
|
1024
|
+
if (genes !== void 0) process.stdout.write(`Genes: ${genes.length}
|
|
1025
|
+
`);
|
|
1026
|
+
if (signals !== void 0) process.stdout.write(`Signals: ${signals.length}
|
|
1027
|
+
`);
|
|
1028
|
+
}
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
handleError(err);
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
evolve.command("export-skill <gene-id>").description("Export a gene as a reusable skill").option("--slug <slug>", "skill slug identifier").option("--name <displayName>", "skill display name").option("--json", "output raw JSON response").action(async (geneId, opts) => {
|
|
1034
|
+
const client = getIMClient2();
|
|
1035
|
+
try {
|
|
1036
|
+
const res = await client.im.evolution.exportAsSkill(geneId, {
|
|
1037
|
+
slug: opts.slug,
|
|
1038
|
+
displayName: opts.name
|
|
1039
|
+
});
|
|
1040
|
+
if (opts.json) {
|
|
1041
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
printResult(res);
|
|
1045
|
+
const data = res.data;
|
|
1046
|
+
process.stdout.write(`Skill exported from gene: ${geneId}
|
|
1047
|
+
`);
|
|
1048
|
+
if (data?.skill_id) process.stdout.write(`skill_id: ${data.skill_id}
|
|
1049
|
+
`);
|
|
1050
|
+
if (data?.slug) process.stdout.write(`slug: ${data.slug}
|
|
1051
|
+
`);
|
|
1052
|
+
if (data?.display_name) process.stdout.write(`display_name: ${data.display_name}
|
|
1053
|
+
`);
|
|
1054
|
+
} catch (err) {
|
|
1055
|
+
handleError(err);
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
evolve.command("scopes").description("List available evolution scopes").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1059
|
+
const client = getIMClient2();
|
|
1060
|
+
try {
|
|
1061
|
+
const res = await client.im.evolution.listScopes();
|
|
1062
|
+
if (opts.json) {
|
|
1063
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
printResult(res);
|
|
1067
|
+
const data = res.data;
|
|
1068
|
+
const scopes = Array.isArray(data) ? data : data?.scopes ?? data?.items ?? [];
|
|
1069
|
+
if (scopes.length === 0) {
|
|
1070
|
+
process.stdout.write("No scopes found.\n");
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
process.stdout.write(`${scopes.length} scope(s):
|
|
1074
|
+
`);
|
|
1075
|
+
for (const s of scopes) {
|
|
1076
|
+
if (typeof s === "string") {
|
|
1077
|
+
process.stdout.write(` \u2022 ${s}
|
|
1078
|
+
`);
|
|
1079
|
+
} else {
|
|
1080
|
+
const name = s.name ?? s.scope ?? s.id ?? JSON.stringify(s);
|
|
1081
|
+
process.stdout.write(` \u2022 ${name}
|
|
1082
|
+
`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
} catch (err) {
|
|
1086
|
+
handleError(err);
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
evolve.command("browse").description("Browse published evolution genes").option("-c, --category <cat>", "filter by category").option("--search <query>", "full-text search query").option("--sort <field>", "sort field (e.g. score, created_at)").option("-n, --limit <n>", "max results to return", "20").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1090
|
+
const client = getIMClient2();
|
|
1091
|
+
try {
|
|
1092
|
+
const limit = parseInt(opts.limit ?? "20", 10);
|
|
1093
|
+
const res = await client.im.evolution.browseGenes({
|
|
1094
|
+
category: opts.category,
|
|
1095
|
+
search: opts.search,
|
|
1096
|
+
sort: opts.sort,
|
|
1097
|
+
limit
|
|
1098
|
+
});
|
|
1099
|
+
if (opts.json) {
|
|
1100
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
printResult(res);
|
|
1104
|
+
const data = res.data;
|
|
1105
|
+
const genes = Array.isArray(data) ? data : data?.genes ?? data?.items ?? data?.results ?? [];
|
|
1106
|
+
if (genes.length === 0) {
|
|
1107
|
+
process.stdout.write("No genes found.\n");
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
process.stdout.write(`${genes.length} gene(s):
|
|
1111
|
+
`);
|
|
1112
|
+
for (const g of genes) {
|
|
1113
|
+
const id = g.gene_id ?? g.id ?? "?";
|
|
1114
|
+
const title = g.title ?? g.name ?? "";
|
|
1115
|
+
const category = g.category ? ` [${g.category}]` : "";
|
|
1116
|
+
const score = g.score !== void 0 ? ` score=${g.score}` : "";
|
|
1117
|
+
process.stdout.write(` \u2022 ${id}${title ? ` \u2014 ${title}` : ""}${category}${score}
|
|
1118
|
+
`);
|
|
1119
|
+
}
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
handleError(err);
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
evolve.command("publish <gene-id>").description("Publish a private gene to the evolution network").option("--skip-canary", "skip canary phase and publish directly").option("--json", "output raw JSON response").action(async (geneId, opts) => {
|
|
1125
|
+
const client = getIMClient2();
|
|
1126
|
+
try {
|
|
1127
|
+
const res = await client.im.evolution.publishGene(geneId, { skipCanary: opts.skipCanary });
|
|
1128
|
+
if (opts.json) {
|
|
1129
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
printResult(res, `Gene ${geneId} published${opts.skipCanary ? " (skipped canary)" : " (canary phase)"}`);
|
|
1133
|
+
} catch (err) {
|
|
1134
|
+
handleError(err);
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
evolve.command("fork <gene-id>").description("Fork a public gene with optional modifications").option("--strategy <steps...>", "override strategy steps").option("--json", "output raw JSON response").action(async (geneId, opts) => {
|
|
1138
|
+
const client = getIMClient2();
|
|
1139
|
+
try {
|
|
1140
|
+
const res = await client.im.evolution.forkGene({
|
|
1141
|
+
gene_id: geneId,
|
|
1142
|
+
modifications: opts.strategy ? { strategy: opts.strategy } : void 0
|
|
1143
|
+
});
|
|
1144
|
+
if (opts.json) {
|
|
1145
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
printResult(res);
|
|
1149
|
+
const data = res.data;
|
|
1150
|
+
const newId = data?.id ?? data?.gene_id ?? "unknown";
|
|
1151
|
+
process.stdout.write(`Forked gene ${geneId} \u2192 ${newId}
|
|
1152
|
+
`);
|
|
1153
|
+
} catch (err) {
|
|
1154
|
+
handleError(err);
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
evolve.command("delete <gene-id>").description("Delete a gene you own").option("--json", "output raw JSON response").action(async (geneId, opts) => {
|
|
1158
|
+
const client = getIMClient2();
|
|
1159
|
+
try {
|
|
1160
|
+
const res = await client.im.evolution.deleteGene(geneId);
|
|
1161
|
+
if (opts.json) {
|
|
1162
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
printResult(res, `Gene ${geneId} deleted`);
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
handleError(err);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
evolve.command("import <gene-id>").description("Import a published gene into your collection").option("--json", "output raw JSON response").action(async (geneId, opts) => {
|
|
1171
|
+
const client = getIMClient2();
|
|
1172
|
+
try {
|
|
1173
|
+
const res = await client.im.evolution.importGene(geneId);
|
|
1174
|
+
if (opts.json) {
|
|
1175
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
printResult(res, `Gene imported: ${geneId}`);
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
handleError(err);
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
evolve.command("distill").description("Trigger gene distillation (consolidate learnings)").option("--dry-run", "preview distillation without applying changes").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1184
|
+
const client = getIMClient2();
|
|
1185
|
+
try {
|
|
1186
|
+
const res = await client.im.evolution.distill(opts.dryRun);
|
|
1187
|
+
if (opts.json) {
|
|
1188
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
printResult(res);
|
|
1192
|
+
const data = res.data;
|
|
1193
|
+
if (opts.dryRun) {
|
|
1194
|
+
process.stdout.write("Dry-run distillation preview:\n");
|
|
1195
|
+
} else {
|
|
1196
|
+
process.stdout.write("Distillation triggered.\n");
|
|
1197
|
+
}
|
|
1198
|
+
if (data) {
|
|
1199
|
+
for (const [key, val] of Object.entries(data)) {
|
|
1200
|
+
process.stdout.write(` ${key}: ${JSON.stringify(val)}
|
|
1201
|
+
`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
handleError(err);
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// src/commands/task.ts
|
|
1211
|
+
function register4(parent, getIMClient2, _getAPIClient) {
|
|
1212
|
+
const task = parent.command("task").description("Manage tasks in the task marketplace");
|
|
1213
|
+
task.command("create").description("Create a new task").requiredOption("--title <title>", "task title").option("--description <description>", "task description").option("--capability <capability>", "required agent capability").option("--budget <budget>", "budget in credits", parseFloat).option("--json", "output raw JSON response").action(async (opts) => {
|
|
1214
|
+
const client = getIMClient2();
|
|
1215
|
+
try {
|
|
1216
|
+
const res = await client.im.tasks.create({
|
|
1217
|
+
title: opts.title,
|
|
1218
|
+
description: opts.description,
|
|
1219
|
+
capability: opts.capability,
|
|
1220
|
+
budget: opts.budget
|
|
1221
|
+
});
|
|
1222
|
+
if (opts.json) {
|
|
1223
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
if (!res.ok) {
|
|
1227
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1228
|
+
`);
|
|
1229
|
+
process.exit(1);
|
|
1230
|
+
}
|
|
1231
|
+
const t = res.data;
|
|
1232
|
+
process.stdout.write(`Task created successfully
|
|
1233
|
+
|
|
1234
|
+
`);
|
|
1235
|
+
process.stdout.write(`ID: ${t.id}
|
|
1236
|
+
`);
|
|
1237
|
+
process.stdout.write(`Title: ${t.title}
|
|
1238
|
+
`);
|
|
1239
|
+
process.stdout.write(`Status: ${t.status}
|
|
1240
|
+
`);
|
|
1241
|
+
if (t.description) process.stdout.write(`Description: ${t.description}
|
|
1242
|
+
`);
|
|
1243
|
+
if (t.capability) process.stdout.write(`Capability: ${t.capability}
|
|
1244
|
+
`);
|
|
1245
|
+
if (t.budget !== void 0) process.stdout.write(`Budget: ${t.budget}
|
|
1246
|
+
`);
|
|
1247
|
+
} catch (err) {
|
|
1248
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1249
|
+
process.stderr.write(`Error: ${message}
|
|
1250
|
+
`);
|
|
1251
|
+
process.exit(1);
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
task.command("list").description("List tasks").option("--status <status>", "filter by status").option("--capability <capability>", "filter by required capability").option("-n, --limit <n>", "maximum number of tasks to return", "20").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1255
|
+
const client = getIMClient2();
|
|
1256
|
+
try {
|
|
1257
|
+
const res = await client.im.tasks.list({
|
|
1258
|
+
status: opts.status,
|
|
1259
|
+
capability: opts.capability,
|
|
1260
|
+
limit: parseInt(opts.limit, 10)
|
|
1261
|
+
});
|
|
1262
|
+
if (opts.json) {
|
|
1263
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
if (!res.ok) {
|
|
1267
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1268
|
+
`);
|
|
1269
|
+
process.exit(1);
|
|
1270
|
+
}
|
|
1271
|
+
const tasks = res.data;
|
|
1272
|
+
if (!tasks || tasks.length === 0) {
|
|
1273
|
+
process.stdout.write("No tasks found.\n");
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
const idW = 24;
|
|
1277
|
+
const statusW = 12;
|
|
1278
|
+
const titleW = 40;
|
|
1279
|
+
const header = "ID".padEnd(idW) + "STATUS".padEnd(statusW) + "TITLE";
|
|
1280
|
+
const sep = "-".repeat(idW + statusW + titleW);
|
|
1281
|
+
process.stdout.write(header + "\n");
|
|
1282
|
+
process.stdout.write(sep + "\n");
|
|
1283
|
+
for (const t of tasks) {
|
|
1284
|
+
const title = t.title.length > titleW ? t.title.slice(0, titleW - 3) + "..." : t.title;
|
|
1285
|
+
process.stdout.write(
|
|
1286
|
+
String(t.id).padEnd(idW) + String(t.status).padEnd(statusW) + title + "\n"
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
process.stdout.write(`
|
|
1290
|
+
${tasks.length} task(s) listed.
|
|
1291
|
+
`);
|
|
1292
|
+
} catch (err) {
|
|
1293
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1294
|
+
process.stderr.write(`Error: ${message}
|
|
1295
|
+
`);
|
|
1296
|
+
process.exit(1);
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
task.command("get <task-id>").description("Get task details and logs").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1300
|
+
const client = getIMClient2();
|
|
1301
|
+
try {
|
|
1302
|
+
const res = await client.im.tasks.get(taskId);
|
|
1303
|
+
if (opts.json) {
|
|
1304
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
if (!res.ok) {
|
|
1308
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1309
|
+
`);
|
|
1310
|
+
process.exit(1);
|
|
1311
|
+
}
|
|
1312
|
+
const t = res.data;
|
|
1313
|
+
process.stdout.write(`ID: ${t.id}
|
|
1314
|
+
`);
|
|
1315
|
+
process.stdout.write(`Title: ${t.title}
|
|
1316
|
+
`);
|
|
1317
|
+
process.stdout.write(`Status: ${t.status}
|
|
1318
|
+
`);
|
|
1319
|
+
if (t.description) process.stdout.write(`Description: ${t.description}
|
|
1320
|
+
`);
|
|
1321
|
+
if (t.capability) process.stdout.write(`Capability: ${t.capability}
|
|
1322
|
+
`);
|
|
1323
|
+
if (t.budget !== void 0) process.stdout.write(`Budget: ${t.budget}
|
|
1324
|
+
`);
|
|
1325
|
+
if (t.progress != null) process.stdout.write(`Progress: ${t.progress}
|
|
1326
|
+
`);
|
|
1327
|
+
if (t.statusMessage) process.stdout.write(`Status Msg: ${t.statusMessage}
|
|
1328
|
+
`);
|
|
1329
|
+
if (t.creatorId) process.stdout.write(`Creator: ${t.creatorId}
|
|
1330
|
+
`);
|
|
1331
|
+
if (t.assigneeId) process.stdout.write(`Assignee: ${t.assigneeId}
|
|
1332
|
+
`);
|
|
1333
|
+
if (t.createdAt) process.stdout.write(`Created: ${t.createdAt}
|
|
1334
|
+
`);
|
|
1335
|
+
if (t.updatedAt) process.stdout.write(`Updated: ${t.updatedAt}
|
|
1336
|
+
`);
|
|
1337
|
+
if (t.completedAt) process.stdout.write(`Completed: ${t.completedAt}
|
|
1338
|
+
`);
|
|
1339
|
+
if (t.result) process.stdout.write(`Result: ${t.result}
|
|
1340
|
+
`);
|
|
1341
|
+
if (t.error) process.stdout.write(`Error: ${t.error}
|
|
1342
|
+
`);
|
|
1343
|
+
const logs = t.logs ?? t.taskLogs ?? [];
|
|
1344
|
+
if (logs.length > 0) {
|
|
1345
|
+
process.stdout.write(`
|
|
1346
|
+
Logs (${logs.length}):
|
|
1347
|
+
`);
|
|
1348
|
+
for (const log of logs) {
|
|
1349
|
+
const ts = log.createdAt ?? log.timestamp ?? "";
|
|
1350
|
+
const msg = log.message ?? log.content ?? JSON.stringify(log);
|
|
1351
|
+
process.stdout.write(` [${ts}] ${msg}
|
|
1352
|
+
`);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1357
|
+
process.stderr.write(`Error: ${message}
|
|
1358
|
+
`);
|
|
1359
|
+
process.exit(1);
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
task.command("claim <task-id>").description("Claim a pending task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1363
|
+
const client = getIMClient2();
|
|
1364
|
+
try {
|
|
1365
|
+
const res = await client.im.tasks.claim(taskId);
|
|
1366
|
+
if (opts.json) {
|
|
1367
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (!res.ok) {
|
|
1371
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1372
|
+
`);
|
|
1373
|
+
process.exit(1);
|
|
1374
|
+
}
|
|
1375
|
+
const t = res.data;
|
|
1376
|
+
process.stdout.write(`Task claimed successfully
|
|
1377
|
+
|
|
1378
|
+
`);
|
|
1379
|
+
process.stdout.write(`ID: ${t.id}
|
|
1380
|
+
`);
|
|
1381
|
+
process.stdout.write(`Title: ${t.title}
|
|
1382
|
+
`);
|
|
1383
|
+
process.stdout.write(`Status: ${t.status}
|
|
1384
|
+
`);
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1387
|
+
process.stderr.write(`Error: ${message}
|
|
1388
|
+
`);
|
|
1389
|
+
process.exit(1);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
task.command("update <task-id>").description("Update a task").option("--title <title>", "new title").option("--description <description>", "new description").option("--status <status>", "new status").option("--progress <progress>", "progress (0.0 to 1.0)", parseFloat).option("--status-message <statusMessage>", "status message").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1393
|
+
const client = getIMClient2();
|
|
1394
|
+
try {
|
|
1395
|
+
const res = await client.im.tasks.update(taskId, {
|
|
1396
|
+
title: opts.title,
|
|
1397
|
+
description: opts.description,
|
|
1398
|
+
status: opts.status,
|
|
1399
|
+
progress: opts.progress,
|
|
1400
|
+
statusMessage: opts.statusMessage
|
|
1401
|
+
});
|
|
1402
|
+
if (opts.json) {
|
|
1403
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
if (!res.ok) {
|
|
1407
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1408
|
+
`);
|
|
1409
|
+
process.exit(1);
|
|
1410
|
+
}
|
|
1411
|
+
const t = res.data;
|
|
1412
|
+
process.stdout.write(`Task updated successfully
|
|
1413
|
+
|
|
1414
|
+
`);
|
|
1415
|
+
process.stdout.write(`ID: ${t.id}
|
|
1416
|
+
`);
|
|
1417
|
+
process.stdout.write(`Title: ${t.title}
|
|
1418
|
+
`);
|
|
1419
|
+
process.stdout.write(`Status: ${t.status}
|
|
1420
|
+
`);
|
|
1421
|
+
if (t.progress != null) process.stdout.write(`Progress: ${t.progress}
|
|
1422
|
+
`);
|
|
1423
|
+
if (t.statusMessage) process.stdout.write(`Message: ${t.statusMessage}
|
|
1424
|
+
`);
|
|
1425
|
+
} catch (err) {
|
|
1426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1427
|
+
process.stderr.write(`Error: ${message}
|
|
1428
|
+
`);
|
|
1429
|
+
process.exit(1);
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
task.command("complete <task-id>").description("Mark a task as complete").option("--result <result>", "result or output of the task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1433
|
+
const client = getIMClient2();
|
|
1434
|
+
try {
|
|
1435
|
+
const res = await client.im.tasks.complete(taskId, {
|
|
1436
|
+
result: opts.result
|
|
1437
|
+
});
|
|
1438
|
+
if (opts.json) {
|
|
1439
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
if (!res.ok) {
|
|
1443
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1444
|
+
`);
|
|
1445
|
+
process.exit(1);
|
|
1446
|
+
}
|
|
1447
|
+
const t = res.data;
|
|
1448
|
+
process.stdout.write(`Task completed successfully
|
|
1449
|
+
|
|
1450
|
+
`);
|
|
1451
|
+
process.stdout.write(`ID: ${t.id}
|
|
1452
|
+
`);
|
|
1453
|
+
process.stdout.write(`Title: ${t.title}
|
|
1454
|
+
`);
|
|
1455
|
+
process.stdout.write(`Status: ${t.status}
|
|
1456
|
+
`);
|
|
1457
|
+
if (t.result) process.stdout.write(`Result: ${t.result}
|
|
1458
|
+
`);
|
|
1459
|
+
} catch (err) {
|
|
1460
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1461
|
+
process.stderr.write(`Error: ${message}
|
|
1462
|
+
`);
|
|
1463
|
+
process.exit(1);
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1466
|
+
task.command("fail <task-id>").description("Mark a task as failed").requiredOption("--error <error>", "error message describing why the task failed").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1467
|
+
const client = getIMClient2();
|
|
1468
|
+
try {
|
|
1469
|
+
const res = await client.im.tasks.fail(taskId, opts.error);
|
|
1470
|
+
if (opts.json) {
|
|
1471
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
if (!res.ok) {
|
|
1475
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1476
|
+
`);
|
|
1477
|
+
process.exit(1);
|
|
1478
|
+
}
|
|
1479
|
+
const t = res.data;
|
|
1480
|
+
process.stdout.write(`Task marked as failed
|
|
1481
|
+
|
|
1482
|
+
`);
|
|
1483
|
+
process.stdout.write(`ID: ${t.id}
|
|
1484
|
+
`);
|
|
1485
|
+
process.stdout.write(`Title: ${t.title}
|
|
1486
|
+
`);
|
|
1487
|
+
process.stdout.write(`Status: ${t.status}
|
|
1488
|
+
`);
|
|
1489
|
+
if (t.error) process.stdout.write(`Error: ${t.error}
|
|
1490
|
+
`);
|
|
1491
|
+
} catch (err) {
|
|
1492
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1493
|
+
process.stderr.write(`Error: ${message}
|
|
1494
|
+
`);
|
|
1495
|
+
process.exit(1);
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
task.command("approve <task-id>").description("Approve a completed task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1499
|
+
const client = getIMClient2();
|
|
1500
|
+
try {
|
|
1501
|
+
const res = await client.im.tasks.approve(taskId);
|
|
1502
|
+
if (opts.json) {
|
|
1503
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
if (!res.ok) {
|
|
1507
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1508
|
+
`);
|
|
1509
|
+
process.exit(1);
|
|
1510
|
+
}
|
|
1511
|
+
const t = res.data;
|
|
1512
|
+
process.stdout.write(`Task approved successfully
|
|
1513
|
+
|
|
1514
|
+
`);
|
|
1515
|
+
process.stdout.write(`ID: ${t.id}
|
|
1516
|
+
`);
|
|
1517
|
+
process.stdout.write(`Title: ${t.title}
|
|
1518
|
+
`);
|
|
1519
|
+
process.stdout.write(`Status: ${t.status}
|
|
1520
|
+
`);
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1523
|
+
process.stderr.write(`Error: ${message}
|
|
1524
|
+
`);
|
|
1525
|
+
process.exit(1);
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
task.command("reject <task-id>").description("Reject a task").requiredOption("--reason <reason>", "reason for rejection").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1529
|
+
const client = getIMClient2();
|
|
1530
|
+
try {
|
|
1531
|
+
const res = await client.im.tasks.reject(taskId, opts.reason);
|
|
1532
|
+
if (opts.json) {
|
|
1533
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
if (!res.ok) {
|
|
1537
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1538
|
+
`);
|
|
1539
|
+
process.exit(1);
|
|
1540
|
+
}
|
|
1541
|
+
const t = res.data;
|
|
1542
|
+
process.stdout.write(`Task rejected
|
|
1543
|
+
|
|
1544
|
+
`);
|
|
1545
|
+
process.stdout.write(`ID: ${t.id}
|
|
1546
|
+
`);
|
|
1547
|
+
process.stdout.write(`Title: ${t.title}
|
|
1548
|
+
`);
|
|
1549
|
+
process.stdout.write(`Status: ${t.status}
|
|
1550
|
+
`);
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1553
|
+
process.stderr.write(`Error: ${message}
|
|
1554
|
+
`);
|
|
1555
|
+
process.exit(1);
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
task.command("cancel <task-id>").description("Cancel a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
|
|
1559
|
+
const client = getIMClient2();
|
|
1560
|
+
try {
|
|
1561
|
+
const res = await client.im.tasks.cancel(taskId);
|
|
1562
|
+
if (opts.json) {
|
|
1563
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
if (!res.ok) {
|
|
1567
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1568
|
+
`);
|
|
1569
|
+
process.exit(1);
|
|
1570
|
+
}
|
|
1571
|
+
const t = res.data;
|
|
1572
|
+
process.stdout.write(`Task cancelled
|
|
1573
|
+
|
|
1574
|
+
`);
|
|
1575
|
+
process.stdout.write(`ID: ${t.id}
|
|
1576
|
+
`);
|
|
1577
|
+
process.stdout.write(`Title: ${t.title}
|
|
1578
|
+
`);
|
|
1579
|
+
process.stdout.write(`Status: ${t.status}
|
|
1580
|
+
`);
|
|
1581
|
+
} catch (err) {
|
|
1582
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1583
|
+
process.stderr.write(`Error: ${message}
|
|
1584
|
+
`);
|
|
1585
|
+
process.exit(1);
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/commands/memory.ts
|
|
1591
|
+
function register5(parent, getIMClient2, _getAPIClient) {
|
|
1592
|
+
const mem = parent.command("memory").description("Agent memory file management");
|
|
1593
|
+
mem.command("write").description("Write a memory file").requiredOption("-s, --scope <scope>", "memory scope").requiredOption("-p, --path <path>", "file path within scope").requiredOption("-c, --content <content>", "file content").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1594
|
+
const client = getIMClient2();
|
|
1595
|
+
try {
|
|
1596
|
+
const res = await client.im.memory.createFile({
|
|
1597
|
+
scope: opts.scope,
|
|
1598
|
+
path: opts.path,
|
|
1599
|
+
content: opts.content
|
|
1600
|
+
});
|
|
1601
|
+
if (opts.json) {
|
|
1602
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (!res.ok) {
|
|
1606
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1607
|
+
`);
|
|
1608
|
+
process.exit(1);
|
|
1609
|
+
}
|
|
1610
|
+
const file = res.data;
|
|
1611
|
+
process.stdout.write(`Memory file created
|
|
1612
|
+
`);
|
|
1613
|
+
process.stdout.write(` ID: ${file.id}
|
|
1614
|
+
`);
|
|
1615
|
+
process.stdout.write(` Scope: ${file.scope}
|
|
1616
|
+
`);
|
|
1617
|
+
process.stdout.write(` Path: ${file.path}
|
|
1618
|
+
`);
|
|
1619
|
+
} catch (err) {
|
|
1620
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1621
|
+
process.stderr.write(`Error: ${message}
|
|
1622
|
+
`);
|
|
1623
|
+
process.exit(1);
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
mem.command("read [file-id]").description("Read a memory file by ID, or filter by scope/path").option("-s, --scope <scope>", "filter by scope (used when no file-id given)").option("-p, --path <path>", "filter by path (used when no file-id given)").option("--json", "output raw JSON response").action(async (fileId, opts) => {
|
|
1627
|
+
const client = getIMClient2();
|
|
1628
|
+
try {
|
|
1629
|
+
if (fileId) {
|
|
1630
|
+
const res = await client.im.memory.getFile(fileId);
|
|
1631
|
+
if (opts.json) {
|
|
1632
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
if (!res.ok) {
|
|
1636
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1637
|
+
`);
|
|
1638
|
+
process.exit(1);
|
|
1639
|
+
}
|
|
1640
|
+
const file = res.data;
|
|
1641
|
+
process.stdout.write(`ID: ${file.id}
|
|
1642
|
+
`);
|
|
1643
|
+
process.stdout.write(`Scope: ${file.scope}
|
|
1644
|
+
`);
|
|
1645
|
+
process.stdout.write(`Path: ${file.path}
|
|
1646
|
+
`);
|
|
1647
|
+
process.stdout.write(`
|
|
1648
|
+
${file.content ?? ""}
|
|
1649
|
+
`);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
const listRes = await client.im.memory.listFiles({
|
|
1653
|
+
scope: opts.scope,
|
|
1654
|
+
path: opts.path
|
|
1655
|
+
});
|
|
1656
|
+
if (opts.json) {
|
|
1657
|
+
if (listRes.ok && Array.isArray(listRes.data) && listRes.data.length === 1) {
|
|
1658
|
+
const detailRes = await client.im.memory.getFile(listRes.data[0].id);
|
|
1659
|
+
process.stdout.write(JSON.stringify(detailRes, null, 2) + "\n");
|
|
1660
|
+
} else {
|
|
1661
|
+
process.stdout.write(JSON.stringify(listRes, null, 2) + "\n");
|
|
1662
|
+
}
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
if (!listRes.ok) {
|
|
1666
|
+
process.stderr.write(`Error: ${listRes.error?.message || "Unknown error"}
|
|
1667
|
+
`);
|
|
1668
|
+
process.exit(1);
|
|
1669
|
+
}
|
|
1670
|
+
const files = listRes.data;
|
|
1671
|
+
if (files.length === 0) {
|
|
1672
|
+
process.stdout.write("No memory files found.\n");
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
if (files.length === 1) {
|
|
1676
|
+
const detailRes = await client.im.memory.getFile(files[0].id);
|
|
1677
|
+
if (!detailRes.ok) {
|
|
1678
|
+
process.stderr.write(`Error: ${detailRes.error?.message || "Unknown error"}
|
|
1679
|
+
`);
|
|
1680
|
+
process.exit(1);
|
|
1681
|
+
}
|
|
1682
|
+
const file = detailRes.data;
|
|
1683
|
+
process.stdout.write(`ID: ${file.id}
|
|
1684
|
+
`);
|
|
1685
|
+
process.stdout.write(`Scope: ${file.scope}
|
|
1686
|
+
`);
|
|
1687
|
+
process.stdout.write(`Path: ${file.path}
|
|
1688
|
+
`);
|
|
1689
|
+
process.stdout.write(`
|
|
1690
|
+
${file.content ?? ""}
|
|
1691
|
+
`);
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
printFileTable(files);
|
|
1695
|
+
} catch (err) {
|
|
1696
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1697
|
+
process.stderr.write(`Error: ${message}
|
|
1698
|
+
`);
|
|
1699
|
+
process.exit(1);
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
mem.command("list").description("List memory files").option("-s, --scope <scope>", "filter by scope").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1703
|
+
const client = getIMClient2();
|
|
1704
|
+
try {
|
|
1705
|
+
const res = await client.im.memory.listFiles({ scope: opts.scope });
|
|
1706
|
+
if (opts.json) {
|
|
1707
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
if (!res.ok) {
|
|
1711
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1712
|
+
`);
|
|
1713
|
+
process.exit(1);
|
|
1714
|
+
}
|
|
1715
|
+
const files = res.data;
|
|
1716
|
+
if (files.length === 0) {
|
|
1717
|
+
process.stdout.write("No memory files found.\n");
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
printFileTable(files);
|
|
1721
|
+
} catch (err) {
|
|
1722
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1723
|
+
process.stderr.write(`Error: ${message}
|
|
1724
|
+
`);
|
|
1725
|
+
process.exit(1);
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1728
|
+
mem.command("delete <file-id>").description("Delete a memory file by ID").option("--json", "output raw JSON response").action(async (fileId, opts) => {
|
|
1729
|
+
const client = getIMClient2();
|
|
1730
|
+
try {
|
|
1731
|
+
const res = await client.im.memory.deleteFile(fileId);
|
|
1732
|
+
if (opts.json) {
|
|
1733
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
if (!res.ok) {
|
|
1737
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1738
|
+
`);
|
|
1739
|
+
process.exit(1);
|
|
1740
|
+
}
|
|
1741
|
+
process.stdout.write(`Deleted memory file: ${fileId}
|
|
1742
|
+
`);
|
|
1743
|
+
} catch (err) {
|
|
1744
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1745
|
+
process.stderr.write(`Error: ${message}
|
|
1746
|
+
`);
|
|
1747
|
+
process.exit(1);
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1750
|
+
mem.command("compact <conversation-id>").description("Create a compaction summary for a conversation").option("--json", "output raw JSON response").action(async (conversationId, opts) => {
|
|
1751
|
+
const client = getIMClient2();
|
|
1752
|
+
try {
|
|
1753
|
+
const res = await client.im.memory.compact({ conversationId });
|
|
1754
|
+
if (opts.json) {
|
|
1755
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
if (!res.ok) {
|
|
1759
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1760
|
+
`);
|
|
1761
|
+
process.exit(1);
|
|
1762
|
+
}
|
|
1763
|
+
const summary = res.data;
|
|
1764
|
+
process.stdout.write(`Compaction complete
|
|
1765
|
+
`);
|
|
1766
|
+
if (summary?.id) {
|
|
1767
|
+
process.stdout.write(` Summary ID: ${summary.id}
|
|
1768
|
+
`);
|
|
1769
|
+
}
|
|
1770
|
+
if (summary?.conversationId) {
|
|
1771
|
+
process.stdout.write(` Conversation ID: ${summary.conversationId}
|
|
1772
|
+
`);
|
|
1773
|
+
}
|
|
1774
|
+
} catch (err) {
|
|
1775
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1776
|
+
process.stderr.write(`Error: ${message}
|
|
1777
|
+
`);
|
|
1778
|
+
process.exit(1);
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
mem.command("load").description("Load session memory context").option("-s, --scope <scope>", "scope to load").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1782
|
+
const client = getIMClient2();
|
|
1783
|
+
try {
|
|
1784
|
+
const res = await client.im.memory.load(opts.scope);
|
|
1785
|
+
if (opts.json) {
|
|
1786
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
if (!res.ok) {
|
|
1790
|
+
process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
|
|
1791
|
+
`);
|
|
1792
|
+
process.exit(1);
|
|
1793
|
+
}
|
|
1794
|
+
const context = res.data;
|
|
1795
|
+
if (!context || typeof context === "object" && Object.keys(context).length === 0) {
|
|
1796
|
+
process.stdout.write("No memory context available.\n");
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
process.stdout.write("Memory context loaded:\n\n");
|
|
1800
|
+
if (typeof context === "string") {
|
|
1801
|
+
process.stdout.write(context + "\n");
|
|
1802
|
+
} else {
|
|
1803
|
+
process.stdout.write(JSON.stringify(context, null, 2) + "\n");
|
|
1804
|
+
}
|
|
1805
|
+
} catch (err) {
|
|
1806
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1807
|
+
process.stderr.write(`Error: ${message}
|
|
1808
|
+
`);
|
|
1809
|
+
process.exit(1);
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
function printFileTable(files) {
|
|
1814
|
+
const idLen = Math.max(2, ...files.map((f) => f.id.length));
|
|
1815
|
+
const scopeLen = Math.max(5, ...files.map((f) => f.scope.length));
|
|
1816
|
+
const pathLen = Math.max(4, ...files.map((f) => f.path.length));
|
|
1817
|
+
const row = (id, scope, path4) => `${id.padEnd(idLen)} ${scope.padEnd(scopeLen)} ${path4.padEnd(pathLen)}`;
|
|
1818
|
+
process.stdout.write(row("ID", "SCOPE", "PATH") + "\n");
|
|
1819
|
+
process.stdout.write(`${"-".repeat(idLen)} ${"-".repeat(scopeLen)} ${"-".repeat(pathLen)}
|
|
1820
|
+
`);
|
|
1821
|
+
for (const f of files) {
|
|
1822
|
+
process.stdout.write(row(f.id, f.scope, f.path) + "\n");
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// src/commands/skill.ts
|
|
1827
|
+
function padEnd(str, len) {
|
|
1828
|
+
if (str.length >= len) return str.slice(0, len);
|
|
1829
|
+
return str + " ".repeat(len - str.length);
|
|
1830
|
+
}
|
|
1831
|
+
function formatTable(rows) {
|
|
1832
|
+
if (rows.length === 0) return "";
|
|
1833
|
+
const cols = rows[0].length;
|
|
1834
|
+
const widths = Array(cols).fill(0);
|
|
1835
|
+
for (const row of rows) {
|
|
1836
|
+
for (let i = 0; i < cols; i++) {
|
|
1837
|
+
widths[i] = Math.max(widths[i], (row[i] ?? "").length);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
return rows.map((row) => row.map((cell, i) => padEnd(cell ?? "", widths[i])).join(" ")).join("\n");
|
|
1841
|
+
}
|
|
1842
|
+
function register6(parent, getIMClient2, _getAPIClient) {
|
|
1843
|
+
const skill = parent.command("skill").description("Browse, install, and manage skills");
|
|
1844
|
+
skill.command("find [query]").description("Search the skill marketplace").option("-c, --category <category>", "filter by category").option("-n, --limit <n>", "max results to return", "20").option("--json", "output raw JSON response").action(async (query, opts) => {
|
|
1845
|
+
const client = getIMClient2();
|
|
1846
|
+
try {
|
|
1847
|
+
const limit = parseInt(opts.limit, 10);
|
|
1848
|
+
const res = await client.im.evolution.searchSkills({
|
|
1849
|
+
query,
|
|
1850
|
+
category: opts.category,
|
|
1851
|
+
limit
|
|
1852
|
+
});
|
|
1853
|
+
if (opts.json) {
|
|
1854
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
const skills = Array.isArray(res) ? res : res?.skills ?? [];
|
|
1858
|
+
if (skills.length === 0) {
|
|
1859
|
+
process.stdout.write("No skills found.\n");
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
const header = ["Slug", "Name", "Installs", "Category"];
|
|
1863
|
+
const rows = skills.map((s) => {
|
|
1864
|
+
const sk = s;
|
|
1865
|
+
return [
|
|
1866
|
+
String(sk.slug ?? sk.id ?? ""),
|
|
1867
|
+
String(sk.name ?? ""),
|
|
1868
|
+
String(sk.installCount ?? sk.installs ?? "0"),
|
|
1869
|
+
String(sk.category ?? "")
|
|
1870
|
+
];
|
|
1871
|
+
});
|
|
1872
|
+
process.stdout.write(formatTable([header, ...rows]) + "\n");
|
|
1873
|
+
} catch (err) {
|
|
1874
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1875
|
+
process.stderr.write(`Error: ${message}
|
|
1876
|
+
`);
|
|
1877
|
+
process.exit(1);
|
|
1878
|
+
}
|
|
1879
|
+
});
|
|
1880
|
+
skill.command("install <slug>").description("Install a skill").option("--platform <platform>", "target platform: claude-code, openclaw, opencode, or all", "all").option("--project <path>", "project directory for local file writes").option("--no-local", "cloud-only install, do not write local files").option("--json", "output raw JSON response").action(async (slug, opts) => {
|
|
1881
|
+
const client = getIMClient2();
|
|
1882
|
+
try {
|
|
1883
|
+
let res;
|
|
1884
|
+
if (!opts.local) {
|
|
1885
|
+
res = await client.im.evolution.installSkill(slug);
|
|
1886
|
+
} else {
|
|
1887
|
+
const platforms = opts.platform === "all" ? void 0 : [opts.platform];
|
|
1888
|
+
res = await client.im.evolution.installSkillLocal(slug, {
|
|
1889
|
+
platforms,
|
|
1890
|
+
project: opts.project
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
if (opts.json) {
|
|
1894
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
const result = res;
|
|
1898
|
+
if (result?.ok === false) {
|
|
1899
|
+
process.stderr.write(`Install failed.
|
|
1900
|
+
`);
|
|
1901
|
+
process.exit(1);
|
|
1902
|
+
}
|
|
1903
|
+
const skillData = result?.data?.skill ?? {};
|
|
1904
|
+
const name = String(skillData.name ?? slug);
|
|
1905
|
+
process.stdout.write(`Installed: ${name}
|
|
1906
|
+
`);
|
|
1907
|
+
const localPaths = result?.data?.localPaths ?? [];
|
|
1908
|
+
if (localPaths.length > 0) {
|
|
1909
|
+
process.stdout.write("Local files written:\n");
|
|
1910
|
+
for (const p of localPaths) {
|
|
1911
|
+
process.stdout.write(` ${p}
|
|
1912
|
+
`);
|
|
1913
|
+
}
|
|
1914
|
+
} else if (!opts.local) {
|
|
1915
|
+
process.stdout.write("Cloud-only install complete (no local files written).\n");
|
|
1916
|
+
}
|
|
1917
|
+
} catch (err) {
|
|
1918
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1919
|
+
process.stderr.write(`Error: ${message}
|
|
1920
|
+
`);
|
|
1921
|
+
process.exit(1);
|
|
1922
|
+
}
|
|
1923
|
+
});
|
|
1924
|
+
skill.command("list").description("List installed skills").option("--json", "output raw JSON response").action(async (opts) => {
|
|
1925
|
+
const client = getIMClient2();
|
|
1926
|
+
try {
|
|
1927
|
+
const res = await client.im.evolution.installedSkills();
|
|
1928
|
+
if (opts.json) {
|
|
1929
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
const records = Array.isArray(res) ? res : res?.skills ?? [];
|
|
1933
|
+
if (records.length === 0) {
|
|
1934
|
+
process.stdout.write("No skills installed.\n");
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
const header = ["Slug", "Name", "Installs", "Category"];
|
|
1938
|
+
const rows = records.map((r) => {
|
|
1939
|
+
const rec = r;
|
|
1940
|
+
const sk = rec.skill ?? rec;
|
|
1941
|
+
return [
|
|
1942
|
+
String(sk.slug ?? sk.id ?? ""),
|
|
1943
|
+
String(sk.name ?? ""),
|
|
1944
|
+
String(sk.installCount ?? sk.installs ?? "0"),
|
|
1945
|
+
String(sk.category ?? "")
|
|
1946
|
+
];
|
|
1947
|
+
});
|
|
1948
|
+
process.stdout.write(formatTable([header, ...rows]) + "\n");
|
|
1949
|
+
} catch (err) {
|
|
1950
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1951
|
+
process.stderr.write(`Error: ${message}
|
|
1952
|
+
`);
|
|
1953
|
+
process.exit(1);
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
skill.command("show <slug>").description("Show skill content and details").option("--json", "output raw JSON response").action(async (slug, opts) => {
|
|
1957
|
+
const client = getIMClient2();
|
|
1958
|
+
try {
|
|
1959
|
+
const res = await client.im.evolution.getSkillContent(slug);
|
|
1960
|
+
if (opts.json) {
|
|
1961
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
const result = res;
|
|
1965
|
+
if (result?.packageUrl) {
|
|
1966
|
+
process.stdout.write(`Package URL: ${result.packageUrl}
|
|
1967
|
+
`);
|
|
1968
|
+
}
|
|
1969
|
+
if (result?.checksum) {
|
|
1970
|
+
process.stdout.write(`Checksum: ${result.checksum}
|
|
1971
|
+
`);
|
|
1972
|
+
}
|
|
1973
|
+
if (result?.files && result.files.length > 0) {
|
|
1974
|
+
process.stdout.write(`Files:
|
|
1975
|
+
`);
|
|
1976
|
+
for (const f of result.files) {
|
|
1977
|
+
process.stdout.write(` ${f}
|
|
1978
|
+
`);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
if (result?.content) {
|
|
1982
|
+
process.stdout.write(`
|
|
1983
|
+
${result.content}
|
|
1984
|
+
`);
|
|
1985
|
+
}
|
|
1986
|
+
} catch (err) {
|
|
1987
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1988
|
+
process.stderr.write(`Error: ${message}
|
|
1989
|
+
`);
|
|
1990
|
+
process.exit(1);
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
skill.command("uninstall <slug>").description("Uninstall a skill").option("--no-local", "cloud-only uninstall, do not remove local files").option("--json", "output raw JSON response").action(async (slug, opts) => {
|
|
1994
|
+
const client = getIMClient2();
|
|
1995
|
+
try {
|
|
1996
|
+
let res;
|
|
1997
|
+
if (!opts.local) {
|
|
1998
|
+
res = await client.im.evolution.uninstallSkill(slug);
|
|
1999
|
+
} else {
|
|
2000
|
+
res = await client.im.evolution.uninstallSkillLocal(slug);
|
|
2001
|
+
}
|
|
2002
|
+
if (opts.json) {
|
|
2003
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
const result = res;
|
|
2007
|
+
if (result?.ok === false) {
|
|
2008
|
+
process.stderr.write(`Uninstall failed.
|
|
2009
|
+
`);
|
|
2010
|
+
process.exit(1);
|
|
2011
|
+
}
|
|
2012
|
+
process.stdout.write(`Uninstalled: ${slug}
|
|
2013
|
+
`);
|
|
2014
|
+
const removedPaths = result?.data?.removedPaths ?? [];
|
|
2015
|
+
if (removedPaths.length > 0) {
|
|
2016
|
+
process.stdout.write("Local files removed:\n");
|
|
2017
|
+
for (const p of removedPaths) {
|
|
2018
|
+
process.stdout.write(` ${p}
|
|
2019
|
+
`);
|
|
2020
|
+
}
|
|
2021
|
+
} else if (!opts.local) {
|
|
2022
|
+
process.stdout.write("Cloud-only uninstall complete (no local files removed).\n");
|
|
2023
|
+
}
|
|
2024
|
+
} catch (err) {
|
|
2025
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2026
|
+
process.stderr.write(`Error: ${message}
|
|
2027
|
+
`);
|
|
2028
|
+
process.exit(1);
|
|
2029
|
+
}
|
|
2030
|
+
});
|
|
2031
|
+
skill.command("sync").description("Re-sync all installed skills to local filesystem").option("--platform <platform>", "target platform: claude-code, openclaw, opencode, or all", "all").option("--json", "output raw JSON response").action(async (opts) => {
|
|
2032
|
+
const client = getIMClient2();
|
|
2033
|
+
try {
|
|
2034
|
+
const platforms = opts.platform === "all" ? void 0 : [opts.platform];
|
|
2035
|
+
const res = await client.im.evolution.syncSkillsLocal({ platforms });
|
|
2036
|
+
if (opts.json) {
|
|
2037
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
const result = res;
|
|
2041
|
+
const synced = result?.synced ?? 0;
|
|
2042
|
+
const failed = result?.failed ?? 0;
|
|
2043
|
+
process.stdout.write(`Synced: ${synced} skill(s)`);
|
|
2044
|
+
if (failed > 0) {
|
|
2045
|
+
process.stdout.write(`, failed: ${failed}`);
|
|
2046
|
+
}
|
|
2047
|
+
process.stdout.write("\n");
|
|
2048
|
+
const paths = result?.paths ?? [];
|
|
2049
|
+
if (paths.length > 0) {
|
|
2050
|
+
process.stdout.write("Files written:\n");
|
|
2051
|
+
for (const p of paths) {
|
|
2052
|
+
process.stdout.write(` ${p}
|
|
2053
|
+
`);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
} catch (err) {
|
|
2057
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2058
|
+
process.stderr.write(`Error: ${message}
|
|
2059
|
+
`);
|
|
2060
|
+
process.exit(1);
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
// src/commands/files.ts
|
|
2066
|
+
function register7(parent, getIMClient2, _getAPIClient) {
|
|
2067
|
+
const file = parent.command("file").description("File upload, transfer, quota, and type management");
|
|
2068
|
+
file.command("upload <path>").description("Upload a file and get its upload ID and CDN URL").option("--mime <type>", "Override MIME type (e.g. image/png)").option("--json", "Output raw JSON response").action(async (filePath, opts) => {
|
|
2069
|
+
const client = getIMClient2();
|
|
2070
|
+
try {
|
|
2071
|
+
const uploadOpts = {};
|
|
2072
|
+
if (opts.mime) uploadOpts.mimeType = opts.mime;
|
|
2073
|
+
const res = await client.im.files.upload(filePath, Object.keys(uploadOpts).length ? uploadOpts : void 0);
|
|
2074
|
+
if (opts.json) {
|
|
2075
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
process.stdout.write(`Uploaded: ${res.fileName}
|
|
2079
|
+
`);
|
|
2080
|
+
process.stdout.write(`Upload ID: ${res.uploadId}
|
|
2081
|
+
`);
|
|
2082
|
+
process.stdout.write(`CDN URL: ${res.cdnUrl}
|
|
2083
|
+
`);
|
|
2084
|
+
process.stdout.write(`Size: ${res.fileSize} bytes
|
|
2085
|
+
`);
|
|
2086
|
+
process.stdout.write(`MIME: ${res.mimeType}
|
|
2087
|
+
`);
|
|
2088
|
+
} catch (err) {
|
|
2089
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2090
|
+
`);
|
|
2091
|
+
process.exit(1);
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
file.command("send <conversation-id> <path>").description("Upload a file and send it as a message in a conversation").option("-c, --content <text>", "Optional text caption to accompany the file").option("--mime <type>", "Override MIME type").option("--json", "Output raw JSON response").action(async (conversationId, filePath, opts) => {
|
|
2095
|
+
const client = getIMClient2();
|
|
2096
|
+
try {
|
|
2097
|
+
const sendOpts = {};
|
|
2098
|
+
if (opts.content) sendOpts.content = opts.content;
|
|
2099
|
+
if (opts.mime) sendOpts.mimeType = opts.mime;
|
|
2100
|
+
const res = await client.im.files.sendFile(
|
|
2101
|
+
conversationId,
|
|
2102
|
+
filePath,
|
|
2103
|
+
Object.keys(sendOpts).length ? sendOpts : void 0
|
|
2104
|
+
);
|
|
2105
|
+
if (opts.json) {
|
|
2106
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
process.stdout.write(`File sent (messageId: ${res.message?.id || res.message?.messageId || "-"})
|
|
2110
|
+
`);
|
|
2111
|
+
process.stdout.write(`Upload ID: ${res.upload?.uploadId || "-"}
|
|
2112
|
+
`);
|
|
2113
|
+
process.stdout.write(`CDN URL: ${res.upload?.cdnUrl || "-"}
|
|
2114
|
+
`);
|
|
2115
|
+
} catch (err) {
|
|
2116
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2117
|
+
`);
|
|
2118
|
+
process.exit(1);
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
file.command("quota").description("Show file storage quota and usage").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
2122
|
+
const client = getIMClient2();
|
|
2123
|
+
const res = await client.im.files.quota();
|
|
2124
|
+
if (!res.ok) {
|
|
2125
|
+
process.stderr.write(`Error: ${JSON.stringify(res)}
|
|
2126
|
+
`);
|
|
2127
|
+
process.exit(1);
|
|
2128
|
+
}
|
|
2129
|
+
if (opts.json) {
|
|
2130
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
const d = res.data;
|
|
2134
|
+
process.stdout.write(`Tier: ${d?.tier || "-"}
|
|
2135
|
+
`);
|
|
2136
|
+
process.stdout.write(`Used: ${d?.used ?? "-"} bytes
|
|
2137
|
+
`);
|
|
2138
|
+
process.stdout.write(`Limit: ${d?.limit ?? "-"} bytes
|
|
2139
|
+
`);
|
|
2140
|
+
process.stdout.write(`File Count: ${d?.fileCount ?? "-"}
|
|
2141
|
+
`);
|
|
2142
|
+
});
|
|
2143
|
+
file.command("delete <upload-id>").description("Delete an uploaded file by its upload ID").action(async (uploadId) => {
|
|
2144
|
+
const client = getIMClient2();
|
|
2145
|
+
const res = await client.im.files.delete(uploadId);
|
|
2146
|
+
if (!res.ok) {
|
|
2147
|
+
process.stderr.write(`Error: ${JSON.stringify(res)}
|
|
2148
|
+
`);
|
|
2149
|
+
process.exit(1);
|
|
2150
|
+
}
|
|
2151
|
+
process.stdout.write(`File ${uploadId} deleted.
|
|
2152
|
+
`);
|
|
2153
|
+
});
|
|
2154
|
+
file.command("types").description("List allowed MIME types for file uploads").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
2155
|
+
const client = getIMClient2();
|
|
2156
|
+
const res = await client.im.files.types();
|
|
2157
|
+
if (!res.ok) {
|
|
2158
|
+
process.stderr.write(`Error: ${JSON.stringify(res)}
|
|
2159
|
+
`);
|
|
2160
|
+
process.exit(1);
|
|
2161
|
+
}
|
|
2162
|
+
if (opts.json) {
|
|
2163
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
const types = res.data?.allowedMimeTypes || [];
|
|
2167
|
+
if (types.length === 0) {
|
|
2168
|
+
process.stdout.write("No allowed MIME types returned.\n");
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
process.stdout.write("Allowed MIME types:\n");
|
|
2172
|
+
for (const t of types) {
|
|
2173
|
+
process.stdout.write(` ${t}
|
|
2174
|
+
`);
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
// src/commands/workspace.ts
|
|
2180
|
+
function register8(parent, getIMClient2, _getAPIClient) {
|
|
2181
|
+
const workspace = parent.command("workspace").description("Workspace management \u2014 init, groups, and agent assignment");
|
|
2182
|
+
workspace.command("init <name>").description("Initialize a workspace with a user and agent").requiredOption("--user-id <id>", "User ID").requiredOption("--user-name <name>", "User display name").requiredOption("--agent-id <id>", "Agent ID").requiredOption("--agent-name <name>", "Agent display name").option("--agent-type <type>", "Agent type", "assistant").option("--agent-capabilities <caps>", "Comma-separated list of agent capabilities").option("--json", "Output raw JSON response").action(async (name, opts) => {
|
|
2183
|
+
const client = getIMClient2();
|
|
2184
|
+
try {
|
|
2185
|
+
const capabilities = opts.agentCapabilities ? opts.agentCapabilities.split(",").map((s) => s.trim()) : void 0;
|
|
2186
|
+
const res = await client.im.workspace.init({
|
|
2187
|
+
workspaceId: name,
|
|
2188
|
+
userId: opts.userId,
|
|
2189
|
+
userDisplayName: opts.userName,
|
|
2190
|
+
agentName: opts.agentId,
|
|
2191
|
+
agentDisplayName: opts.agentName,
|
|
2192
|
+
agentType: opts.agentType,
|
|
2193
|
+
...capabilities !== void 0 && { agentCapabilities: capabilities }
|
|
2194
|
+
});
|
|
2195
|
+
if (!res.ok) {
|
|
2196
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2197
|
+
`);
|
|
2198
|
+
process.exit(1);
|
|
2199
|
+
}
|
|
2200
|
+
if (opts.json) {
|
|
2201
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
process.stdout.write(`Workspace initialized (workspaceId: ${res.data?.workspaceId})
|
|
2205
|
+
`);
|
|
2206
|
+
} catch (err) {
|
|
2207
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2208
|
+
`);
|
|
2209
|
+
process.exit(1);
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
workspace.command("init-group <name>").description("Initialize a group workspace with a set of members").requiredOption("--members <json>", "JSON array of member objects").option("--json", "Output raw JSON response").action(async (name, opts) => {
|
|
2213
|
+
const client = getIMClient2();
|
|
2214
|
+
try {
|
|
2215
|
+
let users;
|
|
2216
|
+
try {
|
|
2217
|
+
const parsed = JSON.parse(opts.members);
|
|
2218
|
+
if (!Array.isArray(parsed)) throw new Error("not an array");
|
|
2219
|
+
users = parsed;
|
|
2220
|
+
} catch {
|
|
2221
|
+
process.stderr.write("Error: --members must be a valid JSON array of {userId, displayName}\n");
|
|
2222
|
+
process.exit(1);
|
|
2223
|
+
}
|
|
2224
|
+
const res = await client.im.workspace.initGroup({ workspaceId: name, title: name, users });
|
|
2225
|
+
if (!res.ok) {
|
|
2226
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2227
|
+
`);
|
|
2228
|
+
process.exit(1);
|
|
2229
|
+
}
|
|
2230
|
+
if (opts.json) {
|
|
2231
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
process.stdout.write(`Group workspace initialized (workspaceId: ${res.data?.workspaceId})
|
|
2235
|
+
`);
|
|
2236
|
+
} catch (err) {
|
|
2237
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2238
|
+
`);
|
|
2239
|
+
process.exit(1);
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
workspace.command("add-agent <workspace-id> <agent-id>").description("Add an agent to a workspace").option("--json", "Output raw JSON response").action(async (workspaceId, agentId, opts) => {
|
|
2243
|
+
const client = getIMClient2();
|
|
2244
|
+
try {
|
|
2245
|
+
const res = await client.im.workspace.addAgent(workspaceId, agentId);
|
|
2246
|
+
if (!res.ok) {
|
|
2247
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2248
|
+
`);
|
|
2249
|
+
process.exit(1);
|
|
2250
|
+
}
|
|
2251
|
+
if (opts.json) {
|
|
2252
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
process.stdout.write(`Agent ${agentId} added to workspace ${workspaceId}.
|
|
2256
|
+
`);
|
|
2257
|
+
} catch (err) {
|
|
2258
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2259
|
+
`);
|
|
2260
|
+
process.exit(1);
|
|
2261
|
+
}
|
|
2262
|
+
});
|
|
2263
|
+
workspace.command("agents <workspace-id>").description("List agents in a workspace").option("--json", "Output raw JSON response").action(async (workspaceId, opts) => {
|
|
2264
|
+
const client = getIMClient2();
|
|
2265
|
+
try {
|
|
2266
|
+
const res = await client.im.workspace.listAgents(workspaceId);
|
|
2267
|
+
if (!res.ok) {
|
|
2268
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2269
|
+
`);
|
|
2270
|
+
process.exit(1);
|
|
2271
|
+
}
|
|
2272
|
+
const agents = res.data || [];
|
|
2273
|
+
if (opts.json) {
|
|
2274
|
+
process.stdout.write(JSON.stringify(agents, null, 2) + "\n");
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
if (agents.length === 0) {
|
|
2278
|
+
process.stdout.write("No agents in this workspace.\n");
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
process.stdout.write("Agent ID".padEnd(36) + "Type".padEnd(14) + "Name\n");
|
|
2282
|
+
for (const a of agents) {
|
|
2283
|
+
process.stdout.write(
|
|
2284
|
+
`${(a.agentId || a.id || "").padEnd(36)}${(a.agentType || "").padEnd(14)}${a.name || a.displayName || ""}
|
|
2285
|
+
`
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
} catch (err) {
|
|
2289
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2290
|
+
`);
|
|
2291
|
+
process.exit(1);
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
// src/commands/security.ts
|
|
2297
|
+
function register9(parent, getIMClient2, _getAPIClient) {
|
|
2298
|
+
const security = parent.command("security").description("Per-conversation encryption and key management");
|
|
2299
|
+
security.command("get <conversation-id>").description("Get security settings for a conversation").option("--json", "Output raw JSON response").action(async (convId, opts) => {
|
|
2300
|
+
const client = getIMClient2();
|
|
2301
|
+
try {
|
|
2302
|
+
const res = await client.im.security.getConversationSecurity(convId);
|
|
2303
|
+
if (!res.ok) {
|
|
2304
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2305
|
+
`);
|
|
2306
|
+
process.exit(1);
|
|
2307
|
+
}
|
|
2308
|
+
if (opts.json) {
|
|
2309
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
const d = res.data;
|
|
2313
|
+
process.stdout.write(`Encryption Mode: ${d?.encryptionMode ?? "-"}
|
|
2314
|
+
`);
|
|
2315
|
+
process.stdout.write(`Signing Policy: ${d?.signingPolicy ?? "-"}
|
|
2316
|
+
`);
|
|
2317
|
+
} catch (err) {
|
|
2318
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2319
|
+
`);
|
|
2320
|
+
process.exit(1);
|
|
2321
|
+
}
|
|
2322
|
+
});
|
|
2323
|
+
security.command("set <conversation-id>").description("Set encryption mode for a conversation").requiredOption("--mode <mode>", "Encryption mode: none, available, or required").option("--json", "Output raw JSON response").action(async (convId, opts) => {
|
|
2324
|
+
const client = getIMClient2();
|
|
2325
|
+
try {
|
|
2326
|
+
const res = await client.im.security.setConversationSecurity(convId, {
|
|
2327
|
+
encryptionMode: opts.mode
|
|
2328
|
+
});
|
|
2329
|
+
if (!res.ok) {
|
|
2330
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2331
|
+
`);
|
|
2332
|
+
process.exit(1);
|
|
2333
|
+
}
|
|
2334
|
+
if (opts.json) {
|
|
2335
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
process.stdout.write(`Encryption mode set to: ${opts.mode}
|
|
2339
|
+
`);
|
|
2340
|
+
} catch (err) {
|
|
2341
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2342
|
+
`);
|
|
2343
|
+
process.exit(1);
|
|
2344
|
+
}
|
|
2345
|
+
});
|
|
2346
|
+
security.command("upload-key <conversation-id>").description("Upload an ECDH public key for a conversation").requiredOption("--key <base64>", "Base64-encoded public key").option("--algorithm <alg>", "Key algorithm", "ecdh-p256").option("--json", "Output raw JSON response").action(async (convId, opts) => {
|
|
2347
|
+
const client = getIMClient2();
|
|
2348
|
+
try {
|
|
2349
|
+
const res = await client.im.security.uploadKey(convId, opts.key, opts.algorithm);
|
|
2350
|
+
if (!res.ok) {
|
|
2351
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2352
|
+
`);
|
|
2353
|
+
process.exit(1);
|
|
2354
|
+
}
|
|
2355
|
+
if (opts.json) {
|
|
2356
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
process.stdout.write(`Key uploaded (algorithm: ${opts.algorithm})
|
|
2360
|
+
`);
|
|
2361
|
+
} catch (err) {
|
|
2362
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2363
|
+
`);
|
|
2364
|
+
process.exit(1);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
security.command("keys <conversation-id>").description("List all member public keys for a conversation").option("--json", "Output raw JSON response").action(async (convId, opts) => {
|
|
2368
|
+
const client = getIMClient2();
|
|
2369
|
+
try {
|
|
2370
|
+
const res = await client.im.security.getKeys(convId);
|
|
2371
|
+
if (!res.ok) {
|
|
2372
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2373
|
+
`);
|
|
2374
|
+
process.exit(1);
|
|
2375
|
+
}
|
|
2376
|
+
const keys = res.data;
|
|
2377
|
+
if (opts.json) {
|
|
2378
|
+
process.stdout.write(JSON.stringify(keys, null, 2) + "\n");
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
if (!keys || Array.isArray(keys) && keys.length === 0) {
|
|
2382
|
+
process.stdout.write("No keys found.\n");
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
process.stdout.write("User ID".padEnd(36) + "Algorithm".padEnd(16) + "Public Key\n");
|
|
2386
|
+
for (const k of keys) {
|
|
2387
|
+
process.stdout.write(
|
|
2388
|
+
`${String(k.userId ?? "").padEnd(36)}${String(k.algorithm ?? "").padEnd(16)}${String(k.publicKey ?? "")}
|
|
2389
|
+
`
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
} catch (err) {
|
|
2393
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2394
|
+
`);
|
|
2395
|
+
process.exit(1);
|
|
2396
|
+
}
|
|
2397
|
+
});
|
|
2398
|
+
security.command("revoke-key <conversation-id> <user-id>").description("Revoke a member key from a conversation").option("--json", "Output raw JSON response").action(async (convId, userId, opts) => {
|
|
2399
|
+
const client = getIMClient2();
|
|
2400
|
+
try {
|
|
2401
|
+
const res = await client.im.security.revokeKey(convId, userId);
|
|
2402
|
+
if (!res.ok) {
|
|
2403
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2404
|
+
`);
|
|
2405
|
+
process.exit(1);
|
|
2406
|
+
}
|
|
2407
|
+
if (opts.json) {
|
|
2408
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
process.stdout.write(`Key revoked for user: ${userId}
|
|
2412
|
+
`);
|
|
2413
|
+
} catch (err) {
|
|
2414
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2415
|
+
`);
|
|
2416
|
+
process.exit(1);
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2419
|
+
const identity = parent.command("identity").description("Identity key management and audit log verification");
|
|
2420
|
+
identity.command("server-key").description("Get the server's identity public key").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
2421
|
+
const client = getIMClient2();
|
|
2422
|
+
try {
|
|
2423
|
+
const res = await client.im.identity.getServerKey();
|
|
2424
|
+
if (!res.ok) {
|
|
2425
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2426
|
+
`);
|
|
2427
|
+
process.exit(1);
|
|
2428
|
+
}
|
|
2429
|
+
if (opts.json) {
|
|
2430
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
const d = res.data;
|
|
2434
|
+
process.stdout.write(`Server Public Key: ${d?.publicKey ?? "-"}
|
|
2435
|
+
`);
|
|
2436
|
+
} catch (err) {
|
|
2437
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2438
|
+
`);
|
|
2439
|
+
process.exit(1);
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2442
|
+
identity.command("register-key").description("Register an identity public key").requiredOption("--algorithm <alg>", "Key algorithm (e.g. ed25519, ecdh-p256)").requiredOption("--public-key <base64>", "Base64-encoded public key").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
2443
|
+
const client = getIMClient2();
|
|
2444
|
+
try {
|
|
2445
|
+
const res = await client.im.identity.registerKey({
|
|
2446
|
+
algorithm: opts.algorithm,
|
|
2447
|
+
publicKey: opts.publicKey
|
|
2448
|
+
});
|
|
2449
|
+
if (!res.ok) {
|
|
2450
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2451
|
+
`);
|
|
2452
|
+
process.exit(1);
|
|
2453
|
+
}
|
|
2454
|
+
if (opts.json) {
|
|
2455
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
process.stdout.write(`Identity key registered (algorithm: ${opts.algorithm})
|
|
2459
|
+
`);
|
|
2460
|
+
} catch (err) {
|
|
2461
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2462
|
+
`);
|
|
2463
|
+
process.exit(1);
|
|
2464
|
+
}
|
|
2465
|
+
});
|
|
2466
|
+
identity.command("get-key <user-id>").description("Get a user's identity public key").option("--json", "Output raw JSON response").action(async (userId, opts) => {
|
|
2467
|
+
const client = getIMClient2();
|
|
2468
|
+
try {
|
|
2469
|
+
const res = await client.im.identity.getKey(userId);
|
|
2470
|
+
if (!res.ok) {
|
|
2471
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2472
|
+
`);
|
|
2473
|
+
process.exit(1);
|
|
2474
|
+
}
|
|
2475
|
+
if (opts.json) {
|
|
2476
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
2479
|
+
const d = res.data;
|
|
2480
|
+
process.stdout.write(`Algorithm: ${d?.algorithm ?? "-"}
|
|
2481
|
+
`);
|
|
2482
|
+
process.stdout.write(`Public Key: ${d?.publicKey ?? "-"}
|
|
2483
|
+
`);
|
|
2484
|
+
} catch (err) {
|
|
2485
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2486
|
+
`);
|
|
2487
|
+
process.exit(1);
|
|
2488
|
+
}
|
|
2489
|
+
});
|
|
2490
|
+
identity.command("revoke-key").description("Revoke your own identity key").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
2491
|
+
const client = getIMClient2();
|
|
2492
|
+
try {
|
|
2493
|
+
const res = await client.im.identity.revokeKey();
|
|
2494
|
+
if (!res.ok) {
|
|
2495
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2496
|
+
`);
|
|
2497
|
+
process.exit(1);
|
|
2498
|
+
}
|
|
2499
|
+
if (opts.json) {
|
|
2500
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
process.stdout.write("Identity key revoked.\n");
|
|
2504
|
+
} catch (err) {
|
|
2505
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2506
|
+
`);
|
|
2507
|
+
process.exit(1);
|
|
2508
|
+
}
|
|
2509
|
+
});
|
|
2510
|
+
identity.command("audit-log <user-id>").description("Get key audit log entries for a user").option("--json", "Output raw JSON response").action(async (userId, opts) => {
|
|
2511
|
+
const client = getIMClient2();
|
|
2512
|
+
try {
|
|
2513
|
+
const res = await client.im.identity.getAuditLog(userId);
|
|
2514
|
+
if (!res.ok) {
|
|
2515
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2516
|
+
`);
|
|
2517
|
+
process.exit(1);
|
|
2518
|
+
}
|
|
2519
|
+
const entries = res.data;
|
|
2520
|
+
if (opts.json) {
|
|
2521
|
+
process.stdout.write(JSON.stringify(entries, null, 2) + "\n");
|
|
2522
|
+
return;
|
|
2523
|
+
}
|
|
2524
|
+
if (!entries || Array.isArray(entries) && entries.length === 0) {
|
|
2525
|
+
process.stdout.write("No audit log entries.\n");
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
process.stdout.write("Date".padEnd(24) + "Action".padEnd(20) + "Details\n");
|
|
2529
|
+
for (const e of entries) {
|
|
2530
|
+
const date = e.createdAt ? new Date(String(e.createdAt)).toLocaleString() : "";
|
|
2531
|
+
process.stdout.write(
|
|
2532
|
+
`${date.padEnd(24)}${String(e.action ?? "").padEnd(20)}${e.details ? JSON.stringify(e.details) : ""}
|
|
2533
|
+
`
|
|
2534
|
+
);
|
|
2535
|
+
}
|
|
2536
|
+
} catch (err) {
|
|
2537
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2538
|
+
`);
|
|
2539
|
+
process.exit(1);
|
|
2540
|
+
}
|
|
2541
|
+
});
|
|
2542
|
+
identity.command("verify-audit <user-id>").description("Verify the integrity of the key audit log for a user").option("--json", "Output raw JSON response").action(async (userId, opts) => {
|
|
2543
|
+
const client = getIMClient2();
|
|
2544
|
+
try {
|
|
2545
|
+
const res = await client.im.identity.verifyAuditLog(userId);
|
|
2546
|
+
if (!res.ok) {
|
|
2547
|
+
process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
|
|
2548
|
+
`);
|
|
2549
|
+
process.exit(1);
|
|
2550
|
+
}
|
|
2551
|
+
if (opts.json) {
|
|
2552
|
+
process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
const d = res.data;
|
|
2556
|
+
if (d?.valid) {
|
|
2557
|
+
process.stdout.write("Audit log verified: VALID\n");
|
|
2558
|
+
} else {
|
|
2559
|
+
process.stdout.write("Audit log verified: INVALID\n");
|
|
2560
|
+
if (d?.errors && Array.isArray(d.errors) && d.errors.length > 0) {
|
|
2561
|
+
process.stdout.write("Errors:\n");
|
|
2562
|
+
for (const err of d.errors) {
|
|
2563
|
+
process.stdout.write(` - ${JSON.stringify(err)}
|
|
2564
|
+
`);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
} catch (err) {
|
|
2569
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2570
|
+
`);
|
|
2571
|
+
process.exit(1);
|
|
2572
|
+
}
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
// src/commands/community.ts
|
|
2577
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
2578
|
+
function printJson(res, opts) {
|
|
2579
|
+
if (opts.json) {
|
|
2580
|
+
console.log(JSON.stringify(res, null, 2));
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
if (!res.ok) {
|
|
2584
|
+
const errMsg = res.error && typeof res.error === "object" && "message" in res.error ? res.error.message : JSON.stringify(res.error);
|
|
2585
|
+
console.error("Error:", errMsg || "Unknown");
|
|
2586
|
+
process.exit(1);
|
|
2587
|
+
}
|
|
2588
|
+
console.log(typeof res.data === "string" ? res.data : JSON.stringify(res.data, null, 2));
|
|
2589
|
+
}
|
|
2590
|
+
function formatPostsMarkdown(data) {
|
|
2591
|
+
const d = data;
|
|
2592
|
+
const posts = d?.posts ?? [];
|
|
2593
|
+
let t = "## Feed\n\n";
|
|
2594
|
+
if (posts.length === 0) return t + "_Empty._\n";
|
|
2595
|
+
for (const p of posts) {
|
|
2596
|
+
t += `- **${String(p.title || "")}** (\`${String(p.id)}\`) \u2014 ${String(p.boardId || "")}
|
|
2597
|
+
`;
|
|
2598
|
+
}
|
|
2599
|
+
if (d?.nextCursor) t += `
|
|
2600
|
+
_Next cursor:_ \`${d.nextCursor}\`
|
|
2601
|
+
`;
|
|
2602
|
+
return t;
|
|
2603
|
+
}
|
|
2604
|
+
function register10(parent, getIMClient2, _getAPIClient) {
|
|
2605
|
+
const comm = parent.command("community").description("Evolution community forum \u2014 feed, ask, search, notify");
|
|
2606
|
+
comm.command("feed").description("Browse posts (uses hub cache when fresh)").option("-b, --board <id>", "Board: showcase, genelab, helpdesk, ideas, changelog").option("-n, --limit <n>", "Max posts", "15").option("--json", "JSON output").action(async (opts) => {
|
|
2607
|
+
const c = getIMClient2();
|
|
2608
|
+
const res = await c.im.community.feed({
|
|
2609
|
+
boardId: opts.board,
|
|
2610
|
+
limit: parseInt(opts.limit || "15", 10)
|
|
2611
|
+
});
|
|
2612
|
+
if (opts.json) {
|
|
2613
|
+
printJson(res, opts);
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
if (!res.ok) {
|
|
2617
|
+
printJson(res, { json: false });
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
process.stdout.write(formatPostsMarkdown(res.data));
|
|
2621
|
+
});
|
|
2622
|
+
comm.command("ask").description("Post a helpdesk question").argument("<title>", "Title").argument("[body]", "Body (Markdown); omit if using --file").option("-f, --file <path>", "Read body from file").option("--tags <csv>", "Comma-separated tags").option("--json", "JSON output").action(async (title, body, opts) => {
|
|
2623
|
+
const content = opts.file ? readFileSync2(opts.file, "utf8") : body || "(no body)";
|
|
2624
|
+
const tags = opts.tags?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2625
|
+
const c = getIMClient2();
|
|
2626
|
+
const res = await c.im.community.ask(title, content, tags);
|
|
2627
|
+
printJson(res, opts);
|
|
2628
|
+
});
|
|
2629
|
+
comm.command("search").description("Full-text community search").argument("<query>", "Search query").option("-b, --board <id>", "Limit to board").option("-n, --limit <n>", "Max hits", "8").option("--json", "JSON output").action(async (query, opts) => {
|
|
2630
|
+
const c = getIMClient2();
|
|
2631
|
+
const res = await c.im.community.search(query, {
|
|
2632
|
+
boardId: opts.board,
|
|
2633
|
+
limit: parseInt(opts.limit || "8", 10)
|
|
2634
|
+
});
|
|
2635
|
+
printJson(res, opts);
|
|
2636
|
+
});
|
|
2637
|
+
comm.command("check").description("List notifications; optionally mark all read").option("--unread-only", "Unread only").option("--mark-read", "Mark all read after listing").option("--json", "JSON output").action(async (opts) => {
|
|
2638
|
+
const c = getIMClient2();
|
|
2639
|
+
const list = await c.im.community.getNotifications({
|
|
2640
|
+
unread: opts.unreadOnly,
|
|
2641
|
+
limit: 50
|
|
2642
|
+
});
|
|
2643
|
+
if (opts.json) {
|
|
2644
|
+
console.log(JSON.stringify(list, null, 2));
|
|
2645
|
+
} else if (list.ok && list.data) {
|
|
2646
|
+
const payload = list.data;
|
|
2647
|
+
console.log(`## Notifications (${payload.items?.length ?? 0})
|
|
2648
|
+
`);
|
|
2649
|
+
console.log(JSON.stringify(list.data, null, 2));
|
|
2650
|
+
} else {
|
|
2651
|
+
printJson(list, { json: false });
|
|
2652
|
+
}
|
|
2653
|
+
if (opts.markRead) {
|
|
2654
|
+
const mr = await c.im.community.markNotificationsRead();
|
|
2655
|
+
if (opts.json) console.log(JSON.stringify(mr, null, 2));
|
|
2656
|
+
else console.log("\nMarked read:", mr.ok ? "ok" : mr.error);
|
|
2657
|
+
}
|
|
2658
|
+
});
|
|
2659
|
+
comm.command("report").description("Publish a showcase battle-report style post").requiredOption("-t, --title <t>", "Title").option("-c, --content <md>", "Body markdown").option("--genes <csv>", "Linked gene IDs").option("--agent <id>", "linkedAgentId").option("--json", "JSON output").action(async (opts) => {
|
|
2660
|
+
const c = getIMClient2();
|
|
2661
|
+
const geneIds = opts.genes?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2662
|
+
const res = await c.im.community.reportBattle({
|
|
2663
|
+
title: opts.title,
|
|
2664
|
+
content: opts.content || "_Battle report_",
|
|
2665
|
+
linkedGeneIds: geneIds,
|
|
2666
|
+
linkedAgentId: opts.agent
|
|
2667
|
+
});
|
|
2668
|
+
printJson(res, opts);
|
|
2669
|
+
});
|
|
2670
|
+
comm.command("post").description("Create a post on any board").argument("<board>", "Board id").argument("<title>", "Title").option("-c, --content <md>", "Body", "").option("--tags <csv>", "Tags").option("--json", "JSON output").action(async (board, title, opts) => {
|
|
2671
|
+
const tags = opts.tags?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2672
|
+
const c = getIMClient2();
|
|
2673
|
+
const res = await c.im.community.createPost({
|
|
2674
|
+
boardId: board,
|
|
2675
|
+
title,
|
|
2676
|
+
content: opts.content || "",
|
|
2677
|
+
tags
|
|
2678
|
+
});
|
|
2679
|
+
if (res.ok) c.im.community.invalidateCache(board);
|
|
2680
|
+
printJson(res, opts);
|
|
2681
|
+
});
|
|
2682
|
+
comm.command("reply").description("Comment on a post").argument("<postId>", "Post ID").argument("<content>", "Comment (markdown)").option("--json", "JSON output").action(async (postId, content, opts) => {
|
|
2683
|
+
const c = getIMClient2();
|
|
2684
|
+
const res = await c.im.community.createComment(postId, { content });
|
|
2685
|
+
printJson(res, opts);
|
|
2686
|
+
});
|
|
2687
|
+
comm.command("vote").description("Vote on post or comment").argument("<type>", "post | comment").argument("<id>", "Target id").argument("<value>", "up | down | cancel").option("--json", "JSON output").action(async (type, id, value, opts) => {
|
|
2688
|
+
const tt = type === "comment" ? "comment" : "post";
|
|
2689
|
+
let v = 0;
|
|
2690
|
+
if (value === "up") v = 1;
|
|
2691
|
+
else if (value === "down") v = -1;
|
|
2692
|
+
const c = getIMClient2();
|
|
2693
|
+
const res = await c.im.community.vote(tt, id, v);
|
|
2694
|
+
printJson(res, opts);
|
|
2695
|
+
});
|
|
2696
|
+
const my = comm.command("my").description("Your bookmarks (auth)");
|
|
2697
|
+
my.command("bookmarks").description("List bookmarked posts").option("--json", "JSON output").action(async (opts) => {
|
|
2698
|
+
const c = getIMClient2();
|
|
2699
|
+
const res = await c.im.community.listBookmarks({ limit: 30 });
|
|
2700
|
+
printJson(res, opts);
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
// src/daemon.ts
|
|
2705
|
+
import * as fs2 from "fs";
|
|
2706
|
+
import * as path2 from "path";
|
|
2707
|
+
import { join as join2, dirname } from "path";
|
|
2708
|
+
import * as os from "os";
|
|
2709
|
+
import { homedir as homedir2 } from "os";
|
|
2710
|
+
import * as http from "http";
|
|
2711
|
+
import { createServer } from "http";
|
|
2712
|
+
import { execSync } from "child_process";
|
|
2713
|
+
import * as TOML from "@iarna/toml";
|
|
2714
|
+
var CONFIG_DIR = path2.join(os.homedir(), ".prismer");
|
|
2715
|
+
var CONFIG_PATH = path2.join(CONFIG_DIR, "config.toml");
|
|
2716
|
+
var PID_PATH = path2.join(CONFIG_DIR, "daemon.pid");
|
|
2717
|
+
var PORT_PATH = path2.join(CONFIG_DIR, "daemon.port");
|
|
2718
|
+
var CACHE_DIR = path2.join(CONFIG_DIR, "cache");
|
|
2719
|
+
var EVOLUTION_CACHE_PATH = path2.join(CACHE_DIR, "evolution.json");
|
|
2720
|
+
var OUTBOX_PATH = path2.join(CACHE_DIR, "outbox.json");
|
|
2721
|
+
var SYNC_INTERVAL_MS = 6e4;
|
|
2722
|
+
var FLUSH_INTERVAL_MS = 3e4;
|
|
2723
|
+
var API_TIMEOUT_MS = 1e4;
|
|
2724
|
+
var EVENTS_FILE = join2(CACHE_DIR, "events.json");
|
|
2725
|
+
var MAX_EVENTS = 1e3;
|
|
2726
|
+
function loadConfig() {
|
|
2727
|
+
if (!fs2.existsSync(CONFIG_PATH)) return null;
|
|
2728
|
+
try {
|
|
2729
|
+
const raw = fs2.readFileSync(CONFIG_PATH, "utf-8");
|
|
2730
|
+
const parsed = TOML.parse(raw);
|
|
2731
|
+
const apiKey = parsed?.default?.api_key || "";
|
|
2732
|
+
const baseUrl = parsed?.default?.base_url || "https://prismer.cloud";
|
|
2733
|
+
if (!apiKey) return null;
|
|
2734
|
+
return { apiKey, baseUrl };
|
|
2735
|
+
} catch {
|
|
2736
|
+
return null;
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
function ensureCacheDir() {
|
|
2740
|
+
if (!fs2.existsSync(CACHE_DIR)) {
|
|
2741
|
+
fs2.mkdirSync(CACHE_DIR, { recursive: true });
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
function loadEvents() {
|
|
2745
|
+
try {
|
|
2746
|
+
return JSON.parse(fs2.readFileSync(EVENTS_FILE, "utf-8"));
|
|
2747
|
+
} catch {
|
|
2748
|
+
return [];
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
function appendEvent(event) {
|
|
2752
|
+
const events = loadEvents();
|
|
2753
|
+
events.push(event);
|
|
2754
|
+
if (events.length > MAX_EVENTS) events.splice(0, events.length - MAX_EVENTS);
|
|
2755
|
+
fs2.writeFileSync(EVENTS_FILE, JSON.stringify(events), { encoding: "utf-8", mode: 384 });
|
|
2756
|
+
}
|
|
2757
|
+
function emitSyncEvent(genesCount) {
|
|
2758
|
+
if (genesCount > 0) {
|
|
2759
|
+
appendEvent({
|
|
2760
|
+
type: "evolution.sync",
|
|
2761
|
+
source: "evolution",
|
|
2762
|
+
priority: "low",
|
|
2763
|
+
title: "Evolution sync complete",
|
|
2764
|
+
body: `${genesCount} genes updated`,
|
|
2765
|
+
timestamp: Date.now()
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
function readPid() {
|
|
2770
|
+
if (!fs2.existsSync(PID_PATH)) return null;
|
|
2771
|
+
try {
|
|
2772
|
+
const raw = fs2.readFileSync(PID_PATH, "utf-8").trim();
|
|
2773
|
+
const pid = parseInt(raw, 10);
|
|
2774
|
+
return isNaN(pid) ? null : pid;
|
|
2775
|
+
} catch {
|
|
2776
|
+
return null;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
function readPort() {
|
|
2780
|
+
if (!fs2.existsSync(PORT_PATH)) return null;
|
|
2781
|
+
try {
|
|
2782
|
+
const raw = fs2.readFileSync(PORT_PATH, "utf-8").trim();
|
|
2783
|
+
const port = parseInt(raw, 10);
|
|
2784
|
+
return isNaN(port) ? null : port;
|
|
2785
|
+
} catch {
|
|
2786
|
+
return null;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
function isProcessRunning(pid) {
|
|
2790
|
+
try {
|
|
2791
|
+
process.kill(pid, 0);
|
|
2792
|
+
return true;
|
|
2793
|
+
} catch {
|
|
2794
|
+
return false;
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
function writePid(pid) {
|
|
2798
|
+
ensureCacheDir();
|
|
2799
|
+
if (!fs2.existsSync(CONFIG_DIR)) {
|
|
2800
|
+
fs2.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
2801
|
+
}
|
|
2802
|
+
fs2.writeFileSync(PID_PATH, String(pid), { encoding: "utf-8", mode: 384 });
|
|
2803
|
+
}
|
|
2804
|
+
function writePort(port) {
|
|
2805
|
+
if (!fs2.existsSync(CONFIG_DIR)) {
|
|
2806
|
+
fs2.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
2807
|
+
}
|
|
2808
|
+
fs2.writeFileSync(PORT_PATH, String(port), { encoding: "utf-8", mode: 384 });
|
|
2809
|
+
}
|
|
2810
|
+
function cleanupPidFiles() {
|
|
2811
|
+
try {
|
|
2812
|
+
if (fs2.existsSync(PID_PATH)) fs2.unlinkSync(PID_PATH);
|
|
2813
|
+
} catch {
|
|
2814
|
+
}
|
|
2815
|
+
try {
|
|
2816
|
+
if (fs2.existsSync(PORT_PATH)) fs2.unlinkSync(PORT_PATH);
|
|
2817
|
+
} catch {
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
async function fetchWithTimeout(url, options, timeoutMs = API_TIMEOUT_MS) {
|
|
2821
|
+
const controller = new AbortController();
|
|
2822
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2823
|
+
try {
|
|
2824
|
+
return await fetch(url, { ...options, signal: controller.signal });
|
|
2825
|
+
} finally {
|
|
2826
|
+
clearTimeout(timer);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
async function runDaemonProcess() {
|
|
2830
|
+
const cfg = loadConfig();
|
|
2831
|
+
if (!cfg) {
|
|
2832
|
+
process.stderr.write('[prismer-daemon] No config found. Run "prismer setup" first.\n');
|
|
2833
|
+
process.exit(1);
|
|
2834
|
+
}
|
|
2835
|
+
ensureCacheDir();
|
|
2836
|
+
let lastSync = 0;
|
|
2837
|
+
let syncCount = 0;
|
|
2838
|
+
let evolutionCursor = 0;
|
|
2839
|
+
if (fs2.existsSync(EVOLUTION_CACHE_PATH)) {
|
|
2840
|
+
try {
|
|
2841
|
+
const cached = JSON.parse(fs2.readFileSync(EVOLUTION_CACHE_PATH, "utf-8"));
|
|
2842
|
+
if (typeof cached?.cursor === "number") evolutionCursor = cached.cursor;
|
|
2843
|
+
} catch {
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
const server = createServer((req, res) => {
|
|
2847
|
+
if (req.method === "GET" && req.url === "/health") {
|
|
2848
|
+
let outboxSize = 0;
|
|
2849
|
+
if (fs2.existsSync(OUTBOX_PATH)) {
|
|
2850
|
+
try {
|
|
2851
|
+
const entries = JSON.parse(fs2.readFileSync(OUTBOX_PATH, "utf-8"));
|
|
2852
|
+
if (Array.isArray(entries)) outboxSize = entries.length;
|
|
2853
|
+
} catch {
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
const body = JSON.stringify({
|
|
2857
|
+
pid: process.pid,
|
|
2858
|
+
uptime: Math.floor(process.uptime()),
|
|
2859
|
+
lastSync,
|
|
2860
|
+
syncCount,
|
|
2861
|
+
outboxSize
|
|
2862
|
+
});
|
|
2863
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2864
|
+
res.end(body);
|
|
2865
|
+
} else if (req.method === "GET" && req.url === "/events") {
|
|
2866
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2867
|
+
const events = loadEvents();
|
|
2868
|
+
res.end(JSON.stringify(events.slice(-50)));
|
|
2869
|
+
} else {
|
|
2870
|
+
res.writeHead(404);
|
|
2871
|
+
res.end("Not found");
|
|
2872
|
+
}
|
|
2873
|
+
});
|
|
2874
|
+
server.listen(0, "127.0.0.1", () => {
|
|
2875
|
+
const addr = server.address();
|
|
2876
|
+
const port = addr.port;
|
|
2877
|
+
writePid(process.pid);
|
|
2878
|
+
writePort(port);
|
|
2879
|
+
process.stdout.write(`[prismer-daemon] Started. PID=${process.pid} port=${port}
|
|
2880
|
+
`);
|
|
2881
|
+
});
|
|
2882
|
+
const shutdown = () => {
|
|
2883
|
+
process.stdout.write("[prismer-daemon] Shutting down.\n");
|
|
2884
|
+
cleanupPidFiles();
|
|
2885
|
+
server.close();
|
|
2886
|
+
process.exit(0);
|
|
2887
|
+
};
|
|
2888
|
+
process.on("SIGINT", shutdown);
|
|
2889
|
+
process.on("SIGTERM", shutdown);
|
|
2890
|
+
const doEvolutionSync = async () => {
|
|
2891
|
+
try {
|
|
2892
|
+
const res = await fetchWithTimeout(
|
|
2893
|
+
`${cfg.baseUrl}/api/im/evolution/sync`,
|
|
2894
|
+
{
|
|
2895
|
+
method: "POST",
|
|
2896
|
+
headers: {
|
|
2897
|
+
"Content-Type": "application/json",
|
|
2898
|
+
Authorization: `Bearer ${cfg.apiKey}`
|
|
2899
|
+
},
|
|
2900
|
+
body: JSON.stringify({ pull: { since: evolutionCursor, scope: "global" } })
|
|
2901
|
+
}
|
|
2902
|
+
);
|
|
2903
|
+
if (res.ok) {
|
|
2904
|
+
const data = await res.json();
|
|
2905
|
+
lastSync = Date.now();
|
|
2906
|
+
syncCount++;
|
|
2907
|
+
if (typeof data?.data?.cursor === "number") {
|
|
2908
|
+
evolutionCursor = data.data.cursor;
|
|
2909
|
+
} else if (typeof data?.cursor === "number") {
|
|
2910
|
+
evolutionCursor = data.cursor;
|
|
2911
|
+
}
|
|
2912
|
+
ensureCacheDir();
|
|
2913
|
+
const pulled = data?.data || data;
|
|
2914
|
+
fs2.writeFileSync(
|
|
2915
|
+
EVOLUTION_CACHE_PATH,
|
|
2916
|
+
JSON.stringify({ cursor: evolutionCursor, lastSync, data: pulled }, null, 2),
|
|
2917
|
+
{ encoding: "utf-8", mode: 384 }
|
|
2918
|
+
);
|
|
2919
|
+
emitSyncEvent(pulled?.genes?.length || 0);
|
|
2920
|
+
}
|
|
2921
|
+
} catch {
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
const doOutboxFlush = async () => {
|
|
2925
|
+
if (!fs2.existsSync(OUTBOX_PATH)) return;
|
|
2926
|
+
let entries = [];
|
|
2927
|
+
try {
|
|
2928
|
+
entries = JSON.parse(fs2.readFileSync(OUTBOX_PATH, "utf-8"));
|
|
2929
|
+
if (!Array.isArray(entries) || entries.length === 0) return;
|
|
2930
|
+
} catch {
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2933
|
+
try {
|
|
2934
|
+
const res = await fetchWithTimeout(
|
|
2935
|
+
`${cfg.baseUrl}/api/im/evolution/sync`,
|
|
2936
|
+
{
|
|
2937
|
+
method: "POST",
|
|
2938
|
+
headers: {
|
|
2939
|
+
"Content-Type": "application/json",
|
|
2940
|
+
Authorization: `Bearer ${cfg.apiKey}`
|
|
2941
|
+
},
|
|
2942
|
+
body: JSON.stringify({
|
|
2943
|
+
push: { outcomes: entries },
|
|
2944
|
+
pull: { since: 0 }
|
|
2945
|
+
})
|
|
2946
|
+
}
|
|
2947
|
+
);
|
|
2948
|
+
if (res.ok) {
|
|
2949
|
+
fs2.writeFileSync(OUTBOX_PATH, "[]", { encoding: "utf-8", mode: 384 });
|
|
2950
|
+
}
|
|
2951
|
+
} catch {
|
|
2952
|
+
}
|
|
2953
|
+
};
|
|
2954
|
+
await doEvolutionSync();
|
|
2955
|
+
await doOutboxFlush();
|
|
2956
|
+
const syncTimer = setInterval(doEvolutionSync, SYNC_INTERVAL_MS);
|
|
2957
|
+
const flushTimer = setInterval(doOutboxFlush, FLUSH_INTERVAL_MS);
|
|
2958
|
+
const originalShutdown = shutdown;
|
|
2959
|
+
const fullShutdown = () => {
|
|
2960
|
+
clearInterval(syncTimer);
|
|
2961
|
+
clearInterval(flushTimer);
|
|
2962
|
+
originalShutdown();
|
|
2963
|
+
};
|
|
2964
|
+
process.removeListener("SIGINT", shutdown);
|
|
2965
|
+
process.removeListener("SIGTERM", shutdown);
|
|
2966
|
+
process.on("SIGINT", fullShutdown);
|
|
2967
|
+
process.on("SIGTERM", fullShutdown);
|
|
2968
|
+
}
|
|
2969
|
+
async function startDaemon() {
|
|
2970
|
+
const existingPid = readPid();
|
|
2971
|
+
if (existingPid !== null && isProcessRunning(existingPid)) {
|
|
2972
|
+
const port = readPort();
|
|
2973
|
+
console.log(`Daemon already running. PID=${existingPid}${port ? ` port=${port}` : ""}`);
|
|
2974
|
+
return;
|
|
2975
|
+
}
|
|
2976
|
+
cleanupPidFiles();
|
|
2977
|
+
const cfg = loadConfig();
|
|
2978
|
+
if (!cfg) {
|
|
2979
|
+
console.error('No API key found. Run "prismer setup" first.');
|
|
2980
|
+
process.exit(1);
|
|
2981
|
+
}
|
|
2982
|
+
if (process.env["PRISMER_DAEMON"] === "1") {
|
|
2983
|
+
await runDaemonProcess();
|
|
2984
|
+
return;
|
|
2985
|
+
}
|
|
2986
|
+
const { spawn } = __require("child_process");
|
|
2987
|
+
const child = spawn(process.execPath, [process.argv[1], "daemon", "start"], {
|
|
2988
|
+
env: { ...process.env, PRISMER_DAEMON: "1" },
|
|
2989
|
+
detached: true,
|
|
2990
|
+
stdio: "ignore"
|
|
2991
|
+
});
|
|
2992
|
+
child.unref();
|
|
2993
|
+
let waited = 0;
|
|
2994
|
+
while (waited < 3e3) {
|
|
2995
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
2996
|
+
waited += 100;
|
|
2997
|
+
const pid = readPid();
|
|
2998
|
+
const port = readPort();
|
|
2999
|
+
if (pid !== null && port !== null) {
|
|
3000
|
+
console.log(`Daemon started. PID=${pid} port=${port}`);
|
|
3001
|
+
return;
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
console.log("Daemon spawned (PID file not yet written \u2014 may take a moment).");
|
|
3005
|
+
}
|
|
3006
|
+
function stopDaemon() {
|
|
3007
|
+
const pid = readPid();
|
|
3008
|
+
if (pid === null || !isProcessRunning(pid)) {
|
|
3009
|
+
console.log("Daemon: not running");
|
|
3010
|
+
cleanupPidFiles();
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
try {
|
|
3014
|
+
process.kill(pid, "SIGTERM");
|
|
3015
|
+
console.log(`Daemon stopped (PID=${pid})`);
|
|
3016
|
+
cleanupPidFiles();
|
|
3017
|
+
} catch (err) {
|
|
3018
|
+
console.error(`Failed to stop daemon: ${err.message}`);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
function daemonStatus() {
|
|
3022
|
+
const pid = readPid();
|
|
3023
|
+
if (pid === null || !isProcessRunning(pid)) {
|
|
3024
|
+
console.log("Daemon: not running");
|
|
3025
|
+
cleanupPidFiles();
|
|
3026
|
+
return;
|
|
3027
|
+
}
|
|
3028
|
+
const port = readPort();
|
|
3029
|
+
if (!port) {
|
|
3030
|
+
console.log(`Daemon: running (PID=${pid}, port unknown)`);
|
|
3031
|
+
return;
|
|
3032
|
+
}
|
|
3033
|
+
const req = http.request(
|
|
3034
|
+
{ hostname: "127.0.0.1", port, path: "/health", method: "GET", timeout: 3e3 },
|
|
3035
|
+
(res) => {
|
|
3036
|
+
let body = "";
|
|
3037
|
+
res.on("data", (chunk) => {
|
|
3038
|
+
body += chunk.toString();
|
|
3039
|
+
});
|
|
3040
|
+
res.on("end", () => {
|
|
3041
|
+
try {
|
|
3042
|
+
const health = JSON.parse(body);
|
|
3043
|
+
console.log(`Daemon: running`);
|
|
3044
|
+
console.log(` PID: ${health.pid}`);
|
|
3045
|
+
console.log(` Uptime: ${health.uptime}s`);
|
|
3046
|
+
console.log(` Last sync: ${health.lastSync ? new Date(health.lastSync).toISOString() : "never"}`);
|
|
3047
|
+
console.log(` Sync count: ${health.syncCount}`);
|
|
3048
|
+
console.log(` Outbox: ${health.outboxSize} entries`);
|
|
3049
|
+
console.log(` Port: ${port}`);
|
|
3050
|
+
} catch {
|
|
3051
|
+
console.log(`Daemon: running (PID=${pid} port=${port})`);
|
|
3052
|
+
}
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
3055
|
+
);
|
|
3056
|
+
req.on("error", () => {
|
|
3057
|
+
console.log(`Daemon: running (PID=${pid} port=${port}, health check failed)`);
|
|
3058
|
+
});
|
|
3059
|
+
req.end();
|
|
3060
|
+
}
|
|
3061
|
+
function resolveNpxPath() {
|
|
3062
|
+
try {
|
|
3063
|
+
return execSync("which npx", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3064
|
+
} catch {
|
|
3065
|
+
for (const p of ["/usr/local/bin/npx", "/opt/homebrew/bin/npx", `${homedir2()}/.nvm/current/bin/npx`]) {
|
|
3066
|
+
try {
|
|
3067
|
+
fs2.accessSync(p);
|
|
3068
|
+
return p;
|
|
3069
|
+
} catch {
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
return "npx";
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
function installLaunchd() {
|
|
3076
|
+
const plistPath = join2(homedir2(), "Library", "LaunchAgents", "cloud.prismer.daemon.plist");
|
|
3077
|
+
const npxPath = resolveNpxPath();
|
|
3078
|
+
const nodePath = process.execPath;
|
|
3079
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3080
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3081
|
+
<plist version="1.0">
|
|
3082
|
+
<dict>
|
|
3083
|
+
<key>Label</key>
|
|
3084
|
+
<string>cloud.prismer.daemon</string>
|
|
3085
|
+
<key>ProgramArguments</key>
|
|
3086
|
+
<array>
|
|
3087
|
+
<string>${npxPath}</string>
|
|
3088
|
+
<string>@prismer/sdk</string>
|
|
3089
|
+
<string>daemon</string>
|
|
3090
|
+
<string>start</string>
|
|
3091
|
+
</array>
|
|
3092
|
+
<key>EnvironmentVariables</key>
|
|
3093
|
+
<dict>
|
|
3094
|
+
<key>PRISMER_DAEMON</key>
|
|
3095
|
+
<string>1</string>
|
|
3096
|
+
<key>PATH</key>
|
|
3097
|
+
<string>${dirname(nodePath)}:/usr/local/bin:/usr/bin:/bin</string>
|
|
3098
|
+
</dict>
|
|
3099
|
+
<key>RunAtLoad</key>
|
|
3100
|
+
<true/>
|
|
3101
|
+
<key>KeepAlive</key>
|
|
3102
|
+
<true/>
|
|
3103
|
+
<key>StandardOutPath</key>
|
|
3104
|
+
<string>${join2(homedir2(), ".prismer", "daemon.stdout.log")}</string>
|
|
3105
|
+
<key>StandardErrorPath</key>
|
|
3106
|
+
<string>${join2(homedir2(), ".prismer", "daemon.stderr.log")}</string>
|
|
3107
|
+
</dict>
|
|
3108
|
+
</plist>`;
|
|
3109
|
+
fs2.mkdirSync(dirname(plistPath), { recursive: true });
|
|
3110
|
+
fs2.writeFileSync(plistPath, plist, { mode: 384 });
|
|
3111
|
+
try {
|
|
3112
|
+
execSync(`launchctl load ${plistPath}`, { stdio: "pipe" });
|
|
3113
|
+
console.log("[prismer] Daemon service installed and started (launchd)");
|
|
3114
|
+
console.log(` Plist: ${plistPath}`);
|
|
3115
|
+
} catch {
|
|
3116
|
+
console.log("[prismer] Plist written. Load manually: launchctl load " + plistPath);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
function uninstallLaunchd() {
|
|
3120
|
+
const plistPath = join2(homedir2(), "Library", "LaunchAgents", "cloud.prismer.daemon.plist");
|
|
3121
|
+
try {
|
|
3122
|
+
execSync(`launchctl unload ${plistPath}`, { stdio: "pipe" });
|
|
3123
|
+
} catch {
|
|
3124
|
+
}
|
|
3125
|
+
try {
|
|
3126
|
+
fs2.unlinkSync(plistPath);
|
|
3127
|
+
} catch {
|
|
3128
|
+
}
|
|
3129
|
+
console.log("[prismer] Daemon service uninstalled (launchd)");
|
|
3130
|
+
}
|
|
3131
|
+
function installSystemd() {
|
|
3132
|
+
const serviceDir = join2(homedir2(), ".config", "systemd", "user");
|
|
3133
|
+
const servicePath = join2(serviceDir, "prismer-daemon.service");
|
|
3134
|
+
const npxPath = resolveNpxPath();
|
|
3135
|
+
const nodePath = process.execPath;
|
|
3136
|
+
const unit = `[Unit]
|
|
3137
|
+
Description=Prismer Daemon \u2014 background evolution sync
|
|
3138
|
+
After=network-online.target
|
|
3139
|
+
|
|
3140
|
+
[Service]
|
|
3141
|
+
Type=simple
|
|
3142
|
+
Environment=PRISMER_DAEMON=1
|
|
3143
|
+
Environment=PATH=${dirname(nodePath)}:/usr/local/bin:/usr/bin:/bin
|
|
3144
|
+
ExecStart=${npxPath} @prismer/sdk daemon start
|
|
3145
|
+
Restart=on-failure
|
|
3146
|
+
RestartSec=10
|
|
3147
|
+
|
|
3148
|
+
[Install]
|
|
3149
|
+
WantedBy=default.target
|
|
3150
|
+
`;
|
|
3151
|
+
fs2.mkdirSync(serviceDir, { recursive: true });
|
|
3152
|
+
fs2.writeFileSync(servicePath, unit, { mode: 420 });
|
|
3153
|
+
try {
|
|
3154
|
+
execSync("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
3155
|
+
execSync("systemctl --user enable prismer-daemon", { stdio: "pipe" });
|
|
3156
|
+
execSync("systemctl --user start prismer-daemon", { stdio: "pipe" });
|
|
3157
|
+
console.log("[prismer] Daemon service installed and started (systemd)");
|
|
3158
|
+
console.log(` Service: ${servicePath}`);
|
|
3159
|
+
} catch {
|
|
3160
|
+
console.log("[prismer] Service file written. Enable manually:");
|
|
3161
|
+
console.log(" systemctl --user enable --now prismer-daemon");
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
function uninstallSystemd() {
|
|
3165
|
+
try {
|
|
3166
|
+
execSync("systemctl --user stop prismer-daemon", { stdio: "pipe" });
|
|
3167
|
+
} catch {
|
|
3168
|
+
}
|
|
3169
|
+
try {
|
|
3170
|
+
execSync("systemctl --user disable prismer-daemon", { stdio: "pipe" });
|
|
3171
|
+
} catch {
|
|
3172
|
+
}
|
|
3173
|
+
const servicePath = join2(homedir2(), ".config", "systemd", "user", "prismer-daemon.service");
|
|
3174
|
+
try {
|
|
3175
|
+
fs2.unlinkSync(servicePath);
|
|
3176
|
+
} catch {
|
|
3177
|
+
}
|
|
3178
|
+
try {
|
|
3179
|
+
execSync("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
3180
|
+
} catch {
|
|
3181
|
+
}
|
|
3182
|
+
console.log("[prismer] Daemon service uninstalled (systemd)");
|
|
3183
|
+
}
|
|
3184
|
+
function installDaemonService() {
|
|
3185
|
+
const platform = process.platform;
|
|
3186
|
+
if (platform === "darwin") {
|
|
3187
|
+
installLaunchd();
|
|
3188
|
+
} else if (platform === "linux") {
|
|
3189
|
+
installSystemd();
|
|
3190
|
+
} else {
|
|
3191
|
+
console.log(`Daemon auto-start not supported on ${platform}. Use: prismer daemon start`);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
function uninstallDaemonService() {
|
|
3195
|
+
const platform = process.platform;
|
|
3196
|
+
if (platform === "darwin") {
|
|
3197
|
+
uninstallLaunchd();
|
|
3198
|
+
} else if (platform === "linux") {
|
|
3199
|
+
uninstallSystemd();
|
|
3200
|
+
} else {
|
|
3201
|
+
console.log(`No daemon service to uninstall on ${platform}.`);
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
if (process.env["PRISMER_DAEMON"] === "1") {
|
|
3205
|
+
runDaemonProcess().catch((err) => {
|
|
3206
|
+
process.stderr.write(`[prismer-daemon] Fatal: ${err.message}
|
|
3207
|
+
`);
|
|
3208
|
+
process.exit(1);
|
|
3209
|
+
});
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
// src/cli.ts
|
|
3213
|
+
var cliVersion = "1.7.2";
|
|
3214
|
+
try {
|
|
3215
|
+
const pkgPath = path3.join(__dirname, "..", "package.json");
|
|
3216
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
|
|
3217
|
+
cliVersion = pkg.version || cliVersion;
|
|
3218
|
+
} catch {
|
|
3219
|
+
}
|
|
3220
|
+
var CONFIG_DIR2 = path3.join(os2.homedir(), ".prismer");
|
|
3221
|
+
var CONFIG_PATH2 = path3.join(CONFIG_DIR2, "config.toml");
|
|
3222
|
+
function ensureConfigDir() {
|
|
3223
|
+
if (!fs3.existsSync(CONFIG_DIR2)) {
|
|
3224
|
+
fs3.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
function readConfig() {
|
|
3228
|
+
if (!fs3.existsSync(CONFIG_PATH2)) return {};
|
|
3229
|
+
const raw = fs3.readFileSync(CONFIG_PATH2, "utf-8");
|
|
3230
|
+
return TOML2.parse(raw);
|
|
3231
|
+
}
|
|
3232
|
+
function writeConfig(config) {
|
|
3233
|
+
ensureConfigDir();
|
|
3234
|
+
fs3.writeFileSync(CONFIG_PATH2, TOML2.stringify(config), { encoding: "utf-8", mode: 384 });
|
|
3235
|
+
}
|
|
3236
|
+
function setNestedValue(obj, dotPath, value) {
|
|
3237
|
+
const parts = dotPath.split(".");
|
|
3238
|
+
let current = obj;
|
|
3239
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
3240
|
+
const key = parts[i];
|
|
3241
|
+
if (current[key] === void 0 || typeof current[key] !== "object") current[key] = {};
|
|
3242
|
+
current = current[key];
|
|
3243
|
+
}
|
|
3244
|
+
current[parts[parts.length - 1]] = value;
|
|
3245
|
+
}
|
|
3246
|
+
function getIMClient() {
|
|
3247
|
+
const cfg = readConfig();
|
|
3248
|
+
const token = cfg?.auth?.im_token;
|
|
3249
|
+
if (!token) {
|
|
3250
|
+
error('No IM token. Run "prismer setup --agent" or "prismer register <username>" first.');
|
|
3251
|
+
process.exit(1);
|
|
3252
|
+
}
|
|
3253
|
+
const env = cfg?.default?.environment || "production";
|
|
3254
|
+
const baseUrl = cfg?.default?.base_url || "";
|
|
3255
|
+
return new PrismerClient({ apiKey: token, environment: env, ...baseUrl ? { baseUrl } : {} });
|
|
3256
|
+
}
|
|
3257
|
+
function getAPIClient() {
|
|
3258
|
+
const cfg = readConfig();
|
|
3259
|
+
const apiKey = cfg?.default?.api_key;
|
|
3260
|
+
if (!apiKey) {
|
|
3261
|
+
error('No API key. Run "prismer setup" to sign in and get your key.');
|
|
3262
|
+
process.exit(1);
|
|
3263
|
+
}
|
|
3264
|
+
const env = cfg?.default?.environment || "production";
|
|
3265
|
+
const baseUrl = cfg?.default?.base_url || "";
|
|
3266
|
+
return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {} });
|
|
3267
|
+
}
|
|
3268
|
+
var program = new Command();
|
|
3269
|
+
program.name("prismer").description("Prismer Cloud SDK CLI").version(cliVersion);
|
|
3270
|
+
async function verifyAndSaveKey(config, apiKey) {
|
|
3271
|
+
if (!apiKey) {
|
|
3272
|
+
error("No key provided.");
|
|
3273
|
+
process.exit(1);
|
|
3274
|
+
}
|
|
3275
|
+
if (!apiKey.startsWith("sk-prismer-")) {
|
|
3276
|
+
error("Invalid key format. API keys start with sk-prismer-");
|
|
3277
|
+
dim2(" Get your key at: https://prismer.cloud/setup");
|
|
3278
|
+
process.exit(1);
|
|
3279
|
+
}
|
|
3280
|
+
const baseUrl = config.default?.base_url || "https://prismer.cloud";
|
|
3281
|
+
try {
|
|
3282
|
+
const res = await fetch(`${baseUrl}/api/version`, {
|
|
3283
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3284
|
+
});
|
|
3285
|
+
if (res.status === 401) {
|
|
3286
|
+
error("API key is invalid or expired.");
|
|
3287
|
+
dim2(" Get a new key at: https://prismer.cloud/setup");
|
|
3288
|
+
process.exit(1);
|
|
3289
|
+
}
|
|
3290
|
+
success("API key verified");
|
|
3291
|
+
} catch (err) {
|
|
3292
|
+
warn(`Could not verify key (${err.message}). Saving anyway.`);
|
|
3293
|
+
}
|
|
3294
|
+
if (!config.default) config.default = {};
|
|
3295
|
+
config.default.api_key = apiKey;
|
|
3296
|
+
if (!config.default.environment) config.default.environment = "production";
|
|
3297
|
+
writeConfig(config);
|
|
3298
|
+
console.log("");
|
|
3299
|
+
success("Saved to ~/.prismer/config.toml");
|
|
3300
|
+
info("You can now use: CLI commands, MCP tools, Claude Code plugin, and all SDKs.");
|
|
3301
|
+
try {
|
|
3302
|
+
installDaemonService();
|
|
3303
|
+
} catch {
|
|
3304
|
+
dim2("Daemon auto-start setup skipped. Run manually: prismer daemon install");
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
function openBrowser(url) {
|
|
3308
|
+
const { execFile } = __require("child_process");
|
|
3309
|
+
if (process.platform === "darwin") {
|
|
3310
|
+
execFile("open", [url], (err) => {
|
|
3311
|
+
if (err) console.warn("Could not open browser. Please open the URL above manually.");
|
|
3312
|
+
});
|
|
3313
|
+
} else if (process.platform === "win32") {
|
|
3314
|
+
execFile("cmd.exe", ["/c", "start", "", url], (err) => {
|
|
3315
|
+
if (err) console.warn("Could not open browser. Please open the URL above manually.");
|
|
3316
|
+
});
|
|
3317
|
+
} else {
|
|
3318
|
+
execFile("xdg-open", [url], (err) => {
|
|
3319
|
+
if (err) console.warn("Could not open browser. Please open the URL above manually.");
|
|
3320
|
+
});
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
async function runSetup(opts, apiKey) {
|
|
3324
|
+
const config = readConfig();
|
|
3325
|
+
if (!config.default) config.default = {};
|
|
3326
|
+
const baseUrl = config.default.base_url || "https://prismer.cloud";
|
|
3327
|
+
if (!opts.force && config.default.api_key?.startsWith("sk-prismer-")) {
|
|
3328
|
+
const masked = config.default.api_key.slice(0, 12) + "..." + config.default.api_key.slice(-4);
|
|
3329
|
+
success(`Already configured: ${masked}`);
|
|
3330
|
+
console.log("");
|
|
3331
|
+
dim2(" To reconfigure, run: prismer setup --force");
|
|
3332
|
+
dim2(" To check status: prismer status");
|
|
3333
|
+
return;
|
|
3334
|
+
}
|
|
3335
|
+
if (apiKey) {
|
|
3336
|
+
await verifyAndSaveKey(config, apiKey);
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
if (opts.agent) {
|
|
3340
|
+
if (!opts.force && config.auth?.im_token) {
|
|
3341
|
+
success("Already registered as agent (IM token exists).");
|
|
3342
|
+
dim2(" For API key access, run: prismer setup");
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3345
|
+
const username = `agent-${Date.now().toString(36)}`;
|
|
3346
|
+
try {
|
|
3347
|
+
const res = await fetch(`${baseUrl}/api/im/register`, {
|
|
3348
|
+
method: "POST",
|
|
3349
|
+
headers: { "Content-Type": "application/json" },
|
|
3350
|
+
body: JSON.stringify({ username, displayName: username, type: "agent" })
|
|
3351
|
+
});
|
|
3352
|
+
const data = await res.json();
|
|
3353
|
+
if (!data.ok) throw new Error(data.error?.message || "Registration failed");
|
|
3354
|
+
if (!config.auth) config.auth = {};
|
|
3355
|
+
config.auth.im_token = data.data?.token;
|
|
3356
|
+
config.auth.im_user_id = data.data?.imUserId || data.data?.userId;
|
|
3357
|
+
config.auth.im_username = data.data?.username || username;
|
|
3358
|
+
writeConfig(config);
|
|
3359
|
+
success("Agent registered with free credits");
|
|
3360
|
+
keyValue({
|
|
3361
|
+
"Username": config.auth.im_username || "",
|
|
3362
|
+
"User ID": config.auth.im_user_id || ""
|
|
3363
|
+
});
|
|
3364
|
+
console.log("");
|
|
3365
|
+
info("For full API access, sign in: prismer setup");
|
|
3366
|
+
} catch (err) {
|
|
3367
|
+
error(`Agent registration failed: ${err.message}`);
|
|
3368
|
+
dim2(" Try signing in instead: prismer setup");
|
|
3369
|
+
process.exit(1);
|
|
3370
|
+
}
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3373
|
+
if (opts.manual) {
|
|
3374
|
+
const setupUrl = `${baseUrl}/setup?utm_source=cli&utm_medium=manual`;
|
|
3375
|
+
info("Opening browser to sign in...");
|
|
3376
|
+
dim2(` ${setupUrl}`);
|
|
3377
|
+
console.log("");
|
|
3378
|
+
openBrowser(setupUrl);
|
|
3379
|
+
info("After signing in, copy the API key from the page and paste it below.");
|
|
3380
|
+
console.log("");
|
|
3381
|
+
const readline = __require("readline");
|
|
3382
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3383
|
+
rl.question("Paste your API key: ", (key) => {
|
|
3384
|
+
rl.close();
|
|
3385
|
+
verifyAndSaveKey(config, key.trim()).catch((err) => {
|
|
3386
|
+
error(`Setup failed: ${err.message}`);
|
|
3387
|
+
process.exit(1);
|
|
3388
|
+
});
|
|
3389
|
+
});
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3392
|
+
const http2 = __require("http");
|
|
3393
|
+
const crypto = __require("crypto");
|
|
3394
|
+
const state = crypto.randomBytes(16).toString("hex");
|
|
3395
|
+
let resolved = false;
|
|
3396
|
+
const server = http2.createServer((req, res) => {
|
|
3397
|
+
const url = new URL(req.url, `http://localhost`);
|
|
3398
|
+
if (url.pathname === "/callback") {
|
|
3399
|
+
const key = url.searchParams.get("key");
|
|
3400
|
+
const returnedState = url.searchParams.get("state");
|
|
3401
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3402
|
+
if (!key || !returnedState || returnedState !== state) {
|
|
3403
|
+
res.end('<html><head><meta name="referrer" content="no-referrer"></head><body style="font-family:system-ui;text-align:center;padding:60px"><h2>Setup failed</h2><p>Invalid or missing parameters. Please try again.</p></body></html>');
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
if (!key.startsWith("sk-prismer-")) {
|
|
3407
|
+
res.end('<html><head><meta name="referrer" content="no-referrer"></head><body style="font-family:system-ui;text-align:center;padding:60px"><h2>Invalid key</h2><p>The key format is unexpected. Please try again.</p></body></html>');
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3410
|
+
res.end('<html><head><meta name="referrer" content="no-referrer"></head><body style="font-family:system-ui;text-align:center;padding:60px"><h2>Done!</h2><p>API key received. You can close this tab.</p></body></html>');
|
|
3411
|
+
resolved = true;
|
|
3412
|
+
verifyAndSaveKey(config, key).then(() => {
|
|
3413
|
+
server.close();
|
|
3414
|
+
process.exit(0);
|
|
3415
|
+
}).catch((err) => {
|
|
3416
|
+
console.error(`Setup failed: ${err.message}`);
|
|
3417
|
+
server.close();
|
|
3418
|
+
process.exit(1);
|
|
3419
|
+
});
|
|
3420
|
+
} else {
|
|
3421
|
+
res.writeHead(404);
|
|
3422
|
+
res.end("Not found");
|
|
3423
|
+
}
|
|
3424
|
+
});
|
|
3425
|
+
server.listen(0, "127.0.0.1", () => {
|
|
3426
|
+
const port = server.address().port;
|
|
3427
|
+
const callbackUrl = `http://127.0.0.1:${port}/callback`;
|
|
3428
|
+
const setupUrl = `${baseUrl}/setup?callback=${encodeURIComponent(callbackUrl)}&state=${state}&utm_source=cli&utm_medium=auto`;
|
|
3429
|
+
info("Opening browser to sign in...");
|
|
3430
|
+
console.log("");
|
|
3431
|
+
openBrowser(setupUrl);
|
|
3432
|
+
info("Waiting for authentication...");
|
|
3433
|
+
dim2(" (If the browser didn't open, visit this URL manually:)");
|
|
3434
|
+
dim2(` ${setupUrl}`);
|
|
3435
|
+
console.log("");
|
|
3436
|
+
setTimeout(() => {
|
|
3437
|
+
if (!resolved) {
|
|
3438
|
+
error("Timed out waiting for authentication (5 min).");
|
|
3439
|
+
console.log("");
|
|
3440
|
+
dim2(" Alternatives:");
|
|
3441
|
+
dim2(" prismer setup --manual Paste key manually");
|
|
3442
|
+
dim2(" prismer setup --agent Register as agent (free credits, no browser)");
|
|
3443
|
+
server.close();
|
|
3444
|
+
process.exit(1);
|
|
3445
|
+
}
|
|
3446
|
+
}, 5 * 60 * 1e3);
|
|
3447
|
+
});
|
|
3448
|
+
}
|
|
3449
|
+
program.command("setup [api-key]").description("Set up Prismer \u2014 sign in via browser, register as agent, or provide your API key").option("--manual", "Paste API key manually instead of browser auto-flow").option("--agent", "Register as agent with free credits (no browser, for CI/scripts)").option("--force", "Reconfigure even if already set up").action(async (apiKey, opts) => {
|
|
3450
|
+
await runSetup(opts, apiKey);
|
|
3451
|
+
});
|
|
3452
|
+
program.command("init [api-key]").description('Alias for "prismer setup" (deprecated, use setup instead)').option("--manual", "Paste API key manually").option("--agent", "Register as agent with free credits").option("--force", "Reconfigure even if already set up").action(async (apiKey, opts) => {
|
|
3453
|
+
warn('"prismer init" is deprecated. Use "prismer setup" instead.');
|
|
3454
|
+
console.log("");
|
|
3455
|
+
await runSetup(opts, apiKey);
|
|
3456
|
+
});
|
|
3457
|
+
program.command("register <username>").description("Register an IM identity and store the token").option("--type <type>", "Identity type: agent or human", "agent").option("--display-name <name>", "Display name").option("--agent-type <agentType>", "Agent type: assistant, specialist, orchestrator, tool, bot").option("--capabilities <caps>", "Comma-separated capabilities").option("--endpoint <url>", "Webhook endpoint URL").option("--webhook-secret <secret>", "Webhook HMAC secret").action(async (username, opts) => {
|
|
3458
|
+
const config = readConfig();
|
|
3459
|
+
const apiKey = config.default?.api_key;
|
|
3460
|
+
if (!apiKey) {
|
|
3461
|
+
error('No API key. Run "prismer setup" first.');
|
|
3462
|
+
process.exit(1);
|
|
3463
|
+
}
|
|
3464
|
+
const client = new PrismerClient({
|
|
3465
|
+
apiKey,
|
|
3466
|
+
environment: config.default?.environment || "production",
|
|
3467
|
+
baseUrl: config.default?.base_url || void 0
|
|
3468
|
+
});
|
|
3469
|
+
const registerOpts = {
|
|
3470
|
+
type: opts.type,
|
|
3471
|
+
username,
|
|
3472
|
+
displayName: opts.displayName || username
|
|
3473
|
+
};
|
|
3474
|
+
if (opts.agentType) registerOpts.agentType = opts.agentType;
|
|
3475
|
+
if (opts.capabilities) registerOpts.capabilities = opts.capabilities.split(",").map((c) => c.trim());
|
|
3476
|
+
if (opts.endpoint) registerOpts.endpoint = opts.endpoint;
|
|
3477
|
+
if (opts.webhookSecret) registerOpts.webhookSecret = opts.webhookSecret;
|
|
3478
|
+
try {
|
|
3479
|
+
const result = await client.im.account.register(registerOpts);
|
|
3480
|
+
if (!result.ok || !result.data) {
|
|
3481
|
+
error(`Registration failed: ${result.error?.message || "Unknown error"}`);
|
|
3482
|
+
process.exit(1);
|
|
3483
|
+
}
|
|
3484
|
+
const data = result.data;
|
|
3485
|
+
if (!config.auth) config.auth = {};
|
|
3486
|
+
config.auth.im_token = data.token;
|
|
3487
|
+
config.auth.im_user_id = data.imUserId;
|
|
3488
|
+
config.auth.im_username = data.username;
|
|
3489
|
+
config.auth.im_token_expires = data.expiresIn;
|
|
3490
|
+
writeConfig(config);
|
|
3491
|
+
success("Registration successful!");
|
|
3492
|
+
keyValue({
|
|
3493
|
+
"User ID": data.imUserId,
|
|
3494
|
+
"Username": data.username,
|
|
3495
|
+
"Display": data.displayName,
|
|
3496
|
+
"Role": data.role,
|
|
3497
|
+
"New": String(data.isNew)
|
|
3498
|
+
});
|
|
3499
|
+
dim2(" Token stored in ~/.prismer/config.toml");
|
|
3500
|
+
} catch (err) {
|
|
3501
|
+
error(`Registration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3502
|
+
process.exit(1);
|
|
3503
|
+
}
|
|
3504
|
+
});
|
|
3505
|
+
program.command("status").description("Show current config and live info").action(async () => {
|
|
3506
|
+
const config = readConfig();
|
|
3507
|
+
info("Prismer Status");
|
|
3508
|
+
console.log("");
|
|
3509
|
+
const apiKey = config.default?.api_key;
|
|
3510
|
+
const maskedKey = apiKey ? apiKey.length > 16 ? apiKey.slice(0, 12) + "..." + apiKey.slice(-4) : "***" : "(not set)";
|
|
3511
|
+
keyValue({
|
|
3512
|
+
"API Key": maskedKey,
|
|
3513
|
+
"Environment": config.default?.environment || "(not set)",
|
|
3514
|
+
"Base URL": config.default?.base_url || "(default)"
|
|
3515
|
+
});
|
|
3516
|
+
console.log("");
|
|
3517
|
+
const token = config.auth?.im_token;
|
|
3518
|
+
if (token) {
|
|
3519
|
+
let tokenStatus = "set (expiry unknown)";
|
|
3520
|
+
const expires = config.auth?.im_token_expires;
|
|
3521
|
+
if (expires) {
|
|
3522
|
+
const expiresDate = new Date(expires);
|
|
3523
|
+
if (!isNaN(expiresDate.getTime())) {
|
|
3524
|
+
tokenStatus = expiresDate <= /* @__PURE__ */ new Date() ? "EXPIRED" : `valid (expires ${expiresDate.toISOString()})`;
|
|
3525
|
+
} else {
|
|
3526
|
+
tokenStatus = `set (expires in ${expires})`;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
keyValue({
|
|
3530
|
+
"IM User ID": config.auth?.im_user_id || "(unknown)",
|
|
3531
|
+
"IM Username": config.auth?.im_username || "(unknown)",
|
|
3532
|
+
"IM Token": tokenStatus
|
|
3533
|
+
});
|
|
3534
|
+
console.log("");
|
|
3535
|
+
const me = await withSpinner("Fetching live info", async () => {
|
|
3536
|
+
const client = new PrismerClient({
|
|
3537
|
+
apiKey: token,
|
|
3538
|
+
environment: config.default?.environment || "production",
|
|
3539
|
+
baseUrl: config.default?.base_url || void 0
|
|
3540
|
+
});
|
|
3541
|
+
return client.im.account.me();
|
|
3542
|
+
}).catch((err) => {
|
|
3543
|
+
warn(`Could not fetch live info: ${err instanceof Error ? err.message : String(err)}`);
|
|
3544
|
+
return null;
|
|
3545
|
+
});
|
|
3546
|
+
if (me && me.ok && me.data) {
|
|
3547
|
+
keyValue({
|
|
3548
|
+
"Display": me.data.user.displayName,
|
|
3549
|
+
"Role": me.data.user.role,
|
|
3550
|
+
"Credits": String(me.data.credits.balance),
|
|
3551
|
+
"Messages": String(me.data.stats.messagesSent),
|
|
3552
|
+
"Unread": String(me.data.stats.unreadCount)
|
|
3553
|
+
});
|
|
3554
|
+
} else if (me) {
|
|
3555
|
+
warn(`Could not fetch live info: ${me.error?.message || "unknown error"}`);
|
|
3556
|
+
}
|
|
3557
|
+
} else {
|
|
3558
|
+
dim2(" IM Token: (not registered)");
|
|
3559
|
+
}
|
|
3560
|
+
});
|
|
3561
|
+
var configCmd = program.command("config").description("Manage config file");
|
|
3562
|
+
configCmd.command("show").description("Print config file").action(() => {
|
|
3563
|
+
if (!fs3.existsSync(CONFIG_PATH2)) {
|
|
3564
|
+
warn('No config file. Run "prismer setup" to create one.');
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
console.log(fs3.readFileSync(CONFIG_PATH2, "utf-8"));
|
|
3568
|
+
});
|
|
3569
|
+
configCmd.command("set <key> <value>").description("Set a config value (e.g. default.base_url)").action((key, value) => {
|
|
3570
|
+
const config = readConfig();
|
|
3571
|
+
setNestedValue(config, key, value);
|
|
3572
|
+
writeConfig(config);
|
|
3573
|
+
success(`Set ${key} = ${value}`);
|
|
3574
|
+
});
|
|
3575
|
+
var tokenCmd = program.command("token").description("Token management");
|
|
3576
|
+
tokenCmd.command("refresh").description("Refresh IM JWT token").option("--json", "JSON output").action(async (opts) => {
|
|
3577
|
+
const client = getIMClient();
|
|
3578
|
+
const res = await client.im.account.refreshToken();
|
|
3579
|
+
if (opts.json) {
|
|
3580
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
if (!res.ok) {
|
|
3584
|
+
error(`Token refresh failed: ${JSON.stringify(res.error)}`);
|
|
3585
|
+
process.exit(1);
|
|
3586
|
+
}
|
|
3587
|
+
const data = res.data;
|
|
3588
|
+
const config = readConfig();
|
|
3589
|
+
if (!config.auth) config.auth = {};
|
|
3590
|
+
if (data?.token) {
|
|
3591
|
+
config.auth.im_token = data.token;
|
|
3592
|
+
if (data.expiresIn) config.auth.im_token_expires = data.expiresIn;
|
|
3593
|
+
writeConfig(config);
|
|
3594
|
+
success("Token refreshed and saved.");
|
|
3595
|
+
} else {
|
|
3596
|
+
info("Token refreshed (no new token in response).");
|
|
3597
|
+
}
|
|
3598
|
+
});
|
|
3599
|
+
register(program, getIMClient, getAPIClient);
|
|
3600
|
+
register2(program, getIMClient, getAPIClient);
|
|
3601
|
+
register3(program, getIMClient, getAPIClient);
|
|
3602
|
+
register4(program, getIMClient, getAPIClient);
|
|
3603
|
+
register5(program, getIMClient, getAPIClient);
|
|
3604
|
+
register6(program, getIMClient, getAPIClient);
|
|
3605
|
+
register7(program, getIMClient, getAPIClient);
|
|
3606
|
+
register8(program, getIMClient, getAPIClient);
|
|
3607
|
+
register9(program, getIMClient, getAPIClient);
|
|
3608
|
+
register10(program, getIMClient, getAPIClient);
|
|
3609
|
+
program.command("send").description("Send a direct message (shortcut for: im send)").argument("<user-id>", "Target user/agent ID").argument("<message>", "Message content").option("-t, --type <type>", "Message type: text, markdown, code, etc.", "text").option("--reply-to <id>", "Reply to a message ID").option("--json", "JSON output").action(async (userId, message, opts) => {
|
|
3610
|
+
const client = getIMClient();
|
|
3611
|
+
const sendOpts = {};
|
|
3612
|
+
if (opts.type && opts.type !== "text") sendOpts.type = opts.type;
|
|
3613
|
+
if (opts.replyTo) sendOpts.parentId = opts.replyTo;
|
|
3614
|
+
const res = await withSpinner("Sending message", async () => {
|
|
3615
|
+
return client.im.direct.send(userId, message, sendOpts);
|
|
3616
|
+
});
|
|
3617
|
+
if (opts.json) {
|
|
3618
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3619
|
+
return;
|
|
3620
|
+
}
|
|
3621
|
+
if (!res.ok) {
|
|
3622
|
+
error(`Send failed: ${JSON.stringify(res.error)}`);
|
|
3623
|
+
process.exit(1);
|
|
3624
|
+
}
|
|
3625
|
+
success(`Message sent (conversation: ${res.data?.conversationId})`);
|
|
3626
|
+
});
|
|
3627
|
+
program.command("load").description("Load URL(s) \u2192 compressed HQCC (shortcut for: context load)").argument("<urls...>", "One or more URLs").option("-f, --format <fmt>", "Return format: hqcc, raw, both", "hqcc").option("--json", "JSON output").action(async (urls, opts) => {
|
|
3628
|
+
const client = getAPIClient();
|
|
3629
|
+
const input = urls.length === 1 ? urls[0] : urls;
|
|
3630
|
+
const loadOpts = {};
|
|
3631
|
+
if (opts.format) loadOpts.return = { format: opts.format };
|
|
3632
|
+
const res = await withSpinner(`Loading ${urls.length} URL(s)`, async () => {
|
|
3633
|
+
return client.load(input, loadOpts);
|
|
3634
|
+
});
|
|
3635
|
+
if (opts.json) {
|
|
3636
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3637
|
+
return;
|
|
3638
|
+
}
|
|
3639
|
+
if (!res.success) {
|
|
3640
|
+
error(res.error?.message || "Load failed");
|
|
3641
|
+
process.exit(1);
|
|
3642
|
+
}
|
|
3643
|
+
const results = res.results || (res.result ? [res.result] : []);
|
|
3644
|
+
for (const r of results) {
|
|
3645
|
+
keyValue({
|
|
3646
|
+
"URL": r.url || "?",
|
|
3647
|
+
"Status": r.cached ? "cached" : "loaded"
|
|
3648
|
+
});
|
|
3649
|
+
if (r.hqcc) console.log(`
|
|
3650
|
+
--- HQCC ---
|
|
3651
|
+
${r.hqcc.substring(0, 2e3)}`);
|
|
3652
|
+
if (r.raw) console.log(`
|
|
3653
|
+
--- Raw ---
|
|
3654
|
+
${r.raw.substring(0, 2e3)}`);
|
|
3655
|
+
console.log("");
|
|
3656
|
+
}
|
|
3657
|
+
});
|
|
3658
|
+
program.command("search").description("Search web content (shortcut for: context search)").argument("<query>", "Search query").option("-k, --top-k <n>", "Number of results", "5").option("--json", "JSON output").action(async (query, opts) => {
|
|
3659
|
+
const client = getAPIClient();
|
|
3660
|
+
const res = await withSpinner(`Searching: ${query}`, async () => {
|
|
3661
|
+
return client.search(query, { topK: parseInt(opts.topK || "5") });
|
|
3662
|
+
});
|
|
3663
|
+
if (opts.json) {
|
|
3664
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
if (!res.success) {
|
|
3668
|
+
error(res.error?.message || "Search failed");
|
|
3669
|
+
process.exit(1);
|
|
3670
|
+
}
|
|
3671
|
+
const results = res.results || [];
|
|
3672
|
+
if (results.length === 0) {
|
|
3673
|
+
warn("No results.");
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
const rows = results.map((r, i) => [
|
|
3677
|
+
String(i + 1),
|
|
3678
|
+
r.url || "(no url)",
|
|
3679
|
+
String(r.ranking?.score ?? "-")
|
|
3680
|
+
]);
|
|
3681
|
+
table(["#", "URL", "Score"], rows);
|
|
3682
|
+
for (let i = 0; i < results.length; i++) {
|
|
3683
|
+
const r = results[i];
|
|
3684
|
+
if (r.hqcc) {
|
|
3685
|
+
console.log("");
|
|
3686
|
+
dim2(` ${i + 1}. ${r.hqcc.substring(0, 200)}`);
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
});
|
|
3690
|
+
program.command("parse").description("Parse a document via OCR (shortcut for: parse run)").argument("<url>", "Document URL").option("-m, --mode <mode>", "Parse mode: fast, hires, auto", "fast").option("--async", "Async mode (returns task ID)").option("--json", "JSON output").action(async (url, opts) => {
|
|
3691
|
+
const client = getAPIClient();
|
|
3692
|
+
const res = await withSpinner(`Parsing: ${url}`, async () => {
|
|
3693
|
+
return client.parsePdf(url, opts.mode);
|
|
3694
|
+
});
|
|
3695
|
+
if (opts.json) {
|
|
3696
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3697
|
+
return;
|
|
3698
|
+
}
|
|
3699
|
+
if (!res.success) {
|
|
3700
|
+
error(res.error?.message || "Parse failed");
|
|
3701
|
+
process.exit(1);
|
|
3702
|
+
}
|
|
3703
|
+
if (res.taskId) {
|
|
3704
|
+
keyValue({
|
|
3705
|
+
"Task ID": res.taskId,
|
|
3706
|
+
"Status": res.status || "processing"
|
|
3707
|
+
});
|
|
3708
|
+
console.log("");
|
|
3709
|
+
dim2(` Check: prismer parse-status ${res.taskId}`);
|
|
3710
|
+
} else if (res.document) {
|
|
3711
|
+
success("Parse complete");
|
|
3712
|
+
const content = res.document.markdown || res.document.text || JSON.stringify(res.document, null, 2);
|
|
3713
|
+
console.log(content.substring(0, 5e3));
|
|
3714
|
+
}
|
|
3715
|
+
});
|
|
3716
|
+
var parseCmd = program.commands.find((c) => c.name() === "parse");
|
|
3717
|
+
if (parseCmd) {
|
|
3718
|
+
}
|
|
3719
|
+
program.command("parse-status").description("Check parse task status").argument("<task-id>", "Task ID").option("--json", "JSON output").action(async (taskId, opts) => {
|
|
3720
|
+
const client = getAPIClient();
|
|
3721
|
+
const res = await client.parseStatus(taskId);
|
|
3722
|
+
if (opts.json) {
|
|
3723
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3724
|
+
return;
|
|
3725
|
+
}
|
|
3726
|
+
keyValue({
|
|
3727
|
+
"Task": taskId,
|
|
3728
|
+
"Status": res.status || (res.success ? "complete" : "unknown")
|
|
3729
|
+
});
|
|
3730
|
+
});
|
|
3731
|
+
program.command("parse-result").description("Get parse result").argument("<task-id>", "Task ID").option("--json", "JSON output").action(async (taskId, opts) => {
|
|
3732
|
+
const client = getAPIClient();
|
|
3733
|
+
const res = await client.parseResult(taskId);
|
|
3734
|
+
if (opts.json) {
|
|
3735
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3736
|
+
return;
|
|
3737
|
+
}
|
|
3738
|
+
if (!res.success) {
|
|
3739
|
+
error(res.error?.message || "Not ready");
|
|
3740
|
+
process.exit(1);
|
|
3741
|
+
}
|
|
3742
|
+
success("Parse result ready");
|
|
3743
|
+
const content = res.document?.markdown || res.document?.text || JSON.stringify(res.document, null, 2);
|
|
3744
|
+
console.log(content);
|
|
3745
|
+
});
|
|
3746
|
+
program.command("recall").description("Search across memory, cache, and evolution (shortcut for: memory recall)").argument("<query>", "Search query").option("--scope <scope>", "Scope: all, memory, cache, evolution", "all").option("-n, --limit <n>", "Max results", "10").option("--json", "JSON output").action(async (query, opts) => {
|
|
3747
|
+
const client = getIMClient();
|
|
3748
|
+
const params = { q: query };
|
|
3749
|
+
if (opts.scope) params.scope = opts.scope;
|
|
3750
|
+
if (opts.limit) params.limit = opts.limit;
|
|
3751
|
+
const res = await withSpinner(`Recalling: ${query}`, async () => {
|
|
3752
|
+
return client.im.memory._r("GET", "/api/im/recall", void 0, params);
|
|
3753
|
+
});
|
|
3754
|
+
if (opts.json) {
|
|
3755
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3756
|
+
return;
|
|
3757
|
+
}
|
|
3758
|
+
if (!res.ok) {
|
|
3759
|
+
error(`Recall failed: ${JSON.stringify(res.error)}`);
|
|
3760
|
+
process.exit(1);
|
|
3761
|
+
}
|
|
3762
|
+
const data = res.data || [];
|
|
3763
|
+
if (data.length === 0) {
|
|
3764
|
+
warn(`No results for "${query}".`);
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
const rows = data.map((item) => [
|
|
3768
|
+
(item.source || "").toUpperCase(),
|
|
3769
|
+
item.title || "?",
|
|
3770
|
+
(item.score || 0).toFixed(2)
|
|
3771
|
+
]);
|
|
3772
|
+
table(["Source", "Title", "Score"], rows);
|
|
3773
|
+
for (const item of data) {
|
|
3774
|
+
if (item.snippet) {
|
|
3775
|
+
dim2(` ${item.snippet.substring(0, 200)}`);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
});
|
|
3779
|
+
program.command("discover").description("Discover available agents (shortcut for: im discover)").option("--type <type>", "Filter by agent type").option("--capability <cap>", "Filter by capability").option("--json", "JSON output").action(async (opts) => {
|
|
3780
|
+
const client = getIMClient();
|
|
3781
|
+
const discoverOpts = {};
|
|
3782
|
+
if (opts.type) discoverOpts.type = opts.type;
|
|
3783
|
+
if (opts.capability) discoverOpts.capability = opts.capability;
|
|
3784
|
+
const res = await withSpinner("Discovering agents", async () => {
|
|
3785
|
+
return client.im.contacts.discover(discoverOpts);
|
|
3786
|
+
});
|
|
3787
|
+
if (opts.json) {
|
|
3788
|
+
console.log(JSON.stringify(res, null, 2));
|
|
3789
|
+
return;
|
|
3790
|
+
}
|
|
3791
|
+
if (!res.ok) {
|
|
3792
|
+
error(`Discovery failed: ${JSON.stringify(res.error)}`);
|
|
3793
|
+
process.exit(1);
|
|
3794
|
+
}
|
|
3795
|
+
const agents = res.data || [];
|
|
3796
|
+
if (agents.length === 0) {
|
|
3797
|
+
warn("No agents found.");
|
|
3798
|
+
return;
|
|
3799
|
+
}
|
|
3800
|
+
const rows = agents.map((a) => [
|
|
3801
|
+
a.username || "",
|
|
3802
|
+
a.agentType || "",
|
|
3803
|
+
a.status || "",
|
|
3804
|
+
a.displayName || ""
|
|
3805
|
+
]);
|
|
3806
|
+
table(["Username", "Type", "Status", "Display Name"], rows);
|
|
3807
|
+
});
|
|
3808
|
+
program.command("daemon <action>").description("Manage background sync daemon (start|stop|status|install|uninstall)").action(async (action) => {
|
|
3809
|
+
switch (action) {
|
|
3810
|
+
case "start":
|
|
3811
|
+
await startDaemon();
|
|
3812
|
+
break;
|
|
3813
|
+
case "stop":
|
|
3814
|
+
stopDaemon();
|
|
3815
|
+
break;
|
|
3816
|
+
case "status":
|
|
3817
|
+
daemonStatus();
|
|
3818
|
+
break;
|
|
3819
|
+
case "install":
|
|
3820
|
+
installDaemonService();
|
|
3821
|
+
break;
|
|
3822
|
+
case "uninstall":
|
|
3823
|
+
uninstallDaemonService();
|
|
3824
|
+
break;
|
|
3825
|
+
default:
|
|
3826
|
+
error(`Unknown daemon action: ${action}. Use: start, stop, status, install, uninstall`);
|
|
3827
|
+
process.exit(1);
|
|
3828
|
+
}
|
|
3829
|
+
});
|
|
3830
|
+
displayBanner();
|
|
3831
|
+
program.parse(process.argv);
|
|
3832
|
+
export {
|
|
3833
|
+
getAPIClient,
|
|
3834
|
+
getIMClient
|
|
3835
|
+
};
|
|
3836
|
+
t,
|
|
3837
|
+
getIMClient
|
|
3838
|
+
};
|