@meet-ai/cli 0.0.8 → 0.0.9
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/index.js +150 -6
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
2
4
|
|
|
3
5
|
// src/client.ts
|
|
4
6
|
function wsLog(data) {
|
|
@@ -37,6 +39,23 @@ async function withRetry(fn, options) {
|
|
|
37
39
|
}
|
|
38
40
|
throw lastError;
|
|
39
41
|
}
|
|
42
|
+
var ATTACHMENTS_DIR = "/tmp/meet-ai-attachments";
|
|
43
|
+
var MAX_AGE_MS = 5 * 60 * 1000;
|
|
44
|
+
function cleanupOldAttachments() {
|
|
45
|
+
try {
|
|
46
|
+
const { readdirSync, statSync, unlinkSync } = __require("fs");
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
for (const entry of readdirSync(ATTACHMENTS_DIR)) {
|
|
49
|
+
try {
|
|
50
|
+
const filePath = `${ATTACHMENTS_DIR}/${entry}`;
|
|
51
|
+
const mtime = statSync(filePath).mtimeMs;
|
|
52
|
+
if (now - mtime > MAX_AGE_MS) {
|
|
53
|
+
unlinkSync(filePath);
|
|
54
|
+
}
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
40
59
|
function createClient(baseUrl, apiKey) {
|
|
41
60
|
function headers(extra) {
|
|
42
61
|
const h = { "Content-Type": "application/json", ...extra };
|
|
@@ -213,6 +232,45 @@ function createClient(baseUrl, apiKey) {
|
|
|
213
232
|
return res.text();
|
|
214
233
|
});
|
|
215
234
|
},
|
|
235
|
+
async sendTasks(roomId, payload) {
|
|
236
|
+
return withRetry(async () => {
|
|
237
|
+
const res = await fetch(`${baseUrl}/api/rooms/${roomId}/tasks`, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: headers(),
|
|
240
|
+
body: payload
|
|
241
|
+
});
|
|
242
|
+
if (!res.ok) {
|
|
243
|
+
const err = await res.json().catch(() => ({}));
|
|
244
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
245
|
+
}
|
|
246
|
+
return res.text();
|
|
247
|
+
});
|
|
248
|
+
},
|
|
249
|
+
async getMessageAttachments(roomId, messageId) {
|
|
250
|
+
const res = await fetch(`${baseUrl}/api/rooms/${roomId}/messages/${messageId}/attachments`, { headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined });
|
|
251
|
+
if (!res.ok) {
|
|
252
|
+
const err = await res.json().catch(() => ({}));
|
|
253
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
254
|
+
}
|
|
255
|
+
return res.json();
|
|
256
|
+
},
|
|
257
|
+
async downloadAttachment(attachmentId, filename) {
|
|
258
|
+
cleanupOldAttachments();
|
|
259
|
+
const res = await fetch(`${baseUrl}/api/attachments/${attachmentId}`, {
|
|
260
|
+
headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined
|
|
261
|
+
});
|
|
262
|
+
if (!res.ok) {
|
|
263
|
+
const err = await res.json().catch(() => ({}));
|
|
264
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
265
|
+
}
|
|
266
|
+
const { mkdirSync, writeFileSync } = await import("fs");
|
|
267
|
+
const dir = "/tmp/meet-ai-attachments";
|
|
268
|
+
mkdirSync(dir, { recursive: true });
|
|
269
|
+
const localPath = `${dir}/${attachmentId}-${filename}`;
|
|
270
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
271
|
+
writeFileSync(localPath, buffer);
|
|
272
|
+
return localPath;
|
|
273
|
+
},
|
|
216
274
|
async generateKey() {
|
|
217
275
|
const res = await fetch(`${baseUrl}/api/keys`, {
|
|
218
276
|
method: "POST",
|
|
@@ -285,6 +343,25 @@ var API_URL = process.env.MEET_AI_URL || "https://meet-ai.cc";
|
|
|
285
343
|
var API_KEY = process.env.MEET_AI_KEY;
|
|
286
344
|
var client = createClient(API_URL, API_KEY);
|
|
287
345
|
var [command, ...args] = process.argv.slice(2);
|
|
346
|
+
async function downloadMessageAttachments(roomId, messageId) {
|
|
347
|
+
try {
|
|
348
|
+
const attachments = await client.getMessageAttachments(roomId, messageId);
|
|
349
|
+
if (!attachments.length)
|
|
350
|
+
return [];
|
|
351
|
+
const paths = [];
|
|
352
|
+
for (const att of attachments) {
|
|
353
|
+
try {
|
|
354
|
+
const localPath = await client.downloadAttachment(att.id, att.filename);
|
|
355
|
+
paths.push(localPath);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
console.error(JSON.stringify({ event: "attachment_download_error", attachmentId: att.id, error: err instanceof Error ? err.message : String(err) }));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return paths;
|
|
361
|
+
} catch {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
288
365
|
function parseFlags(args2) {
|
|
289
366
|
const positional = [];
|
|
290
367
|
const flags = {};
|
|
@@ -334,11 +411,15 @@ switch (command) {
|
|
|
334
411
|
exclude: flags.exclude,
|
|
335
412
|
senderType: flags["sender-type"]
|
|
336
413
|
});
|
|
337
|
-
|
|
414
|
+
const enriched = await Promise.all(messages.map(async (msg) => {
|
|
415
|
+
const paths = await downloadMessageAttachments(roomId, msg.id);
|
|
416
|
+
return paths.length ? { ...msg, attachments: paths } : msg;
|
|
417
|
+
}));
|
|
418
|
+
console.log(JSON.stringify(enriched));
|
|
338
419
|
break;
|
|
339
420
|
}
|
|
340
421
|
case "listen": {
|
|
341
|
-
let routeToInbox = function(msg) {
|
|
422
|
+
let routeToInbox = function(msg, attachmentPaths) {
|
|
342
423
|
if (!inboxDir)
|
|
343
424
|
return;
|
|
344
425
|
const entry = {
|
|
@@ -347,6 +428,9 @@ switch (command) {
|
|
|
347
428
|
timestamp: new Date().toISOString(),
|
|
348
429
|
read: false
|
|
349
430
|
};
|
|
431
|
+
if (attachmentPaths?.length) {
|
|
432
|
+
entry.attachments = attachmentPaths;
|
|
433
|
+
}
|
|
350
434
|
const members = teamDir ? getTeamMembers(teamDir) : new Set;
|
|
351
435
|
const targets = resolveInboxTargets(msg.content, members);
|
|
352
436
|
if (targets) {
|
|
@@ -375,10 +459,20 @@ switch (command) {
|
|
|
375
459
|
const inboxDir = team ? `${process.env.HOME}/.claude/teams/${team}/inboxes` : null;
|
|
376
460
|
const defaultInboxPath = inboxDir && inbox ? `${inboxDir}/${inbox}.json` : null;
|
|
377
461
|
const teamDir = team ? `${process.env.HOME}/.claude/teams/${team}` : null;
|
|
378
|
-
const onMessage =
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
462
|
+
const onMessage = (msg) => {
|
|
463
|
+
if (msg.id && msg.room_id && msg.attachment_count > 0) {
|
|
464
|
+
downloadMessageAttachments(msg.room_id, msg.id).then((paths) => {
|
|
465
|
+
const output = paths.length ? { ...msg, attachments: paths } : msg;
|
|
466
|
+
console.log(JSON.stringify(output));
|
|
467
|
+
if (inboxDir)
|
|
468
|
+
routeToInbox(msg, paths);
|
|
469
|
+
});
|
|
470
|
+
} else {
|
|
471
|
+
console.log(JSON.stringify(msg));
|
|
472
|
+
if (inboxDir)
|
|
473
|
+
routeToInbox(msg);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
382
476
|
const ws = client.listen(roomId, { exclude: flags.exclude, senderType: flags["sender-type"], onMessage });
|
|
383
477
|
let idleCheckTimeout = null;
|
|
384
478
|
const idleNotified = new Set;
|
|
@@ -436,6 +530,54 @@ switch (command) {
|
|
|
436
530
|
console.log("Team info sent");
|
|
437
531
|
break;
|
|
438
532
|
}
|
|
533
|
+
case "send-tasks": {
|
|
534
|
+
const [stRoomId, stPayload] = args;
|
|
535
|
+
if (!stRoomId || !stPayload) {
|
|
536
|
+
console.error("Usage: cli send-tasks <roomId> '<json-payload>'");
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
JSON.parse(stPayload);
|
|
541
|
+
} catch {
|
|
542
|
+
console.error("Error: payload must be valid JSON");
|
|
543
|
+
process.exit(1);
|
|
544
|
+
}
|
|
545
|
+
await client.sendTasks(stRoomId, stPayload);
|
|
546
|
+
console.log("Tasks info sent");
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
case "download-attachment": {
|
|
550
|
+
const attachmentId = args[0];
|
|
551
|
+
if (!attachmentId) {
|
|
552
|
+
console.error("Usage: cli download-attachment <attachmentId>");
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
cleanupOldAttachments();
|
|
557
|
+
const res = await fetch(`${API_URL}/api/attachments/${attachmentId}`, {
|
|
558
|
+
headers: API_KEY ? { Authorization: `Bearer ${API_KEY}` } : undefined
|
|
559
|
+
});
|
|
560
|
+
if (!res.ok) {
|
|
561
|
+
const err = await res.json().catch(() => ({}));
|
|
562
|
+
console.error(err.error ?? `HTTP ${res.status}`);
|
|
563
|
+
process.exit(1);
|
|
564
|
+
}
|
|
565
|
+
const disposition = res.headers.get("Content-Disposition") || "";
|
|
566
|
+
const filenameMatch = disposition.match(/filename="(.+?)"/);
|
|
567
|
+
const filename = filenameMatch?.[1] || attachmentId;
|
|
568
|
+
const { mkdirSync: mkdirSync2, writeFileSync: writeFileSync2 } = await import("fs");
|
|
569
|
+
const dir = "/tmp/meet-ai-attachments";
|
|
570
|
+
mkdirSync2(dir, { recursive: true });
|
|
571
|
+
const localPath = `${dir}/${attachmentId}-${filename}`;
|
|
572
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
573
|
+
writeFileSync2(localPath, buffer);
|
|
574
|
+
console.log(localPath);
|
|
575
|
+
} catch (err) {
|
|
576
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
439
581
|
case "generate-key": {
|
|
440
582
|
const result = await client.generateKey();
|
|
441
583
|
console.log(`API Key: ${result.key}`);
|
|
@@ -465,6 +607,8 @@ Commands:
|
|
|
465
607
|
--sender-type <type> Filter by sender_type (human|agent)
|
|
466
608
|
--team <name> Write to Claude Code team inbox
|
|
467
609
|
--inbox <agent> Target agent inbox (requires --team)
|
|
610
|
+
download-attachment <attachmentId> Download an attachment to /tmp
|
|
468
611
|
send-team-info <roomId> '<json>' Send team info to a room
|
|
612
|
+
send-tasks <roomId> '<json>' Send tasks info to a room
|
|
469
613
|
generate-key Generate a new API key`);
|
|
470
614
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meet-ai/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "CLI for meet-ai chat rooms — create rooms, send messages, and stream via WebSocket",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chat",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"typecheck": "tsc --noEmit"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "
|
|
38
|
-
"typescript": "
|
|
37
|
+
"@types/node": "catalog:",
|
|
38
|
+
"typescript": "catalog:"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=22"
|