@herdingchats/friday 0.1.563 → 0.1.567

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 +68 -24
  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.563", gitHead: "055f38c1feb0ea86dac8b463c249d3f7cd782018" };
21
+ define_HERD_PACKAGE_default = { version: "0.1.567", gitHead: "816afa2c448b0a5330ddece39cdb0567ef65a3c5" };
22
22
  }
23
23
  });
24
24
 
@@ -3156,6 +3156,7 @@ import { spawn as spawn6 } from "node:child_process";
3156
3156
  import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
3157
3157
  import { homedir as homedir4 } from "node:os";
3158
3158
  import { dirname as dirname10, join as join14 } from "node:path";
3159
+ import { createInterface } from "node:readline/promises";
3159
3160
  import { z as z22 } from "zod";
3160
3161
 
3161
3162
  // src/auth.ts
@@ -3384,7 +3385,7 @@ var HERD_FLAGS = {
3384
3385
  persona: []
3385
3386
  };
3386
3387
  var FRIDAY_FLAGS = {
3387
- start: ["--agents"],
3388
+ start: ["--agents", "--config"],
3388
3389
  stop: [],
3389
3390
  status: [],
3390
3391
  logs: ["--follow", "-f", "-n"]
@@ -5170,16 +5171,18 @@ async function chooseAgents(options, print) {
5170
5171
  }
5171
5172
  const saved = loaded.status === "ok" ? loaded.config : void 0;
5172
5173
  const choices = agentChoices(installed, saved);
5173
- const asked = options.given ? null : await (options.pick?.(choices) ?? Promise.resolve(null));
5174
+ const asked = options.given || !options.ask ? null : await (options.pick?.(choices) ?? Promise.resolve(null));
5174
5175
  const shares = options.given ?? asked ?? // No terminal: what was chosen before, or what's installed.
5175
5176
  Object.fromEntries(choices.map((choice) => [choice.cli, choice.share]));
5176
- if (!sameChoice(saved, shares) || !existsSync5(options.configPath)) {
5177
+ const choosing = options.ask || options.given !== void 0;
5178
+ if (choosing && (!sameChoice(saved, shares) || !existsSync5(options.configPath))) {
5177
5179
  mkdirSync6(dirname6(options.configPath), { recursive: true, mode: 448 });
5178
5180
  writeFileSync5(options.configPath, configSource(withChoice(saved, shares)), { mode: 384 });
5179
5181
  }
5180
5182
  const chosen = ADAPTERS.map(({ cli }) => cli).filter((cli) => (shares[cli] ?? "off") !== "off");
5183
+ const change = options.ask ? "" : ` ${START_COMMAND} --config changes that.`;
5181
5184
  print(
5182
- 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."
5185
+ chosen.length > 0 ? `This machine runs ${chosen.map((cli) => `${label2(cli)} (${SHARE_LABELS[shares[cli]]})`).join(", ")}.${change}` : `This machine runs no agents, so it takes no work. ${START_COMMAND} --config chooses some.`
5183
5186
  );
5184
5187
  return chosen;
5185
5188
  }
@@ -5888,16 +5891,25 @@ async function runStart(options) {
5888
5891
  const existing = readRoot2(paths);
5889
5892
  const wanted = chooseRoot(options.cwd, options.home);
5890
5893
  const running = runningSupervisor(paths);
5894
+ let moving = false;
5891
5895
  if (existing && real(existing) !== real(wanted)) {
5892
5896
  print(`This machine's root is ${existing}, which holds the repos Friday works on.`);
5893
- if (running) {
5894
- print(`Friday is running there, version ${running.version}, so nothing changed.`);
5895
- await waitForAgents(options, await chooseAndAsk(options, print), print);
5896
- await printSetup(options, print);
5897
- return 0;
5897
+ moving = await options.confirm?.(
5898
+ `Make ${wanted} the root instead? Conversations already going carry on where they are.`
5899
+ ) ?? false;
5900
+ if (!moving) {
5901
+ print(
5902
+ options.confirm ? "The root stays where it is." : `Run this in a terminal to move the root to ${wanted}.`
5903
+ );
5904
+ if (running) {
5905
+ print(`Friday is running there, version ${running.version}, so nothing changed.`);
5906
+ await waitForAgents(options, await chooseAndAsk(options, print), print);
5907
+ await printSetup(options, print);
5908
+ return 0;
5909
+ }
5898
5910
  }
5899
5911
  }
5900
- const root = existing ?? wanted;
5912
+ const root = moving ? wanted : existing ?? wanted;
5901
5913
  if (running && root === existing) {
5902
5914
  print(`Friday is running from ${root}, version ${running.version}. Nothing changed.`);
5903
5915
  await waitForAgents(options, await chooseAndAsk(options, print), print);
@@ -5906,6 +5918,7 @@ async function runStart(options) {
5906
5918
  }
5907
5919
  const repo = insideWorkTree(root);
5908
5920
  if (repo) {
5921
+ if (moving) print("The root stays where it is.");
5909
5922
  throw new StartError(
5910
5923
  `${root} is inside the git repo ${repo}, so it can't be the machine's root: every conversation would load that repo's settings. Run ${START_COMMAND} in a folder outside any repo, such as the one that holds your checkouts, like ~/code.`
5911
5924
  );
@@ -5929,13 +5942,18 @@ async function runStart(options) {
5929
5942
  `Stopped the old daemon: removed ${retired.join(" and ")}. Its checkout is left as it was.`
5930
5943
  );
5931
5944
  await ensureInstalled(options, print);
5932
- mkdirSync10(root, { recursive: true });
5933
- writeRoot(paths, root);
5934
5945
  const command = [process.execPath, binOf(paths.current, "friday"), "supervise"];
5935
5946
  const env = { HERD_HOME: paths.herdHome, ...options.env };
5936
5947
  const service = (options.service ?? defaultService(options))(command, env);
5948
+ if (moving && running) {
5949
+ print(`Stopping Friday at ${existing} to start it again from ${root}.`);
5950
+ await service.stop();
5951
+ await stopped(options);
5952
+ }
5953
+ mkdirSync10(root, { recursive: true });
5954
+ writeRoot(paths, root);
5937
5955
  const changed = await service.install();
5938
- await service.start(changed);
5956
+ await service.start(changed || moving);
5939
5957
  print(
5940
5958
  `Friday is running from ${root}, version ${currentVersion(paths)}, ${service.kind === "launchd" ? "and starts whenever you log in" : service.kind === "systemd" ? "and starts whenever you log in, as a systemd user service" : "in the background"}.`
5941
5959
  );
@@ -5946,10 +5964,22 @@ async function runStart(options) {
5946
5964
  );
5947
5965
  return 0;
5948
5966
  }
5967
+ async function stopped(options) {
5968
+ const deadline = Date.now() + (options.stopWaitMs ?? 15e3);
5969
+ while (runningSupervisor(options.paths)) {
5970
+ if (Date.now() > deadline) {
5971
+ throw new StartError(
5972
+ `Friday didn't stop within ${Math.round((options.stopWaitMs ?? 15e3) / 1e3)} seconds, so the root wasn't moved. Run ${FRIDAY_COMMAND} stop, then this again.`
5973
+ );
5974
+ }
5975
+ await new Promise((done) => setTimeout(done, options.stopPollMs ?? 250));
5976
+ }
5977
+ }
5949
5978
  async function chooseAndAsk(options, print) {
5950
5979
  if (!options.agents) return null;
5951
5980
  const chosen = await chooseAgents(options.agents, print);
5952
5981
  if (!chosen) return null;
5982
+ if (!options.agents.ask && !options.agents.given) return null;
5953
5983
  return { chosen, since: askToActivate(options.paths.herdHome) };
5954
5984
  }
5955
5985
  async function waitForAgents(options, asked, print) {
@@ -6235,10 +6265,12 @@ Usage: ${FRIDAY_COMMAND} <command>
6235
6265
 
6236
6266
  start Make this computer a machine, with the folder this runs in as its root, which
6237
6267
  holds your repos, such as ~/code. Signs in through your browser when
6238
- needed, asks how much of each agent you have, starts the daemon now and whenever
6239
- you log in, waits for each agent to check in, then shows what's left to set up.
6240
- Safe to run again. --agents claude=lot,codex=little sets the shares without
6241
- asking: off, little, some or lot, where a bare name is some and one left out off
6268
+ needed, asks how much of each agent you have the first time, starts the daemon
6269
+ now and whenever you log in, then shows what's left to set up. Safe to run
6270
+ again. Run in another folder, it offers to move the root there. --config asks
6271
+ about the agents again, and --agents claude=lot,codex=little sets their shares
6272
+ without asking: off, little, some or lot, where a bare name is some and one
6273
+ left out off
6242
6274
  stop Stop the daemon until you log in again, or run start again
6243
6275
  status Whether the daemon is running, which version, and its root
6244
6276
  logs [-f] Show the daemon's latest log lines. -f keeps following them
@@ -6312,7 +6344,11 @@ async function friday(args, deps = defaults()) {
6312
6344
  print: deps.print,
6313
6345
  setup: fetchSetup,
6314
6346
  ...process.env.HERD_PACKAGE_SPEC ? { install: { spec: process.env.HERD_PACKAGE_SPEC } } : {},
6347
+ ...process.stdin.isTTY && process.stdout.isTTY ? { confirm: confirmInTerminal } : {},
6315
6348
  agents: {
6349
+ // The choice lives in herd.config.ts, so it's asked for only before there is one, or
6350
+ // with --config.
6351
+ ask: rest.includes("--config") || !existsSync11(herdConfigIn(deps.paths.herdHome)),
6316
6352
  installed: async () => installedAgents(),
6317
6353
  load: () => loadHerdConfig(herdConfigIn(deps.paths.herdHome)),
6318
6354
  configPath: herdConfigIn(deps.paths.herdHome),
@@ -6345,6 +6381,14 @@ ${HELP}`);
6345
6381
  throw error;
6346
6382
  }
6347
6383
  }
6384
+ async function confirmInTerminal(question) {
6385
+ const lines = createInterface({ input: process.stdin, output: process.stdout });
6386
+ try {
6387
+ return /^y(es)?$/i.test((await lines.question(`${question} [y/N] `)).trim());
6388
+ } finally {
6389
+ lines.close();
6390
+ }
6391
+ }
6348
6392
  function agentsFlag(args) {
6349
6393
  const at = args.findIndex((arg) => arg === "--agents" || arg.startsWith("--agents="));
6350
6394
  if (at < 0) return void 0;
@@ -9764,7 +9808,7 @@ var HostTerminals = class {
9764
9808
  await this.askFor(name, { op: "kill", name });
9765
9809
  }
9766
9810
  async retire(canStop) {
9767
- const stopped = [];
9811
+ const stopped2 = [];
9768
9812
  for (const host of await this.discover()) {
9769
9813
  if (host.current) continue;
9770
9814
  const version = host.hello.version ?? "unversioned";
@@ -9776,12 +9820,12 @@ var HostTerminals = class {
9776
9820
  continue;
9777
9821
  }
9778
9822
  if (await this.stop(host)) {
9779
- stopped.push(version);
9823
+ stopped2.push(version);
9780
9824
  this.log(`Retired terminal host ${version}, which had nothing left running.`);
9781
9825
  }
9782
9826
  }
9783
- if (stopped.length > 0) await this.discover();
9784
- return stopped;
9827
+ if (stopped2.length > 0) await this.discover();
9828
+ return stopped2;
9785
9829
  }
9786
9830
  /** Stops a host: politely where it knows how, and otherwise by its process. */
9787
9831
  async stop(host) {
@@ -10856,12 +10900,12 @@ async function retireHosts(ws) {
10856
10900
  if (!terminals.retire || Date.now() - ws.retiredAt < every) return;
10857
10901
  ws.retiredAt = Date.now();
10858
10902
  const inHost = (version) => Object.values(ws.load()).filter((workspace) => workspace.hostVersion === version);
10859
- const stopped = await terminals.retire(
10903
+ const stopped2 = await terminals.retire(
10860
10904
  (version) => inHost(version).every(
10861
10905
  (workspace) => !workspace.claimId && (workspace.status === "idle" || workspace.status === "exited")
10862
10906
  )
10863
10907
  );
10864
- for (const version of stopped) {
10908
+ for (const version of stopped2) {
10865
10909
  for (const workspace of inHost(version)) {
10866
10910
  const id2 = workspace.conversationId;
10867
10911
  await ws.serially(id2, async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herdingchats/friday",
3
- "version": "0.1.563",
3
+ "version": "0.1.567",
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": "055f38c1feb0ea86dac8b463c249d3f7cd782018"
25
+ "gitHead": "816afa2c448b0a5330ddece39cdb0567ef65a3c5"
26
26
  }