@herdingchats/friday 0.1.1 → 0.1.522

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.
Files changed (2) hide show
  1. package/herd.mjs +168 -105
  2. 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.1", gitHead: "91b08aebd285af82cf2ea35b4b380b52acf2a33f" };
21
+ define_HERD_PACKAGE_default = { version: "0.1.522", gitHead: "3dab37264f9426b4af9a912efcddadf9fb04cdcc" };
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
- * Whether this machine runs the agent at all, as chosen in \`friday start\`. false means it's no
623
- * worker here. Without a configuration file, every installed agent is.
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
- enabled?: boolean;
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 existsSync9, readFileSync as readFileSync10 } from "node:fs";
3203
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
3175
3204
  import { homedir as homedir4 } from "node:os";
3176
- import { dirname as dirname8, join as join13 } from "node:path";
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
@@ -3402,9 +3431,9 @@ async function runLogout() {
3402
3431
  init_define_HERD_PACKAGE();
3403
3432
  import { execFileSync as execFileSync3 } from "node:child_process";
3404
3433
  import { randomUUID } from "node:crypto";
3405
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
3434
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
3406
3435
  import { homedir as homedir3, hostname } from "node:os";
3407
- import { basename, join as join5, resolve } from "node:path";
3436
+ import { basename, dirname as dirname3, join as join5, resolve } from "node:path";
3408
3437
  import { z as z18 } from "zod";
3409
3438
 
3410
3439
  // src/machine-facts.ts
@@ -3649,7 +3678,7 @@ var ADAPTERS = [
3649
3678
  { cli: "agy", capabilities: ["image_generation"] }
3650
3679
  ];
3651
3680
  function chosenClis(clis, config) {
3652
- return clis.filter((cli) => config?.harnesses?.[agentShortName(cli.name)]?.enabled !== false);
3681
+ return clis.filter((cli) => shareOf(config?.harnesses?.[agentShortName(cli.name)]) !== "off");
3653
3682
  }
3654
3683
  var runCommand = (command, args) => {
3655
3684
  try {
@@ -4111,6 +4140,20 @@ function readRoot() {
4111
4140
  function setRoot(path) {
4112
4141
  return save({ ...loadConfig(), root: resolve(path) });
4113
4142
  }
4143
+ async function waitForRoot(root, {
4144
+ pollMs = 2e3,
4145
+ exists = existsSync2,
4146
+ sleep: sleep8 = (ms) => new Promise((done) => setTimeout(done, ms)),
4147
+ log = (line2) => console.log(line2)
4148
+ } = {}) {
4149
+ const parent = dirname3(root);
4150
+ if (exists(parent)) return;
4151
+ log(
4152
+ `Waiting for ${parent}, which holds this machine's root, to be there, as when its drive mounts.`
4153
+ );
4154
+ while (!exists(parent)) await sleep8(pollMs);
4155
+ log(`${parent} is there.`);
4156
+ }
4114
4157
  var machineRoot = () => readRoot() ?? defaultRoot();
4115
4158
  function describeRepo(path) {
4116
4159
  const full = resolve(path);
@@ -4267,15 +4310,15 @@ async function readSetup(fetch2, init) {
4267
4310
  // src/friday/agents.ts
4268
4311
  init_define_HERD_PACKAGE();
4269
4312
  init_src();
4270
- import { existsSync as existsSync3, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
4271
- import { dirname as dirname4, join as join7 } from "node:path";
4313
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
4314
+ import { dirname as dirname5, join as join7 } from "node:path";
4272
4315
 
4273
4316
  // src/activation.ts
4274
4317
  init_define_HERD_PACKAGE();
4275
4318
  init_src();
4276
4319
  import { randomBytes as randomBytes4, randomUUID as randomUUID3 } from "node:crypto";
4277
4320
  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 dirname3, join as join6 } from "node:path";
4321
+ import { dirname as dirname4, join as join6 } from "node:path";
4279
4322
  import { z as z21 } from "zod";
4280
4323
 
4281
4324
  // src/permissions.ts
@@ -4287,7 +4330,7 @@ import { z as z20 } from "zod";
4287
4330
  // src/local-socket.ts
4288
4331
  init_define_HERD_PACKAGE();
4289
4332
  import { randomBytes as randomBytes3 } from "node:crypto";
4290
- import { chmodSync as chmodSync2, existsSync as existsSync2, rmSync as rmSync4 } from "node:fs";
4333
+ import { chmodSync as chmodSync2, existsSync as existsSync3, rmSync as rmSync4 } from "node:fs";
4291
4334
  import { connect, createServer as createServer2 } from "node:net";
4292
4335
  import { z as z19 } from "zod";
4293
4336
  var localRequestSchema = z19.object({
@@ -4423,7 +4466,7 @@ var LocalSocket = class {
4423
4466
  }
4424
4467
  }
4425
4468
  async listen() {
4426
- if (existsSync2(this.path)) rmSync4(this.path);
4469
+ if (existsSync3(this.path)) rmSync4(this.path);
4427
4470
  this.server = createServer2((socket) => this.serve(socket));
4428
4471
  await new Promise((resolve6, reject) => {
4429
4472
  this.server?.once("error", reject);
@@ -4505,7 +4548,7 @@ function askDaemon(path, request) {
4505
4548
  }
4506
4549
  function isAnswering(path) {
4507
4550
  return new Promise((resolve6) => {
4508
- if (!existsSync2(path)) return resolve6(false);
4551
+ if (!existsSync3(path)) return resolve6(false);
4509
4552
  const socket = connect(path);
4510
4553
  socket.on("connect", () => {
4511
4554
  socket.destroy();
@@ -4744,7 +4787,7 @@ var Activator = class {
4744
4787
  }
4745
4788
  record(agent, record) {
4746
4789
  const agents = { ...readActivations(this.path), [agent]: record };
4747
- mkdirSync4(dirname3(this.path), { recursive: true, mode: 448 });
4790
+ mkdirSync4(dirname4(this.path), { recursive: true, mode: 448 });
4748
4791
  const temporary = `${this.path}.${process.pid}`;
4749
4792
  writeFileSync4(temporary, JSON.stringify({ agents }, null, 2), { mode: 384 });
4750
4793
  renameSync2(temporary, this.path);
@@ -4956,36 +4999,42 @@ var ACTIVATION_READ_MS = 500;
4956
4999
  var label2 = (cli) => AGENTS[cli]?.label ?? cli;
4957
5000
  function agentChoices(installed, saved) {
4958
5001
  const harnesses = saved?.harnesses ?? {};
4959
- const chosenBefore = Object.values(harnesses).some((harness) => harness.enabled !== void 0);
5002
+ const chosenBefore = Object.values(harnesses).some(
5003
+ (harness) => harness.enabled !== void 0 || harness.share !== void 0
5004
+ );
4960
5005
  return ADAPTERS.map(({ cli }) => {
4961
5006
  const isInstalled = installed.includes(cli);
4962
- const enabled = harnesses[agentShortName(cli)]?.enabled;
5007
+ const harness = harnesses[agentShortName(cli)];
5008
+ const before = harness?.share ?? (harness?.enabled ? "some" : "off");
4963
5009
  return {
4964
5010
  cli,
4965
5011
  label: label2(cli),
4966
5012
  installed: isInstalled,
4967
- checked: chosenBefore ? enabled === true : isInstalled
5013
+ share: chosenBefore ? before : isInstalled ? "some" : "off"
4968
5014
  };
4969
5015
  });
4970
5016
  }
4971
5017
  function parseAgents(value) {
4972
- const names = value.split(",").map((name) => name.trim().toLowerCase()).filter(Boolean);
4973
- const agents = [];
5018
+ const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
5019
+ const shares = Object.fromEntries(ADAPTERS.map(({ cli }) => [cli, "off"]));
4974
5020
  const unknown = [];
4975
- for (const name of names) {
5021
+ for (const part of parts) {
5022
+ const [name = "", share = "some"] = part.split("=", 2).map((piece) => piece.trim());
4976
5023
  const adapter = ADAPTERS.find(
4977
5024
  ({ cli }) => cli === name || agentShortName(cli) === name || label2(cli).toLowerCase() === name
4978
5025
  );
4979
- if (adapter) agents.push(adapter.cli);
4980
- else unknown.push(name);
5026
+ if (adapter && SHARES.includes(share)) {
5027
+ shares[adapter.cli] = share;
5028
+ } else unknown.push(part);
4981
5029
  }
4982
- return unknown.length > 0 ? { unknown } : { agents: [...new Set(agents)] };
5030
+ return unknown.length > 0 ? { unknown } : { shares };
4983
5031
  }
4984
- function withChoice(config, chosen) {
5032
+ function withChoice(config, shares) {
4985
5033
  const harnesses = { ...config?.harnesses ?? {} };
4986
5034
  for (const { cli } of ADAPTERS) {
4987
5035
  const short = agentShortName(cli);
4988
- harnesses[short] = { ...harnesses[short], enabled: chosen.includes(cli) };
5036
+ const { enabled: _, ...rest } = harnesses[short] ?? {};
5037
+ harnesses[short] = { ...rest, share: shares[cli] ?? "off" };
4989
5038
  }
4990
5039
  return { ...config, harnesses };
4991
5040
  }
@@ -4994,9 +5043,10 @@ function configSource(config) {
4994
5043
  export default ${JSON.stringify(config, null, 2)};
4995
5044
  `;
4996
5045
  }
4997
- var sameChoice = (config, chosen) => ADAPTERS.every(
4998
- ({ cli }) => config?.harnesses?.[agentShortName(cli)]?.enabled === chosen.includes(cli)
4999
- );
5046
+ var sameChoice = (config, shares) => ADAPTERS.every(({ cli }) => {
5047
+ const harness = config?.harnesses?.[agentShortName(cli)];
5048
+ return harness?.enabled === void 0 && harness?.share === (shares[cli] ?? "off");
5049
+ });
5000
5050
  async function chooseAgents(options, print) {
5001
5051
  const [installed, loaded] = await Promise.all([options.installed(), options.load()]);
5002
5052
  if (loaded.status === "error") {
@@ -5006,14 +5056,15 @@ async function chooseAgents(options, print) {
5006
5056
  const saved = loaded.status === "ok" ? loaded.config : void 0;
5007
5057
  const choices = agentChoices(installed, saved);
5008
5058
  const asked = options.given ? null : await (options.pick?.(choices) ?? Promise.resolve(null));
5009
- const chosen = options.given ?? asked ?? // No terminal: what was chosen before, or what's installed.
5010
- choices.filter((choice) => choice.checked).map((choice) => choice.cli);
5011
- if (!sameChoice(saved, chosen) || !existsSync3(options.configPath)) {
5012
- mkdirSync5(dirname4(options.configPath), { recursive: true, mode: 448 });
5013
- writeFileSync5(options.configPath, configSource(withChoice(saved, chosen)), { mode: 384 });
5059
+ const shares = options.given ?? asked ?? // No terminal: what was chosen before, or what's installed.
5060
+ Object.fromEntries(choices.map((choice) => [choice.cli, choice.share]));
5061
+ if (!sameChoice(saved, shares) || !existsSync4(options.configPath)) {
5062
+ mkdirSync5(dirname5(options.configPath), { recursive: true, mode: 448 });
5063
+ writeFileSync5(options.configPath, configSource(withChoice(saved, shares)), { mode: 384 });
5014
5064
  }
5065
+ const chosen = ADAPTERS.map(({ cli }) => cli).filter((cli) => (shares[cli] ?? "off") !== "off");
5015
5066
  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."
5067
+ 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
5068
  );
5018
5069
  return chosen;
5019
5070
  }
@@ -5081,7 +5132,7 @@ init_define_HERD_PACKAGE();
5081
5132
  import { spawnSync } from "node:child_process";
5082
5133
  import {
5083
5134
  closeSync as closeSync2,
5084
- existsSync as existsSync4,
5135
+ existsSync as existsSync5,
5085
5136
  mkdirSync as mkdirSync6,
5086
5137
  openSync as openSync2,
5087
5138
  readFileSync as readFileSync6,
@@ -5089,7 +5140,7 @@ import {
5089
5140
  statSync as statSync5,
5090
5141
  writeFileSync as writeFileSync6
5091
5142
  } from "node:fs";
5092
- import { dirname as dirname5, join as join8, resolve as resolve2 } from "node:path";
5143
+ import { dirname as dirname6, join as join8, resolve as resolve2 } from "node:path";
5093
5144
  var PACKAGE_NAME = "@herdingchats/friday";
5094
5145
  function fridayPaths(herdHome2) {
5095
5146
  const logs = join8(herdHome2, "logs");
@@ -5118,8 +5169,8 @@ function alive2(pid) {
5118
5169
  }
5119
5170
  }
5120
5171
  function takeLock(path, staleMs = 10 * 6e4) {
5121
- mkdirSync6(dirname5(path), { recursive: true });
5122
- if (existsSync4(path)) {
5172
+ mkdirSync6(dirname6(path), { recursive: true });
5173
+ if (existsSync5(path)) {
5123
5174
  const holder2 = Number(readFileSync6(path, "utf8").trim());
5124
5175
  const old = Date.now() - statSync5(path).mtimeMs > staleMs;
5125
5176
  if (old || !alive2(holder2)) rmSync6(path, { force: true });
@@ -5169,7 +5220,7 @@ function writeRoot(paths, root) {
5169
5220
  }
5170
5221
  function insideWorkTree(folder) {
5171
5222
  let dir = resolve2(folder);
5172
- while (!existsSync4(dir) && dirname5(dir) !== dir) dir = dirname5(dir);
5223
+ while (!existsSync5(dir) && dirname6(dir) !== dir) dir = dirname6(dir);
5173
5224
  const result = spawnSync("git", ["-C", dir, "rev-parse", "--show-toplevel"], {
5174
5225
  encoding: "utf8",
5175
5226
  stdio: ["ignore", "pipe", "ignore"]
@@ -5189,6 +5240,9 @@ var VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
5189
5240
 
5190
5241
  // src/friday/picker.ts
5191
5242
  init_define_HERD_PACKAGE();
5243
+ init_src();
5244
+ var LESS = /* @__PURE__ */ new Set(["-", "_", "\x1B[D"]);
5245
+ var MORE = /* @__PURE__ */ new Set(["+", "=", "\x1B[C"]);
5192
5246
  function pickerKey(state, key) {
5193
5247
  const { choices, cursor } = state;
5194
5248
  if (key === "" || key === "\x1B") return null;
@@ -5197,22 +5251,28 @@ function pickerKey(state, key) {
5197
5251
  return { ...state, cursor: (cursor + choices.length - 1) % choices.length };
5198
5252
  }
5199
5253
  if (key === "\x1B[B" || key === "j") return { ...state, cursor: (cursor + 1) % choices.length };
5200
- if (key === " ") {
5201
- return {
5202
- ...state,
5203
- choices: choices.map(
5204
- (choice, at) => at === cursor ? { ...choice, checked: !choice.checked } : choice
5205
- )
5206
- };
5207
- }
5208
- return state;
5254
+ const step = LESS.has(key) ? -1 : MORE.has(key) ? 1 : 0;
5255
+ if (step === 0) return state;
5256
+ return {
5257
+ ...state,
5258
+ choices: choices.map((choice, at) => {
5259
+ if (at !== cursor) return choice;
5260
+ const next = Math.min(SHARES.length - 1, Math.max(0, SHARES.indexOf(choice.share) + step));
5261
+ return { ...choice, share: SHARES[next] ?? choice.share };
5262
+ })
5263
+ };
5209
5264
  }
5210
5265
  function pickerLines(state) {
5266
+ const width = Math.max(...state.choices.map((choice) => choice.label.length));
5267
+ const cells = SHARES.length - 1;
5211
5268
  return [
5212
- "Which agents do you use on this computer? (\u2191/\u2193 to move, space to choose, Enter to accept)",
5213
- ...state.choices.map(
5214
- (choice, at) => `${at === state.cursor ? "\u276F" : " "} ${choice.checked ? "\u25C9" : "\u25CB"} ${choice.label}${choice.installed ? "" : " (not installed here)"}`
5215
- )
5269
+ "How much of each agent do you have on this computer? (\u2191/\u2193 to move, -/+ to change, Enter to accept)",
5270
+ ...state.choices.map((choice, at) => {
5271
+ const filled = SHARES.indexOf(choice.share);
5272
+ const bar = "\u25A0".repeat(filled) + "\u25A1".repeat(cells - filled);
5273
+ const missing = choice.installed ? "" : " (not installed here)";
5274
+ return `${at === state.cursor ? "\u276F" : " "} ${choice.label.padEnd(width)} ${bar} ${SHARE_LABELS[choice.share]}${missing}`;
5275
+ })
5216
5276
  ];
5217
5277
  }
5218
5278
  function pickInTerminal(choices, input = process.stdin, output2 = process.stdout) {
@@ -5229,18 +5289,18 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
5229
5289
  input.setRawMode(true);
5230
5290
  input.resume();
5231
5291
  draw(true);
5232
- const finish = (chosen) => {
5292
+ const finish = (shares) => {
5233
5293
  input.off("data", onData);
5234
5294
  input.setRawMode(wasRaw);
5235
5295
  input.pause();
5236
- resolve6(chosen);
5296
+ resolve6(shares);
5237
5297
  };
5238
5298
  const onData = (data) => {
5239
5299
  const next = pickerKey(state, data.toString("utf8"));
5240
5300
  if (!next) return finish(null);
5241
5301
  state = next;
5242
5302
  draw(false);
5243
- if (state.done) finish(state.choices.filter((c) => c.checked).map((c) => c.cli));
5303
+ if (state.done) finish(Object.fromEntries(state.choices.map((c) => [c.cli, c.share])));
5244
5304
  };
5245
5305
  input.on("data", onData);
5246
5306
  });
@@ -5249,9 +5309,9 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
5249
5309
  // src/friday/service.ts
5250
5310
  init_define_HERD_PACKAGE();
5251
5311
  import { spawn as spawn2 } from "node:child_process";
5252
- import { existsSync as existsSync5, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
5312
+ import { existsSync as existsSync6, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
5253
5313
  import { userInfo } from "node:os";
5254
- import { dirname as dirname6, join as join9 } from "node:path";
5314
+ import { dirname as dirname7, join as join9 } from "node:path";
5255
5315
  var LAUNCH_AGENT = "com.herdingchats.friday";
5256
5316
  var SYSTEMD_UNIT = "herdingchats-friday.service";
5257
5317
  async function detectService(run4, platform2 = process.platform, override = process.env.HERD_SERVICE) {
@@ -5266,7 +5326,7 @@ async function detectService(run4, platform2 = process.platform, override = proc
5266
5326
  }
5267
5327
  function servicePath(node) {
5268
5328
  return [
5269
- dirname6(node),
5329
+ dirname7(node),
5270
5330
  "/opt/homebrew/bin",
5271
5331
  "/usr/local/bin",
5272
5332
  "/usr/bin",
@@ -5337,8 +5397,8 @@ WantedBy=default.target
5337
5397
  `;
5338
5398
  }
5339
5399
  function writeIfChanged(path, text4) {
5340
- if (existsSync5(path) && readFileSync7(path, "utf8") === text4) return false;
5341
- mkdirSync7(dirname6(path), { recursive: true });
5400
+ if (existsSync6(path) && readFileSync7(path, "utf8") === text4) return false;
5401
+ mkdirSync7(dirname7(path), { recursive: true });
5342
5402
  writeFileSync7(path, text4);
5343
5403
  return true;
5344
5404
  }
@@ -5393,7 +5453,7 @@ function systemd(context2) {
5393
5453
  };
5394
5454
  }
5395
5455
  function spawnInBackground(command, env, log) {
5396
- mkdirSync7(dirname6(log), { recursive: true });
5456
+ mkdirSync7(dirname7(log), { recursive: true });
5397
5457
  const out = openSync3(log, "a");
5398
5458
  const [program = process.execPath, ...args] = command;
5399
5459
  spawn2(program, args, {
@@ -5442,20 +5502,20 @@ import { join as join12, resolve as resolve3 } from "node:path";
5442
5502
 
5443
5503
  // src/friday/legacy.ts
5444
5504
  init_define_HERD_PACKAGE();
5445
- import { existsSync as existsSync6, readdirSync as readdirSync3, rmSync as rmSync8 } from "node:fs";
5505
+ import { existsSync as existsSync7, readdirSync as readdirSync3, rmSync as rmSync8 } from "node:fs";
5446
5506
  import { userInfo as userInfo2 } from "node:os";
5447
5507
  import { join as join10 } from "node:path";
5448
5508
  var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
5449
5509
  var OLD_APPS = ["herd-daemon", "herd-updater"];
5450
5510
  function oldPm2s(paths) {
5451
5511
  const dir = join10(paths.herdHome, "daemon");
5452
- if (!existsSync6(dir)) return [];
5453
- return readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join10(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync6(pm2));
5512
+ if (!existsSync7(dir)) return [];
5513
+ 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
5514
  }
5455
5515
  async function retireOldDaemon(paths, { home, run: run4 }) {
5456
5516
  const removed = [];
5457
5517
  const plist = join10(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
5458
- if (existsSync6(plist)) {
5518
+ if (existsSync7(plist)) {
5459
5519
  await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
5460
5520
  rmSync8(plist, { force: true });
5461
5521
  removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
@@ -5481,7 +5541,7 @@ async function retireOldDaemon(paths, { home, run: run4 }) {
5481
5541
  init_define_HERD_PACKAGE();
5482
5542
  import { spawn as spawn3 } from "node:child_process";
5483
5543
  import {
5484
- existsSync as existsSync7,
5544
+ existsSync as existsSync8,
5485
5545
  mkdirSync as mkdirSync8,
5486
5546
  readdirSync as readdirSync4,
5487
5547
  readFileSync as readFileSync8,
@@ -5492,7 +5552,7 @@ import {
5492
5552
  symlinkSync,
5493
5553
  writeFileSync as writeFileSync8
5494
5554
  } from "node:fs";
5495
- import { basename as basename2, dirname as dirname7, join as join11 } from "node:path";
5555
+ import { basename as basename2, dirname as dirname8, join as join11 } from "node:path";
5496
5556
  var exec = (command, args, options = {}) => new Promise((resolve6) => {
5497
5557
  let stdout = "";
5498
5558
  let stderr = "";
@@ -5514,8 +5574,8 @@ var exec = (command, args, options = {}) => new Promise((resolve6) => {
5514
5574
  });
5515
5575
  });
5516
5576
  function npmCommand() {
5517
- const beside = join11(dirname7(process.execPath), "npm");
5518
- return existsSync7(beside) ? beside : "npm";
5577
+ const beside = join11(dirname8(process.execPath), "npm");
5578
+ return existsSync8(beside) ? beside : "npm";
5519
5579
  }
5520
5580
  function currentVersion(paths) {
5521
5581
  try {
@@ -5525,7 +5585,7 @@ function currentVersion(paths) {
5525
5585
  }
5526
5586
  }
5527
5587
  function installedVersions(paths) {
5528
- if (!existsSync7(paths.versions)) return [];
5588
+ if (!existsSync8(paths.versions)) return [];
5529
5589
  return readdirSync4(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
5530
5590
  }
5531
5591
  var binOf = (versionDir, bin) => join11(packageDir(versionDir), `${bin}.mjs`);
@@ -5556,7 +5616,7 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
5556
5616
  } } = {}) {
5557
5617
  if (!VERSION.test(version)) return { ok: false, reason: `${version} isn't a version.` };
5558
5618
  const dir = join11(paths.versions, version);
5559
- if (existsSync7(dir)) {
5619
+ if (existsSync8(dir)) {
5560
5620
  const installed2 = recordedIntegrity(dir);
5561
5621
  if (integrity && installed2 !== integrity)
5562
5622
  return { ok: false, reason: `the installed ${version} doesn't have the integrity named.` };
@@ -5609,7 +5669,7 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
5609
5669
  return { ok: true, dir };
5610
5670
  }
5611
5671
  function sweepStaging(paths) {
5612
- if (!existsSync7(paths.versions)) return;
5672
+ if (!existsSync8(paths.versions)) return;
5613
5673
  for (const name of readdirSync4(paths.versions)) {
5614
5674
  const match = /^\.staging-.+-(\d+)$/.exec(name);
5615
5675
  if (match && !alive2(Number(match[1])))
@@ -5815,7 +5875,7 @@ init_define_HERD_PACKAGE();
5815
5875
  import { spawn as spawn4 } from "node:child_process";
5816
5876
  import {
5817
5877
  closeSync as closeSync3,
5818
- existsSync as existsSync8,
5878
+ existsSync as existsSync9,
5819
5879
  mkdirSync as mkdirSync10,
5820
5880
  openSync as openSync4,
5821
5881
  readFileSync as readFileSync9,
@@ -5900,7 +5960,7 @@ var Supervisor = class {
5900
5960
  const { daemonLog, logs } = this.options.paths;
5901
5961
  mkdirSync10(logs, { recursive: true });
5902
5962
  try {
5903
- if (existsSync8(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
5963
+ if (existsSync9(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
5904
5964
  renameSync4(daemonLog, `${daemonLog}.1`);
5905
5965
  } catch {
5906
5966
  }
@@ -6034,9 +6094,10 @@ Usage: friday <command>
6034
6094
 
6035
6095
  start Make this computer a machine, with the folder this runs in as its root, where
6036
6096
  Friday keeps its repos and conversations. Signs in through your browser when
6037
- needed, asks which agents you use, starts the daemon now and whenever you log
6038
- in, waits for each agent to check in, then shows what's left to set up. Safe to
6039
- run again. --agents claude,codex chooses the agents without asking
6097
+ needed, asks how much of each agent you have, starts the daemon now and whenever
6098
+ you log in, waits for each agent to check in, then shows what's left to set up.
6099
+ Safe to run again. --agents claude=lot,codex=little sets the shares without
6100
+ asking: off, little, some or lot, where a bare name is some and one left out off
6040
6101
  stop Stop the daemon until you log in again, or run friday start
6041
6102
  status Whether the daemon is running, which version, and its root
6042
6103
  logs [-f] Show the daemon's latest log lines. -f keeps following them
@@ -6138,13 +6199,13 @@ function agentsFlag(args) {
6138
6199
  const parsed = parseAgents(value ?? "");
6139
6200
  if ("unknown" in parsed) {
6140
6201
  throw new StartError(
6141
- `friday start doesn't know the agent ${parsed.unknown.join(", ")}. Choose from claude, codex and agy.`
6202
+ `friday start doesn't understand ${parsed.unknown.join(", ")}. Name claude, codex or agy, each with =off, =little, =some or =lot if you like.`
6142
6203
  );
6143
6204
  }
6144
- return parsed.agents;
6205
+ return parsed.shares;
6145
6206
  }
6146
6207
  function attachTo(paths, terminal) {
6147
- const herd = existsSync9(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join13(dirname8(process.argv[1] ?? ""), "herd.mjs");
6208
+ const herd = existsSync10(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join13(dirname9(process.argv[1] ?? ""), "herd.mjs");
6148
6209
  const child = spawn5(process.execPath, [herd, "attach", terminal], {
6149
6210
  stdio: "inherit",
6150
6211
  env: { ...process.env, HERD_HOME: paths.herdHome }
@@ -6200,7 +6261,7 @@ function runStatus(deps) {
6200
6261
  return running ? 0 : 1;
6201
6262
  }
6202
6263
  function tail(path, count) {
6203
- if (!existsSync9(path)) return [];
6264
+ if (!existsSync10(path)) return [];
6204
6265
  const lines = readFileSync10(path, "utf8").split("\n");
6205
6266
  if (lines.at(-1) === "") lines.pop();
6206
6267
  return lines.slice(-count);
@@ -6208,7 +6269,7 @@ function tail(path, count) {
6208
6269
  async function runLogs(args, deps) {
6209
6270
  const { paths, print } = deps;
6210
6271
  const follow = args.includes("-f") || args.includes("--follow");
6211
- const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync9(file));
6272
+ const files = [paths.supervisorLog, paths.daemonLog].filter((file) => existsSync10(file));
6212
6273
  if (files.length === 0) {
6213
6274
  print(`There are no logs in ${paths.logs} yet.`);
6214
6275
  return 0;
@@ -6258,7 +6319,7 @@ init_define_HERD_PACKAGE();
6258
6319
  init_define_HERD_PACKAGE();
6259
6320
  import { spawn as spawn6 } from "node:child_process";
6260
6321
  import { appendFileSync, readFileSync as readFileSync11 } from "node:fs";
6261
- import { dirname as dirname9, join as join14 } from "node:path";
6322
+ import { dirname as dirname10, join as join14 } from "node:path";
6262
6323
  var DEPLOY_HOSTS = ["api.cloudflare.com", "api.github.com", "registry.npmjs.org"];
6263
6324
  function buildPolicy(input) {
6264
6325
  const platform2 = input.platform ?? process.platform;
@@ -6288,7 +6349,7 @@ function describePolicy(policy, platform2 = process.platform) {
6288
6349
  const hosts = policy.network.allowedDomains.join(", ");
6289
6350
  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
6351
  }
6291
- var nodePrefix = (execPath = process.execPath) => dirname9(dirname9(execPath));
6352
+ var nodePrefix = (execPath = process.execPath) => dirname10(dirname10(execPath));
6292
6353
  var shellQuote = (args) => args.map((arg) => `'${arg.replace(/'/g, "'\\''")}'`).join(" ");
6293
6354
  var BLOCKED_LOG = "blocked.log";
6294
6355
  async function runSandboxed(args) {
@@ -6779,7 +6840,7 @@ init_define_HERD_PACKAGE();
6779
6840
  // src/build-runner-key.ts
6780
6841
  init_define_HERD_PACKAGE();
6781
6842
  init_src();
6782
- import { chmodSync as chmodSync3, existsSync as existsSync10, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
6843
+ import { chmodSync as chmodSync3, existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
6783
6844
  import { join as join15 } from "node:path";
6784
6845
  import { z as z24 } from "zod";
6785
6846
  var BUILDS_URL = "https://builds.herdingchats.com";
@@ -6791,7 +6852,7 @@ var runnerKeySchema = z24.object({
6791
6852
  var runnerKeyPath = () => join15(herdHome(), "build-runner.json");
6792
6853
  function readRunnerKey() {
6793
6854
  const path = runnerKeyPath();
6794
- if (!existsSync10(path)) return null;
6855
+ if (!existsSync11(path)) return null;
6795
6856
  try {
6796
6857
  return runnerKeySchema.parse(JSON.parse(readFileSync13(path, "utf8")));
6797
6858
  } catch {
@@ -7765,7 +7826,7 @@ init_define_HERD_PACKAGE();
7765
7826
  import { execFile as execFile2, execFileSync as execFileSync4, spawn as spawn7 } from "node:child_process";
7766
7827
  import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
7767
7828
  import {
7768
- existsSync as existsSync11,
7829
+ existsSync as existsSync12,
7769
7830
  mkdirSync as mkdirSync15,
7770
7831
  mkdtempSync,
7771
7832
  readdirSync as readdirSync6,
@@ -7859,7 +7920,7 @@ var real2 = (path) => {
7859
7920
  }
7860
7921
  };
7861
7922
  function worktreeOf(dir, store) {
7862
- if (!existsSync11(join20(dir, ".git"))) return false;
7923
+ if (!existsSync12(join20(dir, ".git"))) return false;
7863
7924
  try {
7864
7925
  return real2(git2(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")) === real2(store);
7865
7926
  } catch {
@@ -7871,10 +7932,10 @@ function cloneFolder(from, to) {
7871
7932
  execFileSync4("cp", [...args, from, to]);
7872
7933
  }
7873
7934
  function moduleFolders(dir) {
7874
- const found = existsSync11(join20(dir, "node_modules")) ? ["node_modules"] : [];
7935
+ const found = existsSync12(join20(dir, "node_modules")) ? ["node_modules"] : [];
7875
7936
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
7876
7937
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
7877
- if (existsSync11(join20(dir, entry.name, "node_modules")))
7938
+ if (existsSync12(join20(dir, entry.name, "node_modules")))
7878
7939
  found.push(join20(entry.name, "node_modules"));
7879
7940
  }
7880
7941
  }
@@ -7970,11 +8031,11 @@ var BuildRunner = class _BuildRunner {
7970
8031
  async ensureModules(dir) {
7971
8032
  const wanted = lockHash(dir);
7972
8033
  const marker = join20(dir, "node_modules", ".herd-lock");
7973
- if (existsSync11(marker) && readFileSync16(marker, "utf8") === wanted) return;
8034
+ if (existsSync12(marker) && readFileSync16(marker, "utf8") === wanted) return;
7974
8035
  const cache = join20(this.home(), "node_modules", wanted);
7975
8036
  for (const folder of moduleFolders(dir))
7976
8037
  rmSync12(join20(dir, folder), { recursive: true, force: true });
7977
- if (existsSync11(join20(cache, ".complete"))) {
8038
+ if (existsSync12(join20(cache, ".complete"))) {
7978
8039
  for (const folder of readFileSync16(join20(cache, ".complete"), "utf8").split("\n").filter(Boolean)) {
7979
8040
  cloneFolder(join20(cache, folder), join20(dir, folder));
7980
8041
  }
@@ -8156,7 +8217,7 @@ var BuildRunner = class _BuildRunner {
8156
8217
  clearTimeout(timer);
8157
8218
  if (status2 === 10)
8158
8219
  return { ok: false, reason: "the sandbox let a build read the owner's files" };
8159
- if (status2 !== 0 || !existsSync11(join20(dir, "home", "ok"))) {
8220
+ if (status2 !== 0 || !existsSync12(join20(dir, "home", "ok"))) {
8160
8221
  const why = stderr.trim().split("\n").at(-1) || `the probe exited with ${status2}`;
8161
8222
  return { ok: false, reason: why.slice(0, 300) };
8162
8223
  }
@@ -8929,7 +8990,7 @@ init_define_HERD_PACKAGE();
8929
8990
  import { execFileSync as execFileSync5 } from "node:child_process";
8930
8991
  import {
8931
8992
  appendFileSync as appendFileSync2,
8932
- existsSync as existsSync12,
8993
+ existsSync as existsSync13,
8933
8994
  mkdirSync as mkdirSync16,
8934
8995
  readFileSync as readFileSync17,
8935
8996
  renameSync as renameSync5,
@@ -8945,7 +9006,7 @@ var shellQuote2 = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
8945
9006
  function writeWorktreeSettings(worktree, settings2, ownCommand) {
8946
9007
  const path = join22(worktree, LOCAL_SETTINGS);
8947
9008
  let current = {};
8948
- if (existsSync12(path)) {
9009
+ if (existsSync13(path)) {
8949
9010
  try {
8950
9011
  const parsed = JSON.parse(readFileSync17(path, "utf8"));
8951
9012
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
@@ -8990,7 +9051,7 @@ function ignoreLocalSettings(worktree) {
8990
9051
  const info = join22(isAbsolute3(common) ? common : join22(worktree, common), "info");
8991
9052
  const exclude = join22(info, "exclude");
8992
9053
  const line2 = "/.claude/settings.local.json";
8993
- const existing = existsSync12(exclude) ? readFileSync17(exclude, "utf8") : "";
9054
+ const existing = existsSync13(exclude) ? readFileSync17(exclude, "utf8") : "";
8994
9055
  if (existing.split("\n").includes(line2)) return;
8995
9056
  mkdirSync16(info, { recursive: true });
8996
9057
  appendFileSync2(exclude, `${existing && !existing.endsWith("\n") ? "\n" : ""}${line2}
@@ -9039,7 +9100,7 @@ function loginShellEnv() {
9039
9100
 
9040
9101
  // src/machine-checks.ts
9041
9102
  init_define_HERD_PACKAGE();
9042
- import { existsSync as existsSync13, rmSync as rmSync13 } from "node:fs";
9103
+ import { existsSync as existsSync14, rmSync as rmSync13 } from "node:fs";
9043
9104
  var REQUEST_POLL_MS = 2e3;
9044
9105
  var MachineChecks = class {
9045
9106
  constructor(options) {
@@ -9085,7 +9146,7 @@ var MachineChecks = class {
9085
9146
  /** `friday start` asked for every agent to be activated again. */
9086
9147
  takeRequest() {
9087
9148
  const path = activationRequestPath(this.options.herdHome);
9088
- if (!existsSync13(path)) return;
9149
+ if (!existsSync14(path)) return;
9089
9150
  rmSync13(path, { force: true });
9090
9151
  this.activateChosen(true);
9091
9152
  }
@@ -10623,7 +10684,7 @@ import {
10623
10684
  statSync as statSync8,
10624
10685
  writeFileSync as writeFileSync16
10625
10686
  } from "node:fs";
10626
- import { dirname as dirname10, join as join29, sep as sep2 } from "node:path";
10687
+ import { dirname as dirname11, join as join29, sep as sep2 } from "node:path";
10627
10688
  import { Readable } from "node:stream";
10628
10689
  import { pipeline } from "node:stream/promises";
10629
10690
 
@@ -10811,6 +10872,7 @@ function describe(worker, current) {
10811
10872
  );
10812
10873
  lines.push(` ${label3}: ${listed.join(", ")}`);
10813
10874
  }
10875
+ lines.push(` share: ${SHARE_LABELS[worker.share]}`);
10814
10876
  if (worker.priority !== 0) lines.push(` priority: ${worker.priority}`);
10815
10877
  const repos = worker.repos.map(
10816
10878
  (repo) => `${normalizeRemote(repo.remote) ?? repo.name} (${isCloned(repo) ? "cloned" : "not cloned"})`
@@ -11673,8 +11735,8 @@ async function download(ws, workspace, file) {
11673
11735
  if (lstatSync2(herd, { throwIfNoEntry: false })?.isSymbolicLink()) {
11674
11736
  throw new Error("its folder leads outside the worktree");
11675
11737
  }
11676
- mkdirSync20(dirname10(target), { recursive: true });
11677
- if (!realpathSync8(dirname10(target)).startsWith(realpathSync8(workspace.folder) + sep2)) {
11738
+ mkdirSync20(dirname11(target), { recursive: true });
11739
+ if (!realpathSync8(dirname11(target)).startsWith(realpathSync8(workspace.folder) + sep2)) {
11678
11740
  throw new Error("its folder leads outside the worktree");
11679
11741
  }
11680
11742
  writeFileSync16(join29(herd, ".gitignore"), "*\n");
@@ -12187,6 +12249,7 @@ async function runDaemon(args) {
12187
12249
  const root = setRoot(defaultRoot()).root;
12188
12250
  console.log(`No root folder is set for this machine, so it works from ${root}.`);
12189
12251
  }
12252
+ await waitForRoot(machineRoot(), { pollMs: Number(process.env.HERD_ROOT_POLL_MS) || 2e3 });
12190
12253
  const api = apiUrl();
12191
12254
  const terminals = new HostTerminals({ log: (line2) => console.log(line2) });
12192
12255
  await terminals.ensureHost();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herdingchats/friday",
3
- "version": "0.1.1",
3
+ "version": "0.1.522",
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": "91b08aebd285af82cf2ea35b4b380b52acf2a33f"
25
+ "gitHead": "3dab37264f9426b4af9a912efcddadf9fb04cdcc"
26
26
  }