@herdingchats/friday 0.1.1 → 0.1.525
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 +275 -105
- 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.525", gitHead: "cbd227dfdbed52c13e4cb3a2b388d6e7d4a6866e" };
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
@@ -513,6 +513,9 @@ import { z as z8 } from "zod";
|
|
|
513
513
|
function agentShortName(cli) {
|
|
514
514
|
return AGENTS[cli]?.short ?? cli;
|
|
515
515
|
}
|
|
516
|
+
function shareOf(harness) {
|
|
517
|
+
return harness?.share ?? (harness?.enabled === false ? "off" : "some");
|
|
518
|
+
}
|
|
516
519
|
function runnerCapacity(setting) {
|
|
517
520
|
if (setting === void 0 || setting === true) return 1;
|
|
518
521
|
return setting.max;
|
|
@@ -528,7 +531,7 @@ function workerKey(machineId, cli) {
|
|
|
528
531
|
function machineShortName(name) {
|
|
529
532
|
return name.trim().toLowerCase().replace(/\.local$/, "").replace(/\s+/g, "-");
|
|
530
533
|
}
|
|
531
|
-
var CAPABILITIES, capabilitySchema, PLATFORMS, platformSchema, TOOLS, toolSchema, CAPABILITY_WORDING, AGENTS, PHRASE_MAX, PHRASES_MAX, ABOUT_MAX, phrase, phrases, harnessConfigSchema, BUILDS_MAX, buildsSettingSchema, CONFIG_REPOS_MAX, configRepoSchema, herdConfigSchema, HERD_CONFIG_TYPE, PROFILE_LEVELS, profileEntrySchema, workerSchema;
|
|
534
|
+
var CAPABILITIES, capabilitySchema, PLATFORMS, platformSchema, TOOLS, toolSchema, CAPABILITY_WORDING, AGENTS, PHRASE_MAX, PHRASES_MAX, ABOUT_MAX, phrase, phrases, SHARES, shareSchema, SHARE_LABELS, harnessConfigSchema, BUILDS_MAX, buildsSettingSchema, CONFIG_REPOS_MAX, configRepoSchema, herdConfigSchema, HERD_CONFIG_TYPE, PROFILE_LEVELS, profileEntrySchema, workerSchema;
|
|
532
535
|
var init_workers = __esm({
|
|
533
536
|
"../common-chat/src/workers.ts"() {
|
|
534
537
|
"use strict";
|
|
@@ -566,7 +569,17 @@ var init_workers = __esm({
|
|
|
566
569
|
ABOUT_MAX = 500;
|
|
567
570
|
phrase = z8.string().trim().min(1).max(PHRASE_MAX, `at most ${PHRASE_MAX} characters`);
|
|
568
571
|
phrases = z8.array(phrase).max(PHRASES_MAX, `at most ${PHRASES_MAX} phrases`);
|
|
572
|
+
SHARES = ["off", "little", "some", "lot"];
|
|
573
|
+
shareSchema = z8.enum(SHARES);
|
|
574
|
+
SHARE_LABELS = {
|
|
575
|
+
off: "off",
|
|
576
|
+
little: "a little",
|
|
577
|
+
some: "some",
|
|
578
|
+
lot: "a lot"
|
|
579
|
+
};
|
|
569
580
|
harnessConfigSchema = z8.object({
|
|
581
|
+
share: shareSchema.optional(),
|
|
582
|
+
/** Read as `share: "off"` when false, from files written before shares. */
|
|
570
583
|
enabled: z8.boolean().optional(),
|
|
571
584
|
only: phrases.optional(),
|
|
572
585
|
great: phrases.optional(),
|
|
@@ -619,10 +632,12 @@ var init_workers = __esm({
|
|
|
619
632
|
/** Each harness by its short name, such as claude, codex or gemini. */
|
|
620
633
|
harnesses?: Record<string, {
|
|
621
634
|
/**
|
|
622
|
-
*
|
|
623
|
-
*
|
|
635
|
+
* How much of this agent its owner has, as chosen in \`friday start\`: "off", "little", "some"
|
|
636
|
+
* or "lot". Routing gives each its share of the work several agents suit equally, weighted
|
|
637
|
+
* 1, 2 and 4. "off" means it's no worker here. Without a configuration file, every installed
|
|
638
|
+
* agent is at "some".
|
|
624
639
|
*/
|
|
625
|
-
|
|
640
|
+
share?: "off" | "little" | "some" | "lot";
|
|
626
641
|
/** It takes only work about one of these, however good it is at other work. */
|
|
627
642
|
only?: string[];
|
|
628
643
|
/**
|
|
@@ -664,6 +679,8 @@ export default {
|
|
|
664
679
|
/** What it's great, good and okay at, and can't do: its agent's defaults with its owner's word. */
|
|
665
680
|
profile: z8.array(profileEntrySchema),
|
|
666
681
|
priority: z8.number().int(),
|
|
682
|
+
/** How much of its agent its owner has. Servers from before shares send none, which is "some". */
|
|
683
|
+
share: shareSchema.default("some"),
|
|
667
684
|
enabled: z8.boolean(),
|
|
668
685
|
/** Whether its agent checked in from herd's terminal on this version, so it's offered work. */
|
|
669
686
|
activated: z8.boolean(),
|
|
@@ -1002,6 +1019,7 @@ var init_events = __esm({
|
|
|
1002
1019
|
init_prompts();
|
|
1003
1020
|
init_usage();
|
|
1004
1021
|
init_work_objects();
|
|
1022
|
+
init_workers();
|
|
1005
1023
|
routeSchema = z13.enum(["direct", "mention", "joined"]);
|
|
1006
1024
|
deliveryModeSchema = z13.enum(["interrupt", "defer"]);
|
|
1007
1025
|
eventSourceSchema = z13.enum(["slack", "discord", "web", "api", "cli", "mcp", "system"]);
|
|
@@ -1056,7 +1074,18 @@ var init_events = __esm({
|
|
|
1056
1074
|
// The repos the work hinted, and the workers whose machine had cloned every one, which go first
|
|
1057
1075
|
// among workers that tie on fit and priority.
|
|
1058
1076
|
hints: z13.array(nonEmpty6).optional(),
|
|
1059
|
-
warm: z13.array(nonEmpty6).optional()
|
|
1077
|
+
warm: z13.array(nonEmpty6).optional(),
|
|
1078
|
+
// Each ordered worker's share, how its weekly usage is pacing, and the weight that gives it. The
|
|
1079
|
+
// first worker was drawn by weight from those in `drawn`, which suited the work equally.
|
|
1080
|
+
shares: z13.array(
|
|
1081
|
+
z13.object({
|
|
1082
|
+
key: nonEmpty6,
|
|
1083
|
+
share: shareSchema,
|
|
1084
|
+
pacing: z13.enum(["ahead", "even", "under", "unknown"]),
|
|
1085
|
+
weight: z13.number().nonnegative()
|
|
1086
|
+
})
|
|
1087
|
+
).optional(),
|
|
1088
|
+
drawn: z13.array(nonEmpty6).optional()
|
|
1060
1089
|
});
|
|
1061
1090
|
conversationEventSchema = z13.discriminatedUnion("type", [
|
|
1062
1091
|
event("message.created", {
|
|
@@ -3171,9 +3200,9 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
3171
3200
|
// src/friday/main.ts
|
|
3172
3201
|
init_define_HERD_PACKAGE();
|
|
3173
3202
|
import { spawn as spawn5 } from "node:child_process";
|
|
3174
|
-
import { existsSync as
|
|
3203
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
|
|
3175
3204
|
import { homedir as homedir4 } from "node:os";
|
|
3176
|
-
import { dirname as
|
|
3205
|
+
import { dirname as dirname9, join as join13 } from "node:path";
|
|
3177
3206
|
import { z as z22 } from "zod";
|
|
3178
3207
|
|
|
3179
3208
|
// src/auth.ts
|
|
@@ -3369,6 +3398,89 @@ async function apiFetch(path, init = {}) {
|
|
|
3369
3398
|
return response;
|
|
3370
3399
|
}
|
|
3371
3400
|
|
|
3401
|
+
// src/cli-flags.ts
|
|
3402
|
+
init_define_HERD_PACKAGE();
|
|
3403
|
+
var HERD_FLAGS = {
|
|
3404
|
+
login: [],
|
|
3405
|
+
logout: [],
|
|
3406
|
+
daemon: ["--repo"],
|
|
3407
|
+
attach: [],
|
|
3408
|
+
builds: [],
|
|
3409
|
+
inbox: [],
|
|
3410
|
+
send: ["--json"],
|
|
3411
|
+
automations: ["--json", "--wait"],
|
|
3412
|
+
channels: [],
|
|
3413
|
+
watch: [],
|
|
3414
|
+
unwatch: [],
|
|
3415
|
+
watching: [],
|
|
3416
|
+
conversations: ["--about", "--json"],
|
|
3417
|
+
github: ["--on", "--off", "--json"],
|
|
3418
|
+
setup: ["--json"],
|
|
3419
|
+
repo: [],
|
|
3420
|
+
repos: ["--json"],
|
|
3421
|
+
work: ["--kind", "--title", "--status", "--detail", "--phase", "--made", "--remove", "--json"],
|
|
3422
|
+
check: [],
|
|
3423
|
+
handoff: [],
|
|
3424
|
+
delegate: ["--repo"],
|
|
3425
|
+
tell: ["--json"],
|
|
3426
|
+
workers: ["--json", "--types"],
|
|
3427
|
+
status: ["--clear"],
|
|
3428
|
+
title: [],
|
|
3429
|
+
handled: [],
|
|
3430
|
+
pass: [],
|
|
3431
|
+
exit: [],
|
|
3432
|
+
persona: []
|
|
3433
|
+
};
|
|
3434
|
+
var FRIDAY_FLAGS = {
|
|
3435
|
+
start: ["--agents"],
|
|
3436
|
+
stop: [],
|
|
3437
|
+
status: [],
|
|
3438
|
+
logs: ["--follow", "-f", "-n"]
|
|
3439
|
+
};
|
|
3440
|
+
var HERD_OWN_HELP = /* @__PURE__ */ new Set([
|
|
3441
|
+
"builds",
|
|
3442
|
+
"send",
|
|
3443
|
+
"automations",
|
|
3444
|
+
"watch",
|
|
3445
|
+
"unwatch",
|
|
3446
|
+
"watching",
|
|
3447
|
+
"github",
|
|
3448
|
+
"repo",
|
|
3449
|
+
"repos",
|
|
3450
|
+
"work",
|
|
3451
|
+
"check",
|
|
3452
|
+
"handoff",
|
|
3453
|
+
"delegate",
|
|
3454
|
+
"tell",
|
|
3455
|
+
"workers",
|
|
3456
|
+
"status",
|
|
3457
|
+
"title"
|
|
3458
|
+
]);
|
|
3459
|
+
var FLAG = /^--?[A-Za-z][\w-]*(=.*)?$/;
|
|
3460
|
+
function unknownFlag(program, command, args, known) {
|
|
3461
|
+
for (const arg of args) {
|
|
3462
|
+
if (arg === "--") break;
|
|
3463
|
+
if (!FLAG.test(arg)) continue;
|
|
3464
|
+
const name = arg.split("=")[0] ?? arg;
|
|
3465
|
+
if (name === "--help" || name === "-h" || known.includes(name)) continue;
|
|
3466
|
+
const takes = known.length ? `It takes ${known.join(", ")}.` : "It takes no options.";
|
|
3467
|
+
return `${program} ${command} doesn't have a "${name}" option, so nothing ran. ${takes} See ${program} ${command} --help.`;
|
|
3468
|
+
}
|
|
3469
|
+
return null;
|
|
3470
|
+
}
|
|
3471
|
+
var asksForHelp = (args) => args.some((arg) => arg === "--help" || arg === "-h");
|
|
3472
|
+
function commandHelp(overview, command) {
|
|
3473
|
+
const lines = overview.split("\n");
|
|
3474
|
+
const start = lines.findIndex((line2) => new RegExp(`^ ${command}(\\s|$)`).test(line2));
|
|
3475
|
+
if (start === -1) return null;
|
|
3476
|
+
const out = [lines[start] ?? ""];
|
|
3477
|
+
for (const line2 of lines.slice(start + 1)) {
|
|
3478
|
+
if (!/^ {4,}\S/.test(line2)) break;
|
|
3479
|
+
out.push(line2);
|
|
3480
|
+
}
|
|
3481
|
+
return out.join("\n");
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3372
3484
|
// src/commands/login.ts
|
|
3373
3485
|
init_define_HERD_PACKAGE();
|
|
3374
3486
|
import { spawn } from "node:child_process";
|
|
@@ -3402,9 +3514,9 @@ async function runLogout() {
|
|
|
3402
3514
|
init_define_HERD_PACKAGE();
|
|
3403
3515
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
3404
3516
|
import { randomUUID } from "node:crypto";
|
|
3405
|
-
import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3517
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3406
3518
|
import { homedir as homedir3, hostname } from "node:os";
|
|
3407
|
-
import { basename, join as join5, resolve } from "node:path";
|
|
3519
|
+
import { basename, dirname as dirname3, join as join5, resolve } from "node:path";
|
|
3408
3520
|
import { z as z18 } from "zod";
|
|
3409
3521
|
|
|
3410
3522
|
// src/machine-facts.ts
|
|
@@ -3649,7 +3761,7 @@ var ADAPTERS = [
|
|
|
3649
3761
|
{ cli: "agy", capabilities: ["image_generation"] }
|
|
3650
3762
|
];
|
|
3651
3763
|
function chosenClis(clis, config) {
|
|
3652
|
-
return clis.filter((cli) => config?.harnesses?.[agentShortName(cli.name)]
|
|
3764
|
+
return clis.filter((cli) => shareOf(config?.harnesses?.[agentShortName(cli.name)]) !== "off");
|
|
3653
3765
|
}
|
|
3654
3766
|
var runCommand = (command, args) => {
|
|
3655
3767
|
try {
|
|
@@ -4111,6 +4223,20 @@ function readRoot() {
|
|
|
4111
4223
|
function setRoot(path) {
|
|
4112
4224
|
return save({ ...loadConfig(), root: resolve(path) });
|
|
4113
4225
|
}
|
|
4226
|
+
async function waitForRoot(root, {
|
|
4227
|
+
pollMs = 2e3,
|
|
4228
|
+
exists = existsSync2,
|
|
4229
|
+
sleep: sleep8 = (ms) => new Promise((done) => setTimeout(done, ms)),
|
|
4230
|
+
log = (line2) => console.log(line2)
|
|
4231
|
+
} = {}) {
|
|
4232
|
+
const parent = dirname3(root);
|
|
4233
|
+
if (exists(parent)) return;
|
|
4234
|
+
log(
|
|
4235
|
+
`Waiting for ${parent}, which holds this machine's root, to be there, as when its drive mounts.`
|
|
4236
|
+
);
|
|
4237
|
+
while (!exists(parent)) await sleep8(pollMs);
|
|
4238
|
+
log(`${parent} is there.`);
|
|
4239
|
+
}
|
|
4114
4240
|
var machineRoot = () => readRoot() ?? defaultRoot();
|
|
4115
4241
|
function describeRepo(path) {
|
|
4116
4242
|
const full = resolve(path);
|
|
@@ -4267,15 +4393,15 @@ async function readSetup(fetch2, init) {
|
|
|
4267
4393
|
// src/friday/agents.ts
|
|
4268
4394
|
init_define_HERD_PACKAGE();
|
|
4269
4395
|
init_src();
|
|
4270
|
-
import { existsSync as
|
|
4271
|
-
import { dirname as
|
|
4396
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4397
|
+
import { dirname as dirname5, join as join7 } from "node:path";
|
|
4272
4398
|
|
|
4273
4399
|
// src/activation.ts
|
|
4274
4400
|
init_define_HERD_PACKAGE();
|
|
4275
4401
|
init_src();
|
|
4276
4402
|
import { randomBytes as randomBytes4, randomUUID as randomUUID3 } from "node:crypto";
|
|
4277
4403
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4278
|
-
import { dirname as
|
|
4404
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
4279
4405
|
import { z as z21 } from "zod";
|
|
4280
4406
|
|
|
4281
4407
|
// src/permissions.ts
|
|
@@ -4287,7 +4413,7 @@ import { z as z20 } from "zod";
|
|
|
4287
4413
|
// src/local-socket.ts
|
|
4288
4414
|
init_define_HERD_PACKAGE();
|
|
4289
4415
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
4290
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
4416
|
+
import { chmodSync as chmodSync2, existsSync as existsSync3, rmSync as rmSync4 } from "node:fs";
|
|
4291
4417
|
import { connect, createServer as createServer2 } from "node:net";
|
|
4292
4418
|
import { z as z19 } from "zod";
|
|
4293
4419
|
var localRequestSchema = z19.object({
|
|
@@ -4423,7 +4549,7 @@ var LocalSocket = class {
|
|
|
4423
4549
|
}
|
|
4424
4550
|
}
|
|
4425
4551
|
async listen() {
|
|
4426
|
-
if (
|
|
4552
|
+
if (existsSync3(this.path)) rmSync4(this.path);
|
|
4427
4553
|
this.server = createServer2((socket) => this.serve(socket));
|
|
4428
4554
|
await new Promise((resolve6, reject) => {
|
|
4429
4555
|
this.server?.once("error", reject);
|
|
@@ -4505,7 +4631,7 @@ function askDaemon(path, request) {
|
|
|
4505
4631
|
}
|
|
4506
4632
|
function isAnswering(path) {
|
|
4507
4633
|
return new Promise((resolve6) => {
|
|
4508
|
-
if (!
|
|
4634
|
+
if (!existsSync3(path)) return resolve6(false);
|
|
4509
4635
|
const socket = connect(path);
|
|
4510
4636
|
socket.on("connect", () => {
|
|
4511
4637
|
socket.destroy();
|
|
@@ -4744,7 +4870,7 @@ var Activator = class {
|
|
|
4744
4870
|
}
|
|
4745
4871
|
record(agent, record) {
|
|
4746
4872
|
const agents = { ...readActivations(this.path), [agent]: record };
|
|
4747
|
-
mkdirSync4(
|
|
4873
|
+
mkdirSync4(dirname4(this.path), { recursive: true, mode: 448 });
|
|
4748
4874
|
const temporary = `${this.path}.${process.pid}`;
|
|
4749
4875
|
writeFileSync4(temporary, JSON.stringify({ agents }, null, 2), { mode: 384 });
|
|
4750
4876
|
renameSync2(temporary, this.path);
|
|
@@ -4956,36 +5082,42 @@ var ACTIVATION_READ_MS = 500;
|
|
|
4956
5082
|
var label2 = (cli) => AGENTS[cli]?.label ?? cli;
|
|
4957
5083
|
function agentChoices(installed, saved) {
|
|
4958
5084
|
const harnesses = saved?.harnesses ?? {};
|
|
4959
|
-
const chosenBefore = Object.values(harnesses).some(
|
|
5085
|
+
const chosenBefore = Object.values(harnesses).some(
|
|
5086
|
+
(harness) => harness.enabled !== void 0 || harness.share !== void 0
|
|
5087
|
+
);
|
|
4960
5088
|
return ADAPTERS.map(({ cli }) => {
|
|
4961
5089
|
const isInstalled = installed.includes(cli);
|
|
4962
|
-
const
|
|
5090
|
+
const harness = harnesses[agentShortName(cli)];
|
|
5091
|
+
const before = harness?.share ?? (harness?.enabled ? "some" : "off");
|
|
4963
5092
|
return {
|
|
4964
5093
|
cli,
|
|
4965
5094
|
label: label2(cli),
|
|
4966
5095
|
installed: isInstalled,
|
|
4967
|
-
|
|
5096
|
+
share: chosenBefore ? before : isInstalled ? "some" : "off"
|
|
4968
5097
|
};
|
|
4969
5098
|
});
|
|
4970
5099
|
}
|
|
4971
5100
|
function parseAgents(value) {
|
|
4972
|
-
const
|
|
4973
|
-
const
|
|
5101
|
+
const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
|
|
5102
|
+
const shares = Object.fromEntries(ADAPTERS.map(({ cli }) => [cli, "off"]));
|
|
4974
5103
|
const unknown = [];
|
|
4975
|
-
for (const
|
|
5104
|
+
for (const part of parts) {
|
|
5105
|
+
const [name = "", share = "some"] = part.split("=", 2).map((piece) => piece.trim());
|
|
4976
5106
|
const adapter = ADAPTERS.find(
|
|
4977
5107
|
({ cli }) => cli === name || agentShortName(cli) === name || label2(cli).toLowerCase() === name
|
|
4978
5108
|
);
|
|
4979
|
-
if (adapter
|
|
4980
|
-
|
|
5109
|
+
if (adapter && SHARES.includes(share)) {
|
|
5110
|
+
shares[adapter.cli] = share;
|
|
5111
|
+
} else unknown.push(part);
|
|
4981
5112
|
}
|
|
4982
|
-
return unknown.length > 0 ? { unknown } : {
|
|
5113
|
+
return unknown.length > 0 ? { unknown } : { shares };
|
|
4983
5114
|
}
|
|
4984
|
-
function withChoice(config,
|
|
5115
|
+
function withChoice(config, shares) {
|
|
4985
5116
|
const harnesses = { ...config?.harnesses ?? {} };
|
|
4986
5117
|
for (const { cli } of ADAPTERS) {
|
|
4987
5118
|
const short = agentShortName(cli);
|
|
4988
|
-
|
|
5119
|
+
const { enabled: _, ...rest } = harnesses[short] ?? {};
|
|
5120
|
+
harnesses[short] = { ...rest, share: shares[cli] ?? "off" };
|
|
4989
5121
|
}
|
|
4990
5122
|
return { ...config, harnesses };
|
|
4991
5123
|
}
|
|
@@ -4994,9 +5126,10 @@ function configSource(config) {
|
|
|
4994
5126
|
export default ${JSON.stringify(config, null, 2)};
|
|
4995
5127
|
`;
|
|
4996
5128
|
}
|
|
4997
|
-
var sameChoice = (config,
|
|
4998
|
-
|
|
4999
|
-
);
|
|
5129
|
+
var sameChoice = (config, shares) => ADAPTERS.every(({ cli }) => {
|
|
5130
|
+
const harness = config?.harnesses?.[agentShortName(cli)];
|
|
5131
|
+
return harness?.enabled === void 0 && harness?.share === (shares[cli] ?? "off");
|
|
5132
|
+
});
|
|
5000
5133
|
async function chooseAgents(options, print) {
|
|
5001
5134
|
const [installed, loaded] = await Promise.all([options.installed(), options.load()]);
|
|
5002
5135
|
if (loaded.status === "error") {
|
|
@@ -5006,14 +5139,15 @@ async function chooseAgents(options, print) {
|
|
|
5006
5139
|
const saved = loaded.status === "ok" ? loaded.config : void 0;
|
|
5007
5140
|
const choices = agentChoices(installed, saved);
|
|
5008
5141
|
const asked = options.given ? null : await (options.pick?.(choices) ?? Promise.resolve(null));
|
|
5009
|
-
const
|
|
5010
|
-
choices.
|
|
5011
|
-
if (!sameChoice(saved,
|
|
5012
|
-
mkdirSync5(
|
|
5013
|
-
writeFileSync5(options.configPath, configSource(withChoice(saved,
|
|
5142
|
+
const shares = options.given ?? asked ?? // No terminal: what was chosen before, or what's installed.
|
|
5143
|
+
Object.fromEntries(choices.map((choice) => [choice.cli, choice.share]));
|
|
5144
|
+
if (!sameChoice(saved, shares) || !existsSync4(options.configPath)) {
|
|
5145
|
+
mkdirSync5(dirname5(options.configPath), { recursive: true, mode: 448 });
|
|
5146
|
+
writeFileSync5(options.configPath, configSource(withChoice(saved, shares)), { mode: 384 });
|
|
5014
5147
|
}
|
|
5148
|
+
const chosen = ADAPTERS.map(({ cli }) => cli).filter((cli) => (shares[cli] ?? "off") !== "off");
|
|
5015
5149
|
print(
|
|
5016
|
-
chosen.length > 0 ? `This machine runs ${chosen.map(label2).join(", ")}.` : "This machine runs no agents, so it takes no work. Run this again to choose some."
|
|
5150
|
+
chosen.length > 0 ? `This machine runs ${chosen.map((cli) => `${label2(cli)} (${SHARE_LABELS[shares[cli]]})`).join(", ")}.` : "This machine runs no agents, so it takes no work. Run this again to choose some."
|
|
5017
5151
|
);
|
|
5018
5152
|
return chosen;
|
|
5019
5153
|
}
|
|
@@ -5081,7 +5215,7 @@ init_define_HERD_PACKAGE();
|
|
|
5081
5215
|
import { spawnSync } from "node:child_process";
|
|
5082
5216
|
import {
|
|
5083
5217
|
closeSync as closeSync2,
|
|
5084
|
-
existsSync as
|
|
5218
|
+
existsSync as existsSync5,
|
|
5085
5219
|
mkdirSync as mkdirSync6,
|
|
5086
5220
|
openSync as openSync2,
|
|
5087
5221
|
readFileSync as readFileSync6,
|
|
@@ -5089,7 +5223,7 @@ import {
|
|
|
5089
5223
|
statSync as statSync5,
|
|
5090
5224
|
writeFileSync as writeFileSync6
|
|
5091
5225
|
} from "node:fs";
|
|
5092
|
-
import { dirname as
|
|
5226
|
+
import { dirname as dirname6, join as join8, resolve as resolve2 } from "node:path";
|
|
5093
5227
|
var PACKAGE_NAME = "@herdingchats/friday";
|
|
5094
5228
|
function fridayPaths(herdHome2) {
|
|
5095
5229
|
const logs = join8(herdHome2, "logs");
|
|
@@ -5118,8 +5252,8 @@ function alive2(pid) {
|
|
|
5118
5252
|
}
|
|
5119
5253
|
}
|
|
5120
5254
|
function takeLock(path, staleMs = 10 * 6e4) {
|
|
5121
|
-
mkdirSync6(
|
|
5122
|
-
if (
|
|
5255
|
+
mkdirSync6(dirname6(path), { recursive: true });
|
|
5256
|
+
if (existsSync5(path)) {
|
|
5123
5257
|
const holder2 = Number(readFileSync6(path, "utf8").trim());
|
|
5124
5258
|
const old = Date.now() - statSync5(path).mtimeMs > staleMs;
|
|
5125
5259
|
if (old || !alive2(holder2)) rmSync6(path, { force: true });
|
|
@@ -5169,7 +5303,7 @@ function writeRoot(paths, root) {
|
|
|
5169
5303
|
}
|
|
5170
5304
|
function insideWorkTree(folder) {
|
|
5171
5305
|
let dir = resolve2(folder);
|
|
5172
|
-
while (!
|
|
5306
|
+
while (!existsSync5(dir) && dirname6(dir) !== dir) dir = dirname6(dir);
|
|
5173
5307
|
const result = spawnSync("git", ["-C", dir, "rev-parse", "--show-toplevel"], {
|
|
5174
5308
|
encoding: "utf8",
|
|
5175
5309
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -5189,6 +5323,9 @@ var VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
|
5189
5323
|
|
|
5190
5324
|
// src/friday/picker.ts
|
|
5191
5325
|
init_define_HERD_PACKAGE();
|
|
5326
|
+
init_src();
|
|
5327
|
+
var LESS = /* @__PURE__ */ new Set(["-", "_", "\x1B[D"]);
|
|
5328
|
+
var MORE = /* @__PURE__ */ new Set(["+", "=", "\x1B[C"]);
|
|
5192
5329
|
function pickerKey(state, key) {
|
|
5193
5330
|
const { choices, cursor } = state;
|
|
5194
5331
|
if (key === "" || key === "\x1B") return null;
|
|
@@ -5197,22 +5334,28 @@ function pickerKey(state, key) {
|
|
|
5197
5334
|
return { ...state, cursor: (cursor + choices.length - 1) % choices.length };
|
|
5198
5335
|
}
|
|
5199
5336
|
if (key === "\x1B[B" || key === "j") return { ...state, cursor: (cursor + 1) % choices.length };
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
)
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5337
|
+
const step = LESS.has(key) ? -1 : MORE.has(key) ? 1 : 0;
|
|
5338
|
+
if (step === 0) return state;
|
|
5339
|
+
return {
|
|
5340
|
+
...state,
|
|
5341
|
+
choices: choices.map((choice, at) => {
|
|
5342
|
+
if (at !== cursor) return choice;
|
|
5343
|
+
const next = Math.min(SHARES.length - 1, Math.max(0, SHARES.indexOf(choice.share) + step));
|
|
5344
|
+
return { ...choice, share: SHARES[next] ?? choice.share };
|
|
5345
|
+
})
|
|
5346
|
+
};
|
|
5209
5347
|
}
|
|
5210
5348
|
function pickerLines(state) {
|
|
5349
|
+
const width = Math.max(...state.choices.map((choice) => choice.label.length));
|
|
5350
|
+
const cells = SHARES.length - 1;
|
|
5211
5351
|
return [
|
|
5212
|
-
"
|
|
5213
|
-
...state.choices.map(
|
|
5214
|
-
|
|
5215
|
-
|
|
5352
|
+
"How much of each agent do you have on this computer? (\u2191/\u2193 to move, -/+ to change, Enter to accept)",
|
|
5353
|
+
...state.choices.map((choice, at) => {
|
|
5354
|
+
const filled = SHARES.indexOf(choice.share);
|
|
5355
|
+
const bar = "\u25A0".repeat(filled) + "\u25A1".repeat(cells - filled);
|
|
5356
|
+
const missing = choice.installed ? "" : " (not installed here)";
|
|
5357
|
+
return `${at === state.cursor ? "\u276F" : " "} ${choice.label.padEnd(width)} ${bar} ${SHARE_LABELS[choice.share]}${missing}`;
|
|
5358
|
+
})
|
|
5216
5359
|
];
|
|
5217
5360
|
}
|
|
5218
5361
|
function pickInTerminal(choices, input = process.stdin, output2 = process.stdout) {
|
|
@@ -5229,18 +5372,18 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
|
|
|
5229
5372
|
input.setRawMode(true);
|
|
5230
5373
|
input.resume();
|
|
5231
5374
|
draw(true);
|
|
5232
|
-
const finish = (
|
|
5375
|
+
const finish = (shares) => {
|
|
5233
5376
|
input.off("data", onData);
|
|
5234
5377
|
input.setRawMode(wasRaw);
|
|
5235
5378
|
input.pause();
|
|
5236
|
-
resolve6(
|
|
5379
|
+
resolve6(shares);
|
|
5237
5380
|
};
|
|
5238
5381
|
const onData = (data) => {
|
|
5239
5382
|
const next = pickerKey(state, data.toString("utf8"));
|
|
5240
5383
|
if (!next) return finish(null);
|
|
5241
5384
|
state = next;
|
|
5242
5385
|
draw(false);
|
|
5243
|
-
if (state.done) finish(state.choices.
|
|
5386
|
+
if (state.done) finish(Object.fromEntries(state.choices.map((c) => [c.cli, c.share])));
|
|
5244
5387
|
};
|
|
5245
5388
|
input.on("data", onData);
|
|
5246
5389
|
});
|
|
@@ -5249,9 +5392,9 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
|
|
|
5249
5392
|
// src/friday/service.ts
|
|
5250
5393
|
init_define_HERD_PACKAGE();
|
|
5251
5394
|
import { spawn as spawn2 } from "node:child_process";
|
|
5252
|
-
import { existsSync as
|
|
5395
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
5253
5396
|
import { userInfo } from "node:os";
|
|
5254
|
-
import { dirname as
|
|
5397
|
+
import { dirname as dirname7, join as join9 } from "node:path";
|
|
5255
5398
|
var LAUNCH_AGENT = "com.herdingchats.friday";
|
|
5256
5399
|
var SYSTEMD_UNIT = "herdingchats-friday.service";
|
|
5257
5400
|
async function detectService(run4, platform2 = process.platform, override = process.env.HERD_SERVICE) {
|
|
@@ -5266,7 +5409,7 @@ async function detectService(run4, platform2 = process.platform, override = proc
|
|
|
5266
5409
|
}
|
|
5267
5410
|
function servicePath(node) {
|
|
5268
5411
|
return [
|
|
5269
|
-
|
|
5412
|
+
dirname7(node),
|
|
5270
5413
|
"/opt/homebrew/bin",
|
|
5271
5414
|
"/usr/local/bin",
|
|
5272
5415
|
"/usr/bin",
|
|
@@ -5337,8 +5480,8 @@ WantedBy=default.target
|
|
|
5337
5480
|
`;
|
|
5338
5481
|
}
|
|
5339
5482
|
function writeIfChanged(path, text4) {
|
|
5340
|
-
if (
|
|
5341
|
-
mkdirSync7(
|
|
5483
|
+
if (existsSync6(path) && readFileSync7(path, "utf8") === text4) return false;
|
|
5484
|
+
mkdirSync7(dirname7(path), { recursive: true });
|
|
5342
5485
|
writeFileSync7(path, text4);
|
|
5343
5486
|
return true;
|
|
5344
5487
|
}
|
|
@@ -5393,7 +5536,7 @@ function systemd(context2) {
|
|
|
5393
5536
|
};
|
|
5394
5537
|
}
|
|
5395
5538
|
function spawnInBackground(command, env, log) {
|
|
5396
|
-
mkdirSync7(
|
|
5539
|
+
mkdirSync7(dirname7(log), { recursive: true });
|
|
5397
5540
|
const out = openSync3(log, "a");
|
|
5398
5541
|
const [program = process.execPath, ...args] = command;
|
|
5399
5542
|
spawn2(program, args, {
|
|
@@ -5442,20 +5585,20 @@ import { join as join12, resolve as resolve3 } from "node:path";
|
|
|
5442
5585
|
|
|
5443
5586
|
// src/friday/legacy.ts
|
|
5444
5587
|
init_define_HERD_PACKAGE();
|
|
5445
|
-
import { existsSync as
|
|
5588
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, rmSync as rmSync8 } from "node:fs";
|
|
5446
5589
|
import { userInfo as userInfo2 } from "node:os";
|
|
5447
5590
|
import { join as join10 } from "node:path";
|
|
5448
5591
|
var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
|
|
5449
5592
|
var OLD_APPS = ["herd-daemon", "herd-updater"];
|
|
5450
5593
|
function oldPm2s(paths) {
|
|
5451
5594
|
const dir = join10(paths.herdHome, "daemon");
|
|
5452
|
-
if (!
|
|
5453
|
-
return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join10(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) =>
|
|
5595
|
+
if (!existsSync7(dir)) return [];
|
|
5596
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join10(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync7(pm2));
|
|
5454
5597
|
}
|
|
5455
5598
|
async function retireOldDaemon(paths, { home, run: run4 }) {
|
|
5456
5599
|
const removed = [];
|
|
5457
5600
|
const plist = join10(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
|
|
5458
|
-
if (
|
|
5601
|
+
if (existsSync7(plist)) {
|
|
5459
5602
|
await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
|
|
5460
5603
|
rmSync8(plist, { force: true });
|
|
5461
5604
|
removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
|
|
@@ -5481,7 +5624,7 @@ async function retireOldDaemon(paths, { home, run: run4 }) {
|
|
|
5481
5624
|
init_define_HERD_PACKAGE();
|
|
5482
5625
|
import { spawn as spawn3 } from "node:child_process";
|
|
5483
5626
|
import {
|
|
5484
|
-
existsSync as
|
|
5627
|
+
existsSync as existsSync8,
|
|
5485
5628
|
mkdirSync as mkdirSync8,
|
|
5486
5629
|
readdirSync as readdirSync4,
|
|
5487
5630
|
readFileSync as readFileSync8,
|
|
@@ -5492,7 +5635,7 @@ import {
|
|
|
5492
5635
|
symlinkSync,
|
|
5493
5636
|
writeFileSync as writeFileSync8
|
|
5494
5637
|
} from "node:fs";
|
|
5495
|
-
import { basename as basename2, dirname as
|
|
5638
|
+
import { basename as basename2, dirname as dirname8, join as join11 } from "node:path";
|
|
5496
5639
|
var exec = (command, args, options = {}) => new Promise((resolve6) => {
|
|
5497
5640
|
let stdout = "";
|
|
5498
5641
|
let stderr = "";
|
|
@@ -5514,8 +5657,8 @@ var exec = (command, args, options = {}) => new Promise((resolve6) => {
|
|
|
5514
5657
|
});
|
|
5515
5658
|
});
|
|
5516
5659
|
function npmCommand() {
|
|
5517
|
-
const beside = join11(
|
|
5518
|
-
return
|
|
5660
|
+
const beside = join11(dirname8(process.execPath), "npm");
|
|
5661
|
+
return existsSync8(beside) ? beside : "npm";
|
|
5519
5662
|
}
|
|
5520
5663
|
function currentVersion(paths) {
|
|
5521
5664
|
try {
|
|
@@ -5525,7 +5668,7 @@ function currentVersion(paths) {
|
|
|
5525
5668
|
}
|
|
5526
5669
|
}
|
|
5527
5670
|
function installedVersions(paths) {
|
|
5528
|
-
if (!
|
|
5671
|
+
if (!existsSync8(paths.versions)) return [];
|
|
5529
5672
|
return readdirSync4(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
|
|
5530
5673
|
}
|
|
5531
5674
|
var binOf = (versionDir, bin) => join11(packageDir(versionDir), `${bin}.mjs`);
|
|
@@ -5556,7 +5699,7 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
|
|
|
5556
5699
|
} } = {}) {
|
|
5557
5700
|
if (!VERSION.test(version)) return { ok: false, reason: `${version} isn't a version.` };
|
|
5558
5701
|
const dir = join11(paths.versions, version);
|
|
5559
|
-
if (
|
|
5702
|
+
if (existsSync8(dir)) {
|
|
5560
5703
|
const installed2 = recordedIntegrity(dir);
|
|
5561
5704
|
if (integrity && installed2 !== integrity)
|
|
5562
5705
|
return { ok: false, reason: `the installed ${version} doesn't have the integrity named.` };
|
|
@@ -5609,7 +5752,7 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
|
|
|
5609
5752
|
return { ok: true, dir };
|
|
5610
5753
|
}
|
|
5611
5754
|
function sweepStaging(paths) {
|
|
5612
|
-
if (!
|
|
5755
|
+
if (!existsSync8(paths.versions)) return;
|
|
5613
5756
|
for (const name of readdirSync4(paths.versions)) {
|
|
5614
5757
|
const match = /^\.staging-.+-(\d+)$/.exec(name);
|
|
5615
5758
|
if (match && !alive2(Number(match[1])))
|
|
@@ -5815,7 +5958,7 @@ init_define_HERD_PACKAGE();
|
|
|
5815
5958
|
import { spawn as spawn4 } from "node:child_process";
|
|
5816
5959
|
import {
|
|
5817
5960
|
closeSync as closeSync3,
|
|
5818
|
-
existsSync as
|
|
5961
|
+
existsSync as existsSync9,
|
|
5819
5962
|
mkdirSync as mkdirSync10,
|
|
5820
5963
|
openSync as openSync4,
|
|
5821
5964
|
readFileSync as readFileSync9,
|
|
@@ -5900,7 +6043,7 @@ var Supervisor = class {
|
|
|
5900
6043
|
const { daemonLog, logs } = this.options.paths;
|
|
5901
6044
|
mkdirSync10(logs, { recursive: true });
|
|
5902
6045
|
try {
|
|
5903
|
-
if (
|
|
6046
|
+
if (existsSync9(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
|
|
5904
6047
|
renameSync4(daemonLog, `${daemonLog}.1`);
|
|
5905
6048
|
} catch {
|
|
5906
6049
|
}
|
|
@@ -6034,9 +6177,10 @@ Usage: friday <command>
|
|
|
6034
6177
|
|
|
6035
6178
|
start Make this computer a machine, with the folder this runs in as its root, where
|
|
6036
6179
|
Friday keeps its repos and conversations. Signs in through your browser when
|
|
6037
|
-
needed, asks
|
|
6038
|
-
in, waits for each agent to check in, then shows what's left to set up.
|
|
6039
|
-
run again. --agents claude,codex
|
|
6180
|
+
needed, asks how much of each agent you have, starts the daemon now and whenever
|
|
6181
|
+
you log in, waits for each agent to check in, then shows what's left to set up.
|
|
6182
|
+
Safe to run again. --agents claude=lot,codex=little sets the shares without
|
|
6183
|
+
asking: off, little, some or lot, where a bare name is some and one left out off
|
|
6040
6184
|
stop Stop the daemon until you log in again, or run friday start
|
|
6041
6185
|
status Whether the daemon is running, which version, and its root
|
|
6042
6186
|
logs [-f] Show the daemon's latest log lines. -f keeps following them
|
|
@@ -6072,6 +6216,18 @@ function passedEnv() {
|
|
|
6072
6216
|
}
|
|
6073
6217
|
async function friday(args, deps = defaults()) {
|
|
6074
6218
|
const [command = "help", ...rest] = args;
|
|
6219
|
+
const known = FRIDAY_FLAGS[command];
|
|
6220
|
+
if (known) {
|
|
6221
|
+
if (asksForHelp(rest)) {
|
|
6222
|
+
deps.print(commandHelp(HELP, command) ?? HELP);
|
|
6223
|
+
return 0;
|
|
6224
|
+
}
|
|
6225
|
+
const wrong = unknownFlag("friday", command, rest, known);
|
|
6226
|
+
if (wrong) {
|
|
6227
|
+
deps.print(wrong);
|
|
6228
|
+
return 1;
|
|
6229
|
+
}
|
|
6230
|
+
}
|
|
6075
6231
|
try {
|
|
6076
6232
|
switch (command) {
|
|
6077
6233
|
case "help":
|
|
@@ -6138,13 +6294,13 @@ function agentsFlag(args) {
|
|
|
6138
6294
|
const parsed = parseAgents(value ?? "");
|
|
6139
6295
|
if ("unknown" in parsed) {
|
|
6140
6296
|
throw new StartError(
|
|
6141
|
-
`friday start doesn't
|
|
6297
|
+
`friday start doesn't understand ${parsed.unknown.join(", ")}. Name claude, codex or agy, each with =off, =little, =some or =lot if you like.`
|
|
6142
6298
|
);
|
|
6143
6299
|
}
|
|
6144
|
-
return parsed.
|
|
6300
|
+
return parsed.shares;
|
|
6145
6301
|
}
|
|
6146
6302
|
function attachTo(paths, terminal) {
|
|
6147
|
-
const herd =
|
|
6303
|
+
const herd = existsSync10(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join13(dirname9(process.argv[1] ?? ""), "herd.mjs");
|
|
6148
6304
|
const child = spawn5(process.execPath, [herd, "attach", terminal], {
|
|
6149
6305
|
stdio: "inherit",
|
|
6150
6306
|
env: { ...process.env, HERD_HOME: paths.herdHome }
|
|
@@ -6200,7 +6356,7 @@ function runStatus(deps) {
|
|
|
6200
6356
|
return running ? 0 : 1;
|
|
6201
6357
|
}
|
|
6202
6358
|
function tail(path, count) {
|
|
6203
|
-
if (!
|
|
6359
|
+
if (!existsSync10(path)) return [];
|
|
6204
6360
|
const lines = readFileSync10(path, "utf8").split("\n");
|
|
6205
6361
|
if (lines.at(-1) === "") lines.pop();
|
|
6206
6362
|
return lines.slice(-count);
|
|
@@ -6208,7 +6364,7 @@ function tail(path, count) {
|
|
|
6208
6364
|
async function runLogs(args, deps) {
|
|
6209
6365
|
const { paths, print } = deps;
|
|
6210
6366
|
const follow = args.includes("-f") || args.includes("--follow");
|
|
6211
|
-
const files = [paths.supervisorLog, paths.daemonLog].filter((file) =>
|
|
6367
|
+
const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync10(file));
|
|
6212
6368
|
if (files.length === 0) {
|
|
6213
6369
|
print(`There are no logs in ${paths.logs} yet.`);
|
|
6214
6370
|
return 0;
|
|
@@ -6258,7 +6414,7 @@ init_define_HERD_PACKAGE();
|
|
|
6258
6414
|
init_define_HERD_PACKAGE();
|
|
6259
6415
|
import { spawn as spawn6 } from "node:child_process";
|
|
6260
6416
|
import { appendFileSync, readFileSync as readFileSync11 } from "node:fs";
|
|
6261
|
-
import { dirname as
|
|
6417
|
+
import { dirname as dirname10, join as join14 } from "node:path";
|
|
6262
6418
|
var DEPLOY_HOSTS = ["api.cloudflare.com", "api.github.com", "registry.npmjs.org"];
|
|
6263
6419
|
function buildPolicy(input) {
|
|
6264
6420
|
const platform2 = input.platform ?? process.platform;
|
|
@@ -6288,7 +6444,7 @@ function describePolicy(policy, platform2 = process.platform) {
|
|
|
6288
6444
|
const hosts = policy.network.allowedDomains.join(", ");
|
|
6289
6445
|
return `Sandboxed with ${kind}: the owner's home is unreadable, writes go only to the worktree and the build's home, and the only hosts it reaches are ${hosts}.`;
|
|
6290
6446
|
}
|
|
6291
|
-
var nodePrefix = (execPath = process.execPath) =>
|
|
6447
|
+
var nodePrefix = (execPath = process.execPath) => dirname10(dirname10(execPath));
|
|
6292
6448
|
var shellQuote = (args) => args.map((arg) => `'${arg.replace(/'/g, "'\\''")}'`).join(" ");
|
|
6293
6449
|
var BLOCKED_LOG = "blocked.log";
|
|
6294
6450
|
async function runSandboxed(args) {
|
|
@@ -6779,7 +6935,7 @@ init_define_HERD_PACKAGE();
|
|
|
6779
6935
|
// src/build-runner-key.ts
|
|
6780
6936
|
init_define_HERD_PACKAGE();
|
|
6781
6937
|
init_src();
|
|
6782
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
6938
|
+
import { chmodSync as chmodSync3, existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
6783
6939
|
import { join as join15 } from "node:path";
|
|
6784
6940
|
import { z as z24 } from "zod";
|
|
6785
6941
|
var BUILDS_URL = "https://builds.herdingchats.com";
|
|
@@ -6791,7 +6947,7 @@ var runnerKeySchema = z24.object({
|
|
|
6791
6947
|
var runnerKeyPath = () => join15(herdHome(), "build-runner.json");
|
|
6792
6948
|
function readRunnerKey() {
|
|
6793
6949
|
const path = runnerKeyPath();
|
|
6794
|
-
if (!
|
|
6950
|
+
if (!existsSync11(path)) return null;
|
|
6795
6951
|
try {
|
|
6796
6952
|
return runnerKeySchema.parse(JSON.parse(readFileSync13(path, "utf8")));
|
|
6797
6953
|
} catch {
|
|
@@ -7765,7 +7921,7 @@ init_define_HERD_PACKAGE();
|
|
|
7765
7921
|
import { execFile as execFile2, execFileSync as execFileSync4, spawn as spawn7 } from "node:child_process";
|
|
7766
7922
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
7767
7923
|
import {
|
|
7768
|
-
existsSync as
|
|
7924
|
+
existsSync as existsSync12,
|
|
7769
7925
|
mkdirSync as mkdirSync15,
|
|
7770
7926
|
mkdtempSync,
|
|
7771
7927
|
readdirSync as readdirSync6,
|
|
@@ -7859,7 +8015,7 @@ var real2 = (path) => {
|
|
|
7859
8015
|
}
|
|
7860
8016
|
};
|
|
7861
8017
|
function worktreeOf(dir, store) {
|
|
7862
|
-
if (!
|
|
8018
|
+
if (!existsSync12(join20(dir, ".git"))) return false;
|
|
7863
8019
|
try {
|
|
7864
8020
|
return real2(git2(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")) === real2(store);
|
|
7865
8021
|
} catch {
|
|
@@ -7871,10 +8027,10 @@ function cloneFolder(from, to) {
|
|
|
7871
8027
|
execFileSync4("cp", [...args, from, to]);
|
|
7872
8028
|
}
|
|
7873
8029
|
function moduleFolders(dir) {
|
|
7874
|
-
const found =
|
|
8030
|
+
const found = existsSync12(join20(dir, "node_modules")) ? ["node_modules"] : [];
|
|
7875
8031
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
7876
8032
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
7877
|
-
if (
|
|
8033
|
+
if (existsSync12(join20(dir, entry.name, "node_modules")))
|
|
7878
8034
|
found.push(join20(entry.name, "node_modules"));
|
|
7879
8035
|
}
|
|
7880
8036
|
}
|
|
@@ -7970,11 +8126,11 @@ var BuildRunner = class _BuildRunner {
|
|
|
7970
8126
|
async ensureModules(dir) {
|
|
7971
8127
|
const wanted = lockHash(dir);
|
|
7972
8128
|
const marker = join20(dir, "node_modules", ".herd-lock");
|
|
7973
|
-
if (
|
|
8129
|
+
if (existsSync12(marker) && readFileSync16(marker, "utf8") === wanted) return;
|
|
7974
8130
|
const cache = join20(this.home(), "node_modules", wanted);
|
|
7975
8131
|
for (const folder of moduleFolders(dir))
|
|
7976
8132
|
rmSync12(join20(dir, folder), { recursive: true, force: true });
|
|
7977
|
-
if (
|
|
8133
|
+
if (existsSync12(join20(cache, ".complete"))) {
|
|
7978
8134
|
for (const folder of readFileSync16(join20(cache, ".complete"), "utf8").split("\n").filter(Boolean)) {
|
|
7979
8135
|
cloneFolder(join20(cache, folder), join20(dir, folder));
|
|
7980
8136
|
}
|
|
@@ -8156,7 +8312,7 @@ var BuildRunner = class _BuildRunner {
|
|
|
8156
8312
|
clearTimeout(timer);
|
|
8157
8313
|
if (status2 === 10)
|
|
8158
8314
|
return { ok: false, reason: "the sandbox let a build read the owner's files" };
|
|
8159
|
-
if (status2 !== 0 || !
|
|
8315
|
+
if (status2 !== 0 || !existsSync12(join20(dir, "home", "ok"))) {
|
|
8160
8316
|
const why = stderr.trim().split("\n").at(-1) || `the probe exited with ${status2}`;
|
|
8161
8317
|
return { ok: false, reason: why.slice(0, 300) };
|
|
8162
8318
|
}
|
|
@@ -8929,7 +9085,7 @@ init_define_HERD_PACKAGE();
|
|
|
8929
9085
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
8930
9086
|
import {
|
|
8931
9087
|
appendFileSync as appendFileSync2,
|
|
8932
|
-
existsSync as
|
|
9088
|
+
existsSync as existsSync13,
|
|
8933
9089
|
mkdirSync as mkdirSync16,
|
|
8934
9090
|
readFileSync as readFileSync17,
|
|
8935
9091
|
renameSync as renameSync5,
|
|
@@ -8945,7 +9101,7 @@ var shellQuote2 = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
|
8945
9101
|
function writeWorktreeSettings(worktree, settings2, ownCommand) {
|
|
8946
9102
|
const path = join22(worktree, LOCAL_SETTINGS);
|
|
8947
9103
|
let current = {};
|
|
8948
|
-
if (
|
|
9104
|
+
if (existsSync13(path)) {
|
|
8949
9105
|
try {
|
|
8950
9106
|
const parsed = JSON.parse(readFileSync17(path, "utf8"));
|
|
8951
9107
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
@@ -8990,7 +9146,7 @@ function ignoreLocalSettings(worktree) {
|
|
|
8990
9146
|
const info = join22(isAbsolute3(common) ? common : join22(worktree, common), "info");
|
|
8991
9147
|
const exclude = join22(info, "exclude");
|
|
8992
9148
|
const line2 = "/.claude/settings.local.json";
|
|
8993
|
-
const existing =
|
|
9149
|
+
const existing = existsSync13(exclude) ? readFileSync17(exclude, "utf8") : "";
|
|
8994
9150
|
if (existing.split("\n").includes(line2)) return;
|
|
8995
9151
|
mkdirSync16(info, { recursive: true });
|
|
8996
9152
|
appendFileSync2(exclude, `${existing && !existing.endsWith("\n") ? "\n" : ""}${line2}
|
|
@@ -9039,7 +9195,7 @@ function loginShellEnv() {
|
|
|
9039
9195
|
|
|
9040
9196
|
// src/machine-checks.ts
|
|
9041
9197
|
init_define_HERD_PACKAGE();
|
|
9042
|
-
import { existsSync as
|
|
9198
|
+
import { existsSync as existsSync14, rmSync as rmSync13 } from "node:fs";
|
|
9043
9199
|
var REQUEST_POLL_MS = 2e3;
|
|
9044
9200
|
var MachineChecks = class {
|
|
9045
9201
|
constructor(options) {
|
|
@@ -9085,7 +9241,7 @@ var MachineChecks = class {
|
|
|
9085
9241
|
/** `friday start` asked for every agent to be activated again. */
|
|
9086
9242
|
takeRequest() {
|
|
9087
9243
|
const path = activationRequestPath(this.options.herdHome);
|
|
9088
|
-
if (!
|
|
9244
|
+
if (!existsSync14(path)) return;
|
|
9089
9245
|
rmSync13(path, { force: true });
|
|
9090
9246
|
this.activateChosen(true);
|
|
9091
9247
|
}
|
|
@@ -10623,7 +10779,7 @@ import {
|
|
|
10623
10779
|
statSync as statSync8,
|
|
10624
10780
|
writeFileSync as writeFileSync16
|
|
10625
10781
|
} from "node:fs";
|
|
10626
|
-
import { dirname as
|
|
10782
|
+
import { dirname as dirname11, join as join29, sep as sep2 } from "node:path";
|
|
10627
10783
|
import { Readable } from "node:stream";
|
|
10628
10784
|
import { pipeline } from "node:stream/promises";
|
|
10629
10785
|
|
|
@@ -10811,6 +10967,7 @@ function describe(worker, current) {
|
|
|
10811
10967
|
);
|
|
10812
10968
|
lines.push(` ${label3}: ${listed.join(", ")}`);
|
|
10813
10969
|
}
|
|
10970
|
+
lines.push(` share: ${SHARE_LABELS[worker.share]}`);
|
|
10814
10971
|
if (worker.priority !== 0) lines.push(` priority: ${worker.priority}`);
|
|
10815
10972
|
const repos = worker.repos.map(
|
|
10816
10973
|
(repo) => `${normalizeRemote(repo.remote) ?? repo.name} (${isCloned(repo) ? "cloned" : "not cloned"})`
|
|
@@ -11673,8 +11830,8 @@ async function download(ws, workspace, file) {
|
|
|
11673
11830
|
if (lstatSync2(herd, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
11674
11831
|
throw new Error("its folder leads outside the worktree");
|
|
11675
11832
|
}
|
|
11676
|
-
mkdirSync20(
|
|
11677
|
-
if (!realpathSync8(
|
|
11833
|
+
mkdirSync20(dirname11(target), { recursive: true });
|
|
11834
|
+
if (!realpathSync8(dirname11(target)).startsWith(realpathSync8(workspace.folder) + sep2)) {
|
|
11678
11835
|
throw new Error("its folder leads outside the worktree");
|
|
11679
11836
|
}
|
|
11680
11837
|
writeFileSync16(join29(herd, ".gitignore"), "*\n");
|
|
@@ -12187,6 +12344,7 @@ async function runDaemon(args) {
|
|
|
12187
12344
|
const root = setRoot(defaultRoot()).root;
|
|
12188
12345
|
console.log(`No root folder is set for this machine, so it works from ${root}.`);
|
|
12189
12346
|
}
|
|
12347
|
+
await waitForRoot(machineRoot(), { pollMs: Number(process.env.HERD_ROOT_POLL_MS) || 2e3 });
|
|
12190
12348
|
const api = apiUrl();
|
|
12191
12349
|
const terminals = new HostTerminals({ log: (line2) => console.log(line2) });
|
|
12192
12350
|
await terminals.ensureHost();
|
|
@@ -13837,6 +13995,18 @@ Inside a terminal the daemon started:
|
|
|
13837
13995
|
Set HERD_API_URL to use another API, such as http://localhost:5174 for the local dev server.`;
|
|
13838
13996
|
async function main(args) {
|
|
13839
13997
|
const [command = "help", ...rest] = args;
|
|
13998
|
+
const known = HERD_FLAGS[command];
|
|
13999
|
+
if (known) {
|
|
14000
|
+
if (asksForHelp(rest) && !HERD_OWN_HELP.has(command)) {
|
|
14001
|
+
console.log(commandHelp(HELP5, command) ?? HELP5);
|
|
14002
|
+
return 0;
|
|
14003
|
+
}
|
|
14004
|
+
const wrong = unknownFlag("herd", command, rest, known);
|
|
14005
|
+
if (wrong) {
|
|
14006
|
+
console.error(wrong);
|
|
14007
|
+
return 1;
|
|
14008
|
+
}
|
|
14009
|
+
}
|
|
13840
14010
|
try {
|
|
13841
14011
|
switch (command) {
|
|
13842
14012
|
case "help":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herdingchats/friday",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.525",
|
|
4
4
|
"description": "Friday, HerdingChats' agent daemon, and the herd command it runs agents with.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
"satori": "0.32.0",
|
|
23
23
|
"zod": "4.6.7"
|
|
24
24
|
},
|
|
25
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "cbd227dfdbed52c13e4cb3a2b388d6e7d4a6866e"
|
|
26
26
|
}
|