chatroom-cli 1.2.4 → 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +256 -361
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10371,6 +10371,20 @@ function listAgentEntries(machineId) {
|
|
|
10371
10371
|
}
|
|
10372
10372
|
return results;
|
|
10373
10373
|
}
|
|
10374
|
+
function persistEventCursor(machineId, lastSeenEventId) {
|
|
10375
|
+
try {
|
|
10376
|
+
const state = loadOrCreate(machineId);
|
|
10377
|
+
state.lastSeenEventId = lastSeenEventId;
|
|
10378
|
+
state.updatedAt = new Date().toISOString();
|
|
10379
|
+
saveDaemonState(state);
|
|
10380
|
+
} catch (err) {
|
|
10381
|
+
console.warn(`⚠️ Failed to persist event cursor: ${err.message}`);
|
|
10382
|
+
}
|
|
10383
|
+
}
|
|
10384
|
+
function loadEventCursor(machineId) {
|
|
10385
|
+
const state = loadDaemonState(machineId);
|
|
10386
|
+
return state?.lastSeenEventId ?? null;
|
|
10387
|
+
}
|
|
10374
10388
|
var CHATROOM_DIR3, STATE_DIR, STATE_VERSION = "1";
|
|
10375
10389
|
var init_daemon_state = __esm(() => {
|
|
10376
10390
|
CHATROOM_DIR3 = join4(homedir3(), ".chatroom");
|
|
@@ -11314,7 +11328,7 @@ var init_register_agent = __esm(() => {
|
|
|
11314
11328
|
init_opencode();
|
|
11315
11329
|
init_pi();
|
|
11316
11330
|
});
|
|
11317
|
-
// ../../services/backend/prompts/
|
|
11331
|
+
// ../../services/backend/prompts/cli/task-started/command.ts
|
|
11318
11332
|
function taskStartedCommand(params) {
|
|
11319
11333
|
const prefix = params.cliEnvPrefix || "";
|
|
11320
11334
|
const chatroomId = params.chatroomId || "<chatroom-id>";
|
|
@@ -11338,22 +11352,22 @@ EOF`;
|
|
|
11338
11352
|
return baseCmd;
|
|
11339
11353
|
}
|
|
11340
11354
|
|
|
11341
|
-
// ../../services/backend/prompts/
|
|
11355
|
+
// ../../services/backend/prompts/cli/task-started/classification/new-feature.ts
|
|
11342
11356
|
var init_new_feature = () => {};
|
|
11343
|
-
// ../../services/backend/prompts/
|
|
11357
|
+
// ../../services/backend/prompts/cli/task-started/classification/index.ts
|
|
11344
11358
|
var init_classification = __esm(() => {
|
|
11345
11359
|
init_new_feature();
|
|
11346
11360
|
});
|
|
11347
|
-
// ../../services/backend/prompts/
|
|
11361
|
+
// ../../services/backend/prompts/cli/task-started/main-prompt.ts
|
|
11348
11362
|
var init_main_prompt = () => {};
|
|
11349
11363
|
|
|
11350
|
-
// ../../services/backend/prompts/
|
|
11364
|
+
// ../../services/backend/prompts/cli/task-started/index.ts
|
|
11351
11365
|
var init_task_started = __esm(() => {
|
|
11352
11366
|
init_classification();
|
|
11353
11367
|
init_main_prompt();
|
|
11354
11368
|
});
|
|
11355
11369
|
|
|
11356
|
-
// ../../services/backend/prompts/
|
|
11370
|
+
// ../../services/backend/prompts/cli/get-next-task/reminder.ts
|
|
11357
11371
|
function getNextTaskGuidance() {
|
|
11358
11372
|
return `\uD83D\uDD17 STAYING CONNECTED TO YOUR TEAM
|
|
11359
11373
|
|
|
@@ -11442,7 +11456,7 @@ var init_getting_started_content = __esm(() => {
|
|
|
11442
11456
|
init_utils();
|
|
11443
11457
|
});
|
|
11444
11458
|
|
|
11445
|
-
// ../../services/backend/prompts/
|
|
11459
|
+
// ../../services/backend/prompts/cli/index.ts
|
|
11446
11460
|
var init_cli = __esm(() => {
|
|
11447
11461
|
init_task_started();
|
|
11448
11462
|
init_reminder();
|
|
@@ -11468,7 +11482,7 @@ var init_errorCodes = __esm(() => {
|
|
|
11468
11482
|
];
|
|
11469
11483
|
});
|
|
11470
11484
|
|
|
11471
|
-
// ../../services/backend/prompts/
|
|
11485
|
+
// ../../services/backend/prompts/cli/get-next-task/command.ts
|
|
11472
11486
|
function getNextTaskCommand(params) {
|
|
11473
11487
|
const prefix = params.cliEnvPrefix || "";
|
|
11474
11488
|
const chatroomId = params.chatroomId || "<chatroom-id>";
|
|
@@ -12169,7 +12183,7 @@ function formatDecodeError(error) {
|
|
|
12169
12183
|
return message;
|
|
12170
12184
|
}
|
|
12171
12185
|
|
|
12172
|
-
// ../../services/backend/prompts/
|
|
12186
|
+
// ../../services/backend/prompts/cli/handoff/command.ts
|
|
12173
12187
|
function handoffCommand(params) {
|
|
12174
12188
|
const prefix = params.cliEnvPrefix || "";
|
|
12175
12189
|
const chatroomId = params.chatroomId || "<chatroom-id>";
|
|
@@ -13643,6 +13657,7 @@ async function getSystemPrompt(chatroomId, options, deps) {
|
|
|
13643
13657
|
const prompt = await d.backend.query(api.prompts.webapp.getAgentPrompt, {
|
|
13644
13658
|
chatroomId,
|
|
13645
13659
|
role,
|
|
13660
|
+
teamId: chatroom.teamId,
|
|
13646
13661
|
teamName: chatroom.teamName,
|
|
13647
13662
|
teamRoles: chatroom.teamRoles,
|
|
13648
13663
|
teamEntryPoint: chatroom.teamEntryPoint,
|
|
@@ -13663,66 +13678,14 @@ var init_get_system_prompt = __esm(() => {
|
|
|
13663
13678
|
});
|
|
13664
13679
|
|
|
13665
13680
|
// ../../services/backend/config/reliability.ts
|
|
13666
|
-
var DAEMON_HEARTBEAT_INTERVAL_MS = 30000;
|
|
13681
|
+
var DAEMON_HEARTBEAT_INTERVAL_MS = 30000, AGENT_REQUEST_DEADLINE_MS = 120000;
|
|
13667
13682
|
|
|
13668
13683
|
// src/commands/machine/daemon-start/utils.ts
|
|
13669
13684
|
function formatTimestamp() {
|
|
13670
13685
|
return new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
13671
13686
|
}
|
|
13672
|
-
function parseMachineCommand(raw) {
|
|
13673
|
-
switch (raw.type) {
|
|
13674
|
-
case "ping":
|
|
13675
|
-
return { _id: raw._id, type: "ping", payload: {}, createdAt: raw.createdAt };
|
|
13676
|
-
case "status":
|
|
13677
|
-
return { _id: raw._id, type: "status", payload: {}, createdAt: raw.createdAt };
|
|
13678
|
-
case "start-agent": {
|
|
13679
|
-
const { chatroomId, role, agentHarness } = raw.payload;
|
|
13680
|
-
if (!chatroomId || !role || !agentHarness) {
|
|
13681
|
-
console.error(` ⚠️ Invalid start-agent command: missing chatroomId, role, or agentHarness`);
|
|
13682
|
-
return null;
|
|
13683
|
-
}
|
|
13684
|
-
if (!raw.reason) {
|
|
13685
|
-
console.error(` ⚠️ Invalid start-agent command: missing required reason field`);
|
|
13686
|
-
return null;
|
|
13687
|
-
}
|
|
13688
|
-
return {
|
|
13689
|
-
_id: raw._id,
|
|
13690
|
-
type: "start-agent",
|
|
13691
|
-
reason: raw.reason,
|
|
13692
|
-
payload: {
|
|
13693
|
-
chatroomId,
|
|
13694
|
-
role,
|
|
13695
|
-
agentHarness,
|
|
13696
|
-
model: raw.payload.model,
|
|
13697
|
-
workingDir: raw.payload.workingDir
|
|
13698
|
-
},
|
|
13699
|
-
createdAt: raw.createdAt
|
|
13700
|
-
};
|
|
13701
|
-
}
|
|
13702
|
-
case "stop-agent": {
|
|
13703
|
-
const { chatroomId, role } = raw.payload;
|
|
13704
|
-
if (!chatroomId || !role) {
|
|
13705
|
-
console.error(` ⚠️ Invalid stop-agent command: missing chatroomId or role`);
|
|
13706
|
-
return null;
|
|
13707
|
-
}
|
|
13708
|
-
if (!raw.reason) {
|
|
13709
|
-
console.error(` ⚠️ Invalid stop-agent command: missing required reason field`);
|
|
13710
|
-
return null;
|
|
13711
|
-
}
|
|
13712
|
-
return {
|
|
13713
|
-
_id: raw._id,
|
|
13714
|
-
type: "stop-agent",
|
|
13715
|
-
reason: raw.reason,
|
|
13716
|
-
payload: { chatroomId, role },
|
|
13717
|
-
createdAt: raw.createdAt
|
|
13718
|
-
};
|
|
13719
|
-
}
|
|
13720
|
-
default:
|
|
13721
|
-
return null;
|
|
13722
|
-
}
|
|
13723
|
-
}
|
|
13724
13687
|
|
|
13725
|
-
// src/
|
|
13688
|
+
// src/events/lifecycle/on-agent-shutdown.ts
|
|
13726
13689
|
async function onAgentShutdown(ctx, options) {
|
|
13727
13690
|
const { chatroomId, role, pid, skipKill } = options;
|
|
13728
13691
|
try {
|
|
@@ -13781,42 +13744,13 @@ async function onAgentShutdown(ctx, options) {
|
|
|
13781
13744
|
console.log(` ⚠️ Failed to clear local PID for ${role}: ${e.message}`);
|
|
13782
13745
|
}
|
|
13783
13746
|
}
|
|
13784
|
-
let spawnedAgentCleared = false;
|
|
13785
|
-
if (killed || skipKill) {
|
|
13786
|
-
try {
|
|
13787
|
-
await ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
13788
|
-
sessionId: ctx.sessionId,
|
|
13789
|
-
machineId: ctx.machineId,
|
|
13790
|
-
chatroomId,
|
|
13791
|
-
role,
|
|
13792
|
-
pid: undefined
|
|
13793
|
-
});
|
|
13794
|
-
spawnedAgentCleared = true;
|
|
13795
|
-
} catch (e) {
|
|
13796
|
-
console.log(` ⚠️ Failed to clear spawnedAgent for ${role}: ${e.message}`);
|
|
13797
|
-
}
|
|
13798
|
-
}
|
|
13799
|
-
let participantRemoved = false;
|
|
13800
|
-
try {
|
|
13801
|
-
await ctx.deps.backend.mutation(api.participants.leave, {
|
|
13802
|
-
sessionId: ctx.sessionId,
|
|
13803
|
-
chatroomId,
|
|
13804
|
-
role
|
|
13805
|
-
});
|
|
13806
|
-
participantRemoved = true;
|
|
13807
|
-
} catch (e) {
|
|
13808
|
-
console.log(` ⚠️ Failed to remove participant for ${role}: ${e.message}`);
|
|
13809
|
-
}
|
|
13810
13747
|
return {
|
|
13811
13748
|
killed: killed || (skipKill ?? false),
|
|
13812
|
-
cleaned:
|
|
13749
|
+
cleaned: killed || (skipKill ?? false)
|
|
13813
13750
|
};
|
|
13814
13751
|
}
|
|
13815
|
-
var init_on_agent_shutdown = __esm(() => {
|
|
13816
|
-
init_api3();
|
|
13817
|
-
});
|
|
13818
13752
|
|
|
13819
|
-
// src/
|
|
13753
|
+
// src/events/lifecycle/on-daemon-shutdown.ts
|
|
13820
13754
|
async function onDaemonShutdown(ctx) {
|
|
13821
13755
|
const agents = ctx.deps.machine.listAgentEntries(ctx.machineId);
|
|
13822
13756
|
if (agents.length > 0) {
|
|
@@ -13855,110 +13789,17 @@ async function onDaemonShutdown(ctx) {
|
|
|
13855
13789
|
var AGENT_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
13856
13790
|
var init_on_daemon_shutdown = __esm(() => {
|
|
13857
13791
|
init_api3();
|
|
13858
|
-
init_on_agent_shutdown();
|
|
13859
13792
|
});
|
|
13860
13793
|
|
|
13861
|
-
// src/commands/machine/pid.ts
|
|
13862
|
-
import { createHash } from "node:crypto";
|
|
13863
|
-
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "node:fs";
|
|
13864
|
-
import { homedir as homedir4 } from "node:os";
|
|
13865
|
-
import { join as join5 } from "node:path";
|
|
13866
|
-
function getUrlHash() {
|
|
13867
|
-
const url = getConvexUrl();
|
|
13868
|
-
return createHash("sha256").update(url).digest("hex").substring(0, 8);
|
|
13869
|
-
}
|
|
13870
|
-
function getPidFileName() {
|
|
13871
|
-
return `daemon-${getUrlHash()}.pid`;
|
|
13872
|
-
}
|
|
13873
|
-
function ensureChatroomDir() {
|
|
13874
|
-
if (!existsSync4(CHATROOM_DIR4)) {
|
|
13875
|
-
mkdirSync4(CHATROOM_DIR4, { recursive: true, mode: 448 });
|
|
13876
|
-
}
|
|
13877
|
-
}
|
|
13878
|
-
function getPidFilePath() {
|
|
13879
|
-
return join5(CHATROOM_DIR4, getPidFileName());
|
|
13880
|
-
}
|
|
13881
|
-
function isProcessRunning(pid) {
|
|
13882
|
-
try {
|
|
13883
|
-
process.kill(pid, 0);
|
|
13884
|
-
return true;
|
|
13885
|
-
} catch {
|
|
13886
|
-
return false;
|
|
13887
|
-
}
|
|
13888
|
-
}
|
|
13889
|
-
function readPid() {
|
|
13890
|
-
const pidPath = getPidFilePath();
|
|
13891
|
-
if (!existsSync4(pidPath)) {
|
|
13892
|
-
return null;
|
|
13893
|
-
}
|
|
13894
|
-
try {
|
|
13895
|
-
const content = readFileSync6(pidPath, "utf-8").trim();
|
|
13896
|
-
const pid = parseInt(content, 10);
|
|
13897
|
-
if (isNaN(pid) || pid <= 0) {
|
|
13898
|
-
return null;
|
|
13899
|
-
}
|
|
13900
|
-
return pid;
|
|
13901
|
-
} catch {
|
|
13902
|
-
return null;
|
|
13903
|
-
}
|
|
13904
|
-
}
|
|
13905
|
-
function writePid() {
|
|
13906
|
-
ensureChatroomDir();
|
|
13907
|
-
const pidPath = getPidFilePath();
|
|
13908
|
-
writeFileSync4(pidPath, process.pid.toString(), "utf-8");
|
|
13909
|
-
}
|
|
13910
|
-
function removePid() {
|
|
13911
|
-
const pidPath = getPidFilePath();
|
|
13912
|
-
try {
|
|
13913
|
-
if (existsSync4(pidPath)) {
|
|
13914
|
-
unlinkSync2(pidPath);
|
|
13915
|
-
}
|
|
13916
|
-
} catch {}
|
|
13917
|
-
}
|
|
13918
|
-
function isDaemonRunning() {
|
|
13919
|
-
const pid = readPid();
|
|
13920
|
-
if (pid === null) {
|
|
13921
|
-
return { running: false, pid: null };
|
|
13922
|
-
}
|
|
13923
|
-
if (isProcessRunning(pid)) {
|
|
13924
|
-
return { running: true, pid };
|
|
13925
|
-
}
|
|
13926
|
-
removePid();
|
|
13927
|
-
return { running: false, pid: null };
|
|
13928
|
-
}
|
|
13929
|
-
function acquireLock() {
|
|
13930
|
-
const { running, pid } = isDaemonRunning();
|
|
13931
|
-
if (running) {
|
|
13932
|
-
console.error(`❌ Daemon already running for ${getConvexUrl()} (PID: ${pid})`);
|
|
13933
|
-
return false;
|
|
13934
|
-
}
|
|
13935
|
-
writePid();
|
|
13936
|
-
return true;
|
|
13937
|
-
}
|
|
13938
|
-
function releaseLock() {
|
|
13939
|
-
removePid();
|
|
13940
|
-
}
|
|
13941
|
-
var CHATROOM_DIR4;
|
|
13942
|
-
var init_pid = __esm(() => {
|
|
13943
|
-
init_client2();
|
|
13944
|
-
CHATROOM_DIR4 = join5(homedir4(), ".chatroom");
|
|
13945
|
-
});
|
|
13946
|
-
|
|
13947
|
-
// src/commands/machine/daemon-start/handlers/ping.ts
|
|
13948
|
-
function handlePing() {
|
|
13949
|
-
console.log(` ↪ Responding: pong`);
|
|
13950
|
-
return { result: "pong", failed: false };
|
|
13951
|
-
}
|
|
13952
|
-
|
|
13953
13794
|
// src/commands/machine/daemon-start/handlers/start-agent.ts
|
|
13954
|
-
async function
|
|
13955
|
-
const { chatroomId, role, agentHarness, model, workingDir } =
|
|
13795
|
+
async function executeStartAgent(ctx, args) {
|
|
13796
|
+
const { chatroomId, role, agentHarness, model, workingDir, reason } = args;
|
|
13956
13797
|
console.log(` ↪ start-agent command received`);
|
|
13957
13798
|
console.log(` Chatroom: ${chatroomId}`);
|
|
13958
13799
|
console.log(` Role: ${role}`);
|
|
13959
13800
|
console.log(` Harness: ${agentHarness}`);
|
|
13960
|
-
if (
|
|
13961
|
-
console.log(` Reason: ${
|
|
13801
|
+
if (reason) {
|
|
13802
|
+
console.log(` Reason: ${reason}`);
|
|
13962
13803
|
}
|
|
13963
13804
|
if (model) {
|
|
13964
13805
|
console.log(` Model: ${model}`);
|
|
@@ -14090,19 +13931,26 @@ async function handleStartAgent(ctx, command) {
|
|
|
14090
13931
|
var init_start_agent = __esm(() => {
|
|
14091
13932
|
init_api3();
|
|
14092
13933
|
init_client2();
|
|
14093
|
-
init_on_agent_shutdown();
|
|
14094
13934
|
});
|
|
14095
13935
|
|
|
14096
|
-
// src/
|
|
14097
|
-
function
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
|
|
13936
|
+
// src/events/daemon/agent/on-request-start-agent.ts
|
|
13937
|
+
async function onRequestStartAgent(ctx, event) {
|
|
13938
|
+
if (Date.now() > event.deadline) {
|
|
13939
|
+
console.log(`[daemon] ⏰ Skipping expired agent.requestStart for role=${event.role} (deadline passed)`);
|
|
13940
|
+
return;
|
|
13941
|
+
}
|
|
13942
|
+
await executeStartAgent(ctx, {
|
|
13943
|
+
chatroomId: event.chatroomId,
|
|
13944
|
+
role: event.role,
|
|
13945
|
+
agentHarness: event.agentHarness,
|
|
13946
|
+
model: event.model,
|
|
13947
|
+
workingDir: event.workingDir,
|
|
13948
|
+
reason: event.reason
|
|
14102
13949
|
});
|
|
14103
|
-
console.log(` ↪ Responding with status`);
|
|
14104
|
-
return { result, failed: false };
|
|
14105
13950
|
}
|
|
13951
|
+
var init_on_request_start_agent = __esm(() => {
|
|
13952
|
+
init_start_agent();
|
|
13953
|
+
});
|
|
14106
13954
|
|
|
14107
13955
|
// src/commands/machine/daemon-start/handlers/shared.ts
|
|
14108
13956
|
async function clearAgentPidEverywhere(ctx, chatroomId, role) {
|
|
@@ -14124,8 +13972,8 @@ var init_shared = __esm(() => {
|
|
|
14124
13972
|
});
|
|
14125
13973
|
|
|
14126
13974
|
// src/commands/machine/daemon-start/handlers/stop-agent.ts
|
|
14127
|
-
async function
|
|
14128
|
-
const { chatroomId, role } =
|
|
13975
|
+
async function executeStopAgent(ctx, args) {
|
|
13976
|
+
const { chatroomId, role } = args;
|
|
14129
13977
|
console.log(` ↪ stop-agent command received`);
|
|
14130
13978
|
console.log(` Chatroom: ${chatroomId}`);
|
|
14131
13979
|
console.log(` Role: ${role}`);
|
|
@@ -14150,7 +13998,7 @@ async function handleStopAgent(ctx, command) {
|
|
|
14150
13998
|
console.log(` Stopping agent with PID: ${pid}`);
|
|
14151
13999
|
const isAlive = anyService ? anyService.isAlive(pid) : false;
|
|
14152
14000
|
if (!isAlive) {
|
|
14153
|
-
console.log(` ⚠️ PID ${pid}
|
|
14001
|
+
console.log(` ⚠️ PID ${pid} not found — process already exited or was never started`);
|
|
14154
14002
|
await clearAgentPidEverywhere(ctx, chatroomId, role);
|
|
14155
14003
|
console.log(` Cleared stale PID`);
|
|
14156
14004
|
try {
|
|
@@ -14195,10 +14043,117 @@ async function handleStopAgent(ctx, command) {
|
|
|
14195
14043
|
}
|
|
14196
14044
|
var init_stop_agent = __esm(() => {
|
|
14197
14045
|
init_api3();
|
|
14198
|
-
init_on_agent_shutdown();
|
|
14199
14046
|
init_shared();
|
|
14200
14047
|
});
|
|
14201
14048
|
|
|
14049
|
+
// src/events/daemon/agent/on-request-stop-agent.ts
|
|
14050
|
+
async function onRequestStopAgent(ctx, event) {
|
|
14051
|
+
if (Date.now() > event.deadline) {
|
|
14052
|
+
console.log(`[daemon] ⏰ Skipping expired agent.requestStop for role=${event.role} (deadline passed)`);
|
|
14053
|
+
return;
|
|
14054
|
+
}
|
|
14055
|
+
await executeStopAgent(ctx, {
|
|
14056
|
+
chatroomId: event.chatroomId,
|
|
14057
|
+
role: event.role,
|
|
14058
|
+
reason: event.reason
|
|
14059
|
+
});
|
|
14060
|
+
}
|
|
14061
|
+
var init_on_request_stop_agent = __esm(() => {
|
|
14062
|
+
init_stop_agent();
|
|
14063
|
+
});
|
|
14064
|
+
|
|
14065
|
+
// src/commands/machine/pid.ts
|
|
14066
|
+
import { createHash } from "node:crypto";
|
|
14067
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "node:fs";
|
|
14068
|
+
import { homedir as homedir4 } from "node:os";
|
|
14069
|
+
import { join as join5 } from "node:path";
|
|
14070
|
+
function getUrlHash() {
|
|
14071
|
+
const url = getConvexUrl();
|
|
14072
|
+
return createHash("sha256").update(url).digest("hex").substring(0, 8);
|
|
14073
|
+
}
|
|
14074
|
+
function getPidFileName() {
|
|
14075
|
+
return `daemon-${getUrlHash()}.pid`;
|
|
14076
|
+
}
|
|
14077
|
+
function ensureChatroomDir() {
|
|
14078
|
+
if (!existsSync4(CHATROOM_DIR4)) {
|
|
14079
|
+
mkdirSync4(CHATROOM_DIR4, { recursive: true, mode: 448 });
|
|
14080
|
+
}
|
|
14081
|
+
}
|
|
14082
|
+
function getPidFilePath() {
|
|
14083
|
+
return join5(CHATROOM_DIR4, getPidFileName());
|
|
14084
|
+
}
|
|
14085
|
+
function isProcessRunning(pid) {
|
|
14086
|
+
try {
|
|
14087
|
+
process.kill(pid, 0);
|
|
14088
|
+
return true;
|
|
14089
|
+
} catch {
|
|
14090
|
+
return false;
|
|
14091
|
+
}
|
|
14092
|
+
}
|
|
14093
|
+
function readPid() {
|
|
14094
|
+
const pidPath = getPidFilePath();
|
|
14095
|
+
if (!existsSync4(pidPath)) {
|
|
14096
|
+
return null;
|
|
14097
|
+
}
|
|
14098
|
+
try {
|
|
14099
|
+
const content = readFileSync6(pidPath, "utf-8").trim();
|
|
14100
|
+
const pid = parseInt(content, 10);
|
|
14101
|
+
if (isNaN(pid) || pid <= 0) {
|
|
14102
|
+
return null;
|
|
14103
|
+
}
|
|
14104
|
+
return pid;
|
|
14105
|
+
} catch {
|
|
14106
|
+
return null;
|
|
14107
|
+
}
|
|
14108
|
+
}
|
|
14109
|
+
function writePid() {
|
|
14110
|
+
ensureChatroomDir();
|
|
14111
|
+
const pidPath = getPidFilePath();
|
|
14112
|
+
writeFileSync4(pidPath, process.pid.toString(), "utf-8");
|
|
14113
|
+
}
|
|
14114
|
+
function removePid() {
|
|
14115
|
+
const pidPath = getPidFilePath();
|
|
14116
|
+
try {
|
|
14117
|
+
if (existsSync4(pidPath)) {
|
|
14118
|
+
unlinkSync2(pidPath);
|
|
14119
|
+
}
|
|
14120
|
+
} catch {}
|
|
14121
|
+
}
|
|
14122
|
+
function isDaemonRunning() {
|
|
14123
|
+
const pid = readPid();
|
|
14124
|
+
if (pid === null) {
|
|
14125
|
+
return { running: false, pid: null };
|
|
14126
|
+
}
|
|
14127
|
+
if (isProcessRunning(pid)) {
|
|
14128
|
+
return { running: true, pid };
|
|
14129
|
+
}
|
|
14130
|
+
removePid();
|
|
14131
|
+
return { running: false, pid: null };
|
|
14132
|
+
}
|
|
14133
|
+
function acquireLock() {
|
|
14134
|
+
const { running, pid } = isDaemonRunning();
|
|
14135
|
+
if (running) {
|
|
14136
|
+
console.error(`❌ Daemon already running for ${getConvexUrl()} (PID: ${pid})`);
|
|
14137
|
+
return false;
|
|
14138
|
+
}
|
|
14139
|
+
writePid();
|
|
14140
|
+
return true;
|
|
14141
|
+
}
|
|
14142
|
+
function releaseLock() {
|
|
14143
|
+
removePid();
|
|
14144
|
+
}
|
|
14145
|
+
var CHATROOM_DIR4;
|
|
14146
|
+
var init_pid = __esm(() => {
|
|
14147
|
+
init_client2();
|
|
14148
|
+
CHATROOM_DIR4 = join5(homedir4(), ".chatroom");
|
|
14149
|
+
});
|
|
14150
|
+
|
|
14151
|
+
// src/commands/machine/daemon-start/handlers/ping.ts
|
|
14152
|
+
function handlePing() {
|
|
14153
|
+
console.log(` ↪ Responding: pong`);
|
|
14154
|
+
return { result: "pong", failed: false };
|
|
14155
|
+
}
|
|
14156
|
+
|
|
14202
14157
|
// src/commands/machine/daemon-start/command-loop.ts
|
|
14203
14158
|
async function refreshModels(ctx) {
|
|
14204
14159
|
const models = {};
|
|
@@ -14227,70 +14182,6 @@ async function refreshModels(ctx) {
|
|
|
14227
14182
|
console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${error.message}`);
|
|
14228
14183
|
}
|
|
14229
14184
|
}
|
|
14230
|
-
async function processCommand(ctx, command) {
|
|
14231
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDCE8 Command received: ${command.type}`);
|
|
14232
|
-
try {
|
|
14233
|
-
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
14234
|
-
sessionId: ctx.sessionId,
|
|
14235
|
-
commandId: command._id,
|
|
14236
|
-
status: "processing"
|
|
14237
|
-
});
|
|
14238
|
-
ctx.events.emit("command:processing", {
|
|
14239
|
-
commandId: command._id.toString(),
|
|
14240
|
-
type: command.type
|
|
14241
|
-
});
|
|
14242
|
-
let commandResult;
|
|
14243
|
-
switch (command.type) {
|
|
14244
|
-
case "ping":
|
|
14245
|
-
commandResult = handlePing();
|
|
14246
|
-
break;
|
|
14247
|
-
case "status":
|
|
14248
|
-
commandResult = handleStatus(ctx);
|
|
14249
|
-
break;
|
|
14250
|
-
case "start-agent":
|
|
14251
|
-
commandResult = await handleStartAgent(ctx, command);
|
|
14252
|
-
break;
|
|
14253
|
-
case "stop-agent":
|
|
14254
|
-
commandResult = await handleStopAgent(ctx, command);
|
|
14255
|
-
break;
|
|
14256
|
-
default: {
|
|
14257
|
-
const _exhaustive = command;
|
|
14258
|
-
commandResult = {
|
|
14259
|
-
result: `Unknown command type: ${_exhaustive.type}`,
|
|
14260
|
-
failed: true
|
|
14261
|
-
};
|
|
14262
|
-
}
|
|
14263
|
-
}
|
|
14264
|
-
const finalStatus = commandResult.failed ? "failed" : "completed";
|
|
14265
|
-
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
14266
|
-
sessionId: ctx.sessionId,
|
|
14267
|
-
commandId: command._id,
|
|
14268
|
-
status: finalStatus,
|
|
14269
|
-
result: commandResult.result
|
|
14270
|
-
});
|
|
14271
|
-
ctx.events.emit("command:completed", {
|
|
14272
|
-
commandId: command._id.toString(),
|
|
14273
|
-
type: command.type,
|
|
14274
|
-
failed: commandResult.failed,
|
|
14275
|
-
result: commandResult.result
|
|
14276
|
-
});
|
|
14277
|
-
if (commandResult.failed) {
|
|
14278
|
-
console.log(` ❌ Command failed: ${commandResult.result}`);
|
|
14279
|
-
} else {
|
|
14280
|
-
console.log(` ✅ Command completed`);
|
|
14281
|
-
}
|
|
14282
|
-
} catch (error) {
|
|
14283
|
-
console.error(` ❌ Command failed: ${error.message}`);
|
|
14284
|
-
try {
|
|
14285
|
-
await ctx.deps.backend.mutation(api.machines.ackCommand, {
|
|
14286
|
-
sessionId: ctx.sessionId,
|
|
14287
|
-
commandId: command._id,
|
|
14288
|
-
status: "failed",
|
|
14289
|
-
result: error.message
|
|
14290
|
-
});
|
|
14291
|
-
} catch {}
|
|
14292
|
-
}
|
|
14293
|
-
}
|
|
14294
14185
|
async function startCommandLoop(ctx) {
|
|
14295
14186
|
let heartbeatCount = 0;
|
|
14296
14187
|
const heartbeatTimer = setInterval(() => {
|
|
@@ -14317,66 +14208,56 @@ async function startCommandLoop(ctx) {
|
|
|
14317
14208
|
process.on("SIGTERM", shutdown);
|
|
14318
14209
|
process.on("SIGHUP", shutdown);
|
|
14319
14210
|
const wsClient2 = await getConvexWsClient();
|
|
14320
|
-
const commandQueue = [];
|
|
14321
|
-
const queuedCommandIds = new Set;
|
|
14322
|
-
let drainingQueue = false;
|
|
14323
|
-
const enqueueCommands = (commands) => {
|
|
14324
|
-
for (const command of commands) {
|
|
14325
|
-
const commandId = command._id.toString();
|
|
14326
|
-
if (queuedCommandIds.has(commandId))
|
|
14327
|
-
continue;
|
|
14328
|
-
queuedCommandIds.add(commandId);
|
|
14329
|
-
commandQueue.push(command);
|
|
14330
|
-
}
|
|
14331
|
-
};
|
|
14332
|
-
const drainQueue = async () => {
|
|
14333
|
-
if (drainingQueue)
|
|
14334
|
-
return;
|
|
14335
|
-
drainingQueue = true;
|
|
14336
|
-
try {
|
|
14337
|
-
while (commandQueue.length > 0) {
|
|
14338
|
-
const command = commandQueue.shift();
|
|
14339
|
-
const commandId = command._id.toString();
|
|
14340
|
-
queuedCommandIds.delete(commandId);
|
|
14341
|
-
try {
|
|
14342
|
-
await processCommand(ctx, command);
|
|
14343
|
-
} catch (error) {
|
|
14344
|
-
console.error(` ❌ Command processing failed: ${error.message}`);
|
|
14345
|
-
}
|
|
14346
|
-
}
|
|
14347
|
-
} finally {
|
|
14348
|
-
drainingQueue = false;
|
|
14349
|
-
}
|
|
14350
|
-
};
|
|
14351
14211
|
console.log(`
|
|
14352
14212
|
Listening for commands...`);
|
|
14353
14213
|
console.log(`Press Ctrl+C to stop
|
|
14354
14214
|
`);
|
|
14355
|
-
|
|
14215
|
+
const processedCommandIds = new Map;
|
|
14216
|
+
const processedPingIds = new Map;
|
|
14217
|
+
wsClient2.onUpdate(api.machines.getCommandEvents, {
|
|
14356
14218
|
sessionId: ctx.sessionId,
|
|
14357
14219
|
machineId: ctx.machineId
|
|
14358
14220
|
}, async (result) => {
|
|
14359
|
-
if (!result.
|
|
14221
|
+
if (!result.events || result.events.length === 0)
|
|
14360
14222
|
return;
|
|
14361
|
-
const
|
|
14362
|
-
for (const
|
|
14363
|
-
|
|
14364
|
-
|
|
14365
|
-
|
|
14366
|
-
|
|
14367
|
-
|
|
14368
|
-
|
|
14223
|
+
const evictBefore = Date.now() - AGENT_REQUEST_DEADLINE_MS;
|
|
14224
|
+
for (const [id, ts] of processedCommandIds) {
|
|
14225
|
+
if (ts < evictBefore)
|
|
14226
|
+
processedCommandIds.delete(id);
|
|
14227
|
+
}
|
|
14228
|
+
for (const [id, ts] of processedPingIds) {
|
|
14229
|
+
if (ts < evictBefore)
|
|
14230
|
+
processedPingIds.delete(id);
|
|
14231
|
+
}
|
|
14232
|
+
for (const event of result.events) {
|
|
14233
|
+
const eventId = event._id.toString();
|
|
14234
|
+
try {
|
|
14235
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCE1 Stream command event: ${event.type}`);
|
|
14236
|
+
if (event.type === "agent.requestStart") {
|
|
14237
|
+
if (processedCommandIds.has(eventId))
|
|
14238
|
+
continue;
|
|
14239
|
+
processedCommandIds.set(eventId, Date.now());
|
|
14240
|
+
await onRequestStartAgent(ctx, event);
|
|
14241
|
+
} else if (event.type === "agent.requestStop") {
|
|
14242
|
+
if (processedCommandIds.has(eventId))
|
|
14243
|
+
continue;
|
|
14244
|
+
processedCommandIds.set(eventId, Date.now());
|
|
14245
|
+
await onRequestStopAgent(ctx, event);
|
|
14246
|
+
} else if (event.type === "daemon.ping") {
|
|
14247
|
+
if (processedPingIds.has(eventId))
|
|
14248
|
+
continue;
|
|
14249
|
+
processedPingIds.set(eventId, Date.now());
|
|
14250
|
+
handlePing();
|
|
14251
|
+
await ctx.deps.backend.mutation(api.machines.ackPing, {
|
|
14369
14252
|
sessionId: ctx.sessionId,
|
|
14370
|
-
|
|
14371
|
-
|
|
14372
|
-
result: `Invalid command: type="${raw.type}" missing required payload fields`
|
|
14253
|
+
machineId: ctx.machineId,
|
|
14254
|
+
pingEventId: event._id
|
|
14373
14255
|
});
|
|
14374
|
-
|
|
14375
|
-
|
|
14256
|
+
}
|
|
14257
|
+
} catch (err) {
|
|
14258
|
+
console.error(`[${formatTimestamp()}] ❌ Stream command event failed: ${err.message}`);
|
|
14376
14259
|
}
|
|
14377
14260
|
}
|
|
14378
|
-
enqueueCommands(parsed);
|
|
14379
|
-
await drainQueue();
|
|
14380
14261
|
});
|
|
14381
14262
|
const modelRefreshTimer = setInterval(() => {
|
|
14382
14263
|
refreshModels(ctx).catch((err) => {
|
|
@@ -14391,9 +14272,9 @@ var init_command_loop = __esm(() => {
|
|
|
14391
14272
|
init_api3();
|
|
14392
14273
|
init_client2();
|
|
14393
14274
|
init_on_daemon_shutdown();
|
|
14275
|
+
init_on_request_start_agent();
|
|
14276
|
+
init_on_request_stop_agent();
|
|
14394
14277
|
init_pid();
|
|
14395
|
-
init_start_agent();
|
|
14396
|
-
init_stop_agent();
|
|
14397
14278
|
MODEL_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
14398
14279
|
});
|
|
14399
14280
|
|
|
@@ -14425,7 +14306,7 @@ var init_state_recovery = __esm(() => {
|
|
|
14425
14306
|
init_shared();
|
|
14426
14307
|
});
|
|
14427
14308
|
|
|
14428
|
-
// src/
|
|
14309
|
+
// src/events/daemon/event-bus.ts
|
|
14429
14310
|
class DaemonEventBus {
|
|
14430
14311
|
listeners = new Map;
|
|
14431
14312
|
on(event, listener) {
|
|
@@ -14454,54 +14335,66 @@ class DaemonEventBus {
|
|
|
14454
14335
|
}
|
|
14455
14336
|
}
|
|
14456
14337
|
|
|
14457
|
-
// src/
|
|
14338
|
+
// src/events/daemon/agent/on-agent-exited.ts
|
|
14339
|
+
function onAgentExited(ctx, payload) {
|
|
14340
|
+
const { chatroomId, role, pid, code: code2, signal, intentional } = payload;
|
|
14341
|
+
const ts = formatTimestamp();
|
|
14342
|
+
if (intentional) {
|
|
14343
|
+
console.log(`[${ts}] ℹ️ Agent process exited after intentional stop ` + `(PID: ${pid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
14344
|
+
} else {
|
|
14345
|
+
console.log(`[${ts}] ⚠️ Agent process exited ` + `(PID: ${pid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
14346
|
+
}
|
|
14347
|
+
ctx.deps.backend.mutation(api.machines.recordAgentExited, {
|
|
14348
|
+
sessionId: ctx.sessionId,
|
|
14349
|
+
machineId: ctx.machineId,
|
|
14350
|
+
chatroomId,
|
|
14351
|
+
role,
|
|
14352
|
+
pid,
|
|
14353
|
+
intentional,
|
|
14354
|
+
exitCode: code2 ?? undefined,
|
|
14355
|
+
signal: signal ?? undefined
|
|
14356
|
+
}).catch((err) => {
|
|
14357
|
+
console.log(` ⚠️ Failed to record agent exit event: ${err.message}`);
|
|
14358
|
+
});
|
|
14359
|
+
ctx.deps.machine.clearAgentPid(ctx.machineId, chatroomId, role);
|
|
14360
|
+
for (const service of ctx.agentServices.values()) {
|
|
14361
|
+
service.untrack(pid);
|
|
14362
|
+
}
|
|
14363
|
+
}
|
|
14364
|
+
var init_on_agent_exited = __esm(() => {
|
|
14365
|
+
init_api3();
|
|
14366
|
+
});
|
|
14367
|
+
|
|
14368
|
+
// src/events/daemon/agent/on-agent-started.ts
|
|
14369
|
+
function onAgentStarted(ctx, payload) {
|
|
14370
|
+
const ts = formatTimestamp();
|
|
14371
|
+
console.log(`[${ts}] \uD83D\uDFE2 Agent started: ${payload.role} (PID: ${payload.pid}, harness: ${payload.harness})`);
|
|
14372
|
+
}
|
|
14373
|
+
var init_on_agent_started = () => {};
|
|
14374
|
+
|
|
14375
|
+
// src/events/daemon/agent/on-agent-stopped.ts
|
|
14376
|
+
function onAgentStopped(ctx, payload) {
|
|
14377
|
+
const ts = formatTimestamp();
|
|
14378
|
+
console.log(`[${ts}] \uD83D\uDD34 Agent stopped: ${payload.role} (PID: ${payload.pid})`);
|
|
14379
|
+
}
|
|
14380
|
+
var init_on_agent_stopped = () => {};
|
|
14381
|
+
|
|
14382
|
+
// src/events/daemon/register-listeners.ts
|
|
14458
14383
|
function registerEventListeners(ctx) {
|
|
14459
14384
|
const unsubs = [];
|
|
14460
|
-
unsubs.push(ctx.events.on("agent:exited", (payload) =>
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
if (intentional) {
|
|
14464
|
-
console.log(`[${ts}] ℹ️ Agent process exited after intentional stop ` + `(PID: ${pid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
14465
|
-
} else {
|
|
14466
|
-
console.log(`[${ts}] ⚠️ Agent process exited ` + `(PID: ${pid}, role: ${role}, code: ${code2}, signal: ${signal})`);
|
|
14467
|
-
}
|
|
14468
|
-
ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
14469
|
-
sessionId: ctx.sessionId,
|
|
14470
|
-
machineId: ctx.machineId,
|
|
14471
|
-
chatroomId,
|
|
14472
|
-
role,
|
|
14473
|
-
pid: undefined
|
|
14474
|
-
}).catch((err) => {
|
|
14475
|
-
console.log(` ⚠️ Failed to clear PID in backend: ${err.message}`);
|
|
14476
|
-
});
|
|
14477
|
-
ctx.deps.machine.clearAgentPid(ctx.machineId, chatroomId, role);
|
|
14478
|
-
for (const service of ctx.agentServices.values()) {
|
|
14479
|
-
service.untrack(pid);
|
|
14480
|
-
}
|
|
14481
|
-
ctx.deps.backend.mutation(api.participants.leave, {
|
|
14482
|
-
sessionId: ctx.sessionId,
|
|
14483
|
-
chatroomId,
|
|
14484
|
-
role
|
|
14485
|
-
}).catch((err) => {
|
|
14486
|
-
console.log(` ⚠️ Could not remove participant: ${err.message}`);
|
|
14487
|
-
});
|
|
14488
|
-
}));
|
|
14489
|
-
unsubs.push(ctx.events.on("agent:started", (payload) => {
|
|
14490
|
-
const ts = formatTimestamp();
|
|
14491
|
-
console.log(`[${ts}] \uD83D\uDFE2 Agent started: ${payload.role} (PID: ${payload.pid}, harness: ${payload.harness})`);
|
|
14492
|
-
}));
|
|
14493
|
-
unsubs.push(ctx.events.on("agent:stopped", (payload) => {
|
|
14494
|
-
const ts = formatTimestamp();
|
|
14495
|
-
console.log(`[${ts}] \uD83D\uDD34 Agent stopped: ${payload.role} (PID: ${payload.pid})`);
|
|
14496
|
-
}));
|
|
14385
|
+
unsubs.push(ctx.events.on("agent:exited", (payload) => onAgentExited(ctx, payload)));
|
|
14386
|
+
unsubs.push(ctx.events.on("agent:started", (payload) => onAgentStarted(ctx, payload)));
|
|
14387
|
+
unsubs.push(ctx.events.on("agent:stopped", (payload) => onAgentStopped(ctx, payload)));
|
|
14497
14388
|
return () => {
|
|
14498
14389
|
for (const unsub of unsubs) {
|
|
14499
14390
|
unsub();
|
|
14500
14391
|
}
|
|
14501
14392
|
};
|
|
14502
14393
|
}
|
|
14503
|
-
var
|
|
14504
|
-
|
|
14394
|
+
var init_register_listeners = __esm(() => {
|
|
14395
|
+
init_on_agent_exited();
|
|
14396
|
+
init_on_agent_started();
|
|
14397
|
+
init_on_agent_stopped();
|
|
14505
14398
|
});
|
|
14506
14399
|
|
|
14507
14400
|
// src/commands/machine/daemon-start/init.ts
|
|
@@ -14543,7 +14436,9 @@ function createDefaultDeps16() {
|
|
|
14543
14436
|
machine: {
|
|
14544
14437
|
clearAgentPid,
|
|
14545
14438
|
persistAgentPid,
|
|
14546
|
-
listAgentEntries
|
|
14439
|
+
listAgentEntries,
|
|
14440
|
+
persistEventCursor,
|
|
14441
|
+
loadEventCursor
|
|
14547
14442
|
},
|
|
14548
14443
|
clock: {
|
|
14549
14444
|
now: () => Date.now(),
|
|
@@ -14672,7 +14567,7 @@ var init_init2 = __esm(() => {
|
|
|
14672
14567
|
init_error_formatting();
|
|
14673
14568
|
init_version();
|
|
14674
14569
|
init_pid();
|
|
14675
|
-
|
|
14570
|
+
init_register_listeners();
|
|
14676
14571
|
});
|
|
14677
14572
|
|
|
14678
14573
|
// src/commands/machine/daemon-start/index.ts
|