@episoda/cli 0.2.182 → 0.2.187
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.
|
@@ -3046,7 +3046,7 @@ var require_package = __commonJS({
|
|
|
3046
3046
|
"package.json"(exports2, module2) {
|
|
3047
3047
|
module2.exports = {
|
|
3048
3048
|
name: "@episoda/cli",
|
|
3049
|
-
version: "0.2.
|
|
3049
|
+
version: "0.2.187",
|
|
3050
3050
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
3051
3051
|
main: "dist/index.js",
|
|
3052
3052
|
types: "dist/index.d.ts",
|
|
@@ -12875,7 +12875,7 @@ var pty = __toESM(require("@lydell/node-pty"));
|
|
|
12875
12875
|
var INACTIVITY_TIMEOUT_MS3 = 30 * 60 * 1e3;
|
|
12876
12876
|
var sessions = /* @__PURE__ */ new Map();
|
|
12877
12877
|
async function handlePtySpawn(payload, client) {
|
|
12878
|
-
const { moduleUid, agent_run_id, command, args, env, cwd, cols = 220, rows = 50 } = payload;
|
|
12878
|
+
const { moduleUid, agent_run_id, command, args, stdin, env, cwd, cols = 220, rows = 50 } = payload;
|
|
12879
12879
|
if (sessions.has(agent_run_id)) {
|
|
12880
12880
|
console.warn(`[PTY] Session already exists for agent_run_id ${agent_run_id}, ignoring spawn`);
|
|
12881
12881
|
return;
|
|
@@ -12933,6 +12933,9 @@ async function handlePtySpawn(payload, client) {
|
|
|
12933
12933
|
console.error(`[PTY] Failed to send pty_data for ${agent_run_id}:`, err.message);
|
|
12934
12934
|
});
|
|
12935
12935
|
});
|
|
12936
|
+
if (typeof stdin === "string" && stdin.trim().length > 0) {
|
|
12937
|
+
proc.write(`${stdin.replace(/\n/g, "\r")}\r`);
|
|
12938
|
+
}
|
|
12936
12939
|
proc.onExit(({ exitCode }) => {
|
|
12937
12940
|
const durationMs = Date.now() - session.startedAt;
|
|
12938
12941
|
if (session.watchdogTimer) clearTimeout(session.watchdogTimer);
|
|
@@ -15012,9 +15015,19 @@ var ProjectMessageRouter = class {
|
|
|
15012
15015
|
client.on("pty_spawn", async (message) => {
|
|
15013
15016
|
if (message.type === "pty_spawn") {
|
|
15014
15017
|
const payload = message.payload;
|
|
15018
|
+
let resolvedPayload = payload;
|
|
15019
|
+
if (!payload.cwd && payload.moduleUid) {
|
|
15020
|
+
const worktreeInfo = await getWorktreeInfoForModule(payload.moduleUid);
|
|
15021
|
+
if (worktreeInfo?.path) {
|
|
15022
|
+
resolvedPayload = {
|
|
15023
|
+
...payload,
|
|
15024
|
+
cwd: worktreeInfo.path
|
|
15025
|
+
};
|
|
15026
|
+
}
|
|
15027
|
+
}
|
|
15015
15028
|
console.log(`[Daemon] EP1441: Received pty_spawn for ${projectId}: ${payload.agent_run_id}`);
|
|
15016
15029
|
client.updateActivity();
|
|
15017
|
-
await handlePtySpawn(
|
|
15030
|
+
await handlePtySpawn(resolvedPayload, client);
|
|
15018
15031
|
}
|
|
15019
15032
|
});
|
|
15020
15033
|
client.on("pty_resize", (message) => {
|
|
@@ -15311,10 +15324,7 @@ var Daemon = class _Daemon {
|
|
|
15311
15324
|
});
|
|
15312
15325
|
}
|
|
15313
15326
|
static {
|
|
15314
|
-
this.AGENT_HEARTBEAT_INTERVAL_MS =
|
|
15315
|
-
}
|
|
15316
|
-
static {
|
|
15317
|
-
this.AGENT_HEARTBEAT_REQUEST_TIMEOUT_MS = 12e3;
|
|
15327
|
+
this.AGENT_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
15318
15328
|
}
|
|
15319
15329
|
static {
|
|
15320
15330
|
this.HEALTH_PORT = 9999;
|
|
@@ -15341,59 +15351,58 @@ var Daemon = class _Daemon {
|
|
|
15341
15351
|
loop.stop();
|
|
15342
15352
|
this.agentHeartbeatLoops.delete(sessionId);
|
|
15343
15353
|
}
|
|
15344
|
-
async
|
|
15354
|
+
async shouldContinueAgentSession(sessionId) {
|
|
15355
|
+
try {
|
|
15356
|
+
const config = await (0, import_core22.loadConfig)();
|
|
15357
|
+
const apiUrl = config?.api_url || "https://episoda.dev";
|
|
15358
|
+
const response = await fetchWithAuth(`${apiUrl}/api/agent-sessions/${encodeURIComponent(sessionId)}/heartbeat`, {
|
|
15359
|
+
method: "POST"
|
|
15360
|
+
});
|
|
15361
|
+
if (!response.ok) {
|
|
15362
|
+
console.warn(`[Daemon] EP1429: Heartbeat API returned ${response.status} for session ${sessionId}; continuing session`);
|
|
15363
|
+
return true;
|
|
15364
|
+
}
|
|
15365
|
+
const payload = await response.json().catch(() => null);
|
|
15366
|
+
return payload?.continue !== false;
|
|
15367
|
+
} catch (error) {
|
|
15368
|
+
console.warn(`[Daemon] EP1429: Heartbeat API check failed for session ${sessionId}; continuing session`, error);
|
|
15369
|
+
return true;
|
|
15370
|
+
}
|
|
15371
|
+
}
|
|
15372
|
+
async stopAgentSessionFromServerRequest(sessionId) {
|
|
15345
15373
|
try {
|
|
15346
15374
|
const agentManager = getAgentControlPlane();
|
|
15375
|
+
await agentManager.initialize();
|
|
15347
15376
|
await agentManager.stopSession(sessionId);
|
|
15348
|
-
|
|
15377
|
+
this.agentEventSeq.delete(sessionId);
|
|
15378
|
+
console.log(`[Daemon] EP1429: Stopped agent session ${sessionId} on server heartbeat continue=false`);
|
|
15349
15379
|
} catch (error) {
|
|
15350
|
-
console.warn(
|
|
15351
|
-
`[Daemon] EP1430: Failed to stop session ${sessionId} after server continue=false:`,
|
|
15352
|
-
error
|
|
15353
|
-
);
|
|
15380
|
+
console.warn(`[Daemon] EP1429: Failed to stop session ${sessionId} after continue=false`, error);
|
|
15354
15381
|
}
|
|
15355
15382
|
}
|
|
15356
|
-
startAgentHeartbeatLoop(sessionId) {
|
|
15383
|
+
startAgentHeartbeatLoop(sessionId, client) {
|
|
15357
15384
|
if (this.agentHeartbeatLoops.has(sessionId)) return;
|
|
15358
15385
|
let stopped = false;
|
|
15359
|
-
let inFlight = false;
|
|
15360
15386
|
let timer = null;
|
|
15361
15387
|
const runHeartbeat = async () => {
|
|
15362
|
-
if (stopped
|
|
15363
|
-
inFlight = true;
|
|
15364
|
-
const abortController = new AbortController();
|
|
15365
|
-
const requestTimeout = setTimeout(() => {
|
|
15366
|
-
abortController.abort();
|
|
15367
|
-
}, _Daemon.AGENT_HEARTBEAT_REQUEST_TIMEOUT_MS);
|
|
15388
|
+
if (stopped) return;
|
|
15368
15389
|
try {
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
signal: abortController.signal
|
|
15377
|
-
}
|
|
15378
|
-
);
|
|
15379
|
-
if (!response.ok && response.status !== 409) {
|
|
15380
|
-
return;
|
|
15381
|
-
}
|
|
15382
|
-
const payload = await response.json().catch(() => null);
|
|
15383
|
-
const shouldContinue = response.status === 409 ? false : Boolean(payload?.data?.continue ?? payload?.continue ?? true);
|
|
15390
|
+
await client.send({
|
|
15391
|
+
type: "agent_heartbeat",
|
|
15392
|
+
sessionId,
|
|
15393
|
+
pid: process.pid,
|
|
15394
|
+
status: "running"
|
|
15395
|
+
});
|
|
15396
|
+
const shouldContinue = await this.shouldContinueAgentSession(sessionId);
|
|
15384
15397
|
if (!shouldContinue) {
|
|
15385
|
-
await this.handleServerRequestedAgentStop(sessionId);
|
|
15386
15398
|
this.stopAgentHeartbeatLoop(sessionId);
|
|
15399
|
+
await this.stopAgentSessionFromServerRequest(sessionId);
|
|
15387
15400
|
}
|
|
15388
15401
|
} catch (error) {
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
}
|
|
15394
|
-
} finally {
|
|
15395
|
-
clearTimeout(requestTimeout);
|
|
15396
|
-
inFlight = false;
|
|
15402
|
+
console.warn(
|
|
15403
|
+
`[Daemon] EP1429: Failed to send agent_heartbeat for session ${sessionId}:`,
|
|
15404
|
+
error
|
|
15405
|
+
);
|
|
15397
15406
|
}
|
|
15398
15407
|
};
|
|
15399
15408
|
timer = setInterval(() => {
|
|
@@ -15848,7 +15857,7 @@ var Daemon = class _Daemon {
|
|
|
15848
15857
|
await agentManager.initialize();
|
|
15849
15858
|
let result;
|
|
15850
15859
|
if (cmd.action === "start") {
|
|
15851
|
-
this.startAgentHeartbeatLoop(cmd.sessionId);
|
|
15860
|
+
this.startAgentHeartbeatLoop(cmd.sessionId, client);
|
|
15852
15861
|
const callbacks = createStreamingCallbacks(cmd.sessionId, message.id);
|
|
15853
15862
|
const agentWorkingDir = await resolveAgentWorkingDirectory({
|
|
15854
15863
|
command: cmd,
|
|
@@ -15889,7 +15898,7 @@ var Daemon = class _Daemon {
|
|
|
15889
15898
|
error: startResult.error
|
|
15890
15899
|
};
|
|
15891
15900
|
} else if (cmd.action === "message") {
|
|
15892
|
-
this.startAgentHeartbeatLoop(cmd.sessionId);
|
|
15901
|
+
this.startAgentHeartbeatLoop(cmd.sessionId, client);
|
|
15893
15902
|
const callbacks = createStreamingCallbacks(cmd.sessionId, message.id);
|
|
15894
15903
|
const sendResult = await agentManager.sendMessage({
|
|
15895
15904
|
sessionId: cmd.sessionId,
|