baychat 0.17.0 → 0.17.1

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/connect.js CHANGED
@@ -32,8 +32,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32
32
  };
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.renderConnectMenu = renderConnectMenu;
35
+ exports.isClaudeConnectTarget = isClaudeConnectTarget;
35
36
  exports.parseConnectClient = parseConnectClient;
36
37
  exports.writeClientConfig = writeClientConfig;
38
+ exports.cmdConnectClaude = cmdConnectClaude;
37
39
  exports.cmdConnect = cmdConnect;
38
40
  const node_fs_1 = __importDefault(require("node:fs"));
39
41
  const node_path_1 = __importDefault(require("node:path"));
@@ -61,14 +63,32 @@ function renderConnectMenu() {
61
63
  "Connect this laptop to BayChat, once, and configure your client:",
62
64
  "",
63
65
  ...rows,
66
+ " npx baychat connect claude → Claude Code (refreshes its skill)",
64
67
  "",
65
68
  "You approve a QR on your phone. After that, in any coding session:",
66
69
  "",
67
70
  " /baychat <name> join BayChat as a session called <name>",
68
71
  "",
69
- "Claude Code needs no config — `npx baychat login` sets it up for you.",
72
+ "First-time Claude Code setup is `npx baychat login` it registers the MCP",
73
+ "server AND writes the skill. After that, `connect claude` refreshes the skill",
74
+ "on its own: no QR, no re-pairing. Run it after every CLI upgrade.",
70
75
  ].join("\n");
71
76
  }
77
+ /** Spellings of Claude Code accepted by `connect`. */
78
+ const CLAUDE_CONNECT_ALIASES = new Set(["claude", "claude-code", "claudecode"]);
79
+ /**
80
+ * Is this `connect` argument Claude Code?
81
+ *
82
+ * Claude Code is deliberately NOT in `MCP_CLIENTS`: that list is clients whose
83
+ * MCP config is a file we write, and Claude Code's is registered by
84
+ * `claude mcp add` instead. But refusing the word entirely made the runtime this
85
+ * product is mostly used from the only one you could not `connect` — and sent a
86
+ * user whose credential was perfectly valid to `login`, which opens a pairing QR,
87
+ * when all they needed was the skill file refreshed after a CLI upgrade.
88
+ */
89
+ function isClaudeConnectTarget(value) {
90
+ return CLAUDE_CONNECT_ALIASES.has(value.trim().toLowerCase());
91
+ }
72
92
  /**
73
93
  * Narrow a client argument.
74
94
  *
@@ -159,11 +179,38 @@ const realIo = {
159
179
  *
160
180
  * @returns 0 on success, 2 when the laptop login expired without approval.
161
181
  */
182
+ /**
183
+ * `baychat connect claude` — refresh Claude Code's skill.
184
+ *
185
+ * Deliberately does NOT touch the credential or the MCP registration. Both are
186
+ * `login`'s job, and re-doing them would put a device-pairing QR in front of
187
+ * somebody whose login is fine. Writing the skill needs no auth at all: it is
188
+ * generated locally from this CLI's own version, which is exactly why it goes
189
+ * stale on upgrade and exactly why this command has to exist.
190
+ */
191
+ function cmdConnectClaude() {
192
+ try {
193
+ for (const line of (0, runtime_install_1.describeInstall)((0, runtime_install_1.installRuntimeCommand)("claude")))
194
+ console.log(` ${line}`);
195
+ console.log("");
196
+ console.log(" Restart Claude Code once so it picks the skill up.");
197
+ console.log(" Not logged in yet? `npx baychat login` — that registers the MCP server too.");
198
+ return 0;
199
+ }
200
+ catch (err) {
201
+ console.log(`Could not install the Claude Code skill: ${err instanceof Error ? err.message : String(err)}`);
202
+ return 1;
203
+ }
204
+ }
162
205
  async function cmdConnect(clientArg, opts = {}) {
163
206
  if (clientArg === undefined) {
164
207
  console.log(renderConnectMenu());
165
208
  return 0;
166
209
  }
210
+ // Claude Code first: it is not an MCP-config client, so it must not reach
211
+ // `parseConnectClient`, which would reject it.
212
+ if (isClaudeConnectTarget(clientArg))
213
+ return cmdConnectClaude();
167
214
  const client = parseConnectClient(clientArg);
168
215
  const base = (opts.base || process.env.BAYCHAT_API_URL || config_1.DEFAULT_API_URL).replace(/\/$/, "");
169
216
  const steps = (0, connect_plan_1.planConnect)({
@@ -746,7 +746,7 @@ async function cmdRelayAttach(opts) {
746
746
  * bare name, which is exactly the behaviour that shipped before this existed — a
747
747
  * machine where PATH is fine keeps working, and one where it is not now works too.
748
748
  */
749
- function resolveRuntimeBin(runtime) {
749
+ function resolveRuntimeBin(runtime, env = process.env, execPath = process.execPath) {
750
750
  // Delegates to the prover rather than walking PATH itself. The hand-rolled
751
751
  // walk this replaces had two Windows faults, and both produced a recorded
752
752
  // path the daemon could never spawn:
@@ -763,7 +763,7 @@ function resolveRuntimeBin(runtime) {
763
763
  // `resolveRuntimeBinary` tries the platform's real extension order and PROVES
764
764
  // each candidate by running it, which is the same question this function was
765
765
  // always asking — just answered correctly.
766
- const decided = decideRuntimeBin(runtime);
766
+ const decided = decideRuntimeBin(runtime, env, execPath);
767
767
  return decided.ok ? decided.bin : undefined;
768
768
  }
769
769
  /**
@@ -775,16 +775,16 @@ function resolveRuntimeBin(runtime) {
775
775
  * then re-probed could still register `runtimeBin: undefined` — the exact state
776
776
  * the guard exists to prevent, reached by way of the guard.
777
777
  */
778
- function decideRuntimeBin(runtime) {
779
- const override = process.env[`BAYCHAT_${runtime.toUpperCase()}_BIN`];
780
- const resolved = (0, runtime_binary_1.resolveRuntimeBinary)(runtime, (0, runtime_binary_1.currentBinaryEnv)(override));
778
+ function decideRuntimeBin(runtime, env = process.env, execPath = process.execPath) {
779
+ const override = env[`BAYCHAT_${runtime.toUpperCase()}_BIN`];
780
+ const resolved = (0, runtime_binary_1.resolveRuntimeBinary)(runtime, (0, runtime_binary_1.currentBinaryEnv)(override, env, execPath));
781
781
  if (!resolved.ok) {
782
782
  // An explicit override is a user ASSERTION. If it cannot be proven, stop —
783
783
  // see the guard in `cmdRelayAttach`. Absence of one is not an assertion, so
784
784
  // a plain failed probe keeps the best-effort fallback.
785
785
  if (override)
786
786
  return { ok: false, reason: (0, runtime_binary_1.summarizeResolutionFailure)(resolved) };
787
- return { ok: true, bin: managedRuntimeBin(runtime) };
787
+ return { ok: true, bin: managedRuntimeBin(runtime, env) };
788
788
  }
789
789
  try {
790
790
  // Resolve symlinks: ~/.local/bin/claude is typically a link into a versioned
@@ -797,10 +797,10 @@ function decideRuntimeBin(runtime) {
797
797
  return { ok: true, bin: resolved.path };
798
798
  }
799
799
  }
800
- function managedRuntimeBin(runtime) {
800
+ function managedRuntimeBin(runtime, env = process.env) {
801
801
  if (runtime !== "codex")
802
802
  return undefined;
803
- const root = process.env.CODEX_MANAGED_PACKAGE_ROOT?.trim();
803
+ const root = env.CODEX_MANAGED_PACKAGE_ROOT?.trim();
804
804
  if (!root)
805
805
  return undefined;
806
806
  const candidate = path.join(root, "bin", "codex.js");
@@ -273,6 +273,24 @@ function currentBinaryEnv(override, env = process.env, execPath = process.execPa
273
273
  timeout: PROBE_TIMEOUT_MS,
274
274
  encoding: "utf8",
275
275
  shell: false,
276
+ // THE PROBE MUST BE ABLE TO START WHAT IT IS PROBING.
277
+ //
278
+ // An npm runtime is a `#!/usr/bin/env node` script, and a spawn inherits
279
+ // THIS process's environment — the daemon's, under systemd, with no node
280
+ // on it. So probing the real binary died with
281
+ // `/usr/bin/env: 'node': No such file or directory`, the candidate was
282
+ // rejected as broken rather than unstartable, and resolution fell through
283
+ // to `/snap/bin/codex`.
284
+ //
285
+ // Measured 2026-09-01 18:00 from the daemon's log, after the PATH search
286
+ // was already fixed: `codex resolved to /snap/bin/codex (codex-cli
287
+ // 0.114.0)`, then `queue did not deliver ... this codex build has no
288
+ // \`queue\` subcommand`. The snap is 0.114.0; `queue` landed in 0.149.0.
289
+ // So a probe that could not start a shebang script cost Codex the one
290
+ // rung a live session can answer on.
291
+ //
292
+ // Same environment the spawn will use, for the same reason.
293
+ env: (0, spawn_env_1.headlessSpawnEnv)(env, execPath),
276
294
  });
277
295
  return summarizeProbe({
278
296
  status: run.status,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baychat",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "BayChat connector CLI — pair an agent session (Claude Code, Codex) with BayChat and chat in groups",
5
5
  "bin": {
6
6
  "baychat": "dist/index.js"