@hydra-acp/cli 0.1.56 → 0.1.57

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 (3) hide show
  1. package/dist/cli.js +102 -28
  2. package/dist/index.js +49 -28
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2102,12 +2102,6 @@ var init_stream_buffer = __esm({
2102
2102
  });
2103
2103
 
2104
2104
  // src/core/hydra-commands.ts
2105
- function hydraCommandsAsAdvertised() {
2106
- return HYDRA_COMMANDS.map((c) => ({
2107
- name: c.argsHint ? `${c.name} ${c.argsHint}` : c.name,
2108
- description: c.description
2109
- }));
2110
- }
2111
2105
  var HYDRA_COMMANDS, VERB_INDEX;
2112
2106
  var init_hydra_commands = __esm({
2113
2107
  "src/core/hydra-commands.ts"() {
@@ -4283,19 +4277,30 @@ var init_session = __esm({
4283
4277
  // emits a non-spec shape (e.g. config_option_update). Fires modelHandlers
4284
4278
  // (persistence) and broadcasts a synthetic current_model_update so all
4285
4279
  // attached clients — including the originator — repaint immediately.
4280
+ //
4281
+ // The broadcast fires even when `modelId` already equals currentModel.
4282
+ // claude-acp emits a stale current_model_update (with the pre-change
4283
+ // value) during set_model processing and a separate config_option_update
4284
+ // with the new value; the configOption path updates currentModel here
4285
+ // before our synthetic broadcast would run, so a value-equality guard
4286
+ // would suppress the corrective broadcast and leave attached clients
4287
+ // (notably the TUI, which doesn't render config_option_update) showing
4288
+ // the stale model from the agent's earlier notification.
4286
4289
  applyModelChange(modelId) {
4287
4290
  const trimmed = modelId.trim();
4288
- if (!trimmed || trimmed === this.currentModel) {
4291
+ if (!trimmed) {
4289
4292
  return;
4290
4293
  }
4291
- this.logger?.info(
4292
- `applyModelChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentModel)} \u2192 ${JSON.stringify(trimmed)}`
4293
- );
4294
- this.currentModel = trimmed;
4295
- for (const handler of this.modelHandlers) {
4296
- try {
4297
- handler(trimmed);
4298
- } catch {
4294
+ if (trimmed !== this.currentModel) {
4295
+ this.logger?.info(
4296
+ `applyModelChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentModel)} \u2192 ${JSON.stringify(trimmed)}`
4297
+ );
4298
+ this.currentModel = trimmed;
4299
+ for (const handler of this.modelHandlers) {
4300
+ try {
4301
+ handler(trimmed);
4302
+ } catch {
4303
+ }
4299
4304
  }
4300
4305
  }
4301
4306
  const update = {
@@ -4312,23 +4317,39 @@ var init_session = __esm({
4312
4317
  }
4313
4318
  // Apply a mode change initiated by a client request (session/set_mode)
4314
4319
  // when the agent doesn't emit a current_mode_update notification on its
4315
- // own. Fires modeHandlers so the persistence hook and any other listeners
4316
- // see the change, identical to the agent-notification path.
4320
+ // own. Fires modeHandlers (persistence) and broadcasts a synthetic
4321
+ // current_mode_update so all attached clients — including the originator
4322
+ // — repaint immediately, mirroring applyModelChange. Without the
4323
+ // broadcast, peer clients (e.g. the TUI when set_mode was issued by Zed
4324
+ // through the shim) would stay on the prior mode.
4317
4325
  applyModeChange(modeId) {
4318
4326
  const trimmed = modeId.trim();
4319
- if (!trimmed || trimmed === this.currentMode) {
4327
+ if (!trimmed) {
4320
4328
  return;
4321
4329
  }
4322
- this.logger?.info(
4323
- `applyModeChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentMode)} \u2192 ${JSON.stringify(trimmed)}`
4324
- );
4325
- this.currentMode = trimmed;
4326
- for (const handler of this.modeHandlers) {
4327
- try {
4328
- handler(trimmed);
4329
- } catch {
4330
+ if (trimmed !== this.currentMode) {
4331
+ this.logger?.info(
4332
+ `applyModeChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentMode)} \u2192 ${JSON.stringify(trimmed)}`
4333
+ );
4334
+ this.currentMode = trimmed;
4335
+ for (const handler of this.modeHandlers) {
4336
+ try {
4337
+ handler(trimmed);
4338
+ } catch {
4339
+ }
4330
4340
  }
4331
4341
  }
4342
+ const update = {
4343
+ sessionUpdate: "current_mode_update",
4344
+ currentModeId: trimmed
4345
+ };
4346
+ if (this.agentAdvertisedModes.length > 0) {
4347
+ update.availableModes = [...this.agentAdvertisedModes];
4348
+ }
4349
+ this.recordAndBroadcast("session/update", {
4350
+ sessionId: this.upstreamSessionId,
4351
+ update
4352
+ });
4332
4353
  }
4333
4354
  onUsageChange(handler) {
4334
4355
  this.usageHandlers.push(handler);
@@ -4340,8 +4361,8 @@ var init_session = __esm({
4340
4361
  // entries, then whatever the agent advertised.
4341
4362
  mergedAvailableCommands() {
4342
4363
  const out = [
4343
- ...hydraCommandsAsAdvertised(),
4344
- { name: "model <model-id>", description: "Switch model; omit arg to list available models" },
4364
+ { name: "hydra", description: "Hydra session command (kill, restart, title, agent <agent>)" },
4365
+ { name: "model", description: "Switch model; omit arg to list available models" },
4345
4366
  { name: "sessions", description: "List all sessions" },
4346
4367
  { name: "help", description: "Show available commands" }
4347
4368
  ];
@@ -28380,6 +28401,7 @@ function maxLen3(headerCell, values) {
28380
28401
  init_config();
28381
28402
  init_service_token();
28382
28403
  init_paths();
28404
+ import * as fsp12 from "fs/promises";
28383
28405
  async function runAgentsList() {
28384
28406
  const config = await loadConfig();
28385
28407
  const serviceToken = await loadServiceToken();
@@ -28609,6 +28631,53 @@ async function runAgentsLogs(agentId, rest) {
28609
28631
  const logPath = paths.agentLogFile(agentId);
28610
28632
  await runLogTail(logPath, rest, "No log file (agent never ran?)");
28611
28633
  }
28634
+ async function runAgentsSetDefault(agentId) {
28635
+ if (!agentId) {
28636
+ process.stderr.write("Usage: hydra-acp agent set <agent-id>\n");
28637
+ process.exit(2);
28638
+ return;
28639
+ }
28640
+ const config = await loadConfig();
28641
+ const serviceToken = await loadServiceToken();
28642
+ const baseUrl = httpBase(config.daemon.host, config.daemon.port, !!config.daemon.tls);
28643
+ let known;
28644
+ try {
28645
+ const r = await fetch(`${baseUrl}/v1/agents`, {
28646
+ headers: { Authorization: `Bearer ${serviceToken}` }
28647
+ });
28648
+ if (r.ok) {
28649
+ const body = await r.json();
28650
+ known = body.agents.map((a) => a.id);
28651
+ }
28652
+ } catch {
28653
+ }
28654
+ if (known !== void 0 && !known.includes(agentId)) {
28655
+ process.stderr.write(
28656
+ `hydra agent set: '${agentId}' is not in the registry. Known ids: ${known.join(", ")}
28657
+ `
28658
+ );
28659
+ process.exit(1);
28660
+ return;
28661
+ }
28662
+ const raw = await readRawConfig3();
28663
+ raw.defaultAgent = agentId;
28664
+ await writeRawConfig3(raw);
28665
+ process.stdout.write(
28666
+ `Set defaultAgent to '${agentId}' in ${paths.config()}
28667
+ `
28668
+ );
28669
+ }
28670
+ async function readRawConfig3() {
28671
+ const raw = await fsp12.readFile(paths.config(), "utf8");
28672
+ return JSON.parse(raw);
28673
+ }
28674
+ async function writeRawConfig3(raw) {
28675
+ await fsp12.writeFile(
28676
+ paths.config(),
28677
+ JSON.stringify(raw, null, 2) + "\n",
28678
+ { encoding: "utf8", mode: 384 }
28679
+ );
28680
+ }
28612
28681
  async function runAgentsRefresh() {
28613
28682
  const config = await loadConfig();
28614
28683
  const serviceToken = await loadServiceToken();
@@ -30262,6 +30331,10 @@ async function main() {
30262
30331
  await runAgentsSync(positional[2]);
30263
30332
  return;
30264
30333
  }
30334
+ if (sub === "set") {
30335
+ await runAgentsSetDefault(positional[2]);
30336
+ return;
30337
+ }
30265
30338
  if (sub === "log" || sub === "logs") {
30266
30339
  const agIdx = argv.indexOf(subcommand);
30267
30340
  const tail = argv.slice(agIdx + 2);
@@ -30498,6 +30571,7 @@ function printHelp() {
30498
30571
  " hydra-acp agent [list] List agents in the cached registry",
30499
30572
  " hydra-acp agent refresh Force a registry re-fetch",
30500
30573
  " hydra-acp agent install <id> Pre-install <id> from the registry (else lazy on first session)",
30574
+ " hydra-acp agent set <id> Set <id> as the default agent (config.defaultAgent)",
30501
30575
  " hydra-acp agent sync <id> Spawn <id> just long enough to ACP session/list it, then persist any sessions it remembers (across every cwd) as cold rows in `session list`",
30502
30576
  " hydra-acp agent log <id> [-f] [-n N] Tail or follow an agent's spawn/stderr log",
30503
30577
  " hydra-acp auth password [--force] Set the daemon's master password",
package/dist/index.js CHANGED
@@ -2672,12 +2672,6 @@ var HYDRA_COMMANDS = [
2672
2672
  }
2673
2673
  ];
2674
2674
  var VERB_INDEX = new Map(HYDRA_COMMANDS.map((c) => [c.verb, c]));
2675
- function hydraCommandsAsAdvertised() {
2676
- return HYDRA_COMMANDS.map((c) => ({
2677
- name: c.argsHint ? `${c.name} ${c.argsHint}` : c.name,
2678
- description: c.description
2679
- }));
2680
- }
2681
2675
 
2682
2676
  // src/core/coalesce-replay.ts
2683
2677
  function coalesceReplay(entries) {
@@ -4543,19 +4537,30 @@ var Session = class {
4543
4537
  // emits a non-spec shape (e.g. config_option_update). Fires modelHandlers
4544
4538
  // (persistence) and broadcasts a synthetic current_model_update so all
4545
4539
  // attached clients — including the originator — repaint immediately.
4540
+ //
4541
+ // The broadcast fires even when `modelId` already equals currentModel.
4542
+ // claude-acp emits a stale current_model_update (with the pre-change
4543
+ // value) during set_model processing and a separate config_option_update
4544
+ // with the new value; the configOption path updates currentModel here
4545
+ // before our synthetic broadcast would run, so a value-equality guard
4546
+ // would suppress the corrective broadcast and leave attached clients
4547
+ // (notably the TUI, which doesn't render config_option_update) showing
4548
+ // the stale model from the agent's earlier notification.
4546
4549
  applyModelChange(modelId) {
4547
4550
  const trimmed = modelId.trim();
4548
- if (!trimmed || trimmed === this.currentModel) {
4551
+ if (!trimmed) {
4549
4552
  return;
4550
4553
  }
4551
- this.logger?.info(
4552
- `applyModelChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentModel)} \u2192 ${JSON.stringify(trimmed)}`
4553
- );
4554
- this.currentModel = trimmed;
4555
- for (const handler of this.modelHandlers) {
4556
- try {
4557
- handler(trimmed);
4558
- } catch {
4554
+ if (trimmed !== this.currentModel) {
4555
+ this.logger?.info(
4556
+ `applyModelChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentModel)} \u2192 ${JSON.stringify(trimmed)}`
4557
+ );
4558
+ this.currentModel = trimmed;
4559
+ for (const handler of this.modelHandlers) {
4560
+ try {
4561
+ handler(trimmed);
4562
+ } catch {
4563
+ }
4559
4564
  }
4560
4565
  }
4561
4566
  const update = {
@@ -4572,23 +4577,39 @@ var Session = class {
4572
4577
  }
4573
4578
  // Apply a mode change initiated by a client request (session/set_mode)
4574
4579
  // when the agent doesn't emit a current_mode_update notification on its
4575
- // own. Fires modeHandlers so the persistence hook and any other listeners
4576
- // see the change, identical to the agent-notification path.
4580
+ // own. Fires modeHandlers (persistence) and broadcasts a synthetic
4581
+ // current_mode_update so all attached clients — including the originator
4582
+ // — repaint immediately, mirroring applyModelChange. Without the
4583
+ // broadcast, peer clients (e.g. the TUI when set_mode was issued by Zed
4584
+ // through the shim) would stay on the prior mode.
4577
4585
  applyModeChange(modeId) {
4578
4586
  const trimmed = modeId.trim();
4579
- if (!trimmed || trimmed === this.currentMode) {
4587
+ if (!trimmed) {
4580
4588
  return;
4581
4589
  }
4582
- this.logger?.info(
4583
- `applyModeChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentMode)} \u2192 ${JSON.stringify(trimmed)}`
4584
- );
4585
- this.currentMode = trimmed;
4586
- for (const handler of this.modeHandlers) {
4587
- try {
4588
- handler(trimmed);
4589
- } catch {
4590
+ if (trimmed !== this.currentMode) {
4591
+ this.logger?.info(
4592
+ `applyModeChange: sessionId=${this.sessionId} ${JSON.stringify(this.currentMode)} \u2192 ${JSON.stringify(trimmed)}`
4593
+ );
4594
+ this.currentMode = trimmed;
4595
+ for (const handler of this.modeHandlers) {
4596
+ try {
4597
+ handler(trimmed);
4598
+ } catch {
4599
+ }
4590
4600
  }
4591
4601
  }
4602
+ const update = {
4603
+ sessionUpdate: "current_mode_update",
4604
+ currentModeId: trimmed
4605
+ };
4606
+ if (this.agentAdvertisedModes.length > 0) {
4607
+ update.availableModes = [...this.agentAdvertisedModes];
4608
+ }
4609
+ this.recordAndBroadcast("session/update", {
4610
+ sessionId: this.upstreamSessionId,
4611
+ update
4612
+ });
4592
4613
  }
4593
4614
  onUsageChange(handler) {
4594
4615
  this.usageHandlers.push(handler);
@@ -4600,8 +4621,8 @@ var Session = class {
4600
4621
  // entries, then whatever the agent advertised.
4601
4622
  mergedAvailableCommands() {
4602
4623
  const out = [
4603
- ...hydraCommandsAsAdvertised(),
4604
- { name: "model <model-id>", description: "Switch model; omit arg to list available models" },
4624
+ { name: "hydra", description: "Hydra session command (kill, restart, title, agent <agent>)" },
4625
+ { name: "model", description: "Switch model; omit arg to list available models" },
4605
4626
  { name: "sessions", description: "List all sessions" },
4606
4627
  { name: "help", description: "Show available commands" }
4607
4628
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydra-acp/cli",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "Multi-client ACP session daemon: spawn agents, attach over WSS, multiplex sessions across editors.",
5
5
  "license": "MIT",
6
6
  "type": "module",