baychat 0.27.0 → 0.28.0
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/.claude-plugin/plugin.json +12 -0
- package/README.md +2 -2
- package/dist/channel/detect.js +28 -20
- package/dist/channel/feed.js +1 -1
- package/dist/channel/launch.js +92 -3
- package/dist/channel/permission.js +118 -0
- package/dist/channel/server.js +103 -6
- package/dist/claude-onboarding.js +7 -5
- package/dist/connect-claude.js +7 -3
- package/dist/main.js +5 -0
- package/dist/skill-bootstrap.js +1 -1
- package/dist/start-command.js +3 -11
- package/package.json +2 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "baychat",
|
|
3
|
+
"displayName": "BayChat",
|
|
4
|
+
"description": "Wakes this Claude Code session the moment a BayChat message arrives: no Monitor, no 30-minute deadline. Uses the baychat CLI already on this computer (npm install -g baychat, then baychat login).",
|
|
5
|
+
"author": { "name": "BayChat", "url": "https://baychat.io" },
|
|
6
|
+
"homepage": "https://baychat.io",
|
|
7
|
+
"keywords": ["baychat", "chat", "channel", "mobile", "notifications"],
|
|
8
|
+
"mcpServers": {
|
|
9
|
+
"channel": { "command": "baychat", "args": ["channel"] }
|
|
10
|
+
},
|
|
11
|
+
"channels": [{ "server": "channel", "displayName": "BayChat" }]
|
|
12
|
+
}
|
package/README.md
CHANGED
|
@@ -112,8 +112,8 @@ the old shell/relay join is available only as an explicitly chosen fallback.
|
|
|
112
112
|
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
|
-
|
|
116
|
-
`baychat
|
|
115
|
+
a local Claude Code channel (as the `baychat` plugin, from https://baychat.io/claude/marketplace.json). Start Claude Code with
|
|
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.
|
package/dist/channel/detect.js
CHANGED
|
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.DEV_CHANNELS_FLAG = exports.CHANNEL_FLAG_VALUE = exports.CHANNEL_MCP_ENTRY = void 0;
|
|
36
|
+
exports.DEV_CHANNELS_FLAG = exports.PLUGIN_FLAG_VALUE = exports.PLUGIN_ID = exports.CHANNEL_FLAG_VALUE = exports.CHANNEL_MCP_ENTRY = void 0;
|
|
37
|
+
exports.isOurChannelValue = isOurChannelValue;
|
|
37
38
|
exports.argsEnableChannel = argsEnableChannel;
|
|
38
39
|
exports.realProcessReader = realProcessReader;
|
|
39
40
|
exports.detectChannel = detectChannel;
|
|
@@ -47,12 +48,19 @@ exports.detectChannel = detectChannel;
|
|
|
47
48
|
* replaced. So `listen` answers active only on evidence, and otherwise sends the session back
|
|
48
49
|
* to `listen_messages` + Monitor.
|
|
49
50
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* `
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
51
|
+
* Two kinds of evidence, either enough:
|
|
52
|
+
*
|
|
53
|
+
* 1. `BAYCHAT_CHANNEL=1` in this process's environment. `baychat claude` and `baychat start` set
|
|
54
|
+
* it on the Claude Code they launch WITH the flag, and Claude Code passes its environment to
|
|
55
|
+
* the servers it spawns (checked on 2.1.284). Set per launch, never exported, so a plain
|
|
56
|
+
* `claude` does not inherit it.
|
|
57
|
+
* 2. The flag on an ancestor's command line: `--dangerously-load-development-channels` or
|
|
58
|
+
* `--channels` naming `server:baychat_channel`, for people who type the flag themselves.
|
|
59
|
+
* NOT reliable on its own: Claude Code sets `process.title = "claude"` at start, and where it
|
|
60
|
+
* runs on Node (an npm install, e.g. under nvm on WSL) that overwrites /proc/<pid>/cmdline,
|
|
61
|
+
* so the flags vanish. Found 2026-10-02 on the owner's WSL: launched with the flag, `ps`
|
|
62
|
+
* showed only `claude`, and `listen` wrongly answered inactive. A wrong "inactive" is safe —
|
|
63
|
+
* the session falls back to a Monitor — but it is why (1) exists.
|
|
56
64
|
*/
|
|
57
65
|
const node_child_process_1 = require("node:child_process");
|
|
58
66
|
const fs = __importStar(require("node:fs"));
|
|
@@ -60,6 +68,13 @@ const fs = __importStar(require("node:fs"));
|
|
|
60
68
|
exports.CHANNEL_MCP_ENTRY = "baychat_channel";
|
|
61
69
|
/** What `--dangerously-load-development-channels` takes for a bare MCP server. */
|
|
62
70
|
exports.CHANNEL_FLAG_VALUE = `server:${exports.CHANNEL_MCP_ENTRY}`;
|
|
71
|
+
/** The plugin's id and its channel entry, when BayChat is installed as a Claude Code plugin. */
|
|
72
|
+
exports.PLUGIN_ID = "baychat@baychat";
|
|
73
|
+
exports.PLUGIN_FLAG_VALUE = `plugin:${exports.PLUGIN_ID}`;
|
|
74
|
+
/** Does one channel-flag value name BayChat's channel, as a server or as the plugin (any marketplace)? */
|
|
75
|
+
function isOurChannelValue(value) {
|
|
76
|
+
return value === exports.CHANNEL_FLAG_VALUE || /^plugin:baychat@[A-Za-z0-9._-]+$/.test(value);
|
|
77
|
+
}
|
|
63
78
|
exports.DEV_CHANNELS_FLAG = "--dangerously-load-development-channels";
|
|
64
79
|
const CHANNEL_FLAGS = [exports.DEV_CHANNELS_FLAG, "--channels"];
|
|
65
80
|
/** How far up the process tree to look. */
|
|
@@ -81,7 +96,7 @@ function argsEnableChannel(args) {
|
|
|
81
96
|
for (let j = i + 1; j < args.length && !args[j].startsWith("-"); j++)
|
|
82
97
|
values.push(...args[j].split(/[\s,]+/));
|
|
83
98
|
}
|
|
84
|
-
if (values.
|
|
99
|
+
if (values.some(isOurChannelValue))
|
|
85
100
|
return true;
|
|
86
101
|
}
|
|
87
102
|
return false;
|
|
@@ -121,25 +136,18 @@ function realProcessReader(platform = process.platform) {
|
|
|
121
136
|
}
|
|
122
137
|
/** Look for the flag on this process's ancestors, then fall back to the env marker. */
|
|
123
138
|
function detectChannel(opts = {}) {
|
|
124
|
-
const read = opts.read ?? realProcessReader();
|
|
125
139
|
const env = opts.env ?? process.env;
|
|
140
|
+
if (env.BAYCHAT_CHANNEL === "1")
|
|
141
|
+
return { active: true, how: "env" };
|
|
142
|
+
const read = opts.read ?? realProcessReader();
|
|
126
143
|
let pid = opts.pid ?? process.ppid;
|
|
127
|
-
let readable = false;
|
|
128
144
|
for (let depth = 0; depth < MAX_DEPTH && pid > 1; depth++) {
|
|
129
145
|
const info = read(pid);
|
|
130
146
|
if (!info)
|
|
131
147
|
break;
|
|
132
|
-
if (info.args)
|
|
133
|
-
|
|
134
|
-
if (argsEnableChannel(info.args))
|
|
135
|
-
return { active: true, how: "process" };
|
|
136
|
-
}
|
|
148
|
+
if (info.args && argsEnableChannel(info.args))
|
|
149
|
+
return { active: true, how: "process" };
|
|
137
150
|
pid = info.ppid;
|
|
138
151
|
}
|
|
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
152
|
return { active: false, how: "none" };
|
|
145
153
|
}
|
package/dist/channel/feed.js
CHANGED
|
@@ -183,7 +183,7 @@ class ChannelFeed {
|
|
|
183
183
|
this.stop();
|
|
184
184
|
await this.deps.push(`BayChat stopped listening for session "${this.deps.session}": ${minted.detail} ` +
|
|
185
185
|
`If the session expired, call join_session again with the same name and room, then call ` +
|
|
186
|
-
`
|
|
186
|
+
`this BayChat channel's listen tool again. Until then this session does not hear the Bay.`, this.meta({ event: "stopped" }));
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
this.scheduleReconnect(`could not reconnect (${minted.detail})`);
|
package/dist/channel/launch.js
CHANGED
|
@@ -33,13 +33,18 @@ 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.PLUGIN_MARKETPLACE_URL = 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;
|
|
42
44
|
exports.channelRegistered = channelRegistered;
|
|
45
|
+
exports.claudeUserSettingsPath = claudeUserSettingsPath;
|
|
46
|
+
exports.pluginEnabled = pluginEnabled;
|
|
47
|
+
exports.installPluginWithClaude = installPluginWithClaude;
|
|
43
48
|
/**
|
|
44
49
|
* Getting the channel into Claude Code: registering the server, and starting sessions with it on.
|
|
45
50
|
*
|
|
@@ -47,9 +52,11 @@ exports.channelRegistered = channelRegistered;
|
|
|
47
52
|
* AND each session must be started with the channel flag — being in the MCP config is not enough
|
|
48
53
|
* for a channel to deliver anything (docs: "a server also has to be named in --channels").
|
|
49
54
|
*/
|
|
55
|
+
const node_child_process_1 = require("node:child_process");
|
|
50
56
|
const fs = __importStar(require("node:fs"));
|
|
51
57
|
const os = __importStar(require("node:os"));
|
|
52
58
|
const path = __importStar(require("node:path"));
|
|
59
|
+
const runtime_binary_1 = require("../runtime-binary");
|
|
53
60
|
const detect_1 = require("./detect");
|
|
54
61
|
/**
|
|
55
62
|
* The first Claude Code this is switched on for. Channels exist from earlier releases, but
|
|
@@ -73,10 +80,62 @@ function claudeSupportsChannel(version) {
|
|
|
73
80
|
}
|
|
74
81
|
return true;
|
|
75
82
|
}
|
|
76
|
-
/** The arguments
|
|
83
|
+
/** The arguments that switch the hand-registered channel server on. */
|
|
77
84
|
exports.CHANNEL_LAUNCH_ARGS = [detect_1.DEV_CHANNELS_FLAG, detect_1.CHANNEL_FLAG_VALUE];
|
|
85
|
+
/** Where `claude plugin marketplace add` finds BayChat's plugin catalog (served by the web app). */
|
|
86
|
+
exports.PLUGIN_MARKETPLACE_URL = "https://baychat.io/claude/marketplace.json";
|
|
78
87
|
/** What to type to start Claude Code with the channel on. */
|
|
79
|
-
exports.CHANNEL_LAUNCH_COMMAND =
|
|
88
|
+
exports.CHANNEL_LAUNCH_COMMAND = "baychat claude";
|
|
89
|
+
/**
|
|
90
|
+
* The marker the channel trusts (detect.ts). Set on the ONE process launched, never exported:
|
|
91
|
+
* Claude Code can hide its own flags, so the launcher vouches for them instead.
|
|
92
|
+
*/
|
|
93
|
+
exports.CHANNEL_ENV = { BAYCHAT_CHANNEL: "1" };
|
|
94
|
+
/**
|
|
95
|
+
* Decide for one Claude Code binary. Plain (and the session uses a Monitor) when the channel
|
|
96
|
+
* server is not registered or this Claude Code is too old for the flag — an unknown flag would
|
|
97
|
+
* stop it from starting at all.
|
|
98
|
+
*/
|
|
99
|
+
function channelLaunchFor(claudePath, deps = {}) {
|
|
100
|
+
// Every way BayChat's channel is installed here, each named on the flag. Both can exist after a
|
|
101
|
+
// move to the plugin; Claude Code then keeps the hand-registered server and hides the plugin's
|
|
102
|
+
// duplicate, so naming both is what keeps the channel switched on either way.
|
|
103
|
+
const entries = [
|
|
104
|
+
...((deps.plugin ?? (() => pluginEnabled()))() ? [detect_1.PLUGIN_FLAG_VALUE] : []),
|
|
105
|
+
...((deps.registered ?? (() => channelRegistered()))() ? [detect_1.CHANNEL_FLAG_VALUE] : []),
|
|
106
|
+
];
|
|
107
|
+
if (!entries.length)
|
|
108
|
+
return { on: false, args: [], env: {}, why: "the BayChat channel is not set up here (run `baychat connect claude`)" };
|
|
109
|
+
const text = (deps.version ?? probeVersion)(claudePath);
|
|
110
|
+
if (!claudeSupportsChannel(text ? parseClaudeVersion(text) : undefined)) {
|
|
111
|
+
return { on: false, args: [], env: {}, why: `Claude Code ${exports.CHANNEL_MIN_CLAUDE.join(".")} or newer is needed for the channel (run \`claude update\`)` };
|
|
112
|
+
}
|
|
113
|
+
return { on: true, args: [detect_1.DEV_CHANNELS_FLAG, ...entries], env: { ...exports.CHANNEL_ENV } };
|
|
114
|
+
}
|
|
115
|
+
function probeVersion(claudePath) {
|
|
116
|
+
const probe = (0, node_child_process_1.spawnSync)(claudePath, ["--version"], { encoding: "utf8", timeout: 10_000 });
|
|
117
|
+
return probe.error || probe.status !== 0 ? undefined : String(probe.stdout ?? "");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* `baychat claude [args…]` — Claude Code with the BayChat channel switched on, in this terminal.
|
|
121
|
+
* No multiplexer (that is `baychat start`), nothing else changed: every argument is passed on.
|
|
122
|
+
*/
|
|
123
|
+
function cmdClaude(args) {
|
|
124
|
+
const binary = (0, runtime_binary_1.resolveRuntimeBinary)("claude", (0, runtime_binary_1.currentBinaryEnv)());
|
|
125
|
+
if (!binary.ok) {
|
|
126
|
+
console.error(`Could not start Claude Code: ${binary.reason}`);
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
const launch = channelLaunchFor(binary.path);
|
|
130
|
+
if (!launch.on)
|
|
131
|
+
console.error(`Starting Claude Code without the BayChat channel: ${launch.why}. Messages will use the Monitor.`);
|
|
132
|
+
const result = (0, node_child_process_1.spawnSync)(binary.path, [...launch.args, ...args], { stdio: "inherit", env: { ...process.env, ...launch.env } });
|
|
133
|
+
if (result.error) {
|
|
134
|
+
console.error(`Could not start Claude Code: ${result.error.message}`);
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
return result.status ?? 0;
|
|
138
|
+
}
|
|
80
139
|
/** `claude mcp add` argv for the channel server: this CLI by absolute path, run as `channel`. */
|
|
81
140
|
function claudeChannelAddArgs(launch) {
|
|
82
141
|
return ["mcp", "add", "--scope", "user", detect_1.CHANNEL_MCP_ENTRY, "--", launch.node, launch.entry, "channel"];
|
|
@@ -109,3 +168,33 @@ function channelRegistered(file = claudeUserConfigPath(), read = (p) => fs.readF
|
|
|
109
168
|
return false;
|
|
110
169
|
}
|
|
111
170
|
}
|
|
171
|
+
/** Claude Code's user settings, where installed plugins are switched on. */
|
|
172
|
+
function claudeUserSettingsPath(home = os.homedir()) {
|
|
173
|
+
return path.join(home, ".claude", "settings.json");
|
|
174
|
+
}
|
|
175
|
+
/** Is BayChat's plugin installed and switched on for this user? Unreadable counts as no. */
|
|
176
|
+
function pluginEnabled(file = claudeUserSettingsPath(), read = (p) => fs.readFileSync(p, "utf8")) {
|
|
177
|
+
try {
|
|
178
|
+
const parsed = JSON.parse(read(file));
|
|
179
|
+
return parsed?.enabledPlugins?.[detect_1.PLUGIN_ID] === true;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Install BayChat as a Claude Code plugin: add the catalog from baychat.io, install from it (the
|
|
187
|
+
* plugin itself is fetched from the `baychat` npm package), then remove the hand-registered
|
|
188
|
+
* server, which would otherwise hide the plugin's. Returns false at the first step that fails,
|
|
189
|
+
* leaving whatever worked before untouched — the caller then registers the server by hand.
|
|
190
|
+
*/
|
|
191
|
+
function installPluginWithClaude(run) {
|
|
192
|
+
const added = run(["plugin", "marketplace", "add", exports.PLUGIN_MARKETPLACE_URL]);
|
|
193
|
+
if (added.status !== 0 && !/already/i.test(added.stderr))
|
|
194
|
+
return false;
|
|
195
|
+
const installed = run(["plugin", "install", detect_1.PLUGIN_ID]);
|
|
196
|
+
if (installed.status !== 0 && !/already installed/i.test(installed.stderr))
|
|
197
|
+
return false;
|
|
198
|
+
run(["mcp", "remove", "--scope", "user", detect_1.CHANNEL_MCP_ENTRY]); // Absent is fine.
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PermissionRelay = exports.RELAY_OPTIONS = void 0;
|
|
4
|
+
exports.composeRelayQuestion = composeRelayQuestion;
|
|
5
|
+
/** Button labels, and the ONLY index that means yes. Same as `baychat approve-hook`. */
|
|
6
|
+
exports.RELAY_OPTIONS = ["Allow this once", "Deny"];
|
|
7
|
+
const ALLOW_INDEX = 0;
|
|
8
|
+
/** One window of the server's wait; it answers "waiting" when nobody tapped yet. */
|
|
9
|
+
const POLL_WAIT_SECONDS = 25;
|
|
10
|
+
const MAX_CONSECUTIVE_FAILURES = 5;
|
|
11
|
+
const RETRY_DELAY_MS = 3_000;
|
|
12
|
+
const QUESTION_MAX = 2_000;
|
|
13
|
+
const PREVIEW_MAX = 1_200;
|
|
14
|
+
function clip(text, max) {
|
|
15
|
+
return text.length <= max ? text : `${text.slice(0, max - 1)}…`;
|
|
16
|
+
}
|
|
17
|
+
/** What the person on the phone reads. They decide from this text alone. */
|
|
18
|
+
function composeRelayQuestion(req, host, session) {
|
|
19
|
+
const lines = [
|
|
20
|
+
`Claude Code (session "${session}" on ${host}) asks to use ${req.tool_name}: ${req.description}`,
|
|
21
|
+
"",
|
|
22
|
+
req.input_preview ? clip(req.input_preview, PREVIEW_MAX) : "",
|
|
23
|
+
"",
|
|
24
|
+
"The same question is open in the terminal; whichever answer comes first counts. " +
|
|
25
|
+
"Allowing runs it on that machine, now. If you did not expect this, deny.",
|
|
26
|
+
];
|
|
27
|
+
return clip(lines.join("\n").replace(/\n{3,}/g, "\n\n").trim(), QUESTION_MAX);
|
|
28
|
+
}
|
|
29
|
+
class PermissionRelay {
|
|
30
|
+
deps;
|
|
31
|
+
current;
|
|
32
|
+
constructor(deps) {
|
|
33
|
+
this.deps = deps;
|
|
34
|
+
}
|
|
35
|
+
/** Relay one dialog. Resolves when it is settled, superseded, or given up on. Never throws. */
|
|
36
|
+
async onRequest(req) {
|
|
37
|
+
await this.retire();
|
|
38
|
+
const target = this.deps.target();
|
|
39
|
+
if (!target)
|
|
40
|
+
return; // Nobody is listening through this channel: the terminal alone decides.
|
|
41
|
+
const pending = { claudeId: req.request_id, login: target.login, retired: false };
|
|
42
|
+
this.current = pending;
|
|
43
|
+
try {
|
|
44
|
+
let reply = await this.deps.api(target.login, "POST", "/api/device-api/approvals", {
|
|
45
|
+
session: target.session,
|
|
46
|
+
question: composeRelayQuestion(req, this.deps.host(), target.session),
|
|
47
|
+
options: [...exports.RELAY_OPTIONS],
|
|
48
|
+
// The private chat with the owner: a session is usually in a group too, and a prompt about
|
|
49
|
+
// one person's machine belongs to that person, not to the room.
|
|
50
|
+
direct: true,
|
|
51
|
+
wait: 1,
|
|
52
|
+
});
|
|
53
|
+
pending.cardId = typeof reply.requestId === "string" && reply.requestId ? reply.requestId : undefined;
|
|
54
|
+
if (!pending.cardId) {
|
|
55
|
+
this.deps.log("permission relay: BayChat returned no card id");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let failures = 0;
|
|
59
|
+
for (;;) {
|
|
60
|
+
if (pending.retired) {
|
|
61
|
+
await this.withdraw(pending);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (reply.status === "answered") {
|
|
65
|
+
await this.deps.verdict(req.request_id, reply.answerIndex === ALLOW_INDEX ? "allow" : "deny");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (reply.status !== "waiting")
|
|
69
|
+
return; // cancelled, or a status we do not know: no verdict.
|
|
70
|
+
try {
|
|
71
|
+
reply = await this.deps.api(pending.login, "GET", `/api/device-api/approvals/${encodeURIComponent(pending.cardId)}?wait=${POLL_WAIT_SECONDS}`);
|
|
72
|
+
failures = 0;
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
failures++;
|
|
76
|
+
this.deps.log(`permission relay: waiting failed (${err instanceof Error ? err.message : String(err)}), attempt ${failures}`);
|
|
77
|
+
if (failures >= MAX_CONSECUTIVE_FAILURES) {
|
|
78
|
+
// Give the decision back to the terminal alone, and take the card off the phone.
|
|
79
|
+
await this.withdraw(pending);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
await (this.deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))))(RETRY_DELAY_MS);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
this.deps.log(`permission relay: could not ask BayChat (${err instanceof Error ? err.message : String(err)}); the terminal still decides`);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
if (this.current === pending)
|
|
91
|
+
this.current = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The current dialog is over (a newer one opened, or shutdown): stop and withdraw its card. */
|
|
95
|
+
async retire() {
|
|
96
|
+
const pending = this.current;
|
|
97
|
+
if (!pending)
|
|
98
|
+
return;
|
|
99
|
+
pending.retired = true;
|
|
100
|
+
this.current = undefined;
|
|
101
|
+
await this.withdraw(pending);
|
|
102
|
+
}
|
|
103
|
+
async withdraw(pending) {
|
|
104
|
+
if (!pending.cardId)
|
|
105
|
+
return; // Not created yet: the create path sees `retired` and withdraws.
|
|
106
|
+
const cardId = pending.cardId;
|
|
107
|
+
pending.cardId = undefined;
|
|
108
|
+
try {
|
|
109
|
+
await this.deps.api(pending.login, "POST", `/api/device-api/approvals/${encodeURIComponent(cardId)}/withdraw`, {});
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
// An older server (no route) or a blip: the card stays up, and a tap on it is dropped by
|
|
113
|
+
// Claude Code as an unknown id. Untidy, never unsafe.
|
|
114
|
+
this.deps.log(`permission relay: could not withdraw card ${cardId} (${err instanceof Error ? err.message : String(err)})`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.PermissionRelay = PermissionRelay;
|
package/dist/channel/server.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.ChannelService = exports.nodeFeedConnect = exports.CHANNEL_INSTRUCTIONS = void 0;
|
|
4
37
|
exports.realLogin = realLogin;
|
|
@@ -23,20 +56,26 @@ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
|
23
56
|
const index_js_2 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
24
57
|
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
25
58
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
59
|
+
const os = __importStar(require("node:os"));
|
|
60
|
+
const zod_1 = require("zod");
|
|
61
|
+
const api_1 = require("../api");
|
|
26
62
|
const bays_1 = require("../bays");
|
|
27
63
|
const config_1 = require("../config");
|
|
28
64
|
const ws_1 = require("../relay/ws");
|
|
29
65
|
const detect_1 = require("./detect");
|
|
30
66
|
const feed_1 = require("./feed");
|
|
67
|
+
const permission_1 = require("./permission");
|
|
31
68
|
/** Delivered to Claude as context when this server connects. */
|
|
32
69
|
exports.CHANNEL_INSTRUCTIONS = `This is BayChat's wake-up channel. After join_session, call this server's listen tool with the ` +
|
|
33
70
|
`confirmed session name (and bay, when the session joined a Bay other than the default). If it ` +
|
|
34
|
-
`answers active, BayChat messages arrive as <channel source="
|
|
71
|
+
`answers active, BayChat messages arrive as <channel source="…" session="…" ` +
|
|
35
72
|
`event="messages" conversation_ids="…">: read those conversations with the remote BayChat ` +
|
|
36
73
|
`get_messages tool (the same Bay's tools the session joined with), obey shouldRespond and answer ` +
|
|
37
74
|
`in BayChat. Do NOT also open a Monitor or call listen_messages: that would replace this feed. ` +
|
|
38
75
|
`If listen answers inactive or failed, use listen_messages and a native Monitor as before. ` +
|
|
39
|
-
`event="reset" means catch up with get_messages; event="stopped" means the feed ended and says why
|
|
76
|
+
`event="reset" means catch up with get_messages; event="stopped" means the feed ended and says why. ` +
|
|
77
|
+
`Tool-permission prompts are also offered to the session's owner as a BayChat card; answer in the terminal or ` +
|
|
78
|
+
`on the phone, whichever is first.`;
|
|
40
79
|
const LISTEN_TOOL = {
|
|
41
80
|
name: "listen",
|
|
42
81
|
description: "Hear this BayChat session's incoming messages through the Claude Code channel instead of a Monitor. " +
|
|
@@ -167,6 +206,8 @@ class ChannelService {
|
|
|
167
206
|
deps;
|
|
168
207
|
push;
|
|
169
208
|
feeds = new Map();
|
|
209
|
+
/** The most recent session that listened successfully: where permission prompts go. */
|
|
210
|
+
last;
|
|
170
211
|
constructor(deps, push) {
|
|
171
212
|
this.deps = deps;
|
|
172
213
|
this.push = push;
|
|
@@ -180,7 +221,8 @@ class ChannelService {
|
|
|
180
221
|
if (!evidence.active) {
|
|
181
222
|
return toolResult("inactive", `Inactive: this Claude Code session was not started with the BayChat channel, so its events would be ` +
|
|
182
223
|
`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
|
|
224
|
+
`next time, start Claude Code with \`baychat claude\` (or \`baychat start\`) — the plain \`claude ${detect_1.DEV_CHANNELS_FLAG} ${detect_1.CHANNEL_FLAG_VALUE}\` ` +
|
|
225
|
+
`works too, except where Claude Code hides its own flags (an npm/Node install).`, { session });
|
|
184
226
|
}
|
|
185
227
|
let bayName;
|
|
186
228
|
try {
|
|
@@ -216,17 +258,40 @@ class ChannelService {
|
|
|
216
258
|
const started = await feed.start();
|
|
217
259
|
if (!started.ok) {
|
|
218
260
|
this.feeds.delete(key);
|
|
261
|
+
if (this.last?.key === key)
|
|
262
|
+
this.last = undefined;
|
|
219
263
|
return toolResult("failed", `Failed: ${started.detail} Use listen_messages and a native Monitor instead.`, { session });
|
|
220
264
|
}
|
|
265
|
+
this.last = { key, session, bay };
|
|
221
266
|
return toolResult("active", `Active: session "${session}"${bayName ? ` (${bayName})` : ""} now hears the Bay through the channel. New messages ` +
|
|
222
|
-
`arrive here as <channel
|
|
267
|
+
`arrive here as <channel event="messages"> events, with no deadline and no re-arming. Do NOT open a ` +
|
|
223
268
|
`Monitor or call listen_messages — either would replace this feed. Now read one get_messages batch (limit 10) ` +
|
|
224
269
|
`to catch up on anything from before listening.`, { session, ...(bayName ? { bay: bayName } : {}), how: evidence.how });
|
|
225
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Where a permission prompt goes: the session that most recently started listening, while its
|
|
273
|
+
* feed is still up. None → the prompt stays in the terminal only.
|
|
274
|
+
*/
|
|
275
|
+
permissionTarget() {
|
|
276
|
+
const last = this.last;
|
|
277
|
+
if (!last)
|
|
278
|
+
return undefined;
|
|
279
|
+
const state = this.feeds.get(last.key)?.currentState;
|
|
280
|
+
if (!state || state === "stopped")
|
|
281
|
+
return undefined;
|
|
282
|
+
try {
|
|
283
|
+
const login = this.deps.login(last.bay);
|
|
284
|
+
return { session: last.session, login: { baseUrl: login.baseUrl, token: login.token } };
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
226
290
|
stopAll() {
|
|
227
291
|
for (const feed of this.feeds.values())
|
|
228
292
|
feed.stop();
|
|
229
293
|
this.feeds.clear();
|
|
294
|
+
this.last = undefined;
|
|
230
295
|
}
|
|
231
296
|
}
|
|
232
297
|
exports.ChannelService = ChannelService;
|
|
@@ -234,11 +299,24 @@ function safeJsonStatus(text) {
|
|
|
234
299
|
const parsed = safeJson(text);
|
|
235
300
|
return parsed?.status;
|
|
236
301
|
}
|
|
302
|
+
/** What Claude Code sends when a tool-approval dialog opens (channels reference). */
|
|
303
|
+
const PermissionRequestSchema = zod_1.z.object({
|
|
304
|
+
method: zod_1.z.literal("notifications/claude/channel/permission_request"),
|
|
305
|
+
params: zod_1.z.object({
|
|
306
|
+
request_id: zod_1.z.string(),
|
|
307
|
+
tool_name: zod_1.z.string(),
|
|
308
|
+
description: zod_1.z.string(),
|
|
309
|
+
input_preview: zod_1.z.string(),
|
|
310
|
+
}),
|
|
311
|
+
});
|
|
237
312
|
/** Boot on stdio and block on the transport. */
|
|
238
313
|
async function startChannelServer(deps) {
|
|
239
314
|
const log = deps?.log ?? ((line) => console.error(`[baychat channel] ${line}`));
|
|
240
315
|
const mcp = new index_js_1.Server({ name: detect_1.CHANNEL_MCP_ENTRY, version: "1" }, {
|
|
241
|
-
|
|
316
|
+
// `claude/channel/permission`: Claude Code also offers this channel its tool-approval
|
|
317
|
+
// dialogs (permission.ts). Safe to declare because only the session's owner can answer the
|
|
318
|
+
// card it becomes — the docs' condition for declaring it.
|
|
319
|
+
capabilities: { experimental: { "claude/channel": {}, "claude/channel/permission": {} }, tools: {} },
|
|
242
320
|
instructions: exports.CHANNEL_INSTRUCTIONS,
|
|
243
321
|
});
|
|
244
322
|
const service = new ChannelService({
|
|
@@ -255,6 +333,23 @@ async function startChannelServer(deps) {
|
|
|
255
333
|
log(`could not hand an event to Claude Code: ${err instanceof Error ? err.message : String(err)}`);
|
|
256
334
|
}
|
|
257
335
|
});
|
|
336
|
+
const relay = new permission_1.PermissionRelay({
|
|
337
|
+
target: () => service.permissionTarget(),
|
|
338
|
+
api: (login, method, path, body) => (0, api_1.apiRequest)(login, method, path, body),
|
|
339
|
+
verdict: async (requestId, behavior) => {
|
|
340
|
+
try {
|
|
341
|
+
await mcp.notification({ method: "notifications/claude/channel/permission", params: { request_id: requestId, behavior } });
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
log(`could not hand a verdict to Claude Code: ${err instanceof Error ? err.message : String(err)}`);
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
host: () => os.hostname(),
|
|
348
|
+
log,
|
|
349
|
+
});
|
|
350
|
+
mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {
|
|
351
|
+
void relay.onRequest(params);
|
|
352
|
+
});
|
|
258
353
|
mcp.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: [LISTEN_TOOL] }));
|
|
259
354
|
mcp.setRequestHandler(types_js_1.CallToolRequestSchema, async (req) => {
|
|
260
355
|
if (req.params.name === LISTEN_TOOL.name)
|
|
@@ -265,7 +360,9 @@ async function startChannelServer(deps) {
|
|
|
265
360
|
// keep this process alive with nobody left to tell, holding the session "connected".
|
|
266
361
|
const shutdown = () => {
|
|
267
362
|
service.stopAll();
|
|
268
|
-
|
|
363
|
+
// Take any open approval card off the phone before going: its dialog dies with this session.
|
|
364
|
+
// Bounded, so a dead network cannot hold the exit.
|
|
365
|
+
void Promise.race([relay.retire(), new Promise((r) => setTimeout(r, 2_000))]).finally(() => process.exit(0));
|
|
269
366
|
};
|
|
270
367
|
mcp.onclose = shutdown;
|
|
271
368
|
process.stdin.once("end", shutdown);
|
|
@@ -5,7 +5,8 @@ exports.BAY_CHOICE = exports.CLAUDE_REACHABILITY = exports.CLAUDE_JOIN_STEPS = v
|
|
|
5
5
|
exports.CLAUDE_JOIN_STEPS = `1. Use **remote BayChat MCP + native WebSocket Monitor**. Search deferred tools once
|
|
6
6
|
if needed, for \`join_session\`, \`listen_messages\`, \`get_delivery_status\`,
|
|
7
7
|
\`send_message\` and \`get_messages\` (also \`create_group\` for a named group),
|
|
8
|
-
and
|
|
8
|
+
and BayChat's channel \`listen\` tool when it is present
|
|
9
|
+
(\`mcp__plugin_baychat_channel__listen\` or \`mcp__baychat_channel__listen\`).
|
|
9
10
|
Check that native Monitor supports \`ws\` before creating membership.
|
|
10
11
|
If a required BayChat tool is absent, follow Automatic setup above once,
|
|
11
12
|
then continue these steps with the original name and room when tools are loaded.
|
|
@@ -24,10 +25,11 @@ exports.CLAUDE_JOIN_STEPS = `1. Use **remote BayChat MCP + native WebSocket Moni
|
|
|
24
25
|
Do not ask again when the owner already requested creation. A supplied group title
|
|
25
26
|
also authorizes creation on GROUP_NOT_FOUND; never substitute a similarly named group.
|
|
26
27
|
If blocked, state the exact requested title and needed action in your reply.
|
|
27
|
-
3. **Channel first.** If
|
|
28
|
-
(\`
|
|
28
|
+
3. **Channel first.** If BayChat's channel \`listen\` tool exists
|
|
29
|
+
(\`mcp__plugin_baychat_channel__listen\` from the plugin, or
|
|
30
|
+
\`mcp__baychat_channel__listen\`), call it with \`{session: "<confirmed name>"}\`
|
|
29
31
|
(add \`bay: "<Bay>"\` when the session joined a Bay other than the default).
|
|
30
|
-
If it answers **active**, messages now arrive as \`<channel
|
|
32
|
+
If it answers **active**, messages now arrive as \`<channel … event="messages">\`
|
|
31
33
|
events: open NO Monitor and do not call \`listen_messages\` — either replaces the
|
|
32
34
|
channel's feed. Go to step 4. If it answers inactive or failed, or the tool is
|
|
33
35
|
absent, use the Monitor instead:
|
|
@@ -60,7 +62,7 @@ exports.CLAUDE_JOIN_STEPS = `1. Use **remote BayChat MCP + native WebSocket Moni
|
|
|
60
62
|
exports.CLAUDE_REACHABILITY = `## Staying reachable — native remote messages
|
|
61
63
|
|
|
62
64
|
**With the channel** (\`listen\` answered active): each new message arrives as a
|
|
63
|
-
\`<channel
|
|
65
|
+
\`<channel … event="messages" conversation_ids="…">\` event.
|
|
64
66
|
Read those conversations with \`get_messages\` and answer as below. There is no
|
|
65
67
|
deadline and nothing to re-arm; the channel reconnects by itself. \`event="reset"\`
|
|
66
68
|
means catch up with \`get_messages\`; \`event="stopped"\` says why the feed ended
|
package/dist/connect-claude.js
CHANGED
|
@@ -66,7 +66,12 @@ function registerChannel() {
|
|
|
66
66
|
const problem = (0, context_hooks_command_1.launchProblem)(launch);
|
|
67
67
|
if (problem)
|
|
68
68
|
return { skipped: problem };
|
|
69
|
-
|
|
69
|
+
const run = (0, bay_mcp_2.realClaudeRun)();
|
|
70
|
+
// The plugin first: it updates with the npm package and is the form Anthropic can approve.
|
|
71
|
+
// Where this Claude Code cannot install it, the same server is registered by hand.
|
|
72
|
+
if ((0, launch_1.installPluginWithClaude)(run))
|
|
73
|
+
return "registered";
|
|
74
|
+
return (0, launch_1.registerChannelWithClaude)(launch, run)
|
|
70
75
|
? "registered"
|
|
71
76
|
: { skipped: "Claude Code did not accept the channel server." };
|
|
72
77
|
}
|
|
@@ -77,8 +82,7 @@ function registerChannel() {
|
|
|
77
82
|
function reportChannel(result) {
|
|
78
83
|
if (result === "registered") {
|
|
79
84
|
console.log("✓ BayChat channel added to Claude Code (wakes a session with no Monitor deadline)");
|
|
80
|
-
console.log(
|
|
81
|
-
console.log(` ${launch_1.CHANNEL_LAUNCH_COMMAND}`);
|
|
85
|
+
console.log(` Start Claude Code with \`${launch_1.CHANNEL_LAUNCH_COMMAND}\` (or \`baychat start\`) to use it.`);
|
|
82
86
|
console.log(" Without it, incoming messages use the native WebSocket Monitor as before.");
|
|
83
87
|
return;
|
|
84
88
|
}
|
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.
|
package/dist/skill-bootstrap.js
CHANGED
|
@@ -5,7 +5,7 @@ exports.renderSkillBootstrap = renderSkillBootstrap;
|
|
|
5
5
|
function renderSkillBootstrap(runtime) {
|
|
6
6
|
const invocation = runtime === "codex" ? "$baychat" : "/baychat";
|
|
7
7
|
const tools = runtime === "claude"
|
|
8
|
-
? "allowed-tools:\n - Bash\n - Monitor\n - mcp__baychat\n - mcp__baychat_channel\n"
|
|
8
|
+
? "allowed-tools:\n - Bash\n - Monitor\n - mcp__baychat\n - mcp__baychat_channel\n - mcp__plugin_baychat_channel\n"
|
|
9
9
|
: "";
|
|
10
10
|
return `---
|
|
11
11
|
name: baychat
|
package/dist/start-command.js
CHANGED
|
@@ -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
|
|
140
|
-
const
|
|
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.
|
|
3
|
+
"version": "0.28.0",
|
|
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"
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
+
".claude-plugin",
|
|
40
41
|
"README.md",
|
|
41
42
|
"LICENSE"
|
|
42
43
|
],
|