baychat 0.25.2 → 0.25.3
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/relay/autostart.js +77 -2
- package/dist/relay/commands.js +16 -1
- package/package.json +1 -1
package/dist/relay/autostart.js
CHANGED
|
@@ -33,9 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.AUTOSTART_IMPLEMENTATIONS = exports.WINDOWS_RELAY_LAUNCHER = exports.LingerUnavailable = exports.SYSTEMD_UNIT_NAME = exports.SERVICE_LABEL = void 0;
|
|
36
|
+
exports.AUTOSTART_IMPLEMENTATIONS = exports.WINDOWS_RELAY_LAUNCHER = exports.MAC_SEARCH_PATH = exports.LingerUnavailable = exports.SYSTEMD_UNIT_NAME = exports.SERVICE_LABEL = void 0;
|
|
37
|
+
exports.launchdLogPath = launchdLogPath;
|
|
37
38
|
exports.autostartForPlatform = autostartForPlatform;
|
|
38
39
|
exports.relayCommand = relayCommand;
|
|
40
|
+
exports.stableNodePath = stableNodePath;
|
|
39
41
|
// Making the relay come back by itself, on all three platforms.
|
|
40
42
|
//
|
|
41
43
|
// THE BUG THIS EXISTS TO FIX. `baychat relay start` used to write a systemd user
|
|
@@ -174,7 +176,13 @@ const launchd = {
|
|
|
174
176
|
<dict>
|
|
175
177
|
<key>NODE_ENV</key>
|
|
176
178
|
<string>production</string>
|
|
179
|
+
<key>PATH</key>
|
|
180
|
+
<string>${exports.MAC_SEARCH_PATH}</string>
|
|
177
181
|
</dict>
|
|
182
|
+
<key>StandardOutPath</key>
|
|
183
|
+
<string>${escapeXml(launchdLogPath())}</string>
|
|
184
|
+
<key>StandardErrorPath</key>
|
|
185
|
+
<string>${escapeXml(launchdLogPath())}</string>
|
|
178
186
|
<key>ProcessType</key>
|
|
179
187
|
<string>Background</string>
|
|
180
188
|
</dict>
|
|
@@ -185,6 +193,7 @@ const launchd = {
|
|
|
185
193
|
const target = launchd.unitPath();
|
|
186
194
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
187
195
|
fs.writeFileSync(target, launchd.unit(cmd), { mode: 0o644 });
|
|
196
|
+
prepareLaunchdLog();
|
|
188
197
|
const domain = `gui/${os.userInfo().uid}`;
|
|
189
198
|
// Replacing an agent means removing the old one first; bootout on a label
|
|
190
199
|
// that was never loaded exits non-zero, which is not a failure here.
|
|
@@ -203,6 +212,46 @@ const launchd = {
|
|
|
203
212
|
fs.rmSync(launchd.unitPath(), { force: true });
|
|
204
213
|
},
|
|
205
214
|
};
|
|
215
|
+
/**
|
|
216
|
+
* The PATH the relay — and every session it wakes — runs with on macOS.
|
|
217
|
+
*
|
|
218
|
+
* launchd starts an agent with `/usr/bin:/bin:/usr/sbin:/sbin` and nothing else
|
|
219
|
+
* (measured on a Mac mini, 2026-09-27), so a woken session could not find `gh`,
|
|
220
|
+
* `docker` or anything else Homebrew installed. This is the PATH a Mac Terminal has:
|
|
221
|
+
* Homebrew's two prefixes (Apple silicon), `/usr/local/bin` (Homebrew on Intel, and
|
|
222
|
+
* most installers), then the system's own. A missing directory in PATH costs nothing.
|
|
223
|
+
* Node's own directory is added again at spawn time — see ./spawn-env.ts.
|
|
224
|
+
*/
|
|
225
|
+
exports.MAC_SEARCH_PATH = "/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
226
|
+
/** Past this size the log is set aside when the relay is (re)installed. */
|
|
227
|
+
const LAUNCHD_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
228
|
+
/**
|
|
229
|
+
* Where the macOS relay's output goes: the per-user Logs folder, which Console.app
|
|
230
|
+
* lists under Log Reports. systemd keeps a unit's output in the journal; launchd
|
|
231
|
+
* throws it away unless the plist names a file.
|
|
232
|
+
*/
|
|
233
|
+
function launchdLogPath() {
|
|
234
|
+
return path.join(os.homedir(), "Library", "Logs", "BayChat", "relay.log");
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Make sure the log's folder exists, and set a large log aside.
|
|
238
|
+
*
|
|
239
|
+
* launchd appends and never rotates. `relay start` runs at every install and every
|
|
240
|
+
* `baychat update`, so keeping one previous file here bounds the log without a
|
|
241
|
+
* rotation daemon (newsyslog needs root).
|
|
242
|
+
*/
|
|
243
|
+
function prepareLaunchdLog() {
|
|
244
|
+
const log = launchdLogPath();
|
|
245
|
+
fs.mkdirSync(path.dirname(log), { recursive: true });
|
|
246
|
+
try {
|
|
247
|
+
if (fs.statSync(log).size > LAUNCHD_LOG_MAX_BYTES)
|
|
248
|
+
fs.renameSync(log, `${log}.1`);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
if (err.code !== "ENOENT")
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
206
255
|
/**
|
|
207
256
|
* Windows — a Task Scheduler task, triggered at logon.
|
|
208
257
|
*
|
|
@@ -370,10 +419,36 @@ function relayCommand(physicalPath = (value) => fs.realpathSync.native(value)) {
|
|
|
370
419
|
// be absent from Task Scheduler's filesystem view. Resolve the actual file
|
|
371
420
|
// before handing it to a process outside that package (observed on Windows).
|
|
372
421
|
return {
|
|
373
|
-
node: physicalPath(process.execPath),
|
|
422
|
+
node: stableNodePath(physicalPath(process.execPath), physicalPath),
|
|
374
423
|
entry: physicalPath(path.resolve(process.argv[1] ?? "")),
|
|
375
424
|
};
|
|
376
425
|
}
|
|
426
|
+
/** `<prefix>/Cellar/<formula>/<version>/bin/node` — one Homebrew version's own folder. */
|
|
427
|
+
const HOMEBREW_CELLAR_NODE = /^(.*)\/Cellar\/([^/]+)\/[^/]+\/bin\/node$/;
|
|
428
|
+
/**
|
|
429
|
+
* The same node, by a path that survives `brew upgrade`.
|
|
430
|
+
*
|
|
431
|
+
* Homebrew installs every version into its own `Cellar/<formula>/<version>` folder and
|
|
432
|
+
* deletes the old one on upgrade; `process.execPath` names that folder. A unit written
|
|
433
|
+
* with it stops starting after the next upgrade, silently, at the next login. The
|
|
434
|
+
* formula's `opt/<formula>` link always points at the installed version, so it is used —
|
|
435
|
+
* but only when it resolves to this very node now, so nothing changes about which node
|
|
436
|
+
* runs today. Any other install (system, nvm, Windows) is returned unchanged.
|
|
437
|
+
*/
|
|
438
|
+
function stableNodePath(node, physicalPath) {
|
|
439
|
+
const match = HOMEBREW_CELLAR_NODE.exec(node);
|
|
440
|
+
if (!match)
|
|
441
|
+
return node;
|
|
442
|
+
const [, prefix, formula] = match;
|
|
443
|
+
const opt = `${prefix}/opt/${formula}/bin/node`;
|
|
444
|
+
try {
|
|
445
|
+
return physicalPath(opt) === node ? opt : node;
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
// No opt link (an unusual Homebrew layout): the versioned path still works today.
|
|
449
|
+
return node;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
377
452
|
/** Exported for tests, which need to reach a platform other than the one they
|
|
378
453
|
* run on. Production code goes through {@link autostartForPlatform}. */
|
|
379
454
|
exports.AUTOSTART_IMPLEMENTATIONS = {
|
package/dist/relay/commands.js
CHANGED
|
@@ -101,6 +101,12 @@ function request(sock, frame, timeoutMs = 5_000) {
|
|
|
101
101
|
clearTimeout(timer);
|
|
102
102
|
reject(err);
|
|
103
103
|
});
|
|
104
|
+
// The relay went away without answering. Waiting out the timer would only delay
|
|
105
|
+
// the same answer; the caller decides whether gone was what it wanted.
|
|
106
|
+
sock.on("close", () => {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
reject(new Error("relay closed the connection without answering"));
|
|
109
|
+
});
|
|
104
110
|
(0, socket_1.writeFrame)(sock, frame);
|
|
105
111
|
});
|
|
106
112
|
}
|
|
@@ -386,7 +392,16 @@ async function cmdRelayStop() {
|
|
|
386
392
|
if (await (0, socket_1.probeSocket)((0, socket_1.socketPath)())) {
|
|
387
393
|
// A windowless supervisor may stop without killing its child. Ask the
|
|
388
394
|
// relay itself to shut down, and wait so the next start cannot reuse it.
|
|
389
|
-
|
|
395
|
+
try {
|
|
396
|
+
await request(await connectOrFail(), { type: "stop" });
|
|
397
|
+
}
|
|
398
|
+
catch (err) {
|
|
399
|
+
// launchd's bootout above already sent the relay SIGTERM; it can accept this
|
|
400
|
+
// connection and exit before answering. A relay that is gone is what was asked
|
|
401
|
+
// for — only a relay still listening is a failure to report.
|
|
402
|
+
if (await (0, socket_1.probeSocket)((0, socket_1.socketPath)()))
|
|
403
|
+
throw err;
|
|
404
|
+
}
|
|
390
405
|
const deadline = Date.now() + 5_000;
|
|
391
406
|
while (await (0, socket_1.probeSocket)((0, socket_1.socketPath)())) {
|
|
392
407
|
if (Date.now() >= deadline)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "baychat",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.3",
|
|
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"
|