nawabari 0.9.2 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -803
- package/dist/cli-command-registry.js +46 -6
- package/dist/cli-command-registry.js.map +1 -1
- package/dist/cli.d.ts +5 -3
- package/dist/cli.js +157 -34
- package/dist/cli.js.map +1 -1
- package/dist/contract.js +11 -3
- package/dist/contract.js.map +1 -1
- package/dist/domain/compatibility-runtime-projection.d.ts +24 -0
- package/dist/domain/compatibility-runtime-projection.js +197 -0
- package/dist/domain/compatibility-runtime-projection.js.map +1 -0
- package/dist/domain/doctor.d.ts +2 -2
- package/dist/domain/doctor.js +72 -51
- package/dist/domain/doctor.js.map +1 -1
- package/dist/domain/errors.d.ts +1 -1
- package/dist/domain/errors.js +5 -1
- package/dist/domain/errors.js.map +1 -1
- package/dist/domain/fhs-development-runtime.d.ts +90 -0
- package/dist/domain/fhs-development-runtime.js +385 -0
- package/dist/domain/fhs-development-runtime.js.map +1 -0
- package/dist/domain/fhs-runtime.d.ts +36 -0
- package/dist/domain/fhs-runtime.js +776 -0
- package/dist/domain/fhs-runtime.js.map +1 -0
- package/dist/domain/landlock.d.ts +11 -4
- package/dist/domain/landlock.js +44 -21
- package/dist/domain/landlock.js.map +1 -1
- package/dist/domain/nix-runtime-closure.d.ts +70 -0
- package/dist/domain/nix-runtime-closure.js +449 -0
- package/dist/domain/nix-runtime-closure.js.map +1 -0
- package/dist/domain/pnpm-middleware-backend-materialization.d.ts +117 -0
- package/dist/domain/pnpm-middleware-backend-materialization.js +311 -0
- package/dist/domain/pnpm-middleware-backend-materialization.js.map +1 -0
- package/dist/domain/runtime-executable-projection.d.ts +33 -0
- package/dist/domain/runtime-executable-projection.js +204 -0
- package/dist/domain/runtime-executable-projection.js.map +1 -0
- package/dist/domain/runtime-file-identity.d.ts +37 -0
- package/dist/domain/runtime-file-identity.js +114 -0
- package/dist/domain/runtime-file-identity.js.map +1 -0
- package/dist/domain/runtime-profile.d.ts +66 -0
- package/dist/domain/runtime-profile.js +356 -0
- package/dist/domain/runtime-profile.js.map +1 -0
- package/dist/domain/runtime-projection.d.ts +101 -0
- package/dist/domain/runtime-projection.js +316 -0
- package/dist/domain/runtime-projection.js.map +1 -0
- package/dist/domain/runtime-provider-pnpm-middleware.d.ts +41 -0
- package/dist/domain/runtime-provider-pnpm-middleware.js +316 -0
- package/dist/domain/runtime-provider-pnpm-middleware.js.map +1 -0
- package/dist/domain/runtime-provider-tgrep.d.ts +60 -0
- package/dist/domain/runtime-provider-tgrep.js +537 -0
- package/dist/domain/runtime-provider-tgrep.js.map +1 -0
- package/dist/domain/runtime-resolution.d.ts +57 -0
- package/dist/domain/runtime-resolution.js +242 -0
- package/dist/domain/runtime-resolution.js.map +1 -0
- package/dist/domain/sandbox-launcher.d.ts +7 -0
- package/dist/domain/sandbox-launcher.js +380 -56
- package/dist/domain/sandbox-launcher.js.map +1 -1
- package/dist/domain/sandbox.d.ts +59 -4
- package/dist/domain/sandbox.js +100 -5
- package/dist/domain/sandbox.js.map +1 -1
- package/dist/domain/session-backend.js +20 -1
- package/dist/domain/session-backend.js.map +1 -1
- package/dist/domain/session.d.ts +3 -2
- package/dist/domain/session.js.map +1 -1
- package/dist/domain/tgrep-runtime-materialization.d.ts +61 -0
- package/dist/domain/tgrep-runtime-materialization.js +172 -0
- package/dist/domain/tgrep-runtime-materialization.js.map +1 -0
- package/dist/operation-authorization.d.ts +10 -0
- package/dist/operation-authorization.js +12 -1
- package/dist/operation-authorization.js.map +1 -1
- package/dist/presentation.js +6 -3
- package/dist/presentation.js.map +1 -1
- package/dist/session-registry.d.ts +11 -3
- package/dist/session-registry.js +61 -45
- package/dist/session-registry.js.map +1 -1
- package/package.json +3 -1
|
@@ -222,12 +222,19 @@ export type GarbageCollectEligibility = "eligible" | "ineligible" | "ambiguous";
|
|
|
222
222
|
export type GarbageCollectSuspicionReason = "none" | "elapsed-age" | "explicit-lifecycle-state" | "missing-worktree" | "ambiguous-physical-state";
|
|
223
223
|
export type GarbageCollectEligibilityReason = "not-suspected" | "age-only" | "explicit-stale-state" | "explicit-closing-state" | "prunable-missing-worktree" | "physical-state-ambiguous";
|
|
224
224
|
/** Bounded machine-readable GC assessment. Session identity stays flat for API compatibility. */
|
|
225
|
-
export interface
|
|
225
|
+
export interface GarbageCollectAssessment extends SessionRecord {
|
|
226
226
|
readonly physicalState: string;
|
|
227
227
|
readonly suspicion: GarbageCollectSuspicion;
|
|
228
228
|
readonly suspicionReason: GarbageCollectSuspicionReason;
|
|
229
229
|
readonly destructiveEligibility: GarbageCollectEligibility;
|
|
230
230
|
readonly destructiveEligibilityReason: GarbageCollectEligibilityReason;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* A GC result adds lifecycle actions to the assessment. Diagnostics use the
|
|
234
|
+
* assessment alone so inspect has one canonical lifecycle projection rather
|
|
235
|
+
* than a copied nested projection.
|
|
236
|
+
*/
|
|
237
|
+
export interface GarbageCollectCandidate extends GarbageCollectAssessment {
|
|
231
238
|
/** Canonical read-only lifecycle projection for this GC observation. */
|
|
232
239
|
readonly lifecycle?: SessionLifecycleClassification;
|
|
233
240
|
/** Typed caller actions projected from the same lifecycle snapshot. */
|
|
@@ -312,7 +319,7 @@ export interface SessionDiagnostic {
|
|
|
312
319
|
/** All bounded actions available for the observed state, in deterministic order. */
|
|
313
320
|
readonly nextActions: readonly SessionLifecycleAction[];
|
|
314
321
|
readonly integrationEvidence: SessionDiagnosticIntegrationEvidence;
|
|
315
|
-
readonly garbageCollection:
|
|
322
|
+
readonly garbageCollection: GarbageCollectAssessment;
|
|
316
323
|
/** Canonical termination/recovery projection; additive for old consumers. */
|
|
317
324
|
readonly lifecycle?: SessionLifecycleClassification;
|
|
318
325
|
}
|
|
@@ -619,12 +626,13 @@ export declare class SessionRegistry {
|
|
|
619
626
|
private readonly protectedBranchNames;
|
|
620
627
|
private readonly protectedWorktreePaths;
|
|
621
628
|
private readonly worktreeRoot;
|
|
629
|
+
private readonly worktreeRootIsDefault;
|
|
622
630
|
private readonly staleAfterMs;
|
|
623
631
|
private readonly lockStaleAfterMs;
|
|
624
632
|
private readonly lockMetadataGraceMs;
|
|
625
633
|
private readonly lock;
|
|
626
634
|
constructor(options?: SessionRegistryOptions);
|
|
627
|
-
/** Resolved
|
|
635
|
+
/** Resolved managed root used for default session worktrees. */
|
|
628
636
|
get managedWorktreeRoot(): string;
|
|
629
637
|
read(): readonly SessionRecord[];
|
|
630
638
|
list(): readonly SessionRecord[];
|
package/dist/session-registry.js
CHANGED
|
@@ -75,6 +75,7 @@ export class SessionRegistry {
|
|
|
75
75
|
protectedBranchNames;
|
|
76
76
|
protectedWorktreePaths;
|
|
77
77
|
worktreeRoot;
|
|
78
|
+
worktreeRootIsDefault;
|
|
78
79
|
staleAfterMs;
|
|
79
80
|
lockStaleAfterMs;
|
|
80
81
|
lockMetadataGraceMs;
|
|
@@ -88,7 +89,14 @@ export class SessionRegistry {
|
|
|
88
89
|
this.defaultBranchName = options.defaultBranchName;
|
|
89
90
|
this.protectedBranchNames = Object.freeze([...(options.protectedBranchNames ?? [])]);
|
|
90
91
|
this.protectedWorktreePaths = Object.freeze([...(options.protectedWorktreePaths ?? [])]);
|
|
91
|
-
|
|
92
|
+
const configuredWorktreeRoot = options.worktreeRoot;
|
|
93
|
+
this.worktreeRootIsDefault = configuredWorktreeRoot === undefined;
|
|
94
|
+
if (configuredWorktreeRoot === undefined) {
|
|
95
|
+
this.worktreeRoot = defaultManagedWorktreeRoot(this.repository.worktreePath);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.worktreeRoot = resolveManagedWorktreeRoot(configuredWorktreeRoot);
|
|
99
|
+
}
|
|
92
100
|
this.staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
|
|
93
101
|
this.lockStaleAfterMs = options.lockStaleAfterMs ?? this.lockTimeoutMs;
|
|
94
102
|
this.lockMetadataGraceMs = options.lockMetadataGraceMs ?? DEFAULT_LOCK_METADATA_GRACE_MS;
|
|
@@ -117,7 +125,7 @@ export class SessionRegistry {
|
|
|
117
125
|
metadataGraceMs: this.lockMetadataGraceMs,
|
|
118
126
|
});
|
|
119
127
|
}
|
|
120
|
-
/** Resolved
|
|
128
|
+
/** Resolved managed root used for default session worktrees. */
|
|
121
129
|
get managedWorktreeRoot() {
|
|
122
130
|
return this.worktreeRoot;
|
|
123
131
|
}
|
|
@@ -1753,8 +1761,8 @@ export class SessionRegistry {
|
|
|
1753
1761
|
if (assessment.terminalOperation === "discard")
|
|
1754
1762
|
return false;
|
|
1755
1763
|
// Eligibility is evaluated from the GC authority's own bounded
|
|
1756
|
-
// evidence. Full cleanup blockers
|
|
1757
|
-
//
|
|
1764
|
+
// evidence. Full cleanup blockers are applied by the existing
|
|
1765
|
+
// preflight below.
|
|
1758
1766
|
const gcLifecycle = projectGarbageCollectAuthorization(assessment);
|
|
1759
1767
|
if (assessment.destructiveEligibility === "eligible" &&
|
|
1760
1768
|
(gcLifecycle.destructiveCleanupEligible || assessment.physicalState === "prunable-missing")) {
|
|
@@ -2624,8 +2632,7 @@ export class SessionRegistry {
|
|
|
2624
2632
|
phase: "termination",
|
|
2625
2633
|
});
|
|
2626
2634
|
const nextActions = projectSessionLifecycleActions({ classification: lifecycle, sessionId: record.sessionId });
|
|
2627
|
-
const garbageCollection = closedGarbageCollectionAssessment(record
|
|
2628
|
-
const projectedGarbageCollection = projectGarbageCollectCandidateLifecycle(garbageCollection, lifecycle, []);
|
|
2635
|
+
const garbageCollection = closedGarbageCollectionAssessment(record);
|
|
2629
2636
|
return Object.freeze({
|
|
2630
2637
|
schemaVersion: SESSION_DIAGNOSTIC_SCHEMA_VERSION,
|
|
2631
2638
|
operation: "diagnostic",
|
|
@@ -2644,7 +2651,7 @@ export class SessionRegistry {
|
|
|
2644
2651
|
...(nextActions.length === 0 ? {} : { nextAction: nextActions[0] }),
|
|
2645
2652
|
nextActions,
|
|
2646
2653
|
integrationEvidence: Object.freeze(integrationEvidence),
|
|
2647
|
-
garbageCollection
|
|
2654
|
+
garbageCollection,
|
|
2648
2655
|
lifecycle,
|
|
2649
2656
|
});
|
|
2650
2657
|
}
|
|
@@ -2677,7 +2684,6 @@ export class SessionRegistry {
|
|
|
2677
2684
|
ageSuspicious: Date.parse(now) - Date.parse(record.updatedAt) >= this.staleAfterMs,
|
|
2678
2685
|
phase: "termination",
|
|
2679
2686
|
});
|
|
2680
|
-
garbageCollection = projectGarbageCollectCandidateLifecycle(garbageCollection, lifecycle, blockers);
|
|
2681
2687
|
const safeActions = blockers.length > 0
|
|
2682
2688
|
? sortStrings(Array.from(new Set(blockers.flatMap((blocker) => blocker.safeActions))))
|
|
2683
2689
|
: closeReadiness === "ready"
|
|
@@ -3019,7 +3025,17 @@ export class SessionRegistry {
|
|
|
3019
3025
|
: options.worktreeRoot !== undefined
|
|
3020
3026
|
? resolveManagedWorktreeRoot(options.worktreeRoot)
|
|
3021
3027
|
: this.worktreeRoot;
|
|
3022
|
-
const
|
|
3028
|
+
const legacyRoot = path.dirname(this.repository.worktreePath);
|
|
3029
|
+
const legacyCompatibilityEnabled = this.worktreeRootIsDefault && options.worktreeRoot === undefined;
|
|
3030
|
+
const defaultRootSelected = legacyCompatibilityEnabled &&
|
|
3031
|
+
(options.worktreePath === undefined ||
|
|
3032
|
+
!path.isAbsolute(options.worktreePath) ||
|
|
3033
|
+
isPathInside(this.worktreeRoot, path.resolve(options.worktreePath)));
|
|
3034
|
+
if (defaultRootSelected)
|
|
3035
|
+
ensureManagedWorktreeRoot(this.worktreeRoot);
|
|
3036
|
+
const requestedWorktreePath = options.worktreePath === undefined
|
|
3037
|
+
? resolveProvisionedWorktreePath(path.join(effectiveRoot, `${path.basename(this.repository.worktreePath)}-${sessionId}`), effectiveRoot)
|
|
3038
|
+
: resolveProvisionedWorktreePathWithLegacyCompatibility(options.worktreePath, effectiveRoot, legacyCompatibilityEnabled ? legacyRoot : undefined);
|
|
3023
3039
|
const defaultWorktreePath = worktrees.find((worktree) => !worktree.prunable)?.worktreePath ?? this.repository.worktreePath;
|
|
3024
3040
|
const configuredProtectedWorktrees = [
|
|
3025
3041
|
...this.protectedWorktreePaths,
|
|
@@ -3359,17 +3375,6 @@ function assessGarbageCollection(record, now, staleAfterMs, worktrees) {
|
|
|
3359
3375
|
destructiveEligibility = "ineligible";
|
|
3360
3376
|
destructiveEligibilityReason = "age-only";
|
|
3361
3377
|
}
|
|
3362
|
-
const lifecycle = projectGarbageCollectAuthorization({
|
|
3363
|
-
state: record.state,
|
|
3364
|
-
physicalState: worktreeState.kind,
|
|
3365
|
-
terminalOperation: record.terminalOperation,
|
|
3366
|
-
suspicion,
|
|
3367
|
-
destructiveEligibility,
|
|
3368
|
-
});
|
|
3369
|
-
const nextActions = projectSessionLifecycleActions({
|
|
3370
|
-
classification: lifecycle,
|
|
3371
|
-
sessionId: record.sessionId,
|
|
3372
|
-
});
|
|
3373
3378
|
return Object.freeze({
|
|
3374
3379
|
...cloneSessionRecord(record),
|
|
3375
3380
|
physicalState: worktreeState.kind,
|
|
@@ -3377,8 +3382,6 @@ function assessGarbageCollection(record, now, staleAfterMs, worktrees) {
|
|
|
3377
3382
|
suspicionReason,
|
|
3378
3383
|
destructiveEligibility,
|
|
3379
3384
|
destructiveEligibilityReason,
|
|
3380
|
-
lifecycle,
|
|
3381
|
-
nextActions,
|
|
3382
3385
|
});
|
|
3383
3386
|
}
|
|
3384
3387
|
/** Evaluate GC's independent authorization through the canonical machine. */
|
|
@@ -3394,18 +3397,7 @@ function projectGarbageCollectAuthorization(candidate) {
|
|
|
3394
3397
|
phase: "termination",
|
|
3395
3398
|
});
|
|
3396
3399
|
}
|
|
3397
|
-
function closedGarbageCollectionAssessment(record
|
|
3398
|
-
const lifecycle = classifySessionLifecycle({
|
|
3399
|
-
sessionState: record.state,
|
|
3400
|
-
physicalState: "closed",
|
|
3401
|
-
closeReadiness: "ready",
|
|
3402
|
-
terminalOperation: record.terminalOperation,
|
|
3403
|
-
phase: "termination",
|
|
3404
|
-
});
|
|
3405
|
-
const nextActions = projectSessionLifecycleActions({
|
|
3406
|
-
classification: lifecycle,
|
|
3407
|
-
sessionId: record.sessionId,
|
|
3408
|
-
});
|
|
3400
|
+
function closedGarbageCollectionAssessment(record) {
|
|
3409
3401
|
return Object.freeze({
|
|
3410
3402
|
...cloneSessionRecord(record),
|
|
3411
3403
|
physicalState: "closed",
|
|
@@ -3413,8 +3405,6 @@ function closedGarbageCollectionAssessment(record, now) {
|
|
|
3413
3405
|
suspicionReason: "none",
|
|
3414
3406
|
destructiveEligibility: "ineligible",
|
|
3415
3407
|
destructiveEligibilityReason: "not-suspected",
|
|
3416
|
-
lifecycle,
|
|
3417
|
-
nextActions,
|
|
3418
3408
|
});
|
|
3419
3409
|
}
|
|
3420
3410
|
function isDestructiveGcEligible(record, worktrees) {
|
|
@@ -3576,15 +3566,6 @@ function toCleanupBlocker(error) {
|
|
|
3576
3566
|
recoveryHints: Object.freeze(recoveryHints),
|
|
3577
3567
|
});
|
|
3578
3568
|
}
|
|
3579
|
-
/** Keep the nested GC assessment on the same observed lifecycle snapshot. */
|
|
3580
|
-
function projectGarbageCollectCandidateLifecycle(candidate, lifecycle, blockers) {
|
|
3581
|
-
const nextActions = projectSessionLifecycleActions({
|
|
3582
|
-
classification: lifecycle,
|
|
3583
|
-
sessionId: candidate.sessionId,
|
|
3584
|
-
blockers,
|
|
3585
|
-
});
|
|
3586
|
-
return Object.freeze({ ...candidate, lifecycle, nextActions });
|
|
3587
|
-
}
|
|
3588
3569
|
function toDiagnosticBlocker(error) {
|
|
3589
3570
|
const blocker = toCleanupBlocker(error);
|
|
3590
3571
|
return Object.freeze({
|
|
@@ -3949,9 +3930,18 @@ function runMutationGit(git, args, cwd, phase, failureCode) {
|
|
|
3949
3930
|
...(error instanceof SessionRegistryError ? error.details : {}),
|
|
3950
3931
|
...(error instanceof SessionRegistryError ? { gitCode: error.code } : {}),
|
|
3951
3932
|
};
|
|
3933
|
+
if (failureCode === "COMMIT_FAILED" && isMissingGitIdentityFailure(details.stderr)) {
|
|
3934
|
+
throw new SessionRegistryError(failureCode, `${phase} Git operation failed: Git author identity (user.name/user.email) is unavailable`, {
|
|
3935
|
+
...details,
|
|
3936
|
+
hint: "Set repository-local identity with `git config user.name`/`git config user.email`, or configure the host's global Git identity before starting the session.",
|
|
3937
|
+
}, error);
|
|
3938
|
+
}
|
|
3952
3939
|
throw new SessionRegistryError(failureCode, `${phase} Git operation failed`, details, error);
|
|
3953
3940
|
}
|
|
3954
3941
|
}
|
|
3942
|
+
function isMissingGitIdentityFailure(stderr) {
|
|
3943
|
+
return typeof stderr === "string" && /Please tell me who you are|unable to auto-detect email address/u.test(stderr);
|
|
3944
|
+
}
|
|
3955
3945
|
/**
|
|
3956
3946
|
* Reconcile a bounded commit transport failure against the only authoritative
|
|
3957
3947
|
* local commit evidence: HEAD and the paths recorded by that HEAD. A
|
|
@@ -4467,6 +4457,23 @@ function generateUniqueSessionId(records, idGenerator) {
|
|
|
4467
4457
|
}
|
|
4468
4458
|
throw new SessionRegistryError("SESSION_ID_COLLISION", `Could not generate a unique session ID after ${MAX_ID_GENERATION_ATTEMPTS} attempts`, { attempts: MAX_ID_GENERATION_ATTEMPTS });
|
|
4469
4459
|
}
|
|
4460
|
+
function defaultManagedWorktreeRoot(repositoryWorktreePath) {
|
|
4461
|
+
const resolved = path.resolve(path.dirname(repositoryWorktreePath), ".nawabari", "worktrees");
|
|
4462
|
+
assertNoSymlinkPath(resolved);
|
|
4463
|
+
return resolved;
|
|
4464
|
+
}
|
|
4465
|
+
function ensureManagedWorktreeRoot(candidate) {
|
|
4466
|
+
try {
|
|
4467
|
+
assertNoSymlinkPath(candidate);
|
|
4468
|
+
fs.mkdirSync(candidate, { recursive: true, mode: 0o700 });
|
|
4469
|
+
return resolveManagedWorktreeRoot(candidate);
|
|
4470
|
+
}
|
|
4471
|
+
catch (error) {
|
|
4472
|
+
if (error instanceof SessionRegistryError)
|
|
4473
|
+
throw error;
|
|
4474
|
+
throw new SessionRegistryError("INVALID_WORKTREE_PATH", `Could not create managed worktree root: ${candidate}`, { worktree: candidate }, error);
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4470
4477
|
function resolveManagedWorktreeRoot(candidate) {
|
|
4471
4478
|
if (candidate.includes("\u0000") || candidate.trim().length === 0) {
|
|
4472
4479
|
throw new SessionRegistryError("INVALID_WORKTREE_PATH", `Invalid managed worktree root: ${candidate}`, {
|
|
@@ -4489,6 +4496,15 @@ function resolveManagedWorktreeRoot(candidate) {
|
|
|
4489
4496
|
throw new SessionRegistryError("INVALID_WORKTREE_PATH", `Could not resolve managed worktree root: ${resolved}`, { worktree: resolved }, error);
|
|
4490
4497
|
}
|
|
4491
4498
|
}
|
|
4499
|
+
function resolveProvisionedWorktreePathWithLegacyCompatibility(candidate, managedRoot, legacyRoot) {
|
|
4500
|
+
if (legacyRoot !== undefined && path.isAbsolute(candidate)) {
|
|
4501
|
+
const resolved = path.resolve(candidate);
|
|
4502
|
+
if (!isPathInside(managedRoot, resolved) && isPathInside(legacyRoot, resolved)) {
|
|
4503
|
+
return resolveProvisionedWorktreePath(candidate, legacyRoot);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
return resolveProvisionedWorktreePath(candidate, managedRoot);
|
|
4507
|
+
}
|
|
4492
4508
|
function resolveProvisionedWorktreePath(candidate, managedRoot) {
|
|
4493
4509
|
if (candidate.includes("\u0000") || candidate.trim().length === 0) {
|
|
4494
4510
|
throw new SessionRegistryError("INVALID_WORKTREE_PATH", `Invalid worktree path: ${candidate}`, {
|