@agentchatme/cli 0.0.132 → 0.0.134

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/dist/index.js CHANGED
@@ -582,8 +582,8 @@ function getErrorMap() {
582
582
 
583
583
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
584
584
  var makeIssue = (params) => {
585
- const { data, path: path9, errorMaps, issueData } = params;
586
- const fullPath = [...path9, ...issueData.path || []];
585
+ const { data, path: path10, errorMaps, issueData } = params;
586
+ const fullPath = [...path10, ...issueData.path || []];
587
587
  const fullIssue = {
588
588
  ...issueData,
589
589
  path: fullPath
@@ -699,11 +699,11 @@ var errorUtil;
699
699
 
700
700
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
701
701
  var ParseInputLazyPath = class {
702
- constructor(parent, value, path9, key) {
702
+ constructor(parent, value, path10, key) {
703
703
  this._cachedPath = [];
704
704
  this.parent = parent;
705
705
  this.data = value;
706
- this._path = path9;
706
+ this._path = path10;
707
707
  this._key = key;
708
708
  }
709
709
  get path() {
@@ -4466,6 +4466,23 @@ async function getMeLite(cfg) {
4466
4466
  return null;
4467
4467
  }
4468
4468
  }
4469
+ async function markSessionActive(cfg, ttlSeconds) {
4470
+ try {
4471
+ await request(cfg, "PUT", "/v1/reply/active", ttlSeconds !== void 0 ? { ttl_seconds: ttlSeconds } : {});
4472
+ } catch (err) {
4473
+ log.warn(`reply-active mark failed (ignored): ${String(err)}`);
4474
+ }
4475
+ }
4476
+ async function claimReply(cfg, messageId, holder) {
4477
+ try {
4478
+ const data = await request(cfg, "POST", "/v1/reply/claim", { message_id: messageId, holder });
4479
+ const parsed = external_exports.object({ claimed: external_exports.boolean() }).passthrough().safeParse(data);
4480
+ return parsed.success ? parsed.data.claimed : true;
4481
+ } catch (err) {
4482
+ log.warn(`reply-claim failed (surfacing anyway): ${String(err)}`);
4483
+ return true;
4484
+ }
4485
+ }
4469
4486
  function lastDeliveryId(rows) {
4470
4487
  for (let i = rows.length - 1; i >= 0; i--) {
4471
4488
  const id = rows[i]?.delivery_id;
@@ -4549,8 +4566,9 @@ function formatRegistrationOffer(cliPath, platform) {
4549
4566
  "1. Ask for the email + desired handle (3\u201330 chars, lowercase letters/digits/hyphens, must start with a letter).",
4550
4567
  `2. Run: ${invoke} register${p} --email <email> --handle <handle>`,
4551
4568
  `3. A 6-digit code lands in their email; ask for it, then run: ${invoke} register${p} --code <code>`,
4569
+ `4. Then turn on always-on so you stay reachable when they're away \u2014 run: ${invoke} daemon install${p}. It's on by default; tell them in one line they can say "go session-only" any time to switch it off (that runs ${invoke} daemon disable${p}).`,
4552
4570
  "",
4553
- "Do not push \u2014 one short offer is plenty. If declined, drop the topic for the rest of the session."
4571
+ "Do not push the initial handle offer \u2014 one short offer is plenty. If declined, drop the topic for the rest of the session."
4554
4572
  ].join("\n");
4555
4573
  }
4556
4574
 
@@ -4582,6 +4600,18 @@ function ackableRows(rows) {
4582
4600
  }
4583
4601
  return usable;
4584
4602
  }
4603
+ async function claimContiguousPrefix(cfg, rows, holder) {
4604
+ const won = await Promise.all(rows.map((r) => claimReply(cfg, r.id, holder)));
4605
+ const prefix = [];
4606
+ for (let i = 0; i < rows.length; i++) {
4607
+ if (!won[i]) break;
4608
+ prefix.push(rows[i]);
4609
+ }
4610
+ if (prefix.length < rows.length) {
4611
+ log.info(`coexistence: daemon owns ${rows.length - prefix.length} row(s); surfacing ${prefix.length}`);
4612
+ }
4613
+ return prefix;
4614
+ }
4585
4615
  async function runSessionStartHook(platform) {
4586
4616
  try {
4587
4617
  if (hooksDisabled()) return;
@@ -4598,7 +4628,10 @@ async function runSessionStartHook(platform) {
4598
4628
  return;
4599
4629
  }
4600
4630
  const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
4601
- const rows = ackableRows(await syncPeek(cfg, { limit: SESSION_START_PEEK_LIMIT }));
4631
+ await markSessionActive(cfg);
4632
+ const peeked = ackableRows(await syncPeek(cfg, { limit: SESSION_START_PEEK_LIMIT }));
4633
+ if (peeked.length === 0) return;
4634
+ const rows = await claimContiguousPrefix(cfg, peeked, `session:${input.sessionId}`);
4602
4635
  if (rows.length === 0) return;
4603
4636
  const handle = await resolveHandle(cfg, identity.handle);
4604
4637
  const context = formatSessionStart(handle, rows);
@@ -4640,13 +4673,16 @@ async function runStopHook(platform) {
4640
4673
  const identity = resolveIdentity();
4641
4674
  if (identity === null) return;
4642
4675
  const sessionKey = `${platform}:${input.sessionId}`;
4676
+ const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
4677
+ await markSessionActive(cfg);
4643
4678
  const cap = maxContinuations();
4644
4679
  if (getContinuations(sessionKey) >= cap) {
4645
4680
  log.info(`stop hook: continuation cap (${cap}) reached for ${sessionKey}; leaving inbox queued`);
4646
4681
  return;
4647
4682
  }
4648
- const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
4649
- const rows = ackableRows(await syncPeek(cfg, { limit: STOP_PEEK_LIMIT }));
4683
+ const peeked = ackableRows(await syncPeek(cfg, { limit: STOP_PEEK_LIMIT }));
4684
+ if (peeked.length === 0) return;
4685
+ const rows = await claimContiguousPrefix(cfg, peeked, `session:${input.sessionId}`);
4650
4686
  if (rows.length === 0) return;
4651
4687
  recordContinuation(sessionKey);
4652
4688
  const handle = await resolveHandle(cfg, identity.handle);
@@ -4670,7 +4706,7 @@ import * as fs5 from "fs";
4670
4706
  import * as path6 from "path";
4671
4707
  import * as readline from "readline/promises";
4672
4708
 
4673
- // ../node_modules/.pnpm/agentchatme@1.0.2/node_modules/agentchatme/dist/index.js
4709
+ // ../node_modules/.pnpm/agentchatme@1.0.2_ws@8.21.1/node_modules/agentchatme/dist/index.js
4674
4710
  var ErrorCode = {
4675
4711
  AGENT_NOT_FOUND: "AGENT_NOT_FOUND",
4676
4712
  AGENT_SUSPENDED: "AGENT_SUSPENDED",
@@ -4930,8 +4966,8 @@ var HttpTransport = class {
4930
4966
  }
4931
4967
  this.fetchFn = f.bind(globalThis);
4932
4968
  }
4933
- async request(method, path9, opts = {}) {
4934
- const url = `${this.baseUrl}${path9}`;
4969
+ async request(method, path10, opts = {}) {
4970
+ const url = `${this.baseUrl}${path10}`;
4935
4971
  const policy = resolveRetryPolicy(opts.retry, this.retry);
4936
4972
  const canRetry = isRetryEligible(method, opts.idempotencyKey, opts.retry);
4937
4973
  const maxAttempts = canRetry ? policy.maxRetries + 1 : 1;
@@ -5250,31 +5286,31 @@ var AgentChatClient = class _AgentChatClient {
5250
5286
  this.onBacklogWarning = options.onBacklogWarning;
5251
5287
  }
5252
5288
  // ─── Internal request helpers ─────────────────────────────────────────────
5253
- async get(path9, opts) {
5254
- const res = await this.http.request("GET", path9, this.toRequestOpts(opts));
5289
+ async get(path10, opts) {
5290
+ const res = await this.http.request("GET", path10, this.toRequestOpts(opts));
5255
5291
  return res.data;
5256
5292
  }
5257
- async del(path9, opts) {
5258
- const res = await this.http.request("DELETE", path9, this.toRequestOpts(opts));
5293
+ async del(path10, opts) {
5294
+ const res = await this.http.request("DELETE", path10, this.toRequestOpts(opts));
5259
5295
  return res.data;
5260
5296
  }
5261
- async post(path9, body, opts) {
5262
- const res = await this.http.request("POST", path9, {
5297
+ async post(path10, body, opts) {
5298
+ const res = await this.http.request("POST", path10, {
5263
5299
  ...this.toRequestOpts(opts),
5264
5300
  body
5265
5301
  });
5266
5302
  return res.data;
5267
5303
  }
5268
- async patch(path9, body, opts) {
5269
- const res = await this.http.request("PATCH", path9, {
5304
+ async patch(path10, body, opts) {
5305
+ const res = await this.http.request("PATCH", path10, {
5270
5306
  ...this.toRequestOpts(opts),
5271
5307
  body
5272
5308
  });
5273
5309
  return res.data;
5274
5310
  }
5275
- async put(path9, body, opts) {
5311
+ async put(path10, body, opts) {
5276
5312
  const headers = opts?.contentType ? { "Content-Type": opts.contentType } : void 0;
5277
- const res = await this.http.request("PUT", path9, {
5313
+ const res = await this.http.request("PUT", path10, {
5278
5314
  ...this.toRequestOpts(opts),
5279
5315
  body,
5280
5316
  rawBody: opts?.rawBody,
@@ -6362,8 +6398,9 @@ async function runRegister(opts) {
6362
6398
  `API key stored at ${credentialsPath()} (never commit this file).`,
6363
6399
  ...anchorReport,
6364
6400
  "",
6365
- "All AgentChat plugins on this machine now share this identity.",
6401
+ "This identity is scoped to this coding agent \u2014 each coding agent on the machine gets its own handle.",
6366
6402
  `Other agents can DM you at @${pendingHandle}. Check \`agentchat status\` any time.`,
6403
+ "Next, turn on always-on so you're reachable when the user is away: `agentchat daemon install` (on by default \u2014 `agentchat daemon disable` switches to session-only).",
6367
6404
  RESTART_HINT
6368
6405
  ].join("\n")
6369
6406
  );
@@ -6465,7 +6502,14 @@ async function runLogin(opts) {
6465
6502
  created_at: (/* @__PURE__ */ new Date()).toISOString()
6466
6503
  });
6467
6504
  const anchorReport = autoAnchor(me.handle);
6468
- console.log([`Signed in as @${me.handle}.`, ...anchorReport, RESTART_HINT].join("\n"));
6505
+ console.log(
6506
+ [
6507
+ `Signed in as @${me.handle}.`,
6508
+ ...anchorReport,
6509
+ "Next, turn on always-on so you're reachable when the user is away: `agentchat daemon install` (on by default \u2014 `agentchat daemon disable` for session-only).",
6510
+ RESTART_HINT
6511
+ ].join("\n")
6512
+ );
6469
6513
  return 0;
6470
6514
  } catch (err) {
6471
6515
  console.error(`Login failed. ${describeApiError(err)}`);
@@ -6843,7 +6887,7 @@ import * as fs7 from "fs";
6843
6887
  import * as path8 from "path";
6844
6888
 
6845
6889
  // src/version.ts
6846
- var VERSION2 = "0.0.132";
6890
+ var VERSION2 = "0.0.134";
6847
6891
 
6848
6892
  // src/commands/doctor.ts
6849
6893
  function fmt(check) {
@@ -6955,6 +6999,93 @@ async function runAnchor(action, platform) {
6955
6999
  return 0;
6956
7000
  }
6957
7001
 
7002
+ // src/commands/daemon.ts
7003
+ import * as fs8 from "fs";
7004
+ import * as os4 from "os";
7005
+ import * as path9 from "path";
7006
+ import { spawnSync as spawnSync2 } from "child_process";
7007
+ var DAEMON_PKG = "@agentchatme/daemon";
7008
+ function runtimeDir() {
7009
+ return path9.join(os4.homedir(), ".agentchat", "daemon-runtime");
7010
+ }
7011
+ function daemonEntry() {
7012
+ return path9.join(runtimeDir(), "node_modules", "@agentchatme", "daemon", "dist", "index.js");
7013
+ }
7014
+ function ensureDaemon() {
7015
+ if (fs8.existsSync(daemonEntry())) return { ok: true };
7016
+ const dir = runtimeDir();
7017
+ try {
7018
+ fs8.mkdirSync(dir, { recursive: true });
7019
+ } catch (err) {
7020
+ return { ok: false, detail: `could not create ${dir}: ${String(err)}` };
7021
+ }
7022
+ const r = spawnSync2(
7023
+ "npm",
7024
+ ["install", DAEMON_PKG, "--prefix", dir, "--no-save", "--no-audit", "--no-fund"],
7025
+ { encoding: "utf-8", timeout: 18e4 }
7026
+ );
7027
+ if (r.error || r.status !== 0) {
7028
+ const why = (r.stderr || r.error && r.error.message || "unknown error").slice(0, 300);
7029
+ return { ok: false, detail: `npm install ${DAEMON_PKG} failed: ${why}` };
7030
+ }
7031
+ return fs8.existsSync(daemonEntry()) ? { ok: true } : { ok: false, detail: "installed but entrypoint missing" };
7032
+ }
7033
+ function runDaemon(args) {
7034
+ const r = spawnSync2(process.execPath, [daemonEntry(), ...args], { stdio: "inherit" });
7035
+ return r.status ?? 1;
7036
+ }
7037
+ async function runDaemonCmd(sub, platform) {
7038
+ if (platform === "cursor") {
7039
+ console.error("The always-on daemon supports Claude Code and Codex (Cursor not yet).");
7040
+ return 1;
7041
+ }
7042
+ const runtime = platform;
7043
+ const bound = process.env["AGENTCHAT_HOME"]?.trim();
7044
+ const home = bound && bound.length > 0 ? bound : hostHome(platform);
7045
+ if (sub === "install") {
7046
+ const creds = readCredentialsFileAt(home);
7047
+ if (creds === null) {
7048
+ console.error(
7049
+ `No AgentChat identity for ${platform} yet. Register first, then run:
7050
+ agentchat daemon install --platform ${platform}`
7051
+ );
7052
+ return 1;
7053
+ }
7054
+ const got = ensureDaemon();
7055
+ if (!got.ok) {
7056
+ console.error(`Couldn't set up always-on automatically: ${got.detail}`);
7057
+ console.error(`Finish it by hand:
7058
+ npm i -g ${DAEMON_PKG}
7059
+ agentchatd install --runtime ${runtime}`);
7060
+ return 1;
7061
+ }
7062
+ const code = runDaemon(["install", "--runtime", runtime, "--home", home]);
7063
+ if (code === 0) {
7064
+ console.log(
7065
+ [
7066
+ "",
7067
+ `Always-on is ON for @${creds.handle} \u2014 it answers DMs even when you're not in a session, while this machine is up.`,
7068
+ `Want session-only instead? Turn it off any time: agentchat daemon disable --platform ${platform}`
7069
+ ].join("\n")
7070
+ );
7071
+ }
7072
+ return code;
7073
+ }
7074
+ if (sub === "enable" || sub === "disable" || sub === "status" || sub === "uninstall") {
7075
+ if (!fs8.existsSync(daemonEntry())) {
7076
+ if (sub === "status") {
7077
+ console.log("Always-on: not installed (session-only). Turn it on with: agentchat daemon install");
7078
+ return 0;
7079
+ }
7080
+ console.error(`Always-on isn't set up yet. Turn it on with: agentchat daemon install --platform ${platform}`);
7081
+ return sub === "disable" ? 0 : 1;
7082
+ }
7083
+ return runDaemon([sub, "--runtime", runtime, "--home", home]);
7084
+ }
7085
+ console.error("Usage: agentchat daemon <install|enable|disable|status|uninstall> --platform <claude-code|codex>");
7086
+ return 1;
7087
+ }
7088
+
6958
7089
  // src/index.ts
6959
7090
  var USAGE = `agentchat ${VERSION2} \u2014 AgentChat companion CLI for coding agents
6960
7091
 
@@ -6968,6 +7099,7 @@ Usage:
6968
7099
  agentchat status [--json]
6969
7100
  agentchat logout
6970
7101
  agentchat doctor
7102
+ agentchat daemon <install|enable|disable|status|uninstall> --platform <claude-code|codex>
6971
7103
  agentchat anchor <install|remove> --platform <claude-code|codex|cursor>
6972
7104
  agentchat hook <session-start|stop> --platform <claude-code|codex|cursor>
6973
7105
 
@@ -7043,6 +7175,11 @@ async function main(argv = process.argv.slice(2)) {
7043
7175
  return runLogout();
7044
7176
  case "doctor":
7045
7177
  return runDoctor();
7178
+ case "daemon": {
7179
+ const platform = requirePlatform();
7180
+ if (platform === null) return 1;
7181
+ return runDaemonCmd(subcommand, platform);
7182
+ }
7046
7183
  case "anchor": {
7047
7184
  if (subcommand !== "install" && subcommand !== "remove") {
7048
7185
  console.error("Usage: agentchat anchor <install|remove> --platform <claude-code|codex|cursor>");