@younndai/lyt 0.13.4 → 0.20.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/README.md +27 -19
- package/dist/automator-run.d.ts +1 -0
- package/dist/automator-run.d.ts.map +1 -1
- package/dist/automator-run.js +5 -3
- package/dist/automator-run.js.map +1 -1
- package/dist/bench/pod-harness.d.ts.map +1 -1
- package/dist/bench/pod-harness.js +49 -1
- package/dist/bench/pod-harness.js.map +1 -1
- package/dist/build-program.d.ts +10 -1
- package/dist/build-program.d.ts.map +1 -1
- package/dist/build-program.js +23 -3
- package/dist/build-program.js.map +1 -1
- package/dist/cli.js +10 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +1 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +406 -224
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +107 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/outdated.d.ts.map +1 -1
- package/dist/commands/outdated.js +19 -5
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/update.d.ts +90 -2
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +883 -48
- package/dist/commands/update.js.map +1 -1
- package/dist/flows/init-bootstrap.d.ts +8 -1
- package/dist/flows/init-bootstrap.d.ts.map +1 -1
- package/dist/flows/init-bootstrap.js +244 -74
- package/dist/flows/init-bootstrap.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/install/doctor-health.d.ts +16 -0
- package/dist/install/doctor-health.d.ts.map +1 -0
- package/dist/install/doctor-health.js +575 -0
- package/dist/install/doctor-health.js.map +1 -0
- package/dist/install/provider-inventory.d.ts +40 -0
- package/dist/install/provider-inventory.d.ts.map +1 -0
- package/dist/install/provider-inventory.js +73 -0
- package/dist/install/provider-inventory.js.map +1 -0
- package/dist/install/reconcile-engine.d.ts +87 -0
- package/dist/install/reconcile-engine.d.ts.map +1 -0
- package/dist/install/reconcile-engine.js +771 -0
- package/dist/install/reconcile-engine.js.map +1 -0
- package/dist/install/target-artifacts.d.ts +91 -0
- package/dist/install/target-artifacts.d.ts.map +1 -0
- package/dist/install/target-artifacts.js +597 -0
- package/dist/install/target-artifacts.js.map +1 -0
- package/dist/install/update-operation.d.ts +47 -0
- package/dist/install/update-operation.d.ts.map +1 -0
- package/dist/install/update-operation.js +318 -0
- package/dist/install/update-operation.js.map +1 -0
- package/dist/install/update-plan.d.ts +132 -0
- package/dist/install/update-plan.d.ts.map +1 -0
- package/dist/install/update-plan.js +418 -0
- package/dist/install/update-plan.js.map +1 -0
- package/package.json +16 -6
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,10 @@ export { healPod, summarizeHeal } from "./flows/heal.js";
|
|
|
8
8
|
export type { HealResult, HealPodOptions, ManualHealAction, ManualHealEntry, } from "./flows/heal.js";
|
|
9
9
|
export { initBootstrapFlow } from "./flows/init-bootstrap.js";
|
|
10
10
|
export type { DiscoveredRepo, DiscoveredRepoKind, DiscoveryProbe, DiscoveryProbeResult, InitBootstrapArgs, InitBootstrapBranch, InitBootstrapCustomOverrides, InitBootstrapFederation, InitBootstrapMeshAssignment, InitBootstrapMode, InitBootstrapResult, IntegrityIssue, IntegrityStatus, } from "./flows/init-bootstrap.js";
|
|
11
|
+
export { applyExactUpdatePlanV1, canonicalJson, digestCanonical, parseUpdatePlanV1, prepareUpdatePlanV1, UPDATE_PLAN_PACKAGES, UPDATE_PLAN_SCHEMA_ID, } from "./install/update-plan.js";
|
|
12
|
+
export type { ExactUpdateApplyEffects, ExactUpdateApplyResultV1, ManagedDestinationV1, PrepareUpdatePlanV1Input, RegistryPackageFactV1, UpdatePlanV1, } from "./install/update-plan.js";
|
|
13
|
+
export { buildInstallProviderInventoryV1 } from "./install/provider-inventory.js";
|
|
14
|
+
export type { InstallableProviderObjectV1, InstallProviderV1, } from "./install/provider-inventory.js";
|
|
15
|
+
export { defaultReconcileJournalRoot, prepareInstallReconcilePlanV1, reconcileInstallProvidersV1, } from "./install/reconcile-engine.js";
|
|
16
|
+
export type { InstallReconcileEnvelopeV1, InstallReconcilePlanV1, ReconcileEngineOptions, } from "./install/reconcile-engine.js";
|
|
11
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,EAC5B,uBAAuB,EACvB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,eAAe,GAChB,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,EAC5B,uBAAuB,EACvB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,eAAe,GAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,YAAY,EACV,2BAA2B,EAC3B,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,0BAA0B,EAC1B,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,+BAA+B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -21,4 +21,7 @@ export { buildPrimerCommand } from "./commands/primer.js";
|
|
|
21
21
|
export { buildLytInitCommand } from "./commands/init.js";
|
|
22
22
|
export { healPod, summarizeHeal } from "./flows/heal.js";
|
|
23
23
|
export { initBootstrapFlow } from "./flows/init-bootstrap.js";
|
|
24
|
+
export { applyExactUpdatePlanV1, canonicalJson, digestCanonical, parseUpdatePlanV1, prepareUpdatePlanV1, UPDATE_PLAN_PACKAGES, UPDATE_PLAN_SCHEMA_ID, } from "./install/update-plan.js";
|
|
25
|
+
export { buildInstallProviderInventoryV1 } from "./install/provider-inventory.js";
|
|
26
|
+
export { defaultReconcileJournalRoot, prepareInstallReconcilePlanV1, reconcileInstallProvidersV1, } from "./install/reconcile-engine.js";
|
|
24
27
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAOzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAOzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAgB9D,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AASlC,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAKlF,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CheckResult } from "@younndai/lyt-vault";
|
|
2
|
+
import { type InstallProviderV1 } from "./provider-inventory.js";
|
|
3
|
+
import { inspectUpdateOperationJournalV1 } from "./update-operation.js";
|
|
4
|
+
import { type UpdatePackageName } from "./update-plan.js";
|
|
5
|
+
export interface DoctorInstallHealthOptions {
|
|
6
|
+
readonly topLevelVersion: string;
|
|
7
|
+
readonly homeDir?: string;
|
|
8
|
+
readonly lytHome?: string;
|
|
9
|
+
readonly providers?: readonly InstallProviderV1[];
|
|
10
|
+
readonly providerInventory?: () => Promise<readonly InstallProviderV1[]>;
|
|
11
|
+
readonly updateOperationInspector?: typeof inspectUpdateOperationJournalV1;
|
|
12
|
+
readonly installedPackageRootResolver?: (packageName: UpdatePackageName) => string | null;
|
|
13
|
+
readonly scopedPackageRoot?: string | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function checkInstallHealthV1(options: DoctorInstallHealthOptions): Promise<readonly CheckResult[]>;
|
|
16
|
+
//# sourceMappingURL=doctor-health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-health.d.ts","sourceRoot":"","sources":["../../src/install/doctor-health.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAmC,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAOlG,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAClD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAAC;IACzE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,+BAA+B,CAAC;IAC3E,QAAQ,CAAC,4BAA4B,CAAC,EAAE,CAAC,WAAW,EAAE,iBAAiB,KAAK,MAAM,GAAG,IAAI,CAAC;IAE1F,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5C;AAQD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAiDjC"}
|
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 MARLINK TRADING SRL (YounndAI)
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
17
|
+
import { createRequire } from "node:module";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { dirname, join, resolve } from "node:path";
|
|
20
|
+
import { detectInstalledRuntimes, INSTALLABLE_RUNTIMES, inspectCurrencyStateV1, } from "@younndai/lyt-vault";
|
|
21
|
+
import { buildInstallProviderInventoryV1 } from "./provider-inventory.js";
|
|
22
|
+
import { inspectReconcileJournalV1, prepareInstallReconcilePlanV1 } from "./reconcile-engine.js";
|
|
23
|
+
import { inspectInstalledPackagePayloadsV1, readInstalledStateAnchorV1, verifyInstalledStateAnchorV1, } from "./target-artifacts.js";
|
|
24
|
+
import { inspectUpdateOperationJournalV1 } from "./update-operation.js";
|
|
25
|
+
import { UPDATE_PLAN_PACKAGES } from "./update-plan.js";
|
|
26
|
+
export async function checkInstallHealthV1(options) {
|
|
27
|
+
const profileHome = resolve(options.homeDir ?? homedir());
|
|
28
|
+
const lytHome = resolve(options.lytHome ??
|
|
29
|
+
(options.homeDir !== undefined
|
|
30
|
+
? join(profileHome, "lyt")
|
|
31
|
+
: (process.env.LYT_HOME ?? join(profileHome, "lyt"))));
|
|
32
|
+
const checks = [];
|
|
33
|
+
let providers = null;
|
|
34
|
+
try {
|
|
35
|
+
providers =
|
|
36
|
+
options.providers ??
|
|
37
|
+
(await (options.providerInventory ??
|
|
38
|
+
(() => buildInstallProviderInventoryV1({ homeDir: profileHome })))());
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
checks.push(failure("install.package-graph", "installed Lyt package graph", `could not inspect the exact seven-package graph: ${safeError(error)}`));
|
|
42
|
+
}
|
|
43
|
+
if (providers !== null) {
|
|
44
|
+
checks.push(...packageVersionChecks(providers, options.topLevelVersion));
|
|
45
|
+
checks.push(graphCheck(providers, options.topLevelVersion));
|
|
46
|
+
checks.push(...providerContentChecks(providers, join(lytHome, ".update-operations")));
|
|
47
|
+
}
|
|
48
|
+
checks.push(...packagePayloadChecks(join(lytHome, ".update-operations"), options.installedPackageRootResolver));
|
|
49
|
+
checks.push(...optionalRuntimeChecks(profileHome));
|
|
50
|
+
checks.push(channelCheck(lytHome));
|
|
51
|
+
checks.push(...journalChecks(lytHome));
|
|
52
|
+
checks.push(...updateOperationChecks(join(lytHome, ".update-operations"), options.updateOperationInspector ?? inspectUpdateOperationJournalV1));
|
|
53
|
+
checks.push(...residueChecks(options.scopedPackageRoot ?? defaultScopedPackageRoot()));
|
|
54
|
+
return checks;
|
|
55
|
+
}
|
|
56
|
+
function packageVersionChecks(providers, topLevelVersion) {
|
|
57
|
+
const byName = new Map(providers.map((provider) => [provider.package, provider]));
|
|
58
|
+
return UPDATE_PLAN_PACKAGES.map((name) => {
|
|
59
|
+
const provider = byName.get(name);
|
|
60
|
+
const observed = provider?.version ?? null;
|
|
61
|
+
const current = observed === topLevelVersion;
|
|
62
|
+
return {
|
|
63
|
+
id: `install.package.${checkId(name)}`,
|
|
64
|
+
group: "install",
|
|
65
|
+
label: `${name} version`,
|
|
66
|
+
status: current ? "pass" : "fail",
|
|
67
|
+
message: observed === null
|
|
68
|
+
? `missing (top-level lyt is v${topLevelVersion})`
|
|
69
|
+
: current
|
|
70
|
+
? `v${observed} (matches top-level lyt)`
|
|
71
|
+
: `v${observed} (top-level lyt is v${topLevelVersion})`,
|
|
72
|
+
remediation: current ? undefined : "Run: lyt update --check, then lyt update",
|
|
73
|
+
detail: { package: name, observed, authoritative_top_level_version: topLevelVersion },
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function graphCheck(providers, topLevelVersion) {
|
|
78
|
+
const names = providers.map((provider) => provider.package).sort();
|
|
79
|
+
const exactNames = [...UPDATE_PLAN_PACKAGES].sort();
|
|
80
|
+
const exactSet = JSON.stringify(names) === JSON.stringify(exactNames);
|
|
81
|
+
const exactVersions = providers.every((provider) => provider.version === topLevelVersion);
|
|
82
|
+
if (exactSet && exactVersions) {
|
|
83
|
+
try {
|
|
84
|
+
prepareInstallReconcilePlanV1(providers);
|
|
85
|
+
return {
|
|
86
|
+
id: "install.package-graph",
|
|
87
|
+
group: "install",
|
|
88
|
+
label: "installed Lyt package graph",
|
|
89
|
+
status: "pass",
|
|
90
|
+
message: `seven packages at v${topLevelVersion}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return failure("install.package-graph", "installed Lyt package graph", `incompatible or unsafe provider graph: ${safeError(error)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return failure("install.package-graph", "installed Lyt package graph", "the installed Lyt package graph is incomplete or version-skewed", {
|
|
98
|
+
expected_packages: exactNames,
|
|
99
|
+
observed_packages: names,
|
|
100
|
+
authoritative_top_level_version: topLevelVersion,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function providerContentChecks(providers, updateOperationRoot) {
|
|
104
|
+
let plan;
|
|
105
|
+
try {
|
|
106
|
+
plan = prepareInstallReconcilePlanV1(providers);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return [
|
|
110
|
+
failure("install.provider-content", "managed skills and manuals", `cannot safely inspect provider content: ${safeError(error)}`),
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
if (plan.objects.length === 0) {
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
id: "install.provider-content",
|
|
117
|
+
group: "install",
|
|
118
|
+
label: "managed skills and manuals",
|
|
119
|
+
status: "info",
|
|
120
|
+
message: "no optional agent runtime homes detected",
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
const observed = new Map(plan.objects.map((object) => [object.object_id, object.observed_digest]));
|
|
125
|
+
const anchorRead = readInstalledStateAnchorV1(updateOperationRoot);
|
|
126
|
+
const verification = verifyInstalledStateAnchorV1(updateOperationRoot, observed);
|
|
127
|
+
const expected = new Map(anchorRead.status === "present"
|
|
128
|
+
? anchorRead.anchor.provider_destinations.map((entry) => [entry.object_id, entry.digest])
|
|
129
|
+
: []);
|
|
130
|
+
const mismatched = new Set(verification.mismatched_object_ids);
|
|
131
|
+
const anchorCheck = verification.status === "match"
|
|
132
|
+
? {
|
|
133
|
+
id: "install.provider-anchor",
|
|
134
|
+
group: "install",
|
|
135
|
+
label: "sealed managed-content anchor",
|
|
136
|
+
status: "pass",
|
|
137
|
+
message: "installed managed content matches externally anchored update evidence",
|
|
138
|
+
}
|
|
139
|
+
: verification.status === "missing"
|
|
140
|
+
? {
|
|
141
|
+
id: "install.provider-anchor",
|
|
142
|
+
group: "install",
|
|
143
|
+
label: "sealed managed-content anchor",
|
|
144
|
+
status: "warn",
|
|
145
|
+
message: "no sealed installed-state anchor is available; managed-content health is unknown",
|
|
146
|
+
remediation: "Run a successful exact Lyt update to create installed-state evidence",
|
|
147
|
+
}
|
|
148
|
+
: failure("install.provider-anchor", "sealed managed-content anchor", verification.status === "invalid"
|
|
149
|
+
? "installed-state anchor is invalid or unsafe"
|
|
150
|
+
: "installed managed content does not match its sealed update evidence", { mismatched_object_ids: verification.mismatched_object_ids }, "Run: lyt doctor --json, then resume or repeat the exact Lyt update");
|
|
151
|
+
return [
|
|
152
|
+
anchorCheck,
|
|
153
|
+
...plan.objects.map((object) => {
|
|
154
|
+
const anchoredDigest = expected.get(object.object_id) ?? null;
|
|
155
|
+
const anchorUnknown = verification.status === "missing" || anchoredDigest === null;
|
|
156
|
+
const anchorInvalid = verification.status === "invalid";
|
|
157
|
+
const anchorMismatch = mismatched.has(object.object_id);
|
|
158
|
+
const status = anchorInvalid || anchorMismatch ? "fail" : anchorUnknown ? "warn" : "pass";
|
|
159
|
+
const message = anchorInvalid
|
|
160
|
+
? `${object.target_path} cannot be verified because its installed-state anchor is invalid`
|
|
161
|
+
: anchorMismatch
|
|
162
|
+
? `${object.target_path} differs from its externally anchored digest`
|
|
163
|
+
: anchorUnknown
|
|
164
|
+
? `${object.target_path} has no externally anchored expected digest; health is unknown`
|
|
165
|
+
: `${object.target_path} matches its externally anchored digest`;
|
|
166
|
+
return {
|
|
167
|
+
id: `install.provider.${checkId(object.object_id)}`,
|
|
168
|
+
group: "install",
|
|
169
|
+
label: `${object.kind === "directory-link" ? "skill" : "agent manual"} ${object.object_id}`,
|
|
170
|
+
status,
|
|
171
|
+
message,
|
|
172
|
+
remediation: status === "pass"
|
|
173
|
+
? undefined
|
|
174
|
+
: "Run: lyt doctor --json, then resume or repeat the exact Lyt update",
|
|
175
|
+
detail: {
|
|
176
|
+
object_id: object.object_id,
|
|
177
|
+
provider_package: object.provider_package,
|
|
178
|
+
provider_version: object.provider_version,
|
|
179
|
+
target_path: object.target_path,
|
|
180
|
+
anchored_expected_digest: anchoredDigest,
|
|
181
|
+
observed_digest: object.observed_digest,
|
|
182
|
+
disposition: object.disposition,
|
|
183
|
+
refusal_code: object.refusal_code,
|
|
184
|
+
anchor_status: verification.status,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}),
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
function packagePayloadChecks(updateOperationRoot, resolveRoot) {
|
|
191
|
+
const anchor = readInstalledStateAnchorV1(updateOperationRoot);
|
|
192
|
+
if (anchor.status === "missing") {
|
|
193
|
+
return [
|
|
194
|
+
{
|
|
195
|
+
id: "install.package-payloads",
|
|
196
|
+
group: "install",
|
|
197
|
+
label: "sealed installed package payloads",
|
|
198
|
+
status: "warn",
|
|
199
|
+
message: "no sealed installed-state anchor is available; package payload health is unknown",
|
|
200
|
+
remediation: "Run a successful exact Lyt update to create installed-state evidence",
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
if (anchor.status === "invalid") {
|
|
205
|
+
return [
|
|
206
|
+
failure("install.package-payloads", "sealed installed package payloads", "installed-state anchor is invalid or unsafe", undefined, "Run: lyt doctor --json, then resume or repeat the exact Lyt update"),
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
if (anchor.anchor === null) {
|
|
210
|
+
return [
|
|
211
|
+
failure("install.package-payloads", "sealed installed package payloads", "installed-state anchor could not be read"),
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
return inspectInstalledPackagePayloadsV1(anchor.anchor, resolveRoot).map((inspection) => ({
|
|
215
|
+
id: `install.package-payload.${checkId(inspection.package)}`,
|
|
216
|
+
group: "install",
|
|
217
|
+
label: `${inspection.package} installed payload`,
|
|
218
|
+
status: inspection.status === "match" ? "pass" : "fail",
|
|
219
|
+
message: inspection.status === "match"
|
|
220
|
+
? `${inspection.package} matches its externally anchored payload digest`
|
|
221
|
+
: `${inspection.package} payload is ${inspection.status}`,
|
|
222
|
+
remediation: inspection.status === "match"
|
|
223
|
+
? undefined
|
|
224
|
+
: "Run: lyt doctor --json, then resume or repeat the exact Lyt update",
|
|
225
|
+
detail: {
|
|
226
|
+
package: inspection.package,
|
|
227
|
+
root: inspection.root,
|
|
228
|
+
expected_digest: inspection.expected_digest,
|
|
229
|
+
observed_digest: inspection.observed_digest,
|
|
230
|
+
payload_status: inspection.status,
|
|
231
|
+
},
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
function optionalRuntimeChecks(home) {
|
|
235
|
+
const detected = new Set(detectInstalledRuntimes(home));
|
|
236
|
+
return INSTALLABLE_RUNTIMES.filter((runtime) => !detected.has(runtime)).map((runtime) => ({
|
|
237
|
+
id: `install.runtime.${runtime}`,
|
|
238
|
+
group: "install",
|
|
239
|
+
label: `${runtime} runtime home`,
|
|
240
|
+
status: "info",
|
|
241
|
+
message: `not detected at ${join(home, `.${runtime}`)} (optional; no managed content expected)`,
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
function channelCheck(lytHome) {
|
|
245
|
+
const path = join(lytHome, ".update-channel.json");
|
|
246
|
+
const inspection = inspectCurrencyStateV1({ homeDir: lytHome });
|
|
247
|
+
if (inspection.channelStatus === "missing") {
|
|
248
|
+
return {
|
|
249
|
+
id: "install.update-channel",
|
|
250
|
+
group: "install",
|
|
251
|
+
label: "configured update channel",
|
|
252
|
+
status: "warn",
|
|
253
|
+
message: "no update channel configured",
|
|
254
|
+
remediation: "Run: lyt update --channel <alpha|latest> --configure --json",
|
|
255
|
+
detail: { configured: null, observed: null, path },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (inspection.channelStatus === "malformed" || inspection.channel === null) {
|
|
259
|
+
return failure("install.update-channel", "configured update channel", "update-channel configuration exists but is malformed", { configured: null, observed: null, path });
|
|
260
|
+
}
|
|
261
|
+
const configured = inspection.channel;
|
|
262
|
+
const observation = inspection.observation;
|
|
263
|
+
const validObserved = inspection.cacheStatus === "current" && observation !== null;
|
|
264
|
+
const invalidObserved = inspection.cacheStatus === "malformed" || inspection.cacheStatus === "tampered";
|
|
265
|
+
return {
|
|
266
|
+
id: "install.update-channel",
|
|
267
|
+
group: "install",
|
|
268
|
+
label: "configured and observed update channel",
|
|
269
|
+
status: validObserved ? "pass" : invalidObserved ? "fail" : "warn",
|
|
270
|
+
message: validObserved
|
|
271
|
+
? `${configured} configured; canonical cache observation is valid (${observation.version})`
|
|
272
|
+
: `${configured} configured; canonical cache is ${inspection.cacheStatus}`,
|
|
273
|
+
remediation: validObserved
|
|
274
|
+
? undefined
|
|
275
|
+
: invalidObserved
|
|
276
|
+
? "Inspect the local currency cache, then run: lyt outdated --channel " + configured
|
|
277
|
+
: `Run: lyt outdated --channel ${configured}`,
|
|
278
|
+
detail: {
|
|
279
|
+
configured,
|
|
280
|
+
observed: observation?.observedTag ?? null,
|
|
281
|
+
channel_status: inspection.channelStatus,
|
|
282
|
+
cache_status: inspection.cacheStatus,
|
|
283
|
+
registry: observation?.registry ?? null,
|
|
284
|
+
package: observation?.packageName ?? null,
|
|
285
|
+
version: observation?.version ?? null,
|
|
286
|
+
integrity: observation?.integrity ?? null,
|
|
287
|
+
checked_at: observation?.checkedAt ?? null,
|
|
288
|
+
channel_path: path,
|
|
289
|
+
cache_path: join(lytHome, ".currency-checks.json"),
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function journalChecks(lytHome) {
|
|
294
|
+
const journalRoot = join(lytHome, ".install-reconcile");
|
|
295
|
+
const operationsRoot = join(journalRoot, "operations");
|
|
296
|
+
if (!existsSync(operationsRoot)) {
|
|
297
|
+
return [
|
|
298
|
+
{
|
|
299
|
+
id: "install.reconcile-journal",
|
|
300
|
+
group: "install",
|
|
301
|
+
label: "install reconciliation journal",
|
|
302
|
+
status: "pass",
|
|
303
|
+
message: "no pending install reconciliation journal",
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
if (lstatSync(journalRoot).isSymbolicLink() || lstatSync(operationsRoot).isSymbolicLink()) {
|
|
309
|
+
return [
|
|
310
|
+
failure("install.reconcile-journal", "install reconciliation journal", "unsafe journal path"),
|
|
311
|
+
];
|
|
312
|
+
}
|
|
313
|
+
const entries = readdirSync(operationsRoot, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
314
|
+
const checks = [];
|
|
315
|
+
if (entries.length > 128) {
|
|
316
|
+
checks.push(failure("install.reconcile-journal.truncated", "install reconciliation journal", `journal inspection refused full coverage: ${entries.length} operations exceed the 128-entry bound`, { observed_operations: entries.length, inspected_operations: 128 }));
|
|
317
|
+
}
|
|
318
|
+
let completed = 0;
|
|
319
|
+
for (const entry of entries.slice(0, 128)) {
|
|
320
|
+
const operationPath = join(operationsRoot, entry.name);
|
|
321
|
+
if (entry.isSymbolicLink() ||
|
|
322
|
+
lstatSync(operationPath).isSymbolicLink() ||
|
|
323
|
+
!entry.isDirectory()) {
|
|
324
|
+
checks.push(failure(`install.reconcile-journal.${checkId(entry.name)}`, "install reconciliation journal", `${operationPath} is an unsafe operation-journal shape`));
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const inspection = inspectReconcileJournalV1(journalRoot, entry.name);
|
|
328
|
+
if (!inspection.valid || inspection.journal === null) {
|
|
329
|
+
checks.push(failure(`install.reconcile-journal.${checkId(entry.name)}`, "install reconciliation journal", `operation ${entry.name} is malformed or tampered (${inspection.error_code ?? "unknown"})`, { operation_id: entry.name, error_code: inspection.error_code }));
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const journal = inspection.journal;
|
|
333
|
+
const incomplete = journal.pending.length > 0 ||
|
|
334
|
+
journal.refused.length > 0 ||
|
|
335
|
+
journal.active_attempt_id !== null;
|
|
336
|
+
if (!incomplete) {
|
|
337
|
+
completed += 1;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
checks.push(failure(`install.reconcile-journal.${checkId(entry.name)}`, "install reconciliation journal", `operation ${entry.name} is incomplete`, {
|
|
341
|
+
operation_id: entry.name,
|
|
342
|
+
pending: journal.pending,
|
|
343
|
+
refused: journal.refused,
|
|
344
|
+
active_attempt_id: journal.active_attempt_id,
|
|
345
|
+
resume_command: `lyt install reconcile --resume ${entry.name} --apply --json`,
|
|
346
|
+
}, `Run: lyt install reconcile --resume ${entry.name} --apply --json`));
|
|
347
|
+
}
|
|
348
|
+
if (checks.length === 0) {
|
|
349
|
+
return [
|
|
350
|
+
{
|
|
351
|
+
id: "install.reconcile-journal",
|
|
352
|
+
group: "install",
|
|
353
|
+
label: "install reconciliation journal",
|
|
354
|
+
status: "pass",
|
|
355
|
+
message: `${completed} completed journal(s); none pending`,
|
|
356
|
+
},
|
|
357
|
+
];
|
|
358
|
+
}
|
|
359
|
+
return checks;
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
return [
|
|
363
|
+
failure("install.reconcile-journal", "install reconciliation journal", `cannot safely inspect journal: ${safeError(error)}`),
|
|
364
|
+
];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function updateOperationChecks(root, inspect) {
|
|
368
|
+
const operationsRoot = join(root, "operations");
|
|
369
|
+
const pass = (count) => ({
|
|
370
|
+
id: "install.update-operation",
|
|
371
|
+
group: "install",
|
|
372
|
+
label: "durable update operations",
|
|
373
|
+
status: "pass",
|
|
374
|
+
message: `${count} completed update operation(s); none pending`,
|
|
375
|
+
});
|
|
376
|
+
if (!existsSync(root))
|
|
377
|
+
return [pass(0)];
|
|
378
|
+
try {
|
|
379
|
+
if (lstatSync(root).isSymbolicLink()) {
|
|
380
|
+
return [failure("install.update-operation", "durable update operations", "unsafe root path")];
|
|
381
|
+
}
|
|
382
|
+
if (!existsSync(operationsRoot))
|
|
383
|
+
return [pass(0)];
|
|
384
|
+
if (lstatSync(operationsRoot).isSymbolicLink()) {
|
|
385
|
+
return [
|
|
386
|
+
failure("install.update-operation", "durable update operations", "unsafe operations path"),
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
const entries = readdirSync(operationsRoot, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
390
|
+
const checks = [];
|
|
391
|
+
if (entries.length > 128) {
|
|
392
|
+
checks.push(failure("install.update-operation.truncated", "durable update operations", `update-operation inspection refused full coverage: ${entries.length} operations exceed the 128-entry bound`, { observed_operations: entries.length, inspected_operations: 128 }));
|
|
393
|
+
}
|
|
394
|
+
let completed = 0;
|
|
395
|
+
for (const entry of entries.slice(0, 128)) {
|
|
396
|
+
const operationPath = join(operationsRoot, entry.name);
|
|
397
|
+
if (entry.isSymbolicLink() ||
|
|
398
|
+
lstatSync(operationPath).isSymbolicLink() ||
|
|
399
|
+
!entry.isDirectory()) {
|
|
400
|
+
checks.push(failure(`install.update-operation.${checkId(entry.name)}`, "durable update operations", `${operationPath} is an unsafe operation-journal shape`));
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const inspection = inspect(entry.name, root);
|
|
404
|
+
if (!inspection.valid || inspection.journal === null) {
|
|
405
|
+
checks.push(failure(`install.update-operation.${checkId(entry.name)}`, "durable update operations", `operation ${entry.name} is missing, failed, or tampered (${inspection.error_code})`, { operation_id: entry.name, error_code: inspection.error_code }, `Run: lyt update --resume ${entry.name} --yes --json`));
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (inspection.status === "completed") {
|
|
409
|
+
completed += 1;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
checks.push(failure(`install.update-operation.${checkId(entry.name)}`, "durable update operations", `operation ${entry.name} is pending or interrupted`, {
|
|
413
|
+
operation_id: entry.name,
|
|
414
|
+
status: inspection.status,
|
|
415
|
+
completed_boundaries: inspection.journal.completed_boundaries,
|
|
416
|
+
active_attempt_id: inspection.journal.active_attempt_id,
|
|
417
|
+
resume_command: `lyt update --resume ${entry.name} --yes --json`,
|
|
418
|
+
}, `Run: lyt update --resume ${entry.name} --yes --json`));
|
|
419
|
+
}
|
|
420
|
+
return checks.length === 0 ? [pass(completed)] : checks;
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
return [
|
|
424
|
+
failure("install.update-operation", "durable update operations", `cannot safely inspect update operations: ${safeError(error)}`),
|
|
425
|
+
];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function residueChecks(scopedRoot) {
|
|
429
|
+
if (scopedRoot === null || !existsSync(scopedRoot)) {
|
|
430
|
+
return [
|
|
431
|
+
{
|
|
432
|
+
id: "install.npm-residue",
|
|
433
|
+
group: "install",
|
|
434
|
+
label: "npm Lyt temporary residue",
|
|
435
|
+
status: "info",
|
|
436
|
+
message: "global Lyt package parent is unavailable for residue inspection",
|
|
437
|
+
},
|
|
438
|
+
];
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
if (lstatSync(scopedRoot).isSymbolicLink()) {
|
|
442
|
+
return [
|
|
443
|
+
failure("install.npm-residue", "npm Lyt temporary residue", "unsafe package parent path"),
|
|
444
|
+
];
|
|
445
|
+
}
|
|
446
|
+
const allCandidates = readdirSync(scopedRoot, { withFileTypes: true })
|
|
447
|
+
.filter((entry) => /^\.lyt-[A-Za-z0-9]+$/u.test(entry.name))
|
|
448
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
449
|
+
if (allCandidates.length === 0) {
|
|
450
|
+
return [
|
|
451
|
+
{
|
|
452
|
+
id: "install.npm-residue",
|
|
453
|
+
group: "install",
|
|
454
|
+
label: "npm Lyt temporary residue",
|
|
455
|
+
status: "pass",
|
|
456
|
+
message: "no direct .lyt-* residue beside the installed Lyt package",
|
|
457
|
+
},
|
|
458
|
+
];
|
|
459
|
+
}
|
|
460
|
+
const checks = [];
|
|
461
|
+
if (allCandidates.length > 32) {
|
|
462
|
+
checks.push(failure("install.npm-residue.truncated", "npm Lyt temporary residue", `residue inspection refused full coverage: ${allCandidates.length} candidates exceed the 32-entry bound`, { observed_candidates: allCandidates.length, inspected_candidates: 32 }));
|
|
463
|
+
}
|
|
464
|
+
for (const entry of allCandidates.slice(0, 32)) {
|
|
465
|
+
const path = join(scopedRoot, entry.name);
|
|
466
|
+
if (entry.isSymbolicLink() || lstatSync(path).isSymbolicLink()) {
|
|
467
|
+
checks.push(failure(`install.npm-residue.${checkId(entry.name)}`, "npm Lyt temporary residue", `${path} is unsafe to inspect or remove`));
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (!entry.isDirectory()) {
|
|
471
|
+
checks.push(failure(`install.npm-residue.${checkId(entry.name)}`, "npm Lyt temporary residue", `${path} has an unsafe non-directory residue shape`));
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const tree = inspectResidueTree(path, 2048);
|
|
475
|
+
if (tree.unsafePath !== null) {
|
|
476
|
+
checks.push(failure(`install.npm-residue.${checkId(entry.name)}`, "npm Lyt temporary residue", `${tree.unsafePath} is a nested reparse/symlink path; residue is unsafe`));
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (tree.truncated) {
|
|
480
|
+
checks.push(failure(`install.npm-residue.${checkId(entry.name)}`, "npm Lyt temporary residue", `${path} exceeds the 2048-entry safe inspection bound`, { path, inspected_entries: tree.entries, truncated: true }));
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
const packageJson = join(path, "package.json");
|
|
484
|
+
let attributable = false;
|
|
485
|
+
try {
|
|
486
|
+
const manifest = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
487
|
+
attributable = manifest.name === "@younndai/lyt";
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
attributable = false;
|
|
491
|
+
}
|
|
492
|
+
checks.push({
|
|
493
|
+
id: `install.npm-residue.${checkId(entry.name)}`,
|
|
494
|
+
group: "install",
|
|
495
|
+
label: "npm Lyt temporary residue",
|
|
496
|
+
status: attributable ? "warn" : "info",
|
|
497
|
+
message: attributable
|
|
498
|
+
? `${path} is attributable temporary Lyt residue (preserved; no automatic deletion)`
|
|
499
|
+
: `${path} is unattributed .lyt-* residue (preserved; no automatic deletion)`,
|
|
500
|
+
detail: { path, attributable, automatic_deletion: false },
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
return checks;
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
return [
|
|
507
|
+
failure("install.npm-residue", "npm Lyt temporary residue", `cannot safely inspect package residue: ${safeError(error)}`),
|
|
508
|
+
];
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function inspectResidueTree(root, maximumEntries) {
|
|
512
|
+
const pending = [root];
|
|
513
|
+
let entries = 0;
|
|
514
|
+
while (pending.length > 0) {
|
|
515
|
+
const directory = pending.pop();
|
|
516
|
+
const children = readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
517
|
+
for (const child of children) {
|
|
518
|
+
entries += 1;
|
|
519
|
+
if (entries > maximumEntries) {
|
|
520
|
+
return { entries: maximumEntries, truncated: true, unsafePath: null };
|
|
521
|
+
}
|
|
522
|
+
const path = join(directory, child.name);
|
|
523
|
+
if (child.isSymbolicLink() || lstatSync(path).isSymbolicLink()) {
|
|
524
|
+
return { entries, truncated: false, unsafePath: path };
|
|
525
|
+
}
|
|
526
|
+
if (child.isDirectory())
|
|
527
|
+
pending.push(path);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return { entries, truncated: false, unsafePath: null };
|
|
531
|
+
}
|
|
532
|
+
function defaultScopedPackageRoot() {
|
|
533
|
+
try {
|
|
534
|
+
const require = createRequire(import.meta.url);
|
|
535
|
+
const entry = require.resolve("@younndai/lyt");
|
|
536
|
+
let current = dirname(entry);
|
|
537
|
+
for (let depth = 0; depth < 6; depth += 1) {
|
|
538
|
+
const manifest = join(current, "package.json");
|
|
539
|
+
if (existsSync(manifest)) {
|
|
540
|
+
const parsed = JSON.parse(readFileSync(manifest, "utf8"));
|
|
541
|
+
if (parsed.name === "@younndai/lyt")
|
|
542
|
+
return dirname(current);
|
|
543
|
+
}
|
|
544
|
+
const parent = dirname(current);
|
|
545
|
+
if (parent === current)
|
|
546
|
+
break;
|
|
547
|
+
current = parent;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
catch {
|
|
551
|
+
}
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
function failure(id, label, message, detail, remediation) {
|
|
555
|
+
return {
|
|
556
|
+
id,
|
|
557
|
+
group: "install",
|
|
558
|
+
label,
|
|
559
|
+
status: "fail",
|
|
560
|
+
message,
|
|
561
|
+
...(remediation === undefined ? {} : { remediation }),
|
|
562
|
+
...(detail === undefined ? {} : { detail }),
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function checkId(value) {
|
|
566
|
+
return value
|
|
567
|
+
.replace(/[^A-Za-z0-9]+/g, "-")
|
|
568
|
+
.replace(/^-|-$/g, "")
|
|
569
|
+
.toLowerCase();
|
|
570
|
+
}
|
|
571
|
+
function safeError(error) {
|
|
572
|
+
const value = error instanceof Error ? error.message : String(error);
|
|
573
|
+
return value.replace(/[\r\n]+/g, " ").slice(0, 240);
|
|
574
|
+
}
|
|
575
|
+
//# sourceMappingURL=doctor-health.js.map
|