baychat 0.37.0 → 0.39.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 +1 -1
- package/README.md +9 -4
- package/dist/approval-wait.js +14 -0
- package/dist/approve-hook.js +65 -7
- package/dist/cli-help.js +17 -5
- package/dist/main.js +20 -5
- package/dist/notify-answer.js +196 -0
- package/dist/notify-hook.js +227 -0
- package/dist/notify-state.js +136 -0
- package/dist/protocol-content.js +1 -1
- package/dist/relay/daemon.js +7 -0
- package/dist/relay/launcher/loop.js +12 -1
- package/dist/relay/launcher/session-spots.js +17 -2
- package/dist/setup-command.js +126 -0
- package/dist/setup-facts.js +44 -0
- package/dist/setup-plan.js +94 -0
- package/dist/start-command.js +1 -1
- package/hooks/hooks.json +14 -2
- package/package.json +1 -1
package/dist/relay/daemon.js
CHANGED
|
@@ -1887,6 +1887,7 @@ class RelayDaemon {
|
|
|
1887
1887
|
*/
|
|
1888
1888
|
registerChannel(frame, sock) {
|
|
1889
1889
|
const previous = this.channels.get(frame.session);
|
|
1890
|
+
const before = this.registry.get(frame.session);
|
|
1890
1891
|
if (previous && previous !== sock) {
|
|
1891
1892
|
this.log(`replacing channel for ${frame.session}: dropping the previous one`);
|
|
1892
1893
|
(0, socket_1.writeFrame)(previous, { type: "error", message: channel_sessions_1.CHANNEL_REPLACED });
|
|
@@ -1905,6 +1906,12 @@ class RelayDaemon {
|
|
|
1905
1906
|
(0, socket_1.writeFrame)(sock, { type: "attached", session: frame.session, delivery: "channel" });
|
|
1906
1907
|
const where = frame.pane?.kind === "tmux" ? `tmux pane ${frame.pane.pane}` : (frame.pane?.kind ?? "no multiplexer");
|
|
1907
1908
|
this.log(`channel registered: ${frame.session} (${frame.runtime}, commands only, ${where})`);
|
|
1909
|
+
// Another Claude Code process took this name (a new terminal, 2026-10-30): its memory starts
|
|
1910
|
+
// empty, so the server must not keep showing the old process's reading and tidy count.
|
|
1911
|
+
// The same process re-registering (a reconnect) keeps its pid and sends nothing.
|
|
1912
|
+
if (before?.ownerPid && frame.ownerPid && before.ownerPid !== frame.ownerPid) {
|
|
1913
|
+
this.reportContext(frame.session, { runtime: "claude-code", event: "session_start", source: "exact" });
|
|
1914
|
+
}
|
|
1908
1915
|
}
|
|
1909
1916
|
/**
|
|
1910
1917
|
* Forget every channel session that is DEAD: its channel link is closed and its Claude Code
|
|
@@ -133,7 +133,18 @@ async function reportSessionSpots(deps, auth, signal, last, log) {
|
|
|
133
133
|
if (key === last)
|
|
134
134
|
return last;
|
|
135
135
|
try {
|
|
136
|
-
|
|
136
|
+
try {
|
|
137
|
+
await deps.request(auth, "PUT", "/api/device-api/launch-sessions", { sessions }, signal);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
// A server older than the `withinSpotId` hint (2026-10-30) refuses the whole report as
|
|
141
|
+
// INVALID_SESSION_SPOTS: send it again without the hints, so "Start again" places still land.
|
|
142
|
+
const hinted = sessions.some((s) => s.withinSpotId !== undefined);
|
|
143
|
+
if (!(hinted && err instanceof api_1.ApiError && err.status === 400))
|
|
144
|
+
throw err;
|
|
145
|
+
const plain = sessions.map(({ withinSpotId: _hint, ...rest }) => rest);
|
|
146
|
+
await deps.request(auth, "PUT", "/api/device-api/launch-sessions", { sessions: plain }, signal);
|
|
147
|
+
}
|
|
137
148
|
return key;
|
|
138
149
|
}
|
|
139
150
|
catch (err) {
|
|
@@ -37,6 +37,7 @@ exports.MAX_SESSION_SPOTS = void 0;
|
|
|
37
37
|
exports.sessionSpots = sessionSpots;
|
|
38
38
|
exports.realRealpath = realRealpath;
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
40
41
|
const policy_1 = require("../../launch/policy");
|
|
41
42
|
const argv_1 = require("./argv");
|
|
42
43
|
/** The server takes at most this many per report. */
|
|
@@ -56,13 +57,27 @@ function sessionSpots(sessions, policy, deps) {
|
|
|
56
57
|
continue;
|
|
57
58
|
const real = deps.realpath(dir);
|
|
58
59
|
const runtime = session.runtime;
|
|
59
|
-
const
|
|
60
|
-
|
|
60
|
+
const offered = folders.filter((f) => f.spot.runtimes.includes(runtime));
|
|
61
|
+
const match = real ? offered.find((f) => f.path === real) : undefined;
|
|
62
|
+
// The deepest spot folder above it: the longest path that is a proper prefix at a separator.
|
|
63
|
+
const within = real && !match ? offered.filter((f) => isInside(real, f.path)).sort((a, b) => b.path.length - a.path.length)[0] : undefined;
|
|
64
|
+
out.push({
|
|
65
|
+
name: session.name,
|
|
66
|
+
spotId: match?.spot.id ?? null,
|
|
67
|
+
...(within ? { withinSpotId: within.spot.id } : {}),
|
|
68
|
+
runtime,
|
|
69
|
+
listen: session.delivery === "channel" ? "channel" : "monitor",
|
|
70
|
+
});
|
|
61
71
|
if (out.length === exports.MAX_SESSION_SPOTS)
|
|
62
72
|
break;
|
|
63
73
|
}
|
|
64
74
|
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
65
75
|
}
|
|
76
|
+
/** Is `dir` strictly inside `parent` (both realpaths)? `/work/web2` is not inside `/work/web`. */
|
|
77
|
+
function isInside(dir, parent) {
|
|
78
|
+
const prefix = parent.endsWith(path.sep) ? parent : parent + path.sep;
|
|
79
|
+
return dir.startsWith(prefix);
|
|
80
|
+
}
|
|
66
81
|
function realRealpath(dir) {
|
|
67
82
|
try {
|
|
68
83
|
return fs.realpathSync(dir);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `npx baychat` and `baychat setup` — ONE COMMAND (Karmen + Lorenzo, 31 Oct 2026; plan in BAYCHAT, vps-reviewed).
|
|
3
|
+
//
|
|
4
|
+
// Bare `baychat`: the first time (no "set up" mark yet), sign in once and set up every agent app found (setup-plan.ts),
|
|
5
|
+
// print one checklist, then start a session — only inside a project (vps: never in the home folder, where a session
|
|
6
|
+
// could read and write everything). Once set up, bare `baychat` goes straight to the start, as before, without asking
|
|
7
|
+
// every app again. `baychat setup` always looks again (after installing a new app).
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.runSetup = runSetup;
|
|
13
|
+
exports.runBare = runBare;
|
|
14
|
+
exports.runSetupCommand = runSetupCommand;
|
|
15
|
+
exports.setupMarkPath = setupMarkPath;
|
|
16
|
+
exports.realSetupDeps = realSetupDeps;
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const os_1 = __importDefault(require("os"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const config_1 = require("./config");
|
|
21
|
+
const commands_1 = require("./commands");
|
|
22
|
+
const connect_1 = require("./connect");
|
|
23
|
+
const connect_claude_1 = require("./connect-claude");
|
|
24
|
+
const doctor_1 = require("./doctor");
|
|
25
|
+
const doctor_command_1 = require("./doctor-command");
|
|
26
|
+
const machine_name_1 = require("./machine-name");
|
|
27
|
+
const multiplexer_launch_1 = require("./multiplexer-launch");
|
|
28
|
+
const setup_facts_1 = require("./setup-facts");
|
|
29
|
+
const start_command_1 = require("./start-command");
|
|
30
|
+
const setup_plan_1 = require("./setup-plan");
|
|
31
|
+
/** Look, set up what is missing, and say what is ready. Returns 2 when the login was not approved. */
|
|
32
|
+
async function runSetup(deps) {
|
|
33
|
+
const facts = await deps.facts();
|
|
34
|
+
const steps = (0, setup_plan_1.planSetup)(facts);
|
|
35
|
+
const done = new Set();
|
|
36
|
+
const failed = new Set();
|
|
37
|
+
for (const step of steps) {
|
|
38
|
+
if (step.kind === "login") {
|
|
39
|
+
deps.log("Sign this computer in to BayChat (once): scan the code with the BayChat app.");
|
|
40
|
+
if (!(await deps.login()))
|
|
41
|
+
return 2;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
deps.log(`Setting up ${step.client}…`);
|
|
45
|
+
if ((await deps.connect(step.client)) === 0)
|
|
46
|
+
done.add(step.client);
|
|
47
|
+
else
|
|
48
|
+
failed.add(step.client);
|
|
49
|
+
}
|
|
50
|
+
deps.log("");
|
|
51
|
+
for (const line of (0, setup_plan_1.setupChecklist)(facts, { done, failed }))
|
|
52
|
+
deps.log(` ${line}`);
|
|
53
|
+
deps.markSetUp();
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Bare `baychat`: set up the first time, then start — in a project only. Once set up, a project starts straight away;
|
|
58
|
+
* any other folder (the home folder above all) asks first, default no (vps, 31 Oct: nobody gives an agent all of home
|
|
59
|
+
* by accident).
|
|
60
|
+
*/
|
|
61
|
+
async function runBare(deps) {
|
|
62
|
+
if (deps.wasSetUp()) {
|
|
63
|
+
if (deps.inProject())
|
|
64
|
+
return deps.start();
|
|
65
|
+
if (await deps.confirm("This isn't a project folder. Start a session here anyway? (y/N) "))
|
|
66
|
+
return deps.start();
|
|
67
|
+
deps.log(" Not started. cd into a project and run npx baychat again (or start a session from the BayChat app).");
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
const code = await runSetup(deps);
|
|
71
|
+
if (code !== 0)
|
|
72
|
+
return code;
|
|
73
|
+
return startOrPoint(deps);
|
|
74
|
+
}
|
|
75
|
+
/** `baychat setup`: always look again; start only when asked to and in a project. */
|
|
76
|
+
async function runSetupCommand(deps) {
|
|
77
|
+
const code = await runSetup(deps);
|
|
78
|
+
if (code !== 0)
|
|
79
|
+
return code;
|
|
80
|
+
deps.log("");
|
|
81
|
+
deps.log(" Next: run npx baychat in a project folder to start a session (or start one from the BayChat app).");
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
async function startOrPoint(deps) {
|
|
85
|
+
deps.log("");
|
|
86
|
+
if (deps.inProject())
|
|
87
|
+
return deps.start();
|
|
88
|
+
deps.log(" Next: cd into a project and run npx baychat again (or start a session from the BayChat app).");
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
/** Where the "set up" mark lives: beside the credentials, removed with them. */
|
|
92
|
+
function setupMarkPath() {
|
|
93
|
+
return path_1.default.join((0, config_1.configDir)(), "setup-done.json");
|
|
94
|
+
}
|
|
95
|
+
/** The real dependencies. `wsl` only for tests: a test run inside WSL still exercises the Claude Desktop path. */
|
|
96
|
+
function realSetupDeps(over = {}) {
|
|
97
|
+
const exists = (p) => fs_1.default.existsSync(p);
|
|
98
|
+
return {
|
|
99
|
+
facts: async () => {
|
|
100
|
+
const env = await (0, doctor_command_1.currentDoctorEnv)();
|
|
101
|
+
return (0, setup_facts_1.setupFactsFrom)(env, (0, doctor_1.buildReport)(env), { wsl: over.wsl ?? (0, machine_name_1.isWsl)(), exists });
|
|
102
|
+
},
|
|
103
|
+
login: () => (0, commands_1.cmdLogin)({ registerClaude: false, hint: false }),
|
|
104
|
+
connect: (client) => (client === "claude" ? (0, connect_claude_1.cmdConnectClaude)({}) : (0, connect_1.cmdConnect)(client, {})),
|
|
105
|
+
start: () => (0, start_command_1.cmdStart)([]),
|
|
106
|
+
inProject: () => (0, setup_plan_1.isProjectDir)(process.cwd(), exists, os_1.default.homedir()),
|
|
107
|
+
confirm: async (question) => {
|
|
108
|
+
if (!process.stdin.isTTY)
|
|
109
|
+
return false;
|
|
110
|
+
return /^y(es)?$/i.test((await (0, multiplexer_launch_1.askLine)(question)).trim());
|
|
111
|
+
},
|
|
112
|
+
// Signed out since (or the credentials were removed): look again, which signs in.
|
|
113
|
+
wasSetUp: () => exists(setupMarkPath()) && (0, config_1.loadDeviceCredentials)() !== null,
|
|
114
|
+
markSetUp: () => {
|
|
115
|
+
try {
|
|
116
|
+
fs_1.default.mkdirSync(path_1.default.dirname(setupMarkPath()), { recursive: true });
|
|
117
|
+
fs_1.default.writeFileSync(setupMarkPath(), JSON.stringify({ at: new Date().toISOString() }) + "\n");
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
// Only costs a second look next time.
|
|
121
|
+
console.warn(`Could not record that setup is done: ${err instanceof Error ? err.message : String(err)}`);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
log: (line) => console.log(line),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The facts `planSetup` decides from, read off this computer through what `baychat doctor` already knows: its report
|
|
3
|
+
// says whether BayChat is in each app (it asks `claude mcp list` / `codex mcp list` themselves, and reads every file
|
|
4
|
+
// Claude Desktop or Cursor may open), and its binary resolver says what is installed. Nothing new is guessed here.
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.configuredFrom = configuredFrom;
|
|
10
|
+
exports.installedFrom = installedFrom;
|
|
11
|
+
exports.setupFactsFrom = setupFactsFrom;
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const client_paths_1 = require("./client-paths");
|
|
14
|
+
const setup_plan_1 = require("./setup-plan");
|
|
15
|
+
/** Is BayChat in this app already? Doctor's MCP check passes for it (an unknown answer counts as no: connect again). */
|
|
16
|
+
function configuredFrom(report) {
|
|
17
|
+
const out = {};
|
|
18
|
+
for (const client of setup_plan_1.SETUP_CLIENTS) {
|
|
19
|
+
const runtime = report.runtimes.find((r) => r.runtime === client);
|
|
20
|
+
out[client] = Boolean(runtime?.checks.some((c) => c.name === "mcp" && c.status === "pass"));
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
/** Is the app on this computer? A CLI is a resolvable binary; a GUI app is the folder its config lives in. */
|
|
25
|
+
function installedFrom(env, exists) {
|
|
26
|
+
const pathEnv = env.pathEnv ?? { home: env.home, platform: env.platform };
|
|
27
|
+
return {
|
|
28
|
+
claude: env.resolveBinary("claude").ok,
|
|
29
|
+
desktop: (0, client_paths_1.configPathsFor)("desktop", pathEnv).some((file) => exists((0, client_paths_1.configDirOf)(file))),
|
|
30
|
+
codex: env.resolveBinary("codex").ok || exists(path_1.default.join(pathEnv.codexHome ?? path_1.default.join(env.home, ".codex"))),
|
|
31
|
+
cursor: exists(path_1.default.join(env.home, ".cursor")) || env.resolveBinary("cursor-agent").ok,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Every fact `planSetup` needs. */
|
|
35
|
+
function setupFactsFrom(env, report, host) {
|
|
36
|
+
const expires = env.device ? Date.parse(env.device.expiresAt) : NaN;
|
|
37
|
+
return {
|
|
38
|
+
signedIn: env.device !== null && Number.isFinite(expires) && expires > env.now,
|
|
39
|
+
installed: installedFrom(env, host.exists),
|
|
40
|
+
configured: configuredFrom(report),
|
|
41
|
+
wsl: host.wsl,
|
|
42
|
+
hermesFound: env.resolveBinary("hermes").ok || host.exists(path_1.default.join(env.home, ".hermes")),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// What `npx baychat` (and `baychat setup`) should DO, decided before anything is done.
|
|
3
|
+
//
|
|
4
|
+
// ONE COMMAND (Karmen + Lorenzo, 31 Oct 2026): people did not know which of `login`, `connect desktop`,
|
|
5
|
+
// `connect codex`… was theirs. Now the one line signs this computer in ONCE (the phone QR) and sets up every agent
|
|
6
|
+
// app it finds that is not set up yet — through the very same `connect` paths, so nothing new is written anywhere.
|
|
7
|
+
// Hermes is named, not set up: it gets an agent of its own, chosen on the phone (a second QR), which is not "one".
|
|
8
|
+
//
|
|
9
|
+
// Facts in, steps out — like `connect-plan.ts` — so the order is testable without a phone, a network or the apps.
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.SETUP_CLIENTS = void 0;
|
|
12
|
+
exports.planSetup = planSetup;
|
|
13
|
+
exports.setupChecklist = setupChecklist;
|
|
14
|
+
exports.isProjectDir = isProjectDir;
|
|
15
|
+
/** The apps one command sets up, in the order it does them. */
|
|
16
|
+
exports.SETUP_CLIENTS = ["claude", "desktop", "codex", "cursor"];
|
|
17
|
+
const LABELS = {
|
|
18
|
+
claude: "Claude Code",
|
|
19
|
+
desktop: "Claude Desktop",
|
|
20
|
+
codex: "Codex",
|
|
21
|
+
cursor: "Cursor",
|
|
22
|
+
};
|
|
23
|
+
/** The steps: the one login when there is none, then every app found and not yet set up. */
|
|
24
|
+
function planSetup(facts) {
|
|
25
|
+
const steps = facts.signedIn ? [] : [{ kind: "login" }];
|
|
26
|
+
for (const client of exports.SETUP_CLIENTS) {
|
|
27
|
+
if (client === "desktop" && facts.wsl)
|
|
28
|
+
continue;
|
|
29
|
+
if (facts.installed[client] && !facts.configured[client])
|
|
30
|
+
steps.push({ kind: "connect", client });
|
|
31
|
+
}
|
|
32
|
+
return steps;
|
|
33
|
+
}
|
|
34
|
+
/** The closing list, one line per app: ready, set up now, not here, or not finished (with what to run). */
|
|
35
|
+
function setupChecklist(facts, outcome) {
|
|
36
|
+
const lines = exports.SETUP_CLIENTS.map((client) => {
|
|
37
|
+
const label = LABELS[client];
|
|
38
|
+
if (client === "desktop" && facts.wsl)
|
|
39
|
+
return `– ${label} (it runs on Windows: run npx baychat once in Windows PowerShell)`;
|
|
40
|
+
if (outcome.failed.has(client))
|
|
41
|
+
return `✗ ${label} (did not finish; run npx baychat connect ${client})`;
|
|
42
|
+
if (outcome.done.has(client))
|
|
43
|
+
return client === "desktop" ? `✓ ${label} (just set up; restart it once)` : `✓ ${label} (just set up)`;
|
|
44
|
+
if (facts.configured[client])
|
|
45
|
+
return `✓ ${label}`;
|
|
46
|
+
return `– ${label} (not on this computer)`;
|
|
47
|
+
});
|
|
48
|
+
if (facts.hermesFound)
|
|
49
|
+
lines.push("• Hermes found: npx baychat connect hermes (it gets its own agent, chosen on your phone)");
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
/** Files that make a folder a project worth starting an agent in. */
|
|
53
|
+
const PROJECT_MARKERS = [
|
|
54
|
+
"package.json",
|
|
55
|
+
"pyproject.toml",
|
|
56
|
+
"requirements.txt",
|
|
57
|
+
"Cargo.toml",
|
|
58
|
+
"go.mod",
|
|
59
|
+
"Gemfile",
|
|
60
|
+
"pom.xml",
|
|
61
|
+
"build.gradle",
|
|
62
|
+
"composer.json",
|
|
63
|
+
"deno.json",
|
|
64
|
+
"Package.swift",
|
|
65
|
+
"mix.exs",
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* May a session start here? vps, 31 Oct: a first-time user often runs `npx baychat` in the home folder, and a session
|
|
69
|
+
* started there can read and write all of it. So only in a project: a folder with a project file, or inside a git
|
|
70
|
+
* repository (found by walking up, stopping BEFORE the home folder). The home folder itself never counts.
|
|
71
|
+
*/
|
|
72
|
+
function isProjectDir(cwd, exists, home) {
|
|
73
|
+
const sep = cwd.includes("\\") && !cwd.includes("/") ? "\\" : "/";
|
|
74
|
+
const trim = (p) => (p.length > 1 && (p.endsWith("/") || p.endsWith("\\")) ? p.slice(0, -1) : p);
|
|
75
|
+
const here = trim(cwd);
|
|
76
|
+
const stop = trim(home);
|
|
77
|
+
if (here === stop)
|
|
78
|
+
return false;
|
|
79
|
+
const join = (dir, name) => `${dir}${sep}${name}`;
|
|
80
|
+
if (PROJECT_MARKERS.some((marker) => exists(join(here, marker))))
|
|
81
|
+
return true;
|
|
82
|
+
for (let dir = here; dir !== stop;) {
|
|
83
|
+
if (exists(join(dir, ".git")))
|
|
84
|
+
return true;
|
|
85
|
+
const cut = dir.lastIndexOf(sep);
|
|
86
|
+
if (cut <= 0 || dir.slice(0, cut) === dir)
|
|
87
|
+
break;
|
|
88
|
+
const parent = dir.slice(0, cut);
|
|
89
|
+
if (parent.length < stop.length)
|
|
90
|
+
break;
|
|
91
|
+
dir = parent;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
package/dist/start-command.js
CHANGED
|
@@ -95,7 +95,7 @@ function startRefusal(runtime) {
|
|
|
95
95
|
}
|
|
96
96
|
async function cmdStart(args) {
|
|
97
97
|
if (!(0, config_1.loadDeviceCredentials)()) {
|
|
98
|
-
console.error("This machine is not signed in yet. Run `npx baychat
|
|
98
|
+
console.error("This machine is not signed in yet. Run `npx baychat` first: it signs in and sets up your agent apps.");
|
|
99
99
|
return 1;
|
|
100
100
|
}
|
|
101
101
|
// THE UPDATE NOBODY HAS TO REMEMBER.
|
package/hooks/hooks.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "BayChat's denied-command card for every session, also ones started by hand: an auto-mode denial becomes a card in the session's BayChat chat once it has joined (docs/features/DENIED_COMMAND_CARD.md). After a compact, a channel session is told when its BayChat feed is down (channel-check). No PostToolUse hook: it would start baychat on every tool call.",
|
|
2
|
+
"description": "BayChat's denied-command card for every session, also ones started by hand: an auto-mode denial becomes a card in the session's BayChat chat once it has joined (docs/features/DENIED_COMMAND_CARD.md). After a compact, a channel session is told when its BayChat feed is down (channel-check). A question left open in the terminal (a permission dialog, an MCP form) becomes one line on the owner's phone (notify-hook). No PostToolUse hook: it would start baychat on every tool call.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PermissionDenied": [
|
|
5
5
|
{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "command",
|
|
10
10
|
"command": "baychat denied-hook --session-from-claude --plugin --timeout 3600",
|
|
11
11
|
"timeout": 3900,
|
|
12
|
-
"statusMessage": "Blocked by auto mode
|
|
12
|
+
"statusMessage": "Blocked by auto mode — asking in BayChat…"
|
|
13
13
|
}
|
|
14
14
|
]
|
|
15
15
|
}
|
|
@@ -25,6 +25,18 @@
|
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
|
+
],
|
|
29
|
+
"Notification": [
|
|
30
|
+
{
|
|
31
|
+
"matcher": "permission_prompt|elicitation_dialog|elicitation_url_dialog",
|
|
32
|
+
"hooks": [
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "baychat notify-hook",
|
|
36
|
+
"timeout": 30
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
28
40
|
]
|
|
29
41
|
}
|
|
30
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "baychat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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"
|