@tomflow/proflow-platform-cli 0.1.13 → 0.1.15
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 +48 -43
- package/dist/deployment/adapter.d.ts +5 -5
- package/dist/deployment/adapter.js +2 -1
- package/dist/deployment/descriptor.d.ts +2 -3
- package/dist/deployment/descriptor.js +2 -3
- package/dist/src/binding/production-bindings.d.ts +17 -15
- package/dist/src/binding/production-bindings.js +18 -59
- package/dist/src/cli.d.ts +7 -9
- package/dist/src/cli.js +329 -1237
- package/dist/src/contracts.d.ts +4 -97
- package/dist/src/discovery/discover.js +0 -1
- package/dist/src/docs/aggregate.d.ts +18 -0
- package/dist/src/docs/aggregate.js +44 -0
- package/dist/src/docs/docs.d.ts +0 -31
- package/dist/src/docs/docs.js +0 -61
- package/dist/src/docs/index.d.ts +2 -1
- package/dist/src/docs/index.js +1 -1
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +0 -18
- package/dist/src/graph/graph.d.ts +1 -4
- package/dist/src/graph/graph.js +1 -13
- package/dist/src/graph/index.d.ts +2 -2
- package/dist/src/graph/index.js +1 -1
- package/dist/src/index.d.ts +4 -24
- package/dist/src/index.js +2 -16
- package/dist/src/install/package-manager.d.ts +21 -0
- package/dist/src/install/package-manager.js +153 -0
- package/dist/src/lifecycle/dispatch.d.ts +0 -30
- package/dist/src/lifecycle/dispatch.js +0 -120
- package/dist/src/lifecycle/index.d.ts +4 -3
- package/dist/src/lifecycle/index.js +2 -2
- package/dist/src/lifecycle/thin.d.ts +11 -0
- package/dist/src/lifecycle/thin.js +68 -0
- package/dist/src/paths.d.ts +0 -14
- package/dist/src/paths.js +0 -39
- package/dist/src/persistence/config.d.ts +0 -6
- package/dist/src/persistence/config.js +0 -14
- package/dist/src/persistence/guards.d.ts +0 -6
- package/dist/src/persistence/guards.js +0 -114
- package/dist/src/persistence/index.d.ts +4 -6
- package/dist/src/persistence/index.js +2 -5
- package/dist/src/persistence/workspace-metadata.d.ts +7 -0
- package/dist/src/persistence/workspace-metadata.js +39 -0
- package/dist/src/registry/index.d.ts +1 -1
- package/dist/src/registry/index.js +1 -1
- package/dist/src/registry/npm-registry.d.ts +0 -14
- package/dist/src/registry/npm-registry.js +1 -132
- package/package.json +2 -6
- package/proflow.module.json +2 -3
- package/CHANGELOG.md +0 -79
- package/dist/src/apply/apply.d.ts +0 -26
- package/dist/src/apply/apply.js +0 -314
- package/dist/src/apply/cleanup.d.ts +0 -12
- package/dist/src/apply/cleanup.js +0 -37
- package/dist/src/apply/current.d.ts +0 -5
- package/dist/src/apply/current.js +0 -95
- package/dist/src/apply/driver.d.ts +0 -28
- package/dist/src/apply/driver.js +0 -239
- package/dist/src/apply/execute.d.ts +0 -36
- package/dist/src/apply/execute.js +0 -152
- package/dist/src/apply/index.d.ts +0 -8
- package/dist/src/apply/index.js +0 -4
- package/dist/src/apply/reality.d.ts +0 -20
- package/dist/src/apply/reality.js +0 -105
- package/dist/src/binding/global-binding.d.ts +0 -69
- package/dist/src/binding/global-binding.js +0 -349
- package/dist/src/doctor/doctor.d.ts +0 -41
- package/dist/src/doctor/doctor.js +0 -80
- package/dist/src/doctor/index.d.ts +0 -2
- package/dist/src/doctor/index.js +0 -1
- package/dist/src/install/bootstrap.d.ts +0 -10
- package/dist/src/install/bootstrap.js +0 -79
- package/dist/src/install/environment.d.ts +0 -30
- package/dist/src/install/environment.js +0 -230
- package/dist/src/install/index.d.ts +0 -6
- package/dist/src/install/index.js +0 -3
- package/dist/src/install/install.d.ts +0 -19
- package/dist/src/install/install.js +0 -172
- package/dist/src/lifecycle/service-process.d.ts +0 -11
- package/dist/src/lifecycle/service-process.js +0 -356
- package/dist/src/manifest/index.d.ts +0 -2
- package/dist/src/manifest/index.js +0 -1
- package/dist/src/manifest/manifest.d.ts +0 -61
- package/dist/src/manifest/manifest.js +0 -167
- package/dist/src/observer/deployment-summary.d.ts +0 -31
- package/dist/src/observer/deployment-summary.js +0 -34
- package/dist/src/persistence/atomic.d.ts +0 -2
- package/dist/src/persistence/atomic.js +0 -16
- package/dist/src/persistence/effective-config.d.ts +0 -3
- package/dist/src/persistence/effective-config.js +0 -20
- package/dist/src/persistence/generated.d.ts +0 -2
- package/dist/src/persistence/generated.js +0 -4
- package/dist/src/persistence/plans.d.ts +0 -5
- package/dist/src/persistence/plans.js +0 -34
- package/dist/src/persistence/state.d.ts +0 -7
- package/dist/src/persistence/state.js +0 -34
- package/dist/src/persistence/verification.d.ts +0 -5
- package/dist/src/persistence/verification.js +0 -47
- package/dist/src/planner/assemble.d.ts +0 -16
- package/dist/src/planner/assemble.js +0 -107
- package/dist/src/planner/check.d.ts +0 -20
- package/dist/src/planner/check.js +0 -107
- package/dist/src/planner/fingerprint.d.ts +0 -12
- package/dist/src/planner/fingerprint.js +0 -164
- package/dist/src/planner/index.d.ts +0 -13
- package/dist/src/planner/index.js +0 -7
- package/dist/src/planner/plan.d.ts +0 -15
- package/dist/src/planner/plan.js +0 -144
- package/dist/src/planner/repair.d.ts +0 -28
- package/dist/src/planner/repair.js +0 -131
- package/dist/src/planner/stale.d.ts +0 -7
- package/dist/src/planner/stale.js +0 -23
- package/dist/src/planner/steps.d.ts +0 -37
- package/dist/src/planner/steps.js +0 -165
- package/dist/src/planner/target-catalog.d.ts +0 -13
- package/dist/src/planner/target-catalog.js +0 -48
- package/dist/src/planner/upgrade.d.ts +0 -18
- package/dist/src/planner/upgrade.js +0 -114
- package/dist/src/preflight/config.d.ts +0 -11
- package/dist/src/preflight/config.js +0 -88
- package/dist/src/preflight/findings.d.ts +0 -26
- package/dist/src/preflight/findings.js +0 -1
- package/dist/src/preflight/index.d.ts +0 -7
- package/dist/src/preflight/index.js +0 -3
- package/dist/src/preflight/preflight.d.ts +0 -10
- package/dist/src/preflight/preflight.js +0 -190
- package/dist/src/preflight/requirements.d.ts +0 -11
- package/dist/src/preflight/requirements.js +0 -231
- package/dist/src/ready/index.d.ts +0 -2
- package/dist/src/ready/index.js +0 -1
- package/dist/src/ready/ready.d.ts +0 -40
- package/dist/src/ready/ready.js +0 -201
- package/dist/src/security/index.d.ts +0 -3
- package/dist/src/security/index.js +0 -2
- package/dist/src/security/lock.d.ts +0 -9
- package/dist/src/security/lock.js +0 -127
- package/dist/src/security/redact.d.ts +0 -6
- package/dist/src/security/redact.js +0 -48
- package/dist/src/verification/index.d.ts +0 -2
- package/dist/src/verification/index.js +0 -1
- package/dist/src/verification/verify.d.ts +0 -33
- package/dist/src/verification/verify.js +0 -114
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { assessModuleCompatibility, } from "@tomflow/proflow-module-contract";
|
|
2
|
-
import { PlatformError } from "../errors.js";
|
|
3
|
-
import { buildDependencyGraph } from "../graph/graph.js";
|
|
4
|
-
import { assemblePlan, toResolvedModule } from "./assemble.js";
|
|
5
|
-
import { configStep, createSequencer, humanActionsFromModules, humanStep, lifecycleStep, packageStep, upstreamDependencies, } from "./steps.js";
|
|
6
|
-
export function assessUpgrade(current, target) {
|
|
7
|
-
const assessment = assessModuleCompatibility(current, target);
|
|
8
|
-
const migrationDeclared = target.lifecycle.supported.includes("migrate");
|
|
9
|
-
return {
|
|
10
|
-
compatible: assessment.compatible,
|
|
11
|
-
breakingChanges: assessment.breakingChanges,
|
|
12
|
-
templateVersionChanged: current.templateVersion !== target.templateVersion,
|
|
13
|
-
migrationDeclared,
|
|
14
|
-
migrationRequired: migrationDeclared && assessment.breakingChanges.length > 0,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function canonicalize(value) {
|
|
18
|
-
if (Array.isArray(value))
|
|
19
|
-
return value.map(canonicalize);
|
|
20
|
-
if (value !== null && typeof value === "object") {
|
|
21
|
-
const record = value;
|
|
22
|
-
const out = {};
|
|
23
|
-
for (const key of Object.keys(record).sort()) {
|
|
24
|
-
out[key] = canonicalize(record[key]);
|
|
25
|
-
}
|
|
26
|
-
return out;
|
|
27
|
-
}
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Deterministic descriptor equality (key-order independent). Used to detect
|
|
32
|
-
* an already-satisfied upgrade target so we never emit a pretend
|
|
33
|
-
* `package:upgrade` mutation for an unchanged module.
|
|
34
|
-
*/
|
|
35
|
-
export function descriptorsEqual(current, target) {
|
|
36
|
-
return (JSON.stringify(canonicalize(current)) ===
|
|
37
|
-
JSON.stringify(canonicalize(target)));
|
|
38
|
-
}
|
|
39
|
-
export function planUpgrade(input) {
|
|
40
|
-
const currentDescriptors = input.currentDescriptors;
|
|
41
|
-
const targetDescriptors = input.targetDescriptors;
|
|
42
|
-
if (currentDescriptors === undefined || targetDescriptors === undefined) {
|
|
43
|
-
throw new PlatformError("INVALID_REQUEST", "upgrade requires currentDescriptors and targetDescriptors");
|
|
44
|
-
}
|
|
45
|
-
const modules = targetDescriptors.map(toResolvedModule);
|
|
46
|
-
const graph = buildDependencyGraph(modules);
|
|
47
|
-
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
48
|
-
const currentByRef = new Map(currentDescriptors.map((descriptor) => [descriptor.moduleRef, descriptor]));
|
|
49
|
-
const targetByRef = new Map(targetDescriptors.map((descriptor) => [descriptor.moduleRef, descriptor]));
|
|
50
|
-
const seq = createSequencer();
|
|
51
|
-
const deps = upstreamDependencies(graph);
|
|
52
|
-
const steps = [];
|
|
53
|
-
for (const ref of graph.order) {
|
|
54
|
-
const module = byRef.get(ref);
|
|
55
|
-
if (module === undefined)
|
|
56
|
-
continue;
|
|
57
|
-
const current = currentByRef.get(ref);
|
|
58
|
-
const target = targetByRef.get(ref);
|
|
59
|
-
if (current !== undefined &&
|
|
60
|
-
target !== undefined &&
|
|
61
|
-
descriptorsEqual(current, target)) {
|
|
62
|
-
// already satisfied: no pretend package/config/lifecycle mutation
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
const assessment = current !== undefined && target !== undefined
|
|
66
|
-
? assessUpgrade(current, target)
|
|
67
|
-
: undefined;
|
|
68
|
-
if (assessment !== undefined &&
|
|
69
|
-
!assessment.compatible &&
|
|
70
|
-
!assessment.migrationDeclared) {
|
|
71
|
-
throw new PlatformError("UPGRADE_FAILED", `upgrade ${ref} has breaking changes but declares no migrate lifecycle: ${assessment.breakingChanges.join("; ")}`);
|
|
72
|
-
}
|
|
73
|
-
const dependencies = deps.get(ref) ?? [];
|
|
74
|
-
const configTarget = input.config?.[ref];
|
|
75
|
-
const missingRequiredConfig = module.configSlots
|
|
76
|
-
.filter((slot) => slot.required &&
|
|
77
|
-
slot.default === undefined &&
|
|
78
|
-
configTarget?.[slot.key] === undefined)
|
|
79
|
-
.map((slot) => slot.key);
|
|
80
|
-
if (missingRequiredConfig.length > 0) {
|
|
81
|
-
throw new PlatformError("UPGRADE_FAILED", `upgrade ${ref} requires configuration before package mutation: ${missingRequiredConfig.join(", ")}`);
|
|
82
|
-
}
|
|
83
|
-
steps.push(packageStep(seq, module, "upgrade", dependencies));
|
|
84
|
-
if (configTarget !== undefined) {
|
|
85
|
-
steps.push(configStep(seq, module));
|
|
86
|
-
}
|
|
87
|
-
if (assessment?.migrationRequired === true) {
|
|
88
|
-
steps.push(lifecycleStep(seq, module, "migrate"));
|
|
89
|
-
}
|
|
90
|
-
if (module.kind === "service") {
|
|
91
|
-
if (module.lifecycle.includes("restart")) {
|
|
92
|
-
steps.push(lifecycleStep(seq, module, "restart"));
|
|
93
|
-
}
|
|
94
|
-
else if (module.lifecycle.includes("start")) {
|
|
95
|
-
steps.push(lifecycleStep(seq, module, "start"));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
for (const requirement of module.requirements) {
|
|
99
|
-
if (requirement.kind === "human") {
|
|
100
|
-
steps.push(humanStep(seq, module, requirement.action));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return assemblePlan({
|
|
105
|
-
intent: "upgrade",
|
|
106
|
-
modules,
|
|
107
|
-
targets: input.targets ?? [],
|
|
108
|
-
config: input.config,
|
|
109
|
-
steps,
|
|
110
|
-
humanActions: humanActionsFromModules(modules),
|
|
111
|
-
now: input.now ?? new Date(),
|
|
112
|
-
targetDescriptors,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { ResolvedModule } from "../contracts.ts";
|
|
2
|
-
import type { PreflightFinding } from "./findings.ts";
|
|
3
|
-
export interface ResolvedModuleConfig {
|
|
4
|
-
moduleRef: string;
|
|
5
|
-
values: Record<string, string>;
|
|
6
|
-
secretRefs: string[];
|
|
7
|
-
missing: string[];
|
|
8
|
-
}
|
|
9
|
-
export declare function resolveModuleConfig(module: ResolvedModule, provided: Record<string, string> | undefined): ResolvedModuleConfig;
|
|
10
|
-
export declare function checkConfigReadiness(modules: readonly ResolvedModule[], config: Record<string, Record<string, string>> | undefined): PreflightFinding[];
|
|
11
|
-
export declare function checkConfigReality(modules: readonly ResolvedModule[], config: Record<string, Record<string, string>> | undefined): Promise<PreflightFinding[]>;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { constants } from "node:fs";
|
|
2
|
-
import { access, readFile, stat } from "node:fs/promises";
|
|
3
|
-
import { PlatformError } from "../errors.js";
|
|
4
|
-
import { isValidSecretRef } from "../security/redact.js";
|
|
5
|
-
function compareRef(a, b) {
|
|
6
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
7
|
-
}
|
|
8
|
-
export function resolveModuleConfig(module, provided) {
|
|
9
|
-
const values = {};
|
|
10
|
-
const secretRefs = [];
|
|
11
|
-
const missing = [];
|
|
12
|
-
for (const slot of module.configSlots) {
|
|
13
|
-
let value = provided?.[slot.key];
|
|
14
|
-
if (value === undefined && slot.default !== undefined) {
|
|
15
|
-
value = String(slot.default);
|
|
16
|
-
}
|
|
17
|
-
if (slot.required && value === undefined) {
|
|
18
|
-
missing.push(slot.key);
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (value === undefined)
|
|
22
|
-
continue;
|
|
23
|
-
values[slot.key] = value;
|
|
24
|
-
if (slot.type === "secretRef") {
|
|
25
|
-
if (!isValidSecretRef(value))
|
|
26
|
-
throw new PlatformError("SECRET_REF_INVALID", `secretRef config "${slot.key}" for ${module.moduleRef} must be an opaque reference identity (e.g. secret://provider/name)`);
|
|
27
|
-
secretRefs.push(slot.key);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return { moduleRef: module.moduleRef, values, secretRefs, missing };
|
|
31
|
-
}
|
|
32
|
-
export function checkConfigReadiness(modules, config) {
|
|
33
|
-
const findings = [];
|
|
34
|
-
const sorted = [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
|
|
35
|
-
for (const module of sorted) {
|
|
36
|
-
const resolved = resolveModuleConfig(module, config?.[module.moduleRef]);
|
|
37
|
-
for (const key of resolved.missing) {
|
|
38
|
-
findings.push({
|
|
39
|
-
code: "CONFIG_MISSING",
|
|
40
|
-
severity: "error",
|
|
41
|
-
moduleRef: module.moduleRef,
|
|
42
|
-
message: `missing required config "${key}" for ${module.moduleRef}`,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return findings;
|
|
47
|
-
}
|
|
48
|
-
function requiresExistingInputFile(key, sensitive) {
|
|
49
|
-
return (sensitive === true ||
|
|
50
|
-
/(?:ConfigPath|ProfilesFile|CredentialFile|TokenFile|tokenFile|credentialFile)$/.test(key));
|
|
51
|
-
}
|
|
52
|
-
function requiresJsonFile(key) {
|
|
53
|
-
return /(?:ConfigPath|ProfilesFile)$/.test(key);
|
|
54
|
-
}
|
|
55
|
-
export async function checkConfigReality(modules, config) {
|
|
56
|
-
const findings = [];
|
|
57
|
-
for (const module of [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef))) {
|
|
58
|
-
for (const slot of module.configSlots) {
|
|
59
|
-
if (slot.type !== "path")
|
|
60
|
-
continue;
|
|
61
|
-
const value = config?.[module.moduleRef]?.[slot.key] ??
|
|
62
|
-
(slot.default === undefined ? undefined : String(slot.default));
|
|
63
|
-
if (!value || !requiresExistingInputFile(slot.key, slot.sensitive))
|
|
64
|
-
continue;
|
|
65
|
-
try {
|
|
66
|
-
await access(value, constants.R_OK);
|
|
67
|
-
const info = await stat(value);
|
|
68
|
-
if (!info.isFile())
|
|
69
|
-
throw new Error("not a file");
|
|
70
|
-
const raw = await readFile(value, "utf8");
|
|
71
|
-
if (slot.sensitive === true && raw.trim().length < 32) {
|
|
72
|
-
throw new Error("credential/token file is shorter than 32 characters");
|
|
73
|
-
}
|
|
74
|
-
if (requiresJsonFile(slot.key))
|
|
75
|
-
JSON.parse(raw);
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
findings.push({
|
|
79
|
-
code: "CONFIG_INVALID",
|
|
80
|
-
severity: "error",
|
|
81
|
-
moduleRef: module.moduleRef,
|
|
82
|
-
message: `config path "${slot.key}" for ${module.moduleRef} is not runtime-ready: ${error instanceof Error ? error.message : String(error)}`,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return findings;
|
|
88
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { PlatformState } from "../contracts.ts";
|
|
2
|
-
import type { PlatformErrorCode } from "../errors.ts";
|
|
3
|
-
import type { DependencyGraph } from "../graph/graph.ts";
|
|
4
|
-
import type { RequirementProbe } from "./requirements.ts";
|
|
5
|
-
export type FindingSeverity = "error" | "warning" | "action";
|
|
6
|
-
export type PreflightFindingCode = PlatformErrorCode | "REQUIREMENT_FAIL" | "HUMAN_ACTION" | "MODULE_REF_UNRESOLVED" | "MODULE_PREFLIGHT_BLOCKED" | "MODULE_PREFLIGHT_FAILED" | "MODULE_PREFLIGHT_ACTION_REQUIRED";
|
|
7
|
-
export interface PreflightFinding {
|
|
8
|
-
code: PreflightFindingCode;
|
|
9
|
-
severity: FindingSeverity;
|
|
10
|
-
moduleRef?: string;
|
|
11
|
-
message: string;
|
|
12
|
-
}
|
|
13
|
-
export type ModulePreflightStatus = "SUCCEEDED" | "ACTION_REQUIRED" | "BLOCKED" | "FAILED" | "UNBOUND";
|
|
14
|
-
export interface ModulePreflightResult {
|
|
15
|
-
moduleRef: string;
|
|
16
|
-
status: ModulePreflightStatus;
|
|
17
|
-
message: string;
|
|
18
|
-
}
|
|
19
|
-
export interface PreflightResult {
|
|
20
|
-
ok: boolean;
|
|
21
|
-
status: PlatformState;
|
|
22
|
-
findings: PreflightFinding[];
|
|
23
|
-
requirementProbes: RequirementProbe[];
|
|
24
|
-
modulePreflight: ModulePreflightResult[];
|
|
25
|
-
dependency: DependencyGraph | undefined;
|
|
26
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export type { ResolvedModuleConfig } from "./config.ts";
|
|
2
|
-
export { checkConfigReadiness, resolveModuleConfig } from "./config.ts";
|
|
3
|
-
export type { FindingSeverity, ModulePreflightResult, ModulePreflightStatus, PreflightFinding, PreflightFindingCode, PreflightResult, } from "./findings.ts";
|
|
4
|
-
export type { PreflightOptions } from "./preflight.ts";
|
|
5
|
-
export { runPreflight } from "./preflight.ts";
|
|
6
|
-
export type { ProbeStatus, RequirementProbe } from "./requirements.ts";
|
|
7
|
-
export { probeAllRequirements, probeRequirement } from "./requirements.ts";
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ResolvedModule } from "../contracts.ts";
|
|
2
|
-
import type { ModuleCatalog } from "../modules.ts";
|
|
3
|
-
import type { WorkspacePaths } from "../paths.ts";
|
|
4
|
-
import type { PreflightResult } from "./findings.ts";
|
|
5
|
-
export interface PreflightOptions {
|
|
6
|
-
config?: Record<string, Record<string, string>>;
|
|
7
|
-
catalog?: ModuleCatalog;
|
|
8
|
-
paths?: WorkspacePaths;
|
|
9
|
-
}
|
|
10
|
-
export declare function runPreflight(modules: readonly ResolvedModule[], options?: PreflightOptions): Promise<PreflightResult>;
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { PlatformError } from "../errors.js";
|
|
2
|
-
import { buildDependencyGraph, ModuleRefUnresolvedError, } from "../graph/graph.js";
|
|
3
|
-
import { dispatchLifecycle } from "../lifecycle/index.js";
|
|
4
|
-
import { loadLatestVerification } from "../persistence/index.js";
|
|
5
|
-
import { checkConfigReadiness, checkConfigReality } from "./config.js";
|
|
6
|
-
import { probeAllRequirements } from "./requirements.js";
|
|
7
|
-
function compareRef(a, b) {
|
|
8
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
9
|
-
}
|
|
10
|
-
async function humanVerifiedModules(modules, paths) {
|
|
11
|
-
const verified = new Set();
|
|
12
|
-
if (paths === undefined)
|
|
13
|
-
return verified;
|
|
14
|
-
for (const module of modules) {
|
|
15
|
-
const latest = await loadLatestVerification(paths, module.moduleRef);
|
|
16
|
-
if (latest?.result === "PASS" &&
|
|
17
|
-
latest.moduleVersion === module.moduleVersion) {
|
|
18
|
-
verified.add(module.moduleRef);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return verified;
|
|
22
|
-
}
|
|
23
|
-
export async function runPreflight(modules, options = {}) {
|
|
24
|
-
const findings = [];
|
|
25
|
-
let dependency;
|
|
26
|
-
try {
|
|
27
|
-
dependency = buildDependencyGraph(modules, options.config === undefined ? {} : { config: options.config });
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
if (error instanceof ModuleRefUnresolvedError) {
|
|
31
|
-
findings.push({
|
|
32
|
-
code: "MODULE_REF_UNRESOLVED",
|
|
33
|
-
severity: "error",
|
|
34
|
-
moduleRef: error.from,
|
|
35
|
-
message: error.message,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
else if (error instanceof PlatformError) {
|
|
39
|
-
findings.push({
|
|
40
|
-
code: error.code,
|
|
41
|
-
severity: "error",
|
|
42
|
-
message: error.message,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
findings.push(...checkConfigReadiness(modules, options.config));
|
|
50
|
-
findings.push(...(await checkConfigReality(modules, options.config)));
|
|
51
|
-
const verifiedHumanModules = await humanVerifiedModules(modules, options.paths);
|
|
52
|
-
const requirementProbes = await probeAllRequirements(modules, verifiedHumanModules);
|
|
53
|
-
for (const probe of requirementProbes) {
|
|
54
|
-
if (probe.status === "ACTION_REQUIRED") {
|
|
55
|
-
findings.push({
|
|
56
|
-
code: "HUMAN_ACTION",
|
|
57
|
-
severity: "action",
|
|
58
|
-
moduleRef: probe.moduleRef,
|
|
59
|
-
message: probe.message,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
else if (probe.status === "FAIL") {
|
|
63
|
-
findings.push({
|
|
64
|
-
code: "REQUIREMENT_FAIL",
|
|
65
|
-
severity: "warning",
|
|
66
|
-
moduleRef: probe.moduleRef,
|
|
67
|
-
message: probe.message,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
const modulePreflight = await runModulePreflight(modules, options.catalog);
|
|
72
|
-
for (const result of modulePreflight) {
|
|
73
|
-
findings.push(...toFindings(result));
|
|
74
|
-
}
|
|
75
|
-
const status = deriveStatus(findings);
|
|
76
|
-
return {
|
|
77
|
-
ok: status === "READY",
|
|
78
|
-
status,
|
|
79
|
-
findings,
|
|
80
|
-
requirementProbes,
|
|
81
|
-
modulePreflight,
|
|
82
|
-
dependency,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
async function runModulePreflight(modules, catalog) {
|
|
86
|
-
if (catalog === undefined)
|
|
87
|
-
return [];
|
|
88
|
-
const sorted = [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
|
|
89
|
-
const results = [];
|
|
90
|
-
for (const module of sorted) {
|
|
91
|
-
if (!module.lifecycle.includes("preflight"))
|
|
92
|
-
continue;
|
|
93
|
-
results.push(await preflightOne(catalog, module));
|
|
94
|
-
}
|
|
95
|
-
return results;
|
|
96
|
-
}
|
|
97
|
-
async function preflightOne(catalog, module) {
|
|
98
|
-
try {
|
|
99
|
-
const dispatched = await dispatchLifecycle(catalog, module, "preflight");
|
|
100
|
-
const result = dispatched.result;
|
|
101
|
-
return {
|
|
102
|
-
moduleRef: module.moduleRef,
|
|
103
|
-
status: result.status,
|
|
104
|
-
message: modulePreflightMessage(module.moduleRef, result),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
return {
|
|
109
|
-
moduleRef: module.moduleRef,
|
|
110
|
-
status: "UNBOUND",
|
|
111
|
-
message: `module ${module.moduleRef} preflight unavailable: adapter is not bound to a runtime (${errorMessage(error)})`,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function modulePreflightMessage(moduleRef, result) {
|
|
116
|
-
switch (result.status) {
|
|
117
|
-
case "SUCCEEDED":
|
|
118
|
-
return `module ${moduleRef} preflight succeeded`;
|
|
119
|
-
case "ACTION_REQUIRED": {
|
|
120
|
-
const action = result.actionRequired;
|
|
121
|
-
return action !== undefined
|
|
122
|
-
? `module ${moduleRef} preflight requires action "${action.action}": ${action.description}`
|
|
123
|
-
: `module ${moduleRef} preflight requires an unspecified human action`;
|
|
124
|
-
}
|
|
125
|
-
case "BLOCKED":
|
|
126
|
-
case "FAILED": {
|
|
127
|
-
const error = result.error;
|
|
128
|
-
return error !== undefined
|
|
129
|
-
? `module ${moduleRef} preflight ${result.status.toLowerCase()}: ${error.code}: ${error.message}`
|
|
130
|
-
: `module ${moduleRef} preflight ${result.status.toLowerCase()}`;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function toFindings(result) {
|
|
135
|
-
switch (result.status) {
|
|
136
|
-
case "SUCCEEDED":
|
|
137
|
-
return [];
|
|
138
|
-
case "ACTION_REQUIRED":
|
|
139
|
-
case "UNBOUND":
|
|
140
|
-
return [
|
|
141
|
-
{
|
|
142
|
-
code: "MODULE_PREFLIGHT_ACTION_REQUIRED",
|
|
143
|
-
severity: "action",
|
|
144
|
-
moduleRef: result.moduleRef,
|
|
145
|
-
message: result.message,
|
|
146
|
-
},
|
|
147
|
-
];
|
|
148
|
-
case "BLOCKED":
|
|
149
|
-
return [
|
|
150
|
-
{
|
|
151
|
-
code: "MODULE_PREFLIGHT_BLOCKED",
|
|
152
|
-
severity: "error",
|
|
153
|
-
moduleRef: result.moduleRef,
|
|
154
|
-
message: result.message,
|
|
155
|
-
},
|
|
156
|
-
];
|
|
157
|
-
case "FAILED":
|
|
158
|
-
return [
|
|
159
|
-
{
|
|
160
|
-
code: "MODULE_PREFLIGHT_FAILED",
|
|
161
|
-
severity: "error",
|
|
162
|
-
moduleRef: result.moduleRef,
|
|
163
|
-
message: result.message,
|
|
164
|
-
},
|
|
165
|
-
];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
function errorMessage(error) {
|
|
169
|
-
return error instanceof Error ? error.message : String(error);
|
|
170
|
-
}
|
|
171
|
-
function deriveStatus(findings) {
|
|
172
|
-
let hasError = false;
|
|
173
|
-
let hasAction = false;
|
|
174
|
-
let hasWarning = false;
|
|
175
|
-
for (const finding of findings) {
|
|
176
|
-
if (finding.severity === "error")
|
|
177
|
-
hasError = true;
|
|
178
|
-
else if (finding.severity === "action")
|
|
179
|
-
hasAction = true;
|
|
180
|
-
else if (finding.severity === "warning")
|
|
181
|
-
hasWarning = true;
|
|
182
|
-
}
|
|
183
|
-
if (hasError)
|
|
184
|
-
return "NOT_READY";
|
|
185
|
-
if (hasAction)
|
|
186
|
-
return "ACTION_REQUIRED";
|
|
187
|
-
if (hasWarning)
|
|
188
|
-
return "DEGRADED";
|
|
189
|
-
return "READY";
|
|
190
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { ModuleRequirement } from "@tomflow/proflow-module-contract";
|
|
2
|
-
import type { ResolvedModule } from "../contracts.ts";
|
|
3
|
-
export type ProbeStatus = "PASS" | "FAIL" | "ACTION_REQUIRED";
|
|
4
|
-
export interface RequirementProbe {
|
|
5
|
-
moduleRef: string;
|
|
6
|
-
requirement: ModuleRequirement;
|
|
7
|
-
status: ProbeStatus;
|
|
8
|
-
message: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function probeAllRequirements(modules: readonly ResolvedModule[], humanVerifiedModuleRefs?: ReadonlySet<string>): Promise<RequirementProbe[]>;
|
|
11
|
-
export declare function probeRequirement(moduleRef: string, requirement: ModuleRequirement, modules: readonly ResolvedModule[], humanVerifiedModuleRefs?: ReadonlySet<string>): Promise<RequirementProbe>;
|