chatroom-cli 1.0.64 → 1.0.66

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 +1150 -21
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -10192,6 +10192,464 @@ var init_config2 = __esm(() => {
10192
10192
  WEB_SERVER_PORT = parseInt(process.env.WEB_PORT || "3456", 10);
10193
10193
  });
10194
10194
 
10195
+ // src/infrastructure/agent-drivers/process-driver.ts
10196
+ import { spawn } from "node:child_process";
10197
+ function buildCombinedPrompt(rolePrompt, initialMessage) {
10198
+ return `${rolePrompt}
10199
+
10200
+ ${initialMessage}`;
10201
+ }
10202
+
10203
+ class ProcessDriver {
10204
+ async start(options) {
10205
+ const config3 = this.buildSpawnConfig(options);
10206
+ console.log(` Spawning ${this.harness} agent...`);
10207
+ console.log(` Working dir: ${options.workingDir}`);
10208
+ if (options.harnessVersion) {
10209
+ console.log(` Harness version: v${options.harnessVersion.version} (major: ${options.harnessVersion.major})`);
10210
+ }
10211
+ if (options.model) {
10212
+ console.log(` Model: ${options.model}`);
10213
+ }
10214
+ try {
10215
+ const childProcess = spawn(config3.command, config3.args, {
10216
+ cwd: options.workingDir,
10217
+ stdio: config3.stdio,
10218
+ detached: true,
10219
+ shell: false
10220
+ });
10221
+ if (config3.writePromptToStdin && config3.stdinPrompt) {
10222
+ childProcess.stdin?.write(config3.stdinPrompt);
10223
+ childProcess.stdin?.end();
10224
+ }
10225
+ config3.afterSpawn?.(childProcess);
10226
+ childProcess.unref();
10227
+ await new Promise((resolve) => setTimeout(resolve, 500));
10228
+ if (childProcess.killed || childProcess.exitCode !== null) {
10229
+ return {
10230
+ success: false,
10231
+ message: `Agent process exited immediately (exit code: ${childProcess.exitCode})`
10232
+ };
10233
+ }
10234
+ const handle = {
10235
+ harness: this.harness,
10236
+ type: "process",
10237
+ pid: childProcess.pid,
10238
+ workingDir: options.workingDir
10239
+ };
10240
+ return {
10241
+ success: true,
10242
+ message: "Agent spawned successfully",
10243
+ handle
10244
+ };
10245
+ } catch (error) {
10246
+ return {
10247
+ success: false,
10248
+ message: `Failed to spawn agent: ${error.message}`
10249
+ };
10250
+ }
10251
+ }
10252
+ async stop(handle) {
10253
+ if (handle.type !== "process" || !handle.pid) {
10254
+ throw new Error(`Cannot stop: handle has no PID (type=${handle.type})`);
10255
+ }
10256
+ process.kill(handle.pid, "SIGTERM");
10257
+ }
10258
+ async isAlive(handle) {
10259
+ if (handle.type !== "process" || !handle.pid) {
10260
+ return false;
10261
+ }
10262
+ try {
10263
+ process.kill(handle.pid, 0);
10264
+ return true;
10265
+ } catch {
10266
+ return false;
10267
+ }
10268
+ }
10269
+ async recover(_workingDir) {
10270
+ return [];
10271
+ }
10272
+ async listModels() {
10273
+ return [];
10274
+ }
10275
+ }
10276
+ var init_process_driver = () => {};
10277
+
10278
+ // src/infrastructure/machine/types.ts
10279
+ var AGENT_HARNESSES, AGENT_HARNESS_COMMANDS, MACHINE_CONFIG_VERSION = "1";
10280
+ var init_types = __esm(() => {
10281
+ AGENT_HARNESSES = ["opencode"];
10282
+ AGENT_HARNESS_COMMANDS = {
10283
+ opencode: "opencode"
10284
+ };
10285
+ });
10286
+
10287
+ // src/infrastructure/agent-drivers/opencode-process-driver.ts
10288
+ import { execSync } from "node:child_process";
10289
+ var OpenCodeProcessDriver;
10290
+ var init_opencode_process_driver = __esm(() => {
10291
+ init_process_driver();
10292
+ init_types();
10293
+ OpenCodeProcessDriver = class OpenCodeProcessDriver extends ProcessDriver {
10294
+ harness = "opencode";
10295
+ capabilities = {
10296
+ sessionPersistence: false,
10297
+ abort: false,
10298
+ modelSelection: true,
10299
+ compaction: false,
10300
+ eventStreaming: false,
10301
+ messageInjection: false,
10302
+ dynamicModelDiscovery: true
10303
+ };
10304
+ buildSpawnConfig(options) {
10305
+ const command = AGENT_HARNESS_COMMANDS[this.harness];
10306
+ const combinedPrompt = buildCombinedPrompt(options.rolePrompt, options.initialMessage);
10307
+ const args = ["run"];
10308
+ if (options.model) {
10309
+ args.push("--model", options.model);
10310
+ }
10311
+ return {
10312
+ command,
10313
+ args,
10314
+ stdio: ["pipe", "inherit", "inherit"],
10315
+ writePromptToStdin: true,
10316
+ stdinPrompt: combinedPrompt
10317
+ };
10318
+ }
10319
+ async listModels() {
10320
+ try {
10321
+ const output = execSync("opencode models", {
10322
+ stdio: ["pipe", "pipe", "pipe"],
10323
+ timeout: 1e4
10324
+ }).toString().trim();
10325
+ if (!output)
10326
+ return [];
10327
+ return output.split(`
10328
+ `).map((line) => line.trim()).filter((line) => line.length > 0);
10329
+ } catch {
10330
+ return [];
10331
+ }
10332
+ }
10333
+ };
10334
+ });
10335
+
10336
+ // src/infrastructure/agent-drivers/registry.ts
10337
+ class DefaultDriverRegistry {
10338
+ drivers;
10339
+ constructor(drivers) {
10340
+ this.drivers = new Map;
10341
+ for (const driver of drivers) {
10342
+ this.drivers.set(driver.harness, driver);
10343
+ }
10344
+ }
10345
+ get(harness) {
10346
+ const driver = this.drivers.get(harness);
10347
+ if (!driver) {
10348
+ throw new Error(`No driver registered for harness: ${harness}`);
10349
+ }
10350
+ return driver;
10351
+ }
10352
+ all() {
10353
+ return Array.from(this.drivers.values());
10354
+ }
10355
+ capabilities(harness) {
10356
+ return this.get(harness).capabilities;
10357
+ }
10358
+ }
10359
+ function getDriverRegistry() {
10360
+ if (!registryInstance) {
10361
+ registryInstance = new DefaultDriverRegistry([new OpenCodeProcessDriver]);
10362
+ }
10363
+ return registryInstance;
10364
+ }
10365
+ var registryInstance = null;
10366
+ var init_registry = __esm(() => {
10367
+ init_opencode_process_driver();
10368
+ });
10369
+
10370
+ // src/infrastructure/agent-drivers/index.ts
10371
+ var init_agent_drivers = __esm(() => {
10372
+ init_registry();
10373
+ init_process_driver();
10374
+ init_process_driver();
10375
+ init_opencode_process_driver();
10376
+ });
10377
+
10378
+ // src/infrastructure/machine/detection.ts
10379
+ import { execSync as execSync2 } from "node:child_process";
10380
+ function commandExists(command) {
10381
+ try {
10382
+ const checkCommand = process.platform === "win32" ? `where ${command}` : `which ${command}`;
10383
+ execSync2(checkCommand, { stdio: "ignore" });
10384
+ return true;
10385
+ } catch {
10386
+ return false;
10387
+ }
10388
+ }
10389
+ function parseVersion(versionStr) {
10390
+ const match = versionStr.match(/v?(\d+)\.(\d+)\.(\d+)/);
10391
+ if (!match)
10392
+ return null;
10393
+ const major = parseInt(match[1], 10);
10394
+ const version2 = `${match[1]}.${match[2]}.${match[3]}`;
10395
+ return { version: version2, major };
10396
+ }
10397
+ function detectHarnessVersion(harness) {
10398
+ const versionCommand = HARNESS_VERSION_COMMANDS[harness];
10399
+ if (!versionCommand)
10400
+ return null;
10401
+ try {
10402
+ const output = execSync2(versionCommand, {
10403
+ stdio: ["pipe", "pipe", "pipe"],
10404
+ timeout: 5000
10405
+ }).toString().trim();
10406
+ return parseVersion(output);
10407
+ } catch {
10408
+ return null;
10409
+ }
10410
+ }
10411
+ function detectHarnessVersions(harnesses) {
10412
+ const versions = {};
10413
+ for (const harness of harnesses) {
10414
+ const version2 = detectHarnessVersion(harness);
10415
+ if (version2) {
10416
+ versions[harness] = version2;
10417
+ }
10418
+ }
10419
+ return versions;
10420
+ }
10421
+ function detectAvailableHarnesses() {
10422
+ const available = [];
10423
+ for (const harness of AGENT_HARNESSES) {
10424
+ const command = AGENT_HARNESS_COMMANDS[harness];
10425
+ if (commandExists(command)) {
10426
+ available.push(harness);
10427
+ }
10428
+ }
10429
+ return available;
10430
+ }
10431
+ var HARNESS_VERSION_COMMANDS;
10432
+ var init_detection = __esm(() => {
10433
+ init_types();
10434
+ HARNESS_VERSION_COMMANDS = {
10435
+ opencode: "opencode --version"
10436
+ };
10437
+ });
10438
+
10439
+ // src/infrastructure/machine/storage.ts
10440
+ import { randomUUID } from "node:crypto";
10441
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync } from "node:fs";
10442
+ import { homedir as homedir2, hostname as hostname2 } from "node:os";
10443
+ import { join as join3 } from "node:path";
10444
+ function ensureConfigDir2() {
10445
+ if (!existsSync2(CHATROOM_DIR2)) {
10446
+ mkdirSync2(CHATROOM_DIR2, { recursive: true, mode: 448 });
10447
+ }
10448
+ }
10449
+ function getMachineConfigPath() {
10450
+ return join3(CHATROOM_DIR2, MACHINE_FILE);
10451
+ }
10452
+ function loadConfigFile() {
10453
+ const configPath = getMachineConfigPath();
10454
+ if (!existsSync2(configPath)) {
10455
+ return null;
10456
+ }
10457
+ try {
10458
+ const content = readFileSync3(configPath, "utf-8");
10459
+ return JSON.parse(content);
10460
+ } catch (error) {
10461
+ console.warn(`⚠️ Failed to read machine config at ${configPath}: ${error.message}`);
10462
+ console.warn(` The machine will re-register with a new identity on next startup.`);
10463
+ console.warn(` If this is unexpected, check the file for corruption.`);
10464
+ return null;
10465
+ }
10466
+ }
10467
+ function saveConfigFile(configFile) {
10468
+ ensureConfigDir2();
10469
+ const configPath = getMachineConfigPath();
10470
+ const tempPath = `${configPath}.tmp`;
10471
+ const content = JSON.stringify(configFile, null, 2);
10472
+ writeFileSync2(tempPath, content, { encoding: "utf-8", mode: 384 });
10473
+ renameSync(tempPath, configPath);
10474
+ }
10475
+ function loadMachineConfig() {
10476
+ const configFile = loadConfigFile();
10477
+ if (!configFile)
10478
+ return null;
10479
+ const convexUrl = getConvexUrl();
10480
+ return configFile.machines[convexUrl] ?? null;
10481
+ }
10482
+ function saveMachineConfig(config3) {
10483
+ const configFile = loadConfigFile() ?? {
10484
+ version: MACHINE_CONFIG_VERSION,
10485
+ machines: {}
10486
+ };
10487
+ const convexUrl = getConvexUrl();
10488
+ configFile.machines[convexUrl] = config3;
10489
+ saveConfigFile(configFile);
10490
+ }
10491
+ function createNewEndpointConfig() {
10492
+ const now = new Date().toISOString();
10493
+ const availableHarnesses = detectAvailableHarnesses();
10494
+ return {
10495
+ machineId: randomUUID(),
10496
+ hostname: hostname2(),
10497
+ os: process.platform,
10498
+ registeredAt: now,
10499
+ lastSyncedAt: now,
10500
+ availableHarnesses,
10501
+ harnessVersions: detectHarnessVersions(availableHarnesses),
10502
+ chatroomAgents: {}
10503
+ };
10504
+ }
10505
+ function ensureMachineRegistered() {
10506
+ let config3 = loadMachineConfig();
10507
+ if (!config3) {
10508
+ config3 = createNewEndpointConfig();
10509
+ saveMachineConfig(config3);
10510
+ } else {
10511
+ const now = new Date().toISOString();
10512
+ config3.availableHarnesses = detectAvailableHarnesses();
10513
+ config3.harnessVersions = detectHarnessVersions(config3.availableHarnesses);
10514
+ config3.lastSyncedAt = now;
10515
+ config3.hostname = hostname2();
10516
+ saveMachineConfig(config3);
10517
+ }
10518
+ return {
10519
+ machineId: config3.machineId,
10520
+ hostname: config3.hostname,
10521
+ os: config3.os,
10522
+ availableHarnesses: config3.availableHarnesses,
10523
+ harnessVersions: config3.harnessVersions
10524
+ };
10525
+ }
10526
+ function getMachineId() {
10527
+ const config3 = loadMachineConfig();
10528
+ return config3?.machineId ?? null;
10529
+ }
10530
+ function updateAgentContext(chatroomId, role, agentType, workingDir) {
10531
+ const config3 = loadMachineConfig();
10532
+ if (!config3) {
10533
+ throw new Error("Machine not registered. Run ensureMachineRegistered() first.");
10534
+ }
10535
+ const now = new Date().toISOString();
10536
+ if (!config3.chatroomAgents[chatroomId]) {
10537
+ config3.chatroomAgents[chatroomId] = {};
10538
+ }
10539
+ config3.chatroomAgents[chatroomId][role] = {
10540
+ agentType,
10541
+ workingDir,
10542
+ lastStartedAt: now
10543
+ };
10544
+ saveMachineConfig(config3);
10545
+ }
10546
+ function getAgentContext(chatroomId, role) {
10547
+ const config3 = loadMachineConfig();
10548
+ if (!config3) {
10549
+ return null;
10550
+ }
10551
+ return config3.chatroomAgents[chatroomId]?.[role] ?? null;
10552
+ }
10553
+ var CHATROOM_DIR2, MACHINE_FILE = "machine.json";
10554
+ var init_storage2 = __esm(() => {
10555
+ init_detection();
10556
+ init_types();
10557
+ init_client2();
10558
+ CHATROOM_DIR2 = join3(homedir2(), ".chatroom");
10559
+ });
10560
+
10561
+ // src/infrastructure/machine/daemon-state.ts
10562
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2 } from "node:fs";
10563
+ import { homedir as homedir3 } from "node:os";
10564
+ import { join as join4 } from "node:path";
10565
+ function agentKey(chatroomId, role) {
10566
+ return `${chatroomId}/${role}`;
10567
+ }
10568
+ function ensureStateDir() {
10569
+ if (!existsSync3(STATE_DIR)) {
10570
+ mkdirSync3(STATE_DIR, { recursive: true, mode: 448 });
10571
+ }
10572
+ }
10573
+ function stateFilePath(machineId) {
10574
+ return join4(STATE_DIR, `${machineId}.json`);
10575
+ }
10576
+ function loadDaemonState(machineId) {
10577
+ const filePath = stateFilePath(machineId);
10578
+ if (!existsSync3(filePath)) {
10579
+ return null;
10580
+ }
10581
+ try {
10582
+ const content = readFileSync4(filePath, "utf-8");
10583
+ return JSON.parse(content);
10584
+ } catch {
10585
+ return null;
10586
+ }
10587
+ }
10588
+ function saveDaemonState(state) {
10589
+ ensureStateDir();
10590
+ const filePath = stateFilePath(state.machineId);
10591
+ const tempPath = `${filePath}.tmp`;
10592
+ const content = JSON.stringify(state, null, 2);
10593
+ writeFileSync3(tempPath, content, { encoding: "utf-8", mode: 384 });
10594
+ renameSync2(tempPath, filePath);
10595
+ }
10596
+ function loadOrCreate(machineId) {
10597
+ return loadDaemonState(machineId) ?? {
10598
+ version: STATE_VERSION,
10599
+ machineId,
10600
+ updatedAt: new Date().toISOString(),
10601
+ agents: {}
10602
+ };
10603
+ }
10604
+ function persistAgentPid(machineId, chatroomId, role, pid, harness) {
10605
+ const state = loadOrCreate(machineId);
10606
+ state.agents[agentKey(chatroomId, role)] = {
10607
+ pid,
10608
+ harness,
10609
+ startedAt: new Date().toISOString()
10610
+ };
10611
+ state.updatedAt = new Date().toISOString();
10612
+ saveDaemonState(state);
10613
+ }
10614
+ function clearAgentPid(machineId, chatroomId, role) {
10615
+ const state = loadDaemonState(machineId);
10616
+ if (!state)
10617
+ return;
10618
+ const key = agentKey(chatroomId, role);
10619
+ if (!(key in state.agents))
10620
+ return;
10621
+ delete state.agents[key];
10622
+ state.updatedAt = new Date().toISOString();
10623
+ saveDaemonState(state);
10624
+ }
10625
+ function listAgentEntries(machineId) {
10626
+ const state = loadDaemonState(machineId);
10627
+ if (!state)
10628
+ return [];
10629
+ const results = [];
10630
+ for (const [key, entry] of Object.entries(state.agents)) {
10631
+ const separatorIndex = key.lastIndexOf("/");
10632
+ if (separatorIndex === -1)
10633
+ continue;
10634
+ const chatroomId = key.substring(0, separatorIndex);
10635
+ const role = key.substring(separatorIndex + 1);
10636
+ results.push({ chatroomId, role, entry });
10637
+ }
10638
+ return results;
10639
+ }
10640
+ var CHATROOM_DIR3, STATE_DIR, STATE_VERSION = "1";
10641
+ var init_daemon_state = __esm(() => {
10642
+ CHATROOM_DIR3 = join4(homedir3(), ".chatroom");
10643
+ STATE_DIR = join4(CHATROOM_DIR3, "machines", "state");
10644
+ });
10645
+
10646
+ // src/infrastructure/machine/index.ts
10647
+ var init_machine = __esm(() => {
10648
+ init_types();
10649
+ init_storage2();
10650
+ init_daemon_state();
10651
+ });
10652
+
10195
10653
  // src/commands/wait-for-task.ts
10196
10654
  var exports_wait_for_task = {};
10197
10655
  __export(exports_wait_for_task, {
@@ -10264,13 +10722,54 @@ async function waitForTask(chatroomId, options) {
10264
10722
  console.error(`❌ Chatroom ${chatroomId} not found or access denied`);
10265
10723
  process.exit(1);
10266
10724
  }
10725
+ try {
10726
+ const machineInfo = ensureMachineRegistered();
10727
+ let availableModels = [];
10728
+ try {
10729
+ const registry = getDriverRegistry();
10730
+ for (const driver of registry.all()) {
10731
+ if (driver.capabilities.dynamicModelDiscovery) {
10732
+ const models = await driver.listModels();
10733
+ availableModels = availableModels.concat(models);
10734
+ }
10735
+ }
10736
+ } catch {}
10737
+ await client2.mutation(api.machines.register, {
10738
+ sessionId,
10739
+ machineId: machineInfo.machineId,
10740
+ hostname: machineInfo.hostname,
10741
+ os: machineInfo.os,
10742
+ availableHarnesses: machineInfo.availableHarnesses,
10743
+ harnessVersions: machineInfo.harnessVersions,
10744
+ availableModels
10745
+ });
10746
+ const agentType = options.agentType ?? (machineInfo.availableHarnesses.length > 0 ? machineInfo.availableHarnesses[0] : undefined);
10747
+ if (agentType) {
10748
+ const workingDir = process.cwd();
10749
+ updateAgentContext(chatroomId, role, agentType, workingDir);
10750
+ await client2.mutation(api.machines.updateAgentConfig, {
10751
+ sessionId,
10752
+ machineId: machineInfo.machineId,
10753
+ chatroomId,
10754
+ role,
10755
+ agentType,
10756
+ workingDir
10757
+ });
10758
+ }
10759
+ } catch (machineError) {
10760
+ if (!silent) {
10761
+ console.warn(`⚠️ Machine registration failed: ${machineError.message}`);
10762
+ }
10763
+ }
10267
10764
  const effectiveTimeout = timeout || DEFAULT_WAIT_TIMEOUT_MS;
10268
10765
  const readyUntil = Date.now() + effectiveTimeout;
10766
+ const connectionId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
10269
10767
  await client2.mutation(api.participants.join, {
10270
10768
  sessionId,
10271
10769
  chatroomId,
10272
10770
  role,
10273
- readyUntil
10771
+ readyUntil,
10772
+ connectionId
10274
10773
  });
10275
10774
  const connectionTime = new Date().toISOString().replace("T", " ").substring(0, 19);
10276
10775
  if (!silent) {
@@ -10326,6 +10825,29 @@ ${"─".repeat(50)}`);
10326
10825
  const handlePendingTasks = async (pendingTasks) => {
10327
10826
  if (taskProcessed)
10328
10827
  return;
10828
+ const currentConnectionId = await client2.query(api.participants.getConnectionId, {
10829
+ sessionId,
10830
+ chatroomId,
10831
+ role
10832
+ });
10833
+ if (currentConnectionId && currentConnectionId !== connectionId) {
10834
+ if (unsubscribe)
10835
+ unsubscribe();
10836
+ clearTimeout(timeoutHandle);
10837
+ const takeoverTime = new Date().toISOString().replace("T", " ").substring(0, 19);
10838
+ console.log(`
10839
+ ${"─".repeat(50)}`);
10840
+ console.log(`⚠️ CONNECTION SUPERSEDED
10841
+ `);
10842
+ console.log(`[${takeoverTime}] Why: Another wait-for-task process started for this role`);
10843
+ console.log(`Impact: This process is being replaced by the newer connection`);
10844
+ console.log(`Action: This is expected if you started a new wait-for-task session
10845
+ `);
10846
+ console.log(`If you meant to use THIS terminal, run:`);
10847
+ console.log(waitForTaskCommand({ chatroomId, role, cliEnvPrefix }));
10848
+ console.log(`${"─".repeat(50)}`);
10849
+ process.exit(0);
10850
+ }
10329
10851
  const taskWithMessage = pendingTasks.length > 0 ? pendingTasks[0] : null;
10330
10852
  if (!taskWithMessage) {
10331
10853
  return;
@@ -10360,20 +10882,6 @@ ${"─".repeat(50)}`);
10360
10882
  status: "active",
10361
10883
  expiresAt: activeUntil
10362
10884
  });
10363
- if (message && message.type === "interrupt") {
10364
- const interruptTime = new Date().toISOString().replace("T", " ").substring(0, 19);
10365
- console.log(`
10366
- ${"─".repeat(50)}`);
10367
- console.log(`⚠️ RECONNECTION REQUIRED
10368
- `);
10369
- console.log(`[${interruptTime}] Why: Interrupt message received from team`);
10370
- console.log(`Impact: You are no longer listening for tasks`);
10371
- console.log(`Action: Run this command immediately to resume availability
10372
- `);
10373
- console.log(waitForTaskCommand({ chatroomId, role, cliEnvPrefix }));
10374
- console.log(`${"─".repeat(50)}`);
10375
- process.exit(0);
10376
- }
10377
10885
  const taskDeliveryPrompt = await client2.query(api.messages.getTaskDeliveryPrompt, {
10378
10886
  sessionId,
10379
10887
  chatroomId,
@@ -10526,8 +11034,10 @@ var init_wait_for_task = __esm(() => {
10526
11034
  init_env();
10527
11035
  init_api3();
10528
11036
  init_config2();
11037
+ init_agent_drivers();
10529
11038
  init_storage();
10530
11039
  init_client2();
11040
+ init_machine();
10531
11041
  });
10532
11042
 
10533
11043
  // src/commands/task-started.ts
@@ -11548,12 +12058,12 @@ __export(exports_file_content, {
11548
12058
  resolveContent: () => resolveContent,
11549
12059
  readFileContent: () => readFileContent
11550
12060
  });
11551
- import { readFileSync as readFileSync3 } from "fs";
12061
+ import { readFileSync as readFileSync5 } from "fs";
11552
12062
  import { resolve } from "path";
11553
12063
  function readFileContent(filePath, optionName) {
11554
12064
  const absolutePath = resolve(process.cwd(), filePath);
11555
12065
  try {
11556
- return readFileSync3(absolutePath, "utf-8");
12066
+ return readFileSync5(absolutePath, "utf-8");
11557
12067
  } catch (err) {
11558
12068
  const nodeErr = err;
11559
12069
  throw new Error(`Cannot read file for --${optionName}: ${absolutePath}
@@ -12037,6 +12547,610 @@ var init_artifact = __esm(() => {
12037
12547
  init_file_content();
12038
12548
  });
12039
12549
 
12550
+ // src/commands/machine/pid.ts
12551
+ import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "node:fs";
12552
+ import { homedir as homedir4 } from "node:os";
12553
+ import { join as join5 } from "node:path";
12554
+ function ensureChatroomDir() {
12555
+ if (!existsSync4(CHATROOM_DIR4)) {
12556
+ mkdirSync4(CHATROOM_DIR4, { recursive: true, mode: 448 });
12557
+ }
12558
+ }
12559
+ function getPidFilePath() {
12560
+ return join5(CHATROOM_DIR4, PID_FILE);
12561
+ }
12562
+ function isProcessRunning(pid) {
12563
+ try {
12564
+ process.kill(pid, 0);
12565
+ return true;
12566
+ } catch {
12567
+ return false;
12568
+ }
12569
+ }
12570
+ function readPid() {
12571
+ const pidPath = getPidFilePath();
12572
+ if (!existsSync4(pidPath)) {
12573
+ return null;
12574
+ }
12575
+ try {
12576
+ const content = readFileSync6(pidPath, "utf-8").trim();
12577
+ const pid = parseInt(content, 10);
12578
+ if (isNaN(pid) || pid <= 0) {
12579
+ return null;
12580
+ }
12581
+ return pid;
12582
+ } catch {
12583
+ return null;
12584
+ }
12585
+ }
12586
+ function writePid() {
12587
+ ensureChatroomDir();
12588
+ const pidPath = getPidFilePath();
12589
+ writeFileSync4(pidPath, process.pid.toString(), "utf-8");
12590
+ }
12591
+ function removePid() {
12592
+ const pidPath = getPidFilePath();
12593
+ try {
12594
+ if (existsSync4(pidPath)) {
12595
+ unlinkSync2(pidPath);
12596
+ }
12597
+ } catch {}
12598
+ }
12599
+ function isDaemonRunning() {
12600
+ const pid = readPid();
12601
+ if (pid === null) {
12602
+ return { running: false, pid: null };
12603
+ }
12604
+ if (isProcessRunning(pid)) {
12605
+ return { running: true, pid };
12606
+ }
12607
+ removePid();
12608
+ return { running: false, pid: null };
12609
+ }
12610
+ function acquireLock() {
12611
+ const { running, pid } = isDaemonRunning();
12612
+ if (running) {
12613
+ console.error(`❌ Daemon already running (PID: ${pid})`);
12614
+ return false;
12615
+ }
12616
+ writePid();
12617
+ return true;
12618
+ }
12619
+ function releaseLock() {
12620
+ removePid();
12621
+ }
12622
+ var CHATROOM_DIR4, PID_FILE = "daemon.pid";
12623
+ var init_pid = __esm(() => {
12624
+ CHATROOM_DIR4 = join5(homedir4(), ".chatroom");
12625
+ });
12626
+
12627
+ // src/commands/machine/daemon-start.ts
12628
+ import { execSync as execSync3 } from "node:child_process";
12629
+ import { stat } from "node:fs/promises";
12630
+ function parseMachineCommand(raw) {
12631
+ switch (raw.type) {
12632
+ case "ping":
12633
+ return { _id: raw._id, type: "ping", payload: {}, createdAt: raw.createdAt };
12634
+ case "status":
12635
+ return { _id: raw._id, type: "status", payload: {}, createdAt: raw.createdAt };
12636
+ case "start-agent": {
12637
+ const { chatroomId, role, agentHarness } = raw.payload;
12638
+ if (!chatroomId || !role || !agentHarness) {
12639
+ console.error(` ⚠️ Invalid start-agent command: missing chatroomId, role, or agentHarness`);
12640
+ return null;
12641
+ }
12642
+ return {
12643
+ _id: raw._id,
12644
+ type: "start-agent",
12645
+ payload: {
12646
+ chatroomId,
12647
+ role,
12648
+ agentHarness,
12649
+ model: raw.payload.model,
12650
+ workingDir: raw.payload.workingDir
12651
+ },
12652
+ createdAt: raw.createdAt
12653
+ };
12654
+ }
12655
+ case "stop-agent": {
12656
+ const { chatroomId, role } = raw.payload;
12657
+ if (!chatroomId || !role) {
12658
+ console.error(` ⚠️ Invalid stop-agent command: missing chatroomId or role`);
12659
+ return null;
12660
+ }
12661
+ return {
12662
+ _id: raw._id,
12663
+ type: "stop-agent",
12664
+ payload: { chatroomId, role },
12665
+ createdAt: raw.createdAt
12666
+ };
12667
+ }
12668
+ default:
12669
+ return null;
12670
+ }
12671
+ }
12672
+ function formatTimestamp() {
12673
+ return new Date().toISOString().replace("T", " ").substring(0, 19);
12674
+ }
12675
+ function verifyPidOwnership(pid, expectedHarness) {
12676
+ try {
12677
+ process.kill(pid, 0);
12678
+ } catch {
12679
+ return false;
12680
+ }
12681
+ if (!expectedHarness) {
12682
+ return true;
12683
+ }
12684
+ try {
12685
+ const platform = process.platform;
12686
+ let processName = "";
12687
+ if (platform === "darwin" || platform === "linux") {
12688
+ processName = execSync3(`ps -p ${pid} -o comm= 2>/dev/null`, {
12689
+ encoding: "utf-8",
12690
+ timeout: 3000
12691
+ }).trim();
12692
+ }
12693
+ if (!processName) {
12694
+ return true;
12695
+ }
12696
+ const harnessLower = expectedHarness.toLowerCase();
12697
+ const procLower = processName.toLowerCase();
12698
+ return procLower.includes(harnessLower) || procLower.includes("node") || procLower.includes("bun");
12699
+ } catch {
12700
+ return true;
12701
+ }
12702
+ }
12703
+ async function clearAgentPidEverywhere(ctx, chatroomId, role) {
12704
+ try {
12705
+ await ctx.client.mutation(api.machines.updateSpawnedAgent, {
12706
+ sessionId: ctx.sessionId,
12707
+ machineId: ctx.machineId,
12708
+ chatroomId,
12709
+ role,
12710
+ pid: undefined
12711
+ });
12712
+ } catch (e) {
12713
+ console.log(` ⚠️ Failed to clear PID in backend: ${e.message}`);
12714
+ }
12715
+ clearAgentPid(ctx.machineId, chatroomId, role);
12716
+ }
12717
+ async function recoverAgentState(ctx) {
12718
+ const entries = listAgentEntries(ctx.machineId);
12719
+ if (entries.length === 0) {
12720
+ console.log(` No agent entries found — nothing to recover`);
12721
+ return;
12722
+ }
12723
+ let recovered = 0;
12724
+ let cleared = 0;
12725
+ for (const { chatroomId, role, entry } of entries) {
12726
+ const { pid, harness } = entry;
12727
+ const alive = verifyPidOwnership(pid, harness);
12728
+ if (alive) {
12729
+ console.log(` ✅ Recovered: ${role} (PID ${pid}, harness: ${harness})`);
12730
+ recovered++;
12731
+ } else {
12732
+ console.log(` \uD83E\uDDF9 Stale PID ${pid} for ${role} — clearing`);
12733
+ await clearAgentPidEverywhere(ctx, chatroomId, role);
12734
+ cleared++;
12735
+ }
12736
+ }
12737
+ console.log(` Recovery complete: ${recovered} alive, ${cleared} stale cleared`);
12738
+ }
12739
+ function handlePing() {
12740
+ console.log(` ↪ Responding: pong`);
12741
+ return { result: "pong", failed: false };
12742
+ }
12743
+ function handleStatus(ctx) {
12744
+ const result = JSON.stringify({
12745
+ hostname: ctx.config?.hostname,
12746
+ os: ctx.config?.os,
12747
+ availableHarnesses: ctx.config?.availableHarnesses,
12748
+ chatroomAgents: Object.keys(ctx.config?.chatroomAgents ?? {})
12749
+ });
12750
+ console.log(` ↪ Responding with status`);
12751
+ return { result, failed: false };
12752
+ }
12753
+ async function handleStartAgent(ctx, command) {
12754
+ const { chatroomId, role, agentHarness, model, workingDir } = command.payload;
12755
+ console.log(` ↪ start-agent command received`);
12756
+ console.log(` Chatroom: ${chatroomId}`);
12757
+ console.log(` Role: ${role}`);
12758
+ console.log(` Harness: ${agentHarness}`);
12759
+ if (model) {
12760
+ console.log(` Model: ${model}`);
12761
+ }
12762
+ let agentContext = getAgentContext(chatroomId, role);
12763
+ if (!agentContext && workingDir) {
12764
+ console.log(` No local agent context, using workingDir from command payload`);
12765
+ updateAgentContext(chatroomId, role, agentHarness, workingDir);
12766
+ agentContext = getAgentContext(chatroomId, role);
12767
+ }
12768
+ if (!agentContext) {
12769
+ const msg = `No agent context found for ${chatroomId}/${role}`;
12770
+ console.log(` ⚠️ ${msg}`);
12771
+ return { result: msg, failed: true };
12772
+ }
12773
+ console.log(` Working dir: ${agentContext.workingDir}`);
12774
+ try {
12775
+ const dirStat = await stat(agentContext.workingDir);
12776
+ if (!dirStat.isDirectory()) {
12777
+ const msg = `Working directory is not a directory: ${agentContext.workingDir}`;
12778
+ console.log(` ⚠️ ${msg}`);
12779
+ return { result: msg, failed: true };
12780
+ }
12781
+ } catch {
12782
+ const msg = `Working directory does not exist: ${agentContext.workingDir}`;
12783
+ console.log(` ⚠️ ${msg}`);
12784
+ return { result: msg, failed: true };
12785
+ }
12786
+ const convexUrl = getConvexUrl();
12787
+ const initPromptResult = await ctx.client.query(api.messages.getInitPrompt, {
12788
+ sessionId: ctx.sessionId,
12789
+ chatroomId,
12790
+ role,
12791
+ convexUrl
12792
+ });
12793
+ if (!initPromptResult?.prompt) {
12794
+ const msg = "Failed to fetch init prompt from backend";
12795
+ console.log(` ⚠️ ${msg}`);
12796
+ return { result: msg, failed: true };
12797
+ }
12798
+ console.log(` Fetched split init prompt from backend`);
12799
+ const harnessVersion = ctx.config?.harnessVersions?.[agentHarness];
12800
+ const registry = getDriverRegistry();
12801
+ let driver;
12802
+ try {
12803
+ driver = registry.get(agentHarness);
12804
+ } catch {
12805
+ const msg = `No driver registered for harness: ${agentHarness}`;
12806
+ console.log(` ⚠️ ${msg}`);
12807
+ return { result: msg, failed: true };
12808
+ }
12809
+ const startResult = await driver.start({
12810
+ workingDir: agentContext.workingDir,
12811
+ rolePrompt: initPromptResult.rolePrompt,
12812
+ initialMessage: initPromptResult.initialMessage,
12813
+ harnessVersion: harnessVersion ?? undefined,
12814
+ model
12815
+ });
12816
+ if (startResult.success && startResult.handle) {
12817
+ const msg = `Agent spawned (PID: ${startResult.handle.pid})`;
12818
+ console.log(` ✅ ${msg}`);
12819
+ if (startResult.handle.pid) {
12820
+ try {
12821
+ await ctx.client.mutation(api.machines.updateSpawnedAgent, {
12822
+ sessionId: ctx.sessionId,
12823
+ machineId: ctx.machineId,
12824
+ chatroomId,
12825
+ role,
12826
+ pid: startResult.handle.pid
12827
+ });
12828
+ console.log(` Updated backend with PID: ${startResult.handle.pid}`);
12829
+ persistAgentPid(ctx.machineId, chatroomId, role, startResult.handle.pid, agentHarness);
12830
+ } catch (e) {
12831
+ console.log(` ⚠️ Failed to update PID in backend: ${e.message}`);
12832
+ }
12833
+ }
12834
+ return { result: msg, failed: false };
12835
+ }
12836
+ console.log(` ⚠️ ${startResult.message}`);
12837
+ return { result: startResult.message, failed: true };
12838
+ }
12839
+ async function handleStopAgent(ctx, command) {
12840
+ const { chatroomId, role } = command.payload;
12841
+ console.log(` ↪ stop-agent command received`);
12842
+ console.log(` Chatroom: ${chatroomId}`);
12843
+ console.log(` Role: ${role}`);
12844
+ const configsResult = await ctx.client.query(api.machines.getAgentConfigs, {
12845
+ sessionId: ctx.sessionId,
12846
+ chatroomId
12847
+ });
12848
+ const targetConfig = configsResult.configs.find((c) => c.machineId === ctx.machineId && c.role.toLowerCase() === role.toLowerCase());
12849
+ if (!targetConfig?.spawnedAgentPid) {
12850
+ const msg = "No running agent found (no PID recorded)";
12851
+ console.log(` ⚠️ ${msg}`);
12852
+ return { result: msg, failed: true };
12853
+ }
12854
+ const pidToKill = targetConfig.spawnedAgentPid;
12855
+ const agentHarness = targetConfig.agentType || undefined;
12856
+ console.log(` Stopping agent with PID: ${pidToKill}`);
12857
+ const stopHandle = {
12858
+ harness: agentHarness || "opencode",
12859
+ type: "process",
12860
+ pid: pidToKill,
12861
+ workingDir: ""
12862
+ };
12863
+ const registry = getDriverRegistry();
12864
+ let stopDriver;
12865
+ try {
12866
+ stopDriver = agentHarness ? registry.get(agentHarness) : null;
12867
+ } catch {
12868
+ stopDriver = null;
12869
+ }
12870
+ const isAlive = stopDriver ? await stopDriver.isAlive(stopHandle) : verifyPidOwnership(pidToKill, agentHarness);
12871
+ if (!isAlive) {
12872
+ console.log(` ⚠️ PID ${pidToKill} does not appear to belong to the expected agent`);
12873
+ await clearAgentPidEverywhere(ctx, chatroomId, role);
12874
+ console.log(` Cleared stale PID`);
12875
+ return {
12876
+ result: `PID ${pidToKill} appears stale (process not found or belongs to different program)`,
12877
+ failed: true
12878
+ };
12879
+ }
12880
+ try {
12881
+ if (stopDriver) {
12882
+ await stopDriver.stop(stopHandle);
12883
+ } else {
12884
+ process.kill(pidToKill, "SIGTERM");
12885
+ }
12886
+ const msg = `Agent stopped (PID: ${pidToKill})`;
12887
+ console.log(` ✅ ${msg}`);
12888
+ await clearAgentPidEverywhere(ctx, chatroomId, role);
12889
+ console.log(` Cleared PID`);
12890
+ return { result: msg, failed: false };
12891
+ } catch (e) {
12892
+ const err = e;
12893
+ if (err.code === "ESRCH") {
12894
+ await clearAgentPidEverywhere(ctx, chatroomId, role);
12895
+ const msg2 = "Process not found (may have already exited)";
12896
+ console.log(` ⚠️ ${msg2}`);
12897
+ return { result: msg2, failed: true };
12898
+ }
12899
+ const msg = `Failed to stop agent: ${err.message}`;
12900
+ console.log(` ⚠️ ${msg}`);
12901
+ return { result: msg, failed: true };
12902
+ }
12903
+ }
12904
+ async function processCommand(ctx, command) {
12905
+ console.log(`[${formatTimestamp()}] \uD83D\uDCE8 Command received: ${command.type}`);
12906
+ try {
12907
+ await ctx.client.mutation(api.machines.ackCommand, {
12908
+ sessionId: ctx.sessionId,
12909
+ commandId: command._id,
12910
+ status: "processing"
12911
+ });
12912
+ let commandResult;
12913
+ switch (command.type) {
12914
+ case "ping":
12915
+ commandResult = handlePing();
12916
+ break;
12917
+ case "status":
12918
+ commandResult = handleStatus(ctx);
12919
+ break;
12920
+ case "start-agent":
12921
+ commandResult = await handleStartAgent(ctx, command);
12922
+ break;
12923
+ case "stop-agent":
12924
+ commandResult = await handleStopAgent(ctx, command);
12925
+ break;
12926
+ default: {
12927
+ const _exhaustive = command;
12928
+ commandResult = {
12929
+ result: `Unknown command type: ${_exhaustive.type}`,
12930
+ failed: true
12931
+ };
12932
+ }
12933
+ }
12934
+ const finalStatus = commandResult.failed ? "failed" : "completed";
12935
+ await ctx.client.mutation(api.machines.ackCommand, {
12936
+ sessionId: ctx.sessionId,
12937
+ commandId: command._id,
12938
+ status: finalStatus,
12939
+ result: commandResult.result
12940
+ });
12941
+ if (commandResult.failed) {
12942
+ console.log(` ❌ Command failed: ${commandResult.result}`);
12943
+ } else {
12944
+ console.log(` ✅ Command completed`);
12945
+ }
12946
+ } catch (error) {
12947
+ console.error(` ❌ Command failed: ${error.message}`);
12948
+ try {
12949
+ await ctx.client.mutation(api.machines.ackCommand, {
12950
+ sessionId: ctx.sessionId,
12951
+ commandId: command._id,
12952
+ status: "failed",
12953
+ result: error.message
12954
+ });
12955
+ } catch {}
12956
+ }
12957
+ }
12958
+ async function initDaemon() {
12959
+ if (!acquireLock()) {
12960
+ process.exit(1);
12961
+ }
12962
+ const convexUrl = getConvexUrl();
12963
+ const sessionId = getSessionId();
12964
+ if (!sessionId) {
12965
+ const otherUrls = getOtherSessionUrls();
12966
+ console.error(`❌ Not authenticated for: ${convexUrl}`);
12967
+ if (otherUrls.length > 0) {
12968
+ console.error(`
12969
+ \uD83D\uDCA1 You have sessions for other environments:`);
12970
+ for (const url of otherUrls) {
12971
+ console.error(` • ${url}`);
12972
+ }
12973
+ }
12974
+ console.error(`
12975
+ Run: chatroom auth login`);
12976
+ releaseLock();
12977
+ process.exit(1);
12978
+ }
12979
+ const machineId = getMachineId();
12980
+ if (!machineId) {
12981
+ console.error(`❌ Machine not registered`);
12982
+ console.error(`
12983
+ Run any chatroom command first to register this machine,`);
12984
+ console.error(`for example: chatroom auth status`);
12985
+ releaseLock();
12986
+ process.exit(1);
12987
+ }
12988
+ const client2 = await getConvexClient();
12989
+ const typedSessionId = sessionId;
12990
+ try {
12991
+ await client2.mutation(api.machines.updateDaemonStatus, {
12992
+ sessionId: typedSessionId,
12993
+ machineId,
12994
+ connected: true
12995
+ });
12996
+ } catch (error) {
12997
+ console.error(`❌ Failed to update daemon status: ${error.message}`);
12998
+ releaseLock();
12999
+ process.exit(1);
13000
+ }
13001
+ const config3 = loadMachineConfig();
13002
+ const ctx = { client: client2, sessionId: typedSessionId, machineId, config: config3 };
13003
+ console.log(`[${formatTimestamp()}] \uD83D\uDE80 Daemon started`);
13004
+ console.log(` Machine ID: ${machineId}`);
13005
+ console.log(` Hostname: ${config3?.hostname ?? "Unknown"}`);
13006
+ console.log(` Available harnesses: ${config3?.availableHarnesses.join(", ") || "none"}`);
13007
+ console.log(` PID: ${process.pid}`);
13008
+ console.log(`
13009
+ [${formatTimestamp()}] \uD83D\uDD04 Recovering agent state...`);
13010
+ try {
13011
+ await recoverAgentState(ctx);
13012
+ } catch (e) {
13013
+ console.log(` ⚠️ Recovery failed: ${e.message}`);
13014
+ console.log(` Continuing with fresh state`);
13015
+ }
13016
+ return ctx;
13017
+ }
13018
+ async function startCommandLoop(ctx) {
13019
+ const shutdown = async () => {
13020
+ console.log(`
13021
+ [${formatTimestamp()}] Shutting down...`);
13022
+ try {
13023
+ await ctx.client.mutation(api.machines.updateDaemonStatus, {
13024
+ sessionId: ctx.sessionId,
13025
+ machineId: ctx.machineId,
13026
+ connected: false
13027
+ });
13028
+ } catch {}
13029
+ releaseLock();
13030
+ process.exit(0);
13031
+ };
13032
+ process.on("SIGINT", shutdown);
13033
+ process.on("SIGTERM", shutdown);
13034
+ process.on("SIGHUP", shutdown);
13035
+ const wsClient2 = await getConvexWsClient();
13036
+ const commandQueue = [];
13037
+ const queuedCommandIds = new Set;
13038
+ let drainingQueue = false;
13039
+ const enqueueCommands = (commands) => {
13040
+ for (const command of commands) {
13041
+ const commandId = command._id.toString();
13042
+ if (queuedCommandIds.has(commandId))
13043
+ continue;
13044
+ queuedCommandIds.add(commandId);
13045
+ commandQueue.push(command);
13046
+ }
13047
+ };
13048
+ const drainQueue = async () => {
13049
+ if (drainingQueue)
13050
+ return;
13051
+ drainingQueue = true;
13052
+ try {
13053
+ while (commandQueue.length > 0) {
13054
+ const command = commandQueue.shift();
13055
+ const commandId = command._id.toString();
13056
+ queuedCommandIds.delete(commandId);
13057
+ try {
13058
+ await processCommand(ctx, command);
13059
+ } catch (error) {
13060
+ console.error(` ❌ Command processing failed: ${error.message}`);
13061
+ }
13062
+ }
13063
+ } finally {
13064
+ drainingQueue = false;
13065
+ }
13066
+ };
13067
+ console.log(`
13068
+ Listening for commands...`);
13069
+ console.log(`Press Ctrl+C to stop
13070
+ `);
13071
+ wsClient2.onUpdate(api.machines.getPendingCommands, {
13072
+ sessionId: ctx.sessionId,
13073
+ machineId: ctx.machineId
13074
+ }, async (result) => {
13075
+ if (!result.commands || result.commands.length === 0)
13076
+ return;
13077
+ const parsed = result.commands.map(parseMachineCommand).filter((c) => c !== null);
13078
+ enqueueCommands(parsed);
13079
+ await drainQueue();
13080
+ });
13081
+ return await new Promise(() => {});
13082
+ }
13083
+ async function daemonStart() {
13084
+ const ctx = await initDaemon();
13085
+ await startCommandLoop(ctx);
13086
+ }
13087
+ var init_daemon_start = __esm(() => {
13088
+ init_pid();
13089
+ init_api3();
13090
+ init_agent_drivers();
13091
+ init_storage();
13092
+ init_client2();
13093
+ init_machine();
13094
+ });
13095
+
13096
+ // src/commands/machine/daemon-stop.ts
13097
+ async function daemonStop() {
13098
+ const { running, pid } = isDaemonRunning();
13099
+ if (!running) {
13100
+ console.log(`⚪ Daemon is not running`);
13101
+ return;
13102
+ }
13103
+ console.log(`Stopping daemon (PID: ${pid})...`);
13104
+ try {
13105
+ process.kill(pid, "SIGTERM");
13106
+ await new Promise((resolve2) => setTimeout(resolve2, 1000));
13107
+ try {
13108
+ process.kill(pid, 0);
13109
+ console.log(`Process did not exit gracefully, forcing...`);
13110
+ process.kill(pid, "SIGKILL");
13111
+ } catch {}
13112
+ removePid();
13113
+ console.log(`✅ Daemon stopped`);
13114
+ } catch (error) {
13115
+ console.error(`❌ Failed to stop daemon: ${error.message}`);
13116
+ removePid();
13117
+ }
13118
+ }
13119
+ var init_daemon_stop = __esm(() => {
13120
+ init_pid();
13121
+ });
13122
+
13123
+ // src/commands/machine/daemon-status.ts
13124
+ async function daemonStatus() {
13125
+ const { running, pid } = isDaemonRunning();
13126
+ if (running) {
13127
+ console.log(`✅ Daemon is running`);
13128
+ console.log(` PID: ${pid}`);
13129
+ console.log(` PID file: ${getPidFilePath()}`);
13130
+ } else {
13131
+ console.log(`⚪ Daemon is not running`);
13132
+ console.log(`
13133
+ To start the daemon:`);
13134
+ console.log(` chatroom machine daemon start`);
13135
+ }
13136
+ }
13137
+ var init_daemon_status = __esm(() => {
13138
+ init_pid();
13139
+ });
13140
+
13141
+ // src/commands/machine/index.ts
13142
+ var exports_machine = {};
13143
+ __export(exports_machine, {
13144
+ daemonStop: () => daemonStop,
13145
+ daemonStatus: () => daemonStatus,
13146
+ daemonStart: () => daemonStart
13147
+ });
13148
+ var init_machine2 = __esm(() => {
13149
+ init_daemon_start();
13150
+ init_daemon_stop();
13151
+ init_daemon_status();
13152
+ });
13153
+
12040
13154
  // src/commands/opencode-install.ts
12041
13155
  var exports_opencode_install = {};
12042
13156
  __export(exports_opencode_install, {
@@ -12044,8 +13158,8 @@ __export(exports_opencode_install, {
12044
13158
  });
12045
13159
  async function isChatroomInstalled() {
12046
13160
  try {
12047
- const { execSync } = await import("child_process");
12048
- execSync("chatroom --version", { stdio: "pipe" });
13161
+ const { execSync: execSync4 } = await import("child_process");
13162
+ execSync4("chatroom --version", { stdio: "pipe" });
12049
13163
  return true;
12050
13164
  } catch {
12051
13165
  return false;
@@ -12740,8 +13854,23 @@ artifactCommand.command("view-many").description("View multiple artifacts").requ
12740
13854
  artifactIds: options.artifact || []
12741
13855
  });
12742
13856
  });
12743
- var opencodeCommand = program2.command("opencode").description("OpenCode integration tools");
12744
- opencodeCommand.command("install").description("Install chatroom as an OpenCode tool").option("--force", "Overwrite existing tool installation").action(async (options) => {
13857
+ var machineCommand = program2.command("machine").description("Machine daemon management for remote agent control");
13858
+ var daemonCommand = machineCommand.command("daemon").description("Manage the machine daemon");
13859
+ daemonCommand.command("start").description("Start the machine daemon to listen for remote commands").action(async () => {
13860
+ await maybeRequireAuth();
13861
+ const { daemonStart: daemonStart2 } = await Promise.resolve().then(() => (init_machine2(), exports_machine));
13862
+ await daemonStart2();
13863
+ });
13864
+ daemonCommand.command("stop").description("Stop the running machine daemon").action(async () => {
13865
+ const { daemonStop: daemonStop2 } = await Promise.resolve().then(() => (init_machine2(), exports_machine));
13866
+ await daemonStop2();
13867
+ });
13868
+ daemonCommand.command("status").description("Check if the machine daemon is running").action(async () => {
13869
+ const { daemonStatus: daemonStatus2 } = await Promise.resolve().then(() => (init_machine2(), exports_machine));
13870
+ await daemonStatus2();
13871
+ });
13872
+ var opencodeCommand = program2.command("opencode").description("OpenCode integration harness");
13873
+ opencodeCommand.command("install").description("Install chatroom as an OpenCode harness").option("--force", "Overwrite existing harness installation").action(async (options) => {
12745
13874
  const { installTool: installTool2 } = await Promise.resolve().then(() => exports_opencode_install);
12746
13875
  await installTool2({ checkExisting: !options.force });
12747
13876
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatroom-cli",
3
- "version": "1.0.64",
3
+ "version": "1.0.66",
4
4
  "description": "CLI for multi-agent chatroom collaboration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "convex"
35
35
  ],
36
36
  "author": "",
37
- "license": "ISC",
37
+ "license": "Elastic-2.0",
38
38
  "repository": {
39
39
  "type": "git",
40
40
  "url": "https://github.com/conradkoh/chatroom"