baychat 0.27.0 → 0.27.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/README.md CHANGED
@@ -113,7 +113,7 @@ Updating npm alone does not refresh a skill already loaded in a running session.
113
113
 
114
114
  **No Monitor deadline with the channel.** `baychat connect claude` also registers
115
115
  `baychat_channel`, a local Claude Code channel. Start Claude Code with
116
- `baychat start` (or `claude --dangerously-load-development-channels server:baychat_channel`)
116
+ `baychat claude` (or `baychat start`, which uses tmux when installed)
117
117
  and the skill calls its `listen` tool instead of opening a Monitor: the channel holds the
118
118
  feed, reconnects by itself, and wakes the session only when a message arrives. Started
119
119
  without the flag, `listen` answers `inactive` and the Monitor path above is used.
@@ -47,12 +47,19 @@ exports.detectChannel = detectChannel;
47
47
  * replaced. So `listen` answers active only on evidence, and otherwise sends the session back
48
48
  * to `listen_messages` + Monitor.
49
49
  *
50
- * The evidence is the command line Claude Code itself was started with: an ancestor process
51
- * whose arguments carry `--dangerously-load-development-channels` or `--channels` naming
52
- * `server:baychat_channel`. Claude Code spawns this server directly, so it is the parent; a few
53
- * more levels are walked for launchers that wrap it. Where process arguments cannot be read
54
- * (Windows), `BAYCHAT_CHANNEL=1` — set by `baychat start` on the process it launches with the
55
- * flag — is accepted instead.
50
+ * Two kinds of evidence, either enough:
51
+ *
52
+ * 1. `BAYCHAT_CHANNEL=1` in this process's environment. `baychat claude` and `baychat start` set
53
+ * it on the Claude Code they launch WITH the flag, and Claude Code passes its environment to
54
+ * the servers it spawns (checked on 2.1.284). Set per launch, never exported, so a plain
55
+ * `claude` does not inherit it.
56
+ * 2. The flag on an ancestor's command line: `--dangerously-load-development-channels` or
57
+ * `--channels` naming `server:baychat_channel`, for people who type the flag themselves.
58
+ * NOT reliable on its own: Claude Code sets `process.title = "claude"` at start, and where it
59
+ * runs on Node (an npm install, e.g. under nvm on WSL) that overwrites /proc/<pid>/cmdline,
60
+ * so the flags vanish. Found 2026-10-02 on the owner's WSL: launched with the flag, `ps`
61
+ * showed only `claude`, and `listen` wrongly answered inactive. A wrong "inactive" is safe —
62
+ * the session falls back to a Monitor — but it is why (1) exists.
56
63
  */
57
64
  const node_child_process_1 = require("node:child_process");
58
65
  const fs = __importStar(require("node:fs"));
@@ -121,25 +128,18 @@ function realProcessReader(platform = process.platform) {
121
128
  }
122
129
  /** Look for the flag on this process's ancestors, then fall back to the env marker. */
123
130
  function detectChannel(opts = {}) {
124
- const read = opts.read ?? realProcessReader();
125
131
  const env = opts.env ?? process.env;
132
+ if (env.BAYCHAT_CHANNEL === "1")
133
+ return { active: true, how: "env" };
134
+ const read = opts.read ?? realProcessReader();
126
135
  let pid = opts.pid ?? process.ppid;
127
- let readable = false;
128
136
  for (let depth = 0; depth < MAX_DEPTH && pid > 1; depth++) {
129
137
  const info = read(pid);
130
138
  if (!info)
131
139
  break;
132
- if (info.args) {
133
- readable = true;
134
- if (argsEnableChannel(info.args))
135
- return { active: true, how: "process" };
136
- }
140
+ if (info.args && argsEnableChannel(info.args))
141
+ return { active: true, how: "process" };
137
142
  pid = info.ppid;
138
143
  }
139
- // Only where nothing could be read: a readable tree without the flag is a real "no", and an
140
- // inherited marker must not overrule it (a plain `claude` started inside a `baychat start`
141
- // terminal would carry the marker without the flag).
142
- if (!readable && env.BAYCHAT_CHANNEL === "1")
143
- return { active: true, how: "env" };
144
144
  return { active: false, how: "none" };
145
145
  }
@@ -33,9 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.CHANNEL_LAUNCH_COMMAND = exports.CHANNEL_LAUNCH_ARGS = exports.CHANNEL_MIN_CLAUDE = void 0;
36
+ exports.CHANNEL_ENV = exports.CHANNEL_LAUNCH_COMMAND = exports.CHANNEL_LAUNCH_ARGS = exports.CHANNEL_MIN_CLAUDE = void 0;
37
37
  exports.parseClaudeVersion = parseClaudeVersion;
38
38
  exports.claudeSupportsChannel = claudeSupportsChannel;
39
+ exports.channelLaunchFor = channelLaunchFor;
40
+ exports.cmdClaude = cmdClaude;
39
41
  exports.claudeChannelAddArgs = claudeChannelAddArgs;
40
42
  exports.registerChannelWithClaude = registerChannelWithClaude;
41
43
  exports.claudeUserConfigPath = claudeUserConfigPath;
@@ -47,9 +49,11 @@ exports.channelRegistered = channelRegistered;
47
49
  * AND each session must be started with the channel flag — being in the MCP config is not enough
48
50
  * for a channel to deliver anything (docs: "a server also has to be named in --channels").
49
51
  */
52
+ const node_child_process_1 = require("node:child_process");
50
53
  const fs = __importStar(require("node:fs"));
51
54
  const os = __importStar(require("node:os"));
52
55
  const path = __importStar(require("node:path"));
56
+ const runtime_binary_1 = require("../runtime-binary");
53
57
  const detect_1 = require("./detect");
54
58
  /**
55
59
  * The first Claude Code this is switched on for. Channels exist from earlier releases, but
@@ -76,7 +80,51 @@ function claudeSupportsChannel(version) {
76
80
  /** The arguments `baychat start` adds to a Claude Code launch. */
77
81
  exports.CHANNEL_LAUNCH_ARGS = [detect_1.DEV_CHANNELS_FLAG, detect_1.CHANNEL_FLAG_VALUE];
78
82
  /** What to type to start Claude Code with the channel on. */
79
- exports.CHANNEL_LAUNCH_COMMAND = `claude ${exports.CHANNEL_LAUNCH_ARGS.join(" ")}`;
83
+ exports.CHANNEL_LAUNCH_COMMAND = "baychat claude";
84
+ /**
85
+ * The marker the channel trusts (detect.ts). Set on the ONE process launched, never exported:
86
+ * Claude Code can hide its own flags, so the launcher vouches for them instead.
87
+ */
88
+ exports.CHANNEL_ENV = { BAYCHAT_CHANNEL: "1" };
89
+ /**
90
+ * Decide for one Claude Code binary. Plain (and the session uses a Monitor) when the channel
91
+ * server is not registered or this Claude Code is too old for the flag — an unknown flag would
92
+ * stop it from starting at all.
93
+ */
94
+ function channelLaunchFor(claudePath, deps = {}) {
95
+ const registered = deps.registered ?? (() => channelRegistered());
96
+ if (!registered())
97
+ return { on: false, args: [], env: {}, why: "the BayChat channel is not set up here (run `baychat connect claude`)" };
98
+ const text = (deps.version ?? probeVersion)(claudePath);
99
+ if (!claudeSupportsChannel(text ? parseClaudeVersion(text) : undefined)) {
100
+ return { on: false, args: [], env: {}, why: `Claude Code ${exports.CHANNEL_MIN_CLAUDE.join(".")} or newer is needed for the channel (run \`claude update\`)` };
101
+ }
102
+ return { on: true, args: [...exports.CHANNEL_LAUNCH_ARGS], env: { ...exports.CHANNEL_ENV } };
103
+ }
104
+ function probeVersion(claudePath) {
105
+ const probe = (0, node_child_process_1.spawnSync)(claudePath, ["--version"], { encoding: "utf8", timeout: 10_000 });
106
+ return probe.error || probe.status !== 0 ? undefined : String(probe.stdout ?? "");
107
+ }
108
+ /**
109
+ * `baychat claude [args…]` — Claude Code with the BayChat channel switched on, in this terminal.
110
+ * No multiplexer (that is `baychat start`), nothing else changed: every argument is passed on.
111
+ */
112
+ function cmdClaude(args) {
113
+ const binary = (0, runtime_binary_1.resolveRuntimeBinary)("claude", (0, runtime_binary_1.currentBinaryEnv)());
114
+ if (!binary.ok) {
115
+ console.error(`Could not start Claude Code: ${binary.reason}`);
116
+ return 1;
117
+ }
118
+ const launch = channelLaunchFor(binary.path);
119
+ if (!launch.on)
120
+ console.error(`Starting Claude Code without the BayChat channel: ${launch.why}. Messages will use the Monitor.`);
121
+ const result = (0, node_child_process_1.spawnSync)(binary.path, [...launch.args, ...args], { stdio: "inherit", env: { ...process.env, ...launch.env } });
122
+ if (result.error) {
123
+ console.error(`Could not start Claude Code: ${result.error.message}`);
124
+ return 1;
125
+ }
126
+ return result.status ?? 0;
127
+ }
80
128
  /** `claude mcp add` argv for the channel server: this CLI by absolute path, run as `channel`. */
81
129
  function claudeChannelAddArgs(launch) {
82
130
  return ["mcp", "add", "--scope", "user", detect_1.CHANNEL_MCP_ENTRY, "--", launch.node, launch.entry, "channel"];
@@ -180,7 +180,8 @@ class ChannelService {
180
180
  if (!evidence.active) {
181
181
  return toolResult("inactive", `Inactive: this Claude Code session was not started with the BayChat channel, so its events would be ` +
182
182
  `dropped. Use listen_messages and a native Monitor for session "${session}" as usual. To use the channel ` +
183
- `next time, start Claude Code with \`baychat start\`, or \`claude ${detect_1.DEV_CHANNELS_FLAG} ${detect_1.CHANNEL_FLAG_VALUE}\`.`, { session });
183
+ `next time, start Claude Code with \`baychat claude\` (or \`baychat start\`) — the plain \`claude ${detect_1.DEV_CHANNELS_FLAG} ${detect_1.CHANNEL_FLAG_VALUE}\` ` +
184
+ `works too, except where Claude Code hides its own flags (an npm/Node install).`, { session });
184
185
  }
185
186
  let bayName;
186
187
  try {
@@ -77,8 +77,7 @@ function registerChannel() {
77
77
  function reportChannel(result) {
78
78
  if (result === "registered") {
79
79
  console.log("✓ BayChat channel added to Claude Code (wakes a session with no Monitor deadline)");
80
- console.log(" Start sessions with `baychat start`, or type:");
81
- console.log(` ${launch_1.CHANNEL_LAUNCH_COMMAND}`);
80
+ console.log(` Start Claude Code with \`${launch_1.CHANNEL_LAUNCH_COMMAND}\` (or \`baychat start\`) to use it.`);
82
81
  console.log(" Without it, incoming messages use the native WebSocket Monitor as before.");
83
82
  return;
84
83
  }
package/dist/main.js CHANGED
@@ -16,6 +16,7 @@ const connect_plan_1 = require("./connect-plan");
16
16
  const runtimes_1 = require("./runtimes");
17
17
  const mcp_1 = require("./mcp");
18
18
  const server_1 = require("./channel/server");
19
+ const launch_1 = require("./channel/launch");
19
20
  const mcp_config_1 = require("./mcp-config");
20
21
  const commands_2 = require("./relay/commands");
21
22
  const bays_1 = require("./bays");
@@ -473,6 +474,10 @@ async function main() {
473
474
  (0, mcp_config_1.cmdMcpConfig)(args.includes("--client") ? ((0, args_1.flag)(args, "--client") ?? "") : undefined);
474
475
  return 0;
475
476
  }
477
+ case "claude":
478
+ // Claude Code with the BayChat channel on (docs/features/CLAUDE_CODE_CHANNEL.md). Every
479
+ // argument after `claude` is Claude Code's own.
480
+ return (0, launch_1.cmdClaude)(args);
476
481
  case "channel": {
477
482
  // The Claude Code channel server (docs/features/CLAUDE_CODE_CHANNEL.md). Claude Code starts
478
483
  // it; like `mcp`, it blocks on stdio and exits when Claude Code closes its stdin.
@@ -136,8 +136,9 @@ async function cmdStart(args) {
136
136
  // `connect claude` registered the server and this Claude Code is new enough to take the flag
137
137
  // (an unknown flag would stop it starting). Otherwise it starts exactly as before and the
138
138
  // session listens through a Monitor.
139
- const agentArgs = runtime === "claude" ? channelArgs(binary.path) : [];
140
- const env = agentArgs.length ? { ...process.env, BAYCHAT_CHANNEL: "1" } : process.env;
139
+ const channel = runtime === "claude" ? (0, launch_1.channelLaunchFor)(binary.path) : undefined;
140
+ const agentArgs = channel?.args ?? [];
141
+ const env = { ...process.env, ...(channel?.env ?? {}) };
141
142
  // ALREADY INSIDE ONE? Then just run the agent. Nesting multiplexers sends every prefix
142
143
  // key to the wrong one, and the person already has everything this command provides.
143
144
  const inside = (0, terminal_pane_1.detectPane)();
@@ -241,12 +242,3 @@ function valueAfter(args, flagName) {
241
242
  const index = args.indexOf(flagName);
242
243
  return index === -1 ? undefined : args[index + 1];
243
244
  }
244
- /** The channel flag for this Claude Code, or nothing when it is not registered or too old. */
245
- function channelArgs(claudePath) {
246
- if (!(0, launch_1.channelRegistered)())
247
- return [];
248
- const probe = (0, child_process_1.spawnSync)(claudePath, ["--version"], { encoding: "utf8", timeout: 10_000 });
249
- if (probe.error || probe.status !== 0)
250
- return [];
251
- return (0, launch_1.claudeSupportsChannel)((0, launch_1.parseClaudeVersion)(String(probe.stdout ?? ""))) ? [...launch_1.CHANNEL_LAUNCH_ARGS] : [];
252
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baychat",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
4
4
  "description": "BayChat connector CLI — pair an agent session (Claude Code, Codex, Cursor) with BayChat, or have the relay run DeepSeek or the Cursor CLI for you",
5
5
  "bin": {
6
6
  "baychat": "dist/index.js"