maxsimcli 2.1.0 → 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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/assets/CHANGELOG.md +21 -0
- package/dist/assets/dashboard/client/assets/index-BjfrCU-v.css +1 -0
- package/dist/assets/dashboard/client/assets/{index-LGK5h_Pf.js → index-I1VQFVyf.js} +21 -21
- package/dist/assets/dashboard/client/index.html +2 -2
- package/dist/assets/dashboard/server.js +202 -1
- package/dist/install.cjs +48 -43
- package/dist/install.cjs.map +1 -1
- package/dist/install.js +62 -45
- package/dist/install.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/dashboard/client/assets/index-G0lFZow6.css +0 -1
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
|
11
11
|
rel="stylesheet"
|
|
12
12
|
/>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-I1VQFVyf.js"></script>
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BjfrCU-v.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -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 === "/
|
|
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
|
}
|
|
@@ -7599,57 +7620,41 @@ const subcommand = args.find((a) => !a.startsWith("-"));
|
|
|
7599
7620
|
console.log(chalk.blue("Starting dashboard..."));
|
|
7600
7621
|
console.log(chalk.gray(` Project: ${projectCwd}`));
|
|
7601
7622
|
console.log(chalk.gray(` Server: ${serverPath}\n`));
|
|
7602
|
-
|
|
7623
|
+
spawnDash(process.execPath, [serverPath], {
|
|
7603
7624
|
cwd: dashboardDir,
|
|
7604
7625
|
detached: true,
|
|
7605
|
-
stdio:
|
|
7606
|
-
"ignore",
|
|
7607
|
-
"ignore",
|
|
7608
|
-
"pipe"
|
|
7609
|
-
],
|
|
7626
|
+
stdio: "ignore",
|
|
7610
7627
|
env: {
|
|
7611
7628
|
...process.env,
|
|
7612
7629
|
MAXSIM_PROJECT_CWD: projectCwd,
|
|
7613
7630
|
NODE_ENV: "production"
|
|
7614
7631
|
}
|
|
7615
|
-
});
|
|
7616
|
-
const
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
}
|
|
7639
|
-
setTimeout(() => done(null), 2e4);
|
|
7640
|
-
});
|
|
7641
|
-
if (result.url) {
|
|
7642
|
-
child.unref();
|
|
7643
|
-
if (child.stderr) child.stderr.destroy();
|
|
7644
|
-
console.log(chalk.green(` Dashboard ready at ${result.url}`));
|
|
7645
|
-
} else {
|
|
7646
|
-
if (result.stderr.trim()) {
|
|
7647
|
-
console.log(chalk.red("\n Dashboard failed to start:\n"));
|
|
7648
|
-
console.log(chalk.gray(" " + result.stderr.trim().split("\n").join("\n ")));
|
|
7649
|
-
} else console.log(chalk.yellow("\n Dashboard did not respond after 20s. Run with DEBUG=1 for details."));
|
|
7650
|
-
child.unref();
|
|
7651
|
-
if (child.stderr) child.stderr.destroy();
|
|
7632
|
+
}).unref();
|
|
7633
|
+
const POLL_INTERVAL_MS = 500;
|
|
7634
|
+
const POLL_TIMEOUT_MS = 2e4;
|
|
7635
|
+
const HEALTH_TIMEOUT_MS = 1e3;
|
|
7636
|
+
const DEFAULT_PORT = 3333;
|
|
7637
|
+
const PORT_RANGE_END = 3343;
|
|
7638
|
+
let foundUrl = null;
|
|
7639
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
7640
|
+
while (Date.now() < deadline) {
|
|
7641
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
7642
|
+
for (let p = DEFAULT_PORT; p <= PORT_RANGE_END; p++) try {
|
|
7643
|
+
const controller = new AbortController();
|
|
7644
|
+
const timer = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
7645
|
+
const res = await fetch(`http://localhost:${p}/api/health`, { signal: controller.signal });
|
|
7646
|
+
clearTimeout(timer);
|
|
7647
|
+
if (res.ok) {
|
|
7648
|
+
if ((await res.json()).status === "ok") {
|
|
7649
|
+
foundUrl = `http://localhost:${p}`;
|
|
7650
|
+
break;
|
|
7651
|
+
}
|
|
7652
|
+
}
|
|
7653
|
+
} catch {}
|
|
7654
|
+
if (foundUrl) break;
|
|
7652
7655
|
}
|
|
7656
|
+
if (foundUrl) console.log(chalk.green(` Dashboard ready at ${foundUrl}`));
|
|
7657
|
+
else console.log(chalk.yellow("\n Dashboard did not respond after 20s. The server may still be starting — check http://localhost:3333"));
|
|
7653
7658
|
process.exit(0);
|
|
7654
7659
|
}
|
|
7655
7660
|
if (hasGlobal && hasLocal) {
|