maxsimcli 2.1.1 → 2.2.0

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.
@@ -69,6 +69,8 @@ node_tty = __toESM(node_tty);
69
69
  let fs_promises = require("fs/promises");
70
70
  let node_stream = require("node:stream");
71
71
  let os = require("os");
72
+ let node_pty = require("node-pty");
73
+ node_pty = __toESM(node_pty);
72
74
 
73
75
  //#region ../../node_modules/.pnpm/depd@2.0.0/node_modules/depd/index.js
74
76
  var require_depd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
@@ -41393,6 +41395,162 @@ function watch(paths, options = {}) {
41393
41395
  return watcher;
41394
41396
  }
41395
41397
 
41398
+ //#endregion
41399
+ //#region src/terminal/session-store.ts
41400
+ const MAX_SCROLLBACK = 5e4;
41401
+ var SessionStore = class {
41402
+ scrollback = [];
41403
+ append(data) {
41404
+ this.scrollback.push(data);
41405
+ if (this.scrollback.length > MAX_SCROLLBACK) this.scrollback = this.scrollback.slice(-MAX_SCROLLBACK);
41406
+ }
41407
+ getAll() {
41408
+ return this.scrollback.join("");
41409
+ }
41410
+ clear() {
41411
+ this.scrollback = [];
41412
+ }
41413
+ };
41414
+
41415
+ //#endregion
41416
+ //#region src/terminal/pty-manager.ts
41417
+ const DISCONNECT_TIMEOUT_MS = 6e4;
41418
+ const STATUS_INTERVAL_MS = 1e3;
41419
+ const ACTIVE_THRESHOLD_MS = 2e3;
41420
+ var PtyManager = class PtyManager {
41421
+ static instance = null;
41422
+ session = null;
41423
+ connectedClients = /* @__PURE__ */ new Set();
41424
+ lastOutputTime = 0;
41425
+ statusInterval = null;
41426
+ static getInstance() {
41427
+ if (!PtyManager.instance) PtyManager.instance = new PtyManager();
41428
+ return PtyManager.instance;
41429
+ }
41430
+ spawn(opts) {
41431
+ if (this.session) this.kill();
41432
+ const shell = process.platform === "win32" ? "claude.cmd" : "claude";
41433
+ const args = [];
41434
+ if (opts.skipPermissions) args.push("--dangerously-skip-permissions");
41435
+ const proc = node_pty.spawn(shell, args, {
41436
+ name: "xterm-256color",
41437
+ cols: opts.cols ?? 120,
41438
+ rows: opts.rows ?? 30,
41439
+ cwd: opts.cwd,
41440
+ env: process.env
41441
+ });
41442
+ const store = new SessionStore();
41443
+ this.session = {
41444
+ process: proc,
41445
+ pid: proc.pid,
41446
+ startTime: Date.now(),
41447
+ cwd: opts.cwd,
41448
+ skipPermissions: opts.skipPermissions,
41449
+ disconnectTimer: null,
41450
+ store
41451
+ };
41452
+ this.lastOutputTime = Date.now();
41453
+ proc.onData((data) => {
41454
+ this.lastOutputTime = Date.now();
41455
+ store.append(data);
41456
+ this.broadcastToClients({
41457
+ type: "output",
41458
+ data
41459
+ });
41460
+ });
41461
+ proc.onExit(({ exitCode }) => {
41462
+ this.broadcastToClients({
41463
+ type: "exit",
41464
+ code: exitCode
41465
+ });
41466
+ this.stopStatusBroadcast();
41467
+ this.session = null;
41468
+ });
41469
+ this.broadcastToClients({
41470
+ type: "started",
41471
+ pid: proc.pid
41472
+ });
41473
+ this.startStatusBroadcast();
41474
+ }
41475
+ write(data) {
41476
+ if (this.session) this.session.process.write(data);
41477
+ }
41478
+ resize(cols, rows) {
41479
+ if (this.session) this.session.process.resize(cols, rows);
41480
+ }
41481
+ kill() {
41482
+ if (this.session) {
41483
+ this.stopStatusBroadcast();
41484
+ try {
41485
+ this.session.process.kill();
41486
+ } catch {}
41487
+ if (this.session.disconnectTimer) clearTimeout(this.session.disconnectTimer);
41488
+ this.session = null;
41489
+ }
41490
+ }
41491
+ getStatus() {
41492
+ if (!this.session) return null;
41493
+ return {
41494
+ pid: this.session.pid,
41495
+ uptime: Math.floor((Date.now() - this.session.startTime) / 1e3),
41496
+ cwd: this.session.cwd,
41497
+ memoryMB: Math.round(process.memoryUsage().rss / 1024 / 1024 * 10) / 10,
41498
+ isActive: Date.now() - this.lastOutputTime < ACTIVE_THRESHOLD_MS,
41499
+ skipPermissions: this.session.skipPermissions,
41500
+ alive: true
41501
+ };
41502
+ }
41503
+ addClient(ws) {
41504
+ this.connectedClients.add(ws);
41505
+ if (this.session?.disconnectTimer) {
41506
+ clearTimeout(this.session.disconnectTimer);
41507
+ this.session.disconnectTimer = null;
41508
+ }
41509
+ if (this.session) {
41510
+ const scrollback = this.session.store.getAll();
41511
+ if (scrollback) ws.send(JSON.stringify({
41512
+ type: "scrollback",
41513
+ data: scrollback
41514
+ }));
41515
+ const status = this.getStatus();
41516
+ if (status) ws.send(JSON.stringify({
41517
+ type: "status",
41518
+ ...status
41519
+ }));
41520
+ }
41521
+ }
41522
+ removeClient(ws) {
41523
+ this.connectedClients.delete(ws);
41524
+ if (this.connectedClients.size === 0 && this.session) this.session.disconnectTimer = setTimeout(() => {
41525
+ console.error("[pty] No clients connected for 60s, killing process");
41526
+ this.kill();
41527
+ }, DISCONNECT_TIMEOUT_MS);
41528
+ }
41529
+ isAlive() {
41530
+ return this.session !== null;
41531
+ }
41532
+ broadcastToClients(message) {
41533
+ const data = JSON.stringify(message);
41534
+ for (const client of this.connectedClients) if (client.readyState === import_websocket.default.OPEN) client.send(data);
41535
+ }
41536
+ startStatusBroadcast() {
41537
+ this.stopStatusBroadcast();
41538
+ this.statusInterval = setInterval(() => {
41539
+ const status = this.getStatus();
41540
+ if (status) this.broadcastToClients({
41541
+ type: "status",
41542
+ ...status
41543
+ });
41544
+ }, STATUS_INTERVAL_MS);
41545
+ }
41546
+ stopStatusBroadcast() {
41547
+ if (this.statusInterval) {
41548
+ clearInterval(this.statusInterval);
41549
+ this.statusInterval = null;
41550
+ }
41551
+ }
41552
+ };
41553
+
41396
41554
  //#endregion
41397
41555
  //#region src/server.ts
41398
41556
  const projectCwd = process.env.MAXSIM_PROJECT_CWD || process.cwd();
@@ -41894,10 +42052,48 @@ else app.get("/", (_req, res) => {
41894
42052
  });
41895
42053
  async function main() {
41896
42054
  const wss = createWSS();
42055
+ const terminalWss = new import_websocket_server.default({ noServer: true });
42056
+ const ptyManager = PtyManager.getInstance();
42057
+ terminalWss.on("connection", (ws) => {
42058
+ ptyManager.addClient(ws);
42059
+ ws.on("message", (raw) => {
42060
+ try {
42061
+ const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString());
42062
+ switch (msg.type) {
42063
+ case "input":
42064
+ ptyManager.write(msg.data);
42065
+ break;
42066
+ case "resize":
42067
+ ptyManager.resize(msg.cols, msg.rows);
42068
+ break;
42069
+ case "spawn":
42070
+ ptyManager.spawn({
42071
+ skipPermissions: !!msg.skipPermissions,
42072
+ cwd: projectCwd,
42073
+ cols: msg.cols,
42074
+ rows: msg.rows
42075
+ });
42076
+ break;
42077
+ case "kill":
42078
+ ptyManager.kill();
42079
+ break;
42080
+ }
42081
+ } catch {}
42082
+ });
42083
+ ws.on("close", () => {
42084
+ ptyManager.removeClient(ws);
42085
+ });
42086
+ ws.on("error", (err) => {
42087
+ console.error("[terminal-ws] Client error:", err.message);
42088
+ });
42089
+ });
41897
42090
  const server = (0, node_http.createServer)(app);
41898
42091
  server.on("upgrade", (req, socket, head) => {
41899
42092
  const url = req.url || "/";
41900
- if (url === "/api/ws" || url.startsWith("/api/ws?")) wss.handleUpgrade(req, socket, head, (ws) => {
42093
+ if (url === "/ws/terminal" || url.startsWith("/ws/terminal?")) terminalWss.handleUpgrade(req, socket, head, (ws) => {
42094
+ terminalWss.emit("connection", ws, req);
42095
+ });
42096
+ else if (url === "/api/ws" || url.startsWith("/api/ws?")) wss.handleUpgrade(req, socket, head, (ws) => {
41901
42097
  wss.emit("connection", ws, req);
41902
42098
  });
41903
42099
  else socket.destroy();
@@ -41916,7 +42112,9 @@ async function main() {
41916
42112
  });
41917
42113
  function shutdown() {
41918
42114
  console.error("\n[server] Shutting down...");
42115
+ ptyManager.kill();
41919
42116
  if (watcher) watcher.close().catch(() => {});
42117
+ terminalWss.close(() => {});
41920
42118
  wss.close(() => {
41921
42119
  server.close(() => {
41922
42120
  process.exit(0);
@@ -41929,6 +42127,9 @@ async function main() {
41929
42127
  }
41930
42128
  process.on("SIGINT", shutdown);
41931
42129
  process.on("SIGTERM", shutdown);
42130
+ process.on("exit", () => {
42131
+ ptyManager.kill();
42132
+ });
41932
42133
  }
41933
42134
  main().catch((err) => {
41934
42135
  console.error("[server] Fatal error:", err);
package/dist/install.cjs CHANGED
@@ -7548,6 +7548,20 @@ async function promptLocation(runtimes) {
7548
7548
  }) === "global";
7549
7549
  }
7550
7550
  /**
7551
+ * Prompt whether to enable Agent Teams (Claude only, experimental feature)
7552
+ */
7553
+ async function promptAgentTeams() {
7554
+ console.log();
7555
+ console.log(chalk.cyan(" Agent Teams") + chalk.dim(" (experimental)"));
7556
+ console.log(chalk.dim(" Coordinate multiple Claude Code instances working in parallel."));
7557
+ console.log(chalk.dim(" Enables CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS in settings.json."));
7558
+ console.log();
7559
+ return dist_default$1({
7560
+ message: "Enable Agent Teams?",
7561
+ default: false
7562
+ });
7563
+ }
7564
+ /**
7551
7565
  * Install MAXSIM for all selected runtimes
7552
7566
  */
7553
7567
  async function installAllRuntimes(runtimes, isGlobal, isInteractive) {
@@ -7560,8 +7574,15 @@ async function installAllRuntimes(runtimes, isGlobal, isInteractive) {
7560
7574
  const primaryStatuslineResult = results.find((r) => statuslineRuntimes.includes(r.runtime));
7561
7575
  let shouldInstallStatusline = false;
7562
7576
  if (primaryStatuslineResult && primaryStatuslineResult.settings) shouldInstallStatusline = await handleStatusline(primaryStatuslineResult.settings, isInteractive);
7577
+ let enableAgentTeams = false;
7578
+ if (isInteractive && runtimes.includes("claude")) enableAgentTeams = await promptAgentTeams();
7563
7579
  for (const result of results) {
7564
7580
  const useStatusline = statuslineRuntimes.includes(result.runtime) && shouldInstallStatusline;
7581
+ if (result.runtime === "claude" && enableAgentTeams && result.settings) {
7582
+ const env = result.settings.env ?? {};
7583
+ env["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"] = "1";
7584
+ result.settings.env = env;
7585
+ }
7565
7586
  finishInstall(result.settingsPath, result.settings, result.statuslineCommand, useStatusline, result.runtime, isGlobal);
7566
7587
  }
7567
7588
  }