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.
@@ -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 { constants } from "node:os";
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$1 } from "fs/promises";
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$1(sessionsFile, JSON.stringify(sessionMap, null, 2));
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$1(tempFile, JSON.stringify(lockFile, null, 2), "utf8");
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$1(this.tempPath, content);
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$1(gitignorePath, gitignoreContent, { flag: "wx" });
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$1(logPath, content).catch(() => null);
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$1(logFilePath, content);
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$1(rawLogPath, chars, { flag: "a" }).catch(() => null);
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, PidStore as o, agentYes as r, removeControlCharacters as s, SUPPORTED_CLIS as t };
10773
- //# sourceMappingURL=SUPPORTED_CLIS-Db5tikbx.js.map
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 { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Db5tikbx.js";
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, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-Db5tikbx.js";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.55.0",
3
+ "version": "1.57.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
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
 
@@ -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
+ }