@herdingchats/friday 0.1.613 → 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 +527 -284
- 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
|
|
|
@@ -1496,8 +1496,6 @@ var init_conversation_state = __esm({
|
|
|
1496
1496
|
waiting: "eyes",
|
|
1497
1497
|
working: "hourglass_flowing_sand",
|
|
1498
1498
|
handled: "white_check_mark",
|
|
1499
|
-
// Seen, and not meant for the bot, so it left the message alone.
|
|
1500
|
-
passed: "ok_hand",
|
|
1501
1499
|
stuck: "warning"
|
|
1502
1500
|
};
|
|
1503
1501
|
conversationStateSchema = z13.enum([
|
|
@@ -3206,9 +3204,9 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
3206
3204
|
init_define_HERD_PACKAGE();
|
|
3207
3205
|
init_src();
|
|
3208
3206
|
import { spawn as spawn7 } from "node:child_process";
|
|
3209
|
-
import { existsSync as
|
|
3210
|
-
import { homedir as
|
|
3211
|
-
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";
|
|
3212
3210
|
import { createInterface } from "node:readline/promises";
|
|
3213
3211
|
import { z as z22 } from "zod";
|
|
3214
3212
|
|
|
@@ -5662,6 +5660,9 @@ StandardError=append:${context2.paths.supervisorLog}
|
|
|
5662
5660
|
WantedBy=default.target
|
|
5663
5661
|
`;
|
|
5664
5662
|
}
|
|
5663
|
+
function makeLogFolder(context2) {
|
|
5664
|
+
mkdirSync8(dirname8(context2.paths.supervisorLog), { recursive: true });
|
|
5665
|
+
}
|
|
5665
5666
|
function writeIfChanged(path, text4) {
|
|
5666
5667
|
if (existsSync7(path) && readFileSync7(path, "utf8") === text4) return false;
|
|
5667
5668
|
mkdirSync8(dirname8(path), { recursive: true });
|
|
@@ -5675,7 +5676,10 @@ function launchd(context2) {
|
|
|
5675
5676
|
const loaded = async () => (await context2.run("launchctl", ["print", target])).status === 0;
|
|
5676
5677
|
return {
|
|
5677
5678
|
kind: "launchd",
|
|
5678
|
-
install: async () =>
|
|
5679
|
+
install: async () => {
|
|
5680
|
+
makeLogFolder(context2);
|
|
5681
|
+
return writeIfChanged(plist, launchAgentPlist(context2));
|
|
5682
|
+
},
|
|
5679
5683
|
async start(changed) {
|
|
5680
5684
|
if (await loaded()) {
|
|
5681
5685
|
if (!changed) return;
|
|
@@ -5703,7 +5707,10 @@ function systemd(context2) {
|
|
|
5703
5707
|
};
|
|
5704
5708
|
return {
|
|
5705
5709
|
kind: "systemd",
|
|
5706
|
-
install: async () =>
|
|
5710
|
+
install: async () => {
|
|
5711
|
+
makeLogFolder(context2);
|
|
5712
|
+
return writeIfChanged(unit, systemdUnit(context2));
|
|
5713
|
+
},
|
|
5707
5714
|
async start(changed) {
|
|
5708
5715
|
if (changed) await systemctl("daemon-reload");
|
|
5709
5716
|
await systemctl("enable", SYSTEMD_UNIT);
|
|
@@ -5775,87 +5782,30 @@ function serviceFor(kind, context2) {
|
|
|
5775
5782
|
init_define_HERD_PACKAGE();
|
|
5776
5783
|
init_src();
|
|
5777
5784
|
import { mkdirSync as mkdirSync10, realpathSync as realpathSync4 } from "node:fs";
|
|
5778
|
-
import { join as
|
|
5779
|
-
|
|
5780
|
-
// src/friday/legacy.ts
|
|
5781
|
-
init_define_HERD_PACKAGE();
|
|
5782
|
-
import { existsSync as existsSync8, readdirSync as readdirSync3, rmSync as rmSync8 } from "node:fs";
|
|
5783
|
-
import { userInfo as userInfo2 } from "node:os";
|
|
5784
|
-
import { join as join11 } from "node:path";
|
|
5785
|
-
var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
|
|
5786
|
-
var OLD_APPS = ["herd-daemon", "herd-updater"];
|
|
5787
|
-
function oldPm2s(paths) {
|
|
5788
|
-
const dir = join11(paths.herdHome, "daemon");
|
|
5789
|
-
if (!existsSync8(dir)) return [];
|
|
5790
|
-
return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join11(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync8(pm2));
|
|
5791
|
-
}
|
|
5792
|
-
async function retireOldDaemon(paths, { home, run: run4 }) {
|
|
5793
|
-
const removed = [];
|
|
5794
|
-
const plist = join11(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
|
|
5795
|
-
if (existsSync8(plist)) {
|
|
5796
|
-
await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
|
|
5797
|
-
rmSync8(plist, { force: true });
|
|
5798
|
-
removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
|
|
5799
|
-
}
|
|
5800
|
-
for (const pm2 of oldPm2s(paths)) {
|
|
5801
|
-
const listed = await run4(process.execPath, [pm2, "jlist"]);
|
|
5802
|
-
let names = [];
|
|
5803
|
-
try {
|
|
5804
|
-
names = JSON.parse(listed.stdout).map((app) => app.name ?? "");
|
|
5805
|
-
} catch {
|
|
5806
|
-
names = [];
|
|
5807
|
-
}
|
|
5808
|
-
const ours = OLD_APPS.filter((name) => names.includes(name));
|
|
5809
|
-
if (ours.length === 0) continue;
|
|
5810
|
-
for (const name of ours) await run4(process.execPath, [pm2, "delete", name]);
|
|
5811
|
-
await run4(process.execPath, [pm2, "save", "--force"]);
|
|
5812
|
-
removed.push(`pm2's ${ours.join(" and ")}`);
|
|
5813
|
-
}
|
|
5814
|
-
return removed;
|
|
5815
|
-
}
|
|
5785
|
+
import { join as join14, resolve as resolve3 } from "node:path";
|
|
5816
5786
|
|
|
5817
|
-
// src/
|
|
5787
|
+
// src/package-manager.ts
|
|
5818
5788
|
init_define_HERD_PACKAGE();
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
if (!process.env[VERBOSE_ENV]) return;
|
|
5823
|
-
const seconds = ((Date.now() - began) / 1e3).toFixed(1);
|
|
5824
|
-
process.stderr.write(`[friday +${seconds}s] ${line2}
|
|
5825
|
-
`);
|
|
5826
|
-
}
|
|
5827
|
-
var tail = (text4) => text4.trim().split("\n").slice(-3).join(" | ");
|
|
5828
|
-
function verboseExec(run4) {
|
|
5829
|
-
return async (command, args, options) => {
|
|
5830
|
-
if (!process.env[VERBOSE_ENV]) return run4(command, args, options);
|
|
5831
|
-
const shown = [command, ...args].join(" ");
|
|
5832
|
-
verbose2(`running ${shown}`);
|
|
5833
|
-
const started = Date.now();
|
|
5834
|
-
const ran = await run4(command, args, options);
|
|
5835
|
-
const took = `${Date.now() - started}ms`;
|
|
5836
|
-
verbose2(
|
|
5837
|
-
ran.status === 0 ? `ran ${shown} (${took})` : `${shown} exited ${ran.status} (${took})${ran.stderr.trim() ? `: ${tail(ran.stderr)}` : ""}`
|
|
5838
|
-
);
|
|
5839
|
-
return ran;
|
|
5840
|
-
};
|
|
5841
|
-
}
|
|
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";
|
|
5842
5792
|
|
|
5843
5793
|
// src/friday/versions.ts
|
|
5844
5794
|
init_define_HERD_PACKAGE();
|
|
5845
5795
|
import { spawn as spawn5 } from "node:child_process";
|
|
5846
5796
|
import {
|
|
5847
|
-
existsSync as
|
|
5797
|
+
existsSync as existsSync8,
|
|
5848
5798
|
mkdirSync as mkdirSync9,
|
|
5849
|
-
readdirSync as
|
|
5799
|
+
readdirSync as readdirSync3,
|
|
5850
5800
|
readFileSync as readFileSync8,
|
|
5851
5801
|
readlinkSync,
|
|
5852
5802
|
realpathSync as realpathSync3,
|
|
5853
5803
|
renameSync as renameSync3,
|
|
5854
|
-
rmSync as
|
|
5804
|
+
rmSync as rmSync8,
|
|
5855
5805
|
symlinkSync,
|
|
5856
5806
|
writeFileSync as writeFileSync8
|
|
5857
5807
|
} from "node:fs";
|
|
5858
|
-
import { basename as basename2,
|
|
5808
|
+
import { basename as basename2, join as join11 } from "node:path";
|
|
5859
5809
|
var exec = (command, args, options = {}) => new Promise((resolve7) => {
|
|
5860
5810
|
let stdout = "";
|
|
5861
5811
|
let stderr = "";
|
|
@@ -5876,10 +5826,6 @@ var exec = (command, args, options = {}) => new Promise((resolve7) => {
|
|
|
5876
5826
|
resolve7({ status: status2 ?? 1, stdout, stderr });
|
|
5877
5827
|
});
|
|
5878
5828
|
});
|
|
5879
|
-
function npmCommand() {
|
|
5880
|
-
const beside = join12(dirname9(process.execPath), "npm");
|
|
5881
|
-
return existsSync9(beside) ? beside : "npm";
|
|
5882
|
-
}
|
|
5883
5829
|
function currentVersion(paths) {
|
|
5884
5830
|
try {
|
|
5885
5831
|
return basename2(readlinkSync(paths.current));
|
|
@@ -5888,10 +5834,10 @@ function currentVersion(paths) {
|
|
|
5888
5834
|
}
|
|
5889
5835
|
}
|
|
5890
5836
|
function installedVersions(paths) {
|
|
5891
|
-
if (!
|
|
5892
|
-
return
|
|
5837
|
+
if (!existsSync8(paths.versions)) return [];
|
|
5838
|
+
return readdirSync3(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
|
|
5893
5839
|
}
|
|
5894
|
-
var binOf = (versionDir, bin) =>
|
|
5840
|
+
var binOf = (versionDir, bin) => join11(packageDir(versionDir), `${bin}.mjs`);
|
|
5895
5841
|
async function versionRuns(versionDir, version, run4 = exec) {
|
|
5896
5842
|
const ran = await run4(process.execPath, [binOf(versionDir, "herd"), "--version"], {
|
|
5897
5843
|
timeoutMs: 3e4
|
|
@@ -5903,12 +5849,19 @@ async function versionRuns(versionDir, version, run4 = exec) {
|
|
|
5903
5849
|
return null;
|
|
5904
5850
|
}
|
|
5905
5851
|
function recordedIntegrity(dir) {
|
|
5906
|
-
|
|
5907
|
-
const
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
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
|
+
}
|
|
5911
5863
|
}
|
|
5864
|
+
return null;
|
|
5912
5865
|
}
|
|
5913
5866
|
function npmFreeEnv() {
|
|
5914
5867
|
return Object.fromEntries(
|
|
@@ -5916,10 +5869,10 @@ function npmFreeEnv() {
|
|
|
5916
5869
|
);
|
|
5917
5870
|
}
|
|
5918
5871
|
async function installVersion(paths, version, { spec, integrity, run: run4 = exec, env = {}, log = () => {
|
|
5919
|
-
} } = {}) {
|
|
5872
|
+
}, manager } = {}) {
|
|
5920
5873
|
if (!VERSION.test(version)) return { ok: false, reason: `${version} isn't a version.` };
|
|
5921
|
-
const dir =
|
|
5922
|
-
if (
|
|
5874
|
+
const dir = join11(paths.versions, version);
|
|
5875
|
+
if (existsSync8(dir)) {
|
|
5923
5876
|
const installed2 = recordedIntegrity(dir);
|
|
5924
5877
|
if (integrity && installed2 !== integrity)
|
|
5925
5878
|
return { ok: false, reason: `the installed ${version} doesn't have the integrity named.` };
|
|
@@ -5927,62 +5880,63 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
|
|
|
5927
5880
|
if (!broken2) return { ok: true, dir };
|
|
5928
5881
|
return { ok: false, reason: `the installed ${version} doesn't run: ${broken2}.` };
|
|
5929
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
|
+
};
|
|
5930
5889
|
sweepStaging(paths);
|
|
5931
|
-
const named =
|
|
5932
|
-
|
|
5890
|
+
const named = join11(paths.versions, `.staging-${version}-${process.pid}`);
|
|
5891
|
+
rmSync8(named, { recursive: true, force: true });
|
|
5933
5892
|
mkdirSync9(named, { recursive: true });
|
|
5934
5893
|
const staging = realpathSync3(named);
|
|
5935
5894
|
writeFileSync8(
|
|
5936
|
-
|
|
5937
|
-
`${JSON.stringify(
|
|
5895
|
+
join11(staging, "package.json"),
|
|
5896
|
+
`${JSON.stringify(allowScriptsManifest(["node-pty"]), null, 2)}
|
|
5938
5897
|
`
|
|
5939
5898
|
);
|
|
5899
|
+
if (using.name === "pnpm")
|
|
5900
|
+
writeFileSync8(join11(staging, "pnpm-workspace.yaml"), allowScriptsWorkspace(["node-pty"]));
|
|
5940
5901
|
const fail = (reason) => {
|
|
5941
|
-
|
|
5902
|
+
rmSync8(staging, { recursive: true, force: true });
|
|
5942
5903
|
return { ok: false, reason };
|
|
5943
5904
|
};
|
|
5944
|
-
log(`Installing ${PACKAGE_NAME}@${version}.`);
|
|
5905
|
+
log(`Installing ${PACKAGE_NAME}@${version} with ${using.name} ${using.version}.`);
|
|
5945
5906
|
const installed = await run4(
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
"install",
|
|
5949
|
-
"--prefix",
|
|
5950
|
-
staging,
|
|
5951
|
-
spec ?? `${PACKAGE_NAME}@${version}`,
|
|
5952
|
-
"--no-audit",
|
|
5953
|
-
"--no-fund",
|
|
5954
|
-
"--no-update-notifier",
|
|
5955
|
-
"--save-exact"
|
|
5956
|
-
],
|
|
5907
|
+
using.command,
|
|
5908
|
+
addArgs(using.name, staging, spec ?? `${PACKAGE_NAME}@${version}`),
|
|
5957
5909
|
// From inside the folder, so a workspace around wherever this ran isn't taken for its project.
|
|
5958
5910
|
{ cwd: staging, env: { ...npmFreeEnv(), ...env }, timeoutMs: 10 * 6e4 }
|
|
5959
5911
|
);
|
|
5960
5912
|
if (installed.status !== 0)
|
|
5961
|
-
return fail(
|
|
5913
|
+
return fail(
|
|
5914
|
+
`${using.name} couldn't install it: ${(installed.stderr || installed.stdout).trim().slice(-500)}`
|
|
5915
|
+
);
|
|
5962
5916
|
if (integrity) {
|
|
5963
5917
|
const got = recordedIntegrity(staging);
|
|
5964
5918
|
if (got !== integrity)
|
|
5965
5919
|
return fail(
|
|
5966
|
-
`its integrity doesn't match the release:
|
|
5920
|
+
`its integrity doesn't match the release: ${using.name} recorded ${got ?? "nothing"}, and herd named ${integrity}`
|
|
5967
5921
|
);
|
|
5968
5922
|
}
|
|
5969
5923
|
const broken = await versionRuns(staging, version, run4);
|
|
5970
5924
|
if (broken) return fail(broken);
|
|
5971
|
-
renameSync3(staging,
|
|
5925
|
+
renameSync3(staging, join11(realpathSync3(paths.versions), version));
|
|
5972
5926
|
return { ok: true, dir };
|
|
5973
5927
|
}
|
|
5974
5928
|
function sweepStaging(paths) {
|
|
5975
|
-
if (!
|
|
5976
|
-
for (const name of
|
|
5929
|
+
if (!existsSync8(paths.versions)) return;
|
|
5930
|
+
for (const name of readdirSync3(paths.versions)) {
|
|
5977
5931
|
const match = /^\.staging-.+-(\d+)$/.exec(name);
|
|
5978
5932
|
if (match && !alive2(Number(match[1])))
|
|
5979
|
-
|
|
5933
|
+
rmSync8(join11(paths.versions, name), { recursive: true, force: true });
|
|
5980
5934
|
}
|
|
5981
5935
|
}
|
|
5982
5936
|
function activate(paths, version) {
|
|
5983
5937
|
const next = `${paths.current}.next-${process.pid}`;
|
|
5984
|
-
|
|
5985
|
-
symlinkSync(
|
|
5938
|
+
rmSync8(next, { force: true });
|
|
5939
|
+
symlinkSync(join11("versions", version), next);
|
|
5986
5940
|
renameSync3(next, paths.current);
|
|
5987
5941
|
}
|
|
5988
5942
|
function pruneVersions(paths, { inUse = [], keep = 3 } = {}) {
|
|
@@ -5997,7 +5951,7 @@ function pruneVersions(paths, { inUse = [], keep = 3 } = {}) {
|
|
|
5997
5951
|
const removed = [];
|
|
5998
5952
|
for (const version of versions) {
|
|
5999
5953
|
if (kept.has(version)) continue;
|
|
6000
|
-
|
|
5954
|
+
rmSync8(join11(paths.versions, version), { recursive: true, force: true });
|
|
6001
5955
|
removed.push(version);
|
|
6002
5956
|
}
|
|
6003
5957
|
return removed;
|
|
@@ -6021,6 +5975,271 @@ async function hostVersions(paths, run4 = exec) {
|
|
|
6021
5975
|
return [...found].filter(Boolean);
|
|
6022
5976
|
}
|
|
6023
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
|
+
|
|
6024
6243
|
// src/friday/start.ts
|
|
6025
6244
|
var MIN_NODE = [22, 18];
|
|
6026
6245
|
var SETUP_WAIT_MS = 2e4;
|
|
@@ -6051,7 +6270,7 @@ var real = (path) => {
|
|
|
6051
6270
|
}
|
|
6052
6271
|
};
|
|
6053
6272
|
function chooseRoot(cwd, home) {
|
|
6054
|
-
return real(cwd) === real(home) ?
|
|
6273
|
+
return real(cwd) === real(home) ? join14(home, "friday") : resolve3(cwd);
|
|
6055
6274
|
}
|
|
6056
6275
|
async function runStart(options) {
|
|
6057
6276
|
const { paths, run: run4 } = options;
|
|
@@ -6116,9 +6335,15 @@ async function runStart(options) {
|
|
|
6116
6335
|
print(
|
|
6117
6336
|
`Stopped the old daemon: removed ${retired.join(" and ")}. Its checkout is left as it was.`
|
|
6118
6337
|
);
|
|
6119
|
-
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);
|
|
6120
6345
|
const command = [process.execPath, binOf(paths.current, "friday"), "supervise"];
|
|
6121
|
-
const env = { HERD_HOME: paths.herdHome, ...options.env };
|
|
6346
|
+
const env = { HERD_HOME: paths.herdHome, [MANAGER_ENV]: manager.command, ...options.env };
|
|
6122
6347
|
const service = (options.service ?? defaultService(options))(command, env);
|
|
6123
6348
|
if (moving && running) {
|
|
6124
6349
|
print(`Stopping Friday at ${existing} to start it again from ${root}.`);
|
|
@@ -6224,7 +6449,7 @@ init_src();
|
|
|
6224
6449
|
import { spawn as spawn6 } from "node:child_process";
|
|
6225
6450
|
import {
|
|
6226
6451
|
closeSync as closeSync3,
|
|
6227
|
-
existsSync as
|
|
6452
|
+
existsSync as existsSync11,
|
|
6228
6453
|
mkdirSync as mkdirSync11,
|
|
6229
6454
|
openSync as openSync4,
|
|
6230
6455
|
readFileSync as readFileSync9,
|
|
@@ -6309,7 +6534,7 @@ var Supervisor = class {
|
|
|
6309
6534
|
const { daemonLog, logs } = this.options.paths;
|
|
6310
6535
|
mkdirSync11(logs, { recursive: true });
|
|
6311
6536
|
try {
|
|
6312
|
-
if (
|
|
6537
|
+
if (existsSync11(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
|
|
6313
6538
|
renameSync4(daemonLog, `${daemonLog}.1`);
|
|
6314
6539
|
} catch {
|
|
6315
6540
|
}
|
|
@@ -6462,9 +6687,9 @@ Usage: ${FRIDAY_COMMAND} <command>
|
|
|
6462
6687
|
|
|
6463
6688
|
Agents use the herd command, which comes with this one. herd setup shows what's left to set up.`;
|
|
6464
6689
|
function defaults() {
|
|
6465
|
-
const home =
|
|
6690
|
+
const home = homedir5();
|
|
6466
6691
|
return {
|
|
6467
|
-
paths: fridayPaths(process.env.HERD_HOME ??
|
|
6692
|
+
paths: fridayPaths(process.env.HERD_HOME ?? join15(home, ".herd")),
|
|
6468
6693
|
home,
|
|
6469
6694
|
cwd: process.cwd(),
|
|
6470
6695
|
run: exec,
|
|
@@ -6536,7 +6761,7 @@ async function friday(args, deps = defaults()) {
|
|
|
6536
6761
|
agents: {
|
|
6537
6762
|
// The choice lives in herd.config.ts, so it's asked for only before there is one, or
|
|
6538
6763
|
// with --config.
|
|
6539
|
-
ask: rest.includes("--config") || !
|
|
6764
|
+
ask: rest.includes("--config") || !existsSync12(herdConfigIn(deps.paths.herdHome)),
|
|
6540
6765
|
installed: async () => {
|
|
6541
6766
|
verbose2("looking for installed agents");
|
|
6542
6767
|
const found = installedAgents();
|
|
@@ -6595,7 +6820,7 @@ function agentsFlag(args) {
|
|
|
6595
6820
|
return parsed.shares;
|
|
6596
6821
|
}
|
|
6597
6822
|
function attachTo(paths, terminal) {
|
|
6598
|
-
const herd =
|
|
6823
|
+
const herd = existsSync12(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join15(dirname10(process.argv[1] ?? ""), "herd.mjs");
|
|
6599
6824
|
const child = spawn7(process.execPath, [herd, "attach", terminal], {
|
|
6600
6825
|
stdio: "inherit",
|
|
6601
6826
|
env: { ...process.env, HERD_HOME: paths.herdHome }
|
|
@@ -6659,14 +6884,14 @@ function runStatus(deps) {
|
|
|
6659
6884
|
function readMissingFolder(herdHome2) {
|
|
6660
6885
|
try {
|
|
6661
6886
|
return folderUnavailableSchema.parse(
|
|
6662
|
-
JSON.parse(readFileSync10(
|
|
6887
|
+
JSON.parse(readFileSync10(join15(herdHome2, "folder-unavailable.json"), "utf8"))
|
|
6663
6888
|
);
|
|
6664
6889
|
} catch {
|
|
6665
6890
|
return null;
|
|
6666
6891
|
}
|
|
6667
6892
|
}
|
|
6668
6893
|
function tail2(path, count) {
|
|
6669
|
-
if (!
|
|
6894
|
+
if (!existsSync12(path)) return [];
|
|
6670
6895
|
const lines = readFileSync10(path, "utf8").split("\n");
|
|
6671
6896
|
if (lines.at(-1) === "") lines.pop();
|
|
6672
6897
|
return lines.slice(-count);
|
|
@@ -6674,7 +6899,7 @@ function tail2(path, count) {
|
|
|
6674
6899
|
async function runLogs(args, deps) {
|
|
6675
6900
|
const { paths, print } = deps;
|
|
6676
6901
|
const follow = args.includes("-f") || args.includes("--follow");
|
|
6677
|
-
const files = [paths.supervisorLog, paths.daemonLog].filter((file) =>
|
|
6902
|
+
const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync12(file));
|
|
6678
6903
|
if (files.length === 0) {
|
|
6679
6904
|
print(`There are no logs in ${paths.logs} yet.`);
|
|
6680
6905
|
return 0;
|
|
@@ -6724,7 +6949,7 @@ init_define_HERD_PACKAGE();
|
|
|
6724
6949
|
init_define_HERD_PACKAGE();
|
|
6725
6950
|
import { spawn as spawn8 } from "node:child_process";
|
|
6726
6951
|
import { appendFileSync, readFileSync as readFileSync11 } from "node:fs";
|
|
6727
|
-
import { dirname as dirname11, join as
|
|
6952
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
6728
6953
|
var DEPLOY_HOSTS = ["api.cloudflare.com", "api.github.com", "registry.npmjs.org"];
|
|
6729
6954
|
function buildPolicy(input) {
|
|
6730
6955
|
const platform2 = input.platform ?? process.platform;
|
|
@@ -6771,7 +6996,7 @@ async function runSandboxed(args) {
|
|
|
6771
6996
|
return 3;
|
|
6772
6997
|
}
|
|
6773
6998
|
await SandboxManager.initialize(policy, void 0, true);
|
|
6774
|
-
const blocked =
|
|
6999
|
+
const blocked = join16(process.env.HOME ?? ".", BLOCKED_LOG);
|
|
6775
7000
|
let written = 0;
|
|
6776
7001
|
const unsubscribe = SandboxManager.getSandboxViolationStore().subscribe((violations) => {
|
|
6777
7002
|
const fresh = violations.slice(written).map((violation) => describeRefusal(violation.line)).filter((line2) => line2 !== null);
|
|
@@ -7272,8 +7497,8 @@ init_define_HERD_PACKAGE();
|
|
|
7272
7497
|
// src/build-runner-key.ts
|
|
7273
7498
|
init_define_HERD_PACKAGE();
|
|
7274
7499
|
init_src();
|
|
7275
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
7276
|
-
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";
|
|
7277
7502
|
import { z as z24 } from "zod";
|
|
7278
7503
|
var BUILDS_URL = "https://builds.herdingchats.com";
|
|
7279
7504
|
var runnerKeySchema = z24.object({
|
|
@@ -7281,10 +7506,10 @@ var runnerKeySchema = z24.object({
|
|
|
7281
7506
|
key: z24.string().min(20),
|
|
7282
7507
|
url: z24.string().url()
|
|
7283
7508
|
});
|
|
7284
|
-
var runnerKeyPath = () =>
|
|
7509
|
+
var runnerKeyPath = () => join17(herdHome(), "build-runner.json");
|
|
7285
7510
|
function readRunnerKey() {
|
|
7286
7511
|
const path = runnerKeyPath();
|
|
7287
|
-
if (!
|
|
7512
|
+
if (!existsSync13(path)) return null;
|
|
7288
7513
|
try {
|
|
7289
7514
|
return runnerKeySchema.parse(JSON.parse(readFileSync13(path, "utf8")));
|
|
7290
7515
|
} catch {
|
|
@@ -7463,7 +7688,7 @@ function formatSpeed(report) {
|
|
|
7463
7688
|
// src/builds-admin.ts
|
|
7464
7689
|
init_define_HERD_PACKAGE();
|
|
7465
7690
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
7466
|
-
import { join as
|
|
7691
|
+
import { join as join18 } from "node:path";
|
|
7467
7692
|
var NOT_ADMIN = "builds are only for the admin's machines";
|
|
7468
7693
|
async function isAdmin(fetch2) {
|
|
7469
7694
|
const response = await fetch2("/api/people/me").catch(() => null);
|
|
@@ -7471,13 +7696,13 @@ async function isAdmin(fetch2) {
|
|
|
7471
7696
|
const body = await response.json().catch(() => ({}));
|
|
7472
7697
|
return body.admin === true;
|
|
7473
7698
|
}
|
|
7474
|
-
var refusalPath = () =>
|
|
7699
|
+
var refusalPath = () => join18(herdHome(), "builds", "refused.json");
|
|
7475
7700
|
function writeBuildsRefusal(reason) {
|
|
7476
7701
|
if (!reason) {
|
|
7477
7702
|
rmSync10(refusalPath(), { force: true });
|
|
7478
7703
|
return;
|
|
7479
7704
|
}
|
|
7480
|
-
mkdirSync13(
|
|
7705
|
+
mkdirSync13(join18(herdHome(), "builds"), { recursive: true });
|
|
7481
7706
|
writeFileSync10(refusalPath(), JSON.stringify({ reason }));
|
|
7482
7707
|
}
|
|
7483
7708
|
function readBuildsRefusal() {
|
|
@@ -7641,7 +7866,7 @@ async function runConversations(rest) {
|
|
|
7641
7866
|
init_define_HERD_PACKAGE();
|
|
7642
7867
|
init_src();
|
|
7643
7868
|
import { availableParallelism, totalmem } from "node:os";
|
|
7644
|
-
import { join as
|
|
7869
|
+
import { join as join34 } from "node:path";
|
|
7645
7870
|
|
|
7646
7871
|
// src/agent.ts
|
|
7647
7872
|
init_define_HERD_PACKAGE();
|
|
@@ -7750,20 +7975,20 @@ function readClaudeScreen(raw) {
|
|
|
7750
7975
|
|
|
7751
7976
|
// src/worktrees.ts
|
|
7752
7977
|
init_define_HERD_PACKAGE();
|
|
7753
|
-
import { existsSync as
|
|
7754
|
-
import { isAbsolute as
|
|
7755
|
-
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");
|
|
7756
7981
|
var WORD_ID = /^[a-z]+-[a-z]+-[a-z]+$/;
|
|
7757
7982
|
var conversationPrefix = (conversationId) => WORD_ID.test(conversationId) ? conversationId : conversationId.replace(/[^A-Za-z0-9]/g, "").slice(0, 12).toLowerCase();
|
|
7758
7983
|
var conversationBranch = (conversationId) => `herd/${conversationPrefix(conversationId)}`;
|
|
7759
|
-
var conversationFolder = (convos, conversationId) =>
|
|
7984
|
+
var conversationFolder = (convos, conversationId) => join19(convos, conversationPrefix(conversationId));
|
|
7760
7985
|
function folderForConversation(root, conversationId) {
|
|
7761
7986
|
const earlier = conversationFolder(legacyConvosDir(root), conversationId);
|
|
7762
|
-
return
|
|
7987
|
+
return existsSync14(earlier) ? earlier : conversationFolder(convosHome(root), conversationId);
|
|
7763
7988
|
}
|
|
7764
7989
|
var within = (dir, path) => {
|
|
7765
7990
|
const between = relative(dir, path);
|
|
7766
|
-
return between !== "" && !between.startsWith("..") && !
|
|
7991
|
+
return between !== "" && !between.startsWith("..") && !isAbsolute3(between);
|
|
7767
7992
|
};
|
|
7768
7993
|
var real2 = (path) => {
|
|
7769
7994
|
try {
|
|
@@ -8062,8 +8287,8 @@ function readAgyScreen(raw) {
|
|
|
8062
8287
|
// src/agy-settings.ts
|
|
8063
8288
|
init_define_HERD_PACKAGE();
|
|
8064
8289
|
import { lstatSync as lstatSync2, mkdirSync as mkdirSync14, realpathSync as realpathSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
8065
|
-
import { join as
|
|
8066
|
-
var AGY_SETTINGS_FILE =
|
|
8290
|
+
import { join as join20, sep } from "node:path";
|
|
8291
|
+
var AGY_SETTINGS_FILE = join20(".herd", "agy-settings.json");
|
|
8067
8292
|
var AGY_HOOK_EVENTS = ["SessionStart", "PreInvocation", "Stop"];
|
|
8068
8293
|
var agyHookCommand = (event2, herd) => `${herd} hook agy ${event2} || true`;
|
|
8069
8294
|
function agyHooks(herd) {
|
|
@@ -8080,7 +8305,7 @@ function agySettings(herd) {
|
|
|
8080
8305
|
return { statusLine: { type: "command", command: `${herd} statusline agy` } };
|
|
8081
8306
|
}
|
|
8082
8307
|
function ownFolder(worktree, name) {
|
|
8083
|
-
const folder =
|
|
8308
|
+
const folder = join20(worktree, name);
|
|
8084
8309
|
if (lstatSync2(folder, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
8085
8310
|
throw new Error(`${name} in the worktree is a link`);
|
|
8086
8311
|
}
|
|
@@ -8092,13 +8317,13 @@ function ownFolder(worktree, name) {
|
|
|
8092
8317
|
}
|
|
8093
8318
|
function writeAgyFiles(worktree, herd) {
|
|
8094
8319
|
const agents = ownFolder(worktree, ".agents");
|
|
8095
|
-
writeFileSync11(
|
|
8320
|
+
writeFileSync11(join20(agents, "hooks.json"), `${JSON.stringify(agyHooks(herd), null, 2)}
|
|
8096
8321
|
`);
|
|
8097
|
-
writeFileSync11(
|
|
8322
|
+
writeFileSync11(join20(agents, ".gitignore"), "*\n");
|
|
8098
8323
|
const herdFolder = ownFolder(worktree, ".herd");
|
|
8099
|
-
writeFileSync11(
|
|
8324
|
+
writeFileSync11(join20(herdFolder, ".gitignore"), "*\n");
|
|
8100
8325
|
writeFileSync11(
|
|
8101
|
-
|
|
8326
|
+
join20(worktree, AGY_SETTINGS_FILE),
|
|
8102
8327
|
`${JSON.stringify(agySettings(herd), null, 2)}
|
|
8103
8328
|
`
|
|
8104
8329
|
);
|
|
@@ -8253,15 +8478,15 @@ var AgyDriver = class {
|
|
|
8253
8478
|
// src/bare-clones.ts
|
|
8254
8479
|
init_define_HERD_PACKAGE();
|
|
8255
8480
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
8256
|
-
import { existsSync as
|
|
8257
|
-
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";
|
|
8258
8483
|
function bareClonePath(dir, remote) {
|
|
8259
8484
|
const key = repoKey(remote);
|
|
8260
8485
|
if (!key) throw new StoreError(`${remote} isn't a repo's URL.`);
|
|
8261
|
-
return
|
|
8486
|
+
return join21(dir, `${key}.git`);
|
|
8262
8487
|
}
|
|
8263
8488
|
async function ensureBareClone(store, remote, env) {
|
|
8264
|
-
if (
|
|
8489
|
+
if (existsSync15(store)) {
|
|
8265
8490
|
let fetchFailed = null;
|
|
8266
8491
|
try {
|
|
8267
8492
|
await git(env, store, "fetch", "--quiet", "--prune", "origin");
|
|
@@ -8302,7 +8527,7 @@ function bareClones(dir) {
|
|
|
8302
8527
|
return;
|
|
8303
8528
|
}
|
|
8304
8529
|
for (const name of entries.sort()) {
|
|
8305
|
-
const path =
|
|
8530
|
+
const path = join21(at, name);
|
|
8306
8531
|
if (name.endsWith(".git")) {
|
|
8307
8532
|
try {
|
|
8308
8533
|
const remote = gitSync(path, "config", "--get", "remote.origin.url");
|
|
@@ -8483,7 +8708,7 @@ init_define_HERD_PACKAGE();
|
|
|
8483
8708
|
import { execFile as execFile2, execFileSync as execFileSync5, spawn as spawn9 } from "node:child_process";
|
|
8484
8709
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
8485
8710
|
import {
|
|
8486
|
-
existsSync as
|
|
8711
|
+
existsSync as existsSync16,
|
|
8487
8712
|
mkdirSync as mkdirSync17,
|
|
8488
8713
|
mkdtempSync,
|
|
8489
8714
|
readdirSync as readdirSync7,
|
|
@@ -8492,16 +8717,16 @@ import {
|
|
|
8492
8717
|
rmSync as rmSync13,
|
|
8493
8718
|
writeFileSync as writeFileSync13
|
|
8494
8719
|
} from "node:fs";
|
|
8495
|
-
import { homedir as
|
|
8496
|
-
import { join as
|
|
8720
|
+
import { homedir as homedir7, tmpdir } from "node:os";
|
|
8721
|
+
import { join as join23 } from "node:path";
|
|
8497
8722
|
import { promisify as promisify2 } from "node:util";
|
|
8498
8723
|
|
|
8499
8724
|
// src/check-registry.ts
|
|
8500
8725
|
init_define_HERD_PACKAGE();
|
|
8501
8726
|
import { mkdirSync as mkdirSync16, readdirSync as readdirSync6, readFileSync as readFileSync15, rmSync as rmSync12, writeFileSync as writeFileSync12 } from "node:fs";
|
|
8502
|
-
import { homedir as
|
|
8503
|
-
import { join as
|
|
8504
|
-
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");
|
|
8505
8730
|
var RESERVED_MB = 6 * 1024;
|
|
8506
8731
|
var MIN_MEMORY_MB = 1024;
|
|
8507
8732
|
var alive3 = (pid) => {
|
|
@@ -8514,7 +8739,7 @@ var alive3 = (pid) => {
|
|
|
8514
8739
|
};
|
|
8515
8740
|
function registerCheck(dir, pid, startedAt = Date.now()) {
|
|
8516
8741
|
mkdirSync16(dir, { recursive: true });
|
|
8517
|
-
const file =
|
|
8742
|
+
const file = join22(dir, `${pid}.json`);
|
|
8518
8743
|
writeFileSync12(file, JSON.stringify({ pid, startedAt }));
|
|
8519
8744
|
return () => rmSync12(file, { force: true });
|
|
8520
8745
|
}
|
|
@@ -8530,12 +8755,12 @@ function runningChecks(dir, isAlive = alive3) {
|
|
|
8530
8755
|
const pid = Number(name.slice(0, -".json".length));
|
|
8531
8756
|
let entry = null;
|
|
8532
8757
|
try {
|
|
8533
|
-
entry = JSON.parse(readFileSync15(
|
|
8758
|
+
entry = JSON.parse(readFileSync15(join22(dir, name), "utf8"));
|
|
8534
8759
|
} catch {
|
|
8535
8760
|
continue;
|
|
8536
8761
|
}
|
|
8537
8762
|
if (entry?.pid === pid && isAlive(pid)) count += 1;
|
|
8538
|
-
else rmSync12(
|
|
8763
|
+
else rmSync12(join22(dir, name), { force: true });
|
|
8539
8764
|
}
|
|
8540
8765
|
return count;
|
|
8541
8766
|
}
|
|
@@ -8543,9 +8768,9 @@ var memoryShareOf = (totalMb, running) => Math.max(MIN_MEMORY_MB, Math.floor((to
|
|
|
8543
8768
|
|
|
8544
8769
|
// src/builds.ts
|
|
8545
8770
|
init_self();
|
|
8546
|
-
var sandboxStatusPath = () =>
|
|
8771
|
+
var sandboxStatusPath = () => join23(herdHome(), "builds", "sandbox.json");
|
|
8547
8772
|
function writeSandboxStatus(status2, now = Date.now()) {
|
|
8548
|
-
mkdirSync17(
|
|
8773
|
+
mkdirSync17(join23(herdHome(), "builds"), { recursive: true });
|
|
8549
8774
|
writeFileSync13(sandboxStatusPath(), JSON.stringify({ ...status2, at: now }));
|
|
8550
8775
|
}
|
|
8551
8776
|
function readSandboxStatus() {
|
|
@@ -8577,7 +8802,7 @@ var real3 = (path) => {
|
|
|
8577
8802
|
}
|
|
8578
8803
|
};
|
|
8579
8804
|
function worktreeOf(dir, store) {
|
|
8580
|
-
if (!
|
|
8805
|
+
if (!existsSync16(join23(dir, ".git"))) return false;
|
|
8581
8806
|
try {
|
|
8582
8807
|
return real3(git2(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")) === real3(store);
|
|
8583
8808
|
} catch {
|
|
@@ -8589,36 +8814,54 @@ function cloneFolder(from, to) {
|
|
|
8589
8814
|
execFileSync5("cp", [...args, from, to]);
|
|
8590
8815
|
}
|
|
8591
8816
|
function moduleFolders(dir) {
|
|
8592
|
-
const found =
|
|
8817
|
+
const found = existsSync16(join23(dir, "node_modules")) ? ["node_modules"] : [];
|
|
8593
8818
|
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
8594
8819
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
8595
|
-
if (
|
|
8596
|
-
found.push(
|
|
8820
|
+
if (existsSync16(join23(dir, entry.name, "node_modules")))
|
|
8821
|
+
found.push(join23(entry.name, "node_modules"));
|
|
8597
8822
|
}
|
|
8598
8823
|
}
|
|
8599
8824
|
return found;
|
|
8600
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
|
+
}
|
|
8601
8840
|
function lockHash(dir) {
|
|
8602
|
-
const lock = readFileSync16(
|
|
8841
|
+
const lock = readFileSync16(join23(dir, checkoutManager(dir).lockfile));
|
|
8603
8842
|
const major = process.versions.node.split(".")[0];
|
|
8604
8843
|
return createHash3("sha256").update(lock).update(`${process.platform}-${process.arch}-${major}`).digest("hex").slice(0, 32);
|
|
8605
8844
|
}
|
|
8606
|
-
var
|
|
8607
|
-
const
|
|
8608
|
-
const
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
{
|
|
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, {
|
|
8612
8854
|
cwd: dir,
|
|
8855
|
+
env: { ...env, COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
|
|
8613
8856
|
stdio: "ignore"
|
|
8614
|
-
}
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
);
|
|
8621
|
-
}
|
|
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
|
+
};
|
|
8622
8865
|
var BuildRunner = class _BuildRunner {
|
|
8623
8866
|
constructor(options) {
|
|
8624
8867
|
this.options = options;
|
|
@@ -8626,11 +8869,11 @@ var BuildRunner = class _BuildRunner {
|
|
|
8626
8869
|
options;
|
|
8627
8870
|
running = /* @__PURE__ */ new Map();
|
|
8628
8871
|
home() {
|
|
8629
|
-
return this.options.home ??
|
|
8872
|
+
return this.options.home ?? join23(herdHome(), "builds");
|
|
8630
8873
|
}
|
|
8631
8874
|
/** Where builds' own bare clones are, apart from the root's checkouts. */
|
|
8632
8875
|
clonesDir() {
|
|
8633
|
-
return
|
|
8876
|
+
return join23(this.home(), "clones");
|
|
8634
8877
|
}
|
|
8635
8878
|
/** Herd's bare clone of a build's repo, for builds, whether or not it's there yet. */
|
|
8636
8879
|
storeFor(build) {
|
|
@@ -8651,8 +8894,8 @@ var BuildRunner = class _BuildRunner {
|
|
|
8651
8894
|
const store = bareClonePath(this.clonesDir(), remote);
|
|
8652
8895
|
const env = this.options.gitEnv?.();
|
|
8653
8896
|
const name = (repoKey(build.repo) ?? build.repo).replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
8654
|
-
const dir =
|
|
8655
|
-
mkdirSync17(
|
|
8897
|
+
const dir = join23(this.home(), name, String(slot));
|
|
8898
|
+
mkdirSync17(join23(this.home(), name), { recursive: true });
|
|
8656
8899
|
await withStoreLock(
|
|
8657
8900
|
store,
|
|
8658
8901
|
async () => {
|
|
@@ -8691,30 +8934,30 @@ var BuildRunner = class _BuildRunner {
|
|
|
8691
8934
|
/** Makes `node_modules` match the checkout's lockfile: kept, copied from the cache, or installed. */
|
|
8692
8935
|
async ensureModules(dir) {
|
|
8693
8936
|
const wanted = lockHash(dir);
|
|
8694
|
-
const marker =
|
|
8695
|
-
if (
|
|
8696
|
-
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);
|
|
8697
8940
|
for (const folder of moduleFolders(dir))
|
|
8698
|
-
rmSync13(
|
|
8699
|
-
if (
|
|
8700
|
-
for (const folder of readFileSync16(
|
|
8701
|
-
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));
|
|
8702
8945
|
}
|
|
8703
8946
|
} else {
|
|
8704
8947
|
this.options.log?.(`Installing node_modules for lockfile ${wanted.slice(0, 8)}.`);
|
|
8705
|
-
await (this.options.install ??
|
|
8706
|
-
const staging = mkdtempSync(
|
|
8948
|
+
await (this.options.install ?? frozenInstall(this.options.gitEnv))(dir);
|
|
8949
|
+
const staging = mkdtempSync(join23(this.home(), "node_modules-"));
|
|
8707
8950
|
const folders = moduleFolders(dir);
|
|
8708
8951
|
for (const folder of folders) {
|
|
8709
|
-
mkdirSync17(
|
|
8710
|
-
cloneFolder(
|
|
8952
|
+
mkdirSync17(join23(staging, folder, ".."), { recursive: true });
|
|
8953
|
+
cloneFolder(join23(dir, folder), join23(staging, folder));
|
|
8711
8954
|
}
|
|
8712
|
-
writeFileSync13(
|
|
8713
|
-
mkdirSync17(
|
|
8955
|
+
writeFileSync13(join23(staging, ".complete"), folders.join("\n"));
|
|
8956
|
+
mkdirSync17(join23(this.home(), "node_modules"), { recursive: true });
|
|
8714
8957
|
rmSync13(cache, { recursive: true, force: true });
|
|
8715
8958
|
execFileSync5("mv", [staging, cache]);
|
|
8716
8959
|
}
|
|
8717
|
-
mkdirSync17(
|
|
8960
|
+
mkdirSync17(join23(dir, "node_modules"), { recursive: true });
|
|
8718
8961
|
writeFileSync13(marker, wanted);
|
|
8719
8962
|
}
|
|
8720
8963
|
/**
|
|
@@ -8725,7 +8968,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8725
8968
|
const env = {
|
|
8726
8969
|
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
|
8727
8970
|
HOME: home,
|
|
8728
|
-
TMPDIR:
|
|
8971
|
+
TMPDIR: join23(home, "tmp"),
|
|
8729
8972
|
LANG: process.env.LANG ?? "en_US.UTF-8",
|
|
8730
8973
|
HERD_BUILD_ID: build.id,
|
|
8731
8974
|
HERD_BUILD_SECRET: build.secret,
|
|
@@ -8737,7 +8980,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8737
8980
|
// The sandbox's proxy is the only way out, and Node's own requests use it only when asked.
|
|
8738
8981
|
NODE_USE_ENV_PROXY: "1",
|
|
8739
8982
|
// Where `srt` points TMPDIR inside the sandbox: the build's own temporary folder.
|
|
8740
|
-
CLAUDE_CODE_TMPDIR:
|
|
8983
|
+
CLAUDE_CODE_TMPDIR: join23(home, "tmp")
|
|
8741
8984
|
};
|
|
8742
8985
|
return env;
|
|
8743
8986
|
}
|
|
@@ -8748,8 +8991,8 @@ var BuildRunner = class _BuildRunner {
|
|
|
8748
8991
|
/** Runs `scripts/build.mjs` from the build's own commit, in a process group of its own. */
|
|
8749
8992
|
async run(build, slot) {
|
|
8750
8993
|
const dir = await this.prepare(build, slot);
|
|
8751
|
-
const home = mkdtempSync(
|
|
8752
|
-
mkdirSync17(
|
|
8994
|
+
const home = mkdtempSync(join23(this.options.scratch ?? scratchRoot(), "hb-"));
|
|
8995
|
+
mkdirSync17(join23(home, "tmp"), { recursive: true });
|
|
8753
8996
|
const run4 = randomUUID4();
|
|
8754
8997
|
const env = {
|
|
8755
8998
|
..._BuildRunner.environment(build, home),
|
|
@@ -8759,11 +9002,11 @@ var BuildRunner = class _BuildRunner {
|
|
|
8759
9002
|
env.HERD_CHECK_CPUS = String(Math.max(1, Math.floor(this.options.cpus())));
|
|
8760
9003
|
if (this.options.memoryMb)
|
|
8761
9004
|
env.HERD_CHECK_MEMORY_MB = String(Math.max(1, Math.floor(this.options.memoryMb())));
|
|
8762
|
-
let command = [process.execPath,
|
|
9005
|
+
let command = [process.execPath, join23(dir, "scripts", "build.mjs")];
|
|
8763
9006
|
let policyPath = null;
|
|
8764
9007
|
if (this.sandboxed()) {
|
|
8765
9008
|
const policy = buildPolicy({
|
|
8766
|
-
ownerHome: this.options.ownerHome ??
|
|
9009
|
+
ownerHome: this.options.ownerHome ?? homedir7(),
|
|
8767
9010
|
root: this.options.root(),
|
|
8768
9011
|
convos: convosHome(this.options.root()),
|
|
8769
9012
|
worktree: dir,
|
|
@@ -8774,8 +9017,8 @@ var BuildRunner = class _BuildRunner {
|
|
|
8774
9017
|
main: build.main,
|
|
8775
9018
|
buildsUrl: build.url
|
|
8776
9019
|
});
|
|
8777
|
-
mkdirSync17(
|
|
8778
|
-
policyPath =
|
|
9020
|
+
mkdirSync17(join23(this.home(), "policies"), { recursive: true });
|
|
9021
|
+
policyPath = join23(this.home(), "policies", `${build.id}.json`);
|
|
8779
9022
|
writeFileSync13(policyPath, JSON.stringify(policy), { mode: 384 });
|
|
8780
9023
|
env.HERD_BUILD_SANDBOX = describePolicy(policy);
|
|
8781
9024
|
command = [process.execPath, HERD_BIN, "build-sandbox", policyPath, "--", ...command];
|
|
@@ -8824,7 +9067,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8824
9067
|
* It goes only by where they run from, never by a build ID, since the daemon a build's own tests
|
|
8825
9068
|
* start must never stop the build it runs in.
|
|
8826
9069
|
*/
|
|
8827
|
-
static async sweep(home =
|
|
9070
|
+
static async sweep(home = join23(herdHome(), "builds"), list = listProcesses) {
|
|
8828
9071
|
const left = buildProcesses(list(), null, home);
|
|
8829
9072
|
await stopPids(left);
|
|
8830
9073
|
return left.length;
|
|
@@ -8836,37 +9079,37 @@ var BuildRunner = class _BuildRunner {
|
|
|
8836
9079
|
*/
|
|
8837
9080
|
static async probe(options = { buildsUrl: "" }) {
|
|
8838
9081
|
if (insideBuildSandbox()) return { ok: true };
|
|
8839
|
-
const herd = options.home ??
|
|
8840
|
-
const dir = mkdtempSync(
|
|
8841
|
-
const secret =
|
|
9082
|
+
const herd = options.home ?? join23(herdHome(), "builds");
|
|
9083
|
+
const dir = mkdtempSync(join23(scratchRoot(), "hp-"));
|
|
9084
|
+
const secret = join23(dir, "owner", "secret");
|
|
8842
9085
|
try {
|
|
8843
9086
|
mkdirSync17(herd, { recursive: true });
|
|
8844
|
-
for (const sub of ["work", "git", "home", "owner"]) mkdirSync17(
|
|
9087
|
+
for (const sub of ["work", "git", "home", "owner"]) mkdirSync17(join23(dir, sub));
|
|
8845
9088
|
writeFileSync13(secret, "probe");
|
|
8846
9089
|
const policy = buildPolicy({
|
|
8847
|
-
ownerHome:
|
|
8848
|
-
root:
|
|
8849
|
-
convos:
|
|
8850
|
-
worktree:
|
|
8851
|
-
store:
|
|
8852
|
-
worktreeGitDir:
|
|
8853
|
-
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"),
|
|
8854
9097
|
nodePrefix: nodePrefix(),
|
|
8855
9098
|
main: false,
|
|
8856
9099
|
buildsUrl: options.buildsUrl || "https://builds.herdingchats.com"
|
|
8857
9100
|
});
|
|
8858
|
-
const policyPath =
|
|
9101
|
+
const policyPath = join23(herd, "probe-policy.json");
|
|
8859
9102
|
writeFileSync13(policyPath, JSON.stringify(policy), { mode: 384 });
|
|
8860
9103
|
const script = [
|
|
8861
9104
|
'const fs = require("node:fs");',
|
|
8862
9105
|
`try { fs.readFileSync(${JSON.stringify(secret)}); process.exit(10); } catch {}`,
|
|
8863
|
-
`fs.writeFileSync(${JSON.stringify(
|
|
9106
|
+
`fs.writeFileSync(${JSON.stringify(join23(dir, "home", "ok"))}, "ok");`
|
|
8864
9107
|
].join(" ");
|
|
8865
9108
|
const child = spawn9(
|
|
8866
9109
|
process.execPath,
|
|
8867
9110
|
[HERD_BIN, "build-sandbox", policyPath, "--", process.execPath, "-e", script],
|
|
8868
9111
|
{
|
|
8869
|
-
env: { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME:
|
|
9112
|
+
env: { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME: join23(dir, "home") },
|
|
8870
9113
|
stdio: ["ignore", "ignore", "pipe"]
|
|
8871
9114
|
}
|
|
8872
9115
|
);
|
|
@@ -8880,7 +9123,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8880
9123
|
clearTimeout(timer);
|
|
8881
9124
|
if (status2 === 10)
|
|
8882
9125
|
return { ok: false, reason: "the sandbox let a build read the owner's files" };
|
|
8883
|
-
if (status2 !== 0 || !
|
|
9126
|
+
if (status2 !== 0 || !existsSync16(join23(dir, "home", "ok"))) {
|
|
8884
9127
|
const why = stderr.trim().split("\n").at(-1) || `the probe exited with ${status2}`;
|
|
8885
9128
|
return { ok: false, reason: why.slice(0, 300) };
|
|
8886
9129
|
}
|
|
@@ -9235,7 +9478,7 @@ var CodexDriver = class {
|
|
|
9235
9478
|
|
|
9236
9479
|
// src/daemon.ts
|
|
9237
9480
|
init_define_HERD_PACKAGE();
|
|
9238
|
-
import { join as
|
|
9481
|
+
import { join as join24 } from "node:path";
|
|
9239
9482
|
|
|
9240
9483
|
// src/dispatch-connection.ts
|
|
9241
9484
|
init_define_HERD_PACKAGE();
|
|
@@ -9339,7 +9582,7 @@ var DispatchConnection = class {
|
|
|
9339
9582
|
|
|
9340
9583
|
// src/daemon.ts
|
|
9341
9584
|
var CONFIG_POLL_MS = 2e3;
|
|
9342
|
-
var daemonSocketPath = () =>
|
|
9585
|
+
var daemonSocketPath = () => join24(herdHome(), "daemon.sock");
|
|
9343
9586
|
var Daemon = class {
|
|
9344
9587
|
constructor(options) {
|
|
9345
9588
|
this.options = options;
|
|
@@ -9551,7 +9794,7 @@ var Daemon = class {
|
|
|
9551
9794
|
|
|
9552
9795
|
// src/folders.ts
|
|
9553
9796
|
init_define_HERD_PACKAGE();
|
|
9554
|
-
import { existsSync as
|
|
9797
|
+
import { existsSync as existsSync17, lstatSync as lstatSync3, readlinkSync as readlinkSync2, rmSync as rmSync14, writeFileSync as writeFileSync14 } from "node:fs";
|
|
9555
9798
|
import { dirname as dirname13, resolve as resolve4 } from "node:path";
|
|
9556
9799
|
var FOLDER_WAIT_MS = 2 * 60 * 1e3;
|
|
9557
9800
|
var FOLDER_CHECK_MS = 30 * 1e3;
|
|
@@ -9563,7 +9806,7 @@ function linkOf(path) {
|
|
|
9563
9806
|
return null;
|
|
9564
9807
|
}
|
|
9565
9808
|
}
|
|
9566
|
-
function missingFolder(path, required, { exists =
|
|
9809
|
+
function missingFolder(path, required, { exists = existsSync17, linkTarget = linkOf } = {}) {
|
|
9567
9810
|
if (exists(path)) return null;
|
|
9568
9811
|
const target = linkTarget(path);
|
|
9569
9812
|
if (target) return { path, target };
|
|
@@ -9766,23 +10009,23 @@ init_define_HERD_PACKAGE();
|
|
|
9766
10009
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
9767
10010
|
import {
|
|
9768
10011
|
appendFileSync as appendFileSync2,
|
|
9769
|
-
existsSync as
|
|
10012
|
+
existsSync as existsSync18,
|
|
9770
10013
|
mkdirSync as mkdirSync18,
|
|
9771
10014
|
readFileSync as readFileSync17,
|
|
9772
10015
|
renameSync as renameSync6,
|
|
9773
10016
|
writeFileSync as writeFileSync15
|
|
9774
10017
|
} from "node:fs";
|
|
9775
|
-
import { isAbsolute as
|
|
9776
|
-
var LOCAL_SETTINGS =
|
|
10018
|
+
import { isAbsolute as isAbsolute4, join as join25 } from "node:path";
|
|
10019
|
+
var LOCAL_SETTINGS = join25(".claude", "settings.local.json");
|
|
9777
10020
|
var git3 = (cwd, ...args) => execFileSync6("git", ["-C", cwd, ...args], {
|
|
9778
10021
|
encoding: "utf8",
|
|
9779
10022
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9780
10023
|
}).trim();
|
|
9781
10024
|
var shellQuote2 = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9782
10025
|
function writeWorktreeSettings(worktree, settings, ownCommand) {
|
|
9783
|
-
const path =
|
|
10026
|
+
const path = join25(worktree, LOCAL_SETTINGS);
|
|
9784
10027
|
let current = {};
|
|
9785
|
-
if (
|
|
10028
|
+
if (existsSync18(path)) {
|
|
9786
10029
|
try {
|
|
9787
10030
|
const parsed = JSON.parse(readFileSync17(path, "utf8"));
|
|
9788
10031
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
@@ -9804,7 +10047,7 @@ function writeWorktreeSettings(worktree, settings, ownCommand) {
|
|
|
9804
10047
|
}
|
|
9805
10048
|
next.hooks = hooks;
|
|
9806
10049
|
}
|
|
9807
|
-
mkdirSync18(
|
|
10050
|
+
mkdirSync18(join25(worktree, ".claude"), { recursive: true });
|
|
9808
10051
|
const temporary = `${path}.herd-${process.pid}`;
|
|
9809
10052
|
writeFileSync15(temporary, `${JSON.stringify(next, null, 2)}
|
|
9810
10053
|
`);
|
|
@@ -9824,10 +10067,10 @@ function ignoreLocalSettings(worktree) {
|
|
|
9824
10067
|
} catch {
|
|
9825
10068
|
return;
|
|
9826
10069
|
}
|
|
9827
|
-
const info =
|
|
9828
|
-
const exclude =
|
|
10070
|
+
const info = join25(isAbsolute4(common) ? common : join25(worktree, common), "info");
|
|
10071
|
+
const exclude = join25(info, "exclude");
|
|
9829
10072
|
const line2 = "/.claude/settings.local.json";
|
|
9830
|
-
const existing =
|
|
10073
|
+
const existing = existsSync18(exclude) ? readFileSync17(exclude, "utf8") : "";
|
|
9831
10074
|
if (existing.split("\n").includes(line2)) return;
|
|
9832
10075
|
mkdirSync18(info, { recursive: true });
|
|
9833
10076
|
appendFileSync2(exclude, `${existing && !existing.endsWith("\n") ? "\n" : ""}${line2}
|
|
@@ -9872,7 +10115,7 @@ function loginShellEnv() {
|
|
|
9872
10115
|
|
|
9873
10116
|
// src/machine-checks.ts
|
|
9874
10117
|
init_define_HERD_PACKAGE();
|
|
9875
|
-
import { existsSync as
|
|
10118
|
+
import { existsSync as existsSync19, rmSync as rmSync15 } from "node:fs";
|
|
9876
10119
|
var REQUEST_POLL_MS = 2e3;
|
|
9877
10120
|
var MachineChecks = class {
|
|
9878
10121
|
constructor(options) {
|
|
@@ -9918,7 +10161,7 @@ var MachineChecks = class {
|
|
|
9918
10161
|
/** `friday start` asked for every agent to be activated again. */
|
|
9919
10162
|
takeRequest() {
|
|
9920
10163
|
const path = activationRequestPath(this.options.herdHome);
|
|
9921
|
-
if (!
|
|
10164
|
+
if (!existsSync19(path)) return;
|
|
9922
10165
|
rmSync15(path, { force: true });
|
|
9923
10166
|
this.activateChosen(true);
|
|
9924
10167
|
}
|
|
@@ -9970,15 +10213,15 @@ init_define_HERD_PACKAGE();
|
|
|
9970
10213
|
import { spawn as spawn11 } from "node:child_process";
|
|
9971
10214
|
import { mkdirSync as mkdirSync19, readdirSync as readdirSync8, rmSync as rmSync16 } from "node:fs";
|
|
9972
10215
|
import { connect as connect2 } from "node:net";
|
|
9973
|
-
import { join as
|
|
10216
|
+
import { join as join26 } from "node:path";
|
|
9974
10217
|
init_self();
|
|
9975
10218
|
import {
|
|
9976
10219
|
HOST_PROTOCOL,
|
|
9977
10220
|
hostVersion
|
|
9978
10221
|
} from "./terminal-host.mjs";
|
|
9979
|
-
var hostsDir = () =>
|
|
9980
|
-
var legacyHostSocketPath = () =>
|
|
9981
|
-
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`);
|
|
9982
10225
|
var speaksProtocol = (protocol) => protocol === HOST_PROTOCOL || protocol === HOST_PROTOCOL - 1;
|
|
9983
10226
|
var sleep6 = (ms2) => new Promise((resolve7) => setTimeout(resolve7, ms2));
|
|
9984
10227
|
function askHost(path, request) {
|
|
@@ -10029,7 +10272,7 @@ var HostTerminals = class {
|
|
|
10029
10272
|
names = readdirSync8(dir).filter((name) => name.endsWith(".sock"));
|
|
10030
10273
|
} catch {
|
|
10031
10274
|
}
|
|
10032
|
-
const paths = [legacyHostSocketPath(), ...names.map((name) =>
|
|
10275
|
+
const paths = [legacyHostSocketPath(), ...names.map((name) => join26(dir, name))];
|
|
10033
10276
|
const found = [];
|
|
10034
10277
|
for (const path of paths) {
|
|
10035
10278
|
try {
|
|
@@ -10268,7 +10511,7 @@ var UsageReporter = class {
|
|
|
10268
10511
|
init_define_HERD_PACKAGE();
|
|
10269
10512
|
init_src();
|
|
10270
10513
|
import { mkdirSync as mkdirSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync18 } from "node:fs";
|
|
10271
|
-
import { join as
|
|
10514
|
+
import { join as join33 } from "node:path";
|
|
10272
10515
|
|
|
10273
10516
|
// src/workspaces/codex.ts
|
|
10274
10517
|
init_define_HERD_PACKAGE();
|
|
@@ -10280,7 +10523,7 @@ init_define_HERD_PACKAGE();
|
|
|
10280
10523
|
init_define_HERD_PACKAGE();
|
|
10281
10524
|
init_src();
|
|
10282
10525
|
init_self();
|
|
10283
|
-
import { join as
|
|
10526
|
+
import { join as join27 } from "node:path";
|
|
10284
10527
|
import { z as z26 } from "zod";
|
|
10285
10528
|
function hasThread(events) {
|
|
10286
10529
|
return events.some(
|
|
@@ -10315,7 +10558,7 @@ function filesByMessage(events) {
|
|
|
10315
10558
|
}
|
|
10316
10559
|
return files;
|
|
10317
10560
|
}
|
|
10318
|
-
var FILES_FOLDER =
|
|
10561
|
+
var FILES_FOLDER = join27(".herd", "files");
|
|
10319
10562
|
function safeFileName(name) {
|
|
10320
10563
|
const chars = Array.from(name.replace(/[/\\\p{Cc}]/gu, "_").trim());
|
|
10321
10564
|
while (Buffer.byteLength(chars.join("")) > 200) chars.shift();
|
|
@@ -10548,11 +10791,11 @@ async function claimChange(ws, workspace, change) {
|
|
|
10548
10791
|
// src/workspaces/delivery.ts
|
|
10549
10792
|
init_define_HERD_PACKAGE();
|
|
10550
10793
|
import { randomBytes as randomBytes6, randomUUID as randomUUID6 } from "node:crypto";
|
|
10551
|
-
import { join as
|
|
10794
|
+
import { join as join31 } from "node:path";
|
|
10552
10795
|
|
|
10553
10796
|
// src/workspaces/repos.ts
|
|
10554
10797
|
init_define_HERD_PACKAGE();
|
|
10555
|
-
import { join as
|
|
10798
|
+
import { join as join28 } from "node:path";
|
|
10556
10799
|
var rootOf = (ws) => ws.options.root?.() ?? ws.options.config().root ?? defaultRoot();
|
|
10557
10800
|
var gitEnvOf = (ws) => ws.options.gitEnv?.() ?? loginShellEnv();
|
|
10558
10801
|
function knownRemotes(ws) {
|
|
@@ -10664,7 +10907,7 @@ async function addHints(ws, workspace, hints, handedOff) {
|
|
|
10664
10907
|
const added = await addWorktree({
|
|
10665
10908
|
root,
|
|
10666
10909
|
remote: cloneSource(hint.remote, knownRemotes(ws)),
|
|
10667
|
-
path:
|
|
10910
|
+
path: join28(workspace.folder, place.name),
|
|
10668
10911
|
branch,
|
|
10669
10912
|
...handedOff ? { continueFrom: hint.branch ?? branch } : {},
|
|
10670
10913
|
env: gitEnvOf(ws),
|
|
@@ -10716,14 +10959,14 @@ function handoffRepos(workspace) {
|
|
|
10716
10959
|
return workspace.worktrees.map((repo) => ({
|
|
10717
10960
|
folder: repo.folder,
|
|
10718
10961
|
branch: repo.branch,
|
|
10719
|
-
unpushed: hasUnpushed(
|
|
10962
|
+
unpushed: hasUnpushed(join28(workspace.folder, repo.folder))
|
|
10720
10963
|
}));
|
|
10721
10964
|
}
|
|
10722
10965
|
|
|
10723
10966
|
// src/workspaces/setup.ts
|
|
10724
10967
|
init_define_HERD_PACKAGE();
|
|
10725
10968
|
import { chmodSync as chmodSync4, mkdirSync as mkdirSync20, writeFileSync as writeFileSync16 } from "node:fs";
|
|
10726
|
-
import { join as
|
|
10969
|
+
import { join as join29 } from "node:path";
|
|
10727
10970
|
|
|
10728
10971
|
// src/hooks.ts
|
|
10729
10972
|
init_define_HERD_PACKAGE();
|
|
@@ -10756,7 +10999,7 @@ function configureClaude(ws, workspace) {
|
|
|
10756
10999
|
configureClaudeIn(workspace.folder, (line2) => ws.log(`${workspace.conversationId}: ${line2}`));
|
|
10757
11000
|
}
|
|
10758
11001
|
function configureClaudeIn(folder, log) {
|
|
10759
|
-
const herd = shellQuote2(
|
|
11002
|
+
const herd = shellQuote2(join29(herdPathDir(), "herd"));
|
|
10760
11003
|
try {
|
|
10761
11004
|
const written = writeWorktreeSettings(
|
|
10762
11005
|
folder,
|
|
@@ -10781,9 +11024,9 @@ function configureClaudeIn(folder, log) {
|
|
|
10781
11024
|
}
|
|
10782
11025
|
}
|
|
10783
11026
|
function herdPathDir() {
|
|
10784
|
-
const dir =
|
|
11027
|
+
const dir = join29(herdHome(), "bin");
|
|
10785
11028
|
mkdirSync20(dir, { recursive: true, mode: 448 });
|
|
10786
|
-
const shim =
|
|
11029
|
+
const shim = join29(dir, "herd");
|
|
10787
11030
|
writeFileSync16(shim, `#!/bin/sh
|
|
10788
11031
|
exec "${process.execPath}" "${herdBin}" "$@"
|
|
10789
11032
|
`);
|
|
@@ -10969,7 +11212,7 @@ async function followCheckIn(ws, workspace, reading) {
|
|
|
10969
11212
|
init_define_HERD_PACKAGE();
|
|
10970
11213
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
10971
11214
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync21 } from "node:fs";
|
|
10972
|
-
import { join as
|
|
11215
|
+
import { join as join30 } from "node:path";
|
|
10973
11216
|
function needsInvestigator(ws, workspace, reading) {
|
|
10974
11217
|
const allowed = ws.options.canInvestigate ? ws.options.canInvestigate() : workspace.agent === "claude-code";
|
|
10975
11218
|
if (!allowed || !reading || workspace.investigation) return false;
|
|
@@ -11134,9 +11377,9 @@ async function followInvestigation(ws, workspace) {
|
|
|
11134
11377
|
}
|
|
11135
11378
|
function saveScreen(ws, conversationId, screen, key) {
|
|
11136
11379
|
try {
|
|
11137
|
-
const dir =
|
|
11380
|
+
const dir = join30(herdHome(), "dialogs");
|
|
11138
11381
|
mkdirSync21(dir, { recursive: true });
|
|
11139
|
-
const file =
|
|
11382
|
+
const file = join30(dir, `${conversationId}-${Date.now()}.txt`);
|
|
11140
11383
|
appendFileSync3(file, `${screen.join("\n")}
|
|
11141
11384
|
|
|
11142
11385
|
Way out: ${key}
|
|
@@ -11341,7 +11584,7 @@ async function deliver(ws, conversationId, delivery) {
|
|
|
11341
11584
|
await ensureLeadTerminal(ws, workspace);
|
|
11342
11585
|
const codex = workspace.agent === "codex";
|
|
11343
11586
|
if (workspace.agent === "agy") {
|
|
11344
|
-
writeAgyFiles(workspace.folder, shellQuote2(
|
|
11587
|
+
writeAgyFiles(workspace.folder, shellQuote2(join31(herdPathDir(), "herd")));
|
|
11345
11588
|
} else if (!codex) configureClaude(ws, workspace);
|
|
11346
11589
|
const ownId = codex || workspace.agent === "agy";
|
|
11347
11590
|
if (!workspace.started) {
|
|
@@ -11489,7 +11732,7 @@ import {
|
|
|
11489
11732
|
statSync as statSync9,
|
|
11490
11733
|
writeFileSync as writeFileSync17
|
|
11491
11734
|
} from "node:fs";
|
|
11492
|
-
import { dirname as dirname14, join as
|
|
11735
|
+
import { dirname as dirname14, join as join32, sep as sep2 } from "node:path";
|
|
11493
11736
|
import { Readable } from "node:stream";
|
|
11494
11737
|
import { pipeline } from "node:stream/promises";
|
|
11495
11738
|
|
|
@@ -11503,7 +11746,7 @@ import { readFile } from "node:fs/promises";
|
|
|
11503
11746
|
init_define_HERD_PACKAGE();
|
|
11504
11747
|
init_src();
|
|
11505
11748
|
import { realpathSync as realpathSync9, statSync as statSync8 } from "node:fs";
|
|
11506
|
-
import { basename as
|
|
11749
|
+
import { basename as basename4, extname, isAbsolute as isAbsolute5, relative as relative2, resolve as resolve5 } from "node:path";
|
|
11507
11750
|
var TYPES = {
|
|
11508
11751
|
".png": "image/png",
|
|
11509
11752
|
".jpg": "image/jpeg",
|
|
@@ -11528,7 +11771,7 @@ var TYPES = {
|
|
|
11528
11771
|
var mediaTypeFor = (path) => TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
|
|
11529
11772
|
function isInside(root, path) {
|
|
11530
11773
|
const between = relative2(root, path);
|
|
11531
|
-
return between === "" || !between.startsWith("..") && !
|
|
11774
|
+
return between === "" || !between.startsWith("..") && !isAbsolute5(between);
|
|
11532
11775
|
}
|
|
11533
11776
|
function resolveAttachments(worktree, files) {
|
|
11534
11777
|
let root;
|
|
@@ -11558,7 +11801,7 @@ function resolveAttachments(worktree, files) {
|
|
|
11558
11801
|
}
|
|
11559
11802
|
resolved.push({
|
|
11560
11803
|
path: real4,
|
|
11561
|
-
name:
|
|
11804
|
+
name: basename4(file.path),
|
|
11562
11805
|
type: mediaTypeFor(real4),
|
|
11563
11806
|
size: stats.size,
|
|
11564
11807
|
...file.title ? { title: file.title } : {},
|
|
@@ -12491,10 +12734,10 @@ async function bringIn(ws, workspace, files) {
|
|
|
12491
12734
|
return listed;
|
|
12492
12735
|
}
|
|
12493
12736
|
async function download(ws, workspace, file) {
|
|
12494
|
-
const path =
|
|
12495
|
-
const target =
|
|
12737
|
+
const path = join32(FILES_FOLDER, safeFileName(file.id), safeFileName(file.name));
|
|
12738
|
+
const target = join32(workspace.folder, path);
|
|
12496
12739
|
if (statSync9(target, { throwIfNoEntry: false })?.size === file.size) return path;
|
|
12497
|
-
const herd =
|
|
12740
|
+
const herd = join32(workspace.folder, ".herd");
|
|
12498
12741
|
if (lstatSync4(herd, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
12499
12742
|
throw new Error("its folder leads outside the worktree");
|
|
12500
12743
|
}
|
|
@@ -12502,7 +12745,7 @@ async function download(ws, workspace, file) {
|
|
|
12502
12745
|
if (!realpathSync10(dirname14(target)).startsWith(realpathSync10(workspace.folder) + sep2)) {
|
|
12503
12746
|
throw new Error("its folder leads outside the worktree");
|
|
12504
12747
|
}
|
|
12505
|
-
writeFileSync17(
|
|
12748
|
+
writeFileSync17(join32(herd, ".gitignore"), "*\n");
|
|
12506
12749
|
const response = await ws.api(
|
|
12507
12750
|
`${ws.conversationPath(workspace.conversationId)}/files/${encodeURIComponent(file.id)}`
|
|
12508
12751
|
);
|
|
@@ -12792,7 +13035,7 @@ var Workspaces = class {
|
|
|
12792
13035
|
return this.driver;
|
|
12793
13036
|
}
|
|
12794
13037
|
// ---- State, kept on disk so a restarted daemon picks up where it was.
|
|
12795
|
-
statePath = () =>
|
|
13038
|
+
statePath = () => join33(herdHome(), "state.json");
|
|
12796
13039
|
load() {
|
|
12797
13040
|
try {
|
|
12798
13041
|
return stateSchema.parse(JSON.parse(readFileSync18(this.statePath(), "utf8"))).workspaces;
|
|
@@ -13021,7 +13264,7 @@ async function runDaemon(args) {
|
|
|
13021
13264
|
const folders = new FolderWatch({
|
|
13022
13265
|
check: () => machineFolderProblem(machineRoot(), convosPath(machineRoot())),
|
|
13023
13266
|
initial: missing,
|
|
13024
|
-
statusPath:
|
|
13267
|
+
statusPath: join34(herdHome(), "folder-unavailable.json"),
|
|
13025
13268
|
...process.env.HERD_FOLDER_CHECK_MS ? { everyMs: Number(process.env.HERD_FOLDER_CHECK_MS) } : {},
|
|
13026
13269
|
onChange: () => daemon?.foldersChanged()
|
|
13027
13270
|
});
|
|
@@ -13121,7 +13364,7 @@ async function runDaemon(args) {
|
|
|
13121
13364
|
env: (secret) => envFor(workspaces, secret),
|
|
13122
13365
|
// As for a conversation: Claude Code's hooks and status line, or agy's. Codex has none.
|
|
13123
13366
|
configure: (agent, folder) => {
|
|
13124
|
-
if (agent === "agy") writeAgyFiles(folder, shellQuote2(
|
|
13367
|
+
if (agent === "agy") writeAgyFiles(folder, shellQuote2(join34(herdPathDir(), "herd")));
|
|
13125
13368
|
else if (agent === "claude-code") configureClaudeIn(folder, log);
|
|
13126
13369
|
},
|
|
13127
13370
|
...shellCommand ? { shellCommand } : {},
|
|
@@ -13209,7 +13452,7 @@ async function runDaemon(args) {
|
|
|
13209
13452
|
capacity: () => sandbox.ok && !folders.problem ? runnerCapacity(daemon?.configuration().config?.builds) : 0,
|
|
13210
13453
|
// The repos builds have cloned here, as many as the service's hello takes, to put this runner
|
|
13211
13454
|
// first for their builds.
|
|
13212
|
-
repos: () => bareClones(
|
|
13455
|
+
repos: () => bareClones(join34(herdHome(), "builds", "clones")).map((repo) => repo.key).filter((key) => key.length <= 300).slice(0, 100),
|
|
13213
13456
|
runner: new BuildRunner({
|
|
13214
13457
|
root: () => machineRoot(),
|
|
13215
13458
|
gitEnv,
|
|
@@ -13267,10 +13510,10 @@ init_define_HERD_PACKAGE();
|
|
|
13267
13510
|
// src/agy-hooks.ts
|
|
13268
13511
|
init_define_HERD_PACKAGE();
|
|
13269
13512
|
import { readFileSync as readFileSync20 } from "node:fs";
|
|
13270
|
-
import { basename as
|
|
13513
|
+
import { basename as basename5 } from "node:path";
|
|
13271
13514
|
var text3 = (value) => typeof value === "string" ? value : "";
|
|
13272
13515
|
function lastResponse(transcriptPath) {
|
|
13273
|
-
if (!/^transcript.*\.jsonl$/.test(
|
|
13516
|
+
if (!/^transcript.*\.jsonl$/.test(basename5(transcriptPath))) return "";
|
|
13274
13517
|
let lines;
|
|
13275
13518
|
try {
|
|
13276
13519
|
lines = readFileSync20(transcriptPath, "utf8").split("\n");
|
|
@@ -13396,8 +13639,8 @@ init_define_HERD_PACKAGE();
|
|
|
13396
13639
|
import { spawn as spawn12 } from "node:child_process";
|
|
13397
13640
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
13398
13641
|
import { connect as connect3 } from "node:net";
|
|
13399
|
-
import { homedir as
|
|
13400
|
-
import { join as
|
|
13642
|
+
import { homedir as homedir8 } from "node:os";
|
|
13643
|
+
import { join as join35 } from "node:path";
|
|
13401
13644
|
var MAX_INPUT = 64 * 1024;
|
|
13402
13645
|
var REPORT_TIMEOUT_MS = 300;
|
|
13403
13646
|
var OWN_COMMAND_TIMEOUT_MS = 5e3;
|
|
@@ -13431,10 +13674,10 @@ function agyStateFrom(input) {
|
|
|
13431
13674
|
const state = input?.agent_state;
|
|
13432
13675
|
return state === "working" || state === "idle" ? state : null;
|
|
13433
13676
|
}
|
|
13434
|
-
function ownAgyStatusLine(home =
|
|
13677
|
+
function ownAgyStatusLine(home = homedir8()) {
|
|
13435
13678
|
try {
|
|
13436
13679
|
const settings = JSON.parse(
|
|
13437
|
-
readFileSync21(
|
|
13680
|
+
readFileSync21(join35(home, ".gemini", "antigravity-cli", "settings.json"), "utf8")
|
|
13438
13681
|
);
|
|
13439
13682
|
const command = settings.statusLine?.command;
|
|
13440
13683
|
if (typeof command !== "string" || !command.trim()) return null;
|
|
@@ -13453,8 +13696,8 @@ function commandIn(path) {
|
|
|
13453
13696
|
return null;
|
|
13454
13697
|
}
|
|
13455
13698
|
}
|
|
13456
|
-
function ownStatusLine(projectDir, home =
|
|
13457
|
-
return commandIn(
|
|
13699
|
+
function ownStatusLine(projectDir, home = homedir8()) {
|
|
13700
|
+
return commandIn(join35(projectDir, ".claude", "settings.json")) ?? commandIn(join35(home, ".claude", "settings.json"));
|
|
13458
13701
|
}
|
|
13459
13702
|
function runOwnCommand(command, input, cwd, timeoutMs = OWN_COMMAND_TIMEOUT_MS) {
|
|
13460
13703
|
return new Promise((resolve7) => {
|
|
@@ -13876,7 +14119,7 @@ async function runTerminalHost(rest) {
|
|
|
13876
14119
|
|
|
13877
14120
|
// src/commands/repo.ts
|
|
13878
14121
|
init_define_HERD_PACKAGE();
|
|
13879
|
-
import { join as
|
|
14122
|
+
import { join as join36 } from "node:path";
|
|
13880
14123
|
import { z as z33 } from "zod";
|
|
13881
14124
|
var REPO_HELP = `Usage: herd repo <url or owner/name> [folder]
|
|
13882
14125
|
|
|
@@ -13964,7 +14207,7 @@ async function runRepo(rest) {
|
|
|
13964
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>.`
|
|
13965
14208
|
);
|
|
13966
14209
|
}
|
|
13967
|
-
const path =
|
|
14210
|
+
const path = join36(here.folder, place.name);
|
|
13968
14211
|
const recorded = here.recorded.some((repo) => repo.folder === place.name);
|
|
13969
14212
|
if (place.existing && recorded) {
|
|
13970
14213
|
const branch = here.recorded.find((repo) => repo.folder === place.name)?.branch ?? here.branch;
|