@astrale-os/cli 1.0.0-beta.25 → 1.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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.25",
2581
+ version: "1.0.0-beta.27",
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",
@@ -41750,7 +41751,7 @@ var require_node_gyp_build_optional_packages = __commonJS(function(exports, modu
41750
41751
 
41751
41752
  // node_modules/.pnpm/msgpackr-extract@3.0.4/node_modules/msgpackr-extract/index.js
41752
41753
  var require_msgpackr_extract = __commonJS(function(exports, module) {
41753
- var __dirname = "/home/runner/work/cli/cli/node_modules/.pnpm/msgpackr-extract@3.0.4/node_modules/msgpackr-extract";
41754
+ var __dirname = "/private/tmp/astrale-cli-beta27-a3227da/node_modules/.pnpm/msgpackr-extract@3.0.4/node_modules/msgpackr-extract";
41754
41755
  module.exports = require_node_gyp_build_optional_packages()(__dirname);
41755
41756
  });
41756
41757
 
@@ -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 { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync } from "node:fs";
61300
- import { homedir as homedir2 } from "node:os";
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 installSkills() {
61303
- const { code, stdout, stderr } = await run("npx", [
61304
- "skills",
61305
- "add",
61306
- ASTRALE_CLI_SKILL_SOURCE,
61307
- "-g",
61308
- "-y"
61309
- ]);
61310
- if (code !== 0)
61311
- process.stderr.write(stdout + stderr);
61312
- return code === 0;
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));
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 })));
61313
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(join6(cur, ".claude", "skills"));
61319
- const parent = dirname8(cur);
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(join6(homedir2(), ".claude", "skills"));
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 = join6(dir, name, "SKILL.md");
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(join6(cur, ".agents", "skills")))
61979
+ if (existsSync(join8(cur, ".agents", "skills")))
61349
61980
  return cur;
61350
- const parent = dirname8(cur);
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 = join6(root, ".claude", "skills");
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(dirname8(status.link), { recursive: true });
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 ASTRALE_CLI_SKILL = "astrale-cli", ASTRALE_DOMAIN_SKILL = "astrale-domain", AGENT_BROWSER_SKILL = "agent-browser", ASTRALE_CLI_SKILL_SOURCE = "astrale-os/cli", SKILL_INSTALL_HINT, BRIDGE_TARGET;
62011
+ var AGENT_BROWSER_SKILL = "agent-browser", BRIDGE_TARGET;
61381
62012
  var init_skills = __esm(() => {
61382
62013
  init_browser();
61383
- init_proc();
61384
- SKILL_INSTALL_HINT = `npx skills add ${ASTRALE_CLI_SKILL_SOURCE} -g`;
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 join7 } from "node:path";
62258
+ import { join as join9 } from "node:path";
61629
62259
  function hasDomainProject() {
61630
- return existsSync2(join7(process.cwd(), "astrale.config.ts"));
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 resolve3 = (ref) => {
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(resolve3);
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
- resolve3(definition.ref);
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 resolve3(schema) {
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
- resolve3(dependent);
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
- resolve3(source2);
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
- resolve3(target2);
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: () => resolve4,
74575
+ resolve: () => resolve5,
73946
74576
  revision: () => revisionOfDomainSchema,
73947
74577
  serialize: () => serializeDomainSchema,
73948
74578
  values: () => exports_value_api
73949
74579
  });
73950
- var resolve4;
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
- resolve4 = resolve3;
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 resolve5;
76342
+ var resolve6;
75713
76343
  var init_view5 = __esm(() => {
75714
76344
  init_kernel();
75715
76345
  init_view();
75716
76346
  init_view_resolution();
75717
- resolve5 = methodSyscall(resolve, K.classes.View.static.methods.resolve);
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 join8(chunks, total);
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 join8(parts, length) {
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((resolve6, reject) => {
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
- resolve6(result);
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, bothInstalled = () => detectSkill(ASTRALE_CLI_SKILL).installed && detectSkill(ASTRALE_DOMAIN_SKILL).installed, skillsStep;
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: "astrale agent skills (cli + domain)",
81345
+ title: "Astrale agent skills",
80716
81346
  group: "equip",
80717
81347
  async detect() {
80718
- if (bothInstalled()) {
80719
- return { state: "satisfied", summary: "astrale-cli + astrale-domain skills installed" };
81348
+ const result = await checkAstraleSkills();
81349
+ if (result.status === "current") {
81350
+ return { state: "satisfied", summary: "Astrale skills are installed and up to date" };
80720
81351
  }
80721
- const have = detectSkill(ASTRALE_CLI_SKILL).installed ? "astrale-domain" : "astrale agent skills";
80722
- return { state: "gap", summary: `${have} not installed`, fixHint: FIX5 };
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
+ };
81359
+ }
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
- if (bothInstalled()) {
80726
- log.success("astrale-cli + astrale-domain skills already installed");
80727
- return "unchanged";
80728
- }
80729
- log.step(`Installing the astrale agent skills — ${FIX5}`);
80730
- if (!await installSkills()) {
80731
- log.warn(`Skill install did not complete — run it later: ${FIX5}`);
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 join9 } from "node:path";
81392
+ import { join as join11 } from "node:path";
80747
81393
  function countStaged(root) {
80748
81394
  try {
80749
- const dir = join9(root, ".agents", "skills");
80750
- return readdirSync(dir).filter((e) => existsSync3(join9(dir, e, "SKILL.md"))).length;
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 — ${join9(after.root, ".agents/skills")} → .claude/skills`);
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 join10 } from "node:path";
81902
+ import { join as join12 } from "node:path";
81257
81903
  function inDomainProject(cwd = process.cwd()) {
81258
- return existsSync4(join10(cwd, "astrale.config.ts"));
81904
+ return existsSync4(join12(cwd, "astrale.config.ts"));
81259
81905
  }
81260
81906
  function foreignPackageManager(cwd = process.cwd()) {
81261
- if (existsSync4(join10(cwd, "pnpm-lock.yaml")))
81907
+ if (existsSync4(join12(cwd, "pnpm-lock.yaml")))
81262
81908
  return null;
81263
- if (existsSync4(join10(cwd, "package-lock.json")))
81909
+ if (existsSync4(join12(cwd, "package-lock.json")))
81264
81910
  return "npm";
81265
- if (existsSync4(join10(cwd, "yarn.lock")))
81911
+ if (existsSync4(join12(cwd, "yarn.lock")))
81266
81912
  return "yarn";
81267
- if (existsSync4(join10(cwd, "bun.lockb")) || existsSync4(join10(cwd, "bun.lock")))
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
- if (!detectSkill(ASTRALE_CLI_SKILL).installed) {
81321
- log.dim(` Agent skills not installed — get them with: ${SKILL_INSTALL_HINT}`);
81322
- return;
81323
- }
81324
- log.step(`Refreshing the astrale agent skills — ${SKILL_INSTALL_HINT}`);
81325
- if (await installSkills()) {
81326
- log.success("astrale agent skills up to date");
81327
- } else {
81328
- log.warn(`Skill refresh did not complete — run it later: ${SKILL_INSTALL_HINT}`);
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 refreshing the astrale agent skills" },
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: if the astrale skills (cli +
81469
- domain) are already installed, refreshes them to the latest by delegating to
81470
- "npx skills add astrale-os/cli -g" the same installer "astrale setup" uses;
81471
- fresh installs go through "astrale setup". (3) SDK deps: inside a pnpm domain
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 non-throwing, skills omitted (they ride
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; this is what domain-studio's "Update now" runs.
81484
- --no-skills / --no-deps skip those steps in a real run.
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 = { stale: cli.stale || sdk.stale, cli, sdk };
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 (!opts.check && opts.skills !== false)
81540
- await refreshSkills();
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 writeFile5 } from "node:fs/promises";
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 writeFile5(io.outFile, bytes);
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;
@@ -82114,7 +82790,7 @@ __export(exports_mutate, {
82114
82790
  default: () => mutate_default,
82115
82791
  mutateCommand: () => mutateCommand
82116
82792
  });
82117
- import { readFile as readFile10 } from "node:fs/promises";
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 readFile10(opts.file, "utf8");
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 readFile11 } from "node:fs/promises";
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 readFile11(opts.file, "utf8");
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((resolve6) => setTimeout(resolve6, milliseconds))
83287
+ pause: (milliseconds) => new Promise((resolve7) => setTimeout(resolve7, milliseconds))
82612
83288
  }) {
82613
83289
  validateLogsOpts(opts);
82614
83290
  await dependencies.run({
@@ -82814,10 +83490,10 @@ Examples:
82814
83490
  // src/lib/port.ts
82815
83491
  import net from "node:net";
82816
83492
  function portFree(port, host = LOOPBACK) {
82817
- return new Promise((resolve6) => {
83493
+ return new Promise((resolve7) => {
82818
83494
  const srv = net.createServer();
82819
- srv.once("error", () => resolve6(false));
82820
- srv.once("listening", () => srv.close(() => resolve6(true)));
83495
+ srv.once("error", () => resolve7(false));
83496
+ srv.once("listening", () => srv.close(() => resolve7(true)));
82821
83497
  srv.listen({ port, host, exclusive: true });
82822
83498
  });
82823
83499
  }
@@ -82859,10 +83535,10 @@ var init_external_open_origins = __esm(() => {
82859
83535
 
82860
83536
  // src/lib/view/session.ts
82861
83537
  import { closeSync, fchmodSync, openSync } from "node:fs";
82862
- import { chmod as chmod3, mkdir as mkdir9, readdir as readdir2, readFile as readFile12, rm as rm2 } from "node:fs/promises";
82863
- import { join as join11 } from "node:path";
83538
+ import { chmod as chmod3, mkdir as mkdir11, readdir as readdir4, readFile as readFile14, rm as rm4 } from "node:fs/promises";
83539
+ import { join as join13 } from "node:path";
82864
83540
  async function ensureViewDirectory(directory = VIEW_DIR) {
82865
- await mkdir9(directory, { recursive: true, mode: 448 });
83541
+ await mkdir11(directory, { recursive: true, mode: 448 });
82866
83542
  await chmod3(directory, 448);
82867
83543
  }
82868
83544
  async function saveRecord(record12, directory = VIEW_DIR) {
@@ -82888,9 +83564,9 @@ async function openSessionLog(id, directory = VIEW_DIR) {
82888
83564
  }
82889
83565
  async function removeSessionFiles(id, directory = VIEW_DIR) {
82890
83566
  await Promise.all([
82891
- rm2(recordPath(id, directory), { force: true }),
82892
- rm2(configPath(id, directory), { force: true }),
82893
- rm2(logPath(id, directory), { force: true })
83567
+ rm4(recordPath(id, directory), { force: true }),
83568
+ rm4(configPath(id, directory), { force: true }),
83569
+ rm4(logPath(id, directory), { force: true })
82894
83570
  ]);
82895
83571
  }
82896
83572
  function isAlive(pid) {
@@ -82904,7 +83580,7 @@ function isAlive(pid) {
82904
83580
  async function listSessions() {
82905
83581
  let entries;
82906
83582
  try {
82907
- entries = await readdir2(VIEW_DIR);
83583
+ entries = await readdir4(VIEW_DIR);
82908
83584
  } catch {
82909
83585
  return [];
82910
83586
  }
@@ -82914,7 +83590,7 @@ async function listSessions() {
82914
83590
  continue;
82915
83591
  let record12;
82916
83592
  try {
82917
- record12 = JSON.parse(await readFile12(join11(VIEW_DIR, entry2), "utf8"));
83593
+ record12 = JSON.parse(await readFile14(join13(VIEW_DIR, entry2), "utf8"));
82918
83594
  } catch {
82919
83595
  continue;
82920
83596
  }
@@ -82932,7 +83608,7 @@ async function closeSession(record12) {
82932
83608
  } catch {}
82933
83609
  const deadline = Date.now() + CLOSE_GRACE_MS;
82934
83610
  while (isAlive(record12.pid) && Date.now() < deadline) {
82935
- await new Promise((resolve6) => setTimeout(resolve6, 100));
83611
+ await new Promise((resolve7) => setTimeout(resolve7, 100));
82936
83612
  }
82937
83613
  if (isAlive(record12.pid)) {
82938
83614
  try {
@@ -82942,14 +83618,14 @@ async function closeSession(record12) {
82942
83618
  }
82943
83619
  await removeSessionFiles(record12.id);
82944
83620
  }
82945
- var VIEW_DIR, recordPath = (id, directory = VIEW_DIR) => join11(directory, `${id}.json`), logPath = (id, directory = VIEW_DIR) => join11(directory, `${id}.log`), configPath = (id, directory = VIEW_DIR) => join11(directory, `${id}.config.json`), CLOSE_GRACE_MS = 2000;
83621
+ 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
83622
  var init_session5 = __esm(() => {
82947
83623
  init_state();
82948
- VIEW_DIR = join11(paths2.home, "view");
83624
+ VIEW_DIR = join13(paths2.home, "view");
82949
83625
  });
82950
83626
 
82951
83627
  // src/lib/view/port-allocation.ts
82952
- import { join as join12 } from "node:path";
83628
+ import { join as join14 } from "node:path";
82953
83629
  function withViewPortAllocationLock(fn, lockPath = VIEW_PORT_LOCK) {
82954
83630
  return withFileLock(lockPath, fn);
82955
83631
  }
@@ -82957,7 +83633,7 @@ var VIEW_PORT_LOCK;
82957
83633
  var init_port_allocation = __esm(() => {
82958
83634
  init_state();
82959
83635
  init_session5();
82960
- VIEW_PORT_LOCK = join12(VIEW_DIR, "ports.lock");
83636
+ VIEW_PORT_LOCK = join14(VIEW_DIR, "ports.lock");
82961
83637
  });
82962
83638
 
82963
83639
  // src/lib/view/resolve.ts
@@ -83027,33 +83703,33 @@ var init_resolve2 = __esm(() => {
83027
83703
  // src/lib/view/assets.ts
83028
83704
  import { existsSync as existsSync5, statSync } from "node:fs";
83029
83705
  import { copyFile as copyFile2 } from "node:fs/promises";
83030
- import { dirname as dirname9, join as join13 } from "node:path";
83706
+ import { dirname as dirname11, join as join15 } from "node:path";
83031
83707
  import { fileURLToPath } from "node:url";
83032
83708
  function viewerDistDir(moduleUrl = import.meta.url, entry2 = process.argv[1] ?? ".", executable = process.execPath) {
83033
83709
  const override = process.env.ASTRALE_VIEWER_DIR;
83034
83710
  if (override)
83035
83711
  return override;
83036
- const moduleDirectory = dirname9(fileURLToPath(moduleUrl));
83037
- const published = join13(moduleDirectory, "..", "viewer", "dist");
83038
- const source2 = join13(moduleDirectory, "..", "..", "..", "viewer", "dist");
83039
- const legacy = join13(dirname9(entry2), "..", "viewer", "dist");
83040
- const standalone = entry2.startsWith("/$bunfs/") ? join13(dirname9(executable), "viewer", "dist") : undefined;
83712
+ const moduleDirectory = dirname11(fileURLToPath(moduleUrl));
83713
+ const published = join15(moduleDirectory, "..", "viewer", "dist");
83714
+ const source2 = join15(moduleDirectory, "..", "..", "..", "viewer", "dist");
83715
+ const legacy = join15(dirname11(entry2), "..", "viewer", "dist");
83716
+ const standalone = entry2.startsWith("/$bunfs/") ? join15(dirname11(executable), "viewer", "dist") : undefined;
83041
83717
  const complete = [standalone, published, source2, legacy].find((candidate2) => candidate2 !== undefined && hasViewerBundle(candidate2));
83042
83718
  if (complete)
83043
83719
  return complete;
83044
- if (hasViewerSource(join13(source2, "..")))
83720
+ if (hasViewerSource(join15(source2, "..")))
83045
83721
  return source2;
83046
83722
  return published;
83047
83723
  }
83048
83724
  async function ensureViewerAssets(moduleUrl = import.meta.url, entry2 = process.argv[1] ?? ".") {
83049
83725
  const dist = viewerDistDir(moduleUrl, entry2);
83050
- const srcDir = join13(dist, "..");
83726
+ const srcDir = join15(dist, "..");
83051
83727
  if (hasViewerBundle(dist) && !viewerSourceIsNewer(srcDir, dist))
83052
83728
  return dist;
83053
83729
  const bun = globalThis.Bun;
83054
83730
  if (bun && hasViewerSource(srcDir)) {
83055
83731
  const result = await bun.build({
83056
- entrypoints: [join13(srcDir, "main.ts")],
83732
+ entrypoints: [join15(srcDir, "main.ts")],
83057
83733
  outdir: dist,
83058
83734
  target: "browser",
83059
83735
  minify: false
@@ -83061,32 +83737,32 @@ async function ensureViewerAssets(moduleUrl = import.meta.url, entry2 = process.
83061
83737
  if (!result.success)
83062
83738
  throw new Error(`viewer build failed: ${result.logs.join(`
83063
83739
  `)}`);
83064
- await copyFile2(join13(srcDir, "index.html"), join13(dist, "index.html"));
83740
+ await copyFile2(join15(srcDir, "index.html"), join15(dist, "index.html"));
83065
83741
  return dist;
83066
83742
  }
83067
83743
  throw new Error(`viewer bundle missing at ${dist} — reinstall the CLI (or run \`bun scripts/build.ts\` in a dev checkout)`);
83068
83744
  }
83069
83745
  function hasViewerBundle(directory) {
83070
- return existsSync5(join13(directory, "main.js")) && existsSync5(join13(directory, "index.html"));
83746
+ return existsSync5(join15(directory, "main.js")) && existsSync5(join15(directory, "index.html"));
83071
83747
  }
83072
83748
  function hasViewerSource(directory) {
83073
- return existsSync5(join13(directory, "main.ts")) && existsSync5(join13(directory, "index.html"));
83749
+ return existsSync5(join15(directory, "main.ts")) && existsSync5(join15(directory, "index.html"));
83074
83750
  }
83075
83751
  function viewerSourceIsNewer(source2, dist) {
83076
83752
  if (!hasViewerSource(source2))
83077
83753
  return false;
83078
83754
  if (!hasViewerBundle(dist))
83079
83755
  return true;
83080
- const newestSource = Math.max(statSync(join13(source2, "main.ts")).mtimeMs, statSync(join13(source2, "index.html")).mtimeMs);
83081
- const oldestOutput = Math.min(statSync(join13(dist, "main.js")).mtimeMs, statSync(join13(dist, "index.html")).mtimeMs);
83756
+ const newestSource = Math.max(statSync(join15(source2, "main.ts")).mtimeMs, statSync(join15(source2, "index.html")).mtimeMs);
83757
+ const oldestOutput = Math.min(statSync(join15(dist, "main.js")).mtimeMs, statSync(join15(dist, "index.html")).mtimeMs);
83082
83758
  return newestSource > oldestOutput;
83083
83759
  }
83084
83760
  var init_assets = () => {};
83085
83761
 
83086
83762
  // src/lib/view/server.ts
83087
- import { readFile as readFile13 } from "node:fs/promises";
83763
+ import { readFile as readFile15 } from "node:fs/promises";
83088
83764
  import { createServer } from "node:http";
83089
- import { join as join14 } from "node:path";
83765
+ import { join as join16 } from "node:path";
83090
83766
  import { Readable } from "node:stream";
83091
83767
  function startViewServer(config2) {
83092
83768
  const { session: session3, proxy } = config2;
@@ -83134,11 +83810,11 @@ function startViewServer(config2) {
83134
83810
  return;
83135
83811
  }
83136
83812
  if (sub === "/" || sub === "/index.html") {
83137
- await serveAsset(res, join14(hostDir, "index.html"), "text/html; charset=utf-8");
83813
+ await serveAsset(res, join16(hostDir, "index.html"), "text/html; charset=utf-8");
83138
83814
  return;
83139
83815
  }
83140
83816
  if (sub === "/main.js") {
83141
- await serveAsset(res, join14(hostDir, "main.js"), "text/javascript; charset=utf-8");
83817
+ await serveAsset(res, join16(hostDir, "main.js"), "text/javascript; charset=utf-8");
83142
83818
  return;
83143
83819
  }
83144
83820
  if (sub === "/config.json" && req.method === "GET") {
@@ -83258,7 +83934,7 @@ function json3(res, code, body) {
83258
83934
  }
83259
83935
  async function serveAsset(res, file2, contentType) {
83260
83936
  try {
83261
- const content = await readFile13(file2);
83937
+ const content = await readFile15(file2);
83262
83938
  res.writeHead(200, { "content-type": contentType, "cache-control": "no-store" });
83263
83939
  res.end(content);
83264
83940
  } catch {
@@ -83350,7 +84026,7 @@ var SETTLE_TIMEOUT_MS = 8000, QUIET_WINDOW_MS = 750, POLL_MS = 250, realClock, d
83350
84026
  var init_snapshot = __esm(() => {
83351
84027
  realClock = {
83352
84028
  now: Date.now,
83353
- sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms))
84029
+ sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))
83354
84030
  };
83355
84031
  defaultPolicy = {
83356
84032
  timeoutMs: SETTLE_TIMEOUT_MS,
@@ -83374,8 +84050,8 @@ __export(exports_view3, {
83374
84050
  });
83375
84051
  import { randomBytes } from "node:crypto";
83376
84052
  import { closeSync as closeSync2, existsSync as existsSync6, statSync as statSync2 } from "node:fs";
83377
- import { readdir as readdir3, readFile as readFile14 } from "node:fs/promises";
83378
- import { dirname as dirname10, join as join15 } from "node:path";
84053
+ import { readdir as readdir5, readFile as readFile16 } from "node:fs/promises";
84054
+ import { dirname as dirname12, join as join17 } from "node:path";
83379
84055
  async function resolveSession(spec, opts) {
83380
84056
  rejectUnrepresentableOverrides(opts);
83381
84057
  const parsed = parseViewSpec(spec);
@@ -83430,7 +84106,7 @@ async function resolveServeRuntime(environment = {}) {
83430
84106
  if (node4 && entry2?.endsWith(".js") && exists(entry2))
83431
84107
  return { file: node4, args: [entry2] };
83432
84108
  if (node4 && entry2?.endsWith(".ts")) {
83433
- const dist = join15(dirname10(entry2), "..", "dist", "astrale.js");
84109
+ const dist = join17(dirname12(entry2), "..", "dist", "astrale.js");
83434
84110
  await ensureDevDist(entry2, dist);
83435
84111
  if (exists(dist))
83436
84112
  return { file: node4, args: [dist] };
@@ -83456,8 +84132,8 @@ async function findOnPath(name) {
83456
84132
  async function ensureDevDist(entry2, dist) {
83457
84133
  if (!await devDistIsStale(entry2, dist))
83458
84134
  return;
83459
- const projectDir = join15(dirname10(entry2), "..");
83460
- const buildScript = join15(projectDir, "scripts", "build.ts");
84135
+ const projectDir = join17(dirname12(entry2), "..");
84136
+ const buildScript = join17(projectDir, "scripts", "build.ts");
83461
84137
  const bun = await findOnPath("bun");
83462
84138
  if (!bun || !existsSync6(buildScript))
83463
84139
  return;
@@ -83473,13 +84149,13 @@ async function ensureDevDist(entry2, dist) {
83473
84149
  async function devDistIsStale(entry2, dist) {
83474
84150
  if (!existsSync6(dist))
83475
84151
  return true;
83476
- const projectDir = join15(dirname10(entry2), "..");
84152
+ const projectDir = join17(dirname12(entry2), "..");
83477
84153
  const builtAt = statSync2(dist).mtimeMs;
83478
- const directories = [join15(projectDir, "src"), join15(projectDir, "bin"), join15(projectDir, "vendor")];
84154
+ const directories = [join17(projectDir, "src"), join17(projectDir, "bin"), join17(projectDir, "vendor")];
83479
84155
  const files = [
83480
- join15(projectDir, "scripts", "build.ts"),
83481
- join15(projectDir, "package.json"),
83482
- join15(projectDir, "pnpm-lock.yaml")
84156
+ join17(projectDir, "scripts", "build.ts"),
84157
+ join17(projectDir, "package.json"),
84158
+ join17(projectDir, "pnpm-lock.yaml")
83483
84159
  ];
83484
84160
  for (const directory of directories) {
83485
84161
  if (existsSync6(directory) && await newerThan(directory, builtAt))
@@ -83488,11 +84164,11 @@ async function devDistIsStale(entry2, dist) {
83488
84164
  return files.some((file2) => existsSync6(file2) && statSync2(file2).mtimeMs > builtAt);
83489
84165
  }
83490
84166
  async function newerThan(dir, mtimeMs) {
83491
- const entries = await readdir3(dir, { withFileTypes: true, recursive: true });
84167
+ const entries = await readdir5(dir, { withFileTypes: true, recursive: true });
83492
84168
  for (const item of entries) {
83493
84169
  if (!item.isFile())
83494
84170
  continue;
83495
- if (statSync2(join15(item.parentPath, item.name)).mtimeMs > mtimeMs)
84171
+ if (statSync2(join17(item.parentPath, item.name)).mtimeMs > mtimeMs)
83496
84172
  return true;
83497
84173
  }
83498
84174
  return false;
@@ -83570,7 +84246,7 @@ async function startSessionLocked(view2, opts, kernelTarget, activeInstance, def
83570
84246
  break;
83571
84247
  await sleep2(POLL_MS2);
83572
84248
  }
83573
- const tail = await readFile14(logPath(id), "utf8").catch(() => "");
84249
+ const tail = await readFile16(logPath(id), "utf8").catch(() => "");
83574
84250
  await closeSession(live);
83575
84251
  throw new Error(`View session server did not come up.${tail ? `
83576
84252
  --- server log ---
@@ -83705,7 +84381,7 @@ async function sessionsCommand(opts) {
83705
84381
  console.log(`${source_default.bold(s.id)} /:${s.view.route.key} target ${s.view.target} ${source_default.dim(s.pageUrl)}`);
83706
84382
  }
83707
84383
  }
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((resolve6) => setTimeout(resolve6, ms)), view_default;
84384
+ 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
84385
  var init_view7 = __esm(() => {
83710
84386
  init_source();
83711
84387
  init_connection2();
@@ -83844,7 +84520,7 @@ Examples:
83844
84520
  if (state2?.state === "failed") {
83845
84521
  await reportOpened(record12, state2, mode, opts);
83846
84522
  if (opts.debug) {
83847
- const tail = await readFile14(logPath(record12.id), "utf8").catch(() => "");
84523
+ const tail = await readFile16(logPath(record12.id), "utf8").catch(() => "");
83848
84524
  if (tail)
83849
84525
  console.error(`--- server log ---
83850
84526
  ${tail.slice(-3000)}`);
@@ -83966,7 +84642,7 @@ function reportConnected(session3, machine, opts) {
83966
84642
  console.log(` agent-browser ${drive} open ${session3.url}`);
83967
84643
  console.log(` agent-browser ${drive} click @e3`);
83968
84644
  }
83969
- var LOGIN_TIMEOUT_MS = 180000, POLL_INTERVAL_MS = 2500, sleep3 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)), browser_default;
84645
+ var LOGIN_TIMEOUT_MS = 180000, POLL_INTERVAL_MS = 2500, sleep3 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)), browser_default;
83970
84646
  var init_browser2 = __esm(() => {
83971
84647
  init_source();
83972
84648
  init_browser();
@@ -84083,7 +84759,7 @@ var exports_view_serve = {};
84083
84759
  __export(exports_view_serve, {
84084
84760
  default: () => view_serve_default
84085
84761
  });
84086
- import { readFile as readFile15 } from "node:fs/promises";
84762
+ import { readFile as readFile17 } from "node:fs/promises";
84087
84763
  var view_serve_default;
84088
84764
  var init_view_serve = __esm(() => {
84089
84765
  init_server();
@@ -84095,7 +84771,7 @@ var init_view_serve = __esm(() => {
84095
84771
  action: async (opts) => {
84096
84772
  if (!opts.config)
84097
84773
  throw new Error("--config is required");
84098
- const config2 = JSON.parse(await readFile15(opts.config, "utf8"));
84774
+ const config2 = JSON.parse(await readFile17(opts.config, "utf8"));
84099
84775
  startViewServer(config2);
84100
84776
  console.log(`view session ${config2.session.id} listening on ${config2.session.pageUrl}`);
84101
84777
  await new Promise(() => {});
@@ -84110,7 +84786,7 @@ __export(exports_studio, {
84110
84786
  encodeStudioCliDescriptor: () => encodeStudioCliDescriptor
84111
84787
  });
84112
84788
  import { existsSync as existsSync8, realpathSync } from "node:fs";
84113
- import { dirname as dirname11, join as join16, resolve as resolve6 } from "node:path";
84789
+ import { dirname as dirname13, join as join18, resolve as resolve7 } from "node:path";
84114
84790
  function encodeStudioCliDescriptor(executable = process.execPath, entry2 = process.argv[1]) {
84115
84791
  const args = entry2 && entry2 !== executable && !entry2.startsWith("/$bunfs") && existsSync8(entry2) ? [realpathSync(entry2)] : [];
84116
84792
  return JSON.stringify({ version: 1, executable, args });
@@ -84120,22 +84796,22 @@ function resolveStudioDir() {
84120
84796
  if (process.env.ASTRALE_STUDIO_DIR)
84121
84797
  candidates.push(process.env.ASTRALE_STUDIO_DIR);
84122
84798
  try {
84123
- const entryDir = dirname11(realpathSync(process.argv[1] ?? ""));
84124
- candidates.push(join16(entryDir, "..", "studio"), join16(entryDir, "studio"));
84799
+ const entryDir = dirname13(realpathSync(process.argv[1] ?? ""));
84800
+ candidates.push(join18(entryDir, "..", "studio"), join18(entryDir, "studio"));
84125
84801
  } catch {}
84126
84802
  for (const c of candidates) {
84127
- if (existsSync8(join16(c, "server", "index.ts")))
84128
- return resolve6(c);
84803
+ if (existsSync8(join18(c, "server", "index.ts")))
84804
+ return resolve7(c);
84129
84805
  }
84130
84806
  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
84807
  }
84132
84808
  function isDevSource(studioDir) {
84133
- return existsSync8(join16(studioDir, "vite.config.ts")) && existsSync8(join16(studioDir, "client", "src"));
84809
+ return existsSync8(join18(studioDir, "vite.config.ts")) && existsSync8(join18(studioDir, "client", "src"));
84134
84810
  }
84135
84811
  function resolveViteBin(studioDir) {
84136
84812
  for (const c of [
84137
- join16(studioDir, "node_modules", ".bin", "vite"),
84138
- join16(studioDir, "..", "..", "node_modules", ".bin", "vite")
84813
+ join18(studioDir, "node_modules", ".bin", "vite"),
84814
+ join18(studioDir, "..", "..", "node_modules", ".bin", "vite")
84139
84815
  ]) {
84140
84816
  if (existsSync8(c))
84141
84817
  return c;
@@ -84256,11 +84932,11 @@ Examples:
84256
84932
  `,
84257
84933
  action: async (pathArg, opts) => {
84258
84934
  try {
84259
- const workspace = resolve6(pathArg ?? process.cwd());
84935
+ const workspace = resolve7(pathArg ?? process.cwd());
84260
84936
  if (!existsSync8(workspace))
84261
84937
  throw new Error(`path not found: ${workspace}`);
84262
84938
  const studioDir = resolveStudioDir();
84263
- if (process.env.ASTRALE_STUDIO_DIR && studioDir === resolve6(process.env.ASTRALE_STUDIO_DIR)) {
84939
+ if (process.env.ASTRALE_STUDIO_DIR && studioDir === resolve7(process.env.ASTRALE_STUDIO_DIR)) {
84264
84940
  log.dim(` using ASTRALE_STUDIO_DIR=${studioDir}`);
84265
84941
  }
84266
84942
  await ensureBun();
@@ -84360,8 +85036,8 @@ Examples:
84360
85036
  }
84361
85037
  }), "server");
84362
85038
  } else {
84363
- const dist = join16(studioDir, "client", "dist");
84364
- if (!existsSync8(join16(dist, "index.html"))) {
85039
+ const dist = join18(studioDir, "client", "dist");
85040
+ if (!existsSync8(join18(dist, "index.html"))) {
84365
85041
  throw new Error(`studio client not built at ${dist} — run: pnpm --filter @astrale-os/studio build`);
84366
85042
  }
84367
85043
  serverChild = supervise(spawnHandle("bun", ["server/index.ts", workspace, "--port", String(studioPort), "--no-open"], {
@@ -84414,10 +85090,10 @@ var init_model7 = __esm(() => {
84414
85090
  });
84415
85091
 
84416
85092
  // src/ui/lock.ts
84417
- import { createHash as createHash2 } from "node:crypto";
84418
- import { readFile as readFile16 } from "node:fs/promises";
85093
+ import { createHash as createHash3 } from "node:crypto";
85094
+ import { readFile as readFile18 } from "node:fs/promises";
84419
85095
  function digest5(value3) {
84420
- return createHash2("sha256").update(value3).digest("hex");
85096
+ return createHash3("sha256").update(value3).digest("hex");
84421
85097
  }
84422
85098
  function parseUiLock(value3) {
84423
85099
  const lock = value3;
@@ -84453,14 +85129,14 @@ function pathIsAbsolute(value3) {
84453
85129
  }
84454
85130
  async function readUiLock(target2) {
84455
85131
  try {
84456
- return parseUiLock(JSON.parse(await readFile16(target2, "utf8")));
85132
+ return parseUiLock(JSON.parse(await readFile18(target2, "utf8")));
84457
85133
  } catch (cause) {
84458
85134
  if (cause instanceof UiError)
84459
85135
  throw cause;
84460
85136
  throw new UiError("UI_CONFIG_MISSING", "Unable to read " + UI_LOCK_FILE + ".", "Run astrale ui init.", { cause });
84461
85137
  }
84462
85138
  }
84463
- var init_lock = __esm(() => {
85139
+ var init_lock2 = __esm(() => {
84464
85140
  init_model7();
84465
85141
  });
84466
85142
 
@@ -86484,14 +87160,14 @@ var require_lib5 = __commonJS(function(exports) {
86484
87160
  });
86485
87161
 
86486
87162
  // src/ui/project.ts
86487
- import { access as access2, lstat, readFile as readFile17, realpath as realpath2 } from "node:fs/promises";
87163
+ import { access as access2, lstat as lstat3, readFile as readFile19, realpath as realpath2 } from "node:fs/promises";
86488
87164
  import path5 from "node:path";
86489
87165
  async function exists(target2) {
86490
87166
  return access2(target2).then(() => true, () => false);
86491
87167
  }
86492
87168
  async function readManifest(target2) {
86493
87169
  try {
86494
- return JSON.parse(await readFile17(target2, "utf8"));
87170
+ return JSON.parse(await readFile19(target2, "utf8"));
86495
87171
  } catch (cause) {
86496
87172
  throw new UiError("UI_PROJECT_UNSUPPORTED", "package.json is not valid JSON.", undefined, {
86497
87173
  cause
@@ -86561,7 +87237,7 @@ async function resolveAlias(project2, candidate2) {
86561
87237
  async function resolveUiRegistryTarget(project2, declaredTarget) {
86562
87238
  if (!declaredTarget.startsWith("components/"))
86563
87239
  return declaredTarget;
86564
- const components = await readFile17(project2.componentsPath, "utf8").then((value3) => JSON.parse(value3)).catch(() => {
87240
+ const components = await readFile19(project2.componentsPath, "utf8").then((value3) => JSON.parse(value3)).catch(() => {
86565
87241
  return;
86566
87242
  });
86567
87243
  const componentsAlias = components?.aliases?.components;
@@ -86573,11 +87249,11 @@ async function resolveUiRegistryTarget(project2, declaredTarget) {
86573
87249
  if (!resolved) {
86574
87250
  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
87251
  }
86576
- const relative = path5.relative(project2.root, path5.join(resolved, suffix));
86577
- if (relative === ".." || relative.startsWith(".." + path5.sep) || path5.isAbsolute(relative)) {
87252
+ const relative2 = path5.relative(project2.root, path5.join(resolved, suffix));
87253
+ if (relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
86578
87254
  throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json alias escapes the project.");
86579
87255
  }
86580
- return relative.split(path5.sep).join("/");
87256
+ return relative2.split(path5.sep).join("/");
86581
87257
  }
86582
87258
  function hasReactTailwind(manifest) {
86583
87259
  const dependencies = {
@@ -86597,8 +87273,8 @@ async function assertPhysicalProjectPath(root, target2) {
86597
87273
  existing = parent;
86598
87274
  }
86599
87275
  const physicalTarget = await realpath2(existing);
86600
- const relative = path5.relative(physicalRoot, physicalTarget);
86601
- if (relative === ".." || relative.startsWith(".." + path5.sep) || path5.isAbsolute(relative)) {
87276
+ const relative2 = path5.relative(physicalRoot, physicalTarget);
87277
+ if (relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
86602
87278
  throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json CSS path escapes the project.");
86603
87279
  }
86604
87280
  }
@@ -86607,7 +87283,7 @@ async function discoverUiProject(input = process.cwd()) {
86607
87283
  if (!await exists(root)) {
86608
87284
  throw new UiError("UI_PROJECT_UNSUPPORTED", "Project path does not exist: " + root);
86609
87285
  }
86610
- if (!(await lstat(root)).isDirectory())
87286
+ if (!(await lstat3(root)).isDirectory())
86611
87287
  root = path5.dirname(root);
86612
87288
  while (true) {
86613
87289
  const manifestPath = path5.join(root, "package.json");
@@ -86663,17 +87339,17 @@ async function discoverUiProject(input = process.cwd()) {
86663
87339
  "frontend/src/styles.css"
86664
87340
  ];
86665
87341
  const componentsPath = path5.join(root, "components.json");
86666
- const configuredCss = await readFile17(componentsPath, "utf8").then((value3) => {
87342
+ const configuredCss = await readFile19(componentsPath, "utf8").then((value3) => {
86667
87343
  const components = JSON.parse(value3);
86668
87344
  const css = components.tailwind?.css;
86669
87345
  if (typeof css !== "string" || css.length === 0)
86670
87346
  return;
86671
87347
  const target2 = path5.resolve(root, css);
86672
- const relative = path5.relative(root, target2);
86673
- if (relative === ".." || relative.startsWith(".." + path5.sep) || path5.isAbsolute(relative)) {
87348
+ const relative2 = path5.relative(root, target2);
87349
+ if (relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
86674
87350
  throw new UiError("UI_PROJECT_UNSUPPORTED", "components.json CSS path escapes the project.");
86675
87351
  }
86676
- return { relative: relative.split(path5.sep).join("/"), target: target2 };
87352
+ return { relative: relative2.split(path5.sep).join("/"), target: target2 };
86677
87353
  }).catch((error52) => {
86678
87354
  if (error52 instanceof UiError)
86679
87355
  throw error52;
@@ -86712,11 +87388,11 @@ function assertSupportedUiProject(project2) {
86712
87388
  }
86713
87389
  }
86714
87390
  function projectRelative(project2, target2) {
86715
- const relative = path5.relative(project2.root, target2);
86716
- if (relative === "" || relative === ".." || relative.startsWith(".." + path5.sep) || path5.isAbsolute(relative)) {
87391
+ const relative2 = path5.relative(project2.root, target2);
87392
+ if (relative2 === "" || relative2 === ".." || relative2.startsWith(".." + path5.sep) || path5.isAbsolute(relative2)) {
86717
87393
  throw new UiError("UI_LOCK_INVALID", "Path escapes the project root: " + target2);
86718
87394
  }
86719
- return relative.split(path5.sep).join("/");
87395
+ return relative2.split(path5.sep).join("/");
86720
87396
  }
86721
87397
  var import_tsconfig_paths, MANAGERS;
86722
87398
  var init_project3 = __esm(() => {
@@ -86971,13 +87647,13 @@ var init_runner = __esm(() => {
86971
87647
  });
86972
87648
 
86973
87649
  // src/ui/operations.ts
86974
- import { access as access3, lstat as lstat2, mkdir as mkdir10, readFile as readFile18, realpath as realpath3, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
87650
+ 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
87651
  import path6 from "node:path";
86976
87652
  async function exists2(target2) {
86977
87653
  return access3(target2).then(() => true, () => false);
86978
87654
  }
86979
87655
  async function readOptional(target2) {
86980
- return readFile18(target2, "utf8").catch(() => {
87656
+ return readFile20(target2, "utf8").catch(() => {
86981
87657
  return;
86982
87658
  });
86983
87659
  }
@@ -86993,10 +87669,10 @@ function admitLocalThemeCss(source2, slug) {
86993
87669
  }
86994
87670
  }
86995
87671
  function themeImport(project2, target2) {
86996
- let relative = path6.relative(path6.dirname(project2.cssPath), target2).split(path6.sep).join("/");
86997
- if (!relative.startsWith("."))
86998
- relative = "./" + relative;
86999
- return "@import '" + relative + "';";
87672
+ let relative2 = path6.relative(path6.dirname(project2.cssPath), target2).split(path6.sep).join("/");
87673
+ if (!relative2.startsWith("."))
87674
+ relative2 = "./" + relative2;
87675
+ return "@import '" + relative2 + "';";
87000
87676
  }
87001
87677
  function activateTheme(current, statement) {
87002
87678
  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 +87743,7 @@ async function appendPnpmWorkspace(project2, workspace) {
87067
87743
  const target2 = pnpmWorkspacePath(project2);
87068
87744
  const source2 = await readOptional(target2);
87069
87745
  if (source2 === undefined) {
87070
- await writeFile6(target2, `packages:
87746
+ await writeFile8(target2, `packages:
87071
87747
  - '` + workspace + `'
87072
87748
  `, "utf8");
87073
87749
  return;
@@ -87086,12 +87762,12 @@ async function appendPnpmWorkspace(project2, workspace) {
87086
87762
  }
87087
87763
  if (!values.includes(workspace))
87088
87764
  packages.add(workspace);
87089
- await writeFile6(target2, String(document), "utf8");
87765
+ await writeFile8(target2, String(document), "utf8");
87090
87766
  }
87091
87767
  async function hasDomainRegistryWorkspace(project2) {
87092
87768
  if (!project2.isAstraleDomain || !await exists2(domainRegistryPackagePath(project2)))
87093
87769
  return false;
87094
- const registryManifest = JSON.parse(await readFile18(domainRegistryPackagePath(project2), "utf8"));
87770
+ const registryManifest = JSON.parse(await readFile20(domainRegistryPackagePath(project2), "utf8"));
87095
87771
  if (registryManifest.private !== true || typeof registryManifest.name !== "string" || registryManifest.name === UI_PACKAGE || registryManifest.name === project2.packageJson.name) {
87096
87772
  return false;
87097
87773
  }
@@ -87211,8 +87887,8 @@ async function initUi(options, dependencies = {}) {
87211
87887
  "@import '" + UI_PACKAGE + "/presets/" + preset + ".css';"
87212
87888
  ];
87213
87889
  const withoutAstrale = currentCss.replace(/^@import\s+['"]@astrale-os\/ui\/(?:theme\.css|presets\/[a-z-]+\.css)['"];?\s*$/gmu, "").trimStart();
87214
- await mkdir10(path6.dirname(project2.cssPath), { recursive: true });
87215
- await writeFile6(project2.cssPath, imports.join(`
87890
+ await mkdir12(path6.dirname(project2.cssPath), { recursive: true });
87891
+ await writeFile8(project2.cssPath, imports.join(`
87216
87892
  `) + `
87217
87893
 
87218
87894
  ` + withoutAstrale, "utf8");
@@ -87269,15 +87945,15 @@ async function initUi(options, dependencies = {}) {
87269
87945
  } catch (error52) {
87270
87946
  for (const [target2, value3] of snapshots) {
87271
87947
  if (value3 !== undefined)
87272
- await writeFile6(target2, value3, "utf8");
87948
+ await writeFile8(target2, value3, "utf8");
87273
87949
  else
87274
- await rm3(target2, { force: true });
87950
+ await rm5(target2, { force: true });
87275
87951
  }
87276
87952
  throw error52;
87277
87953
  }
87278
87954
  }
87279
87955
  async function pinUiDependency(project2, version2, section, runner) {
87280
- const manifest = JSON.parse(await readFile18(project2.packageJsonPath, "utf8"));
87956
+ const manifest = JSON.parse(await readFile20(project2.packageJsonPath, "utf8"));
87281
87957
  const current = manifestDependencies(manifest, section)[UI_PACKAGE];
87282
87958
  const other = section === "dependencies" ? "devDependencies" : "dependencies";
87283
87959
  if (current === version2 && manifestDependencies(manifest, other)[UI_PACKAGE] === undefined)
@@ -87316,7 +87992,7 @@ async function lockedRelease(project2, fetcher) {
87316
87992
  }
87317
87993
  async function addLocalTheme(address, project2, options) {
87318
87994
  const sourcePath = path6.resolve(project2.root, address);
87319
- const sourceInfo = await lstat2(sourcePath).catch(() => {
87995
+ const sourceInfo = await lstat4(sourcePath).catch(() => {
87320
87996
  return;
87321
87997
  });
87322
87998
  if (!sourceInfo?.isFile() || sourceInfo.isSymbolicLink()) {
@@ -87326,7 +88002,7 @@ async function addLocalTheme(address, project2, options) {
87326
88002
  if (!THEME_SLUG.test(slug)) {
87327
88003
  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
88004
  }
87329
- const source2 = await readFile18(sourcePath, "utf8");
88005
+ const source2 = await readFile20(sourcePath, "utf8");
87330
88006
  admitLocalThemeCss(source2, slug);
87331
88007
  return installThemeCss(slug, source2, digest5(source2), address, project2, options);
87332
88008
  }
@@ -87361,10 +88037,10 @@ async function installThemeCss(slug, source2, sourceDigest, sourceLabel, project
87361
88037
  snapshots.set(mutation, await readOptional(mutation));
87362
88038
  }
87363
88039
  try {
87364
- await mkdir10(path6.dirname(target2), { recursive: true });
87365
- await writeFile6(target2, source2, "utf8");
88040
+ await mkdir12(path6.dirname(target2), { recursive: true });
88041
+ await writeFile8(target2, source2, "utf8");
87366
88042
  const css = await readOptional(project2.cssPath) ?? "";
87367
- await writeFile6(project2.cssPath, activateTheme(css, statement), "utf8");
88043
+ await writeFile8(project2.cssPath, activateTheme(css, statement), "utf8");
87368
88044
  lock.items[canonicalAddress] = {
87369
88045
  address: canonicalAddress,
87370
88046
  sourceDigest,
@@ -87375,9 +88051,9 @@ async function installThemeCss(slug, source2, sourceDigest, sourceLabel, project
87375
88051
  } catch (error52) {
87376
88052
  for (const [mutation, previous] of snapshots) {
87377
88053
  if (previous === undefined)
87378
- await rm3(mutation, { force: true });
88054
+ await rm5(mutation, { force: true });
87379
88055
  else
87380
- await writeFile6(mutation, previous, "utf8");
88056
+ await writeFile8(mutation, previous, "utf8");
87381
88057
  }
87382
88058
  throw error52;
87383
88059
  }
@@ -87464,15 +88140,15 @@ async function addUi(addresses, options, dependencies = {}) {
87464
88140
  if (themeTarget) {
87465
88141
  const target2 = await safeTarget(project2, themeTarget);
87466
88142
  const css = await readOptional(project2.cssPath) ?? "";
87467
- await writeFile6(project2.cssPath, activateTheme(css, themeImport(project2, target2)), "utf8");
88143
+ await writeFile8(project2.cssPath, activateTheme(css, themeImport(project2, target2)), "utf8");
87468
88144
  }
87469
88145
  }
87470
88146
  } catch (error52) {
87471
88147
  for (const [target2, previous] of snapshots) {
87472
88148
  if (previous === undefined)
87473
- await rm3(target2, { force: true });
88149
+ await rm5(target2, { force: true });
87474
88150
  else
87475
- await writeFile6(target2, previous, "utf8");
88151
+ await writeFile8(target2, previous, "utf8");
87476
88152
  }
87477
88153
  throw error52;
87478
88154
  }
@@ -87501,7 +88177,7 @@ async function addUi(addresses, options, dependencies = {}) {
87501
88177
  if (!file2.target)
87502
88178
  continue;
87503
88179
  const target2 = await safeTarget(project2, resolvedTargets.get(file2.target));
87504
- files[projectRelative(project2, target2)] = digest5(await readFile18(target2));
88180
+ files[projectRelative(project2, target2)] = digest5(await readFile20(target2));
87505
88181
  }
87506
88182
  lock.items[item.meta.canonicalAddress] = {
87507
88183
  address: item.meta.canonicalAddress,
@@ -87513,9 +88189,9 @@ async function addUi(addresses, options, dependencies = {}) {
87513
88189
  } catch (error52) {
87514
88190
  for (const [target2, previous] of snapshots) {
87515
88191
  if (previous === undefined)
87516
- await rm3(target2, { force: true });
88192
+ await rm5(target2, { force: true });
87517
88193
  else
87518
- await writeFile6(target2, previous, "utf8");
88194
+ await writeFile8(target2, previous, "utf8");
87519
88195
  }
87520
88196
  throw error52;
87521
88197
  }
@@ -87533,7 +88209,7 @@ async function doctorUi(input) {
87533
88209
  const checks3 = [];
87534
88210
  let lock;
87535
88211
  try {
87536
- lock = parseUiLock(JSON.parse(await readFile18(project2.uiLockPath, "utf8")));
88212
+ lock = parseUiLock(JSON.parse(await readFile20(project2.uiLockPath, "utf8")));
87537
88213
  checks3.push({ check: "lock", ok: true });
87538
88214
  } catch (error52) {
87539
88215
  checks3.push({
@@ -87561,7 +88237,7 @@ async function doctorUi(input) {
87561
88237
  if (lock) {
87562
88238
  for (const item of Object.values(lock.items)) {
87563
88239
  for (const [file2, expected] of Object.entries(item.files)) {
87564
- const actual = await readFile18(path6.join(project2.root, file2)).then(digest5).catch(() => "");
88240
+ const actual = await readFile20(path6.join(project2.root, file2)).then(digest5).catch(() => "");
87565
88241
  checks3.push({ check: "item:" + item.address + ":" + file2, ok: actual === expected });
87566
88242
  }
87567
88243
  }
@@ -87586,7 +88262,7 @@ async function applyPreset(preset, options) {
87586
88262
  throw new UiError("UI_CONFIG_MISSING", "No Astrale preset import was found.");
87587
88263
  }
87588
88264
  if (!options.dryRun) {
87589
- await writeFile6(project2.cssPath, next, "utf8");
88265
+ await writeFile8(project2.cssPath, next, "utf8");
87590
88266
  lock.preset = preset;
87591
88267
  await writeJson(project2.uiLockPath, lock);
87592
88268
  }
@@ -87614,34 +88290,34 @@ async function rejectLocalChanges(project2, lock, items, overwrite) {
87614
88290
  if (!installed)
87615
88291
  continue;
87616
88292
  for (const [file2, expected] of Object.entries(installed.files)) {
87617
- const actual = await readFile18(path6.join(project2.root, file2)).then(digest5).catch(() => "");
88293
+ const actual = await readFile20(path6.join(project2.root, file2)).then(digest5).catch(() => "");
87618
88294
  if (actual !== expected) {
87619
88295
  throw new UiError("UI_LOCAL_CHANGES", "Installed UI file has local changes: " + file2, "Review the file, then repeat add with explicit --overwrite --yes.");
87620
88296
  }
87621
88297
  }
87622
88298
  }
87623
88299
  }
87624
- async function safeTarget(project2, relative) {
87625
- if (path6.isAbsolute(relative) || relative.split(/[\\/]/u).includes("..")) {
87626
- throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " + relative);
88300
+ async function safeTarget(project2, relative2) {
88301
+ if (path6.isAbsolute(relative2) || relative2.split(/[\\/]/u).includes("..")) {
88302
+ throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " + relative2);
87627
88303
  }
87628
- const target2 = path6.resolve(project2.root, relative);
88304
+ const target2 = path6.resolve(project2.root, relative2);
87629
88305
  projectRelative(project2, target2);
87630
88306
  const parent = await realpath3(path6.dirname(target2));
87631
88307
  const root = await realpath3(project2.root);
87632
88308
  if (parent !== root && !parent.startsWith(root + path6.sep)) {
87633
- throw new UiError("UI_LOCK_INVALID", "Registry target escapes through a symlink: " + relative);
88309
+ throw new UiError("UI_LOCK_INVALID", "Registry target escapes through a symlink: " + relative2);
87634
88310
  }
87635
- if (await exists2(target2) && (await lstat2(target2)).isSymbolicLink()) {
87636
- throw new UiError("UI_LOCK_INVALID", "Registry target is a symlink: " + relative);
88311
+ if (await exists2(target2) && (await lstat4(target2)).isSymbolicLink()) {
88312
+ throw new UiError("UI_LOCK_INVALID", "Registry target is a symlink: " + relative2);
87637
88313
  }
87638
88314
  return target2;
87639
88315
  }
87640
- async function assertSafePlannedTarget(project2, relative) {
87641
- if (path6.isAbsolute(relative) || relative.split(/[\\/]/u).includes("..")) {
87642
- throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " + relative);
88316
+ async function assertSafePlannedTarget(project2, relative2) {
88317
+ if (path6.isAbsolute(relative2) || relative2.split(/[\\/]/u).includes("..")) {
88318
+ throw new UiError("UI_LOCK_INVALID", "Unsafe registry target: " + relative2);
87643
88319
  }
87644
- const target2 = path6.resolve(project2.root, relative);
88320
+ const target2 = path6.resolve(project2.root, relative2);
87645
88321
  projectRelative(project2, target2);
87646
88322
  const root = await realpath3(project2.root);
87647
88323
  const physicalTarget = path6.resolve(root, path6.relative(project2.root, target2));
@@ -87650,21 +88326,21 @@ async function assertSafePlannedTarget(project2, relative) {
87650
88326
  current = path6.join(current, segment);
87651
88327
  if (!await exists2(current))
87652
88328
  break;
87653
- if ((await lstat2(current)).isSymbolicLink()) {
87654
- throw new UiError("UI_LOCK_INVALID", "Registry target traverses a symlink: " + relative);
88329
+ if ((await lstat4(current)).isSymbolicLink()) {
88330
+ throw new UiError("UI_LOCK_INVALID", "Registry target traverses a symlink: " + relative2);
87655
88331
  }
87656
88332
  }
87657
88333
  return target2;
87658
88334
  }
87659
88335
  async function writeJson(target2, value3) {
87660
- await mkdir10(path6.dirname(target2), { recursive: true });
87661
- await writeFile6(target2, JSON.stringify(value3, null, 2) + `
88336
+ await mkdir12(path6.dirname(target2), { recursive: true });
88337
+ await writeFile8(target2, JSON.stringify(value3, null, 2) + `
87662
88338
  `, "utf8");
87663
88339
  }
87664
88340
  var LOCAL_THEME_MAX_BYTES = 131072, THEME_SLUG, REQUIRED_THEME_TOKENS;
87665
88341
  var init_operations3 = __esm(() => {
87666
88342
  init_dist();
87667
- init_lock();
88343
+ init_lock2();
87668
88344
  init_model7();
87669
88345
  init_project3();
87670
88346
  init_release();
@@ -89813,9 +90489,9 @@ __export(exports_register, {
89813
90489
  formatIdentityRegistration: () => formatIdentityRegistration,
89814
90490
  prepareIdentityProvision: () => prepareIdentityProvision
89815
90491
  });
89816
- import { readFile as readFile19 } from "node:fs/promises";
90492
+ import { readFile as readFile21 } from "node:fs/promises";
89817
90493
  async function readJwk(path8) {
89818
- return JSON.parse(await readFile19(path8, "utf8"));
90494
+ return JSON.parse(await readFile21(path8, "utf8"));
89819
90495
  }
89820
90496
  function formatIdentityRegistration(result, format3, machine) {
89821
90497
  output(result, format3);
@@ -90075,7 +90751,7 @@ __export(exports_sync, {
90075
90751
  default: () => sync_default
90076
90752
  });
90077
90753
  var sync_default;
90078
- var init_sync = __esm(() => {
90754
+ var init_sync2 = __esm(() => {
90079
90755
  init_identity7();
90080
90756
  init_log();
90081
90757
  init_output();
@@ -90196,7 +90872,7 @@ var exports_import = {};
90196
90872
  __export(exports_import, {
90197
90873
  default: () => import_default
90198
90874
  });
90199
- import { readFile as readFile20 } from "node:fs/promises";
90875
+ import { readFile as readFile22 } from "node:fs/promises";
90200
90876
  var import_default;
90201
90877
  var init_import2 = __esm(() => {
90202
90878
  init_identity7();
@@ -90224,7 +90900,7 @@ var init_import2 = __esm(() => {
90224
90900
  ],
90225
90901
  action: async (path8, opts) => {
90226
90902
  try {
90227
- const raw2 = await readFile20(path8, "utf-8");
90903
+ const raw2 = await readFile22(path8, "utf-8");
90228
90904
  const passphrase = isEncryptedIdentityExport(raw2) ? await readPassphrase("Passphrase: ") : undefined;
90229
90905
  const envelope = await decodeIdentityExport(raw2, passphrase);
90230
90906
  const name = opts.name ?? envelope.subject;
@@ -90578,21 +91254,21 @@ var init_status4 = __esm(() => {
90578
91254
 
90579
91255
  // src/telemetry/store.ts
90580
91256
  import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
90581
- import { join as join17 } from "node:path";
91257
+ import { join as join19 } from "node:path";
90582
91258
  function sessionsRoot() {
90583
- return join17(createPaths().home, "sessions");
91259
+ return join19(createPaths().home, "sessions");
90584
91260
  }
90585
91261
  function sessionDir(id) {
90586
- return join17(sessionsRoot(), id);
91262
+ return join19(sessionsRoot(), id);
90587
91263
  }
90588
91264
  function eventsPath(id) {
90589
- return join17(sessionDir(id), "events.jsonl");
91265
+ return join19(sessionDir(id), "events.jsonl");
90590
91266
  }
90591
91267
  function metaPath(id) {
90592
- return join17(sessionDir(id), "meta.json");
91268
+ return join19(sessionDir(id), "meta.json");
90593
91269
  }
90594
91270
  function markerPath(id) {
90595
- return join17(sessionDir(id), ".analyzed");
91271
+ return join19(sessionDir(id), ".analyzed");
90596
91272
  }
90597
91273
  function readJsonSafe(path8) {
90598
91274
  try {
@@ -90669,13 +91345,13 @@ var init_list5 = __esm(() => {
90669
91345
 
90670
91346
  // src/telemetry/adapters/claude-code.ts
90671
91347
  import { existsSync as existsSync10, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
90672
- import { homedir as homedir3 } from "node:os";
90673
- import { join as join18 } from "node:path";
91348
+ import { homedir as homedir4 } from "node:os";
91349
+ import { join as join20 } from "node:path";
90674
91350
  function mungeCwd(cwd) {
90675
91351
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
90676
91352
  }
90677
- function claudeCodeAdapter(base2 = join18(homedir3(), ".claude")) {
90678
- const projectsDir = join18(base2, "projects");
91353
+ function claudeCodeAdapter(base2 = join20(homedir4(), ".claude")) {
91354
+ const projectsDir = join20(base2, "projects");
90679
91355
  function detect2() {
90680
91356
  try {
90681
91357
  return existsSync10(projectsDir);
@@ -90699,7 +91375,7 @@ function claudeCodeAdapter(base2 = join18(homedir3(), ".claude")) {
90699
91375
  for (const dir of dirs) {
90700
91376
  if (dir !== munged && !dir.startsWith(prefix))
90701
91377
  continue;
90702
- const projectPath = join18(projectsDir, dir);
91378
+ const projectPath = join20(projectsDir, dir);
90703
91379
  let files;
90704
91380
  try {
90705
91381
  files = readdirSync3(projectPath);
@@ -90709,7 +91385,7 @@ function claudeCodeAdapter(base2 = join18(homedir3(), ".claude")) {
90709
91385
  for (const file2 of files) {
90710
91386
  if (!file2.endsWith(".jsonl"))
90711
91387
  continue;
90712
- const transcriptPath = join18(projectPath, file2);
91388
+ const transcriptPath = join20(projectPath, file2);
90713
91389
  try {
90714
91390
  const st = statSync4(transcriptPath);
90715
91391
  const mtimeMs = st.mtime.getTime();
@@ -90744,8 +91420,8 @@ var init_claude_code = __esm(() => {
90744
91420
  // src/telemetry/adapters/codex.ts
90745
91421
  import { existsSync as existsSync11, openSync as openSync2, readdirSync as readdirSync4, readSync, statSync as statSync5 } from "node:fs";
90746
91422
  import { closeSync as closeSync3 } from "node:fs";
90747
- import { homedir as homedir4 } from "node:os";
90748
- import { join as join19 } from "node:path";
91423
+ import { homedir as homedir5 } from "node:os";
91424
+ import { join as join21 } from "node:path";
90749
91425
  function readFirstLine(path8) {
90750
91426
  let fd = null;
90751
91427
  try {
@@ -90785,8 +91461,8 @@ function numericDirs(path8) {
90785
91461
  return [];
90786
91462
  }
90787
91463
  }
90788
- function codexAdapter(base2 = join19(homedir4(), ".codex")) {
90789
- const sessionsDir = join19(base2, "sessions");
91464
+ function codexAdapter(base2 = join21(homedir5(), ".codex")) {
91465
+ const sessionsDir = join21(base2, "sessions");
90790
91466
  function detect2() {
90791
91467
  try {
90792
91468
  return existsSync11(sessionsDir);
@@ -90801,12 +91477,12 @@ function codexAdapter(base2 = join19(homedir4(), ".codex")) {
90801
91477
  const endMs = window2.end.getTime();
90802
91478
  const lowerMs = startMs - DAY_MS;
90803
91479
  for (const yyyy of numericDirs(sessionsDir)) {
90804
- for (const mm of numericDirs(join19(sessionsDir, yyyy))) {
90805
- for (const dd of numericDirs(join19(sessionsDir, yyyy, mm))) {
91480
+ for (const mm of numericDirs(join21(sessionsDir, yyyy))) {
91481
+ for (const dd of numericDirs(join21(sessionsDir, yyyy, mm))) {
90806
91482
  const dayStart = Date.UTC(Number(yyyy), Number(mm) - 1, Number(dd));
90807
91483
  if (dayStart > endMs + DAY_MS || dayStart + DAY_MS <= lowerMs)
90808
91484
  continue;
90809
- scanDay(join19(sessionsDir, yyyy, mm, dd), root, startMs, endMs, sessions);
91485
+ scanDay(join21(sessionsDir, yyyy, mm, dd), root, startMs, endMs, sessions);
90810
91486
  }
90811
91487
  }
90812
91488
  }
@@ -90828,7 +91504,7 @@ function scanDay(dayPath, root, startMs, endMs, out) {
90828
91504
  for (const file2 of files) {
90829
91505
  if (!file2.startsWith("rollout-") || !file2.endsWith(".jsonl"))
90830
91506
  continue;
90831
- const transcriptPath = join19(dayPath, file2);
91507
+ const transcriptPath = join21(dayPath, file2);
90832
91508
  try {
90833
91509
  const st = statSync5(transcriptPath);
90834
91510
  const mtimeMs = st.mtime.getTime();
@@ -90975,7 +91651,7 @@ var init_gate = () => {};
90975
91651
  // src/telemetry/analyze.ts
90976
91652
  import { spawn as spawn3 } from "node:child_process";
90977
91653
  import { appendFileSync, writeFileSync as writeFileSync2 } from "node:fs";
90978
- import { join as join20 } from "node:path";
91654
+ import { join as join22 } from "node:path";
90979
91655
  function writeMarker(id, marker) {
90980
91656
  writeFileSync2(markerPath(id), JSON.stringify(marker, null, 2) + `
90981
91657
  `);
@@ -91071,7 +91747,7 @@ async function analyzeSession(id, opts = {}) {
91071
91747
  const guides = new Map(adapters.map((a) => [a.name, a.readingGuide]));
91072
91748
  const prompt = buildPrompt({ id, root, signals: signals2, guides, file: opts.file ?? false });
91073
91749
  const dir = sessionDir(id);
91074
- writeFileSync2(join20(dir, "analyzer-prompt.md"), prompt);
91750
+ writeFileSync2(join22(dir, "analyzer-prompt.md"), prompt);
91075
91751
  const outcome = await runClaude(prompt, dir, opts);
91076
91752
  const marker = {
91077
91753
  analyzedAt: new Date().toISOString(),
@@ -91079,10 +91755,10 @@ async function analyzeSession(id, opts = {}) {
91079
91755
  note: outcome.note
91080
91756
  };
91081
91757
  writeMarker(id, marker);
91082
- return { ...marker, reportPath: join20(dir, "report.md") };
91758
+ return { ...marker, reportPath: join22(dir, "report.md") };
91083
91759
  }
91084
91760
  function runClaude(prompt, cwd, opts) {
91085
- return new Promise((resolve7) => {
91761
+ return new Promise((resolve8) => {
91086
91762
  const env2 = { ASTRALE_TELEMETRY: "0" };
91087
91763
  for (const [k, v] of Object.entries(process.env)) {
91088
91764
  if (v !== undefined && !k.startsWith("CLAUDE"))
@@ -91095,10 +91771,10 @@ function runClaude(prompt, cwd, opts) {
91095
91771
  try {
91096
91772
  child3 = spawn3("claude", args, { cwd, env: env2, stdio: ["ignore", "pipe", "pipe"] });
91097
91773
  } catch (e) {
91098
- resolve7({ ok: false, note: `claude spawn failed: ${String(e)}` });
91774
+ resolve8({ ok: false, note: `claude spawn failed: ${String(e)}` });
91099
91775
  return;
91100
91776
  }
91101
- child3.on("error", (e) => resolve7({ ok: false, note: `claude not available: ${e.message}` }));
91777
+ child3.on("error", (e) => resolve8({ ok: false, note: `claude not available: ${e.message}` }));
91102
91778
  let out = "";
91103
91779
  let err = "";
91104
91780
  child3.stdout?.setEncoding("utf8");
@@ -91109,22 +91785,22 @@ function runClaude(prompt, cwd, opts) {
91109
91785
  child3.on("close", (code) => {
91110
91786
  clearTimeout(timer);
91111
91787
  try {
91112
- appendFileSync(join20(cwd, "analyzer.log"), out + (err ? `
91788
+ appendFileSync(join22(cwd, "analyzer.log"), out + (err ? `
91113
91789
  --- stderr ---
91114
91790
  ${err}` : ""));
91115
91791
  } catch {}
91116
91792
  try {
91117
91793
  const result = JSON.parse(out);
91118
91794
  if (result.is_error) {
91119
- resolve7({ ok: false, note: `analyzer errored: ${result.result?.slice(0, 200)}` });
91795
+ resolve8({ ok: false, note: `analyzer errored: ${result.result?.slice(0, 200)}` });
91120
91796
  return;
91121
91797
  }
91122
- resolve7({
91798
+ resolve8({
91123
91799
  ok: true,
91124
91800
  note: `${result.num_turns ?? "?"} turns, $${(result.total_cost_usd ?? 0).toFixed(4)}`
91125
91801
  });
91126
91802
  } catch {
91127
- resolve7({ ok: false, note: `claude exit ${code}, unparseable output` });
91803
+ resolve8({ ok: false, note: `claude exit ${code}, unparseable output` });
91128
91804
  }
91129
91805
  });
91130
91806
  });
@@ -91159,9 +91835,9 @@ var init_settings = __esm(() => {
91159
91835
 
91160
91836
  // src/telemetry/trigger.ts
91161
91837
  import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync as rmSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
91162
- import { join as join21 } from "node:path";
91838
+ import { join as join23 } from "node:path";
91163
91839
  function lockPath() {
91164
- return join21(sessionsRoot(), ".analyzer.lock");
91840
+ return join23(sessionsRoot(), ".analyzer.lock");
91165
91841
  }
91166
91842
  function releaseLock() {
91167
91843
  try {
@@ -91244,7 +91920,7 @@ var exports_add2 = {};
91244
91920
  __export(exports_add2, {
91245
91921
  default: () => add_default2
91246
91922
  });
91247
- import { readFile as readFile21 } from "node:fs/promises";
91923
+ import { readFile as readFile23 } from "node:fs/promises";
91248
91924
  function isString(value3) {
91249
91925
  return typeof value3 === "string";
91250
91926
  }
@@ -91306,7 +91982,7 @@ Security:
91306
91982
  if (!opts.issuer && !opts.metadata && !opts.workosAuthkit) {
91307
91983
  throw new Error("Either --issuer, --metadata, or --workos-authkit is required");
91308
91984
  }
91309
- let metadata = opts.workosAuthkit ? workosAuthKitMetadata(opts.workosApiHostname, opts.clientId) : opts.metadata ? OidcMetadataSchema.parse(JSON.parse(await readFile21(opts.metadata, "utf-8"))) : await fetchOidcMetadata(opts.issuer);
91985
+ 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
91986
  if (opts.issuer) {
91311
91987
  validateUrl(opts.issuer);
91312
91988
  if (normalizeIssuer2(metadata.issuer) !== normalizeIssuer2(opts.issuer)) {
@@ -91872,7 +92548,7 @@ async function buildProgram() {
91872
92548
  (await Promise.resolve().then(() => (init_use4(), exports_use4))).default,
91873
92549
  (await Promise.resolve().then(() => (init_whoami3(), exports_whoami))).default,
91874
92550
  (await Promise.resolve().then(() => (init_delete2(), exports_delete2))).default,
91875
- (await Promise.resolve().then(() => (init_sync(), exports_sync))).default,
92551
+ (await Promise.resolve().then(() => (init_sync2(), exports_sync))).default,
91876
92552
  (await Promise.resolve().then(() => (init_unsync(), exports_unsync))).default,
91877
92553
  (await Promise.resolve().then(() => (init_export2(), exports_export))).default,
91878
92554
  (await Promise.resolve().then(() => (init_import2(), exports_import))).default
@@ -91996,16 +92672,16 @@ function redactArgv(argv) {
91996
92672
 
91997
92673
  // src/telemetry/session.ts
91998
92674
  init_store();
91999
- import { createHash as createHash3 } from "node:crypto";
92675
+ import { createHash as createHash4 } from "node:crypto";
92000
92676
  import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "node:fs";
92001
- import { dirname as dirname12, join as join22 } from "node:path";
92677
+ import { dirname as dirname14, join as join24 } from "node:path";
92002
92678
  var MAX_ID_LEN = 64;
92003
92679
  function findGitRoot(start) {
92004
92680
  let dir = start;
92005
92681
  for (;; ) {
92006
- if (existsSync13(join22(dir, ".git")))
92682
+ if (existsSync13(join24(dir, ".git")))
92007
92683
  return dir;
92008
- const parent = dirname12(dir);
92684
+ const parent = dirname14(dir);
92009
92685
  if (parent === dir)
92010
92686
  return null;
92011
92687
  dir = parent;
@@ -92027,7 +92703,7 @@ function findOpenAmbient(root) {
92027
92703
  return null;
92028
92704
  }
92029
92705
  function mintAmbientId(root) {
92030
- const hash8 = createHash3("sha256").update(root).digest("hex").slice(0, 8);
92706
+ const hash8 = createHash4("sha256").update(root).digest("hex").slice(0, 8);
92031
92707
  return `amb-${hash8}-${stamp()}`;
92032
92708
  }
92033
92709
  function resolveSession2(cwd) {
@@ -92112,7 +92788,7 @@ init_settings();
92112
92788
  init_store();
92113
92789
  import { spawn as spawn4 } from "node:child_process";
92114
92790
  import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync as rmSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
92115
- import { join as join23 } from "node:path";
92791
+ import { join as join25 } from "node:path";
92116
92792
  var LOCK_STALE_MS2 = 30 * 60 * 1000;
92117
92793
  var GC_AGE_MS2 = 30 * 24 * 60 * 60 * 1000;
92118
92794
  var GC_MAX_PER_RUN = 20;
@@ -92131,7 +92807,7 @@ function gcOldSessions(sessions) {
92131
92807
  } catch {}
92132
92808
  }
92133
92809
  function lockPath2() {
92134
- return join23(sessionsRoot(), ".analyzer.lock");
92810
+ return join25(sessionsRoot(), ".analyzer.lock");
92135
92811
  }
92136
92812
  function claimLock() {
92137
92813
  const path8 = lockPath2();