@meet-ai/cli 0.0.27 → 0.0.28-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +234 -193
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14839,6 +14839,15 @@ function createClient(baseUrl, apiKey) {
14839
14839
  const err2 = await res.json().catch(() => ({}));
14840
14840
  throw new Error(err2.error ?? `HTTP ${res.status}`);
14841
14841
  }
14842
+ },
14843
+ async sendTerminalData(roomId, data) {
14844
+ try {
14845
+ await fetch(`${baseUrl}/api/rooms/${roomId}/terminal`, {
14846
+ method: "POST",
14847
+ headers: headers(),
14848
+ body: JSON.stringify({ data })
14849
+ });
14850
+ } catch {}
14842
14851
  }
14843
14852
  };
14844
14853
  }
@@ -15345,6 +15354,190 @@ var init_inbox_router = __esm(() => {
15345
15354
  IDLE_THRESHOLD_MS = 5 * 60 * 1000;
15346
15355
  });
15347
15356
 
15357
+ // src/lib/tmux-client.ts
15358
+ import { execFileSync, execFile as execFileCb } from "node:child_process";
15359
+ function validateSessionName(name) {
15360
+ if (!SESSION_NAME_RE.test(name) || name.length > 256) {
15361
+ throw new Error(`Invalid tmux session name: ${name}`);
15362
+ }
15363
+ }
15364
+ function parseVersion(version2) {
15365
+ if (!version2)
15366
+ return [0, 0];
15367
+ const match = version2.match(/(\d+)\.(\d+)/);
15368
+ if (!match)
15369
+ return [0, 0];
15370
+ return [Number(match[1]), Number(match[2])];
15371
+ }
15372
+
15373
+ class TmuxClient {
15374
+ server;
15375
+ scrollback;
15376
+ constructor(opts) {
15377
+ this.server = opts.server;
15378
+ this.scrollback = opts.scrollback;
15379
+ }
15380
+ checkAvailability() {
15381
+ try {
15382
+ const result = execFileSync("tmux", ["-V"], {
15383
+ encoding: "utf8",
15384
+ timeout: 5000,
15385
+ stdio: ["pipe", "pipe", "pipe"]
15386
+ }).trim();
15387
+ return { available: true, version: result };
15388
+ } catch (error48) {
15389
+ return {
15390
+ available: false,
15391
+ version: null,
15392
+ error: error48 instanceof Error ? error48.message : String(error48)
15393
+ };
15394
+ }
15395
+ }
15396
+ newSession(name, commandArgs, env) {
15397
+ validateSessionName(name);
15398
+ if (env) {
15399
+ for (const [key, value] of Object.entries(env)) {
15400
+ this.exec(["-L", this.server, "set-environment", "-g", key, value]);
15401
+ }
15402
+ }
15403
+ const args = [
15404
+ "-L",
15405
+ this.server,
15406
+ "new-session",
15407
+ "-d",
15408
+ "-s",
15409
+ name,
15410
+ "-x",
15411
+ "120",
15412
+ "-y",
15413
+ "40",
15414
+ "--",
15415
+ ...commandArgs
15416
+ ];
15417
+ const result = this.exec(args);
15418
+ if (env) {
15419
+ for (const key of Object.keys(env)) {
15420
+ this.exec(["-L", this.server, "set-environment", "-g", "-u", key]);
15421
+ }
15422
+ }
15423
+ if (!result.ok)
15424
+ return result;
15425
+ this.exec(["-L", this.server, "set-option", "-t", name, "remain-on-exit", "on"]);
15426
+ this.exec(["-L", this.server, "set-option", "-t", name, "history-limit", String(this.scrollback)]);
15427
+ return result;
15428
+ }
15429
+ killSession(name) {
15430
+ validateSessionName(name);
15431
+ return this.exec(["-L", this.server, "kill-session", "-t", name]);
15432
+ }
15433
+ listSessions() {
15434
+ const result = this.exec([
15435
+ "-L",
15436
+ this.server,
15437
+ "list-sessions",
15438
+ "-F",
15439
+ "#{session_name}\t#{session_activity}\t#{pane_dead}"
15440
+ ]);
15441
+ if (!result.ok)
15442
+ return [];
15443
+ return result.output.trim().split(`
15444
+ `).filter((line) => line.trim()).map((line) => {
15445
+ const [name, activity, dead] = line.split("\t");
15446
+ return {
15447
+ name: name ?? "",
15448
+ activity: Number(activity) || 0,
15449
+ alive: dead !== "1"
15450
+ };
15451
+ });
15452
+ }
15453
+ listPanes(sessionName) {
15454
+ validateSessionName(sessionName);
15455
+ const result = this.exec([
15456
+ "-L",
15457
+ this.server,
15458
+ "list-panes",
15459
+ "-t",
15460
+ sessionName,
15461
+ "-F",
15462
+ "#{pane_index}\t#{pane_title}\t#{pane_active}"
15463
+ ]);
15464
+ if (!result.ok)
15465
+ return [];
15466
+ return result.output.trim().split(`
15467
+ `).filter((line) => line.trim()).map((line) => {
15468
+ const [index, title, active] = line.split("\t");
15469
+ return {
15470
+ index: Number(index),
15471
+ title: title ?? "",
15472
+ active: active === "1"
15473
+ };
15474
+ });
15475
+ }
15476
+ capturePane(target, lines) {
15477
+ const dotIdx = target.indexOf(".");
15478
+ const sessionPart = dotIdx === -1 ? target : target.slice(0, dotIdx);
15479
+ const panePart = dotIdx === -1 ? null : target.slice(dotIdx + 1);
15480
+ validateSessionName(sessionPart);
15481
+ if (panePart !== null && !/^\d+$/.test(panePart)) {
15482
+ throw new Error(`Invalid pane index: ${panePart}`);
15483
+ }
15484
+ const args = [
15485
+ "-L",
15486
+ this.server,
15487
+ "capture-pane",
15488
+ "-t",
15489
+ target,
15490
+ "-p",
15491
+ "-e",
15492
+ "-S",
15493
+ `-${lines}`
15494
+ ];
15495
+ return new Promise((resolve2) => {
15496
+ execFileCb("tmux", args, { encoding: "utf8", timeout: 5000 }, (error48, stdout) => {
15497
+ if (error48) {
15498
+ resolve2([]);
15499
+ return;
15500
+ }
15501
+ const result = stdout.split(`
15502
+ `);
15503
+ while (result.length > 0 && result[result.length - 1] === "") {
15504
+ result.pop();
15505
+ }
15506
+ resolve2(result);
15507
+ });
15508
+ });
15509
+ }
15510
+ attachSession(name) {
15511
+ validateSessionName(name);
15512
+ try {
15513
+ execFileSync("tmux", ["-L", this.server, "attach", "-t", name], {
15514
+ stdio: "inherit",
15515
+ timeout: 0
15516
+ });
15517
+ return 0;
15518
+ } catch {
15519
+ return 1;
15520
+ }
15521
+ }
15522
+ exec(args) {
15523
+ try {
15524
+ const output = execFileSync("tmux", args, {
15525
+ encoding: "utf8",
15526
+ timeout: 5000,
15527
+ stdio: ["pipe", "pipe", "pipe"]
15528
+ });
15529
+ return { ok: true, output: output ?? "" };
15530
+ } catch (error48) {
15531
+ const message = error48 instanceof Error ? error48.message : String(error48);
15532
+ return { ok: false, error: message };
15533
+ }
15534
+ }
15535
+ }
15536
+ var SESSION_NAME_RE;
15537
+ var init_tmux_client = __esm(() => {
15538
+ SESSION_NAME_RE = /^[a-zA-Z0-9_-]+$/;
15539
+ });
15540
+
15348
15541
  // src/commands/listen/usecase.ts
15349
15542
  function routeToInbox(msg, inboxDir, defaultInboxPath, teamDir, stdinPane, attachmentPaths) {
15350
15543
  const entry = {
@@ -15372,7 +15565,33 @@ function listen(client, input) {
15372
15565
  const inboxDir = team ? `${process.env.HOME}/.claude/teams/${team}/inboxes` : null;
15373
15566
  const defaultInboxPath = inboxDir && inbox ? `${inboxDir}/${inbox}.json` : null;
15374
15567
  const teamDir = team ? `${process.env.HOME}/.claude/teams/${team}` : null;
15568
+ const tmuxClient = new TmuxClient({ server: "meet-ai", scrollback: 50000 });
15569
+ let terminalInterval = null;
15375
15570
  const onMessage = (msg) => {
15571
+ if (msg.type === "terminal_subscribe") {
15572
+ const paneId = msg.paneId;
15573
+ if (!paneId)
15574
+ return;
15575
+ if (terminalInterval) {
15576
+ clearInterval(terminalInterval);
15577
+ terminalInterval = null;
15578
+ }
15579
+ terminalInterval = setInterval(async () => {
15580
+ try {
15581
+ const lines = await tmuxClient.capturePane(paneId, 500);
15582
+ await client.sendTerminalData(roomId, lines.join(`
15583
+ `));
15584
+ } catch {}
15585
+ }, 200);
15586
+ return;
15587
+ }
15588
+ if (msg.type === "terminal_unsubscribe") {
15589
+ if (terminalInterval) {
15590
+ clearInterval(terminalInterval);
15591
+ terminalInterval = null;
15592
+ }
15593
+ return;
15594
+ }
15376
15595
  if (msg.id && msg.room_id && msg.attachment_count > 0) {
15377
15596
  downloadMessageAttachments(client, msg.room_id, msg.id).then((paths) => {
15378
15597
  const output = paths.length ? { ...msg, attachments: paths } : msg;
@@ -15413,6 +15632,10 @@ function listen(client, input) {
15413
15632
  function shutdown() {
15414
15633
  if (idleCheckTimeout)
15415
15634
  clearTimeout(idleCheckTimeout);
15635
+ if (terminalInterval) {
15636
+ clearInterval(terminalInterval);
15637
+ terminalInterval = null;
15638
+ }
15416
15639
  if (ws.readyState === WebSocket.OPEN) {
15417
15640
  ws.close(1000, "client shutdown");
15418
15641
  }
@@ -15425,6 +15648,7 @@ function listen(client, input) {
15425
15648
  var init_usecase6 = __esm(() => {
15426
15649
  init_schema6();
15427
15650
  init_inbox_router();
15651
+ init_tmux_client();
15428
15652
  });
15429
15653
 
15430
15654
  // src/commands/listen/command.ts
@@ -16613,7 +16837,7 @@ function formatPermissionRequest(toolName, toolInput) {
16613
16837
  if (entries.length > 0) {
16614
16838
  for (const [key, value] of entries) {
16615
16839
  const valueStr = typeof value === "string" ? value : JSON.stringify(value);
16616
- const truncated = valueStr.length > 200 ? valueStr.slice(0, 200) + "..." : valueStr;
16840
+ const truncated = valueStr.length > 200 ? `${valueStr.slice(0, 200)}...` : valueStr;
16617
16841
  text += `
16618
16842
  **${key}:** \`${truncated}\``;
16619
16843
  }
@@ -16861,7 +17085,7 @@ function mergeHooks(existing) {
16861
17085
  }
16862
17086
  for (const newMatcher of matchers) {
16863
17087
  const existingIdx = result[event].findIndex((m) => m.matcher === newMatcher.matcher && isMeetAiHook(m));
16864
- if (existingIdx >= 0) {
17088
+ if (existingIdx !== -1) {
16865
17089
  result[event][existingIdx] = newMatcher;
16866
17090
  added.push(`updated ${event} [${newMatcher.matcher}]`);
16867
17091
  } else {
@@ -16877,8 +17101,9 @@ function mergeHooks(existing) {
16877
17101
  return true;
16878
17102
  return newMatchers.some((nm) => nm.matcher === m.matcher);
16879
17103
  });
16880
- if (result[event].length === 0)
16881
- delete result[event];
17104
+ if (result[event].length === 0) {
17105
+ Reflect.deleteProperty(result, event);
17106
+ }
16882
17107
  }
16883
17108
  return { merged: result, added };
16884
17109
  }
@@ -16921,7 +17146,7 @@ async function setupHooks(options) {
16921
17146
  delete updated.hooks;
16922
17147
  }
16923
17148
  await mkdir(dirname2(settingsPath), { recursive: true });
16924
- await writeFile(settingsPath, JSON.stringify(updated, null, 2) + `
17149
+ await writeFile(settingsPath, `${JSON.stringify(updated, null, 2)}
16925
17150
  `);
16926
17151
  for (const r of removed) {
16927
17152
  console.log(import_picocolors2.default.yellow(` ${r}`));
@@ -16938,7 +17163,7 @@ async function setupHooks(options) {
16938
17163
  }
16939
17164
  const updated = { ...settings, hooks: merged };
16940
17165
  await mkdir(dirname2(settingsPath), { recursive: true });
16941
- await writeFile(settingsPath, JSON.stringify(updated, null, 2) + `
17166
+ await writeFile(settingsPath, `${JSON.stringify(updated, null, 2)}
16942
17167
  `);
16943
17168
  for (const a of added) {
16944
17169
  console.log(import_picocolors2.default.green(` ${a}`));
@@ -20856,7 +21081,7 @@ var init_wrap_ansi = __esm(() => {
20856
21081
 
20857
21082
  // ../../node_modules/.bun/terminal-size@4.0.1/node_modules/terminal-size/index.js
20858
21083
  import process4 from "node:process";
20859
- import { execFileSync } from "node:child_process";
21084
+ import { execFileSync as execFileSync2 } from "node:child_process";
20860
21085
  import fs from "node:fs";
20861
21086
  import tty from "node:tty";
20862
21087
  function terminalSize() {
@@ -20882,7 +21107,7 @@ function terminalSize() {
20882
21107
  }
20883
21108
  return devTty() ?? tput() ?? resize() ?? fallback;
20884
21109
  }
20885
- var defaultColumns = 80, defaultRows = 24, exec2 = (command, arguments_, { shell, env: env2 } = {}) => execFileSync(command, arguments_, {
21110
+ var defaultColumns = 80, defaultRows = 24, exec2 = (command, arguments_, { shell, env: env2 } = {}) => execFileSync2(command, arguments_, {
20886
21111
  encoding: "utf8",
20887
21112
  stdio: ["ignore", "pipe", "ignore"],
20888
21113
  timeout: 500,
@@ -54640,190 +54865,6 @@ var init_build2 = __esm(async () => {
54640
54865
  ]);
54641
54866
  });
54642
54867
 
54643
- // src/lib/tmux-client.ts
54644
- import { execFileSync as execFileSync2, execFile as execFileCb } from "node:child_process";
54645
- function validateSessionName(name) {
54646
- if (!SESSION_NAME_RE.test(name) || name.length > 256) {
54647
- throw new Error(`Invalid tmux session name: ${name}`);
54648
- }
54649
- }
54650
- function parseVersion(version2) {
54651
- if (!version2)
54652
- return [0, 0];
54653
- const match = version2.match(/(\d+)\.(\d+)/);
54654
- if (!match)
54655
- return [0, 0];
54656
- return [Number(match[1]), Number(match[2])];
54657
- }
54658
-
54659
- class TmuxClient {
54660
- server;
54661
- scrollback;
54662
- constructor(opts) {
54663
- this.server = opts.server;
54664
- this.scrollback = opts.scrollback;
54665
- }
54666
- checkAvailability() {
54667
- try {
54668
- const result = execFileSync2("tmux", ["-V"], {
54669
- encoding: "utf8",
54670
- timeout: 5000,
54671
- stdio: ["pipe", "pipe", "pipe"]
54672
- }).trim();
54673
- return { available: true, version: result };
54674
- } catch (error48) {
54675
- return {
54676
- available: false,
54677
- version: null,
54678
- error: error48 instanceof Error ? error48.message : String(error48)
54679
- };
54680
- }
54681
- }
54682
- newSession(name, commandArgs, env3) {
54683
- validateSessionName(name);
54684
- if (env3) {
54685
- for (const [key, value] of Object.entries(env3)) {
54686
- this.exec(["-L", this.server, "set-environment", "-g", key, value]);
54687
- }
54688
- }
54689
- const args = [
54690
- "-L",
54691
- this.server,
54692
- "new-session",
54693
- "-d",
54694
- "-s",
54695
- name,
54696
- "-x",
54697
- "120",
54698
- "-y",
54699
- "40",
54700
- "--",
54701
- ...commandArgs
54702
- ];
54703
- const result = this.exec(args);
54704
- if (env3) {
54705
- for (const key of Object.keys(env3)) {
54706
- this.exec(["-L", this.server, "set-environment", "-g", "-u", key]);
54707
- }
54708
- }
54709
- if (!result.ok)
54710
- return result;
54711
- this.exec(["-L", this.server, "set-option", "-t", name, "remain-on-exit", "on"]);
54712
- this.exec(["-L", this.server, "set-option", "-t", name, "history-limit", String(this.scrollback)]);
54713
- return result;
54714
- }
54715
- killSession(name) {
54716
- validateSessionName(name);
54717
- return this.exec(["-L", this.server, "kill-session", "-t", name]);
54718
- }
54719
- listSessions() {
54720
- const result = this.exec([
54721
- "-L",
54722
- this.server,
54723
- "list-sessions",
54724
- "-F",
54725
- "#{session_name}\t#{session_activity}\t#{pane_dead}"
54726
- ]);
54727
- if (!result.ok)
54728
- return [];
54729
- return result.output.trim().split(`
54730
- `).filter((line) => line.trim()).map((line) => {
54731
- const [name, activity, dead] = line.split("\t");
54732
- return {
54733
- name: name ?? "",
54734
- activity: Number(activity) || 0,
54735
- alive: dead !== "1"
54736
- };
54737
- });
54738
- }
54739
- listPanes(sessionName) {
54740
- validateSessionName(sessionName);
54741
- const result = this.exec([
54742
- "-L",
54743
- this.server,
54744
- "list-panes",
54745
- "-t",
54746
- sessionName,
54747
- "-F",
54748
- "#{pane_index}\t#{pane_title}\t#{pane_active}"
54749
- ]);
54750
- if (!result.ok)
54751
- return [];
54752
- return result.output.trim().split(`
54753
- `).filter((line) => line.trim()).map((line) => {
54754
- const [index, title, active] = line.split("\t");
54755
- return {
54756
- index: Number(index),
54757
- title: title ?? "",
54758
- active: active === "1"
54759
- };
54760
- });
54761
- }
54762
- capturePane(target, lines) {
54763
- const dotIdx = target.indexOf(".");
54764
- const sessionPart = dotIdx === -1 ? target : target.slice(0, dotIdx);
54765
- const panePart = dotIdx === -1 ? null : target.slice(dotIdx + 1);
54766
- validateSessionName(sessionPart);
54767
- if (panePart !== null && !/^\d+$/.test(panePart)) {
54768
- throw new Error(`Invalid pane index: ${panePart}`);
54769
- }
54770
- const args = [
54771
- "-L",
54772
- this.server,
54773
- "capture-pane",
54774
- "-t",
54775
- target,
54776
- "-p",
54777
- "-e",
54778
- "-S",
54779
- `-${lines}`
54780
- ];
54781
- return new Promise((resolve4) => {
54782
- execFileCb("tmux", args, { encoding: "utf8", timeout: 5000 }, (error48, stdout) => {
54783
- if (error48) {
54784
- resolve4([]);
54785
- return;
54786
- }
54787
- const result = stdout.split(`
54788
- `);
54789
- while (result.length > 0 && result[result.length - 1] === "") {
54790
- result.pop();
54791
- }
54792
- resolve4(result);
54793
- });
54794
- });
54795
- }
54796
- attachSession(name) {
54797
- validateSessionName(name);
54798
- try {
54799
- execFileSync2("tmux", ["-L", this.server, "attach", "-t", name], {
54800
- stdio: "inherit",
54801
- timeout: 0
54802
- });
54803
- return 0;
54804
- } catch {
54805
- return 1;
54806
- }
54807
- }
54808
- exec(args) {
54809
- try {
54810
- const output = execFileSync2("tmux", args, {
54811
- encoding: "utf8",
54812
- timeout: 5000,
54813
- stdio: ["pipe", "pipe", "pipe"]
54814
- });
54815
- return { ok: true, output: output ?? "" };
54816
- } catch (error48) {
54817
- const message = error48 instanceof Error ? error48.message : String(error48);
54818
- return { ok: false, error: message };
54819
- }
54820
- }
54821
- }
54822
- var SESSION_NAME_RE;
54823
- var init_tmux_client = __esm(() => {
54824
- SESSION_NAME_RE = /^[a-zA-Z0-9_-]+$/;
54825
- });
54826
-
54827
54868
  // src/lib/process-manager.ts
54828
54869
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync, lstatSync } from "node:fs";
54829
54870
  import { homedir as homedir4 } from "node:os";
@@ -56314,7 +56355,7 @@ init_output();
56314
56355
  var main = defineCommand({
56315
56356
  meta: {
56316
56357
  name: "meet-ai",
56317
- version: "0.0.27",
56358
+ version: "0.0.28-beta.1",
56318
56359
  description: "CLI for meet-ai chat rooms — create rooms, send messages, and stream via WebSocket"
56319
56360
  },
56320
56361
  args: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meet-ai/cli",
3
- "version": "0.0.27",
3
+ "version": "0.0.28-beta.1",
4
4
  "description": "CLI for meet-ai chat rooms — create rooms, send messages, and stream via WebSocket",
5
5
  "keywords": [
6
6
  "chat",