agent-yes 1.55.0 → 1.56.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-BqLSl2Mv.js} +93 -24
- package/dist/cli.js +2 -2
- 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
|
|
@@ -10151,6 +10152,70 @@ var AgentRegistry = class {
|
|
|
10151
10152
|
};
|
|
10152
10153
|
const globalAgentRegistry = new AgentRegistry();
|
|
10153
10154
|
|
|
10155
|
+
//#endregion
|
|
10156
|
+
//#region ts/installEnv.ts
|
|
10157
|
+
const installDir = path$1.join(import.meta.dir, "..");
|
|
10158
|
+
function parseEnvContent(content) {
|
|
10159
|
+
const result = {};
|
|
10160
|
+
for (const line of content.split("\n")) {
|
|
10161
|
+
const trimmed = line.trim();
|
|
10162
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
10163
|
+
const eqIndex = trimmed.indexOf("=");
|
|
10164
|
+
if (eqIndex < 0) continue;
|
|
10165
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
10166
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
10167
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
10168
|
+
result[key] = value;
|
|
10169
|
+
}
|
|
10170
|
+
return result;
|
|
10171
|
+
}
|
|
10172
|
+
let _installEnv = null;
|
|
10173
|
+
/**
|
|
10174
|
+
* Load .env from the agent-yes install directory (not the working dir).
|
|
10175
|
+
* Install dir is ${import.meta.dir}/.. relative to this file.
|
|
10176
|
+
* Cached after first load.
|
|
10177
|
+
*/
|
|
10178
|
+
async function loadInstallEnv() {
|
|
10179
|
+
if (_installEnv) return _installEnv;
|
|
10180
|
+
const envPath = path$1.join(installDir, ".env");
|
|
10181
|
+
try {
|
|
10182
|
+
_installEnv = parseEnvContent(await readFile(envPath, "utf-8"));
|
|
10183
|
+
} catch {
|
|
10184
|
+
_installEnv = {};
|
|
10185
|
+
}
|
|
10186
|
+
return _installEnv;
|
|
10187
|
+
}
|
|
10188
|
+
/**
|
|
10189
|
+
* Get a value from the install .env, falling back to process.env.
|
|
10190
|
+
*/
|
|
10191
|
+
async function getInstallEnv(key) {
|
|
10192
|
+
return (await loadInstallEnv())[key] ?? process.env[key];
|
|
10193
|
+
}
|
|
10194
|
+
|
|
10195
|
+
//#endregion
|
|
10196
|
+
//#region ts/webhookNotifier.ts
|
|
10197
|
+
/**
|
|
10198
|
+
* Notify the AGENT_YES_MESSAGE_WEBHOOK URL with a status message.
|
|
10199
|
+
*
|
|
10200
|
+
* AGENT_YES_MESSAGE_WEBHOOK should be set in the agent-yes install dir .env, e.g.:
|
|
10201
|
+
* AGENT_YES_MESSAGE_WEBHOOK=https://example.com/hook?q=%s
|
|
10202
|
+
*
|
|
10203
|
+
* The %s placeholder is replaced with the URL-encoded message:
|
|
10204
|
+
* [STATUS] hostname:cwd details
|
|
10205
|
+
*/
|
|
10206
|
+
async function notifyWebhook(status, details, cwd = process.cwd()) {
|
|
10207
|
+
const webhookTemplate = await getInstallEnv("AGENT_YES_MESSAGE_WEBHOOK");
|
|
10208
|
+
if (!webhookTemplate) return;
|
|
10209
|
+
const message = `[${status}] ${os.hostname()}:${cwd}${details ? " " + details : ""}`;
|
|
10210
|
+
const url = webhookTemplate.replace("%s", encodeURIComponent(message));
|
|
10211
|
+
try {
|
|
10212
|
+
const res = await fetch(url);
|
|
10213
|
+
logger.debug(`[webhook] ${status} notified (${res.status}): ${url}`);
|
|
10214
|
+
} catch (error) {
|
|
10215
|
+
logger.warn(`[webhook] Failed to notify ${status}: ${error}`);
|
|
10216
|
+
}
|
|
10217
|
+
}
|
|
10218
|
+
|
|
10154
10219
|
//#endregion
|
|
10155
10220
|
//#region ts/index.ts
|
|
10156
10221
|
const config = await import("./agent-yes.config-mJP0MKqV.js").then((mod) => mod.default || mod);
|
|
@@ -10231,7 +10296,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10231
10296
|
let currentDir = workingDir;
|
|
10232
10297
|
const searchLimit = gitRoot || path.parse(currentDir).root;
|
|
10233
10298
|
while (true) {
|
|
10234
|
-
const md = await readFile(path.resolve(currentDir, "SKILL.md"), "utf8").catch(() => null);
|
|
10299
|
+
const md = await readFile$1(path.resolve(currentDir, "SKILL.md"), "utf8").catch(() => null);
|
|
10235
10300
|
if (md) {
|
|
10236
10301
|
const headerMatch = md.match(/^[\s\S]*?(?=\n##\s)/);
|
|
10237
10302
|
const headerRaw = (headerMatch ? headerMatch[0] : md).trim();
|
|
@@ -10314,6 +10379,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10314
10379
|
} catch (error) {
|
|
10315
10380
|
logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
|
|
10316
10381
|
}
|
|
10382
|
+
notifyWebhook("RUNNING", prompt ?? "", workingDir).catch(() => null);
|
|
10317
10383
|
const logPaths = await initializeLogPaths(pidStore, shell.pid);
|
|
10318
10384
|
setupDebugLogging(logPaths.debuggingLogsPath);
|
|
10319
10385
|
const ctx = new AgentContext({
|
|
@@ -10427,6 +10493,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10427
10493
|
} catch (error) {
|
|
10428
10494
|
logger.warn(`[pidStore] Failed to update status for PID ${exitedPid}:`, error);
|
|
10429
10495
|
}
|
|
10496
|
+
notifyWebhook("EXIT", `fatal exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
|
|
10430
10497
|
return pendingExitCode.resolve(exitCode);
|
|
10431
10498
|
}
|
|
10432
10499
|
try {
|
|
@@ -10495,6 +10562,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10495
10562
|
} catch (error) {
|
|
10496
10563
|
logger.warn(`[pidStore] Failed to update status for PID ${exitedPid}:`, error);
|
|
10497
10564
|
}
|
|
10565
|
+
notifyWebhook("EXIT", `${exitReason} exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
|
|
10498
10566
|
return pendingExitCode.resolve(exitCode);
|
|
10499
10567
|
});
|
|
10500
10568
|
process.stdout.on("resize", () => {
|
|
@@ -10563,6 +10631,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10563
10631
|
return;
|
|
10564
10632
|
}
|
|
10565
10633
|
logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
|
|
10634
|
+
notifyWebhook("IDLE", "", workingDir).catch(() => null);
|
|
10566
10635
|
await exitAgent();
|
|
10567
10636
|
});
|
|
10568
10637
|
const stdinStream = new ReadableStream({
|
|
@@ -10674,10 +10743,10 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
10674
10743
|
}).forkTo(async function rawLogger(f) {
|
|
10675
10744
|
const rawLogPath = ctx.logPaths.rawLogPath;
|
|
10676
10745
|
if (!rawLogPath) return f.run();
|
|
10677
|
-
return await mkdir(path.dirname(rawLogPath), { recursive: true }).then(() => {
|
|
10746
|
+
return await mkdir$1(path.dirname(rawLogPath), { recursive: true }).then(() => {
|
|
10678
10747
|
logger.debug(`[${cli}-yes] raw logs streaming to ${rawLogPath}`);
|
|
10679
10748
|
return f.forEach(async (chars) => {
|
|
10680
|
-
await writeFile$
|
|
10749
|
+
await writeFile$2(rawLogPath, chars, { flag: "a" }).catch(() => null);
|
|
10681
10750
|
}).run();
|
|
10682
10751
|
}).catch(() => f.run());
|
|
10683
10752
|
}).by(function consoleResponder(e) {
|
|
@@ -10770,4 +10839,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
|
10770
10839
|
|
|
10771
10840
|
//#endregion
|
|
10772
10841
|
export { AgentContext as a, config as i, CLIS_CONFIG as n, PidStore as o, agentYes as r, removeControlCharacters as s, SUPPORTED_CLIS as t };
|
|
10773
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
10842
|
+
//# sourceMappingURL=SUPPORTED_CLIS-BqLSl2Mv.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 { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
4
|
+
import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BqLSl2Mv.js";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { argv } from "process";
|
|
7
7
|
import { spawn } from "child_process";
|
|
@@ -4505,7 +4505,7 @@ const Yargs = YargsFactory(esm_default);
|
|
|
4505
4505
|
//#endregion
|
|
4506
4506
|
//#region package.json
|
|
4507
4507
|
var name = "agent-yes";
|
|
4508
|
-
var version = "1.
|
|
4508
|
+
var version = "1.56.0";
|
|
4509
4509
|
|
|
4510
4510
|
//#endregion
|
|
4511
4511
|
//#region ts/parseCliArgs.ts
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./logger-DH1Rx9HI.js";
|
|
2
|
-
import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-
|
|
2
|
+
import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-BqLSl2Mv.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
|
+
}
|