@agentchatme/cli 0.0.131 → 0.0.133

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
@@ -37,6 +37,49 @@ function stopOutput(platform, reason) {
37
37
  }
38
38
  }
39
39
 
40
+ // src/lib/paths.ts
41
+ import * as os from "os";
42
+ import * as path from "path";
43
+ function agentchatHome() {
44
+ const override = process.env["AGENTCHAT_HOME"];
45
+ if (override && override.trim().length > 0) return path.resolve(override);
46
+ return path.join(os.homedir(), ".agentchat");
47
+ }
48
+ function legacyMachineHome() {
49
+ return path.join(os.homedir(), ".agentchat");
50
+ }
51
+ function codexHome() {
52
+ const override = process.env["CODEX_HOME"];
53
+ if (override && override.trim().length > 0) return path.resolve(override);
54
+ return path.join(os.homedir(), ".codex");
55
+ }
56
+ function hostHome(platform) {
57
+ switch (platform) {
58
+ case "claude-code":
59
+ return path.join(os.homedir(), ".claude", "agentchat");
60
+ case "codex":
61
+ return path.join(codexHome(), "agentchat");
62
+ case "cursor":
63
+ return path.join(os.homedir(), ".cursor", "agentchat");
64
+ }
65
+ }
66
+ function bindHostHome(platform) {
67
+ const existing = process.env["AGENTCHAT_HOME"];
68
+ if (existing && existing.trim().length > 0) return path.resolve(existing);
69
+ const home = hostHome(platform);
70
+ process.env["AGENTCHAT_HOME"] = home;
71
+ return home;
72
+ }
73
+ function credentialsPath() {
74
+ return path.join(agentchatHome(), "credentials");
75
+ }
76
+ function pendingPath() {
77
+ return path.join(agentchatHome(), "pending.json");
78
+ }
79
+ function statePath() {
80
+ return path.join(agentchatHome(), "state.json");
81
+ }
82
+
40
83
  // src/lib/log.ts
41
84
  var LEVELS = ["silent", "error", "warn", "info", "debug"];
42
85
  function activeLevel() {
@@ -59,6 +102,7 @@ var log = {
59
102
 
60
103
  // src/lib/credentials.ts
61
104
  import * as fs2 from "fs";
105
+ import * as path3 from "path";
62
106
 
63
107
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
64
108
  var external_exports = {};
@@ -538,8 +582,8 @@ function getErrorMap() {
538
582
 
539
583
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
540
584
  var makeIssue = (params) => {
541
- const { data, path: path8, errorMaps, issueData } = params;
542
- const fullPath = [...path8, ...issueData.path || []];
585
+ const { data, path: path9, errorMaps, issueData } = params;
586
+ const fullPath = [...path9, ...issueData.path || []];
543
587
  const fullIssue = {
544
588
  ...issueData,
545
589
  path: fullPath
@@ -655,11 +699,11 @@ var errorUtil;
655
699
 
656
700
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
657
701
  var ParseInputLazyPath = class {
658
- constructor(parent, value, path8, key) {
702
+ constructor(parent, value, path9, key) {
659
703
  this._cachedPath = [];
660
704
  this.parent = parent;
661
705
  this.data = value;
662
- this._path = path8;
706
+ this._path = path9;
663
707
  this._key = key;
664
708
  }
665
709
  get path() {
@@ -4103,11 +4147,11 @@ var NEVER = INVALID;
4103
4147
 
4104
4148
  // src/lib/fsutil.ts
4105
4149
  import * as fs from "fs";
4106
- import * as path from "path";
4150
+ import * as path2 from "path";
4107
4151
  function atomicWriteFile(filePath, data, mode) {
4108
- const dir = path.dirname(filePath);
4152
+ const dir = path2.dirname(filePath);
4109
4153
  fs.mkdirSync(dir, { recursive: true, mode: 448 });
4110
- const tmp = path.join(dir, `.${path.basename(filePath)}.${process.pid}.tmp`);
4154
+ const tmp = path2.join(dir, `.${path2.basename(filePath)}.${process.pid}.tmp`);
4111
4155
  fs.writeFileSync(tmp, data, mode === void 0 ? {} : { mode });
4112
4156
  fs.renameSync(tmp, filePath);
4113
4157
  if (mode !== void 0) {
@@ -4123,29 +4167,6 @@ function readJsonFile(filePath) {
4123
4167
  }
4124
4168
  }
4125
4169
 
4126
- // src/lib/paths.ts
4127
- import * as os from "os";
4128
- import * as path2 from "path";
4129
- function agentchatHome() {
4130
- const override = process.env["AGENTCHAT_HOME"];
4131
- if (override && override.trim().length > 0) return path2.resolve(override);
4132
- return path2.join(os.homedir(), ".agentchat");
4133
- }
4134
- function credentialsPath() {
4135
- return path2.join(agentchatHome(), "credentials");
4136
- }
4137
- function pendingPath() {
4138
- return path2.join(agentchatHome(), "pending.json");
4139
- }
4140
- function statePath() {
4141
- return path2.join(agentchatHome(), "state.json");
4142
- }
4143
- function codexHome() {
4144
- const override = process.env["CODEX_HOME"];
4145
- if (override && override.trim().length > 0) return path2.resolve(override);
4146
- return path2.join(os.homedir(), ".codex");
4147
- }
4148
-
4149
4170
  // src/lib/credentials.ts
4150
4171
  var DEFAULT_API_BASE = "https://api.agentchat.me";
4151
4172
  var CredentialsSchema = external_exports.object({
@@ -4160,6 +4181,12 @@ function readCredentialsFile() {
4160
4181
  const parsed = CredentialsSchema.safeParse(raw);
4161
4182
  return parsed.success ? parsed.data : null;
4162
4183
  }
4184
+ function readCredentialsFileAt(home) {
4185
+ const raw = readJsonFile(path3.join(home, "credentials"));
4186
+ if (raw === null) return null;
4187
+ const parsed = CredentialsSchema.safeParse(raw);
4188
+ return parsed.success ? parsed.data : null;
4189
+ }
4163
4190
  function resolveIdentity() {
4164
4191
  const envKey = process.env["AGENTCHAT_API_KEY"];
4165
4192
  const envBase = process.env["AGENTCHAT_API_BASE"];
@@ -4439,6 +4466,23 @@ async function getMeLite(cfg) {
4439
4466
  return null;
4440
4467
  }
4441
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
+ }
4442
4486
  function lastDeliveryId(rows) {
4443
4487
  for (let i = rows.length - 1; i >= 0; i--) {
4444
4488
  const id = rows[i]?.delivery_id;
@@ -4510,15 +4554,18 @@ function formatStopPickup(handle, rows) {
4510
4554
  "Handle these per your AgentChat skill, then finish. Reply via agentchat_send_message only where warranted \u2014 if nothing is actionable, simply end the turn (silence is a valid outcome)."
4511
4555
  ].join("\n");
4512
4556
  }
4513
- function formatRegistrationOffer(cliPath) {
4557
+ function formatRegistrationOffer(cliPath, platform) {
4514
4558
  const invoke = cliPath ? `node "${cliPath}"` : "agentchat";
4559
+ const p = platform ? ` --platform ${platform}` : "";
4560
+ const hostNote = platform ? `This ${platform} agent gets its OWN handle (separate from any other coding agent on this machine \u2014 that's what lets them DM each other). Use an email not already tied to another agent.` : "";
4515
4561
  return [
4516
- "The AgentChat plugin is installed but this machine has no AgentChat identity yet.",
4562
+ "The AgentChat plugin is installed but this agent has no AgentChat identity yet.",
4517
4563
  "",
4518
4564
  "AgentChat gives you (the agent) a handle other agents can DM, like a phone number. If the user would like that, offer to set it up conversationally:",
4565
+ ...hostNote ? [hostNote] : [],
4519
4566
  "1. Ask for the email + desired handle (3\u201330 chars, lowercase letters/digits/hyphens, must start with a letter).",
4520
- `2. Run: ${invoke} register --email <email> --handle <handle>`,
4521
- `3. A 6-digit code lands in their email; ask for it, then run: ${invoke} register --code <code>`,
4567
+ `2. Run: ${invoke} register${p} --email <email> --handle <handle>`,
4568
+ `3. A 6-digit code lands in their email; ask for it, then run: ${invoke} register${p} --code <code>`,
4522
4569
  "",
4523
4570
  "Do not push \u2014 one short offer is plenty. If declined, drop the topic for the rest of the session."
4524
4571
  ].join("\n");
@@ -4552,9 +4599,22 @@ function ackableRows(rows) {
4552
4599
  }
4553
4600
  return usable;
4554
4601
  }
4602
+ async function claimContiguousPrefix(cfg, rows, holder) {
4603
+ const won = await Promise.all(rows.map((r) => claimReply(cfg, r.id, holder)));
4604
+ const prefix = [];
4605
+ for (let i = 0; i < rows.length; i++) {
4606
+ if (!won[i]) break;
4607
+ prefix.push(rows[i]);
4608
+ }
4609
+ if (prefix.length < rows.length) {
4610
+ log.info(`coexistence: daemon owns ${rows.length - prefix.length} row(s); surfacing ${prefix.length}`);
4611
+ }
4612
+ return prefix;
4613
+ }
4555
4614
  async function runSessionStartHook(platform) {
4556
4615
  try {
4557
4616
  if (hooksDisabled()) return;
4617
+ bindHostHome(platform);
4558
4618
  const input = await readHookInput();
4559
4619
  if (input.source === "compact") return;
4560
4620
  resetSession(`${platform}:${input.sessionId}`);
@@ -4562,12 +4622,15 @@ async function runSessionStartHook(platform) {
4562
4622
  if (identity === null) {
4563
4623
  if (shouldOfferRegistration()) {
4564
4624
  recordRegistrationOffer();
4565
- printJson(sessionStartOutput(platform, formatRegistrationOffer(process.argv[1])));
4625
+ printJson(sessionStartOutput(platform, formatRegistrationOffer(process.argv[1], platform)));
4566
4626
  }
4567
4627
  return;
4568
4628
  }
4569
4629
  const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
4570
- const rows = ackableRows(await syncPeek(cfg, { limit: SESSION_START_PEEK_LIMIT }));
4630
+ await markSessionActive(cfg);
4631
+ const peeked = ackableRows(await syncPeek(cfg, { limit: SESSION_START_PEEK_LIMIT }));
4632
+ if (peeked.length === 0) return;
4633
+ const rows = await claimContiguousPrefix(cfg, peeked, `session:${input.sessionId}`);
4571
4634
  if (rows.length === 0) return;
4572
4635
  const handle = await resolveHandle(cfg, identity.handle);
4573
4636
  const context = formatSessionStart(handle, rows);
@@ -4583,6 +4646,7 @@ async function runSessionStartHook(platform) {
4583
4646
  async function runUserPromptHook(platform) {
4584
4647
  try {
4585
4648
  if (hooksDisabled()) return;
4649
+ bindHostHome(platform);
4586
4650
  const input = await readHookInput();
4587
4651
  const identity = resolveIdentity();
4588
4652
  if (identity === null) return;
@@ -4603,17 +4667,21 @@ async function runUserPromptHook(platform) {
4603
4667
  async function runStopHook(platform) {
4604
4668
  try {
4605
4669
  if (hooksDisabled()) return;
4670
+ bindHostHome(platform);
4606
4671
  const input = await readHookInput();
4607
4672
  const identity = resolveIdentity();
4608
4673
  if (identity === null) return;
4609
4674
  const sessionKey = `${platform}:${input.sessionId}`;
4675
+ const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
4676
+ await markSessionActive(cfg);
4610
4677
  const cap = maxContinuations();
4611
4678
  if (getContinuations(sessionKey) >= cap) {
4612
4679
  log.info(`stop hook: continuation cap (${cap}) reached for ${sessionKey}; leaving inbox queued`);
4613
4680
  return;
4614
4681
  }
4615
- const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
4616
- const rows = ackableRows(await syncPeek(cfg, { limit: STOP_PEEK_LIMIT }));
4682
+ const peeked = ackableRows(await syncPeek(cfg, { limit: STOP_PEEK_LIMIT }));
4683
+ if (peeked.length === 0) return;
4684
+ const rows = await claimContiguousPrefix(cfg, peeked, `session:${input.sessionId}`);
4617
4685
  if (rows.length === 0) return;
4618
4686
  recordContinuation(sessionKey);
4619
4687
  const handle = await resolveHandle(cfg, identity.handle);
@@ -4634,10 +4702,10 @@ async function runStopHook(platform) {
4634
4702
 
4635
4703
  // src/commands/identity.ts
4636
4704
  import * as fs5 from "fs";
4637
- import * as path5 from "path";
4705
+ import * as path6 from "path";
4638
4706
  import * as readline from "readline/promises";
4639
4707
 
4640
- // ../node_modules/.pnpm/agentchatme@1.0.2/node_modules/agentchatme/dist/index.js
4708
+ // ../node_modules/.pnpm/agentchatme@1.0.2_ws@8.21.1/node_modules/agentchatme/dist/index.js
4641
4709
  var ErrorCode = {
4642
4710
  AGENT_NOT_FOUND: "AGENT_NOT_FOUND",
4643
4711
  AGENT_SUSPENDED: "AGENT_SUSPENDED",
@@ -4897,8 +4965,8 @@ var HttpTransport = class {
4897
4965
  }
4898
4966
  this.fetchFn = f.bind(globalThis);
4899
4967
  }
4900
- async request(method, path8, opts = {}) {
4901
- const url = `${this.baseUrl}${path8}`;
4968
+ async request(method, path9, opts = {}) {
4969
+ const url = `${this.baseUrl}${path9}`;
4902
4970
  const policy = resolveRetryPolicy(opts.retry, this.retry);
4903
4971
  const canRetry = isRetryEligible(method, opts.idempotencyKey, opts.retry);
4904
4972
  const maxAttempts = canRetry ? policy.maxRetries + 1 : 1;
@@ -5217,31 +5285,31 @@ var AgentChatClient = class _AgentChatClient {
5217
5285
  this.onBacklogWarning = options.onBacklogWarning;
5218
5286
  }
5219
5287
  // ─── Internal request helpers ─────────────────────────────────────────────
5220
- async get(path8, opts) {
5221
- const res = await this.http.request("GET", path8, this.toRequestOpts(opts));
5288
+ async get(path9, opts) {
5289
+ const res = await this.http.request("GET", path9, this.toRequestOpts(opts));
5222
5290
  return res.data;
5223
5291
  }
5224
- async del(path8, opts) {
5225
- const res = await this.http.request("DELETE", path8, this.toRequestOpts(opts));
5292
+ async del(path9, opts) {
5293
+ const res = await this.http.request("DELETE", path9, this.toRequestOpts(opts));
5226
5294
  return res.data;
5227
5295
  }
5228
- async post(path8, body, opts) {
5229
- const res = await this.http.request("POST", path8, {
5296
+ async post(path9, body, opts) {
5297
+ const res = await this.http.request("POST", path9, {
5230
5298
  ...this.toRequestOpts(opts),
5231
5299
  body
5232
5300
  });
5233
5301
  return res.data;
5234
5302
  }
5235
- async patch(path8, body, opts) {
5236
- const res = await this.http.request("PATCH", path8, {
5303
+ async patch(path9, body, opts) {
5304
+ const res = await this.http.request("PATCH", path9, {
5237
5305
  ...this.toRequestOpts(opts),
5238
5306
  body
5239
5307
  });
5240
5308
  return res.data;
5241
5309
  }
5242
- async put(path8, body, opts) {
5310
+ async put(path9, body, opts) {
5243
5311
  const headers = opts?.contentType ? { "Content-Type": opts.contentType } : void 0;
5244
- const res = await this.http.request("PUT", path8, {
5312
+ const res = await this.http.request("PUT", path9, {
5245
5313
  ...this.toRequestOpts(opts),
5246
5314
  body,
5247
5315
  rawBody: opts?.rawBody,
@@ -5905,7 +5973,7 @@ var MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024;
5905
5973
  // src/lib/anchor.ts
5906
5974
  import * as fs3 from "fs";
5907
5975
  import * as os2 from "os";
5908
- import * as path3 from "path";
5976
+ import * as path4 from "path";
5909
5977
  var ANCHOR_START = "<!-- agentchat:start -->";
5910
5978
  var ANCHOR_END = "<!-- agentchat:end -->";
5911
5979
  var LEGACY_ANCHOR_START = "<!-- agentchat-skill:start -->";
@@ -5913,9 +5981,9 @@ var LEGACY_ANCHOR_END = "<!-- agentchat-skill:end -->";
5913
5981
  function anchorFilePath(platform) {
5914
5982
  switch (platform) {
5915
5983
  case "claude-code":
5916
- return path3.join(os2.homedir(), ".claude", "CLAUDE.md");
5984
+ return path4.join(os2.homedir(), ".claude", "CLAUDE.md");
5917
5985
  case "codex":
5918
- return path3.join(codexHome(), "AGENTS.md");
5986
+ return path4.join(codexHome(), "AGENTS.md");
5919
5987
  case "cursor":
5920
5988
  return null;
5921
5989
  }
@@ -5940,7 +6008,7 @@ function installAnchor(platform, handle) {
5940
6008
  if (filePath === null) return { platform, path: null, action: "unsupported" };
5941
6009
  const trimmedHandle = handle.trim();
5942
6010
  if (!trimmedHandle) throw new Error("installAnchor: handle is empty");
5943
- fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
6011
+ fs3.mkdirSync(path4.dirname(filePath), { recursive: true });
5944
6012
  const existing = fs3.existsSync(filePath) ? fs3.readFileSync(filePath, "utf-8") : "";
5945
6013
  const next = upsertAnchorBlock(existing, renderAnchorBlock(trimmedHandle));
5946
6014
  fs3.writeFileSync(filePath, next, "utf-8");
@@ -6015,18 +6083,21 @@ function stripAllBlocks(existing, start, end) {
6015
6083
 
6016
6084
  // src/lib/codex-config.ts
6017
6085
  import * as fs4 from "fs";
6018
- import * as path4 from "path";
6086
+ import * as path5 from "path";
6019
6087
  var TOML_START = "# agentchat:start";
6020
6088
  var TOML_END = "# agentchat:end";
6021
- var BUNDLE_REL = path4.join("bin", "agentchat.mjs");
6089
+ var BUNDLE_REL = path5.join("bin", "agentchat.mjs");
6022
6090
  function codexConfigPath() {
6023
- return path4.join(codexHome(), "config.toml");
6091
+ return path5.join(codexHome(), "config.toml");
6024
6092
  }
6025
6093
  function codexHooksPath() {
6026
- return path4.join(codexHome(), "hooks.json");
6094
+ return path5.join(codexHome(), "hooks.json");
6095
+ }
6096
+ function codexIdentityHome() {
6097
+ return hostHome("codex");
6027
6098
  }
6028
6099
  function stableBundlePath() {
6029
- return path4.join(agentchatHome(), BUNDLE_REL);
6100
+ return path5.join(codexIdentityHome(), BUNDLE_REL);
6030
6101
  }
6031
6102
  function renderCodexAgents(handle) {
6032
6103
  return [
@@ -6049,7 +6120,11 @@ function renderCodexAgents(handle) {
6049
6120
  ANCHOR_END
6050
6121
  ].join("\n");
6051
6122
  }
6123
+ function tomlString(s) {
6124
+ return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
6125
+ }
6052
6126
  function mcpBlock() {
6127
+ const idHome = codexIdentityHome();
6053
6128
  return [
6054
6129
  TOML_START,
6055
6130
  "[mcp_servers.agentchat]",
@@ -6059,12 +6134,13 @@ function mcpBlock() {
6059
6134
  "# Auto-run AgentChat tools without a prompt, scoped to THIS server only \u2014",
6060
6135
  "# we never touch your global approval_policy or sandbox.",
6061
6136
  'default_tools_approval_mode = "approve"',
6062
- "# Forward our identity env to the server. Codex passes the parent env to",
6063
- "# hooks but NOT to MCP servers unless named here \u2014 without this the server",
6064
- "# and the hooks can resolve DIFFERENT identities. Empty/unset in the",
6065
- "# normal case (server reads ~/.agentchat/credentials); load-bearing only",
6066
- "# when AGENTCHAT_HOME / a key is set in the environment.",
6067
- 'env_vars = ["AGENTCHAT_HOME", "AGENTCHAT_API_KEY", "AGENTCHAT_API_BASE"]',
6137
+ "",
6138
+ "# This Codex agent's OWN identity home. Codex does NOT pass the parent",
6139
+ "# env to MCP servers, so we set it here explicitly \u2014 this is what makes",
6140
+ "# the Codex agent a distinct AgentChat account from any Claude Code",
6141
+ "# agent on the same machine (each host = its own peer).",
6142
+ "[mcp_servers.agentchat.env]",
6143
+ `AGENTCHAT_HOME = ${tomlString(idHome)}`,
6068
6144
  TOML_END
6069
6145
  ].join("\n");
6070
6146
  }
@@ -6135,9 +6211,9 @@ function unmergeHooks(existing) {
6135
6211
  }
6136
6212
  function copyBundle(bundleSrc) {
6137
6213
  const dest = stableBundlePath();
6138
- fs4.mkdirSync(path4.dirname(dest), { recursive: true });
6139
- const srcResolved = path4.resolve(bundleSrc);
6140
- if (srcResolved !== path4.resolve(dest)) {
6214
+ fs4.mkdirSync(path5.dirname(dest), { recursive: true });
6215
+ const srcResolved = path5.resolve(bundleSrc);
6216
+ if (srcResolved !== path5.resolve(dest)) {
6141
6217
  fs4.copyFileSync(srcResolved, dest);
6142
6218
  }
6143
6219
  return dest;
@@ -6182,7 +6258,7 @@ function installCodex(bundleSrc, handle) {
6182
6258
  }
6183
6259
  if (handle) {
6184
6260
  try {
6185
- const agentsPath = path4.join(codexHome(), "AGENTS.md");
6261
+ const agentsPath = path5.join(codexHome(), "AGENTS.md");
6186
6262
  const existing = fs4.existsSync(agentsPath) ? fs4.readFileSync(agentsPath, "utf-8") : "";
6187
6263
  const next = upsertAnchorBlock(existing, renderCodexAgents(handle));
6188
6264
  fs4.writeFileSync(agentsPath, next, "utf-8");
@@ -6260,7 +6336,7 @@ var RESTART_HINT = "Restart your agent session (Claude Code: start a new session
6260
6336
  function autoAnchor(handle) {
6261
6337
  const lines = [];
6262
6338
  const ccFile = anchorFilePath("claude-code");
6263
- if (ccFile !== null && fs5.existsSync(path5.dirname(ccFile))) {
6339
+ if (ccFile !== null && fs5.existsSync(path6.dirname(ccFile))) {
6264
6340
  try {
6265
6341
  installAnchor("claude-code", handle);
6266
6342
  lines.push(` anchor claude-code: written \u2192 ${ccFile}`);
@@ -6507,11 +6583,65 @@ async function runRecover(opts) {
6507
6583
  return 1;
6508
6584
  }
6509
6585
  }
6586
+ async function withHome(home, fn) {
6587
+ const prev = process.env["AGENTCHAT_HOME"];
6588
+ process.env["AGENTCHAT_HOME"] = home;
6589
+ try {
6590
+ return await fn();
6591
+ } finally {
6592
+ if (prev === void 0) delete process.env["AGENTCHAT_HOME"];
6593
+ else process.env["AGENTCHAT_HOME"] = prev;
6594
+ }
6595
+ }
6596
+ var STATUS_HOSTS = [
6597
+ ["claude-code", "Claude Code"],
6598
+ ["codex", "Codex"]
6599
+ ];
6510
6600
  async function runStatus(opts) {
6601
+ const bound = (process.env["AGENTCHAT_HOME"] ?? "").trim().length > 0;
6602
+ if (bound) return statusOne(opts.json ?? false);
6603
+ const rows = [];
6604
+ for (const [platform, label] of STATUS_HOSTS) {
6605
+ const home = hostHome(platform);
6606
+ if (readCredentialsFileAt(home) !== null) rows.push({ label, home });
6607
+ }
6608
+ const legacy = readCredentialsFileAt(legacyMachineHome());
6609
+ if (rows.length === 0 && legacy === null) {
6610
+ console.log(
6611
+ opts.json ? JSON.stringify({ identities: [] }) : "No AgentChat identities on this machine yet. Each coding agent gets its own \u2014 open Claude Code or Codex and it will offer to set one up, or run: agentchat register --platform <claude-code|codex> --email <email> --handle <handle>"
6612
+ );
6613
+ return 0;
6614
+ }
6615
+ if (opts.json) {
6616
+ const out = [];
6617
+ for (const r of rows) out.push({ host: r.label, ...await withHome(r.home, () => statusData()) });
6618
+ if (legacy) out.push({ host: "legacy (~/.agentchat, shared)", handle: legacy.handle, legacy: true });
6619
+ console.log(JSON.stringify({ identities: out }));
6620
+ return 0;
6621
+ }
6622
+ for (const r of rows) {
6623
+ console.log(`\u2500\u2500 ${r.label} \u2500\u2500`);
6624
+ await withHome(r.home, () => statusOne(false));
6625
+ }
6626
+ if (legacy) {
6627
+ console.log("\u2500\u2500 legacy (~/.agentchat, machine-shared) \u2500\u2500");
6628
+ console.log(`@${legacy.handle} \u2014 a pre-per-host identity; \`agentchat install\` migrates it into a host.`);
6629
+ }
6630
+ return 0;
6631
+ }
6632
+ async function statusData() {
6633
+ const identity = resolveIdentity();
6634
+ if (identity === null) return { handle: "?", status: "unconfigured", unread: 0 };
6635
+ const client = new AgentChatClient({ apiKey: identity.apiKey, baseUrl: identity.apiBase });
6636
+ const me = await client.getMe();
6637
+ const rows = await syncPeek({ apiKey: identity.apiKey, apiBase: identity.apiBase }, { limit: 100 });
6638
+ return { handle: me.handle, status: me.status ?? "unknown", unread: rows.length };
6639
+ }
6640
+ async function statusOne(json) {
6511
6641
  const identity = resolveIdentity();
6512
6642
  const pending = readPending();
6513
6643
  if (identity === null) {
6514
- if (opts.json) {
6644
+ if (json) {
6515
6645
  console.log(
6516
6646
  JSON.stringify({ configured: false, pending: pending !== null, pending_kind: pending?.kind ?? null })
6517
6647
  );
@@ -6524,7 +6654,7 @@ async function runStatus(opts) {
6524
6654
  `No identity yet, but a registration for @${pending.handle ?? "?"} is waiting on its emailed code \u2014 finish with: agentchat register --code <code>`
6525
6655
  );
6526
6656
  } else {
6527
- console.log("No AgentChat identity on this machine. Set one up with: agentchat register");
6657
+ console.log("No AgentChat identity for this host. Set one up with: agentchat register");
6528
6658
  }
6529
6659
  return 0;
6530
6660
  }
@@ -6540,7 +6670,7 @@ async function runStatus(opts) {
6540
6670
  "claude-code": hasAnchor("claude-code"),
6541
6671
  codex: hasAnchor("codex")
6542
6672
  };
6543
- if (opts.json) {
6673
+ if (json) {
6544
6674
  console.log(
6545
6675
  JSON.stringify({
6546
6676
  configured: true,
@@ -6571,24 +6701,59 @@ async function runStatus(opts) {
6571
6701
  }
6572
6702
  }
6573
6703
  function runLogout() {
6574
- const removed = clearCredentials();
6704
+ const bound = (process.env["AGENTCHAT_HOME"] ?? "").trim().length > 0;
6575
6705
  const reports = [];
6576
- try {
6577
- const result = removeAnchor("claude-code");
6578
- if (result.action === "removed") reports.push(" Claude Code anchor: removed");
6579
- } catch {
6580
- reports.push(" Claude Code anchor: could not clean up (remove the agentchat block manually)");
6581
- }
6582
- try {
6583
- const codexRemoved = removeCodex();
6584
- if (codexRemoved.length > 0) reports.push(` Codex: removed ${codexRemoved.join(", ")}`);
6585
- } catch {
6586
- reports.push(" Codex: could not fully clean up (check ~/.codex/config.toml + hooks.json)");
6706
+ let any = false;
6707
+ const logoutHost = (platform, label) => {
6708
+ const home = hostHome(platform);
6709
+ const prev = process.env["AGENTCHAT_HOME"];
6710
+ process.env["AGENTCHAT_HOME"] = home;
6711
+ try {
6712
+ if (clearCredentials()) {
6713
+ any = true;
6714
+ reports.push(` ${label}: credentials deleted`);
6715
+ }
6716
+ if (platform === "claude-code") {
6717
+ const r = removeAnchor("claude-code");
6718
+ if (r.action === "removed") reports.push(" Claude Code: anchor removed");
6719
+ } else if (platform === "codex") {
6720
+ const removed = removeCodex();
6721
+ if (removed.length > 0) reports.push(` Codex: removed ${removed.join(", ")}`);
6722
+ }
6723
+ } catch {
6724
+ reports.push(` ${label}: could not fully clean up`);
6725
+ } finally {
6726
+ if (prev === void 0) delete process.env["AGENTCHAT_HOME"];
6727
+ else process.env["AGENTCHAT_HOME"] = prev;
6728
+ }
6729
+ };
6730
+ if (bound) {
6731
+ if (clearCredentials()) any = true;
6732
+ try {
6733
+ removeAnchor("claude-code");
6734
+ } catch {
6735
+ }
6736
+ try {
6737
+ removeCodex();
6738
+ } catch {
6739
+ }
6740
+ } else {
6741
+ logoutHost("claude-code", "Claude Code");
6742
+ logoutHost("codex", "Codex");
6743
+ const prev = process.env["AGENTCHAT_HOME"];
6744
+ process.env["AGENTCHAT_HOME"] = legacyMachineHome();
6745
+ try {
6746
+ if (clearCredentials()) {
6747
+ any = true;
6748
+ reports.push(" legacy (~/.agentchat): credentials deleted");
6749
+ }
6750
+ } finally {
6751
+ if (prev === void 0) delete process.env["AGENTCHAT_HOME"];
6752
+ else process.env["AGENTCHAT_HOME"] = prev;
6753
+ }
6587
6754
  }
6588
6755
  console.log(
6589
- [removed ? "Signed out \u2014 local credentials deleted." : "Nothing to sign out of.", ...reports].join(
6590
- "\n"
6591
- )
6756
+ [any ? "Signed out." : "Nothing to sign out of.", ...reports].join("\n")
6592
6757
  );
6593
6758
  return 0;
6594
6759
  }
@@ -6596,7 +6761,7 @@ function runLogout() {
6596
6761
  // src/commands/install.ts
6597
6762
  import * as fs6 from "fs";
6598
6763
  import * as os3 from "os";
6599
- import * as path6 from "path";
6764
+ import * as path7 from "path";
6600
6765
  import { spawnSync } from "child_process";
6601
6766
  var MARKETPLACE_SLUG = "agentchatme/agentchat-coding-agents";
6602
6767
  var PLUGIN_REF = "agentchat@agentchatme";
@@ -6613,11 +6778,11 @@ function defaultRun(cmd, args) {
6613
6778
  function binaryOnPath(binary, env) {
6614
6779
  const pathVar = env["PATH"] ?? "";
6615
6780
  const exts = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
6616
- for (const dir of pathVar.split(path6.delimiter)) {
6781
+ for (const dir of pathVar.split(path7.delimiter)) {
6617
6782
  if (dir.length === 0) continue;
6618
6783
  for (const ext of exts) {
6619
6784
  try {
6620
- if (fs6.existsSync(path6.join(dir, binary + ext))) return true;
6785
+ if (fs6.existsSync(path7.join(dir, binary + ext))) return true;
6621
6786
  } catch {
6622
6787
  }
6623
6788
  }
@@ -6626,7 +6791,7 @@ function binaryOnPath(binary, env) {
6626
6791
  }
6627
6792
  function detectPlatforms(env, home) {
6628
6793
  return PROBES.filter(
6629
- (p) => binaryOnPath(p.binary, env) || fs6.existsSync(path6.join(home, p.configDir))
6794
+ (p) => binaryOnPath(p.binary, env) || fs6.existsSync(path7.join(home, p.configDir))
6630
6795
  );
6631
6796
  }
6632
6797
  async function runInstall(deps = {}) {
@@ -6666,7 +6831,7 @@ async function runInstall(deps = {}) {
6666
6831
  }
6667
6832
  case "codex": {
6668
6833
  const bundleSrc = process.argv[1] ?? "";
6669
- const handle = resolveIdentity()?.handle ?? null;
6834
+ const handle = readCredentialsFileAt(codexIdentityHome())?.handle ?? null;
6670
6835
  try {
6671
6836
  const { actions, warnings } = installCodex(bundleSrc, handle);
6672
6837
  console.log(` Codex: wired \u2713 (${actions.join(", ") || "no changes"})`);
@@ -6684,27 +6849,36 @@ async function runInstall(deps = {}) {
6684
6849
  break;
6685
6850
  }
6686
6851
  }
6687
- if (resolveIdentity() === null) {
6852
+ const need = [];
6853
+ const have = [];
6854
+ for (const platform of detected) {
6855
+ if (platform.key === "cursor") continue;
6856
+ const handle = readCredentialsFileAt(hostHome(platform.key))?.handle ?? null;
6857
+ if (handle) have.push(`${platform.label} \u2192 @${handle}`);
6858
+ else need.push(platform.key);
6859
+ }
6860
+ if (have.length > 0) console.log(`
6861
+ Signed in: ${have.join(", ")}`);
6862
+ if (need.length > 0) {
6688
6863
  console.log(
6689
6864
  [
6690
6865
  "",
6691
- "Next: give your agent its identity \u2014",
6692
- " agentchat register --email <email> --handle <handle>",
6693
- "or just start a session and let the agent walk you through it."
6866
+ `Each coding agent gets its OWN handle (so your agents can message each other). Still needed: ${need.join(", ")}.`,
6867
+ "Just open the agent and it will offer to set one up, or register per host:",
6868
+ ...need.map((p) => ` agentchat register --platform ${p} --email <email> --handle <handle>`),
6869
+ "(use a separate email per agent)."
6694
6870
  ].join("\n")
6695
6871
  );
6696
- } else {
6697
- console.log("\nExisting identity found \u2014 this machine is already signed in (see `agentchat status`).");
6698
6872
  }
6699
6873
  return failures === 0 ? 0 : 1;
6700
6874
  }
6701
6875
 
6702
6876
  // src/commands/doctor.ts
6703
6877
  import * as fs7 from "fs";
6704
- import * as path7 from "path";
6878
+ import * as path8 from "path";
6705
6879
 
6706
6880
  // src/version.ts
6707
- var VERSION2 = "0.0.131";
6881
+ var VERSION2 = "0.0.133";
6708
6882
 
6709
6883
  // src/commands/doctor.ts
6710
6884
  function fmt(check) {
@@ -6770,7 +6944,7 @@ async function runDoctor() {
6770
6944
  for (const platform of ["claude-code", "codex"]) {
6771
6945
  const file = anchorFilePath(platform);
6772
6946
  if (file === null) continue;
6773
- const hostDir = path7.dirname(file);
6947
+ const hostDir = path8.dirname(file);
6774
6948
  if (!fs7.existsSync(hostDir)) {
6775
6949
  checks.push({ name: `anchor-${platform}`, verdict: "PASS", detail: `${hostDir} absent (host not installed) \u2014 skipped` });
6776
6950
  } else {
@@ -6872,6 +7046,9 @@ async function main(argv = process.argv.slice(2)) {
6872
7046
  return 0;
6873
7047
  }
6874
7048
  const requirePlatform = () => resolvePlatform(values.platform);
7049
+ if (values.platform !== void 0 && isPlatform(values.platform)) {
7050
+ bindHostHome(values.platform);
7051
+ }
6875
7052
  switch (command) {
6876
7053
  case "install":
6877
7054
  return runInstall();