baychat 0.30.0 → 0.32.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 +36 -3
- package/dist/approval-wait.js +46 -33
- package/dist/approve-hook.js +35 -1
- package/dist/auto-update/describe.js +66 -0
- package/dist/auto-update/notice.js +40 -0
- package/dist/auto-update/policy.js +110 -0
- package/dist/auto-update/relay-glue.js +143 -0
- package/dist/auto-update/store.js +140 -0
- package/dist/auto-update/updater.js +324 -0
- package/dist/channel/feed.js +50 -1
- package/dist/channel/permission.js +6 -0
- package/dist/channel/server.js +1 -0
- package/dist/claude-onboarding.js +3 -1
- package/dist/cli-help.js +6 -1
- package/dist/denied/after-hook.js +8 -2
- package/dist/denied/confirm.js +131 -0
- package/dist/denied/grants.js +60 -2
- package/dist/denied/hook-log.js +75 -0
- package/dist/denied/live-session.js +154 -0
- package/dist/denied/notice.js +29 -0
- package/dist/denied/permission-rule.js +9 -6
- package/dist/denied/settings-rules.js +61 -0
- package/dist/denied/transcript.js +39 -16
- package/dist/denied-hook.js +132 -30
- package/dist/doctor-command.js +7 -1
- package/dist/doctor.js +28 -0
- package/dist/main.js +11 -2
- package/dist/permissions-command.js +18 -1
- package/dist/protocol-content.js +1 -1
- package/dist/refresh-clients.js +36 -0
- package/dist/relay/acp/runner.js +4 -0
- package/dist/relay/commands.js +19 -3
- package/dist/relay/daemon.js +37 -1
- package/dist/relay/launcher/loop.js +4 -1
- package/dist/relay/self-restart.js +149 -0
- package/dist/relay/terminal-pane.js +74 -2
- package/dist/self-upgrade.js +92 -27
- package/dist/start-command.js +15 -1
- package/dist/update-command.js +51 -31
- package/dist/waiting-cards.js +107 -0
- package/package.json +4 -1
package/dist/update-command.js
CHANGED
|
@@ -39,10 +39,10 @@ const child_process_1 = require("child_process");
|
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const config_1 = require("./config");
|
|
42
|
-
const
|
|
43
|
-
const commands_1 = require("./commands");
|
|
44
|
-
const
|
|
45
|
-
const
|
|
42
|
+
const refresh_clients_1 = require("./refresh-clients");
|
|
43
|
+
const commands_1 = require("./relay/commands");
|
|
44
|
+
const policy_1 = require("./auto-update/policy");
|
|
45
|
+
const store_1 = require("./auto-update/store");
|
|
46
46
|
const self_upgrade_1 = require("./self-upgrade");
|
|
47
47
|
/**
|
|
48
48
|
* `baychat update` — bring this whole machine onto the current version, in one word.
|
|
@@ -76,6 +76,8 @@ const self_upgrade_1 = require("./self-upgrade");
|
|
|
76
76
|
const AUTO_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
77
77
|
const STAMP_FILE = "last-update-check.json";
|
|
78
78
|
async function cmdUpdate(opts = {}) {
|
|
79
|
+
if (opts.auto !== undefined)
|
|
80
|
+
return setAutoUpdate(opts.auto);
|
|
79
81
|
const say = (line) => {
|
|
80
82
|
if (!opts.quiet)
|
|
81
83
|
console.log(line);
|
|
@@ -93,12 +95,24 @@ async function cmdUpdate(opts = {}) {
|
|
|
93
95
|
done.push(`upgraded baychat ${upgradedFrom} → ${(0, self_upgrade_1.ownVersion)()}`);
|
|
94
96
|
}
|
|
95
97
|
else {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
98
|
+
// One npm install over this baychat at a time: the relay may be installing one by itself.
|
|
99
|
+
const unlock = (0, store_1.acquireUpdateLock)();
|
|
100
|
+
if (!unlock) {
|
|
101
|
+
console.error("BayChat is updating itself right now (the relay is installing a new version). Try again in a minute.");
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
let outcome;
|
|
105
|
+
try {
|
|
106
|
+
outcome = await (0, self_upgrade_1.upgradePackage)({
|
|
107
|
+
install: (0, self_upgrade_1.classifyInstall)((0, self_upgrade_1.currentEntry)()),
|
|
108
|
+
current: (0, self_upgrade_1.ownVersion)(),
|
|
109
|
+
npm: self_upgrade_1.realNpm,
|
|
110
|
+
readVersion: self_upgrade_1.readPackageVersion,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
unlock();
|
|
115
|
+
}
|
|
102
116
|
const told = (0, self_upgrade_1.describeOutcome)(outcome);
|
|
103
117
|
if (!told.ok) {
|
|
104
118
|
// Never "Updated this machine" over a package that did not move. Stop before the relay
|
|
@@ -122,33 +136,22 @@ async function cmdUpdate(opts = {}) {
|
|
|
122
136
|
if (told.done)
|
|
123
137
|
done.push(told.done);
|
|
124
138
|
}
|
|
125
|
-
// 2. EVERY CONNECTED CLIENT
|
|
126
|
-
|
|
127
|
-
const updated = (0, credential_refresh_1.refreshExistingClientConfigs)({ url: `${device.baseUrl}/api/mcp`, token: device.token }, commands_1.realConfigIo, (0, client_paths_1.currentPathEnv)());
|
|
128
|
-
for (const file of updated)
|
|
129
|
-
done.push(`refreshed ${file}`);
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
// Never fatal: the next steps are independent, and a machine that failed one of four
|
|
133
|
-
// is better off doing the other three than stopping at the first.
|
|
134
|
-
say(` Could not refresh client configs: ${errText(err)}`);
|
|
135
|
-
}
|
|
136
|
-
// 3. CLAUDE CODE, which is not an MCP-config client and registers through its own CLI.
|
|
137
|
-
if (hasBinary("claude") && (0, commands_1.registerWithClaude)(device.baseUrl, device.token)) {
|
|
138
|
-
done.push("re-registered BayChat with Claude Code");
|
|
139
|
-
}
|
|
139
|
+
// 2 + 3. EVERY CONNECTED CLIENT, and Claude Code.
|
|
140
|
+
done.push(...(0, refresh_clients_1.refreshConnectedClients)(device, say));
|
|
140
141
|
// 4. THE RELAY — the step people forget, and the one that silently keeps old code
|
|
141
142
|
// running. Stopped and started rather than signalled: the daemon loads its code at
|
|
142
143
|
// process start, so nothing short of a restart picks up a new version.
|
|
143
144
|
try {
|
|
144
|
-
await (0,
|
|
145
|
+
await (0, commands_1.cmdRelayStop)();
|
|
145
146
|
}
|
|
146
147
|
catch {
|
|
147
148
|
// Not running is the normal case on a fresh boot, and is not a failure to report.
|
|
148
149
|
}
|
|
149
|
-
const relay = await (0,
|
|
150
|
+
const relay = await (0, commands_1.ensureRelayInstalled)();
|
|
150
151
|
done.push(relay.startsWith("Relay:") ? relay.toLowerCase() : relay);
|
|
151
152
|
writeStamp();
|
|
153
|
+
// Whatever the relay left for the terminal ("x is available", "updated itself") is answered now.
|
|
154
|
+
(0, store_1.takeUpdateNotice)();
|
|
152
155
|
if (done.length === 0) {
|
|
153
156
|
say("Everything was already up to date.");
|
|
154
157
|
return 0;
|
|
@@ -164,6 +167,27 @@ async function cmdUpdate(opts = {}) {
|
|
|
164
167
|
console.log(` ✓ ${line}`);
|
|
165
168
|
return 0;
|
|
166
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* `baychat update --auto on|off` — the relay's automatic updates for this computer (default on).
|
|
172
|
+
* Read by the relay on its next look, within a minute; nothing needs restarting.
|
|
173
|
+
*/
|
|
174
|
+
function setAutoUpdate(value) {
|
|
175
|
+
if (value !== "on" && value !== "off") {
|
|
176
|
+
console.error("Usage: baychat update --auto on|off");
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
(0, store_1.saveAutoSetting)(value);
|
|
180
|
+
const envOff = (0, policy_1.autoUpdateOffReason)(process.env, "on");
|
|
181
|
+
if (value === "off") {
|
|
182
|
+
console.log("Automatic updates are off on this computer. Update with `baychat update` when you choose; `baychat update --auto on` turns them back on.");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log("Automatic updates are on: the relay installs new BayChat versions by itself in a quiet moment, and asks on your phone first when a version needs you.");
|
|
186
|
+
if (envOff)
|
|
187
|
+
console.log(`Note: ${envOff} is set here, which keeps them off for anything started from this shell.`);
|
|
188
|
+
}
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
167
191
|
/**
|
|
168
192
|
* Has it been long enough for `start` to check again?
|
|
169
193
|
*
|
|
@@ -217,10 +241,6 @@ function runOnNewCode(packageRoot, from, quiet) {
|
|
|
217
241
|
}
|
|
218
242
|
return child.status ?? 1;
|
|
219
243
|
}
|
|
220
|
-
function hasBinary(name) {
|
|
221
|
-
const probe = (0, child_process_1.spawnSync)(name, ["--version"], { stdio: "ignore", timeout: 15_000 });
|
|
222
|
-
return !probe.error && probe.status === 0;
|
|
223
|
-
}
|
|
224
244
|
function errText(err) {
|
|
225
245
|
return err instanceof Error ? err.message : String(err);
|
|
226
246
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.markWaitingCard = markWaitingCard;
|
|
37
|
+
exports.countWaitingCards = countWaitingCards;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const config_1 = require("./config");
|
|
41
|
+
const parent_watch_1 = require("./relay/parent-watch");
|
|
42
|
+
/**
|
|
43
|
+
* Which processes on this computer are waiting for a tap on a decision card right now.
|
|
44
|
+
*
|
|
45
|
+
* The hooks (`approve-hook`, `denied-hook`) and the Claude Code channel each wait on their own
|
|
46
|
+
* card in their own process, so nothing else on the machine knows a question is open. The relay's
|
|
47
|
+
* automatic update must (docs/features/CLI_AUTO_UPDATE.md): it never restarts BayChat while
|
|
48
|
+
* somebody is about to answer one. So each waiter leaves a marker for as long as it waits —
|
|
49
|
+
* `~/.baychat/waiting-cards/<pid>-<card id>` — and removes it when done. A crashed waiter's marker
|
|
50
|
+
* is ignored (and cleared) because its pid is gone; an absurdly old one because pids are reused.
|
|
51
|
+
*/
|
|
52
|
+
/** Longer than any card is waited on (the denied-command card's 3600 s, an approval's day). */
|
|
53
|
+
const MARKER_MAX_AGE_MS = 2 * 24 * 60 * 60 * 1000;
|
|
54
|
+
function markerDir() {
|
|
55
|
+
return path.join((0, config_1.configDir)(), "waiting-cards");
|
|
56
|
+
}
|
|
57
|
+
/** Mark `cardId` as waited on by this process. Returns the function that ends the mark. Never throws. */
|
|
58
|
+
function markWaitingCard(cardId, pid = process.pid) {
|
|
59
|
+
const safeId = cardId.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 64) || "card";
|
|
60
|
+
let file;
|
|
61
|
+
try {
|
|
62
|
+
file = path.join(markerDir(), `${pid}-${safeId}`);
|
|
63
|
+
fs.mkdirSync(markerDir(), { recursive: true, mode: 0o700 });
|
|
64
|
+
fs.writeFileSync(file, "");
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Without the marker an update might restart the relay mid-question; the card itself still
|
|
68
|
+
// works, because its wait never goes through the relay. Not worth failing a hook over.
|
|
69
|
+
return () => undefined;
|
|
70
|
+
}
|
|
71
|
+
return () => {
|
|
72
|
+
try {
|
|
73
|
+
fs.rmSync(file, { force: true });
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// A marker left behind is ignored once this process has exited (its pid is gone).
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** How many cards live processes are waiting on. Stale markers are removed on the way. */
|
|
81
|
+
function countWaitingCards(isAlive = parent_watch_1.processIsAlive, now = Date.now()) {
|
|
82
|
+
let names;
|
|
83
|
+
try {
|
|
84
|
+
names = fs.readdirSync(markerDir());
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// No directory: nobody has ever waited on a card here.
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
let live = 0;
|
|
91
|
+
for (const name of names) {
|
|
92
|
+
const file = path.join(markerDir(), name);
|
|
93
|
+
const pid = Number(name.split("-")[0]);
|
|
94
|
+
let fresh = false;
|
|
95
|
+
try {
|
|
96
|
+
fresh = now - fs.statSync(file).mtimeMs < MARKER_MAX_AGE_MS;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
continue; // Removed between the listing and the look: its waiter finished.
|
|
100
|
+
}
|
|
101
|
+
if (Number.isInteger(pid) && pid > 1 && fresh && isAlive(pid))
|
|
102
|
+
live += 1;
|
|
103
|
+
else
|
|
104
|
+
fs.rmSync(file, { force: true });
|
|
105
|
+
}
|
|
106
|
+
return live;
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "baychat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.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"
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"vitest": "^4.1.10"
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
|
+
"baychat": {
|
|
26
|
+
"attention": {}
|
|
27
|
+
},
|
|
25
28
|
"homepage": "https://baychat.io",
|
|
26
29
|
"keywords": [
|
|
27
30
|
"baychat",
|