@herdingchats/friday 0.1.621 → 0.1.623
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/herd.mjs +516 -280
- package/package.json +2 -2
package/herd.mjs
CHANGED
|
@@ -18,7 +18,7 @@ var __export = (target, all) => {
|
|
|
18
18
|
var define_HERD_PACKAGE_default;
|
|
19
19
|
var init_define_HERD_PACKAGE = __esm({
|
|
20
20
|
"<define:HERD_PACKAGE>"() {
|
|
21
|
-
define_HERD_PACKAGE_default = { version: "0.1.
|
|
21
|
+
define_HERD_PACKAGE_default = { version: "0.1.623", gitHead: "0c391f8523159d26c6e23d46eb803c56a4e7a9ec" };
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
@@ -3204,9 +3204,9 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
3204
3204
|
init_define_HERD_PACKAGE();
|
|
3205
3205
|
init_src();
|
|
3206
3206
|
import { spawn as spawn7 } from "node:child_process";
|
|
3207
|
-
import { existsSync as
|
|
3208
|
-
import { homedir as
|
|
3209
|
-
import { dirname as dirname10, join as
|
|
3207
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
3208
|
+
import { homedir as homedir5 } from "node:os";
|
|
3209
|
+
import { dirname as dirname10, join as join15 } from "node:path";
|
|
3210
3210
|
import { createInterface } from "node:readline/promises";
|
|
3211
3211
|
import { z as z22 } from "zod";
|
|
3212
3212
|
|
|
@@ -5782,87 +5782,30 @@ function serviceFor(kind, context2) {
|
|
|
5782
5782
|
init_define_HERD_PACKAGE();
|
|
5783
5783
|
init_src();
|
|
5784
5784
|
import { mkdirSync as mkdirSync10, realpathSync as realpathSync4 } from "node:fs";
|
|
5785
|
-
import { join as
|
|
5785
|
+
import { join as join14, resolve as resolve3 } from "node:path";
|
|
5786
5786
|
|
|
5787
|
-
// src/
|
|
5787
|
+
// src/package-manager.ts
|
|
5788
5788
|
init_define_HERD_PACKAGE();
|
|
5789
|
-
import { existsSync as
|
|
5790
|
-
import {
|
|
5791
|
-
import { join as
|
|
5792
|
-
var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
|
|
5793
|
-
var OLD_APPS = ["herd-daemon", "herd-updater"];
|
|
5794
|
-
function oldPm2s(paths) {
|
|
5795
|
-
const dir = join11(paths.herdHome, "daemon");
|
|
5796
|
-
if (!existsSync8(dir)) return [];
|
|
5797
|
-
return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join11(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync8(pm2));
|
|
5798
|
-
}
|
|
5799
|
-
async function retireOldDaemon(paths, { home, run: run4 }) {
|
|
5800
|
-
const removed = [];
|
|
5801
|
-
const plist = join11(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
|
|
5802
|
-
if (existsSync8(plist)) {
|
|
5803
|
-
await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
|
|
5804
|
-
rmSync8(plist, { force: true });
|
|
5805
|
-
removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
|
|
5806
|
-
}
|
|
5807
|
-
for (const pm2 of oldPm2s(paths)) {
|
|
5808
|
-
const listed = await run4(process.execPath, [pm2, "jlist"]);
|
|
5809
|
-
let names = [];
|
|
5810
|
-
try {
|
|
5811
|
-
names = JSON.parse(listed.stdout).map((app) => app.name ?? "");
|
|
5812
|
-
} catch {
|
|
5813
|
-
names = [];
|
|
5814
|
-
}
|
|
5815
|
-
const ours = OLD_APPS.filter((name) => names.includes(name));
|
|
5816
|
-
if (ours.length === 0) continue;
|
|
5817
|
-
for (const name of ours) await run4(process.execPath, [pm2, "delete", name]);
|
|
5818
|
-
await run4(process.execPath, [pm2, "save", "--force"]);
|
|
5819
|
-
removed.push(`pm2's ${ours.join(" and ")}`);
|
|
5820
|
-
}
|
|
5821
|
-
return removed;
|
|
5822
|
-
}
|
|
5823
|
-
|
|
5824
|
-
// src/friday/verbose.ts
|
|
5825
|
-
init_define_HERD_PACKAGE();
|
|
5826
|
-
var VERBOSE_ENV = "HERD_VERBOSE";
|
|
5827
|
-
var began = Date.now();
|
|
5828
|
-
function verbose2(line2) {
|
|
5829
|
-
if (!process.env[VERBOSE_ENV]) return;
|
|
5830
|
-
const seconds = ((Date.now() - began) / 1e3).toFixed(1);
|
|
5831
|
-
process.stderr.write(`[friday +${seconds}s] ${line2}
|
|
5832
|
-
`);
|
|
5833
|
-
}
|
|
5834
|
-
var tail = (text4) => text4.trim().split("\n").slice(-3).join(" | ");
|
|
5835
|
-
function verboseExec(run4) {
|
|
5836
|
-
return async (command, args, options) => {
|
|
5837
|
-
if (!process.env[VERBOSE_ENV]) return run4(command, args, options);
|
|
5838
|
-
const shown = [command, ...args].join(" ");
|
|
5839
|
-
verbose2(`running ${shown}`);
|
|
5840
|
-
const started = Date.now();
|
|
5841
|
-
const ran = await run4(command, args, options);
|
|
5842
|
-
const took = `${Date.now() - started}ms`;
|
|
5843
|
-
verbose2(
|
|
5844
|
-
ran.status === 0 ? `ran ${shown} (${took})` : `${shown} exited ${ran.status} (${took})${ran.stderr.trim() ? `: ${tail(ran.stderr)}` : ""}`
|
|
5845
|
-
);
|
|
5846
|
-
return ran;
|
|
5847
|
-
};
|
|
5848
|
-
}
|
|
5789
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
5790
|
+
import { homedir as homedir4 } from "node:os";
|
|
5791
|
+
import { basename as basename3, delimiter, dirname as dirname9, isAbsolute as isAbsolute2, join as join12 } from "node:path";
|
|
5849
5792
|
|
|
5850
5793
|
// src/friday/versions.ts
|
|
5851
5794
|
init_define_HERD_PACKAGE();
|
|
5852
5795
|
import { spawn as spawn5 } from "node:child_process";
|
|
5853
5796
|
import {
|
|
5854
|
-
existsSync as
|
|
5797
|
+
existsSync as existsSync8,
|
|
5855
5798
|
mkdirSync as mkdirSync9,
|
|
5856
|
-
readdirSync as
|
|
5799
|
+
readdirSync as readdirSync3,
|
|
5857
5800
|
readFileSync as readFileSync8,
|
|
5858
5801
|
readlinkSync,
|
|
5859
5802
|
realpathSync as realpathSync3,
|
|
5860
5803
|
renameSync as renameSync3,
|
|
5861
|
-
rmSync as
|
|
5804
|
+
rmSync as rmSync8,
|
|
5862
5805
|
symlinkSync,
|
|
5863
5806
|
writeFileSync as writeFileSync8
|
|
5864
5807
|
} from "node:fs";
|
|
5865
|
-
import { basename as basename2,
|
|
5808
|
+
import { basename as basename2, join as join11 } from "node:path";
|
|
5866
5809
|
var exec = (command, args, options = {}) => new Promise((resolve7) => {
|
|
5867
5810
|
let stdout = "";
|
|
5868
5811
|
let stderr = "";
|
|
@@ -5883,10 +5826,6 @@ var exec = (command, args, options = {}) => new Promise((resolve7) => {
|
|
|
5883
5826
|
resolve7({ status: status2 ?? 1, stdout, stderr });
|
|
5884
5827
|
});
|
|
5885
5828
|
});
|
|
5886
|
-
function npmCommand() {
|
|
5887
|
-
const beside = join12(dirname9(process.execPath), "npm");
|
|
5888
|
-
return existsSync9(beside) ? beside : "npm";
|
|
5889
|
-
}
|
|
5890
5829
|
function currentVersion(paths) {
|
|
5891
5830
|
try {
|
|
5892
5831
|
return basename2(readlinkSync(paths.current));
|
|
@@ -5895,10 +5834,10 @@ function currentVersion(paths) {
|
|
|
5895
5834
|
}
|
|
5896
5835
|
}
|
|
5897
5836
|
function installedVersions(paths) {
|
|
5898
|
-
if (!
|
|
5899
|
-
return
|
|
5837
|
+
if (!existsSync8(paths.versions)) return [];
|
|
5838
|
+
return readdirSync3(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
|
|
5900
5839
|
}
|
|
5901
|
-
var binOf = (versionDir, bin) =>
|
|
5840
|
+
var binOf = (versionDir, bin) => join11(packageDir(versionDir), `${bin}.mjs`);
|
|
5902
5841
|
async function versionRuns(versionDir, version, run4 = exec) {
|
|
5903
5842
|
const ran = await run4(process.execPath, [binOf(versionDir, "herd"), "--version"], {
|
|
5904
5843
|
timeoutMs: 3e4
|
|
@@ -5910,12 +5849,19 @@ async function versionRuns(versionDir, version, run4 = exec) {
|
|
|
5910
5849
|
return null;
|
|
5911
5850
|
}
|
|
5912
5851
|
function recordedIntegrity(dir) {
|
|
5913
|
-
|
|
5914
|
-
const
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5852
|
+
for (const manager of INSTALLERS) {
|
|
5853
|
+
for (const file of LOCKFILES[manager]) {
|
|
5854
|
+
let lock;
|
|
5855
|
+
try {
|
|
5856
|
+
lock = readFileSync8(join11(dir, file), "utf8");
|
|
5857
|
+
} catch {
|
|
5858
|
+
continue;
|
|
5859
|
+
}
|
|
5860
|
+
const found = lockedIntegrity(manager, lock, PACKAGE_NAME);
|
|
5861
|
+
if (found) return found;
|
|
5862
|
+
}
|
|
5918
5863
|
}
|
|
5864
|
+
return null;
|
|
5919
5865
|
}
|
|
5920
5866
|
function npmFreeEnv() {
|
|
5921
5867
|
return Object.fromEntries(
|
|
@@ -5923,10 +5869,10 @@ function npmFreeEnv() {
|
|
|
5923
5869
|
);
|
|
5924
5870
|
}
|
|
5925
5871
|
async function installVersion(paths, version, { spec, integrity, run: run4 = exec, env = {}, log = () => {
|
|
5926
|
-
} } = {}) {
|
|
5872
|
+
}, manager } = {}) {
|
|
5927
5873
|
if (!VERSION.test(version)) return { ok: false, reason: `${version} isn't a version.` };
|
|
5928
|
-
const dir =
|
|
5929
|
-
if (
|
|
5874
|
+
const dir = join11(paths.versions, version);
|
|
5875
|
+
if (existsSync8(dir)) {
|
|
5930
5876
|
const installed2 = recordedIntegrity(dir);
|
|
5931
5877
|
if (integrity && installed2 !== integrity)
|
|
5932
5878
|
return { ok: false, reason: `the installed ${version} doesn't have the integrity named.` };
|
|
@@ -5934,62 +5880,63 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
|
|
|
5934
5880
|
if (!broken2) return { ok: true, dir };
|
|
5935
5881
|
return { ok: false, reason: `the installed ${version} doesn't run: ${broken2}.` };
|
|
5936
5882
|
}
|
|
5883
|
+
const using = manager ?? await findPackageManager({ env: { ...process.env, ...env }, run: run4 });
|
|
5884
|
+
if (!using)
|
|
5885
|
+
return {
|
|
5886
|
+
ok: false,
|
|
5887
|
+
reason: "there's no package manager to install it with: herd looked for npm, pnpm and Yarn 1 beside Node, on the PATH and in their usual folders."
|
|
5888
|
+
};
|
|
5937
5889
|
sweepStaging(paths);
|
|
5938
|
-
const named =
|
|
5939
|
-
|
|
5890
|
+
const named = join11(paths.versions, `.staging-${version}-${process.pid}`);
|
|
5891
|
+
rmSync8(named, { recursive: true, force: true });
|
|
5940
5892
|
mkdirSync9(named, { recursive: true });
|
|
5941
5893
|
const staging = realpathSync3(named);
|
|
5942
5894
|
writeFileSync8(
|
|
5943
|
-
|
|
5944
|
-
`${JSON.stringify(
|
|
5895
|
+
join11(staging, "package.json"),
|
|
5896
|
+
`${JSON.stringify(allowScriptsManifest(["node-pty"]), null, 2)}
|
|
5945
5897
|
`
|
|
5946
5898
|
);
|
|
5899
|
+
if (using.name === "pnpm")
|
|
5900
|
+
writeFileSync8(join11(staging, "pnpm-workspace.yaml"), allowScriptsWorkspace(["node-pty"]));
|
|
5947
5901
|
const fail = (reason) => {
|
|
5948
|
-
|
|
5902
|
+
rmSync8(staging, { recursive: true, force: true });
|
|
5949
5903
|
return { ok: false, reason };
|
|
5950
5904
|
};
|
|
5951
|
-
log(`Installing ${PACKAGE_NAME}@${version}.`);
|
|
5905
|
+
log(`Installing ${PACKAGE_NAME}@${version} with ${using.name} ${using.version}.`);
|
|
5952
5906
|
const installed = await run4(
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
"install",
|
|
5956
|
-
"--prefix",
|
|
5957
|
-
staging,
|
|
5958
|
-
spec ?? `${PACKAGE_NAME}@${version}`,
|
|
5959
|
-
"--no-audit",
|
|
5960
|
-
"--no-fund",
|
|
5961
|
-
"--no-update-notifier",
|
|
5962
|
-
"--save-exact"
|
|
5963
|
-
],
|
|
5907
|
+
using.command,
|
|
5908
|
+
addArgs(using.name, staging, spec ?? `${PACKAGE_NAME}@${version}`),
|
|
5964
5909
|
// From inside the folder, so a workspace around wherever this ran isn't taken for its project.
|
|
5965
5910
|
{ cwd: staging, env: { ...npmFreeEnv(), ...env }, timeoutMs: 10 * 6e4 }
|
|
5966
5911
|
);
|
|
5967
5912
|
if (installed.status !== 0)
|
|
5968
|
-
return fail(
|
|
5913
|
+
return fail(
|
|
5914
|
+
`${using.name} couldn't install it: ${(installed.stderr || installed.stdout).trim().slice(-500)}`
|
|
5915
|
+
);
|
|
5969
5916
|
if (integrity) {
|
|
5970
5917
|
const got = recordedIntegrity(staging);
|
|
5971
5918
|
if (got !== integrity)
|
|
5972
5919
|
return fail(
|
|
5973
|
-
`its integrity doesn't match the release:
|
|
5920
|
+
`its integrity doesn't match the release: ${using.name} recorded ${got ?? "nothing"}, and herd named ${integrity}`
|
|
5974
5921
|
);
|
|
5975
5922
|
}
|
|
5976
5923
|
const broken = await versionRuns(staging, version, run4);
|
|
5977
5924
|
if (broken) return fail(broken);
|
|
5978
|
-
renameSync3(staging,
|
|
5925
|
+
renameSync3(staging, join11(realpathSync3(paths.versions), version));
|
|
5979
5926
|
return { ok: true, dir };
|
|
5980
5927
|
}
|
|
5981
5928
|
function sweepStaging(paths) {
|
|
5982
|
-
if (!
|
|
5983
|
-
for (const name of
|
|
5929
|
+
if (!existsSync8(paths.versions)) return;
|
|
5930
|
+
for (const name of readdirSync3(paths.versions)) {
|
|
5984
5931
|
const match = /^\.staging-.+-(\d+)$/.exec(name);
|
|
5985
5932
|
if (match && !alive2(Number(match[1])))
|
|
5986
|
-
|
|
5933
|
+
rmSync8(join11(paths.versions, name), { recursive: true, force: true });
|
|
5987
5934
|
}
|
|
5988
5935
|
}
|
|
5989
5936
|
function activate(paths, version) {
|
|
5990
5937
|
const next = `${paths.current}.next-${process.pid}`;
|
|
5991
|
-
|
|
5992
|
-
symlinkSync(
|
|
5938
|
+
rmSync8(next, { force: true });
|
|
5939
|
+
symlinkSync(join11("versions", version), next);
|
|
5993
5940
|
renameSync3(next, paths.current);
|
|
5994
5941
|
}
|
|
5995
5942
|
function pruneVersions(paths, { inUse = [], keep = 3 } = {}) {
|
|
@@ -6004,7 +5951,7 @@ function pruneVersions(paths, { inUse = [], keep = 3 } = {}) {
|
|
|
6004
5951
|
const removed = [];
|
|
6005
5952
|
for (const version of versions) {
|
|
6006
5953
|
if (kept.has(version)) continue;
|
|
6007
|
-
|
|
5954
|
+
rmSync8(join11(paths.versions, version), { recursive: true, force: true });
|
|
6008
5955
|
removed.push(version);
|
|
6009
5956
|
}
|
|
6010
5957
|
return removed;
|
|
@@ -6028,6 +5975,271 @@ async function hostVersions(paths, run4 = exec) {
|
|
|
6028
5975
|
return [...found].filter(Boolean);
|
|
6029
5976
|
}
|
|
6030
5977
|
|
|
5978
|
+
// src/package-manager.ts
|
|
5979
|
+
var MANAGERS = ["npm", "pnpm", "yarn", "bun"];
|
|
5980
|
+
var INSTALLERS = ["npm", "pnpm", "yarn"];
|
|
5981
|
+
var MANAGER_ENV = "HERD_PACKAGE_MANAGER";
|
|
5982
|
+
var nameOf = (command) => {
|
|
5983
|
+
const name = basename3(command).replace(/\.(cmd|exe|ps1)$/i, "");
|
|
5984
|
+
return MANAGERS.includes(name) ? name : null;
|
|
5985
|
+
};
|
|
5986
|
+
function homesOf(name, env, home) {
|
|
5987
|
+
const common = [join12(home, ".volta", "bin"), join12(home, ".local", "bin")];
|
|
5988
|
+
switch (name) {
|
|
5989
|
+
case "pnpm":
|
|
5990
|
+
return [
|
|
5991
|
+
...env.PNPM_HOME ? [env.PNPM_HOME] : [],
|
|
5992
|
+
join12(home, "Library", "pnpm"),
|
|
5993
|
+
join12(home, ".local", "share", "pnpm"),
|
|
5994
|
+
...common
|
|
5995
|
+
];
|
|
5996
|
+
case "bun":
|
|
5997
|
+
return [join12(env.BUN_INSTALL || join12(home, ".bun"), "bin"), ...common];
|
|
5998
|
+
case "yarn":
|
|
5999
|
+
return [join12(home, ".yarn", "bin"), ...common];
|
|
6000
|
+
case "npm":
|
|
6001
|
+
return common;
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
function candidatePaths(name, {
|
|
6005
|
+
env = process.env,
|
|
6006
|
+
execPath = process.execPath,
|
|
6007
|
+
home = homedir4(),
|
|
6008
|
+
platform: platform2 = process.platform
|
|
6009
|
+
} = {}) {
|
|
6010
|
+
const path = (env.PATH ?? env.Path ?? "").split(platform2 === "win32" ? ";" : delimiter);
|
|
6011
|
+
const dirs = [
|
|
6012
|
+
dirname9(execPath),
|
|
6013
|
+
...path,
|
|
6014
|
+
...homesOf(name, env, home),
|
|
6015
|
+
"/opt/homebrew/bin",
|
|
6016
|
+
"/usr/local/bin",
|
|
6017
|
+
"/usr/bin"
|
|
6018
|
+
];
|
|
6019
|
+
const files = platform2 === "win32" ? [`${name}.cmd`, `${name}.exe`] : [name];
|
|
6020
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6021
|
+
for (const dir of dirs.filter(Boolean)) {
|
|
6022
|
+
for (const file of files) seen.add(join12(dir, file));
|
|
6023
|
+
}
|
|
6024
|
+
return [...seen];
|
|
6025
|
+
}
|
|
6026
|
+
async function versionOf(command, run4, env) {
|
|
6027
|
+
const ran = await run4(command, ["--version"], {
|
|
6028
|
+
// A corepack shim would otherwise ask before downloading the manager it stands in for.
|
|
6029
|
+
env: { ...env, COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
|
|
6030
|
+
timeoutMs: 2e4
|
|
6031
|
+
});
|
|
6032
|
+
const version = ran.stdout.trim().split("\n").at(-1)?.trim() ?? "";
|
|
6033
|
+
return ran.status === 0 && /^\d+\.\d+/.test(version) ? version : null;
|
|
6034
|
+
}
|
|
6035
|
+
var installs = (name, version) => name !== "yarn" || Number(version.split(".")[0]) < 2;
|
|
6036
|
+
async function findPackageManager(options = {}) {
|
|
6037
|
+
const env = options.env ?? process.env;
|
|
6038
|
+
const exists = options.exists ?? existsSync9;
|
|
6039
|
+
const run4 = options.run ?? exec;
|
|
6040
|
+
const only = options.only ?? INSTALLERS;
|
|
6041
|
+
const accept = options.accept ?? installs;
|
|
6042
|
+
const tried = /* @__PURE__ */ new Set();
|
|
6043
|
+
const attempt = async (name, command) => {
|
|
6044
|
+
if (tried.has(command)) return null;
|
|
6045
|
+
tried.add(command);
|
|
6046
|
+
if (isAbsolute2(command) && !exists(command)) return null;
|
|
6047
|
+
const version = await versionOf(command, run4, env);
|
|
6048
|
+
return version && accept(name, version) ? { name, command, version } : null;
|
|
6049
|
+
};
|
|
6050
|
+
const override = env[MANAGER_ENV]?.trim();
|
|
6051
|
+
if (override) {
|
|
6052
|
+
const name = nameOf(override);
|
|
6053
|
+
if (name && only.includes(name)) {
|
|
6054
|
+
const paths = isAbsolute2(override) ? [override] : candidatePaths(name, options);
|
|
6055
|
+
for (const path of paths) {
|
|
6056
|
+
const found = await attempt(name, path);
|
|
6057
|
+
if (found) return found;
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
for (const name of only) {
|
|
6062
|
+
for (const path of candidatePaths(name, options)) {
|
|
6063
|
+
const found = await attempt(name, path);
|
|
6064
|
+
if (found) return found;
|
|
6065
|
+
}
|
|
6066
|
+
}
|
|
6067
|
+
return null;
|
|
6068
|
+
}
|
|
6069
|
+
function addArgs(manager, dir, spec) {
|
|
6070
|
+
if (manager === "bun") throw new Error("Bun doesn't install Friday's versions.");
|
|
6071
|
+
switch (manager) {
|
|
6072
|
+
case "npm":
|
|
6073
|
+
return [
|
|
6074
|
+
"install",
|
|
6075
|
+
"--prefix",
|
|
6076
|
+
dir,
|
|
6077
|
+
spec,
|
|
6078
|
+
"--no-audit",
|
|
6079
|
+
"--no-fund",
|
|
6080
|
+
"--no-update-notifier",
|
|
6081
|
+
"--save-exact"
|
|
6082
|
+
];
|
|
6083
|
+
case "pnpm":
|
|
6084
|
+
return ["add", spec, "--dir", dir, "--save-exact", "--config.update-notifier=false"];
|
|
6085
|
+
case "yarn":
|
|
6086
|
+
return ["add", spec, "--cwd", dir, "--exact", "--no-progress", "--non-interactive"];
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
var LOCKFILES = {
|
|
6090
|
+
npm: ["package-lock.json", "npm-shrinkwrap.json"],
|
|
6091
|
+
pnpm: ["pnpm-lock.yaml"],
|
|
6092
|
+
bun: ["bun.lock", "bun.lockb"],
|
|
6093
|
+
yarn: ["yarn.lock"]
|
|
6094
|
+
};
|
|
6095
|
+
var escapeRegExp = (text4) => text4.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
6096
|
+
function lockedIntegrity(manager, lock, name) {
|
|
6097
|
+
const integrity = /sha512-[A-Za-z0-9+/]+=*/;
|
|
6098
|
+
switch (manager) {
|
|
6099
|
+
case "npm": {
|
|
6100
|
+
try {
|
|
6101
|
+
const parsed = JSON.parse(lock);
|
|
6102
|
+
return parsed.packages?.[`node_modules/${name}`]?.integrity ?? null;
|
|
6103
|
+
} catch {
|
|
6104
|
+
return null;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
case "bun":
|
|
6108
|
+
return null;
|
|
6109
|
+
case "pnpm":
|
|
6110
|
+
case "yarn": {
|
|
6111
|
+
const lines = lock.split("\n");
|
|
6112
|
+
const key = new RegExp(`^(\\s*)["']?${escapeRegExp(name)}@[^\\n]*:\\s*$`);
|
|
6113
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6114
|
+
const match = key.exec(lines[i] ?? "");
|
|
6115
|
+
if (!match) continue;
|
|
6116
|
+
const indent = match[1]?.length ?? 0;
|
|
6117
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
6118
|
+
const line2 = lines[j] ?? "";
|
|
6119
|
+
if (line2.trim() === "") continue;
|
|
6120
|
+
if (line2.length - line2.trimStart().length <= indent) break;
|
|
6121
|
+
const found = integrity.exec(line2);
|
|
6122
|
+
if (found) return found[0];
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
return null;
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
}
|
|
6129
|
+
function allowScriptsManifest(packages) {
|
|
6130
|
+
return {
|
|
6131
|
+
private: true,
|
|
6132
|
+
// npm's.
|
|
6133
|
+
allowScripts: Object.fromEntries(packages.map((name) => [name, true])),
|
|
6134
|
+
// pnpm's, before pnpm-workspace.yaml held them.
|
|
6135
|
+
pnpm: { onlyBuiltDependencies: packages }
|
|
6136
|
+
};
|
|
6137
|
+
}
|
|
6138
|
+
var allowScriptsWorkspace = (packages) => `onlyBuiltDependencies:
|
|
6139
|
+
${packages.map((name) => ` - ${name}`).join("\n")}
|
|
6140
|
+
allowBuilds:
|
|
6141
|
+
${packages.map((name) => ` ${name}: true`).join("\n")}
|
|
6142
|
+
`;
|
|
6143
|
+
function managerOfCheckout(packageManagerField, hasFile) {
|
|
6144
|
+
const declared = packageManagerField ? /^(npm|pnpm|bun|yarn)@(\d+)/.exec(packageManagerField) : null;
|
|
6145
|
+
const lockOf = (name) => LOCKFILES[name].find(hasFile) ?? null;
|
|
6146
|
+
if (declared) {
|
|
6147
|
+
const name = declared[1];
|
|
6148
|
+
return { name, lockfile: lockOf(name), major: Number(declared[2]) };
|
|
6149
|
+
}
|
|
6150
|
+
for (const name of MANAGERS) {
|
|
6151
|
+
const lockfile = lockOf(name);
|
|
6152
|
+
if (lockfile) return { name, lockfile, major: null };
|
|
6153
|
+
}
|
|
6154
|
+
return null;
|
|
6155
|
+
}
|
|
6156
|
+
function frozenInstallArgs(name, { offline, yarnMajor }) {
|
|
6157
|
+
switch (name) {
|
|
6158
|
+
case "npm":
|
|
6159
|
+
return [
|
|
6160
|
+
"ci",
|
|
6161
|
+
offline ? "--offline" : "--prefer-offline",
|
|
6162
|
+
"--no-audit",
|
|
6163
|
+
"--no-fund",
|
|
6164
|
+
"--no-update-notifier"
|
|
6165
|
+
];
|
|
6166
|
+
case "pnpm":
|
|
6167
|
+
return ["install", "--frozen-lockfile", offline ? "--offline" : "--prefer-offline"];
|
|
6168
|
+
case "bun":
|
|
6169
|
+
return ["install", "--frozen-lockfile", "--no-progress"];
|
|
6170
|
+
case "yarn":
|
|
6171
|
+
return (yarnMajor ?? 1) >= 2 ? ["install", "--immutable"] : [
|
|
6172
|
+
"install",
|
|
6173
|
+
"--frozen-lockfile",
|
|
6174
|
+
"--non-interactive",
|
|
6175
|
+
...offline ? ["--offline"] : ["--prefer-offline"]
|
|
6176
|
+
];
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
|
|
6180
|
+
// src/friday/legacy.ts
|
|
6181
|
+
init_define_HERD_PACKAGE();
|
|
6182
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, rmSync as rmSync9 } from "node:fs";
|
|
6183
|
+
import { userInfo as userInfo2 } from "node:os";
|
|
6184
|
+
import { join as join13 } from "node:path";
|
|
6185
|
+
var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
|
|
6186
|
+
var OLD_APPS = ["herd-daemon", "herd-updater"];
|
|
6187
|
+
function oldPm2s(paths) {
|
|
6188
|
+
const dir = join13(paths.herdHome, "daemon");
|
|
6189
|
+
if (!existsSync10(dir)) return [];
|
|
6190
|
+
return readdirSync4(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join13(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync10(pm2));
|
|
6191
|
+
}
|
|
6192
|
+
async function retireOldDaemon(paths, { home, run: run4 }) {
|
|
6193
|
+
const removed = [];
|
|
6194
|
+
const plist = join13(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
|
|
6195
|
+
if (existsSync10(plist)) {
|
|
6196
|
+
await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
|
|
6197
|
+
rmSync9(plist, { force: true });
|
|
6198
|
+
removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
|
|
6199
|
+
}
|
|
6200
|
+
for (const pm2 of oldPm2s(paths)) {
|
|
6201
|
+
const listed = await run4(process.execPath, [pm2, "jlist"]);
|
|
6202
|
+
let names = [];
|
|
6203
|
+
try {
|
|
6204
|
+
names = JSON.parse(listed.stdout).map((app) => app.name ?? "");
|
|
6205
|
+
} catch {
|
|
6206
|
+
names = [];
|
|
6207
|
+
}
|
|
6208
|
+
const ours = OLD_APPS.filter((name) => names.includes(name));
|
|
6209
|
+
if (ours.length === 0) continue;
|
|
6210
|
+
for (const name of ours) await run4(process.execPath, [pm2, "delete", name]);
|
|
6211
|
+
await run4(process.execPath, [pm2, "save", "--force"]);
|
|
6212
|
+
removed.push(`pm2's ${ours.join(" and ")}`);
|
|
6213
|
+
}
|
|
6214
|
+
return removed;
|
|
6215
|
+
}
|
|
6216
|
+
|
|
6217
|
+
// src/friday/verbose.ts
|
|
6218
|
+
init_define_HERD_PACKAGE();
|
|
6219
|
+
var VERBOSE_ENV = "HERD_VERBOSE";
|
|
6220
|
+
var began = Date.now();
|
|
6221
|
+
function verbose2(line2) {
|
|
6222
|
+
if (!process.env[VERBOSE_ENV]) return;
|
|
6223
|
+
const seconds = ((Date.now() - began) / 1e3).toFixed(1);
|
|
6224
|
+
process.stderr.write(`[friday +${seconds}s] ${line2}
|
|
6225
|
+
`);
|
|
6226
|
+
}
|
|
6227
|
+
var tail = (text4) => text4.trim().split("\n").slice(-3).join(" | ");
|
|
6228
|
+
function verboseExec(run4) {
|
|
6229
|
+
return async (command, args, options) => {
|
|
6230
|
+
if (!process.env[VERBOSE_ENV]) return run4(command, args, options);
|
|
6231
|
+
const shown = [command, ...args].join(" ");
|
|
6232
|
+
verbose2(`running ${shown}`);
|
|
6233
|
+
const started = Date.now();
|
|
6234
|
+
const ran = await run4(command, args, options);
|
|
6235
|
+
const took = `${Date.now() - started}ms`;
|
|
6236
|
+
verbose2(
|
|
6237
|
+
ran.status === 0 ? `ran ${shown} (${took})` : `${shown} exited ${ran.status} (${took})${ran.stderr.trim() ? `: ${tail(ran.stderr)}` : ""}`
|
|
6238
|
+
);
|
|
6239
|
+
return ran;
|
|
6240
|
+
};
|
|
6241
|
+
}
|
|
6242
|
+
|
|
6031
6243
|
// src/friday/start.ts
|
|
6032
6244
|
var MIN_NODE = [22, 18];
|
|
6033
6245
|
var SETUP_WAIT_MS = 2e4;
|
|
@@ -6058,7 +6270,7 @@ var real = (path) => {
|
|
|
6058
6270
|
}
|
|
6059
6271
|
};
|
|
6060
6272
|
function chooseRoot(cwd, home) {
|
|
6061
|
-
return real(cwd) === real(home) ?
|
|
6273
|
+
return real(cwd) === real(home) ? join14(home, "friday") : resolve3(cwd);
|
|
6062
6274
|
}
|
|
6063
6275
|
async function runStart(options) {
|
|
6064
6276
|
const { paths, run: run4 } = options;
|
|
@@ -6123,9 +6335,15 @@ async function runStart(options) {
|
|
|
6123
6335
|
print(
|
|
6124
6336
|
`Stopped the old daemon: removed ${retired.join(" and ")}. Its checkout is left as it was.`
|
|
6125
6337
|
);
|
|
6126
|
-
await
|
|
6338
|
+
const manager = options.install?.manager ?? await findPackageManager({ run: run4 });
|
|
6339
|
+
if (!manager)
|
|
6340
|
+
throw new StartError(
|
|
6341
|
+
"Friday installs and updates itself with npm, pnpm or Yarn 1, and herd found none of them that runs, beside Node, on your PATH or in their usual folders. Install one, or set HERD_PACKAGE_MANAGER to the path of one, then run this again."
|
|
6342
|
+
);
|
|
6343
|
+
verbose2(`installing with ${manager.name} ${manager.version}, at ${manager.command}`);
|
|
6344
|
+
await ensureInstalled({ ...options, install: { ...options.install, manager } }, print);
|
|
6127
6345
|
const command = [process.execPath, binOf(paths.current, "friday"), "supervise"];
|
|
6128
|
-
const env = { HERD_HOME: paths.herdHome, ...options.env };
|
|
6346
|
+
const env = { HERD_HOME: paths.herdHome, [MANAGER_ENV]: manager.command, ...options.env };
|
|
6129
6347
|
const service = (options.service ?? defaultService(options))(command, env);
|
|
6130
6348
|
if (moving && running) {
|
|
6131
6349
|
print(`Stopping Friday at ${existing} to start it again from ${root}.`);
|
|
@@ -6231,7 +6449,7 @@ init_src();
|
|
|
6231
6449
|
import { spawn as spawn6 } from "node:child_process";
|
|
6232
6450
|
import {
|
|
6233
6451
|
closeSync as closeSync3,
|
|
6234
|
-
existsSync as
|
|
6452
|
+
existsSync as existsSync11,
|
|
6235
6453
|
mkdirSync as mkdirSync11,
|
|
6236
6454
|
openSync as openSync4,
|
|
6237
6455
|
readFileSync as readFileSync9,
|
|
@@ -6316,7 +6534,7 @@ var Supervisor = class {
|
|
|
6316
6534
|
const { daemonLog, logs } = this.options.paths;
|
|
6317
6535
|
mkdirSync11(logs, { recursive: true });
|
|
6318
6536
|
try {
|
|
6319
|
-
if (
|
|
6537
|
+
if (existsSync11(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
|
|
6320
6538
|
renameSync4(daemonLog, `${daemonLog}.1`);
|
|
6321
6539
|
} catch {
|
|
6322
6540
|
}
|
|
@@ -6469,9 +6687,9 @@ Usage: ${FRIDAY_COMMAND} <command>
|
|
|
6469
6687
|
|
|
6470
6688
|
Agents use the herd command, which comes with this one. herd setup shows what's left to set up.`;
|
|
6471
6689
|
function defaults() {
|
|
6472
|
-
const home =
|
|
6690
|
+
const home = homedir5();
|
|
6473
6691
|
return {
|
|
6474
|
-
paths: fridayPaths(process.env.HERD_HOME ??
|
|
6692
|
+
paths: fridayPaths(process.env.HERD_HOME ?? join15(home, ".herd")),
|
|
6475
6693
|
home,
|
|
6476
6694
|
cwd: process.cwd(),
|
|
6477
6695
|
run: exec,
|
|
@@ -6543,7 +6761,7 @@ async function friday(args, deps = defaults()) {
|
|
|
6543
6761
|
agents: {
|
|
6544
6762
|
// The choice lives in herd.config.ts, so it's asked for only before there is one, or
|
|
6545
6763
|
// with --config.
|
|
6546
|
-
ask: rest.includes("--config") || !
|
|
6764
|
+
ask: rest.includes("--config") || !existsSync12(herdConfigIn(deps.paths.herdHome)),
|
|
6547
6765
|
installed: async () => {
|
|
6548
6766
|
verbose2("looking for installed agents");
|
|
6549
6767
|
const found = installedAgents();
|
|
@@ -6602,7 +6820,7 @@ function agentsFlag(args) {
|
|
|
6602
6820
|
return parsed.shares;
|
|
6603
6821
|
}
|
|
6604
6822
|
function attachTo(paths, terminal) {
|
|
6605
|
-
const herd =
|
|
6823
|
+
const herd = existsSync12(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join15(dirname10(process.argv[1] ?? ""), "herd.mjs");
|
|
6606
6824
|
const child = spawn7(process.execPath, [herd, "attach", terminal], {
|
|
6607
6825
|
stdio: "inherit",
|
|
6608
6826
|
env: { ...process.env, HERD_HOME: paths.herdHome }
|
|
@@ -6666,14 +6884,14 @@ function runStatus(deps) {
|
|
|
6666
6884
|
function readMissingFolder(herdHome2) {
|
|
6667
6885
|
try {
|
|
6668
6886
|
return folderUnavailableSchema.parse(
|
|
6669
|
-
JSON.parse(readFileSync10(
|
|
6887
|
+
JSON.parse(readFileSync10(join15(herdHome2, "folder-unavailable.json"), "utf8"))
|
|
6670
6888
|
);
|
|
6671
6889
|
} catch {
|
|
6672
6890
|
return null;
|
|
6673
6891
|
}
|
|
6674
6892
|
}
|
|
6675
6893
|
function tail2(path, count) {
|
|
6676
|
-
if (!
|
|
6894
|
+
if (!existsSync12(path)) return [];
|
|
6677
6895
|
const lines = readFileSync10(path, "utf8").split("\n");
|
|
6678
6896
|
if (lines.at(-1) === "") lines.pop();
|
|
6679
6897
|
return lines.slice(-count);
|
|
@@ -6681,7 +6899,7 @@ function tail2(path, count) {
|
|
|
6681
6899
|
async function runLogs(args, deps) {
|
|
6682
6900
|
const { paths, print } = deps;
|
|
6683
6901
|
const follow = args.includes("-f") || args.includes("--follow");
|
|
6684
|
-
const files = [paths.supervisorLog, paths.daemonLog].filter((file) =>
|
|
6902
|
+
const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync12(file));
|
|
6685
6903
|
if (files.length === 0) {
|
|
6686
6904
|
print(`There are no logs in ${paths.logs} yet.`);
|
|
6687
6905
|
return 0;
|
|
@@ -6731,7 +6949,7 @@ init_define_HERD_PACKAGE();
|
|
|
6731
6949
|
init_define_HERD_PACKAGE();
|
|
6732
6950
|
import { spawn as spawn8 } from "node:child_process";
|
|
6733
6951
|
import { appendFileSync, readFileSync as readFileSync11 } from "node:fs";
|
|
6734
|
-
import { dirname as dirname11, join as
|
|
6952
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
6735
6953
|
var DEPLOY_HOSTS = ["api.cloudflare.com", "api.github.com", "registry.npmjs.org"];
|
|
6736
6954
|
function buildPolicy(input) {
|
|
6737
6955
|
const platform2 = input.platform ?? process.platform;
|
|
@@ -6778,7 +6996,7 @@ async function runSandboxed(args) {
|
|
|
6778
6996
|
return 3;
|
|
6779
6997
|
}
|
|
6780
6998
|
await SandboxManager.initialize(policy, void 0, true);
|
|
6781
|
-
const blocked =
|
|
6999
|
+
const blocked = join16(process.env.HOME ?? ".", BLOCKED_LOG);
|
|
6782
7000
|
let written = 0;
|
|
6783
7001
|
const unsubscribe = SandboxManager.getSandboxViolationStore().subscribe((violations) => {
|
|
6784
7002
|
const fresh = violations.slice(written).map((violation) => describeRefusal(violation.line)).filter((line2) => line2 !== null);
|
|
@@ -7279,8 +7497,8 @@ init_define_HERD_PACKAGE();
|
|
|
7279
7497
|
// src/build-runner-key.ts
|
|
7280
7498
|
init_define_HERD_PACKAGE();
|
|
7281
7499
|
init_src();
|
|
7282
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
7283
|
-
import { join as
|
|
7500
|
+
import { chmodSync as chmodSync3, existsSync as existsSync13, mkdirSync as mkdirSync12, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
7501
|
+
import { join as join17 } from "node:path";
|
|
7284
7502
|
import { z as z24 } from "zod";
|
|
7285
7503
|
var BUILDS_URL = "https://builds.herdingchats.com";
|
|
7286
7504
|
var runnerKeySchema = z24.object({
|
|
@@ -7288,10 +7506,10 @@ var runnerKeySchema = z24.object({
|
|
|
7288
7506
|
key: z24.string().min(20),
|
|
7289
7507
|
url: z24.string().url()
|
|
7290
7508
|
});
|
|
7291
|
-
var runnerKeyPath = () =>
|
|
7509
|
+
var runnerKeyPath = () => join17(herdHome(), "build-runner.json");
|
|
7292
7510
|
function readRunnerKey() {
|
|
7293
7511
|
const path = runnerKeyPath();
|
|
7294
|
-
if (!
|
|
7512
|
+
if (!existsSync13(path)) return null;
|
|
7295
7513
|
try {
|
|
7296
7514
|
return runnerKeySchema.parse(JSON.parse(readFileSync13(path, "utf8")));
|
|
7297
7515
|
} catch {
|
|
@@ -7470,7 +7688,7 @@ function formatSpeed(report) {
|
|
|
7470
7688
|
// src/builds-admin.ts
|
|
7471
7689
|
init_define_HERD_PACKAGE();
|
|
7472
7690
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
7473
|
-
import { join as
|
|
7691
|
+
import { join as join18 } from "node:path";
|
|
7474
7692
|
var NOT_ADMIN = "builds are only for the admin's machines";
|
|
7475
7693
|
async function isAdmin(fetch2) {
|
|
7476
7694
|
const response = await fetch2("/api/people/me").catch(() => null);
|
|
@@ -7478,13 +7696,13 @@ async function isAdmin(fetch2) {
|
|
|
7478
7696
|
const body = await response.json().catch(() => ({}));
|
|
7479
7697
|
return body.admin === true;
|
|
7480
7698
|
}
|
|
7481
|
-
var refusalPath = () =>
|
|
7699
|
+
var refusalPath = () => join18(herdHome(), "builds", "refused.json");
|
|
7482
7700
|
function writeBuildsRefusal(reason) {
|
|
7483
7701
|
if (!reason) {
|
|
7484
7702
|
rmSync10(refusalPath(), { force: true });
|
|
7485
7703
|
return;
|
|
7486
7704
|
}
|
|
7487
|
-
mkdirSync13(
|
|
7705
|
+
mkdirSync13(join18(herdHome(), "builds"), { recursive: true });
|
|
7488
7706
|
writeFileSync10(refusalPath(), JSON.stringify({ reason }));
|
|
7489
7707
|
}
|
|
7490
7708
|
function readBuildsRefusal() {
|
|
@@ -7648,7 +7866,7 @@ async function runConversations(rest) {
|
|
|
7648
7866
|
init_define_HERD_PACKAGE();
|
|
7649
7867
|
init_src();
|
|
7650
7868
|
import { availableParallelism, totalmem } from "node:os";
|
|
7651
|
-
import { join as
|
|
7869
|
+
import { join as join34 } from "node:path";
|
|
7652
7870
|
|
|
7653
7871
|
// src/agent.ts
|
|
7654
7872
|
init_define_HERD_PACKAGE();
|
|
@@ -7757,20 +7975,20 @@ function readClaudeScreen(raw) {
|
|
|
7757
7975
|
|
|
7758
7976
|
// src/worktrees.ts
|
|
7759
7977
|
init_define_HERD_PACKAGE();
|
|
7760
|
-
import { existsSync as
|
|
7761
|
-
import { isAbsolute as
|
|
7762
|
-
var worktreesRoot = () =>
|
|
7978
|
+
import { existsSync as existsSync14, realpathSync as realpathSync6 } from "node:fs";
|
|
7979
|
+
import { isAbsolute as isAbsolute3, join as join19, relative } from "node:path";
|
|
7980
|
+
var worktreesRoot = () => join19(herdHome(), "worktrees");
|
|
7763
7981
|
var WORD_ID = /^[a-z]+-[a-z]+-[a-z]+$/;
|
|
7764
7982
|
var conversationPrefix = (conversationId) => WORD_ID.test(conversationId) ? conversationId : conversationId.replace(/[^A-Za-z0-9]/g, "").slice(0, 12).toLowerCase();
|
|
7765
7983
|
var conversationBranch = (conversationId) => `herd/${conversationPrefix(conversationId)}`;
|
|
7766
|
-
var conversationFolder = (convos, conversationId) =>
|
|
7984
|
+
var conversationFolder = (convos, conversationId) => join19(convos, conversationPrefix(conversationId));
|
|
7767
7985
|
function folderForConversation(root, conversationId) {
|
|
7768
7986
|
const earlier = conversationFolder(legacyConvosDir(root), conversationId);
|
|
7769
|
-
return
|
|
7987
|
+
return existsSync14(earlier) ? earlier : conversationFolder(convosHome(root), conversationId);
|
|
7770
7988
|
}
|
|
7771
7989
|
var within = (dir, path) => {
|
|
7772
7990
|
const between = relative(dir, path);
|
|
7773
|
-
return between !== "" && !between.startsWith("..") && !
|
|
7991
|
+
return between !== "" && !between.startsWith("..") && !isAbsolute3(between);
|
|
7774
7992
|
};
|
|
7775
7993
|
var real2 = (path) => {
|
|
7776
7994
|
try {
|
|
@@ -8069,8 +8287,8 @@ function readAgyScreen(raw) {
|
|
|
8069
8287
|
// src/agy-settings.ts
|
|
8070
8288
|
init_define_HERD_PACKAGE();
|
|
8071
8289
|
import { lstatSync as lstatSync2, mkdirSync as mkdirSync14, realpathSync as realpathSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
8072
|
-
import { join as
|
|
8073
|
-
var AGY_SETTINGS_FILE =
|
|
8290
|
+
import { join as join20, sep } from "node:path";
|
|
8291
|
+
var AGY_SETTINGS_FILE = join20(".herd", "agy-settings.json");
|
|
8074
8292
|
var AGY_HOOK_EVENTS = ["SessionStart", "PreInvocation", "Stop"];
|
|
8075
8293
|
var agyHookCommand = (event2, herd) => `${herd} hook agy ${event2} || true`;
|
|
8076
8294
|
function agyHooks(herd) {
|
|
@@ -8087,7 +8305,7 @@ function agySettings(herd) {
|
|
|
8087
8305
|
return { statusLine: { type: "command", command: `${herd} statusline agy` } };
|
|
8088
8306
|
}
|
|
8089
8307
|
function ownFolder(worktree, name) {
|
|
8090
|
-
const folder =
|
|
8308
|
+
const folder = join20(worktree, name);
|
|
8091
8309
|
if (lstatSync2(folder, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
8092
8310
|
throw new Error(`${name} in the worktree is a link`);
|
|
8093
8311
|
}
|
|
@@ -8099,13 +8317,13 @@ function ownFolder(worktree, name) {
|
|
|
8099
8317
|
}
|
|
8100
8318
|
function writeAgyFiles(worktree, herd) {
|
|
8101
8319
|
const agents = ownFolder(worktree, ".agents");
|
|
8102
|
-
writeFileSync11(
|
|
8320
|
+
writeFileSync11(join20(agents, "hooks.json"), `${JSON.stringify(agyHooks(herd), null, 2)}
|
|
8103
8321
|
`);
|
|
8104
|
-
writeFileSync11(
|
|
8322
|
+
writeFileSync11(join20(agents, ".gitignore"), "*\n");
|
|
8105
8323
|
const herdFolder = ownFolder(worktree, ".herd");
|
|
8106
|
-
writeFileSync11(
|
|
8324
|
+
writeFileSync11(join20(herdFolder, ".gitignore"), "*\n");
|
|
8107
8325
|
writeFileSync11(
|
|
8108
|
-
|
|
8326
|
+
join20(worktree, AGY_SETTINGS_FILE),
|
|
8109
8327
|
`${JSON.stringify(agySettings(herd), null, 2)}
|
|
8110
8328
|
`
|
|
8111
8329
|
);
|
|
@@ -8260,15 +8478,15 @@ var AgyDriver = class {
|
|
|
8260
8478
|
// src/bare-clones.ts
|
|
8261
8479
|
init_define_HERD_PACKAGE();
|
|
8262
8480
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
8263
|
-
import { existsSync as
|
|
8264
|
-
import { dirname as dirname12, join as
|
|
8481
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync15, readdirSync as readdirSync5, renameSync as renameSync5, rmSync as rmSync11, statSync as statSync7 } from "node:fs";
|
|
8482
|
+
import { dirname as dirname12, join as join21 } from "node:path";
|
|
8265
8483
|
function bareClonePath(dir, remote) {
|
|
8266
8484
|
const key = repoKey(remote);
|
|
8267
8485
|
if (!key) throw new StoreError(`${remote} isn't a repo's URL.`);
|
|
8268
|
-
return
|
|
8486
|
+
return join21(dir, `${key}.git`);
|
|
8269
8487
|
}
|
|
8270
8488
|
async function ensureBareClone(store, remote, env) {
|
|
8271
|
-
if (
|
|
8489
|
+
if (existsSync15(store)) {
|
|
8272
8490
|
let fetchFailed = null;
|
|
8273
8491
|
try {
|
|
8274
8492
|
await git(env, store, "fetch", "--quiet", "--prune", "origin");
|
|
@@ -8309,7 +8527,7 @@ function bareClones(dir) {
|
|
|
8309
8527
|
return;
|
|
8310
8528
|
}
|
|
8311
8529
|
for (const name of entries.sort()) {
|
|
8312
|
-
const path =
|
|
8530
|
+
const path = join21(at, name);
|
|
8313
8531
|
if (name.endsWith(".git")) {
|
|
8314
8532
|
try {
|
|
8315
8533
|
const remote = gitSync(path, "config", "--get", "remote.origin.url");
|
|
@@ -8490,7 +8708,7 @@ init_define_HERD_PACKAGE();
|
|
|
8490
8708
|
import { execFile as execFile2, execFileSync as execFileSync5, spawn as spawn9 } from "node:child_process";
|
|
8491
8709
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
8492
8710
|
import {
|
|
8493
|
-
existsSync as
|
|
8711
|
+
existsSync as existsSync16,
|
|
8494
8712
|
mkdirSync as mkdirSync17,
|
|
8495
8713
|
mkdtempSync,
|
|
8496
8714
|
readdirSync as readdirSync7,
|
|
@@ -8499,16 +8717,16 @@ import {
|
|
|
8499
8717
|
rmSync as rmSync13,
|
|
8500
8718
|
writeFileSync as writeFileSync13
|
|
8501
8719
|
} from "node:fs";
|
|
8502
|
-
import { homedir as
|
|
8503
|
-
import { join as
|
|
8720
|
+
import { homedir as homedir7, tmpdir } from "node:os";
|
|
8721
|
+
import { join as join23 } from "node:path";
|
|
8504
8722
|
import { promisify as promisify2 } from "node:util";
|
|
8505
8723
|
|
|
8506
8724
|
// src/check-registry.ts
|
|
8507
8725
|
init_define_HERD_PACKAGE();
|
|
8508
8726
|
import { mkdirSync as mkdirSync16, readdirSync as readdirSync6, readFileSync as readFileSync15, rmSync as rmSync12, writeFileSync as writeFileSync12 } from "node:fs";
|
|
8509
|
-
import { homedir as
|
|
8510
|
-
import { join as
|
|
8511
|
-
var checkRegistryDir = () => process.env.HERDINGCHATS_CHECK_RUNNING ??
|
|
8727
|
+
import { homedir as homedir6 } from "node:os";
|
|
8728
|
+
import { join as join22 } from "node:path";
|
|
8729
|
+
var checkRegistryDir = () => process.env.HERDINGCHATS_CHECK_RUNNING ?? join22(homedir6(), ".cache", "herdingchats", "check", "running");
|
|
8512
8730
|
var RESERVED_MB = 6 * 1024;
|
|
8513
8731
|
var MIN_MEMORY_MB = 1024;
|
|
8514
8732
|
var alive3 = (pid) => {
|
|
@@ -8521,7 +8739,7 @@ var alive3 = (pid) => {
|
|
|
8521
8739
|
};
|
|
8522
8740
|
function registerCheck(dir, pid, startedAt = Date.now()) {
|
|
8523
8741
|
mkdirSync16(dir, { recursive: true });
|
|
8524
|
-
const file =
|
|
8742
|
+
const file = join22(dir, `${pid}.json`);
|
|
8525
8743
|
writeFileSync12(file, JSON.stringify({ pid, startedAt }));
|
|
8526
8744
|
return () => rmSync12(file, { force: true });
|
|
8527
8745
|
}
|
|
@@ -8537,12 +8755,12 @@ function runningChecks(dir, isAlive = alive3) {
|
|
|
8537
8755
|
const pid = Number(name.slice(0, -".json".length));
|
|
8538
8756
|
let entry = null;
|
|
8539
8757
|
try {
|
|
8540
|
-
entry = JSON.parse(readFileSync15(
|
|
8758
|
+
entry = JSON.parse(readFileSync15(join22(dir, name), "utf8"));
|
|
8541
8759
|
} catch {
|
|
8542
8760
|
continue;
|
|
8543
8761
|
}
|
|
8544
8762
|
if (entry?.pid === pid && isAlive(pid)) count += 1;
|
|
8545
|
-
else rmSync12(
|
|
8763
|
+
else rmSync12(join22(dir, name), { force: true });
|
|
8546
8764
|
}
|
|
8547
8765
|
return count;
|
|
8548
8766
|
}
|
|
@@ -8550,9 +8768,9 @@ var memoryShareOf = (totalMb, running) => Math.max(MIN_MEMORY_MB, Math.floor((to
|
|
|
8550
8768
|
|
|
8551
8769
|
// src/builds.ts
|
|
8552
8770
|
init_self();
|
|
8553
|
-
var sandboxStatusPath = () =>
|
|
8771
|
+
var sandboxStatusPath = () => join23(herdHome(), "builds", "sandbox.json");
|
|
8554
8772
|
function writeSandboxStatus(status2, now = Date.now()) {
|
|
8555
|
-
mkdirSync17(
|
|
8773
|
+
mkdirSync17(join23(herdHome(), "builds"), { recursive: true });
|
|
8556
8774
|
writeFileSync13(sandboxStatusPath(), JSON.stringify({ ...status2, at: now }));
|
|
8557
8775
|
}
|
|
8558
8776
|
function readSandboxStatus() {
|
|
@@ -8584,7 +8802,7 @@ var real3 = (path) => {
|
|
|
8584
8802
|
}
|
|
8585
8803
|
};
|
|
8586
8804
|
function worktreeOf(dir, store) {
|
|
8587
|
-
if (!
|
|
8805
|
+
if (!existsSync16(join23(dir, ".git"))) return false;
|
|
8588
8806
|
try {
|
|
8589
8807
|
return real3(git2(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")) === real3(store);
|
|
8590
8808
|
} catch {
|
|
@@ -8596,36 +8814,54 @@ function cloneFolder(from, to) {
|
|
|
8596
8814
|
execFileSync5("cp", [...args, from, to]);
|
|
8597
8815
|
}
|
|
8598
8816
|
function moduleFolders(dir) {
|
|
8599
|
-
const found =
|
|
8817
|
+
const found = existsSync16(join23(dir, "node_modules")) ? ["node_modules"] : [];
|
|
8600
8818
|
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
8601
8819
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
8602
|
-
if (
|
|
8603
|
-
found.push(
|
|
8820
|
+
if (existsSync16(join23(dir, entry.name, "node_modules")))
|
|
8821
|
+
found.push(join23(entry.name, "node_modules"));
|
|
8604
8822
|
}
|
|
8605
8823
|
}
|
|
8606
8824
|
return found;
|
|
8607
8825
|
}
|
|
8826
|
+
function checkoutManager(dir) {
|
|
8827
|
+
let field;
|
|
8828
|
+
try {
|
|
8829
|
+
const pkg = JSON.parse(readFileSync16(join23(dir, "package.json"), "utf8"));
|
|
8830
|
+
if (typeof pkg.packageManager === "string") field = pkg.packageManager;
|
|
8831
|
+
} catch {
|
|
8832
|
+
}
|
|
8833
|
+
const found = managerOfCheckout(field, (name) => existsSync16(join23(dir, name)));
|
|
8834
|
+
if (!found?.lockfile)
|
|
8835
|
+
throw new Error(
|
|
8836
|
+
`${dir} has no lockfile to install node_modules from: herd looked for ${Object.values(LOCKFILES).flat().join(", ")}.`
|
|
8837
|
+
);
|
|
8838
|
+
return { ...found, lockfile: found.lockfile };
|
|
8839
|
+
}
|
|
8608
8840
|
function lockHash(dir) {
|
|
8609
|
-
const lock = readFileSync16(
|
|
8841
|
+
const lock = readFileSync16(join23(dir, checkoutManager(dir).lockfile));
|
|
8610
8842
|
const major = process.versions.node.split(".")[0];
|
|
8611
8843
|
return createHash3("sha256").update(lock).update(`${process.platform}-${process.arch}-${major}`).digest("hex").slice(0, 32);
|
|
8612
8844
|
}
|
|
8613
|
-
var
|
|
8614
|
-
const
|
|
8615
|
-
const
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
{
|
|
8845
|
+
var frozenInstall = (gitEnv) => async (dir) => {
|
|
8846
|
+
const { name, major } = checkoutManager(dir);
|
|
8847
|
+
const env = gitEnv?.() ?? process.env;
|
|
8848
|
+
const manager = await findPackageManager({ env, only: [name], accept: () => true });
|
|
8849
|
+
if (!manager)
|
|
8850
|
+
throw new Error(`${dir} installs with ${name}, which isn't on this machine, or doesn't run.`);
|
|
8851
|
+
const args = frozenInstallArgs(name, { offline: insideBuildSandbox(), yarnMajor: major });
|
|
8852
|
+
await new Promise((resolve7, reject) => {
|
|
8853
|
+
const child = spawn9(manager.command, args, {
|
|
8619
8854
|
cwd: dir,
|
|
8855
|
+
env: { ...env, COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
|
|
8620
8856
|
stdio: "ignore"
|
|
8621
|
-
}
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
);
|
|
8628
|
-
}
|
|
8857
|
+
});
|
|
8858
|
+
child.on("error", reject);
|
|
8859
|
+
child.on(
|
|
8860
|
+
"close",
|
|
8861
|
+
(code) => code === 0 ? resolve7() : reject(new Error(`${name} ${args[0]} exited with ${code}`))
|
|
8862
|
+
);
|
|
8863
|
+
});
|
|
8864
|
+
};
|
|
8629
8865
|
var BuildRunner = class _BuildRunner {
|
|
8630
8866
|
constructor(options) {
|
|
8631
8867
|
this.options = options;
|
|
@@ -8633,11 +8869,11 @@ var BuildRunner = class _BuildRunner {
|
|
|
8633
8869
|
options;
|
|
8634
8870
|
running = /* @__PURE__ */ new Map();
|
|
8635
8871
|
home() {
|
|
8636
|
-
return this.options.home ??
|
|
8872
|
+
return this.options.home ?? join23(herdHome(), "builds");
|
|
8637
8873
|
}
|
|
8638
8874
|
/** Where builds' own bare clones are, apart from the root's checkouts. */
|
|
8639
8875
|
clonesDir() {
|
|
8640
|
-
return
|
|
8876
|
+
return join23(this.home(), "clones");
|
|
8641
8877
|
}
|
|
8642
8878
|
/** Herd's bare clone of a build's repo, for builds, whether or not it's there yet. */
|
|
8643
8879
|
storeFor(build) {
|
|
@@ -8658,8 +8894,8 @@ var BuildRunner = class _BuildRunner {
|
|
|
8658
8894
|
const store = bareClonePath(this.clonesDir(), remote);
|
|
8659
8895
|
const env = this.options.gitEnv?.();
|
|
8660
8896
|
const name = (repoKey(build.repo) ?? build.repo).replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
8661
|
-
const dir =
|
|
8662
|
-
mkdirSync17(
|
|
8897
|
+
const dir = join23(this.home(), name, String(slot));
|
|
8898
|
+
mkdirSync17(join23(this.home(), name), { recursive: true });
|
|
8663
8899
|
await withStoreLock(
|
|
8664
8900
|
store,
|
|
8665
8901
|
async () => {
|
|
@@ -8698,30 +8934,30 @@ var BuildRunner = class _BuildRunner {
|
|
|
8698
8934
|
/** Makes `node_modules` match the checkout's lockfile: kept, copied from the cache, or installed. */
|
|
8699
8935
|
async ensureModules(dir) {
|
|
8700
8936
|
const wanted = lockHash(dir);
|
|
8701
|
-
const marker =
|
|
8702
|
-
if (
|
|
8703
|
-
const cache =
|
|
8937
|
+
const marker = join23(dir, "node_modules", ".herd-lock");
|
|
8938
|
+
if (existsSync16(marker) && readFileSync16(marker, "utf8") === wanted) return;
|
|
8939
|
+
const cache = join23(this.home(), "node_modules", wanted);
|
|
8704
8940
|
for (const folder of moduleFolders(dir))
|
|
8705
|
-
rmSync13(
|
|
8706
|
-
if (
|
|
8707
|
-
for (const folder of readFileSync16(
|
|
8708
|
-
cloneFolder(
|
|
8941
|
+
rmSync13(join23(dir, folder), { recursive: true, force: true });
|
|
8942
|
+
if (existsSync16(join23(cache, ".complete"))) {
|
|
8943
|
+
for (const folder of readFileSync16(join23(cache, ".complete"), "utf8").split("\n").filter(Boolean)) {
|
|
8944
|
+
cloneFolder(join23(cache, folder), join23(dir, folder));
|
|
8709
8945
|
}
|
|
8710
8946
|
} else {
|
|
8711
8947
|
this.options.log?.(`Installing node_modules for lockfile ${wanted.slice(0, 8)}.`);
|
|
8712
|
-
await (this.options.install ??
|
|
8713
|
-
const staging = mkdtempSync(
|
|
8948
|
+
await (this.options.install ?? frozenInstall(this.options.gitEnv))(dir);
|
|
8949
|
+
const staging = mkdtempSync(join23(this.home(), "node_modules-"));
|
|
8714
8950
|
const folders = moduleFolders(dir);
|
|
8715
8951
|
for (const folder of folders) {
|
|
8716
|
-
mkdirSync17(
|
|
8717
|
-
cloneFolder(
|
|
8952
|
+
mkdirSync17(join23(staging, folder, ".."), { recursive: true });
|
|
8953
|
+
cloneFolder(join23(dir, folder), join23(staging, folder));
|
|
8718
8954
|
}
|
|
8719
|
-
writeFileSync13(
|
|
8720
|
-
mkdirSync17(
|
|
8955
|
+
writeFileSync13(join23(staging, ".complete"), folders.join("\n"));
|
|
8956
|
+
mkdirSync17(join23(this.home(), "node_modules"), { recursive: true });
|
|
8721
8957
|
rmSync13(cache, { recursive: true, force: true });
|
|
8722
8958
|
execFileSync5("mv", [staging, cache]);
|
|
8723
8959
|
}
|
|
8724
|
-
mkdirSync17(
|
|
8960
|
+
mkdirSync17(join23(dir, "node_modules"), { recursive: true });
|
|
8725
8961
|
writeFileSync13(marker, wanted);
|
|
8726
8962
|
}
|
|
8727
8963
|
/**
|
|
@@ -8732,7 +8968,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8732
8968
|
const env = {
|
|
8733
8969
|
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
|
8734
8970
|
HOME: home,
|
|
8735
|
-
TMPDIR:
|
|
8971
|
+
TMPDIR: join23(home, "tmp"),
|
|
8736
8972
|
LANG: process.env.LANG ?? "en_US.UTF-8",
|
|
8737
8973
|
HERD_BUILD_ID: build.id,
|
|
8738
8974
|
HERD_BUILD_SECRET: build.secret,
|
|
@@ -8744,7 +8980,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8744
8980
|
// The sandbox's proxy is the only way out, and Node's own requests use it only when asked.
|
|
8745
8981
|
NODE_USE_ENV_PROXY: "1",
|
|
8746
8982
|
// Where `srt` points TMPDIR inside the sandbox: the build's own temporary folder.
|
|
8747
|
-
CLAUDE_CODE_TMPDIR:
|
|
8983
|
+
CLAUDE_CODE_TMPDIR: join23(home, "tmp")
|
|
8748
8984
|
};
|
|
8749
8985
|
return env;
|
|
8750
8986
|
}
|
|
@@ -8755,8 +8991,8 @@ var BuildRunner = class _BuildRunner {
|
|
|
8755
8991
|
/** Runs `scripts/build.mjs` from the build's own commit, in a process group of its own. */
|
|
8756
8992
|
async run(build, slot) {
|
|
8757
8993
|
const dir = await this.prepare(build, slot);
|
|
8758
|
-
const home = mkdtempSync(
|
|
8759
|
-
mkdirSync17(
|
|
8994
|
+
const home = mkdtempSync(join23(this.options.scratch ?? scratchRoot(), "hb-"));
|
|
8995
|
+
mkdirSync17(join23(home, "tmp"), { recursive: true });
|
|
8760
8996
|
const run4 = randomUUID4();
|
|
8761
8997
|
const env = {
|
|
8762
8998
|
..._BuildRunner.environment(build, home),
|
|
@@ -8766,11 +9002,11 @@ var BuildRunner = class _BuildRunner {
|
|
|
8766
9002
|
env.HERD_CHECK_CPUS = String(Math.max(1, Math.floor(this.options.cpus())));
|
|
8767
9003
|
if (this.options.memoryMb)
|
|
8768
9004
|
env.HERD_CHECK_MEMORY_MB = String(Math.max(1, Math.floor(this.options.memoryMb())));
|
|
8769
|
-
let command = [process.execPath,
|
|
9005
|
+
let command = [process.execPath, join23(dir, "scripts", "build.mjs")];
|
|
8770
9006
|
let policyPath = null;
|
|
8771
9007
|
if (this.sandboxed()) {
|
|
8772
9008
|
const policy = buildPolicy({
|
|
8773
|
-
ownerHome: this.options.ownerHome ??
|
|
9009
|
+
ownerHome: this.options.ownerHome ?? homedir7(),
|
|
8774
9010
|
root: this.options.root(),
|
|
8775
9011
|
convos: convosHome(this.options.root()),
|
|
8776
9012
|
worktree: dir,
|
|
@@ -8781,8 +9017,8 @@ var BuildRunner = class _BuildRunner {
|
|
|
8781
9017
|
main: build.main,
|
|
8782
9018
|
buildsUrl: build.url
|
|
8783
9019
|
});
|
|
8784
|
-
mkdirSync17(
|
|
8785
|
-
policyPath =
|
|
9020
|
+
mkdirSync17(join23(this.home(), "policies"), { recursive: true });
|
|
9021
|
+
policyPath = join23(this.home(), "policies", `${build.id}.json`);
|
|
8786
9022
|
writeFileSync13(policyPath, JSON.stringify(policy), { mode: 384 });
|
|
8787
9023
|
env.HERD_BUILD_SANDBOX = describePolicy(policy);
|
|
8788
9024
|
command = [process.execPath, HERD_BIN, "build-sandbox", policyPath, "--", ...command];
|
|
@@ -8831,7 +9067,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8831
9067
|
* It goes only by where they run from, never by a build ID, since the daemon a build's own tests
|
|
8832
9068
|
* start must never stop the build it runs in.
|
|
8833
9069
|
*/
|
|
8834
|
-
static async sweep(home =
|
|
9070
|
+
static async sweep(home = join23(herdHome(), "builds"), list = listProcesses) {
|
|
8835
9071
|
const left = buildProcesses(list(), null, home);
|
|
8836
9072
|
await stopPids(left);
|
|
8837
9073
|
return left.length;
|
|
@@ -8843,37 +9079,37 @@ var BuildRunner = class _BuildRunner {
|
|
|
8843
9079
|
*/
|
|
8844
9080
|
static async probe(options = { buildsUrl: "" }) {
|
|
8845
9081
|
if (insideBuildSandbox()) return { ok: true };
|
|
8846
|
-
const herd = options.home ??
|
|
8847
|
-
const dir = mkdtempSync(
|
|
8848
|
-
const secret =
|
|
9082
|
+
const herd = options.home ?? join23(herdHome(), "builds");
|
|
9083
|
+
const dir = mkdtempSync(join23(scratchRoot(), "hp-"));
|
|
9084
|
+
const secret = join23(dir, "owner", "secret");
|
|
8849
9085
|
try {
|
|
8850
9086
|
mkdirSync17(herd, { recursive: true });
|
|
8851
|
-
for (const sub of ["work", "git", "home", "owner"]) mkdirSync17(
|
|
9087
|
+
for (const sub of ["work", "git", "home", "owner"]) mkdirSync17(join23(dir, sub));
|
|
8852
9088
|
writeFileSync13(secret, "probe");
|
|
8853
9089
|
const policy = buildPolicy({
|
|
8854
|
-
ownerHome:
|
|
8855
|
-
root:
|
|
8856
|
-
convos:
|
|
8857
|
-
worktree:
|
|
8858
|
-
store:
|
|
8859
|
-
worktreeGitDir:
|
|
8860
|
-
home:
|
|
9090
|
+
ownerHome: join23(dir, "owner"),
|
|
9091
|
+
root: join23(dir, "owner"),
|
|
9092
|
+
convos: join23(dir, "owner"),
|
|
9093
|
+
worktree: join23(dir, "work"),
|
|
9094
|
+
store: join23(dir, "git"),
|
|
9095
|
+
worktreeGitDir: join23(dir, "git"),
|
|
9096
|
+
home: join23(dir, "home"),
|
|
8861
9097
|
nodePrefix: nodePrefix(),
|
|
8862
9098
|
main: false,
|
|
8863
9099
|
buildsUrl: options.buildsUrl || "https://builds.herdingchats.com"
|
|
8864
9100
|
});
|
|
8865
|
-
const policyPath =
|
|
9101
|
+
const policyPath = join23(herd, "probe-policy.json");
|
|
8866
9102
|
writeFileSync13(policyPath, JSON.stringify(policy), { mode: 384 });
|
|
8867
9103
|
const script = [
|
|
8868
9104
|
'const fs = require("node:fs");',
|
|
8869
9105
|
`try { fs.readFileSync(${JSON.stringify(secret)}); process.exit(10); } catch {}`,
|
|
8870
|
-
`fs.writeFileSync(${JSON.stringify(
|
|
9106
|
+
`fs.writeFileSync(${JSON.stringify(join23(dir, "home", "ok"))}, "ok");`
|
|
8871
9107
|
].join(" ");
|
|
8872
9108
|
const child = spawn9(
|
|
8873
9109
|
process.execPath,
|
|
8874
9110
|
[HERD_BIN, "build-sandbox", policyPath, "--", process.execPath, "-e", script],
|
|
8875
9111
|
{
|
|
8876
|
-
env: { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME:
|
|
9112
|
+
env: { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME: join23(dir, "home") },
|
|
8877
9113
|
stdio: ["ignore", "ignore", "pipe"]
|
|
8878
9114
|
}
|
|
8879
9115
|
);
|
|
@@ -8887,7 +9123,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8887
9123
|
clearTimeout(timer);
|
|
8888
9124
|
if (status2 === 10)
|
|
8889
9125
|
return { ok: false, reason: "the sandbox let a build read the owner's files" };
|
|
8890
|
-
if (status2 !== 0 || !
|
|
9126
|
+
if (status2 !== 0 || !existsSync16(join23(dir, "home", "ok"))) {
|
|
8891
9127
|
const why = stderr.trim().split("\n").at(-1) || `the probe exited with ${status2}`;
|
|
8892
9128
|
return { ok: false, reason: why.slice(0, 300) };
|
|
8893
9129
|
}
|
|
@@ -9242,7 +9478,7 @@ var CodexDriver = class {
|
|
|
9242
9478
|
|
|
9243
9479
|
// src/daemon.ts
|
|
9244
9480
|
init_define_HERD_PACKAGE();
|
|
9245
|
-
import { join as
|
|
9481
|
+
import { join as join24 } from "node:path";
|
|
9246
9482
|
|
|
9247
9483
|
// src/dispatch-connection.ts
|
|
9248
9484
|
init_define_HERD_PACKAGE();
|
|
@@ -9346,7 +9582,7 @@ var DispatchConnection = class {
|
|
|
9346
9582
|
|
|
9347
9583
|
// src/daemon.ts
|
|
9348
9584
|
var CONFIG_POLL_MS = 2e3;
|
|
9349
|
-
var daemonSocketPath = () =>
|
|
9585
|
+
var daemonSocketPath = () => join24(herdHome(), "daemon.sock");
|
|
9350
9586
|
var Daemon = class {
|
|
9351
9587
|
constructor(options) {
|
|
9352
9588
|
this.options = options;
|
|
@@ -9558,7 +9794,7 @@ var Daemon = class {
|
|
|
9558
9794
|
|
|
9559
9795
|
// src/folders.ts
|
|
9560
9796
|
init_define_HERD_PACKAGE();
|
|
9561
|
-
import { existsSync as
|
|
9797
|
+
import { existsSync as existsSync17, lstatSync as lstatSync3, readlinkSync as readlinkSync2, rmSync as rmSync14, writeFileSync as writeFileSync14 } from "node:fs";
|
|
9562
9798
|
import { dirname as dirname13, resolve as resolve4 } from "node:path";
|
|
9563
9799
|
var FOLDER_WAIT_MS = 2 * 60 * 1e3;
|
|
9564
9800
|
var FOLDER_CHECK_MS = 30 * 1e3;
|
|
@@ -9570,7 +9806,7 @@ function linkOf(path) {
|
|
|
9570
9806
|
return null;
|
|
9571
9807
|
}
|
|
9572
9808
|
}
|
|
9573
|
-
function missingFolder(path, required, { exists =
|
|
9809
|
+
function missingFolder(path, required, { exists = existsSync17, linkTarget = linkOf } = {}) {
|
|
9574
9810
|
if (exists(path)) return null;
|
|
9575
9811
|
const target = linkTarget(path);
|
|
9576
9812
|
if (target) return { path, target };
|
|
@@ -9773,23 +10009,23 @@ init_define_HERD_PACKAGE();
|
|
|
9773
10009
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
9774
10010
|
import {
|
|
9775
10011
|
appendFileSync as appendFileSync2,
|
|
9776
|
-
existsSync as
|
|
10012
|
+
existsSync as existsSync18,
|
|
9777
10013
|
mkdirSync as mkdirSync18,
|
|
9778
10014
|
readFileSync as readFileSync17,
|
|
9779
10015
|
renameSync as renameSync6,
|
|
9780
10016
|
writeFileSync as writeFileSync15
|
|
9781
10017
|
} from "node:fs";
|
|
9782
|
-
import { isAbsolute as
|
|
9783
|
-
var LOCAL_SETTINGS =
|
|
10018
|
+
import { isAbsolute as isAbsolute4, join as join25 } from "node:path";
|
|
10019
|
+
var LOCAL_SETTINGS = join25(".claude", "settings.local.json");
|
|
9784
10020
|
var git3 = (cwd, ...args) => execFileSync6("git", ["-C", cwd, ...args], {
|
|
9785
10021
|
encoding: "utf8",
|
|
9786
10022
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9787
10023
|
}).trim();
|
|
9788
10024
|
var shellQuote2 = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9789
10025
|
function writeWorktreeSettings(worktree, settings, ownCommand) {
|
|
9790
|
-
const path =
|
|
10026
|
+
const path = join25(worktree, LOCAL_SETTINGS);
|
|
9791
10027
|
let current = {};
|
|
9792
|
-
if (
|
|
10028
|
+
if (existsSync18(path)) {
|
|
9793
10029
|
try {
|
|
9794
10030
|
const parsed = JSON.parse(readFileSync17(path, "utf8"));
|
|
9795
10031
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
@@ -9811,7 +10047,7 @@ function writeWorktreeSettings(worktree, settings, ownCommand) {
|
|
|
9811
10047
|
}
|
|
9812
10048
|
next.hooks = hooks;
|
|
9813
10049
|
}
|
|
9814
|
-
mkdirSync18(
|
|
10050
|
+
mkdirSync18(join25(worktree, ".claude"), { recursive: true });
|
|
9815
10051
|
const temporary = `${path}.herd-${process.pid}`;
|
|
9816
10052
|
writeFileSync15(temporary, `${JSON.stringify(next, null, 2)}
|
|
9817
10053
|
`);
|
|
@@ -9831,10 +10067,10 @@ function ignoreLocalSettings(worktree) {
|
|
|
9831
10067
|
} catch {
|
|
9832
10068
|
return;
|
|
9833
10069
|
}
|
|
9834
|
-
const info =
|
|
9835
|
-
const exclude =
|
|
10070
|
+
const info = join25(isAbsolute4(common) ? common : join25(worktree, common), "info");
|
|
10071
|
+
const exclude = join25(info, "exclude");
|
|
9836
10072
|
const line2 = "/.claude/settings.local.json";
|
|
9837
|
-
const existing =
|
|
10073
|
+
const existing = existsSync18(exclude) ? readFileSync17(exclude, "utf8") : "";
|
|
9838
10074
|
if (existing.split("\n").includes(line2)) return;
|
|
9839
10075
|
mkdirSync18(info, { recursive: true });
|
|
9840
10076
|
appendFileSync2(exclude, `${existing && !existing.endsWith("\n") ? "\n" : ""}${line2}
|
|
@@ -9879,7 +10115,7 @@ function loginShellEnv() {
|
|
|
9879
10115
|
|
|
9880
10116
|
// src/machine-checks.ts
|
|
9881
10117
|
init_define_HERD_PACKAGE();
|
|
9882
|
-
import { existsSync as
|
|
10118
|
+
import { existsSync as existsSync19, rmSync as rmSync15 } from "node:fs";
|
|
9883
10119
|
var REQUEST_POLL_MS = 2e3;
|
|
9884
10120
|
var MachineChecks = class {
|
|
9885
10121
|
constructor(options) {
|
|
@@ -9925,7 +10161,7 @@ var MachineChecks = class {
|
|
|
9925
10161
|
/** `friday start` asked for every agent to be activated again. */
|
|
9926
10162
|
takeRequest() {
|
|
9927
10163
|
const path = activationRequestPath(this.options.herdHome);
|
|
9928
|
-
if (!
|
|
10164
|
+
if (!existsSync19(path)) return;
|
|
9929
10165
|
rmSync15(path, { force: true });
|
|
9930
10166
|
this.activateChosen(true);
|
|
9931
10167
|
}
|
|
@@ -9977,15 +10213,15 @@ init_define_HERD_PACKAGE();
|
|
|
9977
10213
|
import { spawn as spawn11 } from "node:child_process";
|
|
9978
10214
|
import { mkdirSync as mkdirSync19, readdirSync as readdirSync8, rmSync as rmSync16 } from "node:fs";
|
|
9979
10215
|
import { connect as connect2 } from "node:net";
|
|
9980
|
-
import { join as
|
|
10216
|
+
import { join as join26 } from "node:path";
|
|
9981
10217
|
init_self();
|
|
9982
10218
|
import {
|
|
9983
10219
|
HOST_PROTOCOL,
|
|
9984
10220
|
hostVersion
|
|
9985
10221
|
} from "./terminal-host.mjs";
|
|
9986
|
-
var hostsDir = () =>
|
|
9987
|
-
var legacyHostSocketPath = () =>
|
|
9988
|
-
var currentHostSocketPath = (version = hostVersion()) =>
|
|
10222
|
+
var hostsDir = () => join26(herdHome(), "terminals");
|
|
10223
|
+
var legacyHostSocketPath = () => join26(herdHome(), "terminals.sock");
|
|
10224
|
+
var currentHostSocketPath = (version = hostVersion()) => join26(hostsDir(), `${version}.sock`);
|
|
9989
10225
|
var speaksProtocol = (protocol) => protocol === HOST_PROTOCOL || protocol === HOST_PROTOCOL - 1;
|
|
9990
10226
|
var sleep6 = (ms2) => new Promise((resolve7) => setTimeout(resolve7, ms2));
|
|
9991
10227
|
function askHost(path, request) {
|
|
@@ -10036,7 +10272,7 @@ var HostTerminals = class {
|
|
|
10036
10272
|
names = readdirSync8(dir).filter((name) => name.endsWith(".sock"));
|
|
10037
10273
|
} catch {
|
|
10038
10274
|
}
|
|
10039
|
-
const paths = [legacyHostSocketPath(), ...names.map((name) =>
|
|
10275
|
+
const paths = [legacyHostSocketPath(), ...names.map((name) => join26(dir, name))];
|
|
10040
10276
|
const found = [];
|
|
10041
10277
|
for (const path of paths) {
|
|
10042
10278
|
try {
|
|
@@ -10275,7 +10511,7 @@ var UsageReporter = class {
|
|
|
10275
10511
|
init_define_HERD_PACKAGE();
|
|
10276
10512
|
init_src();
|
|
10277
10513
|
import { mkdirSync as mkdirSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync18 } from "node:fs";
|
|
10278
|
-
import { join as
|
|
10514
|
+
import { join as join33 } from "node:path";
|
|
10279
10515
|
|
|
10280
10516
|
// src/workspaces/codex.ts
|
|
10281
10517
|
init_define_HERD_PACKAGE();
|
|
@@ -10287,7 +10523,7 @@ init_define_HERD_PACKAGE();
|
|
|
10287
10523
|
init_define_HERD_PACKAGE();
|
|
10288
10524
|
init_src();
|
|
10289
10525
|
init_self();
|
|
10290
|
-
import { join as
|
|
10526
|
+
import { join as join27 } from "node:path";
|
|
10291
10527
|
import { z as z26 } from "zod";
|
|
10292
10528
|
function hasThread(events) {
|
|
10293
10529
|
return events.some(
|
|
@@ -10322,7 +10558,7 @@ function filesByMessage(events) {
|
|
|
10322
10558
|
}
|
|
10323
10559
|
return files;
|
|
10324
10560
|
}
|
|
10325
|
-
var FILES_FOLDER =
|
|
10561
|
+
var FILES_FOLDER = join27(".herd", "files");
|
|
10326
10562
|
function safeFileName(name) {
|
|
10327
10563
|
const chars = Array.from(name.replace(/[/\\\p{Cc}]/gu, "_").trim());
|
|
10328
10564
|
while (Buffer.byteLength(chars.join("")) > 200) chars.shift();
|
|
@@ -10555,11 +10791,11 @@ async function claimChange(ws, workspace, change) {
|
|
|
10555
10791
|
// src/workspaces/delivery.ts
|
|
10556
10792
|
init_define_HERD_PACKAGE();
|
|
10557
10793
|
import { randomBytes as randomBytes6, randomUUID as randomUUID6 } from "node:crypto";
|
|
10558
|
-
import { join as
|
|
10794
|
+
import { join as join31 } from "node:path";
|
|
10559
10795
|
|
|
10560
10796
|
// src/workspaces/repos.ts
|
|
10561
10797
|
init_define_HERD_PACKAGE();
|
|
10562
|
-
import { join as
|
|
10798
|
+
import { join as join28 } from "node:path";
|
|
10563
10799
|
var rootOf = (ws) => ws.options.root?.() ?? ws.options.config().root ?? defaultRoot();
|
|
10564
10800
|
var gitEnvOf = (ws) => ws.options.gitEnv?.() ?? loginShellEnv();
|
|
10565
10801
|
function knownRemotes(ws) {
|
|
@@ -10671,7 +10907,7 @@ async function addHints(ws, workspace, hints, handedOff) {
|
|
|
10671
10907
|
const added = await addWorktree({
|
|
10672
10908
|
root,
|
|
10673
10909
|
remote: cloneSource(hint.remote, knownRemotes(ws)),
|
|
10674
|
-
path:
|
|
10910
|
+
path: join28(workspace.folder, place.name),
|
|
10675
10911
|
branch,
|
|
10676
10912
|
...handedOff ? { continueFrom: hint.branch ?? branch } : {},
|
|
10677
10913
|
env: gitEnvOf(ws),
|
|
@@ -10723,14 +10959,14 @@ function handoffRepos(workspace) {
|
|
|
10723
10959
|
return workspace.worktrees.map((repo) => ({
|
|
10724
10960
|
folder: repo.folder,
|
|
10725
10961
|
branch: repo.branch,
|
|
10726
|
-
unpushed: hasUnpushed(
|
|
10962
|
+
unpushed: hasUnpushed(join28(workspace.folder, repo.folder))
|
|
10727
10963
|
}));
|
|
10728
10964
|
}
|
|
10729
10965
|
|
|
10730
10966
|
// src/workspaces/setup.ts
|
|
10731
10967
|
init_define_HERD_PACKAGE();
|
|
10732
10968
|
import { chmodSync as chmodSync4, mkdirSync as mkdirSync20, writeFileSync as writeFileSync16 } from "node:fs";
|
|
10733
|
-
import { join as
|
|
10969
|
+
import { join as join29 } from "node:path";
|
|
10734
10970
|
|
|
10735
10971
|
// src/hooks.ts
|
|
10736
10972
|
init_define_HERD_PACKAGE();
|
|
@@ -10763,7 +10999,7 @@ function configureClaude(ws, workspace) {
|
|
|
10763
10999
|
configureClaudeIn(workspace.folder, (line2) => ws.log(`${workspace.conversationId}: ${line2}`));
|
|
10764
11000
|
}
|
|
10765
11001
|
function configureClaudeIn(folder, log) {
|
|
10766
|
-
const herd = shellQuote2(
|
|
11002
|
+
const herd = shellQuote2(join29(herdPathDir(), "herd"));
|
|
10767
11003
|
try {
|
|
10768
11004
|
const written = writeWorktreeSettings(
|
|
10769
11005
|
folder,
|
|
@@ -10788,9 +11024,9 @@ function configureClaudeIn(folder, log) {
|
|
|
10788
11024
|
}
|
|
10789
11025
|
}
|
|
10790
11026
|
function herdPathDir() {
|
|
10791
|
-
const dir =
|
|
11027
|
+
const dir = join29(herdHome(), "bin");
|
|
10792
11028
|
mkdirSync20(dir, { recursive: true, mode: 448 });
|
|
10793
|
-
const shim =
|
|
11029
|
+
const shim = join29(dir, "herd");
|
|
10794
11030
|
writeFileSync16(shim, `#!/bin/sh
|
|
10795
11031
|
exec "${process.execPath}" "${herdBin}" "$@"
|
|
10796
11032
|
`);
|
|
@@ -10976,7 +11212,7 @@ async function followCheckIn(ws, workspace, reading) {
|
|
|
10976
11212
|
init_define_HERD_PACKAGE();
|
|
10977
11213
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
10978
11214
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync21 } from "node:fs";
|
|
10979
|
-
import { join as
|
|
11215
|
+
import { join as join30 } from "node:path";
|
|
10980
11216
|
function needsInvestigator(ws, workspace, reading) {
|
|
10981
11217
|
const allowed = ws.options.canInvestigate ? ws.options.canInvestigate() : workspace.agent === "claude-code";
|
|
10982
11218
|
if (!allowed || !reading || workspace.investigation) return false;
|
|
@@ -11141,9 +11377,9 @@ async function followInvestigation(ws, workspace) {
|
|
|
11141
11377
|
}
|
|
11142
11378
|
function saveScreen(ws, conversationId, screen, key) {
|
|
11143
11379
|
try {
|
|
11144
|
-
const dir =
|
|
11380
|
+
const dir = join30(herdHome(), "dialogs");
|
|
11145
11381
|
mkdirSync21(dir, { recursive: true });
|
|
11146
|
-
const file =
|
|
11382
|
+
const file = join30(dir, `${conversationId}-${Date.now()}.txt`);
|
|
11147
11383
|
appendFileSync3(file, `${screen.join("\n")}
|
|
11148
11384
|
|
|
11149
11385
|
Way out: ${key}
|
|
@@ -11348,7 +11584,7 @@ async function deliver(ws, conversationId, delivery) {
|
|
|
11348
11584
|
await ensureLeadTerminal(ws, workspace);
|
|
11349
11585
|
const codex = workspace.agent === "codex";
|
|
11350
11586
|
if (workspace.agent === "agy") {
|
|
11351
|
-
writeAgyFiles(workspace.folder, shellQuote2(
|
|
11587
|
+
writeAgyFiles(workspace.folder, shellQuote2(join31(herdPathDir(), "herd")));
|
|
11352
11588
|
} else if (!codex) configureClaude(ws, workspace);
|
|
11353
11589
|
const ownId = codex || workspace.agent === "agy";
|
|
11354
11590
|
if (!workspace.started) {
|
|
@@ -11496,7 +11732,7 @@ import {
|
|
|
11496
11732
|
statSync as statSync9,
|
|
11497
11733
|
writeFileSync as writeFileSync17
|
|
11498
11734
|
} from "node:fs";
|
|
11499
|
-
import { dirname as dirname14, join as
|
|
11735
|
+
import { dirname as dirname14, join as join32, sep as sep2 } from "node:path";
|
|
11500
11736
|
import { Readable } from "node:stream";
|
|
11501
11737
|
import { pipeline } from "node:stream/promises";
|
|
11502
11738
|
|
|
@@ -11510,7 +11746,7 @@ import { readFile } from "node:fs/promises";
|
|
|
11510
11746
|
init_define_HERD_PACKAGE();
|
|
11511
11747
|
init_src();
|
|
11512
11748
|
import { realpathSync as realpathSync9, statSync as statSync8 } from "node:fs";
|
|
11513
|
-
import { basename as
|
|
11749
|
+
import { basename as basename4, extname, isAbsolute as isAbsolute5, relative as relative2, resolve as resolve5 } from "node:path";
|
|
11514
11750
|
var TYPES = {
|
|
11515
11751
|
".png": "image/png",
|
|
11516
11752
|
".jpg": "image/jpeg",
|
|
@@ -11535,7 +11771,7 @@ var TYPES = {
|
|
|
11535
11771
|
var mediaTypeFor = (path) => TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
|
|
11536
11772
|
function isInside(root, path) {
|
|
11537
11773
|
const between = relative2(root, path);
|
|
11538
|
-
return between === "" || !between.startsWith("..") && !
|
|
11774
|
+
return between === "" || !between.startsWith("..") && !isAbsolute5(between);
|
|
11539
11775
|
}
|
|
11540
11776
|
function resolveAttachments(worktree, files) {
|
|
11541
11777
|
let root;
|
|
@@ -11565,7 +11801,7 @@ function resolveAttachments(worktree, files) {
|
|
|
11565
11801
|
}
|
|
11566
11802
|
resolved.push({
|
|
11567
11803
|
path: real4,
|
|
11568
|
-
name:
|
|
11804
|
+
name: basename4(file.path),
|
|
11569
11805
|
type: mediaTypeFor(real4),
|
|
11570
11806
|
size: stats.size,
|
|
11571
11807
|
...file.title ? { title: file.title } : {},
|
|
@@ -12498,10 +12734,10 @@ async function bringIn(ws, workspace, files) {
|
|
|
12498
12734
|
return listed;
|
|
12499
12735
|
}
|
|
12500
12736
|
async function download(ws, workspace, file) {
|
|
12501
|
-
const path =
|
|
12502
|
-
const target =
|
|
12737
|
+
const path = join32(FILES_FOLDER, safeFileName(file.id), safeFileName(file.name));
|
|
12738
|
+
const target = join32(workspace.folder, path);
|
|
12503
12739
|
if (statSync9(target, { throwIfNoEntry: false })?.size === file.size) return path;
|
|
12504
|
-
const herd =
|
|
12740
|
+
const herd = join32(workspace.folder, ".herd");
|
|
12505
12741
|
if (lstatSync4(herd, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
12506
12742
|
throw new Error("its folder leads outside the worktree");
|
|
12507
12743
|
}
|
|
@@ -12509,7 +12745,7 @@ async function download(ws, workspace, file) {
|
|
|
12509
12745
|
if (!realpathSync10(dirname14(target)).startsWith(realpathSync10(workspace.folder) + sep2)) {
|
|
12510
12746
|
throw new Error("its folder leads outside the worktree");
|
|
12511
12747
|
}
|
|
12512
|
-
writeFileSync17(
|
|
12748
|
+
writeFileSync17(join32(herd, ".gitignore"), "*\n");
|
|
12513
12749
|
const response = await ws.api(
|
|
12514
12750
|
`${ws.conversationPath(workspace.conversationId)}/files/${encodeURIComponent(file.id)}`
|
|
12515
12751
|
);
|
|
@@ -12799,7 +13035,7 @@ var Workspaces = class {
|
|
|
12799
13035
|
return this.driver;
|
|
12800
13036
|
}
|
|
12801
13037
|
// ---- State, kept on disk so a restarted daemon picks up where it was.
|
|
12802
|
-
statePath = () =>
|
|
13038
|
+
statePath = () => join33(herdHome(), "state.json");
|
|
12803
13039
|
load() {
|
|
12804
13040
|
try {
|
|
12805
13041
|
return stateSchema.parse(JSON.parse(readFileSync18(this.statePath(), "utf8"))).workspaces;
|
|
@@ -13028,7 +13264,7 @@ async function runDaemon(args) {
|
|
|
13028
13264
|
const folders = new FolderWatch({
|
|
13029
13265
|
check: () => machineFolderProblem(machineRoot(), convosPath(machineRoot())),
|
|
13030
13266
|
initial: missing,
|
|
13031
|
-
statusPath:
|
|
13267
|
+
statusPath: join34(herdHome(), "folder-unavailable.json"),
|
|
13032
13268
|
...process.env.HERD_FOLDER_CHECK_MS ? { everyMs: Number(process.env.HERD_FOLDER_CHECK_MS) } : {},
|
|
13033
13269
|
onChange: () => daemon?.foldersChanged()
|
|
13034
13270
|
});
|
|
@@ -13128,7 +13364,7 @@ async function runDaemon(args) {
|
|
|
13128
13364
|
env: (secret) => envFor(workspaces, secret),
|
|
13129
13365
|
// As for a conversation: Claude Code's hooks and status line, or agy's. Codex has none.
|
|
13130
13366
|
configure: (agent, folder) => {
|
|
13131
|
-
if (agent === "agy") writeAgyFiles(folder, shellQuote2(
|
|
13367
|
+
if (agent === "agy") writeAgyFiles(folder, shellQuote2(join34(herdPathDir(), "herd")));
|
|
13132
13368
|
else if (agent === "claude-code") configureClaudeIn(folder, log);
|
|
13133
13369
|
},
|
|
13134
13370
|
...shellCommand ? { shellCommand } : {},
|
|
@@ -13216,7 +13452,7 @@ async function runDaemon(args) {
|
|
|
13216
13452
|
capacity: () => sandbox.ok && !folders.problem ? runnerCapacity(daemon?.configuration().config?.builds) : 0,
|
|
13217
13453
|
// The repos builds have cloned here, as many as the service's hello takes, to put this runner
|
|
13218
13454
|
// first for their builds.
|
|
13219
|
-
repos: () => bareClones(
|
|
13455
|
+
repos: () => bareClones(join34(herdHome(), "builds", "clones")).map((repo) => repo.key).filter((key) => key.length <= 300).slice(0, 100),
|
|
13220
13456
|
runner: new BuildRunner({
|
|
13221
13457
|
root: () => machineRoot(),
|
|
13222
13458
|
gitEnv,
|
|
@@ -13274,10 +13510,10 @@ init_define_HERD_PACKAGE();
|
|
|
13274
13510
|
// src/agy-hooks.ts
|
|
13275
13511
|
init_define_HERD_PACKAGE();
|
|
13276
13512
|
import { readFileSync as readFileSync20 } from "node:fs";
|
|
13277
|
-
import { basename as
|
|
13513
|
+
import { basename as basename5 } from "node:path";
|
|
13278
13514
|
var text3 = (value) => typeof value === "string" ? value : "";
|
|
13279
13515
|
function lastResponse(transcriptPath) {
|
|
13280
|
-
if (!/^transcript.*\.jsonl$/.test(
|
|
13516
|
+
if (!/^transcript.*\.jsonl$/.test(basename5(transcriptPath))) return "";
|
|
13281
13517
|
let lines;
|
|
13282
13518
|
try {
|
|
13283
13519
|
lines = readFileSync20(transcriptPath, "utf8").split("\n");
|
|
@@ -13403,8 +13639,8 @@ init_define_HERD_PACKAGE();
|
|
|
13403
13639
|
import { spawn as spawn12 } from "node:child_process";
|
|
13404
13640
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
13405
13641
|
import { connect as connect3 } from "node:net";
|
|
13406
|
-
import { homedir as
|
|
13407
|
-
import { join as
|
|
13642
|
+
import { homedir as homedir8 } from "node:os";
|
|
13643
|
+
import { join as join35 } from "node:path";
|
|
13408
13644
|
var MAX_INPUT = 64 * 1024;
|
|
13409
13645
|
var REPORT_TIMEOUT_MS = 300;
|
|
13410
13646
|
var OWN_COMMAND_TIMEOUT_MS = 5e3;
|
|
@@ -13438,10 +13674,10 @@ function agyStateFrom(input) {
|
|
|
13438
13674
|
const state = input?.agent_state;
|
|
13439
13675
|
return state === "working" || state === "idle" ? state : null;
|
|
13440
13676
|
}
|
|
13441
|
-
function ownAgyStatusLine(home =
|
|
13677
|
+
function ownAgyStatusLine(home = homedir8()) {
|
|
13442
13678
|
try {
|
|
13443
13679
|
const settings = JSON.parse(
|
|
13444
|
-
readFileSync21(
|
|
13680
|
+
readFileSync21(join35(home, ".gemini", "antigravity-cli", "settings.json"), "utf8")
|
|
13445
13681
|
);
|
|
13446
13682
|
const command = settings.statusLine?.command;
|
|
13447
13683
|
if (typeof command !== "string" || !command.trim()) return null;
|
|
@@ -13460,8 +13696,8 @@ function commandIn(path) {
|
|
|
13460
13696
|
return null;
|
|
13461
13697
|
}
|
|
13462
13698
|
}
|
|
13463
|
-
function ownStatusLine(projectDir, home =
|
|
13464
|
-
return commandIn(
|
|
13699
|
+
function ownStatusLine(projectDir, home = homedir8()) {
|
|
13700
|
+
return commandIn(join35(projectDir, ".claude", "settings.json")) ?? commandIn(join35(home, ".claude", "settings.json"));
|
|
13465
13701
|
}
|
|
13466
13702
|
function runOwnCommand(command, input, cwd, timeoutMs = OWN_COMMAND_TIMEOUT_MS) {
|
|
13467
13703
|
return new Promise((resolve7) => {
|
|
@@ -13883,7 +14119,7 @@ async function runTerminalHost(rest) {
|
|
|
13883
14119
|
|
|
13884
14120
|
// src/commands/repo.ts
|
|
13885
14121
|
init_define_HERD_PACKAGE();
|
|
13886
|
-
import { join as
|
|
14122
|
+
import { join as join36 } from "node:path";
|
|
13887
14123
|
import { z as z33 } from "zod";
|
|
13888
14124
|
var REPO_HELP = `Usage: herd repo <url or owner/name> [folder]
|
|
13889
14125
|
|
|
@@ -13971,7 +14207,7 @@ async function runRepo(rest) {
|
|
|
13971
14207
|
folderName ? `${folderName} is already taken in this conversation's folder. Give another name.` : `Both ${repoName(key)} and ${repoOwner(key)}-${repoName(key)} are taken in this conversation's folder. Give a folder name: herd repo ${asked} <folder>.`
|
|
13972
14208
|
);
|
|
13973
14209
|
}
|
|
13974
|
-
const path =
|
|
14210
|
+
const path = join36(here.folder, place.name);
|
|
13975
14211
|
const recorded = here.recorded.some((repo) => repo.folder === place.name);
|
|
13976
14212
|
if (place.existing && recorded) {
|
|
13977
14213
|
const branch = here.recorded.find((repo) => repo.folder === place.name)?.branch ?? here.branch;
|