usebeeline 0.0.113 → 0.0.115
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/usebeeline.mjs +1285 -600
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -2415,7 +2415,7 @@ var require_websocket = __commonJS({
|
|
|
2415
2415
|
var http = __require("http");
|
|
2416
2416
|
var net2 = __require("net");
|
|
2417
2417
|
var tls = __require("tls");
|
|
2418
|
-
var { randomBytes: randomBytes7, createHash:
|
|
2418
|
+
var { randomBytes: randomBytes7, createHash: createHash10 } = __require("crypto");
|
|
2419
2419
|
var { Duplex, Readable } = __require("stream");
|
|
2420
2420
|
var { URL: URL2 } = __require("url");
|
|
2421
2421
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -3083,7 +3083,7 @@ var require_websocket = __commonJS({
|
|
|
3083
3083
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
3084
3084
|
return;
|
|
3085
3085
|
}
|
|
3086
|
-
const digest =
|
|
3086
|
+
const digest = createHash10("sha1").update(key + GUID).digest("base64");
|
|
3087
3087
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
3088
3088
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
3089
3089
|
return;
|
|
@@ -3452,7 +3452,7 @@ var require_websocket_server = __commonJS({
|
|
|
3452
3452
|
var EventEmitter2 = __require("events");
|
|
3453
3453
|
var http = __require("http");
|
|
3454
3454
|
var { Duplex } = __require("stream");
|
|
3455
|
-
var { createHash:
|
|
3455
|
+
var { createHash: createHash10 } = __require("crypto");
|
|
3456
3456
|
var extension2 = require_extension();
|
|
3457
3457
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
3458
3458
|
var subprotocol2 = require_subprotocol();
|
|
@@ -3759,7 +3759,7 @@ var require_websocket_server = __commonJS({
|
|
|
3759
3759
|
);
|
|
3760
3760
|
}
|
|
3761
3761
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
3762
|
-
const digest =
|
|
3762
|
+
const digest = createHash10("sha1").update(key + GUID).digest("base64");
|
|
3763
3763
|
const headers = [
|
|
3764
3764
|
"HTTP/1.1 101 Switching Protocols",
|
|
3765
3765
|
"Upgrade: websocket",
|
|
@@ -3984,14 +3984,14 @@ __export(self_update_exports, {
|
|
|
3984
3984
|
writeUpdateAttemptFixture: () => writeUpdateAttemptFixture,
|
|
3985
3985
|
writeUpdateState: () => writeUpdateState
|
|
3986
3986
|
});
|
|
3987
|
-
import { createHash as
|
|
3987
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
3988
3988
|
import { constants as fsConstants2 } from "node:fs";
|
|
3989
3989
|
import { access, chmod as chmod5, lstat as lstat3, mkdir as mkdir15, open, readFile as readFile10, rename as rename4, rm as rm6, symlink as symlink3, writeFile as writeFile10 } from "node:fs/promises";
|
|
3990
|
-
import { spawn as
|
|
3991
|
-
import { homedir as
|
|
3992
|
-
import { dirname as
|
|
3990
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
3991
|
+
import { homedir as homedir10 } from "node:os";
|
|
3992
|
+
import { dirname as dirname12, join as join11, resolve as resolve23 } from "node:path";
|
|
3993
3993
|
function anchorLayout(rawLibDir) {
|
|
3994
|
-
const libDir =
|
|
3994
|
+
const libDir = resolve23(rawLibDir);
|
|
3995
3995
|
const segments = libDir.split(/[/\\]/);
|
|
3996
3996
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
3997
3997
|
if (idx >= 2 && segments[idx - 1] === "lib") {
|
|
@@ -4007,9 +4007,9 @@ function anchorLayout(rawLibDir) {
|
|
|
4007
4007
|
// prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
|
|
4008
4008
|
// defect that made activateRelease write its stable forwarders where
|
|
4009
4009
|
// nothing executed them, leaving stale raw wrappers in <prefix>/bin.
|
|
4010
|
-
binDir:
|
|
4010
|
+
binDir: resolve23(libDir, "../../bin"),
|
|
4011
4011
|
libDir,
|
|
4012
|
-
releasesRoot:
|
|
4012
|
+
releasesRoot: resolve23(libDir, `../${RELEASES_SEGMENT}`)
|
|
4013
4013
|
};
|
|
4014
4014
|
}
|
|
4015
4015
|
function beelineInstallLayout(env = process.env) {
|
|
@@ -4019,8 +4019,8 @@ function beelineInstallLayout(env = process.env) {
|
|
|
4019
4019
|
return anchorLayout(raw);
|
|
4020
4020
|
}
|
|
4021
4021
|
function defaultBeelineInstallLayout(env = process.env) {
|
|
4022
|
-
const home = env.HOME?.trim() ||
|
|
4023
|
-
return anchorLayout(
|
|
4022
|
+
const home = env.HOME?.trim() || homedir10();
|
|
4023
|
+
return anchorLayout(resolve23(home, ".local", "lib", "beeline"));
|
|
4024
4024
|
}
|
|
4025
4025
|
function discoveredBeelineInstallLayout(env = process.env) {
|
|
4026
4026
|
const explicitAnchor = env.BEELINE_INSTALL_LIB_DIR?.trim();
|
|
@@ -4028,7 +4028,7 @@ function discoveredBeelineInstallLayout(env = process.env) {
|
|
|
4028
4028
|
return anchorLayout(explicitAnchor);
|
|
4029
4029
|
const explicitBinDir = env.BEELINE_INSTALL_DIR?.trim();
|
|
4030
4030
|
if (explicitBinDir)
|
|
4031
|
-
return anchorLayout(
|
|
4031
|
+
return anchorLayout(resolve23(dirname12(resolve23(explicitBinDir)), "lib", "beeline"));
|
|
4032
4032
|
return defaultBeelineInstallLayout(env);
|
|
4033
4033
|
}
|
|
4034
4034
|
function hostPlatformKey() {
|
|
@@ -4039,7 +4039,7 @@ function hostPlatformKey() {
|
|
|
4039
4039
|
return `${os}-${arch}`;
|
|
4040
4040
|
}
|
|
4041
4041
|
function bundleJsonCandidates(bundleDir) {
|
|
4042
|
-
return [
|
|
4042
|
+
return [join11(bundleDir, "lib", "beeline", "bundle.json"), join11(bundleDir, "bundle.json")];
|
|
4043
4043
|
}
|
|
4044
4044
|
async function readBundleJson(bundleDir) {
|
|
4045
4045
|
let raw;
|
|
@@ -4063,7 +4063,7 @@ async function readBundleJson(bundleDir) {
|
|
|
4063
4063
|
}
|
|
4064
4064
|
}
|
|
4065
4065
|
function updateStatePath(layout) {
|
|
4066
|
-
return
|
|
4066
|
+
return join11(layout.releasesRoot, ".state", "update-state.json");
|
|
4067
4067
|
}
|
|
4068
4068
|
async function readUpdateState(layout) {
|
|
4069
4069
|
try {
|
|
@@ -4073,7 +4073,7 @@ async function readUpdateState(layout) {
|
|
|
4073
4073
|
}
|
|
4074
4074
|
}
|
|
4075
4075
|
async function writeUpdateState(layout, state) {
|
|
4076
|
-
await mkdir15(
|
|
4076
|
+
await mkdir15(join11(layout.releasesRoot, ".state"), { recursive: true });
|
|
4077
4077
|
await writeFile10(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
|
|
4078
4078
|
`, "utf8");
|
|
4079
4079
|
}
|
|
@@ -4137,7 +4137,7 @@ async function fetchText(url, fetchImpl) {
|
|
|
4137
4137
|
}
|
|
4138
4138
|
function run(command, args, timeoutMs) {
|
|
4139
4139
|
return new Promise((resolveRun) => {
|
|
4140
|
-
const child =
|
|
4140
|
+
const child = spawn8(command, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
4141
4141
|
let stderr = "";
|
|
4142
4142
|
child.stderr?.setEncoding("utf8");
|
|
4143
4143
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -4155,7 +4155,7 @@ function run(command, args, timeoutMs) {
|
|
|
4155
4155
|
});
|
|
4156
4156
|
}
|
|
4157
4157
|
function entrypointCandidates(bundleDir) {
|
|
4158
|
-
return [
|
|
4158
|
+
return [join11(bundleDir, BUNDLE_ENTRYPOINT), join11(bundleDir, "beeline-cli.mjs")];
|
|
4159
4159
|
}
|
|
4160
4160
|
async function resolveBundleEntrypoint(bundleDir) {
|
|
4161
4161
|
for (const candidate of entrypointCandidates(bundleDir)) {
|
|
@@ -4177,8 +4177,8 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4177
4177
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
4178
4178
|
const log2 = opts.logger ?? ((line) => console.log(`[body] self-update: ${line}`));
|
|
4179
4179
|
const releaseId = sanitizeReleaseId(published.commit ?? published.version ?? `release-${Date.now()}`);
|
|
4180
|
-
const releaseDir =
|
|
4181
|
-
const okMarker =
|
|
4180
|
+
const releaseDir = join11(layout.releasesRoot, releaseId);
|
|
4181
|
+
const okMarker = join11(releaseDir, ".stage-ok");
|
|
4182
4182
|
let previouslyVerified = false;
|
|
4183
4183
|
try {
|
|
4184
4184
|
const recorded = await readFile10(okMarker, "utf8");
|
|
@@ -4188,7 +4188,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4188
4188
|
} catch {
|
|
4189
4189
|
}
|
|
4190
4190
|
await mkdir15(releaseDir, { recursive: true });
|
|
4191
|
-
const tempArchive =
|
|
4191
|
+
const tempArchive = join11(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
|
|
4192
4192
|
try {
|
|
4193
4193
|
log2(`downloading ${published.file}`);
|
|
4194
4194
|
const response = await fetchImpl(archiveUrlFor(manifestUrl, published.file), {
|
|
@@ -4197,7 +4197,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4197
4197
|
if (!response.ok || !response.body) {
|
|
4198
4198
|
throw new Error(`downloading ${published.file} failed: HTTP ${response.status}`);
|
|
4199
4199
|
}
|
|
4200
|
-
const hash =
|
|
4200
|
+
const hash = createHash8("sha256");
|
|
4201
4201
|
const chunks = [];
|
|
4202
4202
|
for await (const chunk of response.body) {
|
|
4203
4203
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
@@ -4210,7 +4210,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4210
4210
|
}
|
|
4211
4211
|
await writeFile10(tempArchive, Buffer.concat(chunks), { mode: 384 });
|
|
4212
4212
|
const entries = (await new Promise((resolveList, rejectList) => {
|
|
4213
|
-
const child =
|
|
4213
|
+
const child = spawn8("tar", ["-tzf", tempArchive], { stdio: ["ignore", "pipe", "inherit"] });
|
|
4214
4214
|
let out = "";
|
|
4215
4215
|
child.stdout?.on("data", (chunk) => {
|
|
4216
4216
|
out += chunk.toString("utf8");
|
|
@@ -4228,13 +4228,13 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4228
4228
|
throw new Error(`extracting bundle failed: ${extract2.stderr}`);
|
|
4229
4229
|
for (const relative3 of requiredBundlePaths()) {
|
|
4230
4230
|
try {
|
|
4231
|
-
await access(
|
|
4231
|
+
await access(join11(releaseDir, relative3), fsConstants2.F_OK);
|
|
4232
4232
|
} catch {
|
|
4233
4233
|
throw new Error(`staged bundle is missing ${relative3}`);
|
|
4234
4234
|
}
|
|
4235
4235
|
}
|
|
4236
4236
|
if (opts.smokeTestCli !== false) {
|
|
4237
|
-
const probe = await run(process.execPath, [
|
|
4237
|
+
const probe = await run(process.execPath, [join11(releaseDir, BUNDLE_ENTRYPOINT), "--version"], 6e4);
|
|
4238
4238
|
if (probe.status !== 0) {
|
|
4239
4239
|
throw new Error(`staged bundle failed its startup smoke test (--version exited ${probe.status})${probe.stderr ? `: ${probe.stderr.trim()}` : ""}`);
|
|
4240
4240
|
}
|
|
@@ -4272,8 +4272,8 @@ async function replaceFile(path, contents, mode) {
|
|
|
4272
4272
|
await rename4(temp, path);
|
|
4273
4273
|
}
|
|
4274
4274
|
async function activateRelease(layout, releaseId) {
|
|
4275
|
-
const releaseDir =
|
|
4276
|
-
await access(
|
|
4275
|
+
const releaseDir = join11(layout.releasesRoot, releaseId);
|
|
4276
|
+
await access(join11(releaseDir, BUNDLE_ENTRYPOINT), fsConstants2.F_OK);
|
|
4277
4277
|
await mkdir15(layout.releasesRoot, { recursive: true });
|
|
4278
4278
|
await mkdir15(layout.binDir, { recursive: true });
|
|
4279
4279
|
let previousReleaseId = await activeReleaseId(layout);
|
|
@@ -4281,12 +4281,12 @@ async function activateRelease(layout, releaseId) {
|
|
|
4281
4281
|
if (kind === "directory") {
|
|
4282
4282
|
const legacyIdentity = await readBundleJson(layout.libDir);
|
|
4283
4283
|
const legacyId = sanitizeReleaseId(legacyIdentity?.commit ?? legacyIdentity?.version ?? `legacy-${Date.now()}`);
|
|
4284
|
-
const legacyDir =
|
|
4284
|
+
const legacyDir = join11(layout.releasesRoot, legacyId);
|
|
4285
4285
|
try {
|
|
4286
4286
|
await access(legacyDir, fsConstants2.F_OK);
|
|
4287
4287
|
previousReleaseId = `${legacyId}-${Date.now()}`;
|
|
4288
|
-
await rename4(layout.libDir,
|
|
4289
|
-
await normalizeLegacyBundleShape(
|
|
4288
|
+
await rename4(layout.libDir, join11(layout.releasesRoot, previousReleaseId));
|
|
4289
|
+
await normalizeLegacyBundleShape(join11(layout.releasesRoot, previousReleaseId));
|
|
4290
4290
|
} catch {
|
|
4291
4291
|
await rename4(layout.libDir, legacyDir);
|
|
4292
4292
|
await normalizeLegacyBundleShape(legacyDir);
|
|
@@ -4295,18 +4295,18 @@ async function activateRelease(layout, releaseId) {
|
|
|
4295
4295
|
}
|
|
4296
4296
|
const tempLink = `${layout.libDir}.new-${process.pid}`;
|
|
4297
4297
|
await rm6(tempLink, { force: true });
|
|
4298
|
-
await symlink3(
|
|
4298
|
+
await symlink3(join11("beeline-releases", releaseId), tempLink);
|
|
4299
4299
|
await rename4(tempLink, layout.libDir);
|
|
4300
|
-
await fsyncDir(
|
|
4300
|
+
await fsyncDir(dirname12(layout.libDir));
|
|
4301
4301
|
await writeBinForwarders(layout, releaseDir);
|
|
4302
4302
|
return { previousReleaseId };
|
|
4303
4303
|
}
|
|
4304
4304
|
async function normalizeLegacyBundleShape(bundleDir) {
|
|
4305
|
-
const innerLib =
|
|
4305
|
+
const innerLib = join11(bundleDir, "lib", "beeline");
|
|
4306
4306
|
let anyFlat = false;
|
|
4307
4307
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
4308
4308
|
try {
|
|
4309
|
-
await access(
|
|
4309
|
+
await access(join11(bundleDir, name), fsConstants2.F_OK);
|
|
4310
4310
|
anyFlat = true;
|
|
4311
4311
|
break;
|
|
4312
4312
|
} catch {
|
|
@@ -4317,11 +4317,11 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
4317
4317
|
await mkdir15(innerLib, { recursive: true });
|
|
4318
4318
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
4319
4319
|
try {
|
|
4320
|
-
await access(
|
|
4320
|
+
await access(join11(innerLib, name), fsConstants2.F_OK);
|
|
4321
4321
|
continue;
|
|
4322
4322
|
} catch {
|
|
4323
4323
|
}
|
|
4324
|
-
await rename4(
|
|
4324
|
+
await rename4(join11(bundleDir, name), join11(innerLib, name)).catch(() => void 0);
|
|
4325
4325
|
}
|
|
4326
4326
|
}
|
|
4327
4327
|
async function writeBinForwarders(layout, activeBundleRoot) {
|
|
@@ -4330,13 +4330,13 @@ async function writeBinForwarders(layout, activeBundleRoot) {
|
|
|
4330
4330
|
...Object.entries(FORWARDER_ALIASES).map(([alias, tool]) => [alias, tool])
|
|
4331
4331
|
];
|
|
4332
4332
|
for (const [name, tool] of entries) {
|
|
4333
|
-
const target =
|
|
4333
|
+
const target = join11(activeBundleRoot, "bin", tool);
|
|
4334
4334
|
try {
|
|
4335
4335
|
await access(target, fsConstants2.X_OK);
|
|
4336
4336
|
} catch {
|
|
4337
4337
|
continue;
|
|
4338
4338
|
}
|
|
4339
|
-
await replaceFile(
|
|
4339
|
+
await replaceFile(join11(layout.binDir, name), forwarderScript(tool), 493);
|
|
4340
4340
|
}
|
|
4341
4341
|
}
|
|
4342
4342
|
async function repairInstallForwarders(layout, opts = {}) {
|
|
@@ -4345,7 +4345,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
4345
4345
|
const forwarderHealthy = async (name, tool) => {
|
|
4346
4346
|
let current;
|
|
4347
4347
|
try {
|
|
4348
|
-
current = await readFile10(
|
|
4348
|
+
current = await readFile10(join11(layout.binDir, name), "utf8");
|
|
4349
4349
|
} catch {
|
|
4350
4350
|
current = void 0;
|
|
4351
4351
|
}
|
|
@@ -4364,19 +4364,19 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
4364
4364
|
return true;
|
|
4365
4365
|
}
|
|
4366
4366
|
async function rollbackToPreviousRelease(layout, previousReleaseId) {
|
|
4367
|
-
const releaseDir =
|
|
4367
|
+
const releaseDir = join11(layout.releasesRoot, previousReleaseId);
|
|
4368
4368
|
const entrypoint = await resolveBundleEntrypoint(releaseDir);
|
|
4369
4369
|
if (!entrypoint) {
|
|
4370
4370
|
throw new Error(`release ${previousReleaseId} has no runnable CLI entrypoint`);
|
|
4371
4371
|
}
|
|
4372
4372
|
const tempLink = `${layout.libDir}.rollback-${process.pid}`;
|
|
4373
4373
|
await rm6(tempLink, { force: true });
|
|
4374
|
-
await symlink3(
|
|
4374
|
+
await symlink3(join11("beeline-releases", previousReleaseId), tempLink);
|
|
4375
4375
|
await rename4(tempLink, layout.libDir);
|
|
4376
|
-
await fsyncDir(
|
|
4376
|
+
await fsyncDir(dirname12(layout.libDir));
|
|
4377
4377
|
}
|
|
4378
4378
|
function updateAttemptPath(layout) {
|
|
4379
|
-
return
|
|
4379
|
+
return join11(layout.releasesRoot, ".state", "update-attempt.json");
|
|
4380
4380
|
}
|
|
4381
4381
|
async function readUpdateAttempt(layout) {
|
|
4382
4382
|
try {
|
|
@@ -4390,7 +4390,7 @@ async function readUpdateAttempt(layout) {
|
|
|
4390
4390
|
}
|
|
4391
4391
|
}
|
|
4392
4392
|
async function writeUpdateAttempt(layout, record3) {
|
|
4393
|
-
await mkdir15(
|
|
4393
|
+
await mkdir15(join11(layout.releasesRoot, ".state"), { recursive: true });
|
|
4394
4394
|
const path = updateAttemptPath(layout);
|
|
4395
4395
|
const staged = `${path}.${process.pid}.tmp`;
|
|
4396
4396
|
await writeFile10(staged, `${JSON.stringify(record3, null, 2)}
|
|
@@ -4668,7 +4668,7 @@ function configureNetworkFamilyDefaults(network = net) {
|
|
|
4668
4668
|
configureNetworkFamilyDefaults();
|
|
4669
4669
|
|
|
4670
4670
|
// apps/body/dist/cli.js
|
|
4671
|
-
import { dirname as
|
|
4671
|
+
import { dirname as dirname18, resolve as resolve31 } from "node:path";
|
|
4672
4672
|
import { readFile as readFile15, unlink as unlink5, writeFile as writeFile16 } from "node:fs/promises";
|
|
4673
4673
|
import { stdin as stdin4, stdout as stdout5 } from "node:process";
|
|
4674
4674
|
|
|
@@ -6079,13 +6079,496 @@ ${r2}
|
|
|
6079
6079
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
6080
6080
|
|
|
6081
6081
|
// apps/body/dist/config.js
|
|
6082
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
6082
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync3, readFileSync } from "node:fs";
|
|
6083
6083
|
import { basename, dirname, resolve as resolve2 } from "node:path";
|
|
6084
6084
|
|
|
6085
6085
|
// apps/body/dist/agent-command.js
|
|
6086
|
-
import { accessSync, constants, existsSync, readdirSync } from "node:fs";
|
|
6086
|
+
import { accessSync, constants, existsSync as existsSync2, readdirSync } from "node:fs";
|
|
6087
6087
|
import { homedir } from "node:os";
|
|
6088
6088
|
import { delimiter, isAbsolute, join, resolve } from "node:path";
|
|
6089
|
+
|
|
6090
|
+
// apps/body/dist/cursor-acp-bridge.js
|
|
6091
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
6092
|
+
import { existsSync } from "node:fs";
|
|
6093
|
+
import { createRequire } from "node:module";
|
|
6094
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
6095
|
+
import { resolve as resolvePath } from "node:path";
|
|
6096
|
+
import { fileURLToPath } from "node:url";
|
|
6097
|
+
import { randomUUID } from "node:crypto";
|
|
6098
|
+
|
|
6099
|
+
// apps/body/dist/cursor-models.js
|
|
6100
|
+
import { spawn } from "node:child_process";
|
|
6101
|
+
var CURSOR_MODELS_TIMEOUT_MS = 1e4;
|
|
6102
|
+
function parseCursorModelsOutput(output) {
|
|
6103
|
+
const options = [];
|
|
6104
|
+
for (const line of output.split(/\r?\n/)) {
|
|
6105
|
+
const match = /^\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s+-\s+(\S.*?)\s*$/.exec(line);
|
|
6106
|
+
if (!match)
|
|
6107
|
+
continue;
|
|
6108
|
+
const id = match[1];
|
|
6109
|
+
const name = match[2].replace(/\s*\(current, default\)\s*$/i, "").trim();
|
|
6110
|
+
options.push({ id, ...name && name !== id ? { name } : {} });
|
|
6111
|
+
}
|
|
6112
|
+
if (!options.length)
|
|
6113
|
+
return void 0;
|
|
6114
|
+
return {
|
|
6115
|
+
currentValue: options.some((option) => option.id === "auto") ? "auto" : options[0].id,
|
|
6116
|
+
options
|
|
6117
|
+
};
|
|
6118
|
+
}
|
|
6119
|
+
async function enumerateCursorModels(env = process.env) {
|
|
6120
|
+
return new Promise((resolve32) => {
|
|
6121
|
+
const child = spawn("cursor-agent", ["models"], {
|
|
6122
|
+
env,
|
|
6123
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6124
|
+
});
|
|
6125
|
+
let output = "";
|
|
6126
|
+
const timer = setTimeout(() => {
|
|
6127
|
+
child.kill("SIGKILL");
|
|
6128
|
+
}, CURSOR_MODELS_TIMEOUT_MS);
|
|
6129
|
+
child.stdout?.on("data", (chunk) => {
|
|
6130
|
+
output += chunk.toString();
|
|
6131
|
+
});
|
|
6132
|
+
child.on("error", () => {
|
|
6133
|
+
clearTimeout(timer);
|
|
6134
|
+
resolve32(void 0);
|
|
6135
|
+
});
|
|
6136
|
+
child.on("close", (code) => {
|
|
6137
|
+
clearTimeout(timer);
|
|
6138
|
+
resolve32(code === 0 ? parseCursorModelsOutput(output) : void 0);
|
|
6139
|
+
});
|
|
6140
|
+
});
|
|
6141
|
+
}
|
|
6142
|
+
|
|
6143
|
+
// apps/body/dist/cursor-acp-bridge.js
|
|
6144
|
+
var CURSOR_ACP_BRIDGE_FLAG = "--cursor-acp-bridge";
|
|
6145
|
+
var CURSOR_ACP_BRIDGE_LABEL = "cursor-acp-bridge";
|
|
6146
|
+
var CURSOR_AGENT_FORCE_FLAG = "--force";
|
|
6147
|
+
var CURSOR_AGENT_TRUST_FLAG = "--trust";
|
|
6148
|
+
var CURSOR_HARNESS_COMMAND = /(^|[/\\])(cursor-acp-bridge|cursor-agent-acp)(\.[a-z]+)?$/i;
|
|
6149
|
+
var STDERR_TAIL_MAX_CHARS = 2e3;
|
|
6150
|
+
var MODEL_AXIS_ID = "model";
|
|
6151
|
+
function harnessIdentityLabel(agent) {
|
|
6152
|
+
if (agent.kind === "cursor")
|
|
6153
|
+
return CURSOR_ACP_BRIDGE_LABEL;
|
|
6154
|
+
return agent.command ?? "";
|
|
6155
|
+
}
|
|
6156
|
+
function cursorAcpBridgeLaunch() {
|
|
6157
|
+
const meta = "usebeeline:npm";
|
|
6158
|
+
if (meta === "beeline:bundle" || meta.startsWith("beeline:")) {
|
|
6159
|
+
const entry = process.argv[1];
|
|
6160
|
+
if (!entry) {
|
|
6161
|
+
throw new Error("Cursor ACP bridge cannot resolve the Beeline CLI entry");
|
|
6162
|
+
}
|
|
6163
|
+
return { command: process.execPath, args: [entry, CURSOR_ACP_BRIDGE_FLAG] };
|
|
6164
|
+
}
|
|
6165
|
+
const js = fileURLToPath(new URL("./cursor-acp-bridge.js", meta));
|
|
6166
|
+
const ts = fileURLToPath(new URL("./cursor-acp-bridge.ts", meta));
|
|
6167
|
+
if (existsSync(js))
|
|
6168
|
+
return { command: process.execPath, args: [js] };
|
|
6169
|
+
if (existsSync(ts)) {
|
|
6170
|
+
const tsx = createRequire(meta).resolve("tsx");
|
|
6171
|
+
return { command: process.execPath, args: ["--import", tsx, ts] };
|
|
6172
|
+
}
|
|
6173
|
+
throw new Error("Cursor ACP bridge entry not found next to the Beeline helper");
|
|
6174
|
+
}
|
|
6175
|
+
function cursorAgentArgv(input) {
|
|
6176
|
+
const argv = [
|
|
6177
|
+
"--print",
|
|
6178
|
+
"--output-format",
|
|
6179
|
+
"stream-json",
|
|
6180
|
+
CURSOR_AGENT_TRUST_FLAG,
|
|
6181
|
+
CURSOR_AGENT_FORCE_FLAG
|
|
6182
|
+
];
|
|
6183
|
+
if (input.model && input.model !== "auto")
|
|
6184
|
+
argv.push("--model", input.model);
|
|
6185
|
+
argv.push(input.prompt);
|
|
6186
|
+
return argv;
|
|
6187
|
+
}
|
|
6188
|
+
function asRecord(value) {
|
|
6189
|
+
return value && typeof value === "object" ? value : void 0;
|
|
6190
|
+
}
|
|
6191
|
+
function textChunksFromContent(content) {
|
|
6192
|
+
if (typeof content === "string" && content)
|
|
6193
|
+
return [content];
|
|
6194
|
+
if (!Array.isArray(content))
|
|
6195
|
+
return [];
|
|
6196
|
+
const chunks = [];
|
|
6197
|
+
for (const block2 of content) {
|
|
6198
|
+
const record3 = asRecord(block2);
|
|
6199
|
+
if (!record3)
|
|
6200
|
+
continue;
|
|
6201
|
+
if (record3.type === "text" && typeof record3.text === "string" && record3.text) {
|
|
6202
|
+
chunks.push(record3.text);
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
return chunks;
|
|
6206
|
+
}
|
|
6207
|
+
function thoughtText(event) {
|
|
6208
|
+
if (typeof event.text === "string")
|
|
6209
|
+
return event.text;
|
|
6210
|
+
const record3 = asRecord(event);
|
|
6211
|
+
if (!record3)
|
|
6212
|
+
return "";
|
|
6213
|
+
if (typeof record3.delta === "string")
|
|
6214
|
+
return record3.delta;
|
|
6215
|
+
return textChunksFromContent(record3.content).join("");
|
|
6216
|
+
}
|
|
6217
|
+
function assistantTexts(event) {
|
|
6218
|
+
if (typeof event.text === "string" && event.text)
|
|
6219
|
+
return [event.text];
|
|
6220
|
+
const message = asRecord(event.message);
|
|
6221
|
+
if (message)
|
|
6222
|
+
return textChunksFromContent(message.content);
|
|
6223
|
+
return textChunksFromContent(asRecord(event)?.content);
|
|
6224
|
+
}
|
|
6225
|
+
function chunkUpdate(sessionUpdate, text2) {
|
|
6226
|
+
return { sessionUpdate, content: { type: "text", text: text2 } };
|
|
6227
|
+
}
|
|
6228
|
+
function translateCursorStreamEvent(event) {
|
|
6229
|
+
if (event.type === "thinking") {
|
|
6230
|
+
if (event.subtype === "completed")
|
|
6231
|
+
return [];
|
|
6232
|
+
const text2 = thoughtText(event);
|
|
6233
|
+
return text2 ? [chunkUpdate("agent_thought_chunk", text2)] : [];
|
|
6234
|
+
}
|
|
6235
|
+
if (event.type === "assistant") {
|
|
6236
|
+
return assistantTexts(event).map((text2) => chunkUpdate("agent_message_chunk", text2));
|
|
6237
|
+
}
|
|
6238
|
+
return [];
|
|
6239
|
+
}
|
|
6240
|
+
function resultEventText(event) {
|
|
6241
|
+
if (typeof event.result === "string")
|
|
6242
|
+
return event.result;
|
|
6243
|
+
const record3 = asRecord(event.result);
|
|
6244
|
+
if (record3 && typeof record3.text === "string")
|
|
6245
|
+
return record3.text;
|
|
6246
|
+
return "";
|
|
6247
|
+
}
|
|
6248
|
+
function isCursorResultEvent(event) {
|
|
6249
|
+
return event.type === "result";
|
|
6250
|
+
}
|
|
6251
|
+
function cursorResultIsError(event) {
|
|
6252
|
+
return event.is_error === true || event.subtype === "error";
|
|
6253
|
+
}
|
|
6254
|
+
function trimReason(value) {
|
|
6255
|
+
return value.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
6256
|
+
}
|
|
6257
|
+
function describeCursorTurnFailure(input) {
|
|
6258
|
+
if (input.spawnError) {
|
|
6259
|
+
return trimReason(`cursor-agent failed to start: ${input.spawnError}`);
|
|
6260
|
+
}
|
|
6261
|
+
const stderr = input.stderr?.trim() ?? "";
|
|
6262
|
+
if (input.isError) {
|
|
6263
|
+
const named = input.resultText?.trim() || stderr || "cursor-agent reported an error with no answer text";
|
|
6264
|
+
return trimReason(`cursor-agent reported an error: ${named}`);
|
|
6265
|
+
}
|
|
6266
|
+
if (input.signal) {
|
|
6267
|
+
return trimReason(`cursor-agent terminated by ${input.signal}${stderr ? `: ${stderr}` : ""}`);
|
|
6268
|
+
}
|
|
6269
|
+
if (input.exitCode !== 0 && input.exitCode !== null) {
|
|
6270
|
+
return trimReason(`cursor-agent exited ${input.exitCode}${stderr ? `: ${stderr}` : " with no answer text"}`);
|
|
6271
|
+
}
|
|
6272
|
+
return void 0;
|
|
6273
|
+
}
|
|
6274
|
+
function promptTextFromParams(params, systemPrompt) {
|
|
6275
|
+
const record3 = asRecord(params);
|
|
6276
|
+
const blocks = Array.isArray(record3?.prompt) ? record3.prompt : [];
|
|
6277
|
+
const user = blocks.map((block2) => {
|
|
6278
|
+
const item = asRecord(block2);
|
|
6279
|
+
return item?.type === "text" && typeof item.text === "string" ? item.text : "";
|
|
6280
|
+
}).filter(Boolean).join("\n");
|
|
6281
|
+
return systemPrompt ? `${systemPrompt}
|
|
6282
|
+
|
|
6283
|
+
${user}` : user;
|
|
6284
|
+
}
|
|
6285
|
+
function defaultSpawnCursorAgent(input) {
|
|
6286
|
+
return spawn2("cursor-agent", input.argv, {
|
|
6287
|
+
cwd: input.cwd,
|
|
6288
|
+
env: input.env,
|
|
6289
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6290
|
+
});
|
|
6291
|
+
}
|
|
6292
|
+
var CursorAcpServer = class {
|
|
6293
|
+
write;
|
|
6294
|
+
spawnCursorAgent;
|
|
6295
|
+
enumerateModels;
|
|
6296
|
+
env;
|
|
6297
|
+
sessions = /* @__PURE__ */ new Map();
|
|
6298
|
+
models;
|
|
6299
|
+
modelsLoaded = false;
|
|
6300
|
+
constructor(options) {
|
|
6301
|
+
this.write = options.write;
|
|
6302
|
+
this.spawnCursorAgent = options.spawnCursorAgent ?? defaultSpawnCursorAgent;
|
|
6303
|
+
this.enumerateModels = options.enumerateModels ?? enumerateCursorModels;
|
|
6304
|
+
this.env = options.env ?? process.env;
|
|
6305
|
+
}
|
|
6306
|
+
async handle(message) {
|
|
6307
|
+
const method = typeof message.method === "string" ? message.method : "";
|
|
6308
|
+
const id = message.id;
|
|
6309
|
+
if (!method)
|
|
6310
|
+
return;
|
|
6311
|
+
if (id === void 0) {
|
|
6312
|
+
if (method === "session/cancel")
|
|
6313
|
+
this.cancel(message.params);
|
|
6314
|
+
return;
|
|
6315
|
+
}
|
|
6316
|
+
try {
|
|
6317
|
+
const result = await this.dispatch(method, message.params);
|
|
6318
|
+
this.write({ jsonrpc: "2.0", id, result });
|
|
6319
|
+
} catch (error) {
|
|
6320
|
+
const rpcError = error;
|
|
6321
|
+
this.write({
|
|
6322
|
+
jsonrpc: "2.0",
|
|
6323
|
+
id,
|
|
6324
|
+
error: {
|
|
6325
|
+
code: typeof rpcError.code === "number" ? rpcError.code : -32e3,
|
|
6326
|
+
message: error instanceof Error ? error.message : String(error)
|
|
6327
|
+
}
|
|
6328
|
+
});
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
async dispatch(method, params) {
|
|
6332
|
+
switch (method) {
|
|
6333
|
+
case "initialize":
|
|
6334
|
+
return {
|
|
6335
|
+
protocolVersion: 1,
|
|
6336
|
+
agentCapabilities: { promptCapabilities: { image: false } }
|
|
6337
|
+
};
|
|
6338
|
+
case "session/new":
|
|
6339
|
+
return this.sessionNew(params);
|
|
6340
|
+
case "session/prompt":
|
|
6341
|
+
return this.sessionPrompt(params);
|
|
6342
|
+
case "session/set_config_option":
|
|
6343
|
+
return this.setConfigOption(params);
|
|
6344
|
+
case "session/set_model":
|
|
6345
|
+
return this.setModel(params);
|
|
6346
|
+
case "shutdown":
|
|
6347
|
+
this.shutdown();
|
|
6348
|
+
return {};
|
|
6349
|
+
default:
|
|
6350
|
+
return {};
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
async loadModels() {
|
|
6354
|
+
if (this.modelsLoaded) {
|
|
6355
|
+
return this.models ?? { currentValue: "auto", options: [{ id: "auto", name: "Auto" }] };
|
|
6356
|
+
}
|
|
6357
|
+
this.modelsLoaded = true;
|
|
6358
|
+
this.models = await this.enumerateModels(this.env);
|
|
6359
|
+
return this.models ?? { currentValue: "auto", options: [{ id: "auto", name: "Auto" }] };
|
|
6360
|
+
}
|
|
6361
|
+
async sessionNew(params) {
|
|
6362
|
+
const record3 = asRecord(params);
|
|
6363
|
+
const cwd = typeof record3?.cwd === "string" && record3.cwd ? record3.cwd : process.cwd();
|
|
6364
|
+
const systemPrompt = typeof record3?.systemPrompt === "string" ? record3.systemPrompt : void 0;
|
|
6365
|
+
const catalog = await this.loadModels();
|
|
6366
|
+
const session = {
|
|
6367
|
+
id: randomUUID(),
|
|
6368
|
+
cwd,
|
|
6369
|
+
...systemPrompt ? { systemPrompt } : {},
|
|
6370
|
+
model: catalog.currentValue
|
|
6371
|
+
};
|
|
6372
|
+
this.sessions.set(session.id, session);
|
|
6373
|
+
return {
|
|
6374
|
+
sessionId: session.id,
|
|
6375
|
+
configOptions: [
|
|
6376
|
+
{
|
|
6377
|
+
id: MODEL_AXIS_ID,
|
|
6378
|
+
category: "model",
|
|
6379
|
+
currentValue: session.model,
|
|
6380
|
+
options: catalog.options.map((option) => ({
|
|
6381
|
+
id: option.id,
|
|
6382
|
+
...option.name ? { name: option.name } : {}
|
|
6383
|
+
}))
|
|
6384
|
+
}
|
|
6385
|
+
]
|
|
6386
|
+
};
|
|
6387
|
+
}
|
|
6388
|
+
session(params) {
|
|
6389
|
+
const record3 = asRecord(params);
|
|
6390
|
+
const sessionId = typeof record3?.sessionId === "string" ? record3.sessionId : "";
|
|
6391
|
+
const session = this.sessions.get(sessionId);
|
|
6392
|
+
if (!session) {
|
|
6393
|
+
const error = new Error(`unknown ACP session ${sessionId || "(missing)"}`);
|
|
6394
|
+
error.code = -32602;
|
|
6395
|
+
throw error;
|
|
6396
|
+
}
|
|
6397
|
+
return session;
|
|
6398
|
+
}
|
|
6399
|
+
setConfigOption(params) {
|
|
6400
|
+
const session = this.session(params);
|
|
6401
|
+
const record3 = asRecord(params);
|
|
6402
|
+
if (record3?.configId === MODEL_AXIS_ID && typeof record3.value === "string" && record3.value) {
|
|
6403
|
+
session.model = record3.value;
|
|
6404
|
+
}
|
|
6405
|
+
return {};
|
|
6406
|
+
}
|
|
6407
|
+
setModel(params) {
|
|
6408
|
+
const session = this.session(params);
|
|
6409
|
+
const record3 = asRecord(params);
|
|
6410
|
+
if (typeof record3?.modelId === "string" && record3.modelId)
|
|
6411
|
+
session.model = record3.modelId;
|
|
6412
|
+
return {};
|
|
6413
|
+
}
|
|
6414
|
+
emitUpdate(sessionId, update) {
|
|
6415
|
+
this.write({
|
|
6416
|
+
jsonrpc: "2.0",
|
|
6417
|
+
method: "session/update",
|
|
6418
|
+
params: { sessionId, update }
|
|
6419
|
+
});
|
|
6420
|
+
}
|
|
6421
|
+
async sessionPrompt(params) {
|
|
6422
|
+
const session = this.session(params);
|
|
6423
|
+
if (session.child) {
|
|
6424
|
+
throw new Error("cursor-agent is already running a turn for this session");
|
|
6425
|
+
}
|
|
6426
|
+
const prompt = promptTextFromParams(params, session.systemPrompt);
|
|
6427
|
+
const argv = cursorAgentArgv({ prompt, model: session.model });
|
|
6428
|
+
let stderr = "";
|
|
6429
|
+
let spawnError;
|
|
6430
|
+
let sawAssistant = false;
|
|
6431
|
+
let resultEvent;
|
|
6432
|
+
const pending = [];
|
|
6433
|
+
const consumeLine = (line) => {
|
|
6434
|
+
const trimmed = line.trim();
|
|
6435
|
+
if (!trimmed)
|
|
6436
|
+
return;
|
|
6437
|
+
let event;
|
|
6438
|
+
try {
|
|
6439
|
+
event = JSON.parse(trimmed);
|
|
6440
|
+
} catch {
|
|
6441
|
+
return;
|
|
6442
|
+
}
|
|
6443
|
+
if (isCursorResultEvent(event)) {
|
|
6444
|
+
resultEvent = event;
|
|
6445
|
+
return;
|
|
6446
|
+
}
|
|
6447
|
+
for (const update of translateCursorStreamEvent(event)) {
|
|
6448
|
+
if (update.sessionUpdate === "agent_message_chunk")
|
|
6449
|
+
sawAssistant = true;
|
|
6450
|
+
this.emitUpdate(session.id, update);
|
|
6451
|
+
}
|
|
6452
|
+
};
|
|
6453
|
+
const child = this.spawnCursorAgent({ argv, cwd: session.cwd, env: this.env });
|
|
6454
|
+
session.child = child;
|
|
6455
|
+
child.stderr?.setEncoding("utf8");
|
|
6456
|
+
child.stderr?.on("data", (chunk) => {
|
|
6457
|
+
stderr = `${stderr}${chunk}`.slice(-STDERR_TAIL_MAX_CHARS);
|
|
6458
|
+
});
|
|
6459
|
+
child.stdout?.setEncoding("utf8");
|
|
6460
|
+
child.stdout?.on("data", (chunk) => {
|
|
6461
|
+
pending.push(chunk);
|
|
6462
|
+
const joined = pending.join("");
|
|
6463
|
+
const lines = joined.split(/\r?\n/);
|
|
6464
|
+
pending.length = 0;
|
|
6465
|
+
const rest = lines.pop();
|
|
6466
|
+
if (rest)
|
|
6467
|
+
pending.push(rest);
|
|
6468
|
+
for (const line of lines)
|
|
6469
|
+
consumeLine(line);
|
|
6470
|
+
});
|
|
6471
|
+
try {
|
|
6472
|
+
const { code, signal } = await new Promise((resolve32) => {
|
|
6473
|
+
let settled = false;
|
|
6474
|
+
const finish = (exitCode, exitSignal) => {
|
|
6475
|
+
if (settled)
|
|
6476
|
+
return;
|
|
6477
|
+
settled = true;
|
|
6478
|
+
resolve32({ code: exitCode, signal: exitSignal });
|
|
6479
|
+
};
|
|
6480
|
+
child.once("error", (error) => {
|
|
6481
|
+
spawnError = error instanceof Error ? error.message : String(error);
|
|
6482
|
+
finish(1, null);
|
|
6483
|
+
});
|
|
6484
|
+
child.once("close", (exitCode, exitSignal) => {
|
|
6485
|
+
finish(exitCode, exitSignal);
|
|
6486
|
+
});
|
|
6487
|
+
});
|
|
6488
|
+
if (pending.length)
|
|
6489
|
+
consumeLine(pending.join(""));
|
|
6490
|
+
const failure = describeCursorTurnFailure({
|
|
6491
|
+
isError: resultEvent ? cursorResultIsError(resultEvent) : false,
|
|
6492
|
+
resultText: resultEvent ? resultEventText(resultEvent) : void 0,
|
|
6493
|
+
exitCode: spawnError ? 1 : code,
|
|
6494
|
+
signal,
|
|
6495
|
+
stderr,
|
|
6496
|
+
spawnError
|
|
6497
|
+
});
|
|
6498
|
+
if (failure)
|
|
6499
|
+
throw new Error(failure);
|
|
6500
|
+
if (!sawAssistant) {
|
|
6501
|
+
const fallback = resultEvent && !cursorResultIsError(resultEvent) ? resultEventText(resultEvent) : "";
|
|
6502
|
+
if (fallback)
|
|
6503
|
+
this.emitUpdate(session.id, chunkUpdate("agent_message_chunk", fallback));
|
|
6504
|
+
}
|
|
6505
|
+
return { stopReason: "end_turn" };
|
|
6506
|
+
} finally {
|
|
6507
|
+
session.child = void 0;
|
|
6508
|
+
}
|
|
6509
|
+
}
|
|
6510
|
+
cancel(params) {
|
|
6511
|
+
const record3 = asRecord(params);
|
|
6512
|
+
const sessionId = typeof record3?.sessionId === "string" ? record3.sessionId : "";
|
|
6513
|
+
const session = this.sessions.get(sessionId);
|
|
6514
|
+
if (!session?.child?.pid)
|
|
6515
|
+
return;
|
|
6516
|
+
try {
|
|
6517
|
+
session.child.kill("SIGTERM");
|
|
6518
|
+
} catch {
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
shutdown() {
|
|
6522
|
+
for (const session of this.sessions.values()) {
|
|
6523
|
+
if (!session.child?.pid)
|
|
6524
|
+
continue;
|
|
6525
|
+
try {
|
|
6526
|
+
session.child.kill("SIGTERM");
|
|
6527
|
+
} catch {
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
this.sessions.clear();
|
|
6531
|
+
}
|
|
6532
|
+
};
|
|
6533
|
+
async function runCursorAcpStdioServer(options = {}) {
|
|
6534
|
+
const server = new CursorAcpServer({
|
|
6535
|
+
...options,
|
|
6536
|
+
write: (message) => {
|
|
6537
|
+
process.stdout.write(`${JSON.stringify(message)}
|
|
6538
|
+
`);
|
|
6539
|
+
}
|
|
6540
|
+
});
|
|
6541
|
+
const lines = createInterface2({ input: process.stdin });
|
|
6542
|
+
for await (const line of lines) {
|
|
6543
|
+
const trimmed = line.trim();
|
|
6544
|
+
if (!trimmed)
|
|
6545
|
+
continue;
|
|
6546
|
+
let message;
|
|
6547
|
+
try {
|
|
6548
|
+
message = JSON.parse(trimmed);
|
|
6549
|
+
} catch {
|
|
6550
|
+
continue;
|
|
6551
|
+
}
|
|
6552
|
+
await server.handle(message);
|
|
6553
|
+
if (message.method === "shutdown")
|
|
6554
|
+
break;
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
function isDirectBridgeRun() {
|
|
6558
|
+
const entry = process.argv[1];
|
|
6559
|
+
if (!entry || entry.includes(".test."))
|
|
6560
|
+
return false;
|
|
6561
|
+
try {
|
|
6562
|
+
return resolvePath(entry) === resolvePath(fileURLToPath("usebeeline:npm"));
|
|
6563
|
+
} catch {
|
|
6564
|
+
return false;
|
|
6565
|
+
}
|
|
6566
|
+
}
|
|
6567
|
+
if (isDirectBridgeRun()) {
|
|
6568
|
+
void runCursorAcpStdioServer();
|
|
6569
|
+
}
|
|
6570
|
+
|
|
6571
|
+
// apps/body/dist/agent-command.js
|
|
6089
6572
|
var AGENT_KINDS = [
|
|
6090
6573
|
"codex",
|
|
6091
6574
|
"claude",
|
|
@@ -6117,10 +6600,6 @@ var ADAPTER_INSTALL_COMMANDS = {
|
|
|
6117
6600
|
pi: {
|
|
6118
6601
|
command: "npm",
|
|
6119
6602
|
args: ["install", "-g", "pi-acp"]
|
|
6120
|
-
},
|
|
6121
|
-
cursor: {
|
|
6122
|
-
command: "npm",
|
|
6123
|
-
args: ["install", "-g", "cursor-agent-acp"]
|
|
6124
6603
|
}
|
|
6125
6604
|
};
|
|
6126
6605
|
function adapterInstallHint(kind) {
|
|
@@ -6190,7 +6669,7 @@ function describeExecutableSearch(env = process.env) {
|
|
|
6190
6669
|
return augmentedSearchDirectories(env).join(", ");
|
|
6191
6670
|
}
|
|
6192
6671
|
function firstExisting(paths) {
|
|
6193
|
-
return paths.find((path) => path &&
|
|
6672
|
+
return paths.find((path) => path && existsSync2(path));
|
|
6194
6673
|
}
|
|
6195
6674
|
function executableOnPath(name, env = process.env) {
|
|
6196
6675
|
for (const directory of augmentedSearchDirectories(env)) {
|
|
@@ -6303,8 +6782,8 @@ function resolveAgentCommand(opts) {
|
|
|
6303
6782
|
}
|
|
6304
6783
|
if (typedKind === "cursor") {
|
|
6305
6784
|
requireExecutable("cursor-agent", env, cwd, "Cursor Agent CLI not found. Install it from https://cursor.com/docs/cli, then retry with `--agent cursor`.");
|
|
6306
|
-
const
|
|
6307
|
-
return { kind: typedKind, command:
|
|
6785
|
+
const bridge = cursorAcpBridgeLaunch();
|
|
6786
|
+
return { kind: typedKind, command: bridge.command, args: bridge.args };
|
|
6308
6787
|
}
|
|
6309
6788
|
if (typedKind === "grok") {
|
|
6310
6789
|
const command2 = requireExecutable("grok", env, cwd, "Grok CLI not found. Install it with `curl -fsSL https://x.ai/cli/install.sh | bash`, then retry with `--agent grok`.");
|
|
@@ -6341,7 +6820,7 @@ function detectInstalledAgentCommands(opts = {}) {
|
|
|
6341
6820
|
agent: resolveAgentCommand({ kind, env, cwd: opts.cwd })
|
|
6342
6821
|
});
|
|
6343
6822
|
} catch {
|
|
6344
|
-
if (kind !== "goose" && kind !== "grok") {
|
|
6823
|
+
if (kind !== "goose" && kind !== "grok" && kind !== "cursor") {
|
|
6345
6824
|
detected.push({
|
|
6346
6825
|
kind,
|
|
6347
6826
|
status: "missing-adapter",
|
|
@@ -6365,7 +6844,7 @@ function formatAdapterInstallCommand(install) {
|
|
|
6365
6844
|
// apps/body/dist/config.js
|
|
6366
6845
|
function firstExisting2(paths) {
|
|
6367
6846
|
for (const p of paths) {
|
|
6368
|
-
if (p &&
|
|
6847
|
+
if (p && existsSync3(p))
|
|
6369
6848
|
return p;
|
|
6370
6849
|
}
|
|
6371
6850
|
return void 0;
|
|
@@ -6418,7 +6897,7 @@ function resolveReadonlyMcpCommand(env = process.env) {
|
|
|
6418
6897
|
if (binary)
|
|
6419
6898
|
return { command: binary, args: [] };
|
|
6420
6899
|
const script = env.BEELINE_READONLY_MCP_SCRIPT;
|
|
6421
|
-
if (script &&
|
|
6900
|
+
if (script && existsSync3(script))
|
|
6422
6901
|
return { command: process.execPath, args: [script] };
|
|
6423
6902
|
const built = firstExisting2([
|
|
6424
6903
|
resolve2(process.cwd(), "apps", "body", "dist", "read-only-mcp.js"),
|
|
@@ -6453,7 +6932,7 @@ function resolveCodegraphCommand(env = process.env) {
|
|
|
6453
6932
|
}
|
|
6454
6933
|
function parseEnvFile(path) {
|
|
6455
6934
|
const out = {};
|
|
6456
|
-
if (!
|
|
6935
|
+
if (!existsSync3(path))
|
|
6457
6936
|
return out;
|
|
6458
6937
|
const text2 = readFileSync(path, "utf8");
|
|
6459
6938
|
for (const raw of text2.split("\n")) {
|
|
@@ -6694,7 +7173,7 @@ import { tmpdir } from "node:os";
|
|
|
6694
7173
|
import { resolve as resolve4 } from "node:path";
|
|
6695
7174
|
|
|
6696
7175
|
// apps/body/dist/acp.js
|
|
6697
|
-
import { spawn } from "node:child_process";
|
|
7176
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
6698
7177
|
import { EventEmitter } from "node:events";
|
|
6699
7178
|
|
|
6700
7179
|
// apps/body/dist/harness-capabilities.js
|
|
@@ -6775,10 +7254,10 @@ var PROFILES = [
|
|
|
6775
7254
|
}
|
|
6776
7255
|
},
|
|
6777
7256
|
{
|
|
6778
|
-
match:
|
|
7257
|
+
match: CURSOR_HARNESS_COMMAND,
|
|
6779
7258
|
profile: {
|
|
6780
7259
|
enforcement: "config-isolated",
|
|
6781
|
-
note: "cursor-
|
|
7260
|
+
note: "cursor-acp-bridge drives cursor-agent; cursor-agent loads MCP servers from ~/.cursor/mcp.json, so an isolated CURSOR_HOME scopes the session"
|
|
6782
7261
|
}
|
|
6783
7262
|
}
|
|
6784
7263
|
];
|
|
@@ -6800,7 +7279,7 @@ function matchProfile(agentCommand) {
|
|
|
6800
7279
|
}
|
|
6801
7280
|
|
|
6802
7281
|
// apps/body/dist/acp.js
|
|
6803
|
-
var
|
|
7282
|
+
var STDERR_TAIL_MAX_CHARS2 = 2e3;
|
|
6804
7283
|
function braceDelta(line) {
|
|
6805
7284
|
let delta = 0;
|
|
6806
7285
|
for (const char of line) {
|
|
@@ -7015,7 +7494,7 @@ function agentStreamSnapshot(updates, agentLabel) {
|
|
|
7015
7494
|
const completedMessages = [];
|
|
7016
7495
|
let messageText2 = "";
|
|
7017
7496
|
let lastWasMessage = false;
|
|
7018
|
-
let
|
|
7497
|
+
let thoughtText2 = "";
|
|
7019
7498
|
let thoughtRunOpen = false;
|
|
7020
7499
|
for (const { update } of updates) {
|
|
7021
7500
|
const kind = typeof update.sessionUpdate === "string" ? update.sessionUpdate : "";
|
|
@@ -7044,8 +7523,8 @@ function agentStreamSnapshot(updates, agentLabel) {
|
|
|
7044
7523
|
if (!delta)
|
|
7045
7524
|
continue;
|
|
7046
7525
|
if (!thoughtRunOpen)
|
|
7047
|
-
|
|
7048
|
-
|
|
7526
|
+
thoughtText2 = "";
|
|
7527
|
+
thoughtText2 += delta;
|
|
7049
7528
|
thoughtRunOpen = true;
|
|
7050
7529
|
} else {
|
|
7051
7530
|
thoughtRunOpen = false;
|
|
@@ -7053,7 +7532,7 @@ function agentStreamSnapshot(updates, agentLabel) {
|
|
|
7053
7532
|
}
|
|
7054
7533
|
return {
|
|
7055
7534
|
messageText: messageText2,
|
|
7056
|
-
...
|
|
7535
|
+
...thoughtText2 || completedMessages.length ? { thoughtText: thoughtText2 || completedMessages.at(-1) } : {}
|
|
7057
7536
|
};
|
|
7058
7537
|
}
|
|
7059
7538
|
function toolCallEntries(updates) {
|
|
@@ -7139,7 +7618,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
7139
7618
|
async start(timeoutMs = 6e4) {
|
|
7140
7619
|
if (this.alive)
|
|
7141
7620
|
return;
|
|
7142
|
-
this.child =
|
|
7621
|
+
this.child = spawn3(this.agentCommand, this.agentArgs, {
|
|
7143
7622
|
// agentEnv is the child's whole environment: buildAgentEnv's allowlist is
|
|
7144
7623
|
// a real boundary, not a decorative one layered over a full inherit.
|
|
7145
7624
|
env: this.inheritProcessEnv ? { ...process.env, ...this.agentEnv } : this.agentEnv,
|
|
@@ -7154,7 +7633,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
7154
7633
|
this.stderrTail = "";
|
|
7155
7634
|
this.child.stderr.setEncoding("utf8");
|
|
7156
7635
|
this.child.stderr.on("data", (chunk) => {
|
|
7157
|
-
this.stderrTail = (this.stderrTail + chunk).slice(-
|
|
7636
|
+
this.stderrTail = (this.stderrTail + chunk).slice(-STDERR_TAIL_MAX_CHARS2);
|
|
7158
7637
|
this.emit("stderr", chunk);
|
|
7159
7638
|
});
|
|
7160
7639
|
this.child.stdout.setEncoding("utf8");
|
|
@@ -7553,7 +8032,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
7553
8032
|
const current = this.activeRunIds.get(sessionId);
|
|
7554
8033
|
if (current)
|
|
7555
8034
|
return Promise.resolve(current);
|
|
7556
|
-
return new Promise((
|
|
8035
|
+
return new Promise((resolve32, reject) => {
|
|
7557
8036
|
const onUpdate = (update) => {
|
|
7558
8037
|
if (update.sessionId !== sessionId)
|
|
7559
8038
|
return;
|
|
@@ -7561,7 +8040,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
7561
8040
|
if (!runId)
|
|
7562
8041
|
return;
|
|
7563
8042
|
cleanup();
|
|
7564
|
-
|
|
8043
|
+
resolve32(runId);
|
|
7565
8044
|
};
|
|
7566
8045
|
const timer = setTimeout(() => {
|
|
7567
8046
|
cleanup();
|
|
@@ -7645,13 +8124,13 @@ var AcpClient = class extends EventEmitter {
|
|
|
7645
8124
|
}
|
|
7646
8125
|
const id = this.nextId++;
|
|
7647
8126
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
7648
|
-
return new Promise((
|
|
8127
|
+
return new Promise((resolve32, reject) => {
|
|
7649
8128
|
const timer = setTimeout(() => {
|
|
7650
8129
|
this.pending.delete(id);
|
|
7651
8130
|
reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart), detail));
|
|
7652
8131
|
}, timeoutMs);
|
|
7653
8132
|
this.pending.set(id, {
|
|
7654
|
-
resolve:
|
|
8133
|
+
resolve: resolve32,
|
|
7655
8134
|
reject,
|
|
7656
8135
|
timer,
|
|
7657
8136
|
method,
|
|
@@ -7945,12 +8424,7 @@ function assertModelSelectionAdvertised(advertisedOptions, selection) {
|
|
|
7945
8424
|
continue;
|
|
7946
8425
|
const axis = advertisedOptions.find((option) => target.categories.includes(option.category));
|
|
7947
8426
|
if (!axis) {
|
|
7948
|
-
|
|
7949
|
-
label: target.label,
|
|
7950
|
-
value: target.value,
|
|
7951
|
-
reason: "axis-missing",
|
|
7952
|
-
guidance: `This harness does not advertise a selectable ${target.label} axis.`
|
|
7953
|
-
});
|
|
8427
|
+
continue;
|
|
7954
8428
|
}
|
|
7955
8429
|
assertModelConfigAxisAllowed(axis.id, advertisedOptions);
|
|
7956
8430
|
if (!axis.options.some((choice) => choice.id === target.value)) {
|
|
@@ -8175,8 +8649,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
8175
8649
|
}
|
|
8176
8650
|
|
|
8177
8651
|
// apps/body/dist/connector-google.js
|
|
8178
|
-
import { readFileSync as
|
|
8179
|
-
import { join as
|
|
8652
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
8653
|
+
import { join as join3 } from "node:path";
|
|
8180
8654
|
|
|
8181
8655
|
// apps/body/dist/google-workspace-client.js
|
|
8182
8656
|
function credentialsTokenSource(credentials) {
|
|
@@ -8396,182 +8870,164 @@ function googleWorkspaceClient(tokenSource, transport = defaultGoogleApiTranspor
|
|
|
8396
8870
|
};
|
|
8397
8871
|
}
|
|
8398
8872
|
|
|
8399
|
-
// apps/body/dist/connector-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
function isGoogleToolConnectorType(type) {
|
|
8411
|
-
return type in GOOGLE_TOOL_SCOPES;
|
|
8873
|
+
// apps/body/dist/connector-squire.js
|
|
8874
|
+
import { execFile, spawn as spawn4 } from "node:child_process";
|
|
8875
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
8876
|
+
import { lstatSync as lstatSync2, readFileSync as readFileSync3, realpathSync, rmSync } from "node:fs";
|
|
8877
|
+
import { homedir as homedir2, hostname, tmpdir as tmpdir2 } from "node:os";
|
|
8878
|
+
import { basename as basename3, dirname as dirname2, join as join2, resolve as resolve6 } from "node:path";
|
|
8879
|
+
var SQUIRE_MCP_NAME = "@trusty-squire/mcp";
|
|
8880
|
+
var SQUIRE_CONNECT_PACKAGE = `${SQUIRE_MCP_NAME}@latest`;
|
|
8881
|
+
var activeConnectSession;
|
|
8882
|
+
function squireConnectSession() {
|
|
8883
|
+
return activeConnectSession;
|
|
8412
8884
|
}
|
|
8413
|
-
|
|
8414
|
-
if (
|
|
8415
|
-
return
|
|
8416
|
-
let raw;
|
|
8885
|
+
function isProcessAlive(pid) {
|
|
8886
|
+
if (pid === void 0 || pid <= 0)
|
|
8887
|
+
return false;
|
|
8417
8888
|
try {
|
|
8418
|
-
|
|
8889
|
+
process.kill(pid, 0);
|
|
8890
|
+
return true;
|
|
8419
8891
|
} catch (error) {
|
|
8420
|
-
return
|
|
8421
|
-
source: "unavailable",
|
|
8422
|
-
reason: `Squire has no Google OAuth grant on record yet (connect your Google account in Squire first: ${describe(error)})`
|
|
8423
|
-
};
|
|
8892
|
+
return error?.code === "EPERM";
|
|
8424
8893
|
}
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
const
|
|
8428
|
-
|
|
8429
|
-
|
|
8894
|
+
}
|
|
8895
|
+
function releaseSquireConnectSession(log2) {
|
|
8896
|
+
const claim = activeConnectSession;
|
|
8897
|
+
activeConnectSession = void 0;
|
|
8898
|
+
if (!claim)
|
|
8899
|
+
return;
|
|
8900
|
+
if (isProcessAlive(claim.pid)) {
|
|
8901
|
+
log2?.(`released trusty-squire connect session claim (pid ${String(claim.pid)} still live \u2014 a fresh connect owns the browser now)`);
|
|
8902
|
+
claim.abort();
|
|
8903
|
+
} else {
|
|
8904
|
+
log2?.("cleared dead trusty-squire connect session claim (owner process gone)");
|
|
8430
8905
|
}
|
|
8431
|
-
return {
|
|
8432
|
-
source: "squire",
|
|
8433
|
-
credentials: {
|
|
8434
|
-
accessToken,
|
|
8435
|
-
refreshToken: typeof record3.refreshToken === "string" ? record3.refreshToken : typeof record3.refresh_token === "string" ? record3.refresh_token : void 0,
|
|
8436
|
-
expiresAt: typeof record3.expiresAt === "number" ? record3.expiresAt : void 0,
|
|
8437
|
-
accountEmail: typeof record3.accountEmail === "string" ? record3.accountEmail : typeof record3.email === "string" ? record3.email : void 0
|
|
8438
|
-
}
|
|
8439
|
-
};
|
|
8440
8906
|
}
|
|
8441
|
-
function
|
|
8442
|
-
return
|
|
8907
|
+
function squireChromeProfileDir() {
|
|
8908
|
+
return process.env.TRUSTY_SQUIRE_PROFILE_DIR ?? join2(homedir2(), ".trusty-squire", "chrome-profile");
|
|
8443
8909
|
}
|
|
8444
|
-
function
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
};
|
|
8450
|
-
}
|
|
8451
|
-
for (const path of manualGoogleCredentialsSearchPaths(home)) {
|
|
8910
|
+
function squireProfilePathIdentity(profileDir) {
|
|
8911
|
+
const absolute = resolve6(profileDir);
|
|
8912
|
+
const suffix = [];
|
|
8913
|
+
let candidate = absolute;
|
|
8914
|
+
for (; ; ) {
|
|
8452
8915
|
try {
|
|
8453
|
-
|
|
8454
|
-
if (typeof parsed.accessToken === "string" || typeof parsed.access_token === "string") {
|
|
8455
|
-
return {
|
|
8456
|
-
source: "manual",
|
|
8457
|
-
credentials: {
|
|
8458
|
-
accessToken: parsed.accessToken ?? parsed.access_token,
|
|
8459
|
-
refreshToken: typeof parsed.refreshToken === "string" ? parsed.refreshToken : void 0,
|
|
8460
|
-
accountEmail: typeof parsed.accountEmail === "string" ? parsed.accountEmail : void 0
|
|
8461
|
-
}
|
|
8462
|
-
};
|
|
8463
|
-
}
|
|
8916
|
+
return join2(realpathSync.native(candidate), ...suffix.reverse());
|
|
8464
8917
|
} catch {
|
|
8918
|
+
const parent = dirname2(candidate);
|
|
8919
|
+
if (parent === candidate)
|
|
8920
|
+
return absolute;
|
|
8921
|
+
suffix.push(basename3(candidate));
|
|
8922
|
+
candidate = parent;
|
|
8465
8923
|
}
|
|
8466
8924
|
}
|
|
8467
|
-
return {
|
|
8468
|
-
source: "manual-missing",
|
|
8469
|
-
reason: `no Google credentials found. Put a google-credentials.json (OAuth access token) at ${manualGoogleCredentialsSearchPaths(home)[0]} or connect your Google account in Trusty Squire first.`
|
|
8470
|
-
};
|
|
8471
8925
|
}
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
...extra
|
|
8476
|
-
});
|
|
8477
|
-
function outputTail(text2, maxChars = 800) {
|
|
8478
|
-
const trimmed = text2.trim();
|
|
8479
|
-
return trimmed.length <= maxChars ? trimmed : `\u2026${trimmed.slice(-maxChars)}`;
|
|
8926
|
+
function squireProfileLockPath(profileDir = squireChromeProfileDir(), lockRoot = tmpdir2()) {
|
|
8927
|
+
const digest = createHash2("sha256").update(squireProfilePathIdentity(profileDir)).digest("hex").slice(0, 24);
|
|
8928
|
+
return join2(lockRoot, `trusty-squire-profile-${digest}.lock`);
|
|
8480
8929
|
}
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
emit();
|
|
8487
|
-
};
|
|
8488
|
-
const fail = (label, reason, output) => {
|
|
8489
|
-
push(step(label, "failed", { reason, ...output ? { output } : {} }));
|
|
8490
|
-
return { status: "error", steps, errorMessage: reason };
|
|
8491
|
-
};
|
|
8492
|
-
emit();
|
|
8493
|
-
if (!isGoogleToolConnectorType(options.connectorType)) {
|
|
8494
|
-
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
8495
|
-
}
|
|
8496
|
-
push(step("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
8497
|
-
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
8498
|
-
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
8499
|
-
return oneClick.source === "squire" ? { ...oneClick } : { ...loadManualGoogleCredentials(options.home, options.env) };
|
|
8500
|
-
})();
|
|
8501
|
-
if (!("credentials" in resolved)) {
|
|
8502
|
-
const reason = resolved.reason;
|
|
8503
|
-
steps[1] = step("Google credentials resolved", "failed", { reason, output: outputTail(reason) });
|
|
8504
|
-
emit();
|
|
8505
|
-
return { status: "error", steps, errorMessage: reason };
|
|
8506
|
-
}
|
|
8507
|
-
steps[1] = step("Google credentials resolved", "done", {
|
|
8508
|
-
output: resolved.source === "squire" ? "one-click: read the Google grant from Trusty Squire" : "manual: local google-credentials.json"
|
|
8509
|
-
});
|
|
8510
|
-
emit();
|
|
8511
|
-
const client = options.client ?? googleWorkspaceClient(credentialsTokenSource(resolved.credentials));
|
|
8512
|
-
push(step("authorized with Google", "running", { output: "verifying the grant with Google\u2026" }));
|
|
8513
|
-
const verify = await client.verify();
|
|
8514
|
-
if (!verify.ok) {
|
|
8515
|
-
return fail("authorized with Google", verify.reason, outputTail(verify.reason));
|
|
8516
|
-
}
|
|
8517
|
-
steps[2] = step("authorized with Google", "done", {
|
|
8518
|
-
...verify.account ? { output: `signed in as ${verify.account}` } : {}
|
|
8519
|
-
});
|
|
8520
|
-
emit();
|
|
8521
|
-
push(step("tools enabled", "done", {
|
|
8522
|
-
output: `${GOOGLE_TOOL_SCOPES[options.connectorType].length} Google scopes granted`
|
|
8523
|
-
}));
|
|
8524
|
-
return {
|
|
8525
|
-
status: "connected",
|
|
8526
|
-
steps,
|
|
8527
|
-
...verify.account ? { signedInAs: verify.account } : {}
|
|
8528
|
-
};
|
|
8930
|
+
var SQUIRE_BROWSER_BUSY_GOOGLE_REASON = "Trusty Squire is still using the browser \u2014 connect Trusty Squire first";
|
|
8931
|
+
function isSquireBrowserSessionFailure(text2) {
|
|
8932
|
+
if (typeof text2 !== "string" || text2.length === 0)
|
|
8933
|
+
return false;
|
|
8934
|
+
return /Trusty Squire[\s\S]{0,80}browser/i.test(text2) || /browser[\s\S]{0,80}Trusty Squire/i.test(text2);
|
|
8529
8935
|
}
|
|
8530
|
-
function
|
|
8531
|
-
return
|
|
8936
|
+
function squireForeignClaimAction(owner) {
|
|
8937
|
+
return `Trusty Squire's browser is in use by another process (pid ${owner.pid}). Finish or close that Trusty Squire session, then press Connect again.`;
|
|
8532
8938
|
}
|
|
8533
|
-
|
|
8534
|
-
// apps/body/dist/connector-squire.js
|
|
8535
|
-
import { execFile, spawn as spawn2 } from "node:child_process";
|
|
8536
|
-
var SQUIRE_MCP_NAME = "@trusty-squire/mcp";
|
|
8537
|
-
var SQUIRE_CONNECT_PACKAGE = `${SQUIRE_MCP_NAME}@latest`;
|
|
8538
|
-
var activeConnectSession;
|
|
8539
|
-
function isProcessAlive(pid) {
|
|
8540
|
-
if (pid === void 0 || pid <= 0)
|
|
8541
|
-
return false;
|
|
8939
|
+
function readLinuxStartTime(pid) {
|
|
8542
8940
|
try {
|
|
8543
|
-
|
|
8941
|
+
const stat4 = readFileSync3(`/proc/${pid}/stat`, "utf8");
|
|
8942
|
+
const close = stat4.lastIndexOf(")");
|
|
8943
|
+
return close < 0 ? void 0 : stat4.slice(close + 2).split(" ")[19];
|
|
8944
|
+
} catch {
|
|
8945
|
+
return void 0;
|
|
8946
|
+
}
|
|
8947
|
+
}
|
|
8948
|
+
function lockOwnerIsAlive(owner) {
|
|
8949
|
+
if (!isProcessAlive(owner.pid))
|
|
8950
|
+
return false;
|
|
8951
|
+
if (owner.startTime === null || owner.startTime === "unknown")
|
|
8544
8952
|
return true;
|
|
8545
|
-
|
|
8546
|
-
|
|
8953
|
+
const actual = readLinuxStartTime(owner.pid);
|
|
8954
|
+
if (actual === void 0)
|
|
8955
|
+
return true;
|
|
8956
|
+
return actual === owner.startTime;
|
|
8957
|
+
}
|
|
8958
|
+
function readLockFileOwner(lockPath) {
|
|
8959
|
+
try {
|
|
8960
|
+
const target = lstatSync2(lockPath).isDirectory() ? join2(lockPath, "owner.json") : lockPath;
|
|
8961
|
+
const parsed = JSON.parse(readFileSync3(target, "utf8"));
|
|
8962
|
+
if (typeof parsed.host !== "string" || typeof parsed.pid !== "number")
|
|
8963
|
+
return void 0;
|
|
8964
|
+
return {
|
|
8965
|
+
host: parsed.host,
|
|
8966
|
+
pid: parsed.pid,
|
|
8967
|
+
startTime: typeof parsed.start_time === "string" ? parsed.start_time : null
|
|
8968
|
+
};
|
|
8969
|
+
} catch {
|
|
8970
|
+
return void 0;
|
|
8547
8971
|
}
|
|
8548
8972
|
}
|
|
8549
|
-
function
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8973
|
+
function removeProfileLock(lockPath) {
|
|
8974
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
8975
|
+
}
|
|
8976
|
+
function reclaimSquireProfileClaim(options) {
|
|
8977
|
+
const profileDir = options?.profileDir ?? squireChromeProfileDir();
|
|
8978
|
+
const lockRoot = options?.lockRoot ?? tmpdir2();
|
|
8979
|
+
const lockPath = squireProfileLockPath(profileDir, lockRoot);
|
|
8980
|
+
const owner = readLockFileOwner(lockPath);
|
|
8981
|
+
if (!owner) {
|
|
8982
|
+
return { kind: "free" };
|
|
8983
|
+
}
|
|
8984
|
+
const ours = (options?.ourPids ?? []).includes(owner.pid);
|
|
8985
|
+
if (ours) {
|
|
8986
|
+
removeProfileLock(lockPath);
|
|
8987
|
+
options?.log?.(`released our trusty-squire on-disk browser claim (pid ${owner.pid})`);
|
|
8988
|
+
return { kind: "released-ours", owner };
|
|
8559
8989
|
}
|
|
8990
|
+
if (owner.host !== hostname() || lockOwnerIsAlive(owner)) {
|
|
8991
|
+
const action = squireForeignClaimAction(owner);
|
|
8992
|
+
options?.log?.(`trusty-squire browser claim blocked by live pid ${owner.pid} \u2014 not superseding a foreign session`);
|
|
8993
|
+
return { kind: "blocked-foreign", owner, action };
|
|
8994
|
+
}
|
|
8995
|
+
removeProfileLock(lockPath);
|
|
8996
|
+
options?.log?.(`cleared dead trusty-squire on-disk browser claim (pid ${owner.pid} gone)`);
|
|
8997
|
+
return { kind: "reclaimed-dead", owner };
|
|
8560
8998
|
}
|
|
8561
|
-
var defaultShellRunner = (command, args) => new Promise((
|
|
8999
|
+
var defaultShellRunner = (command, args) => new Promise((resolve32) => {
|
|
8562
9000
|
execFile(command, [...args], { timeout: 12e4, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (error, stdout6, stderr) => {
|
|
8563
9001
|
const code = error?.code;
|
|
8564
|
-
|
|
9002
|
+
resolve32({
|
|
8565
9003
|
code: typeof code === "number" ? code : error ? 1 : 0,
|
|
8566
9004
|
stdout: String(stdout6 ?? ""),
|
|
8567
9005
|
stderr: String(stderr ?? "")
|
|
8568
9006
|
});
|
|
8569
9007
|
});
|
|
8570
9008
|
});
|
|
9009
|
+
function squireConnectProcessEnv(profileDir = squireChromeProfileDir()) {
|
|
9010
|
+
return {
|
|
9011
|
+
...process.env,
|
|
9012
|
+
TRUSTY_SQUIRE_PROFILE_DIR: profileDir
|
|
9013
|
+
};
|
|
9014
|
+
}
|
|
8571
9015
|
var CONNECT_TIMEOUT_MS = 3e5;
|
|
8572
|
-
|
|
8573
|
-
const
|
|
8574
|
-
|
|
9016
|
+
function killConnectTree(child) {
|
|
9017
|
+
const pid = child.pid;
|
|
9018
|
+
if (pid !== void 0) {
|
|
9019
|
+
try {
|
|
9020
|
+
process.kill(-pid, "SIGTERM");
|
|
9021
|
+
} catch {
|
|
9022
|
+
}
|
|
9023
|
+
}
|
|
9024
|
+
child.kill();
|
|
9025
|
+
}
|
|
9026
|
+
var defaultStreamedRunner = (command, args, env) => new Promise((resolve32) => {
|
|
9027
|
+
const child = spawn4(command, args, {
|
|
9028
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9029
|
+
env: env ?? squireConnectProcessEnv(),
|
|
9030
|
+
detached: true
|
|
8575
9031
|
});
|
|
8576
9032
|
let stdout6 = "";
|
|
8577
9033
|
let stderr = "";
|
|
@@ -8579,14 +9035,14 @@ var defaultStreamedRunner = (command, args) => new Promise((resolve31) => {
|
|
|
8579
9035
|
const safetyTimer = setTimeout(() => {
|
|
8580
9036
|
if (!resolved) {
|
|
8581
9037
|
resolved = true;
|
|
8582
|
-
|
|
9038
|
+
resolve32({ stdout: stdout6, stderr, pid: child.pid ?? void 0, signIn: void 0, abort: () => {
|
|
8583
9039
|
} });
|
|
8584
9040
|
}
|
|
8585
|
-
child
|
|
9041
|
+
killConnectTree(child);
|
|
8586
9042
|
}, CONNECT_TIMEOUT_MS);
|
|
8587
9043
|
const abort = () => {
|
|
8588
9044
|
clearTimeout(safetyTimer);
|
|
8589
|
-
child
|
|
9045
|
+
killConnectTree(child);
|
|
8590
9046
|
};
|
|
8591
9047
|
const claim = {
|
|
8592
9048
|
pid: child.pid ?? void 0,
|
|
@@ -8598,7 +9054,7 @@ var defaultStreamedRunner = (command, args) => new Promise((resolve31) => {
|
|
|
8598
9054
|
if (!resolved) {
|
|
8599
9055
|
resolved = true;
|
|
8600
9056
|
clearTimeout(safetyTimer);
|
|
8601
|
-
|
|
9057
|
+
resolve32(result);
|
|
8602
9058
|
}
|
|
8603
9059
|
};
|
|
8604
9060
|
const checkOutput = () => {
|
|
@@ -8626,7 +9082,7 @@ ${stderr}`;
|
|
|
8626
9082
|
} });
|
|
8627
9083
|
});
|
|
8628
9084
|
});
|
|
8629
|
-
var
|
|
9085
|
+
var step = (label, status, reason) => ({
|
|
8630
9086
|
label,
|
|
8631
9087
|
status,
|
|
8632
9088
|
...reason ? { reason } : {}
|
|
@@ -8690,23 +9146,36 @@ function parseSignedInAs(output) {
|
|
|
8690
9146
|
return output.match(/signed in as ([^\s,;]+)/i)?.[1];
|
|
8691
9147
|
}
|
|
8692
9148
|
async function installSquire(options) {
|
|
9149
|
+
const profileDir = options.profileDir ?? squireChromeProfileDir();
|
|
9150
|
+
const lockRoot = options.lockRoot ?? tmpdir2();
|
|
8693
9151
|
const run2 = options.run ?? defaultShellRunner;
|
|
8694
9152
|
const streamRun = options.streamRun ?? defaultStreamedRunner;
|
|
8695
9153
|
const log2 = options.log ?? (() => {
|
|
8696
9154
|
});
|
|
8697
|
-
const steps = [
|
|
9155
|
+
const steps = [step("helper reached", "done")];
|
|
8698
9156
|
const emit = () => options.onProgress?.([...steps]);
|
|
8699
9157
|
const push = (next) => {
|
|
8700
9158
|
steps.push(next);
|
|
8701
9159
|
emit();
|
|
8702
9160
|
};
|
|
8703
9161
|
const fail = (reason) => {
|
|
8704
|
-
steps.push(
|
|
9162
|
+
steps.push(step("waiting for sign-in", "pending"));
|
|
8705
9163
|
emit();
|
|
8706
9164
|
return { status: "error", steps, errorMessage: reason };
|
|
8707
9165
|
};
|
|
8708
9166
|
emit();
|
|
9167
|
+
const previousPid = squireConnectSession()?.pid;
|
|
8709
9168
|
releaseSquireConnectSession(log2);
|
|
9169
|
+
const claim = reclaimSquireProfileClaim({
|
|
9170
|
+
log: log2,
|
|
9171
|
+
profileDir,
|
|
9172
|
+
lockRoot,
|
|
9173
|
+
...previousPid !== void 0 ? { ourPids: [previousPid] } : {}
|
|
9174
|
+
});
|
|
9175
|
+
if (claim.kind === "blocked-foreign") {
|
|
9176
|
+
push(step("trusty-squire installed", "failed", claim.action));
|
|
9177
|
+
return fail(claim.action);
|
|
9178
|
+
}
|
|
8710
9179
|
const resolution = await resolveSquireConnectSpec(run2);
|
|
8711
9180
|
if (resolution.reResolved) {
|
|
8712
9181
|
log2(`trusty-squire stale copy ${resolution.resolvedVersion ?? "unknown"} re-resolved against current release ${resolution.currentRelease}`);
|
|
@@ -8717,21 +9186,30 @@ async function installSquire(options) {
|
|
|
8717
9186
|
"--force-relogin=google",
|
|
8718
9187
|
"--target=codex",
|
|
8719
9188
|
"--skip-browser"
|
|
8720
|
-
]);
|
|
9189
|
+
], squireConnectProcessEnv(profileDir));
|
|
8721
9190
|
if (!install.signIn) {
|
|
8722
9191
|
const stderr = install.stderr.trim();
|
|
8723
|
-
|
|
8724
|
-
|
|
9192
|
+
let stepReason = stderr || "connect printed no sign-in URL";
|
|
9193
|
+
let failReason = stderr || "the trusty-squire connect command printed no sign-in surface";
|
|
9194
|
+
if (isSquireBrowserSessionFailure(stderr)) {
|
|
9195
|
+
const again = reclaimSquireProfileClaim({ log: log2, profileDir, lockRoot });
|
|
9196
|
+
if (again.kind === "blocked-foreign") {
|
|
9197
|
+
stepReason = again.action;
|
|
9198
|
+
failReason = again.action;
|
|
9199
|
+
}
|
|
9200
|
+
}
|
|
9201
|
+
push(step("trusty-squire installed", "failed", stepReason));
|
|
9202
|
+
return fail(failReason);
|
|
8725
9203
|
}
|
|
8726
9204
|
const version = resolution.resolvedVersion;
|
|
8727
|
-
push(
|
|
9205
|
+
push(step(`trusty-squire${version ? ` ${version}` : ""} installed`, "done"));
|
|
8728
9206
|
const signIn = install.signIn;
|
|
8729
9207
|
const signedInAs = parseSignedInAs(`${install.stdout}
|
|
8730
9208
|
${install.stderr}`);
|
|
8731
|
-
push(
|
|
9209
|
+
push(step("waiting for sign-in", "done"));
|
|
8732
9210
|
const pair = await pairSquire(options.mcp, options.workspaceId);
|
|
8733
9211
|
if (!pair.ok) {
|
|
8734
|
-
push(
|
|
9212
|
+
push(step("paired to workspace", "failed", pair.reason));
|
|
8735
9213
|
return {
|
|
8736
9214
|
status: "installing",
|
|
8737
9215
|
steps,
|
|
@@ -8740,7 +9218,7 @@ ${install.stderr}`);
|
|
|
8740
9218
|
...signedInAs ? { signedInAs } : {}
|
|
8741
9219
|
};
|
|
8742
9220
|
}
|
|
8743
|
-
push(
|
|
9221
|
+
push(step("paired to workspace", "done"));
|
|
8744
9222
|
return {
|
|
8745
9223
|
status: "connected",
|
|
8746
9224
|
steps,
|
|
@@ -8759,7 +9237,7 @@ async function pairSquire(mcp, _workspaceId) {
|
|
|
8759
9237
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
8760
9238
|
}
|
|
8761
9239
|
}
|
|
8762
|
-
function
|
|
9240
|
+
function asRecord2(value) {
|
|
8763
9241
|
return value && typeof value === "object" ? value : {};
|
|
8764
9242
|
}
|
|
8765
9243
|
function asArray(value) {
|
|
@@ -8772,7 +9250,7 @@ function numberOrNull(value) {
|
|
|
8772
9250
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8773
9251
|
}
|
|
8774
9252
|
function vaultConnectionMeta(raw) {
|
|
8775
|
-
const record3 =
|
|
9253
|
+
const record3 = asRecord2(raw);
|
|
8776
9254
|
const reference = String(record3.reference ?? record3.id ?? "");
|
|
8777
9255
|
return {
|
|
8778
9256
|
reference,
|
|
@@ -8786,11 +9264,11 @@ function vaultConnectionMeta(raw) {
|
|
|
8786
9264
|
};
|
|
8787
9265
|
}
|
|
8788
9266
|
async function readVault(mcp) {
|
|
8789
|
-
const result =
|
|
9267
|
+
const result = asRecord2(await mcp.call("list_credentials"));
|
|
8790
9268
|
return asArray(result.credentials ?? result.items ?? result).map(vaultConnectionMeta);
|
|
8791
9269
|
}
|
|
8792
9270
|
function connectionGrant(raw) {
|
|
8793
|
-
const record3 =
|
|
9271
|
+
const record3 = asRecord2(raw);
|
|
8794
9272
|
return {
|
|
8795
9273
|
grantId: String(record3.grant_id ?? record3.grantId ?? record3.id ?? ""),
|
|
8796
9274
|
credentialRef: String(record3.credential_ref ?? record3.credentialRef ?? record3.reference ?? ""),
|
|
@@ -8801,7 +9279,7 @@ function connectionGrant(raw) {
|
|
|
8801
9279
|
};
|
|
8802
9280
|
}
|
|
8803
9281
|
async function readGrants(mcp, ref) {
|
|
8804
|
-
const result =
|
|
9282
|
+
const result = asRecord2(await mcp.call("list_app_access", {}));
|
|
8805
9283
|
return asArray(result.grants ?? result.items ?? result).map(connectionGrant).filter((grant) => grant.credentialRef === ref && grant.revokedAt === void 0);
|
|
8806
9284
|
}
|
|
8807
9285
|
async function revokeGrants(mcp, ref) {
|
|
@@ -8810,7 +9288,7 @@ async function revokeGrants(mcp, ref) {
|
|
|
8810
9288
|
let failed = 0;
|
|
8811
9289
|
for (const grant of grants) {
|
|
8812
9290
|
try {
|
|
8813
|
-
const result =
|
|
9291
|
+
const result = asRecord2(await mcp.call("revoke_app_access", { grant_id: grant.grantId }));
|
|
8814
9292
|
if (result.revoked === false)
|
|
8815
9293
|
failed += 1;
|
|
8816
9294
|
else
|
|
@@ -8822,10 +9300,157 @@ async function revokeGrants(mcp, ref) {
|
|
|
8822
9300
|
return { revoked, failed };
|
|
8823
9301
|
}
|
|
8824
9302
|
|
|
9303
|
+
// apps/body/dist/connector-google.js
|
|
9304
|
+
var GOOGLE_TOOL_SCOPES = {
|
|
9305
|
+
"google-gmail": [
|
|
9306
|
+
"https://www.googleapis.com/auth/gmail.send",
|
|
9307
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
|
9308
|
+
"https://www.googleapis.com/auth/gmail.compose"
|
|
9309
|
+
],
|
|
9310
|
+
"google-calendar": ["https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.readonly"],
|
|
9311
|
+
"google-drive": ["https://www.googleapis.com/auth/drive.readonly"],
|
|
9312
|
+
"google-youtube": ["https://www.googleapis.com/auth/youtube.readonly"]
|
|
9313
|
+
};
|
|
9314
|
+
function isGoogleToolConnectorType(type) {
|
|
9315
|
+
return type in GOOGLE_TOOL_SCOPES;
|
|
9316
|
+
}
|
|
9317
|
+
async function readGoogleCredentialsFromVault(mcp) {
|
|
9318
|
+
if (!mcp)
|
|
9319
|
+
return { source: "unavailable", reason: "no Trusty Squire connector is paired" };
|
|
9320
|
+
let raw;
|
|
9321
|
+
try {
|
|
9322
|
+
raw = await mcp.call("google_oauth_credentials", {});
|
|
9323
|
+
} catch (error) {
|
|
9324
|
+
const detail = describe(error);
|
|
9325
|
+
if (isSquireBrowserSessionFailure(detail)) {
|
|
9326
|
+
return {
|
|
9327
|
+
source: "unavailable",
|
|
9328
|
+
reason: SQUIRE_BROWSER_BUSY_GOOGLE_REASON
|
|
9329
|
+
};
|
|
9330
|
+
}
|
|
9331
|
+
return {
|
|
9332
|
+
source: "unavailable",
|
|
9333
|
+
reason: `Squire has no Google OAuth grant on record yet (connect your Google account in Squire first: ${detail})`
|
|
9334
|
+
};
|
|
9335
|
+
}
|
|
9336
|
+
const record3 = raw && typeof raw === "object" ? raw : {};
|
|
9337
|
+
const inner = record3.credentials && typeof record3.credentials === "object" ? record3.credentials : record3;
|
|
9338
|
+
const accessToken = inner.accessToken ?? inner.access_token;
|
|
9339
|
+
if (!accessToken || typeof accessToken !== "string") {
|
|
9340
|
+
return { source: "unavailable", reason: "Squire returned a Google grant without an access token" };
|
|
9341
|
+
}
|
|
9342
|
+
return {
|
|
9343
|
+
source: "squire",
|
|
9344
|
+
credentials: {
|
|
9345
|
+
accessToken,
|
|
9346
|
+
refreshToken: typeof record3.refreshToken === "string" ? record3.refreshToken : typeof record3.refresh_token === "string" ? record3.refresh_token : void 0,
|
|
9347
|
+
expiresAt: typeof record3.expiresAt === "number" ? record3.expiresAt : void 0,
|
|
9348
|
+
accountEmail: typeof record3.accountEmail === "string" ? record3.accountEmail : typeof record3.email === "string" ? record3.email : void 0
|
|
9349
|
+
}
|
|
9350
|
+
};
|
|
9351
|
+
}
|
|
9352
|
+
function manualGoogleCredentialsSearchPaths(home) {
|
|
9353
|
+
return [join3(home, "google-credentials.json")];
|
|
9354
|
+
}
|
|
9355
|
+
function loadManualGoogleCredentials(home, env = process.env) {
|
|
9356
|
+
if (env.BEELINE_GOOGLE_ACCESS_TOKEN) {
|
|
9357
|
+
return {
|
|
9358
|
+
source: "manual",
|
|
9359
|
+
credentials: { accessToken: env.BEELINE_GOOGLE_ACCESS_TOKEN }
|
|
9360
|
+
};
|
|
9361
|
+
}
|
|
9362
|
+
for (const path of manualGoogleCredentialsSearchPaths(home)) {
|
|
9363
|
+
try {
|
|
9364
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
9365
|
+
if (typeof parsed.accessToken === "string" || typeof parsed.access_token === "string") {
|
|
9366
|
+
return {
|
|
9367
|
+
source: "manual",
|
|
9368
|
+
credentials: {
|
|
9369
|
+
accessToken: parsed.accessToken ?? parsed.access_token,
|
|
9370
|
+
refreshToken: typeof parsed.refreshToken === "string" ? parsed.refreshToken : void 0,
|
|
9371
|
+
accountEmail: typeof parsed.accountEmail === "string" ? parsed.accountEmail : void 0
|
|
9372
|
+
}
|
|
9373
|
+
};
|
|
9374
|
+
}
|
|
9375
|
+
} catch {
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
return {
|
|
9379
|
+
source: "manual-missing",
|
|
9380
|
+
reason: `no Google credentials found. Put a google-credentials.json (OAuth access token) at ${manualGoogleCredentialsSearchPaths(home)[0]} or connect your Google account in Trusty Squire first.`
|
|
9381
|
+
};
|
|
9382
|
+
}
|
|
9383
|
+
var step2 = (label, status, extra) => ({
|
|
9384
|
+
label,
|
|
9385
|
+
status,
|
|
9386
|
+
...extra
|
|
9387
|
+
});
|
|
9388
|
+
function outputTail(text2, maxChars = 800) {
|
|
9389
|
+
const trimmed = text2.trim();
|
|
9390
|
+
return trimmed.length <= maxChars ? trimmed : `\u2026${trimmed.slice(-maxChars)}`;
|
|
9391
|
+
}
|
|
9392
|
+
async function installGoogleTool(options) {
|
|
9393
|
+
const steps = [step2("helper reached", "done")];
|
|
9394
|
+
const emit = () => options.onProgress?.([...steps]);
|
|
9395
|
+
const push = (next) => {
|
|
9396
|
+
steps.push(next);
|
|
9397
|
+
emit();
|
|
9398
|
+
};
|
|
9399
|
+
const fail = (label, reason, output) => {
|
|
9400
|
+
push(step2(label, "failed", { reason, ...output ? { output } : {} }));
|
|
9401
|
+
return { status: "error", steps, errorMessage: reason };
|
|
9402
|
+
};
|
|
9403
|
+
emit();
|
|
9404
|
+
if (!isGoogleToolConnectorType(options.connectorType)) {
|
|
9405
|
+
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
9406
|
+
}
|
|
9407
|
+
push(step2("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
9408
|
+
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
9409
|
+
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
9410
|
+
if (oneClick.source === "squire")
|
|
9411
|
+
return oneClick;
|
|
9412
|
+
if (isSquireBrowserSessionFailure(oneClick.reason))
|
|
9413
|
+
return oneClick;
|
|
9414
|
+
return loadManualGoogleCredentials(options.home, options.env);
|
|
9415
|
+
})();
|
|
9416
|
+
if (!("credentials" in resolved)) {
|
|
9417
|
+
const reason = resolved.reason;
|
|
9418
|
+
steps[1] = step2("Google credentials resolved", "failed", { reason, output: outputTail(reason) });
|
|
9419
|
+
emit();
|
|
9420
|
+
return { status: "error", steps, errorMessage: reason };
|
|
9421
|
+
}
|
|
9422
|
+
steps[1] = step2("Google credentials resolved", "done", {
|
|
9423
|
+
output: resolved.source === "squire" ? "one-click: read the Google grant from Trusty Squire" : "manual: local google-credentials.json"
|
|
9424
|
+
});
|
|
9425
|
+
emit();
|
|
9426
|
+
const client = options.client ?? googleWorkspaceClient(credentialsTokenSource(resolved.credentials));
|
|
9427
|
+
push(step2("authorized with Google", "running", { output: "verifying the grant with Google\u2026" }));
|
|
9428
|
+
const verify = await client.verify();
|
|
9429
|
+
if (!verify.ok) {
|
|
9430
|
+
return fail("authorized with Google", verify.reason, outputTail(verify.reason));
|
|
9431
|
+
}
|
|
9432
|
+
steps[2] = step2("authorized with Google", "done", {
|
|
9433
|
+
...verify.account ? { output: `signed in as ${verify.account}` } : {}
|
|
9434
|
+
});
|
|
9435
|
+
emit();
|
|
9436
|
+
push(step2("tools enabled", "done", {
|
|
9437
|
+
output: `${GOOGLE_TOOL_SCOPES[options.connectorType].length} Google scopes granted`
|
|
9438
|
+
}));
|
|
9439
|
+
return {
|
|
9440
|
+
status: "connected",
|
|
9441
|
+
steps,
|
|
9442
|
+
...verify.account ? { signedInAs: verify.account } : {}
|
|
9443
|
+
};
|
|
9444
|
+
}
|
|
9445
|
+
function describe(error) {
|
|
9446
|
+
return error instanceof Error ? error.message : String(error);
|
|
9447
|
+
}
|
|
9448
|
+
|
|
8825
9449
|
// apps/body/dist/squire-mcp-client.js
|
|
8826
|
-
import { spawn as
|
|
9450
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
8827
9451
|
var INITIALIZE_TIMEOUT_MS = 3e4;
|
|
8828
9452
|
var CALL_TIMEOUT_MS = 12e4;
|
|
9453
|
+
var SQUIRE_MCP_SERVER_ARGS = ["-y", "@trusty-squire/mcp@latest", "server"];
|
|
8829
9454
|
var StdioSquireMcpClient = class {
|
|
8830
9455
|
options;
|
|
8831
9456
|
child;
|
|
@@ -8879,12 +9504,7 @@ var StdioSquireMcpClient = class {
|
|
|
8879
9504
|
return this.initialized;
|
|
8880
9505
|
}
|
|
8881
9506
|
initialize() {
|
|
8882
|
-
const child = (this.options.spawn ??
|
|
8883
|
-
// `@latest`: the current release, never a source-level version pin
|
|
8884
|
-
// (captain). Only the vault MCP server the agent spawns; the connect
|
|
8885
|
-
// install path verifies freshness itself (connector-squire.ts).
|
|
8886
|
-
...this.options.args ?? ["-y", "@trusty-squire/mcp@latest"]
|
|
8887
|
-
]);
|
|
9507
|
+
const child = (this.options.spawn ?? spawn5)(this.options.command ?? "npx", [...this.options.args ?? [...SQUIRE_MCP_SERVER_ARGS]], { env: this.options.env ?? squireConnectProcessEnv() });
|
|
8888
9508
|
this.child = child;
|
|
8889
9509
|
this.buffer = "";
|
|
8890
9510
|
child.stdout.setEncoding("utf8");
|
|
@@ -8918,12 +9538,12 @@ var StdioSquireMcpClient = class {
|
|
|
8918
9538
|
const child = this.child;
|
|
8919
9539
|
if (!child)
|
|
8920
9540
|
return Promise.reject(new Error("Squire MCP session is not running"));
|
|
8921
|
-
return new Promise((
|
|
9541
|
+
return new Promise((resolve32, reject) => {
|
|
8922
9542
|
const timer = setTimeout(() => {
|
|
8923
9543
|
this.pending.delete(id);
|
|
8924
9544
|
reject(new Error(`${method} timed out`));
|
|
8925
9545
|
}, method === "initialize" ? INITIALIZE_TIMEOUT_MS : CALL_TIMEOUT_MS);
|
|
8926
|
-
this.pending.set(id, { resolve:
|
|
9546
|
+
this.pending.set(id, { resolve: resolve32, reject, timer });
|
|
8927
9547
|
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
8928
9548
|
`);
|
|
8929
9549
|
});
|
|
@@ -9039,6 +9659,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
9039
9659
|
this.log(`connector assignments unavailable: ${describe2(error)}`);
|
|
9040
9660
|
return;
|
|
9041
9661
|
}
|
|
9662
|
+
const squireWork = [];
|
|
9042
9663
|
let googleBatch;
|
|
9043
9664
|
for (const assignment of assignments) {
|
|
9044
9665
|
const key = `${assignment.kind}:${assignment.connectorId}`;
|
|
@@ -9054,17 +9675,15 @@ var ConnectorAssignmentLoop = class {
|
|
|
9054
9675
|
if (this.inFlight.has(key))
|
|
9055
9676
|
continue;
|
|
9056
9677
|
this.inFlight.add(key);
|
|
9057
|
-
|
|
9678
|
+
squireWork.push(this.handle(assignment).catch((error) => this.log(`connector assignment ${key} failed: ${describe2(error)}`)).finally(() => this.inFlight.delete(key)));
|
|
9679
|
+
}
|
|
9680
|
+
if (googleBatch) {
|
|
9681
|
+
const keys = googleBatch.map((assignment) => `${assignment.kind}:${assignment.connectorId}`);
|
|
9682
|
+
void Promise.all(squireWork).then(() => this.runGoogleBatch(googleBatch)).catch((error) => this.log(`google connector installs failed: ${describe2(error)}`)).finally(() => {
|
|
9683
|
+
for (const key of keys)
|
|
9684
|
+
this.inFlight.delete(key);
|
|
9685
|
+
});
|
|
9058
9686
|
}
|
|
9059
|
-
if (googleBatch)
|
|
9060
|
-
this.flushGoogleBatch(googleBatch);
|
|
9061
|
-
}
|
|
9062
|
-
flushGoogleBatch(batch) {
|
|
9063
|
-
const keys = batch.map((a2) => `${a2.kind}:${a2.connectorId}`);
|
|
9064
|
-
void this.runGoogleBatch(batch).catch((error) => this.log(`google connector installs failed: ${describe2(error)}`)).finally(() => {
|
|
9065
|
-
for (const key of keys)
|
|
9066
|
-
this.inFlight.delete(key);
|
|
9067
|
-
});
|
|
9068
9687
|
}
|
|
9069
9688
|
/** The Google tool connectors ride ONE grant: every install in the batch
|
|
9070
9689
|
* shares one credential resolution (the single Google consent) and the
|
|
@@ -9088,6 +9707,8 @@ var ConnectorAssignmentLoop = class {
|
|
|
9088
9707
|
const oneClick = await readGoogleCredentialsFromVault(this.squire());
|
|
9089
9708
|
if (oneClick.source === "squire")
|
|
9090
9709
|
return oneClick;
|
|
9710
|
+
if (isSquireBrowserSessionFailure(oneClick.reason))
|
|
9711
|
+
return oneClick;
|
|
9091
9712
|
} catch (error) {
|
|
9092
9713
|
this.log(`google one-click grant lookup failed: ${describe2(error)}`);
|
|
9093
9714
|
}
|
|
@@ -9232,12 +9853,13 @@ var SERVER_EVENT_KINDS = [
|
|
|
9232
9853
|
"check-failed",
|
|
9233
9854
|
"merged",
|
|
9234
9855
|
"grant-decided",
|
|
9856
|
+
"connector-offer-decided",
|
|
9235
9857
|
"turn-cancelled"
|
|
9236
9858
|
];
|
|
9237
9859
|
|
|
9238
9860
|
// apps/body/dist/daemon-api-client.js
|
|
9239
|
-
import { resolve as
|
|
9240
|
-
import { randomUUID } from "node:crypto";
|
|
9861
|
+
import { resolve as resolve8 } from "node:path";
|
|
9862
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
9241
9863
|
|
|
9242
9864
|
// node_modules/ws/wrapper.mjs
|
|
9243
9865
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -9255,9 +9877,9 @@ import { randomBytes as randomBytes5 } from "node:crypto";
|
|
|
9255
9877
|
import { execFile as execFile2 } from "node:child_process";
|
|
9256
9878
|
import { closeSync, openSync } from "node:fs";
|
|
9257
9879
|
import { mkdir, readFile as readFile2, readdir, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
9258
|
-
import { homedir as
|
|
9259
|
-
import { dirname as
|
|
9260
|
-
import { spawn as
|
|
9880
|
+
import { homedir as homedir3 } from "node:os";
|
|
9881
|
+
import { dirname as dirname3, resolve as resolve7 } from "node:path";
|
|
9882
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
9261
9883
|
import { promisify } from "node:util";
|
|
9262
9884
|
|
|
9263
9885
|
// node_modules/@noble/hashes/_u64.js
|
|
@@ -13644,7 +14266,7 @@ function alphabet(letters) {
|
|
|
13644
14266
|
};
|
|
13645
14267
|
}
|
|
13646
14268
|
// @__NO_SIDE_EFFECTS__
|
|
13647
|
-
function
|
|
14269
|
+
function join4(separator = "") {
|
|
13648
14270
|
astr("join", separator);
|
|
13649
14271
|
return {
|
|
13650
14272
|
encode: (from) => {
|
|
@@ -13774,8 +14396,8 @@ var base64 = hasBase64Builtin ? {
|
|
|
13774
14396
|
decode(s) {
|
|
13775
14397
|
return decodeBase64Builtin(s, false);
|
|
13776
14398
|
}
|
|
13777
|
-
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */
|
|
13778
|
-
var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */
|
|
14399
|
+
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join4(""));
|
|
14400
|
+
var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join4(""));
|
|
13779
14401
|
var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
13780
14402
|
function bech32Polymod(pre) {
|
|
13781
14403
|
const b = pre >> 25;
|
|
@@ -17793,15 +18415,15 @@ var DEFAULT_AGENT_IDENTITY_NAME = "beeline-agent";
|
|
|
17793
18415
|
var DEFAULT_BODY_IDENTITY_NAME = "beeline-body";
|
|
17794
18416
|
var DEFAULT_DAEMON_MONOLITH_BASE_URL = "https://server.usebeeline.app";
|
|
17795
18417
|
function defaultSupervisorRoot(env = process.env) {
|
|
17796
|
-
return
|
|
18418
|
+
return resolve7(env.XDG_STATE_HOME ?? resolve7(homedir3(), ".local", "state"));
|
|
17797
18419
|
}
|
|
17798
18420
|
function runtimeDirectory(supervisorRoot, publicKey) {
|
|
17799
18421
|
if (!/^[0-9a-f]{64}$/i.test(publicKey))
|
|
17800
18422
|
throw new Error("invalid agent public key");
|
|
17801
|
-
return
|
|
18423
|
+
return resolve7(supervisorRoot, "beeline", "agents", publicKey.toLowerCase());
|
|
17802
18424
|
}
|
|
17803
18425
|
function runtimeConfigPath(supervisorRoot, publicKey) {
|
|
17804
|
-
return
|
|
18426
|
+
return resolve7(runtimeDirectory(supervisorRoot, publicKey), "runtime.json");
|
|
17805
18427
|
}
|
|
17806
18428
|
function identityFromKey(value, name) {
|
|
17807
18429
|
const secretKey = value ? value.startsWith("nsec1") ? decodeNsec(value) : Uint8Array.from(Buffer.from(value, "hex")) : randomBytes5(32);
|
|
@@ -17822,7 +18444,13 @@ function runtimeIdentity(identity) {
|
|
|
17822
18444
|
throw new Error("stored identity public key mismatch");
|
|
17823
18445
|
return restored;
|
|
17824
18446
|
}
|
|
17825
|
-
function runtimeAgentCommand(runtime) {
|
|
18447
|
+
function runtimeAgentCommand(runtime, env = process.env) {
|
|
18448
|
+
if (runtime.agentKind === "cursor") {
|
|
18449
|
+
try {
|
|
18450
|
+
return resolveAgentCommand({ kind: "cursor", env });
|
|
18451
|
+
} catch {
|
|
18452
|
+
}
|
|
18453
|
+
}
|
|
17826
18454
|
return {
|
|
17827
18455
|
kind: runtime.agentKind ?? "reference",
|
|
17828
18456
|
command: runtime.agentCommand ?? runtime.agentBinary,
|
|
@@ -17831,7 +18459,7 @@ function runtimeAgentCommand(runtime) {
|
|
|
17831
18459
|
}
|
|
17832
18460
|
async function writeRuntimeRecord(runtime) {
|
|
17833
18461
|
const path = runtimeConfigPath(runtime.supervisorRoot, runtime.agent.publicKey);
|
|
17834
|
-
await mkdir(
|
|
18462
|
+
await mkdir(dirname3(path), { recursive: true, mode: 448 });
|
|
17835
18463
|
const staged = `${path}.${process.pid}.tmp`;
|
|
17836
18464
|
await writeFile2(staged, `${JSON.stringify(runtime, null, 2)}
|
|
17837
18465
|
`, { mode: 384 });
|
|
@@ -17857,7 +18485,7 @@ async function migrateRuntimeRecordAccessPolicy(path) {
|
|
|
17857
18485
|
return { runtime, migrated: true };
|
|
17858
18486
|
}
|
|
17859
18487
|
async function stageMonolithAgentRuntime(input) {
|
|
17860
|
-
const supervisorRoot = input.supervisorRoot ?
|
|
18488
|
+
const supervisorRoot = input.supervisorRoot ? resolve7(input.supervisorRoot) : defaultSupervisorRoot();
|
|
17861
18489
|
const configPath = runtimeConfigPath(supervisorRoot, input.agentIdentity.publicKey);
|
|
17862
18490
|
const configuredBaseUrl = input.monolithBaseUrl ?? DEFAULT_DAEMON_MONOLITH_BASE_URL;
|
|
17863
18491
|
const baseUrl = new URL(configuredBaseUrl).origin;
|
|
@@ -17902,9 +18530,9 @@ async function stageMonolithAgentRuntime(input) {
|
|
|
17902
18530
|
return { runtime, configPath };
|
|
17903
18531
|
}
|
|
17904
18532
|
async function runtimePaths(root) {
|
|
17905
|
-
const agents =
|
|
18533
|
+
const agents = resolve7(root, "beeline", "agents");
|
|
17906
18534
|
const entries = await readdir(agents, { withFileTypes: true }).catch(() => []);
|
|
17907
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) =>
|
|
18535
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => resolve7(agents, entry.name, "runtime.json"));
|
|
17908
18536
|
}
|
|
17909
18537
|
async function findAgentRuntimeConfigPaths(env = process.env, _cwd = process.cwd()) {
|
|
17910
18538
|
return runtimePaths(defaultSupervisorRoot(env));
|
|
@@ -17913,13 +18541,13 @@ async function findRuntimeConfigPaths(cwd = process.cwd(), env = process.env) {
|
|
|
17913
18541
|
return findAgentRuntimeConfigPaths(env, cwd);
|
|
17914
18542
|
}
|
|
17915
18543
|
async function resolveRuntimeConfigPath(path) {
|
|
17916
|
-
return
|
|
18544
|
+
return resolve7(path);
|
|
17917
18545
|
}
|
|
17918
18546
|
async function selectRuntimeConfigPaths(options) {
|
|
17919
18547
|
const hostScope = true;
|
|
17920
18548
|
const configs = await options.findHostRuntimes(options.cwd);
|
|
17921
18549
|
const requestedPubkey = options.requestedPubkey;
|
|
17922
|
-
const paths = requestedPubkey ? configs.filter((path) =>
|
|
18550
|
+
const paths = requestedPubkey ? configs.filter((path) => dirname3(path).endsWith(requestedPubkey)) : [...new Set(configs)];
|
|
17923
18551
|
if (!paths.length)
|
|
17924
18552
|
throw new Error(options.noRuntimeMessage(hostScope));
|
|
17925
18553
|
if (options.requestedPubkey && paths.length > 1)
|
|
@@ -17928,7 +18556,7 @@ async function selectRuntimeConfigPaths(options) {
|
|
|
17928
18556
|
}
|
|
17929
18557
|
async function runtimeDaemonPid(configPath) {
|
|
17930
18558
|
try {
|
|
17931
|
-
const pid = Number((await readFile2(
|
|
18559
|
+
const pid = Number((await readFile2(resolve7(dirname3(configPath), "daemon.pid"), "utf8")).trim());
|
|
17932
18560
|
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
17933
18561
|
return null;
|
|
17934
18562
|
process.kill(pid, 0);
|
|
@@ -17941,11 +18569,11 @@ async function daemonIsThisRuntime(pid, configPath) {
|
|
|
17941
18569
|
try {
|
|
17942
18570
|
const argv = (await readFile2(`/proc/${pid}/cmdline`, "utf8")).split("\0").filter(Boolean);
|
|
17943
18571
|
const flag = argv.lastIndexOf("--config");
|
|
17944
|
-
return flag > 0 && argv[flag - 1] === "daemon" &&
|
|
18572
|
+
return flag > 0 && argv[flag - 1] === "daemon" && resolve7(argv[flag + 1]) === resolve7(configPath);
|
|
17945
18573
|
} catch {
|
|
17946
18574
|
try {
|
|
17947
18575
|
const { stdout: stdout6 } = await execFileAsync("ps", ["-p", String(pid), "-o", "command="]);
|
|
17948
|
-
return stdout6.includes(" daemon ") && stdout6.includes(
|
|
18576
|
+
return stdout6.includes(" daemon ") && stdout6.includes(resolve7(configPath));
|
|
17949
18577
|
} catch {
|
|
17950
18578
|
return false;
|
|
17951
18579
|
}
|
|
@@ -17971,14 +18599,14 @@ async function stopRuntimeDaemon(path, opts = {}) {
|
|
|
17971
18599
|
throw new Error(`agent daemon ${pid} did not stop after ${timeout}ms`);
|
|
17972
18600
|
}
|
|
17973
18601
|
async function launchRuntimeDaemon(configPath, opts = {}) {
|
|
17974
|
-
const directory =
|
|
18602
|
+
const directory = dirname3(configPath);
|
|
17975
18603
|
await mkdir(directory, { recursive: true, mode: 448 });
|
|
17976
18604
|
const foreground = opts.foreground === true;
|
|
17977
|
-
const output = foreground ? "inherit" : openSync(
|
|
18605
|
+
const output = foreground ? "inherit" : openSync(resolve7(directory, "daemon.log"), "a", 384);
|
|
17978
18606
|
const entrypoint = opts.entrypoint ?? process.argv[1];
|
|
17979
18607
|
if (!entrypoint)
|
|
17980
18608
|
throw new Error("cannot resolve daemon CLI entrypoint");
|
|
17981
|
-
const child =
|
|
18609
|
+
const child = spawn6(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve7(configPath)], {
|
|
17982
18610
|
cwd: directory,
|
|
17983
18611
|
env: opts.env ?? process.env,
|
|
17984
18612
|
detached: !foreground,
|
|
@@ -17995,9 +18623,9 @@ async function launchRuntimeDaemon(configPath, opts = {}) {
|
|
|
17995
18623
|
}
|
|
17996
18624
|
async function removeAgentRuntime(runtime) {
|
|
17997
18625
|
const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
|
|
17998
|
-
const deletedRoot =
|
|
18626
|
+
const deletedRoot = resolve7(runtime.supervisorRoot, "beeline", "deleted-runtimes");
|
|
17999
18627
|
await mkdir(deletedRoot, { recursive: true, mode: 448 });
|
|
18000
|
-
const target =
|
|
18628
|
+
const target = resolve7(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
|
|
18001
18629
|
await rename(source, target);
|
|
18002
18630
|
return target;
|
|
18003
18631
|
}
|
|
@@ -18038,7 +18666,7 @@ var DaemonApiClient = class {
|
|
|
18038
18666
|
agentId;
|
|
18039
18667
|
fetchImpl;
|
|
18040
18668
|
webSocketFactory;
|
|
18041
|
-
presenceLifecycleId =
|
|
18669
|
+
presenceLifecycleId = randomUUID2();
|
|
18042
18670
|
liveSocket;
|
|
18043
18671
|
liveReconnect;
|
|
18044
18672
|
liveReconnectDelayMs = 1e3;
|
|
@@ -18232,8 +18860,8 @@ var DaemonApiClient = class {
|
|
|
18232
18860
|
};
|
|
18233
18861
|
async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
18234
18862
|
const runtime = await readRuntimeRecord(path);
|
|
18235
|
-
const expectedPath =
|
|
18236
|
-
if (
|
|
18863
|
+
const expectedPath = resolve8(runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey), "runtime.json");
|
|
18864
|
+
if (resolve8(path) !== expectedPath) {
|
|
18237
18865
|
throw new Error(`refusing daemon token exchange outside canonical runtime path: ${path}`);
|
|
18238
18866
|
}
|
|
18239
18867
|
const transport = runtime.transport;
|
|
@@ -18273,18 +18901,18 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
18273
18901
|
|
|
18274
18902
|
// apps/body/dist/room-runtime.js
|
|
18275
18903
|
import { execFile as execFile6 } from "node:child_process";
|
|
18276
|
-
import { createHash as
|
|
18277
|
-
import { existsSync as
|
|
18904
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
18905
|
+
import { existsSync as existsSync5, mkdirSync } from "node:fs";
|
|
18278
18906
|
import { mkdir as mkdir13, rm as rm5 } from "node:fs/promises";
|
|
18279
|
-
import { dirname as
|
|
18907
|
+
import { dirname as dirname9, resolve as resolve21 } from "node:path";
|
|
18280
18908
|
import { promisify as promisify4 } from "node:util";
|
|
18281
18909
|
|
|
18282
18910
|
// apps/body/dist/grant-runner.js
|
|
18283
18911
|
import { execFile as execFile3 } from "node:child_process";
|
|
18284
|
-
import { createHash as
|
|
18912
|
+
import { createHash as createHash3, randomBytes as randomBytes6 } from "node:crypto";
|
|
18285
18913
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
18286
18914
|
import { createServer } from "node:http";
|
|
18287
|
-
import { dirname as
|
|
18915
|
+
import { dirname as dirname5, resolve as resolve11 } from "node:path";
|
|
18288
18916
|
|
|
18289
18917
|
// packages/api-contract/dist/agent-grants.js
|
|
18290
18918
|
var AGENT_GRANT_KINDS = ["path", "host", "secret", "device", "budget", "command"];
|
|
@@ -18507,9 +19135,9 @@ function surfaceAllows(surface, capability) {
|
|
|
18507
19135
|
|
|
18508
19136
|
// apps/body/dist/bwrap-sandbox.js
|
|
18509
19137
|
import { spawnSync } from "node:child_process";
|
|
18510
|
-
import { lstatSync as
|
|
18511
|
-
import { homedir as
|
|
18512
|
-
import { isAbsolute as isAbsolute2, relative, resolve as
|
|
19138
|
+
import { lstatSync as lstatSync3 } from "node:fs";
|
|
19139
|
+
import { homedir as homedir4 } from "node:os";
|
|
19140
|
+
import { isAbsolute as isAbsolute2, relative, resolve as resolve9 } from "node:path";
|
|
18513
19141
|
var DEFAULT_SANDBOX_POLICY = "bwrap";
|
|
18514
19142
|
function isSandboxPolicy(value) {
|
|
18515
19143
|
return value === "bwrap" || value === "off";
|
|
@@ -18541,16 +19169,16 @@ var HARNESS_HOME_STATE_DIRS = [
|
|
|
18541
19169
|
dirs: [".grok"]
|
|
18542
19170
|
},
|
|
18543
19171
|
{
|
|
18544
|
-
match:
|
|
19172
|
+
match: CURSOR_HARNESS_COMMAND,
|
|
18545
19173
|
dirs: [".cursor"]
|
|
18546
19174
|
}
|
|
18547
19175
|
];
|
|
18548
|
-
function harnessHomeStateDirs(agentCommand, home =
|
|
19176
|
+
function harnessHomeStateDirs(agentCommand, home = homedir4()) {
|
|
18549
19177
|
if (!agentCommand)
|
|
18550
19178
|
return [];
|
|
18551
19179
|
for (const { match, dirs } of HARNESS_HOME_STATE_DIRS) {
|
|
18552
19180
|
if (match.test(agentCommand))
|
|
18553
|
-
return dirs.map((dir) =>
|
|
19181
|
+
return dirs.map((dir) => resolve9(home, dir));
|
|
18554
19182
|
}
|
|
18555
19183
|
return [];
|
|
18556
19184
|
}
|
|
@@ -18562,18 +19190,18 @@ var KNOWN_CREDENTIAL_MASK_PATHS = [
|
|
|
18562
19190
|
".git-credentials",
|
|
18563
19191
|
".secrets.env"
|
|
18564
19192
|
];
|
|
18565
|
-
function credentialMaskPaths(extraPaths, home =
|
|
19193
|
+
function credentialMaskPaths(extraPaths, home = homedir4(), stat4 = (path) => {
|
|
18566
19194
|
try {
|
|
18567
|
-
const info =
|
|
19195
|
+
const info = lstatSync3(path);
|
|
18568
19196
|
return { isDirectory: info.isDirectory() };
|
|
18569
19197
|
} catch {
|
|
18570
19198
|
return void 0;
|
|
18571
19199
|
}
|
|
18572
19200
|
}, requiredPaths = []) {
|
|
18573
|
-
const required = new Set(requiredPaths.map((path) =>
|
|
19201
|
+
const required = new Set(requiredPaths.map((path) => resolve9(path)));
|
|
18574
19202
|
const candidates = [
|
|
18575
|
-
...KNOWN_CREDENTIAL_MASK_PATHS.map((entry) =>
|
|
18576
|
-
...(extraPaths ?? []).map((entry) =>
|
|
19203
|
+
...KNOWN_CREDENTIAL_MASK_PATHS.map((entry) => resolve9(home, entry)),
|
|
19204
|
+
...(extraPaths ?? []).map((entry) => resolve9(entry))
|
|
18577
19205
|
];
|
|
18578
19206
|
const seen = /* @__PURE__ */ new Set();
|
|
18579
19207
|
const masks = [];
|
|
@@ -18600,7 +19228,7 @@ function normalize(paths) {
|
|
|
18600
19228
|
for (const path of paths) {
|
|
18601
19229
|
if (!path)
|
|
18602
19230
|
continue;
|
|
18603
|
-
seen.add(
|
|
19231
|
+
seen.add(resolve9(path));
|
|
18604
19232
|
}
|
|
18605
19233
|
return Array.from(seen).sort();
|
|
18606
19234
|
}
|
|
@@ -18638,7 +19266,7 @@ function sandboxMountPlan(spec) {
|
|
|
18638
19266
|
writable,
|
|
18639
19267
|
quotaTmpfs: spec.workbench ? [
|
|
18640
19268
|
{
|
|
18641
|
-
target:
|
|
19269
|
+
target: resolve9(spec.workbench.dir),
|
|
18642
19270
|
maxBytes: spec.workbench.maxBytes,
|
|
18643
19271
|
maxInodes: spec.workbench.maxInodes,
|
|
18644
19272
|
blockGit: true
|
|
@@ -18693,7 +19321,7 @@ function buildBwrapArgv(input) {
|
|
|
18693
19321
|
for (const binding of quotaTmpfs) {
|
|
18694
19322
|
args.push("--dir", binding.target, "--size", String(binding.maxBytes), "--tmpfs", binding.target);
|
|
18695
19323
|
if (binding.blockGit)
|
|
18696
|
-
args.push("--ro-bind", "/dev/null",
|
|
19324
|
+
args.push("--ro-bind", "/dev/null", resolve9(binding.target, ".git"));
|
|
18697
19325
|
}
|
|
18698
19326
|
args.push("--chdir", input.cwd);
|
|
18699
19327
|
args.push("--die-with-parent");
|
|
@@ -18759,8 +19387,8 @@ function detectBwrapSandbox(options = {}) {
|
|
|
18759
19387
|
|
|
18760
19388
|
// apps/body/dist/provider-key-store.js
|
|
18761
19389
|
import { chmod, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
18762
|
-
import { homedir as
|
|
18763
|
-
import { dirname as
|
|
19390
|
+
import { homedir as homedir5 } from "node:os";
|
|
19391
|
+
import { dirname as dirname4, resolve as resolve10 } from "node:path";
|
|
18764
19392
|
var PROVIDER_KEY_ENV_VARS = {
|
|
18765
19393
|
openrouter: "OPENROUTER_API_KEY",
|
|
18766
19394
|
openai: "OPENAI_API_KEY",
|
|
@@ -18770,8 +19398,8 @@ var PROVIDER_KEY_ENV_VARS = {
|
|
|
18770
19398
|
};
|
|
18771
19399
|
var GOOGLE_ENV_ALIAS = "GEMINI_API_KEY";
|
|
18772
19400
|
function providerKeyStorePath(env = process.env) {
|
|
18773
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
18774
|
-
return
|
|
19401
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve10(homedir5(), ".config");
|
|
19402
|
+
return resolve10(configRoot, "beeline", "providers.json");
|
|
18775
19403
|
}
|
|
18776
19404
|
async function readProviderKeyStore(env = process.env) {
|
|
18777
19405
|
const path = providerKeyStorePath(env);
|
|
@@ -18794,7 +19422,7 @@ async function readSavedProviderKey(provider, env = process.env) {
|
|
|
18794
19422
|
async function saveProviderKey(provider, key, env = process.env) {
|
|
18795
19423
|
const path = providerKeyStorePath(env);
|
|
18796
19424
|
const store = { ...await readProviderKeyStore(env), [provider]: key };
|
|
18797
|
-
await mkdir2(
|
|
19425
|
+
await mkdir2(dirname4(path), { recursive: true, mode: 448 });
|
|
18798
19426
|
await writeFile3(path, `${JSON.stringify(store, null, 2)}
|
|
18799
19427
|
`, { mode: 384 });
|
|
18800
19428
|
await chmod(path, 384);
|
|
@@ -18831,7 +19459,7 @@ function operatorSecretResolver(env = process.env) {
|
|
|
18831
19459
|
if (saved)
|
|
18832
19460
|
return saved;
|
|
18833
19461
|
}
|
|
18834
|
-
const raw = await readFile4(
|
|
19462
|
+
const raw = await readFile4(resolve11(dirname5(providerKeyStorePath(env)), "secrets.json"), "utf8").catch(() => void 0);
|
|
18835
19463
|
if (!raw)
|
|
18836
19464
|
return void 0;
|
|
18837
19465
|
try {
|
|
@@ -18943,9 +19571,9 @@ var GrantCommandRunner = class {
|
|
|
18943
19571
|
...Object.fromEntries(secrets)
|
|
18944
19572
|
};
|
|
18945
19573
|
const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
|
|
18946
|
-
const
|
|
19574
|
+
const spawn12 = surfaceAllows(policy.surface, "run-host-command") ? { command: argv[0], args: argv.slice(1) } : roomSandboxCommand(policy, room.cwd, argv);
|
|
18947
19575
|
const outcome = await new Promise((resolveRun) => {
|
|
18948
|
-
const child = execFile3(
|
|
19576
|
+
const child = execFile3(spawn12.command, spawn12.args, {
|
|
18949
19577
|
cwd: room.cwd,
|
|
18950
19578
|
env,
|
|
18951
19579
|
timeout: this.options.timeoutMs ?? GRANT_COMMAND_TIMEOUT_MS,
|
|
@@ -19023,7 +19651,7 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
19023
19651
|
if (!script || script.path !== argument.path) {
|
|
19024
19652
|
throw new Error(`${argument.path} is a script no human has read: this grant was approved without it. Ask with request_grant kind=command so the card carries what the command runs.`);
|
|
19025
19653
|
}
|
|
19026
|
-
if (
|
|
19654
|
+
if (createHash3("sha256").update(bytes).digest("hex") !== script.sha256) {
|
|
19027
19655
|
throw new Error(`${argument.path} changed after it was approved, so the approval no longer covers it. Ask with request_grant kind=command again so a human reads what runs now.`);
|
|
19028
19656
|
}
|
|
19029
19657
|
}
|
|
@@ -19033,8 +19661,8 @@ function scriptCandidates(cwd, scratch, argv) {
|
|
|
19033
19661
|
if (!argument)
|
|
19034
19662
|
return [];
|
|
19035
19663
|
const paths = [
|
|
19036
|
-
|
|
19037
|
-
...scratch ? [
|
|
19664
|
+
resolve11(cwd, argument.path),
|
|
19665
|
+
...scratch ? [resolve11(scratch, argument.path)] : []
|
|
19038
19666
|
];
|
|
19039
19667
|
return [...new Set(paths)];
|
|
19040
19668
|
}
|
|
@@ -19215,20 +19843,20 @@ function captureConnectionUsage(recorder, turn, calls) {
|
|
|
19215
19843
|
}
|
|
19216
19844
|
|
|
19217
19845
|
// apps/body/dist/server-command-intake.js
|
|
19218
|
-
import { randomUUID as
|
|
19846
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
19219
19847
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
|
|
19220
|
-
import { tmpdir as
|
|
19221
|
-
import { dirname as
|
|
19848
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
19849
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
19222
19850
|
var CommandExecutionContext = class {
|
|
19223
|
-
generationId =
|
|
19851
|
+
generationId = randomUUID3();
|
|
19224
19852
|
path;
|
|
19225
19853
|
current;
|
|
19226
19854
|
constructor(root) {
|
|
19227
|
-
this.path =
|
|
19855
|
+
this.path = join5(root ?? tmpdir3(), `beeline-command-${this.generationId}.json`);
|
|
19228
19856
|
}
|
|
19229
19857
|
async enter(command) {
|
|
19230
19858
|
this.current = command;
|
|
19231
|
-
await mkdir3(
|
|
19859
|
+
await mkdir3(dirname6(this.path), { recursive: true });
|
|
19232
19860
|
await writeFile4(this.path, JSON.stringify({
|
|
19233
19861
|
roomId: command.roomId,
|
|
19234
19862
|
requestId: command.turnRequestId,
|
|
@@ -19350,13 +19978,13 @@ async function runServerCommandIntake(options) {
|
|
|
19350
19978
|
}
|
|
19351
19979
|
}
|
|
19352
19980
|
options.onPoll?.();
|
|
19353
|
-
const reconcile = await new Promise((
|
|
19981
|
+
const reconcile = await new Promise((resolve32) => {
|
|
19354
19982
|
const done = (needed) => {
|
|
19355
19983
|
if (timer)
|
|
19356
19984
|
clearTimeout(timer);
|
|
19357
19985
|
signal?.removeEventListener("abort", aborted);
|
|
19358
19986
|
wake = void 0;
|
|
19359
|
-
|
|
19987
|
+
resolve32(needed);
|
|
19360
19988
|
};
|
|
19361
19989
|
const aborted = () => done(false);
|
|
19362
19990
|
wake = done;
|
|
@@ -19382,22 +20010,35 @@ async function runServerCommandIntake(options) {
|
|
|
19382
20010
|
|
|
19383
20011
|
// apps/body/dist/monolith-corner-turn.js
|
|
19384
20012
|
import { execFile as execFile5 } from "node:child_process";
|
|
19385
|
-
import { createHash as
|
|
20013
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
19386
20014
|
import { mkdir as mkdir12 } from "node:fs/promises";
|
|
19387
|
-
import { homedir as
|
|
19388
|
-
import { join as
|
|
20015
|
+
import { homedir as homedir8 } from "node:os";
|
|
20016
|
+
import { join as join10 } from "node:path";
|
|
19389
20017
|
import { promisify as promisify3 } from "node:util";
|
|
19390
20018
|
|
|
19391
20019
|
// apps/body/dist/agent-home.js
|
|
19392
|
-
import { existsSync as
|
|
19393
|
-
import { createHash as
|
|
20020
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "node:fs";
|
|
20021
|
+
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
19394
20022
|
import { chmod as chmod2, copyFile, lstat, mkdir as mkdir5, readFile as readFile6, readdir as readdir2, realpath, rename as rename2, rm as rm2, symlink, unlink, writeFile as writeFile6 } from "node:fs/promises";
|
|
19395
|
-
import { homedir as
|
|
19396
|
-
import { basename as
|
|
20023
|
+
import { homedir as homedir6 } from "node:os";
|
|
20024
|
+
import { basename as basename4, dirname as dirname7, join as join6, relative as relative2, resolve as resolve14, sep } from "node:path";
|
|
19397
20025
|
|
|
19398
20026
|
// apps/body/dist/beeline-skill.js
|
|
19399
|
-
import { readFileSync as
|
|
19400
|
-
import { resolve as
|
|
20027
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
20028
|
+
import { resolve as resolve12 } from "node:path";
|
|
20029
|
+
|
|
20030
|
+
// packages/api-contract/dist/workbench.js
|
|
20031
|
+
var CONNECTABLE_CONNECTOR_KINDS = [
|
|
20032
|
+
"trusty-squire",
|
|
20033
|
+
"google-gmail",
|
|
20034
|
+
"google-calendar",
|
|
20035
|
+
"google-drive",
|
|
20036
|
+
"google-youtube"
|
|
20037
|
+
];
|
|
20038
|
+
|
|
20039
|
+
// packages/api-contract/dist/connector-offers.js
|
|
20040
|
+
var CONNECTOR_OFFER_WINDOW_MS = 2 * 6e4;
|
|
20041
|
+
var OFFERABLE_CONNECTOR_KINDS = CONNECTABLE_CONNECTOR_KINDS.filter((kind) => kind !== "wallet");
|
|
19401
20042
|
|
|
19402
20043
|
// packages/api-contract/dist/agent-pairing-code.js
|
|
19403
20044
|
var CURRENT_AGENT_PAIRING_CODE = /^[0-9A-F]{8}-[0-9A-F]{8}$/;
|
|
@@ -19462,12 +20103,12 @@ function beelineCapabilityContextForHarness(agentCommand, repository, directMess
|
|
|
19462
20103
|
...harnessHonorsSessionSystemPrompt(agentCommand) ? {} : { compatibilityTurnPrefix: primer }
|
|
19463
20104
|
};
|
|
19464
20105
|
}
|
|
19465
|
-
function runningBeelineReleaseId(env = process.env, read = (path) =>
|
|
20106
|
+
function runningBeelineReleaseId(env = process.env, read = (path) => readFileSync5(path, "utf8")) {
|
|
19466
20107
|
try {
|
|
19467
20108
|
const lib = env.BEELINE_LIB_DIR;
|
|
19468
20109
|
if (!lib)
|
|
19469
20110
|
return "source";
|
|
19470
|
-
const manifest = JSON.parse(read(
|
|
20111
|
+
const manifest = JSON.parse(read(resolve12(lib, "bundle.json")));
|
|
19471
20112
|
return [manifest.version, manifest.commit].filter(Boolean).join("-") || "source";
|
|
19472
20113
|
} catch {
|
|
19473
20114
|
return "source";
|
|
@@ -19489,13 +20130,17 @@ When your corner's pull request is ready, merging is your step: once the configu
|
|
|
19489
20130
|
|
|
19490
20131
|
## Tools and the Workbench
|
|
19491
20132
|
|
|
19492
|
-
A **tool** is something you can use once a human
|
|
20133
|
+
A **tool** is something you can use once a human adds it; a **key** is the credential that tool holds for that human. You spend a key through the mounted connector and never see the credential itself.
|
|
19493
20134
|
|
|
19494
|
-
|
|
20135
|
+
The tools this build knows, what each is for, and which the person you are answering already has are one call away: beeline-agent workbench_status. Trusty Squire is vaulted credentials plus a browser that signs up and signs in for you; the Google tools (Gmail, Calendar, Drive, YouTube) work through the person's own Google sign-in. The wallet is created only from the Workbench page, and Tailscale is not available yet.
|
|
19495
20136
|
|
|
19496
|
-
|
|
20137
|
+
**Offer the tool at the moment you need it.** When the work in front of you needs a tool the person does not have, do not send them to a settings page and do not stop at naming it. Call workbench_status first - a tool they already have is used, not offered. Then call offer_connector with the connectorType and one short reason: a card appears in this Room, spoken by you, addressed to the person you are answering, with one action. Only that person or a Workspace admin can accept it; accepting adds the tool on your machine, and the sign-in or keys stay theirs. Your turn pauses on the card - say in prose what you are waiting for and end the turn; you are woken when it is added, and then you carry on.
|
|
19497
20138
|
|
|
19498
|
-
|
|
20139
|
+
**Research before you offer, and say so.** Never offer a tool you cannot describe. If someone asks you to install or add something you do not already know - by name, by purpose, or by what it will hold - say plainly that you are looking it up first, find out what it is and what it does with credentials, and state what you learned in your reply BEFORE the card appears. A person reading "Add X?" must already have read, in your own words, what X is. Refusing to act blind is part of being trusted with keys.
|
|
20140
|
+
|
|
20141
|
+
An offer is setup, never authority: it does not replace a grant, write permission, target-branch confirmation, or the merge gate, and it never needs a raw credential in chat. You never pair a tool yourself and never ask anyone for a key value; once a tool is added, provisioning happens inside it and receipts reach the person through the tool's own status message.
|
|
20142
|
+
|
|
20143
|
+
Keys are sovereign: they belong to the human who provisioned them. You cannot use another member's key and must not ask a member to share one. The Workbench page remains the place a person manages tools and keys by hand (Settings \u2192 Workbench); you point there to MANAGE what exists, not to add what you need.
|
|
19499
20144
|
|
|
19500
20145
|
## Showing a mock
|
|
19501
20146
|
|
|
@@ -19649,7 +20294,7 @@ var SQUIRE_GOVERNED_TOOL_SET = new Set(SQUIRE_GOVERNED_TOOLS);
|
|
|
19649
20294
|
|
|
19650
20295
|
// apps/body/dist/openrouter-routing.js
|
|
19651
20296
|
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
19652
|
-
import { resolve as
|
|
20297
|
+
import { resolve as resolve13 } from "node:path";
|
|
19653
20298
|
var OPENROUTER_ENDPOINTS_BASE_URL = "https://openrouter.ai/api/v1/models";
|
|
19654
20299
|
var OPENROUTER_COMPLETIONS_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
19655
20300
|
var OPENROUTER_ROUTING_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -19751,10 +20396,10 @@ function openRouterRoutingFor(providers, allowFallbacks = true) {
|
|
|
19751
20396
|
};
|
|
19752
20397
|
}
|
|
19753
20398
|
function openRouterRoutingCacheDir(runtimeDir) {
|
|
19754
|
-
return
|
|
20399
|
+
return resolve13(runtimeDir, "openrouter-routing");
|
|
19755
20400
|
}
|
|
19756
20401
|
function cachePath(cacheDir, model) {
|
|
19757
|
-
return
|
|
20402
|
+
return resolve13(cacheDir, `${model.replace(/[^A-Za-z0-9._-]+/g, "_")}.json`);
|
|
19758
20403
|
}
|
|
19759
20404
|
async function readCache(cacheDir, model) {
|
|
19760
20405
|
try {
|
|
@@ -19785,7 +20430,7 @@ async function writeCache(cacheDir, value) {
|
|
|
19785
20430
|
});
|
|
19786
20431
|
}
|
|
19787
20432
|
function probeCachePath(cacheDir, model) {
|
|
19788
|
-
return
|
|
20433
|
+
return resolve13(cacheDir, `${model.replace(/[^A-Za-z0-9._-]+/g, "_")}.probe.json`);
|
|
19789
20434
|
}
|
|
19790
20435
|
async function readProbeCache(cacheDir, model) {
|
|
19791
20436
|
try {
|
|
@@ -20225,8 +20870,8 @@ var SHARED_CREDENTIALS = [
|
|
|
20225
20870
|
// shape as codex).
|
|
20226
20871
|
{ dir: "grok", source: ".grok/auth.json", target: "auth.json" },
|
|
20227
20872
|
{ dir: "pi", source: ".pi/agent/auth.json", target: "auth.json" },
|
|
20228
|
-
// Cursor CLI stores auth state under ~/.cursor/; cursor-agent
|
|
20229
|
-
//
|
|
20873
|
+
// Cursor CLI stores auth state under ~/.cursor/; cursor-agent reads
|
|
20874
|
+
// CURSOR_HOME to relocate the data directory.
|
|
20230
20875
|
{ dir: "cursor", source: ".cursor/agent-cli-state.json", target: "agent-cli-state.json" }
|
|
20231
20876
|
];
|
|
20232
20877
|
var GOOSE_SHARED_CONFIG_FILES = ["config.yaml", "secrets.yaml"];
|
|
@@ -20275,8 +20920,8 @@ var HOME_SUBDIRS = [
|
|
|
20275
20920
|
"tmp"
|
|
20276
20921
|
];
|
|
20277
20922
|
async function prepareRoomAgentHome(input) {
|
|
20278
|
-
const root =
|
|
20279
|
-
const operatorHome = input.operatorHome ??
|
|
20923
|
+
const root = resolve14(input.root);
|
|
20924
|
+
const operatorHome = input.operatorHome ?? homedir6();
|
|
20280
20925
|
try {
|
|
20281
20926
|
await mkdir5(root, { recursive: true, mode: 448 });
|
|
20282
20927
|
const rootStats = await lstat(root);
|
|
@@ -20284,7 +20929,7 @@ async function prepareRoomAgentHome(input) {
|
|
|
20284
20929
|
throw new AgentHomeSecurityError(`agent home root is not an ordinary directory: ${root}`);
|
|
20285
20930
|
}
|
|
20286
20931
|
for (const subdir of HOME_SUBDIRS) {
|
|
20287
|
-
const path =
|
|
20932
|
+
const path = resolve14(root, subdir);
|
|
20288
20933
|
await mkdir5(path, { recursive: true, mode: 448 });
|
|
20289
20934
|
await assertRealContainedDirectory(path, root);
|
|
20290
20935
|
}
|
|
@@ -20295,9 +20940,9 @@ async function prepareRoomAgentHome(input) {
|
|
|
20295
20940
|
return {};
|
|
20296
20941
|
}
|
|
20297
20942
|
for (const credential of SHARED_CREDENTIALS) {
|
|
20298
|
-
const source =
|
|
20299
|
-
const target =
|
|
20300
|
-
if (!
|
|
20943
|
+
const source = resolve14(operatorHome, credential.source);
|
|
20944
|
+
const target = resolve14(root, credential.dir, credential.target);
|
|
20945
|
+
if (!existsSync4(source) || existsSync4(target))
|
|
20301
20946
|
continue;
|
|
20302
20947
|
await symlink(source, target).catch(() => void 0);
|
|
20303
20948
|
}
|
|
@@ -20319,12 +20964,12 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
20319
20964
|
...isReviewer ? [{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }] : []
|
|
20320
20965
|
];
|
|
20321
20966
|
const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
|
|
20322
|
-
await provisionManagedSkillsDir(
|
|
20967
|
+
await provisionManagedSkillsDir(resolve14(root, skillDir, "skills"), managedSkills, shared, sharedSkills.length === 0);
|
|
20323
20968
|
for (const config of HARNESS_MCP_CONFIGS) {
|
|
20324
20969
|
try {
|
|
20325
|
-
const source =
|
|
20326
|
-
const target =
|
|
20327
|
-
const mcpSection =
|
|
20970
|
+
const source = resolve14(operatorHome, config.toml);
|
|
20971
|
+
const target = resolve14(root, config.dir, "config.toml");
|
|
20972
|
+
const mcpSection = existsSync4(source) ? filteredHarnessMcpToml(readFileSync6(source, "utf8")) : void 0;
|
|
20328
20973
|
const section = config.dir === "codex" ? [CODEX_ROOM_AGENT_LOCKDOWN_TOML, CODEX_ROOM_WEB_SEARCH_TOML, mcpSection].filter(Boolean).join("\n") : mcpSection;
|
|
20329
20974
|
if (!section) {
|
|
20330
20975
|
await unlink(target).catch(() => void 0);
|
|
@@ -20338,13 +20983,13 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
20338
20983
|
}
|
|
20339
20984
|
}
|
|
20340
20985
|
try {
|
|
20341
|
-
const gooseConfigDir =
|
|
20986
|
+
const gooseConfigDir = resolve14(root, "goose", "config");
|
|
20342
20987
|
await mkdir5(gooseConfigDir, { recursive: true, mode: 448 });
|
|
20343
20988
|
for (const name of GOOSE_SHARED_CONFIG_FILES) {
|
|
20344
|
-
const source =
|
|
20345
|
-
const target =
|
|
20346
|
-
if (
|
|
20347
|
-
await writeIsolatedHarnessFile(target,
|
|
20989
|
+
const source = resolve14(operatorHome, ".config", "goose", name);
|
|
20990
|
+
const target = resolve14(gooseConfigDir, name);
|
|
20991
|
+
if (existsSync4(source)) {
|
|
20992
|
+
await writeIsolatedHarnessFile(target, readFileSync6(source, "utf8"));
|
|
20348
20993
|
} else {
|
|
20349
20994
|
await unlink(target).catch(() => void 0);
|
|
20350
20995
|
}
|
|
@@ -20355,9 +21000,9 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
20355
21000
|
console.warn("[body] operator Goose configuration passthrough failed:", error);
|
|
20356
21001
|
}
|
|
20357
21002
|
try {
|
|
20358
|
-
const claudeJson =
|
|
20359
|
-
const claudeTarget =
|
|
20360
|
-
const mcpServers =
|
|
21003
|
+
const claudeJson = resolve14(operatorHome, ".claude.json");
|
|
21004
|
+
const claudeTarget = resolve14(root, "claude", ".claude.json");
|
|
21005
|
+
const mcpServers = existsSync4(claudeJson) ? readClaudeUserScopeMcpServers(claudeJson) : void 0;
|
|
20361
21006
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
20362
21007
|
await writeIsolatedHarnessFile(claudeTarget, `${JSON.stringify({ mcpServers }, null, 2)}
|
|
20363
21008
|
`);
|
|
@@ -20371,7 +21016,7 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
20371
21016
|
}
|
|
20372
21017
|
try {
|
|
20373
21018
|
const settings2 = { permissions: { allow: ["WebSearch", "WebFetch"] } };
|
|
20374
|
-
await writeIsolatedHarnessFile(
|
|
21019
|
+
await writeIsolatedHarnessFile(resolve14(root, "claude", "settings.json"), `${JSON.stringify(settings2, null, 2)}
|
|
20375
21020
|
`);
|
|
20376
21021
|
} catch (error) {
|
|
20377
21022
|
if (failClosed)
|
|
@@ -20381,8 +21026,8 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
20381
21026
|
await provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting);
|
|
20382
21027
|
}
|
|
20383
21028
|
async function provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting) {
|
|
20384
|
-
const source =
|
|
20385
|
-
const target =
|
|
21029
|
+
const source = resolve14(operatorHome, PI_CUSTOM_MODEL_CONFIG.source);
|
|
21030
|
+
const target = resolve14(root, "pi", PI_CUSTOM_MODEL_CONFIG.target);
|
|
20386
21031
|
let decision2;
|
|
20387
21032
|
if (openRouterRouting) {
|
|
20388
21033
|
decision2 = await resolveOpenRouterRouting(openRouterRouting);
|
|
@@ -20415,7 +21060,7 @@ async function provisionPiCustomModelConfig(root, operatorHome, failClosed, open
|
|
|
20415
21060
|
if (resolvedSource !== source) {
|
|
20416
21061
|
throw new AgentHomeSecurityError(`Pi custom model config resolves through a link: ${source}`);
|
|
20417
21062
|
}
|
|
20418
|
-
const sourceValue = JSON.parse(
|
|
21063
|
+
const sourceValue = JSON.parse(readFileSync6(resolvedSource, "utf8"));
|
|
20419
21064
|
await writeIsolatedHarnessFile(target, `${JSON.stringify(withOpenRouterModelRouting(sourceValue, pin), null, 2)}
|
|
20420
21065
|
`);
|
|
20421
21066
|
} catch (error) {
|
|
@@ -20427,7 +21072,7 @@ async function provisionPiCustomModelConfig(root, operatorHome, failClosed, open
|
|
|
20427
21072
|
}
|
|
20428
21073
|
function readClaudeUserScopeMcpServers(path) {
|
|
20429
21074
|
try {
|
|
20430
|
-
const parsed = JSON.parse(
|
|
21075
|
+
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
20431
21076
|
if (parsed && typeof parsed.mcpServers === "object" && parsed.mcpServers !== null) {
|
|
20432
21077
|
return Object.fromEntries(Object.entries(parsed.mcpServers).filter(([name, value]) => {
|
|
20433
21078
|
if (name === "squire")
|
|
@@ -20451,21 +21096,21 @@ function filteredHarnessMcpToml(source) {
|
|
|
20451
21096
|
return extractTomlSections(source, ["mcp_servers"], excluded);
|
|
20452
21097
|
}
|
|
20453
21098
|
async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, optionalShares) {
|
|
20454
|
-
const parent =
|
|
20455
|
-
await assertRealContainedDirectory(parent,
|
|
21099
|
+
const parent = dirname7(target);
|
|
21100
|
+
await assertRealContainedDirectory(parent, dirname7(parent));
|
|
20456
21101
|
const plan = await planManagedSkills(managedSkills, sharedSkills, optionalShares);
|
|
20457
21102
|
if (await materializedSkillManifest(target) === plan.manifest)
|
|
20458
21103
|
return;
|
|
20459
|
-
const staged =
|
|
21104
|
+
const staged = resolve14(parent, `.skills.${process.pid}.${randomUUID4()}.tmp`);
|
|
20460
21105
|
await mkdir5(staged, { mode: 448 });
|
|
20461
21106
|
try {
|
|
20462
21107
|
for (const entry of plan.entries) {
|
|
20463
21108
|
if (entry.kind === "managed") {
|
|
20464
|
-
const skillDir =
|
|
21109
|
+
const skillDir = resolve14(staged, entry.name);
|
|
20465
21110
|
await mkdir5(skillDir, { recursive: true });
|
|
20466
|
-
await writeIsolatedHarnessFile(
|
|
21111
|
+
await writeIsolatedHarnessFile(resolve14(skillDir, "SKILL.md"), entry.content);
|
|
20467
21112
|
} else {
|
|
20468
|
-
await copySafeSkillTree(entry.source,
|
|
21113
|
+
await copySafeSkillTree(entry.source, resolve14(staged, entry.name), entry.source);
|
|
20469
21114
|
}
|
|
20470
21115
|
}
|
|
20471
21116
|
const existing = await lstat(target).catch(() => void 0);
|
|
@@ -20492,8 +21137,8 @@ async function planManagedSkills(managedSkills, sharedSkills, optionalShares) {
|
|
|
20492
21137
|
try {
|
|
20493
21138
|
const tree = [];
|
|
20494
21139
|
await walkSafeSkillTree(shared.source, shared.source, {
|
|
20495
|
-
directory: async (rel) => void tree.push(`d ${
|
|
20496
|
-
file: async (rel, realPath) => void tree.push(`f ${
|
|
21140
|
+
directory: async (rel) => void tree.push(`d ${join6(shared.name, rel)}`),
|
|
21141
|
+
file: async (rel, realPath) => void tree.push(`f ${join6(shared.name, rel)} ${sha2563(await readFile6(realPath))}`)
|
|
20497
21142
|
});
|
|
20498
21143
|
entries.push({ kind: "shared", name: shared.name, source: shared.source });
|
|
20499
21144
|
lines.push(...tree);
|
|
@@ -20512,8 +21157,8 @@ async function materializedSkillManifest(target) {
|
|
|
20512
21157
|
const lines = [];
|
|
20513
21158
|
const visit = async (directory, prefix) => {
|
|
20514
21159
|
for (const entry of await readdir2(directory)) {
|
|
20515
|
-
const path =
|
|
20516
|
-
const rel = prefix ?
|
|
21160
|
+
const path = resolve14(directory, entry);
|
|
21161
|
+
const rel = prefix ? join6(prefix, entry) : entry;
|
|
20517
21162
|
const entryStats = await lstat(path);
|
|
20518
21163
|
if (entryStats.isSymbolicLink())
|
|
20519
21164
|
return false;
|
|
@@ -20538,7 +21183,7 @@ async function materializedSkillManifest(target) {
|
|
|
20538
21183
|
return lines.sort().join("\n");
|
|
20539
21184
|
}
|
|
20540
21185
|
function sha2563(value) {
|
|
20541
|
-
return
|
|
21186
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
20542
21187
|
}
|
|
20543
21188
|
async function resolveSharedSkillSources(operatorHome, names) {
|
|
20544
21189
|
if (names.length > 0)
|
|
@@ -20547,14 +21192,14 @@ async function resolveSharedSkillSources(operatorHome, names) {
|
|
|
20547
21192
|
const resolved = [];
|
|
20548
21193
|
const skipped = [];
|
|
20549
21194
|
for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
|
|
20550
|
-
const sourceRoot =
|
|
21195
|
+
const sourceRoot = resolve14(operatorHome, relativeRoot);
|
|
20551
21196
|
const rootStats = await lstat(sourceRoot).catch(() => void 0);
|
|
20552
21197
|
if (!rootStats?.isDirectory() || rootStats.isSymbolicLink())
|
|
20553
21198
|
continue;
|
|
20554
21199
|
for (const entry of await readdir2(sourceRoot)) {
|
|
20555
21200
|
if (!isSharedSkillName(entry) || seen.has(entry))
|
|
20556
21201
|
continue;
|
|
20557
|
-
const candidate =
|
|
21202
|
+
const candidate = resolve14(sourceRoot, entry);
|
|
20558
21203
|
try {
|
|
20559
21204
|
const candidateStats = await lstat(candidate);
|
|
20560
21205
|
if (candidateStats.isSymbolicLink()) {
|
|
@@ -20570,7 +21215,7 @@ async function resolveSharedSkillSources(operatorHome, names) {
|
|
|
20570
21215
|
continue;
|
|
20571
21216
|
}
|
|
20572
21217
|
assertContained(sourceRoot, candidate);
|
|
20573
|
-
const skillMd =
|
|
21218
|
+
const skillMd = resolve14(candidate, "SKILL.md");
|
|
20574
21219
|
const skillStats = await lstat(skillMd).catch((error) => {
|
|
20575
21220
|
if (isMissingPathError(error)) {
|
|
20576
21221
|
skipped.push({ path: candidate, reason: "missing SKILL.md" });
|
|
@@ -20622,8 +21267,8 @@ async function resolveExplicitSkillSources(operatorHome, names) {
|
|
|
20622
21267
|
for (const name of unique) {
|
|
20623
21268
|
const matches = [];
|
|
20624
21269
|
for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
|
|
20625
|
-
const sourceRoot =
|
|
20626
|
-
const candidate =
|
|
21270
|
+
const sourceRoot = resolve14(operatorHome, relativeRoot);
|
|
21271
|
+
const candidate = resolve14(sourceRoot, name);
|
|
20627
21272
|
const rootStats = await lstat(sourceRoot).catch(() => void 0);
|
|
20628
21273
|
const candidateStats = await lstat(candidate).catch(() => void 0);
|
|
20629
21274
|
if (!candidateStats)
|
|
@@ -20640,7 +21285,7 @@ async function resolveExplicitSkillSources(operatorHome, names) {
|
|
|
20640
21285
|
if (matches.length !== 1) {
|
|
20641
21286
|
throw new Error(matches.length === 0 ? `shared skill is unavailable: ${name}` : `shared skill source is ambiguous: ${name}`);
|
|
20642
21287
|
}
|
|
20643
|
-
const skillMd =
|
|
21288
|
+
const skillMd = resolve14(matches[0], "SKILL.md");
|
|
20644
21289
|
const skillStats = await lstat(skillMd).catch(() => void 0);
|
|
20645
21290
|
if (!skillStats?.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
|
|
20646
21291
|
throw new Error(`shared skill requires an ordinary SKILL.md: ${name}`);
|
|
@@ -20651,7 +21296,7 @@ async function resolveExplicitSkillSources(operatorHome, names) {
|
|
|
20651
21296
|
}
|
|
20652
21297
|
function assertContained(root, candidate) {
|
|
20653
21298
|
const rel = relative2(root, candidate);
|
|
20654
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) ||
|
|
21299
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || resolve14(root, rel) !== resolve14(candidate)) {
|
|
20655
21300
|
throw new Error(`path escapes the agent skill boundary: ${candidate}`);
|
|
20656
21301
|
}
|
|
20657
21302
|
}
|
|
@@ -20666,14 +21311,14 @@ var BLOCKED_SHARED_FILENAMES = /^(?:\.env(?:\..*)?|auth\.json|\.credentials\.jso
|
|
|
20666
21311
|
async function walkSafeSkillTree(source, sourceRoot, visitor, rel = "") {
|
|
20667
21312
|
assertContained(sourceRoot, source);
|
|
20668
21313
|
const resolvedSource = await realpath(source);
|
|
20669
|
-
if (resolvedSource !==
|
|
21314
|
+
if (resolvedSource !== resolve14(source)) {
|
|
20670
21315
|
throw new Error(`shared skill path resolves through a link: ${source}`);
|
|
20671
21316
|
}
|
|
20672
21317
|
assertContained(sourceRoot, resolvedSource);
|
|
20673
21318
|
const stats = await lstat(resolvedSource);
|
|
20674
21319
|
if (stats.isSymbolicLink())
|
|
20675
21320
|
throw new Error(`shared skill contains a symlink: ${source}`);
|
|
20676
|
-
if (BLOCKED_SHARED_FILENAMES.test(
|
|
21321
|
+
if (BLOCKED_SHARED_FILENAMES.test(basename4(source))) {
|
|
20677
21322
|
throw new Error(`shared skill contains credential or configuration material: ${source}`);
|
|
20678
21323
|
}
|
|
20679
21324
|
if (stats.isDirectory()) {
|
|
@@ -20681,7 +21326,7 @@ async function walkSafeSkillTree(source, sourceRoot, visitor, rel = "") {
|
|
|
20681
21326
|
for (const entry of await readdir2(resolvedSource)) {
|
|
20682
21327
|
if (entry === "." || entry === "..")
|
|
20683
21328
|
throw new Error("invalid shared skill entry");
|
|
20684
|
-
await walkSafeSkillTree(
|
|
21329
|
+
await walkSafeSkillTree(resolve14(source, entry), sourceRoot, visitor, rel ? join6(rel, entry) : entry);
|
|
20685
21330
|
}
|
|
20686
21331
|
return;
|
|
20687
21332
|
}
|
|
@@ -20693,22 +21338,22 @@ async function walkSafeSkillTree(source, sourceRoot, visitor, rel = "") {
|
|
|
20693
21338
|
async function copySafeSkillTree(source, target, sourceRoot) {
|
|
20694
21339
|
await walkSafeSkillTree(source, sourceRoot, {
|
|
20695
21340
|
directory: async (rel) => {
|
|
20696
|
-
await mkdir5(
|
|
21341
|
+
await mkdir5(resolve14(target, rel), { mode: 448 });
|
|
20697
21342
|
},
|
|
20698
21343
|
file: async (rel, realPath) => {
|
|
20699
|
-
const destination =
|
|
21344
|
+
const destination = resolve14(target, rel);
|
|
20700
21345
|
await copyFile(realPath, destination);
|
|
20701
21346
|
await chmod2(destination, 384);
|
|
20702
21347
|
}
|
|
20703
21348
|
});
|
|
20704
21349
|
}
|
|
20705
21350
|
async function writeIsolatedHarnessFile(path, content) {
|
|
20706
|
-
const parent =
|
|
21351
|
+
const parent = dirname7(path);
|
|
20707
21352
|
const parentStats = await lstat(parent);
|
|
20708
21353
|
if (!parentStats.isDirectory() || parentStats.isSymbolicLink()) {
|
|
20709
21354
|
throw new Error(`isolated harness parent is not a real directory: ${parent}`);
|
|
20710
21355
|
}
|
|
20711
|
-
const temporary =
|
|
21356
|
+
const temporary = resolve14(parent, `.${basename4(path)}.${process.pid}.${randomUUID4()}.tmp`);
|
|
20712
21357
|
try {
|
|
20713
21358
|
await writeFile6(temporary, content, { mode: 384, flag: "wx" });
|
|
20714
21359
|
await chmod2(temporary, 384);
|
|
@@ -20718,18 +21363,18 @@ async function writeIsolatedHarnessFile(path, content) {
|
|
|
20718
21363
|
}
|
|
20719
21364
|
}
|
|
20720
21365
|
function roomAgentHomeEnv(root) {
|
|
20721
|
-
const resolved =
|
|
21366
|
+
const resolved = resolve14(root);
|
|
20722
21367
|
return {
|
|
20723
|
-
HOME:
|
|
20724
|
-
CLAUDE_CONFIG_DIR:
|
|
20725
|
-
CODEX_HOME:
|
|
20726
|
-
GOOSE_PATH_ROOT:
|
|
20727
|
-
GROK_HOME:
|
|
20728
|
-
CURSOR_HOME:
|
|
20729
|
-
PI_CODING_AGENT_DIR:
|
|
20730
|
-
XDG_STATE_HOME:
|
|
20731
|
-
XDG_CACHE_HOME:
|
|
20732
|
-
TMPDIR:
|
|
21368
|
+
HOME: resolve14(resolved, "user"),
|
|
21369
|
+
CLAUDE_CONFIG_DIR: resolve14(resolved, "claude"),
|
|
21370
|
+
CODEX_HOME: resolve14(resolved, "codex"),
|
|
21371
|
+
GOOSE_PATH_ROOT: resolve14(resolved, "goose"),
|
|
21372
|
+
GROK_HOME: resolve14(resolved, "grok"),
|
|
21373
|
+
CURSOR_HOME: resolve14(resolved, "cursor"),
|
|
21374
|
+
PI_CODING_AGENT_DIR: resolve14(resolved, "pi"),
|
|
21375
|
+
XDG_STATE_HOME: resolve14(resolved, "state"),
|
|
21376
|
+
XDG_CACHE_HOME: resolve14(resolved, "cache"),
|
|
21377
|
+
TMPDIR: resolve14(resolved, "tmp")
|
|
20733
21378
|
};
|
|
20734
21379
|
}
|
|
20735
21380
|
var HARNESS_STATE_ENV_VARS = [
|
|
@@ -20748,22 +21393,22 @@ function harnessStateDirsFromEnv(env) {
|
|
|
20748
21393
|
for (const name of HARNESS_STATE_ENV_VARS) {
|
|
20749
21394
|
const value = env[name];
|
|
20750
21395
|
if (value)
|
|
20751
|
-
stateDirs.push(
|
|
21396
|
+
stateDirs.push(resolve14(value));
|
|
20752
21397
|
}
|
|
20753
21398
|
const tmp = env.TMPDIR;
|
|
20754
|
-
return { stateDirs, ...tmp ? { tmpDir:
|
|
21399
|
+
return { stateDirs, ...tmp ? { tmpDir: resolve14(tmp) } : {} };
|
|
20755
21400
|
}
|
|
20756
21401
|
|
|
20757
21402
|
// apps/body/dist/attachment-delivery.js
|
|
20758
21403
|
import { mkdir as mkdir6, writeFile as writeFile7 } from "node:fs/promises";
|
|
20759
|
-
import { basename as
|
|
21404
|
+
import { basename as basename5, extname, join as join7 } from "node:path";
|
|
20760
21405
|
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
20761
21406
|
var MEDIA_TTL_HOURS = 24;
|
|
20762
21407
|
var EXPIRED_REASON = `expired: attachments are kept for ${MEDIA_TTL_HOURS} hours and these bytes are past that window`;
|
|
20763
21408
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
20764
21409
|
var MAX_INLINE_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
20765
21410
|
function safeFileName(attachment, index, taken) {
|
|
20766
|
-
const raw =
|
|
21411
|
+
const raw = basename5(attachment.name ?? "").replace(/[^\w.-]+/g, "_").replace(/^\.+/, "");
|
|
20767
21412
|
let name = raw || `attachment-${index + 1}`;
|
|
20768
21413
|
if (taken.has(name)) {
|
|
20769
21414
|
const ext = extname(name);
|
|
@@ -20800,7 +21445,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
|
|
|
20800
21445
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
20801
21446
|
if (bytes.length > MAX_ATTACHMENT_BYTES)
|
|
20802
21447
|
return tooLarge(bytes.length);
|
|
20803
|
-
const path =
|
|
21448
|
+
const path = join7(dir, safeFileName(attachment, index, taken));
|
|
20804
21449
|
await writeFile7(path, bytes);
|
|
20805
21450
|
const mimeType = attachment.mimeType ?? response.headers.get("content-type") ?? "";
|
|
20806
21451
|
if (!mimeType.startsWith("image/"))
|
|
@@ -21191,7 +21836,7 @@ function sessionConfigFingerprint(input) {
|
|
|
21191
21836
|
|
|
21192
21837
|
// apps/body/dist/pi-mcp-bridge.js
|
|
21193
21838
|
import { mkdir as mkdir7 } from "node:fs/promises";
|
|
21194
|
-
import { resolve as
|
|
21839
|
+
import { resolve as resolve15 } from "node:path";
|
|
21195
21840
|
var PI_MCP_BRIDGE_FILENAME = "beeline-mcp-bridge.js";
|
|
21196
21841
|
function harnessMountsSessionMcpServers(agentCommand) {
|
|
21197
21842
|
return !(agentCommand && /(^|[/\\])pi-acp(\.[a-z]+)?$/i.test(agentCommand));
|
|
@@ -21211,8 +21856,8 @@ async function installPiMcpBridge(input) {
|
|
|
21211
21856
|
return void 0;
|
|
21212
21857
|
if (!input.piHome || !input.servers.length)
|
|
21213
21858
|
return void 0;
|
|
21214
|
-
const directory =
|
|
21215
|
-
const path =
|
|
21859
|
+
const directory = resolve15(input.piHome, "extensions");
|
|
21860
|
+
const path = resolve15(directory, PI_MCP_BRIDGE_FILENAME);
|
|
21216
21861
|
try {
|
|
21217
21862
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
21218
21863
|
await writeIsolatedHarnessFile(path, piMcpBridgeSource(input.servers));
|
|
@@ -21412,7 +22057,7 @@ async function contains(git, head, ancestor) {
|
|
|
21412
22057
|
}
|
|
21413
22058
|
|
|
21414
22059
|
// apps/body/dist/room-session.js
|
|
21415
|
-
import { resolve as
|
|
22060
|
+
import { resolve as resolve16 } from "node:path";
|
|
21416
22061
|
|
|
21417
22062
|
// apps/body/dist/read-only-policy.js
|
|
21418
22063
|
var READ_ONLY_MCP_SERVER_NAME = "beeline-readonly-mcp";
|
|
@@ -21637,21 +22282,21 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
|
|
|
21637
22282
|
command: config.readonlyMcpCommand,
|
|
21638
22283
|
args: [...config.readonlyMcpArgs ?? []],
|
|
21639
22284
|
env: [
|
|
21640
|
-
{ name: "BEELINE_READONLY_ROOT", value:
|
|
22285
|
+
{ name: "BEELINE_READONLY_ROOT", value: resolve16(cwd) },
|
|
21641
22286
|
...config.agentHomeRoot ? [
|
|
21642
22287
|
{
|
|
21643
22288
|
name: "BEELINE_READONLY_AGENT_SKILLS_ROOT",
|
|
21644
|
-
value:
|
|
22289
|
+
value: resolve16(config.agentHomeRoot, skillDir, "skills")
|
|
21645
22290
|
}
|
|
21646
22291
|
] : [],
|
|
21647
|
-
...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value:
|
|
22292
|
+
...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value: resolve16(agentMemoryDir) }] : []
|
|
21648
22293
|
]
|
|
21649
22294
|
};
|
|
21650
22295
|
}
|
|
21651
22296
|
|
|
21652
22297
|
// apps/body/dist/pi-turn-record.js
|
|
21653
22298
|
import { readdir as readdir3, readFile as readFile7 } from "node:fs/promises";
|
|
21654
|
-
import { resolve as
|
|
22299
|
+
import { resolve as resolve17 } from "node:path";
|
|
21655
22300
|
function summarizeProviderError(errorMessage2) {
|
|
21656
22301
|
const trimmed = errorMessage2.trim();
|
|
21657
22302
|
const statusMatch = /^(\d{3}):\s*([\s\S]*)$/.exec(trimmed);
|
|
@@ -21672,7 +22317,7 @@ function summarizeProviderError(errorMessage2) {
|
|
|
21672
22317
|
}
|
|
21673
22318
|
async function sessionFileFromMap(home, sessionId) {
|
|
21674
22319
|
try {
|
|
21675
|
-
const raw = await readFile7(
|
|
22320
|
+
const raw = await readFile7(resolve17(home, ".pi", "pi-acp", "session-map.json"), "utf8");
|
|
21676
22321
|
const map = JSON.parse(raw);
|
|
21677
22322
|
const file = map.sessions?.[sessionId]?.sessionFile;
|
|
21678
22323
|
return typeof file === "string" && file ? file : void 0;
|
|
@@ -21681,7 +22326,7 @@ async function sessionFileFromMap(home, sessionId) {
|
|
|
21681
22326
|
}
|
|
21682
22327
|
}
|
|
21683
22328
|
async function sessionFileFromLayout(piDir, sessionId) {
|
|
21684
|
-
const sessionsRoot =
|
|
22329
|
+
const sessionsRoot = resolve17(piDir, "sessions");
|
|
21685
22330
|
const suffix = `_${sessionId}.jsonl`;
|
|
21686
22331
|
let projects;
|
|
21687
22332
|
try {
|
|
@@ -21690,7 +22335,7 @@ async function sessionFileFromLayout(piDir, sessionId) {
|
|
|
21690
22335
|
return void 0;
|
|
21691
22336
|
}
|
|
21692
22337
|
for (const project of projects) {
|
|
21693
|
-
const dir =
|
|
22338
|
+
const dir = resolve17(sessionsRoot, project);
|
|
21694
22339
|
let files;
|
|
21695
22340
|
try {
|
|
21696
22341
|
files = await readdir3(dir);
|
|
@@ -21699,7 +22344,7 @@ async function sessionFileFromLayout(piDir, sessionId) {
|
|
|
21699
22344
|
}
|
|
21700
22345
|
const match = files.find((file) => file.endsWith(suffix));
|
|
21701
22346
|
if (match)
|
|
21702
|
-
return
|
|
22347
|
+
return resolve17(dir, match);
|
|
21703
22348
|
}
|
|
21704
22349
|
return void 0;
|
|
21705
22350
|
}
|
|
@@ -21874,7 +22519,7 @@ async function withTurnReceiptHeartbeat(api, receipt, task, onHeartbeatError) {
|
|
|
21874
22519
|
|
|
21875
22520
|
// apps/body/dist/turn-trace.js
|
|
21876
22521
|
import { appendFile, mkdir as mkdir8, readdir as readdir4, rm as rm3 } from "node:fs/promises";
|
|
21877
|
-
import { resolve as
|
|
22522
|
+
import { resolve as resolve18 } from "node:path";
|
|
21878
22523
|
import { performance as performance2 } from "node:perf_hooks";
|
|
21879
22524
|
var TURN_PHASES = [
|
|
21880
22525
|
/** Enqueued on `SessionScheduler` until this turn holds a slot. A capacity wait lives here. */
|
|
@@ -22104,7 +22749,7 @@ function formatTurnTraceLine(record3) {
|
|
|
22104
22749
|
return [head, ...record3.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
|
|
22105
22750
|
}
|
|
22106
22751
|
function turnTraceDirectory(runtimeDir) {
|
|
22107
|
-
return
|
|
22752
|
+
return resolve18(runtimeDir, "turn-traces");
|
|
22108
22753
|
}
|
|
22109
22754
|
var TURN_TRACE_RETENTION_DAYS = 7;
|
|
22110
22755
|
function traceFileName(date) {
|
|
@@ -22121,7 +22766,7 @@ var TurnTraceFile = class {
|
|
|
22121
22766
|
this.options = options;
|
|
22122
22767
|
}
|
|
22123
22768
|
path(now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))()) {
|
|
22124
|
-
return
|
|
22769
|
+
return resolve18(this.directory, traceFileName(now2));
|
|
22125
22770
|
}
|
|
22126
22771
|
write(record3) {
|
|
22127
22772
|
this.tail = this.tail.catch(() => void 0).then(async () => {
|
|
@@ -22145,15 +22790,15 @@ var TurnTraceFile = class {
|
|
|
22145
22790
|
this.prunedDay = day;
|
|
22146
22791
|
const cutoff = traceFileName(new Date(now2.getTime() - TURN_TRACE_RETENTION_DAYS * 864e5));
|
|
22147
22792
|
const names = await readdir4(this.directory).catch(() => []);
|
|
22148
|
-
await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm3(
|
|
22793
|
+
await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm3(resolve18(this.directory, name), { force: true })));
|
|
22149
22794
|
}
|
|
22150
22795
|
};
|
|
22151
22796
|
|
|
22152
22797
|
// apps/body/dist/corner-github-auth.js
|
|
22153
22798
|
import { chmod as chmod3, mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
|
|
22154
|
-
import { delimiter as delimiter2, resolve as
|
|
22799
|
+
import { delimiter as delimiter2, resolve as resolve19 } from "node:path";
|
|
22155
22800
|
async function installCornerGitHubWrappers(input) {
|
|
22156
|
-
const bin =
|
|
22801
|
+
const bin = resolve19(input.root, "beeline-github-bin");
|
|
22157
22802
|
await mkdir9(bin, { recursive: true, mode: 448 });
|
|
22158
22803
|
const common = {
|
|
22159
22804
|
node: process.execPath,
|
|
@@ -22163,13 +22808,13 @@ async function installCornerGitHubWrappers(input) {
|
|
|
22163
22808
|
featureBranch: input.featureBranch,
|
|
22164
22809
|
targetBranch: input.targetBranch
|
|
22165
22810
|
};
|
|
22166
|
-
await writeLauncher(
|
|
22811
|
+
await writeLauncher(resolve19(bin, "git"), {
|
|
22167
22812
|
...common,
|
|
22168
22813
|
command: input.gitBinary,
|
|
22169
22814
|
launcher: "git"
|
|
22170
22815
|
});
|
|
22171
22816
|
if (input.ghBinary)
|
|
22172
|
-
await writeLauncher(
|
|
22817
|
+
await writeLauncher(resolve19(bin, "gh"), {
|
|
22173
22818
|
...common,
|
|
22174
22819
|
command: input.ghBinary,
|
|
22175
22820
|
launcher: "gh"
|
|
@@ -22304,25 +22949,25 @@ process.exit(result.status ?? 1);
|
|
|
22304
22949
|
}
|
|
22305
22950
|
|
|
22306
22951
|
// apps/body/dist/warm-node-modules.js
|
|
22307
|
-
import { createHash as
|
|
22952
|
+
import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
|
|
22308
22953
|
import { constants as fsConstants } from "node:fs";
|
|
22309
22954
|
import { chmod as chmod4, copyFile as copyFile2, link, lstat as lstat2, mkdir as mkdir10, readdir as readdir5, readFile as readFile8, readlink, rename as rename3, rm as rm4, stat as stat2, symlink as symlink2, utimes } from "node:fs/promises";
|
|
22310
|
-
import { dirname as
|
|
22955
|
+
import { dirname as dirname8, join as join8, resolve as resolve20 } from "node:path";
|
|
22311
22956
|
var STORE_FORMAT = "v1";
|
|
22312
22957
|
var STAGING_PREFIX = ".beeline-warm-";
|
|
22313
22958
|
var STAGING_SWEEP_MS = 24 * 60 * 60 * 1e3;
|
|
22314
22959
|
var WARM_STORE_MAX_ENTRIES = 3;
|
|
22315
22960
|
function sharedNpmCacheDir(supervisorRoot) {
|
|
22316
|
-
return
|
|
22961
|
+
return resolve20(supervisorRoot, "beeline", "npm-cache");
|
|
22317
22962
|
}
|
|
22318
22963
|
function warmNodeModulesStoreDir(supervisorRoot) {
|
|
22319
|
-
return
|
|
22964
|
+
return resolve20(supervisorRoot, "beeline", "node-modules");
|
|
22320
22965
|
}
|
|
22321
22966
|
function isPlanRefusal(value) {
|
|
22322
22967
|
return "failure" in value;
|
|
22323
22968
|
}
|
|
22324
22969
|
async function readWarmPlan(worktreePath) {
|
|
22325
|
-
const lockfile = await readFile8(
|
|
22970
|
+
const lockfile = await readFile8(resolve20(worktreePath, "package-lock.json")).catch(() => void 0);
|
|
22326
22971
|
if (!lockfile)
|
|
22327
22972
|
return { failure: "no-lockfile" };
|
|
22328
22973
|
const packages = parseLockfilePackages(lockfile);
|
|
@@ -22337,7 +22982,7 @@ async function readWarmPlan(worktreePath) {
|
|
|
22337
22982
|
const unsafe = trees.find((tree) => !isContainedTreePath(tree));
|
|
22338
22983
|
if (unsafe)
|
|
22339
22984
|
return { failure: "unsafe-lockfile", detail: unsafe };
|
|
22340
|
-
const key =
|
|
22985
|
+
const key = createHash5("sha256").update(`${STORE_FORMAT}\0${process.platform}\0${process.arch}\0${process.versions.modules}\0`).update(lockfile).digest("hex").slice(0, 32);
|
|
22341
22986
|
return { key, trees };
|
|
22342
22987
|
}
|
|
22343
22988
|
function nodeModulesTreePaths(packages) {
|
|
@@ -22361,11 +23006,11 @@ async function seedWarmNodeModules(input) {
|
|
|
22361
23006
|
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
22362
23007
|
}
|
|
22363
23008
|
for (const tree of plan.trees) {
|
|
22364
|
-
if (await pathExists(
|
|
23009
|
+
if (await pathExists(resolve20(input.worktreePath, tree))) {
|
|
22365
23010
|
return { reason: "present", key: plan.key };
|
|
22366
23011
|
}
|
|
22367
23012
|
}
|
|
22368
|
-
const entry =
|
|
23013
|
+
const entry = resolve20(input.storeRoot, plan.key);
|
|
22369
23014
|
if (!await isDirectory(entry))
|
|
22370
23015
|
return { reason: "cold", key: plan.key };
|
|
22371
23016
|
const [storeDevice, checkoutDevice] = await Promise.all([
|
|
@@ -22375,19 +23020,19 @@ async function seedWarmNodeModules(input) {
|
|
|
22375
23020
|
if (storeDevice === void 0 || storeDevice !== checkoutDevice) {
|
|
22376
23021
|
return { reason: "cross-device", key: plan.key };
|
|
22377
23022
|
}
|
|
22378
|
-
const staging =
|
|
23023
|
+
const staging = resolve20(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID5()}`);
|
|
22379
23024
|
const placed = [];
|
|
22380
23025
|
const now2 = (input.now ?? Date.now)();
|
|
22381
23026
|
try {
|
|
22382
23027
|
await sweepStaleStaging(input.storeRoot, now2);
|
|
22383
23028
|
await utimes(entry, now2 / 1e3, now2 / 1e3).catch(() => void 0);
|
|
22384
23029
|
for (const tree of plan.trees) {
|
|
22385
|
-
await cloneTree(
|
|
23030
|
+
await cloneTree(resolve20(entry, tree), resolve20(staging, tree), seedFile);
|
|
22386
23031
|
}
|
|
22387
23032
|
for (const tree of plan.trees) {
|
|
22388
|
-
const target =
|
|
22389
|
-
await mkdir10(
|
|
22390
|
-
await rename3(
|
|
23033
|
+
const target = resolve20(input.worktreePath, tree);
|
|
23034
|
+
await mkdir10(dirname8(target), { recursive: true });
|
|
23035
|
+
await rename3(resolve20(staging, tree), target);
|
|
22391
23036
|
placed.push(target);
|
|
22392
23037
|
}
|
|
22393
23038
|
return { reason: "seeded", key: plan.key };
|
|
@@ -22405,11 +23050,11 @@ async function harvestWarmNodeModules(input) {
|
|
|
22405
23050
|
if (isPlanRefusal(plan)) {
|
|
22406
23051
|
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
22407
23052
|
}
|
|
22408
|
-
const entry =
|
|
23053
|
+
const entry = resolve20(input.storeRoot, plan.key);
|
|
22409
23054
|
if (await pathExists(entry))
|
|
22410
23055
|
return { reason: "already-warm", key: plan.key };
|
|
22411
23056
|
for (const tree of plan.trees) {
|
|
22412
|
-
if (!await isDirectory(
|
|
23057
|
+
if (!await isDirectory(resolve20(input.worktreePath, tree))) {
|
|
22413
23058
|
return { reason: "no-node-modules", key: plan.key, detail: tree };
|
|
22414
23059
|
}
|
|
22415
23060
|
}
|
|
@@ -22421,12 +23066,12 @@ async function harvestWarmNodeModules(input) {
|
|
|
22421
23066
|
detail: `${missing.length} absent, e.g. ${missing.slice(0, 3).join(", ")}`
|
|
22422
23067
|
};
|
|
22423
23068
|
}
|
|
22424
|
-
const staging =
|
|
23069
|
+
const staging = resolve20(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID5()}`);
|
|
22425
23070
|
try {
|
|
22426
23071
|
await mkdir10(input.storeRoot, { recursive: true, mode: 493 });
|
|
22427
23072
|
await sweepStaleStaging(input.storeRoot, (input.now ?? Date.now)());
|
|
22428
23073
|
for (const tree of plan.trees) {
|
|
22429
|
-
await cloneTree(
|
|
23074
|
+
await cloneTree(resolve20(input.worktreePath, tree), resolve20(staging, tree), harvestFile);
|
|
22430
23075
|
}
|
|
22431
23076
|
await input.onCopied?.();
|
|
22432
23077
|
if (!await stagedTreeIsPublishable(input.worktreePath, staging, plan.key)) {
|
|
@@ -22447,18 +23092,18 @@ async function stagedTreeIsPublishable(worktreePath, staging, key) {
|
|
|
22447
23092
|
const settled = await readWarmPlan(worktreePath);
|
|
22448
23093
|
if (isPlanRefusal(settled) || settled.key !== key)
|
|
22449
23094
|
return false;
|
|
22450
|
-
const hidden =
|
|
23095
|
+
const hidden = join8("node_modules", ".package-lock.json");
|
|
22451
23096
|
const [copied, current] = await Promise.all([
|
|
22452
|
-
readFile8(
|
|
22453
|
-
readFile8(
|
|
23097
|
+
readFile8(resolve20(staging, hidden)).catch(() => void 0),
|
|
23098
|
+
readFile8(resolve20(worktreePath, hidden)).catch(() => void 0)
|
|
22454
23099
|
]);
|
|
22455
23100
|
if (!copied || !current || !copied.equals(current))
|
|
22456
23101
|
return false;
|
|
22457
23102
|
return (await missingInstalledPackages(worktreePath, staging)).length === 0;
|
|
22458
23103
|
}
|
|
22459
23104
|
async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
22460
|
-
const wanted = parseLockfilePackages(await readFile8(
|
|
22461
|
-
const installed = parseLockfilePackages(await readFile8(
|
|
23105
|
+
const wanted = parseLockfilePackages(await readFile8(resolve20(worktreePath, "package-lock.json")).catch(() => void 0));
|
|
23106
|
+
const installed = parseLockfilePackages(await readFile8(resolve20(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
|
|
22462
23107
|
if (!wanted)
|
|
22463
23108
|
return ["package-lock.json is unreadable"];
|
|
22464
23109
|
if (!installed)
|
|
@@ -22478,14 +23123,14 @@ async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
|
22478
23123
|
required.push(path);
|
|
22479
23124
|
}
|
|
22480
23125
|
await mapWithLimit(required, INTEGRITY_READ_CONCURRENCY, async (path) => {
|
|
22481
|
-
if (!(path in installed) || !await isInstalledPackage(
|
|
23126
|
+
if (!(path in installed) || !await isInstalledPackage(resolve20(treeRoot, path))) {
|
|
22482
23127
|
missing.push(path);
|
|
22483
23128
|
}
|
|
22484
23129
|
});
|
|
22485
23130
|
return missing.sort();
|
|
22486
23131
|
}
|
|
22487
23132
|
async function isInstalledPackage(path) {
|
|
22488
|
-
return stat2(
|
|
23133
|
+
return stat2(join8(path, "package.json")).then((info) => info.isFile(), () => false);
|
|
22489
23134
|
}
|
|
22490
23135
|
var INTEGRITY_READ_CONCURRENCY = 64;
|
|
22491
23136
|
async function mapWithLimit(values, limit, visit) {
|
|
@@ -22519,8 +23164,8 @@ function isContainedTreePath(value) {
|
|
|
22519
23164
|
async function cloneTree(source, target, file, topLevel = true) {
|
|
22520
23165
|
await mkdir10(target, { recursive: true, mode: 493 });
|
|
22521
23166
|
for (const entry of await readdir5(source, { withFileTypes: true })) {
|
|
22522
|
-
const from =
|
|
22523
|
-
const to =
|
|
23167
|
+
const from = join8(source, entry.name);
|
|
23168
|
+
const to = join8(target, entry.name);
|
|
22524
23169
|
if (entry.isSymbolicLink()) {
|
|
22525
23170
|
await symlink2(await readlink(from), to);
|
|
22526
23171
|
continue;
|
|
@@ -22550,13 +23195,13 @@ async function pruneWarmStore(storeRoot, keep) {
|
|
|
22550
23195
|
const names = (await readdir5(storeRoot).catch(() => [])).filter((name) => !name.startsWith(STAGING_PREFIX));
|
|
22551
23196
|
const entries = [];
|
|
22552
23197
|
for (const name of names) {
|
|
22553
|
-
const info = await lstat2(
|
|
23198
|
+
const info = await lstat2(join8(storeRoot, name)).catch(() => void 0);
|
|
22554
23199
|
if (info?.isDirectory())
|
|
22555
23200
|
entries.push({ name, usedAt: info.mtimeMs });
|
|
22556
23201
|
}
|
|
22557
23202
|
const dropped = entries.sort((a2, b) => b.usedAt - a2.usedAt || a2.name.localeCompare(b.name)).slice(keep);
|
|
22558
23203
|
for (const entry of dropped) {
|
|
22559
|
-
await rm4(
|
|
23204
|
+
await rm4(join8(storeRoot, entry.name), { recursive: true, force: true }).catch(() => void 0);
|
|
22560
23205
|
}
|
|
22561
23206
|
return dropped.map((entry) => entry.name);
|
|
22562
23207
|
}
|
|
@@ -22565,7 +23210,7 @@ async function sweepStaleStaging(storeRoot, now2) {
|
|
|
22565
23210
|
for (const name of entries) {
|
|
22566
23211
|
if (!name.startsWith(STAGING_PREFIX))
|
|
22567
23212
|
continue;
|
|
22568
|
-
const path =
|
|
23213
|
+
const path = join8(storeRoot, name);
|
|
22569
23214
|
const info = await lstat2(path).catch(() => void 0);
|
|
22570
23215
|
if (!info || now2 - info.mtimeMs < STAGING_SWEEP_MS)
|
|
22571
23216
|
continue;
|
|
@@ -22587,8 +23232,8 @@ function describe3(error) {
|
|
|
22587
23232
|
|
|
22588
23233
|
// apps/body/dist/monolith-room-turn.js
|
|
22589
23234
|
import { mkdir as mkdir11 } from "node:fs/promises";
|
|
22590
|
-
import { homedir as
|
|
22591
|
-
import { join as
|
|
23235
|
+
import { homedir as homedir7 } from "node:os";
|
|
23236
|
+
import { join as join9 } from "node:path";
|
|
22592
23237
|
|
|
22593
23238
|
// packages/api-contract/dist/scheduled-prompts.js
|
|
22594
23239
|
var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
|
|
@@ -22620,9 +23265,23 @@ function inboxItemPromptBody(item) {
|
|
|
22620
23265
|
return isScheduledPrompt(item) ? item.systemEvent?.consequence ?? item.body : item.body;
|
|
22621
23266
|
}
|
|
22622
23267
|
function pendingGrantToolCall(call) {
|
|
22623
|
-
if (!/(?:^|[._:/-])request_grant$/i.test(call.title ?? ""))
|
|
23268
|
+
if (!/(?:^|[._:/-])(?:request_grant|offer_connector)$/i.test(call.title ?? ""))
|
|
22624
23269
|
return false;
|
|
22625
|
-
return /pending, card posted/i.test(typeof call.content === "string" ? call.content : JSON.stringify(call.content ?? ""));
|
|
23270
|
+
return /(?:pending|already offered), card (?:posted|still open)/i.test(typeof call.content === "string" ? call.content : JSON.stringify(call.content ?? ""));
|
|
23271
|
+
}
|
|
23272
|
+
function resumePrompt(item) {
|
|
23273
|
+
if (item.systemEvent?.kind === "connector-offer-decided") {
|
|
23274
|
+
return [
|
|
23275
|
+
`This is the answer to your connector offer: ${item.body}.`,
|
|
23276
|
+
"Your paused work resumes now. The tool is being installed on your machine; its sign-in and status reach the person through the tool\u2019s own status message, not through you.",
|
|
23277
|
+
'Acknowledge in one short line (for example "Adding Trusty Squire now.") and continue the work that needed it, or say plainly what still has to happen before you can.'
|
|
23278
|
+
].join(" ");
|
|
23279
|
+
}
|
|
23280
|
+
return [
|
|
23281
|
+
"This is the answer to your grant request; your paused work resumes now.",
|
|
23282
|
+
"If it was approved and it is a command grant, run it with run_granted_command and the exact argv.",
|
|
23283
|
+
"If it was declined, try another way or say plainly what you cannot do."
|
|
23284
|
+
].join(" ");
|
|
22626
23285
|
}
|
|
22627
23286
|
function roomMentionDirectory(roster, selfId) {
|
|
22628
23287
|
const rows = [];
|
|
@@ -22724,7 +23383,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
22724
23383
|
...this.options.config.bwrapPath ? { bwrapPath: this.options.config.bwrapPath } : {},
|
|
22725
23384
|
...this.sessionScratchDir ? { scratch: this.sessionScratchDir } : {},
|
|
22726
23385
|
...this.sessionStateDirs.length ? { harnessStateDirs: this.sessionStateDirs } : {},
|
|
22727
|
-
maskPaths: credentialMaskPaths(this.options.config.sandboxMaskPaths, this.options.config.operatorHome ??
|
|
23386
|
+
maskPaths: credentialMaskPaths(this.options.config.sandboxMaskPaths, this.options.config.operatorHome ?? homedir7())
|
|
22728
23387
|
};
|
|
22729
23388
|
}
|
|
22730
23389
|
/**
|
|
@@ -22791,7 +23450,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
22791
23450
|
const cached = this.deliveredAttachments.get(item.id);
|
|
22792
23451
|
if (cached)
|
|
22793
23452
|
return cached;
|
|
22794
|
-
const delivered = await deliverAttachments(item.attachments,
|
|
23453
|
+
const delivered = await deliverAttachments(item.attachments, join9(this.attachmentDir, item.id.replace(/[^\w-]/g, "_")), this.options.fetchImpl);
|
|
22795
23454
|
this.deliveredAttachments.set(item.id, withoutImageData(delivered));
|
|
22796
23455
|
return delivered;
|
|
22797
23456
|
}
|
|
@@ -22878,6 +23537,10 @@ var MonolithRoomTurnLoop = class {
|
|
|
22878
23537
|
})
|
|
22879
23538
|
}) : {};
|
|
22880
23539
|
const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
|
|
23540
|
+
const harnessLabel = harnessIdentityLabel({
|
|
23541
|
+
kind: this.options.config.agentKind,
|
|
23542
|
+
command
|
|
23543
|
+
});
|
|
22881
23544
|
const agentEnv = { ...this.options.config.agentEnv, ...homeOverlay };
|
|
22882
23545
|
this.agentEnv = agentEnv;
|
|
22883
23546
|
const agentArgs = agentArgsWithModelSelection({
|
|
@@ -22885,12 +23548,12 @@ var MonolithRoomTurnLoop = class {
|
|
|
22885
23548
|
command,
|
|
22886
23549
|
args: this.options.config.agentArgs ?? []
|
|
22887
23550
|
}, selection);
|
|
22888
|
-
const operatorHome = this.options.config.operatorHome ??
|
|
23551
|
+
const operatorHome = this.options.config.operatorHome ?? homedir7();
|
|
22889
23552
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
22890
|
-
this.attachmentDir = tmpDir ?
|
|
23553
|
+
this.attachmentDir = tmpDir ? join9(tmpDir, "beeline-attachments") : void 0;
|
|
22891
23554
|
this.sessionScratchDir = tmpDir;
|
|
22892
23555
|
this.sessionStateDirs = stateDirs;
|
|
22893
|
-
const homeStateDirs = harnessHomeStateDirs(
|
|
23556
|
+
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
22894
23557
|
await Promise.all(homeStateDirs.map((dir) => mkdir11(dir, { recursive: true })));
|
|
22895
23558
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
22896
23559
|
if (attachScratchRoot)
|
|
@@ -22926,7 +23589,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
22926
23589
|
})
|
|
22927
23590
|
];
|
|
22928
23591
|
await installPiMcpBridge({
|
|
22929
|
-
agentCommand:
|
|
23592
|
+
agentCommand: harnessLabel,
|
|
22930
23593
|
piHome: agentEnv.PI_CODING_AGENT_DIR,
|
|
22931
23594
|
servers
|
|
22932
23595
|
});
|
|
@@ -22936,7 +23599,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
22936
23599
|
agentArgs: spawnCommand.args,
|
|
22937
23600
|
agentEnv,
|
|
22938
23601
|
agentCwd: this.options.cwd,
|
|
22939
|
-
agentLabel:
|
|
23602
|
+
agentLabel: harnessLabel,
|
|
22940
23603
|
// `bwrapPath` is set only when `detectBwrapSandbox` passed its self-test
|
|
22941
23604
|
// (`config.ts`), which is exactly when `wrapAgentCommand` above wraps.
|
|
22942
23605
|
osSandbox: Boolean(this.options.config.bwrapPath),
|
|
@@ -23012,7 +23675,10 @@ var MonolithRoomTurnLoop = class {
|
|
|
23012
23675
|
if (durableReplyText(result.agentText))
|
|
23013
23676
|
return void 0;
|
|
23014
23677
|
return explainEmptyAgentTurn({
|
|
23015
|
-
agentLabel:
|
|
23678
|
+
agentLabel: harnessIdentityLabel({
|
|
23679
|
+
kind: this.options.config.agentKind,
|
|
23680
|
+
command: this.options.config.agentCommand ?? this.options.config.agentBinary
|
|
23681
|
+
}),
|
|
23016
23682
|
agentEnv: this.agentEnv,
|
|
23017
23683
|
sessionId: this.sessionId,
|
|
23018
23684
|
result
|
|
@@ -23134,11 +23800,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
23134
23800
|
const buildPrompt = () => [
|
|
23135
23801
|
this.turnInstructionPrefix,
|
|
23136
23802
|
WarmTranscript.render(this.warmTranscript.select(this.sessionId, transcriptRows), "Room conversation so far:", "New in the Room since your last turn (the earlier conversation is already in this session):"),
|
|
23137
|
-
grantDecision ?
|
|
23138
|
-
"This is the answer to your grant request; your paused work resumes now.",
|
|
23139
|
-
"If it was approved and it is a command grant, run it with run_granted_command and the exact argv.",
|
|
23140
|
-
"If it was declined, try another way or say plainly what you cannot do."
|
|
23141
|
-
].join(" ") : "",
|
|
23803
|
+
grantDecision ? resumePrompt(item) : "",
|
|
23142
23804
|
roomMentionDirectory(roster, this.agent.publicKey),
|
|
23143
23805
|
(corners.corners ?? []).some((corner) => !corner.archived) ? `Current corners you belong to (use the exact cornerId with steer_corner):
|
|
23144
23806
|
${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}` : "",
|
|
@@ -23224,7 +23886,7 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
23224
23886
|
this.pausedOnGrantRequestId = item.id;
|
|
23225
23887
|
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id} paused on a grant card`);
|
|
23226
23888
|
} else if (resumedRequestId) {
|
|
23227
|
-
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${resumedRequestId} resumed by
|
|
23889
|
+
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${resumedRequestId} resumed by card decision ${item.id}`);
|
|
23228
23890
|
}
|
|
23229
23891
|
if (openCornerCall) {
|
|
23230
23892
|
console.log(`[thin-core] monolith Room ${this.options.roomId} tool call: ${openCornerCall.title} (${openCornerCall.status ?? "no status"})`);
|
|
@@ -23328,7 +23990,7 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
23328
23990
|
const item = {
|
|
23329
23991
|
...command.source,
|
|
23330
23992
|
id: command.turnRequestId,
|
|
23331
|
-
body: command.action === "resume" ? `Resume the paused turn. The server supplied this
|
|
23993
|
+
body: command.action === "resume" ? `Resume the paused turn. The server supplied this answer: ${command.source.body}` : command.source.body
|
|
23332
23994
|
};
|
|
23333
23995
|
this.startPrompt(item);
|
|
23334
23996
|
await this.activeTurn?.promise;
|
|
@@ -23507,7 +24169,7 @@ function toolArguments(call) {
|
|
|
23507
24169
|
return input ? { input: clampBytes(redactToolDetail(input), TOOL_ARGUMENT_MAX_BYTES) } : {};
|
|
23508
24170
|
}
|
|
23509
24171
|
function toolCallKey(call, index) {
|
|
23510
|
-
return call.id ? `id-${
|
|
24172
|
+
return call.id ? `id-${createHash6("sha256").update(call.id).digest("hex")}` : `tool-${index}`;
|
|
23511
24173
|
}
|
|
23512
24174
|
function toolCallSettled(call) {
|
|
23513
24175
|
if (call.resultReceived)
|
|
@@ -23757,6 +24419,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
23757
24419
|
})
|
|
23758
24420
|
}) : {};
|
|
23759
24421
|
const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
|
|
24422
|
+
const harnessLabel = harnessIdentityLabel({
|
|
24423
|
+
kind: this.options.config.agentKind,
|
|
24424
|
+
command
|
|
24425
|
+
});
|
|
23760
24426
|
const repository = this.options.repository;
|
|
23761
24427
|
let githubEnv = repository ? { GH_TOKEN: repository.githubToken, GITHUB_TOKEN: repository.githubToken } : {};
|
|
23762
24428
|
if (repository && this.options.config.runtimeConfigPath && this.options.config.agentHomeRoot) {
|
|
@@ -23788,11 +24454,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
23788
24454
|
command,
|
|
23789
24455
|
args: this.options.config.agentArgs ?? []
|
|
23790
24456
|
}, selection);
|
|
23791
|
-
const operatorHome = this.options.config.operatorHome ??
|
|
24457
|
+
const operatorHome = this.options.config.operatorHome ?? homedir8();
|
|
23792
24458
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
23793
|
-
this.attachmentDir = tmpDir ?
|
|
24459
|
+
this.attachmentDir = tmpDir ? join10(tmpDir, "beeline-attachments") : void 0;
|
|
23794
24460
|
this.sessionScratchDir = tmpDir;
|
|
23795
|
-
const homeStateDirs = harnessHomeStateDirs(
|
|
24461
|
+
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
23796
24462
|
await Promise.all(homeStateDirs.map((dir) => mkdir12(dir, { recursive: true })));
|
|
23797
24463
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
23798
24464
|
if (attachScratchRoot)
|
|
@@ -23825,7 +24491,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
23825
24491
|
agentArgs: spawnCommand.args,
|
|
23826
24492
|
agentEnv,
|
|
23827
24493
|
agentCwd: this.options.worktreePath,
|
|
23828
|
-
agentLabel:
|
|
24494
|
+
agentLabel: harnessLabel,
|
|
23829
24495
|
autoApprovePermissions: true,
|
|
23830
24496
|
permissionHandler: () => Promise.resolve("allow")
|
|
23831
24497
|
};
|
|
@@ -23865,7 +24531,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
23865
24531
|
})
|
|
23866
24532
|
];
|
|
23867
24533
|
await installPiMcpBridge({
|
|
23868
|
-
agentCommand:
|
|
24534
|
+
agentCommand: harnessLabel,
|
|
23869
24535
|
piHome: agentEnv.PI_CODING_AGENT_DIR,
|
|
23870
24536
|
servers
|
|
23871
24537
|
});
|
|
@@ -23892,7 +24558,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
23892
24558
|
selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
|
|
23893
24559
|
],
|
|
23894
24560
|
"Do not tag the user when a corner turn finishes: the server posts the merge summary card and its push already cover completion. Tag a human only mid-turn, and only when you need a decision or input.",
|
|
23895
|
-
"Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. Never merge while approvalPending is true. When approval is pending, wait for the reviewer to tag you. Never merge another pull request. Never create a schedule to poll pr_checks_status or the merge gate: the green transition wakes the reviewer and the reviewer's approval tag wakes you, and tagging any agent other than the configured reviewer cannot clear the gate. If a schedule wakes you in this corner anyway, follow the same rule as a checks turn: say nothing unless you merge, push a fix, or report a genuinely new blocker."
|
|
24561
|
+
"Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. When asked whether the reviewer was woken, call pr_checks_status and report reviewerWake; do not invent a cause. Never merge while approvalPending is true. When approval is pending, wait for the reviewer to tag you. Never merge another pull request. Never create a schedule to poll pr_checks_status or the merge gate: the green transition wakes the reviewer and the reviewer's approval tag wakes you, and tagging any agent other than the configured reviewer cannot clear the gate. If a schedule wakes you in this corner anyway, follow the same rule as a checks turn: say nothing unless you merge, push a fix, or report a genuinely new blocker."
|
|
23896
24562
|
] : [
|
|
23897
24563
|
"This is a chat-only corner with no repository or GitHub workflow.",
|
|
23898
24564
|
"Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then post_artifact with the path to send them back to the corner.",
|
|
@@ -23942,7 +24608,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
23942
24608
|
if (durableReplyText(result.agentText))
|
|
23943
24609
|
return void 0;
|
|
23944
24610
|
return explainEmptyAgentTurn({
|
|
23945
|
-
agentLabel:
|
|
24611
|
+
agentLabel: harnessIdentityLabel({
|
|
24612
|
+
kind: this.options.config.agentKind,
|
|
24613
|
+
command: this.options.config.agentCommand ?? this.options.config.agentBinary
|
|
24614
|
+
}),
|
|
23946
24615
|
agentEnv: this.agentEnv,
|
|
23947
24616
|
sessionId: this.sessionId,
|
|
23948
24617
|
result
|
|
@@ -24011,7 +24680,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
24011
24680
|
const [conversation, roster, delivered] = await trace.measure("context-fetch", () => Promise.all([
|
|
24012
24681
|
api.execute("getRoomConversation", { roomId: cornerId, limit: 200 }),
|
|
24013
24682
|
this.roster(),
|
|
24014
|
-
this.attachmentDir && attachments.length ? deliverAttachments(attachments,
|
|
24683
|
+
this.attachmentDir && attachments.length ? deliverAttachments(attachments, join10(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([])
|
|
24015
24684
|
]));
|
|
24016
24685
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
24017
24686
|
const requestedBy = requester && !requester.name && names.get(requester.pubkey) ? { ...requester, name: names.get(requester.pubkey) } : requester;
|
|
@@ -24778,13 +25447,13 @@ function shouldPostInitialCornerWorkingState(restore, isOpener = true) {
|
|
|
24778
25447
|
}
|
|
24779
25448
|
async function materializeCornerWorktree(input) {
|
|
24780
25449
|
const remote = roomCheckoutRemote(input.remote);
|
|
24781
|
-
const repositoryHash =
|
|
24782
|
-
const gitCommonDir =
|
|
24783
|
-
const path =
|
|
24784
|
-
await mkdir13(
|
|
24785
|
-
await mkdir13(
|
|
25450
|
+
const repositoryHash = createHash7("sha256").update(remote).digest("hex").slice(0, 24);
|
|
25451
|
+
const gitCommonDir = resolve21(input.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
|
|
25452
|
+
const path = resolve21(input.supervisorRoot, "beeline", "corners", input.cornerId);
|
|
25453
|
+
await mkdir13(dirname9(gitCommonDir), { recursive: true, mode: 448 });
|
|
25454
|
+
await mkdir13(dirname9(path), { recursive: true, mode: 448 });
|
|
24786
25455
|
const authEnv = githubGitEnv(input.token);
|
|
24787
|
-
if (!
|
|
25456
|
+
if (!existsSync5(resolve21(gitCommonDir, "HEAD"))) {
|
|
24788
25457
|
await execFileAsync4("git", ["clone", "--bare", remote, gitCommonDir], {
|
|
24789
25458
|
env: authEnv,
|
|
24790
25459
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -24803,7 +25472,7 @@ async function materializeCornerWorktree(input) {
|
|
|
24803
25472
|
"origin",
|
|
24804
25473
|
`+refs/heads/${input.featureBranch}:refs/remotes/origin/${input.featureBranch}`
|
|
24805
25474
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 }).then(() => true, () => false);
|
|
24806
|
-
if (!
|
|
25475
|
+
if (!existsSync5(resolve21(path, ".git"))) {
|
|
24807
25476
|
await rm5(path, { recursive: true, force: true });
|
|
24808
25477
|
await execFileAsync4("git", [
|
|
24809
25478
|
`--git-dir=${gitCommonDir}`,
|
|
@@ -24854,7 +25523,7 @@ async function materializeCornerWorktree(input) {
|
|
|
24854
25523
|
`${input.committer.publicKey.slice(0, 16)}@users.noreply.github.com`
|
|
24855
25524
|
]);
|
|
24856
25525
|
const top = await execFileAsync4("git", ["-C", path, "rev-parse", "--show-toplevel"]);
|
|
24857
|
-
if (
|
|
25526
|
+
if (resolve21(top.stdout.trim()) !== resolve21(path)) {
|
|
24858
25527
|
throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
|
|
24859
25528
|
}
|
|
24860
25529
|
return { path, gitCommonDir };
|
|
@@ -24875,7 +25544,7 @@ async function mapWithConcurrency(values, limit, visit) {
|
|
|
24875
25544
|
async function removeCornerWorktreeAndBranches(worktree) {
|
|
24876
25545
|
const localRef = `refs/heads/${worktree.branch}`;
|
|
24877
25546
|
const remoteRef = `refs/heads/${worktree.branch}`;
|
|
24878
|
-
const worktreeExists =
|
|
25547
|
+
const worktreeExists = existsSync5(worktree.path);
|
|
24879
25548
|
const localExists = await gitRefExists(worktree.gitCommonDir, localRef);
|
|
24880
25549
|
if (worktreeExists) {
|
|
24881
25550
|
const checkedOut = await execFileAsync4("git", [
|
|
@@ -24920,8 +25589,8 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
24920
25589
|
}
|
|
24921
25590
|
var execFileAsync4 = promisify4(execFile6);
|
|
24922
25591
|
async function removeCornerScratchWorkspace(input) {
|
|
24923
|
-
const expected =
|
|
24924
|
-
if (
|
|
25592
|
+
const expected = resolve21(input.roomRoot, "scratch");
|
|
25593
|
+
if (resolve21(input.scratchPath) !== expected) {
|
|
24925
25594
|
throw new Error(`refusing to remove scratch outside corner ${input.cornerId}`);
|
|
24926
25595
|
}
|
|
24927
25596
|
await rm5(expected, { recursive: true, force: true });
|
|
@@ -25135,14 +25804,14 @@ var RoomRuntimeCoordinator = class {
|
|
|
25135
25804
|
return this.runtime.rooms.find((room) => room.channelId === roomId);
|
|
25136
25805
|
}
|
|
25137
25806
|
roomRoot(roomId) {
|
|
25138
|
-
return this.roomRecord(roomId)?.root ??
|
|
25807
|
+
return this.roomRecord(roomId)?.root ?? resolve21(dirname9(this.configPath), "rooms", roomId);
|
|
25139
25808
|
}
|
|
25140
25809
|
roomAgentHomeRoot(workspaceRoot, required = false) {
|
|
25141
25810
|
const flag = process.env.BUZZY_BODY_ROOM_HOME;
|
|
25142
25811
|
if (!required && flag === "0")
|
|
25143
25812
|
return void 0;
|
|
25144
|
-
const home =
|
|
25145
|
-
if (!required && flag !== "1" && !
|
|
25813
|
+
const home = resolve21(workspaceRoot, "agent-home");
|
|
25814
|
+
if (!required && flag !== "1" && !existsSync5(home) && existsSync5(workspaceRoot))
|
|
25146
25815
|
return void 0;
|
|
25147
25816
|
try {
|
|
25148
25817
|
mkdirSync(home, { recursive: true, mode: 448 });
|
|
@@ -25157,10 +25826,10 @@ var RoomRuntimeCoordinator = class {
|
|
|
25157
25826
|
return {
|
|
25158
25827
|
...this.baseConfig,
|
|
25159
25828
|
workspaceRoot,
|
|
25160
|
-
agentPrivateRoot:
|
|
25161
|
-
agentMemoryRoot:
|
|
25162
|
-
openRouterRoutingCacheDir: openRouterRoutingCacheDir(
|
|
25163
|
-
turnTraceDir: turnTraceDirectory(
|
|
25829
|
+
agentPrivateRoot: resolve21(workspaceRoot, "agent-private"),
|
|
25830
|
+
agentMemoryRoot: resolve21(dirname9(this.configPath), "memory"),
|
|
25831
|
+
openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname9(this.configPath)),
|
|
25832
|
+
turnTraceDir: turnTraceDirectory(dirname9(this.configPath)),
|
|
25164
25833
|
...agentHomeRoot ? { agentHomeRoot } : {}
|
|
25165
25834
|
};
|
|
25166
25835
|
}
|
|
@@ -25226,12 +25895,12 @@ var RoomRuntimeCoordinator = class {
|
|
|
25226
25895
|
return this.roomRoot(roomId);
|
|
25227
25896
|
const remote = roomCheckoutRemote(repository.remote);
|
|
25228
25897
|
const targetBranch = repository.targetBranch || "main";
|
|
25229
|
-
const checkoutId =
|
|
25230
|
-
const path =
|
|
25231
|
-
await mkdir13(
|
|
25898
|
+
const checkoutId = createHash7("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
25899
|
+
const path = resolve21(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
25900
|
+
await mkdir13(dirname9(path), { recursive: true, mode: 448 });
|
|
25232
25901
|
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
25233
25902
|
const env = token ? githubGitEnv(token.token) : process.env;
|
|
25234
|
-
if (!
|
|
25903
|
+
if (!existsSync5(resolve21(path, ".git"))) {
|
|
25235
25904
|
await execFileAsync4("git", ["clone", "--no-checkout", remote, path], {
|
|
25236
25905
|
env,
|
|
25237
25906
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -25284,7 +25953,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
25284
25953
|
featureBranch,
|
|
25285
25954
|
token: granted.token
|
|
25286
25955
|
}) : void 0;
|
|
25287
|
-
const workspacePath = worktree?.path ??
|
|
25956
|
+
const workspacePath = worktree?.path ?? resolve21(this.roomRoot(corner.cornerId), "scratch");
|
|
25288
25957
|
if (!worktree)
|
|
25289
25958
|
await mkdir13(workspacePath, { recursive: true, mode: 448 });
|
|
25290
25959
|
const isOpener = !corner.openedBy || corner.openedBy === this.agent.publicKey;
|
|
@@ -25673,8 +26342,8 @@ var ThinDaemonCore = class {
|
|
|
25673
26342
|
// apps/body/dist/systemd.js
|
|
25674
26343
|
import { execFile as execFile7 } from "node:child_process";
|
|
25675
26344
|
import { mkdir as mkdir14, readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
|
|
25676
|
-
import { homedir as
|
|
25677
|
-
import { dirname as
|
|
26345
|
+
import { homedir as homedir9 } from "node:os";
|
|
26346
|
+
import { dirname as dirname10, resolve as resolve22 } from "node:path";
|
|
25678
26347
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
25679
26348
|
import { promisify as promisify5 } from "node:util";
|
|
25680
26349
|
var execFileAsync5 = promisify5(execFile7);
|
|
@@ -25715,10 +26384,10 @@ WantedBy=default.target
|
|
|
25715
26384
|
`;
|
|
25716
26385
|
}
|
|
25717
26386
|
function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
|
|
25718
|
-
const home = env.HOME?.trim() ||
|
|
25719
|
-
const expectedLibDir =
|
|
26387
|
+
const home = env.HOME?.trim() || homedir9();
|
|
26388
|
+
const expectedLibDir = resolve22(home, ".local", "lib", "beeline");
|
|
25720
26389
|
const expectedPrefix = `${expectedLibDir}/`;
|
|
25721
|
-
return
|
|
26390
|
+
return resolve22(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve22(invocationPath).startsWith(expectedPrefix);
|
|
25722
26391
|
}
|
|
25723
26392
|
function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
25724
26393
|
if (isCanonicalInstalledLauncher(env, invocationPath))
|
|
@@ -25726,8 +26395,8 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
|
25726
26395
|
throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
|
|
25727
26396
|
}
|
|
25728
26397
|
function systemdUserUnitPath(env = process.env) {
|
|
25729
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
25730
|
-
return
|
|
26398
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve22(homedir9(), ".config");
|
|
26399
|
+
return resolve22(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
|
|
25731
26400
|
}
|
|
25732
26401
|
var runSystemctl = async (args) => {
|
|
25733
26402
|
const result = await execFileAsync5("systemctl", ["--user", ...args], {
|
|
@@ -25745,7 +26414,7 @@ async function installAgentService(publicKey, options = {}) {
|
|
|
25745
26414
|
const content = agentServiceUnit();
|
|
25746
26415
|
const existing = await readFile9(path, "utf8").catch(() => "");
|
|
25747
26416
|
if (existing !== content) {
|
|
25748
|
-
await mkdir14(
|
|
26417
|
+
await mkdir14(dirname10(path), { recursive: true, mode: 448 });
|
|
25749
26418
|
await writeFile9(path, content, { mode: 384 });
|
|
25750
26419
|
}
|
|
25751
26420
|
const run2 = options.run ?? runSystemctl;
|
|
@@ -25826,7 +26495,7 @@ async function retireRemovedAgent(runtime, options = {}) {
|
|
|
25826
26495
|
}
|
|
25827
26496
|
|
|
25828
26497
|
// apps/body/dist/start-command.js
|
|
25829
|
-
import { dirname as
|
|
26498
|
+
import { dirname as dirname11 } from "node:path";
|
|
25830
26499
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
25831
26500
|
var DEFAULT_RESTART_DRAIN_TIMEOUT_MS = 30 * 6e4;
|
|
25832
26501
|
var RESTART_WAIT_REPORT_INTERVAL_MS = 3e4;
|
|
@@ -25901,7 +26570,7 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
25901
26570
|
continue;
|
|
25902
26571
|
}
|
|
25903
26572
|
const spinnerHandle = spinner();
|
|
25904
|
-
spinnerHandle.start(`Starting ${
|
|
26573
|
+
spinnerHandle.start(`Starting ${dirname11(path)}\u2026`);
|
|
25905
26574
|
try {
|
|
25906
26575
|
await startRuntime(path, spinnerHandle);
|
|
25907
26576
|
spinnerHandle.stop(import_picocolors.default.green("Started."));
|
|
@@ -25916,11 +26585,11 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
25916
26585
|
}
|
|
25917
26586
|
|
|
25918
26587
|
// apps/body/dist/connect-command.js
|
|
25919
|
-
import { spawn as
|
|
25920
|
-
import { createHash as
|
|
26588
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
26589
|
+
import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
|
|
25921
26590
|
import { chmod as chmod6, mkdir as mkdir16, readFile as readFile11, unlink as unlink2, writeFile as writeFile11 } from "node:fs/promises";
|
|
25922
|
-
import { homedir as
|
|
25923
|
-
import { dirname as
|
|
26591
|
+
import { homedir as homedir11, hostname as hostname2 } from "node:os";
|
|
26592
|
+
import { dirname as dirname13, resolve as resolve24 } from "node:path";
|
|
25924
26593
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
25925
26594
|
|
|
25926
26595
|
// apps/body/dist/clack-support.js
|
|
@@ -26004,7 +26673,7 @@ async function verifyProviderKey(input) {
|
|
|
26004
26673
|
}
|
|
26005
26674
|
|
|
26006
26675
|
// apps/body/dist/pair-agent-selection.js
|
|
26007
|
-
import { spawn as
|
|
26676
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
26008
26677
|
import { stdin as stdin2, stdout as stdout3 } from "node:process";
|
|
26009
26678
|
var NO_AGENT_MESSAGE = `No supported ACP-capable coding agent was detected.
|
|
26010
26679
|
Install one of these supported agents:
|
|
@@ -26013,7 +26682,7 @@ Install one of these supported agents:
|
|
|
26013
26682
|
goose https://block.github.io/goose/docs/getting-started/installation/
|
|
26014
26683
|
pi npm install -g @mariozechner/pi-coding-agent pi-acp
|
|
26015
26684
|
grok curl -fsSL https://x.ai/cli/install.sh | bash
|
|
26016
|
-
cursor
|
|
26685
|
+
cursor Install Cursor Agent CLI from https://cursor.com/docs/cli
|
|
26017
26686
|
Then retry, or explicitly use \`--agent reference\` with an LLM key.
|
|
26018
26687
|
For another ACP server, use \`--agent custom --agent-command "<cmd> [args...]"\`.`;
|
|
26019
26688
|
async function clackConfirmInstall(message) {
|
|
@@ -26033,7 +26702,7 @@ async function clackSelectAgent(candidates) {
|
|
|
26033
26702
|
}
|
|
26034
26703
|
async function installAdapter(install, opts) {
|
|
26035
26704
|
await new Promise((resolveInstall, rejectInstall) => {
|
|
26036
|
-
const child =
|
|
26705
|
+
const child = spawn7(install.command, install.args, {
|
|
26037
26706
|
cwd: opts.cwd,
|
|
26038
26707
|
env: opts.env ?? process.env,
|
|
26039
26708
|
stdio: "inherit"
|
|
@@ -26253,7 +26922,9 @@ var DEFAULT_MODELS = {
|
|
|
26253
26922
|
codex: "gpt-5.4",
|
|
26254
26923
|
claude: "claude-opus-4-1",
|
|
26255
26924
|
grok: "grok-4",
|
|
26256
|
-
cursor
|
|
26925
|
+
// cursor-agent's own default; always served, and the only model safe to
|
|
26926
|
+
// assume before `cursor-agent models` has been read.
|
|
26927
|
+
cursor: "auto"
|
|
26257
26928
|
};
|
|
26258
26929
|
function connectHarnessNeedsProvider(harness) {
|
|
26259
26930
|
return CONNECT_PROVIDER_HARNESSES.has(harness);
|
|
@@ -26351,8 +27022,14 @@ async function loadConnectModelCatalog(input) {
|
|
|
26351
27022
|
model
|
|
26352
27023
|
}), input.model ? { model: input.model } : void 0, input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs });
|
|
26353
27024
|
const effort = connectEffortPickerFromAxes(catalog.catalog, catalog.raw);
|
|
27025
|
+
const picker = connectModelPickerFromAxes(catalog.catalog, defaultConnectModel(input.harness, input.provider), input.harness, catalog.raw);
|
|
27026
|
+
if (input.harness === "cursor" && !catalog.catalog.some((axis) => axis.category === "model" && axis.options.length) && !catalog.raw.some((axis) => axis.category === "model" && axis.options.length)) {
|
|
27027
|
+
const cliModels = await enumerateCursorModels();
|
|
27028
|
+
if (cliModels)
|
|
27029
|
+
return { ...cliModels, ...effort ? { effort } : {} };
|
|
27030
|
+
}
|
|
26354
27031
|
return {
|
|
26355
|
-
...
|
|
27032
|
+
...picker,
|
|
26356
27033
|
...effort ? { effort } : {}
|
|
26357
27034
|
};
|
|
26358
27035
|
}
|
|
@@ -26536,23 +27213,23 @@ function parseConnectSubscriptions(value) {
|
|
|
26536
27213
|
];
|
|
26537
27214
|
}
|
|
26538
27215
|
async function readMachineId(env = process.env) {
|
|
26539
|
-
const configDir =
|
|
26540
|
-
const machineIdPath =
|
|
27216
|
+
const configDir = resolve24(env.XDG_CONFIG_HOME ?? resolve24(homedir11(), ".config"), "beeline");
|
|
27217
|
+
const machineIdPath = resolve24(configDir, "machine-id");
|
|
26541
27218
|
let machineId;
|
|
26542
|
-
let machineName =
|
|
27219
|
+
let machineName = hostname2();
|
|
26543
27220
|
try {
|
|
26544
27221
|
const existing = await readFile11(machineIdPath, "utf8");
|
|
26545
27222
|
machineId = existing.trim();
|
|
26546
27223
|
if (!/^[0-9a-f-]{32,}$/.test(machineId))
|
|
26547
27224
|
throw new Error("invalid persisted machine id");
|
|
26548
27225
|
} catch {
|
|
26549
|
-
machineId =
|
|
27226
|
+
machineId = randomUUID6();
|
|
26550
27227
|
try {
|
|
26551
27228
|
await mkdir16(configDir, { recursive: true, mode: 448 });
|
|
26552
27229
|
await writeFile11(machineIdPath, `${machineId}
|
|
26553
27230
|
`, { mode: 384 });
|
|
26554
27231
|
} catch {
|
|
26555
|
-
machineId =
|
|
27232
|
+
machineId = createHash9("sha256").update(machineName).digest("hex").slice(0, 36);
|
|
26556
27233
|
}
|
|
26557
27234
|
}
|
|
26558
27235
|
return { machineId, machineName };
|
|
@@ -26561,7 +27238,7 @@ function requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl, machine
|
|
|
26561
27238
|
const normalizedPairingCode = normalizeAgentPairingCode(pairingCode);
|
|
26562
27239
|
if (!normalizedPairingCode)
|
|
26563
27240
|
throw new Error("invalid pairing code");
|
|
26564
|
-
const avatarSeed =
|
|
27241
|
+
const avatarSeed = createHash9("sha256").update(normalizedPairingCode.toUpperCase()).digest("hex").slice(0, 32);
|
|
26565
27242
|
return jsonRequest(`${baseUrl}/auth/agent/connect`, {
|
|
26566
27243
|
pairing_code: normalizedPairingCode,
|
|
26567
27244
|
harness: selection.harness,
|
|
@@ -26610,7 +27287,7 @@ async function installCurrentRelease(fetchImpl) {
|
|
|
26610
27287
|
});
|
|
26611
27288
|
await activateRelease(layout, releaseId);
|
|
26612
27289
|
return {
|
|
26613
|
-
binary:
|
|
27290
|
+
binary: resolve24(layout.binDir, "beeline"),
|
|
26614
27291
|
version: published.version ?? releaseId
|
|
26615
27292
|
};
|
|
26616
27293
|
}
|
|
@@ -26633,7 +27310,7 @@ function providerEnvironment(selection) {
|
|
|
26633
27310
|
};
|
|
26634
27311
|
}
|
|
26635
27312
|
async function writePrivateJson(path, value) {
|
|
26636
|
-
await mkdir16(
|
|
27313
|
+
await mkdir16(dirname13(path), { recursive: true, mode: 448 });
|
|
26637
27314
|
await writeFile11(path, `${JSON.stringify(value, null, 2)}
|
|
26638
27315
|
`, { mode: 384 });
|
|
26639
27316
|
await chmod6(path, 384);
|
|
@@ -26642,8 +27319,8 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
26642
27319
|
const values = providerEnvironment(selection);
|
|
26643
27320
|
if (Object.keys(values).length === 0)
|
|
26644
27321
|
return void 0;
|
|
26645
|
-
const path =
|
|
26646
|
-
await mkdir16(
|
|
27322
|
+
const path = resolve24(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
|
|
27323
|
+
await mkdir16(dirname13(path), { recursive: true, mode: 448 });
|
|
26647
27324
|
const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
26648
27325
|
await writeFile11(path, `${contents}
|
|
26649
27326
|
`, { mode: 384 });
|
|
@@ -26652,7 +27329,7 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
26652
27329
|
}
|
|
26653
27330
|
async function runInstalledFinish(binary, grantPath) {
|
|
26654
27331
|
await new Promise((resolveRun, rejectRun) => {
|
|
26655
|
-
const child =
|
|
27332
|
+
const child = spawn9(binary, ["connect-finish", grantPath], {
|
|
26656
27333
|
stdio: ["ignore", "pipe", "pipe"]
|
|
26657
27334
|
});
|
|
26658
27335
|
let diagnostic = "";
|
|
@@ -26709,7 +27386,7 @@ async function runConnectWizard(code, fetchImpl, eventSubscriptions, accessPolic
|
|
|
26709
27386
|
await finishConnectedAgentPairing(baseUrl, pairingCode, grant.workspace_joined, eventSubscriptions, fetchImpl);
|
|
26710
27387
|
const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
|
|
26711
27388
|
const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
|
|
26712
|
-
const grantPath =
|
|
27389
|
+
const grantPath = resolve24(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
|
|
26713
27390
|
await writePrivateJson(grantPath, {
|
|
26714
27391
|
agentSecretKey: grant.agent_secret_key,
|
|
26715
27392
|
bodySecretKey: grant.body_secret_key,
|
|
@@ -26794,18 +27471,18 @@ async function runConnectFinishCommand(path) {
|
|
|
26794
27471
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
26795
27472
|
}
|
|
26796
27473
|
try {
|
|
26797
|
-
const grant = JSON.parse(await readFile11(
|
|
27474
|
+
const grant = JSON.parse(await readFile11(resolve24(path), "utf8"));
|
|
26798
27475
|
if (!isDevicePairingGrant(grant))
|
|
26799
27476
|
throw new Error("device connection grant is invalid");
|
|
26800
27477
|
const connected = await completeDevicePairing(grant);
|
|
26801
|
-
await unlink2(
|
|
27478
|
+
await unlink2(resolve24(path));
|
|
26802
27479
|
const providerEnv = grant.llmEnvFile ? await readFile11(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
26803
27480
|
const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
|
|
26804
27481
|
const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
|
|
26805
27482
|
if (model) {
|
|
26806
27483
|
const decision2 = await resolveOpenRouterRouting({
|
|
26807
27484
|
model,
|
|
26808
|
-
cacheDir: openRouterRoutingCacheDir(
|
|
27485
|
+
cacheDir: openRouterRoutingCacheDir(dirname13(connected.configPath)),
|
|
26809
27486
|
...apiKey ? { apiKey } : {},
|
|
26810
27487
|
probeTimeoutMs: 1e4
|
|
26811
27488
|
});
|
|
@@ -26826,22 +27503,22 @@ init_self_update_manifest();
|
|
|
26826
27503
|
|
|
26827
27504
|
// apps/body/dist/managed-update.js
|
|
26828
27505
|
init_self_update();
|
|
26829
|
-
import { spawn as
|
|
27506
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
26830
27507
|
import { mkdir as mkdir18, rm as rm7, stat as stat3, writeFile as writeFile13 } from "node:fs/promises";
|
|
26831
|
-
import { dirname as
|
|
27508
|
+
import { dirname as dirname15, resolve as resolve26 } from "node:path";
|
|
26832
27509
|
|
|
26833
27510
|
// apps/body/dist/update-rollback-alert.js
|
|
26834
27511
|
import { mkdir as mkdir17, readFile as readFile12, rename as rename5, unlink as unlink3, writeFile as writeFile12 } from "node:fs/promises";
|
|
26835
|
-
import { dirname as
|
|
27512
|
+
import { dirname as dirname14, resolve as resolve25 } from "node:path";
|
|
26836
27513
|
var REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
26837
27514
|
var lastLogged = /* @__PURE__ */ new Map();
|
|
26838
27515
|
function updateRollbackAlertPath(runtimeDir) {
|
|
26839
|
-
return
|
|
27516
|
+
return resolve25(runtimeDir, "update-rollback-alert.json");
|
|
26840
27517
|
}
|
|
26841
27518
|
async function writeAlert(runtimeDir, alert) {
|
|
26842
27519
|
const path = updateRollbackAlertPath(runtimeDir);
|
|
26843
27520
|
const staged = `${path}.${process.pid}.tmp`;
|
|
26844
|
-
await mkdir17(
|
|
27521
|
+
await mkdir17(dirname14(path), { recursive: true });
|
|
26845
27522
|
await writeFile12(staged, `${JSON.stringify(alert, null, 2)}
|
|
26846
27523
|
`, { mode: 384 });
|
|
26847
27524
|
await rename5(staged, path);
|
|
@@ -26905,13 +27582,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
|
|
|
26905
27582
|
var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
|
|
26906
27583
|
async function withInstallLock(layout, work, options = {}) {
|
|
26907
27584
|
const now2 = options.now ?? Date.now;
|
|
26908
|
-
const lock =
|
|
27585
|
+
const lock = resolve26(layout.releasesRoot, ".state", "install.lock");
|
|
26909
27586
|
const deadline = now2() + (options.waitMs ?? 1e4);
|
|
26910
|
-
await mkdir18(
|
|
27587
|
+
await mkdir18(dirname15(lock), { recursive: true });
|
|
26911
27588
|
for (; ; ) {
|
|
26912
27589
|
try {
|
|
26913
27590
|
await mkdir18(lock);
|
|
26914
|
-
await writeFile13(
|
|
27591
|
+
await writeFile13(resolve26(lock, "owner"), `${process.pid}
|
|
26915
27592
|
${now2()}
|
|
26916
27593
|
`, "utf8");
|
|
26917
27594
|
break;
|
|
@@ -27034,7 +27711,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
27034
27711
|
if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
|
|
27035
27712
|
const from = await readInstalledBundleIdentity({
|
|
27036
27713
|
...this.#layout,
|
|
27037
|
-
libDir:
|
|
27714
|
+
libDir: resolve26(this.#layout.releasesRoot, this.#loadedRelease)
|
|
27038
27715
|
}).catch(() => void 0) ?? {};
|
|
27039
27716
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
27040
27717
|
const record3 = {
|
|
@@ -27240,7 +27917,7 @@ async function runManagedUpdateWorkerProcess() {
|
|
|
27240
27917
|
if (!entrypoint)
|
|
27241
27918
|
throw new Error("cannot resolve the current Beeline entrypoint");
|
|
27242
27919
|
await new Promise((resolveWorker, rejectWorker) => {
|
|
27243
|
-
const child =
|
|
27920
|
+
const child = spawn10(process.execPath, [entrypoint, "managed-update-worker"], {
|
|
27244
27921
|
detached: true,
|
|
27245
27922
|
env: { ...process.env, BEELINE_INTERNAL_UPDATE_WORKER: "1" },
|
|
27246
27923
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -27321,7 +27998,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
27321
27998
|
});
|
|
27322
27999
|
if (!accepted)
|
|
27323
28000
|
return false;
|
|
27324
|
-
await writeFile13(
|
|
28001
|
+
await writeFile13(resolve26(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
|
|
27325
28002
|
readyAt: (options.now ?? Date.now)(),
|
|
27326
28003
|
loadedRelease,
|
|
27327
28004
|
functionalProof: options.functionalProof
|
|
@@ -27550,11 +28227,11 @@ init_self_update();
|
|
|
27550
28227
|
|
|
27551
28228
|
// apps/body/dist/daemon-failure.js
|
|
27552
28229
|
import { mkdir as mkdir19, readFile as readFile13, rename as rename6, rm as rm8, writeFile as writeFile14 } from "node:fs/promises";
|
|
27553
|
-
import { dirname as
|
|
28230
|
+
import { dirname as dirname16, resolve as resolve27 } from "node:path";
|
|
27554
28231
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
27555
28232
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
27556
28233
|
function daemonFailurePath(runtimeDir) {
|
|
27557
|
-
return
|
|
28234
|
+
return resolve27(runtimeDir, "daemon-distress.json");
|
|
27558
28235
|
}
|
|
27559
28236
|
async function readFailureRecord(runtimeDir) {
|
|
27560
28237
|
try {
|
|
@@ -27570,7 +28247,7 @@ async function readFailureRecord(runtimeDir) {
|
|
|
27570
28247
|
async function writeFailureRecord(runtimeDir, record3) {
|
|
27571
28248
|
const path = daemonFailurePath(runtimeDir);
|
|
27572
28249
|
const staged = `${path}.${process.pid}.tmp`;
|
|
27573
|
-
await mkdir19(
|
|
28250
|
+
await mkdir19(dirname16(path), { recursive: true, mode: 448 });
|
|
27574
28251
|
await writeFile14(staged, `${JSON.stringify(record3, null, 2)}
|
|
27575
28252
|
`, { mode: 384 });
|
|
27576
28253
|
await rename6(staged, path);
|
|
@@ -27594,8 +28271,8 @@ async function clearDaemonStartFailures(runtimeDir) {
|
|
|
27594
28271
|
|
|
27595
28272
|
// apps/body/dist/update-functional-probe.js
|
|
27596
28273
|
import { mkdir as mkdir20, rm as rm9 } from "node:fs/promises";
|
|
27597
|
-
import { homedir as
|
|
27598
|
-
import { resolve as
|
|
28274
|
+
import { homedir as homedir12 } from "node:os";
|
|
28275
|
+
import { resolve as resolve28 } from "node:path";
|
|
27599
28276
|
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
27600
28277
|
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
27601
28278
|
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
@@ -27662,6 +28339,10 @@ function describeCurrentReleaseOutcome(outcome) {
|
|
|
27662
28339
|
}
|
|
27663
28340
|
async function runUpdateFunctionalProbe(input) {
|
|
27664
28341
|
const command = input.config.agentCommand ?? input.config.agentBinary;
|
|
28342
|
+
const harnessLabel = harnessIdentityLabel({
|
|
28343
|
+
kind: input.config.agentKind,
|
|
28344
|
+
command
|
|
28345
|
+
});
|
|
27665
28346
|
const harness = input.config.agentKind ?? command;
|
|
27666
28347
|
if (input.config.modelUnavailable) {
|
|
27667
28348
|
throw new UpdateFunctionalProbeError("model-unavailable", input.config.modelUnavailable.detail);
|
|
@@ -27689,9 +28370,9 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
27689
28370
|
modelAnswerReason: `${detail} (the current release has the same host sandbox failure)`
|
|
27690
28371
|
};
|
|
27691
28372
|
}
|
|
27692
|
-
const root = input.probeRoot ??
|
|
27693
|
-
const cwd =
|
|
27694
|
-
const homeRoot =
|
|
28373
|
+
const root = input.probeRoot ?? resolve28(input.runtimeDir, "update-functional-probe");
|
|
28374
|
+
const cwd = resolve28(root, "checkout");
|
|
28375
|
+
const homeRoot = resolve28(root, "agent-home");
|
|
27695
28376
|
await rm9(root, { recursive: true, force: true });
|
|
27696
28377
|
await mkdir20(cwd, { recursive: true, mode: 448 });
|
|
27697
28378
|
let client;
|
|
@@ -27700,7 +28381,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
27700
28381
|
...input.config.agentEnv,
|
|
27701
28382
|
...await prepareRoomAgentHome({
|
|
27702
28383
|
root: homeRoot,
|
|
27703
|
-
operatorHome: input.config.operatorHome ??
|
|
28384
|
+
operatorHome: input.config.operatorHome ?? homedir12(),
|
|
27704
28385
|
sharedSkills: input.config.sharedSkills ?? [],
|
|
27705
28386
|
...input.config.agentKind ? { agentKind: input.config.agentKind } : {},
|
|
27706
28387
|
skillReleaseId: input.releaseId,
|
|
@@ -27721,8 +28402,8 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
27721
28402
|
let turnCompleted = true;
|
|
27722
28403
|
if (input.config.bwrapPath) {
|
|
27723
28404
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
27724
|
-
const operatorHome = input.config.operatorHome ??
|
|
27725
|
-
const homeStateDirs = harnessHomeStateDirs(
|
|
28405
|
+
const operatorHome = input.config.operatorHome ?? homedir12();
|
|
28406
|
+
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
27726
28407
|
await Promise.all(homeStateDirs.map((dir) => mkdir20(dir, { recursive: true })));
|
|
27727
28408
|
spawnCommand = wrapAgentCommand({
|
|
27728
28409
|
bwrapPath: input.config.bwrapPath,
|
|
@@ -27741,7 +28422,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
27741
28422
|
client = new AcpClient({
|
|
27742
28423
|
agentCommand: spawnCommand.command,
|
|
27743
28424
|
agentArgs: spawnCommand.args,
|
|
27744
|
-
agentLabel:
|
|
28425
|
+
agentLabel: harnessLabel,
|
|
27745
28426
|
agentEnv,
|
|
27746
28427
|
agentCwd: cwd,
|
|
27747
28428
|
autoApprovePermissions: true
|
|
@@ -27772,7 +28453,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
27772
28453
|
modelAnswer = { modelAnswer: "served" };
|
|
27773
28454
|
} else {
|
|
27774
28455
|
const explained = await explainEmptyAgentTurn({
|
|
27775
|
-
agentLabel:
|
|
28456
|
+
agentLabel: harnessLabel,
|
|
27776
28457
|
agentEnv,
|
|
27777
28458
|
sessionId,
|
|
27778
28459
|
result: served
|
|
@@ -27872,8 +28553,8 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
27872
28553
|
}
|
|
27873
28554
|
|
|
27874
28555
|
// apps/body/dist/current-release-probe.js
|
|
27875
|
-
import { spawn as
|
|
27876
|
-
import { dirname as
|
|
28556
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
28557
|
+
import { dirname as dirname17, join as join12 } from "node:path";
|
|
27877
28558
|
init_self_update();
|
|
27878
28559
|
var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
|
|
27879
28560
|
var UPDATE_PROBE_COMMAND = "update-probe";
|
|
@@ -27916,14 +28597,14 @@ function outcomeFromReport(report) {
|
|
|
27916
28597
|
}
|
|
27917
28598
|
}
|
|
27918
28599
|
async function probeReleaseInSubprocess(input) {
|
|
27919
|
-
const bundleDir =
|
|
28600
|
+
const bundleDir = join12(input.layout.releasesRoot, input.releaseId);
|
|
27920
28601
|
const entrypoint = await resolveBundleEntrypoint(bundleDir);
|
|
27921
28602
|
if (!entrypoint) {
|
|
27922
28603
|
return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
|
|
27923
28604
|
}
|
|
27924
28605
|
const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
|
|
27925
|
-
return new Promise((
|
|
27926
|
-
const child =
|
|
28606
|
+
return new Promise((resolve32) => {
|
|
28607
|
+
const child = spawn11(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
27927
28608
|
let stdout6 = "";
|
|
27928
28609
|
let stderr = "";
|
|
27929
28610
|
let settled = false;
|
|
@@ -27932,7 +28613,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
27932
28613
|
return;
|
|
27933
28614
|
settled = true;
|
|
27934
28615
|
clearTimeout(timer);
|
|
27935
|
-
|
|
28616
|
+
resolve32(outcome);
|
|
27936
28617
|
};
|
|
27937
28618
|
const timer = setTimeout(() => {
|
|
27938
28619
|
child.kill("SIGKILL");
|
|
@@ -27979,7 +28660,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
27979
28660
|
const runtime = await readRuntimeRecord(configPath);
|
|
27980
28661
|
const agent = runtimeAgentCommand(runtime);
|
|
27981
28662
|
const config = loadBodyConfig({
|
|
27982
|
-
workspaceRoot:
|
|
28663
|
+
workspaceRoot: join12(dirname17(configPath), "workspace"),
|
|
27983
28664
|
llmEnvFile: runtime.llmEnvFile,
|
|
27984
28665
|
env: { ...env, BUZZ_AGENT_BIN: agent.command, BUZZ_DEV_MCP_BIN: runtime.mcpBinary },
|
|
27985
28666
|
agent
|
|
@@ -27997,7 +28678,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
27997
28678
|
}
|
|
27998
28679
|
const layout = beelineInstallLayout(env);
|
|
27999
28680
|
const releaseId = (layout && await activeReleaseId(layout).catch(() => void 0)) ?? "unknown";
|
|
28000
|
-
const runtimeDir =
|
|
28681
|
+
const runtimeDir = dirname17(configPath);
|
|
28001
28682
|
const outcome = await probeOutcome(() => (options.probe ?? runUpdateFunctionalProbe)({
|
|
28002
28683
|
config,
|
|
28003
28684
|
runtimeDir,
|
|
@@ -28005,7 +28686,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
28005
28686
|
sandboxRequired: runtime.sandbox !== "off",
|
|
28006
28687
|
sandboxUnavailableDetail: sandbox.advisory,
|
|
28007
28688
|
// The successor's probe still holds `<runtimeDir>/update-functional-probe`.
|
|
28008
|
-
probeRoot:
|
|
28689
|
+
probeRoot: join12(runtimeDir, "current-release-probe")
|
|
28009
28690
|
}));
|
|
28010
28691
|
const report = outcome.kind === "served" ? { probe: "served" } : outcome.kind === "refused" ? { probe: "refused", status: outcome.status, reason: outcome.reason } : outcome.kind === "sandbox-unavailable" ? { probe: "sandbox-unavailable", reason: outcome.reason } : { probe: "failed", reason: outcome.reason };
|
|
28011
28692
|
write(JSON.stringify(report));
|
|
@@ -28013,7 +28694,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
28013
28694
|
|
|
28014
28695
|
// apps/body/dist/release-status.js
|
|
28015
28696
|
import { readFile as readFile14, readdir as readdir6, rename as rename7, writeFile as writeFile15 } from "node:fs/promises";
|
|
28016
|
-
import { resolve as
|
|
28697
|
+
import { resolve as resolve29 } from "node:path";
|
|
28017
28698
|
var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
|
|
28018
28699
|
var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
|
|
28019
28700
|
var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
|
|
@@ -28030,7 +28711,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
28030
28711
|
pid: options.pid ?? process.pid,
|
|
28031
28712
|
readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
28032
28713
|
};
|
|
28033
|
-
const target =
|
|
28714
|
+
const target = resolve29(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
|
|
28034
28715
|
const temporary = `${target}.${status.pid}.tmp`;
|
|
28035
28716
|
await writeFile15(temporary, `${JSON.stringify(status, null, 2)}
|
|
28036
28717
|
`, { mode: 384 });
|
|
@@ -28040,7 +28721,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
28040
28721
|
|
|
28041
28722
|
// apps/body/dist/scratch-sweep.js
|
|
28042
28723
|
import { lstat as lstat4, readdir as readdir7, rmdir, unlink as unlink4 } from "node:fs/promises";
|
|
28043
|
-
import { resolve as
|
|
28724
|
+
import { resolve as resolve30 } from "node:path";
|
|
28044
28725
|
var DEFAULT_SCRATCH_TTL_HOURS = 72;
|
|
28045
28726
|
var NEVER_SWEEP_SUBDIR_NAMES = new Set(HOME_SUBDIRS.filter((name) => name !== "tmp"));
|
|
28046
28727
|
function scratchTtlMs(env = process.env) {
|
|
@@ -28049,7 +28730,7 @@ function scratchTtlMs(env = process.env) {
|
|
|
28049
28730
|
return (Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_SCRATCH_TTL_HOURS) * 60 * 60 * 1e3;
|
|
28050
28731
|
}
|
|
28051
28732
|
async function discoverAttachScratchRoots(runtimeDir) {
|
|
28052
|
-
const roomsDir =
|
|
28733
|
+
const roomsDir = resolve30(runtimeDir, "rooms");
|
|
28053
28734
|
let entries;
|
|
28054
28735
|
try {
|
|
28055
28736
|
entries = await readdir7(roomsDir, { withFileTypes: true });
|
|
@@ -28060,7 +28741,7 @@ async function discoverAttachScratchRoots(runtimeDir) {
|
|
|
28060
28741
|
for (const entry of entries) {
|
|
28061
28742
|
if (!entry.isDirectory())
|
|
28062
28743
|
continue;
|
|
28063
|
-
const home =
|
|
28744
|
+
const home = resolve30(roomsDir, entry.name, "agent-home");
|
|
28064
28745
|
const stats = await lstat4(home).catch(() => void 0);
|
|
28065
28746
|
if (stats?.isDirectory())
|
|
28066
28747
|
roots.push(home);
|
|
@@ -28079,7 +28760,7 @@ async function removeStaleFiles(dir, cutoffMs, protectNamesHere) {
|
|
|
28079
28760
|
for (const entry of entries) {
|
|
28080
28761
|
if (protectNamesHere && NEVER_SWEEP_SUBDIR_NAMES.has(entry.name))
|
|
28081
28762
|
continue;
|
|
28082
|
-
const path =
|
|
28763
|
+
const path = resolve30(dir, entry.name);
|
|
28083
28764
|
const stats = await lstat4(path).catch(() => void 0);
|
|
28084
28765
|
if (!stats || stats.isSymbolicLink())
|
|
28085
28766
|
continue;
|
|
@@ -28166,7 +28847,7 @@ var DaemonExitError = class extends Error {
|
|
|
28166
28847
|
};
|
|
28167
28848
|
async function runStoredDaemon(pathOrPointer) {
|
|
28168
28849
|
const configPath = await resolveRuntimeConfigPath(pathOrPointer);
|
|
28169
|
-
daemonFailureRuntimeDir =
|
|
28850
|
+
daemonFailureRuntimeDir = dirname18(configPath);
|
|
28170
28851
|
const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
|
|
28171
28852
|
let runtime = accessMigration.runtime;
|
|
28172
28853
|
if (!runtime.transport) {
|
|
@@ -28178,7 +28859,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
28178
28859
|
runtime = activated.runtime;
|
|
28179
28860
|
const daemonApi = activated.client;
|
|
28180
28861
|
const agent = runtimeAgentCommand(runtime);
|
|
28181
|
-
await writeFile16(
|
|
28862
|
+
await writeFile16(resolve31(dirname18(configPath), "daemon.pid"), `${process.pid}
|
|
28182
28863
|
`, { mode: 384 });
|
|
28183
28864
|
const env = {
|
|
28184
28865
|
...process.env,
|
|
@@ -28186,7 +28867,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
28186
28867
|
BUZZ_DEV_MCP_BIN: runtime.mcpBinary
|
|
28187
28868
|
};
|
|
28188
28869
|
const config = loadBodyConfig({
|
|
28189
|
-
workspaceRoot:
|
|
28870
|
+
workspaceRoot: resolve31(dirname18(configPath), "workspace"),
|
|
28190
28871
|
llmEnvFile: runtime.llmEnvFile,
|
|
28191
28872
|
env,
|
|
28192
28873
|
agent
|
|
@@ -28221,7 +28902,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
28221
28902
|
const stop = () => controller.abort();
|
|
28222
28903
|
process.once("SIGINT", stop);
|
|
28223
28904
|
process.once("SIGTERM", stop);
|
|
28224
|
-
const runtimeDir =
|
|
28905
|
+
const runtimeDir = dirname18(configPath);
|
|
28225
28906
|
const layout = beelineInstallLayout(process.env);
|
|
28226
28907
|
const notifier = new SystemdNotifier();
|
|
28227
28908
|
let rollbackAlertDrain;
|
|
@@ -28377,7 +29058,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
28377
29058
|
} finally {
|
|
28378
29059
|
clearInterval(scratchSweepTimer);
|
|
28379
29060
|
await notifier.stopping(stoppingStatus).catch(() => void 0);
|
|
28380
|
-
const pidPath =
|
|
29061
|
+
const pidPath = resolve31(dirname18(configPath), "daemon.pid");
|
|
28381
29062
|
const recorded = Number((await readFile15(pidPath, "utf8").catch(() => "")).trim());
|
|
28382
29063
|
if (recorded === process.pid) {
|
|
28383
29064
|
await unlink5(pidPath).catch(() => void 0);
|
|
@@ -28389,6 +29070,10 @@ async function main() {
|
|
|
28389
29070
|
if (args.length === 0)
|
|
28390
29071
|
usage();
|
|
28391
29072
|
const command = args[0];
|
|
29073
|
+
if (command === CURSOR_ACP_BRIDGE_FLAG) {
|
|
29074
|
+
await runCursorAcpStdioServer();
|
|
29075
|
+
return;
|
|
29076
|
+
}
|
|
28392
29077
|
if (command === "--help" || command === "-h")
|
|
28393
29078
|
usage(0);
|
|
28394
29079
|
if (command === "managed-update-worker") {
|
|
@@ -28405,7 +29090,7 @@ async function main() {
|
|
|
28405
29090
|
const roomId = roomFlag >= 0 ? args[roomFlag + 1] : void 0;
|
|
28406
29091
|
if (!configPath || !roomId)
|
|
28407
29092
|
throw new Error("corner-read-token requires --config and --room");
|
|
28408
|
-
const activated = await activateDaemonTransport(
|
|
29093
|
+
const activated = await activateDaemonTransport(resolve31(configPath));
|
|
28409
29094
|
if (!activated)
|
|
28410
29095
|
throw new Error("corner-read-token requires monolith transport");
|
|
28411
29096
|
const credential = await activated.client.execute("getRoomGitHubToken", { roomId });
|
|
@@ -28454,14 +29139,14 @@ async function main() {
|
|
|
28454
29139
|
const agentPubkey = agentFlag >= 0 ? args[agentFlag + 1] : void 0;
|
|
28455
29140
|
if (!configPath && agentPubkey) {
|
|
28456
29141
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
28457
|
-
configPath = configs.find((candidate) =>
|
|
29142
|
+
configPath = configs.find((candidate) => dirname18(candidate).endsWith(agentPubkey));
|
|
28458
29143
|
}
|
|
28459
29144
|
if (!configPath && agentPubkey) {
|
|
28460
29145
|
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
|
|
28461
29146
|
}
|
|
28462
29147
|
if (!configPath)
|
|
28463
29148
|
throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
|
|
28464
|
-
await runStoredDaemon(
|
|
29149
|
+
await runStoredDaemon(resolve31(configPath));
|
|
28465
29150
|
return;
|
|
28466
29151
|
}
|
|
28467
29152
|
if (command === "update") {
|
|
@@ -28482,7 +29167,7 @@ async function main() {
|
|
|
28482
29167
|
if (!agentPubkey)
|
|
28483
29168
|
throw new Error("stop requires --agent <pubkey>");
|
|
28484
29169
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
28485
|
-
const configPath = configs.find((candidate) =>
|
|
29170
|
+
const configPath = configs.find((candidate) => dirname18(candidate).endsWith(agentPubkey));
|
|
28486
29171
|
if (!configPath)
|
|
28487
29172
|
throw new Error(`no stored runtime found for agent ${agentPubkey}`);
|
|
28488
29173
|
const runtime = await readRuntimeRecord(configPath);
|