@basou/cli 0.15.0 → 0.16.0
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/index.js +175 -104
- package/dist/index.js.map +1 -1
- package/dist/program.js +175 -104
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2620,6 +2620,62 @@ import {
|
|
|
2620
2620
|
writeMarkdownFile as writeMarkdownFile4
|
|
2621
2621
|
} from "@basou/core";
|
|
2622
2622
|
|
|
2623
|
+
// src/lib/hosts-config.ts
|
|
2624
|
+
import { homedir as homedir5 } from "os";
|
|
2625
|
+
import { isAbsolute as isAbsolute2, join as join5, resolve as resolve6 } from "path";
|
|
2626
|
+
import { readYamlFile as readYamlFile4 } from "@basou/core";
|
|
2627
|
+
var DEFAULT_HOSTS_CONFIG_PATH = join5(homedir5(), ".basou", "hosts.yaml");
|
|
2628
|
+
function expandTilde2(p) {
|
|
2629
|
+
if (p === "~") return homedir5();
|
|
2630
|
+
if (p.startsWith("~/")) return join5(homedir5(), p.slice(2));
|
|
2631
|
+
return p;
|
|
2632
|
+
}
|
|
2633
|
+
function isRecord2(value) {
|
|
2634
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2635
|
+
}
|
|
2636
|
+
async function loadHostsConfig(configPath = DEFAULT_HOSTS_CONFIG_PATH) {
|
|
2637
|
+
let raw;
|
|
2638
|
+
try {
|
|
2639
|
+
raw = await readYamlFile4(configPath);
|
|
2640
|
+
} catch (error) {
|
|
2641
|
+
if (error instanceof Error && error.message === "YAML file not found") {
|
|
2642
|
+
return null;
|
|
2643
|
+
}
|
|
2644
|
+
if (error instanceof Error && error.message === "Failed to parse YAML content") {
|
|
2645
|
+
throw new Error("~/.basou/hosts.yaml is not valid YAML.");
|
|
2646
|
+
}
|
|
2647
|
+
throw error;
|
|
2648
|
+
}
|
|
2649
|
+
if (!isRecord2(raw) || !Array.isArray(raw.hosts)) {
|
|
2650
|
+
throw new Error("~/.basou/hosts.yaml must contain a 'hosts:' list.");
|
|
2651
|
+
}
|
|
2652
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
2653
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
2654
|
+
const result = [];
|
|
2655
|
+
for (const entry of raw.hosts) {
|
|
2656
|
+
if (!isRecord2(entry) || typeof entry.label !== "string" || entry.label.trim().length === 0) {
|
|
2657
|
+
throw new Error("Each host needs a non-empty string 'label'.");
|
|
2658
|
+
}
|
|
2659
|
+
const label = entry.label.trim();
|
|
2660
|
+
if (typeof entry.path !== "string" || entry.path.trim().length === 0) {
|
|
2661
|
+
throw new Error("Each host needs a non-empty string 'path'.");
|
|
2662
|
+
}
|
|
2663
|
+
const expanded = expandTilde2(entry.path.trim());
|
|
2664
|
+
if (!isAbsolute2(expanded)) {
|
|
2665
|
+
throw new Error("Host paths must be absolute (or start with '~').");
|
|
2666
|
+
}
|
|
2667
|
+
const abs = resolve6(expanded);
|
|
2668
|
+
if (seenPaths.has(abs)) continue;
|
|
2669
|
+
if (seenLabels.has(label)) {
|
|
2670
|
+
throw new Error(`Duplicate host label '${label}'; each host needs a distinct label.`);
|
|
2671
|
+
}
|
|
2672
|
+
seenPaths.add(abs);
|
|
2673
|
+
seenLabels.add(label);
|
|
2674
|
+
result.push({ label, path: abs });
|
|
2675
|
+
}
|
|
2676
|
+
return result;
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2623
2679
|
// src/lib/provenance-actions.ts
|
|
2624
2680
|
import {
|
|
2625
2681
|
readMarkdownFile as readMarkdownFile3,
|
|
@@ -2826,14 +2882,29 @@ async function doRunOrient(options, ctx) {
|
|
|
2826
2882
|
if (ctx.claudeProjectsDir !== void 0) probeCtx.claudeProjectsDir = ctx.claudeProjectsDir;
|
|
2827
2883
|
if (ctx.codexSessionsDir !== void 0) probeCtx.codexSessionsDir = ctx.codexSessionsDir;
|
|
2828
2884
|
const staleness = await probeStaleness({ ctx: probeCtx, paths, nowIso });
|
|
2885
|
+
let federatedRoots = [];
|
|
2886
|
+
try {
|
|
2887
|
+
const hosts = await loadHostsConfig(ctx.hostsConfigPath);
|
|
2888
|
+
if (hosts !== null) {
|
|
2889
|
+
federatedRoots = hosts.map((h) => ({ paths: basouPaths9(h.path), host: h.label }));
|
|
2890
|
+
}
|
|
2891
|
+
} catch (error) {
|
|
2892
|
+
console.error(
|
|
2893
|
+
`basou: ignoring ~/.basou/hosts.yaml (${error instanceof Error ? error.message : String(error)}); showing local sessions only.`
|
|
2894
|
+
);
|
|
2895
|
+
}
|
|
2829
2896
|
const result = await renderOrientation2({
|
|
2830
2897
|
paths,
|
|
2831
2898
|
nowIso,
|
|
2832
2899
|
staleness,
|
|
2833
2900
|
verbose: options.verbose === true,
|
|
2901
|
+
federatedRoots,
|
|
2834
2902
|
onWarning: (w, sid) => printReplayWarning(w, sid),
|
|
2835
2903
|
onSessionSkip: (sid, reason) => printSessionSkip(sid, reason),
|
|
2836
|
-
onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
|
|
2904
|
+
onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason),
|
|
2905
|
+
onHostUnavailable: (host, error) => console.error(
|
|
2906
|
+
`basou: host '${host}' mirror unreadable (${error instanceof Error ? error.message : String(error)}); skipping it.`
|
|
2907
|
+
)
|
|
2837
2908
|
});
|
|
2838
2909
|
await writeMarkdownFile4(paths.files.orientation, `${result.body}
|
|
2839
2910
|
`);
|
|
@@ -2870,7 +2941,7 @@ import {
|
|
|
2870
2941
|
unlinkSync,
|
|
2871
2942
|
writeFileSync
|
|
2872
2943
|
} from "fs";
|
|
2873
|
-
import { basename as basename4, dirname, isAbsolute as
|
|
2944
|
+
import { basename as basename4, dirname, isAbsolute as isAbsolute3, join as join6, relative as relative2, resolve as resolve7 } from "path";
|
|
2874
2945
|
import {
|
|
2875
2946
|
basouPaths as basouPaths10,
|
|
2876
2947
|
GENERATED_END,
|
|
@@ -3122,14 +3193,14 @@ async function runProjectAdopt(options, ctx = {}) {
|
|
|
3122
3193
|
}
|
|
3123
3194
|
}
|
|
3124
3195
|
function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
3125
|
-
const absolute =
|
|
3196
|
+
const absolute = resolve7(repositoryRoot, declaredPath);
|
|
3126
3197
|
let real;
|
|
3127
3198
|
try {
|
|
3128
3199
|
real = realpathSync(absolute);
|
|
3129
3200
|
} catch {
|
|
3130
3201
|
return { path: declaredPath, kind: "unresolved" };
|
|
3131
3202
|
}
|
|
3132
|
-
return { path: declaredPath, kind: existsSync(
|
|
3203
|
+
return { path: declaredPath, kind: existsSync(join6(real, ".git")) ? "repo" : "non-repo" };
|
|
3133
3204
|
}
|
|
3134
3205
|
async function doRunProjectAdopt(options, ctx) {
|
|
3135
3206
|
const cwd = ctx.cwd ?? process.cwd();
|
|
@@ -3224,11 +3295,11 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
3224
3295
|
};
|
|
3225
3296
|
let real;
|
|
3226
3297
|
try {
|
|
3227
|
-
real = realpathSync(
|
|
3298
|
+
real = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
3228
3299
|
} catch {
|
|
3229
3300
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
3230
3301
|
}
|
|
3231
|
-
if (!existsSync(
|
|
3302
|
+
if (!existsSync(join6(real, ".git"))) {
|
|
3232
3303
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
3233
3304
|
}
|
|
3234
3305
|
try {
|
|
@@ -3236,7 +3307,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
3236
3307
|
for (const name of INSTRUCTION_FILES) {
|
|
3237
3308
|
let present = true;
|
|
3238
3309
|
try {
|
|
3239
|
-
lstatSync(
|
|
3310
|
+
lstatSync(join6(real, name));
|
|
3240
3311
|
} catch {
|
|
3241
3312
|
present = false;
|
|
3242
3313
|
}
|
|
@@ -3329,14 +3400,14 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
3329
3400
|
};
|
|
3330
3401
|
let real;
|
|
3331
3402
|
try {
|
|
3332
|
-
real = realpathSync(
|
|
3403
|
+
real = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
3333
3404
|
} catch {
|
|
3334
3405
|
return { ...base, reachable: false, currentLines: [] };
|
|
3335
3406
|
}
|
|
3336
|
-
if (!existsSync(
|
|
3407
|
+
if (!existsSync(join6(real, ".git"))) {
|
|
3337
3408
|
return { ...base, reachable: false, currentLines: [] };
|
|
3338
3409
|
}
|
|
3339
|
-
return { ...base, reachable: true, currentLines: readGitignoreLines(
|
|
3410
|
+
return { ...base, reachable: true, currentLines: readGitignoreLines(join6(real, ".gitignore")) };
|
|
3340
3411
|
}
|
|
3341
3412
|
function hasErrorCode(error) {
|
|
3342
3413
|
return error instanceof Error && typeof error.code === "string";
|
|
@@ -3350,7 +3421,7 @@ function readGitignoreLines(file) {
|
|
|
3350
3421
|
}
|
|
3351
3422
|
}
|
|
3352
3423
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
3353
|
-
const file =
|
|
3424
|
+
const file = join6(realpathSync(resolve7(repositoryRoot, plan.path)), ".gitignore");
|
|
3354
3425
|
let existing = "";
|
|
3355
3426
|
try {
|
|
3356
3427
|
existing = readFileSync(file, "utf8");
|
|
@@ -3462,23 +3533,23 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3462
3533
|
const base = { path: entry.path };
|
|
3463
3534
|
let real;
|
|
3464
3535
|
try {
|
|
3465
|
-
real = realpathSync(
|
|
3536
|
+
real = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
3466
3537
|
} catch {
|
|
3467
3538
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
3468
3539
|
}
|
|
3469
3540
|
if (real === anchorReal) {
|
|
3470
3541
|
return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
|
|
3471
3542
|
}
|
|
3472
|
-
if (!existsSync(
|
|
3543
|
+
if (!existsSync(join6(real, ".git"))) {
|
|
3473
3544
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
3474
3545
|
}
|
|
3475
|
-
const canonicalFile =
|
|
3546
|
+
const canonicalFile = join6(anchorReal, "agents", basename4(real), CANONICAL_FILE);
|
|
3476
3547
|
if (!existsSync(canonicalFile)) {
|
|
3477
3548
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
3478
3549
|
}
|
|
3479
3550
|
const files = expectedSymlinkTargets(real, canonicalFile).map(
|
|
3480
3551
|
(spec) => {
|
|
3481
|
-
const { state, actualTarget } = inspectSymlink(
|
|
3552
|
+
const { state, actualTarget } = inspectSymlink(join6(real, spec.name), spec.target);
|
|
3482
3553
|
return {
|
|
3483
3554
|
name: spec.name,
|
|
3484
3555
|
expectedTarget: spec.target,
|
|
@@ -3499,7 +3570,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3499
3570
|
function applySymlinkPlan(repositoryRoot, plan) {
|
|
3500
3571
|
let real;
|
|
3501
3572
|
try {
|
|
3502
|
-
real = realpathSync(
|
|
3573
|
+
real = realpathSync(resolve7(repositoryRoot, plan.path));
|
|
3503
3574
|
} catch (error) {
|
|
3504
3575
|
const message = failureReason(error);
|
|
3505
3576
|
return { created: [], failed: plan.toCreate.map((c) => ({ file: c.name, message })) };
|
|
@@ -3507,7 +3578,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
|
|
|
3507
3578
|
const created = [];
|
|
3508
3579
|
const failed = [];
|
|
3509
3580
|
for (const { name, target } of plan.toCreate) {
|
|
3510
|
-
const filePath =
|
|
3581
|
+
const filePath = join6(real, name);
|
|
3511
3582
|
try {
|
|
3512
3583
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
3513
3584
|
symlinkSync(target, filePath);
|
|
@@ -3644,12 +3715,12 @@ async function runProjectWorkspace(options, ctx = {}) {
|
|
|
3644
3715
|
}
|
|
3645
3716
|
}
|
|
3646
3717
|
function resolveViewDir(repositoryRoot, viewPath) {
|
|
3647
|
-
const abs =
|
|
3718
|
+
const abs = resolve7(repositoryRoot, viewPath);
|
|
3648
3719
|
try {
|
|
3649
3720
|
return realpathSync(abs);
|
|
3650
3721
|
} catch {
|
|
3651
3722
|
try {
|
|
3652
|
-
return
|
|
3723
|
+
return join6(realpathSync(dirname(abs)), basename4(abs));
|
|
3653
3724
|
} catch {
|
|
3654
3725
|
return abs;
|
|
3655
3726
|
}
|
|
@@ -3658,7 +3729,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
3658
3729
|
function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
3659
3730
|
let repoReal;
|
|
3660
3731
|
try {
|
|
3661
|
-
repoReal = realpathSync(
|
|
3732
|
+
repoReal = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
3662
3733
|
} catch {
|
|
3663
3734
|
return { path: entry.path, reachable: false };
|
|
3664
3735
|
}
|
|
@@ -3667,7 +3738,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
|
3667
3738
|
return { path: entry.path, reachable: false };
|
|
3668
3739
|
}
|
|
3669
3740
|
const linkName = basename4(repoReal);
|
|
3670
|
-
const { state, actualTarget } = inspectSymlink(
|
|
3741
|
+
const { state, actualTarget } = inspectSymlink(join6(viewDir, linkName), expectedTarget);
|
|
3671
3742
|
return {
|
|
3672
3743
|
path: entry.path,
|
|
3673
3744
|
reachable: true,
|
|
@@ -3681,7 +3752,7 @@ function applyViewPlan(viewDir, toCreate) {
|
|
|
3681
3752
|
const created = [];
|
|
3682
3753
|
const failed = [];
|
|
3683
3754
|
for (const { name, target } of toCreate) {
|
|
3684
|
-
const filePath =
|
|
3755
|
+
const filePath = join6(viewDir, name);
|
|
3685
3756
|
try {
|
|
3686
3757
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
3687
3758
|
symlinkSync(target, filePath);
|
|
@@ -3696,7 +3767,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
|
|
|
3696
3767
|
INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
|
|
3697
3768
|
);
|
|
3698
3769
|
function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
3699
|
-
const filePath =
|
|
3770
|
+
const filePath = join6(viewDir, name);
|
|
3700
3771
|
let isLink;
|
|
3701
3772
|
try {
|
|
3702
3773
|
isLink = lstatSync(filePath).isSymbolicLink();
|
|
@@ -3710,12 +3781,12 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
3710
3781
|
} catch {
|
|
3711
3782
|
return null;
|
|
3712
3783
|
}
|
|
3713
|
-
const resolved =
|
|
3784
|
+
const resolved = isAbsolute3(target) ? target : resolve7(viewDir, target);
|
|
3714
3785
|
try {
|
|
3715
3786
|
if (rosterRealpaths.has(realpathSync(resolved))) return null;
|
|
3716
3787
|
} catch {
|
|
3717
3788
|
}
|
|
3718
|
-
if (
|
|
3789
|
+
if (isAbsolute3(target)) return { target, kind: "absolute" };
|
|
3719
3790
|
let isDir = false;
|
|
3720
3791
|
try {
|
|
3721
3792
|
isDir = statSync(resolved).isDirectory();
|
|
@@ -3725,7 +3796,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
3725
3796
|
if (!isDir) {
|
|
3726
3797
|
return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
|
|
3727
3798
|
}
|
|
3728
|
-
return { target, kind: existsSync(
|
|
3799
|
+
return { target, kind: existsSync(join6(resolved, ".git")) ? "repo" : "non-repo" };
|
|
3729
3800
|
}
|
|
3730
3801
|
function gatherExistingViewLinks(viewDir, rosterRealpaths) {
|
|
3731
3802
|
let names;
|
|
@@ -3750,7 +3821,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
3750
3821
|
const pruned = [];
|
|
3751
3822
|
const failed = [];
|
|
3752
3823
|
for (const { name } of toPrune) {
|
|
3753
|
-
const filePath =
|
|
3824
|
+
const filePath = join6(viewDir, name);
|
|
3754
3825
|
const c = classifyViewLink(viewDir, name, rosterRealpaths);
|
|
3755
3826
|
if (c === null || c.kind !== "repo") {
|
|
3756
3827
|
failed.push({
|
|
@@ -3796,11 +3867,11 @@ async function doRunProjectWorkspace(options, ctx) {
|
|
|
3796
3867
|
} else {
|
|
3797
3868
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
3798
3869
|
const facts = roster.map((entry) => gatherViewRepo(repositoryRoot, viewDir, entry));
|
|
3799
|
-
const rosterNames = roster.map((entry) => basename4(
|
|
3870
|
+
const rosterNames = roster.map((entry) => basename4(resolve7(repositoryRoot, entry.path)));
|
|
3800
3871
|
const rosterRealpaths = /* @__PURE__ */ new Set();
|
|
3801
3872
|
for (const entry of roster) {
|
|
3802
3873
|
try {
|
|
3803
|
-
rosterRealpaths.add(realpathSync(
|
|
3874
|
+
rosterRealpaths.add(realpathSync(resolve7(repositoryRoot, entry.path)));
|
|
3804
3875
|
} catch {
|
|
3805
3876
|
}
|
|
3806
3877
|
}
|
|
@@ -3961,10 +4032,10 @@ async function runProjectPreset(options, ctx = {}) {
|
|
|
3961
4032
|
}
|
|
3962
4033
|
}
|
|
3963
4034
|
function canonicalFileFor(anchorReal, canonicalName) {
|
|
3964
|
-
return
|
|
4035
|
+
return join6(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
3965
4036
|
}
|
|
3966
4037
|
function canonicalLabelFor(canonicalName) {
|
|
3967
|
-
return
|
|
4038
|
+
return join6("agents", canonicalName, CANONICAL_FILE);
|
|
3968
4039
|
}
|
|
3969
4040
|
async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
3970
4041
|
const declared = {
|
|
@@ -3975,14 +4046,14 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
3975
4046
|
};
|
|
3976
4047
|
let real;
|
|
3977
4048
|
try {
|
|
3978
|
-
real = realpathSync(
|
|
4049
|
+
real = realpathSync(resolve7(repositoryRoot, entry.path));
|
|
3979
4050
|
} catch {
|
|
3980
4051
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
3981
4052
|
}
|
|
3982
4053
|
if (real === anchorReal) {
|
|
3983
4054
|
return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
|
|
3984
4055
|
}
|
|
3985
|
-
if (!existsSync(
|
|
4056
|
+
if (!existsSync(join6(real, ".git"))) {
|
|
3986
4057
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
3987
4058
|
}
|
|
3988
4059
|
const canonicalName = basename4(real);
|
|
@@ -4204,7 +4275,7 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
4204
4275
|
};
|
|
4205
4276
|
let real;
|
|
4206
4277
|
try {
|
|
4207
|
-
real = realpathSync(
|
|
4278
|
+
real = realpathSync(resolve7(repositoryRoot, target));
|
|
4208
4279
|
} catch {
|
|
4209
4280
|
return empty;
|
|
4210
4281
|
}
|
|
@@ -4213,24 +4284,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
4213
4284
|
const instructionFiles = [];
|
|
4214
4285
|
for (const name of INSTRUCTION_FILES) {
|
|
4215
4286
|
try {
|
|
4216
|
-
lstatSync(
|
|
4287
|
+
lstatSync(join6(real, name));
|
|
4217
4288
|
instructionFiles.push(name);
|
|
4218
4289
|
} catch {
|
|
4219
4290
|
}
|
|
4220
4291
|
}
|
|
4221
4292
|
let ignored;
|
|
4222
4293
|
try {
|
|
4223
|
-
ignored = new Set(readGitignoreLines(
|
|
4294
|
+
ignored = new Set(readGitignoreLines(join6(real, ".gitignore")).map((l) => l.trim()));
|
|
4224
4295
|
} catch {
|
|
4225
4296
|
ignored = /* @__PURE__ */ new Set();
|
|
4226
4297
|
}
|
|
4227
4298
|
const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
|
|
4228
|
-
const canonical2 = existsSync(
|
|
4299
|
+
const canonical2 = existsSync(join6(anchorReal, "agents", canonicalName, CANONICAL_FILE));
|
|
4229
4300
|
let viewLink = false;
|
|
4230
4301
|
const viewPath = manifest.workspace.view;
|
|
4231
4302
|
if (viewPath !== void 0) {
|
|
4232
4303
|
try {
|
|
4233
|
-
lstatSync(
|
|
4304
|
+
lstatSync(join6(resolveViewDir(repositoryRoot, viewPath), canonicalName));
|
|
4234
4305
|
viewLink = true;
|
|
4235
4306
|
} catch {
|
|
4236
4307
|
}
|
|
@@ -4272,7 +4343,7 @@ async function doRunProjectArchive(target, options, ctx) {
|
|
|
4272
4343
|
const roster = manifest.repos ?? [];
|
|
4273
4344
|
let targetIsAnchor = false;
|
|
4274
4345
|
try {
|
|
4275
|
-
targetIsAnchor = realpathSync(
|
|
4346
|
+
targetIsAnchor = realpathSync(resolve7(repositoryRoot, target)) === realpathSync(repositoryRoot);
|
|
4276
4347
|
} catch {
|
|
4277
4348
|
targetIsAnchor = false;
|
|
4278
4349
|
}
|
|
@@ -4392,12 +4463,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
|
|
|
4392
4463
|
} catch {
|
|
4393
4464
|
return { canonicalDirOld: false, viewLinkOld: false };
|
|
4394
4465
|
}
|
|
4395
|
-
const canonicalDirOld = existsSync(
|
|
4466
|
+
const canonicalDirOld = existsSync(join6(anchorReal, "agents", oldBasename));
|
|
4396
4467
|
let viewLinkOld = false;
|
|
4397
4468
|
const viewPath = manifest.workspace.view;
|
|
4398
4469
|
if (viewPath !== void 0) {
|
|
4399
4470
|
try {
|
|
4400
|
-
lstatSync(
|
|
4471
|
+
lstatSync(join6(resolveViewDir(repositoryRoot, viewPath), oldBasename));
|
|
4401
4472
|
viewLinkOld = true;
|
|
4402
4473
|
} catch {
|
|
4403
4474
|
}
|
|
@@ -4423,7 +4494,7 @@ async function doRunProjectRename(oldPath, newPath, options, ctx) {
|
|
|
4423
4494
|
const roster = manifest.repos ?? [];
|
|
4424
4495
|
let oldIsAnchor = false;
|
|
4425
4496
|
try {
|
|
4426
|
-
oldIsAnchor = realpathSync(
|
|
4497
|
+
oldIsAnchor = realpathSync(resolve7(repositoryRoot, oldPath)) === realpathSync(repositoryRoot);
|
|
4427
4498
|
} catch {
|
|
4428
4499
|
oldIsAnchor = false;
|
|
4429
4500
|
}
|
|
@@ -4546,7 +4617,7 @@ import {
|
|
|
4546
4617
|
// src/lib/durable-write.ts
|
|
4547
4618
|
import { randomUUID } from "crypto";
|
|
4548
4619
|
import { lstat, open, rename, stat as stat3, unlink as unlink2 } from "fs/promises";
|
|
4549
|
-
import { basename as basename5, dirname as dirname2, join as
|
|
4620
|
+
import { basename as basename5, dirname as dirname2, join as join7 } from "path";
|
|
4550
4621
|
async function assertNotSymlink(targetPath) {
|
|
4551
4622
|
try {
|
|
4552
4623
|
const st = await lstat(targetPath);
|
|
@@ -4562,7 +4633,7 @@ async function assertNotSymlink(targetPath) {
|
|
|
4562
4633
|
}
|
|
4563
4634
|
async function writeFileDurable(targetPath, content) {
|
|
4564
4635
|
const dir = dirname2(targetPath);
|
|
4565
|
-
const tmpPath =
|
|
4636
|
+
const tmpPath = join7(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
|
|
4566
4637
|
let mode = 420;
|
|
4567
4638
|
try {
|
|
4568
4639
|
mode = (await stat3(targetPath)).mode & 511;
|
|
@@ -4597,25 +4668,25 @@ async function writeFileDurable(targetPath, content) {
|
|
|
4597
4668
|
}
|
|
4598
4669
|
|
|
4599
4670
|
// src/lib/protocols-config.ts
|
|
4600
|
-
import { homedir as
|
|
4601
|
-
import { isAbsolute as
|
|
4602
|
-
import { readYamlFile as
|
|
4603
|
-
var DEFAULT_PROTOCOLS_CONFIG_PATH =
|
|
4604
|
-
var DEFAULT_TARGET_PATH =
|
|
4671
|
+
import { homedir as homedir6 } from "os";
|
|
4672
|
+
import { isAbsolute as isAbsolute4, join as join8, resolve as resolve8 } from "path";
|
|
4673
|
+
import { readYamlFile as readYamlFile5 } from "@basou/core";
|
|
4674
|
+
var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir6(), ".basou", "protocols.yaml");
|
|
4675
|
+
var DEFAULT_TARGET_PATH = join8(homedir6(), ".claude", "CLAUDE.md");
|
|
4605
4676
|
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
|
|
4606
4677
|
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
|
|
4607
|
-
function
|
|
4608
|
-
if (p === "~") return
|
|
4609
|
-
if (p.startsWith("~/")) return
|
|
4678
|
+
function expandTilde3(p) {
|
|
4679
|
+
if (p === "~") return homedir6();
|
|
4680
|
+
if (p.startsWith("~/")) return join8(homedir6(), p.slice(2));
|
|
4610
4681
|
return p;
|
|
4611
4682
|
}
|
|
4612
|
-
function
|
|
4683
|
+
function isRecord3(value) {
|
|
4613
4684
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4614
4685
|
}
|
|
4615
4686
|
async function loadProtocolsConfig(configPath = DEFAULT_PROTOCOLS_CONFIG_PATH) {
|
|
4616
4687
|
let raw;
|
|
4617
4688
|
try {
|
|
4618
|
-
raw = await
|
|
4689
|
+
raw = await readYamlFile5(configPath);
|
|
4619
4690
|
} catch (error) {
|
|
4620
4691
|
if (error instanceof Error && error.message === "YAML file not found") {
|
|
4621
4692
|
throw new Error(
|
|
@@ -4627,7 +4698,7 @@ async function loadProtocolsConfig(configPath = DEFAULT_PROTOCOLS_CONFIG_PATH) {
|
|
|
4627
4698
|
}
|
|
4628
4699
|
throw error;
|
|
4629
4700
|
}
|
|
4630
|
-
if (!
|
|
4701
|
+
if (!isRecord3(raw) || !Array.isArray(raw.protocols)) {
|
|
4631
4702
|
throw new Error("~/.basou/protocols.yaml must contain a 'protocols:' list.");
|
|
4632
4703
|
}
|
|
4633
4704
|
for (const key of Object.keys(raw)) {
|
|
@@ -4640,7 +4711,7 @@ async function loadProtocolsConfig(configPath = DEFAULT_PROTOCOLS_CONFIG_PATH) {
|
|
|
4640
4711
|
const seen = /* @__PURE__ */ new Set();
|
|
4641
4712
|
const result = [];
|
|
4642
4713
|
for (const entry of raw.protocols) {
|
|
4643
|
-
if (!
|
|
4714
|
+
if (!isRecord3(entry)) {
|
|
4644
4715
|
throw new Error("Each protocol entry must be a mapping with a 'source' key.");
|
|
4645
4716
|
}
|
|
4646
4717
|
for (const key of Object.keys(entry)) {
|
|
@@ -4654,11 +4725,11 @@ async function loadProtocolsConfig(configPath = DEFAULT_PROTOCOLS_CONFIG_PATH) {
|
|
|
4654
4725
|
if (entry.title !== void 0 && (typeof entry.title !== "string" || entry.title.trim().length === 0)) {
|
|
4655
4726
|
throw new Error("A protocol entry 'title' must be a non-empty string when present.");
|
|
4656
4727
|
}
|
|
4657
|
-
const expanded =
|
|
4658
|
-
if (!
|
|
4728
|
+
const expanded = expandTilde3(entry.source.trim());
|
|
4729
|
+
if (!isAbsolute4(expanded)) {
|
|
4659
4730
|
throw new Error("Protocol 'source' paths must be absolute (or start with '~').");
|
|
4660
4731
|
}
|
|
4661
|
-
const abs =
|
|
4732
|
+
const abs = resolve8(expanded);
|
|
4662
4733
|
if (seen.has(abs)) {
|
|
4663
4734
|
throw new Error("Duplicate protocol source (each source path may appear only once).");
|
|
4664
4735
|
}
|
|
@@ -4858,16 +4929,16 @@ import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
|
4858
4929
|
|
|
4859
4930
|
// src/commands/refresh-watch.ts
|
|
4860
4931
|
import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
4861
|
-
import { homedir as
|
|
4862
|
-
import { join as
|
|
4932
|
+
import { homedir as homedir7 } from "os";
|
|
4933
|
+
import { join as join9 } from "path";
|
|
4863
4934
|
import { findErrorCode as findErrorCode8 } from "@basou/core";
|
|
4864
4935
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
4865
4936
|
var MIN_WATCH_INTERVAL_SEC = 5;
|
|
4866
4937
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
4867
4938
|
function watchedRoots(ctx) {
|
|
4868
4939
|
return [
|
|
4869
|
-
ctx.codexSessionsDir ??
|
|
4870
|
-
ctx.claudeProjectsDir ??
|
|
4940
|
+
ctx.codexSessionsDir ?? join9(homedir7(), ".codex", "sessions"),
|
|
4941
|
+
ctx.claudeProjectsDir ?? join9(homedir7(), ".claude", "projects")
|
|
4871
4942
|
];
|
|
4872
4943
|
}
|
|
4873
4944
|
async function scanSourceLogs(roots) {
|
|
@@ -4881,7 +4952,7 @@ async function scanSourceLogs(roots) {
|
|
|
4881
4952
|
throw new Error("Failed to read a source log directory", { cause: error });
|
|
4882
4953
|
}
|
|
4883
4954
|
for (const entry of entries) {
|
|
4884
|
-
const full =
|
|
4955
|
+
const full = join9(dir, entry.name);
|
|
4885
4956
|
if (entry.isDirectory()) {
|
|
4886
4957
|
await walk(full);
|
|
4887
4958
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -4988,19 +5059,19 @@ function parseInterval(value) {
|
|
|
4988
5059
|
return seconds;
|
|
4989
5060
|
}
|
|
4990
5061
|
function abortableSleep(ms, signal) {
|
|
4991
|
-
return new Promise((
|
|
5062
|
+
return new Promise((resolve12) => {
|
|
4992
5063
|
if (signal.aborted) {
|
|
4993
|
-
|
|
5064
|
+
resolve12();
|
|
4994
5065
|
return;
|
|
4995
5066
|
}
|
|
4996
5067
|
let timer;
|
|
4997
5068
|
const onAbort = () => {
|
|
4998
5069
|
clearTimeout(timer);
|
|
4999
|
-
|
|
5070
|
+
resolve12();
|
|
5000
5071
|
};
|
|
5001
5072
|
timer = setTimeout(() => {
|
|
5002
5073
|
signal.removeEventListener("abort", onAbort);
|
|
5003
|
-
|
|
5074
|
+
resolve12();
|
|
5004
5075
|
}, ms);
|
|
5005
5076
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
5006
5077
|
});
|
|
@@ -5199,7 +5270,7 @@ async function assertWorkspaceInitialized8(basouRoot) {
|
|
|
5199
5270
|
}
|
|
5200
5271
|
|
|
5201
5272
|
// src/commands/report.ts
|
|
5202
|
-
import { isAbsolute as
|
|
5273
|
+
import { isAbsolute as isAbsolute5, resolve as resolve9 } from "path";
|
|
5203
5274
|
import {
|
|
5204
5275
|
assertBasouRootSafe as assertBasouRootSafe10,
|
|
5205
5276
|
basouPaths as basouPaths12,
|
|
@@ -5239,7 +5310,7 @@ async function doRunReportGenerate(options, ctx) {
|
|
|
5239
5310
|
onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
|
|
5240
5311
|
});
|
|
5241
5312
|
if (options.out !== void 0) {
|
|
5242
|
-
const outPath =
|
|
5313
|
+
const outPath = isAbsolute5(options.out) ? options.out : resolve9(cwd, options.out);
|
|
5243
5314
|
await writeMarkdownFile6(outPath, result.body);
|
|
5244
5315
|
const { sessions, decisions, tasks } = result.data;
|
|
5245
5316
|
console.error(
|
|
@@ -5402,8 +5473,8 @@ function renderReviewGaps(summary) {
|
|
|
5402
5473
|
|
|
5403
5474
|
// src/commands/run.ts
|
|
5404
5475
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
5405
|
-
import { homedir as
|
|
5406
|
-
import { join as
|
|
5476
|
+
import { homedir as homedir8 } from "os";
|
|
5477
|
+
import { join as join10 } from "path";
|
|
5407
5478
|
import {
|
|
5408
5479
|
acquireLock as acquireLock5,
|
|
5409
5480
|
assertBasouRootSafe as assertBasouRootSafe11,
|
|
@@ -5417,7 +5488,7 @@ import {
|
|
|
5417
5488
|
overwriteYamlFile as overwriteYamlFile2,
|
|
5418
5489
|
prefixedUlid as prefixedUlid4,
|
|
5419
5490
|
readManifest as readManifest7,
|
|
5420
|
-
readYamlFile as
|
|
5491
|
+
readYamlFile as readYamlFile6,
|
|
5421
5492
|
resolveClaudeCodeCommand,
|
|
5422
5493
|
resolveRepositoryRoot as resolveRepositoryRoot9,
|
|
5423
5494
|
SessionSchema as SessionSchema2,
|
|
@@ -5456,13 +5527,13 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
5456
5527
|
await assertBasouRootSafe11(paths.root);
|
|
5457
5528
|
const manifest = await readManifest7(paths);
|
|
5458
5529
|
const sessionId = prefixedUlid4("ses");
|
|
5459
|
-
const sessionDir =
|
|
5530
|
+
const sessionDir = join10(paths.sessions, sessionId);
|
|
5460
5531
|
await mkdir2(sessionDir, { recursive: true });
|
|
5461
5532
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
5462
5533
|
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
5463
5534
|
});
|
|
5464
5535
|
const startedAt = now().toISOString();
|
|
5465
|
-
const sessionYamlPath =
|
|
5536
|
+
const sessionYamlPath = join10(sessionDir, "session.yaml");
|
|
5466
5537
|
const session = buildInitialSession2({
|
|
5467
5538
|
id: sessionId,
|
|
5468
5539
|
command,
|
|
@@ -5588,7 +5659,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
5588
5659
|
const rawRelated = computeRelatedFiles(preSnapshot, postSnapshot, diff);
|
|
5589
5660
|
const relatedFiles = sanitizeRelatedFiles(rawRelated, {
|
|
5590
5661
|
workingDirectory: repoRoot,
|
|
5591
|
-
homedir:
|
|
5662
|
+
homedir: homedir8()
|
|
5592
5663
|
}).sanitized;
|
|
5593
5664
|
const finalStatus = decideFinalStatus2(result, signalReceived);
|
|
5594
5665
|
await appendEvent(sessionDir, {
|
|
@@ -5732,7 +5803,7 @@ function buildInitialSession2(input) {
|
|
|
5732
5803
|
source: { ...claudeCodeAdapterMetadata },
|
|
5733
5804
|
started_at: input.startedAt,
|
|
5734
5805
|
status: "initialized",
|
|
5735
|
-
working_directory: sanitizeWorkingDirectory2(input.cwd, { homedir:
|
|
5806
|
+
working_directory: sanitizeWorkingDirectory2(input.cwd, { homedir: homedir8() }),
|
|
5736
5807
|
invocation: {
|
|
5737
5808
|
command: input.command,
|
|
5738
5809
|
args: [...input.args],
|
|
@@ -5744,7 +5815,7 @@ function buildInitialSession2(input) {
|
|
|
5744
5815
|
};
|
|
5745
5816
|
}
|
|
5746
5817
|
async function mutateSessionYaml2(filePath, mutator) {
|
|
5747
|
-
const raw = await
|
|
5818
|
+
const raw = await readYamlFile6(filePath);
|
|
5748
5819
|
const parsed = SessionSchema2.parse(raw);
|
|
5749
5820
|
mutator(parsed);
|
|
5750
5821
|
const validated = SessionSchema2.parse(parsed);
|
|
@@ -5805,7 +5876,7 @@ async function resolveRepositoryRootForRun(cwd) {
|
|
|
5805
5876
|
|
|
5806
5877
|
// src/commands/session.ts
|
|
5807
5878
|
import { readFile as readFile4 } from "fs/promises";
|
|
5808
|
-
import { basename as basename6, isAbsolute as
|
|
5879
|
+
import { basename as basename6, isAbsolute as isAbsolute6, join as join11, relative as relative3 } from "path";
|
|
5809
5880
|
import {
|
|
5810
5881
|
acquireLock as acquireLock6,
|
|
5811
5882
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
@@ -5817,7 +5888,7 @@ import {
|
|
|
5817
5888
|
loadSessionEntries,
|
|
5818
5889
|
readAllEvents,
|
|
5819
5890
|
readManifest as readManifest8,
|
|
5820
|
-
readYamlFile as
|
|
5891
|
+
readYamlFile as readYamlFile7,
|
|
5821
5892
|
rechainSessionInPlace,
|
|
5822
5893
|
resolveSessionId as resolveSessionId3,
|
|
5823
5894
|
resolveTaskId,
|
|
@@ -5930,11 +6001,11 @@ async function doRunSessionShow(idInput, options, ctx) {
|
|
|
5930
6001
|
const paths = basouPaths15(repositoryRoot);
|
|
5931
6002
|
await assertWorkspaceInitialized10(paths.root);
|
|
5932
6003
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
5933
|
-
const sessionDir =
|
|
5934
|
-
const sessionYamlPath =
|
|
6004
|
+
const sessionDir = join11(paths.sessions, sessionId);
|
|
6005
|
+
const sessionYamlPath = join11(sessionDir, "session.yaml");
|
|
5935
6006
|
let session;
|
|
5936
6007
|
try {
|
|
5937
|
-
const raw = await
|
|
6008
|
+
const raw = await readYamlFile7(sessionYamlPath);
|
|
5938
6009
|
session = SessionSchema3.parse(raw);
|
|
5939
6010
|
} catch (error) {
|
|
5940
6011
|
if (findErrorCode11(error, "ENOENT")) {
|
|
@@ -6052,7 +6123,7 @@ function formatSessionWork(session, events, now) {
|
|
|
6052
6123
|
}
|
|
6053
6124
|
function formatWorkingDir(workingDir, repositoryRoot, options) {
|
|
6054
6125
|
if (options.fullPath === true) return workingDir;
|
|
6055
|
-
if (!
|
|
6126
|
+
if (!isAbsolute6(workingDir)) {
|
|
6056
6127
|
if (workingDir === ".") return "<repository_root>";
|
|
6057
6128
|
return workingDir;
|
|
6058
6129
|
}
|
|
@@ -6669,7 +6740,7 @@ async function resolveRepositoryRootForStatus(cwd) {
|
|
|
6669
6740
|
|
|
6670
6741
|
// src/commands/task.ts
|
|
6671
6742
|
import { readFile as readFile5 } from "fs/promises";
|
|
6672
|
-
import { join as
|
|
6743
|
+
import { join as join12 } from "path";
|
|
6673
6744
|
import {
|
|
6674
6745
|
archiveTask,
|
|
6675
6746
|
assertBasouRootSafe as assertBasouRootSafe15,
|
|
@@ -6996,7 +7067,7 @@ async function doRunTaskShow(idInput, options, ctx) {
|
|
|
6996
7067
|
const events = [];
|
|
6997
7068
|
const linkedSessionIds = new Set(doc.task.task.linked_sessions);
|
|
6998
7069
|
for (const s of sessions) {
|
|
6999
|
-
const sessionDir =
|
|
7070
|
+
const sessionDir = join12(paths.sessions, s.sessionId);
|
|
7000
7071
|
try {
|
|
7001
7072
|
for await (const ev of replayEvents2(sessionDir, {
|
|
7002
7073
|
onWarning: (w) => printReplayWarning(w, s.sessionId)
|
|
@@ -7899,7 +7970,7 @@ async function assertWorkspaceInitialized13(basouRoot) {
|
|
|
7899
7970
|
// src/commands/view.ts
|
|
7900
7971
|
import { spawn } from "child_process";
|
|
7901
7972
|
import { createHash } from "crypto";
|
|
7902
|
-
import { basename as basename7, resolve as
|
|
7973
|
+
import { basename as basename7, resolve as resolve11 } from "path";
|
|
7903
7974
|
import {
|
|
7904
7975
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
7905
7976
|
basouPaths as basouPaths20,
|
|
@@ -7912,7 +7983,7 @@ import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
|
|
|
7912
7983
|
// src/lib/portfolio-safety.ts
|
|
7913
7984
|
import { execFile } from "child_process";
|
|
7914
7985
|
import { lstat as lstat2, realpath as realpath2 } from "fs/promises";
|
|
7915
|
-
import { isAbsolute as
|
|
7986
|
+
import { isAbsolute as isAbsolute7, join as join13, relative as relative4, resolve as resolve10 } from "path";
|
|
7916
7987
|
import { promisify } from "util";
|
|
7917
7988
|
import { readManifest as readManifest11 } from "@basou/core";
|
|
7918
7989
|
var execFileAsync = promisify(execFile);
|
|
@@ -7923,12 +7994,12 @@ async function canonical(p) {
|
|
|
7923
7994
|
try {
|
|
7924
7995
|
return await realpath2(p);
|
|
7925
7996
|
} catch {
|
|
7926
|
-
return
|
|
7997
|
+
return resolve10(p);
|
|
7927
7998
|
}
|
|
7928
7999
|
}
|
|
7929
8000
|
function isInside(child, parent) {
|
|
7930
8001
|
const rel = relative4(parent, child);
|
|
7931
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
8002
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
|
|
7932
8003
|
}
|
|
7933
8004
|
function isBasouPath(p) {
|
|
7934
8005
|
return p === ".basou" || p.startsWith(".basou/") || p.includes("/.basou/") || p.endsWith("/.basou");
|
|
@@ -7936,7 +8007,7 @@ function isBasouPath(p) {
|
|
|
7936
8007
|
async function inspectRepo(repoPath) {
|
|
7937
8008
|
let hasEntry = false;
|
|
7938
8009
|
try {
|
|
7939
|
-
await lstat2(
|
|
8010
|
+
await lstat2(join13(repoPath, ".basou"));
|
|
7940
8011
|
hasEntry = true;
|
|
7941
8012
|
} catch (error) {
|
|
7942
8013
|
if (errorCode(error) !== "ENOENT") {
|
|
@@ -7985,7 +8056,7 @@ async function checkPortfolioSafety(workspaces) {
|
|
|
7985
8056
|
}
|
|
7986
8057
|
const monitored = /* @__PURE__ */ new Map();
|
|
7987
8058
|
for (const root of sourceRoots) {
|
|
7988
|
-
const display =
|
|
8059
|
+
const display = resolve10(ws.repoRoot, root);
|
|
7989
8060
|
const real = await canonical(display);
|
|
7990
8061
|
if (real !== wsReal) monitored.set(real, display);
|
|
7991
8062
|
}
|
|
@@ -8037,7 +8108,7 @@ function formatSafetyReport(result) {
|
|
|
8037
8108
|
|
|
8038
8109
|
// src/lib/view-server.ts
|
|
8039
8110
|
import { createServer } from "http";
|
|
8040
|
-
import { join as
|
|
8111
|
+
import { join as join14 } from "path";
|
|
8041
8112
|
import {
|
|
8042
8113
|
computeWorkStats as computeWorkStats2,
|
|
8043
8114
|
enumerateApprovals as enumerateApprovals2,
|
|
@@ -8694,7 +8765,7 @@ function startViewServer(opts) {
|
|
|
8694
8765
|
};
|
|
8695
8766
|
let boundPort = port;
|
|
8696
8767
|
const getPort = () => boundPort;
|
|
8697
|
-
return new Promise((
|
|
8768
|
+
return new Promise((resolve12, reject) => {
|
|
8698
8769
|
const server = createServer((req, res) => {
|
|
8699
8770
|
handleRequest(req, res, deps, getPort, runExclusive).catch((error) => {
|
|
8700
8771
|
sendError(res, error instanceof HttpError ? error.status : 500, pathlessMessage(error));
|
|
@@ -8705,7 +8776,7 @@ function startViewServer(opts) {
|
|
|
8705
8776
|
const address = server.address();
|
|
8706
8777
|
boundPort = isAddressInfo(address) ? address.port : port;
|
|
8707
8778
|
server.off("error", reject);
|
|
8708
|
-
|
|
8779
|
+
resolve12({
|
|
8709
8780
|
url: `http://${host}:${boundPort}`,
|
|
8710
8781
|
port: boundPort,
|
|
8711
8782
|
close: () => closeServer(server)
|
|
@@ -8717,8 +8788,8 @@ function isAddressInfo(value) {
|
|
|
8717
8788
|
return value !== null && typeof value === "object";
|
|
8718
8789
|
}
|
|
8719
8790
|
function closeServer(server) {
|
|
8720
|
-
return new Promise((
|
|
8721
|
-
server.close(() =>
|
|
8791
|
+
return new Promise((resolve12) => {
|
|
8792
|
+
server.close(() => resolve12());
|
|
8722
8793
|
server.closeAllConnections();
|
|
8723
8794
|
});
|
|
8724
8795
|
}
|
|
@@ -8992,7 +9063,7 @@ async function sessionDetail(ws, sessionId) {
|
|
|
8992
9063
|
throw error;
|
|
8993
9064
|
}
|
|
8994
9065
|
try {
|
|
8995
|
-
const events = await readAllEvents2(
|
|
9066
|
+
const events = await readAllEvents2(join14(ws.paths.sessions, sessionId));
|
|
8996
9067
|
return { session, events };
|
|
8997
9068
|
} catch {
|
|
8998
9069
|
return { session, events: [], degraded: true };
|
|
@@ -9212,12 +9283,12 @@ async function buildSingleDeps(ctx, cwd) {
|
|
|
9212
9283
|
return { workspaces: [entry], mode: "single", nowProvider: nowProviderOf(ctx) };
|
|
9213
9284
|
}
|
|
9214
9285
|
async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
9215
|
-
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path:
|
|
9286
|
+
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve11(cwd, p) })) : await loadPortfolioConfig(ctx.portfolioConfigPath);
|
|
9216
9287
|
const entries = [];
|
|
9217
9288
|
const seenPath = /* @__PURE__ */ new Set();
|
|
9218
9289
|
const seenKey = /* @__PURE__ */ new Set();
|
|
9219
9290
|
for (const spec of specs) {
|
|
9220
|
-
const repoRoot =
|
|
9291
|
+
const repoRoot = resolve11(spec.path);
|
|
9221
9292
|
if (seenPath.has(repoRoot)) continue;
|
|
9222
9293
|
seenPath.add(repoRoot);
|
|
9223
9294
|
const entry = await buildWorkspaceEntry(repoRoot, ctx, spec.label);
|
|
@@ -9289,7 +9360,7 @@ function openInBrowser(url, override) {
|
|
|
9289
9360
|
}
|
|
9290
9361
|
}
|
|
9291
9362
|
function waitForShutdown(signal) {
|
|
9292
|
-
return new Promise((
|
|
9363
|
+
return new Promise((resolve12) => {
|
|
9293
9364
|
const cleanup = () => {
|
|
9294
9365
|
process.off("SIGINT", onSignal);
|
|
9295
9366
|
process.off("SIGTERM", onSignal);
|
|
@@ -9297,18 +9368,18 @@ function waitForShutdown(signal) {
|
|
|
9297
9368
|
};
|
|
9298
9369
|
const onSignal = () => {
|
|
9299
9370
|
cleanup();
|
|
9300
|
-
|
|
9371
|
+
resolve12();
|
|
9301
9372
|
};
|
|
9302
9373
|
const onAbort = () => {
|
|
9303
9374
|
cleanup();
|
|
9304
|
-
|
|
9375
|
+
resolve12();
|
|
9305
9376
|
};
|
|
9306
9377
|
process.on("SIGINT", onSignal);
|
|
9307
9378
|
process.on("SIGTERM", onSignal);
|
|
9308
9379
|
if (signal !== void 0) {
|
|
9309
9380
|
if (signal.aborted) {
|
|
9310
9381
|
cleanup();
|
|
9311
|
-
|
|
9382
|
+
resolve12();
|
|
9312
9383
|
return;
|
|
9313
9384
|
}
|
|
9314
9385
|
signal.addEventListener("abort", onAbort);
|