chatroom-cli 1.3.0 → 1.4.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.
Files changed (2) hide show
  1. package/dist/index.js +156 -92
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10280,10 +10280,6 @@ function ensureMachineRegistered() {
10280
10280
  harnessVersions: config.harnessVersions
10281
10281
  };
10282
10282
  }
10283
- function getMachineId() {
10284
- const config = loadMachineConfig();
10285
- return config?.machineId ?? null;
10286
- }
10287
10283
  var CHATROOM_DIR2, MACHINE_FILE = "machine.json";
10288
10284
  var init_storage2 = __esm(() => {
10289
10285
  init_detection();
@@ -10756,6 +10752,9 @@ var init_pi_agent_service = __esm(() => {
10756
10752
  const pid = childProcess.pid;
10757
10753
  const context = options.context;
10758
10754
  const entry = this.registerProcess(pid, context);
10755
+ const roleTag = context.role ?? "unknown";
10756
+ const chatroomSuffix = context.chatroomId ? `@${context.chatroomId.slice(-6)}` : "";
10757
+ const logPrefix = `[pi:${roleTag}${chatroomSuffix}`;
10759
10758
  const outputCallbacks = [];
10760
10759
  if (childProcess.stdout) {
10761
10760
  const reader = new PiRpcReader(childProcess.stdout);
@@ -10767,7 +10766,7 @@ var init_pi_agent_service = __esm(() => {
10767
10766
  for (const line of textBuffer.split(`
10768
10767
  `)) {
10769
10768
  if (line)
10770
- process.stdout.write(`[pi text] ${line}
10769
+ process.stdout.write(`${logPrefix} text] ${line}
10771
10770
  `);
10772
10771
  }
10773
10772
  textBuffer = "";
@@ -10778,7 +10777,7 @@ var init_pi_agent_service = __esm(() => {
10778
10777
  for (const line of thinkingBuffer.split(`
10779
10778
  `)) {
10780
10779
  if (line)
10781
- process.stdout.write(`[pi thinking] ${line}
10780
+ process.stdout.write(`${logPrefix} thinking] ${line}
10782
10781
  `);
10783
10782
  }
10784
10783
  thinkingBuffer = "";
@@ -10811,15 +10810,38 @@ var init_pi_agent_service = __esm(() => {
10811
10810
  reader.onAgentEnd(() => {
10812
10811
  flushText();
10813
10812
  flushThinking();
10814
- process.stdout.write(`[pi agent_end]
10813
+ process.stdout.write(`${logPrefix} agent_end]
10815
10814
  `);
10816
10815
  });
10817
10816
  reader.onToolCall((name) => {
10818
10817
  flushText();
10819
10818
  flushThinking();
10820
- process.stdout.write(`[pi tool: ${name}]
10819
+ process.stdout.write(`${logPrefix} tool: ${name}]
10821
10820
  `);
10822
10821
  });
10822
+ if (childProcess.stderr) {
10823
+ childProcess.stderr.pipe(process.stderr, { end: false });
10824
+ childProcess.stderr.on("data", () => {
10825
+ entry.lastOutputAt = Date.now();
10826
+ for (const cb of outputCallbacks)
10827
+ cb();
10828
+ });
10829
+ }
10830
+ return {
10831
+ pid,
10832
+ onExit: (cb) => {
10833
+ childProcess.on("exit", (code2, signal) => {
10834
+ this.deleteProcess(pid);
10835
+ cb({ code: code2, signal, context });
10836
+ });
10837
+ },
10838
+ onOutput: (cb) => {
10839
+ outputCallbacks.push(cb);
10840
+ },
10841
+ onAgentEnd: (cb) => {
10842
+ reader.onAgentEnd(cb);
10843
+ }
10844
+ };
10823
10845
  }
10824
10846
  if (childProcess.stderr) {
10825
10847
  childProcess.stderr.pipe(process.stderr, { end: false });
@@ -11298,6 +11320,15 @@ async function registerAgent(chatroomId, options, deps) {
11298
11320
  harnessVersions: machineInfo.harnessVersions,
11299
11321
  availableModels
11300
11322
  });
11323
+ try {
11324
+ await d.backend.mutation(api.machines.recordAgentRegistered, {
11325
+ sessionId,
11326
+ chatroomId,
11327
+ role,
11328
+ agentType: "remote",
11329
+ machineId: machineInfo.machineId
11330
+ });
11331
+ } catch {}
11301
11332
  console.log(`✅ Registered as remote agent for role "${role}"`);
11302
11333
  console.log(` Machine: ${machineInfo.hostname} (${machineInfo.machineId})`);
11303
11334
  console.log(` Working directory: ${process.cwd()}`);
@@ -13912,6 +13943,13 @@ async function executeStartAgent(ctx, args) {
13912
13943
  intentional: wasIntentional
13913
13944
  });
13914
13945
  });
13946
+ if (spawnResult.onAgentEnd) {
13947
+ spawnResult.onAgentEnd(() => {
13948
+ try {
13949
+ ctx.deps.processes.kill(-pid, "SIGTERM");
13950
+ } catch {}
13951
+ });
13952
+ }
13915
13953
  let lastReportedTokenAt = 0;
13916
13954
  spawnResult.onOutput(() => {
13917
13955
  const now = Date.now();
@@ -14164,6 +14202,9 @@ async function refreshModels(ctx) {
14164
14202
  }
14165
14203
  if (!ctx.config)
14166
14204
  return;
14205
+ const freshConfig = ensureMachineRegistered();
14206
+ ctx.config.availableHarnesses = freshConfig.availableHarnesses;
14207
+ ctx.config.harnessVersions = freshConfig.harnessVersions;
14167
14208
  const totalCount = Object.values(models).flat().length;
14168
14209
  try {
14169
14210
  await ctx.deps.backend.mutation(api.machines.register, {
@@ -14180,6 +14221,41 @@ async function refreshModels(ctx) {
14180
14221
  console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${error.message}`);
14181
14222
  }
14182
14223
  }
14224
+ function evictStaleDedupEntries(processedCommandIds, processedPingIds) {
14225
+ const evictBefore = Date.now() - AGENT_REQUEST_DEADLINE_MS;
14226
+ for (const [id, ts] of processedCommandIds) {
14227
+ if (ts < evictBefore)
14228
+ processedCommandIds.delete(id);
14229
+ }
14230
+ for (const [id, ts] of processedPingIds) {
14231
+ if (ts < evictBefore)
14232
+ processedPingIds.delete(id);
14233
+ }
14234
+ }
14235
+ async function dispatchCommandEvent(ctx, event, processedCommandIds, processedPingIds) {
14236
+ const eventId = event._id.toString();
14237
+ if (event.type === "agent.requestStart") {
14238
+ if (processedCommandIds.has(eventId))
14239
+ return;
14240
+ processedCommandIds.set(eventId, Date.now());
14241
+ await onRequestStartAgent(ctx, event);
14242
+ } else if (event.type === "agent.requestStop") {
14243
+ if (processedCommandIds.has(eventId))
14244
+ return;
14245
+ processedCommandIds.set(eventId, Date.now());
14246
+ await onRequestStopAgent(ctx, event);
14247
+ } else if (event.type === "daemon.ping") {
14248
+ if (processedPingIds.has(eventId))
14249
+ return;
14250
+ processedPingIds.set(eventId, Date.now());
14251
+ handlePing();
14252
+ await ctx.deps.backend.mutation(api.machines.ackPing, {
14253
+ sessionId: ctx.sessionId,
14254
+ machineId: ctx.machineId,
14255
+ pingEventId: event._id
14256
+ });
14257
+ }
14258
+ }
14183
14259
  async function startCommandLoop(ctx) {
14184
14260
  let heartbeatCount = 0;
14185
14261
  const heartbeatTimer = setInterval(() => {
@@ -14218,40 +14294,11 @@ Listening for commands...`);
14218
14294
  }, async (result) => {
14219
14295
  if (!result.events || result.events.length === 0)
14220
14296
  return;
14221
- const evictBefore = Date.now() - AGENT_REQUEST_DEADLINE_MS;
14222
- for (const [id, ts] of processedCommandIds) {
14223
- if (ts < evictBefore)
14224
- processedCommandIds.delete(id);
14225
- }
14226
- for (const [id, ts] of processedPingIds) {
14227
- if (ts < evictBefore)
14228
- processedPingIds.delete(id);
14229
- }
14297
+ evictStaleDedupEntries(processedCommandIds, processedPingIds);
14230
14298
  for (const event of result.events) {
14231
- const eventId = event._id.toString();
14232
14299
  try {
14233
14300
  console.log(`[${formatTimestamp()}] \uD83D\uDCE1 Stream command event: ${event.type}`);
14234
- if (event.type === "agent.requestStart") {
14235
- if (processedCommandIds.has(eventId))
14236
- continue;
14237
- processedCommandIds.set(eventId, Date.now());
14238
- await onRequestStartAgent(ctx, event);
14239
- } else if (event.type === "agent.requestStop") {
14240
- if (processedCommandIds.has(eventId))
14241
- continue;
14242
- processedCommandIds.set(eventId, Date.now());
14243
- await onRequestStopAgent(ctx, event);
14244
- } else if (event.type === "daemon.ping") {
14245
- if (processedPingIds.has(eventId))
14246
- continue;
14247
- processedPingIds.set(eventId, Date.now());
14248
- handlePing();
14249
- await ctx.deps.backend.mutation(api.machines.ackPing, {
14250
- sessionId: ctx.sessionId,
14251
- machineId: ctx.machineId,
14252
- pingEventId: event._id
14253
- });
14254
- }
14301
+ await dispatchCommandEvent(ctx, event, processedCommandIds, processedPingIds);
14255
14302
  } catch (err) {
14256
14303
  console.error(`[${formatTimestamp()}] ❌ Stream command event failed: ${err.message}`);
14257
14304
  }
@@ -14269,6 +14316,7 @@ var MODEL_REFRESH_INTERVAL_MS;
14269
14316
  var init_command_loop = __esm(() => {
14270
14317
  init_api3();
14271
14318
  init_client2();
14319
+ init_machine();
14272
14320
  init_on_daemon_shutdown();
14273
14321
  init_on_request_start_agent();
14274
14322
  init_on_request_stop_agent();
@@ -14447,11 +14495,7 @@ function createDefaultDeps16() {
14447
14495
  }
14448
14496
  };
14449
14497
  }
14450
- async function initDaemon() {
14451
- if (!acquireLock()) {
14452
- process.exit(1);
14453
- }
14454
- const convexUrl = getConvexUrl();
14498
+ function validateAuthentication(convexUrl) {
14455
14499
  const sessionId = getSessionId();
14456
14500
  if (!sessionId) {
14457
14501
  const otherUrls = getOtherSessionUrls();
@@ -14468,18 +14512,10 @@ Run: chatroom auth login`);
14468
14512
  releaseLock();
14469
14513
  process.exit(1);
14470
14514
  }
14471
- const machineId = getMachineId();
14472
- if (!machineId) {
14473
- console.error(`❌ Machine not registered`);
14474
- console.error(`
14475
- Run any chatroom command first to register this machine,`);
14476
- console.error(`for example: chatroom auth status`);
14477
- releaseLock();
14478
- process.exit(1);
14479
- }
14480
- const client2 = await getConvexClient();
14481
- const typedSessionId = sessionId;
14482
- const validation = await client2.query(api.cliAuth.validateSession, { sessionId: typedSessionId });
14515
+ return sessionId;
14516
+ }
14517
+ async function validateSession(client2, sessionId, convexUrl) {
14518
+ const validation = await client2.query(api.cliAuth.validateSession, { sessionId });
14483
14519
  if (!validation.valid) {
14484
14520
  console.error(`❌ Session invalid: ${validation.reason}`);
14485
14521
  console.error(`
@@ -14487,32 +14523,34 @@ Run: chatroom auth login`);
14487
14523
  releaseLock();
14488
14524
  process.exit(1);
14489
14525
  }
14526
+ }
14527
+ function setupMachine() {
14528
+ ensureMachineRegistered();
14490
14529
  const config3 = loadMachineConfig();
14491
- const openCodeService = new OpenCodeAgentService;
14492
- const piService = new PiAgentService;
14493
- const agentServices = new Map([
14494
- ["opencode", openCodeService],
14495
- ["pi", piService]
14496
- ]);
14530
+ return config3;
14531
+ }
14532
+ async function registerCapabilities(client2, sessionId, config3, agentServices) {
14533
+ const { machineId } = config3;
14497
14534
  const availableModels = await discoverModels(agentServices);
14498
- if (config3) {
14499
- try {
14500
- await client2.mutation(api.machines.register, {
14501
- sessionId: typedSessionId,
14502
- machineId,
14503
- hostname: config3.hostname,
14504
- os: config3.os,
14505
- availableHarnesses: config3.availableHarnesses,
14506
- harnessVersions: config3.harnessVersions,
14507
- availableModels
14508
- });
14509
- } catch (error) {
14510
- console.warn(`⚠️ Machine registration update failed: ${error.message}`);
14511
- }
14535
+ try {
14536
+ await client2.mutation(api.machines.register, {
14537
+ sessionId,
14538
+ machineId,
14539
+ hostname: config3.hostname,
14540
+ os: config3.os,
14541
+ availableHarnesses: config3.availableHarnesses,
14542
+ harnessVersions: config3.harnessVersions,
14543
+ availableModels
14544
+ });
14545
+ } catch (error) {
14546
+ console.warn(`⚠️ Machine registration update failed: ${error.message}`);
14512
14547
  }
14548
+ return availableModels;
14549
+ }
14550
+ async function connectDaemon(client2, sessionId, machineId, convexUrl) {
14513
14551
  try {
14514
14552
  await client2.mutation(api.machines.updateDaemonStatus, {
14515
- sessionId: typedSessionId,
14553
+ sessionId,
14516
14554
  machineId,
14517
14555
  connected: true
14518
14556
  });
@@ -14525,6 +14563,45 @@ Run: chatroom auth login`);
14525
14563
  releaseLock();
14526
14564
  process.exit(1);
14527
14565
  }
14566
+ }
14567
+ function logStartup(ctx, availableModels) {
14568
+ console.log(`[${formatTimestamp()}] \uD83D\uDE80 Daemon started`);
14569
+ console.log(` CLI version: ${getVersion()}`);
14570
+ console.log(` Machine ID: ${ctx.machineId}`);
14571
+ console.log(` Hostname: ${ctx.config?.hostname ?? "unknown"}`);
14572
+ console.log(` Available harnesses: ${ctx.config?.availableHarnesses.join(", ") || "none"}`);
14573
+ console.log(` Available models: ${Object.keys(availableModels).length > 0 ? `${Object.values(availableModels).flat().length} models across ${Object.keys(availableModels).join(", ")}` : "none discovered"}`);
14574
+ console.log(` PID: ${process.pid}`);
14575
+ }
14576
+ async function recoverState(ctx) {
14577
+ console.log(`
14578
+ [${formatTimestamp()}] \uD83D\uDD04 Recovering agent state...`);
14579
+ try {
14580
+ await recoverAgentState(ctx);
14581
+ } catch (e) {
14582
+ console.log(` ⚠️ Recovery failed: ${e.message}`);
14583
+ console.log(` Continuing with fresh state`);
14584
+ }
14585
+ }
14586
+ async function initDaemon() {
14587
+ if (!acquireLock()) {
14588
+ process.exit(1);
14589
+ }
14590
+ const convexUrl = getConvexUrl();
14591
+ const sessionId = validateAuthentication(convexUrl);
14592
+ const client2 = await getConvexClient();
14593
+ const typedSessionId = sessionId;
14594
+ await validateSession(client2, typedSessionId, convexUrl);
14595
+ const config3 = setupMachine();
14596
+ const { machineId } = config3;
14597
+ const openCodeService = new OpenCodeAgentService;
14598
+ const piService = new PiAgentService;
14599
+ const agentServices = new Map([
14600
+ ["opencode", openCodeService],
14601
+ ["pi", piService]
14602
+ ]);
14603
+ const availableModels = await registerCapabilities(client2, typedSessionId, config3, agentServices);
14604
+ await connectDaemon(client2, typedSessionId, machineId, convexUrl);
14528
14605
  const deps = createDefaultDeps16();
14529
14606
  deps.backend.mutation = (endpoint, args) => client2.mutation(endpoint, args);
14530
14607
  deps.backend.query = (endpoint, args) => client2.query(endpoint, args);
@@ -14539,21 +14616,8 @@ Run: chatroom auth login`);
14539
14616
  agentServices
14540
14617
  };
14541
14618
  registerEventListeners(ctx);
14542
- console.log(`[${formatTimestamp()}] \uD83D\uDE80 Daemon started`);
14543
- console.log(` CLI version: ${getVersion()}`);
14544
- console.log(` Machine ID: ${machineId}`);
14545
- console.log(` Hostname: ${config3?.hostname ?? "Unknown"}`);
14546
- console.log(` Available harnesses: ${config3?.availableHarnesses.join(", ") || "none"}`);
14547
- console.log(` Available models: ${Object.keys(availableModels).length > 0 ? `${Object.values(availableModels).flat().length} models across ${Object.keys(availableModels).join(", ")}` : "none discovered"}`);
14548
- console.log(` PID: ${process.pid}`);
14549
- console.log(`
14550
- [${formatTimestamp()}] \uD83D\uDD04 Recovering agent state...`);
14551
- try {
14552
- await recoverAgentState(ctx);
14553
- } catch (e) {
14554
- console.log(` ⚠️ Recovery failed: ${e.message}`);
14555
- console.log(` Continuing with fresh state`);
14556
- }
14619
+ logStartup(ctx, availableModels);
14620
+ await recoverState(ctx);
14557
14621
  return ctx;
14558
14622
  }
14559
14623
  var init_init2 = __esm(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatroom-cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "CLI for multi-agent chatroom collaboration",
5
5
  "type": "module",
6
6
  "bin": {