@astrale-os/cli 1.0.0-beta.26 → 1.0.0-beta.28
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 +31 -5
- package/dist/astrale.js +1201 -358
- package/package.json +3 -2
- package/src/commands/__tests__/domain-install-operation.test.ts +23 -0
- package/src/commands/__tests__/install-identity-override.test.ts +3 -3
- package/src/commands/domain/install.ts +19 -11
- package/src/commands/get.ts +1 -1
- package/src/commands/identity/register.ts +3 -4
- package/src/commands/logs.ts +2 -5
- package/src/commands/session/analyze.ts +26 -4
- package/src/commands/update.ts +61 -40
- package/src/lib/__tests__/skills.test.ts +809 -0
- package/src/lib/skills/lock.ts +117 -0
- package/src/lib/skills/sync.ts +814 -0
- package/src/lib/skills.ts +1 -52
- package/src/program/__tests__/program.test.ts +1 -1
- package/src/setup/steps/skills.ts +40 -38
- package/src/telemetry/__tests__/retention.test.ts +180 -0
- package/src/telemetry/__tests__/settings.test.ts +102 -0
- package/src/telemetry/__tests__/store-scan.test.ts +128 -0
- package/src/telemetry/__tests__/trigger.test.ts +33 -4
- package/src/telemetry/recorder.ts +6 -3
- package/src/telemetry/retention.ts +129 -0
- package/src/telemetry/session.ts +18 -12
- package/src/telemetry/settings.ts +64 -11
- package/src/telemetry/store.ts +87 -9
- package/src/telemetry/trigger.ts +16 -28
- package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
- package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
- package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
- package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
- package/studio/client/dist/index.html +3 -3
- package/studio/server/agent/prompts/anchors.test.ts +17 -4
- package/studio/server/agent/prompts/anchors.ts +3 -2
- package/studio/server/agent/prompts/system.test.ts +2 -3
- package/studio/server/agent/prompts/system.ts +3 -3
- package/studio/server/agent/run/preparation.ts +1 -1
- package/studio/server/api/context.ts +1 -1
- package/studio/server/api/deployment.ts +1 -1
- package/studio/server/api/views.ts +2 -2
- package/studio/server/api/workspace.ts +1 -1
- package/studio/server/cache.test.ts +3 -8
- package/studio/server/cache.ts +4 -45
- package/studio/server/cli-consumers.test.ts +31 -2
- package/studio/server/handoff/copy.ts +1 -1
- package/studio/server/introspect/canonical-schema.test.ts +154 -128
- package/studio/server/introspect/canonical-schema.ts +183 -302
- package/studio/server/introspect/core.ts +14 -21
- package/studio/server/introspect/extractor.ts +11 -15
- package/studio/server/introspect/overlay-tsmorph.test.ts +1 -5
- package/studio/server/introspect/overlay-tsmorph.ts +0 -1
- package/studio/server/introspect/overlay.ts +3 -24
- package/studio/server/introspect/revision.test.ts +24 -28
- package/studio/server/introspect/revision.ts +10 -21
- package/studio/server/introspect/runtime.test.ts +14 -14
- package/studio/server/introspect/runtime.ts +1 -46
- package/studio/server/lifecycle.ts +4 -1
- package/studio/server/state/documents.test.ts +69 -0
- package/studio/server/state/documents.ts +57 -10
- package/studio/server/workspace/updates.ts +23 -1
- package/studio/shared/contracts/runtime.ts +4 -0
- package/studio/shared/contracts/schema.ts +10 -26
- package/studio/shared/contracts/surface.test.ts +2 -2
- package/studio/shared/contracts/workspace.ts +3 -0
- package/studio/shared/schema/identity.ts +0 -1
- package/tsconfig.json +1 -1
- package/studio/client/dist/assets/index-C0FAnwNw.css +0 -2
- package/studio/client/dist/assets/index-OeJK1TjG.js +0 -81
- package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +0 -1
- package/studio/client/dist/assets/schema-studio-DRdfu_cQ.js +0 -8
- package/studio/server/introspect/core-extractor.ts +0 -30
- package/studio/server/introspect/overlay.test.ts +0 -44
- package/studio/server/introspect/source-overlay/annotations.ts +0 -14
package/dist/astrale.js
CHANGED
|
@@ -2578,7 +2578,7 @@ var package_default;
|
|
|
2578
2578
|
var init_package = __esm(() => {
|
|
2579
2579
|
package_default = {
|
|
2580
2580
|
name: "@astrale-os/cli",
|
|
2581
|
-
version: "1.0.0-beta.
|
|
2581
|
+
version: "1.0.0-beta.28",
|
|
2582
2582
|
description: "Astrale CLI — connect to existing Astrale kernels",
|
|
2583
2583
|
keywords: [
|
|
2584
2584
|
"astrale",
|
|
@@ -2638,6 +2638,7 @@ var init_package = __esm(() => {
|
|
|
2638
2638
|
format: "pnpm exec oxfmt --write .",
|
|
2639
2639
|
"format:check": "pnpm exec oxfmt --check .",
|
|
2640
2640
|
test: "node scripts/qualification/source-boundary.mjs --target && bun test src studio/client/src studio/server studio/shared && node --test scripts/*.test.mjs",
|
|
2641
|
+
"test:skills-e2e": "node scripts/qualification/skills-update-e2e.mjs",
|
|
2641
2642
|
"test:studio": "pnpm --dir studio test",
|
|
2642
2643
|
"test:watch": "bun test --watch src"
|
|
2643
2644
|
},
|
|
@@ -2660,8 +2661,8 @@ var init_package = __esm(() => {
|
|
|
2660
2661
|
"@astrale/commitlint-config": "jsr:~2.0.1",
|
|
2661
2662
|
"@commitlint/cli": "21.2.2",
|
|
2662
2663
|
"@commitlint/config-conventional": "21.2.2",
|
|
2663
|
-
"@types/bun": "^1.1.16",
|
|
2664
2664
|
"@types/node": "^22.0.0",
|
|
2665
|
+
"bun-types": "1.4.0",
|
|
2665
2666
|
husky: "~9.1.7",
|
|
2666
2667
|
"lint-staged": "17.3.0",
|
|
2667
2668
|
msgpackr: "^2.0.5",
|
|
@@ -61295,38 +61296,668 @@ var init_browser = __esm(() => {
|
|
|
61295
61296
|
AUTH_EVAL = "fetch('/auth/me',{credentials:'include'})" + ".then(r=>r.json())" + ".then(j=>({authed:!!j.authenticated,email:j.user&&j.user.email}))" + ".catch(()=>({authed:false}))";
|
|
61296
61297
|
});
|
|
61297
61298
|
|
|
61298
|
-
// src/lib/skills.ts
|
|
61299
|
-
import {
|
|
61300
|
-
import {
|
|
61299
|
+
// src/lib/skills/lock.ts
|
|
61300
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
61301
|
+
import { lstat, mkdir as mkdir9, readFile as readFile10, readdir as readdir2, rm as rm2, stat as stat2, unlink as unlink4, writeFile as writeFile5 } from "node:fs/promises";
|
|
61301
61302
|
import { dirname as dirname8, join as join6 } from "node:path";
|
|
61302
|
-
async function
|
|
61303
|
-
const
|
|
61304
|
-
|
|
61305
|
-
|
|
61306
|
-
|
|
61307
|
-
|
|
61308
|
-
|
|
61309
|
-
|
|
61310
|
-
|
|
61311
|
-
|
|
61312
|
-
|
|
61303
|
+
async function withFileLock2(lockPath, fn, opts = {}) {
|
|
61304
|
+
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
61305
|
+
const staleAfterMs = opts.staleAfterMs ?? 30000;
|
|
61306
|
+
const timeoutMs = opts.timeoutMs ?? 60000;
|
|
61307
|
+
const deadline = Date.now() + timeoutMs;
|
|
61308
|
+
const claimsRoot = `${lockPath}.claims`;
|
|
61309
|
+
const token = randomUUID4();
|
|
61310
|
+
const claimDirectory = join6(claimsRoot, token);
|
|
61311
|
+
await mkdir9(dirname8(lockPath), { recursive: true });
|
|
61312
|
+
await mkdir9(claimsRoot, { recursive: true });
|
|
61313
|
+
await mkdir9(claimDirectory);
|
|
61314
|
+
await writeFile5(join6(claimDirectory, "owner.json"), JSON.stringify({ pid: process.pid }), {
|
|
61315
|
+
mode: 384
|
|
61316
|
+
});
|
|
61317
|
+
await writeFile5(join6(claimDirectory, "choosing"), "", { mode: 384 });
|
|
61318
|
+
try {
|
|
61319
|
+
const claims = await liveClaims(claimsRoot, staleAfterMs);
|
|
61320
|
+
const ticket = Math.max(0, ...claims.flatMap((claim) => claim.ticket ?? [])) + 1;
|
|
61321
|
+
await writeFile5(join6(claimDirectory, "ticket"), String(ticket), { mode: 384 });
|
|
61322
|
+
await unlink4(join6(claimDirectory, "choosing"));
|
|
61323
|
+
for (;; ) {
|
|
61324
|
+
const contenders = await liveClaims(claimsRoot, staleAfterMs);
|
|
61325
|
+
const blocked = contenders.some((claim) => claim.directory !== claimDirectory && (claim.choosing || claim.ticket === null || claim.ticket < ticket || claim.ticket === ticket && claim.directory < claimDirectory));
|
|
61326
|
+
if (!blocked)
|
|
61327
|
+
return await fn();
|
|
61328
|
+
if (Date.now() >= deadline)
|
|
61329
|
+
throw new Error("Timed out waiting for another skill update");
|
|
61330
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollIntervalMs));
|
|
61331
|
+
}
|
|
61332
|
+
} finally {
|
|
61333
|
+
await rm2(claimDirectory, { recursive: true, force: true });
|
|
61334
|
+
}
|
|
61335
|
+
}
|
|
61336
|
+
async function liveClaims(root, staleAfterMs) {
|
|
61337
|
+
const directories = (await readdir2(root, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map((entry) => join6(root, entry.name));
|
|
61338
|
+
const claims = await Promise.all(directories.map(async (directory) => {
|
|
61339
|
+
try {
|
|
61340
|
+
const owner4 = JSON.parse(await readFile10(join6(directory, "owner.json"), "utf8"));
|
|
61341
|
+
if (typeof owner4.pid !== "number")
|
|
61342
|
+
return await discardIfStale(directory, staleAfterMs);
|
|
61343
|
+
if (!isPidAlive2(owner4.pid)) {
|
|
61344
|
+
await rm2(directory, { recursive: true, force: true });
|
|
61345
|
+
return null;
|
|
61346
|
+
}
|
|
61347
|
+
const choosing = await lstat(join6(directory, "choosing")).then(() => true).catch(() => false);
|
|
61348
|
+
const ticket = await readFile10(join6(directory, "ticket"), "utf8").then((value2) => {
|
|
61349
|
+
const parsed = Number(value2);
|
|
61350
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
61351
|
+
}).catch(() => null);
|
|
61352
|
+
return { directory, pid: owner4.pid, ticket, choosing };
|
|
61353
|
+
} catch {
|
|
61354
|
+
return await discardIfStale(directory, staleAfterMs);
|
|
61355
|
+
}
|
|
61356
|
+
}));
|
|
61357
|
+
return claims.filter((claim) => claim !== null);
|
|
61358
|
+
}
|
|
61359
|
+
async function discardIfStale(directory, staleAfterMs) {
|
|
61360
|
+
try {
|
|
61361
|
+
if (Date.now() - (await stat2(directory)).mtimeMs > staleAfterMs) {
|
|
61362
|
+
await rm2(directory, { recursive: true, force: true });
|
|
61363
|
+
}
|
|
61364
|
+
} catch {}
|
|
61365
|
+
return null;
|
|
61366
|
+
}
|
|
61367
|
+
function isPidAlive2(pid) {
|
|
61368
|
+
try {
|
|
61369
|
+
process.kill(pid, 0);
|
|
61370
|
+
return true;
|
|
61371
|
+
} catch (error52) {
|
|
61372
|
+
return error52.code === "EPERM";
|
|
61373
|
+
}
|
|
61374
|
+
}
|
|
61375
|
+
var init_lock = () => {};
|
|
61376
|
+
|
|
61377
|
+
// src/lib/skills/sync.ts
|
|
61378
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
61379
|
+
import {
|
|
61380
|
+
cp,
|
|
61381
|
+
lstat as lstat2,
|
|
61382
|
+
mkdir as mkdir10,
|
|
61383
|
+
mkdtemp as mkdtemp2,
|
|
61384
|
+
readFile as readFile11,
|
|
61385
|
+
readdir as readdir3,
|
|
61386
|
+
readlink,
|
|
61387
|
+
rename as rename3,
|
|
61388
|
+
rm as rm3,
|
|
61389
|
+
symlink,
|
|
61390
|
+
writeFile as writeFile6
|
|
61391
|
+
} from "node:fs/promises";
|
|
61392
|
+
import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
|
|
61393
|
+
import { dirname as dirname9, join as join7, relative, resolve as resolve3 } from "node:path";
|
|
61394
|
+
function resolvedDependencies(overrides = {}) {
|
|
61395
|
+
const home = overrides.home ?? homedir2();
|
|
61396
|
+
const xdgStateHome = process.env.XDG_STATE_HOME;
|
|
61397
|
+
return {
|
|
61398
|
+
home,
|
|
61399
|
+
lockPath: overrides.lockPath ?? (xdgStateHome ? join7(xdgStateHome, "skills", ".skill-lock.json") : join7(home, ".agents", ".skill-lock.json")),
|
|
61400
|
+
run: overrides.run ?? run,
|
|
61401
|
+
resolveSource: overrides.resolveSource
|
|
61402
|
+
};
|
|
61403
|
+
}
|
|
61404
|
+
function sourceOwned(entry) {
|
|
61405
|
+
return entry.source === ASTRALE_CLI_SKILL_SOURCE || entry.sourceUrl === SOURCE_REPOSITORY_URL || entry.sourceUrl === `https://github.com/${ASTRALE_CLI_SKILL_SOURCE}`;
|
|
61406
|
+
}
|
|
61407
|
+
function folderName(entry) {
|
|
61408
|
+
const match = entry.skillPath?.match(/^skills\/([^/]+)\/SKILL\.md$/u);
|
|
61409
|
+
return match?.[1] ?? null;
|
|
61410
|
+
}
|
|
61411
|
+
async function resolveAstraleSkillSource(execute) {
|
|
61412
|
+
const checkout = await mkdtemp2(join7(tmpdir2(), "astrale-skill-source-"));
|
|
61413
|
+
try {
|
|
61414
|
+
const cloned = await execute("git", [
|
|
61415
|
+
"clone",
|
|
61416
|
+
"--quiet",
|
|
61417
|
+
"--depth",
|
|
61418
|
+
"1",
|
|
61419
|
+
"--filter=blob:none",
|
|
61420
|
+
"--no-checkout",
|
|
61421
|
+
"--single-branch",
|
|
61422
|
+
"--branch",
|
|
61423
|
+
"main",
|
|
61424
|
+
SOURCE_REPOSITORY_URL,
|
|
61425
|
+
checkout
|
|
61426
|
+
]);
|
|
61427
|
+
if (cloned.code !== 0)
|
|
61428
|
+
throw new Error(cloned.stderr || cloned.stdout || "git clone failed");
|
|
61429
|
+
const revision = await execute("git", ["-C", checkout, "rev-parse", "HEAD"]);
|
|
61430
|
+
if (revision.code !== 0) {
|
|
61431
|
+
throw new Error(revision.stderr || revision.stdout || "git rev-parse failed");
|
|
61432
|
+
}
|
|
61433
|
+
const folders = await execute("git", ["-C", checkout, "ls-tree", "HEAD:skills"]);
|
|
61434
|
+
const files = await execute("git", [
|
|
61435
|
+
"-C",
|
|
61436
|
+
checkout,
|
|
61437
|
+
"ls-tree",
|
|
61438
|
+
"-r",
|
|
61439
|
+
"--name-only",
|
|
61440
|
+
"HEAD:skills"
|
|
61441
|
+
]);
|
|
61442
|
+
if (folders.code !== 0 || files.code !== 0) {
|
|
61443
|
+
throw new Error(folders.stderr || files.stderr || "git ls-tree failed");
|
|
61444
|
+
}
|
|
61445
|
+
const skillFiles = new Set(files.stdout.split(`
|
|
61446
|
+
`).filter((path4) => /^[^/]+\/SKILL\.md$/u.test(path4)).map((path4) => path4.slice(0, -"/SKILL.md".length)));
|
|
61447
|
+
const skills = folders.stdout.split(`
|
|
61448
|
+
`).flatMap((line) => {
|
|
61449
|
+
const match = line.match(/^040000 tree ([0-9a-f]{40})\t([^/]+)$/u);
|
|
61450
|
+
if (!match || !SAFE_NAME.test(match[2]) || !skillFiles.has(match[2]))
|
|
61451
|
+
return [];
|
|
61452
|
+
return [{ name: match[2], path: `skills/${match[2]}/SKILL.md`, tree: match[1] }];
|
|
61453
|
+
});
|
|
61454
|
+
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
61455
|
+
if (skills.length === 0)
|
|
61456
|
+
throw new Error("astrale-os/cli publishes no top-level skills");
|
|
61457
|
+
return { ref: "main", revision: revision.stdout.trim(), skills };
|
|
61458
|
+
} finally {
|
|
61459
|
+
await rm3(checkout, { recursive: true, force: true });
|
|
61460
|
+
}
|
|
61461
|
+
}
|
|
61462
|
+
async function readSkillLock(path4) {
|
|
61463
|
+
let raw2;
|
|
61464
|
+
try {
|
|
61465
|
+
raw2 = await readFile11(path4, "utf8");
|
|
61466
|
+
} catch (error52) {
|
|
61467
|
+
if (error52.code === "ENOENT")
|
|
61468
|
+
return { lock: null, raw: null };
|
|
61469
|
+
throw error52;
|
|
61470
|
+
}
|
|
61471
|
+
try {
|
|
61472
|
+
const parsed = JSON.parse(raw2);
|
|
61473
|
+
if (parsed === null || typeof parsed !== "object" || !("skills" in parsed) || parsed.skills === null || typeof parsed.skills !== "object") {
|
|
61474
|
+
return { lock: null, raw: raw2 };
|
|
61475
|
+
}
|
|
61476
|
+
return { lock: parsed, raw: raw2 };
|
|
61477
|
+
} catch {
|
|
61478
|
+
return { lock: null, raw: raw2 };
|
|
61479
|
+
}
|
|
61480
|
+
}
|
|
61481
|
+
async function writeSkillLock(path4, lock) {
|
|
61482
|
+
await mkdir10(dirname9(path4), { recursive: true });
|
|
61483
|
+
const next = `${path4}.next`;
|
|
61484
|
+
await writeFile6(next, `${JSON.stringify(lock, null, 2)}
|
|
61485
|
+
`, { mode: 384 });
|
|
61486
|
+
await rename3(next, path4);
|
|
61487
|
+
}
|
|
61488
|
+
function gitObjectHash(type, body) {
|
|
61489
|
+
return createHash2("sha1").update(Buffer.from(`${type} ${body.length}\x00`)).update(body).digest();
|
|
61490
|
+
}
|
|
61491
|
+
async function computeSkillTreeHash(root) {
|
|
61492
|
+
async function treeHash(directory) {
|
|
61493
|
+
const entries = await Promise.all((await readdir3(directory)).map(async (name) => {
|
|
61494
|
+
const path4 = join7(directory, name);
|
|
61495
|
+
const stat3 = await lstat2(path4);
|
|
61496
|
+
if (stat3.isDirectory()) {
|
|
61497
|
+
return { name, sort: `${name}/`, mode: "40000", hash: await treeHash(path4) };
|
|
61498
|
+
}
|
|
61499
|
+
if (stat3.isSymbolicLink()) {
|
|
61500
|
+
return {
|
|
61501
|
+
name,
|
|
61502
|
+
sort: name,
|
|
61503
|
+
mode: "120000",
|
|
61504
|
+
hash: gitObjectHash("blob", Buffer.from(await readlink(path4)))
|
|
61505
|
+
};
|
|
61506
|
+
}
|
|
61507
|
+
return {
|
|
61508
|
+
name,
|
|
61509
|
+
sort: name,
|
|
61510
|
+
mode: stat3.mode & 73 ? "100755" : "100644",
|
|
61511
|
+
hash: gitObjectHash("blob", await readFile11(path4))
|
|
61512
|
+
};
|
|
61513
|
+
}));
|
|
61514
|
+
entries.sort((a, b) => Buffer.compare(Buffer.from(a.sort), Buffer.from(b.sort)));
|
|
61515
|
+
const body = Buffer.concat(entries.flatMap((entry) => [Buffer.from(`${entry.mode} ${entry.name}\x00`), entry.hash]));
|
|
61516
|
+
return gitObjectHash("tree", body);
|
|
61517
|
+
}
|
|
61518
|
+
return (await treeHash(root)).toString("hex");
|
|
61519
|
+
}
|
|
61520
|
+
async function computeInstallerFolderHash(root) {
|
|
61521
|
+
const files = [];
|
|
61522
|
+
async function collect(directory) {
|
|
61523
|
+
await Promise.all((await readdir3(directory, { withFileTypes: true })).map(async (entry) => {
|
|
61524
|
+
if (entry.name === ".git" || entry.name === "node_modules")
|
|
61525
|
+
return;
|
|
61526
|
+
const path4 = join7(directory, entry.name);
|
|
61527
|
+
if (entry.isDirectory())
|
|
61528
|
+
await collect(path4);
|
|
61529
|
+
else if (entry.isFile()) {
|
|
61530
|
+
files.push({
|
|
61531
|
+
path: relative(root, path4).split("\\").join("/"),
|
|
61532
|
+
content: await readFile11(path4)
|
|
61533
|
+
});
|
|
61534
|
+
}
|
|
61535
|
+
}));
|
|
61536
|
+
}
|
|
61537
|
+
await collect(root);
|
|
61538
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
61539
|
+
const hash2 = createHash2("sha256");
|
|
61540
|
+
for (const file2 of files)
|
|
61541
|
+
hash2.update(file2.path).update(file2.content);
|
|
61542
|
+
return hash2.digest("hex");
|
|
61543
|
+
}
|
|
61544
|
+
async function inspectAstraleSkills(snapshot2, home, lockPath) {
|
|
61545
|
+
const { lock } = await readSkillLock(lockPath);
|
|
61546
|
+
const managed = Object.entries(lock?.skills ?? {}).filter(([, entry]) => sourceOwned(entry));
|
|
61547
|
+
const expected = new Map(snapshot2.skills.map((skill) => [skill.name, skill]));
|
|
61548
|
+
const expectedPresence = await Promise.all(snapshot2.skills.map(async (skill) => {
|
|
61549
|
+
try {
|
|
61550
|
+
return (await lstat2(join7(home, ".agents", "skills", skill.name))).isDirectory();
|
|
61551
|
+
} catch {
|
|
61552
|
+
return false;
|
|
61553
|
+
}
|
|
61554
|
+
}));
|
|
61555
|
+
if (managed.length === 0 && expectedPresence.every((present2) => !present2)) {
|
|
61556
|
+
return { state: "absent", managedNames: [], managedFolders: [] };
|
|
61557
|
+
}
|
|
61558
|
+
const folders = managed.flatMap(([name, entry]) => {
|
|
61559
|
+
const folder = folderName(entry);
|
|
61560
|
+
return folder ? [{ key: name, folder, entry }] : [];
|
|
61561
|
+
});
|
|
61562
|
+
const uniqueFolders = new Set(folders.map((entry) => entry.folder));
|
|
61563
|
+
let coherent = folders.length === managed.length && uniqueFolders.size === folders.length;
|
|
61564
|
+
const actualHashes = new Map;
|
|
61565
|
+
for (const item of folders) {
|
|
61566
|
+
try {
|
|
61567
|
+
const root = join7(home, ".agents", "skills", item.folder);
|
|
61568
|
+
const actual = await computeSkillTreeHash(root);
|
|
61569
|
+
actualHashes.set(item.folder, actual);
|
|
61570
|
+
const receiptHash = item.entry.skillFolderHash;
|
|
61571
|
+
if (!receiptHash || (receiptHash.length === 40 ? actual !== receiptHash : receiptHash.length === 64 ? await computeInstallerFolderHash(root) !== receiptHash : true)) {
|
|
61572
|
+
coherent = false;
|
|
61573
|
+
}
|
|
61574
|
+
if (item.entry.skillPath !== `skills/${item.folder}/SKILL.md`)
|
|
61575
|
+
coherent = false;
|
|
61576
|
+
} catch {
|
|
61577
|
+
coherent = false;
|
|
61578
|
+
}
|
|
61579
|
+
}
|
|
61580
|
+
for (const [index, skill] of snapshot2.skills.entries()) {
|
|
61581
|
+
if (expectedPresence[index] && !uniqueFolders.has(skill.name))
|
|
61582
|
+
coherent = false;
|
|
61583
|
+
}
|
|
61584
|
+
if (await directoryExists(join7(home, ".claude"))) {
|
|
61585
|
+
for (const skill of snapshot2.skills) {
|
|
61586
|
+
const link = join7(home, ".claude", "skills", skill.name);
|
|
61587
|
+
try {
|
|
61588
|
+
if (!(await lstat2(link)).isSymbolicLink())
|
|
61589
|
+
coherent = false;
|
|
61590
|
+
else if (resolve3(dirname9(link), await readlink(link)) !== join7(home, ".agents", "skills", skill.name)) {
|
|
61591
|
+
coherent = false;
|
|
61592
|
+
}
|
|
61593
|
+
} catch {
|
|
61594
|
+
coherent = false;
|
|
61595
|
+
}
|
|
61596
|
+
}
|
|
61597
|
+
}
|
|
61598
|
+
const exactCurrent = coherent && managed.length === snapshot2.skills.length && folders.every(({ key, folder, entry }) => {
|
|
61599
|
+
const skill = expected.get(folder);
|
|
61600
|
+
return key === folder && skill !== undefined && entry.ref === snapshot2.ref && actualHashes.get(folder) === skill.tree;
|
|
61601
|
+
});
|
|
61602
|
+
return {
|
|
61603
|
+
state: exactCurrent ? "current" : coherent ? "outdated" : "unhealthy",
|
|
61604
|
+
managedNames: managed.map(([name]) => name),
|
|
61605
|
+
managedFolders: [...uniqueFolders]
|
|
61606
|
+
};
|
|
61607
|
+
}
|
|
61608
|
+
async function directoryExists(path4) {
|
|
61609
|
+
try {
|
|
61610
|
+
return (await lstat2(path4)).isDirectory();
|
|
61611
|
+
} catch {
|
|
61612
|
+
return false;
|
|
61613
|
+
}
|
|
61614
|
+
}
|
|
61615
|
+
async function sourceSnapshot(dependencies) {
|
|
61616
|
+
return dependencies.resolveSource ? await dependencies.resolveSource() : await resolveAstraleSkillSource(dependencies.run);
|
|
61617
|
+
}
|
|
61618
|
+
function installerArgs(...args) {
|
|
61619
|
+
return ["--yes", SKILLS_INSTALLER_PACKAGE, ...args];
|
|
61620
|
+
}
|
|
61621
|
+
async function installSnapshot(snapshot2, execute, selectedAgents) {
|
|
61622
|
+
return await execute("npx", installerArgs("add", `${ASTRALE_CLI_SKILL_SOURCE}#${snapshot2.ref}`, "-g", "-y", "--skill", ...snapshot2.skills.map((skill) => skill.name), ...selectedAgents.length > 0 ? ["--agent", ...selectedAgents] : []));
|
|
61623
|
+
}
|
|
61624
|
+
async function selectedAgents(home, lockPath) {
|
|
61625
|
+
const { lock } = await readSkillLock(lockPath);
|
|
61626
|
+
const agents = new Set((lock?.lastSelectedAgents ?? []).filter((agent) => typeof agent === "string" && SAFE_NAME.test(agent)));
|
|
61627
|
+
agents.add("codex");
|
|
61628
|
+
if (await directoryExists(join7(home, ".claude")))
|
|
61629
|
+
agents.add("claude-code");
|
|
61630
|
+
return [...agents];
|
|
61631
|
+
}
|
|
61632
|
+
async function cleanManagedState(names, home, lockPath) {
|
|
61633
|
+
for (const name of names) {
|
|
61634
|
+
await rm3(join7(home, ".agents", "skills", name), { recursive: true, force: true });
|
|
61635
|
+
}
|
|
61636
|
+
const { lock, raw: raw2 } = await readSkillLock(lockPath);
|
|
61637
|
+
if (!lock) {
|
|
61638
|
+
if (raw2 !== null)
|
|
61639
|
+
await rm3(lockPath, { force: true });
|
|
61640
|
+
return;
|
|
61641
|
+
}
|
|
61642
|
+
for (const [name, entry] of Object.entries(lock.skills)) {
|
|
61643
|
+
if (sourceOwned(entry))
|
|
61644
|
+
delete lock.skills[name];
|
|
61645
|
+
}
|
|
61646
|
+
await writeSkillLock(lockPath, lock);
|
|
61647
|
+
}
|
|
61648
|
+
async function removeKnownAgentLinks(names, home) {
|
|
61649
|
+
const roots = await readdir3(home, { withFileTypes: true }).catch(() => []);
|
|
61650
|
+
for (const root of roots) {
|
|
61651
|
+
if (!root.isDirectory() || !root.name.startsWith(".") || root.name === ".agents")
|
|
61652
|
+
continue;
|
|
61653
|
+
for (const name of names) {
|
|
61654
|
+
const link = join7(home, root.name, "skills", name);
|
|
61655
|
+
try {
|
|
61656
|
+
const stat3 = await lstat2(link);
|
|
61657
|
+
if (!stat3.isSymbolicLink())
|
|
61658
|
+
continue;
|
|
61659
|
+
const target2 = await readlink(link);
|
|
61660
|
+
if (resolve3(dirname9(link), target2) === join7(home, ".agents", "skills", name)) {
|
|
61661
|
+
await rm3(link, { force: true });
|
|
61662
|
+
}
|
|
61663
|
+
} catch {}
|
|
61664
|
+
}
|
|
61665
|
+
}
|
|
61666
|
+
}
|
|
61667
|
+
async function pruneObsoleteEntries(snapshot2, home, lockPath, execute, knownRetired) {
|
|
61668
|
+
const expected = new Set(snapshot2.skills.map((skill) => skill.name));
|
|
61669
|
+
const { lock } = await readSkillLock(lockPath);
|
|
61670
|
+
const retiredEntries = Object.entries(lock?.skills ?? {}).filter(([, entry]) => {
|
|
61671
|
+
const folder = folderName(entry);
|
|
61672
|
+
return sourceOwned(entry) && folder !== null && !expected.has(folder);
|
|
61673
|
+
});
|
|
61674
|
+
const retired = [
|
|
61675
|
+
...new Set([
|
|
61676
|
+
...knownRetired,
|
|
61677
|
+
...retiredEntries.flatMap(([, entry]) => folderName(entry) ?? [])
|
|
61678
|
+
])
|
|
61679
|
+
];
|
|
61680
|
+
if (retired.length > 0) {
|
|
61681
|
+
const result = await execute("npx", installerArgs("remove", ...retired, "-g", "-y"));
|
|
61682
|
+
if (result.code !== 0)
|
|
61683
|
+
throw new Error(result.stderr || result.stdout || "skill removal failed");
|
|
61684
|
+
}
|
|
61685
|
+
const refreshed = await readSkillLock(lockPath);
|
|
61686
|
+
if (!refreshed.lock)
|
|
61687
|
+
return;
|
|
61688
|
+
let changed = false;
|
|
61689
|
+
for (const [name, entry] of Object.entries(refreshed.lock.skills)) {
|
|
61690
|
+
if (!sourceOwned(entry))
|
|
61691
|
+
continue;
|
|
61692
|
+
const folder = folderName(entry);
|
|
61693
|
+
if (folder === null || name !== folder || !expected.has(folder)) {
|
|
61694
|
+
delete refreshed.lock.skills[name];
|
|
61695
|
+
changed = true;
|
|
61696
|
+
}
|
|
61697
|
+
}
|
|
61698
|
+
if (changed)
|
|
61699
|
+
await writeSkillLock(lockPath, refreshed.lock);
|
|
61700
|
+
await removeKnownAgentLinks(retired, home);
|
|
61701
|
+
for (const name of retired) {
|
|
61702
|
+
await rm3(join7(home, ".agents", "skills", name), { recursive: true, force: true });
|
|
61703
|
+
}
|
|
61704
|
+
}
|
|
61705
|
+
async function captureAgentLinks(home, names) {
|
|
61706
|
+
const links = [];
|
|
61707
|
+
const roots = await readdir3(home, { withFileTypes: true }).catch(() => []);
|
|
61708
|
+
for (const root of roots) {
|
|
61709
|
+
if (!root.isDirectory() || !root.name.startsWith(".") || root.name === ".agents")
|
|
61710
|
+
continue;
|
|
61711
|
+
for (const name of names) {
|
|
61712
|
+
const path4 = join7(home, root.name, "skills", name);
|
|
61713
|
+
try {
|
|
61714
|
+
if ((await lstat2(path4)).isSymbolicLink()) {
|
|
61715
|
+
links.push({ root: root.name, name, target: await readlink(path4) });
|
|
61716
|
+
}
|
|
61717
|
+
} catch {}
|
|
61718
|
+
}
|
|
61719
|
+
}
|
|
61720
|
+
return links;
|
|
61721
|
+
}
|
|
61722
|
+
async function writeBackupManifest(backup) {
|
|
61723
|
+
const next = join7(backup.root, `${BACKUP_MANIFEST}.next`);
|
|
61724
|
+
await writeFile6(next, JSON.stringify(backup), { mode: 384 });
|
|
61725
|
+
await rename3(next, join7(backup.root, BACKUP_MANIFEST));
|
|
61313
61726
|
}
|
|
61727
|
+
async function captureBackup(home, lockPath, names) {
|
|
61728
|
+
const agentsRoot = join7(home, ".agents");
|
|
61729
|
+
await mkdir10(agentsRoot, { recursive: true });
|
|
61730
|
+
const root = await mkdtemp2(join7(agentsRoot, BACKUP_PREFIX));
|
|
61731
|
+
const copied = [];
|
|
61732
|
+
for (const name of names) {
|
|
61733
|
+
const source2 = join7(agentsRoot, "skills", name);
|
|
61734
|
+
try {
|
|
61735
|
+
await cp(source2, join7(root, name), { recursive: true, dereference: false });
|
|
61736
|
+
copied.push(name);
|
|
61737
|
+
} catch (error52) {
|
|
61738
|
+
if (error52.code !== "ENOENT")
|
|
61739
|
+
throw error52;
|
|
61740
|
+
}
|
|
61741
|
+
}
|
|
61742
|
+
const lock = await readSkillLock(lockPath);
|
|
61743
|
+
const backup = {
|
|
61744
|
+
root,
|
|
61745
|
+
names,
|
|
61746
|
+
copied,
|
|
61747
|
+
links: await captureAgentLinks(home, names),
|
|
61748
|
+
lockRaw: lock.raw,
|
|
61749
|
+
createdAt: new Date().toISOString(),
|
|
61750
|
+
phase: "prepared"
|
|
61751
|
+
};
|
|
61752
|
+
await writeBackupManifest(backup);
|
|
61753
|
+
return backup;
|
|
61754
|
+
}
|
|
61755
|
+
async function extendBackup(backup, home, names) {
|
|
61756
|
+
const additions = names.filter((name) => !backup.names.includes(name));
|
|
61757
|
+
if (additions.length === 0)
|
|
61758
|
+
return;
|
|
61759
|
+
for (const name of additions) {
|
|
61760
|
+
const source2 = join7(home, ".agents", "skills", name);
|
|
61761
|
+
try {
|
|
61762
|
+
await cp(source2, join7(backup.root, name), { recursive: true, dereference: false });
|
|
61763
|
+
backup.copied.push(name);
|
|
61764
|
+
} catch (error52) {
|
|
61765
|
+
if (error52.code !== "ENOENT")
|
|
61766
|
+
throw error52;
|
|
61767
|
+
}
|
|
61768
|
+
}
|
|
61769
|
+
backup.names.push(...additions);
|
|
61770
|
+
backup.links.push(...await captureAgentLinks(home, additions));
|
|
61771
|
+
await writeBackupManifest(backup);
|
|
61772
|
+
}
|
|
61773
|
+
async function restoreAgentLinks(backup, home, onlyForeign = false) {
|
|
61774
|
+
for (const link of backup.links) {
|
|
61775
|
+
const path4 = join7(home, link.root, "skills", link.name);
|
|
61776
|
+
const canonical = join7(home, ".agents", "skills", link.name);
|
|
61777
|
+
if (onlyForeign && resolve3(dirname9(path4), link.target) === canonical)
|
|
61778
|
+
continue;
|
|
61779
|
+
await mkdir10(dirname9(path4), { recursive: true });
|
|
61780
|
+
await rm3(path4, { recursive: true, force: true });
|
|
61781
|
+
await symlink(link.target, path4);
|
|
61782
|
+
}
|
|
61783
|
+
}
|
|
61784
|
+
async function restoreBackup(backup, home, lockPath) {
|
|
61785
|
+
for (const name of backup.names) {
|
|
61786
|
+
await rm3(join7(home, ".agents", "skills", name), { recursive: true, force: true });
|
|
61787
|
+
}
|
|
61788
|
+
await mkdir10(join7(home, ".agents", "skills"), { recursive: true });
|
|
61789
|
+
for (const name of backup.copied) {
|
|
61790
|
+
await cp(join7(backup.root, name), join7(home, ".agents", "skills", name), {
|
|
61791
|
+
recursive: true,
|
|
61792
|
+
dereference: false
|
|
61793
|
+
});
|
|
61794
|
+
}
|
|
61795
|
+
if (backup.lockRaw === null)
|
|
61796
|
+
await rm3(lockPath, { force: true });
|
|
61797
|
+
else {
|
|
61798
|
+
await mkdir10(dirname9(lockPath), { recursive: true });
|
|
61799
|
+
const next = `${lockPath}.restore`;
|
|
61800
|
+
await writeFile6(next, backup.lockRaw, { mode: 384 });
|
|
61801
|
+
await rename3(next, lockPath);
|
|
61802
|
+
}
|
|
61803
|
+
await removeKnownAgentLinks(backup.names, home);
|
|
61804
|
+
await restoreAgentLinks(backup, home);
|
|
61805
|
+
}
|
|
61806
|
+
async function recoverInterruptedBackup(home, lockPath) {
|
|
61807
|
+
const agentsRoot = join7(home, ".agents");
|
|
61808
|
+
const candidates = (await readdir3(agentsRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name.startsWith(BACKUP_PREFIX)).map((entry) => join7(agentsRoot, entry.name));
|
|
61809
|
+
const backups = [];
|
|
61810
|
+
for (const root of candidates) {
|
|
61811
|
+
try {
|
|
61812
|
+
const parsed = JSON.parse(await readFile11(join7(root, BACKUP_MANIFEST), "utf8"));
|
|
61813
|
+
if (Array.isArray(parsed.names) && parsed.names.every((name) => typeof name === "string" && SAFE_NAME.test(name)) && Array.isArray(parsed.copied) && parsed.copied.every((name) => typeof name === "string" && parsed.names.includes(name)) && Array.isArray(parsed.links) && (typeof parsed.lockRaw === "string" || parsed.lockRaw === null) && typeof parsed.createdAt === "string" && (parsed.phase === "prepared" || parsed.phase === "verified") && parsed.links.every((link) => link !== null && typeof link === "object" && typeof link.root === "string" && /^\.[^/]+$/u.test(link.root) && typeof link.name === "string" && parsed.names.includes(link.name) && typeof link.target === "string")) {
|
|
61814
|
+
const backup = { ...parsed, root };
|
|
61815
|
+
if (backup.phase === "verified")
|
|
61816
|
+
await rm3(root, { recursive: true, force: true });
|
|
61817
|
+
else
|
|
61818
|
+
backups.push(backup);
|
|
61819
|
+
} else {
|
|
61820
|
+
await rm3(root, { recursive: true, force: true });
|
|
61821
|
+
}
|
|
61822
|
+
} catch {
|
|
61823
|
+
await rm3(root, { recursive: true, force: true });
|
|
61824
|
+
}
|
|
61825
|
+
}
|
|
61826
|
+
if (backups.length === 0)
|
|
61827
|
+
return;
|
|
61828
|
+
backups.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
61829
|
+
await restoreBackup(backups[0], home, lockPath);
|
|
61830
|
+
await Promise.all(candidates.map((root) => rm3(root, { recursive: true, force: true })));
|
|
61831
|
+
}
|
|
61832
|
+
async function markBackupVerified(backup) {
|
|
61833
|
+
backup.phase = "verified";
|
|
61834
|
+
await writeBackupManifest(backup);
|
|
61835
|
+
}
|
|
61836
|
+
function skillFailure(error52) {
|
|
61837
|
+
const detail = error52 instanceof Error && error52.message.trim() ? `: ${error52.message}` : "";
|
|
61838
|
+
return new AstraleError("SKILL_UPDATE_FAILED", `Astrale could not install and verify its agent skills${detail}`, `Retry with \`${ASTRALE_SKILL_REPAIR_COMMAND}\`.`, error52 instanceof Error ? { cause: error52 } : undefined);
|
|
61839
|
+
}
|
|
61840
|
+
async function checkAstraleSkills(overrides = {}) {
|
|
61841
|
+
const dependencies = resolvedDependencies(overrides);
|
|
61842
|
+
try {
|
|
61843
|
+
const snapshot2 = await sourceSnapshot(dependencies);
|
|
61844
|
+
const inspection = await inspectAstraleSkills(snapshot2, dependencies.home, dependencies.lockPath);
|
|
61845
|
+
return {
|
|
61846
|
+
status: inspection.state === "current" ? "current" : inspection.state === "unhealthy" ? "repair-needed" : "update-available"
|
|
61847
|
+
};
|
|
61848
|
+
} catch (error52) {
|
|
61849
|
+
return { status: "unavailable", error: error52 instanceof Error ? error52.message : String(error52) };
|
|
61850
|
+
}
|
|
61851
|
+
}
|
|
61852
|
+
async function syncAstraleSkills(overrides = {}) {
|
|
61853
|
+
const dependencies = resolvedDependencies(overrides);
|
|
61854
|
+
let snapshot2;
|
|
61855
|
+
try {
|
|
61856
|
+
snapshot2 = await sourceSnapshot(dependencies);
|
|
61857
|
+
} catch (error52) {
|
|
61858
|
+
throw skillFailure(error52);
|
|
61859
|
+
}
|
|
61860
|
+
const lockFile = join7(process.env.ASTRALE_HOME ?? join7(dependencies.home, ".astrale"), "locks", "skills-update.lock");
|
|
61861
|
+
try {
|
|
61862
|
+
return await withFileLock2(lockFile, async () => {
|
|
61863
|
+
await recoverInterruptedBackup(dependencies.home, dependencies.lockPath);
|
|
61864
|
+
const initial = await inspectAstraleSkills(snapshot2, dependencies.home, dependencies.lockPath);
|
|
61865
|
+
if (initial.state === "current")
|
|
61866
|
+
return { status: "unchanged" };
|
|
61867
|
+
let expectedNames = snapshot2.skills.map((skill) => skill.name);
|
|
61868
|
+
let managedFolders = [...new Set([...expectedNames, ...initial.managedFolders])];
|
|
61869
|
+
let knownRetired = initial.managedFolders.filter((name) => !expectedNames.includes(name));
|
|
61870
|
+
const agents = await selectedAgents(dependencies.home, dependencies.lockPath);
|
|
61871
|
+
const backup = await captureBackup(dependencies.home, dependencies.lockPath, managedFolders);
|
|
61872
|
+
let lastError;
|
|
61873
|
+
try {
|
|
61874
|
+
for (let attempt = 0;attempt < 2; attempt++) {
|
|
61875
|
+
try {
|
|
61876
|
+
if (attempt === 1) {
|
|
61877
|
+
snapshot2 = await sourceSnapshot(dependencies);
|
|
61878
|
+
expectedNames = snapshot2.skills.map((skill) => skill.name);
|
|
61879
|
+
managedFolders = [...new Set([...managedFolders, ...expectedNames])];
|
|
61880
|
+
await extendBackup(backup, dependencies.home, managedFolders);
|
|
61881
|
+
knownRetired = [
|
|
61882
|
+
...new Set([
|
|
61883
|
+
...knownRetired,
|
|
61884
|
+
...managedFolders.filter((name) => !expectedNames.includes(name))
|
|
61885
|
+
])
|
|
61886
|
+
];
|
|
61887
|
+
}
|
|
61888
|
+
if (initial.state === "unhealthy" || attempt === 1) {
|
|
61889
|
+
await cleanManagedState(managedFolders, dependencies.home, dependencies.lockPath);
|
|
61890
|
+
}
|
|
61891
|
+
const installed = await installSnapshot(snapshot2, dependencies.run, agents);
|
|
61892
|
+
if (installed.code !== 0) {
|
|
61893
|
+
throw new Error(installed.stderr || installed.stdout || "skill installer failed");
|
|
61894
|
+
}
|
|
61895
|
+
await pruneObsoleteEntries(snapshot2, dependencies.home, dependencies.lockPath, dependencies.run, knownRetired);
|
|
61896
|
+
const verified = await inspectAstraleSkills(snapshot2, dependencies.home, dependencies.lockPath);
|
|
61897
|
+
if (verified.state !== "current") {
|
|
61898
|
+
throw new Error("installed Astrale skills did not pass verification");
|
|
61899
|
+
}
|
|
61900
|
+
const latest = await sourceSnapshot(dependencies);
|
|
61901
|
+
if (latest.revision !== snapshot2.revision) {
|
|
61902
|
+
throw new Error("Astrale skill source changed during installation");
|
|
61903
|
+
}
|
|
61904
|
+
await markBackupVerified(backup);
|
|
61905
|
+
await rm3(backup.root, { recursive: true, force: true });
|
|
61906
|
+
return {
|
|
61907
|
+
status: initial.state === "absent" ? "installed" : initial.state === "outdated" ? "updated" : "repaired"
|
|
61908
|
+
};
|
|
61909
|
+
} catch (error52) {
|
|
61910
|
+
lastError = error52;
|
|
61911
|
+
}
|
|
61912
|
+
}
|
|
61913
|
+
throw lastError;
|
|
61914
|
+
} catch (error52) {
|
|
61915
|
+
if (initial.state === "absent" || initial.state === "unhealthy") {
|
|
61916
|
+
await cleanManagedState(managedFolders, dependencies.home, dependencies.lockPath);
|
|
61917
|
+
await removeKnownAgentLinks(managedFolders, dependencies.home);
|
|
61918
|
+
await restoreAgentLinks(backup, dependencies.home, true);
|
|
61919
|
+
} else {
|
|
61920
|
+
await restoreBackup(backup, dependencies.home, dependencies.lockPath);
|
|
61921
|
+
}
|
|
61922
|
+
await rm3(backup.root, { recursive: true, force: true });
|
|
61923
|
+
throw skillFailure(error52);
|
|
61924
|
+
}
|
|
61925
|
+
});
|
|
61926
|
+
} catch (error52) {
|
|
61927
|
+
if (error52 instanceof AstraleError)
|
|
61928
|
+
throw error52;
|
|
61929
|
+
throw skillFailure(error52);
|
|
61930
|
+
}
|
|
61931
|
+
}
|
|
61932
|
+
var ASTRALE_CLI_SKILL_SOURCE = "astrale-os/cli", ASTRALE_SKILL_REPAIR_COMMAND = "astrale update --yes --no-deps", SKILLS_INSTALLER_PACKAGE = "skills@1.5.23", SKILL_INSTALL_HINT, SOURCE_REPOSITORY_URL = "https://github.com/astrale-os/cli.git", SAFE_NAME, BACKUP_PREFIX = ".astrale-skill-backup-", BACKUP_MANIFEST = ".manifest.json";
|
|
61933
|
+
var init_sync = __esm(() => {
|
|
61934
|
+
init_errors2();
|
|
61935
|
+
init_proc();
|
|
61936
|
+
init_lock();
|
|
61937
|
+
SKILL_INSTALL_HINT = ASTRALE_SKILL_REPAIR_COMMAND;
|
|
61938
|
+
SAFE_NAME = /^[a-z0-9][a-z0-9._-]*$/iu;
|
|
61939
|
+
});
|
|
61940
|
+
|
|
61941
|
+
// src/lib/skills.ts
|
|
61942
|
+
import { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync } from "node:fs";
|
|
61943
|
+
import { homedir as homedir3 } from "node:os";
|
|
61944
|
+
import { dirname as dirname10, join as join8 } from "node:path";
|
|
61314
61945
|
function skillSearchDirs() {
|
|
61315
61946
|
const dirs = [];
|
|
61316
61947
|
let cur = process.cwd();
|
|
61317
61948
|
for (;; ) {
|
|
61318
|
-
dirs.push(
|
|
61319
|
-
const parent =
|
|
61949
|
+
dirs.push(join8(cur, ".claude", "skills"));
|
|
61950
|
+
const parent = dirname10(cur);
|
|
61320
61951
|
if (parent === cur)
|
|
61321
61952
|
break;
|
|
61322
61953
|
cur = parent;
|
|
61323
61954
|
}
|
|
61324
|
-
dirs.push(
|
|
61955
|
+
dirs.push(join8(homedir3(), ".claude", "skills"));
|
|
61325
61956
|
return dirs;
|
|
61326
61957
|
}
|
|
61327
61958
|
function detectSkill(name) {
|
|
61328
61959
|
for (const dir of skillSearchDirs()) {
|
|
61329
|
-
const file2 =
|
|
61960
|
+
const file2 = join8(dir, name, "SKILL.md");
|
|
61330
61961
|
if (existsSync(file2))
|
|
61331
61962
|
return { installed: true, location: file2 };
|
|
61332
61963
|
}
|
|
@@ -61345,9 +61976,9 @@ function isSymlink(p) {
|
|
|
61345
61976
|
function findAgentsSkillsRoot(fromDir) {
|
|
61346
61977
|
let cur = fromDir;
|
|
61347
61978
|
for (;; ) {
|
|
61348
|
-
if (existsSync(
|
|
61979
|
+
if (existsSync(join8(cur, ".agents", "skills")))
|
|
61349
61980
|
return cur;
|
|
61350
|
-
const parent =
|
|
61981
|
+
const parent = dirname10(cur);
|
|
61351
61982
|
if (parent === cur)
|
|
61352
61983
|
return null;
|
|
61353
61984
|
cur = parent;
|
|
@@ -61357,7 +61988,7 @@ function skillsBridgeStatus(fromDir = process.cwd()) {
|
|
|
61357
61988
|
const root = findAgentsSkillsRoot(fromDir);
|
|
61358
61989
|
if (!root)
|
|
61359
61990
|
return { kind: "none" };
|
|
61360
|
-
const link =
|
|
61991
|
+
const link = join8(root, ".claude", "skills");
|
|
61361
61992
|
if (isSymlink(link)) {
|
|
61362
61993
|
try {
|
|
61363
61994
|
if (readlinkSync(link) === BRIDGE_TARGET)
|
|
@@ -61373,16 +62004,15 @@ function ensureSkillsBridge(fromDir = process.cwd()) {
|
|
|
61373
62004
|
const status = skillsBridgeStatus(fromDir);
|
|
61374
62005
|
if (status.kind !== "unbridged")
|
|
61375
62006
|
return status;
|
|
61376
|
-
mkdirSync(
|
|
62007
|
+
mkdirSync(dirname10(status.link), { recursive: true });
|
|
61377
62008
|
symlinkSync(BRIDGE_TARGET, status.link);
|
|
61378
62009
|
return { kind: "bridged", root: status.root };
|
|
61379
62010
|
}
|
|
61380
|
-
var
|
|
62011
|
+
var AGENT_BROWSER_SKILL = "agent-browser", BRIDGE_TARGET;
|
|
61381
62012
|
var init_skills = __esm(() => {
|
|
61382
62013
|
init_browser();
|
|
61383
|
-
|
|
61384
|
-
|
|
61385
|
-
BRIDGE_TARGET = join6("..", ".agents", "skills");
|
|
62014
|
+
init_sync();
|
|
62015
|
+
BRIDGE_TARGET = join8("..", ".agents", "skills");
|
|
61386
62016
|
});
|
|
61387
62017
|
|
|
61388
62018
|
// src/setup/steps/agent-browser.ts
|
|
@@ -61625,9 +62255,9 @@ var init_auth5 = __esm(() => {
|
|
|
61625
62255
|
|
|
61626
62256
|
// src/setup/steps/domain.ts
|
|
61627
62257
|
import { existsSync as existsSync2 } from "node:fs";
|
|
61628
|
-
import { join as
|
|
62258
|
+
import { join as join9 } from "node:path";
|
|
61629
62259
|
function hasDomainProject() {
|
|
61630
|
-
return existsSync2(
|
|
62260
|
+
return existsSync2(join9(process.cwd(), "astrale.config.ts"));
|
|
61631
62261
|
}
|
|
61632
62262
|
var FIX4 = "npx create-astrale-domain <name> --instance <slug>", domainStep;
|
|
61633
62263
|
var init_domain2 = __esm(() => {
|
|
@@ -70385,7 +71015,7 @@ function createClasses(definitions, validation) {
|
|
|
70385
71015
|
const declarations2 = new Map(definitions.filter((value2) => value2.ref.kind === "class").map((value2) => [Key.of(value2.ref), value2]));
|
|
70386
71016
|
const resolved = new Map;
|
|
70387
71017
|
const visiting = new Set;
|
|
70388
|
-
const
|
|
71018
|
+
const resolve4 = (ref) => {
|
|
70389
71019
|
const key = Key.of(ref);
|
|
70390
71020
|
const cached3 = resolved.get(key);
|
|
70391
71021
|
if (cached3 !== undefined)
|
|
@@ -70396,7 +71026,7 @@ function createClasses(definitions, validation) {
|
|
|
70396
71026
|
if (definition === undefined)
|
|
70397
71027
|
throw new TypeError(`Class ${key} is absent from Domain input.`);
|
|
70398
71028
|
visiting.add(key);
|
|
70399
|
-
const parents = definition.extends.map(
|
|
71029
|
+
const parents = definition.extends.map(resolve4);
|
|
70400
71030
|
if (parents.some((parent) => parent.kind !== definition.kind)) {
|
|
70401
71031
|
throw new TypeError(`Class ${key} extends a Class of another graph kind.`);
|
|
70402
71032
|
}
|
|
@@ -70510,7 +71140,7 @@ function createClasses(definitions, validation) {
|
|
|
70510
71140
|
return value2;
|
|
70511
71141
|
};
|
|
70512
71142
|
for (const definition of declarations2.values())
|
|
70513
|
-
|
|
71143
|
+
resolve4(definition.ref);
|
|
70514
71144
|
return resolved;
|
|
70515
71145
|
}
|
|
70516
71146
|
function uniquePolicyRefs(values) {
|
|
@@ -70981,7 +71611,7 @@ var init_program = __esm(() => {
|
|
|
70981
71611
|
});
|
|
70982
71612
|
|
|
70983
71613
|
// node_modules/.pnpm/@astrale-os+kernel-dsl@0.2.0-beta.13/node_modules/@astrale-os/kernel-dsl/dist/v1/schema/resolution/domain.js
|
|
70984
|
-
function
|
|
71614
|
+
function resolve4(schema) {
|
|
70985
71615
|
const cached3 = resolvedSchemas.get(schema);
|
|
70986
71616
|
if (cached3 !== undefined)
|
|
70987
71617
|
return cached3;
|
|
@@ -71106,7 +71736,7 @@ var init_schema5 = __esm(() => {
|
|
|
71106
71736
|
|
|
71107
71737
|
// node_modules/.pnpm/@astrale-os+kernel-dsl@0.2.0-beta.13/node_modules/@astrale-os/kernel-dsl/dist/v1/schema/compatibility/meaning.js
|
|
71108
71738
|
function compareDependencyMeaning(dependent, target2) {
|
|
71109
|
-
|
|
71739
|
+
resolve4(dependent);
|
|
71110
71740
|
const closure2 = knownDomainClosure(dependent) ?? [];
|
|
71111
71741
|
const source2 = closure2.find((candidate) => candidate.schema.origin === target2.origin)?.schema;
|
|
71112
71742
|
if (source2 === undefined) {
|
|
@@ -71132,7 +71762,7 @@ function compareMemberMeaning(source2, target2, roots) {
|
|
|
71132
71762
|
diagnostic2("DM_MEMBER_ROOT_INVALID", "/roots", "Member comparison roots must be one non-empty duplicate-free Key set.")
|
|
71133
71763
|
]);
|
|
71134
71764
|
}
|
|
71135
|
-
|
|
71765
|
+
resolve4(source2);
|
|
71136
71766
|
const closure2 = knownDomainClosure(source2) ?? [];
|
|
71137
71767
|
const schemas3 = new Map([
|
|
71138
71768
|
[source2.origin, source2],
|
|
@@ -71143,7 +71773,7 @@ function compareMemberMeaning(source2, target2, roots) {
|
|
|
71143
71773
|
return compareSourceMeanings(source2, target2, sourceMeanings);
|
|
71144
71774
|
}
|
|
71145
71775
|
function compareSourceMeanings(source2, target2, sourceMeanings) {
|
|
71146
|
-
|
|
71776
|
+
resolve4(target2);
|
|
71147
71777
|
const footprint = Object.freeze([...sourceMeanings.keys()].sort(compareUnicode));
|
|
71148
71778
|
const changes = [];
|
|
71149
71779
|
const comparisonCoordinates = [];
|
|
@@ -73942,12 +74572,12 @@ __export(exports_schema, {
|
|
|
73942
74572
|
encode: () => encodeDomainSchema,
|
|
73943
74573
|
header: () => header,
|
|
73944
74574
|
load: () => load2,
|
|
73945
|
-
resolve: () =>
|
|
74575
|
+
resolve: () => resolve5,
|
|
73946
74576
|
revision: () => revisionOfDomainSchema,
|
|
73947
74577
|
serialize: () => serializeDomainSchema,
|
|
73948
74578
|
values: () => exports_value_api
|
|
73949
74579
|
});
|
|
73950
|
-
var
|
|
74580
|
+
var resolve5;
|
|
73951
74581
|
var init_schema6 = __esm(() => {
|
|
73952
74582
|
init_domain7();
|
|
73953
74583
|
init_accept3();
|
|
@@ -73958,7 +74588,7 @@ var init_schema6 = __esm(() => {
|
|
|
73958
74588
|
init_compiled();
|
|
73959
74589
|
init_diagnostic2();
|
|
73960
74590
|
init_value_api();
|
|
73961
|
-
|
|
74591
|
+
resolve5 = resolve4;
|
|
73962
74592
|
});
|
|
73963
74593
|
|
|
73964
74594
|
// node_modules/.pnpm/@astrale-os+kernel-dsl@0.2.0-beta.13/node_modules/@astrale-os/kernel-dsl/dist/v1/bundle/diagnostic.js
|
|
@@ -75709,12 +76339,12 @@ var init_view_resolution = __esm(() => {
|
|
|
75709
76339
|
});
|
|
75710
76340
|
|
|
75711
76341
|
// node_modules/.pnpm/@astrale-os+kernel-core@0.9.0-beta.16/node_modules/@astrale-os/kernel-core/dist/schema/kernel/syscalls/descriptors/view.js
|
|
75712
|
-
var
|
|
76342
|
+
var resolve6;
|
|
75713
76343
|
var init_view5 = __esm(() => {
|
|
75714
76344
|
init_kernel();
|
|
75715
76345
|
init_view();
|
|
75716
76346
|
init_view_resolution();
|
|
75717
|
-
|
|
76347
|
+
resolve6 = methodSyscall(resolve, K.classes.View.static.methods.resolve);
|
|
75718
76348
|
});
|
|
75719
76349
|
|
|
75720
76350
|
// node_modules/.pnpm/@astrale-os+kernel-core@0.9.0-beta.16/node_modules/@astrale-os/kernel-core/dist/schema/kernel/syscalls/descriptors/index.js
|
|
@@ -78542,14 +79172,14 @@ async function boundedResponse(response2, maximum, signal) {
|
|
|
78542
79172
|
signal.removeEventListener("abort", abort);
|
|
78543
79173
|
reader.releaseLock();
|
|
78544
79174
|
}
|
|
78545
|
-
return
|
|
79175
|
+
return join10(chunks, total);
|
|
78546
79176
|
}
|
|
78547
79177
|
function requireExactResponse(response2, expected, label) {
|
|
78548
79178
|
if (response2.redirected || response2.url !== expected) {
|
|
78549
79179
|
throw new ClientError(`${label} did not return the exact requested URL.`);
|
|
78550
79180
|
}
|
|
78551
79181
|
}
|
|
78552
|
-
function
|
|
79182
|
+
function join10(parts, length) {
|
|
78553
79183
|
if (parts.length === 1)
|
|
78554
79184
|
return new Uint8Array(parts[0]);
|
|
78555
79185
|
const output3 = new Uint8Array(length);
|
|
@@ -79426,7 +80056,7 @@ function requestUrl(input) {
|
|
|
79426
80056
|
return new URL(input.url);
|
|
79427
80057
|
}
|
|
79428
80058
|
function fetchWithNode(url3, init, ca) {
|
|
79429
|
-
return new Promise((
|
|
80059
|
+
return new Promise((resolve7, reject) => {
|
|
79430
80060
|
const request = httpsRequest(url3, {
|
|
79431
80061
|
method: init?.method ?? "GET",
|
|
79432
80062
|
headers: headersInitToRecord(init?.headers),
|
|
@@ -79443,7 +80073,7 @@ function fetchWithNode(url3, init, ca) {
|
|
|
79443
80073
|
headers: responseHeaders(response2.headers)
|
|
79444
80074
|
});
|
|
79445
80075
|
Object.defineProperty(result, "url", { value: url3.toString() });
|
|
79446
|
-
|
|
80076
|
+
resolve7(result);
|
|
79447
80077
|
});
|
|
79448
80078
|
});
|
|
79449
80079
|
request.on("error", reject);
|
|
@@ -80705,49 +81335,65 @@ var init_instance3 = __esm(() => {
|
|
|
80705
81335
|
});
|
|
80706
81336
|
|
|
80707
81337
|
// src/setup/steps/skills.ts
|
|
80708
|
-
var FIX5,
|
|
81338
|
+
var FIX5, skillsStep;
|
|
80709
81339
|
var init_skills2 = __esm(() => {
|
|
80710
81340
|
init_log();
|
|
80711
81341
|
init_skills();
|
|
80712
81342
|
FIX5 = SKILL_INSTALL_HINT;
|
|
80713
81343
|
skillsStep = {
|
|
80714
81344
|
id: "skills",
|
|
80715
|
-
title: "
|
|
81345
|
+
title: "Astrale agent skills",
|
|
80716
81346
|
group: "equip",
|
|
80717
81347
|
async detect() {
|
|
80718
|
-
|
|
80719
|
-
|
|
81348
|
+
const result = await checkAstraleSkills();
|
|
81349
|
+
if (result.status === "current") {
|
|
81350
|
+
return { state: "satisfied", summary: "Astrale skills are installed and up to date" };
|
|
81351
|
+
}
|
|
81352
|
+
if (result.status === "unavailable") {
|
|
81353
|
+
return {
|
|
81354
|
+
state: "broken",
|
|
81355
|
+
summary: "Astrale skills could not be verified",
|
|
81356
|
+
detail: result.error,
|
|
81357
|
+
fixHint: FIX5
|
|
81358
|
+
};
|
|
80720
81359
|
}
|
|
80721
|
-
|
|
80722
|
-
|
|
81360
|
+
return {
|
|
81361
|
+
state: "gap",
|
|
81362
|
+
summary: result.status === "repair-needed" ? "Astrale skills need repair" : "Astrale skills need installation or update",
|
|
81363
|
+
fixHint: FIX5
|
|
81364
|
+
};
|
|
80723
81365
|
},
|
|
80724
81366
|
async ensure() {
|
|
80725
|
-
|
|
80726
|
-
|
|
80727
|
-
|
|
80728
|
-
|
|
80729
|
-
|
|
80730
|
-
|
|
80731
|
-
|
|
81367
|
+
log.step("Ensuring Astrale agent skills are current and healthy");
|
|
81368
|
+
try {
|
|
81369
|
+
const result = await syncAstraleSkills();
|
|
81370
|
+
if (result.status === "unchanged") {
|
|
81371
|
+
log.success("Astrale skills already up to date");
|
|
81372
|
+
return "unchanged";
|
|
81373
|
+
}
|
|
81374
|
+
if (result.status === "installed")
|
|
81375
|
+
log.success("Astrale skills installed");
|
|
81376
|
+
else if (result.status === "updated")
|
|
81377
|
+
log.success("Astrale skills updated");
|
|
81378
|
+
else if (result.status === "repaired")
|
|
81379
|
+
log.success("Astrale skills repaired and updated");
|
|
81380
|
+
return "fixed";
|
|
81381
|
+
} catch (error52) {
|
|
81382
|
+
log.warn(`Astrale skills could not be installed safely${error52 instanceof Error ? `: ${error52.message}` : ""}`);
|
|
81383
|
+
log.dim(` Retry: ${FIX5}`);
|
|
80732
81384
|
return "failed";
|
|
80733
81385
|
}
|
|
80734
|
-
if (bothInstalled()) {
|
|
80735
|
-
log.success("astrale-cli + astrale-domain skills installed");
|
|
80736
|
-
} else {
|
|
80737
|
-
log.success("astrale-cli skill installed");
|
|
80738
|
-
}
|
|
80739
|
-
return "fixed";
|
|
80740
81386
|
}
|
|
80741
81387
|
};
|
|
80742
81388
|
});
|
|
80743
81389
|
|
|
80744
81390
|
// src/setup/steps/skills-bridge.ts
|
|
80745
81391
|
import { existsSync as existsSync3, readdirSync } from "node:fs";
|
|
80746
|
-
import { join as
|
|
81392
|
+
import { join as join11 } from "node:path";
|
|
80747
81393
|
function countStaged(root) {
|
|
80748
81394
|
try {
|
|
80749
|
-
const dir =
|
|
80750
|
-
return readdirSync(dir).filter((e) => existsSync3(
|
|
81395
|
+
const dir = join11(root, ".agents", "skills");
|
|
81396
|
+
return readdirSync(dir).filter((e) => existsSync3(join11(dir, e, "SKILL.md"))).length;
|
|
80751
81397
|
} catch {
|
|
80752
81398
|
return 0;
|
|
80753
81399
|
}
|
|
@@ -80785,7 +81431,7 @@ var init_skills_bridge = __esm(() => {
|
|
|
80785
81431
|
}
|
|
80786
81432
|
const after = ensureSkillsBridge();
|
|
80787
81433
|
if (after.kind === "bridged") {
|
|
80788
|
-
log.success(`Workspace skills bridged — ${
|
|
81434
|
+
log.success(`Workspace skills bridged — ${join11(after.root, ".agents/skills")} → .claude/skills`);
|
|
80789
81435
|
return "fixed";
|
|
80790
81436
|
}
|
|
80791
81437
|
log.warn("Could not create the skills bridge — create it manually: ln -s ../.agents/skills .claude/skills");
|
|
@@ -81253,18 +81899,18 @@ Examples:
|
|
|
81253
81899
|
|
|
81254
81900
|
// src/lib/sdk-deps.ts
|
|
81255
81901
|
import { existsSync as existsSync4 } from "node:fs";
|
|
81256
|
-
import { join as
|
|
81902
|
+
import { join as join12 } from "node:path";
|
|
81257
81903
|
function inDomainProject(cwd = process.cwd()) {
|
|
81258
|
-
return existsSync4(
|
|
81904
|
+
return existsSync4(join12(cwd, "astrale.config.ts"));
|
|
81259
81905
|
}
|
|
81260
81906
|
function foreignPackageManager(cwd = process.cwd()) {
|
|
81261
|
-
if (existsSync4(
|
|
81907
|
+
if (existsSync4(join12(cwd, "pnpm-lock.yaml")))
|
|
81262
81908
|
return null;
|
|
81263
|
-
if (existsSync4(
|
|
81909
|
+
if (existsSync4(join12(cwd, "package-lock.json")))
|
|
81264
81910
|
return "npm";
|
|
81265
|
-
if (existsSync4(
|
|
81911
|
+
if (existsSync4(join12(cwd, "yarn.lock")))
|
|
81266
81912
|
return "yarn";
|
|
81267
|
-
if (existsSync4(
|
|
81913
|
+
if (existsSync4(join12(cwd, "bun.lockb")) || existsSync4(join12(cwd, "bun.lock")))
|
|
81268
81914
|
return "bun";
|
|
81269
81915
|
return null;
|
|
81270
81916
|
}
|
|
@@ -81317,15 +81963,30 @@ __export(exports_update, {
|
|
|
81317
81963
|
fetchNpmTargetVersion: () => fetchNpmTargetVersion
|
|
81318
81964
|
});
|
|
81319
81965
|
async function refreshSkills() {
|
|
81320
|
-
|
|
81321
|
-
|
|
81322
|
-
|
|
81323
|
-
|
|
81324
|
-
|
|
81325
|
-
|
|
81326
|
-
|
|
81327
|
-
|
|
81328
|
-
|
|
81966
|
+
log.step("Ensuring Astrale agent skills are current and healthy");
|
|
81967
|
+
const result = await syncAstraleSkills();
|
|
81968
|
+
if (result.status === "unchanged")
|
|
81969
|
+
log.success("Astrale skills already up to date");
|
|
81970
|
+
else if (result.status === "installed")
|
|
81971
|
+
log.success("Astrale skills installed");
|
|
81972
|
+
else if (result.status === "updated")
|
|
81973
|
+
log.success("Astrale skills updated");
|
|
81974
|
+
else if (result.status === "repaired")
|
|
81975
|
+
log.success("Astrale skills repaired and updated");
|
|
81976
|
+
return result;
|
|
81977
|
+
}
|
|
81978
|
+
function skillCheckStale(skills) {
|
|
81979
|
+
return skills.status === "update-available" || skills.status === "repair-needed";
|
|
81980
|
+
}
|
|
81981
|
+
function printSkillCheck(skills) {
|
|
81982
|
+
if (skills.status === "current")
|
|
81983
|
+
log.success("Astrale skills are up to date");
|
|
81984
|
+
else if (skills.status === "update-available")
|
|
81985
|
+
log.info("Astrale skills update available");
|
|
81986
|
+
else if (skills.status === "repair-needed")
|
|
81987
|
+
log.warn("Astrale skills need repair");
|
|
81988
|
+
else if (skills.status === "unavailable") {
|
|
81989
|
+
log.warn(`Could not verify Astrale skills${skills.error ? `: ${skills.error}` : ""}`);
|
|
81329
81990
|
}
|
|
81330
81991
|
}
|
|
81331
81992
|
async function refreshSdkDeps(check3, assumeYes = false) {
|
|
@@ -81452,7 +82113,7 @@ var init_update2 = __esm(() => {
|
|
|
81452
82113
|
default: DEFAULT_UPDATE_CHANNEL
|
|
81453
82114
|
},
|
|
81454
82115
|
{ flags: "--version <version>", description: "Update to an exact version tag" },
|
|
81455
|
-
{ flags: "--no-skills", description: "Skip
|
|
82116
|
+
{ flags: "--no-skills", description: "Skip ensuring the Astrale agent skills" },
|
|
81456
82117
|
{ flags: "--no-deps", description: "Skip checking @astrale-os SDK dependency versions" },
|
|
81457
82118
|
{
|
|
81458
82119
|
flags: "--yes",
|
|
@@ -81465,23 +82126,22 @@ Behavior:
|
|
|
81465
82126
|
Keeps three things current, in order. (1) The CLI binary: updates official
|
|
81466
82127
|
script installs only — if Astrale was installed by another package manager this
|
|
81467
82128
|
command refuses so that manager stays in charge; downloads are checksum-verified
|
|
81468
|
-
before the binary is replaced. (2) The agent skills:
|
|
81469
|
-
|
|
81470
|
-
|
|
81471
|
-
|
|
82129
|
+
before the binary is replaced. (2) The Astrale agent skills: installs every
|
|
82130
|
+
top-level skill published from astrale-os/cli main, updates healthy older
|
|
82131
|
+
installs, repairs inconsistent installs, and verifies the result before
|
|
82132
|
+
reporting success. (3) SDK deps: inside a pnpm domain
|
|
81472
82133
|
project, proposes any @astrale-os/* dependency with a newer release and, on
|
|
81473
82134
|
confirm, runs "pnpm update --latest --lockfile-only" (updates package.json AND
|
|
81474
82135
|
the lockfile, honoring your registry + supply-chain age policy; run "pnpm
|
|
81475
82136
|
install" to materialize).
|
|
81476
82137
|
|
|
81477
82138
|
The default release channel is beta; --channel overrides it for one run.
|
|
81478
|
-
--check is a dry run (binary + SDK deps; exit 10 if anything is available) and
|
|
82139
|
+
--check is a dry run (binary + skills + SDK deps; exit 10 if anything is available) and
|
|
81479
82140
|
never writes. With --json it emits a unified staleness report
|
|
81480
|
-
({ stale, cli, sdk }) for tooling
|
|
81481
|
-
along with an update). --yes applies all three non-interactively (no prompts) and
|
|
82141
|
+
({ stale, cli, skills, sdk }) for tooling. --yes applies all three non-interactively and
|
|
81482
82142
|
is resilient — a binary that can't self-update (package-managed) warns but never
|
|
81483
|
-
blocks the skills/deps steps;
|
|
81484
|
-
--no-skills / --no-deps skip those
|
|
82143
|
+
blocks the skills/deps steps; a skill failure fails the command rather than
|
|
82144
|
+
claiming a partial success. --no-skills / --no-deps explicitly skip those axes.
|
|
81485
82145
|
|
|
81486
82146
|
Examples:
|
|
81487
82147
|
$ astrale update
|
|
@@ -81496,8 +82156,14 @@ Examples:
|
|
|
81496
82156
|
try {
|
|
81497
82157
|
if (opts.check && isMachine(opts)) {
|
|
81498
82158
|
const cli = await cliStale(opts);
|
|
82159
|
+
const skills = opts.skills === false ? { status: "skipped" } : await checkAstraleSkills();
|
|
81499
82160
|
const sdk = await sdkStale();
|
|
81500
|
-
const report = {
|
|
82161
|
+
const report = {
|
|
82162
|
+
stale: cli.stale || skillCheckStale(skills) || sdk.stale,
|
|
82163
|
+
cli,
|
|
82164
|
+
skills,
|
|
82165
|
+
sdk
|
|
82166
|
+
};
|
|
81501
82167
|
output(report, opts);
|
|
81502
82168
|
if (report.stale)
|
|
81503
82169
|
process.exitCode = 10;
|
|
@@ -81536,22 +82202,32 @@ Examples:
|
|
|
81536
82202
|
throw error52;
|
|
81537
82203
|
log.warn(`CLI self-update skipped: ${error52.message}`);
|
|
81538
82204
|
}
|
|
81539
|
-
if (
|
|
81540
|
-
|
|
82205
|
+
if (opts.skills !== false) {
|
|
82206
|
+
if (opts.check) {
|
|
82207
|
+
const skills = await checkAstraleSkills();
|
|
82208
|
+
printSkillCheck(skills);
|
|
82209
|
+
if (skillCheckStale(skills))
|
|
82210
|
+
anyAvailable = true;
|
|
82211
|
+
} else {
|
|
82212
|
+
await refreshSkills();
|
|
82213
|
+
}
|
|
82214
|
+
} else if (!opts.check) {
|
|
82215
|
+
log.dim(" Astrale skills skipped (--no-skills)");
|
|
82216
|
+
}
|
|
81541
82217
|
if (opts.deps !== false && await refreshSdkDeps(opts.check === true, opts.yes === true)) {
|
|
81542
82218
|
anyAvailable = true;
|
|
81543
82219
|
}
|
|
81544
82220
|
if (opts.check && anyAvailable)
|
|
81545
82221
|
process.exitCode = 10;
|
|
81546
82222
|
} catch (e) {
|
|
81547
|
-
fatal(e);
|
|
82223
|
+
fatal(e, opts);
|
|
81548
82224
|
}
|
|
81549
82225
|
}
|
|
81550
82226
|
};
|
|
81551
82227
|
});
|
|
81552
82228
|
|
|
81553
82229
|
// src/lib/binary.ts
|
|
81554
|
-
import { writeFile as
|
|
82230
|
+
import { writeFile as writeFile7 } from "node:fs/promises";
|
|
81555
82231
|
async function readBinaryBody(body) {
|
|
81556
82232
|
if (body instanceof Uint8Array)
|
|
81557
82233
|
return new Uint8Array(body);
|
|
@@ -81603,7 +82279,7 @@ function jsonEnvelope(resp, bytes) {
|
|
|
81603
82279
|
async function presentBinary(resp, opts, io) {
|
|
81604
82280
|
const bytes = await readBinaryBody(resp.body);
|
|
81605
82281
|
if (io?.outFile) {
|
|
81606
|
-
await
|
|
82282
|
+
await writeFile7(io.outFile, bytes);
|
|
81607
82283
|
process.stderr.write(source_default.dim(` wrote ${humanSize(bytes.length)} (${resp.mediaType}) → ${io.outFile}
|
|
81608
82284
|
`));
|
|
81609
82285
|
return;
|
|
@@ -81963,7 +82639,7 @@ var init_get = __esm(() => {
|
|
|
81963
82639
|
description: "Get one canonical node by Path or ID",
|
|
81964
82640
|
afterHelpText: `
|
|
81965
82641
|
Behavior:
|
|
81966
|
-
Resolves one exact Kernel
|
|
82642
|
+
Resolves one exact Kernel Path and prints the canonical Node
|
|
81967
82643
|
{ id, class, props }. Missing and authorization-masked nodes remain
|
|
81968
82644
|
intentionally indistinguishable. @self is expanded before dispatch.
|
|
81969
82645
|
|
|
@@ -82114,7 +82790,7 @@ __export(exports_mutate, {
|
|
|
82114
82790
|
default: () => mutate_default,
|
|
82115
82791
|
mutateCommand: () => mutateCommand
|
|
82116
82792
|
});
|
|
82117
|
-
import { readFile as
|
|
82793
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
82118
82794
|
async function mutateCommand(opts) {
|
|
82119
82795
|
let mutation;
|
|
82120
82796
|
try {
|
|
@@ -82147,7 +82823,7 @@ async function readDocument(opts) {
|
|
|
82147
82823
|
if (opts.file !== undefined) {
|
|
82148
82824
|
let raw3;
|
|
82149
82825
|
try {
|
|
82150
|
-
raw3 = await
|
|
82826
|
+
raw3 = await readFile12(opts.file, "utf8");
|
|
82151
82827
|
} catch (error52) {
|
|
82152
82828
|
throw new AstraleError("FILE_READ_FAILED", `Cannot read --file ${opts.file}.`, undefined, {
|
|
82153
82829
|
cause: error52
|
|
@@ -82231,7 +82907,7 @@ __export(exports_query, {
|
|
|
82231
82907
|
default: () => query_default,
|
|
82232
82908
|
queryCommand: () => queryCommand
|
|
82233
82909
|
});
|
|
82234
|
-
import { readFile as
|
|
82910
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
82235
82911
|
async function queryCommand(sources2, opts) {
|
|
82236
82912
|
let input;
|
|
82237
82913
|
try {
|
|
@@ -82282,7 +82958,7 @@ async function readAst(opts) {
|
|
|
82282
82958
|
return;
|
|
82283
82959
|
let raw2;
|
|
82284
82960
|
try {
|
|
82285
|
-
raw2 = await
|
|
82961
|
+
raw2 = await readFile13(opts.file, "utf8");
|
|
82286
82962
|
} catch (error52) {
|
|
82287
82963
|
throw new AstraleError("FILE_READ_FAILED", `Cannot read --file ${opts.file}.`, undefined, {
|
|
82288
82964
|
cause: error52
|
|
@@ -82608,7 +83284,7 @@ async function runOnce(opts) {
|
|
|
82608
83284
|
}
|
|
82609
83285
|
async function followLogs(opts, dependencies = {
|
|
82610
83286
|
run: runKernelCommand,
|
|
82611
|
-
pause: (milliseconds) => new Promise((
|
|
83287
|
+
pause: (milliseconds) => new Promise((resolve7) => setTimeout(resolve7, milliseconds))
|
|
82612
83288
|
}) {
|
|
82613
83289
|
validateLogsOpts(opts);
|
|
82614
83290
|
await dependencies.run({
|
|
@@ -82776,11 +83452,8 @@ Behavior:
|
|
|
82776
83452
|
Calls the public Kernel journal syscall and emits its { records, cursor }
|
|
82777
83453
|
page. Topic selection is exact or prefix-based; cursors and timestamps are
|
|
82778
83454
|
opaque strings owned by the journal backend. --follow reuses one Client Session
|
|
82779
|
-
and advances only with the returned cursor.
|
|
82780
|
-
one complete admitted record per line; YAML follow is unsupported.
|
|
82781
|
-
|
|
82782
|
-
Historical event-glob lowering and the application-specific services-domain
|
|
82783
|
-
log buffer are not part of the Kernel V2 journal contract.
|
|
83455
|
+
and advances only with the returned cursor. With --json, follow output is
|
|
83456
|
+
NDJSON with one complete admitted record per line; YAML follow is unsupported.
|
|
82784
83457
|
|
|
82785
83458
|
Examples:
|
|
82786
83459
|
$ astrale logs -i staging --limit 50
|
|
@@ -82814,10 +83487,10 @@ Examples:
|
|
|
82814
83487
|
// src/lib/port.ts
|
|
82815
83488
|
import net from "node:net";
|
|
82816
83489
|
function portFree(port, host = LOOPBACK) {
|
|
82817
|
-
return new Promise((
|
|
83490
|
+
return new Promise((resolve7) => {
|
|
82818
83491
|
const srv = net.createServer();
|
|
82819
|
-
srv.once("error", () =>
|
|
82820
|
-
srv.once("listening", () => srv.close(() =>
|
|
83492
|
+
srv.once("error", () => resolve7(false));
|
|
83493
|
+
srv.once("listening", () => srv.close(() => resolve7(true)));
|
|
82821
83494
|
srv.listen({ port, host, exclusive: true });
|
|
82822
83495
|
});
|
|
82823
83496
|
}
|
|
@@ -82859,10 +83532,10 @@ var init_external_open_origins = __esm(() => {
|
|
|
82859
83532
|
|
|
82860
83533
|
// src/lib/view/session.ts
|
|
82861
83534
|
import { closeSync, fchmodSync, openSync } from "node:fs";
|
|
82862
|
-
import { chmod as chmod3, mkdir as
|
|
82863
|
-
import { join as
|
|
83535
|
+
import { chmod as chmod3, mkdir as mkdir11, readdir as readdir4, readFile as readFile14, rm as rm4 } from "node:fs/promises";
|
|
83536
|
+
import { join as join13 } from "node:path";
|
|
82864
83537
|
async function ensureViewDirectory(directory = VIEW_DIR) {
|
|
82865
|
-
await
|
|
83538
|
+
await mkdir11(directory, { recursive: true, mode: 448 });
|
|
82866
83539
|
await chmod3(directory, 448);
|
|
82867
83540
|
}
|
|
82868
83541
|
async function saveRecord(record12, directory = VIEW_DIR) {
|
|
@@ -82888,9 +83561,9 @@ async function openSessionLog(id, directory = VIEW_DIR) {
|
|
|
82888
83561
|
}
|
|
82889
83562
|
async function removeSessionFiles(id, directory = VIEW_DIR) {
|
|
82890
83563
|
await Promise.all([
|
|
82891
|
-
|
|
82892
|
-
|
|
82893
|
-
|
|
83564
|
+
rm4(recordPath(id, directory), { force: true }),
|
|
83565
|
+
rm4(configPath(id, directory), { force: true }),
|
|
83566
|
+
rm4(logPath(id, directory), { force: true })
|
|
82894
83567
|
]);
|
|
82895
83568
|
}
|
|
82896
83569
|
function isAlive(pid) {
|
|
@@ -82904,7 +83577,7 @@ function isAlive(pid) {
|
|
|
82904
83577
|
async function listSessions() {
|
|
82905
83578
|
let entries;
|
|
82906
83579
|
try {
|
|
82907
|
-
entries = await
|
|
83580
|
+
entries = await readdir4(VIEW_DIR);
|
|
82908
83581
|
} catch {
|
|
82909
83582
|
return [];
|
|
82910
83583
|
}
|
|
@@ -82914,7 +83587,7 @@ async function listSessions() {
|
|
|
82914
83587
|
continue;
|
|
82915
83588
|
let record12;
|
|
82916
83589
|
try {
|
|
82917
|
-
record12 = JSON.parse(await
|
|
83590
|
+
record12 = JSON.parse(await readFile14(join13(VIEW_DIR, entry2), "utf8"));
|
|
82918
83591
|
} catch {
|
|
82919
83592
|
continue;
|
|
82920
83593
|
}
|
|
@@ -82932,7 +83605,7 @@ async function closeSession(record12) {
|
|
|
82932
83605
|
} catch {}
|
|
82933
83606
|
const deadline = Date.now() + CLOSE_GRACE_MS;
|
|
82934
83607
|
while (isAlive(record12.pid) && Date.now() < deadline) {
|
|
82935
|
-
await new Promise((
|
|
83608
|
+
await new Promise((resolve7) => setTimeout(resolve7, 100));
|
|
82936
83609
|
}
|
|
82937
83610
|
if (isAlive(record12.pid)) {
|
|
82938
83611
|
try {
|
|
@@ -82942,14 +83615,14 @@ async function closeSession(record12) {
|
|
|
82942
83615
|
}
|
|
82943
83616
|
await removeSessionFiles(record12.id);
|
|
82944
83617
|
}
|
|
82945
|
-
var VIEW_DIR, recordPath = (id, directory = VIEW_DIR) =>
|
|
83618
|
+
var VIEW_DIR, recordPath = (id, directory = VIEW_DIR) => join13(directory, `${id}.json`), logPath = (id, directory = VIEW_DIR) => join13(directory, `${id}.log`), configPath = (id, directory = VIEW_DIR) => join13(directory, `${id}.config.json`), CLOSE_GRACE_MS = 2000;
|
|
82946
83619
|
var init_session5 = __esm(() => {
|
|
82947
83620
|
init_state();
|
|
82948
|
-
VIEW_DIR =
|
|
83621
|
+
VIEW_DIR = join13(paths2.home, "view");
|
|
82949
83622
|
});
|
|
82950
83623
|
|
|
82951
83624
|
// src/lib/view/port-allocation.ts
|
|
82952
|
-
import { join as
|
|
83625
|
+
import { join as join14 } from "node:path";
|
|
82953
83626
|
function withViewPortAllocationLock(fn, lockPath = VIEW_PORT_LOCK) {
|
|
82954
83627
|
return withFileLock(lockPath, fn);
|
|
82955
83628
|
}
|
|
@@ -82957,7 +83630,7 @@ var VIEW_PORT_LOCK;
|
|
|
82957
83630
|
var init_port_allocation = __esm(() => {
|
|
82958
83631
|
init_state();
|
|
82959
83632
|
init_session5();
|
|
82960
|
-
VIEW_PORT_LOCK =
|
|
83633
|
+
VIEW_PORT_LOCK = join14(VIEW_DIR, "ports.lock");
|
|
82961
83634
|
});
|
|
82962
83635
|
|
|
82963
83636
|
// src/lib/view/resolve.ts
|
|
@@ -83027,33 +83700,33 @@ var init_resolve2 = __esm(() => {
|
|
|
83027
83700
|
// src/lib/view/assets.ts
|
|
83028
83701
|
import { existsSync as existsSync5, statSync } from "node:fs";
|
|
83029
83702
|
import { copyFile as copyFile2 } from "node:fs/promises";
|
|
83030
|
-
import { dirname as
|
|
83703
|
+
import { dirname as dirname11, join as join15 } from "node:path";
|
|
83031
83704
|
import { fileURLToPath } from "node:url";
|
|
83032
83705
|
function viewerDistDir(moduleUrl = import.meta.url, entry2 = process.argv[1] ?? ".", executable = process.execPath) {
|
|
83033
83706
|
const override = process.env.ASTRALE_VIEWER_DIR;
|
|
83034
83707
|
if (override)
|
|
83035
83708
|
return override;
|
|
83036
|
-
const moduleDirectory =
|
|
83037
|
-
const published =
|
|
83038
|
-
const source2 =
|
|
83039
|
-
const legacy =
|
|
83040
|
-
const standalone = entry2.startsWith("/$bunfs/") ?
|
|
83709
|
+
const moduleDirectory = dirname11(fileURLToPath(moduleUrl));
|
|
83710
|
+
const published = join15(moduleDirectory, "..", "viewer", "dist");
|
|
83711
|
+
const source2 = join15(moduleDirectory, "..", "..", "..", "viewer", "dist");
|
|
83712
|
+
const legacy = join15(dirname11(entry2), "..", "viewer", "dist");
|
|
83713
|
+
const standalone = entry2.startsWith("/$bunfs/") ? join15(dirname11(executable), "viewer", "dist") : undefined;
|
|
83041
83714
|
const complete = [standalone, published, source2, legacy].find((candidate2) => candidate2 !== undefined && hasViewerBundle(candidate2));
|
|
83042
83715
|
if (complete)
|
|
83043
83716
|
return complete;
|
|
83044
|
-
if (hasViewerSource(
|
|
83717
|
+
if (hasViewerSource(join15(source2, "..")))
|
|
83045
83718
|
return source2;
|
|
83046
83719
|
return published;
|
|
83047
83720
|
}
|
|
83048
83721
|
async function ensureViewerAssets(moduleUrl = import.meta.url, entry2 = process.argv[1] ?? ".") {
|
|
83049
83722
|
const dist = viewerDistDir(moduleUrl, entry2);
|
|
83050
|
-
const srcDir =
|
|
83723
|
+
const srcDir = join15(dist, "..");
|
|
83051
83724
|
if (hasViewerBundle(dist) && !viewerSourceIsNewer(srcDir, dist))
|
|
83052
83725
|
return dist;
|
|
83053
83726
|
const bun = globalThis.Bun;
|
|
83054
83727
|
if (bun && hasViewerSource(srcDir)) {
|
|
83055
83728
|
const result = await bun.build({
|
|
83056
|
-
entrypoints: [
|
|
83729
|
+
entrypoints: [join15(srcDir, "main.ts")],
|
|
83057
83730
|
outdir: dist,
|
|
83058
83731
|
target: "browser",
|
|
83059
83732
|
minify: false
|
|
@@ -83061,32 +83734,32 @@ async function ensureViewerAssets(moduleUrl = import.meta.url, entry2 = process.
|
|
|
83061
83734
|
if (!result.success)
|
|
83062
83735
|
throw new Error(`viewer build failed: ${result.logs.join(`
|
|
83063
83736
|
`)}`);
|
|
83064
|
-
await copyFile2(
|
|
83737
|
+
await copyFile2(join15(srcDir, "index.html"), join15(dist, "index.html"));
|
|
83065
83738
|
return dist;
|
|
83066
83739
|
}
|
|
83067
83740
|
throw new Error(`viewer bundle missing at ${dist} — reinstall the CLI (or run \`bun scripts/build.ts\` in a dev checkout)`);
|
|
83068
83741
|
}
|
|
83069
83742
|
function hasViewerBundle(directory) {
|
|
83070
|
-
return existsSync5(
|
|
83743
|
+
return existsSync5(join15(directory, "main.js")) && existsSync5(join15(directory, "index.html"));
|
|
83071
83744
|
}
|
|
83072
83745
|
function hasViewerSource(directory) {
|
|
83073
|
-
return existsSync5(
|
|
83746
|
+
return existsSync5(join15(directory, "main.ts")) && existsSync5(join15(directory, "index.html"));
|
|
83074
83747
|
}
|
|
83075
83748
|
function viewerSourceIsNewer(source2, dist) {
|
|
83076
83749
|
if (!hasViewerSource(source2))
|
|
83077
83750
|
return false;
|
|
83078
83751
|
if (!hasViewerBundle(dist))
|
|
83079
83752
|
return true;
|
|
83080
|
-
const newestSource = Math.max(statSync(
|
|
83081
|
-
const oldestOutput = Math.min(statSync(
|
|
83753
|
+
const newestSource = Math.max(statSync(join15(source2, "main.ts")).mtimeMs, statSync(join15(source2, "index.html")).mtimeMs);
|
|
83754
|
+
const oldestOutput = Math.min(statSync(join15(dist, "main.js")).mtimeMs, statSync(join15(dist, "index.html")).mtimeMs);
|
|
83082
83755
|
return newestSource > oldestOutput;
|
|
83083
83756
|
}
|
|
83084
83757
|
var init_assets = () => {};
|
|
83085
83758
|
|
|
83086
83759
|
// src/lib/view/server.ts
|
|
83087
|
-
import { readFile as
|
|
83760
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
83088
83761
|
import { createServer } from "node:http";
|
|
83089
|
-
import { join as
|
|
83762
|
+
import { join as join16 } from "node:path";
|
|
83090
83763
|
import { Readable } from "node:stream";
|
|
83091
83764
|
function startViewServer(config2) {
|
|
83092
83765
|
const { session: session3, proxy } = config2;
|
|
@@ -83134,11 +83807,11 @@ function startViewServer(config2) {
|
|
|
83134
83807
|
return;
|
|
83135
83808
|
}
|
|
83136
83809
|
if (sub === "/" || sub === "/index.html") {
|
|
83137
|
-
await serveAsset(res,
|
|
83810
|
+
await serveAsset(res, join16(hostDir, "index.html"), "text/html; charset=utf-8");
|
|
83138
83811
|
return;
|
|
83139
83812
|
}
|
|
83140
83813
|
if (sub === "/main.js") {
|
|
83141
|
-
await serveAsset(res,
|
|
83814
|
+
await serveAsset(res, join16(hostDir, "main.js"), "text/javascript; charset=utf-8");
|
|
83142
83815
|
return;
|
|
83143
83816
|
}
|
|
83144
83817
|
if (sub === "/config.json" && req.method === "GET") {
|
|
@@ -83258,7 +83931,7 @@ function json3(res, code, body) {
|
|
|
83258
83931
|
}
|
|
83259
83932
|
async function serveAsset(res, file2, contentType) {
|
|
83260
83933
|
try {
|
|
83261
|
-
const content = await
|
|
83934
|
+
const content = await readFile15(file2);
|
|
83262
83935
|
res.writeHead(200, { "content-type": contentType, "cache-control": "no-store" });
|
|
83263
83936
|
res.end(content);
|
|
83264
83937
|
} catch {
|
|
@@ -83350,7 +84023,7 @@ var SETTLE_TIMEOUT_MS = 8000, QUIET_WINDOW_MS = 750, POLL_MS = 250, realClock, d
|
|
|
83350
84023
|
var init_snapshot = __esm(() => {
|
|
83351
84024
|
realClock = {
|
|
83352
84025
|
now: Date.now,
|
|
83353
|
-
sleep: (ms) => new Promise((
|
|
84026
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))
|
|
83354
84027
|
};
|
|
83355
84028
|
defaultPolicy = {
|
|
83356
84029
|
timeoutMs: SETTLE_TIMEOUT_MS,
|
|
@@ -83374,8 +84047,8 @@ __export(exports_view3, {
|
|
|
83374
84047
|
});
|
|
83375
84048
|
import { randomBytes } from "node:crypto";
|
|
83376
84049
|
import { closeSync as closeSync2, existsSync as existsSync6, statSync as statSync2 } from "node:fs";
|
|
83377
|
-
import { readdir as
|
|
83378
|
-
import { dirname as
|
|
84050
|
+
import { readdir as readdir5, readFile as readFile16 } from "node:fs/promises";
|
|
84051
|
+
import { dirname as dirname12, join as join17 } from "node:path";
|
|
83379
84052
|
async function resolveSession(spec, opts) {
|
|
83380
84053
|
rejectUnrepresentableOverrides(opts);
|
|
83381
84054
|
const parsed = parseViewSpec(spec);
|
|
@@ -83430,7 +84103,7 @@ async function resolveServeRuntime(environment = {}) {
|
|
|
83430
84103
|
if (node4 && entry2?.endsWith(".js") && exists(entry2))
|
|
83431
84104
|
return { file: node4, args: [entry2] };
|
|
83432
84105
|
if (node4 && entry2?.endsWith(".ts")) {
|
|
83433
|
-
const dist =
|
|
84106
|
+
const dist = join17(dirname12(entry2), "..", "dist", "astrale.js");
|
|
83434
84107
|
await ensureDevDist(entry2, dist);
|
|
83435
84108
|
if (exists(dist))
|
|
83436
84109
|
return { file: node4, args: [dist] };
|
|
@@ -83456,8 +84129,8 @@ async function findOnPath(name) {
|
|
|
83456
84129
|
async function ensureDevDist(entry2, dist) {
|
|
83457
84130
|
if (!await devDistIsStale(entry2, dist))
|
|
83458
84131
|
return;
|
|
83459
|
-
const projectDir =
|
|
83460
|
-
const buildScript =
|
|
84132
|
+
const projectDir = join17(dirname12(entry2), "..");
|
|
84133
|
+
const buildScript = join17(projectDir, "scripts", "build.ts");
|
|
83461
84134
|
const bun = await findOnPath("bun");
|
|
83462
84135
|
if (!bun || !existsSync6(buildScript))
|
|
83463
84136
|
return;
|
|
@@ -83473,13 +84146,13 @@ async function ensureDevDist(entry2, dist) {
|
|
|
83473
84146
|
async function devDistIsStale(entry2, dist) {
|
|
83474
84147
|
if (!existsSync6(dist))
|
|
83475
84148
|
return true;
|
|
83476
|
-
const projectDir =
|
|
84149
|
+
const projectDir = join17(dirname12(entry2), "..");
|
|
83477
84150
|
const builtAt = statSync2(dist).mtimeMs;
|
|
83478
|
-
const directories = [
|
|
84151
|
+
const directories = [join17(projectDir, "src"), join17(projectDir, "bin"), join17(projectDir, "vendor")];
|
|
83479
84152
|
const files = [
|
|
83480
|
-
|
|
83481
|
-
|
|
83482
|
-
|
|
84153
|
+
join17(projectDir, "scripts", "build.ts"),
|
|
84154
|
+
join17(projectDir, "package.json"),
|
|
84155
|
+
join17(projectDir, "pnpm-lock.yaml")
|
|
83483
84156
|
];
|
|
83484
84157
|
for (const directory of directories) {
|
|
83485
84158
|
if (existsSync6(directory) && await newerThan(directory, builtAt))
|
|
@@ -83488,11 +84161,11 @@ async function devDistIsStale(entry2, dist) {
|
|
|
83488
84161
|
return files.some((file2) => existsSync6(file2) && statSync2(file2).mtimeMs > builtAt);
|
|
83489
84162
|
}
|
|
83490
84163
|
async function newerThan(dir, mtimeMs) {
|
|
83491
|
-
const entries = await
|
|
84164
|
+
const entries = await readdir5(dir, { withFileTypes: true, recursive: true });
|
|
83492
84165
|
for (const item of entries) {
|
|
83493
84166
|
if (!item.isFile())
|
|
83494
84167
|
continue;
|
|
83495
|
-
if (statSync2(
|
|
84168
|
+
if (statSync2(join17(item.parentPath, item.name)).mtimeMs > mtimeMs)
|
|
83496
84169
|
return true;
|
|
83497
84170
|
}
|
|
83498
84171
|
return false;
|
|
@@ -83570,7 +84243,7 @@ async function startSessionLocked(view2, opts, kernelTarget, activeInstance, def
|
|
|
83570
84243
|
break;
|
|
83571
84244
|
await sleep2(POLL_MS2);
|
|
83572
84245
|
}
|
|
83573
|
-
const tail = await
|
|
84246
|
+
const tail = await readFile16(logPath(id), "utf8").catch(() => "");
|
|
83574
84247
|
await closeSession(live);
|
|
83575
84248
|
throw new Error(`View session server did not come up.${tail ? `
|
|
83576
84249
|
--- server log ---
|
|
@@ -83705,7 +84378,7 @@ async function sessionsCommand(opts) {
|
|
|
83705
84378
|
console.log(`${source_default.bold(s.id)} /:${s.view.route.key} target ${s.view.target} ${source_default.dim(s.pageUrl)}`);
|
|
83706
84379
|
}
|
|
83707
84380
|
}
|
|
83708
|
-
var VIEW_PORT_BASE = 4419, VIEW_PORT_SPAN = 20, IDLE_MS, READY_TIMEOUT_MS = 8000, STATE_TIMEOUT_MS = 25000, POLL_MS2 = 250, VIEW_PROFILE, sleep2 = (ms) => new Promise((
|
|
84381
|
+
var VIEW_PORT_BASE = 4419, VIEW_PORT_SPAN = 20, IDLE_MS, READY_TIMEOUT_MS = 8000, STATE_TIMEOUT_MS = 25000, POLL_MS2 = 250, VIEW_PROFILE, sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)), view_default;
|
|
83709
84382
|
var init_view7 = __esm(() => {
|
|
83710
84383
|
init_source();
|
|
83711
84384
|
init_connection2();
|
|
@@ -83844,7 +84517,7 @@ Examples:
|
|
|
83844
84517
|
if (state2?.state === "failed") {
|
|
83845
84518
|
await reportOpened(record12, state2, mode, opts);
|
|
83846
84519
|
if (opts.debug) {
|
|
83847
|
-
const tail = await
|
|
84520
|
+
const tail = await readFile16(logPath(record12.id), "utf8").catch(() => "");
|
|
83848
84521
|
if (tail)
|
|
83849
84522
|
console.error(`--- server log ---
|
|
83850
84523
|
${tail.slice(-3000)}`);
|
|
@@ -83966,7 +84639,7 @@ function reportConnected(session3, machine, opts) {
|
|
|
83966
84639
|
console.log(` agent-browser ${drive} open ${session3.url}`);
|
|
83967
84640
|
console.log(` agent-browser ${drive} click @e3`);
|
|
83968
84641
|
}
|
|
83969
|
-
var LOGIN_TIMEOUT_MS = 180000, POLL_INTERVAL_MS = 2500, sleep3 = (ms) => new Promise((
|
|
84642
|
+
var LOGIN_TIMEOUT_MS = 180000, POLL_INTERVAL_MS = 2500, sleep3 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)), browser_default;
|
|
83970
84643
|
var init_browser2 = __esm(() => {
|
|
83971
84644
|
init_source();
|
|
83972
84645
|
init_browser();
|
|
@@ -84083,7 +84756,7 @@ var exports_view_serve = {};
|
|
|
84083
84756
|
__export(exports_view_serve, {
|
|
84084
84757
|
default: () => view_serve_default
|
|
84085
84758
|
});
|
|
84086
|
-
import { readFile as
|
|
84759
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
84087
84760
|
var view_serve_default;
|
|
84088
84761
|
var init_view_serve = __esm(() => {
|
|
84089
84762
|
init_server();
|
|
@@ -84095,7 +84768,7 @@ var init_view_serve = __esm(() => {
|
|
|
84095
84768
|
action: async (opts) => {
|
|
84096
84769
|
if (!opts.config)
|
|
84097
84770
|
throw new Error("--config is required");
|
|
84098
|
-
const config2 = JSON.parse(await
|
|
84771
|
+
const config2 = JSON.parse(await readFile17(opts.config, "utf8"));
|
|
84099
84772
|
startViewServer(config2);
|
|
84100
84773
|
console.log(`view session ${config2.session.id} listening on ${config2.session.pageUrl}`);
|
|
84101
84774
|
await new Promise(() => {});
|
|
@@ -84110,7 +84783,7 @@ __export(exports_studio, {
|
|
|
84110
84783
|
encodeStudioCliDescriptor: () => encodeStudioCliDescriptor
|
|
84111
84784
|
});
|
|
84112
84785
|
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
84113
|
-
import { dirname as
|
|
84786
|
+
import { dirname as dirname13, join as join18, resolve as resolve7 } from "node:path";
|
|
84114
84787
|
function encodeStudioCliDescriptor(executable = process.execPath, entry2 = process.argv[1]) {
|
|
84115
84788
|
const args = entry2 && entry2 !== executable && !entry2.startsWith("/$bunfs") && existsSync8(entry2) ? [realpathSync(entry2)] : [];
|
|
84116
84789
|
return JSON.stringify({ version: 1, executable, args });
|
|
@@ -84120,22 +84793,22 @@ function resolveStudioDir() {
|
|
|
84120
84793
|
if (process.env.ASTRALE_STUDIO_DIR)
|
|
84121
84794
|
candidates.push(process.env.ASTRALE_STUDIO_DIR);
|
|
84122
84795
|
try {
|
|
84123
|
-
const entryDir =
|
|
84124
|
-
candidates.push(
|
|
84796
|
+
const entryDir = dirname13(realpathSync(process.argv[1] ?? ""));
|
|
84797
|
+
candidates.push(join18(entryDir, "..", "studio"), join18(entryDir, "studio"));
|
|
84125
84798
|
} catch {}
|
|
84126
84799
|
for (const c of candidates) {
|
|
84127
|
-
if (existsSync8(
|
|
84128
|
-
return
|
|
84800
|
+
if (existsSync8(join18(c, "server", "index.ts")))
|
|
84801
|
+
return resolve7(c);
|
|
84129
84802
|
}
|
|
84130
84803
|
throw new Error(`Domain Studio assets not found (looked in: ${candidates.join(", ") || "<none>"}). ` + `Reinstall the astrale CLI, or set ASTRALE_STUDIO_DIR to a studio checkout.`);
|
|
84131
84804
|
}
|
|
84132
84805
|
function isDevSource(studioDir) {
|
|
84133
|
-
return existsSync8(
|
|
84806
|
+
return existsSync8(join18(studioDir, "vite.config.ts")) && existsSync8(join18(studioDir, "client", "src"));
|
|
84134
84807
|
}
|
|
84135
84808
|
function resolveViteBin(studioDir) {
|
|
84136
84809
|
for (const c of [
|
|
84137
|
-
|
|
84138
|
-
|
|
84810
|
+
join18(studioDir, "node_modules", ".bin", "vite"),
|
|
84811
|
+
join18(studioDir, "..", "..", "node_modules", ".bin", "vite")
|
|
84139
84812
|
]) {
|
|
84140
84813
|
if (existsSync8(c))
|
|
84141
84814
|
return c;
|
|
@@ -84256,11 +84929,11 @@ Examples:
|
|
|
84256
84929
|
`,
|
|
84257
84930
|
action: async (pathArg, opts) => {
|
|
84258
84931
|
try {
|
|
84259
|
-
const workspace =
|
|
84932
|
+
const workspace = resolve7(pathArg ?? process.cwd());
|
|
84260
84933
|
if (!existsSync8(workspace))
|
|
84261
84934
|
throw new Error(`path not found: ${workspace}`);
|
|
84262
84935
|
const studioDir = resolveStudioDir();
|
|
84263
|
-
if (process.env.ASTRALE_STUDIO_DIR && studioDir ===
|
|
84936
|
+
if (process.env.ASTRALE_STUDIO_DIR && studioDir === resolve7(process.env.ASTRALE_STUDIO_DIR)) {
|
|
84264
84937
|
log.dim(` using ASTRALE_STUDIO_DIR=${studioDir}`);
|
|
84265
84938
|
}
|
|
84266
84939
|
await ensureBun();
|
|
@@ -84360,8 +85033,8 @@ Examples:
|
|
|
84360
85033
|
}
|
|
84361
85034
|
}), "server");
|
|
84362
85035
|
} else {
|
|
84363
|
-
const dist =
|
|
84364
|
-
if (!existsSync8(
|
|
85036
|
+
const dist = join18(studioDir, "client", "dist");
|
|
85037
|
+
if (!existsSync8(join18(dist, "index.html"))) {
|
|
84365
85038
|
throw new Error(`studio client not built at ${dist} — run: pnpm --filter @astrale-os/studio build`);
|
|
84366
85039
|
}
|
|
84367
85040
|
serverChild = supervise(spawnHandle("bun", ["server/index.ts", workspace, "--port", String(studioPort), "--no-open"], {
|
|
@@ -84414,10 +85087,10 @@ var init_model7 = __esm(() => {
|
|
|
84414
85087
|
});
|
|
84415
85088
|
|
|
84416
85089
|
// src/ui/lock.ts
|
|
84417
|
-
import { createHash as
|
|
84418
|
-
import { readFile as
|
|
85090
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
85091
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
84419
85092
|
function digest5(value3) {
|
|
84420
|
-
return
|
|
85093
|
+
return createHash3("sha256").update(value3).digest("hex");
|
|
84421
85094
|
}
|
|
84422
85095
|
function parseUiLock(value3) {
|
|
84423
85096
|
const lock = value3;
|
|
@@ -84453,14 +85126,14 @@ function pathIsAbsolute(value3) {
|
|
|
84453
85126
|
}
|
|
84454
85127
|
async function readUiLock(target2) {
|
|
84455
85128
|
try {
|
|
84456
|
-
return parseUiLock(JSON.parse(await
|
|
85129
|
+
return parseUiLock(JSON.parse(await readFile18(target2, "utf8")));
|
|
84457
85130
|
} catch (cause) {
|
|
84458
85131
|
if (cause instanceof UiError)
|
|
84459
85132
|
throw cause;
|
|
84460
85133
|
throw new UiError("UI_CONFIG_MISSING", "Unable to read " + UI_LOCK_FILE + ".", "Run astrale ui init.", { cause });
|
|
84461
85134
|
}
|
|
84462
85135
|
}
|
|
84463
|
-
var
|
|
85136
|
+
var init_lock2 = __esm(() => {
|
|
84464
85137
|
init_model7();
|
|
84465
85138
|
});
|
|
84466
85139
|
|
|
@@ -86484,14 +87157,14 @@ var require_lib5 = __commonJS(function(exports) {
|
|
|
86484
87157
|
});
|
|
86485
87158
|
|
|
86486
87159
|
// src/ui/project.ts
|
|
86487
|
-
import { access as access2, lstat, readFile as
|
|
87160
|
+
import { access as access2, lstat as lstat3, readFile as readFile19, realpath as realpath2 } from "node:fs/promises";
|
|
86488
87161
|
import path5 from "node:path";
|
|
86489
87162
|
async function exists(target2) {
|
|
86490
87163
|
return access2(target2).then(() => true, () => false);
|
|
86491
87164
|
}
|
|
86492
87165
|
async function readManifest(target2) {
|
|
86493
87166
|
try {
|
|
86494
|
-
return JSON.parse(await
|
|
87167
|
+
return JSON.parse(await readFile19(target2, "utf8"));
|
|
86495
87168
|
} catch (cause) {
|
|
86496
87169
|
throw new UiError("UI_PROJECT_UNSUPPORTED", "package.json is not valid JSON.", undefined, {
|
|
86497
87170
|
cause
|
|
@@ -86561,7 +87234,7 @@ async function resolveAlias(project2, candidate2) {
|
|
|
86561
87234
|
async function resolveUiRegistryTarget(project2, declaredTarget) {
|
|
86562
87235
|
if (!declaredTarget.startsWith("components/"))
|
|
86563
87236
|
return declaredTarget;
|
|
86564
|
-
const components = await
|
|
87237
|
+
const components = await readFile19(project2.componentsPath, "utf8").then((value3) => JSON.parse(value3)).catch(() => {
|
|
86565
87238
|
return;
|
|
86566
87239
|
});
|
|
86567
87240
|
const componentsAlias = components?.aliases?.components;
|
|
@@ -86573,11 +87246,11 @@ async function resolveUiRegistryTarget(project2, declaredTarget) {
|
|
|
86573
87246
|
if (!resolved) {
|
|
86574
87247
|
throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json alias cannot be resolved through tsconfig.json or jsconfig.json.", "Define a matching compilerOptions.paths entry for " + componentsAlias + ".");
|
|
86575
87248
|
}
|
|
86576
|
-
const
|
|
86577
|
-
if (
|
|
87249
|
+
const relative2 = path5.relative(project2.root, path5.join(resolved, suffix));
|
|
87250
|
+
if (relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
|
|
86578
87251
|
throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json alias escapes the project.");
|
|
86579
87252
|
}
|
|
86580
|
-
return
|
|
87253
|
+
return relative2.split(path5.sep).join("/");
|
|
86581
87254
|
}
|
|
86582
87255
|
function hasReactTailwind(manifest) {
|
|
86583
87256
|
const dependencies = {
|
|
@@ -86597,8 +87270,8 @@ async function assertPhysicalProjectPath(root, target2) {
|
|
|
86597
87270
|
existing = parent;
|
|
86598
87271
|
}
|
|
86599
87272
|
const physicalTarget = await realpath2(existing);
|
|
86600
|
-
const
|
|
86601
|
-
if (
|
|
87273
|
+
const relative2 = path5.relative(physicalRoot, physicalTarget);
|
|
87274
|
+
if (relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
|
|
86602
87275
|
throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json CSS path escapes the project.");
|
|
86603
87276
|
}
|
|
86604
87277
|
}
|
|
@@ -86607,7 +87280,7 @@ async function discoverUiProject(input = process.cwd()) {
|
|
|
86607
87280
|
if (!await exists(root)) {
|
|
86608
87281
|
throw new UiError("UI_PROJECT_UNSUPPORTED", "Project path does not exist: " + root);
|
|
86609
87282
|
}
|
|
86610
|
-
if (!(await
|
|
87283
|
+
if (!(await lstat3(root)).isDirectory())
|
|
86611
87284
|
root = path5.dirname(root);
|
|
86612
87285
|
while (true) {
|
|
86613
87286
|
const manifestPath = path5.join(root, "package.json");
|
|
@@ -86663,17 +87336,17 @@ async function discoverUiProject(input = process.cwd()) {
|
|
|
86663
87336
|
"frontend/src/styles.css"
|
|
86664
87337
|
];
|
|
86665
87338
|
const componentsPath = path5.join(root, "components.json");
|
|
86666
|
-
const configuredCss = await
|
|
87339
|
+
const configuredCss = await readFile19(componentsPath, "utf8").then((value3) => {
|
|
86667
87340
|
const components = JSON.parse(value3);
|
|
86668
87341
|
const css = components.tailwind?.css;
|
|
86669
87342
|
if (typeof css !== "string" || css.length === 0)
|
|
86670
87343
|
return;
|
|
86671
87344
|
const target2 = path5.resolve(root, css);
|
|
86672
|
-
const
|
|
86673
|
-
if (
|
|
87345
|
+
const relative2 = path5.relative(root, target2);
|
|
87346
|
+
if (relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
|
|
86674
87347
|
throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json CSS path escapes the project.");
|
|
86675
87348
|
}
|
|
86676
|
-
return { relative:
|
|
87349
|
+
return { relative: relative2.split(path5.sep).join("/"), target: target2 };
|
|
86677
87350
|
}).catch((error52) => {
|
|
86678
87351
|
if (error52 instanceof UiError)
|
|
86679
87352
|
throw error52;
|
|
@@ -86712,11 +87385,11 @@ function assertSupportedUiProject(project2) {
|
|
|
86712
87385
|
}
|
|
86713
87386
|
}
|
|
86714
87387
|
function projectRelative(project2, target2) {
|
|
86715
|
-
const
|
|
86716
|
-
if (
|
|
87388
|
+
const relative2 = path5.relative(project2.root, target2);
|
|
87389
|
+
if (relative2 === "" || relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
|
|
86717
87390
|
throw new UiError("UI_LOCK_INVALID", "Path escapes the project root: " + target2);
|
|
86718
87391
|
}
|
|
86719
|
-
return
|
|
87392
|
+
return relative2.split(path5.sep).join("/");
|
|
86720
87393
|
}
|
|
86721
87394
|
var import_tsconfig_paths, MANAGERS;
|
|
86722
87395
|
var init_project3 = __esm(() => {
|
|
@@ -86971,13 +87644,13 @@ var init_runner = __esm(() => {
|
|
|
86971
87644
|
});
|
|
86972
87645
|
|
|
86973
87646
|
// src/ui/operations.ts
|
|
86974
|
-
import { access as access3, lstat as
|
|
87647
|
+
import { access as access3, lstat as lstat4, mkdir as mkdir12, readFile as readFile20, realpath as realpath3, rm as rm5, writeFile as writeFile8 } from "node:fs/promises";
|
|
86975
87648
|
import path6 from "node:path";
|
|
86976
87649
|
async function exists2(target2) {
|
|
86977
87650
|
return access3(target2).then(() => true, () => false);
|
|
86978
87651
|
}
|
|
86979
87652
|
async function readOptional(target2) {
|
|
86980
|
-
return
|
|
87653
|
+
return readFile20(target2, "utf8").catch(() => {
|
|
86981
87654
|
return;
|
|
86982
87655
|
});
|
|
86983
87656
|
}
|
|
@@ -86993,10 +87666,10 @@ function admitLocalThemeCss(source2, slug) {
|
|
|
86993
87666
|
}
|
|
86994
87667
|
}
|
|
86995
87668
|
function themeImport(project2, target2) {
|
|
86996
|
-
let
|
|
86997
|
-
if (!
|
|
86998
|
-
|
|
86999
|
-
return "@import '" +
|
|
87669
|
+
let relative2 = path6.relative(path6.dirname(project2.cssPath), target2).split(path6.sep).join("/");
|
|
87670
|
+
if (!relative2.startsWith("."))
|
|
87671
|
+
relative2 = "./" + relative2;
|
|
87672
|
+
return "@import '" + relative2 + "';";
|
|
87000
87673
|
}
|
|
87001
87674
|
function activateTheme(current, statement) {
|
|
87002
87675
|
const lines = current.split(/\r?\n/u).filter((line) => !/^@import\s+['"][^'"]*components\/astrale\/theme\/[a-z][a-z0-9-]*\.css['"];?\s*$/u.test(line));
|
|
@@ -87067,7 +87740,7 @@ async function appendPnpmWorkspace(project2, workspace) {
|
|
|
87067
87740
|
const target2 = pnpmWorkspacePath(project2);
|
|
87068
87741
|
const source2 = await readOptional(target2);
|
|
87069
87742
|
if (source2 === undefined) {
|
|
87070
|
-
await
|
|
87743
|
+
await writeFile8(target2, `packages:
|
|
87071
87744
|
- '` + workspace + `'
|
|
87072
87745
|
`, "utf8");
|
|
87073
87746
|
return;
|
|
@@ -87086,12 +87759,12 @@ async function appendPnpmWorkspace(project2, workspace) {
|
|
|
87086
87759
|
}
|
|
87087
87760
|
if (!values.includes(workspace))
|
|
87088
87761
|
packages.add(workspace);
|
|
87089
|
-
await
|
|
87762
|
+
await writeFile8(target2, String(document), "utf8");
|
|
87090
87763
|
}
|
|
87091
87764
|
async function hasDomainRegistryWorkspace(project2) {
|
|
87092
87765
|
if (!project2.isAstraleDomain || !await exists2(domainRegistryPackagePath(project2)))
|
|
87093
87766
|
return false;
|
|
87094
|
-
const registryManifest = JSON.parse(await
|
|
87767
|
+
const registryManifest = JSON.parse(await readFile20(domainRegistryPackagePath(project2), "utf8"));
|
|
87095
87768
|
if (registryManifest.private !== true || typeof registryManifest.name !== "string" || registryManifest.name === UI_PACKAGE || registryManifest.name === project2.packageJson.name) {
|
|
87096
87769
|
return false;
|
|
87097
87770
|
}
|
|
@@ -87211,8 +87884,8 @@ async function initUi(options, dependencies = {}) {
|
|
|
87211
87884
|
"@import '" + UI_PACKAGE + "/presets/" + preset + ".css';"
|
|
87212
87885
|
];
|
|
87213
87886
|
const withoutAstrale = currentCss.replace(/^@import\s+['"]@astrale-os\/ui\/(?:theme\.css|presets\/[a-z-]+\.css)['"];?\s*$/gmu, "").trimStart();
|
|
87214
|
-
await
|
|
87215
|
-
await
|
|
87887
|
+
await mkdir12(path6.dirname(project2.cssPath), { recursive: true });
|
|
87888
|
+
await writeFile8(project2.cssPath, imports.join(`
|
|
87216
87889
|
`) + `
|
|
87217
87890
|
|
|
87218
87891
|
` + withoutAstrale, "utf8");
|
|
@@ -87269,15 +87942,15 @@ async function initUi(options, dependencies = {}) {
|
|
|
87269
87942
|
} catch (error52) {
|
|
87270
87943
|
for (const [target2, value3] of snapshots) {
|
|
87271
87944
|
if (value3 !== undefined)
|
|
87272
|
-
await
|
|
87945
|
+
await writeFile8(target2, value3, "utf8");
|
|
87273
87946
|
else
|
|
87274
|
-
await
|
|
87947
|
+
await rm5(target2, { force: true });
|
|
87275
87948
|
}
|
|
87276
87949
|
throw error52;
|
|
87277
87950
|
}
|
|
87278
87951
|
}
|
|
87279
87952
|
async function pinUiDependency(project2, version2, section, runner) {
|
|
87280
|
-
const manifest = JSON.parse(await
|
|
87953
|
+
const manifest = JSON.parse(await readFile20(project2.packageJsonPath, "utf8"));
|
|
87281
87954
|
const current = manifestDependencies(manifest, section)[UI_PACKAGE];
|
|
87282
87955
|
const other = section === "dependencies" ? "devDependencies" : "dependencies";
|
|
87283
87956
|
if (current === version2 && manifestDependencies(manifest, other)[UI_PACKAGE] === undefined)
|
|
@@ -87316,7 +87989,7 @@ async function lockedRelease(project2, fetcher) {
|
|
|
87316
87989
|
}
|
|
87317
87990
|
async function addLocalTheme(address, project2, options) {
|
|
87318
87991
|
const sourcePath = path6.resolve(project2.root, address);
|
|
87319
|
-
const sourceInfo = await
|
|
87992
|
+
const sourceInfo = await lstat4(sourcePath).catch(() => {
|
|
87320
87993
|
return;
|
|
87321
87994
|
});
|
|
87322
87995
|
if (!sourceInfo?.isFile() || sourceInfo.isSymbolicLink()) {
|
|
@@ -87326,7 +87999,7 @@ async function addLocalTheme(address, project2, options) {
|
|
|
87326
87999
|
if (!THEME_SLUG.test(slug)) {
|
|
87327
88000
|
throw new UiError("UI_ITEM_CONFLICT", "Local theme filename must be a kebab-case theme name.", "Rename it to a name such as observatory.css.");
|
|
87328
88001
|
}
|
|
87329
|
-
const source2 = await
|
|
88002
|
+
const source2 = await readFile20(sourcePath, "utf8");
|
|
87330
88003
|
admitLocalThemeCss(source2, slug);
|
|
87331
88004
|
return installThemeCss(slug, source2, digest5(source2), address, project2, options);
|
|
87332
88005
|
}
|
|
@@ -87361,10 +88034,10 @@ async function installThemeCss(slug, source2, sourceDigest, sourceLabel, project
|
|
|
87361
88034
|
snapshots.set(mutation, await readOptional(mutation));
|
|
87362
88035
|
}
|
|
87363
88036
|
try {
|
|
87364
|
-
await
|
|
87365
|
-
await
|
|
88037
|
+
await mkdir12(path6.dirname(target2), { recursive: true });
|
|
88038
|
+
await writeFile8(target2, source2, "utf8");
|
|
87366
88039
|
const css = await readOptional(project2.cssPath) ?? "";
|
|
87367
|
-
await
|
|
88040
|
+
await writeFile8(project2.cssPath, activateTheme(css, statement), "utf8");
|
|
87368
88041
|
lock.items[canonicalAddress] = {
|
|
87369
88042
|
address: canonicalAddress,
|
|
87370
88043
|
sourceDigest,
|
|
@@ -87375,9 +88048,9 @@ async function installThemeCss(slug, source2, sourceDigest, sourceLabel, project
|
|
|
87375
88048
|
} catch (error52) {
|
|
87376
88049
|
for (const [mutation, previous] of snapshots) {
|
|
87377
88050
|
if (previous === undefined)
|
|
87378
|
-
await
|
|
88051
|
+
await rm5(mutation, { force: true });
|
|
87379
88052
|
else
|
|
87380
|
-
await
|
|
88053
|
+
await writeFile8(mutation, previous, "utf8");
|
|
87381
88054
|
}
|
|
87382
88055
|
throw error52;
|
|
87383
88056
|
}
|
|
@@ -87464,15 +88137,15 @@ async function addUi(addresses, options, dependencies = {}) {
|
|
|
87464
88137
|
if (themeTarget) {
|
|
87465
88138
|
const target2 = await safeTarget(project2, themeTarget);
|
|
87466
88139
|
const css = await readOptional(project2.cssPath) ?? "";
|
|
87467
|
-
await
|
|
88140
|
+
await writeFile8(project2.cssPath, activateTheme(css, themeImport(project2, target2)), "utf8");
|
|
87468
88141
|
}
|
|
87469
88142
|
}
|
|
87470
88143
|
} catch (error52) {
|
|
87471
88144
|
for (const [target2, previous] of snapshots) {
|
|
87472
88145
|
if (previous === undefined)
|
|
87473
|
-
await
|
|
88146
|
+
await rm5(target2, { force: true });
|
|
87474
88147
|
else
|
|
87475
|
-
await
|
|
88148
|
+
await writeFile8(target2, previous, "utf8");
|
|
87476
88149
|
}
|
|
87477
88150
|
throw error52;
|
|
87478
88151
|
}
|
|
@@ -87501,7 +88174,7 @@ async function addUi(addresses, options, dependencies = {}) {
|
|
|
87501
88174
|
if (!file2.target)
|
|
87502
88175
|
continue;
|
|
87503
88176
|
const target2 = await safeTarget(project2, resolvedTargets.get(file2.target));
|
|
87504
|
-
files[projectRelative(project2, target2)] = digest5(await
|
|
88177
|
+
files[projectRelative(project2, target2)] = digest5(await readFile20(target2));
|
|
87505
88178
|
}
|
|
87506
88179
|
lock.items[item.meta.canonicalAddress] = {
|
|
87507
88180
|
address: item.meta.canonicalAddress,
|
|
@@ -87513,9 +88186,9 @@ async function addUi(addresses, options, dependencies = {}) {
|
|
|
87513
88186
|
} catch (error52) {
|
|
87514
88187
|
for (const [target2, previous] of snapshots) {
|
|
87515
88188
|
if (previous === undefined)
|
|
87516
|
-
await
|
|
88189
|
+
await rm5(target2, { force: true });
|
|
87517
88190
|
else
|
|
87518
|
-
await
|
|
88191
|
+
await writeFile8(target2, previous, "utf8");
|
|
87519
88192
|
}
|
|
87520
88193
|
throw error52;
|
|
87521
88194
|
}
|
|
@@ -87533,7 +88206,7 @@ async function doctorUi(input) {
|
|
|
87533
88206
|
const checks3 = [];
|
|
87534
88207
|
let lock;
|
|
87535
88208
|
try {
|
|
87536
|
-
lock = parseUiLock(JSON.parse(await
|
|
88209
|
+
lock = parseUiLock(JSON.parse(await readFile20(project2.uiLockPath, "utf8")));
|
|
87537
88210
|
checks3.push({ check: "lock", ok: true });
|
|
87538
88211
|
} catch (error52) {
|
|
87539
88212
|
checks3.push({
|
|
@@ -87561,7 +88234,7 @@ async function doctorUi(input) {
|
|
|
87561
88234
|
if (lock) {
|
|
87562
88235
|
for (const item of Object.values(lock.items)) {
|
|
87563
88236
|
for (const [file2, expected] of Object.entries(item.files)) {
|
|
87564
|
-
const actual = await
|
|
88237
|
+
const actual = await readFile20(path6.join(project2.root, file2)).then(digest5).catch(() => "");
|
|
87565
88238
|
checks3.push({ check: "item:" + item.address + ":" + file2, ok: actual === expected });
|
|
87566
88239
|
}
|
|
87567
88240
|
}
|
|
@@ -87586,7 +88259,7 @@ async function applyPreset(preset, options) {
|
|
|
87586
88259
|
throw new UiError("UI_CONFIG_MISSING", "No Astrale preset import was found.");
|
|
87587
88260
|
}
|
|
87588
88261
|
if (!options.dryRun) {
|
|
87589
|
-
await
|
|
88262
|
+
await writeFile8(project2.cssPath, next, "utf8");
|
|
87590
88263
|
lock.preset = preset;
|
|
87591
88264
|
await writeJson(project2.uiLockPath, lock);
|
|
87592
88265
|
}
|
|
@@ -87614,34 +88287,34 @@ async function rejectLocalChanges(project2, lock, items, overwrite) {
|
|
|
87614
88287
|
if (!installed)
|
|
87615
88288
|
continue;
|
|
87616
88289
|
for (const [file2, expected] of Object.entries(installed.files)) {
|
|
87617
|
-
const actual = await
|
|
88290
|
+
const actual = await readFile20(path6.join(project2.root, file2)).then(digest5).catch(() => "");
|
|
87618
88291
|
if (actual !== expected) {
|
|
87619
88292
|
throw new UiError("UI_LOCAL_CHANGES", "Installed UI file has local changes: " + file2, "Review the file, then repeat add with explicit --overwrite --yes.");
|
|
87620
88293
|
}
|
|
87621
88294
|
}
|
|
87622
88295
|
}
|
|
87623
88296
|
}
|
|
87624
|
-
async function safeTarget(project2,
|
|
87625
|
-
if (path6.isAbsolute(
|
|
87626
|
-
throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " +
|
|
88297
|
+
async function safeTarget(project2, relative2) {
|
|
88298
|
+
if (path6.isAbsolute(relative2) || relative2.split(/[\\/]/u).includes("..")) {
|
|
88299
|
+
throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " + relative2);
|
|
87627
88300
|
}
|
|
87628
|
-
const target2 = path6.resolve(project2.root,
|
|
88301
|
+
const target2 = path6.resolve(project2.root, relative2);
|
|
87629
88302
|
projectRelative(project2, target2);
|
|
87630
88303
|
const parent = await realpath3(path6.dirname(target2));
|
|
87631
88304
|
const root = await realpath3(project2.root);
|
|
87632
88305
|
if (parent !== root && !parent.startsWith(root + path6.sep)) {
|
|
87633
|
-
throw new UiError("UI_LOCK_INVALID", "Registry target escapes through a symlink: " +
|
|
88306
|
+
throw new UiError("UI_LOCK_INVALID", "Registry target escapes through a symlink: " + relative2);
|
|
87634
88307
|
}
|
|
87635
|
-
if (await exists2(target2) && (await
|
|
87636
|
-
throw new UiError("UI_LOCK_INVALID", "Registry target is a symlink: " +
|
|
88308
|
+
if (await exists2(target2) && (await lstat4(target2)).isSymbolicLink()) {
|
|
88309
|
+
throw new UiError("UI_LOCK_INVALID", "Registry target is a symlink: " + relative2);
|
|
87637
88310
|
}
|
|
87638
88311
|
return target2;
|
|
87639
88312
|
}
|
|
87640
|
-
async function assertSafePlannedTarget(project2,
|
|
87641
|
-
if (path6.isAbsolute(
|
|
87642
|
-
throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " +
|
|
88313
|
+
async function assertSafePlannedTarget(project2, relative2) {
|
|
88314
|
+
if (path6.isAbsolute(relative2) || relative2.split(/[\\/]/u).includes("..")) {
|
|
88315
|
+
throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " + relative2);
|
|
87643
88316
|
}
|
|
87644
|
-
const target2 = path6.resolve(project2.root,
|
|
88317
|
+
const target2 = path6.resolve(project2.root, relative2);
|
|
87645
88318
|
projectRelative(project2, target2);
|
|
87646
88319
|
const root = await realpath3(project2.root);
|
|
87647
88320
|
const physicalTarget = path6.resolve(root, path6.relative(project2.root, target2));
|
|
@@ -87650,21 +88323,21 @@ async function assertSafePlannedTarget(project2, relative) {
|
|
|
87650
88323
|
current = path6.join(current, segment);
|
|
87651
88324
|
if (!await exists2(current))
|
|
87652
88325
|
break;
|
|
87653
|
-
if ((await
|
|
87654
|
-
throw new UiError("UI_LOCK_INVALID", "Registry target traverses a symlink: " +
|
|
88326
|
+
if ((await lstat4(current)).isSymbolicLink()) {
|
|
88327
|
+
throw new UiError("UI_LOCK_INVALID", "Registry target traverses a symlink: " + relative2);
|
|
87655
88328
|
}
|
|
87656
88329
|
}
|
|
87657
88330
|
return target2;
|
|
87658
88331
|
}
|
|
87659
88332
|
async function writeJson(target2, value3) {
|
|
87660
|
-
await
|
|
87661
|
-
await
|
|
88333
|
+
await mkdir12(path6.dirname(target2), { recursive: true });
|
|
88334
|
+
await writeFile8(target2, JSON.stringify(value3, null, 2) + `
|
|
87662
88335
|
`, "utf8");
|
|
87663
88336
|
}
|
|
87664
88337
|
var LOCAL_THEME_MAX_BYTES = 131072, THEME_SLUG, REQUIRED_THEME_TOKENS;
|
|
87665
88338
|
var init_operations3 = __esm(() => {
|
|
87666
88339
|
init_dist();
|
|
87667
|
-
|
|
88340
|
+
init_lock2();
|
|
87668
88341
|
init_model7();
|
|
87669
88342
|
init_project3();
|
|
87670
88343
|
init_release();
|
|
@@ -89350,7 +90023,7 @@ async function installDirect(target2, opts, dependencies = {}) {
|
|
|
89350
90023
|
}
|
|
89351
90024
|
host = validateInstallUrl(target2);
|
|
89352
90025
|
operation = opts.operation === undefined ? direct.createOperationId() : direct.acceptOperationId(opts.operation);
|
|
89353
|
-
consentedOrigin = await ensureIdentityOverrideConsent(target2, host, opts.allowIdentityOverride ?? false);
|
|
90026
|
+
consentedOrigin = await ensureIdentityOverrideConsent(target2, host, opts.allowIdentityOverride ?? false, isMachine(opts));
|
|
89354
90027
|
} catch (e) {
|
|
89355
90028
|
fatal(e, opts);
|
|
89356
90029
|
}
|
|
@@ -89370,7 +90043,9 @@ async function installDirect(target2, opts, dependencies = {}) {
|
|
|
89370
90043
|
log.success(`Domain ${installed.status}: ${installed.origin}@${installed.revision}`);
|
|
89371
90044
|
log.dim(` operation: ${installed.operation}`);
|
|
89372
90045
|
if (isIdentityOverride(installed.origin, host) && installed.origin !== consentedOrigin) {
|
|
89373
|
-
|
|
90046
|
+
if (!isMachine(fmtOpts)) {
|
|
90047
|
+
log.warn(`Installed origin "${installed.origin}" differs from the serving host "${host}" ` + `and was not confirmed before install (the worker Publication was unavailable). ` + `Every ${installed.origin}/* call on this instance now routes to ${host}.`);
|
|
90048
|
+
}
|
|
89374
90049
|
}
|
|
89375
90050
|
}
|
|
89376
90051
|
});
|
|
@@ -89394,16 +90069,20 @@ function validateInstallUrl(value3) {
|
|
|
89394
90069
|
function isIdentityOverride(origin2, host) {
|
|
89395
90070
|
return origin2.toLowerCase() !== host.toLowerCase();
|
|
89396
90071
|
}
|
|
89397
|
-
async function ensureIdentityOverrideConsent(url3, host, allow) {
|
|
90072
|
+
async function ensureIdentityOverrideConsent(url3, host, allow, machine) {
|
|
89398
90073
|
const origin2 = await probeDeclaredOrigin(url3);
|
|
89399
90074
|
if (origin2 === undefined) {
|
|
89400
|
-
|
|
90075
|
+
if (!machine) {
|
|
90076
|
+
log.warn(`Could not read a declared origin from ` + `${new URL("/.well-known/astrale/domain.json", url3).href} — ` + `skipping the pre-install identity check (the installed origin is verified after install).`);
|
|
90077
|
+
}
|
|
89401
90078
|
return;
|
|
89402
90079
|
}
|
|
89403
90080
|
if (!isIdentityOverride(origin2, host))
|
|
89404
90081
|
return;
|
|
89405
90082
|
if (allow) {
|
|
89406
|
-
|
|
90083
|
+
if (!machine) {
|
|
90084
|
+
log.warn(`Identity override consented via --allow-identity-override: ${origin2} ← ${host}`);
|
|
90085
|
+
}
|
|
89407
90086
|
return origin2;
|
|
89408
90087
|
}
|
|
89409
90088
|
const banner = source_default.red.bold("⚠ DANGER — IDENTITY OVERRIDE") + `
|
|
@@ -89813,9 +90492,9 @@ __export(exports_register, {
|
|
|
89813
90492
|
formatIdentityRegistration: () => formatIdentityRegistration,
|
|
89814
90493
|
prepareIdentityProvision: () => prepareIdentityProvision
|
|
89815
90494
|
});
|
|
89816
|
-
import { readFile as
|
|
90495
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
89817
90496
|
async function readJwk(path8) {
|
|
89818
|
-
return JSON.parse(await
|
|
90497
|
+
return JSON.parse(await readFile21(path8, "utf8"));
|
|
89819
90498
|
}
|
|
89820
90499
|
function formatIdentityRegistration(result, format3, machine) {
|
|
89821
90500
|
output(result, format3);
|
|
@@ -89882,10 +90561,9 @@ Behavior:
|
|
|
89882
90561
|
its result and stores the same target-bound registration. The callable owns
|
|
89883
90562
|
authorization; the CLI never receives installed Domain authority.
|
|
89884
90563
|
|
|
89885
|
-
Kernel
|
|
89886
|
-
|
|
89887
|
-
|
|
89888
|
-
the returned (issuer, subject) for subsequent calls.
|
|
90564
|
+
The Kernel assigns each Node ID; callers do not choose a storage path. The
|
|
90565
|
+
CLI binds the key proof to the exact provision fingerprint and target Kernel
|
|
90566
|
+
audience, then caches the returned (issuer, subject) for subsequent calls.
|
|
89889
90567
|
|
|
89890
90568
|
Example:
|
|
89891
90569
|
$ astrale identity register alice --class /:accounts.example:class.User --props '{"accounts.example:class.User.property.name":"Alice"}' -i staging
|
|
@@ -90075,7 +90753,7 @@ __export(exports_sync, {
|
|
|
90075
90753
|
default: () => sync_default
|
|
90076
90754
|
});
|
|
90077
90755
|
var sync_default;
|
|
90078
|
-
var
|
|
90756
|
+
var init_sync2 = __esm(() => {
|
|
90079
90757
|
init_identity7();
|
|
90080
90758
|
init_log();
|
|
90081
90759
|
init_output();
|
|
@@ -90196,7 +90874,7 @@ var exports_import = {};
|
|
|
90196
90874
|
__export(exports_import, {
|
|
90197
90875
|
default: () => import_default
|
|
90198
90876
|
});
|
|
90199
|
-
import { readFile as
|
|
90877
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
90200
90878
|
var import_default;
|
|
90201
90879
|
var init_import2 = __esm(() => {
|
|
90202
90880
|
init_identity7();
|
|
@@ -90224,7 +90902,7 @@ var init_import2 = __esm(() => {
|
|
|
90224
90902
|
],
|
|
90225
90903
|
action: async (path8, opts) => {
|
|
90226
90904
|
try {
|
|
90227
|
-
const raw2 = await
|
|
90905
|
+
const raw2 = await readFile22(path8, "utf-8");
|
|
90228
90906
|
const passphrase = isEncryptedIdentityExport(raw2) ? await readPassphrase("Passphrase: ") : undefined;
|
|
90229
90907
|
const envelope = await decodeIdentityExport(raw2, passphrase);
|
|
90230
90908
|
const name = opts.name ?? envelope.subject;
|
|
@@ -90578,21 +91256,68 @@ var init_status4 = __esm(() => {
|
|
|
90578
91256
|
|
|
90579
91257
|
// src/telemetry/store.ts
|
|
90580
91258
|
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
90581
|
-
import { join as
|
|
91259
|
+
import { join as join19 } from "node:path";
|
|
90582
91260
|
function sessionsRoot() {
|
|
90583
|
-
return
|
|
91261
|
+
return join19(createPaths().home, "sessions");
|
|
90584
91262
|
}
|
|
90585
91263
|
function sessionDir(id) {
|
|
90586
|
-
return
|
|
91264
|
+
return join19(sessionsRoot(), id);
|
|
90587
91265
|
}
|
|
90588
91266
|
function eventsPath(id) {
|
|
90589
|
-
return
|
|
91267
|
+
return join19(sessionDir(id), "events.jsonl");
|
|
90590
91268
|
}
|
|
90591
91269
|
function metaPath(id) {
|
|
90592
|
-
return
|
|
91270
|
+
return join19(sessionDir(id), "meta.json");
|
|
90593
91271
|
}
|
|
90594
91272
|
function markerPath(id) {
|
|
90595
|
-
return
|
|
91273
|
+
return join19(sessionDir(id), ".analyzed");
|
|
91274
|
+
}
|
|
91275
|
+
function sessionIds() {
|
|
91276
|
+
try {
|
|
91277
|
+
return readdirSync2(sessionsRoot()).filter((name) => !name.startsWith("."));
|
|
91278
|
+
} catch {
|
|
91279
|
+
return [];
|
|
91280
|
+
}
|
|
91281
|
+
}
|
|
91282
|
+
function scanSessions(now = Date.now()) {
|
|
91283
|
+
return sessionIds().map((id) => {
|
|
91284
|
+
let lastEventAt = null;
|
|
91285
|
+
try {
|
|
91286
|
+
lastEventAt = statSync3(eventsPath(id)).mtime;
|
|
91287
|
+
} catch {}
|
|
91288
|
+
return {
|
|
91289
|
+
id,
|
|
91290
|
+
lastEventAt,
|
|
91291
|
+
closed: lastEventAt !== null && now - lastEventAt.getTime() > IDLE_WINDOW_MS,
|
|
91292
|
+
analyzed: existsSync9(markerPath(id))
|
|
91293
|
+
};
|
|
91294
|
+
}).sort((a, b) => (b.lastEventAt?.getTime() ?? 0) - (a.lastEventAt?.getTime() ?? 0));
|
|
91295
|
+
}
|
|
91296
|
+
function directoryBytes(dir) {
|
|
91297
|
+
let entries;
|
|
91298
|
+
try {
|
|
91299
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
91300
|
+
} catch {
|
|
91301
|
+
return 0;
|
|
91302
|
+
}
|
|
91303
|
+
let total = 0;
|
|
91304
|
+
for (const entry2 of entries) {
|
|
91305
|
+
const path8 = join19(dir, entry2.name);
|
|
91306
|
+
if (entry2.isDirectory()) {
|
|
91307
|
+
total += directoryBytes(path8);
|
|
91308
|
+
} else if (entry2.isFile()) {
|
|
91309
|
+
try {
|
|
91310
|
+
total += statSync3(path8).size;
|
|
91311
|
+
} catch {}
|
|
91312
|
+
}
|
|
91313
|
+
}
|
|
91314
|
+
return total;
|
|
91315
|
+
}
|
|
91316
|
+
function sessionBytes(id) {
|
|
91317
|
+
return directoryBytes(sessionDir(id));
|
|
91318
|
+
}
|
|
91319
|
+
function readMeta(id) {
|
|
91320
|
+
return readJsonSafe(metaPath(id));
|
|
90596
91321
|
}
|
|
90597
91322
|
function readJsonSafe(path8) {
|
|
90598
91323
|
try {
|
|
@@ -90618,13 +91343,7 @@ function inspectSession(id, now = Date.now()) {
|
|
|
90618
91343
|
};
|
|
90619
91344
|
}
|
|
90620
91345
|
function listSessions2(now = Date.now()) {
|
|
90621
|
-
|
|
90622
|
-
try {
|
|
90623
|
-
ids = readdirSync2(sessionsRoot()).filter((n) => !n.startsWith("."));
|
|
90624
|
-
} catch {
|
|
90625
|
-
return [];
|
|
90626
|
-
}
|
|
90627
|
-
return ids.map((id) => inspectSession(id, now)).filter((s) => s !== null).sort((a, b) => (b.lastEventAt?.getTime() ?? 0) - (a.lastEventAt?.getTime() ?? 0));
|
|
91346
|
+
return sessionIds().map((id) => inspectSession(id, now)).filter((s) => s !== null).sort((a, b) => (b.lastEventAt?.getTime() ?? 0) - (a.lastEventAt?.getTime() ?? 0));
|
|
90628
91347
|
}
|
|
90629
91348
|
var IDLE_WINDOW_MS;
|
|
90630
91349
|
var init_store = __esm(() => {
|
|
@@ -90669,13 +91388,13 @@ var init_list5 = __esm(() => {
|
|
|
90669
91388
|
|
|
90670
91389
|
// src/telemetry/adapters/claude-code.ts
|
|
90671
91390
|
import { existsSync as existsSync10, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
90672
|
-
import { homedir as
|
|
90673
|
-
import { join as
|
|
91391
|
+
import { homedir as homedir4 } from "node:os";
|
|
91392
|
+
import { join as join20 } from "node:path";
|
|
90674
91393
|
function mungeCwd(cwd) {
|
|
90675
91394
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
90676
91395
|
}
|
|
90677
|
-
function claudeCodeAdapter(base2 =
|
|
90678
|
-
const projectsDir =
|
|
91396
|
+
function claudeCodeAdapter(base2 = join20(homedir4(), ".claude")) {
|
|
91397
|
+
const projectsDir = join20(base2, "projects");
|
|
90679
91398
|
function detect2() {
|
|
90680
91399
|
try {
|
|
90681
91400
|
return existsSync10(projectsDir);
|
|
@@ -90699,7 +91418,7 @@ function claudeCodeAdapter(base2 = join18(homedir3(), ".claude")) {
|
|
|
90699
91418
|
for (const dir of dirs) {
|
|
90700
91419
|
if (dir !== munged && !dir.startsWith(prefix))
|
|
90701
91420
|
continue;
|
|
90702
|
-
const projectPath =
|
|
91421
|
+
const projectPath = join20(projectsDir, dir);
|
|
90703
91422
|
let files;
|
|
90704
91423
|
try {
|
|
90705
91424
|
files = readdirSync3(projectPath);
|
|
@@ -90709,7 +91428,7 @@ function claudeCodeAdapter(base2 = join18(homedir3(), ".claude")) {
|
|
|
90709
91428
|
for (const file2 of files) {
|
|
90710
91429
|
if (!file2.endsWith(".jsonl"))
|
|
90711
91430
|
continue;
|
|
90712
|
-
const transcriptPath =
|
|
91431
|
+
const transcriptPath = join20(projectPath, file2);
|
|
90713
91432
|
try {
|
|
90714
91433
|
const st = statSync4(transcriptPath);
|
|
90715
91434
|
const mtimeMs = st.mtime.getTime();
|
|
@@ -90744,8 +91463,8 @@ var init_claude_code = __esm(() => {
|
|
|
90744
91463
|
// src/telemetry/adapters/codex.ts
|
|
90745
91464
|
import { existsSync as existsSync11, openSync as openSync2, readdirSync as readdirSync4, readSync, statSync as statSync5 } from "node:fs";
|
|
90746
91465
|
import { closeSync as closeSync3 } from "node:fs";
|
|
90747
|
-
import { homedir as
|
|
90748
|
-
import { join as
|
|
91466
|
+
import { homedir as homedir5 } from "node:os";
|
|
91467
|
+
import { join as join21 } from "node:path";
|
|
90749
91468
|
function readFirstLine(path8) {
|
|
90750
91469
|
let fd = null;
|
|
90751
91470
|
try {
|
|
@@ -90785,8 +91504,8 @@ function numericDirs(path8) {
|
|
|
90785
91504
|
return [];
|
|
90786
91505
|
}
|
|
90787
91506
|
}
|
|
90788
|
-
function codexAdapter(base2 =
|
|
90789
|
-
const sessionsDir =
|
|
91507
|
+
function codexAdapter(base2 = join21(homedir5(), ".codex")) {
|
|
91508
|
+
const sessionsDir = join21(base2, "sessions");
|
|
90790
91509
|
function detect2() {
|
|
90791
91510
|
try {
|
|
90792
91511
|
return existsSync11(sessionsDir);
|
|
@@ -90801,12 +91520,12 @@ function codexAdapter(base2 = join19(homedir4(), ".codex")) {
|
|
|
90801
91520
|
const endMs = window2.end.getTime();
|
|
90802
91521
|
const lowerMs = startMs - DAY_MS;
|
|
90803
91522
|
for (const yyyy of numericDirs(sessionsDir)) {
|
|
90804
|
-
for (const mm of numericDirs(
|
|
90805
|
-
for (const dd of numericDirs(
|
|
91523
|
+
for (const mm of numericDirs(join21(sessionsDir, yyyy))) {
|
|
91524
|
+
for (const dd of numericDirs(join21(sessionsDir, yyyy, mm))) {
|
|
90806
91525
|
const dayStart = Date.UTC(Number(yyyy), Number(mm) - 1, Number(dd));
|
|
90807
91526
|
if (dayStart > endMs + DAY_MS || dayStart + DAY_MS <= lowerMs)
|
|
90808
91527
|
continue;
|
|
90809
|
-
scanDay(
|
|
91528
|
+
scanDay(join21(sessionsDir, yyyy, mm, dd), root, startMs, endMs, sessions);
|
|
90810
91529
|
}
|
|
90811
91530
|
}
|
|
90812
91531
|
}
|
|
@@ -90828,7 +91547,7 @@ function scanDay(dayPath, root, startMs, endMs, out) {
|
|
|
90828
91547
|
for (const file2 of files) {
|
|
90829
91548
|
if (!file2.startsWith("rollout-") || !file2.endsWith(".jsonl"))
|
|
90830
91549
|
continue;
|
|
90831
|
-
const transcriptPath =
|
|
91550
|
+
const transcriptPath = join21(dayPath, file2);
|
|
90832
91551
|
try {
|
|
90833
91552
|
const st = statSync5(transcriptPath);
|
|
90834
91553
|
const mtimeMs = st.mtime.getTime();
|
|
@@ -90975,7 +91694,7 @@ var init_gate = () => {};
|
|
|
90975
91694
|
// src/telemetry/analyze.ts
|
|
90976
91695
|
import { spawn as spawn3 } from "node:child_process";
|
|
90977
91696
|
import { appendFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
90978
|
-
import { join as
|
|
91697
|
+
import { join as join22 } from "node:path";
|
|
90979
91698
|
function writeMarker(id, marker) {
|
|
90980
91699
|
writeFileSync2(markerPath(id), JSON.stringify(marker, null, 2) + `
|
|
90981
91700
|
`);
|
|
@@ -91071,7 +91790,7 @@ async function analyzeSession(id, opts = {}) {
|
|
|
91071
91790
|
const guides = new Map(adapters.map((a) => [a.name, a.readingGuide]));
|
|
91072
91791
|
const prompt = buildPrompt({ id, root, signals: signals2, guides, file: opts.file ?? false });
|
|
91073
91792
|
const dir = sessionDir(id);
|
|
91074
|
-
writeFileSync2(
|
|
91793
|
+
writeFileSync2(join22(dir, "analyzer-prompt.md"), prompt);
|
|
91075
91794
|
const outcome = await runClaude(prompt, dir, opts);
|
|
91076
91795
|
const marker = {
|
|
91077
91796
|
analyzedAt: new Date().toISOString(),
|
|
@@ -91079,10 +91798,10 @@ async function analyzeSession(id, opts = {}) {
|
|
|
91079
91798
|
note: outcome.note
|
|
91080
91799
|
};
|
|
91081
91800
|
writeMarker(id, marker);
|
|
91082
|
-
return { ...marker, reportPath:
|
|
91801
|
+
return { ...marker, reportPath: join22(dir, "report.md") };
|
|
91083
91802
|
}
|
|
91084
91803
|
function runClaude(prompt, cwd, opts) {
|
|
91085
|
-
return new Promise((
|
|
91804
|
+
return new Promise((resolve8) => {
|
|
91086
91805
|
const env2 = { ASTRALE_TELEMETRY: "0" };
|
|
91087
91806
|
for (const [k, v] of Object.entries(process.env)) {
|
|
91088
91807
|
if (v !== undefined && !k.startsWith("CLAUDE"))
|
|
@@ -91095,10 +91814,10 @@ function runClaude(prompt, cwd, opts) {
|
|
|
91095
91814
|
try {
|
|
91096
91815
|
child3 = spawn3("claude", args, { cwd, env: env2, stdio: ["ignore", "pipe", "pipe"] });
|
|
91097
91816
|
} catch (e) {
|
|
91098
|
-
|
|
91817
|
+
resolve8({ ok: false, note: `claude spawn failed: ${String(e)}` });
|
|
91099
91818
|
return;
|
|
91100
91819
|
}
|
|
91101
|
-
child3.on("error", (e) =>
|
|
91820
|
+
child3.on("error", (e) => resolve8({ ok: false, note: `claude not available: ${e.message}` }));
|
|
91102
91821
|
let out = "";
|
|
91103
91822
|
let err = "";
|
|
91104
91823
|
child3.stdout?.setEncoding("utf8");
|
|
@@ -91109,22 +91828,22 @@ function runClaude(prompt, cwd, opts) {
|
|
|
91109
91828
|
child3.on("close", (code) => {
|
|
91110
91829
|
clearTimeout(timer);
|
|
91111
91830
|
try {
|
|
91112
|
-
appendFileSync(
|
|
91831
|
+
appendFileSync(join22(cwd, "analyzer.log"), out + (err ? `
|
|
91113
91832
|
--- stderr ---
|
|
91114
91833
|
${err}` : ""));
|
|
91115
91834
|
} catch {}
|
|
91116
91835
|
try {
|
|
91117
91836
|
const result = JSON.parse(out);
|
|
91118
91837
|
if (result.is_error) {
|
|
91119
|
-
|
|
91838
|
+
resolve8({ ok: false, note: `analyzer errored: ${result.result?.slice(0, 200)}` });
|
|
91120
91839
|
return;
|
|
91121
91840
|
}
|
|
91122
|
-
|
|
91841
|
+
resolve8({
|
|
91123
91842
|
ok: true,
|
|
91124
91843
|
note: `${result.num_turns ?? "?"} turns, $${(result.total_cost_usd ?? 0).toFixed(4)}`
|
|
91125
91844
|
});
|
|
91126
91845
|
} catch {
|
|
91127
|
-
|
|
91846
|
+
resolve8({ ok: false, note: `claude exit ${code}, unparseable output` });
|
|
91128
91847
|
}
|
|
91129
91848
|
});
|
|
91130
91849
|
});
|
|
@@ -91140,28 +91859,111 @@ var init_analyze = __esm(() => {
|
|
|
91140
91859
|
|
|
91141
91860
|
// src/telemetry/settings.ts
|
|
91142
91861
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
91862
|
+
function readConfig2() {
|
|
91863
|
+
try {
|
|
91864
|
+
const parsed = JSON.parse(readFileSync5(createPaths().config, "utf-8"));
|
|
91865
|
+
const telemetry = parsed.telemetry;
|
|
91866
|
+
return typeof telemetry === "object" && telemetry !== null ? telemetry : {};
|
|
91867
|
+
} catch {
|
|
91868
|
+
return {};
|
|
91869
|
+
}
|
|
91870
|
+
}
|
|
91143
91871
|
function telemetryEnabled() {
|
|
91144
91872
|
const env2 = process.env.ASTRALE_TELEMETRY;
|
|
91145
91873
|
if (env2 !== undefined && OFF_VALUES.has(env2.trim().toLowerCase()))
|
|
91146
91874
|
return false;
|
|
91147
|
-
|
|
91148
|
-
|
|
91149
|
-
|
|
91150
|
-
|
|
91151
|
-
|
|
91152
|
-
|
|
91875
|
+
return readConfig2().enabled !== false;
|
|
91876
|
+
}
|
|
91877
|
+
function firstPositive(candidates) {
|
|
91878
|
+
for (const candidate2 of candidates) {
|
|
91879
|
+
if (candidate2 === undefined)
|
|
91880
|
+
continue;
|
|
91881
|
+
const n = typeof candidate2 === "string" ? Number(candidate2.trim()) : candidate2;
|
|
91882
|
+
if (Number.isFinite(n) && n > 0)
|
|
91883
|
+
return n;
|
|
91884
|
+
}
|
|
91885
|
+
return null;
|
|
91886
|
+
}
|
|
91887
|
+
function retentionBudget() {
|
|
91888
|
+
const config2 = readConfig2();
|
|
91889
|
+
const days = firstPositive([process.env.ASTRALE_TELEMETRY_MAX_AGE_DAYS, config2.maxAgeDays]) ?? DEFAULT_MAX_AGE_DAYS;
|
|
91890
|
+
const bytes = firstPositive([process.env.ASTRALE_TELEMETRY_MAX_BYTES, config2.maxBytes]) ?? DEFAULT_MAX_BYTES;
|
|
91891
|
+
return { maxAgeMs: days * 24 * 60 * 60 * 1000, maxBytes: bytes };
|
|
91153
91892
|
}
|
|
91154
|
-
var OFF_VALUES;
|
|
91893
|
+
var OFF_VALUES, DEFAULT_MAX_AGE_DAYS = 30, DEFAULT_MAX_BYTES;
|
|
91155
91894
|
var init_settings = __esm(() => {
|
|
91156
91895
|
init_state();
|
|
91157
91896
|
OFF_VALUES = new Set(["0", "false", "off"]);
|
|
91897
|
+
DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
|
|
91898
|
+
});
|
|
91899
|
+
|
|
91900
|
+
// src/telemetry/retention.ts
|
|
91901
|
+
import { rmSync as rmSync2 } from "node:fs";
|
|
91902
|
+
function remove(id, into) {
|
|
91903
|
+
try {
|
|
91904
|
+
rmSync2(sessionDir(id), { recursive: true, force: true });
|
|
91905
|
+
into.push(id);
|
|
91906
|
+
} catch {}
|
|
91907
|
+
}
|
|
91908
|
+
function sweepByAge(sessions, options = {}) {
|
|
91909
|
+
const { maxAgeMs } = options.budget ?? retentionBudget();
|
|
91910
|
+
const cutoff = (options.now ?? Date.now()) - maxAgeMs;
|
|
91911
|
+
const limit = options.limit ?? Number.POSITIVE_INFINITY;
|
|
91912
|
+
const protect = options.protect;
|
|
91913
|
+
const removed = [];
|
|
91914
|
+
for (const session3 of sessions) {
|
|
91915
|
+
if (removed.length >= limit)
|
|
91916
|
+
break;
|
|
91917
|
+
if (protect?.has(session3.id))
|
|
91918
|
+
continue;
|
|
91919
|
+
if (session3.lastEventAt !== null && session3.lastEventAt.getTime() < cutoff) {
|
|
91920
|
+
remove(session3.id, removed);
|
|
91921
|
+
}
|
|
91922
|
+
}
|
|
91923
|
+
return { removed };
|
|
91924
|
+
}
|
|
91925
|
+
function sweepToBudget(sessions, options = {}) {
|
|
91926
|
+
const { maxBytes } = options.budget ?? retentionBudget();
|
|
91927
|
+
const protect = options.protect;
|
|
91928
|
+
const removed = [];
|
|
91929
|
+
const sized = sessions.map((session3) => ({ session: session3, bytes: sessionBytes(session3.id) }));
|
|
91930
|
+
let total = sized.reduce((sum, entry2) => sum + entry2.bytes, 0);
|
|
91931
|
+
if (total <= maxBytes)
|
|
91932
|
+
return { removed };
|
|
91933
|
+
const evictable = sized.filter((entry2) => entry2.session.closed && !protect?.has(entry2.session.id)).sort((a, b) => (a.session.lastEventAt?.getTime() ?? 0) - (b.session.lastEventAt?.getTime() ?? 0));
|
|
91934
|
+
for (const analyzedFirst of [true, false]) {
|
|
91935
|
+
for (const entry2 of evictable) {
|
|
91936
|
+
if (total <= maxBytes)
|
|
91937
|
+
return { removed };
|
|
91938
|
+
if (entry2.session.analyzed !== analyzedFirst)
|
|
91939
|
+
continue;
|
|
91940
|
+
const before = removed.length;
|
|
91941
|
+
remove(entry2.session.id, removed);
|
|
91942
|
+
if (removed.length > before)
|
|
91943
|
+
total -= entry2.bytes;
|
|
91944
|
+
}
|
|
91945
|
+
}
|
|
91946
|
+
return { removed };
|
|
91947
|
+
}
|
|
91948
|
+
function sweepStore(options = {}) {
|
|
91949
|
+
const budget = options.budget ?? retentionBudget();
|
|
91950
|
+
const sessions = scanSessions();
|
|
91951
|
+
const byAge = sweepByAge(sessions, { ...options, budget });
|
|
91952
|
+
const gone = new Set(byAge.removed);
|
|
91953
|
+
const bySize = sweepToBudget(sessions.filter((session3) => !gone.has(session3.id)), { ...options, budget });
|
|
91954
|
+
return { removed: [...byAge.removed, ...bySize.removed] };
|
|
91955
|
+
}
|
|
91956
|
+
var MAX_REMOVALS_ON_START = 20;
|
|
91957
|
+
var init_retention = __esm(() => {
|
|
91958
|
+
init_settings();
|
|
91959
|
+
init_store();
|
|
91158
91960
|
});
|
|
91159
91961
|
|
|
91160
91962
|
// src/telemetry/trigger.ts
|
|
91161
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync6,
|
|
91162
|
-
import { join as
|
|
91963
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
91964
|
+
import { join as join23 } from "node:path";
|
|
91163
91965
|
function lockPath() {
|
|
91164
|
-
return
|
|
91966
|
+
return join23(sessionsRoot(), ".analyzer.lock");
|
|
91165
91967
|
}
|
|
91166
91968
|
function releaseLock() {
|
|
91167
91969
|
try {
|
|
@@ -91173,12 +91975,12 @@ function restampLock() {
|
|
|
91173
91975
|
writeFileSync3(lockPath(), JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
91174
91976
|
} catch {}
|
|
91175
91977
|
}
|
|
91176
|
-
var LOCK_STALE_MS
|
|
91978
|
+
var LOCK_STALE_MS;
|
|
91177
91979
|
var init_trigger = __esm(() => {
|
|
91980
|
+
init_retention();
|
|
91178
91981
|
init_settings();
|
|
91179
91982
|
init_store();
|
|
91180
91983
|
LOCK_STALE_MS = 30 * 60 * 1000;
|
|
91181
|
-
GC_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
91182
91984
|
});
|
|
91183
91985
|
|
|
91184
91986
|
// src/commands/session/analyze.ts
|
|
@@ -91191,12 +91993,19 @@ var init_analyze2 = __esm(() => {
|
|
|
91191
91993
|
init_source();
|
|
91192
91994
|
init_log();
|
|
91193
91995
|
init_analyze();
|
|
91996
|
+
init_retention();
|
|
91194
91997
|
init_store();
|
|
91195
91998
|
init_trigger();
|
|
91196
91999
|
analyze_default = {
|
|
91197
92000
|
name: "analyze",
|
|
91198
92001
|
description: "Analyze a recorded session for DX frictions (dry-run: writes report.md)",
|
|
91199
|
-
arguments: [
|
|
92002
|
+
arguments: [
|
|
92003
|
+
{
|
|
92004
|
+
name: "id",
|
|
92005
|
+
description: "Session id (default: most recent closed, unanalyzed)",
|
|
92006
|
+
required: false
|
|
92007
|
+
}
|
|
92008
|
+
],
|
|
91200
92009
|
options: [
|
|
91201
92010
|
{ flags: "--file", description: "File cleared findings as issues on the admin tracker" },
|
|
91202
92011
|
{ flags: "--model <model>", description: "Model for the analyzer pass" },
|
|
@@ -91206,10 +92015,10 @@ var init_analyze2 = __esm(() => {
|
|
|
91206
92015
|
action: async (id, opts) => {
|
|
91207
92016
|
if (opts.auto)
|
|
91208
92017
|
restampLock();
|
|
92018
|
+
let target2 = id;
|
|
91209
92019
|
try {
|
|
91210
|
-
let target2 = id;
|
|
91211
92020
|
if (!target2) {
|
|
91212
|
-
const candidate2 =
|
|
92021
|
+
const candidate2 = scanSessions().find((s) => s.closed && (opts.force || !s.analyzed));
|
|
91213
92022
|
if (!candidate2) {
|
|
91214
92023
|
if (!opts.auto)
|
|
91215
92024
|
log.dim("Nothing to analyze — no closed, unanalyzed session.");
|
|
@@ -91234,6 +92043,12 @@ var init_analyze2 = __esm(() => {
|
|
|
91234
92043
|
} finally {
|
|
91235
92044
|
if (opts.auto)
|
|
91236
92045
|
releaseLock();
|
|
92046
|
+
try {
|
|
92047
|
+
const swept = sweepStore({ protect: new Set(target2 === undefined ? [] : [target2]) });
|
|
92048
|
+
if (!opts.auto && swept.removed.length > 0) {
|
|
92049
|
+
log.dim(`retention: removed ${swept.removed.length} session(s)`);
|
|
92050
|
+
}
|
|
92051
|
+
} catch {}
|
|
91237
92052
|
}
|
|
91238
92053
|
}
|
|
91239
92054
|
};
|
|
@@ -91244,7 +92059,7 @@ var exports_add2 = {};
|
|
|
91244
92059
|
__export(exports_add2, {
|
|
91245
92060
|
default: () => add_default2
|
|
91246
92061
|
});
|
|
91247
|
-
import { readFile as
|
|
92062
|
+
import { readFile as readFile23 } from "node:fs/promises";
|
|
91248
92063
|
function isString(value3) {
|
|
91249
92064
|
return typeof value3 === "string";
|
|
91250
92065
|
}
|
|
@@ -91306,7 +92121,7 @@ Security:
|
|
|
91306
92121
|
if (!opts.issuer && !opts.metadata && !opts.workosAuthkit) {
|
|
91307
92122
|
throw new Error("Either --issuer, --metadata, or --workos-authkit is required");
|
|
91308
92123
|
}
|
|
91309
|
-
let metadata = opts.workosAuthkit ? workosAuthKitMetadata(opts.workosApiHostname, opts.clientId) : opts.metadata ? OidcMetadataSchema.parse(JSON.parse(await
|
|
92124
|
+
let metadata = opts.workosAuthkit ? workosAuthKitMetadata(opts.workosApiHostname, opts.clientId) : opts.metadata ? OidcMetadataSchema.parse(JSON.parse(await readFile23(opts.metadata, "utf-8"))) : await fetchOidcMetadata(opts.issuer);
|
|
91310
92125
|
if (opts.issuer) {
|
|
91311
92126
|
validateUrl(opts.issuer);
|
|
91312
92127
|
if (normalizeIssuer2(metadata.issuer) !== normalizeIssuer2(opts.issuer)) {
|
|
@@ -91872,7 +92687,7 @@ async function buildProgram() {
|
|
|
91872
92687
|
(await Promise.resolve().then(() => (init_use4(), exports_use4))).default,
|
|
91873
92688
|
(await Promise.resolve().then(() => (init_whoami3(), exports_whoami))).default,
|
|
91874
92689
|
(await Promise.resolve().then(() => (init_delete2(), exports_delete2))).default,
|
|
91875
|
-
(await Promise.resolve().then(() => (
|
|
92690
|
+
(await Promise.resolve().then(() => (init_sync2(), exports_sync))).default,
|
|
91876
92691
|
(await Promise.resolve().then(() => (init_unsync(), exports_unsync))).default,
|
|
91877
92692
|
(await Promise.resolve().then(() => (init_export2(), exports_export))).default,
|
|
91878
92693
|
(await Promise.resolve().then(() => (init_import2(), exports_import))).default
|
|
@@ -91996,16 +92811,16 @@ function redactArgv(argv) {
|
|
|
91996
92811
|
|
|
91997
92812
|
// src/telemetry/session.ts
|
|
91998
92813
|
init_store();
|
|
91999
|
-
import { createHash as
|
|
92814
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
92000
92815
|
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
92001
|
-
import { dirname as
|
|
92816
|
+
import { dirname as dirname14, join as join24 } from "node:path";
|
|
92002
92817
|
var MAX_ID_LEN = 64;
|
|
92003
92818
|
function findGitRoot(start) {
|
|
92004
92819
|
let dir = start;
|
|
92005
92820
|
for (;; ) {
|
|
92006
|
-
if (existsSync13(
|
|
92821
|
+
if (existsSync13(join24(dir, ".git")))
|
|
92007
92822
|
return dir;
|
|
92008
|
-
const parent =
|
|
92823
|
+
const parent = dirname14(dir);
|
|
92009
92824
|
if (parent === dir)
|
|
92010
92825
|
return null;
|
|
92011
92826
|
dir = parent;
|
|
@@ -92018,19 +92833,21 @@ function stamp(d = new Date) {
|
|
|
92018
92833
|
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
92019
92834
|
return `${p(d.getFullYear(), 4)}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}`;
|
|
92020
92835
|
}
|
|
92021
|
-
function findOpenAmbient(root) {
|
|
92022
|
-
for (const s of
|
|
92023
|
-
if (s.
|
|
92836
|
+
function findOpenAmbient(root, sessions) {
|
|
92837
|
+
for (const s of sessions) {
|
|
92838
|
+
if (s.closed || s.analyzed)
|
|
92839
|
+
continue;
|
|
92840
|
+
const meta3 = readMeta(s.id);
|
|
92841
|
+
if (meta3?.root === root && meta3.explicit === false)
|
|
92024
92842
|
return s.id;
|
|
92025
|
-
}
|
|
92026
92843
|
}
|
|
92027
92844
|
return null;
|
|
92028
92845
|
}
|
|
92029
92846
|
function mintAmbientId(root) {
|
|
92030
|
-
const hash8 =
|
|
92847
|
+
const hash8 = createHash4("sha256").update(root).digest("hex").slice(0, 8);
|
|
92031
92848
|
return `amb-${hash8}-${stamp()}`;
|
|
92032
92849
|
}
|
|
92033
|
-
function resolveSession2(cwd) {
|
|
92850
|
+
function resolveSession2(cwd, sessions) {
|
|
92034
92851
|
const root = findGitRoot(cwd) ?? cwd;
|
|
92035
92852
|
const pinned = process.env.ASTRALE_SESSION;
|
|
92036
92853
|
if (pinned) {
|
|
@@ -92038,10 +92855,11 @@ function resolveSession2(cwd) {
|
|
|
92038
92855
|
if (id.length > 0)
|
|
92039
92856
|
return { id, root, explicit: true };
|
|
92040
92857
|
}
|
|
92041
|
-
|
|
92858
|
+
const scan2 = sessions ?? scanSessions();
|
|
92859
|
+
return { id: findOpenAmbient(root, scan2) ?? mintAmbientId(root), root, explicit: false };
|
|
92042
92860
|
}
|
|
92043
|
-
function ensureSession(cwd) {
|
|
92044
|
-
const resolved = resolveSession2(cwd);
|
|
92861
|
+
function ensureSession(cwd, sessions) {
|
|
92862
|
+
const resolved = resolveSession2(cwd, sessions);
|
|
92045
92863
|
const dir = sessionDir(resolved.id);
|
|
92046
92864
|
if (!existsSync13(dir)) {
|
|
92047
92865
|
try {
|
|
@@ -92075,7 +92893,7 @@ var HELP_VERSION = new Set([
|
|
|
92075
92893
|
function isHelpOrVersion(args) {
|
|
92076
92894
|
return args.every((a) => HELP_VERSION.has(a));
|
|
92077
92895
|
}
|
|
92078
|
-
function beginInvocation(argv) {
|
|
92896
|
+
function beginInvocation(argv, sessions) {
|
|
92079
92897
|
try {
|
|
92080
92898
|
const args = argv.slice(2);
|
|
92081
92899
|
if (!telemetryEnabled() || isHelpOrVersion(args))
|
|
@@ -92083,7 +92901,7 @@ function beginInvocation(argv) {
|
|
|
92083
92901
|
const startMs = Date.now();
|
|
92084
92902
|
const ts = new Date(startMs).toISOString();
|
|
92085
92903
|
const cwd = process.cwd();
|
|
92086
|
-
const { id, root } = ensureSession(cwd);
|
|
92904
|
+
const { id, root } = ensureSession(cwd, sessions);
|
|
92087
92905
|
return (exitCode, errorName) => {
|
|
92088
92906
|
try {
|
|
92089
92907
|
const event = {
|
|
@@ -92107,37 +92925,61 @@ function beginInvocation(argv) {
|
|
|
92107
92925
|
}
|
|
92108
92926
|
}
|
|
92109
92927
|
|
|
92928
|
+
// src/telemetry/store.ts
|
|
92929
|
+
init_state();
|
|
92930
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync7, statSync as statSync6 } from "node:fs";
|
|
92931
|
+
import { join as join25 } from "node:path";
|
|
92932
|
+
var IDLE_WINDOW_MS2 = 30 * 60 * 1000;
|
|
92933
|
+
function sessionsRoot2() {
|
|
92934
|
+
return join25(createPaths().home, "sessions");
|
|
92935
|
+
}
|
|
92936
|
+
function sessionDir2(id) {
|
|
92937
|
+
return join25(sessionsRoot2(), id);
|
|
92938
|
+
}
|
|
92939
|
+
function eventsPath2(id) {
|
|
92940
|
+
return join25(sessionDir2(id), "events.jsonl");
|
|
92941
|
+
}
|
|
92942
|
+
function markerPath2(id) {
|
|
92943
|
+
return join25(sessionDir2(id), ".analyzed");
|
|
92944
|
+
}
|
|
92945
|
+
function sessionIds2() {
|
|
92946
|
+
try {
|
|
92947
|
+
return readdirSync5(sessionsRoot2()).filter((name) => !name.startsWith("."));
|
|
92948
|
+
} catch {
|
|
92949
|
+
return [];
|
|
92950
|
+
}
|
|
92951
|
+
}
|
|
92952
|
+
function scanSessions2(now = Date.now()) {
|
|
92953
|
+
return sessionIds2().map((id) => {
|
|
92954
|
+
let lastEventAt = null;
|
|
92955
|
+
try {
|
|
92956
|
+
lastEventAt = statSync6(eventsPath2(id)).mtime;
|
|
92957
|
+
} catch {}
|
|
92958
|
+
return {
|
|
92959
|
+
id,
|
|
92960
|
+
lastEventAt,
|
|
92961
|
+
closed: lastEventAt !== null && now - lastEventAt.getTime() > IDLE_WINDOW_MS2,
|
|
92962
|
+
analyzed: existsSync14(markerPath2(id))
|
|
92963
|
+
};
|
|
92964
|
+
}).sort((a, b) => (b.lastEventAt?.getTime() ?? 0) - (a.lastEventAt?.getTime() ?? 0));
|
|
92965
|
+
}
|
|
92966
|
+
|
|
92110
92967
|
// src/telemetry/trigger.ts
|
|
92968
|
+
init_retention();
|
|
92111
92969
|
init_settings();
|
|
92112
92970
|
init_store();
|
|
92113
92971
|
import { spawn as spawn4 } from "node:child_process";
|
|
92114
|
-
import { existsSync as
|
|
92115
|
-
import { join as
|
|
92972
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
92973
|
+
import { join as join26 } from "node:path";
|
|
92116
92974
|
var LOCK_STALE_MS2 = 30 * 60 * 1000;
|
|
92117
|
-
var GC_AGE_MS2 = 30 * 24 * 60 * 60 * 1000;
|
|
92118
|
-
var GC_MAX_PER_RUN = 20;
|
|
92119
|
-
function gcOldSessions(sessions) {
|
|
92120
|
-
try {
|
|
92121
|
-
const cutoff = Date.now() - GC_AGE_MS2;
|
|
92122
|
-
let removed = 0;
|
|
92123
|
-
for (const s of sessions) {
|
|
92124
|
-
if (removed >= GC_MAX_PER_RUN)
|
|
92125
|
-
break;
|
|
92126
|
-
if (s.analyzed !== null && s.lastEventAt !== null && s.lastEventAt.getTime() < cutoff) {
|
|
92127
|
-
rmSync3(sessionDir(s.id), { recursive: true, force: true });
|
|
92128
|
-
removed++;
|
|
92129
|
-
}
|
|
92130
|
-
}
|
|
92131
|
-
} catch {}
|
|
92132
|
-
}
|
|
92133
92975
|
function lockPath2() {
|
|
92134
|
-
return
|
|
92976
|
+
return join26(sessionsRoot(), ".analyzer.lock");
|
|
92135
92977
|
}
|
|
92136
92978
|
function claimLock() {
|
|
92137
92979
|
const path8 = lockPath2();
|
|
92138
92980
|
try {
|
|
92139
|
-
if (
|
|
92140
|
-
const lock = JSON.parse(
|
|
92981
|
+
if (existsSync15(path8)) {
|
|
92982
|
+
const lock = JSON.parse(readFileSync8(path8, "utf-8"));
|
|
92141
92983
|
const stale2 = Date.now() - lock.at > LOCK_STALE_MS2;
|
|
92142
92984
|
let alive = false;
|
|
92143
92985
|
try {
|
|
@@ -92155,17 +92997,17 @@ function claimLock() {
|
|
|
92155
92997
|
return false;
|
|
92156
92998
|
}
|
|
92157
92999
|
}
|
|
92158
|
-
function maybeTriggerAnalysis(argv) {
|
|
93000
|
+
function maybeTriggerAnalysis(argv, scan2) {
|
|
92159
93001
|
try {
|
|
92160
|
-
if (!telemetryEnabled())
|
|
92161
|
-
return;
|
|
92162
93002
|
if (process.env.ASTRALE_TELEMETRY_NO_TRIGGER === "1")
|
|
92163
93003
|
return;
|
|
93004
|
+
const sessions = scan2 ?? scanSessions();
|
|
93005
|
+
const swept = new Set(sweepByAge(sessions, { limit: MAX_REMOVALS_ON_START }).removed);
|
|
93006
|
+
if (!telemetryEnabled())
|
|
93007
|
+
return;
|
|
92164
93008
|
if (argv[2] === "session")
|
|
92165
93009
|
return;
|
|
92166
|
-
const
|
|
92167
|
-
gcOldSessions(sessions);
|
|
92168
|
-
const target2 = sessions.find((s) => s.closed && s.analyzed === null && s.lastEventAt !== null);
|
|
93010
|
+
const target2 = sessions.find((s) => s.closed && !s.analyzed && s.lastEventAt !== null && !swept.has(s.id));
|
|
92169
93011
|
if (!target2)
|
|
92170
93012
|
return;
|
|
92171
93013
|
if (!claimLock())
|
|
@@ -92188,11 +93030,12 @@ function overrideExits(command) {
|
|
|
92188
93030
|
for (const child3 of command.commands)
|
|
92189
93031
|
overrideExits(child3);
|
|
92190
93032
|
}
|
|
92191
|
-
var
|
|
93033
|
+
var sessions = scanSessions2();
|
|
93034
|
+
var finalize2 = process.argv[2] === "session" ? undefined : beginInvocation(process.argv, sessions);
|
|
92192
93035
|
var errorName;
|
|
92193
93036
|
if (finalize2)
|
|
92194
93037
|
process.on("exit", (code) => finalize2(code ?? 0, errorName));
|
|
92195
|
-
maybeTriggerAnalysis(process.argv);
|
|
93038
|
+
maybeTriggerAnalysis(process.argv, sessions);
|
|
92196
93039
|
var argv = normalizeRootVersionArgv(process.argv);
|
|
92197
93040
|
configureInvocation(argv);
|
|
92198
93041
|
var program4 = await buildProgram();
|