usebeeline 0.1.57 → 0.1.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/usebeeline.mjs +255 -54
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -126,6 +126,152 @@ var init_lifecycle_test_guard = __esm({
|
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
+
// apps/body/dist/trusty-squire-storage.js
|
|
130
|
+
import { homedir as homedir2 } from "node:os";
|
|
131
|
+
import { isAbsolute, resolve as resolve4 } from "node:path";
|
|
132
|
+
function trustySquireLegacyStorePaths(operatorHome = homedir2(), env = process.env) {
|
|
133
|
+
const roots = /* @__PURE__ */ new Set([resolve4(operatorHome, ".config")]);
|
|
134
|
+
const xdgConfig = env.XDG_CONFIG_HOME?.trim();
|
|
135
|
+
if (xdgConfig && isAbsolute(xdgConfig))
|
|
136
|
+
roots.add(resolve4(xdgConfig));
|
|
137
|
+
return [...roots].map((root) => resolve4(root, "trusty-squire"));
|
|
138
|
+
}
|
|
139
|
+
var init_trusty_squire_storage = __esm({
|
|
140
|
+
"apps/body/dist/trusty-squire-storage.js"() {
|
|
141
|
+
"use strict";
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// apps/body/dist/storage-filesystem.js
|
|
146
|
+
import { chmod, lstat as lstat2, readdir as readdir2, realpath, rm as rm2 } from "node:fs/promises";
|
|
147
|
+
import { resolve as resolve5, sep } from "node:path";
|
|
148
|
+
import { spawn } from "node:child_process";
|
|
149
|
+
function canonicalStoragePath(path) {
|
|
150
|
+
const absolute = resolve5(path);
|
|
151
|
+
return process.platform === "darwin" ? absolute.replace(/^\/(var|tmp|etc)(?=\/|$)/, "/private/$1") : absolute;
|
|
152
|
+
}
|
|
153
|
+
async function ordinaryDirectory(path) {
|
|
154
|
+
const info = await lstat2(path).catch(() => void 0);
|
|
155
|
+
return Boolean(info?.isDirectory() && canonicalStoragePath(await realpath(path)) === canonicalStoragePath(path));
|
|
156
|
+
}
|
|
157
|
+
async function storageTree(root) {
|
|
158
|
+
if (process.platform === "darwin") {
|
|
159
|
+
const tree2 = /* @__PURE__ */ new Map();
|
|
160
|
+
const visit = async (path) => {
|
|
161
|
+
const info = await lstat2(path);
|
|
162
|
+
tree2.set(path, {
|
|
163
|
+
dev: info.dev,
|
|
164
|
+
ino: info.ino,
|
|
165
|
+
blocks: info.blocks * 512,
|
|
166
|
+
links: info.nlink,
|
|
167
|
+
directory: info.isDirectory(),
|
|
168
|
+
lastUsed: Math.max(info.atimeMs, info.mtimeMs)
|
|
169
|
+
});
|
|
170
|
+
if (info.isDirectory() && !isSquirePath(path))
|
|
171
|
+
for (const name of await readdir2(path))
|
|
172
|
+
await visit(resolve5(path, name));
|
|
173
|
+
};
|
|
174
|
+
await visit(root);
|
|
175
|
+
return tree2;
|
|
176
|
+
}
|
|
177
|
+
const child = spawn("/usr/bin/python3", ["-c", TREE_INVENTORY, root], { stdio: ["ignore", "pipe", "pipe"] });
|
|
178
|
+
const tree = /* @__PURE__ */ new Map();
|
|
179
|
+
let pending = "", stderr = "";
|
|
180
|
+
child.stderr.setEncoding("utf8");
|
|
181
|
+
child.stderr.on("data", (chunk) => {
|
|
182
|
+
stderr = (stderr + chunk).slice(-8192);
|
|
183
|
+
});
|
|
184
|
+
const completed = new Promise((done, fail) => {
|
|
185
|
+
child.once("error", fail);
|
|
186
|
+
child.once("close", (code) => code === 0 ? done() : fail(new Error(`storage inventory failed (${code}): ${stderr}`)));
|
|
187
|
+
});
|
|
188
|
+
void completed.catch(() => void 0);
|
|
189
|
+
const add = (line) => {
|
|
190
|
+
const [path, dev, ino, blocks, links, directory, lastUsed] = JSON.parse(line);
|
|
191
|
+
tree.set(path, { dev, ino, blocks, links, directory, lastUsed });
|
|
192
|
+
};
|
|
193
|
+
child.stdout.setEncoding("utf8");
|
|
194
|
+
for await (const chunk of child.stdout) {
|
|
195
|
+
pending += chunk;
|
|
196
|
+
let newline;
|
|
197
|
+
while ((newline = pending.indexOf("\n")) >= 0) {
|
|
198
|
+
add(pending.slice(0, newline));
|
|
199
|
+
pending = pending.slice(newline + 1);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
await completed;
|
|
203
|
+
if (pending)
|
|
204
|
+
add(pending);
|
|
205
|
+
return tree;
|
|
206
|
+
}
|
|
207
|
+
function isSquirePath(path) {
|
|
208
|
+
const absolute = resolve5(path);
|
|
209
|
+
return absolute.split(sep).some((part, index, parts) => [".trusty-squire", "squire-host-config"].includes(part) || part === "trusty-squire" && parts[index - 1] !== "skills") || trustySquireLegacyStorePaths().some((root) => absolute === root || absolute.startsWith(`${root}${sep}`));
|
|
210
|
+
}
|
|
211
|
+
async function removeStoragePath(root, knownTree) {
|
|
212
|
+
const original = knownTree ?? await storageTree(root);
|
|
213
|
+
let index = removalIndexes.get(original);
|
|
214
|
+
if (!index) {
|
|
215
|
+
index = {
|
|
216
|
+
excluded: [...original.keys()].filter(isSquirePath),
|
|
217
|
+
directories: [...original].filter(([, item]) => item.directory).map(([path]) => path).sort()
|
|
218
|
+
};
|
|
219
|
+
removalIndexes.set(original, index);
|
|
220
|
+
}
|
|
221
|
+
const { excluded, directories } = index;
|
|
222
|
+
if (excluded.some((path) => path === root || path.startsWith(`${root}/`)))
|
|
223
|
+
throw new Error("refusing removal of Trusty Squire storage");
|
|
224
|
+
if (isSquirePath(root))
|
|
225
|
+
throw new Error("refusing removal of Trusty Squire storage");
|
|
226
|
+
const prefix = `${root}/`;
|
|
227
|
+
let low = 0, high = directories.length;
|
|
228
|
+
while (low < high) {
|
|
229
|
+
const middle = low + high >>> 1;
|
|
230
|
+
if (directories[middle] < prefix)
|
|
231
|
+
low = middle + 1;
|
|
232
|
+
else
|
|
233
|
+
high = middle;
|
|
234
|
+
}
|
|
235
|
+
const ownedDirectories = original.get(root)?.directory ? [root] : [];
|
|
236
|
+
for (let cursor = low; cursor < directories.length && directories[cursor].startsWith(prefix); cursor++)
|
|
237
|
+
ownedDirectories.push(directories[cursor]);
|
|
238
|
+
for (const path of ownedDirectories) {
|
|
239
|
+
const info = await lstat2(path).catch((error) => {
|
|
240
|
+
if (error.code === "ENOENT")
|
|
241
|
+
return void 0;
|
|
242
|
+
throw error;
|
|
243
|
+
});
|
|
244
|
+
if (!info)
|
|
245
|
+
continue;
|
|
246
|
+
if (info.isDirectory() && process.getuid?.() === info.uid && (info.mode & 448) !== 448)
|
|
247
|
+
await chmod(path, info.mode | 448);
|
|
248
|
+
}
|
|
249
|
+
await rm2(root, { recursive: true, force: true, maxRetries: 3 });
|
|
250
|
+
}
|
|
251
|
+
var TREE_INVENTORY, removalIndexes;
|
|
252
|
+
var init_storage_filesystem = __esm({
|
|
253
|
+
"apps/body/dist/storage-filesystem.js"() {
|
|
254
|
+
"use strict";
|
|
255
|
+
init_trusty_squire_storage();
|
|
256
|
+
TREE_INVENTORY = String.raw`
|
|
257
|
+
import os, sys, json, stat
|
|
258
|
+
root=sys.argv[1]
|
|
259
|
+
def visit(path):
|
|
260
|
+
s=os.lstat(path)
|
|
261
|
+
directory=stat.S_ISDIR(s.st_mode)
|
|
262
|
+
print(json.dumps([path,s.st_dev,s.st_ino,s.st_blocks*512,s.st_nlink,directory,max(s.st_atime_ns,s.st_mtime_ns)/1000000]))
|
|
263
|
+
if directory and not (os.path.basename(path) in ['.trusty-squire','squire-host-config'] or (os.path.basename(path)=='trusty-squire' and os.path.basename(os.path.dirname(path))!='skills')):
|
|
264
|
+
fd=os.open(path,os.O_RDONLY|os.O_DIRECTORY|os.O_NOATIME|os.O_NOFOLLOW)
|
|
265
|
+
try: names=os.listdir(fd)
|
|
266
|
+
finally: os.close(fd)
|
|
267
|
+
for name in names: visit(os.path.join(path,name))
|
|
268
|
+
visit(root)
|
|
269
|
+
|
|
270
|
+
`;
|
|
271
|
+
removalIndexes = /* @__PURE__ */ new WeakMap();
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
129
275
|
// apps/body/dist/self-update-manifest.js
|
|
130
276
|
function resolveManifestUrl(env = process.env) {
|
|
131
277
|
const override = env.BEELINE_UPDATE_MANIFEST_URL?.trim();
|
|
@@ -252,6 +398,7 @@ __export(self_update_exports, {
|
|
|
252
398
|
nodeVersionAtLeast: () => nodeVersionAtLeast,
|
|
253
399
|
normalizeLegacyBundleShape: () => normalizeLegacyBundleShape,
|
|
254
400
|
prepareLatestRelease: () => prepareLatestRelease,
|
|
401
|
+
pruneHelperReleases: () => pruneHelperReleases,
|
|
255
402
|
readBundleEngineMinimum: () => readBundleEngineMinimum,
|
|
256
403
|
readInstalledBundleIdentity: () => readInstalledBundleIdentity,
|
|
257
404
|
readUpdateState: () => readUpdateState,
|
|
@@ -264,12 +411,12 @@ __export(self_update_exports, {
|
|
|
264
411
|
});
|
|
265
412
|
import { createHash as createHash3 } from "node:crypto";
|
|
266
413
|
import { constants as fsConstants } from "node:fs";
|
|
267
|
-
import { access, chmod, copyFile, lstat as
|
|
268
|
-
import { spawn } from "node:child_process";
|
|
269
|
-
import { homedir as
|
|
270
|
-
import { dirname as dirname4, join, resolve as
|
|
414
|
+
import { access, chmod as chmod2, copyFile, lstat as lstat3, mkdir as mkdir3, open, readFile as readFile2, readdir as readdir3, rename, rm as rm3, symlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
415
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
416
|
+
import { homedir as homedir3 } from "node:os";
|
|
417
|
+
import { dirname as dirname4, join, resolve as resolve6 } from "node:path";
|
|
271
418
|
function anchorLayout(rawLibDir) {
|
|
272
|
-
const libDir =
|
|
419
|
+
const libDir = resolve6(rawLibDir);
|
|
273
420
|
const segments = libDir.split(/[/\\]/);
|
|
274
421
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
275
422
|
if (idx >= 2 && segments[idx - 1] === "lib") {
|
|
@@ -285,9 +432,9 @@ function anchorLayout(rawLibDir) {
|
|
|
285
432
|
// prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
|
|
286
433
|
// defect that made activateRelease write its stable forwarders where
|
|
287
434
|
// nothing executed them, leaving stale raw wrappers in <prefix>/bin.
|
|
288
|
-
binDir:
|
|
435
|
+
binDir: resolve6(libDir, "../../bin"),
|
|
289
436
|
libDir,
|
|
290
|
-
releasesRoot:
|
|
437
|
+
releasesRoot: resolve6(libDir, `../${RELEASES_SEGMENT}`)
|
|
291
438
|
};
|
|
292
439
|
}
|
|
293
440
|
function beelineInstallLayout(env = process.env) {
|
|
@@ -299,18 +446,18 @@ function beelineInstallLayout(env = process.env) {
|
|
|
299
446
|
function releaseIdFromPath(path) {
|
|
300
447
|
if (!path?.trim())
|
|
301
448
|
return void 0;
|
|
302
|
-
const segments =
|
|
449
|
+
const segments = resolve6(path.trim()).split(/[/\\]/);
|
|
303
450
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
304
451
|
const id = idx >= 0 ? segments[idx + 1] : void 0;
|
|
305
452
|
return id ? sanitizeReleaseId(id) : void 0;
|
|
306
453
|
}
|
|
307
454
|
function runningReleaseId(layout, env = process.env, invocationPath = process.argv[1]) {
|
|
308
|
-
const releasesRoot =
|
|
309
|
-
const anchor =
|
|
455
|
+
const releasesRoot = resolve6(layout.releasesRoot);
|
|
456
|
+
const anchor = resolve6(layout.libDir);
|
|
310
457
|
for (const raw of [env.BEELINE_LIB_DIR, invocationPath]) {
|
|
311
458
|
if (!raw?.trim())
|
|
312
459
|
continue;
|
|
313
|
-
const resolved =
|
|
460
|
+
const resolved = resolve6(raw.trim());
|
|
314
461
|
if (resolved !== anchor && !resolved.startsWith(`${releasesRoot}/`))
|
|
315
462
|
continue;
|
|
316
463
|
const id = releaseIdFromPath(resolved);
|
|
@@ -320,8 +467,8 @@ function runningReleaseId(layout, env = process.env, invocationPath = process.ar
|
|
|
320
467
|
return void 0;
|
|
321
468
|
}
|
|
322
469
|
function defaultBeelineInstallLayout(env = process.env) {
|
|
323
|
-
const home = env.HOME?.trim() ||
|
|
324
|
-
return anchorLayout(
|
|
470
|
+
const home = env.HOME?.trim() || homedir3();
|
|
471
|
+
return anchorLayout(resolve6(home, ".local", "lib", "beeline"));
|
|
325
472
|
}
|
|
326
473
|
function discoveredBeelineInstallLayout(env = process.env) {
|
|
327
474
|
const explicitAnchor = env.BEELINE_INSTALL_LIB_DIR?.trim();
|
|
@@ -329,7 +476,7 @@ function discoveredBeelineInstallLayout(env = process.env) {
|
|
|
329
476
|
return anchorLayout(explicitAnchor);
|
|
330
477
|
const explicitBinDir = env.BEELINE_INSTALL_DIR?.trim();
|
|
331
478
|
if (explicitBinDir)
|
|
332
|
-
return anchorLayout(
|
|
479
|
+
return anchorLayout(resolve6(dirname4(resolve6(explicitBinDir)), "lib", "beeline"));
|
|
333
480
|
return defaultBeelineInstallLayout(env);
|
|
334
481
|
}
|
|
335
482
|
function hostPlatformKey() {
|
|
@@ -422,7 +569,7 @@ function sanitizeReleaseId(id) {
|
|
|
422
569
|
}
|
|
423
570
|
async function pathKind(path) {
|
|
424
571
|
try {
|
|
425
|
-
const info = await
|
|
572
|
+
const info = await lstat3(path);
|
|
426
573
|
if (info.isSymbolicLink())
|
|
427
574
|
return "symlink";
|
|
428
575
|
return info.isDirectory() ? "directory" : "missing";
|
|
@@ -470,7 +617,7 @@ async function fetchText(url, fetchImpl) {
|
|
|
470
617
|
}
|
|
471
618
|
function run(command, args, timeoutMs) {
|
|
472
619
|
return new Promise((resolveRun) => {
|
|
473
|
-
const child =
|
|
620
|
+
const child = spawn2(command, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
474
621
|
let stderr = "";
|
|
475
622
|
child.stderr?.setEncoding("utf8");
|
|
476
623
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -566,7 +713,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
566
713
|
}
|
|
567
714
|
await writeFile2(tempArchive, Buffer.concat(chunks), { mode: 384 });
|
|
568
715
|
const entries = (await new Promise((resolveList, rejectList) => {
|
|
569
|
-
const child =
|
|
716
|
+
const child = spawn2("tar", ["-tzf", tempArchive], { stdio: ["ignore", "pipe", "inherit"] });
|
|
570
717
|
const timer = setTimeout(() => child.kill("SIGKILL"), Math.min(3e4, remaining()));
|
|
571
718
|
let out = "";
|
|
572
719
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -619,10 +766,10 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
619
766
|
return releaseId;
|
|
620
767
|
} catch (error) {
|
|
621
768
|
if (!previouslyVerified)
|
|
622
|
-
await
|
|
769
|
+
await rm3(releaseDir, { recursive: true, force: true });
|
|
623
770
|
throw error;
|
|
624
771
|
} finally {
|
|
625
|
-
await
|
|
772
|
+
await rm3(tempArchive, { force: true });
|
|
626
773
|
}
|
|
627
774
|
}
|
|
628
775
|
function forwarderScript(tool) {
|
|
@@ -656,12 +803,13 @@ function forwarderScript(tool) {
|
|
|
656
803
|
async function replaceFile(path, contents, mode) {
|
|
657
804
|
const temp = `${path}.new-${process.pid}`;
|
|
658
805
|
await writeFile2(temp, contents, { mode });
|
|
659
|
-
await
|
|
806
|
+
await chmod2(temp, mode);
|
|
660
807
|
await rename(temp, path);
|
|
661
808
|
}
|
|
662
809
|
async function activateRelease(layout, releaseId) {
|
|
663
810
|
assertInstallTestIsolation(layout);
|
|
664
811
|
let releaseDir = join(layout.releasesRoot, releaseId);
|
|
812
|
+
let previousReleaseId = await activeReleaseId(layout);
|
|
665
813
|
await access(join(releaseDir, BUNDLE_ENTRYPOINT), fsConstants.F_OK);
|
|
666
814
|
await mkdir3(layout.releasesRoot, { recursive: true });
|
|
667
815
|
await mkdir3(layout.binDir, { recursive: true });
|
|
@@ -675,27 +823,36 @@ async function activateRelease(layout, releaseId) {
|
|
|
675
823
|
const preservedId = `${legacyId}-${Date.now()}`;
|
|
676
824
|
await rename(layout.libDir, join(layout.releasesRoot, preservedId));
|
|
677
825
|
await normalizeLegacyBundleShape(join(layout.releasesRoot, preservedId));
|
|
826
|
+
previousReleaseId = preservedId;
|
|
678
827
|
} catch {
|
|
679
828
|
await rename(layout.libDir, legacyDir);
|
|
680
829
|
await normalizeLegacyBundleShape(legacyDir);
|
|
830
|
+
previousReleaseId = legacyId;
|
|
681
831
|
}
|
|
682
832
|
}
|
|
683
833
|
const tempLink = `${layout.libDir}.new-${process.pid}`;
|
|
684
|
-
await
|
|
834
|
+
await rm3(tempLink, { force: true });
|
|
685
835
|
await symlink(join("beeline-releases", releaseId), tempLink);
|
|
686
836
|
await rename(tempLink, layout.libDir);
|
|
687
837
|
await fsyncDir(dirname4(layout.libDir));
|
|
688
838
|
await installRecoveryLauncher(layout, releaseDir);
|
|
689
839
|
await writeBinForwarders(layout, releaseDir);
|
|
840
|
+
const state = await readUpdateState(layout);
|
|
841
|
+
await writeUpdateState(layout, {
|
|
842
|
+
...state,
|
|
843
|
+
stagedReleaseId: void 0,
|
|
844
|
+
previousReleaseId: previousReleaseId && previousReleaseId !== releaseId ? previousReleaseId : state.previousReleaseId
|
|
845
|
+
});
|
|
846
|
+
await pruneHelperReleases(layout);
|
|
690
847
|
}
|
|
691
848
|
async function installRecoveryLauncher(layout, releaseDir) {
|
|
692
849
|
const bootstrap = join(releaseDir, "lib/beeline/recovery-cli.mjs");
|
|
693
850
|
if (await access(bootstrap).then(() => true, () => false)) {
|
|
694
|
-
const stable =
|
|
851
|
+
const stable = resolve6(layout.libDir, "../beeline-bootstrap.mjs");
|
|
695
852
|
const temporary = `${stable}.${process.pid}.tmp`;
|
|
696
853
|
await copyFile(bootstrap, temporary);
|
|
697
854
|
await rename(temporary, stable);
|
|
698
|
-
await replaceFile(
|
|
855
|
+
await replaceFile(resolve6(layout.libDir, "../beeline-node-path"), `${process.execPath}
|
|
699
856
|
`, 384);
|
|
700
857
|
}
|
|
701
858
|
}
|
|
@@ -734,7 +891,7 @@ async function writeBinForwarders(layout, activeBundleRoot) {
|
|
|
734
891
|
} catch {
|
|
735
892
|
continue;
|
|
736
893
|
}
|
|
737
|
-
const independent = await access(
|
|
894
|
+
const independent = await access(resolve6(layout.libDir, "../beeline-bootstrap.mjs")).then(() => true, () => false);
|
|
738
895
|
await replaceFile(join(layout.binDir, name), independent || tool !== "beeline" ? forwarderScript(tool) : forwarderScript("legacy-beeline").replace("/bin/legacy-beeline", "/bin/beeline"), 493);
|
|
739
896
|
}
|
|
740
897
|
}
|
|
@@ -750,7 +907,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
750
907
|
} catch {
|
|
751
908
|
current = void 0;
|
|
752
909
|
}
|
|
753
|
-
const independent = await access(
|
|
910
|
+
const independent = await access(resolve6(layout.libDir, "../beeline-bootstrap.mjs")).then(() => true, () => false);
|
|
754
911
|
const expected = independent || tool !== "beeline" ? forwarderScript(tool) : forwarderScript("legacy-beeline").replace("/bin/legacy-beeline", "/bin/beeline");
|
|
755
912
|
return current === expected;
|
|
756
913
|
};
|
|
@@ -789,6 +946,7 @@ async function prepareLatestRelease(layout, options = {}) {
|
|
|
789
946
|
deadlineAt: options.deadlineAt
|
|
790
947
|
});
|
|
791
948
|
await writeUpdateState(layout, {
|
|
949
|
+
...await readUpdateState(layout),
|
|
792
950
|
lastCheckAt: Date.now(),
|
|
793
951
|
lastCheckResult: "verified",
|
|
794
952
|
stagedReleaseId: releaseId
|
|
@@ -804,10 +962,53 @@ async function prepareLatestRelease(layout, options = {}) {
|
|
|
804
962
|
throw error;
|
|
805
963
|
}
|
|
806
964
|
}
|
|
965
|
+
async function pruneHelperReleases(layout, loadedRelease) {
|
|
966
|
+
assertInstallTestIsolation(layout);
|
|
967
|
+
if (!await ordinaryDirectory(layout.releasesRoot))
|
|
968
|
+
return [];
|
|
969
|
+
const active = await activeReleaseId(layout);
|
|
970
|
+
if (!active)
|
|
971
|
+
return [];
|
|
972
|
+
const state = await readUpdateState(layout);
|
|
973
|
+
const candidates = [];
|
|
974
|
+
for (const entry of await readdir3(layout.releasesRoot, { withFileTypes: true })) {
|
|
975
|
+
if (entry.name.startsWith("."))
|
|
976
|
+
continue;
|
|
977
|
+
if (entry.isSymbolicLink()) {
|
|
978
|
+
if (![active, state.previousReleaseId, state.stagedReleaseId, loadedRelease].includes(entry.name))
|
|
979
|
+
await rm3(join(layout.releasesRoot, entry.name), { force: true });
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (!entry.isDirectory())
|
|
983
|
+
continue;
|
|
984
|
+
const path = join(layout.releasesRoot, entry.name);
|
|
985
|
+
if (!await ordinaryDirectory(path))
|
|
986
|
+
continue;
|
|
987
|
+
candidates.push({
|
|
988
|
+
id: entry.name,
|
|
989
|
+
modified: (await lstat3(path)).mtimeMs,
|
|
990
|
+
bootable: await access(join(path, BUNDLE_ENTRYPOINT)).then(() => true, () => false)
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
const recordedPrevious = candidates.find((entry) => entry.id === state.previousReleaseId && entry.id !== active && entry.bootable)?.id;
|
|
994
|
+
const previous = recordedPrevious ?? candidates.filter((entry) => entry.bootable && entry.id !== active && entry.id !== state.stagedReleaseId).sort((a, b) => b.modified - a.modified)[0]?.id;
|
|
995
|
+
const keep = /* @__PURE__ */ new Set([active, previous, loadedRelease, state.stagedReleaseId]);
|
|
996
|
+
const removed = [];
|
|
997
|
+
for (const entry of candidates) {
|
|
998
|
+
if (keep.has(entry.id))
|
|
999
|
+
continue;
|
|
1000
|
+
await removeStoragePath(join(layout.releasesRoot, entry.id));
|
|
1001
|
+
removed.push(entry.id);
|
|
1002
|
+
}
|
|
1003
|
+
if (previous !== state.previousReleaseId)
|
|
1004
|
+
await writeUpdateState(layout, { ...state, previousReleaseId: previous });
|
|
1005
|
+
return removed;
|
|
1006
|
+
}
|
|
807
1007
|
var RELEASES_SEGMENT, STAGED_NODE_PATH_FILE, BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, CODEGRAPH_ENTRYPOINT, FORWARDER_TOOLS, FORWARDER_ALIASES, LEGACY_FLAT_BUNDLE_FILES, SelfUpdateManager;
|
|
808
1008
|
var init_self_update = __esm({
|
|
809
1009
|
"apps/body/dist/self-update.js"() {
|
|
810
1010
|
"use strict";
|
|
1011
|
+
init_storage_filesystem();
|
|
811
1012
|
init_lifecycle_test_guard();
|
|
812
1013
|
init_self_update_manifest();
|
|
813
1014
|
RELEASES_SEGMENT = "beeline-releases";
|
|
@@ -859,10 +1060,10 @@ __export(managed_update_exports, {
|
|
|
859
1060
|
versionAtLeast: () => versionAtLeast,
|
|
860
1061
|
withInstallLock: () => withInstallLock
|
|
861
1062
|
});
|
|
862
|
-
import { spawn as
|
|
863
|
-
import { resolve as
|
|
1063
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1064
|
+
import { resolve as resolve7 } from "node:path";
|
|
864
1065
|
function withInstallLock(layout, work, options = {}) {
|
|
865
|
-
return withKernelLock(`beeline-install:${
|
|
1066
|
+
return withKernelLock(`beeline-install:${resolve7(layout.libDir)}`, work, options.waitMs);
|
|
866
1067
|
}
|
|
867
1068
|
function versionAtLeast(actual, minimum) {
|
|
868
1069
|
const parse = (value) => /^v?(\d+)\.(\d+)\.(\d+)$/.exec(value)?.slice(1).map(Number);
|
|
@@ -887,9 +1088,9 @@ async function runManagedUpdateWorker() {
|
|
|
887
1088
|
}
|
|
888
1089
|
async function prepareReleaseInWorker(env, deadlineAt, entrypoint) {
|
|
889
1090
|
const layout = beelineInstallLayout(env);
|
|
890
|
-
const bootstrap = entrypoint ??
|
|
1091
|
+
const bootstrap = entrypoint ?? resolve7(layout.libDir, "../beeline-bootstrap.mjs");
|
|
891
1092
|
return new Promise((done, reject) => {
|
|
892
|
-
const child =
|
|
1093
|
+
const child = spawn3(process.execPath, [bootstrap, "managed-update-worker"], {
|
|
893
1094
|
detached: true,
|
|
894
1095
|
env: {
|
|
895
1096
|
...env,
|
|
@@ -993,7 +1194,7 @@ var init_managed_update = __esm({
|
|
|
993
1194
|
if (this.#minimum) {
|
|
994
1195
|
const identity = await readInstalledBundleIdentity({
|
|
995
1196
|
...this.options.layout,
|
|
996
|
-
libDir:
|
|
1197
|
+
libDir: resolve7(this.options.layout.releasesRoot, releaseId)
|
|
997
1198
|
});
|
|
998
1199
|
if (!identity?.version || !versionAtLeast(identity.version, this.#minimum)) {
|
|
999
1200
|
this.#prepared = void 0;
|
|
@@ -1075,13 +1276,13 @@ var init_managed_update = __esm({
|
|
|
1075
1276
|
});
|
|
1076
1277
|
|
|
1077
1278
|
// apps/body/dist/recovery-cli.js
|
|
1078
|
-
import { spawn as
|
|
1079
|
-
import { readFile as readFile4, readdir as
|
|
1080
|
-
import { resolve as
|
|
1279
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
1280
|
+
import { readFile as readFile4, readdir as readdir5, stat as stat2, unlink } from "node:fs/promises";
|
|
1281
|
+
import { resolve as resolve9 } from "node:path";
|
|
1081
1282
|
|
|
1082
1283
|
// apps/body/dist/recovery.js
|
|
1083
|
-
import { readFile as readFile3, readdir as
|
|
1084
|
-
import { resolve as
|
|
1284
|
+
import { readFile as readFile3, readdir as readdir4, stat } from "node:fs/promises";
|
|
1285
|
+
import { resolve as resolve8 } from "node:path";
|
|
1085
1286
|
|
|
1086
1287
|
// apps/body/dist/machine-control.js
|
|
1087
1288
|
import { watch } from "node:fs";
|
|
@@ -1449,7 +1650,7 @@ async function prepareMachineRelease(layout, options = {}) {
|
|
|
1449
1650
|
return withInstallLock(layout, () => prepareLatestRelease(layout, { ...options, deadlineAt }), {
|
|
1450
1651
|
waitMs: Math.max(0, deadlineAt - Date.now())
|
|
1451
1652
|
});
|
|
1452
|
-
const stable =
|
|
1653
|
+
const stable = resolve8(layout.libDir, "../beeline-bootstrap.mjs");
|
|
1453
1654
|
const entrypoint = env.BEELINE_RECOVERY_ENTRY || await readFile3(stable).then(() => stable, () => process.argv[1]);
|
|
1454
1655
|
return prepareReleaseInWorker({ ...env, ...options?.force ? { BEELINE_FORCE_REPAIR: "1" } : {} }, deadlineAt, entrypoint);
|
|
1455
1656
|
}
|
|
@@ -1458,9 +1659,9 @@ async function convergeMachine(options = {}) {
|
|
|
1458
1659
|
assertLifecycleTestIsolation(env, Boolean(options.run));
|
|
1459
1660
|
const layout = options.layout ?? beelineInstallLayout(env) ?? discoveredBeelineInstallLayout(env);
|
|
1460
1661
|
const deadlineAt = options.deadlineAt ?? Date.now() + (options.timeoutMs ?? 6e5);
|
|
1461
|
-
const stateRoot = env.XDG_STATE_HOME ||
|
|
1462
|
-
const root =
|
|
1463
|
-
const configured = await Promise.all((await
|
|
1662
|
+
const stateRoot = env.XDG_STATE_HOME || resolve8(env.HOME || "", ".local/state");
|
|
1663
|
+
const root = resolve8(stateRoot, "beeline/agents");
|
|
1664
|
+
const configured = await Promise.all((await readdir4(root).catch(() => [])).filter((id) => /^[0-9a-f]{64}$/i.test(id)).map(async (id) => await stat(resolve8(root, id, "runtime.json")).then(() => true, () => false) && !await stat(resolve8(root, id, "stopped")).then(() => true, () => false) ? id : void 0));
|
|
1464
1665
|
const required = /* @__PURE__ */ new Set([
|
|
1465
1666
|
...configured.filter((id) => Boolean(id)),
|
|
1466
1667
|
...options.agentIds ?? []
|
|
@@ -1481,7 +1682,7 @@ async function convergeMachine(options = {}) {
|
|
|
1481
1682
|
let expectedRelease = options.releaseId;
|
|
1482
1683
|
const targetIdentity = options.releaseId ? await readInstalledBundleIdentity({
|
|
1483
1684
|
...layout,
|
|
1484
|
-
libDir:
|
|
1685
|
+
libDir: resolve8(layout.releasesRoot, options.releaseId)
|
|
1485
1686
|
}) : void 0;
|
|
1486
1687
|
const request = options.releaseId && hasControl ? {
|
|
1487
1688
|
operation: "update",
|
|
@@ -1528,7 +1729,7 @@ async function bootstrapMachine(env = process.env) {
|
|
|
1528
1729
|
} catch (error) {
|
|
1529
1730
|
const state = await readUpdateState(layout);
|
|
1530
1731
|
const candidates = [state.stagedReleaseId, await activeReleaseId(layout)];
|
|
1531
|
-
const verified = await Promise.all(candidates.map(async (id) => id && /^[0-9a-f]{64}$/i.test((await readFile3(
|
|
1732
|
+
const verified = await Promise.all(candidates.map(async (id) => id && /^[0-9a-f]{64}$/i.test((await readFile3(resolve8(layout.releasesRoot, id, ".stage-ok"), "utf8").catch(() => "")).trim()) ? id : void 0));
|
|
1532
1733
|
const candidate = verified.find(Boolean);
|
|
1533
1734
|
if (!candidate)
|
|
1534
1735
|
throw new Error(`startup release selection failed; no verified runnable release: ${String(error)}`);
|
|
@@ -1538,7 +1739,7 @@ async function bootstrapMachine(env = process.env) {
|
|
|
1538
1739
|
waitMs: Math.max(0, bootDeadlineAt - Date.now())
|
|
1539
1740
|
});
|
|
1540
1741
|
}
|
|
1541
|
-
const entry = await resolveBundleEntrypoint(
|
|
1742
|
+
const entry = await resolveBundleEntrypoint(resolve8(layout.releasesRoot, releaseId));
|
|
1542
1743
|
if (!entry)
|
|
1543
1744
|
throw new Error(`selected release ${releaseId} has no body entry`);
|
|
1544
1745
|
return entry;
|
|
@@ -1599,18 +1800,18 @@ async function main() {
|
|
|
1599
1800
|
if (args[i] === "--manifest-url" && !args[++i])
|
|
1600
1801
|
throw new Error("--manifest-url requires a URL");
|
|
1601
1802
|
}
|
|
1602
|
-
const stateRoot2 = process.env.XDG_STATE_HOME ||
|
|
1803
|
+
const stateRoot2 = process.env.XDG_STATE_HOME || resolve9(process.env.HOME || "", ".local/state");
|
|
1603
1804
|
if (args.includes("--status")) {
|
|
1604
1805
|
const [native, helper] = await Promise.all([
|
|
1605
1806
|
nativeHelperStatus(),
|
|
1606
1807
|
requestMachine(stateRoot2, { operation: "status" }, () => true, 2e3).catch(() => void 0)
|
|
1607
1808
|
]);
|
|
1608
|
-
const root =
|
|
1609
|
-
const agents = await Promise.all((await
|
|
1809
|
+
const root = resolve9(stateRoot2, "beeline/agents");
|
|
1810
|
+
const agents = await Promise.all((await readdir5(root).catch(() => [])).filter((id) => /^[0-9a-f]{64}$/.test(id)).map(async (id) => ({
|
|
1610
1811
|
agentId: id,
|
|
1611
|
-
enabled: !await stat2(
|
|
1812
|
+
enabled: !await stat2(resolve9(root, id, "stopped")).then(() => true, () => false),
|
|
1612
1813
|
...helper?.agents[id] ?? { state: "not hosted" },
|
|
1613
|
-
validRecord: await readFile4(
|
|
1814
|
+
validRecord: await readFile4(resolve9(root, id, "runtime.json"), "utf8").then((raw) => Boolean(JSON.parse(raw).agent)).catch(() => false)
|
|
1614
1815
|
})));
|
|
1615
1816
|
console.log(JSON.stringify({
|
|
1616
1817
|
native,
|
|
@@ -1660,7 +1861,7 @@ async function main() {
|
|
|
1660
1861
|
console.log(`[beeline] executing release ${status.loadedRelease}; helper ${status.pid}; every enabled agent serving`);
|
|
1661
1862
|
return;
|
|
1662
1863
|
}
|
|
1663
|
-
const stateRoot = process.env.XDG_STATE_HOME ||
|
|
1864
|
+
const stateRoot = process.env.XDG_STATE_HOME || resolve9(process.env.HOME || "", ".local/state");
|
|
1664
1865
|
let selectedRelease;
|
|
1665
1866
|
if (command === "start") {
|
|
1666
1867
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -1675,13 +1876,13 @@ async function main() {
|
|
|
1675
1876
|
const requested = agentFlag >= 0 ? args[agentFlag + 1] : args[1] && !args[1].startsWith("--") ? args[1] : void 0;
|
|
1676
1877
|
if (agentFlag >= 0 && !requested)
|
|
1677
1878
|
throw new Error("--agent requires an agent public key");
|
|
1678
|
-
const agentRoot =
|
|
1679
|
-
const ids = (await
|
|
1879
|
+
const agentRoot = resolve9(stateRoot, "beeline/agents");
|
|
1880
|
+
const ids = (await readdir5(agentRoot).catch(() => [])).filter((id) => /^[0-9a-f]{64}$/.test(id));
|
|
1680
1881
|
const selected = requested ? ids.filter((id) => id.startsWith(requested)) : args.includes("--all") ? ids : [];
|
|
1681
1882
|
if (requested && selected.length !== 1)
|
|
1682
1883
|
throw new Error(`no unique paired agent matches ${requested}`);
|
|
1683
1884
|
for (const id of selected)
|
|
1684
|
-
await unlink(
|
|
1885
|
+
await unlink(resolve9(agentRoot, id, "stopped")).catch((error) => {
|
|
1685
1886
|
if (error.code !== "ENOENT")
|
|
1686
1887
|
throw error;
|
|
1687
1888
|
});
|
|
@@ -1705,7 +1906,7 @@ async function main() {
|
|
|
1705
1906
|
const entry = await resolveBundleEntrypoint(layout.libDir);
|
|
1706
1907
|
if (!entry)
|
|
1707
1908
|
throw new Error("no installed Beeline body; run npx usebeeline connect");
|
|
1708
|
-
const child =
|
|
1909
|
+
const child = spawn4(process.execPath, [entry, ...args], {
|
|
1709
1910
|
env: {
|
|
1710
1911
|
...process.env,
|
|
1711
1912
|
BEELINE_LIB_DIR: layout.libDir,
|
|
@@ -1723,7 +1924,7 @@ async function convergeDependencies(layout, deadlineAt) {
|
|
|
1723
1924
|
if (!entry)
|
|
1724
1925
|
throw new Error("dependency phase has no selected body");
|
|
1725
1926
|
await new Promise((done, reject) => {
|
|
1726
|
-
const child =
|
|
1927
|
+
const child = spawn4(process.execPath, [entry, "converge-dependencies"], {
|
|
1727
1928
|
env: { ...process.env, BEELINE_LIB_DIR: layout.libDir, BEELINE_INTERNAL_RECOVERY: "1" },
|
|
1728
1929
|
stdio: "inherit"
|
|
1729
1930
|
});
|