@livx.cc/agentx 0.99.77 → 0.99.79

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.
@@ -28,6 +28,19 @@ function redactSecrets(text) {
28
28
  return text.replace(SECRET_PAIR, (_m, head, quote, _val) => `${head}${quote}${REDACTED}`).replace(SECRET_TOKEN, REDACTED);
29
29
  }
30
30
 
31
+ // src/hostPath.ts
32
+ var currentPlatform = () => typeof process !== "undefined" && process.platform || "browser";
33
+ var isWinPaths = (platform = currentPlatform()) => platform === "win32";
34
+ function toVfsPath(p, platform = currentPlatform()) {
35
+ if (!isWinPaths(platform) || typeof p !== "string") return p;
36
+ let s = p.replace(/\\/g, "/");
37
+ s = s.replace(/^\/\/\?\/UNC\//i, "//").replace(/^\/\/\?\//, "");
38
+ const drive = /^([A-Za-z]):(?=\/|$)/.exec(s);
39
+ if (drive) return `/${drive[1].toLowerCase()}${s.slice(2) || "/"}`.replace(/\/+$/, "") || "/";
40
+ if (/^\/\/[^/]/.test(s)) return `/unc/${s.slice(2)}`;
41
+ return s;
42
+ }
43
+
31
44
  // src/tools.structured.ts
32
45
  function ckAbort(signal) {
33
46
  if (signal?.aborted) throw new Error("aborted");
@@ -72,7 +85,7 @@ async function walkFiles(fs, dir, signal, out = [], st, depth = 0) {
72
85
  }
73
86
  function scanRoots(fs, path, opts) {
74
87
  const asked = path == null ? [] : (Array.isArray(path) ? path : [path]).map((p) => String(p)).filter(Boolean);
75
- const roots = (asked.length ? asked : opts?.roots?.length ? opts.roots : [fsCwd(fs)]).map((r) => r.length > 1 && r.endsWith("/") ? r.slice(0, -1) : r);
88
+ const roots = (asked.length ? asked : opts?.roots?.length ? opts.roots : [fsCwd(fs)]).map((r) => toVfsPath(r)).map((r) => r.length > 1 && r.endsWith("/") ? r.slice(0, -1) : r);
76
89
  return [...new Set(roots)].filter((r, _i, all) => !all.some((other) => other !== r && r.startsWith(`${other}/`)));
77
90
  }
78
91
  function scanNote(states) {
@@ -866,21 +879,130 @@ var JobRegistry = class {
866
879
  }
867
880
  };
868
881
 
869
- // src/tools.shell.ts
870
- var log7 = forComponent("shell");
871
- var clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
872
- var DETACHED = { stdio: ["ignore", "pipe", "pipe"], detached: true };
873
- function killGroup(proc, signal) {
874
- if (!proc?.pid) return false;
882
+ // src/platform.ts
883
+ import { spawnSync, execFile } from "child_process";
884
+ import { statSync as statSync3 } from "fs";
885
+ import * as np from "path";
886
+ var IS_WIN = process.platform === "win32";
887
+ var POSIX_SH = { bin: "/bin/sh", args: (c) => ["-c", c], label: "/bin/sh", kind: "sh" };
888
+ function specFor(bin) {
889
+ const name = np.basename(bin).toLowerCase().replace(/\.exe$/, "");
890
+ if (name === "powershell" || name === "pwsh") {
891
+ const pre = "$ProgressPreference='SilentlyContinue';[Console]::OutputEncoding=[Text.Encoding]::UTF8;$OutputEncoding=[Text.Encoding]::UTF8;";
892
+ return {
893
+ bin,
894
+ kind: "powershell",
895
+ label: name === "pwsh" ? "PowerShell (pwsh)" : "Windows PowerShell",
896
+ args: (c) => ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", Buffer.from(pre + c, "utf16le").toString("base64")]
897
+ };
898
+ }
899
+ if (name === "cmd") return { bin, kind: "cmd", label: "cmd.exe", args: (c) => ["/d", "/s", "/c", `"${c}"`], spawnOptions: { windowsVerbatimArguments: true } };
900
+ return { bin, kind: "sh", label: /bash/.test(name) && /git/i.test(bin) ? "Git Bash" : name, args: (c) => ["-c", c] };
901
+ }
902
+ var isFile = (p) => {
875
903
  try {
876
- process.kill(-proc.pid, signal);
877
- return true;
904
+ return statSync3(p).isFile();
878
905
  } catch {
879
906
  return false;
880
907
  }
908
+ };
909
+ function findGitBash(env = process.env) {
910
+ const roots = [];
911
+ const git = findOnPath("git", env);
912
+ if (git) {
913
+ const dir = np.dirname(git);
914
+ const up = (p, n) => {
915
+ for (let i = 0; i < n; i++) p = np.dirname(p);
916
+ return p;
917
+ };
918
+ roots.push(/[\\/](mingw(32|64)|ucrt64|clang(32|64|arm64))[\\/]bin$/i.test(dir) ? up(dir, 2) : up(dir, 1));
919
+ }
920
+ for (const base of [env.ProgramFiles, env["ProgramFiles(x86)"], env.ProgramW6432, env.LOCALAPPDATA && np.join(env.LOCALAPPDATA, "Programs")])
921
+ if (base) roots.push(np.join(base, "Git"));
922
+ for (const r of roots) {
923
+ const b = np.join(r, "bin", "bash.exe");
924
+ if (isFile(b)) return b;
925
+ }
926
+ return void 0;
881
927
  }
928
+ var _shell;
929
+ function defaultShell() {
930
+ if (!IS_WIN) return POSIX_SH;
931
+ if (_shell) return _shell;
932
+ const override = process.env.AGENTX_SHELL;
933
+ if (override && (isFile(override) || findOnPath(override))) return _shell = specFor(isFile(override) ? override : findOnPath(override));
934
+ const bash = findGitBash();
935
+ if (bash) return _shell = specFor(bash);
936
+ const sysRoot = process.env.SystemRoot || "C:\\Windows";
937
+ const ps = np.join(sysRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
938
+ if (isFile(ps)) return _shell = specFor(ps);
939
+ return _shell = specFor(process.env.ComSpec || "cmd.exe");
940
+ }
941
+ function shellFor(shell) {
942
+ if (!IS_WIN) return shell ? { ...POSIX_SH, bin: shell } : POSIX_SH;
943
+ return shell && isFile(shell) ? specFor(shell) : defaultShell();
944
+ }
945
+ var DETACH_OPTIONS = IS_WIN ? { windowsHide: true } : { detached: true };
946
+ function killTree(pid, signal) {
947
+ if (!pid) return false;
948
+ if (!IS_WIN) {
949
+ try {
950
+ process.kill(-pid, signal);
951
+ return true;
952
+ } catch {
953
+ return false;
954
+ }
955
+ }
956
+ try {
957
+ const r = spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 1e4 });
958
+ return r.status === 0;
959
+ } catch {
960
+ return false;
961
+ }
962
+ }
963
+ async function winTreeCpuSeconds(root) {
964
+ const script = 'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId) $($_.ParentProcessId) $([uint64]$_.UserModeTime + [uint64]$_.KernelModeTime)" }';
965
+ const out = await new Promise((res, rej) => execFile(
966
+ "powershell.exe",
967
+ ["-NoProfile", "-NonInteractive", "-Command", script],
968
+ { timeout: 15e3, windowsHide: true },
969
+ (e, so) => e ? rej(e) : res(String(so))
970
+ ));
971
+ const rows = out.split(/\r?\n/).map((l) => l.trim().split(/\s+/)).filter((r) => r.length === 3).map(([pid, ppid, ticks]) => ({ pid: Number(pid), ppid: Number(ppid), sec: Number(ticks) / 1e7 }));
972
+ const inTree = new Set(rows.filter((r) => r.pid === root).map((r) => r.pid));
973
+ for (let grew = true; grew; ) {
974
+ grew = false;
975
+ for (const r of rows) if (!inTree.has(r.pid) && inTree.has(r.ppid) && r.pid !== r.ppid) {
976
+ inTree.add(r.pid);
977
+ grew = true;
978
+ }
979
+ }
980
+ let total;
981
+ for (const r of rows) if (inTree.has(r.pid) && Number.isFinite(r.sec)) total = (total ?? 0) + r.sec;
982
+ return total;
983
+ }
984
+ function findOnPath(cmd, env = process.env) {
985
+ const exts = IS_WIN && !/\.[^\\/.]+$/.test(cmd) ? (env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
986
+ const cands = (base) => exts.map((e) => base + e);
987
+ if (/[\\/]/.test(cmd)) return cands(cmd).find(isFile);
988
+ const PATH = env.PATH ?? env.Path ?? "";
989
+ for (const dir of PATH.split(np.delimiter)) {
990
+ if (!dir) continue;
991
+ for (const p of cands(np.join(dir, cmd))) if (isFile(p)) return p;
992
+ }
993
+ return void 0;
994
+ }
995
+
996
+ // src/tools.shell.ts
997
+ var log7 = forComponent("shell");
998
+ var clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
999
+ var DETACHED = { stdio: ["ignore", "pipe", "pipe"], ...DETACH_OPTIONS };
1000
+ function killGroup(proc, signal) {
1001
+ return killTree(proc?.pid, signal);
1002
+ }
1003
+ var shellArgv = (sh, command) => ({ bin: sh.bin, args: sh.args(command), ...sh.spawnOptions ? { spawnOptions: sh.spawnOptions } : {} });
882
1004
  async function spawnArgvFor(command, cwd, osSandbox) {
883
- if (!osSandbox) return { bin: "/bin/sh", args: ["-c", command] };
1005
+ if (!osSandbox) return shellArgv(defaultShell(), command);
884
1006
  const opts = osSandbox === true ? {} : osSandbox;
885
1007
  const wrapper = await findSandboxWrapper();
886
1008
  const wrapped = wrapper ? sandboxArgv(command, cwd, opts, process.platform, process.env.TMPDIR) : null;
@@ -910,9 +1032,17 @@ function parsePsTime(t) {
910
1032
  return Number(m[1] ?? 0) * 86400 + Number(m[2] ?? 0) * 3600 + Number(m[3]) * 60 + Number(m[4]);
911
1033
  }
912
1034
  async function psGroupCpuSeconds(pgid) {
1035
+ if (IS_WIN) {
1036
+ try {
1037
+ return await winTreeCpuSeconds(pgid);
1038
+ } catch (e) {
1039
+ log7.debug(`cpu probe failed for ${pgid}: ${e}`);
1040
+ return void 0;
1041
+ }
1042
+ }
913
1043
  try {
914
- const { execFile } = await import("child_process");
915
- const out = await new Promise((res, rej) => execFile("ps", ["-A", "-o", "pid=,ppid=,pgid=,time="], { timeout: 5e3, env: { ...process.env, LC_ALL: "C" } }, (e, so) => e ? rej(e) : res(String(so))));
1044
+ const { execFile: execFile2 } = await import("child_process");
1045
+ const out = await new Promise((res, rej) => execFile2("ps", ["-A", "-o", "pid=,ppid=,pgid=,time="], { timeout: 5e3, env: { ...process.env, LC_ALL: "C" } }, (e, so) => e ? rej(e) : res(String(so))));
916
1046
  return sumTreeCpu(out, pgid);
917
1047
  } catch (e) {
918
1048
  log7.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
@@ -946,8 +1076,8 @@ var ShellJobRegistry = class {
946
1076
  const h = this.track(command, { idleMs: 0 });
947
1077
  try {
948
1078
  const spawn = this.cfg.spawn ?? await nodeSpawn();
949
- const argv = this.cfg.osSandbox ? await spawnArgvFor(command, this.cfg.cwd, this.cfg.osSandbox) : { bin: "/bin/sh", args: ["-c", command] };
950
- this.attach(h, spawn(argv.bin, argv.args, { cwd: this.cfg.cwd, env: childEnv(this.cfg), ...DETACHED }));
1079
+ const argv = this.cfg.osSandbox ? await spawnArgvFor(command, this.cfg.cwd, this.cfg.osSandbox) : shellArgv(defaultShell(), command);
1080
+ this.attach(h, spawn(argv.bin, argv.args, { cwd: this.cfg.cwd, env: childEnv(this.cfg), ...DETACHED, ...argv.spawnOptions }));
951
1081
  } catch (e) {
952
1082
  h.chunk(`failed to spawn: ${e?.message ?? e}`);
953
1083
  h.finish("error", void 0, e?.message ?? String(e));
@@ -1101,7 +1231,7 @@ function makeRealShellTool(options) {
1101
1231
  return { ...this, description: rebound.description, run: rebound.run, withCwd: rebound.withCwd };
1102
1232
  },
1103
1233
  get description() {
1104
- return "Run a shell command via /bin/sh in the working directory. Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed `[exit N]`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. `sudo -n`), or ask the user to run the command themselves. " + (options.registry?.adopt ? `A command still running after ${defaultTimeoutMs}ms is NOT killed \u2014 it is handed to a background job and you get its id, so you can keep checking on it (\`[still running]\`). Pass \`timeoutMs\` (max ${maxTimeoutMs}) to wait longer in the foreground, and always bound network commands yourself (e.g. \`curl -m 10\`). ` : `Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). `) + backgroundDoc();
1234
+ return `Run a shell command via ${shellFor(options.shell).label} in the working directory. ` + windowsShellNote(shellFor(options.shell)) + "Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed `[exit N]`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. `sudo -n`), or ask the user to run the command themselves. " + (options.registry?.adopt ? `A command still running after ${defaultTimeoutMs}ms is NOT killed \u2014 it is handed to a background job and you get its id, so you can keep checking on it (\`[still running]\`). Pass \`timeoutMs\` (max ${maxTimeoutMs}) to wait longer in the foreground, and always bound network commands yourself (e.g. \`curl -m 10\`). ` : `Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). `) + backgroundDoc();
1105
1235
  },
1106
1236
  // Declared so transports above the agent loop can size their own deadline with headroom (see AgentTool.maxDurationMs).
1107
1237
  maxDurationMs: maxTimeoutMs,
@@ -1125,7 +1255,7 @@ function makeRealShellTool(options) {
1125
1255
  return `Started background job ${id}. Poll output with ShellOutput({id:"${id}"}), check ShellStatus({id:"${id}"}), stop with ShellKill({id:"${id}"}).`;
1126
1256
  }
1127
1257
  const spawn = options.spawn ?? await nodeSpawn();
1128
- let argv = { bin: options.shell || "/bin/sh", args: ["-c", cmd] };
1258
+ let argv = shellArgv(shellFor(options.shell), cmd);
1129
1259
  if (options.osSandbox) {
1130
1260
  try {
1131
1261
  argv = await spawnArgvFor(cmd, options.cwd, options.osSandbox);
@@ -1170,12 +1300,13 @@ function makeRealShellTool(options) {
1170
1300
  };
1171
1301
  let proc;
1172
1302
  try {
1173
- proc = spawn(argv.bin, argv.args, { cwd: options.cwd, env: childEnv(options), signal: ctl.signal, ...DETACHED });
1303
+ if (IS_WIN) ctl.signal.addEventListener("abort", () => killGroup(proc, "SIGKILL"), { once: true });
1304
+ proc = spawn(argv.bin, argv.args, { cwd: options.cwd, env: childEnv(options), signal: ctl.signal, ...DETACHED, ...argv.spawnOptions });
1174
1305
  } catch (e) {
1175
1306
  return finish(`[exit 1] failed to spawn shell: ${e?.message ?? e}`);
1176
1307
  }
1177
1308
  if (ctl.signal.aborted) killGroup(proc, "SIGKILL");
1178
- else ctl.signal.addEventListener("abort", () => killGroup(proc, "SIGKILL"), { once: true });
1309
+ else if (!IS_WIN) ctl.signal.addEventListener("abort", () => killGroup(proc, "SIGKILL"), { once: true });
1179
1310
  const registry = options.registry;
1180
1311
  if (registry?.adopt) {
1181
1312
  onDeadline = () => {
@@ -1220,6 +1351,12 @@ function makeRealShellTool(options) {
1220
1351
  }
1221
1352
  };
1222
1353
  }
1354
+ function windowsShellNote(sh) {
1355
+ if (!IS_WIN) return "";
1356
+ if (sh.kind === "sh") return "This is Windows: the shell is bash (POSIX syntax); drive paths are written /c/Users/\u2026 or C:/Users/\u2026, and Windows programs (.exe/.cmd) run directly. ";
1357
+ if (sh.kind === "powershell") return "This is Windows and the shell is PowerShell \u2014 use PowerShell syntax (`;` not `&&`, `$env:VAR`, Get-ChildItem/Select-String), not bash. ";
1358
+ return "This is Windows and the shell is cmd.exe \u2014 use cmd syntax (`&&`, `%VAR%`, dir/type/findstr), not bash. ";
1359
+ }
1223
1360
  function handoffFor(timeoutMs, id, body, notifies) {
1224
1361
  const head = `[still running] exceeded ${timeoutMs}ms, so it was handed to background job ${id} \u2014 NOT killed, it is still going (it is only stopped if it goes silent with no CPU activity for a long time, and then you are told why). Check on it with ShellOutput({id:"${id}"}) / ShellStatus({id:"${id}"}), stop it with ShellKill({id:"${id}"}). ` + (notifies ? "Its completion will be reported to you, so you may continue with other work meanwhile." : "Nothing will tell you when it finishes \u2014 poll it yourself before you rely on its result.");
1225
1362
  return body ? `${head}
@@ -1282,6 +1419,7 @@ export {
1282
1419
  makeShellJobTools,
1283
1420
  parsePsTime,
1284
1421
  psGroupCpuSeconds,
1285
- sumTreeCpu
1422
+ sumTreeCpu,
1423
+ windowsShellNote
1286
1424
  };
1287
1425
  //# sourceMappingURL=tools.shell.js.map