agent-yes 1.55.0 → 1.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{SUPPORTED_CLIS-Db5tikbx.js → SUPPORTED_CLIS-DNBWqwwx.js} +100 -26
- package/dist/cli.js +1 -6
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/ts/index.ts +5 -0
- package/ts/installEnv.ts +53 -0
- package/ts/webhookNotifier.ts +34 -0
|
@@ -3,7 +3,8 @@ import { arch, platform } from "process";
|
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync } from "fs";
|
|
5
5
|
import path, { dirname, join } from "path";
|
|
6
|
-
import {
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import os, { constants } from "node:os";
|
|
7
8
|
import path$1 from "node:path";
|
|
8
9
|
import { homedir } from "os";
|
|
9
10
|
import { fileURLToPath } from "url";
|
|
@@ -21,7 +22,7 @@ import { finished } from "node:stream/promises";
|
|
|
21
22
|
import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
|
|
22
23
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
23
24
|
import { fromWritable } from "from-node-stream";
|
|
24
|
-
import { appendFile, mkdir, readFile, readdir, rename, writeFile as writeFile$
|
|
25
|
+
import { appendFile, mkdir as mkdir$1, readFile as readFile$1, readdir, rename, writeFile as writeFile$2 } from "fs/promises";
|
|
25
26
|
import { lock } from "proper-lockfile";
|
|
26
27
|
|
|
27
28
|
//#region node_modules/is-plain-obj/index.js
|
|
@@ -8937,7 +8938,7 @@ const getCodexSessionsDir = () => process.env.CLI_YES_TEST_HOME ? path.join(proc
|
|
|
8937
8938
|
*/
|
|
8938
8939
|
async function loadSessionMap() {
|
|
8939
8940
|
try {
|
|
8940
|
-
const content = await readFile(getSessionsFile(), "utf-8");
|
|
8941
|
+
const content = await readFile$1(getSessionsFile(), "utf-8");
|
|
8941
8942
|
return JSON.parse(content);
|
|
8942
8943
|
} catch {
|
|
8943
8944
|
return {};
|
|
@@ -8949,8 +8950,8 @@ async function loadSessionMap() {
|
|
|
8949
8950
|
async function saveSessionMap(sessionMap) {
|
|
8950
8951
|
try {
|
|
8951
8952
|
const sessionsFile = getSessionsFile();
|
|
8952
|
-
await mkdir(path.dirname(sessionsFile), { recursive: true });
|
|
8953
|
-
await writeFile$
|
|
8953
|
+
await mkdir$1(path.dirname(sessionsFile), { recursive: true });
|
|
8954
|
+
await writeFile$2(sessionsFile, JSON.stringify(sessionMap, null, 2));
|
|
8954
8955
|
} catch (error) {
|
|
8955
8956
|
console.warn("Failed to save codex session map:", error);
|
|
8956
8957
|
}
|
|
@@ -8971,7 +8972,7 @@ async function storeSessionForCwd(cwd, sessionId) {
|
|
|
8971
8972
|
*/
|
|
8972
8973
|
async function parseCodexSessionFile(filePath) {
|
|
8973
8974
|
try {
|
|
8974
|
-
const lines = (await readFile(filePath, "utf-8")).trim().split("\n");
|
|
8975
|
+
const lines = (await readFile$1(filePath, "utf-8")).trim().split("\n");
|
|
8975
8976
|
for (const line of lines) {
|
|
8976
8977
|
if (!line.trim()) continue;
|
|
8977
8978
|
const data = JSON.parse(line);
|
|
@@ -9145,9 +9146,9 @@ async function readLockFile() {
|
|
|
9145
9146
|
try {
|
|
9146
9147
|
const lockDir = getLockDir();
|
|
9147
9148
|
const lockFilePath = getLockFile();
|
|
9148
|
-
await mkdir(lockDir, { recursive: true });
|
|
9149
|
+
await mkdir$1(lockDir, { recursive: true });
|
|
9149
9150
|
if (!existsSync(lockFilePath)) return { tasks: [] };
|
|
9150
|
-
const content = await readFile(lockFilePath, "utf8");
|
|
9151
|
+
const content = await readFile$1(lockFilePath, "utf8");
|
|
9151
9152
|
const lockFile = JSON.parse(content);
|
|
9152
9153
|
lockFile.tasks = lockFile.tasks.filter((task) => {
|
|
9153
9154
|
if (isProcessRunning(task.pid)) return true;
|
|
@@ -9165,9 +9166,9 @@ async function writeLockFile(lockFile, retryCount = 0) {
|
|
|
9165
9166
|
try {
|
|
9166
9167
|
const lockDir = getLockDir();
|
|
9167
9168
|
const lockFilePath = getLockFile();
|
|
9168
|
-
await mkdir(lockDir, { recursive: true });
|
|
9169
|
+
await mkdir$1(lockDir, { recursive: true });
|
|
9169
9170
|
const tempFile = `${lockFilePath}.tmp.${process.pid}`;
|
|
9170
|
-
await writeFile$
|
|
9171
|
+
await writeFile$2(tempFile, JSON.stringify(lockFile, null, 2), "utf8");
|
|
9171
9172
|
await rename(tempFile, lockFilePath);
|
|
9172
9173
|
} catch (error) {
|
|
9173
9174
|
if (retryCount < MAX_RETRIES) {
|
|
@@ -9342,7 +9343,7 @@ var JsonlStore = class {
|
|
|
9342
9343
|
* Handles crash recovery: partial last line skipped, temp file recovery.
|
|
9343
9344
|
*/
|
|
9344
9345
|
async load() {
|
|
9345
|
-
await mkdir(path.dirname(this.filePath), { recursive: true });
|
|
9346
|
+
await mkdir$1(path.dirname(this.filePath), { recursive: true });
|
|
9346
9347
|
if (!existsSync(this.filePath) && existsSync(this.tempPath)) {
|
|
9347
9348
|
logger.debug("[JsonlStore] Recovering from temp file");
|
|
9348
9349
|
await rename(this.tempPath, this.filePath);
|
|
@@ -9350,7 +9351,7 @@ var JsonlStore = class {
|
|
|
9350
9351
|
this.docs = /* @__PURE__ */ new Map();
|
|
9351
9352
|
let raw = "";
|
|
9352
9353
|
try {
|
|
9353
|
-
raw = await readFile(this.filePath, "utf-8");
|
|
9354
|
+
raw = await readFile$1(this.filePath, "utf-8");
|
|
9354
9355
|
} catch (err) {
|
|
9355
9356
|
if (err.code === "ENOENT") return this.docs;
|
|
9356
9357
|
throw err;
|
|
@@ -9462,7 +9463,7 @@ var JsonlStore = class {
|
|
|
9462
9463
|
});
|
|
9463
9464
|
}).join("\n");
|
|
9464
9465
|
const content = lines ? lines + "\n" : "";
|
|
9465
|
-
await writeFile$
|
|
9466
|
+
await writeFile$2(this.tempPath, content);
|
|
9466
9467
|
const fd = openSync(this.tempPath, "r");
|
|
9467
9468
|
fsyncSync(fd);
|
|
9468
9469
|
closeSync(fd);
|
|
@@ -9603,8 +9604,8 @@ pid-db/
|
|
|
9603
9604
|
|
|
9604
9605
|
`;
|
|
9605
9606
|
try {
|
|
9606
|
-
await mkdir(this.storeDir, { recursive: true });
|
|
9607
|
-
await writeFile$
|
|
9607
|
+
await mkdir$1(this.storeDir, { recursive: true });
|
|
9608
|
+
await writeFile$2(gitignorePath, gitignoreContent, { flag: "wx" });
|
|
9608
9609
|
logger.debug(`[pidStore] Created .gitignore in ${this.storeDir}`);
|
|
9609
9610
|
} catch (error) {
|
|
9610
9611
|
if (error.code !== "EEXIST") logger.warn(`[pidStore] Failed to create .gitignore:`, error);
|
|
@@ -9727,7 +9728,7 @@ var import_winston = /* @__PURE__ */ __toESM(require_winston(), 1);
|
|
|
9727
9728
|
*/
|
|
9728
9729
|
async function initializeLogPaths(pidStore, pid) {
|
|
9729
9730
|
const logDir = pidStore.getLogDir();
|
|
9730
|
-
await mkdir(logDir, { recursive: true });
|
|
9731
|
+
await mkdir$1(logDir, { recursive: true });
|
|
9731
9732
|
return {
|
|
9732
9733
|
logPath: logDir,
|
|
9733
9734
|
rawLogPath: path.resolve(path.dirname(logDir), `${pid}.raw.log`),
|
|
@@ -9752,8 +9753,8 @@ function setupDebugLogging(debuggingLogsPath) {
|
|
|
9752
9753
|
*/
|
|
9753
9754
|
async function saveLogFile(logPath, content) {
|
|
9754
9755
|
if (!logPath) return;
|
|
9755
|
-
await mkdir(path.dirname(logPath), { recursive: true }).catch(() => null);
|
|
9756
|
-
await writeFile$
|
|
9756
|
+
await mkdir$1(path.dirname(logPath), { recursive: true }).catch(() => null);
|
|
9757
|
+
await writeFile$2(logPath, content).catch(() => null);
|
|
9757
9758
|
logger.info(`Full logs saved to ${logPath}`);
|
|
9758
9759
|
}
|
|
9759
9760
|
/**
|
|
@@ -9766,8 +9767,8 @@ async function saveDeprecatedLogFile(logFile, content, verbose) {
|
|
|
9766
9767
|
if (!logFile) return;
|
|
9767
9768
|
if (verbose) logger.info(`Writing rendered logs to ${logFile}`);
|
|
9768
9769
|
const logFilePath = path.resolve(logFile);
|
|
9769
|
-
await mkdir(path.dirname(logFilePath), { recursive: true }).catch(() => null);
|
|
9770
|
-
await writeFile$
|
|
9770
|
+
await mkdir$1(path.dirname(logFilePath), { recursive: true }).catch(() => null);
|
|
9771
|
+
await writeFile$2(logFilePath, content);
|
|
9771
9772
|
}
|
|
9772
9773
|
|
|
9773
9774
|
//#endregion
|
|
@@ -9793,6 +9794,11 @@ function tryCatch(catchFn, fn) {
|
|
|
9793
9794
|
};
|
|
9794
9795
|
}
|
|
9795
9796
|
|
|
9797
|
+
//#endregion
|
|
9798
|
+
//#region package.json
|
|
9799
|
+
var name = "agent-yes";
|
|
9800
|
+
var version = "1.57.0";
|
|
9801
|
+
|
|
9796
9802
|
//#endregion
|
|
9797
9803
|
//#region ts/pty-fix.ts
|
|
9798
9804
|
var pty_fix_exports = /* @__PURE__ */ __exportAll({});
|
|
@@ -9961,7 +9967,7 @@ function spawnAgent(options) {
|
|
|
9961
9967
|
let [bin, ...args] = [...parseCommandString(cliConf?.binary || cli), ...cliArgs];
|
|
9962
9968
|
logger.debug(`Spawning ${bin} with args: ${JSON.stringify(args)}`);
|
|
9963
9969
|
const spawned = pty.spawn(bin, args, ptyOptions);
|
|
9964
|
-
logger.info(`[${cli}-yes] Spawned ${bin} with PID ${spawned.pid}`);
|
|
9970
|
+
logger.info(`[${cli}-yes] Spawned ${bin} with PID ${spawned.pid} (agent-yes v${version})`);
|
|
9965
9971
|
return spawned;
|
|
9966
9972
|
};
|
|
9967
9973
|
return tryCatch((error, attempts, spawn, ...args) => {
|
|
@@ -10151,6 +10157,70 @@ var AgentRegistry = class {
|
|
|
10151
10157
|
};
|
|
10152
10158
|
const globalAgentRegistry = new AgentRegistry();
|
|
10153
10159
|
|
|
10160
|
+
//#endregion
|
|
10161
|
+
//#region ts/installEnv.ts
|
|
10162
|
+
const installDir = path$1.join(import.meta.dir, "..");
|
|
10163
|
+
function parseEnvContent(content) {
|
|
10164
|
+
const result = {};
|
|
10165
|
+
for (const line of content.split("\n")) {
|
|
10166
|
+
const trimmed = line.trim();
|
|
10167
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
10168
|
+
const eqIndex = trimmed.indexOf("=");
|
|
10169
|
+
if (eqIndex < 0) continue;
|
|
10170
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
10171
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
10172
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
10173
|
+
result[key] = value;
|
|
10174
|
+
}
|
|
10175
|
+
return result;
|
|
10176
|
+
}
|
|
10177
|
+
let _installEnv = null;
|
|
10178
|
+
/**
|
|
10179
|
+
* Load .env from the agent-yes install directory (not the working dir).
|
|
10180
|
+
* Install dir is ${import.meta.dir}/.. relative to this file.
|
|
10181
|
+
* Cached after first load.
|
|
10182
|
+
*/
|
|
10183
|
+
async function loadInstallEnv() {
|
|
10184
|
+
if (_installEnv) return _installEnv;
|
|
10185
|
+
const envPath = path$1.join(installDir, ".env");
|
|
10186
|
+
try {
|
|
10187
|
+
_installEnv = parseEnvContent(await readFile(envPath, "utf-8"));
|
|
10188
|
+
} catch {
|
|
10189
|
+
_installEnv = {};
|
|
10190
|
+
}
|
|
10191
|
+
return _installEnv;
|
|
10192
|
+
}
|
|
10193
|
+
/**
|
|
10194
|
+
* Get a value from the install .env, falling back to process.env.
|
|
10195
|
+
*/
|
|
10196
|
+
async function getInstallEnv(key) {
|
|
10197
|
+
return (await loadInstallEnv())[key] ?? process.env[key];
|
|
10198
|
+
}
|
|
10199
|
+
|
|
10200
|
+
//#endregion
|
|
10201
|
+
//#region ts/webhookNotifier.ts
|
|
10202
|
+
/**
|
|
10203
|
+
* Notify the AGENT_YES_MESSAGE_WEBHOOK URL with a status message.
|
|
10204
|
+
*
|
|
10205
|
+
* AGENT_YES_MESSAGE_WEBHOOK should be set in the agent-yes install dir .env, e.g.:
|
|
10206
|
+
* AGENT_YES_MESSAGE_WEBHOOK=https://example.com/hook?q=%s
|
|
10207
|
+
*
|
|
10208
|
+
* The %s placeholder is replaced with the URL-encoded message:
|
|
10209
|
+
* [STATUS] hostname:cwd details
|
|
10210
|
+
*/
|
|
10211
|
+
async function notifyWebhook(status, details, cwd = process.cwd()) {
|
|
10212
|
+
const webhookTemplate = await getInstallEnv("AGENT_YES_MESSAGE_WEBHOOK");
|
|
10213
|
+
if (!webhookTemplate) return;
|
|
10214
|
+
const message = `[${status}] ${os.hostname()}:${cwd}${details ? " " + details : ""}`;
|
|
10215
|
+
const url = webhookTemplate.replace("%s", encodeURIComponent(message));
|
|
10216
|
+
try {
|
|
10217
|
+
const res = await fetch(url);
|
|
10218
|
+
logger.debug(`[webhook] ${status} notified (${res.status}): ${url}`);
|
|
10219
|
+
} catch (error) {
|
|
10220
|
+
logger.warn(`[webhook] Failed to notify ${status}: ${error}`);
|
|
10221
|
+
}
|
|
10222
|
+
}
|
|
10223
|
+
|
|
10154
10224
|
//#endregion
|
|
10155
10225
|
//#region ts/index.ts
|
|
10156
10226
|
const config = await import("./agent-yes.config-mJP0MKqV.js").then((mod) => mod.default || mod);
|
|
@@ -10231,7 +10301,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10231
10301
|
let currentDir = workingDir;
|
|
10232
10302
|
const searchLimit = gitRoot || path.parse(currentDir).root;
|
|
10233
10303
|
while (true) {
|
|
10234
|
-
const md = await readFile(path.resolve(currentDir, "SKILL.md"), "utf8").catch(() => null);
|
|
10304
|
+
const md = await readFile$1(path.resolve(currentDir, "SKILL.md"), "utf8").catch(() => null);
|
|
10235
10305
|
if (md) {
|
|
10236
10306
|
const headerMatch = md.match(/^[\s\S]*?(?=\n##\s)/);
|
|
10237
10307
|
const headerRaw = (headerMatch ? headerMatch[0] : md).trim();
|
|
@@ -10314,6 +10384,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10314
10384
|
} catch (error) {
|
|
10315
10385
|
logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
|
|
10316
10386
|
}
|
|
10387
|
+
notifyWebhook("RUNNING", prompt ?? "", workingDir).catch(() => null);
|
|
10317
10388
|
const logPaths = await initializeLogPaths(pidStore, shell.pid);
|
|
10318
10389
|
setupDebugLogging(logPaths.debuggingLogsPath);
|
|
10319
10390
|
const ctx = new AgentContext({
|
|
@@ -10427,6 +10498,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10427
10498
|
} catch (error) {
|
|
10428
10499
|
logger.warn(`[pidStore] Failed to update status for PID ${exitedPid}:`, error);
|
|
10429
10500
|
}
|
|
10501
|
+
notifyWebhook("EXIT", `fatal exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
|
|
10430
10502
|
return pendingExitCode.resolve(exitCode);
|
|
10431
10503
|
}
|
|
10432
10504
|
try {
|
|
@@ -10495,6 +10567,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10495
10567
|
} catch (error) {
|
|
10496
10568
|
logger.warn(`[pidStore] Failed to update status for PID ${exitedPid}:`, error);
|
|
10497
10569
|
}
|
|
10570
|
+
notifyWebhook("EXIT", `${exitReason} exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
|
|
10498
10571
|
return pendingExitCode.resolve(exitCode);
|
|
10499
10572
|
});
|
|
10500
10573
|
process.stdout.on("resize", () => {
|
|
@@ -10563,6 +10636,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10563
10636
|
return;
|
|
10564
10637
|
}
|
|
10565
10638
|
logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
|
|
10639
|
+
notifyWebhook("IDLE", "", workingDir).catch(() => null);
|
|
10566
10640
|
await exitAgent();
|
|
10567
10641
|
});
|
|
10568
10642
|
const stdinStream = new ReadableStream({
|
|
@@ -10674,10 +10748,10 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10674
10748
|
}).forkTo(async function rawLogger(f) {
|
|
10675
10749
|
const rawLogPath = ctx.logPaths.rawLogPath;
|
|
10676
10750
|
if (!rawLogPath) return f.run();
|
|
10677
|
-
return await mkdir(path.dirname(rawLogPath), { recursive: true }).then(() => {
|
|
10751
|
+
return await mkdir$1(path.dirname(rawLogPath), { recursive: true }).then(() => {
|
|
10678
10752
|
logger.debug(`[${cli}-yes] raw logs streaming to ${rawLogPath}`);
|
|
10679
10753
|
return f.forEach(async (chars) => {
|
|
10680
|
-
await writeFile$
|
|
10754
|
+
await writeFile$2(rawLogPath, chars, { flag: "a" }).catch(() => null);
|
|
10681
10755
|
}).run();
|
|
10682
10756
|
}).catch(() => f.run());
|
|
10683
10757
|
}).by(function consoleResponder(e) {
|
|
@@ -10769,5 +10843,5 @@ function sleep(ms) {
|
|
|
10769
10843
|
const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
10770
10844
|
|
|
10771
10845
|
//#endregion
|
|
10772
|
-
export { AgentContext as a, config as i, CLIS_CONFIG as n,
|
|
10773
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
10846
|
+
export { AgentContext as a, PidStore as c, config as i, removeControlCharacters as l, CLIS_CONFIG as n, name as o, agentYes as r, version as s, SUPPORTED_CLIS as t };
|
|
10847
|
+
//# sourceMappingURL=SUPPORTED_CLIS-DNBWqwwx.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { c as __toESM, i as __commonJSMin, r as require_ms, t as logger } from "./logger-DH1Rx9HI.js";
|
|
3
3
|
import "./agent-yes.config-Dr2p5kBW.js";
|
|
4
|
-
import {
|
|
4
|
+
import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DNBWqwwx.js";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { argv } from "process";
|
|
7
7
|
import { spawn } from "child_process";
|
|
@@ -4502,11 +4502,6 @@ function isYargsInstance(y) {
|
|
|
4502
4502
|
//#region node_modules/yargs/index.mjs
|
|
4503
4503
|
const Yargs = YargsFactory(esm_default);
|
|
4504
4504
|
|
|
4505
|
-
//#endregion
|
|
4506
|
-
//#region package.json
|
|
4507
|
-
var name = "agent-yes";
|
|
4508
|
-
var version = "1.55.0";
|
|
4509
|
-
|
|
4510
4505
|
//#endregion
|
|
4511
4506
|
//#region ts/parseCliArgs.ts
|
|
4512
4507
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./logger-DH1Rx9HI.js";
|
|
2
|
-
import { a as AgentContext, i as config,
|
|
2
|
+
import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-DNBWqwwx.js";
|
|
3
3
|
|
|
4
4
|
export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
|
package/package.json
CHANGED
package/ts/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { createAutoResponseHandler } from "./core/responders.ts";
|
|
|
30
30
|
import { createTerminatorStream } from "./core/streamHelpers.ts";
|
|
31
31
|
import { globalAgentRegistry } from "./agentRegistry.ts";
|
|
32
32
|
import { ReadyManager } from "./ReadyManager.ts";
|
|
33
|
+
import { notifyWebhook } from "./webhookNotifier.ts";
|
|
33
34
|
|
|
34
35
|
export { removeControlCharacters };
|
|
35
36
|
export { AgentContext };
|
|
@@ -330,6 +331,7 @@ export default async function agentYes({
|
|
|
330
331
|
} catch (error) {
|
|
331
332
|
logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
|
|
332
333
|
}
|
|
334
|
+
notifyWebhook("RUNNING", prompt ?? "", workingDir).catch(() => null);
|
|
333
335
|
|
|
334
336
|
// Initialize log paths (independent of registration)
|
|
335
337
|
const logPaths = await initializeLogPaths(pidStore, shell.pid);
|
|
@@ -483,6 +485,7 @@ export default async function agentYes({
|
|
|
483
485
|
} catch (error) {
|
|
484
486
|
logger.warn(`[pidStore] Failed to update status for PID ${exitedPid}:`, error);
|
|
485
487
|
}
|
|
488
|
+
notifyWebhook("EXIT", `fatal exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
|
|
486
489
|
return pendingExitCode.resolve(exitCode);
|
|
487
490
|
}
|
|
488
491
|
|
|
@@ -558,6 +561,7 @@ export default async function agentYes({
|
|
|
558
561
|
} catch (error) {
|
|
559
562
|
logger.warn(`[pidStore] Failed to update status for PID ${exitedPid}:`, error);
|
|
560
563
|
}
|
|
564
|
+
notifyWebhook("EXIT", `${exitReason} exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
|
|
561
565
|
return pendingExitCode.resolve(exitCode);
|
|
562
566
|
});
|
|
563
567
|
|
|
@@ -660,6 +664,7 @@ export default async function agentYes({
|
|
|
660
664
|
}
|
|
661
665
|
|
|
662
666
|
logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
|
|
667
|
+
notifyWebhook("IDLE", "", workingDir).catch(() => null);
|
|
663
668
|
await exitAgent();
|
|
664
669
|
});
|
|
665
670
|
|
package/ts/installEnv.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
// Install dir is one level up from this file (ts/ -> package root)
|
|
5
|
+
const installDir = path.join(import.meta.dir, "..");
|
|
6
|
+
|
|
7
|
+
function parseEnvContent(content: string): Record<string, string> {
|
|
8
|
+
const result: Record<string, string> = {};
|
|
9
|
+
for (const line of content.split("\n")) {
|
|
10
|
+
const trimmed = line.trim();
|
|
11
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
12
|
+
const eqIndex = trimmed.indexOf("=");
|
|
13
|
+
if (eqIndex < 0) continue;
|
|
14
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
15
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
16
|
+
// Strip surrounding quotes
|
|
17
|
+
if (
|
|
18
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
19
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
20
|
+
) {
|
|
21
|
+
value = value.slice(1, -1);
|
|
22
|
+
}
|
|
23
|
+
result[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let _installEnv: Record<string, string> | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load .env from the agent-yes install directory (not the working dir).
|
|
32
|
+
* Install dir is ${import.meta.dir}/.. relative to this file.
|
|
33
|
+
* Cached after first load.
|
|
34
|
+
*/
|
|
35
|
+
export async function loadInstallEnv(): Promise<Record<string, string>> {
|
|
36
|
+
if (_installEnv) return _installEnv;
|
|
37
|
+
const envPath = path.join(installDir, ".env");
|
|
38
|
+
try {
|
|
39
|
+
const content = await readFile(envPath, "utf-8");
|
|
40
|
+
_installEnv = parseEnvContent(content);
|
|
41
|
+
} catch {
|
|
42
|
+
_installEnv = {};
|
|
43
|
+
}
|
|
44
|
+
return _installEnv;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a value from the install .env, falling back to process.env.
|
|
49
|
+
*/
|
|
50
|
+
export async function getInstallEnv(key: string): Promise<string | undefined> {
|
|
51
|
+
const env = await loadInstallEnv();
|
|
52
|
+
return env[key] ?? process.env[key];
|
|
53
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import { getInstallEnv } from "./installEnv.ts";
|
|
3
|
+
import { logger } from "./logger.ts";
|
|
4
|
+
|
|
5
|
+
export type WebhookStatus = "RUNNING" | "IDLE" | "EXIT";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Notify the AGENT_YES_MESSAGE_WEBHOOK URL with a status message.
|
|
9
|
+
*
|
|
10
|
+
* AGENT_YES_MESSAGE_WEBHOOK should be set in the agent-yes install dir .env, e.g.:
|
|
11
|
+
* AGENT_YES_MESSAGE_WEBHOOK=https://example.com/hook?q=%s
|
|
12
|
+
*
|
|
13
|
+
* The %s placeholder is replaced with the URL-encoded message:
|
|
14
|
+
* [STATUS] hostname:cwd details
|
|
15
|
+
*/
|
|
16
|
+
export async function notifyWebhook(
|
|
17
|
+
status: WebhookStatus,
|
|
18
|
+
details: string,
|
|
19
|
+
cwd = process.cwd(),
|
|
20
|
+
): Promise<void> {
|
|
21
|
+
const webhookTemplate = await getInstallEnv("AGENT_YES_MESSAGE_WEBHOOK");
|
|
22
|
+
if (!webhookTemplate) return;
|
|
23
|
+
|
|
24
|
+
const hostname = os.hostname();
|
|
25
|
+
const message = `[${status}] ${hostname}:${cwd}${details ? " " + details : ""}`;
|
|
26
|
+
const url = webhookTemplate.replace("%s", encodeURIComponent(message));
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const res = await fetch(url);
|
|
30
|
+
logger.debug(`[webhook] ${status} notified (${res.status}): ${url}`);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
logger.warn(`[webhook] Failed to notify ${status}: ${error}`);
|
|
33
|
+
}
|
|
34
|
+
}
|