patchcord 0.6.36 → 0.6.37

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "patchcord",
3
3
  "description": "Cross-machine agent messaging. Messages from other agents land in the inbox and wake the agent to reply.",
4
- "version": "0.6.36",
4
+ "version": "0.6.37",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/bin/patchcord.mjs CHANGED
@@ -1735,6 +1735,7 @@ if (cmd === "login" || cmd === "orchestrator" || cmd === "teamlead" || cmd === "
1735
1735
  // supersede anything.
1736
1736
  if (!forceRemint) {
1737
1737
  let reusable = null;
1738
+ let undetermined = null; // a candidate we could NOT judge — see below
1738
1739
  try {
1739
1740
  const { listProjectBearers } = await import(
1740
1741
  new URL("../scripts/lib/resolve-project-bearer.mjs", import.meta.url).href
@@ -1747,7 +1748,27 @@ if (cmd === "login" || cmd === "orchestrator" || cmd === "teamlead" || cmd === "
1747
1748
  seen.add(cand.token);
1748
1749
  const baseUrl = String(cand.url || "").replace(/\/mcp(\/bearer)?$/, "") || DEFAULT_API;
1749
1750
  const probe = await _httpJSON("GET", `${baseUrl}/api/agent/whoami`, cand.token);
1750
- if (probe.status !== "200") continue; // dead or superseded — skip
1751
+ const code = String(probe.status ?? "");
1752
+
1753
+ // THREE outcomes, not two. Collapsing them is the whole bug class this
1754
+ // command exists to stop causing:
1755
+ //
1756
+ // 401/403 the credential is genuinely dead (revoked in the web
1757
+ // console, or superseded). Skip it; minting is CORRECT.
1758
+ // 200 alive — check whose it is below.
1759
+ // anything else (000 from a failed connection, null when curl itself
1760
+ // died, any 5xx) we DO NOT KNOW. Treating "could not ask"
1761
+ // as "dead" would skip a HEALTHY token and mint, which
1762
+ // destroys the live session this command exists to protect
1763
+ // — and it would do that exactly when the backend is
1764
+ // already unwell. Absent, dead and unavailable are three
1765
+ // different answers.
1766
+ if (code === "401" || code === "403") continue;
1767
+ if (code !== "200") {
1768
+ undetermined = { token: cand.token, base: baseUrl, code: code || "no response" };
1769
+ continue;
1770
+ }
1771
+
1751
1772
  const who = probe.json || {};
1752
1773
  const gotNs = who.namespace_id || who.namespace || (who.self && who.self.namespace_id);
1753
1774
  const gotAgent = who.agent_id || who.agent || (who.self && who.self.agent_id);
@@ -1760,9 +1781,22 @@ if (cmd === "login" || cmd === "orchestrator" || cmd === "teamlead" || cmd === "
1760
1781
  }
1761
1782
  }
1762
1783
  } catch (e) {
1763
- // Reuse is an optimisation over the old behaviour; if it cannot run we
1764
- // fall through and mint, exactly as before.
1765
- console.error(` ${M.dim}(could not check for a reusable credential: ${e?.message || e})${M.rst}`);
1784
+ // A crash in the reuse scan is itself "undetermined", not "dead". Same
1785
+ // reasoning: never let a local failure authorise a destructive remint.
1786
+ undetermined = undetermined || { code: `scan failed: ${e?.message || e}` };
1787
+ }
1788
+
1789
+ // Could not determine, and found nothing provably alive: STOP. Do not
1790
+ // mint. Minting is the irreversible half of this command — it kills a
1791
+ // credential a running session may be holding — and an unreachable server
1792
+ // is not evidence that anything needs replacing. Retrying is free; a
1793
+ // revoked live session is not.
1794
+ if (!reusable && undetermined) {
1795
+ console.error(`✗ cannot verify the existing credential for ${M.green}${ns}:${arg}${M.rst} (${undetermined.code}).`);
1796
+ console.error(` Refusing to mint a replacement: that would revoke the token any`);
1797
+ console.error(` running session for this agent is currently using.`);
1798
+ console.error(` ${M.dim}Retry when the server is reachable, or force it deliberately with --force-remint.${M.rst}`);
1799
+ process.exit(1);
1766
1800
  }
1767
1801
  if (reusable) {
1768
1802
  writeWorkerConfig(tool, dir, reusable.base, reusable.token, hostname);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.36",
3
+ "version": "0.6.37",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "scripts": {
6
6
  "version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin/plugin.json"