chatroom-cli 1.0.65 → 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 +1124 -20
  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,6 +10722,45 @@ 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;
10269
10766
  const connectionId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
@@ -10385,20 +10882,6 @@ ${"─".repeat(50)}`);
10385
10882
  status: "active",
10386
10883
  expiresAt: activeUntil
10387
10884
  });
10388
- if (message && message.type === "interrupt") {
10389
- const interruptTime = new Date().toISOString().replace("T", " ").substring(0, 19);
10390
- console.log(`
10391
- ${"─".repeat(50)}`);
10392
- console.log(`⚠️ RECONNECTION REQUIRED
10393
- `);
10394
- console.log(`[${interruptTime}] Why: Interrupt message received from team`);
10395
- console.log(`Impact: You are no longer listening for tasks`);
10396
- console.log(`Action: Run this command immediately to resume availability
10397
- `);
10398
- console.log(waitForTaskCommand({ chatroomId, role, cliEnvPrefix }));
10399
- console.log(`${"─".repeat(50)}`);
10400
- process.exit(0);
10401
- }
10402
10885
  const taskDeliveryPrompt = await client2.query(api.messages.getTaskDeliveryPrompt, {
10403
10886
  sessionId,
10404
10887
  chatroomId,
@@ -10551,8 +11034,10 @@ var init_wait_for_task = __esm(() => {
10551
11034
  init_env();
10552
11035
  init_api3();
10553
11036
  init_config2();
11037
+ init_agent_drivers();
10554
11038
  init_storage();
10555
11039
  init_client2();
11040
+ init_machine();
10556
11041
  });
10557
11042
 
10558
11043
  // src/commands/task-started.ts
@@ -11573,12 +12058,12 @@ __export(exports_file_content, {
11573
12058
  resolveContent: () => resolveContent,
11574
12059
  readFileContent: () => readFileContent
11575
12060
  });
11576
- import { readFileSync as readFileSync3 } from "fs";
12061
+ import { readFileSync as readFileSync5 } from "fs";
11577
12062
  import { resolve } from "path";
11578
12063
  function readFileContent(filePath, optionName) {
11579
12064
  const absolutePath = resolve(process.cwd(), filePath);
11580
12065
  try {
11581
- return readFileSync3(absolutePath, "utf-8");
12066
+ return readFileSync5(absolutePath, "utf-8");
11582
12067
  } catch (err) {
11583
12068
  const nodeErr = err;
11584
12069
  throw new Error(`Cannot read file for --${optionName}: ${absolutePath}
@@ -12062,6 +12547,610 @@ var init_artifact = __esm(() => {
12062
12547
  init_file_content();
12063
12548
  });
12064
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
+
12065
13154
  // src/commands/opencode-install.ts
12066
13155
  var exports_opencode_install = {};
12067
13156
  __export(exports_opencode_install, {
@@ -12069,8 +13158,8 @@ __export(exports_opencode_install, {
12069
13158
  });
12070
13159
  async function isChatroomInstalled() {
12071
13160
  try {
12072
- const { execSync } = await import("child_process");
12073
- execSync("chatroom --version", { stdio: "pipe" });
13161
+ const { execSync: execSync4 } = await import("child_process");
13162
+ execSync4("chatroom --version", { stdio: "pipe" });
12074
13163
  return true;
12075
13164
  } catch {
12076
13165
  return false;
@@ -12765,8 +13854,23 @@ artifactCommand.command("view-many").description("View multiple artifacts").requ
12765
13854
  artifactIds: options.artifact || []
12766
13855
  });
12767
13856
  });
12768
- var opencodeCommand = program2.command("opencode").description("OpenCode integration tools");
12769
- 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) => {
12770
13874
  const { installTool: installTool2 } = await Promise.resolve().then(() => exports_opencode_install);
12771
13875
  await installTool2({ checkExisting: !options.force });
12772
13876
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatroom-cli",
3
- "version": "1.0.65",
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"