@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.
Files changed (2) hide show
  1. package/dist/index.js +150 -6
  2. 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
- console.log(JSON.stringify(messages));
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 = inboxDir ? (msg) => {
379
- console.log(JSON.stringify(msg));
380
- routeToInbox(msg);
381
- } : undefined;
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.8",
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": "25.0.10",
38
- "typescript": "5.9.3"
37
+ "@types/node": "catalog:",
38
+ "typescript": "catalog:"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=22"