usebeeline 0.0.121 → 0.0.122
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/README.md +11 -1
- package/dist/usebeeline.mjs +1083 -655
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -11315,12 +11315,12 @@ __export(self_update_exports, {
|
|
|
11315
11315
|
});
|
|
11316
11316
|
import { createHash as createHash8 } from "node:crypto";
|
|
11317
11317
|
import { constants as fsConstants2 } from "node:fs";
|
|
11318
|
-
import { access, chmod as chmod6, lstat as lstat4, mkdir as
|
|
11318
|
+
import { access as access2, chmod as chmod6, lstat as lstat4, mkdir as mkdir18, open, readFile as readFile11, rename as rename6, rm as rm6, symlink as symlink3, writeFile as writeFile11 } from "node:fs/promises";
|
|
11319
11319
|
import { spawn as spawn8 } from "node:child_process";
|
|
11320
11320
|
import { homedir as homedir12 } from "node:os";
|
|
11321
|
-
import { dirname as
|
|
11321
|
+
import { dirname as dirname16, join as join13, resolve as resolve27 } from "node:path";
|
|
11322
11322
|
function anchorLayout(rawLibDir) {
|
|
11323
|
-
const libDir =
|
|
11323
|
+
const libDir = resolve27(rawLibDir);
|
|
11324
11324
|
const segments = libDir.split(/[/\\]/);
|
|
11325
11325
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
11326
11326
|
if (idx >= 2 && segments[idx - 1] === "lib") {
|
|
@@ -11336,9 +11336,9 @@ function anchorLayout(rawLibDir) {
|
|
|
11336
11336
|
// prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
|
|
11337
11337
|
// defect that made activateRelease write its stable forwarders where
|
|
11338
11338
|
// nothing executed them, leaving stale raw wrappers in <prefix>/bin.
|
|
11339
|
-
binDir:
|
|
11339
|
+
binDir: resolve27(libDir, "../../bin"),
|
|
11340
11340
|
libDir,
|
|
11341
|
-
releasesRoot:
|
|
11341
|
+
releasesRoot: resolve27(libDir, `../${RELEASES_SEGMENT}`)
|
|
11342
11342
|
};
|
|
11343
11343
|
}
|
|
11344
11344
|
function beelineInstallLayout(env = process.env) {
|
|
@@ -11350,18 +11350,18 @@ function beelineInstallLayout(env = process.env) {
|
|
|
11350
11350
|
function releaseIdFromPath(path) {
|
|
11351
11351
|
if (!path?.trim())
|
|
11352
11352
|
return void 0;
|
|
11353
|
-
const segments =
|
|
11353
|
+
const segments = resolve27(path.trim()).split(/[/\\]/);
|
|
11354
11354
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
11355
11355
|
const id = idx >= 0 ? segments[idx + 1] : void 0;
|
|
11356
11356
|
return id ? sanitizeReleaseId(id) : void 0;
|
|
11357
11357
|
}
|
|
11358
11358
|
function runningReleaseId(layout, env = process.env, invocationPath = process.argv[1]) {
|
|
11359
|
-
const releasesRoot =
|
|
11360
|
-
const anchor =
|
|
11359
|
+
const releasesRoot = resolve27(layout.releasesRoot);
|
|
11360
|
+
const anchor = resolve27(layout.libDir);
|
|
11361
11361
|
for (const raw of [env.BEELINE_LIB_DIR, invocationPath]) {
|
|
11362
11362
|
if (!raw?.trim())
|
|
11363
11363
|
continue;
|
|
11364
|
-
const resolved =
|
|
11364
|
+
const resolved = resolve27(raw.trim());
|
|
11365
11365
|
if (resolved !== anchor && !resolved.startsWith(`${releasesRoot}/`))
|
|
11366
11366
|
continue;
|
|
11367
11367
|
const id = releaseIdFromPath(resolved);
|
|
@@ -11372,7 +11372,7 @@ function runningReleaseId(layout, env = process.env, invocationPath = process.ar
|
|
|
11372
11372
|
}
|
|
11373
11373
|
function defaultBeelineInstallLayout(env = process.env) {
|
|
11374
11374
|
const home = env.HOME?.trim() || homedir12();
|
|
11375
|
-
return anchorLayout(
|
|
11375
|
+
return anchorLayout(resolve27(home, ".local", "lib", "beeline"));
|
|
11376
11376
|
}
|
|
11377
11377
|
function discoveredBeelineInstallLayout(env = process.env) {
|
|
11378
11378
|
const explicitAnchor = env.BEELINE_INSTALL_LIB_DIR?.trim();
|
|
@@ -11380,7 +11380,7 @@ function discoveredBeelineInstallLayout(env = process.env) {
|
|
|
11380
11380
|
return anchorLayout(explicitAnchor);
|
|
11381
11381
|
const explicitBinDir = env.BEELINE_INSTALL_DIR?.trim();
|
|
11382
11382
|
if (explicitBinDir)
|
|
11383
|
-
return anchorLayout(
|
|
11383
|
+
return anchorLayout(resolve27(dirname16(resolve27(explicitBinDir)), "lib", "beeline"));
|
|
11384
11384
|
return defaultBeelineInstallLayout(env);
|
|
11385
11385
|
}
|
|
11386
11386
|
function hostPlatformKey() {
|
|
@@ -11397,7 +11397,7 @@ async function readBundleJson(bundleDir) {
|
|
|
11397
11397
|
let raw;
|
|
11398
11398
|
for (const candidate of bundleJsonCandidates(bundleDir)) {
|
|
11399
11399
|
try {
|
|
11400
|
-
raw = await
|
|
11400
|
+
raw = await readFile11(candidate, "utf8");
|
|
11401
11401
|
break;
|
|
11402
11402
|
} catch {
|
|
11403
11403
|
}
|
|
@@ -11419,13 +11419,13 @@ function updateStatePath(layout) {
|
|
|
11419
11419
|
}
|
|
11420
11420
|
async function readUpdateState(layout) {
|
|
11421
11421
|
try {
|
|
11422
|
-
return JSON.parse(await
|
|
11422
|
+
return JSON.parse(await readFile11(updateStatePath(layout), "utf8"));
|
|
11423
11423
|
} catch {
|
|
11424
11424
|
return {};
|
|
11425
11425
|
}
|
|
11426
11426
|
}
|
|
11427
11427
|
async function writeUpdateState(layout, state) {
|
|
11428
|
-
await
|
|
11428
|
+
await mkdir18(join13(layout.releasesRoot, ".state"), { recursive: true });
|
|
11429
11429
|
await writeFile11(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
|
|
11430
11430
|
`, "utf8");
|
|
11431
11431
|
}
|
|
@@ -11512,7 +11512,7 @@ function entrypointCandidates(bundleDir) {
|
|
|
11512
11512
|
async function resolveBundleEntrypoint(bundleDir) {
|
|
11513
11513
|
for (const candidate of entrypointCandidates(bundleDir)) {
|
|
11514
11514
|
try {
|
|
11515
|
-
await
|
|
11515
|
+
await access2(candidate, fsConstants2.F_OK);
|
|
11516
11516
|
return candidate;
|
|
11517
11517
|
} catch {
|
|
11518
11518
|
}
|
|
@@ -11520,10 +11520,7 @@ async function resolveBundleEntrypoint(bundleDir) {
|
|
|
11520
11520
|
return void 0;
|
|
11521
11521
|
}
|
|
11522
11522
|
function requiredBundlePaths() {
|
|
11523
|
-
return [
|
|
11524
|
-
BUNDLE_ENTRYPOINT,
|
|
11525
|
-
PI_MCP_ADAPTER_ENTRYPOINT
|
|
11526
|
-
];
|
|
11523
|
+
return [BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, CODEGRAPH_ENTRYPOINT];
|
|
11527
11524
|
}
|
|
11528
11525
|
async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
11529
11526
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
@@ -11533,13 +11530,13 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
11533
11530
|
const okMarker = join13(releaseDir, ".stage-ok");
|
|
11534
11531
|
let previouslyVerified = false;
|
|
11535
11532
|
try {
|
|
11536
|
-
const recorded = await
|
|
11533
|
+
const recorded = await readFile11(okMarker, "utf8");
|
|
11537
11534
|
if (recorded.trim() === published.sha256)
|
|
11538
11535
|
return releaseId;
|
|
11539
11536
|
previouslyVerified = true;
|
|
11540
11537
|
} catch {
|
|
11541
11538
|
}
|
|
11542
|
-
await
|
|
11539
|
+
await mkdir18(releaseDir, { recursive: true });
|
|
11543
11540
|
const tempArchive = join13(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
|
|
11544
11541
|
try {
|
|
11545
11542
|
log2(`downloading ${published.file}`);
|
|
@@ -11580,7 +11577,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
11580
11577
|
throw new Error(`extracting bundle failed: ${extract2.stderr}`);
|
|
11581
11578
|
for (const relative3 of requiredBundlePaths()) {
|
|
11582
11579
|
try {
|
|
11583
|
-
await
|
|
11580
|
+
await access2(join13(releaseDir, relative3), fsConstants2.F_OK);
|
|
11584
11581
|
} catch {
|
|
11585
11582
|
throw new Error(`staged bundle is missing ${relative3}`);
|
|
11586
11583
|
}
|
|
@@ -11625,9 +11622,9 @@ async function replaceFile(path, contents, mode) {
|
|
|
11625
11622
|
}
|
|
11626
11623
|
async function activateRelease(layout, releaseId) {
|
|
11627
11624
|
const releaseDir = join13(layout.releasesRoot, releaseId);
|
|
11628
|
-
await
|
|
11629
|
-
await
|
|
11630
|
-
await
|
|
11625
|
+
await access2(join13(releaseDir, BUNDLE_ENTRYPOINT), fsConstants2.F_OK);
|
|
11626
|
+
await mkdir18(layout.releasesRoot, { recursive: true });
|
|
11627
|
+
await mkdir18(layout.binDir, { recursive: true });
|
|
11631
11628
|
let previousReleaseId = await activeReleaseId(layout);
|
|
11632
11629
|
const kind = await pathKind(layout.libDir);
|
|
11633
11630
|
if (kind === "directory") {
|
|
@@ -11635,7 +11632,7 @@ async function activateRelease(layout, releaseId) {
|
|
|
11635
11632
|
const legacyId = sanitizeReleaseId(legacyIdentity?.commit ?? legacyIdentity?.version ?? `legacy-${Date.now()}`);
|
|
11636
11633
|
const legacyDir = join13(layout.releasesRoot, legacyId);
|
|
11637
11634
|
try {
|
|
11638
|
-
await
|
|
11635
|
+
await access2(legacyDir, fsConstants2.F_OK);
|
|
11639
11636
|
previousReleaseId = `${legacyId}-${Date.now()}`;
|
|
11640
11637
|
await rename6(layout.libDir, join13(layout.releasesRoot, previousReleaseId));
|
|
11641
11638
|
await normalizeLegacyBundleShape(join13(layout.releasesRoot, previousReleaseId));
|
|
@@ -11649,7 +11646,7 @@ async function activateRelease(layout, releaseId) {
|
|
|
11649
11646
|
await rm6(tempLink, { force: true });
|
|
11650
11647
|
await symlink3(join13("beeline-releases", releaseId), tempLink);
|
|
11651
11648
|
await rename6(tempLink, layout.libDir);
|
|
11652
|
-
await fsyncDir(
|
|
11649
|
+
await fsyncDir(dirname16(layout.libDir));
|
|
11653
11650
|
await writeBinForwarders(layout, releaseDir);
|
|
11654
11651
|
return { previousReleaseId };
|
|
11655
11652
|
}
|
|
@@ -11658,7 +11655,7 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
11658
11655
|
let anyFlat = false;
|
|
11659
11656
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
11660
11657
|
try {
|
|
11661
|
-
await
|
|
11658
|
+
await access2(join13(bundleDir, name), fsConstants2.F_OK);
|
|
11662
11659
|
anyFlat = true;
|
|
11663
11660
|
break;
|
|
11664
11661
|
} catch {
|
|
@@ -11666,10 +11663,10 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
11666
11663
|
}
|
|
11667
11664
|
if (!anyFlat)
|
|
11668
11665
|
return;
|
|
11669
|
-
await
|
|
11666
|
+
await mkdir18(innerLib, { recursive: true });
|
|
11670
11667
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
11671
11668
|
try {
|
|
11672
|
-
await
|
|
11669
|
+
await access2(join13(innerLib, name), fsConstants2.F_OK);
|
|
11673
11670
|
continue;
|
|
11674
11671
|
} catch {
|
|
11675
11672
|
}
|
|
@@ -11684,7 +11681,7 @@ async function writeBinForwarders(layout, activeBundleRoot) {
|
|
|
11684
11681
|
for (const [name, tool] of entries) {
|
|
11685
11682
|
const target = join13(activeBundleRoot, "bin", tool);
|
|
11686
11683
|
try {
|
|
11687
|
-
await
|
|
11684
|
+
await access2(target, fsConstants2.X_OK);
|
|
11688
11685
|
} catch {
|
|
11689
11686
|
continue;
|
|
11690
11687
|
}
|
|
@@ -11697,7 +11694,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
11697
11694
|
const forwarderHealthy = async (name, tool) => {
|
|
11698
11695
|
let current;
|
|
11699
11696
|
try {
|
|
11700
|
-
current = await
|
|
11697
|
+
current = await readFile11(join13(layout.binDir, name), "utf8");
|
|
11701
11698
|
} catch {
|
|
11702
11699
|
current = void 0;
|
|
11703
11700
|
}
|
|
@@ -11710,7 +11707,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
11710
11707
|
}
|
|
11711
11708
|
if (healthy)
|
|
11712
11709
|
return false;
|
|
11713
|
-
await
|
|
11710
|
+
await mkdir18(layout.binDir, { recursive: true });
|
|
11714
11711
|
await writeBinForwarders(layout, layout.libDir);
|
|
11715
11712
|
opts.logger?.(`[body] self-update: repaired <prefix>/bin forwarders to follow the active-bundle anchor (${layout.libDir})`);
|
|
11716
11713
|
return true;
|
|
@@ -11725,14 +11722,14 @@ async function rollbackToPreviousRelease(layout, previousReleaseId) {
|
|
|
11725
11722
|
await rm6(tempLink, { force: true });
|
|
11726
11723
|
await symlink3(join13("beeline-releases", previousReleaseId), tempLink);
|
|
11727
11724
|
await rename6(tempLink, layout.libDir);
|
|
11728
|
-
await fsyncDir(
|
|
11725
|
+
await fsyncDir(dirname16(layout.libDir));
|
|
11729
11726
|
}
|
|
11730
11727
|
function updateAttemptPath(layout) {
|
|
11731
11728
|
return join13(layout.releasesRoot, ".state", "update-attempt.json");
|
|
11732
11729
|
}
|
|
11733
11730
|
async function readUpdateAttempt(layout) {
|
|
11734
11731
|
try {
|
|
11735
|
-
const raw = JSON.parse(await
|
|
11732
|
+
const raw = JSON.parse(await readFile11(updateAttemptPath(layout), "utf8"));
|
|
11736
11733
|
if (raw.version !== 1 || typeof raw.appliedAt !== "number" || typeof raw.confirmBy !== "number" || typeof raw.releaseId !== "string" || !["pending", "confirmed", "reverted"].includes(raw.status)) {
|
|
11737
11734
|
return void 0;
|
|
11738
11735
|
}
|
|
@@ -11742,7 +11739,7 @@ async function readUpdateAttempt(layout) {
|
|
|
11742
11739
|
}
|
|
11743
11740
|
}
|
|
11744
11741
|
async function writeUpdateAttempt(layout, record3) {
|
|
11745
|
-
await
|
|
11742
|
+
await mkdir18(join13(layout.releasesRoot, ".state"), { recursive: true });
|
|
11746
11743
|
const path = updateAttemptPath(layout);
|
|
11747
11744
|
const staged = `${path}.${process.pid}.tmp`;
|
|
11748
11745
|
await writeFile11(staged, `${JSON.stringify(record3, null, 2)}
|
|
@@ -11790,7 +11787,7 @@ function describeIdentity(identity) {
|
|
|
11790
11787
|
parts.push(identity.commit.slice(0, 12));
|
|
11791
11788
|
return parts.join(" ") || "unknown";
|
|
11792
11789
|
}
|
|
11793
|
-
var RELEASES_SEGMENT, BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, FORWARDER_TOOLS, FORWARDER_ALIASES, LEGACY_FLAT_BUNDLE_FILES, DEFAULT_UPDATE_CONFIRM_WINDOW_MS, SelfUpdateManager;
|
|
11790
|
+
var RELEASES_SEGMENT, BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, CODEGRAPH_ENTRYPOINT, FORWARDER_TOOLS, FORWARDER_ALIASES, LEGACY_FLAT_BUNDLE_FILES, DEFAULT_UPDATE_CONFIRM_WINDOW_MS, SelfUpdateManager;
|
|
11794
11791
|
var init_self_update = __esm({
|
|
11795
11792
|
"apps/body/dist/self-update.js"() {
|
|
11796
11793
|
"use strict";
|
|
@@ -11798,7 +11795,14 @@ var init_self_update = __esm({
|
|
|
11798
11795
|
RELEASES_SEGMENT = "beeline-releases";
|
|
11799
11796
|
BUNDLE_ENTRYPOINT = "lib/beeline/beeline-cli.mjs";
|
|
11800
11797
|
PI_MCP_ADAPTER_ENTRYPOINT = "lib/beeline/pi-mcp-adapter.mjs";
|
|
11801
|
-
|
|
11798
|
+
CODEGRAPH_ENTRYPOINT = "lib/beeline/codegraph/bin/codegraph";
|
|
11799
|
+
FORWARDER_TOOLS = [
|
|
11800
|
+
"beeline",
|
|
11801
|
+
"buzz-agent",
|
|
11802
|
+
"buzz-dev-mcp",
|
|
11803
|
+
"beeline-readonly-mcp",
|
|
11804
|
+
"codegraph"
|
|
11805
|
+
];
|
|
11802
11806
|
FORWARDER_ALIASES = {
|
|
11803
11807
|
usebeeline: "beeline"
|
|
11804
11808
|
};
|
|
@@ -12020,7 +12024,7 @@ function configureNetworkFamilyDefaults(network = net) {
|
|
|
12020
12024
|
configureNetworkFamilyDefaults();
|
|
12021
12025
|
|
|
12022
12026
|
// apps/body/dist/cli.js
|
|
12023
|
-
import { dirname as
|
|
12027
|
+
import { dirname as dirname23, resolve as resolve35 } from "node:path";
|
|
12024
12028
|
import { stdin as stdin4, stdout as stdout5 } from "node:process";
|
|
12025
12029
|
|
|
12026
12030
|
// node_modules/@clack/core/dist/index.mjs
|
|
@@ -13469,7 +13473,7 @@ function parseCursorModelsOutput(output) {
|
|
|
13469
13473
|
};
|
|
13470
13474
|
}
|
|
13471
13475
|
async function enumerateCursorModels(env = process.env) {
|
|
13472
|
-
return new Promise((
|
|
13476
|
+
return new Promise((resolve36) => {
|
|
13473
13477
|
const child = spawn("cursor-agent", ["models"], {
|
|
13474
13478
|
env,
|
|
13475
13479
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -13483,11 +13487,11 @@ async function enumerateCursorModels(env = process.env) {
|
|
|
13483
13487
|
});
|
|
13484
13488
|
child.on("error", () => {
|
|
13485
13489
|
clearTimeout(timer);
|
|
13486
|
-
|
|
13490
|
+
resolve36(void 0);
|
|
13487
13491
|
});
|
|
13488
13492
|
child.on("close", (code) => {
|
|
13489
13493
|
clearTimeout(timer);
|
|
13490
|
-
|
|
13494
|
+
resolve36(code === 0 ? parseCursorModelsOutput(output) : void 0);
|
|
13491
13495
|
});
|
|
13492
13496
|
});
|
|
13493
13497
|
}
|
|
@@ -13914,13 +13918,13 @@ var CursorAcpServer = class {
|
|
|
13914
13918
|
consumeLine(line);
|
|
13915
13919
|
});
|
|
13916
13920
|
try {
|
|
13917
|
-
const { code, signal } = await new Promise((
|
|
13921
|
+
const { code, signal } = await new Promise((resolve36) => {
|
|
13918
13922
|
let settled = false;
|
|
13919
13923
|
const finish = (exitCode, exitSignal) => {
|
|
13920
13924
|
if (settled)
|
|
13921
13925
|
return;
|
|
13922
13926
|
settled = true;
|
|
13923
|
-
|
|
13927
|
+
resolve36({ code: exitCode, signal: exitSignal });
|
|
13924
13928
|
};
|
|
13925
13929
|
child.once("error", (error) => {
|
|
13926
13930
|
spawnError = error instanceof Error ? error.message : String(error);
|
|
@@ -14364,7 +14368,10 @@ function resolveReadonlyMcpCommand(env = process.env) {
|
|
|
14364
14368
|
throw new Error("read-only tools unavailable: beeline-readonly-mcp was not found. Reinstall Beeline or set BEELINE_READONLY_MCP_BIN / BEELINE_READONLY_MCP_SCRIPT");
|
|
14365
14369
|
}
|
|
14366
14370
|
function resolveCodegraphCommand(env = process.env) {
|
|
14367
|
-
const
|
|
14371
|
+
const bundledBinary = commandInRunningBundle("codegraph");
|
|
14372
|
+
if (bundledBinary)
|
|
14373
|
+
return bundledBinary;
|
|
14374
|
+
const configured = env.BEELINE_CODEGRAPH_BIN ?? env.BUZZ_CODEGRAPH_BIN;
|
|
14368
14375
|
if (configured) {
|
|
14369
14376
|
try {
|
|
14370
14377
|
accessSync2(configured, constants2.X_OK);
|
|
@@ -15172,6 +15179,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15172
15179
|
osSandbox;
|
|
15173
15180
|
permissionHandler;
|
|
15174
15181
|
permissionAllowlist;
|
|
15182
|
+
commandsHandler;
|
|
15175
15183
|
constructor(opts) {
|
|
15176
15184
|
super();
|
|
15177
15185
|
const command = opts.agentCommand ?? opts.agentBinary;
|
|
@@ -15188,6 +15196,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15188
15196
|
this.osSandbox = opts.osSandbox ?? false;
|
|
15189
15197
|
this.permissionHandler = opts.permissionHandler;
|
|
15190
15198
|
this.permissionAllowlist = opts.permissionAllowlist;
|
|
15199
|
+
this.commandsHandler = opts.onCommands;
|
|
15191
15200
|
}
|
|
15192
15201
|
async start(timeoutMs = 6e4) {
|
|
15193
15202
|
if (this.alive)
|
|
@@ -15562,6 +15571,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15562
15571
|
if (params.update.sessionUpdate === "available_commands_update") {
|
|
15563
15572
|
const commands = parseAvailableCommands(params.update.availableCommands);
|
|
15564
15573
|
this.sessionCommands.set(params.sessionId, commands);
|
|
15574
|
+
this.commandsHandler?.(commands);
|
|
15565
15575
|
this.emit("commands", { sessionId: params.sessionId, commands });
|
|
15566
15576
|
}
|
|
15567
15577
|
const toolCallId = params.update.toolCallId;
|
|
@@ -15609,7 +15619,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15609
15619
|
const current = this.activeRunIds.get(sessionId);
|
|
15610
15620
|
if (current)
|
|
15611
15621
|
return Promise.resolve(current);
|
|
15612
|
-
return new Promise((
|
|
15622
|
+
return new Promise((resolve36, reject) => {
|
|
15613
15623
|
const onUpdate = (update) => {
|
|
15614
15624
|
if (update.sessionId !== sessionId)
|
|
15615
15625
|
return;
|
|
@@ -15617,7 +15627,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15617
15627
|
if (!runId)
|
|
15618
15628
|
return;
|
|
15619
15629
|
cleanup();
|
|
15620
|
-
|
|
15630
|
+
resolve36(runId);
|
|
15621
15631
|
};
|
|
15622
15632
|
const timer = setTimeout(() => {
|
|
15623
15633
|
cleanup();
|
|
@@ -15701,13 +15711,13 @@ var AcpClient = class extends EventEmitter {
|
|
|
15701
15711
|
}
|
|
15702
15712
|
const id = this.nextId++;
|
|
15703
15713
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
15704
|
-
return new Promise((
|
|
15714
|
+
return new Promise((resolve36, reject) => {
|
|
15705
15715
|
const timer = setTimeout(() => {
|
|
15706
15716
|
this.pending.delete(id);
|
|
15707
15717
|
reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart), detail));
|
|
15708
15718
|
}, timeoutMs);
|
|
15709
15719
|
this.pending.set(id, {
|
|
15710
|
-
resolve:
|
|
15720
|
+
resolve: resolve36,
|
|
15711
15721
|
reject,
|
|
15712
15722
|
timer,
|
|
15713
15723
|
method,
|
|
@@ -16226,8 +16236,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
16226
16236
|
}
|
|
16227
16237
|
|
|
16228
16238
|
// apps/body/dist/connector-google.js
|
|
16229
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
16230
|
-
import { dirname as
|
|
16239
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
16240
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
16231
16241
|
|
|
16232
16242
|
// apps/body/dist/google-workspace-client.js
|
|
16233
16243
|
function credentialsTokenSource(credentials) {
|
|
@@ -16601,14 +16611,170 @@ function googleWorkspaceClient(tokenSource, transport = defaultGoogleApiTranspor
|
|
|
16601
16611
|
};
|
|
16602
16612
|
}
|
|
16603
16613
|
|
|
16614
|
+
// apps/body/dist/connector-google.js
|
|
16615
|
+
var GOOGLE_TOOL_SCOPES = {
|
|
16616
|
+
"google-gmail": [
|
|
16617
|
+
"https://www.googleapis.com/auth/gmail.send",
|
|
16618
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
|
16619
|
+
"https://www.googleapis.com/auth/gmail.compose"
|
|
16620
|
+
],
|
|
16621
|
+
"google-calendar": ["https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.readonly"],
|
|
16622
|
+
"google-drive": ["https://www.googleapis.com/auth/drive.readonly"],
|
|
16623
|
+
"google-youtube": [
|
|
16624
|
+
"https://www.googleapis.com/auth/youtube.readonly",
|
|
16625
|
+
"https://www.googleapis.com/auth/yt-analytics.readonly"
|
|
16626
|
+
]
|
|
16627
|
+
};
|
|
16628
|
+
function isGoogleToolConnectorType(type) {
|
|
16629
|
+
return type in GOOGLE_TOOL_SCOPES;
|
|
16630
|
+
}
|
|
16631
|
+
async function readGoogleCredentialsFromVault(mcp) {
|
|
16632
|
+
if (!mcp)
|
|
16633
|
+
return { source: "unavailable", reason: "no Trusty Squire connector is paired" };
|
|
16634
|
+
let raw;
|
|
16635
|
+
try {
|
|
16636
|
+
raw = await mcp.call("google_oauth_credentials", {});
|
|
16637
|
+
} catch (error) {
|
|
16638
|
+
const detail = describe(error);
|
|
16639
|
+
return {
|
|
16640
|
+
source: "unavailable",
|
|
16641
|
+
reason: `Squire has no Google OAuth grant on record yet (connect your Google account in Squire first: ${detail})`
|
|
16642
|
+
};
|
|
16643
|
+
}
|
|
16644
|
+
const record3 = raw && typeof raw === "object" ? raw : {};
|
|
16645
|
+
const inner = record3.credentials && typeof record3.credentials === "object" ? record3.credentials : record3;
|
|
16646
|
+
const accessToken = inner.accessToken ?? inner.access_token;
|
|
16647
|
+
if (!accessToken || typeof accessToken !== "string") {
|
|
16648
|
+
return { source: "unavailable", reason: "Squire returned a Google grant without an access token" };
|
|
16649
|
+
}
|
|
16650
|
+
return {
|
|
16651
|
+
source: "squire",
|
|
16652
|
+
credentials: {
|
|
16653
|
+
accessToken,
|
|
16654
|
+
refreshToken: typeof record3.refreshToken === "string" ? record3.refreshToken : typeof record3.refresh_token === "string" ? record3.refresh_token : void 0,
|
|
16655
|
+
expiresAt: typeof record3.expiresAt === "number" ? record3.expiresAt : void 0,
|
|
16656
|
+
accountEmail: typeof record3.accountEmail === "string" ? record3.accountEmail : typeof record3.email === "string" ? record3.email : void 0
|
|
16657
|
+
}
|
|
16658
|
+
};
|
|
16659
|
+
}
|
|
16660
|
+
function manualGoogleCredentialsSearchPaths(home) {
|
|
16661
|
+
return [join3(home, "google-credentials.json")];
|
|
16662
|
+
}
|
|
16663
|
+
function loadManualGoogleCredentials(home, env = process.env) {
|
|
16664
|
+
if (env.BEELINE_GOOGLE_ACCESS_TOKEN) {
|
|
16665
|
+
return {
|
|
16666
|
+
source: "manual",
|
|
16667
|
+
credentials: { accessToken: env.BEELINE_GOOGLE_ACCESS_TOKEN }
|
|
16668
|
+
};
|
|
16669
|
+
}
|
|
16670
|
+
for (const path of manualGoogleCredentialsSearchPaths(home)) {
|
|
16671
|
+
try {
|
|
16672
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
16673
|
+
if (typeof parsed.accessToken === "string" || typeof parsed.access_token === "string") {
|
|
16674
|
+
return {
|
|
16675
|
+
source: "manual",
|
|
16676
|
+
credentials: {
|
|
16677
|
+
accessToken: parsed.accessToken ?? parsed.access_token,
|
|
16678
|
+
refreshToken: typeof parsed.refreshToken === "string" ? parsed.refreshToken : void 0,
|
|
16679
|
+
expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : void 0,
|
|
16680
|
+
accountEmail: typeof parsed.accountEmail === "string" ? parsed.accountEmail : void 0
|
|
16681
|
+
}
|
|
16682
|
+
};
|
|
16683
|
+
}
|
|
16684
|
+
} catch {
|
|
16685
|
+
}
|
|
16686
|
+
}
|
|
16687
|
+
return {
|
|
16688
|
+
source: "manual-missing",
|
|
16689
|
+
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.`
|
|
16690
|
+
};
|
|
16691
|
+
}
|
|
16692
|
+
function persistManualGoogleCredentials(home, credentials) {
|
|
16693
|
+
const path = manualGoogleCredentialsSearchPaths(home)[0];
|
|
16694
|
+
mkdirSync2(dirname4(path), { recursive: true, mode: 448 });
|
|
16695
|
+
writeFileSync(path, `${JSON.stringify({
|
|
16696
|
+
accessToken: credentials.accessToken,
|
|
16697
|
+
...credentials.refreshToken ? { refreshToken: credentials.refreshToken } : {},
|
|
16698
|
+
...credentials.expiresAt ? { expiresAt: credentials.expiresAt } : {},
|
|
16699
|
+
...credentials.accountEmail ? { accountEmail: credentials.accountEmail } : {}
|
|
16700
|
+
})}
|
|
16701
|
+
`, { encoding: "utf8", mode: 384 });
|
|
16702
|
+
return path;
|
|
16703
|
+
}
|
|
16704
|
+
var step = (label, status, extra) => ({
|
|
16705
|
+
label,
|
|
16706
|
+
status,
|
|
16707
|
+
...extra
|
|
16708
|
+
});
|
|
16709
|
+
function outputTail(text2, maxChars = 800) {
|
|
16710
|
+
const trimmed = text2.trim();
|
|
16711
|
+
return trimmed.length <= maxChars ? trimmed : `\u2026${trimmed.slice(-maxChars)}`;
|
|
16712
|
+
}
|
|
16713
|
+
async function installGoogleTool(options) {
|
|
16714
|
+
const steps = [step("helper reached", "done")];
|
|
16715
|
+
const emit = () => options.onProgress?.([...steps]);
|
|
16716
|
+
const push = (next) => {
|
|
16717
|
+
steps.push(next);
|
|
16718
|
+
emit();
|
|
16719
|
+
};
|
|
16720
|
+
const fail = (label, reason, output) => {
|
|
16721
|
+
push(step(label, "failed", { reason, ...output ? { output } : {} }));
|
|
16722
|
+
return { status: "error", steps, errorMessage: reason };
|
|
16723
|
+
};
|
|
16724
|
+
emit();
|
|
16725
|
+
if (!isGoogleToolConnectorType(options.connectorType)) {
|
|
16726
|
+
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
16727
|
+
}
|
|
16728
|
+
push(step("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
16729
|
+
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
16730
|
+
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
16731
|
+
if (oneClick.source === "squire")
|
|
16732
|
+
return oneClick;
|
|
16733
|
+
return loadManualGoogleCredentials(options.home, options.env);
|
|
16734
|
+
})();
|
|
16735
|
+
if (!("credentials" in resolved)) {
|
|
16736
|
+
const reason = resolved.reason;
|
|
16737
|
+
steps[1] = step("Google credentials resolved", "failed", { reason, output: outputTail(reason) });
|
|
16738
|
+
emit();
|
|
16739
|
+
return { status: "error", steps, errorMessage: reason };
|
|
16740
|
+
}
|
|
16741
|
+
steps[1] = step("Google credentials resolved", "done", {
|
|
16742
|
+
output: resolved.source === "squire" ? "one-click: read the Google grant from Trusty Squire" : "manual: local google-credentials.json"
|
|
16743
|
+
});
|
|
16744
|
+
emit();
|
|
16745
|
+
const client = options.client ?? googleWorkspaceClient(refreshableTokenSource(resolved.credentials, options.env?.BEELINE_GOOGLE_CLIENT_ID, options.env?.BEELINE_GOOGLE_CLIENT_SECRET));
|
|
16746
|
+
push(step("authorized with Google", "running", { output: "verifying the grant with Google\u2026" }));
|
|
16747
|
+
const verify = await client.verify();
|
|
16748
|
+
if (!verify.ok) {
|
|
16749
|
+
return fail("authorized with Google", verify.reason, outputTail(verify.reason));
|
|
16750
|
+
}
|
|
16751
|
+
steps[2] = step("authorized with Google", "done", {
|
|
16752
|
+
...verify.account ? { output: `signed in as ${verify.account}` } : {}
|
|
16753
|
+
});
|
|
16754
|
+
emit();
|
|
16755
|
+
push(step("tools enabled", "done", {
|
|
16756
|
+
output: `${GOOGLE_TOOL_SCOPES[options.connectorType].length} Google scopes granted`
|
|
16757
|
+
}));
|
|
16758
|
+
persistManualGoogleCredentials(options.home, resolved.credentials);
|
|
16759
|
+
return {
|
|
16760
|
+
status: "connected",
|
|
16761
|
+
steps,
|
|
16762
|
+
...verify.account ? { signedInAs: verify.account } : {}
|
|
16763
|
+
};
|
|
16764
|
+
}
|
|
16765
|
+
function describe(error) {
|
|
16766
|
+
return error instanceof Error ? error.message : String(error);
|
|
16767
|
+
}
|
|
16768
|
+
|
|
16604
16769
|
// apps/body/dist/connector-squire.js
|
|
16605
16770
|
import { execFile, spawn as spawn5 } from "node:child_process";
|
|
16606
16771
|
import { createHash as createHash2 } from "node:crypto";
|
|
16607
|
-
import { lstatSync as lstatSync3, readFileSync as
|
|
16772
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync4, realpathSync, rmSync } from "node:fs";
|
|
16608
16773
|
import { homedir as homedir3, hostname, tmpdir as tmpdir2 } from "node:os";
|
|
16609
|
-
import { basename as basename4, dirname as
|
|
16774
|
+
import { basename as basename4, dirname as dirname5, join as join4, resolve as resolve7 } from "node:path";
|
|
16610
16775
|
var SQUIRE_MCP_NAME = "@trusty-squire/mcp";
|
|
16611
|
-
var
|
|
16776
|
+
var SQUIRE_CONNECT_VERSION = "1.1.16-rc.4";
|
|
16777
|
+
var SQUIRE_CONNECT_PACKAGE = `${SQUIRE_MCP_NAME}@${SQUIRE_CONNECT_VERSION}`;
|
|
16612
16778
|
var activeConnectSession;
|
|
16613
16779
|
function squireConnectSession() {
|
|
16614
16780
|
return activeConnectSession;
|
|
@@ -16644,9 +16810,9 @@ function squireProfilePathIdentity(profileDir) {
|
|
|
16644
16810
|
let candidate = absolute;
|
|
16645
16811
|
for (; ; ) {
|
|
16646
16812
|
try {
|
|
16647
|
-
return
|
|
16813
|
+
return join4(realpathSync.native(candidate), ...suffix.reverse());
|
|
16648
16814
|
} catch {
|
|
16649
|
-
const parent =
|
|
16815
|
+
const parent = dirname5(candidate);
|
|
16650
16816
|
if (parent === candidate)
|
|
16651
16817
|
return absolute;
|
|
16652
16818
|
suffix.push(basename4(candidate));
|
|
@@ -16656,20 +16822,14 @@ function squireProfilePathIdentity(profileDir) {
|
|
|
16656
16822
|
}
|
|
16657
16823
|
function squireProfileLockPath(profileDir = squireChromeProfileDir(), lockRoot = tmpdir2()) {
|
|
16658
16824
|
const digest = createHash2("sha256").update(squireProfilePathIdentity(profileDir)).digest("hex").slice(0, 24);
|
|
16659
|
-
return
|
|
16660
|
-
}
|
|
16661
|
-
var SQUIRE_BROWSER_BUSY_GOOGLE_REASON = "Trusty Squire is still using the browser \u2014 connect Trusty Squire first";
|
|
16662
|
-
function isSquireBrowserSessionFailure(text2) {
|
|
16663
|
-
if (typeof text2 !== "string" || text2.length === 0)
|
|
16664
|
-
return false;
|
|
16665
|
-
return /Trusty Squire[\s\S]{0,80}browser/i.test(text2) || /browser[\s\S]{0,80}Trusty Squire/i.test(text2);
|
|
16825
|
+
return join4(lockRoot, `trusty-squire-profile-${digest}.lock`);
|
|
16666
16826
|
}
|
|
16667
16827
|
function squireForeignClaimAction(owner) {
|
|
16668
16828
|
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.`;
|
|
16669
16829
|
}
|
|
16670
16830
|
function readLinuxStartTime(pid) {
|
|
16671
16831
|
try {
|
|
16672
|
-
const stat5 =
|
|
16832
|
+
const stat5 = readFileSync4(`/proc/${pid}/stat`, "utf8");
|
|
16673
16833
|
const close = stat5.lastIndexOf(")");
|
|
16674
16834
|
return close < 0 ? void 0 : stat5.slice(close + 2).split(" ")[19];
|
|
16675
16835
|
} catch {
|
|
@@ -16688,8 +16848,8 @@ function lockOwnerIsAlive(owner) {
|
|
|
16688
16848
|
}
|
|
16689
16849
|
function readLockFileOwner(lockPath) {
|
|
16690
16850
|
try {
|
|
16691
|
-
const target = lstatSync3(lockPath).isDirectory() ?
|
|
16692
|
-
const parsed = JSON.parse(
|
|
16851
|
+
const target = lstatSync3(lockPath).isDirectory() ? join4(lockPath, "owner.json") : lockPath;
|
|
16852
|
+
const parsed = JSON.parse(readFileSync4(target, "utf8"));
|
|
16693
16853
|
if (typeof parsed.host !== "string" || typeof parsed.pid !== "number")
|
|
16694
16854
|
return void 0;
|
|
16695
16855
|
return {
|
|
@@ -16727,10 +16887,10 @@ function reclaimSquireProfileClaim(options) {
|
|
|
16727
16887
|
options?.log?.(`cleared dead trusty-squire on-disk browser claim (pid ${owner.pid} gone)`);
|
|
16728
16888
|
return { kind: "reclaimed-dead", owner };
|
|
16729
16889
|
}
|
|
16730
|
-
var defaultShellRunner = (command, args) => new Promise((
|
|
16890
|
+
var defaultShellRunner = (command, args) => new Promise((resolve36) => {
|
|
16731
16891
|
execFile(command, [...args], { timeout: 12e4, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (error, stdout6, stderr) => {
|
|
16732
16892
|
const code = error?.code;
|
|
16733
|
-
|
|
16893
|
+
resolve36({
|
|
16734
16894
|
code: typeof code === "number" ? code : error ? 1 : 0,
|
|
16735
16895
|
stdout: String(stdout6 ?? ""),
|
|
16736
16896
|
stderr: String(stderr ?? "")
|
|
@@ -16755,7 +16915,7 @@ function killConnectTree(child) {
|
|
|
16755
16915
|
}
|
|
16756
16916
|
child.kill();
|
|
16757
16917
|
}
|
|
16758
|
-
var defaultStreamedRunner = (command, args, env) => new Promise((
|
|
16918
|
+
var defaultStreamedRunner = (command, args, env) => new Promise((resolve36) => {
|
|
16759
16919
|
const child = spawn5(command, args, {
|
|
16760
16920
|
stdio: ["ignore", "pipe", "pipe"],
|
|
16761
16921
|
env: env ?? squireConnectProcessEnv(),
|
|
@@ -16764,10 +16924,12 @@ var defaultStreamedRunner = (command, args, env) => new Promise((resolve35) => {
|
|
|
16764
16924
|
let stdout6 = "";
|
|
16765
16925
|
let stderr = "";
|
|
16766
16926
|
let resolved = false;
|
|
16927
|
+
let consumed = 0;
|
|
16928
|
+
let report;
|
|
16767
16929
|
const safetyTimer = setTimeout(() => {
|
|
16768
16930
|
if (!resolved) {
|
|
16769
16931
|
resolved = true;
|
|
16770
|
-
|
|
16932
|
+
resolve36({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort: () => {
|
|
16771
16933
|
} });
|
|
16772
16934
|
}
|
|
16773
16935
|
killConnectTree(child);
|
|
@@ -16785,128 +16947,163 @@ var defaultStreamedRunner = (command, args, env) => new Promise((resolve35) => {
|
|
|
16785
16947
|
const finish = (result) => {
|
|
16786
16948
|
if (!resolved) {
|
|
16787
16949
|
resolved = true;
|
|
16788
|
-
|
|
16950
|
+
resolve36(result);
|
|
16789
16951
|
}
|
|
16790
16952
|
};
|
|
16791
16953
|
const settle = (result) => {
|
|
16792
16954
|
clearTimeout(safetyTimer);
|
|
16793
16955
|
finish(result);
|
|
16794
16956
|
};
|
|
16795
|
-
const
|
|
16796
|
-
const
|
|
16797
|
-
|
|
16798
|
-
|
|
16957
|
+
const consumeReports = (final) => {
|
|
16958
|
+
const pending = stdout6.slice(consumed);
|
|
16959
|
+
const lastNewline = pending.lastIndexOf("\n");
|
|
16960
|
+
if (lastNewline < 0 && !final)
|
|
16961
|
+
return;
|
|
16962
|
+
const complete = lastNewline < 0 ? pending : pending.slice(0, lastNewline);
|
|
16963
|
+
for (const line of complete.split("\n")) {
|
|
16964
|
+
if (line.trim() === "")
|
|
16965
|
+
continue;
|
|
16966
|
+
const parsed = parseConnectReport(line);
|
|
16967
|
+
if (parsed)
|
|
16968
|
+
report = parsed;
|
|
16969
|
+
}
|
|
16970
|
+
consumed += lastNewline < 0 ? pending.length : lastNewline + 1;
|
|
16971
|
+
};
|
|
16972
|
+
const publishSignIn = () => {
|
|
16973
|
+
consumeReports(false);
|
|
16974
|
+
if (report && isPublishableConnectReport(report)) {
|
|
16975
|
+
finish({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort });
|
|
16799
16976
|
}
|
|
16800
16977
|
};
|
|
16801
|
-
const finalSignIn = () => parseConnectOutput(`${stdout6}
|
|
16802
|
-
`) ?? parseConnectOutput(`${stderr}
|
|
16803
|
-
`);
|
|
16804
16978
|
child.stdout?.on("data", (chunk) => {
|
|
16805
16979
|
stdout6 += String(chunk);
|
|
16806
|
-
|
|
16980
|
+
publishSignIn();
|
|
16807
16981
|
});
|
|
16808
16982
|
child.stderr?.on("data", (chunk) => {
|
|
16809
16983
|
stderr += String(chunk);
|
|
16810
|
-
checkOutput();
|
|
16811
16984
|
});
|
|
16812
16985
|
child.on("close", () => {
|
|
16986
|
+
consumeReports(true);
|
|
16813
16987
|
settle({
|
|
16814
16988
|
stdout: stdout6,
|
|
16815
16989
|
stderr,
|
|
16816
16990
|
pid: child.pid ?? void 0,
|
|
16817
|
-
|
|
16991
|
+
report,
|
|
16818
16992
|
abort: () => {
|
|
16819
16993
|
}
|
|
16820
16994
|
});
|
|
16821
16995
|
});
|
|
16822
16996
|
child.on("error", () => {
|
|
16823
|
-
settle({ stdout: stdout6, stderr, pid: child.pid ?? void 0,
|
|
16997
|
+
settle({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort: () => {
|
|
16824
16998
|
} });
|
|
16825
16999
|
});
|
|
16826
17000
|
});
|
|
16827
|
-
var
|
|
17001
|
+
var step2 = (label, status, reason) => ({
|
|
16828
17002
|
label,
|
|
16829
17003
|
status,
|
|
16830
17004
|
...reason ? { reason } : {}
|
|
16831
17005
|
});
|
|
16832
|
-
function
|
|
16833
|
-
|
|
16834
|
-
const parsed = new URL(url);
|
|
16835
|
-
if (parsed.protocol !== "https:")
|
|
16836
|
-
return false;
|
|
16837
|
-
return parsed.hash.startsWith("#p=") || /(^|\/)install(\/|$)/.test(parsed.pathname);
|
|
16838
|
-
} catch {
|
|
16839
|
-
return false;
|
|
16840
|
-
}
|
|
17006
|
+
function isRecord(value) {
|
|
17007
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16841
17008
|
}
|
|
16842
|
-
function
|
|
17009
|
+
function connectAccount(raw) {
|
|
17010
|
+
if (!isRecord(raw) || typeof raw.id !== "string")
|
|
17011
|
+
return null;
|
|
17012
|
+
return {
|
|
17013
|
+
id: raw.id,
|
|
17014
|
+
// An absent or unreadable `providers` is `null` (could not look), never
|
|
17015
|
+
// `[]` (looked and found nothing).
|
|
17016
|
+
providers: Array.isArray(raw.providers) ? raw.providers.filter((provider) => typeof provider === "string") : null
|
|
17017
|
+
};
|
|
17018
|
+
}
|
|
17019
|
+
function parseConnectReport(line) {
|
|
17020
|
+
let parsed;
|
|
16843
17021
|
try {
|
|
16844
|
-
|
|
16845
|
-
const host = parsed.hostname.toLowerCase();
|
|
16846
|
-
if (host === "developers.cloudflare.com")
|
|
16847
|
-
return true;
|
|
16848
|
-
if (host === "github.com" && parsed.pathname.toLowerCase().startsWith("/npm/cli"))
|
|
16849
|
-
return true;
|
|
16850
|
-
return host === "npmjs.com" || host === "www.npmjs.com";
|
|
17022
|
+
parsed = JSON.parse(line);
|
|
16851
17023
|
} catch {
|
|
16852
|
-
return
|
|
17024
|
+
return void 0;
|
|
16853
17025
|
}
|
|
17026
|
+
if (!isRecord(parsed) || typeof parsed.state !== "string")
|
|
17027
|
+
return void 0;
|
|
17028
|
+
return {
|
|
17029
|
+
state: parsed.state,
|
|
17030
|
+
terminal: parsed.terminal === true,
|
|
17031
|
+
reason: typeof parsed.reason === "string" ? parsed.reason : null,
|
|
17032
|
+
sign_in_url: typeof parsed.sign_in_url === "string" ? parsed.sign_in_url : null,
|
|
17033
|
+
account: connectAccount(parsed.account),
|
|
17034
|
+
holder: parsed.holder,
|
|
17035
|
+
browser_location: parsed.browser_location
|
|
17036
|
+
};
|
|
16854
17037
|
}
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
const
|
|
16859
|
-
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
17038
|
+
function isOutstandingSignIn(report) {
|
|
17039
|
+
if (report.terminal || report.state !== "needs-sign-in" || !report.sign_in_url)
|
|
17040
|
+
return false;
|
|
17041
|
+
const location = report.browser_location;
|
|
17042
|
+
if (!isRecord(location) || typeof location.kind !== "string")
|
|
17043
|
+
return false;
|
|
17044
|
+
return location.kind !== "none" && location.kind !== "unreachable";
|
|
17045
|
+
}
|
|
17046
|
+
function isPublishableConnectReport(report) {
|
|
17047
|
+
return report.terminal || isOutstandingSignIn(report);
|
|
17048
|
+
}
|
|
17049
|
+
var CONNECT_REASON_LINES = {
|
|
17050
|
+
provider_session_missing: "the bot's Chrome profile has no live provider session",
|
|
17051
|
+
requested_provider_missing: "the provider sign-in you asked to refresh did not complete",
|
|
17052
|
+
account_mismatch: "this machine is bound to a different account",
|
|
17053
|
+
profile_unverifiable: "the bot's Chrome profile could not be verified",
|
|
17054
|
+
install_expired: "the sign-in page expired before it was used",
|
|
17055
|
+
cached_cookie_evidence: "the shared profile already carried the session",
|
|
17056
|
+
run_failed: "the connect run failed before it could finish"
|
|
17057
|
+
};
|
|
17058
|
+
function holderLine(raw) {
|
|
17059
|
+
if (!isRecord(raw))
|
|
17060
|
+
return void 0;
|
|
17061
|
+
if (raw.kind === "other") {
|
|
17062
|
+
const pid = typeof raw.pid === "number" ? raw.pid : void 0;
|
|
17063
|
+
return pid === void 0 ? "another Trusty Squire session is using the browser" : squireForeignClaimAction({ host: hostname(), pid, startTime: null });
|
|
16872
17064
|
}
|
|
16873
|
-
if (
|
|
16874
|
-
|
|
16875
|
-
return
|
|
17065
|
+
if (raw.kind === "unknown")
|
|
17066
|
+
return "another process may be using the browser";
|
|
17067
|
+
return void 0;
|
|
16876
17068
|
}
|
|
16877
|
-
function
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
if (framed) {
|
|
16883
|
-
box.push(framed[1] ?? "");
|
|
16884
|
-
continue;
|
|
16885
|
-
}
|
|
16886
|
-
if (box.length > 0 && BOX_BORDER.test(row))
|
|
16887
|
-
lines.push(...rejoinBoxRows(box));
|
|
16888
|
-
box = [];
|
|
16889
|
-
lines.push(row);
|
|
17069
|
+
function connectBlockedLine(report) {
|
|
17070
|
+
if (report.state === "connected" || isOutstandingSignIn(report))
|
|
17071
|
+
return void 0;
|
|
17072
|
+
if (report.reason && CONNECT_REASON_LINES[report.reason]) {
|
|
17073
|
+
return CONNECT_REASON_LINES[report.reason];
|
|
16890
17074
|
}
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
16894
|
-
const urls = unframeBoxedOutput(output).match(/https:\/\/[^\s"'<>]+(?=[\s"'<>])/g) ?? [];
|
|
16895
|
-
const preferred = urls.find(isKnownCeremonySurface);
|
|
16896
|
-
if (preferred)
|
|
16897
|
-
return { method: "streamed-page", url: preferred };
|
|
16898
|
-
const fallback = urls.find((url) => !isForeignUrl(url));
|
|
16899
|
-
if (fallback) {
|
|
16900
|
-
console.log(`[body] squire connect: no known ceremony surface matched; using fallback URL ${fallback}`);
|
|
16901
|
-
return { method: "streamed-page", url: fallback };
|
|
17075
|
+
const location = isRecord(report.browser_location) ? report.browser_location : void 0;
|
|
17076
|
+
if (location?.kind === "unreachable") {
|
|
17077
|
+
return "the sign-in page could not be shown on this machine";
|
|
16902
17078
|
}
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
17079
|
+
if (report.state === "needs-sign-in") {
|
|
17080
|
+
return "the connect run ended before the sign-in was completed";
|
|
17081
|
+
}
|
|
17082
|
+
const holder = holderLine(report.holder);
|
|
17083
|
+
if (holder)
|
|
17084
|
+
return holder;
|
|
17085
|
+
return `Trusty Squire reported the connect state "${report.state}"`;
|
|
16907
17086
|
}
|
|
16908
|
-
function
|
|
16909
|
-
|
|
17087
|
+
function connectBrowserLocation(raw) {
|
|
17088
|
+
if (!isRecord(raw))
|
|
17089
|
+
return void 0;
|
|
17090
|
+
switch (raw.kind) {
|
|
17091
|
+
case "host_screen":
|
|
17092
|
+
return { kind: "host_screen" };
|
|
17093
|
+
case "virtual":
|
|
17094
|
+
return typeof raw.url === "string" ? { kind: "virtual", url: raw.url } : void 0;
|
|
17095
|
+
case "unreachable":
|
|
17096
|
+
return {
|
|
17097
|
+
kind: "unreachable",
|
|
17098
|
+
reason: typeof raw.reason === "string" ? raw.reason : "the sign-in page could not be shown"
|
|
17099
|
+
};
|
|
17100
|
+
case "none":
|
|
17101
|
+
return { kind: "none" };
|
|
17102
|
+
case "unknown":
|
|
17103
|
+
return typeof raw.reason === "string" ? { kind: "unknown", reason: raw.reason } : void 0;
|
|
17104
|
+
default:
|
|
17105
|
+
return void 0;
|
|
17106
|
+
}
|
|
16910
17107
|
}
|
|
16911
17108
|
async function installedSquireVersion(run2, spec = SQUIRE_CONNECT_PACKAGE, preferOnline = false) {
|
|
16912
17109
|
const probe = await run2("npx", [
|
|
@@ -16919,7 +17116,7 @@ async function installedSquireVersion(run2, spec = SQUIRE_CONNECT_PACKAGE, prefe
|
|
|
16919
17116
|
return version;
|
|
16920
17117
|
}
|
|
16921
17118
|
async function currentSquireRelease(run2) {
|
|
16922
|
-
const probe = await run2("npm", ["view",
|
|
17119
|
+
const probe = await run2("npm", ["view", SQUIRE_CONNECT_PACKAGE, "version"]);
|
|
16923
17120
|
if (probe.code !== 0)
|
|
16924
17121
|
return void 0;
|
|
16925
17122
|
return probe.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0];
|
|
@@ -16951,9 +17148,6 @@ async function resolveSquireConnectSpec(run2) {
|
|
|
16951
17148
|
reResolved: true
|
|
16952
17149
|
};
|
|
16953
17150
|
}
|
|
16954
|
-
function parseSignedInAs(output) {
|
|
16955
|
-
return output.match(/signed in as ([^\s,;]+)/i)?.[1];
|
|
16956
|
-
}
|
|
16957
17151
|
async function installSquire(options) {
|
|
16958
17152
|
const profileDir = options.profileDir ?? squireChromeProfileDir();
|
|
16959
17153
|
const lockRoot = options.lockRoot ?? tmpdir2();
|
|
@@ -16961,14 +17155,14 @@ async function installSquire(options) {
|
|
|
16961
17155
|
const streamRun = options.streamRun ?? defaultStreamedRunner;
|
|
16962
17156
|
const log2 = options.log ?? (() => {
|
|
16963
17157
|
});
|
|
16964
|
-
const steps = [
|
|
17158
|
+
const steps = [step2("helper reached", "done")];
|
|
16965
17159
|
const emit = () => options.onProgress?.([...steps]);
|
|
16966
17160
|
const push = (next) => {
|
|
16967
17161
|
steps.push(next);
|
|
16968
17162
|
emit();
|
|
16969
17163
|
};
|
|
16970
17164
|
const fail = (reason) => {
|
|
16971
|
-
steps.push(
|
|
17165
|
+
steps.push(step2("waiting for sign-in", "pending"));
|
|
16972
17166
|
emit();
|
|
16973
17167
|
return { status: "error", steps, errorMessage: reason };
|
|
16974
17168
|
};
|
|
@@ -16982,62 +17176,62 @@ async function installSquire(options) {
|
|
|
16982
17176
|
...previousPid !== void 0 ? { ourPids: [previousPid] } : {}
|
|
16983
17177
|
});
|
|
16984
17178
|
if (claim.kind === "blocked-foreign") {
|
|
16985
|
-
push(
|
|
17179
|
+
push(step2("trusty-squire installed", "failed", claim.action));
|
|
16986
17180
|
return fail(claim.action);
|
|
16987
17181
|
}
|
|
16988
17182
|
const resolution = await resolveSquireConnectSpec(run2);
|
|
16989
17183
|
if (resolution.reResolved) {
|
|
16990
17184
|
log2(`trusty-squire stale copy ${resolution.resolvedVersion ?? "unknown"} re-resolved against current release ${resolution.currentRelease}`);
|
|
16991
17185
|
}
|
|
16992
|
-
const install = await streamRun("npx", [...resolution.npxArgs, "connect", "--target=codex"], squireConnectProcessEnv(profileDir));
|
|
16993
|
-
const
|
|
16994
|
-
${install.stderr}`);
|
|
16995
|
-
if (!install.signIn && !alreadyConnected) {
|
|
16996
|
-
const stderr = withoutForceReloginHint(install.stderr);
|
|
16997
|
-
let stepReason = stderr || "connect printed no sign-in URL";
|
|
16998
|
-
let failReason = stderr || "the trusty-squire connect command printed no sign-in surface";
|
|
16999
|
-
if (isSquireBrowserSessionFailure(stderr)) {
|
|
17000
|
-
const again = reclaimSquireProfileClaim({ log: log2, profileDir, lockRoot });
|
|
17001
|
-
if (again.kind === "blocked-foreign") {
|
|
17002
|
-
stepReason = again.action;
|
|
17003
|
-
failReason = again.action;
|
|
17004
|
-
}
|
|
17005
|
-
}
|
|
17006
|
-
push(step("trusty-squire installed", "failed", stepReason));
|
|
17007
|
-
return fail(failReason);
|
|
17008
|
-
}
|
|
17186
|
+
const install = await streamRun("npx", [...resolution.npxArgs, "connect", "--target=codex", "--json"], squireConnectProcessEnv(profileDir));
|
|
17187
|
+
const report = install.report;
|
|
17009
17188
|
const version = resolution.resolvedVersion;
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17189
|
+
if (!report) {
|
|
17190
|
+
const reason = "Trusty Squire did not report a connect result";
|
|
17191
|
+
push(step2("trusty-squire installed", "failed", reason));
|
|
17192
|
+
return fail(reason);
|
|
17193
|
+
}
|
|
17194
|
+
if (report.state === "needs-sign-in" && !report.sign_in_url) {
|
|
17195
|
+
const reason = "Trusty Squire reported a sign-in without a page";
|
|
17196
|
+
push(step2("trusty-squire installed", "failed", reason));
|
|
17197
|
+
return fail(reason);
|
|
17198
|
+
}
|
|
17199
|
+
if (report.state !== "connected" && !isOutstandingSignIn(report)) {
|
|
17200
|
+
const reason = connectBlockedLine(report) ?? "Trusty Squire could not connect";
|
|
17201
|
+
push(step2("trusty-squire installed", "failed", reason));
|
|
17202
|
+
return fail(reason);
|
|
17203
|
+
}
|
|
17204
|
+
push(step2(`trusty-squire${version ? ` ${version}` : ""} installed`, "done"));
|
|
17205
|
+
const location = connectBrowserLocation(report.browser_location);
|
|
17206
|
+
const signIn = isOutstandingSignIn(report) && report.sign_in_url ? {
|
|
17207
|
+
method: "streamed-page",
|
|
17208
|
+
url: report.sign_in_url,
|
|
17209
|
+
...location ? { browserLocation: location } : {}
|
|
17210
|
+
} : void 0;
|
|
17211
|
+
push(step2("waiting for sign-in", signIn ? "pending" : "done"));
|
|
17015
17212
|
const pair = await pairSquire(options.mcp, options.workspaceId);
|
|
17016
17213
|
if (!pair.ok) {
|
|
17017
|
-
push(
|
|
17214
|
+
push(step2("paired to workspace", "failed", pair.reason));
|
|
17018
17215
|
return {
|
|
17019
17216
|
status: "installing",
|
|
17020
17217
|
steps,
|
|
17021
|
-
signIn,
|
|
17022
|
-
...version ? { squireVersion: version } : {}
|
|
17023
|
-
...signedInAs ? { signedInAs } : {}
|
|
17218
|
+
...signIn ? { signIn } : {},
|
|
17219
|
+
...version ? { squireVersion: version } : {}
|
|
17024
17220
|
};
|
|
17025
17221
|
}
|
|
17026
|
-
push(
|
|
17222
|
+
push(step2("paired to workspace", "done"));
|
|
17027
17223
|
if (signIn) {
|
|
17028
17224
|
return {
|
|
17029
17225
|
status: "installing",
|
|
17030
17226
|
steps,
|
|
17031
17227
|
signIn,
|
|
17032
|
-
...version ? { squireVersion: version } : {}
|
|
17033
|
-
...signedInAs ? { signedInAs } : {}
|
|
17228
|
+
...version ? { squireVersion: version } : {}
|
|
17034
17229
|
};
|
|
17035
17230
|
}
|
|
17036
17231
|
return {
|
|
17037
17232
|
status: "connected",
|
|
17038
17233
|
steps,
|
|
17039
|
-
...version ? { squireVersion: version } : {}
|
|
17040
|
-
...signedInAs ? { signedInAs } : {}
|
|
17234
|
+
...version ? { squireVersion: version } : {}
|
|
17041
17235
|
};
|
|
17042
17236
|
}
|
|
17043
17237
|
async function pairSquire(mcp, _workspaceId) {
|
|
@@ -17115,174 +17309,11 @@ async function revokeGrants(mcp, ref) {
|
|
|
17115
17309
|
failed += 1;
|
|
17116
17310
|
else
|
|
17117
17311
|
revoked += 1;
|
|
17118
|
-
} catch {
|
|
17119
|
-
failed += 1;
|
|
17120
|
-
}
|
|
17121
|
-
}
|
|
17122
|
-
return { revoked, failed };
|
|
17123
|
-
}
|
|
17124
|
-
|
|
17125
|
-
// apps/body/dist/connector-google.js
|
|
17126
|
-
var GOOGLE_TOOL_SCOPES = {
|
|
17127
|
-
"google-gmail": [
|
|
17128
|
-
"https://www.googleapis.com/auth/gmail.send",
|
|
17129
|
-
"https://www.googleapis.com/auth/gmail.readonly",
|
|
17130
|
-
"https://www.googleapis.com/auth/gmail.compose"
|
|
17131
|
-
],
|
|
17132
|
-
"google-calendar": ["https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.readonly"],
|
|
17133
|
-
"google-drive": ["https://www.googleapis.com/auth/drive.readonly"],
|
|
17134
|
-
"google-youtube": [
|
|
17135
|
-
"https://www.googleapis.com/auth/youtube.readonly",
|
|
17136
|
-
"https://www.googleapis.com/auth/yt-analytics.readonly"
|
|
17137
|
-
]
|
|
17138
|
-
};
|
|
17139
|
-
function isGoogleToolConnectorType(type) {
|
|
17140
|
-
return type in GOOGLE_TOOL_SCOPES;
|
|
17141
|
-
}
|
|
17142
|
-
async function readGoogleCredentialsFromVault(mcp) {
|
|
17143
|
-
if (!mcp)
|
|
17144
|
-
return { source: "unavailable", reason: "no Trusty Squire connector is paired" };
|
|
17145
|
-
let raw;
|
|
17146
|
-
try {
|
|
17147
|
-
raw = await mcp.call("google_oauth_credentials", {});
|
|
17148
|
-
} catch (error) {
|
|
17149
|
-
const detail = describe(error);
|
|
17150
|
-
if (isSquireBrowserSessionFailure(detail)) {
|
|
17151
|
-
return {
|
|
17152
|
-
source: "unavailable",
|
|
17153
|
-
reason: SQUIRE_BROWSER_BUSY_GOOGLE_REASON
|
|
17154
|
-
};
|
|
17155
|
-
}
|
|
17156
|
-
return {
|
|
17157
|
-
source: "unavailable",
|
|
17158
|
-
reason: `Squire has no Google OAuth grant on record yet (connect your Google account in Squire first: ${detail})`
|
|
17159
|
-
};
|
|
17160
|
-
}
|
|
17161
|
-
const record3 = raw && typeof raw === "object" ? raw : {};
|
|
17162
|
-
const inner = record3.credentials && typeof record3.credentials === "object" ? record3.credentials : record3;
|
|
17163
|
-
const accessToken = inner.accessToken ?? inner.access_token;
|
|
17164
|
-
if (!accessToken || typeof accessToken !== "string") {
|
|
17165
|
-
return { source: "unavailable", reason: "Squire returned a Google grant without an access token" };
|
|
17166
|
-
}
|
|
17167
|
-
return {
|
|
17168
|
-
source: "squire",
|
|
17169
|
-
credentials: {
|
|
17170
|
-
accessToken,
|
|
17171
|
-
refreshToken: typeof record3.refreshToken === "string" ? record3.refreshToken : typeof record3.refresh_token === "string" ? record3.refresh_token : void 0,
|
|
17172
|
-
expiresAt: typeof record3.expiresAt === "number" ? record3.expiresAt : void 0,
|
|
17173
|
-
accountEmail: typeof record3.accountEmail === "string" ? record3.accountEmail : typeof record3.email === "string" ? record3.email : void 0
|
|
17174
|
-
}
|
|
17175
|
-
};
|
|
17176
|
-
}
|
|
17177
|
-
function manualGoogleCredentialsSearchPaths(home) {
|
|
17178
|
-
return [join4(home, "google-credentials.json")];
|
|
17179
|
-
}
|
|
17180
|
-
function loadManualGoogleCredentials(home, env = process.env) {
|
|
17181
|
-
if (env.BEELINE_GOOGLE_ACCESS_TOKEN) {
|
|
17182
|
-
return {
|
|
17183
|
-
source: "manual",
|
|
17184
|
-
credentials: { accessToken: env.BEELINE_GOOGLE_ACCESS_TOKEN }
|
|
17185
|
-
};
|
|
17186
|
-
}
|
|
17187
|
-
for (const path of manualGoogleCredentialsSearchPaths(home)) {
|
|
17188
|
-
try {
|
|
17189
|
-
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
17190
|
-
if (typeof parsed.accessToken === "string" || typeof parsed.access_token === "string") {
|
|
17191
|
-
return {
|
|
17192
|
-
source: "manual",
|
|
17193
|
-
credentials: {
|
|
17194
|
-
accessToken: parsed.accessToken ?? parsed.access_token,
|
|
17195
|
-
refreshToken: typeof parsed.refreshToken === "string" ? parsed.refreshToken : void 0,
|
|
17196
|
-
expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : void 0,
|
|
17197
|
-
accountEmail: typeof parsed.accountEmail === "string" ? parsed.accountEmail : void 0
|
|
17198
|
-
}
|
|
17199
|
-
};
|
|
17200
|
-
}
|
|
17201
|
-
} catch {
|
|
17202
|
-
}
|
|
17203
|
-
}
|
|
17204
|
-
return {
|
|
17205
|
-
source: "manual-missing",
|
|
17206
|
-
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.`
|
|
17207
|
-
};
|
|
17208
|
-
}
|
|
17209
|
-
function persistManualGoogleCredentials(home, credentials) {
|
|
17210
|
-
const path = manualGoogleCredentialsSearchPaths(home)[0];
|
|
17211
|
-
mkdirSync2(dirname5(path), { recursive: true, mode: 448 });
|
|
17212
|
-
writeFileSync(path, `${JSON.stringify({
|
|
17213
|
-
accessToken: credentials.accessToken,
|
|
17214
|
-
...credentials.refreshToken ? { refreshToken: credentials.refreshToken } : {},
|
|
17215
|
-
...credentials.expiresAt ? { expiresAt: credentials.expiresAt } : {},
|
|
17216
|
-
...credentials.accountEmail ? { accountEmail: credentials.accountEmail } : {}
|
|
17217
|
-
})}
|
|
17218
|
-
`, { encoding: "utf8", mode: 384 });
|
|
17219
|
-
return path;
|
|
17220
|
-
}
|
|
17221
|
-
var step2 = (label, status, extra) => ({
|
|
17222
|
-
label,
|
|
17223
|
-
status,
|
|
17224
|
-
...extra
|
|
17225
|
-
});
|
|
17226
|
-
function outputTail(text2, maxChars = 800) {
|
|
17227
|
-
const trimmed = text2.trim();
|
|
17228
|
-
return trimmed.length <= maxChars ? trimmed : `\u2026${trimmed.slice(-maxChars)}`;
|
|
17229
|
-
}
|
|
17230
|
-
async function installGoogleTool(options) {
|
|
17231
|
-
const steps = [step2("helper reached", "done")];
|
|
17232
|
-
const emit = () => options.onProgress?.([...steps]);
|
|
17233
|
-
const push = (next) => {
|
|
17234
|
-
steps.push(next);
|
|
17235
|
-
emit();
|
|
17236
|
-
};
|
|
17237
|
-
const fail = (label, reason, output) => {
|
|
17238
|
-
push(step2(label, "failed", { reason, ...output ? { output } : {} }));
|
|
17239
|
-
return { status: "error", steps, errorMessage: reason };
|
|
17240
|
-
};
|
|
17241
|
-
emit();
|
|
17242
|
-
if (!isGoogleToolConnectorType(options.connectorType)) {
|
|
17243
|
-
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
17244
|
-
}
|
|
17245
|
-
push(step2("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
17246
|
-
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
17247
|
-
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
17248
|
-
if (oneClick.source === "squire")
|
|
17249
|
-
return oneClick;
|
|
17250
|
-
if (isSquireBrowserSessionFailure(oneClick.reason))
|
|
17251
|
-
return oneClick;
|
|
17252
|
-
return loadManualGoogleCredentials(options.home, options.env);
|
|
17253
|
-
})();
|
|
17254
|
-
if (!("credentials" in resolved)) {
|
|
17255
|
-
const reason = resolved.reason;
|
|
17256
|
-
steps[1] = step2("Google credentials resolved", "failed", { reason, output: outputTail(reason) });
|
|
17257
|
-
emit();
|
|
17258
|
-
return { status: "error", steps, errorMessage: reason };
|
|
17259
|
-
}
|
|
17260
|
-
steps[1] = step2("Google credentials resolved", "done", {
|
|
17261
|
-
output: resolved.source === "squire" ? "one-click: read the Google grant from Trusty Squire" : "manual: local google-credentials.json"
|
|
17262
|
-
});
|
|
17263
|
-
emit();
|
|
17264
|
-
const client = options.client ?? googleWorkspaceClient(refreshableTokenSource(resolved.credentials, options.env?.BEELINE_GOOGLE_CLIENT_ID, options.env?.BEELINE_GOOGLE_CLIENT_SECRET));
|
|
17265
|
-
push(step2("authorized with Google", "running", { output: "verifying the grant with Google\u2026" }));
|
|
17266
|
-
const verify = await client.verify();
|
|
17267
|
-
if (!verify.ok) {
|
|
17268
|
-
return fail("authorized with Google", verify.reason, outputTail(verify.reason));
|
|
17269
|
-
}
|
|
17270
|
-
steps[2] = step2("authorized with Google", "done", {
|
|
17271
|
-
...verify.account ? { output: `signed in as ${verify.account}` } : {}
|
|
17272
|
-
});
|
|
17273
|
-
emit();
|
|
17274
|
-
push(step2("tools enabled", "done", {
|
|
17275
|
-
output: `${GOOGLE_TOOL_SCOPES[options.connectorType].length} Google scopes granted`
|
|
17276
|
-
}));
|
|
17277
|
-
persistManualGoogleCredentials(options.home, resolved.credentials);
|
|
17278
|
-
return {
|
|
17279
|
-
status: "connected",
|
|
17280
|
-
steps,
|
|
17281
|
-
...verify.account ? { signedInAs: verify.account } : {}
|
|
17282
|
-
};
|
|
17283
|
-
}
|
|
17284
|
-
function describe(error) {
|
|
17285
|
-
return error instanceof Error ? error.message : String(error);
|
|
17312
|
+
} catch {
|
|
17313
|
+
failed += 1;
|
|
17314
|
+
}
|
|
17315
|
+
}
|
|
17316
|
+
return { revoked, failed };
|
|
17286
17317
|
}
|
|
17287
17318
|
|
|
17288
17319
|
// apps/body/dist/squire-mcp-client.js
|
|
@@ -17378,12 +17409,12 @@ var StdioSquireMcpClient = class {
|
|
|
17378
17409
|
const child = this.child;
|
|
17379
17410
|
if (!child)
|
|
17380
17411
|
return Promise.reject(new Error("Squire MCP session is not running"));
|
|
17381
|
-
return new Promise((
|
|
17412
|
+
return new Promise((resolve36, reject) => {
|
|
17382
17413
|
const timer = setTimeout(() => {
|
|
17383
17414
|
this.pending.delete(id);
|
|
17384
17415
|
reject(new Error(`${method} timed out`));
|
|
17385
17416
|
}, method === "initialize" ? INITIALIZE_TIMEOUT_MS : CALL_TIMEOUT_MS);
|
|
17386
|
-
this.pending.set(id, { resolve:
|
|
17417
|
+
this.pending.set(id, { resolve: resolve36, reject, timer });
|
|
17387
17418
|
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
17388
17419
|
`);
|
|
17389
17420
|
});
|
|
@@ -17426,7 +17457,8 @@ function defaultSquireMcpClient() {
|
|
|
17426
17457
|
}
|
|
17427
17458
|
|
|
17428
17459
|
// apps/body/dist/connector-assignments.js
|
|
17429
|
-
var CONNECTOR_POLL_INTERVAL_MS =
|
|
17460
|
+
var CONNECTOR_POLL_INTERVAL_MS = 5 * 6e4;
|
|
17461
|
+
var CONNECT_WATCH_INTERVAL_MS = 2e3;
|
|
17430
17462
|
var CEREMONY_EXPIRED = "the Trusty Squire sign-in page expired before it was used \xB7 tap Retry to open a new one";
|
|
17431
17463
|
var ConnectorAssignmentLoop = class {
|
|
17432
17464
|
agentId;
|
|
@@ -17441,6 +17473,8 @@ var ConnectorAssignmentLoop = class {
|
|
|
17441
17473
|
schedule;
|
|
17442
17474
|
cancel;
|
|
17443
17475
|
timer;
|
|
17476
|
+
/** Armed only while this helper owns a live sign-in ceremony. */
|
|
17477
|
+
connectWatch;
|
|
17444
17478
|
started = false;
|
|
17445
17479
|
stopped = false;
|
|
17446
17480
|
/** One install at a time per connector; other polls skip it. */
|
|
@@ -17476,12 +17510,56 @@ var ConnectorAssignmentLoop = class {
|
|
|
17476
17510
|
void this.runOnce();
|
|
17477
17511
|
this.timer = this.schedule(() => this.poll(), this.intervalMs);
|
|
17478
17512
|
}
|
|
17513
|
+
/** Event-driven drain. The interval stays the recovery net. */
|
|
17514
|
+
wake() {
|
|
17515
|
+
if (this.stopped)
|
|
17516
|
+
return;
|
|
17517
|
+
void this.runOnce();
|
|
17518
|
+
}
|
|
17479
17519
|
stop() {
|
|
17480
17520
|
this.stopped = true;
|
|
17481
17521
|
if (this.timer !== void 0) {
|
|
17482
17522
|
this.cancel(this.timer);
|
|
17483
17523
|
this.timer = void 0;
|
|
17484
17524
|
}
|
|
17525
|
+
if (this.connectWatch !== void 0) {
|
|
17526
|
+
this.cancel(this.connectWatch);
|
|
17527
|
+
this.connectWatch = void 0;
|
|
17528
|
+
}
|
|
17529
|
+
}
|
|
17530
|
+
/**
|
|
17531
|
+
* Watch the sign-in this helper is holding open. The phone paints
|
|
17532
|
+
* `installing` for the whole ceremony and only a drain that reaches
|
|
17533
|
+
* Squire's already-connected short-circuit flips the row to `connected`;
|
|
17534
|
+
* nothing on the wire announces that the human finished, so the connect
|
|
17535
|
+
* process exiting is the signal. Drain on that, and the row settles on the
|
|
17536
|
+
* same cadence it always has instead of waiting out the recovery poll.
|
|
17537
|
+
*/
|
|
17538
|
+
watchConnectSignIn() {
|
|
17539
|
+
if (this.stopped || this.connectWatch !== void 0)
|
|
17540
|
+
return;
|
|
17541
|
+
if (!this.connectCeremonyLive())
|
|
17542
|
+
return;
|
|
17543
|
+
this.connectWatch = this.schedule(() => this.checkConnectSignIn(), CONNECT_WATCH_INTERVAL_MS);
|
|
17544
|
+
}
|
|
17545
|
+
/** A ceremony of ours still worth waiting on: claimed, unspent, alive. */
|
|
17546
|
+
connectCeremonyLive() {
|
|
17547
|
+
const claim = squireConnectSession();
|
|
17548
|
+
if (!claim)
|
|
17549
|
+
return false;
|
|
17550
|
+
if (Date.now() - claim.claimedAt >= CONNECT_TIMEOUT_MS)
|
|
17551
|
+
return false;
|
|
17552
|
+
return isProcessAlive(claim.pid);
|
|
17553
|
+
}
|
|
17554
|
+
checkConnectSignIn() {
|
|
17555
|
+
this.connectWatch = void 0;
|
|
17556
|
+
if (this.stopped)
|
|
17557
|
+
return;
|
|
17558
|
+
if (this.connectCeremonyLive()) {
|
|
17559
|
+
this.connectWatch = this.schedule(() => this.checkConnectSignIn(), CONNECT_WATCH_INTERVAL_MS);
|
|
17560
|
+
return;
|
|
17561
|
+
}
|
|
17562
|
+
void this.runOnce();
|
|
17485
17563
|
}
|
|
17486
17564
|
/** One interval tick: poll, then re-arm. */
|
|
17487
17565
|
poll() {
|
|
@@ -17548,8 +17626,6 @@ var ConnectorAssignmentLoop = class {
|
|
|
17548
17626
|
const oneClick = await readGoogleCredentialsFromVault(this.squire());
|
|
17549
17627
|
if (oneClick.source === "squire")
|
|
17550
17628
|
return oneClick;
|
|
17551
|
-
if (isSquireBrowserSessionFailure(oneClick.reason))
|
|
17552
|
-
return oneClick;
|
|
17553
17629
|
} catch (error) {
|
|
17554
17630
|
this.log(`google one-click grant lookup failed: ${describe2(error)}`);
|
|
17555
17631
|
}
|
|
@@ -17605,6 +17681,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
17605
17681
|
if (claim && !spent && isProcessAlive(claim.pid)) {
|
|
17606
17682
|
if (!await this.rearmedByHuman(connectorId)) {
|
|
17607
17683
|
this.log(`trusty-squire connect still waiting for sign-in (pid ${String(claim.pid)}); leaving it alone`);
|
|
17684
|
+
this.watchConnectSignIn();
|
|
17608
17685
|
return;
|
|
17609
17686
|
}
|
|
17610
17687
|
this.log("trusty-squire re-pair requested; superseding the connect holding the browser");
|
|
@@ -17646,8 +17723,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
17646
17723
|
await this.api.execute("installConnector", {
|
|
17647
17724
|
agentId: this.agentId,
|
|
17648
17725
|
connectorId,
|
|
17649
|
-
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17650
|
-
...result.signedInAs ? { signedInAs: result.signedInAs } : {}
|
|
17726
|
+
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17651
17727
|
});
|
|
17652
17728
|
await this.reportVault(connectorId);
|
|
17653
17729
|
return;
|
|
@@ -17657,9 +17733,9 @@ var ConnectorAssignmentLoop = class {
|
|
|
17657
17733
|
connectorId,
|
|
17658
17734
|
steps: result.steps,
|
|
17659
17735
|
signIn: result.signIn ?? null,
|
|
17660
|
-
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17661
|
-
...result.signedInAs ? { signedInAs: result.signedInAs } : {}
|
|
17736
|
+
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17662
17737
|
});
|
|
17738
|
+
this.watchConnectSignIn();
|
|
17663
17739
|
}
|
|
17664
17740
|
/** This connector's own row, or undefined when the server cannot answer. */
|
|
17665
17741
|
async connectorRow(connectorId) {
|
|
@@ -23759,9 +23835,9 @@ function isHex32(input) {
|
|
|
23759
23835
|
return true;
|
|
23760
23836
|
}
|
|
23761
23837
|
var verifiedSymbol = /* @__PURE__ */ Symbol("verified");
|
|
23762
|
-
var
|
|
23838
|
+
var isRecord2 = (obj) => obj instanceof Object;
|
|
23763
23839
|
function validateEvent(event) {
|
|
23764
|
-
if (!
|
|
23840
|
+
if (!isRecord2(event))
|
|
23765
23841
|
return false;
|
|
23766
23842
|
if (typeof event.kind !== "number")
|
|
23767
23843
|
return false;
|
|
@@ -26685,6 +26761,15 @@ var AGENT_REMOVED_CODE = "agent_removed";
|
|
|
26685
26761
|
function isAgentRemovedError(error) {
|
|
26686
26762
|
return error instanceof DaemonApiError && error.status === 403 && error.code === AGENT_REMOVED_CODE;
|
|
26687
26763
|
}
|
|
26764
|
+
function membershipChange(event) {
|
|
26765
|
+
return {
|
|
26766
|
+
...typeof event.roomId === "string" && event.roomId ? { roomId: event.roomId } : {},
|
|
26767
|
+
...typeof event.parentRoomId === "string" ? { parentRoomId: event.parentRoomId } : {},
|
|
26768
|
+
...typeof event.openedBy === "string" ? { openedBy: event.openedBy } : {},
|
|
26769
|
+
...event.archived === true ? { archived: true } : {},
|
|
26770
|
+
...event.removed === true ? { removed: true } : {}
|
|
26771
|
+
};
|
|
26772
|
+
}
|
|
26688
26773
|
function endpoint(origin, path) {
|
|
26689
26774
|
return new URL(path, `${origin}/`).toString();
|
|
26690
26775
|
}
|
|
@@ -26712,6 +26797,8 @@ var DaemonApiClient = class {
|
|
|
26712
26797
|
roomsChangedListener;
|
|
26713
26798
|
configChangedListener;
|
|
26714
26799
|
hiccupRestartListener;
|
|
26800
|
+
connectorAssignmentListener;
|
|
26801
|
+
cornerCompleteListener;
|
|
26715
26802
|
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
|
|
26716
26803
|
this.baseUrl = baseUrl;
|
|
26717
26804
|
this.daemonToken = daemonToken;
|
|
@@ -26759,8 +26846,8 @@ var DaemonApiClient = class {
|
|
|
26759
26846
|
};
|
|
26760
26847
|
}
|
|
26761
26848
|
/** Register the one listener invoked when the server reports this agent's
|
|
26762
|
-
* Room/corner memberships changed —
|
|
26763
|
-
*
|
|
26849
|
+
* Room/corner memberships changed — a scoped event applies incrementally;
|
|
26850
|
+
* an unscoped wake (reconnect) still runs the recovery reconcile. */
|
|
26764
26851
|
setRoomsChangedListener(listener) {
|
|
26765
26852
|
this.roomsChangedListener = listener;
|
|
26766
26853
|
}
|
|
@@ -26774,6 +26861,14 @@ var DaemonApiClient = class {
|
|
|
26774
26861
|
setHiccupRestartListener(listener) {
|
|
26775
26862
|
this.hiccupRestartListener = listener;
|
|
26776
26863
|
}
|
|
26864
|
+
/** Connect / pending_ops drain wake. Never a catalog. */
|
|
26865
|
+
setConnectorAssignmentListener(listener) {
|
|
26866
|
+
this.connectorAssignmentListener = listener;
|
|
26867
|
+
}
|
|
26868
|
+
/** corner-complete on the subscribed corner — close now, poll is recovery. */
|
|
26869
|
+
setCornerCompleteListener(listener) {
|
|
26870
|
+
this.cornerCompleteListener = listener;
|
|
26871
|
+
}
|
|
26777
26872
|
updateLiveCursor(roomId, cursor3) {
|
|
26778
26873
|
const room = this.liveRooms.get(roomId);
|
|
26779
26874
|
if (room && cursor3)
|
|
@@ -26815,6 +26910,7 @@ var DaemonApiClient = class {
|
|
|
26815
26910
|
for (const roomId of this.liveRooms.keys())
|
|
26816
26911
|
this.sendLiveSubscription(roomId);
|
|
26817
26912
|
this.roomsChangedListener?.();
|
|
26913
|
+
this.connectorAssignmentListener?.();
|
|
26818
26914
|
};
|
|
26819
26915
|
socket.onmessage = (message) => {
|
|
26820
26916
|
let value;
|
|
@@ -26827,7 +26923,15 @@ var DaemonApiClient = class {
|
|
|
26827
26923
|
return;
|
|
26828
26924
|
const event = value;
|
|
26829
26925
|
if (event.type === "rooms-changed") {
|
|
26830
|
-
this.roomsChangedListener?.();
|
|
26926
|
+
this.roomsChangedListener?.(membershipChange(event));
|
|
26927
|
+
return;
|
|
26928
|
+
}
|
|
26929
|
+
if (event.type === "corner-complete" && typeof event.roomId === "string") {
|
|
26930
|
+
this.cornerCompleteListener?.(event.roomId);
|
|
26931
|
+
return;
|
|
26932
|
+
}
|
|
26933
|
+
if (event.type === "connector-assignment") {
|
|
26934
|
+
this.connectorAssignmentListener?.();
|
|
26831
26935
|
return;
|
|
26832
26936
|
}
|
|
26833
26937
|
if (event.type === "config-changed") {
|
|
@@ -26954,12 +27058,12 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
26954
27058
|
}
|
|
26955
27059
|
|
|
26956
27060
|
// apps/body/dist/room-runtime.js
|
|
26957
|
-
import { execFile as
|
|
27061
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
26958
27062
|
import { createHash as createHash7 } from "node:crypto";
|
|
26959
27063
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "node:fs";
|
|
26960
|
-
import { mkdir as
|
|
26961
|
-
import { dirname as
|
|
26962
|
-
import { promisify as
|
|
27064
|
+
import { mkdir as mkdir15, rm as rm5 } from "node:fs/promises";
|
|
27065
|
+
import { dirname as dirname13, resolve as resolve24 } from "node:path";
|
|
27066
|
+
import { promisify as promisify5 } from "node:util";
|
|
26963
27067
|
|
|
26964
27068
|
// apps/body/dist/grant-runner.js
|
|
26965
27069
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -27297,9 +27401,10 @@ function sandboxMountPlan(spec) {
|
|
|
27297
27401
|
...spec.additionalWritablePaths ?? [],
|
|
27298
27402
|
...harnessState
|
|
27299
27403
|
] : (
|
|
27300
|
-
// A Room
|
|
27301
|
-
// state,
|
|
27302
|
-
//
|
|
27404
|
+
// A Room keeps source files read-only. Its explicit capabilities are
|
|
27405
|
+
// limited to harness state, agent-private paths, and release-owned
|
|
27406
|
+
// generated state such as the repository's .codegraph index; callers
|
|
27407
|
+
// must name each path. /tmp remains private.
|
|
27303
27408
|
[...spec.additionalWritablePaths ?? [], ...harnessState]
|
|
27304
27409
|
));
|
|
27305
27410
|
const tmpRestores = normalize([
|
|
@@ -27426,8 +27531,8 @@ function detectBwrapSandbox(options = {}) {
|
|
|
27426
27531
|
});
|
|
27427
27532
|
const result = run2(probe.command, probe.args);
|
|
27428
27533
|
if (result.status !== 0) {
|
|
27429
|
-
const detail = (result.stderr ?? "").trim().split("\n").pop() ?? `exit ${result.status}`;
|
|
27430
|
-
const appArmorRemediation = /No permissions to create new namespace|setting up uid map: Permission denied|userns_create/i.test(result.stderr ?? "") ? " Ubuntu AppArmor may be blocking unprivileged user namespaces.
|
|
27534
|
+
const detail = (result.stderr ?? "").trim().split("\n").filter(Boolean).pop() ?? `exit ${result.status}`;
|
|
27535
|
+
const appArmorRemediation = /No permissions to create (?:a )?new namespace|setting up uid map: Permission denied|userns_create/i.test(result.stderr ?? "") ? " Ubuntu AppArmor may be blocking unprivileged user namespaces. If the agent's user service inherited an AppArmor profile, reinstall the Beeline user unit and restart the agent; the unit explicitly transitions to unconfined before /usr/bin/bwrap enters Ubuntu's bwrap profile." : "";
|
|
27431
27536
|
return {
|
|
27432
27537
|
advisory: `harness OS sandbox UNAVAILABLE: ${bwrapPath} self-test failed (${detail}); ACP children run unconfined and the Room read-only rule rests on the permission handler alone.${appArmorRemediation}`
|
|
27433
27538
|
};
|
|
@@ -27501,6 +27606,7 @@ var GRANT_COMMAND_OUTPUT_CAP_BYTES = 64 * 1024;
|
|
|
27501
27606
|
var ARGV_MAX_WORDS = 256;
|
|
27502
27607
|
var ARGV_WORD_MAX_LENGTH = 4096;
|
|
27503
27608
|
var WRITE_REFUSED = /Read-only file system|EROFS/;
|
|
27609
|
+
var SANDBOX_STARTED_SCRIPT = ['printf "%s\\n" "$1" >&2', "shift", 'exec "$@"'].join("\n");
|
|
27504
27610
|
var ROOM_SANDBOX_UNAVAILABLE = "this Room cannot run granted commands: a Room promises a read-only filesystem and that promise is enforced by bubblewrap, which is not usable on this host. Open a corner with open_corner and run it there, where writes belong.";
|
|
27505
27611
|
var ROOM_WRITE_REFUSED_NOTE = "[beeline] the Room filesystem is read-only outside your scratch directory, so this write was refused by the kernel. Open a corner with open_corner to change files.";
|
|
27506
27612
|
function operatorSecretResolver(env = process.env) {
|
|
@@ -27624,7 +27730,9 @@ var GrantCommandRunner = class {
|
|
|
27624
27730
|
...Object.fromEntries(secrets)
|
|
27625
27731
|
};
|
|
27626
27732
|
const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
|
|
27627
|
-
const
|
|
27733
|
+
const runsOnHost = surfaceAllows(policy.surface, "run-host-command");
|
|
27734
|
+
const sandboxMarker = runsOnHost ? void 0 : `[beeline-sandbox-started:${randomBytes6(16).toString("hex")}]`;
|
|
27735
|
+
const spawn13 = runsOnHost ? { command: argv[0], args: argv.slice(1) } : roomSandboxCommand(policy, room.cwd, argv, sandboxMarker);
|
|
27628
27736
|
const outcome = await new Promise((resolveRun) => {
|
|
27629
27737
|
const child = execFile3(spawn13.command, spawn13.args, {
|
|
27630
27738
|
cwd: room.cwd,
|
|
@@ -27645,7 +27753,13 @@ var GrantCommandRunner = class {
|
|
|
27645
27753
|
});
|
|
27646
27754
|
});
|
|
27647
27755
|
});
|
|
27648
|
-
const
|
|
27756
|
+
const sandboxStarted = sandboxMarker ? outcome.output.includes(sandboxMarker) : true;
|
|
27757
|
+
if (sandboxMarker && sandboxStarted) {
|
|
27758
|
+
outcome.output = outcome.output.replace(`${sandboxMarker}
|
|
27759
|
+
`, "").replace(sandboxMarker, "");
|
|
27760
|
+
}
|
|
27761
|
+
const writeRefused = !runsOnHost && WRITE_REFUSED.test(outcome.output);
|
|
27762
|
+
const sandboxFailure = !runsOnHost && !sandboxStarted;
|
|
27649
27763
|
const output = capOutput(scrubSecrets(writeRefused ? `${outcome.output.trimEnd()}
|
|
27650
27764
|
${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
27651
27765
|
const turn = room.turn();
|
|
@@ -27653,7 +27767,7 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
27653
27767
|
pubkey: grant.requestedBy,
|
|
27654
27768
|
...grant.requestedByName ? { name: grant.requestedByName } : {}
|
|
27655
27769
|
};
|
|
27656
|
-
const status = outcome.timedOut ? "timed out" : outcome.exitCode === null ? "error" : `exit ${outcome.exitCode}`;
|
|
27770
|
+
const status = sandboxFailure ? "sandbox failed" : outcome.timedOut ? "timed out" : outcome.exitCode === null ? "error" : `exit ${outcome.exitCode}`;
|
|
27657
27771
|
await this.options.api.execute("postAgentActivity", {
|
|
27658
27772
|
agentId: this.options.agentId,
|
|
27659
27773
|
roomId: input.roomId,
|
|
@@ -27677,7 +27791,8 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
27677
27791
|
...outcome.signal ? { signal: outcome.signal } : {},
|
|
27678
27792
|
timedOut: outcome.timedOut,
|
|
27679
27793
|
output,
|
|
27680
|
-
...writeRefused ? { writeRefused: true } : {}
|
|
27794
|
+
...writeRefused ? { writeRefused: true } : {},
|
|
27795
|
+
...sandboxFailure ? { sandboxFailure: true } : {}
|
|
27681
27796
|
};
|
|
27682
27797
|
}
|
|
27683
27798
|
/**
|
|
@@ -27719,7 +27834,7 @@ function scriptCandidates(cwd, scratch, argv) {
|
|
|
27719
27834
|
];
|
|
27720
27835
|
return [...new Set(paths)];
|
|
27721
27836
|
}
|
|
27722
|
-
function roomSandboxCommand(policy, cwd, argv) {
|
|
27837
|
+
function roomSandboxCommand(policy, cwd, argv, sandboxMarker) {
|
|
27723
27838
|
if (!policy.bwrapPath)
|
|
27724
27839
|
throw new Error(ROOM_SANDBOX_UNAVAILABLE);
|
|
27725
27840
|
return wrapAgentCommand({
|
|
@@ -27734,8 +27849,8 @@ function roomSandboxCommand(policy, cwd, argv) {
|
|
|
27734
27849
|
...policy.scratch ? { tmpDir: policy.scratch } : {},
|
|
27735
27850
|
...policy.maskPaths ? { maskPaths: policy.maskPaths } : {}
|
|
27736
27851
|
},
|
|
27737
|
-
command:
|
|
27738
|
-
args: argv
|
|
27852
|
+
command: "/bin/sh",
|
|
27853
|
+
args: ["-c", SANDBOX_STARTED_SCRIPT, "beeline-grant-sandbox", sandboxMarker, ...argv]
|
|
27739
27854
|
});
|
|
27740
27855
|
}
|
|
27741
27856
|
var GrantRunnerServer = class {
|
|
@@ -28050,13 +28165,13 @@ async function runServerCommandIntake(options) {
|
|
|
28050
28165
|
}
|
|
28051
28166
|
}
|
|
28052
28167
|
options.onPoll?.();
|
|
28053
|
-
const reconcile = await new Promise((
|
|
28168
|
+
const reconcile = await new Promise((resolve36) => {
|
|
28054
28169
|
const done = (needed) => {
|
|
28055
28170
|
if (timer)
|
|
28056
28171
|
clearTimeout(timer);
|
|
28057
28172
|
signal?.removeEventListener("abort", aborted);
|
|
28058
28173
|
wake = void 0;
|
|
28059
|
-
|
|
28174
|
+
resolve36(needed);
|
|
28060
28175
|
};
|
|
28061
28176
|
const aborted = () => done(false);
|
|
28062
28177
|
wake = done;
|
|
@@ -28081,12 +28196,12 @@ async function runServerCommandIntake(options) {
|
|
|
28081
28196
|
}
|
|
28082
28197
|
|
|
28083
28198
|
// apps/body/dist/monolith-corner-turn.js
|
|
28084
|
-
import { execFile as
|
|
28199
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
28085
28200
|
import { createHash as createHash6 } from "node:crypto";
|
|
28086
|
-
import { mkdir as
|
|
28201
|
+
import { mkdir as mkdir14 } from "node:fs/promises";
|
|
28087
28202
|
import { homedir as homedir10 } from "node:os";
|
|
28088
28203
|
import { join as join12 } from "node:path";
|
|
28089
|
-
import { promisify as
|
|
28204
|
+
import { promisify as promisify4 } from "node:util";
|
|
28090
28205
|
|
|
28091
28206
|
// apps/body/dist/agent-home.js
|
|
28092
28207
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
@@ -28896,6 +29011,9 @@ import { basename as basename5, dirname as dirname10, join as join8, relative as
|
|
|
28896
29011
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
28897
29012
|
import { resolve as resolve13 } from "node:path";
|
|
28898
29013
|
|
|
29014
|
+
// packages/api-contract/dist/phone-types.js
|
|
29015
|
+
var MESSAGE_REACTION_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F389}", "\u{1F440}", "\u2705"];
|
|
29016
|
+
|
|
28899
29017
|
// packages/api-contract/dist/workbench.js
|
|
28900
29018
|
var CONNECTABLE_CONNECTOR_KINDS = [
|
|
28901
29019
|
"trusty-squire",
|
|
@@ -28942,11 +29060,13 @@ var BEELINE_ROOM_CAPABILITIES = [
|
|
|
28942
29060
|
"You may address any Room member, including another agent, by writing @name in your reply; the server routes that mention to them. Each turn prompt lists the Room members and the exact spelling that tags each one - use those spellings, and never guess or reuse one from an older message.",
|
|
28943
29061
|
"Tag another agent only when you need something from them: a question, a handoff, a task. Never tag to acknowledge, agree, or say you are ready. If nothing is actionable, do not reply.",
|
|
28944
29062
|
"Tag the user only when you need a decision or input, or when the task they asked for is finished. Never tag for progress, acknowledgement, or questions the transcript already answers.",
|
|
29063
|
+
"If shell access is blocked, continue with read-only inspection instead of retrying it: call beeline-readonly-mcp.search_text to find code and beeline-readonly-mcp.read_file to read it. Use CodeGraph first when it is available for indexed code relationships.",
|
|
28945
29064
|
"Every MCP server mounted into this session is approved tool by tool - use operator and host tools freely; the read-only filesystem sandbox is the boundary, not a tool list. Network web search is enabled.",
|
|
28946
29065
|
BEELINE_AMBIENT_CONNECTOR_CAPABILITY,
|
|
28947
29066
|
"Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
|
|
28948
29067
|
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture. To put a real photograph in an artifact, call beeline-agent fetch_image with the photo URL; it writes the bytes to your session scratch and returns the path, mime, and size \u2014 read them, base64-encode, and embed as a data: URL. The validator still refuses every http(s) image reference, and drawing an SVG stand-in is not a photograph.",
|
|
28949
29068
|
"To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
|
|
29069
|
+
`To react to a message in this Room, call beeline-agent react_to_message with its message id and one supported emoji (${MESSAGE_REACTION_EMOJIS.join(" ")}).`,
|
|
28950
29070
|
`To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. Subscriptions are per Room and cover every way the event lands: joining a Room you subscribed to wakes you, and so does a person arriving in the Workspace when that arrival projects into this Room - subscribe to joined in an onboarding Room and every newcomer wakes you, exactly like a greeter. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant. choice-answered, choice-skipped, and poll-closed start a new input turn for the asking agent; they do not resume a paused grant.`,
|
|
28951
29071
|
"If you need reach outside the sandbox, call beeline-agent request_grant. If you already know discrete options, call beeline-agent ask_choice (one human, optional) or open_poll (every human in this Room, required deadline). A poll is refused below two electors and above fifty, and in a DM. A plurality is a fact, never permission to deploy, delete, merge, or spend. Open-ended asks stay tagged prose. Never put Always / Once / No on a preference.",
|
|
28952
29072
|
"To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing.",
|
|
@@ -28966,6 +29086,7 @@ var BEELINE_DM_CAPABILITIES = [
|
|
|
28966
29086
|
"Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
|
|
28967
29087
|
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture. To put a real photograph in an artifact, call beeline-agent fetch_image with the photo URL; it writes the bytes to your session scratch and returns the path, mime, and size \u2014 read them, base64-encode, and embed as a data: URL. The validator still refuses every http(s) image reference, and drawing an SVG stand-in is not a photograph.",
|
|
28968
29088
|
"Tag the person only when you need a decision or input, or when the task they asked for is finished.",
|
|
29089
|
+
`To react to a message in this Room, call beeline-agent react_to_message with its message id and one supported emoji (${MESSAGE_REACTION_EMOJIS.join(" ")}).`,
|
|
28969
29090
|
"If you already know discrete options, call beeline-agent ask_choice. A pick is a preference, never sandbox, spend, or merge authority. open_poll is refused here: one human is a question.",
|
|
28970
29091
|
"Never claim an action or reply happened unless the prompt or a tool result proves it."
|
|
28971
29092
|
].join(" ");
|
|
@@ -30807,12 +30928,22 @@ function harnessStateDirsFromEnv(env) {
|
|
|
30807
30928
|
return { stateDirs, ...tmp ? { tmpDir: resolve16(tmp) } : {} };
|
|
30808
30929
|
}
|
|
30809
30930
|
|
|
30931
|
+
// apps/body/dist/agent-command-catalog.js
|
|
30932
|
+
function agentCommandCatalogPublisher(input) {
|
|
30933
|
+
return (commands) => {
|
|
30934
|
+
void input.api.execute("postAgentCommands", {
|
|
30935
|
+
agentId: input.agentId,
|
|
30936
|
+
workspaceId: input.workspaceId,
|
|
30937
|
+
commands
|
|
30938
|
+
}).catch(input.report ?? ((error) => console.error("[body] agent command catalog publish failed:", error)));
|
|
30939
|
+
};
|
|
30940
|
+
}
|
|
30941
|
+
|
|
30810
30942
|
// apps/body/dist/attachment-delivery.js
|
|
30811
30943
|
import { mkdir as mkdir7, writeFile as writeFile8 } from "node:fs/promises";
|
|
30812
30944
|
import { basename as basename6, extname, join as join9 } from "node:path";
|
|
30813
30945
|
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
30814
|
-
var
|
|
30815
|
-
var EXPIRED_REASON = `expired: attachments are kept for ${MEDIA_TTL_HOURS} hours and these bytes are past that window`;
|
|
30946
|
+
var EXPIRED_REASON = "expired: these attachment bytes are past their retention window";
|
|
30816
30947
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
30817
30948
|
var BoundedSizeError = class extends Error {
|
|
30818
30949
|
bytes;
|
|
@@ -31996,9 +32127,91 @@ function youtubeMcpServer(config, accessToken) {
|
|
|
31996
32127
|
};
|
|
31997
32128
|
}
|
|
31998
32129
|
|
|
32130
|
+
// apps/body/dist/codegraph.js
|
|
32131
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
32132
|
+
import { access, appendFile, mkdir as mkdir9, readFile as readFile7 } from "node:fs/promises";
|
|
32133
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, resolve as resolve19 } from "node:path";
|
|
32134
|
+
import { promisify as promisify3 } from "node:util";
|
|
32135
|
+
var execFileAsync3 = promisify3(execFile5);
|
|
32136
|
+
var CODEGRAPH_MCP_SERVER_NAME = "codegraph";
|
|
32137
|
+
var CODEGRAPH_INDEX_PATH = ".codegraph/codegraph.db";
|
|
32138
|
+
var CODEGRAPH_PREPARE_TIMEOUT_MS = 12e4;
|
|
32139
|
+
function codegraphIndexDirectory(cwd) {
|
|
32140
|
+
return resolve19(cwd, dirname11(CODEGRAPH_INDEX_PATH));
|
|
32141
|
+
}
|
|
32142
|
+
function codegraphMcpServer(config, cwd, options) {
|
|
32143
|
+
if (!config.codegraphCommand)
|
|
32144
|
+
return void 0;
|
|
32145
|
+
return {
|
|
32146
|
+
name: CODEGRAPH_MCP_SERVER_NAME,
|
|
32147
|
+
command: config.codegraphCommand,
|
|
32148
|
+
args: ["serve", "--mcp", "--path", resolve19(cwd), ...options.readonly ? ["--no-watch"] : []],
|
|
32149
|
+
env: [
|
|
32150
|
+
{ name: "CODEGRAPH_TELEMETRY", value: "0" },
|
|
32151
|
+
// One MCP child per harness session is easier to contain than a daemon
|
|
32152
|
+
// that can outlive the Room/corner whose index and permissions it used.
|
|
32153
|
+
{ name: "CODEGRAPH_NO_DAEMON", value: "1" }
|
|
32154
|
+
]
|
|
32155
|
+
};
|
|
32156
|
+
}
|
|
32157
|
+
function codegraphFingerprintServers(config, servers, mounted = Boolean(config.codegraphCommand)) {
|
|
32158
|
+
return config.codegraphCommand && mounted ? [...servers, CODEGRAPH_MCP_SERVER_NAME] : [...servers];
|
|
32159
|
+
}
|
|
32160
|
+
async function isGitWorktree(cwd) {
|
|
32161
|
+
try {
|
|
32162
|
+
const { stdout: stdout6 } = await execFileAsync3("git", ["rev-parse", "--show-toplevel"], {
|
|
32163
|
+
cwd,
|
|
32164
|
+
timeout: 1e4
|
|
32165
|
+
});
|
|
32166
|
+
return resolve19(stdout6.trim()) === resolve19(cwd);
|
|
32167
|
+
} catch {
|
|
32168
|
+
return false;
|
|
32169
|
+
}
|
|
32170
|
+
}
|
|
32171
|
+
async function excludeIndexFromGitStatus(cwd) {
|
|
32172
|
+
try {
|
|
32173
|
+
const { stdout: stdout6 } = await execFileAsync3("git", ["rev-parse", "--git-path", "info/exclude"], {
|
|
32174
|
+
cwd,
|
|
32175
|
+
timeout: 1e4
|
|
32176
|
+
});
|
|
32177
|
+
const gitPath = stdout6.trim();
|
|
32178
|
+
if (!gitPath)
|
|
32179
|
+
return;
|
|
32180
|
+
const path = isAbsolute3(gitPath) ? gitPath : resolve19(cwd, gitPath);
|
|
32181
|
+
await mkdir9(dirname11(path), { recursive: true });
|
|
32182
|
+
const existing = await readFile7(path, "utf8").catch(() => "");
|
|
32183
|
+
if (existing.split("\n").includes(".codegraph/"))
|
|
32184
|
+
return;
|
|
32185
|
+
await appendFile(path, `${existing && !existing.endsWith("\n") ? "\n" : ""}.codegraph/
|
|
32186
|
+
`);
|
|
32187
|
+
} catch {
|
|
32188
|
+
}
|
|
32189
|
+
}
|
|
32190
|
+
async function prepareCodegraphIndex(config, cwd) {
|
|
32191
|
+
const command = config.codegraphCommand;
|
|
32192
|
+
if (!command || !await isGitWorktree(cwd))
|
|
32193
|
+
return false;
|
|
32194
|
+
await excludeIndexFromGitStatus(cwd);
|
|
32195
|
+
const indexPath = resolve19(cwd, CODEGRAPH_INDEX_PATH);
|
|
32196
|
+
const indexed = await access(indexPath).then(() => true, () => false);
|
|
32197
|
+
const args = indexed ? ["sync", "--quiet", cwd] : ["init", "--yes", cwd];
|
|
32198
|
+
try {
|
|
32199
|
+
await execFileAsync3(command, args, {
|
|
32200
|
+
cwd,
|
|
32201
|
+
env: { ...process.env, CODEGRAPH_TELEMETRY: "0", CODEGRAPH_NO_DAEMON: "1" },
|
|
32202
|
+
timeout: CODEGRAPH_PREPARE_TIMEOUT_MS,
|
|
32203
|
+
maxBuffer: 4 * 1024 * 1024
|
|
32204
|
+
});
|
|
32205
|
+
return true;
|
|
32206
|
+
} catch (error) {
|
|
32207
|
+
console.warn(`[body] CodeGraph ${args[0]} failed for ${cwd}; continuing with Beeline read tools:`, error);
|
|
32208
|
+
return false;
|
|
32209
|
+
}
|
|
32210
|
+
}
|
|
32211
|
+
|
|
31999
32212
|
// apps/body/dist/pi-turn-record.js
|
|
32000
|
-
import { readdir as readdir3, readFile as
|
|
32001
|
-
import { resolve as
|
|
32213
|
+
import { readdir as readdir3, readFile as readFile8 } from "node:fs/promises";
|
|
32214
|
+
import { resolve as resolve20 } from "node:path";
|
|
32002
32215
|
function summarizeProviderError(errorMessage2) {
|
|
32003
32216
|
const trimmed = errorMessage2.trim();
|
|
32004
32217
|
const statusMatch = /^(\d{3}):\s*([\s\S]*)$/.exec(trimmed);
|
|
@@ -32019,7 +32232,7 @@ function summarizeProviderError(errorMessage2) {
|
|
|
32019
32232
|
}
|
|
32020
32233
|
async function sessionFileFromMap(home, sessionId) {
|
|
32021
32234
|
try {
|
|
32022
|
-
const raw = await
|
|
32235
|
+
const raw = await readFile8(resolve20(home, ".pi", "pi-acp", "session-map.json"), "utf8");
|
|
32023
32236
|
const map = JSON.parse(raw);
|
|
32024
32237
|
const file = map.sessions?.[sessionId]?.sessionFile;
|
|
32025
32238
|
return typeof file === "string" && file ? file : void 0;
|
|
@@ -32028,7 +32241,7 @@ async function sessionFileFromMap(home, sessionId) {
|
|
|
32028
32241
|
}
|
|
32029
32242
|
}
|
|
32030
32243
|
async function sessionFileFromLayout(piDir, sessionId) {
|
|
32031
|
-
const sessionsRoot =
|
|
32244
|
+
const sessionsRoot = resolve20(piDir, "sessions");
|
|
32032
32245
|
const suffix = `_${sessionId}.jsonl`;
|
|
32033
32246
|
let projects;
|
|
32034
32247
|
try {
|
|
@@ -32037,7 +32250,7 @@ async function sessionFileFromLayout(piDir, sessionId) {
|
|
|
32037
32250
|
return void 0;
|
|
32038
32251
|
}
|
|
32039
32252
|
for (const project of projects) {
|
|
32040
|
-
const dir =
|
|
32253
|
+
const dir = resolve20(sessionsRoot, project);
|
|
32041
32254
|
let files;
|
|
32042
32255
|
try {
|
|
32043
32256
|
files = await readdir3(dir);
|
|
@@ -32046,7 +32259,7 @@ async function sessionFileFromLayout(piDir, sessionId) {
|
|
|
32046
32259
|
}
|
|
32047
32260
|
const match = files.find((file) => file.endsWith(suffix));
|
|
32048
32261
|
if (match)
|
|
32049
|
-
return
|
|
32262
|
+
return resolve20(dir, match);
|
|
32050
32263
|
}
|
|
32051
32264
|
return void 0;
|
|
32052
32265
|
}
|
|
@@ -32064,7 +32277,7 @@ async function readPiTurnRecord(input) {
|
|
|
32064
32277
|
return void 0;
|
|
32065
32278
|
let raw;
|
|
32066
32279
|
try {
|
|
32067
|
-
raw = await
|
|
32280
|
+
raw = await readFile8(file, "utf8");
|
|
32068
32281
|
} catch {
|
|
32069
32282
|
return void 0;
|
|
32070
32283
|
}
|
|
@@ -32220,8 +32433,8 @@ async function withTurnReceiptHeartbeat(api, receipt, task, onHeartbeatError) {
|
|
|
32220
32433
|
}
|
|
32221
32434
|
|
|
32222
32435
|
// apps/body/dist/turn-trace.js
|
|
32223
|
-
import { appendFile, mkdir as
|
|
32224
|
-
import { resolve as
|
|
32436
|
+
import { appendFile as appendFile2, mkdir as mkdir10, readdir as readdir4, rm as rm3 } from "node:fs/promises";
|
|
32437
|
+
import { resolve as resolve21 } from "node:path";
|
|
32225
32438
|
import { performance as performance2 } from "node:perf_hooks";
|
|
32226
32439
|
var TURN_PHASES = [
|
|
32227
32440
|
/** Enqueued on `SessionScheduler` until this turn holds a slot. A capacity wait lives here. */
|
|
@@ -32451,7 +32664,7 @@ function formatTurnTraceLine(record3) {
|
|
|
32451
32664
|
return [head, ...record3.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
|
|
32452
32665
|
}
|
|
32453
32666
|
function turnTraceDirectory(runtimeDir) {
|
|
32454
|
-
return
|
|
32667
|
+
return resolve21(runtimeDir, "turn-traces");
|
|
32455
32668
|
}
|
|
32456
32669
|
var TURN_TRACE_RETENTION_DAYS = 7;
|
|
32457
32670
|
function traceFileName(date) {
|
|
@@ -32468,14 +32681,14 @@ var TurnTraceFile = class {
|
|
|
32468
32681
|
this.options = options;
|
|
32469
32682
|
}
|
|
32470
32683
|
path(now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))()) {
|
|
32471
|
-
return
|
|
32684
|
+
return resolve21(this.directory, traceFileName(now2));
|
|
32472
32685
|
}
|
|
32473
32686
|
write(record3) {
|
|
32474
32687
|
this.tail = this.tail.catch(() => void 0).then(async () => {
|
|
32475
32688
|
const now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))();
|
|
32476
32689
|
const path = this.path(now2);
|
|
32477
|
-
await
|
|
32478
|
-
await
|
|
32690
|
+
await mkdir10(this.directory, { recursive: true, mode: 448 });
|
|
32691
|
+
await appendFile2(path, `${JSON.stringify(record3)}
|
|
32479
32692
|
`, { mode: 384 });
|
|
32480
32693
|
await this.prune(now2);
|
|
32481
32694
|
const log2 = this.options.log ?? ((line) => console.log(line));
|
|
@@ -32492,16 +32705,16 @@ var TurnTraceFile = class {
|
|
|
32492
32705
|
this.prunedDay = day;
|
|
32493
32706
|
const cutoff = traceFileName(new Date(now2.getTime() - TURN_TRACE_RETENTION_DAYS * 864e5));
|
|
32494
32707
|
const names = await readdir4(this.directory).catch(() => []);
|
|
32495
|
-
await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm3(
|
|
32708
|
+
await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm3(resolve21(this.directory, name), { force: true })));
|
|
32496
32709
|
}
|
|
32497
32710
|
};
|
|
32498
32711
|
|
|
32499
32712
|
// apps/body/dist/corner-github-auth.js
|
|
32500
|
-
import { chmod as chmod4, mkdir as
|
|
32501
|
-
import { delimiter as delimiter2, resolve as
|
|
32713
|
+
import { chmod as chmod4, mkdir as mkdir11, writeFile as writeFile9 } from "node:fs/promises";
|
|
32714
|
+
import { delimiter as delimiter2, resolve as resolve22 } from "node:path";
|
|
32502
32715
|
async function installCornerGitHubWrappers(input) {
|
|
32503
|
-
const bin =
|
|
32504
|
-
await
|
|
32716
|
+
const bin = resolve22(input.root, "beeline-github-bin");
|
|
32717
|
+
await mkdir11(bin, { recursive: true, mode: 448 });
|
|
32505
32718
|
const common = {
|
|
32506
32719
|
node: process.execPath,
|
|
32507
32720
|
cli: input.cliEntrypoint,
|
|
@@ -32510,13 +32723,13 @@ async function installCornerGitHubWrappers(input) {
|
|
|
32510
32723
|
featureBranch: input.featureBranch,
|
|
32511
32724
|
targetBranch: input.targetBranch
|
|
32512
32725
|
};
|
|
32513
|
-
await writeLauncher(
|
|
32726
|
+
await writeLauncher(resolve22(bin, "git"), {
|
|
32514
32727
|
...common,
|
|
32515
32728
|
command: input.gitBinary,
|
|
32516
32729
|
launcher: "git"
|
|
32517
32730
|
});
|
|
32518
32731
|
if (input.ghBinary)
|
|
32519
|
-
await writeLauncher(
|
|
32732
|
+
await writeLauncher(resolve22(bin, "gh"), {
|
|
32520
32733
|
...common,
|
|
32521
32734
|
command: input.ghBinary,
|
|
32522
32735
|
launcher: "gh"
|
|
@@ -32653,23 +32866,23 @@ process.exit(result.status ?? 1);
|
|
|
32653
32866
|
// apps/body/dist/warm-node-modules.js
|
|
32654
32867
|
import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
|
|
32655
32868
|
import { constants as fsConstants } from "node:fs";
|
|
32656
|
-
import { chmod as chmod5, copyFile as copyFile2, link, lstat as lstat3, mkdir as
|
|
32657
|
-
import { dirname as
|
|
32869
|
+
import { chmod as chmod5, copyFile as copyFile2, link, lstat as lstat3, mkdir as mkdir12, readdir as readdir5, readFile as readFile9, readlink, rename as rename4, rm as rm4, stat as stat2, symlink as symlink2, utimes } from "node:fs/promises";
|
|
32870
|
+
import { dirname as dirname12, join as join10, resolve as resolve23 } from "node:path";
|
|
32658
32871
|
var STORE_FORMAT = "v1";
|
|
32659
32872
|
var STAGING_PREFIX = ".beeline-warm-";
|
|
32660
32873
|
var STAGING_SWEEP_MS = 24 * 60 * 60 * 1e3;
|
|
32661
32874
|
var WARM_STORE_MAX_ENTRIES = 3;
|
|
32662
32875
|
function sharedNpmCacheDir(supervisorRoot) {
|
|
32663
|
-
return
|
|
32876
|
+
return resolve23(supervisorRoot, "beeline", "npm-cache");
|
|
32664
32877
|
}
|
|
32665
32878
|
function warmNodeModulesStoreDir(supervisorRoot) {
|
|
32666
|
-
return
|
|
32879
|
+
return resolve23(supervisorRoot, "beeline", "node-modules");
|
|
32667
32880
|
}
|
|
32668
32881
|
function isPlanRefusal(value) {
|
|
32669
32882
|
return "failure" in value;
|
|
32670
32883
|
}
|
|
32671
32884
|
async function readWarmPlan(worktreePath) {
|
|
32672
|
-
const lockfile = await
|
|
32885
|
+
const lockfile = await readFile9(resolve23(worktreePath, "package-lock.json")).catch(() => void 0);
|
|
32673
32886
|
if (!lockfile)
|
|
32674
32887
|
return { failure: "no-lockfile" };
|
|
32675
32888
|
const packages = parseLockfilePackages(lockfile);
|
|
@@ -32708,11 +32921,11 @@ async function seedWarmNodeModules(input) {
|
|
|
32708
32921
|
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
32709
32922
|
}
|
|
32710
32923
|
for (const tree of plan.trees) {
|
|
32711
|
-
if (await pathExists(
|
|
32924
|
+
if (await pathExists(resolve23(input.worktreePath, tree))) {
|
|
32712
32925
|
return { reason: "present", key: plan.key };
|
|
32713
32926
|
}
|
|
32714
32927
|
}
|
|
32715
|
-
const entry =
|
|
32928
|
+
const entry = resolve23(input.storeRoot, plan.key);
|
|
32716
32929
|
if (!await isDirectory(entry))
|
|
32717
32930
|
return { reason: "cold", key: plan.key };
|
|
32718
32931
|
const [storeDevice, checkoutDevice] = await Promise.all([
|
|
@@ -32722,19 +32935,19 @@ async function seedWarmNodeModules(input) {
|
|
|
32722
32935
|
if (storeDevice === void 0 || storeDevice !== checkoutDevice) {
|
|
32723
32936
|
return { reason: "cross-device", key: plan.key };
|
|
32724
32937
|
}
|
|
32725
|
-
const staging =
|
|
32938
|
+
const staging = resolve23(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID5()}`);
|
|
32726
32939
|
const placed = [];
|
|
32727
32940
|
const now2 = (input.now ?? Date.now)();
|
|
32728
32941
|
try {
|
|
32729
32942
|
await sweepStaleStaging(input.storeRoot, now2);
|
|
32730
32943
|
await utimes(entry, now2 / 1e3, now2 / 1e3).catch(() => void 0);
|
|
32731
32944
|
for (const tree of plan.trees) {
|
|
32732
|
-
await cloneTree(
|
|
32945
|
+
await cloneTree(resolve23(entry, tree), resolve23(staging, tree), seedFile);
|
|
32733
32946
|
}
|
|
32734
32947
|
for (const tree of plan.trees) {
|
|
32735
|
-
const target =
|
|
32736
|
-
await
|
|
32737
|
-
await rename4(
|
|
32948
|
+
const target = resolve23(input.worktreePath, tree);
|
|
32949
|
+
await mkdir12(dirname12(target), { recursive: true });
|
|
32950
|
+
await rename4(resolve23(staging, tree), target);
|
|
32738
32951
|
placed.push(target);
|
|
32739
32952
|
}
|
|
32740
32953
|
return { reason: "seeded", key: plan.key };
|
|
@@ -32752,11 +32965,11 @@ async function harvestWarmNodeModules(input) {
|
|
|
32752
32965
|
if (isPlanRefusal(plan)) {
|
|
32753
32966
|
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
32754
32967
|
}
|
|
32755
|
-
const entry =
|
|
32968
|
+
const entry = resolve23(input.storeRoot, plan.key);
|
|
32756
32969
|
if (await pathExists(entry))
|
|
32757
32970
|
return { reason: "already-warm", key: plan.key };
|
|
32758
32971
|
for (const tree of plan.trees) {
|
|
32759
|
-
if (!await isDirectory(
|
|
32972
|
+
if (!await isDirectory(resolve23(input.worktreePath, tree))) {
|
|
32760
32973
|
return { reason: "no-node-modules", key: plan.key, detail: tree };
|
|
32761
32974
|
}
|
|
32762
32975
|
}
|
|
@@ -32768,12 +32981,12 @@ async function harvestWarmNodeModules(input) {
|
|
|
32768
32981
|
detail: `${missing.length} absent, e.g. ${missing.slice(0, 3).join(", ")}`
|
|
32769
32982
|
};
|
|
32770
32983
|
}
|
|
32771
|
-
const staging =
|
|
32984
|
+
const staging = resolve23(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID5()}`);
|
|
32772
32985
|
try {
|
|
32773
|
-
await
|
|
32986
|
+
await mkdir12(input.storeRoot, { recursive: true, mode: 493 });
|
|
32774
32987
|
await sweepStaleStaging(input.storeRoot, (input.now ?? Date.now)());
|
|
32775
32988
|
for (const tree of plan.trees) {
|
|
32776
|
-
await cloneTree(
|
|
32989
|
+
await cloneTree(resolve23(input.worktreePath, tree), resolve23(staging, tree), harvestFile);
|
|
32777
32990
|
}
|
|
32778
32991
|
await input.onCopied?.();
|
|
32779
32992
|
if (!await stagedTreeIsPublishable(input.worktreePath, staging, plan.key)) {
|
|
@@ -32796,16 +33009,16 @@ async function stagedTreeIsPublishable(worktreePath, staging, key) {
|
|
|
32796
33009
|
return false;
|
|
32797
33010
|
const hidden = join10("node_modules", ".package-lock.json");
|
|
32798
33011
|
const [copied, current] = await Promise.all([
|
|
32799
|
-
|
|
32800
|
-
|
|
33012
|
+
readFile9(resolve23(staging, hidden)).catch(() => void 0),
|
|
33013
|
+
readFile9(resolve23(worktreePath, hidden)).catch(() => void 0)
|
|
32801
33014
|
]);
|
|
32802
33015
|
if (!copied || !current || !copied.equals(current))
|
|
32803
33016
|
return false;
|
|
32804
33017
|
return (await missingInstalledPackages(worktreePath, staging)).length === 0;
|
|
32805
33018
|
}
|
|
32806
33019
|
async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
32807
|
-
const wanted = parseLockfilePackages(await
|
|
32808
|
-
const installed = parseLockfilePackages(await
|
|
33020
|
+
const wanted = parseLockfilePackages(await readFile9(resolve23(worktreePath, "package-lock.json")).catch(() => void 0));
|
|
33021
|
+
const installed = parseLockfilePackages(await readFile9(resolve23(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
|
|
32809
33022
|
if (!wanted)
|
|
32810
33023
|
return ["package-lock.json is unreadable"];
|
|
32811
33024
|
if (!installed)
|
|
@@ -32825,7 +33038,7 @@ async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
|
32825
33038
|
required.push(path);
|
|
32826
33039
|
}
|
|
32827
33040
|
await mapWithLimit(required, INTEGRITY_READ_CONCURRENCY, async (path) => {
|
|
32828
|
-
if (!(path in installed) || !await isInstalledPackage(
|
|
33041
|
+
if (!(path in installed) || !await isInstalledPackage(resolve23(treeRoot, path))) {
|
|
32829
33042
|
missing.push(path);
|
|
32830
33043
|
}
|
|
32831
33044
|
});
|
|
@@ -32864,7 +33077,7 @@ function isContainedTreePath(value) {
|
|
|
32864
33077
|
return isContainedPath(value) && value.split("/").pop() === "node_modules";
|
|
32865
33078
|
}
|
|
32866
33079
|
async function cloneTree(source, target, file, topLevel = true) {
|
|
32867
|
-
await
|
|
33080
|
+
await mkdir12(target, { recursive: true, mode: 493 });
|
|
32868
33081
|
for (const entry of await readdir5(source, { withFileTypes: true })) {
|
|
32869
33082
|
const from = join10(source, entry.name);
|
|
32870
33083
|
const to = join10(target, entry.name);
|
|
@@ -32933,7 +33146,7 @@ function describe3(error) {
|
|
|
32933
33146
|
}
|
|
32934
33147
|
|
|
32935
33148
|
// apps/body/dist/monolith-room-turn.js
|
|
32936
|
-
import { mkdir as
|
|
33149
|
+
import { mkdir as mkdir13 } from "node:fs/promises";
|
|
32937
33150
|
import { homedir as homedir9 } from "node:os";
|
|
32938
33151
|
import { join as join11 } from "node:path";
|
|
32939
33152
|
|
|
@@ -33020,6 +33233,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
33020
33233
|
sessionId;
|
|
33021
33234
|
/** The configuration the live session baked in; a change invalidates it. */
|
|
33022
33235
|
sessionFingerprint;
|
|
33236
|
+
/** Whether CodeGraph preparation succeeded for the live session. */
|
|
33237
|
+
sessionCodegraphReady = false;
|
|
33023
33238
|
/** The live session's environment, read back for pi's own turn record. */
|
|
33024
33239
|
agentEnv = {};
|
|
33025
33240
|
/** OpenRouter providers this activation pinned, in order (C92). */
|
|
@@ -33168,6 +33383,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33168
33383
|
this.client = void 0;
|
|
33169
33384
|
this.sessionId = void 0;
|
|
33170
33385
|
this.sessionFingerprint = void 0;
|
|
33386
|
+
this.sessionCodegraphReady = false;
|
|
33171
33387
|
this.pinnedProviderOverride = void 0;
|
|
33172
33388
|
if (client?.isAlive)
|
|
33173
33389
|
await client.stop();
|
|
@@ -33199,11 +33415,11 @@ var MonolithRoomTurnLoop = class {
|
|
|
33199
33415
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
33200
33416
|
soul: configuration.soul ?? self?.soul,
|
|
33201
33417
|
agentName: self?.name ?? this.agent.name,
|
|
33202
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
33418
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
33203
33419
|
operatorHome: this.options.config.operatorHome,
|
|
33204
33420
|
agentKind: this.options.config.agentKind,
|
|
33205
33421
|
grantedHostRoutes
|
|
33206
|
-
})
|
|
33422
|
+
}), this.sessionCodegraphReady)
|
|
33207
33423
|
});
|
|
33208
33424
|
}
|
|
33209
33425
|
async grantedHostRoutes() {
|
|
@@ -33237,7 +33453,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33237
33453
|
]);
|
|
33238
33454
|
const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
|
|
33239
33455
|
const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
|
|
33240
|
-
await
|
|
33456
|
+
await mkdir13(this.options.cwd, { recursive: true });
|
|
33241
33457
|
const selectionModel = configuration.model ?? this.options.config.modelSelection?.model;
|
|
33242
33458
|
const selectionEffort = configuration.effort ?? this.options.config.modelSelection?.effort;
|
|
33243
33459
|
const selection = selectionModel || selectionEffort ? { model: selectionModel, effort: selectionEffort } : void 0;
|
|
@@ -33264,17 +33480,6 @@ var MonolithRoomTurnLoop = class {
|
|
|
33264
33480
|
command
|
|
33265
33481
|
});
|
|
33266
33482
|
const agentEnv = { ...this.options.config.agentEnv, ...homeOverlay };
|
|
33267
|
-
const fingerprint = sessionConfigFingerprint({
|
|
33268
|
-
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
33269
|
-
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
33270
|
-
soul: configuration.soul ?? self?.soul,
|
|
33271
|
-
agentName: self?.name ?? this.agent.name,
|
|
33272
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
33273
|
-
operatorHome: this.options.config.operatorHome,
|
|
33274
|
-
agentKind: this.options.config.agentKind,
|
|
33275
|
-
grantedHostRoutes
|
|
33276
|
-
})
|
|
33277
|
-
});
|
|
33278
33483
|
this.agentEnv = agentEnv;
|
|
33279
33484
|
const agentArgs = agentArgsWithModelSelection({
|
|
33280
33485
|
kind: this.options.config.agentKind,
|
|
@@ -33286,10 +33491,22 @@ var MonolithRoomTurnLoop = class {
|
|
|
33286
33491
|
this.sessionScratchDir = tmpDir;
|
|
33287
33492
|
this.sessionStateDirs = stateDirs;
|
|
33288
33493
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
33289
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
33494
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir13(dir, { recursive: true })));
|
|
33290
33495
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
33291
33496
|
if (attachScratchRoot)
|
|
33292
|
-
await
|
|
33497
|
+
await mkdir13(attachScratchRoot, { recursive: true });
|
|
33498
|
+
const codegraphReady = await prepareCodegraphIndex(this.options.config, this.options.cwd);
|
|
33499
|
+
const fingerprint = sessionConfigFingerprint({
|
|
33500
|
+
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
33501
|
+
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
33502
|
+
soul: configuration.soul ?? self?.soul,
|
|
33503
|
+
agentName: self?.name ?? this.agent.name,
|
|
33504
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
33505
|
+
operatorHome: this.options.config.operatorHome,
|
|
33506
|
+
agentKind: this.options.config.agentKind,
|
|
33507
|
+
grantedHostRoutes
|
|
33508
|
+
}), codegraphReady)
|
|
33509
|
+
});
|
|
33293
33510
|
const spawnCommand = wrapAgentCommand({
|
|
33294
33511
|
bwrapPath: this.options.config.bwrapPath,
|
|
33295
33512
|
spec: {
|
|
@@ -33300,6 +33517,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33300
33517
|
...tmpDir ? { tmpDir } : {},
|
|
33301
33518
|
additionalWritablePaths: [
|
|
33302
33519
|
...attachScratchRoot ? [attachScratchRoot] : [],
|
|
33520
|
+
...codegraphReady ? [codegraphIndexDirectory(this.options.cwd)] : [],
|
|
33303
33521
|
...grantedSquireHostBindPaths({
|
|
33304
33522
|
operatorHome,
|
|
33305
33523
|
agentKind: this.options.config.agentKind,
|
|
@@ -33324,9 +33542,16 @@ var MonolithRoomTurnLoop = class {
|
|
|
33324
33542
|
attachScratchRoot,
|
|
33325
33543
|
turnContextPath: this.commandContext.path,
|
|
33326
33544
|
directMessage,
|
|
33327
|
-
...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
33545
|
+
...this.options.grantRunnerEndpoint && this.options.config.bwrapPath ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
33328
33546
|
})
|
|
33329
33547
|
];
|
|
33548
|
+
if (codegraphReady) {
|
|
33549
|
+
const codegraph = codegraphMcpServer(this.options.config, this.options.cwd, {
|
|
33550
|
+
readonly: true
|
|
33551
|
+
});
|
|
33552
|
+
if (codegraph)
|
|
33553
|
+
servers.push(codegraph);
|
|
33554
|
+
}
|
|
33330
33555
|
const youtube = youtubeMcpServer(this.options.config, this.options.youtubeAccessToken);
|
|
33331
33556
|
if (youtube)
|
|
33332
33557
|
servers.push(youtube);
|
|
@@ -33358,7 +33583,12 @@ var MonolithRoomTurnLoop = class {
|
|
|
33358
33583
|
// (`config.ts`), which is exactly when `wrapAgentCommand` above wraps.
|
|
33359
33584
|
osSandbox: Boolean(this.options.config.bwrapPath),
|
|
33360
33585
|
autoApprovePermissions: false,
|
|
33361
|
-
permissionAllowlist: (request) => isRoomMcpPermissionRequest(request, mountedServers, hostServers)
|
|
33586
|
+
permissionAllowlist: (request) => isRoomMcpPermissionRequest(request, mountedServers, hostServers),
|
|
33587
|
+
onCommands: agentCommandCatalogPublisher({
|
|
33588
|
+
api: this.options.api,
|
|
33589
|
+
agentId: this.agent.publicKey,
|
|
33590
|
+
workspaceId: this.options.workspaceId
|
|
33591
|
+
})
|
|
33362
33592
|
};
|
|
33363
33593
|
this.client = (this.options.createAcpClient ?? ((value) => new AcpClient(value)))(clientOptions);
|
|
33364
33594
|
await this.client.start();
|
|
@@ -33390,6 +33620,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33390
33620
|
});
|
|
33391
33621
|
this.sessionId = opened.sessionId;
|
|
33392
33622
|
this.sessionFingerprint = fingerprint;
|
|
33623
|
+
this.sessionCodegraphReady = codegraphReady;
|
|
33393
33624
|
if (selection) {
|
|
33394
33625
|
const options = filterAllowedModelConfigOptions(parseAdvertisedConfigOptions(opened.raw, selection.model));
|
|
33395
33626
|
await applyAgentModelSelection(this.client, opened.sessionId, options, selection);
|
|
@@ -33453,6 +33684,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33453
33684
|
this.client = void 0;
|
|
33454
33685
|
this.sessionId = void 0;
|
|
33455
33686
|
this.sessionFingerprint = void 0;
|
|
33687
|
+
this.sessionCodegraphReady = false;
|
|
33456
33688
|
if (client?.isAlive)
|
|
33457
33689
|
await client.stop();
|
|
33458
33690
|
this.pinnedProviderOverride = next;
|
|
@@ -33545,7 +33777,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33545
33777
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
33546
33778
|
const transcriptRows = conversation.items.filter((message) => message.type === "message" && message.id !== item.id && !active.steers.some((steerItem) => steerItem.id === message.id)).slice(-80).map((message) => ({
|
|
33547
33779
|
id: message.id,
|
|
33548
|
-
line: roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id), this.acceptsImages())
|
|
33780
|
+
line: roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id), this.acceptsImages(), message.id)
|
|
33549
33781
|
}));
|
|
33550
33782
|
const grantDecision = this.commandContext.current?.action === "resume";
|
|
33551
33783
|
const resumedRequestId = grantDecision ? this.pausedOnGrantRequestId : void 0;
|
|
@@ -33565,7 +33797,7 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
33565
33797
|
MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE
|
|
33566
33798
|
].join(" "),
|
|
33567
33799
|
`Current task selected by the server from ${inboxItemAuthorName(item, names)}:`,
|
|
33568
|
-
roomMessagePrompt("", inboxItemPromptBody(item), item.attachments, delivered, this.acceptsImages())
|
|
33800
|
+
roomMessagePrompt("", inboxItemPromptBody(item), item.attachments, delivered, this.acceptsImages(), item.type === "message" ? item.id : void 0)
|
|
33569
33801
|
].filter(Boolean).join("\n\n");
|
|
33570
33802
|
const stream = new AgentTurnStream({
|
|
33571
33803
|
api,
|
|
@@ -33608,7 +33840,7 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
33608
33840
|
"The previous run was cancelled because its harness could not accept every live steer.",
|
|
33609
33841
|
"Resume the same turn. Keep the original request and everything that happened before it was cancelled.",
|
|
33610
33842
|
"Human messages that arrived after the original request, in transcript order:",
|
|
33611
|
-
...active.steers.map((steerItem) => roomMessagePrompt(steerItem.authorId.slice(0, 12), steerItem.body, steerItem.attachments, this.deliveredAttachments.get(steerItem.id), this.acceptsImages())),
|
|
33843
|
+
...active.steers.map((steerItem) => roomMessagePrompt(steerItem.authorId.slice(0, 12), steerItem.body, steerItem.attachments, this.deliveredAttachments.get(steerItem.id), this.acceptsImages(), steerItem.type === "message" ? steerItem.id : void 0)),
|
|
33612
33844
|
"Continue now and answer the updated request without erasing the earlier context."
|
|
33613
33845
|
].join("\n\n");
|
|
33614
33846
|
}
|
|
@@ -33766,14 +33998,18 @@ function openCornerToolCall(calls) {
|
|
|
33766
33998
|
function openedACorner(call) {
|
|
33767
33999
|
return !!call && isCompletedToolCall(call);
|
|
33768
34000
|
}
|
|
33769
|
-
function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true) {
|
|
34001
|
+
function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true, messageId) {
|
|
33770
34002
|
const message = body.trim() || "(shared attachments)";
|
|
33771
34003
|
const rendered = author ? `${author}: ${message}` : message;
|
|
33772
|
-
return [
|
|
34004
|
+
return [
|
|
34005
|
+
...messageId ? [`[message id: ${messageId}]`] : [],
|
|
34006
|
+
rendered,
|
|
34007
|
+
...attachmentPromptLines(attachments, delivered, harnessAcceptsImages)
|
|
34008
|
+
].join("\n");
|
|
33773
34009
|
}
|
|
33774
34010
|
|
|
33775
34011
|
// apps/body/dist/monolith-corner-turn.js
|
|
33776
|
-
var
|
|
34012
|
+
var execFileAsync4 = promisify4(execFile6);
|
|
33777
34013
|
var TOOL_ARGUMENT_MAX_BYTES = 1200;
|
|
33778
34014
|
var TOOL_OUTPUT_MAX_BYTES = 3200;
|
|
33779
34015
|
var TOOL_PATH_LIMIT = 12;
|
|
@@ -33816,8 +34052,8 @@ function isCornerChecksTurn(trigger, restates) {
|
|
|
33816
34052
|
async function cornerUndeliveredRepositoryState(worktreePath, featureBranch, targetBranch) {
|
|
33817
34053
|
try {
|
|
33818
34054
|
const [{ stdout: status }, ahead] = await Promise.all([
|
|
33819
|
-
|
|
33820
|
-
featureBranch ? firstResolvedRemoteRef(worktreePath, [featureBranch, targetBranch]).then((remoteRef) => remoteRef ?
|
|
34055
|
+
execFileAsync4("git", ["-C", worktreePath, "status", "--porcelain=v1"]),
|
|
34056
|
+
featureBranch ? firstResolvedRemoteRef(worktreePath, [featureBranch, targetBranch]).then((remoteRef) => remoteRef ? execFileAsync4("git", [
|
|
33821
34057
|
"-C",
|
|
33822
34058
|
worktreePath,
|
|
33823
34059
|
"rev-list",
|
|
@@ -33837,7 +34073,7 @@ async function firstResolvedRemoteRef(worktreePath, branches) {
|
|
|
33837
34073
|
if (!branch)
|
|
33838
34074
|
continue;
|
|
33839
34075
|
const ref = `refs/remotes/origin/${branch}`;
|
|
33840
|
-
const resolved = await
|
|
34076
|
+
const resolved = await execFileAsync4("git", ["-C", worktreePath, "rev-parse", "--verify", ref]).then(() => true).catch(() => false);
|
|
33841
34077
|
if (resolved)
|
|
33842
34078
|
return ref;
|
|
33843
34079
|
}
|
|
@@ -33945,7 +34181,7 @@ async function cornerToolActivity(call, worktreePath, requestedBy) {
|
|
|
33945
34181
|
let title = oneLine(redactToolDetail(call.title ?? "")) || `${operation} tool`;
|
|
33946
34182
|
if (isSuccessfulCommit(call)) {
|
|
33947
34183
|
try {
|
|
33948
|
-
const shown = await
|
|
34184
|
+
const shown = await execFileAsync4("git", ["-C", worktreePath, "show", "--format=%s", "--name-only", "--no-renames", "HEAD"], { maxBuffer: 1024 * 1024 });
|
|
33949
34185
|
const lines = shown.stdout.split(/\r?\n/);
|
|
33950
34186
|
const subject = oneLine(lines.shift() ?? "commit");
|
|
33951
34187
|
const files = new Set(lines.map(oneLine).filter(Boolean));
|
|
@@ -33967,20 +34203,36 @@ async function cornerToolActivity(call, worktreePath, requestedBy) {
|
|
|
33967
34203
|
...paths.length ? { files: paths.map((path) => ({ path })) } : {}
|
|
33968
34204
|
};
|
|
33969
34205
|
}
|
|
34206
|
+
var CORNER_CLOSE_POLL_BASE_MS = 10 * 6e4;
|
|
34207
|
+
function cornerClosePollMs(random = Math.random) {
|
|
34208
|
+
return CORNER_CLOSE_POLL_BASE_MS + Math.floor(random() * 3e3);
|
|
34209
|
+
}
|
|
33970
34210
|
var MonolithCornerTurnLoop = class {
|
|
33971
34211
|
options;
|
|
33972
34212
|
commandContext;
|
|
33973
34213
|
agent;
|
|
33974
34214
|
wakeIntake;
|
|
33975
|
-
/**
|
|
34215
|
+
/**
|
|
34216
|
+
* Called by the daemon's one slow workspace reconciliation sweep, and by the
|
|
34217
|
+
* fast reconcile a socket reconnect arms. A `corner-complete` published while
|
|
34218
|
+
* that socket was down is never replayed, so the sweep clears the close
|
|
34219
|
+
* throttle too: the durable read is what recovers the frame nobody heard.
|
|
34220
|
+
*
|
|
34221
|
+
* The wake is the intake loop's own stable notify and is handed back exactly
|
|
34222
|
+
* once, so it is kept: clearing it here left `requestClose` waking nothing
|
|
34223
|
+
* after the first sweep, and a pushed `corner-complete` then waited out the
|
|
34224
|
+
* idle timer. Intake clears it itself when it exits.
|
|
34225
|
+
*/
|
|
33976
34226
|
requestReconciliation() {
|
|
34227
|
+
this.lastCloseCheck = 0;
|
|
33977
34228
|
this.wakeIntake?.();
|
|
33978
|
-
this.wakeIntake = void 0;
|
|
33979
34229
|
}
|
|
33980
34230
|
client;
|
|
33981
34231
|
sessionId;
|
|
33982
34232
|
/** The configuration the live session baked in; a change invalidates it. */
|
|
33983
34233
|
sessionFingerprint;
|
|
34234
|
+
/** Whether CodeGraph preparation succeeded for the live session. */
|
|
34235
|
+
sessionCodegraphReady = false;
|
|
33984
34236
|
/** What this exact ACP session has already been prompted with (`warm-transcript.ts`). */
|
|
33985
34237
|
warmTranscript = new WarmTranscript();
|
|
33986
34238
|
/** The live session's environment, read back for pi's own turn record. */
|
|
@@ -34023,10 +34275,17 @@ var MonolithCornerTurnLoop = class {
|
|
|
34023
34275
|
* by the same replay window the inbox already de-duplicates over.
|
|
34024
34276
|
*/
|
|
34025
34277
|
stoppedTurns = /* @__PURE__ */ new Set();
|
|
34278
|
+
/** Live corner-complete: close now, do not wait for the recovery GET. */
|
|
34279
|
+
closePushed = false;
|
|
34280
|
+
/** Last durable close read; 0 means the first check still runs. */
|
|
34281
|
+
lastCloseCheck = 0;
|
|
34282
|
+
/** This corner's own jittered recovery interval, drawn once. */
|
|
34283
|
+
closePollMs;
|
|
34026
34284
|
/** In-flight warm-store harvest, awaited at shutdown and never by a turn. */
|
|
34027
34285
|
harvest;
|
|
34028
34286
|
constructor(options) {
|
|
34029
34287
|
this.options = options;
|
|
34288
|
+
this.closePollMs = options.closePollMs ?? cornerClosePollMs();
|
|
34030
34289
|
this.agent = runtimeIdentity(options.runtime.agent);
|
|
34031
34290
|
this.commandContext = new CommandExecutionContext(options.config.agentHomeRoot);
|
|
34032
34291
|
this.options = { ...options, api: this.commandContext.bind(options.api) };
|
|
@@ -34046,6 +34305,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
34046
34305
|
isBusy() {
|
|
34047
34306
|
return this.busy;
|
|
34048
34307
|
}
|
|
34308
|
+
/** corner-complete on the wire: wake intake so `closed()` reaps now. */
|
|
34309
|
+
requestClose() {
|
|
34310
|
+
this.closePushed = true;
|
|
34311
|
+
this.wakeIntake?.();
|
|
34312
|
+
}
|
|
34049
34313
|
refreshPersonaForSoulUpdate() {
|
|
34050
34314
|
return this.options.scheduler.suspend(this.options.cornerId);
|
|
34051
34315
|
}
|
|
@@ -34091,6 +34355,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34091
34355
|
this.client = void 0;
|
|
34092
34356
|
this.sessionId = void 0;
|
|
34093
34357
|
this.sessionFingerprint = void 0;
|
|
34358
|
+
this.sessionCodegraphReady = false;
|
|
34094
34359
|
this.pinnedProviderOverride = void 0;
|
|
34095
34360
|
if (client?.isAlive)
|
|
34096
34361
|
await client.stop();
|
|
@@ -34117,11 +34382,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
34117
34382
|
soul: configuration.soul ?? self?.soul,
|
|
34118
34383
|
agentName: self?.name ?? this.agent.name,
|
|
34119
34384
|
yoloMode: configuration.yoloMode,
|
|
34120
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
34385
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
34121
34386
|
operatorHome: this.options.config.operatorHome,
|
|
34122
34387
|
agentKind: this.options.config.agentKind,
|
|
34123
34388
|
grantedHostRoutes
|
|
34124
|
-
}),
|
|
34389
|
+
}), this.sessionCodegraphReady),
|
|
34125
34390
|
reviewerHandle: configuration.reviewerHandle
|
|
34126
34391
|
});
|
|
34127
34392
|
}
|
|
@@ -34169,7 +34434,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34169
34434
|
}
|
|
34170
34435
|
const selfReviewerInstruction = cornerSelfReviewerInstruction(reviewerInput);
|
|
34171
34436
|
this.cornerTurnEndNudge = reviewerInstruction ?? selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
|
|
34172
|
-
await
|
|
34437
|
+
await mkdir14(this.options.worktreePath, { recursive: true });
|
|
34173
34438
|
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
34174
34439
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
34175
34440
|
root: this.options.config.agentHomeRoot,
|
|
@@ -34195,8 +34460,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
34195
34460
|
const repository = this.options.repository;
|
|
34196
34461
|
let githubEnv = repository ? { GH_TOKEN: repository.githubToken, GITHUB_TOKEN: repository.githubToken } : {};
|
|
34197
34462
|
if (repository && this.options.config.runtimeConfigPath && this.options.config.agentHomeRoot) {
|
|
34198
|
-
const gitBinary = (await
|
|
34199
|
-
const ghBinary = await
|
|
34463
|
+
const gitBinary = (await execFileAsync4("which", ["git"])).stdout.trim();
|
|
34464
|
+
const ghBinary = await execFileAsync4("which", ["gh"]).then((result) => result.stdout.trim()).catch(() => void 0);
|
|
34200
34465
|
githubEnv = await installCornerGitHubWrappers({
|
|
34201
34466
|
root: this.options.config.agentHomeRoot,
|
|
34202
34467
|
runtimeConfigPath: this.options.config.runtimeConfigPath,
|
|
@@ -34210,7 +34475,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34210
34475
|
});
|
|
34211
34476
|
}
|
|
34212
34477
|
const npmCacheDir = sharedNpmCacheDir(this.options.runtime.supervisorRoot);
|
|
34213
|
-
await
|
|
34478
|
+
await mkdir14(npmCacheDir, { recursive: true, mode: 448 });
|
|
34214
34479
|
const agentEnv = {
|
|
34215
34480
|
...this.options.config.agentEnv,
|
|
34216
34481
|
...homeOverlay,
|
|
@@ -34218,19 +34483,6 @@ var MonolithCornerTurnLoop = class {
|
|
|
34218
34483
|
npm_config_cache: npmCacheDir
|
|
34219
34484
|
};
|
|
34220
34485
|
const operatorHome = this.options.config.operatorHome ?? homedir10();
|
|
34221
|
-
const fingerprint = sessionConfigFingerprint({
|
|
34222
|
-
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
34223
|
-
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
34224
|
-
soul: configuration.soul ?? self?.soul,
|
|
34225
|
-
agentName: self?.name ?? this.agent.name,
|
|
34226
|
-
yoloMode: configuration.yoloMode,
|
|
34227
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
34228
|
-
operatorHome: this.options.config.operatorHome,
|
|
34229
|
-
agentKind: this.options.config.agentKind,
|
|
34230
|
-
grantedHostRoutes
|
|
34231
|
-
}),
|
|
34232
|
-
reviewerHandle: configuration.reviewerHandle
|
|
34233
|
-
});
|
|
34234
34486
|
this.agentEnv = agentEnv;
|
|
34235
34487
|
const agentArgs = agentArgsWithModelSelection({
|
|
34236
34488
|
kind: this.options.config.agentKind,
|
|
@@ -34241,10 +34493,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
34241
34493
|
this.attachmentDir = tmpDir ? join12(tmpDir, "beeline-attachments") : void 0;
|
|
34242
34494
|
this.sessionScratchDir = tmpDir;
|
|
34243
34495
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
34244
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
34496
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir14(dir, { recursive: true })));
|
|
34245
34497
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
34246
34498
|
if (attachScratchRoot)
|
|
34247
|
-
await
|
|
34499
|
+
await mkdir14(attachScratchRoot, { recursive: true });
|
|
34248
34500
|
const spawnCommand = wrapAgentCommand({
|
|
34249
34501
|
bwrapPath: this.options.config.bwrapPath,
|
|
34250
34502
|
spec: {
|
|
@@ -34280,10 +34532,29 @@ var MonolithCornerTurnLoop = class {
|
|
|
34280
34532
|
agentCwd: this.options.worktreePath,
|
|
34281
34533
|
agentLabel: harnessLabel,
|
|
34282
34534
|
autoApprovePermissions: true,
|
|
34283
|
-
permissionHandler: () => Promise.resolve("allow")
|
|
34535
|
+
permissionHandler: () => Promise.resolve("allow"),
|
|
34536
|
+
onCommands: agentCommandCatalogPublisher({
|
|
34537
|
+
api: this.options.api,
|
|
34538
|
+
agentId: this.agent.publicKey,
|
|
34539
|
+
workspaceId: this.options.workspaceId
|
|
34540
|
+
})
|
|
34284
34541
|
};
|
|
34285
34542
|
this.client = (this.options.createAcpClient ?? ((value) => new AcpClient(value)))(clientOptions);
|
|
34286
34543
|
await this.client.start();
|
|
34544
|
+
const codegraphReady = await prepareCodegraphIndex(this.options.config, this.options.worktreePath);
|
|
34545
|
+
const fingerprint = sessionConfigFingerprint({
|
|
34546
|
+
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
34547
|
+
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
34548
|
+
soul: configuration.soul ?? self?.soul,
|
|
34549
|
+
agentName: self?.name ?? this.agent.name,
|
|
34550
|
+
yoloMode: configuration.yoloMode,
|
|
34551
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
34552
|
+
operatorHome: this.options.config.operatorHome,
|
|
34553
|
+
agentKind: this.options.config.agentKind,
|
|
34554
|
+
grantedHostRoutes
|
|
34555
|
+
}), codegraphReady),
|
|
34556
|
+
reviewerHandle: configuration.reviewerHandle
|
|
34557
|
+
});
|
|
34287
34558
|
const servers = [
|
|
34288
34559
|
...repository ? [
|
|
34289
34560
|
{
|
|
@@ -34317,6 +34588,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
34317
34588
|
...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
34318
34589
|
})
|
|
34319
34590
|
];
|
|
34591
|
+
if (codegraphReady) {
|
|
34592
|
+
const codegraph = codegraphMcpServer(this.options.config, this.options.worktreePath, {
|
|
34593
|
+
readonly: false
|
|
34594
|
+
});
|
|
34595
|
+
if (codegraph)
|
|
34596
|
+
servers.push(codegraph);
|
|
34597
|
+
}
|
|
34320
34598
|
const youtube = youtubeMcpServer(this.options.config, this.options.youtubeAccessToken);
|
|
34321
34599
|
if (youtube)
|
|
34322
34600
|
servers.push(youtube);
|
|
@@ -34357,15 +34635,17 @@ var MonolithCornerTurnLoop = class {
|
|
|
34357
34635
|
"This is a no-code corner with no repository checkout and no GitHub workflow.",
|
|
34358
34636
|
"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.",
|
|
34359
34637
|
"Do not initialize a repository, create a branch, commit, push, open a pull request, or wait for GitHub checks.",
|
|
34360
|
-
// This lane has no pull request URL and no merge card, so
|
|
34361
|
-
//
|
|
34362
|
-
//
|
|
34363
|
-
|
|
34638
|
+
// This lane has no pull request URL and no merge card, so its
|
|
34639
|
+
// attached final reply is the requester-facing completion
|
|
34640
|
+
// signal. close_corner is still the lifecycle completion: it
|
|
34641
|
+
// archives the corner and reaps this scratch workspace.
|
|
34642
|
+
this.options.requesterHandle ? `Deliver the result as artifacts: post_artifact everything the objective asked for, then call close_corner. Finish the turn by replying with @${this.options.requesterHandle} and one line on what you posted; that attached reply is the requester-facing completion signal.` : `Deliver the result as artifacts: post_artifact everything the objective asked for, then call close_corner. Finish the turn by replying with one line on what you posted; that attached reply is the requester-facing completion signal.`
|
|
34364
34643
|
]
|
|
34365
34644
|
].filter(Boolean).join("\n\n")
|
|
34366
34645
|
});
|
|
34367
34646
|
this.sessionId = opened.sessionId;
|
|
34368
34647
|
this.sessionFingerprint = fingerprint;
|
|
34648
|
+
this.sessionCodegraphReady = codegraphReady;
|
|
34369
34649
|
if (selection) {
|
|
34370
34650
|
const options = filterAllowedModelConfigOptions(parseAdvertisedConfigOptions(opened.raw, selection.model));
|
|
34371
34651
|
await applyAgentModelSelection(this.client, opened.sessionId, options, selection);
|
|
@@ -34429,6 +34709,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34429
34709
|
this.client = void 0;
|
|
34430
34710
|
this.sessionId = void 0;
|
|
34431
34711
|
this.sessionFingerprint = void 0;
|
|
34712
|
+
this.sessionCodegraphReady = false;
|
|
34432
34713
|
if (client?.isAlive)
|
|
34433
34714
|
await client.stop();
|
|
34434
34715
|
this.pinnedProviderOverride = next;
|
|
@@ -34448,7 +34729,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34448
34729
|
...this.turnTraceSink ? { sink: this.turnTraceSink } : {}
|
|
34449
34730
|
});
|
|
34450
34731
|
}
|
|
34451
|
-
async prompt(requestId, trigger, attachments = [], requestedById, restates) {
|
|
34732
|
+
async prompt(requestId, trigger, attachments = [], requestedById, restates, sourceMessageId) {
|
|
34452
34733
|
const { api, cornerId } = this.options;
|
|
34453
34734
|
if (this.stoppedTurns.has(requestId))
|
|
34454
34735
|
return;
|
|
@@ -34487,7 +34768,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
34487
34768
|
this.currentTurn = { requestId, requester: requestedBy };
|
|
34488
34769
|
const transcriptRows = conversation.items.slice(-120).map((message) => ({
|
|
34489
34770
|
id: message.id,
|
|
34490
|
-
line:
|
|
34771
|
+
line: [
|
|
34772
|
+
...message.type === "message" ? [`[message id: ${message.id}]`] : [],
|
|
34773
|
+
`${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body}`
|
|
34774
|
+
].join("\n")
|
|
34491
34775
|
}));
|
|
34492
34776
|
const buildPrompt = () => [
|
|
34493
34777
|
this.turnIdentityInstructions,
|
|
@@ -34496,6 +34780,7 @@ ${this.options.objective}`,
|
|
|
34496
34780
|
WarmTranscript.render(this.warmTranscript.select(this.sessionId, transcriptRows), "Corner transcript:", "New in the corner since your last turn (the earlier transcript is already in this session):"),
|
|
34497
34781
|
roomMentionDirectory(roster, this.agent.publicKey),
|
|
34498
34782
|
[
|
|
34783
|
+
...sourceMessageId ? [`Reaction target message id: ${sourceMessageId}`] : [],
|
|
34499
34784
|
`Newest trigger:
|
|
34500
34785
|
${trigger}`,
|
|
34501
34786
|
...attachmentPromptLines(attachments, delivered, this.acceptsImages())
|
|
@@ -34779,6 +35064,16 @@ ${trigger}`,
|
|
|
34779
35064
|
onError: (error) => console.error("[thin-core] corner command failed", error),
|
|
34780
35065
|
stop: (requestId) => this.stopTurn(requestId),
|
|
34781
35066
|
closed: async () => {
|
|
35067
|
+
if (this.closePushed) {
|
|
35068
|
+
this.closePushed = false;
|
|
35069
|
+
await this.harvest;
|
|
35070
|
+
await this.options.onCloseRequested();
|
|
35071
|
+
return true;
|
|
35072
|
+
}
|
|
35073
|
+
const now2 = Date.now();
|
|
35074
|
+
if (this.lastCloseCheck !== 0 && now2 - this.lastCloseCheck < this.closePollMs)
|
|
35075
|
+
return false;
|
|
35076
|
+
this.lastCloseCheck = now2;
|
|
34782
35077
|
const state = await api.execute("getCornerRestoreState", { cornerId });
|
|
34783
35078
|
if (!state.closeRequested)
|
|
34784
35079
|
return false;
|
|
@@ -34786,7 +35081,9 @@ ${trigger}`,
|
|
|
34786
35081
|
await this.options.onCloseRequested();
|
|
34787
35082
|
return true;
|
|
34788
35083
|
},
|
|
34789
|
-
run: (command) => this.prompt(command.turnRequestId, command.source.body, command.source.attachments, command.source.authorId, command.reason === "corner_check" ? [command.source.body] : void 0)
|
|
35084
|
+
run: (command) => this.prompt(command.turnRequestId, command.source.body, command.source.attachments, command.source.authorId, command.reason === "corner_check" ? [command.source.body] : void 0, command.source.type === "message" ? command.sourceMessageId : void 0).finally(() => {
|
|
35085
|
+
this.lastCloseCheck = 0;
|
|
35086
|
+
})
|
|
34790
35087
|
});
|
|
34791
35088
|
} finally {
|
|
34792
35089
|
this.options.grantRunner?.unregister(cornerId);
|
|
@@ -35221,7 +35518,7 @@ var SessionScheduler = class {
|
|
|
35221
35518
|
var REMOVAL_CONFIRMATION_READS = 2;
|
|
35222
35519
|
var ROOM_JOIN_CONCURRENCY = 4;
|
|
35223
35520
|
var DEFAULT_ROOM_WATCHDOG_STALE_MS = 9e4;
|
|
35224
|
-
var DEFAULT_RECONCILE_HEARTBEAT_MS = 6e4;
|
|
35521
|
+
var DEFAULT_RECONCILE_HEARTBEAT_MS = 10 * 6e4;
|
|
35225
35522
|
var DEFAULT_DRAIN_DEADLINE_MS = 30 * 6e4;
|
|
35226
35523
|
var CORNER_BRANCH_DELETE_ATTEMPTS = 3;
|
|
35227
35524
|
var DiscoveryWakes = class {
|
|
@@ -35261,33 +35558,33 @@ function isStandingCornerStartFault(error) {
|
|
|
35261
35558
|
async function materializeCornerWorktree(input) {
|
|
35262
35559
|
const remote = roomCheckoutRemote(input.remote);
|
|
35263
35560
|
const repositoryHash = createHash7("sha256").update(remote).digest("hex").slice(0, 24);
|
|
35264
|
-
const gitCommonDir =
|
|
35265
|
-
const path =
|
|
35266
|
-
await
|
|
35267
|
-
await
|
|
35561
|
+
const gitCommonDir = resolve24(input.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
|
|
35562
|
+
const path = resolve24(input.supervisorRoot, "beeline", "corners", input.cornerId);
|
|
35563
|
+
await mkdir15(dirname13(gitCommonDir), { recursive: true, mode: 448 });
|
|
35564
|
+
await mkdir15(dirname13(path), { recursive: true, mode: 448 });
|
|
35268
35565
|
const authEnv = githubGitEnv(input.token);
|
|
35269
|
-
if (!existsSync6(
|
|
35270
|
-
await
|
|
35566
|
+
if (!existsSync6(resolve24(gitCommonDir, "HEAD"))) {
|
|
35567
|
+
await execFileAsync5("git", ["clone", "--bare", remote, gitCommonDir], {
|
|
35271
35568
|
env: authEnv,
|
|
35272
35569
|
maxBuffer: 4 * 1024 * 1024
|
|
35273
35570
|
});
|
|
35274
35571
|
}
|
|
35275
|
-
await
|
|
35572
|
+
await execFileAsync5("git", [
|
|
35276
35573
|
`--git-dir=${gitCommonDir}`,
|
|
35277
35574
|
"fetch",
|
|
35278
35575
|
"--prune",
|
|
35279
35576
|
"origin",
|
|
35280
35577
|
`+refs/heads/${input.targetBranch}:refs/remotes/origin/${input.targetBranch}`
|
|
35281
35578
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 });
|
|
35282
|
-
const restored = await
|
|
35579
|
+
const restored = await execFileAsync5("git", [
|
|
35283
35580
|
`--git-dir=${gitCommonDir}`,
|
|
35284
35581
|
"fetch",
|
|
35285
35582
|
"origin",
|
|
35286
35583
|
`+refs/heads/${input.featureBranch}:refs/remotes/origin/${input.featureBranch}`
|
|
35287
35584
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 }).then(() => true, () => false);
|
|
35288
|
-
if (!existsSync6(
|
|
35585
|
+
if (!existsSync6(resolve24(path, ".git"))) {
|
|
35289
35586
|
await rm5(path, { recursive: true, force: true });
|
|
35290
|
-
await
|
|
35587
|
+
await execFileAsync5("git", [
|
|
35291
35588
|
`--git-dir=${gitCommonDir}`,
|
|
35292
35589
|
"worktree",
|
|
35293
35590
|
"add",
|
|
@@ -35304,14 +35601,14 @@ async function materializeCornerWorktree(input) {
|
|
|
35304
35601
|
console.log(`[thin-core] corner ${input.cornerId} warm node_modules: ${seed.reason}${seed.detail ? ` (${seed.detail})` : ""}`);
|
|
35305
35602
|
}
|
|
35306
35603
|
}
|
|
35307
|
-
await
|
|
35604
|
+
await execFileAsync5("git", [
|
|
35308
35605
|
`--git-dir=${gitCommonDir}`,
|
|
35309
35606
|
"config",
|
|
35310
35607
|
"extensions.worktreeConfig",
|
|
35311
35608
|
"true"
|
|
35312
35609
|
]);
|
|
35313
|
-
await
|
|
35314
|
-
await
|
|
35610
|
+
await execFileAsync5("git", ["-C", path, "config", "--worktree", "core.bare", "false"]);
|
|
35611
|
+
await execFileAsync5("git", [
|
|
35315
35612
|
"-C",
|
|
35316
35613
|
path,
|
|
35317
35614
|
"config",
|
|
@@ -35319,7 +35616,7 @@ async function materializeCornerWorktree(input) {
|
|
|
35319
35616
|
"credential.https://github.com.helper",
|
|
35320
35617
|
"!f() { echo username=x-access-token; echo password=$GH_TOKEN; }; f"
|
|
35321
35618
|
]);
|
|
35322
|
-
await
|
|
35619
|
+
await execFileAsync5("git", [
|
|
35323
35620
|
"-C",
|
|
35324
35621
|
path,
|
|
35325
35622
|
"config",
|
|
@@ -35327,7 +35624,7 @@ async function materializeCornerWorktree(input) {
|
|
|
35327
35624
|
"user.name",
|
|
35328
35625
|
input.committer.name
|
|
35329
35626
|
]);
|
|
35330
|
-
await
|
|
35627
|
+
await execFileAsync5("git", [
|
|
35331
35628
|
"-C",
|
|
35332
35629
|
path,
|
|
35333
35630
|
"config",
|
|
@@ -35335,8 +35632,8 @@ async function materializeCornerWorktree(input) {
|
|
|
35335
35632
|
"user.email",
|
|
35336
35633
|
`${input.committer.publicKey.slice(0, 16)}@users.noreply.github.com`
|
|
35337
35634
|
]);
|
|
35338
|
-
const top = await
|
|
35339
|
-
if (
|
|
35635
|
+
const top = await execFileAsync5("git", ["-C", path, "rev-parse", "--show-toplevel"]);
|
|
35636
|
+
if (resolve24(top.stdout.trim()) !== resolve24(path)) {
|
|
35340
35637
|
throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
|
|
35341
35638
|
}
|
|
35342
35639
|
return { path, gitCommonDir };
|
|
@@ -35360,7 +35657,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35360
35657
|
const worktreeExists = existsSync6(worktree.path);
|
|
35361
35658
|
const localExists = await gitRefExists(worktree.gitCommonDir, localRef);
|
|
35362
35659
|
if (worktreeExists) {
|
|
35363
|
-
const checkedOut = await
|
|
35660
|
+
const checkedOut = await execFileAsync5("git", [
|
|
35364
35661
|
"-C",
|
|
35365
35662
|
worktree.path,
|
|
35366
35663
|
"symbolic-ref",
|
|
@@ -35370,7 +35667,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35370
35667
|
if (checkedOut.stdout.trim() !== localRef) {
|
|
35371
35668
|
throw new Error(`corner worktree branch mismatch: expected ${localRef}`);
|
|
35372
35669
|
}
|
|
35373
|
-
await
|
|
35670
|
+
await execFileAsync5("git", [
|
|
35374
35671
|
`--git-dir=${worktree.gitCommonDir}`,
|
|
35375
35672
|
"worktree",
|
|
35376
35673
|
"remove",
|
|
@@ -35390,7 +35687,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35390
35687
|
}
|
|
35391
35688
|
await deleteExactRemoteBranch(worktree.gitCommonDir, remoteRef, worktree.token);
|
|
35392
35689
|
if (await gitRefExists(worktree.gitCommonDir, localRef)) {
|
|
35393
|
-
await
|
|
35690
|
+
await execFileAsync5("git", [
|
|
35394
35691
|
`--git-dir=${worktree.gitCommonDir}`,
|
|
35395
35692
|
"branch",
|
|
35396
35693
|
"--delete",
|
|
@@ -35400,10 +35697,10 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35400
35697
|
]);
|
|
35401
35698
|
}
|
|
35402
35699
|
}
|
|
35403
|
-
var
|
|
35700
|
+
var execFileAsync5 = promisify5(execFile7);
|
|
35404
35701
|
async function removeCornerScratchWorkspace(input) {
|
|
35405
|
-
const expected =
|
|
35406
|
-
if (
|
|
35702
|
+
const expected = resolve24(input.roomRoot, "scratch");
|
|
35703
|
+
if (resolve24(input.scratchPath) !== expected) {
|
|
35407
35704
|
throw new Error(`refusing to remove scratch outside corner ${input.cornerId}`);
|
|
35408
35705
|
}
|
|
35409
35706
|
await rm5(expected, { recursive: true, force: true });
|
|
@@ -35417,6 +35714,19 @@ var RoomRuntimeCoordinator = class {
|
|
|
35417
35714
|
/** Close/reconcile leftovers retried until local and remote refs are gone. */
|
|
35418
35715
|
pendingCornerReaps = /* @__PURE__ */ new Map();
|
|
35419
35716
|
startingCorners = /* @__PURE__ */ new Set();
|
|
35717
|
+
/**
|
|
35718
|
+
* Rooms whose start is in flight. `running` is not set until the checkout
|
|
35719
|
+
* clone finishes, and three callers race for it — the membership push, the
|
|
35720
|
+
* reconcile sweep, and the watchdog restart — so without this two checkouts
|
|
35721
|
+
* run in the same shared directory and the second start orphans the first
|
|
35722
|
+
* loop's AbortController.
|
|
35723
|
+
*/
|
|
35724
|
+
startingRooms = /* @__PURE__ */ new Set();
|
|
35725
|
+
/** Pushed membership changes awaiting a bounded apply, latest per Room. */
|
|
35726
|
+
pendingMembershipEvents = /* @__PURE__ */ new Map();
|
|
35727
|
+
membershipDrain;
|
|
35728
|
+
/** Set by `shutdown`: a pushed event may no longer start anything. */
|
|
35729
|
+
stopped = false;
|
|
35420
35730
|
/** Corners whose start failure has already been said out loud, once each. */
|
|
35421
35731
|
reportedCornerStartFailures = /* @__PURE__ */ new Set();
|
|
35422
35732
|
/** Standing workspace-configuration faults, keyed by the config that failed. */
|
|
@@ -35433,10 +35743,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
35433
35743
|
workspaceRemovalConfirmations = 0;
|
|
35434
35744
|
roomRemovalConfirmations = /* @__PURE__ */ new Map();
|
|
35435
35745
|
confirmationPending = false;
|
|
35436
|
-
/**
|
|
35437
|
-
* wake that arrives while a reconcile is already running must
|
|
35438
|
-
* start-pass clearing, or a
|
|
35439
|
-
* for a wake the daemon already received.
|
|
35746
|
+
/** Unscoped agent-directed discovery wakes (#1369), counted instead of
|
|
35747
|
+
* flagged: a wake that arrives while a reconcile is already running must
|
|
35748
|
+
* survive its start-pass clearing, or a reconnect mid-reconcile waits a
|
|
35749
|
+
* heartbeat for a wake the daemon already received. A scoped membership
|
|
35750
|
+
* event applies incrementally and never touches this latch. */
|
|
35440
35751
|
discoveryWakes = new DiscoveryWakes();
|
|
35441
35752
|
/** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
|
|
35442
35753
|
grantRunner;
|
|
@@ -35460,8 +35771,16 @@ var RoomRuntimeCoordinator = class {
|
|
|
35460
35771
|
});
|
|
35461
35772
|
this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
|
|
35462
35773
|
this.connectorUsage = new ConnectorUsageRecorder();
|
|
35463
|
-
this.options.daemonApi.setRoomsChangedListener?.(() => {
|
|
35464
|
-
|
|
35774
|
+
this.options.daemonApi.setRoomsChangedListener?.((event) => {
|
|
35775
|
+
const roomId = event?.roomId;
|
|
35776
|
+
if (!roomId) {
|
|
35777
|
+
this.discoveryWakes.wake();
|
|
35778
|
+
return;
|
|
35779
|
+
}
|
|
35780
|
+
this.queueMembershipEvent({ ...event, roomId });
|
|
35781
|
+
});
|
|
35782
|
+
this.options.daemonApi.setCornerCompleteListener?.((roomId) => {
|
|
35783
|
+
void this.applyCornerComplete(roomId).catch((error) => console.error("[thin-core] live corner-complete apply failed", error));
|
|
35465
35784
|
});
|
|
35466
35785
|
this.options.daemonApi.setConfigChangedListener?.(() => {
|
|
35467
35786
|
void this.scheduler.suspendIdle().catch((error) => console.error("[body] config-change session restart failed", error));
|
|
@@ -35590,17 +35909,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
35590
35909
|
this.confirmationPending = true;
|
|
35591
35910
|
continue;
|
|
35592
35911
|
}
|
|
35593
|
-
|
|
35594
|
-
await running.promise.catch(() => void 0);
|
|
35595
|
-
try {
|
|
35596
|
-
if (running.worktree)
|
|
35597
|
-
await this.reapCornerWorktree(running.worktree);
|
|
35598
|
-
else if (running.scratch)
|
|
35599
|
-
await this.reapCornerScratch(running.scratch);
|
|
35600
|
-
} catch (error) {
|
|
35601
|
-
console.error(`[thin-core] corner ${channelId} cleanup failed; will retry:`, error);
|
|
35602
|
-
this.confirmationPending = true;
|
|
35603
|
-
}
|
|
35912
|
+
await this.stopRunning(channelId, running);
|
|
35604
35913
|
}
|
|
35605
35914
|
await mapWithConcurrency(desiredTopRooms, ROOM_JOIN_CONCURRENCY, async (roomId) => {
|
|
35606
35915
|
if (this.running.has(roomId))
|
|
@@ -35620,17 +35929,97 @@ var RoomRuntimeCoordinator = class {
|
|
|
35620
35929
|
this.discoveryWakes.completeReconcile(coveredWakes);
|
|
35621
35930
|
return "member";
|
|
35622
35931
|
}
|
|
35932
|
+
/**
|
|
35933
|
+
* Pushed membership changes are applied at the same bound the reconcile pass
|
|
35934
|
+
* uses. One `rooms-changed` per row means a single human action (adding an
|
|
35935
|
+
* agent to a Room inherits a membership per corner under it) arrives as a
|
|
35936
|
+
* burst; unbounded, that burst is exactly the concurrent restore reads and
|
|
35937
|
+
* worktree checkouts this change exists to stop.
|
|
35938
|
+
*/
|
|
35939
|
+
queueMembershipEvent(event) {
|
|
35940
|
+
if (this.stopped)
|
|
35941
|
+
return;
|
|
35942
|
+
this.pendingMembershipEvents.set(event.roomId, event);
|
|
35943
|
+
this.membershipDrain ??= this.drainMembershipEvents().finally(() => {
|
|
35944
|
+
this.membershipDrain = void 0;
|
|
35945
|
+
});
|
|
35946
|
+
}
|
|
35947
|
+
async drainMembershipEvents() {
|
|
35948
|
+
while (this.pendingMembershipEvents.size) {
|
|
35949
|
+
const batch = [...this.pendingMembershipEvents.values()];
|
|
35950
|
+
this.pendingMembershipEvents.clear();
|
|
35951
|
+
await mapWithConcurrency(batch, ROOM_JOIN_CONCURRENCY, (event) => this.applyMembershipEvent(event).catch((error) => {
|
|
35952
|
+
console.error("[thin-core] live membership apply failed", error);
|
|
35953
|
+
this.discoveryWakes.wake();
|
|
35954
|
+
}));
|
|
35955
|
+
}
|
|
35956
|
+
}
|
|
35957
|
+
/**
|
|
35958
|
+
* Incremental apply of one scoped membership push.
|
|
35959
|
+
* An unscoped wake still uses the slow reconcile as recovery.
|
|
35960
|
+
*/
|
|
35961
|
+
async applyMembershipEvent(event) {
|
|
35962
|
+
const roomId = event.roomId;
|
|
35963
|
+
if (!roomId) {
|
|
35964
|
+
this.discoveryWakes.wake();
|
|
35965
|
+
return;
|
|
35966
|
+
}
|
|
35967
|
+
if (event.removed === true) {
|
|
35968
|
+
const running = this.running.get(roomId);
|
|
35969
|
+
if (running)
|
|
35970
|
+
await this.stopRunning(roomId, running);
|
|
35971
|
+
this.monolithCornerParents.delete(roomId);
|
|
35972
|
+
return;
|
|
35973
|
+
}
|
|
35974
|
+
if (event.archived === true)
|
|
35975
|
+
return;
|
|
35976
|
+
this.roomRemovalConfirmations.delete(roomId);
|
|
35977
|
+
if (this.running.has(roomId) || this.startingCorners.has(roomId))
|
|
35978
|
+
return;
|
|
35979
|
+
if (event.parentRoomId) {
|
|
35980
|
+
this.monolithCornerParents.set(roomId, event.parentRoomId);
|
|
35981
|
+
if (!event.openedBy) {
|
|
35982
|
+
this.discoveryWakes.wake();
|
|
35983
|
+
return;
|
|
35984
|
+
}
|
|
35985
|
+
await this.startCorner({
|
|
35986
|
+
cornerId: roomId,
|
|
35987
|
+
parentRoomId: event.parentRoomId,
|
|
35988
|
+
openedBy: event.openedBy
|
|
35989
|
+
});
|
|
35990
|
+
if (!this.running.has(roomId))
|
|
35991
|
+
this.discoveryWakes.wake();
|
|
35992
|
+
return;
|
|
35993
|
+
}
|
|
35994
|
+
await this.startRoom(roomId);
|
|
35995
|
+
}
|
|
35996
|
+
async applyCornerComplete(cornerId) {
|
|
35997
|
+
this.running.get(cornerId)?.body.requestClose?.();
|
|
35998
|
+
}
|
|
35999
|
+
async stopRunning(channelId, running) {
|
|
36000
|
+
running.controller.abort();
|
|
36001
|
+
await running.promise.catch(() => void 0);
|
|
36002
|
+
try {
|
|
36003
|
+
if (running.worktree)
|
|
36004
|
+
await this.reapCornerWorktree(running.worktree);
|
|
36005
|
+
else if (running.scratch)
|
|
36006
|
+
await this.reapCornerScratch(running.scratch);
|
|
36007
|
+
} catch (error) {
|
|
36008
|
+
console.error(`[thin-core] corner ${channelId} cleanup failed; will retry:`, error);
|
|
36009
|
+
this.confirmationPending = true;
|
|
36010
|
+
}
|
|
36011
|
+
}
|
|
35623
36012
|
roomRecord(roomId) {
|
|
35624
36013
|
return this.runtime.rooms.find((room) => room.channelId === roomId);
|
|
35625
36014
|
}
|
|
35626
36015
|
roomRoot(roomId) {
|
|
35627
|
-
return this.roomRecord(roomId)?.root ??
|
|
36016
|
+
return this.roomRecord(roomId)?.root ?? resolve24(dirname13(this.configPath), "rooms", roomId);
|
|
35628
36017
|
}
|
|
35629
36018
|
roomAgentHomeRoot(workspaceRoot, required = false) {
|
|
35630
36019
|
const flag = process.env.BUZZY_BODY_ROOM_HOME;
|
|
35631
36020
|
if (!required && flag === "0")
|
|
35632
36021
|
return void 0;
|
|
35633
|
-
const home =
|
|
36022
|
+
const home = resolve24(workspaceRoot, "agent-home");
|
|
35634
36023
|
if (!required && flag !== "1" && !existsSync6(home) && existsSync6(workspaceRoot))
|
|
35635
36024
|
return void 0;
|
|
35636
36025
|
try {
|
|
@@ -35646,10 +36035,10 @@ var RoomRuntimeCoordinator = class {
|
|
|
35646
36035
|
return {
|
|
35647
36036
|
...this.baseConfig,
|
|
35648
36037
|
workspaceRoot,
|
|
35649
|
-
agentPrivateRoot:
|
|
35650
|
-
agentMemoryRoot:
|
|
35651
|
-
openRouterRoutingCacheDir: openRouterRoutingCacheDir(
|
|
35652
|
-
turnTraceDir: turnTraceDirectory(
|
|
36038
|
+
agentPrivateRoot: resolve24(workspaceRoot, "agent-private"),
|
|
36039
|
+
agentMemoryRoot: resolve24(dirname13(this.configPath), "memory"),
|
|
36040
|
+
openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname13(this.configPath)),
|
|
36041
|
+
turnTraceDir: turnTraceDirectory(dirname13(this.configPath)),
|
|
35653
36042
|
...agentHomeRoot ? { agentHomeRoot } : {}
|
|
35654
36043
|
};
|
|
35655
36044
|
}
|
|
@@ -35671,6 +36060,16 @@ var RoomRuntimeCoordinator = class {
|
|
|
35671
36060
|
});
|
|
35672
36061
|
}
|
|
35673
36062
|
async startRoom(roomId) {
|
|
36063
|
+
if (this.running.has(roomId) || this.startingRooms.has(roomId))
|
|
36064
|
+
return;
|
|
36065
|
+
this.startingRooms.add(roomId);
|
|
36066
|
+
try {
|
|
36067
|
+
await this.startRoomOnce(roomId);
|
|
36068
|
+
} finally {
|
|
36069
|
+
this.startingRooms.delete(roomId);
|
|
36070
|
+
}
|
|
36071
|
+
}
|
|
36072
|
+
async startRoomOnce(roomId) {
|
|
35674
36073
|
const controller = new AbortController();
|
|
35675
36074
|
const cwd = await this.materializeRoomCheckout(roomId);
|
|
35676
36075
|
const grantRunnerEndpoint = await this.grantRunnerEndpoint();
|
|
@@ -35703,6 +36102,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
35703
36102
|
if (this.running.get(roomId)?.body === loop)
|
|
35704
36103
|
this.running.delete(roomId);
|
|
35705
36104
|
});
|
|
36105
|
+
if (this.stopped) {
|
|
36106
|
+
controller.abort();
|
|
36107
|
+
await promise;
|
|
36108
|
+
return;
|
|
36109
|
+
}
|
|
35706
36110
|
this.running.set(roomId, {
|
|
35707
36111
|
body: loop,
|
|
35708
36112
|
controller,
|
|
@@ -35727,17 +36131,17 @@ var RoomRuntimeCoordinator = class {
|
|
|
35727
36131
|
const remote = roomCheckoutRemote(repository.remote);
|
|
35728
36132
|
const targetBranch = repository.targetBranch || "main";
|
|
35729
36133
|
const checkoutId = createHash7("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
35730
|
-
const path =
|
|
35731
|
-
await
|
|
36134
|
+
const path = resolve24(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
36135
|
+
await mkdir15(dirname13(path), { recursive: true, mode: 448 });
|
|
35732
36136
|
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
35733
36137
|
const env = token ? githubGitEnv(token.token) : process.env;
|
|
35734
|
-
if (!existsSync6(
|
|
35735
|
-
await
|
|
36138
|
+
if (!existsSync6(resolve24(path, ".git"))) {
|
|
36139
|
+
await execFileAsync5("git", ["clone", "--no-checkout", remote, path], {
|
|
35736
36140
|
env,
|
|
35737
36141
|
maxBuffer: 4 * 1024 * 1024
|
|
35738
36142
|
});
|
|
35739
36143
|
}
|
|
35740
|
-
await
|
|
36144
|
+
await execFileAsync5("git", [
|
|
35741
36145
|
"-C",
|
|
35742
36146
|
path,
|
|
35743
36147
|
"fetch",
|
|
@@ -35745,7 +36149,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
35745
36149
|
"origin",
|
|
35746
36150
|
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`
|
|
35747
36151
|
], { env, maxBuffer: 4 * 1024 * 1024 });
|
|
35748
|
-
await
|
|
36152
|
+
await execFileAsync5("git", ["-C", path, "checkout", "--detach", "--force", `origin/${targetBranch}`], {
|
|
35749
36153
|
env,
|
|
35750
36154
|
maxBuffer: 4 * 1024 * 1024
|
|
35751
36155
|
});
|
|
@@ -35763,7 +36167,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
35763
36167
|
roomId: corner.parentRoomId
|
|
35764
36168
|
})
|
|
35765
36169
|
]);
|
|
35766
|
-
|
|
36170
|
+
const objective = (restore.objective ?? "").trim() || (restore.kind === "human" ? (restore.title ?? "").trim() : "");
|
|
36171
|
+
configKey = cornerStartConfigKey(repository, objective);
|
|
35767
36172
|
const previousStanding = this.standingCornerStartFaults.get(corner.cornerId);
|
|
35768
36173
|
if (previousStanding === configKey)
|
|
35769
36174
|
return;
|
|
@@ -35777,7 +36182,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
35777
36182
|
if (repository.resolution === "repository" && (!repository.remote || !repository.key)) {
|
|
35778
36183
|
throw new Error("corner parent Room has an incomplete repository binding");
|
|
35779
36184
|
}
|
|
35780
|
-
const objective = (restore.objective ?? "").trim();
|
|
35781
36185
|
if (!objective)
|
|
35782
36186
|
throw new Error("corner has no authoritative objective fact");
|
|
35783
36187
|
const repositoryBacked = repository.resolution === "repository" && restore.lane !== "no_code";
|
|
@@ -35793,10 +36197,10 @@ var RoomRuntimeCoordinator = class {
|
|
|
35793
36197
|
featureBranch,
|
|
35794
36198
|
token: granted.token
|
|
35795
36199
|
}) : void 0;
|
|
35796
|
-
const workspacePath = worktree?.path ??
|
|
36200
|
+
const workspacePath = worktree?.path ?? resolve24(this.roomRoot(corner.cornerId), "scratch");
|
|
35797
36201
|
if (!worktree)
|
|
35798
|
-
await
|
|
35799
|
-
const isOpener =
|
|
36202
|
+
await mkdir15(workspacePath, { recursive: true, mode: 448 });
|
|
36203
|
+
const isOpener = corner.openedBy === this.agent.publicKey;
|
|
35800
36204
|
if (worktree && shouldPostInitialCornerWorkingState(restore, isOpener)) {
|
|
35801
36205
|
await this.options.daemonApi.execute("postCornerRemoteState", {
|
|
35802
36206
|
cornerId: corner.cornerId,
|
|
@@ -35852,6 +36256,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
35852
36256
|
this.running.delete(corner.cornerId);
|
|
35853
36257
|
}
|
|
35854
36258
|
});
|
|
36259
|
+
if (this.stopped) {
|
|
36260
|
+
controller.abort();
|
|
36261
|
+
await promise;
|
|
36262
|
+
return;
|
|
36263
|
+
}
|
|
35855
36264
|
this.running.set(corner.cornerId, {
|
|
35856
36265
|
body: loop,
|
|
35857
36266
|
controller,
|
|
@@ -35997,18 +36406,28 @@ var RoomRuntimeCoordinator = class {
|
|
|
35997
36406
|
}
|
|
35998
36407
|
}
|
|
35999
36408
|
async shutdown() {
|
|
36409
|
+
this.stopped = true;
|
|
36410
|
+
this.pendingMembershipEvents.clear();
|
|
36411
|
+
const deadlineAt = Math.min(this.now() + this.drainDeadlineMs, this.drainDeadlineAt ?? Number.POSITIVE_INFINITY);
|
|
36412
|
+
const untilDeadline = async (work) => {
|
|
36413
|
+
let timer;
|
|
36414
|
+
const deadline = new Promise((resolveDeadline) => {
|
|
36415
|
+
timer = setTimeout(() => resolveDeadline("deadline"), Math.max(0, deadlineAt - this.now()));
|
|
36416
|
+
});
|
|
36417
|
+
try {
|
|
36418
|
+
return await Promise.race([work, deadline]);
|
|
36419
|
+
} finally {
|
|
36420
|
+
if (timer)
|
|
36421
|
+
clearTimeout(timer);
|
|
36422
|
+
}
|
|
36423
|
+
};
|
|
36424
|
+
if (this.membershipDrain)
|
|
36425
|
+
await untilDeadline(this.membershipDrain);
|
|
36000
36426
|
const rooms = [...this.running.values()];
|
|
36001
36427
|
for (const room of rooms)
|
|
36002
36428
|
room.controller.abort();
|
|
36003
36429
|
const drained = Promise.all(rooms.map((room) => room.promise.catch(() => void 0)));
|
|
36004
|
-
const
|
|
36005
|
-
let timer;
|
|
36006
|
-
const deadline = new Promise((resolveDeadline) => {
|
|
36007
|
-
timer = setTimeout(() => resolveDeadline("deadline"), Math.max(0, deadlineAt - this.now()));
|
|
36008
|
-
});
|
|
36009
|
-
const result = await Promise.race([drained.then(() => "drained"), deadline]);
|
|
36010
|
-
if (timer)
|
|
36011
|
-
clearTimeout(timer);
|
|
36430
|
+
const result = await untilDeadline(drained.then(() => "drained"));
|
|
36012
36431
|
if (result === "deadline") {
|
|
36013
36432
|
await Promise.allSettled(rooms.map((room) => room.body.forceRecoverRoom()));
|
|
36014
36433
|
await drained;
|
|
@@ -36044,7 +36463,7 @@ function githubGitEnv(token) {
|
|
|
36044
36463
|
}
|
|
36045
36464
|
async function gitRefExists(gitCommonDir, ref) {
|
|
36046
36465
|
try {
|
|
36047
|
-
await
|
|
36466
|
+
await execFileAsync5("git", [`--git-dir=${gitCommonDir}`, "show-ref", "--verify", "--quiet", ref]);
|
|
36048
36467
|
return true;
|
|
36049
36468
|
} catch (error) {
|
|
36050
36469
|
if (error.code === 1)
|
|
@@ -36054,7 +36473,7 @@ async function gitRefExists(gitCommonDir, ref) {
|
|
|
36054
36473
|
}
|
|
36055
36474
|
async function repositoryHeadRef(gitCommonDir) {
|
|
36056
36475
|
try {
|
|
36057
|
-
const head = await
|
|
36476
|
+
const head = await execFileAsync5("git", [
|
|
36058
36477
|
`--git-dir=${gitCommonDir}`,
|
|
36059
36478
|
"symbolic-ref",
|
|
36060
36479
|
"--quiet",
|
|
@@ -36074,7 +36493,7 @@ ${gitStderr(error)}`);
|
|
|
36074
36493
|
}
|
|
36075
36494
|
async function deleteExactRemoteBranch(gitCommonDir, remoteRef, token, options = {}) {
|
|
36076
36495
|
const authEnv = githubGitEnv(token);
|
|
36077
|
-
const listRemote = () =>
|
|
36496
|
+
const listRemote = () => execFileAsync5("git", [`--git-dir=${gitCommonDir}`, "ls-remote", "--heads", "origin", remoteRef], {
|
|
36078
36497
|
env: authEnv,
|
|
36079
36498
|
maxBuffer: 4 * 1024 * 1024
|
|
36080
36499
|
});
|
|
@@ -36090,7 +36509,7 @@ async function deleteExactRemoteBranch(gitCommonDir, remoteRef, token, options =
|
|
|
36090
36509
|
try {
|
|
36091
36510
|
const remote = await listRemote();
|
|
36092
36511
|
if (remote.stdout.trim()) {
|
|
36093
|
-
await
|
|
36512
|
+
await execFileAsync5("git", [`--git-dir=${gitCommonDir}`, "push", "origin", `:${remoteRef}`], {
|
|
36094
36513
|
env: authEnv,
|
|
36095
36514
|
maxBuffer: 4 * 1024 * 1024
|
|
36096
36515
|
});
|
|
@@ -36185,17 +36604,17 @@ var ThinDaemonCore = class {
|
|
|
36185
36604
|
};
|
|
36186
36605
|
|
|
36187
36606
|
// apps/body/dist/agent-retirement.js
|
|
36188
|
-
import { mkdir as
|
|
36189
|
-
import { dirname as
|
|
36607
|
+
import { mkdir as mkdir17, rename as rename5 } from "node:fs/promises";
|
|
36608
|
+
import { dirname as dirname15, resolve as resolve26 } from "node:path";
|
|
36190
36609
|
|
|
36191
36610
|
// apps/body/dist/systemd.js
|
|
36192
|
-
import { execFile as
|
|
36193
|
-
import { mkdir as
|
|
36611
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
36612
|
+
import { mkdir as mkdir16, readFile as readFile10, stat as stat3, writeFile as writeFile10 } from "node:fs/promises";
|
|
36194
36613
|
import { homedir as homedir11 } from "node:os";
|
|
36195
|
-
import { dirname as
|
|
36614
|
+
import { dirname as dirname14, resolve as resolve25 } from "node:path";
|
|
36196
36615
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
36197
|
-
import { promisify as
|
|
36198
|
-
var
|
|
36616
|
+
import { promisify as promisify6 } from "node:util";
|
|
36617
|
+
var execFileAsync6 = promisify6(execFile8);
|
|
36199
36618
|
var DELIBERATE_REMOVAL_EXIT_STATUS = 78;
|
|
36200
36619
|
var DAEMON_DISTRESS_EXIT_STATUS = 77;
|
|
36201
36620
|
var UNKNOWN_AGENT_EXIT_STATUS = 79;
|
|
@@ -36227,8 +36646,13 @@ TimeoutStartSec=90s
|
|
|
36227
36646
|
TimeoutStopSec=10min
|
|
36228
36647
|
KillMode=control-group
|
|
36229
36648
|
UMask=0077
|
|
36230
|
-
|
|
36231
|
-
|
|
36649
|
+
# A desktop-launched user manager may inherit Ubuntu's unprivileged_userns
|
|
36650
|
+
# AppArmor profile. Without an explicit transition every agent inherits it too,
|
|
36651
|
+
# so /usr/bin/bwrap cannot enter its package-provided bwrap profile.
|
|
36652
|
+
# Do not set NoNewPrivileges or PrivateTmp on this outer service: systemd applies
|
|
36653
|
+
# either before AppArmorProfile, which blocks this transition. Bubblewrap sets
|
|
36654
|
+
# no-new-privs and a private /tmp inside each agent sandbox it creates.
|
|
36655
|
+
AppArmorProfile=-unconfined
|
|
36232
36656
|
|
|
36233
36657
|
[Install]
|
|
36234
36658
|
WantedBy=default.target
|
|
@@ -36236,9 +36660,9 @@ WantedBy=default.target
|
|
|
36236
36660
|
}
|
|
36237
36661
|
function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
|
|
36238
36662
|
const home = env.HOME?.trim() || homedir11();
|
|
36239
|
-
const expectedLibDir =
|
|
36663
|
+
const expectedLibDir = resolve25(home, ".local", "lib", "beeline");
|
|
36240
36664
|
const expectedPrefix = `${expectedLibDir}/`;
|
|
36241
|
-
return
|
|
36665
|
+
return resolve25(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve25(invocationPath).startsWith(expectedPrefix);
|
|
36242
36666
|
}
|
|
36243
36667
|
function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
36244
36668
|
if (isCanonicalInstalledLauncher(env, invocationPath))
|
|
@@ -36246,12 +36670,12 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
|
36246
36670
|
throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
|
|
36247
36671
|
}
|
|
36248
36672
|
function systemdUserUnitPath(env = process.env) {
|
|
36249
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
36250
|
-
return
|
|
36673
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve25(homedir11(), ".config");
|
|
36674
|
+
return resolve25(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
|
|
36251
36675
|
}
|
|
36252
36676
|
function systemdBrokerUnitPath(env = process.env) {
|
|
36253
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
36254
|
-
return
|
|
36677
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve25(homedir11(), ".config");
|
|
36678
|
+
return resolve25(configRoot, "systemd", "user", TRUSTY_SQUIRE_BROKER_UNIT_NAME);
|
|
36255
36679
|
}
|
|
36256
36680
|
async function installTrustySquireBrokerService(options = {}) {
|
|
36257
36681
|
const env = options.env ?? process.env;
|
|
@@ -36260,9 +36684,9 @@ async function installTrustySquireBrokerService(options = {}) {
|
|
|
36260
36684
|
ensureSquireHostDir(home);
|
|
36261
36685
|
const path = systemdBrokerUnitPath(env);
|
|
36262
36686
|
const content = trustySquireBrokerUnit();
|
|
36263
|
-
const existing = await
|
|
36687
|
+
const existing = await readFile10(path, "utf8").catch(() => "");
|
|
36264
36688
|
if (existing !== content) {
|
|
36265
|
-
await
|
|
36689
|
+
await mkdir16(dirname14(path), { recursive: true, mode: 448 });
|
|
36266
36690
|
await writeFile10(path, content, { mode: 384 });
|
|
36267
36691
|
}
|
|
36268
36692
|
const run2 = options.run ?? runSystemctl;
|
|
@@ -36271,7 +36695,7 @@ async function installTrustySquireBrokerService(options = {}) {
|
|
|
36271
36695
|
}
|
|
36272
36696
|
var AGENT_SERVICE = /^beeline-agent@([0-9a-f]{64})\.service$/i;
|
|
36273
36697
|
var runSystemctl = async (args) => {
|
|
36274
|
-
const result = await
|
|
36698
|
+
const result = await execFileAsync6("systemctl", ["--user", ...args], {
|
|
36275
36699
|
timeout: SYSTEMD_COMMAND_TIMEOUT_MS,
|
|
36276
36700
|
encoding: "utf8"
|
|
36277
36701
|
});
|
|
@@ -36284,9 +36708,9 @@ async function installAgentService(publicKey, options = {}) {
|
|
|
36284
36708
|
assertCanonicalInstalledLauncher(env, options.invocationPath);
|
|
36285
36709
|
const path = systemdUserUnitPath(env);
|
|
36286
36710
|
const content = agentServiceUnit();
|
|
36287
|
-
const existing = await
|
|
36711
|
+
const existing = await readFile10(path, "utf8").catch(() => "");
|
|
36288
36712
|
if (existing !== content) {
|
|
36289
|
-
await
|
|
36713
|
+
await mkdir16(dirname14(path), { recursive: true, mode: 448 });
|
|
36290
36714
|
await writeFile10(path, content, { mode: 384 });
|
|
36291
36715
|
}
|
|
36292
36716
|
const run2 = options.run ?? runSystemctl;
|
|
@@ -36407,7 +36831,7 @@ async function reconcileAgentServices(options = {}) {
|
|
|
36407
36831
|
async function notify(fields) {
|
|
36408
36832
|
if (process.env.BEELINE_MANAGED_BY_SYSTEMD !== "1")
|
|
36409
36833
|
return;
|
|
36410
|
-
await
|
|
36834
|
+
await execFileAsync6("systemd-notify", fields, { timeout: SYSTEMD_COMMAND_TIMEOUT_MS });
|
|
36411
36835
|
}
|
|
36412
36836
|
async function extendSystemdStartTimeout(ms) {
|
|
36413
36837
|
await notify([`EXTEND_TIMEOUT_USEC=${Math.max(0, Math.round(ms)) * 1e3}`]).catch(() => void 0);
|
|
@@ -36426,15 +36850,15 @@ var SystemdNotifier = class {
|
|
|
36426
36850
|
|
|
36427
36851
|
// apps/body/dist/agent-retirement.js
|
|
36428
36852
|
async function retireRemovedAgent(runtime, options = {}) {
|
|
36429
|
-
const deletedRoot =
|
|
36430
|
-
const target =
|
|
36853
|
+
const deletedRoot = resolve26(runtime.supervisorRoot, "beeline", "deleted-runtimes");
|
|
36854
|
+
const target = resolve26(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
|
|
36431
36855
|
return relocateAgentRuntime(runtime, target, {
|
|
36432
36856
|
...options.run ? { run: options.run } : {}
|
|
36433
36857
|
});
|
|
36434
36858
|
}
|
|
36435
36859
|
async function relocateAgentRuntime(runtime, target, options = {}) {
|
|
36436
36860
|
const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
|
|
36437
|
-
const destination =
|
|
36861
|
+
const destination = resolve26(target);
|
|
36438
36862
|
if (destination === source || destination.startsWith(`${source}/`)) {
|
|
36439
36863
|
throw new Error("agent runtime destination must be outside the live runtime");
|
|
36440
36864
|
}
|
|
@@ -36443,13 +36867,13 @@ async function relocateAgentRuntime(runtime, target, options = {}) {
|
|
|
36443
36867
|
...options.run ? { run: options.run } : {}
|
|
36444
36868
|
});
|
|
36445
36869
|
}
|
|
36446
|
-
await
|
|
36870
|
+
await mkdir17(dirname15(destination), { recursive: true, mode: 448 });
|
|
36447
36871
|
await rename5(source, destination);
|
|
36448
36872
|
return destination;
|
|
36449
36873
|
}
|
|
36450
36874
|
|
|
36451
36875
|
// apps/body/dist/start-command.js
|
|
36452
|
-
import { basename as basename7, dirname as
|
|
36876
|
+
import { basename as basename7, dirname as dirname19 } from "node:path";
|
|
36453
36877
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
36454
36878
|
|
|
36455
36879
|
// apps/body/dist/self-update-cli.js
|
|
@@ -36460,21 +36884,21 @@ init_self_update_manifest();
|
|
|
36460
36884
|
// apps/body/dist/managed-update.js
|
|
36461
36885
|
init_self_update();
|
|
36462
36886
|
import { spawn as spawn9 } from "node:child_process";
|
|
36463
|
-
import { mkdir as
|
|
36464
|
-
import { dirname as
|
|
36887
|
+
import { mkdir as mkdir20, rm as rm7, stat as stat4, writeFile as writeFile13 } from "node:fs/promises";
|
|
36888
|
+
import { dirname as dirname18, resolve as resolve29 } from "node:path";
|
|
36465
36889
|
|
|
36466
36890
|
// apps/body/dist/update-rollback-alert.js
|
|
36467
|
-
import { mkdir as
|
|
36468
|
-
import { dirname as
|
|
36891
|
+
import { mkdir as mkdir19, readFile as readFile12, rename as rename7, unlink as unlink4, writeFile as writeFile12 } from "node:fs/promises";
|
|
36892
|
+
import { dirname as dirname17, resolve as resolve28 } from "node:path";
|
|
36469
36893
|
var REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
36470
36894
|
var lastLogged = /* @__PURE__ */ new Map();
|
|
36471
36895
|
function updateRollbackAlertPath(runtimeDir) {
|
|
36472
|
-
return
|
|
36896
|
+
return resolve28(runtimeDir, "update-rollback-alert.json");
|
|
36473
36897
|
}
|
|
36474
36898
|
async function writeAlert(runtimeDir, alert) {
|
|
36475
36899
|
const path = updateRollbackAlertPath(runtimeDir);
|
|
36476
36900
|
const staged = `${path}.${process.pid}.tmp`;
|
|
36477
|
-
await
|
|
36901
|
+
await mkdir19(dirname17(path), { recursive: true });
|
|
36478
36902
|
await writeFile12(staged, `${JSON.stringify(alert, null, 2)}
|
|
36479
36903
|
`, { mode: 384 });
|
|
36480
36904
|
await rename7(staged, path);
|
|
@@ -36487,7 +36911,7 @@ async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()
|
|
|
36487
36911
|
}
|
|
36488
36912
|
async function readUpdateRollbackAlert(runtimeDir) {
|
|
36489
36913
|
try {
|
|
36490
|
-
const value = JSON.parse(await
|
|
36914
|
+
const value = JSON.parse(await readFile12(updateRollbackAlertPath(runtimeDir), "utf8"));
|
|
36491
36915
|
if (value.version !== 1 || typeof value.releaseId !== "string")
|
|
36492
36916
|
return void 0;
|
|
36493
36917
|
return value;
|
|
@@ -36538,13 +36962,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
|
|
|
36538
36962
|
var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
|
|
36539
36963
|
async function withInstallLock(layout, work, options = {}) {
|
|
36540
36964
|
const now2 = options.now ?? Date.now;
|
|
36541
|
-
const lock =
|
|
36965
|
+
const lock = resolve29(layout.releasesRoot, ".state", "install.lock");
|
|
36542
36966
|
const deadline = now2() + (options.waitMs ?? 1e4);
|
|
36543
|
-
await
|
|
36967
|
+
await mkdir20(dirname18(lock), { recursive: true });
|
|
36544
36968
|
for (; ; ) {
|
|
36545
36969
|
try {
|
|
36546
|
-
await
|
|
36547
|
-
await writeFile13(
|
|
36970
|
+
await mkdir20(lock);
|
|
36971
|
+
await writeFile13(resolve29(lock, "owner"), `${process.pid}
|
|
36548
36972
|
${now2()}
|
|
36549
36973
|
`, "utf8");
|
|
36550
36974
|
break;
|
|
@@ -36668,7 +37092,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
36668
37092
|
if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
|
|
36669
37093
|
const from = await readInstalledBundleIdentity({
|
|
36670
37094
|
...this.#layout,
|
|
36671
|
-
libDir:
|
|
37095
|
+
libDir: resolve29(this.#layout.releasesRoot, this.#loadedRelease)
|
|
36672
37096
|
}).catch(() => void 0) ?? {};
|
|
36673
37097
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
36674
37098
|
const record3 = {
|
|
@@ -36955,7 +37379,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
36955
37379
|
});
|
|
36956
37380
|
if (!accepted)
|
|
36957
37381
|
return false;
|
|
36958
|
-
await writeFile13(
|
|
37382
|
+
await writeFile13(resolve29(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
|
|
36959
37383
|
readyAt: (options.now ?? Date.now)(),
|
|
36960
37384
|
loadedRelease,
|
|
36961
37385
|
functionalProof: options.functionalProof
|
|
@@ -37201,7 +37625,7 @@ async function startStoredRuntime(configPath, opts = {}, dependencyOverrides = {
|
|
|
37201
37625
|
return { status: "started", pid };
|
|
37202
37626
|
}
|
|
37203
37627
|
function agentStartId(configPath) {
|
|
37204
|
-
return basename7(
|
|
37628
|
+
return basename7(dirname19(configPath));
|
|
37205
37629
|
}
|
|
37206
37630
|
async function startRuntime(configPath, spinnerHandle) {
|
|
37207
37631
|
const report = (text2) => spinnerHandle ? spinnerHandle.message(text2) : console.log(text2);
|
|
@@ -37267,7 +37691,7 @@ async function runStartCommand(args, interactiveUi, dependencyOverrides = {}) {
|
|
|
37267
37691
|
for (const path of unique) {
|
|
37268
37692
|
const id = agentStartId(path);
|
|
37269
37693
|
const spinnerHandle = interactiveUi ? spinner() : void 0;
|
|
37270
|
-
spinnerHandle?.start(`Starting ${
|
|
37694
|
+
spinnerHandle?.start(`Starting ${dirname19(path)}\u2026`);
|
|
37271
37695
|
try {
|
|
37272
37696
|
const outcome = await deps.startOne(path, spinnerHandle);
|
|
37273
37697
|
const report = { id, path, ...outcome };
|
|
@@ -37301,9 +37725,9 @@ async function runStartCommand(args, interactiveUi, dependencyOverrides = {}) {
|
|
|
37301
37725
|
// apps/body/dist/connect-command.js
|
|
37302
37726
|
import { spawn as spawn11 } from "node:child_process";
|
|
37303
37727
|
import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
|
|
37304
|
-
import { chmod as chmod7, mkdir as
|
|
37728
|
+
import { chmod as chmod7, mkdir as mkdir21, readFile as readFile13, unlink as unlink5, writeFile as writeFile14 } from "node:fs/promises";
|
|
37305
37729
|
import { homedir as homedir13, hostname as hostname2 } from "node:os";
|
|
37306
|
-
import { dirname as
|
|
37730
|
+
import { dirname as dirname20, resolve as resolve30 } from "node:path";
|
|
37307
37731
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
37308
37732
|
|
|
37309
37733
|
// apps/body/dist/clack-support.js
|
|
@@ -37621,6 +38045,9 @@ init_self_update();
|
|
|
37621
38045
|
init_self_update_manifest();
|
|
37622
38046
|
var CONNECT_HARNESSES = AUTO_DETECT_AGENT_KINDS;
|
|
37623
38047
|
var AGENT_NAME_MAX_LENGTH = 32;
|
|
38048
|
+
function isInteractiveConnectTerminal(input = stdin3) {
|
|
38049
|
+
return input.isTTY === true;
|
|
38050
|
+
}
|
|
37624
38051
|
function isReasonableAgentName(value) {
|
|
37625
38052
|
const normalized = value.trim().replace(/\s+/g, " ");
|
|
37626
38053
|
return normalized.length > 0 && normalized.length <= AGENT_NAME_MAX_LENGTH && new RegExp("^\\p{L}[\\p{L}\\p{M}'\u2019 -]*$", "u").test(normalized);
|
|
@@ -37931,19 +38358,19 @@ function parseConnectSubscriptions(value) {
|
|
|
37931
38358
|
];
|
|
37932
38359
|
}
|
|
37933
38360
|
async function readMachineId(env = process.env) {
|
|
37934
|
-
const configDir =
|
|
37935
|
-
const machineIdPath =
|
|
38361
|
+
const configDir = resolve30(env.XDG_CONFIG_HOME ?? resolve30(homedir13(), ".config"), "beeline");
|
|
38362
|
+
const machineIdPath = resolve30(configDir, "machine-id");
|
|
37936
38363
|
let machineId;
|
|
37937
38364
|
let machineName = hostname2();
|
|
37938
38365
|
try {
|
|
37939
|
-
const existing = await
|
|
38366
|
+
const existing = await readFile13(machineIdPath, "utf8");
|
|
37940
38367
|
machineId = existing.trim();
|
|
37941
38368
|
if (!/^[0-9a-f-]{32,}$/.test(machineId))
|
|
37942
38369
|
throw new Error("invalid persisted machine id");
|
|
37943
38370
|
} catch {
|
|
37944
38371
|
machineId = randomUUID6();
|
|
37945
38372
|
try {
|
|
37946
|
-
await
|
|
38373
|
+
await mkdir21(configDir, { recursive: true, mode: 448 });
|
|
37947
38374
|
await writeFile14(machineIdPath, `${machineId}
|
|
37948
38375
|
`, { mode: 384 });
|
|
37949
38376
|
} catch {
|
|
@@ -38005,7 +38432,7 @@ async function installCurrentRelease(fetchImpl) {
|
|
|
38005
38432
|
});
|
|
38006
38433
|
await activateRelease(layout, releaseId);
|
|
38007
38434
|
return {
|
|
38008
|
-
binary:
|
|
38435
|
+
binary: resolve30(layout.binDir, "beeline"),
|
|
38009
38436
|
version: published.version ?? releaseId
|
|
38010
38437
|
};
|
|
38011
38438
|
}
|
|
@@ -38028,7 +38455,7 @@ function providerEnvironment(selection) {
|
|
|
38028
38455
|
};
|
|
38029
38456
|
}
|
|
38030
38457
|
async function writePrivateJson(path, value) {
|
|
38031
|
-
await
|
|
38458
|
+
await mkdir21(dirname20(path), { recursive: true, mode: 448 });
|
|
38032
38459
|
await writeFile14(path, `${JSON.stringify(value, null, 2)}
|
|
38033
38460
|
`, { mode: 384 });
|
|
38034
38461
|
await chmod7(path, 384);
|
|
@@ -38037,8 +38464,8 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
38037
38464
|
const values = providerEnvironment(selection);
|
|
38038
38465
|
if (Object.keys(values).length === 0)
|
|
38039
38466
|
return void 0;
|
|
38040
|
-
const path =
|
|
38041
|
-
await
|
|
38467
|
+
const path = resolve30(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
|
|
38468
|
+
await mkdir21(dirname20(path), { recursive: true, mode: 448 });
|
|
38042
38469
|
const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
38043
38470
|
await writeFile14(path, `${contents}
|
|
38044
38471
|
`, { mode: 384 });
|
|
@@ -38080,7 +38507,7 @@ async function brassSpinner(message, action, completion) {
|
|
|
38080
38507
|
}
|
|
38081
38508
|
}
|
|
38082
38509
|
async function runConnectCommand(code, options = {}) {
|
|
38083
|
-
if (!
|
|
38510
|
+
if (!isInteractiveConnectTerminal()) {
|
|
38084
38511
|
throw new Error("`usebeeline connect` needs an interactive terminal");
|
|
38085
38512
|
}
|
|
38086
38513
|
const restoreRails = paintWizardBrass();
|
|
@@ -38107,7 +38534,7 @@ async function runConnectWizard(code, fetchImpl, eventSubscriptions, accessPolic
|
|
|
38107
38534
|
await finishConnectedAgentPairing(baseUrl, pairingCode, grant.workspace_joined, eventSubscriptions, fetchImpl);
|
|
38108
38535
|
const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
|
|
38109
38536
|
const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
|
|
38110
|
-
const grantPath =
|
|
38537
|
+
const grantPath = resolve30(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
|
|
38111
38538
|
await writePrivateJson(grantPath, {
|
|
38112
38539
|
agentSecretKey: grant.agent_secret_key,
|
|
38113
38540
|
bodySecretKey: grant.body_secret_key,
|
|
@@ -38192,18 +38619,18 @@ async function runConnectFinishCommand(path) {
|
|
|
38192
38619
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
38193
38620
|
}
|
|
38194
38621
|
try {
|
|
38195
|
-
const grant = JSON.parse(await
|
|
38622
|
+
const grant = JSON.parse(await readFile13(resolve30(path), "utf8"));
|
|
38196
38623
|
if (!isDevicePairingGrant(grant))
|
|
38197
38624
|
throw new Error("device connection grant is invalid");
|
|
38198
38625
|
const connected = await completeDevicePairing(grant);
|
|
38199
|
-
await unlink5(
|
|
38200
|
-
const providerEnv = grant.llmEnvFile ? await
|
|
38626
|
+
await unlink5(resolve30(path));
|
|
38627
|
+
const providerEnv = grant.llmEnvFile ? await readFile13(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
38201
38628
|
const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
|
|
38202
38629
|
const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
|
|
38203
38630
|
if (model) {
|
|
38204
38631
|
const decision2 = await resolveOpenRouterRouting({
|
|
38205
38632
|
model,
|
|
38206
|
-
cacheDir: openRouterRoutingCacheDir(
|
|
38633
|
+
cacheDir: openRouterRoutingCacheDir(dirname20(connected.configPath)),
|
|
38207
38634
|
...apiKey ? { apiKey } : {},
|
|
38208
38635
|
probeTimeoutMs: 1e4
|
|
38209
38636
|
});
|
|
@@ -38221,16 +38648,16 @@ async function runConnectFinishCommand(path) {
|
|
|
38221
38648
|
init_self_update();
|
|
38222
38649
|
|
|
38223
38650
|
// apps/body/dist/daemon-failure.js
|
|
38224
|
-
import { mkdir as
|
|
38225
|
-
import { dirname as
|
|
38651
|
+
import { mkdir as mkdir22, readFile as readFile14, rename as rename8, rm as rm8, writeFile as writeFile15 } from "node:fs/promises";
|
|
38652
|
+
import { dirname as dirname21, resolve as resolve31 } from "node:path";
|
|
38226
38653
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
38227
38654
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
38228
38655
|
function daemonFailurePath(runtimeDir) {
|
|
38229
|
-
return
|
|
38656
|
+
return resolve31(runtimeDir, "daemon-distress.json");
|
|
38230
38657
|
}
|
|
38231
38658
|
async function readFailureRecord(runtimeDir) {
|
|
38232
38659
|
try {
|
|
38233
|
-
const value = JSON.parse(await
|
|
38660
|
+
const value = JSON.parse(await readFile14(daemonFailurePath(runtimeDir), "utf8"));
|
|
38234
38661
|
if (value.version !== 1 || !Array.isArray(value.failures) || value.failures.some((failure2) => typeof failure2 !== "number") || typeof value.lastError !== "string") {
|
|
38235
38662
|
return void 0;
|
|
38236
38663
|
}
|
|
@@ -38242,7 +38669,7 @@ async function readFailureRecord(runtimeDir) {
|
|
|
38242
38669
|
async function writeFailureRecord(runtimeDir, record3) {
|
|
38243
38670
|
const path = daemonFailurePath(runtimeDir);
|
|
38244
38671
|
const staged = `${path}.${process.pid}.tmp`;
|
|
38245
|
-
await
|
|
38672
|
+
await mkdir22(dirname21(path), { recursive: true, mode: 448 });
|
|
38246
38673
|
await writeFile15(staged, `${JSON.stringify(record3, null, 2)}
|
|
38247
38674
|
`, { mode: 384 });
|
|
38248
38675
|
await rename8(staged, path);
|
|
@@ -38265,9 +38692,9 @@ async function clearDaemonStartFailures(runtimeDir) {
|
|
|
38265
38692
|
}
|
|
38266
38693
|
|
|
38267
38694
|
// apps/body/dist/update-functional-probe.js
|
|
38268
|
-
import { mkdir as
|
|
38695
|
+
import { mkdir as mkdir23, rm as rm9 } from "node:fs/promises";
|
|
38269
38696
|
import { homedir as homedir14 } from "node:os";
|
|
38270
|
-
import { resolve as
|
|
38697
|
+
import { resolve as resolve32 } from "node:path";
|
|
38271
38698
|
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
38272
38699
|
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
38273
38700
|
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
@@ -38387,11 +38814,11 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
38387
38814
|
modelAnswerReason: `${detail} (the current release has the same host sandbox failure)`
|
|
38388
38815
|
};
|
|
38389
38816
|
}
|
|
38390
|
-
const root = input.probeRoot ??
|
|
38391
|
-
const cwd =
|
|
38392
|
-
const homeRoot =
|
|
38817
|
+
const root = input.probeRoot ?? resolve32(input.runtimeDir, "update-functional-probe");
|
|
38818
|
+
const cwd = resolve32(root, "checkout");
|
|
38819
|
+
const homeRoot = resolve32(root, "agent-home");
|
|
38393
38820
|
await rm9(root, { recursive: true, force: true });
|
|
38394
|
-
await
|
|
38821
|
+
await mkdir23(cwd, { recursive: true, mode: 448 });
|
|
38395
38822
|
let client;
|
|
38396
38823
|
try {
|
|
38397
38824
|
const agentEnv = {
|
|
@@ -38421,7 +38848,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
38421
38848
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
38422
38849
|
const operatorHome = input.config.operatorHome ?? homedir14();
|
|
38423
38850
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
38424
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
38851
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir23(dir, { recursive: true })));
|
|
38425
38852
|
spawnCommand = wrapAgentCommand({
|
|
38426
38853
|
bwrapPath: input.config.bwrapPath,
|
|
38427
38854
|
spec: {
|
|
@@ -38571,7 +38998,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
38571
38998
|
|
|
38572
38999
|
// apps/body/dist/current-release-probe.js
|
|
38573
39000
|
import { spawn as spawn12 } from "node:child_process";
|
|
38574
|
-
import { dirname as
|
|
39001
|
+
import { dirname as dirname22, join as join14 } from "node:path";
|
|
38575
39002
|
init_self_update();
|
|
38576
39003
|
var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
|
|
38577
39004
|
var UPDATE_PROBE_COMMAND = "update-probe";
|
|
@@ -38620,7 +39047,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
38620
39047
|
return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
|
|
38621
39048
|
}
|
|
38622
39049
|
const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
|
|
38623
|
-
return new Promise((
|
|
39050
|
+
return new Promise((resolve36) => {
|
|
38624
39051
|
const child = spawn12(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
38625
39052
|
let stdout6 = "";
|
|
38626
39053
|
let stderr = "";
|
|
@@ -38630,7 +39057,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
38630
39057
|
return;
|
|
38631
39058
|
settled = true;
|
|
38632
39059
|
clearTimeout(timer);
|
|
38633
|
-
|
|
39060
|
+
resolve36(outcome);
|
|
38634
39061
|
};
|
|
38635
39062
|
const timer = setTimeout(() => {
|
|
38636
39063
|
child.kill("SIGKILL");
|
|
@@ -38677,7 +39104,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
38677
39104
|
const runtime = await readRuntimeRecord(configPath);
|
|
38678
39105
|
const agent = runtimeAgentCommand(runtime);
|
|
38679
39106
|
const config = loadBodyConfig({
|
|
38680
|
-
workspaceRoot: join14(
|
|
39107
|
+
workspaceRoot: join14(dirname22(configPath), "workspace"),
|
|
38681
39108
|
llmEnvFile: runtime.llmEnvFile,
|
|
38682
39109
|
env: { ...env, BUZZ_AGENT_BIN: agent.command, BUZZ_DEV_MCP_BIN: runtime.mcpBinary },
|
|
38683
39110
|
agent
|
|
@@ -38695,7 +39122,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
38695
39122
|
}
|
|
38696
39123
|
const layout = beelineInstallLayout(env);
|
|
38697
39124
|
const releaseId = (layout && await activeReleaseId(layout).catch(() => void 0)) ?? "unknown";
|
|
38698
|
-
const runtimeDir =
|
|
39125
|
+
const runtimeDir = dirname22(configPath);
|
|
38699
39126
|
const outcome = await probeOutcome(() => (options.probe ?? runUpdateFunctionalProbe)({
|
|
38700
39127
|
config,
|
|
38701
39128
|
runtimeDir,
|
|
@@ -38710,8 +39137,8 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
38710
39137
|
}
|
|
38711
39138
|
|
|
38712
39139
|
// apps/body/dist/release-status.js
|
|
38713
|
-
import { readFile as
|
|
38714
|
-
import { resolve as
|
|
39140
|
+
import { readFile as readFile15, readdir as readdir6, rename as rename9, writeFile as writeFile16 } from "node:fs/promises";
|
|
39141
|
+
import { resolve as resolve33 } from "node:path";
|
|
38715
39142
|
var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
|
|
38716
39143
|
var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
|
|
38717
39144
|
var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
|
|
@@ -38728,7 +39155,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
38728
39155
|
pid: options.pid ?? process.pid,
|
|
38729
39156
|
readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
38730
39157
|
};
|
|
38731
|
-
const target =
|
|
39158
|
+
const target = resolve33(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
|
|
38732
39159
|
const temporary = `${target}.${status.pid}.tmp`;
|
|
38733
39160
|
await writeFile16(temporary, `${JSON.stringify(status, null, 2)}
|
|
38734
39161
|
`, { mode: 384 });
|
|
@@ -38738,7 +39165,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
38738
39165
|
|
|
38739
39166
|
// apps/body/dist/scratch-sweep.js
|
|
38740
39167
|
import { lstat as lstat5, readdir as readdir7, rmdir, unlink as unlink6 } from "node:fs/promises";
|
|
38741
|
-
import { resolve as
|
|
39168
|
+
import { resolve as resolve34 } from "node:path";
|
|
38742
39169
|
var DEFAULT_SCRATCH_TTL_HOURS = 72;
|
|
38743
39170
|
var NEVER_SWEEP_SUBDIR_NAMES = new Set(HOME_SUBDIRS.filter((name) => name !== "tmp"));
|
|
38744
39171
|
function scratchTtlMs(env = process.env) {
|
|
@@ -38747,7 +39174,7 @@ function scratchTtlMs(env = process.env) {
|
|
|
38747
39174
|
return (Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_SCRATCH_TTL_HOURS) * 60 * 60 * 1e3;
|
|
38748
39175
|
}
|
|
38749
39176
|
async function discoverAttachScratchRoots(runtimeDir) {
|
|
38750
|
-
const roomsDir =
|
|
39177
|
+
const roomsDir = resolve34(runtimeDir, "rooms");
|
|
38751
39178
|
let entries;
|
|
38752
39179
|
try {
|
|
38753
39180
|
entries = await readdir7(roomsDir, { withFileTypes: true });
|
|
@@ -38758,7 +39185,7 @@ async function discoverAttachScratchRoots(runtimeDir) {
|
|
|
38758
39185
|
for (const entry of entries) {
|
|
38759
39186
|
if (!entry.isDirectory())
|
|
38760
39187
|
continue;
|
|
38761
|
-
const home =
|
|
39188
|
+
const home = resolve34(roomsDir, entry.name, "agent-home");
|
|
38762
39189
|
const stats = await lstat5(home).catch(() => void 0);
|
|
38763
39190
|
if (stats?.isDirectory())
|
|
38764
39191
|
roots.push(home);
|
|
@@ -38777,7 +39204,7 @@ async function removeStaleFiles(dir, cutoffMs, protectNamesHere) {
|
|
|
38777
39204
|
for (const entry of entries) {
|
|
38778
39205
|
if (protectNamesHere && NEVER_SWEEP_SUBDIR_NAMES.has(entry.name))
|
|
38779
39206
|
continue;
|
|
38780
|
-
const path =
|
|
39207
|
+
const path = resolve34(dir, entry.name);
|
|
38781
39208
|
const stats = await lstat5(path).catch(() => void 0);
|
|
38782
39209
|
if (!stats || stats.isSymbolicLink())
|
|
38783
39210
|
continue;
|
|
@@ -38865,7 +39292,7 @@ var DaemonExitError = class extends Error {
|
|
|
38865
39292
|
};
|
|
38866
39293
|
async function runStoredDaemon(pathOrPointer) {
|
|
38867
39294
|
const configPath = await resolveRuntimeConfigPath(pathOrPointer);
|
|
38868
|
-
daemonFailureRuntimeDir =
|
|
39295
|
+
daemonFailureRuntimeDir = dirname23(configPath);
|
|
38869
39296
|
const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
|
|
38870
39297
|
let runtime = accessMigration.runtime;
|
|
38871
39298
|
if (!runtime.transport) {
|
|
@@ -38884,7 +39311,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
38884
39311
|
BUZZ_DEV_MCP_BIN: runtime.mcpBinary
|
|
38885
39312
|
};
|
|
38886
39313
|
const config = loadBodyConfig({
|
|
38887
|
-
workspaceRoot:
|
|
39314
|
+
workspaceRoot: resolve35(dirname23(configPath), "workspace"),
|
|
38888
39315
|
llmEnvFile: runtime.llmEnvFile,
|
|
38889
39316
|
env,
|
|
38890
39317
|
agent
|
|
@@ -38919,7 +39346,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
38919
39346
|
const stop = () => controller.abort();
|
|
38920
39347
|
process.once("SIGINT", stop);
|
|
38921
39348
|
process.once("SIGTERM", stop);
|
|
38922
|
-
const runtimeDir =
|
|
39349
|
+
const runtimeDir = dirname23(configPath);
|
|
38923
39350
|
const layout = beelineInstallLayout(process.env);
|
|
38924
39351
|
const notifier = new SystemdNotifier();
|
|
38925
39352
|
let rollbackAlertDrain;
|
|
@@ -39057,6 +39484,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
39057
39484
|
agentId: runtime.agent.publicKey,
|
|
39058
39485
|
log: (message) => console.log(`[body] connector: ${message}`)
|
|
39059
39486
|
});
|
|
39487
|
+
daemonApi.setConnectorAssignmentListener(() => connectorLoop?.wake());
|
|
39060
39488
|
connectorLoop.start();
|
|
39061
39489
|
},
|
|
39062
39490
|
onProgress: async (status) => {
|
|
@@ -39123,7 +39551,7 @@ async function main() {
|
|
|
39123
39551
|
const roomId = roomFlag >= 0 ? args[roomFlag + 1] : void 0;
|
|
39124
39552
|
if (!configPath || !roomId)
|
|
39125
39553
|
throw new Error("corner-read-token requires --config and --room");
|
|
39126
|
-
const activated = await activateDaemonTransport(
|
|
39554
|
+
const activated = await activateDaemonTransport(resolve35(configPath));
|
|
39127
39555
|
if (!activated)
|
|
39128
39556
|
throw new Error("corner-read-token requires monolith transport");
|
|
39129
39557
|
const credential = await activated.client.execute("getRoomGitHubToken", { roomId });
|
|
@@ -39177,14 +39605,14 @@ async function main() {
|
|
|
39177
39605
|
}
|
|
39178
39606
|
if (!configPath && agentPubkey) {
|
|
39179
39607
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
39180
|
-
configPath = configs.find((candidate) =>
|
|
39608
|
+
configPath = configs.find((candidate) => dirname23(candidate).endsWith(agentPubkey));
|
|
39181
39609
|
}
|
|
39182
39610
|
if (!configPath && agentPubkey) {
|
|
39183
39611
|
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
|
|
39184
39612
|
}
|
|
39185
39613
|
if (!configPath)
|
|
39186
39614
|
throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
|
|
39187
|
-
await runStoredDaemon(
|
|
39615
|
+
await runStoredDaemon(resolve35(configPath));
|
|
39188
39616
|
return;
|
|
39189
39617
|
}
|
|
39190
39618
|
if (command === "update") {
|
|
@@ -39205,7 +39633,7 @@ async function main() {
|
|
|
39205
39633
|
if (!agentPubkey)
|
|
39206
39634
|
throw new Error("stop requires --agent <pubkey>");
|
|
39207
39635
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
39208
|
-
const configPath = configs.find((candidate) =>
|
|
39636
|
+
const configPath = configs.find((candidate) => dirname23(candidate).endsWith(agentPubkey));
|
|
39209
39637
|
if (!configPath)
|
|
39210
39638
|
throw new Error(`no stored runtime found for agent ${agentPubkey}`);
|
|
39211
39639
|
const runtime = await readRuntimeRecord(configPath);
|