@tomflow/proflow-platform-cli 0.1.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.
Files changed (144) hide show
  1. package/README.md +37 -0
  2. package/conformance.json +1 -0
  3. package/dist/deployment/adapter.d.ts +57 -0
  4. package/dist/deployment/adapter.js +25 -0
  5. package/dist/deployment/descriptor.d.ts +39 -0
  6. package/dist/deployment/descriptor.js +41 -0
  7. package/dist/src/apply/apply.d.ts +24 -0
  8. package/dist/src/apply/apply.js +271 -0
  9. package/dist/src/apply/cleanup.d.ts +12 -0
  10. package/dist/src/apply/cleanup.js +37 -0
  11. package/dist/src/apply/current.d.ts +4 -0
  12. package/dist/src/apply/current.js +73 -0
  13. package/dist/src/apply/driver.d.ts +23 -0
  14. package/dist/src/apply/driver.js +154 -0
  15. package/dist/src/apply/execute.d.ts +36 -0
  16. package/dist/src/apply/execute.js +124 -0
  17. package/dist/src/apply/index.d.ts +8 -0
  18. package/dist/src/apply/index.js +4 -0
  19. package/dist/src/apply/reality.d.ts +20 -0
  20. package/dist/src/apply/reality.js +93 -0
  21. package/dist/src/binding/production-bindings.d.ts +45 -0
  22. package/dist/src/binding/production-bindings.js +102 -0
  23. package/dist/src/cli.d.ts +12 -0
  24. package/dist/src/cli.js +712 -0
  25. package/dist/src/contracts.d.ts +113 -0
  26. package/dist/src/contracts.js +1 -0
  27. package/dist/src/discovery/catalog.d.ts +10 -0
  28. package/dist/src/discovery/catalog.js +76 -0
  29. package/dist/src/discovery/discover.d.ts +19 -0
  30. package/dist/src/discovery/discover.js +124 -0
  31. package/dist/src/discovery/index.d.ts +5 -0
  32. package/dist/src/discovery/index.js +4 -0
  33. package/dist/src/discovery/installed.d.ts +21 -0
  34. package/dist/src/discovery/installed.js +130 -0
  35. package/dist/src/discovery/workspace.d.ts +10 -0
  36. package/dist/src/discovery/workspace.js +155 -0
  37. package/dist/src/docs/docs.d.ts +47 -0
  38. package/dist/src/docs/docs.js +129 -0
  39. package/dist/src/docs/index.d.ts +1 -0
  40. package/dist/src/docs/index.js +1 -0
  41. package/dist/src/doctor/doctor.d.ts +41 -0
  42. package/dist/src/doctor/doctor.js +80 -0
  43. package/dist/src/doctor/index.d.ts +2 -0
  44. package/dist/src/doctor/index.js +1 -0
  45. package/dist/src/errors.d.ts +6 -0
  46. package/dist/src/errors.js +35 -0
  47. package/dist/src/graph/graph.d.ts +23 -0
  48. package/dist/src/graph/graph.js +132 -0
  49. package/dist/src/graph/index.d.ts +2 -0
  50. package/dist/src/graph/index.js +1 -0
  51. package/dist/src/index.d.ts +28 -0
  52. package/dist/src/index.js +18 -0
  53. package/dist/src/install/bootstrap.d.ts +10 -0
  54. package/dist/src/install/bootstrap.js +79 -0
  55. package/dist/src/install/environment.d.ts +29 -0
  56. package/dist/src/install/environment.js +226 -0
  57. package/dist/src/install/index.d.ts +6 -0
  58. package/dist/src/install/index.js +3 -0
  59. package/dist/src/install/install.d.ts +19 -0
  60. package/dist/src/install/install.js +131 -0
  61. package/dist/src/install/package-manager.d.ts +11 -0
  62. package/dist/src/install/package-manager.js +58 -0
  63. package/dist/src/lifecycle/dispatch.d.ts +47 -0
  64. package/dist/src/lifecycle/dispatch.js +142 -0
  65. package/dist/src/lifecycle/index.d.ts +3 -0
  66. package/dist/src/lifecycle/index.js +2 -0
  67. package/dist/src/lifecycle/service-process.d.ts +11 -0
  68. package/dist/src/lifecycle/service-process.js +326 -0
  69. package/dist/src/manifest/index.d.ts +2 -0
  70. package/dist/src/manifest/index.js +1 -0
  71. package/dist/src/manifest/manifest.d.ts +61 -0
  72. package/dist/src/manifest/manifest.js +160 -0
  73. package/dist/src/modules.d.ts +11 -0
  74. package/dist/src/modules.js +55 -0
  75. package/dist/src/observer/deployment-summary.d.ts +31 -0
  76. package/dist/src/observer/deployment-summary.js +34 -0
  77. package/dist/src/paths.d.ts +21 -0
  78. package/dist/src/paths.js +56 -0
  79. package/dist/src/persistence/atomic.d.ts +2 -0
  80. package/dist/src/persistence/atomic.js +16 -0
  81. package/dist/src/persistence/config.d.ts +12 -0
  82. package/dist/src/persistence/config.js +43 -0
  83. package/dist/src/persistence/generated.d.ts +2 -0
  84. package/dist/src/persistence/generated.js +4 -0
  85. package/dist/src/persistence/guards.d.ts +7 -0
  86. package/dist/src/persistence/guards.js +120 -0
  87. package/dist/src/persistence/index.d.ts +6 -0
  88. package/dist/src/persistence/index.js +5 -0
  89. package/dist/src/persistence/plans.d.ts +5 -0
  90. package/dist/src/persistence/plans.js +34 -0
  91. package/dist/src/persistence/state.d.ts +7 -0
  92. package/dist/src/persistence/state.js +34 -0
  93. package/dist/src/persistence/verification.d.ts +5 -0
  94. package/dist/src/persistence/verification.js +47 -0
  95. package/dist/src/planner/assemble.d.ts +16 -0
  96. package/dist/src/planner/assemble.js +107 -0
  97. package/dist/src/planner/check.d.ts +20 -0
  98. package/dist/src/planner/check.js +96 -0
  99. package/dist/src/planner/fingerprint.d.ts +12 -0
  100. package/dist/src/planner/fingerprint.js +164 -0
  101. package/dist/src/planner/index.d.ts +13 -0
  102. package/dist/src/planner/index.js +7 -0
  103. package/dist/src/planner/plan.d.ts +14 -0
  104. package/dist/src/planner/plan.js +156 -0
  105. package/dist/src/planner/repair.d.ts +28 -0
  106. package/dist/src/planner/repair.js +131 -0
  107. package/dist/src/planner/stale.d.ts +7 -0
  108. package/dist/src/planner/stale.js +23 -0
  109. package/dist/src/planner/steps.d.ts +37 -0
  110. package/dist/src/planner/steps.js +165 -0
  111. package/dist/src/planner/target-catalog.d.ts +13 -0
  112. package/dist/src/planner/target-catalog.js +48 -0
  113. package/dist/src/planner/upgrade.d.ts +18 -0
  114. package/dist/src/planner/upgrade.js +105 -0
  115. package/dist/src/preflight/config.d.ts +10 -0
  116. package/dist/src/preflight/config.js +45 -0
  117. package/dist/src/preflight/findings.d.ts +26 -0
  118. package/dist/src/preflight/findings.js +1 -0
  119. package/dist/src/preflight/index.d.ts +7 -0
  120. package/dist/src/preflight/index.js +3 -0
  121. package/dist/src/preflight/preflight.d.ts +8 -0
  122. package/dist/src/preflight/preflight.js +174 -0
  123. package/dist/src/preflight/requirements.d.ts +11 -0
  124. package/dist/src/preflight/requirements.js +224 -0
  125. package/dist/src/ready/index.d.ts +2 -0
  126. package/dist/src/ready/index.js +1 -0
  127. package/dist/src/ready/ready.d.ts +40 -0
  128. package/dist/src/ready/ready.js +201 -0
  129. package/dist/src/registry/index.d.ts +2 -0
  130. package/dist/src/registry/index.js +1 -0
  131. package/dist/src/registry/npm-registry.d.ts +51 -0
  132. package/dist/src/registry/npm-registry.js +341 -0
  133. package/dist/src/security/index.d.ts +3 -0
  134. package/dist/src/security/index.js +2 -0
  135. package/dist/src/security/lock.d.ts +9 -0
  136. package/dist/src/security/lock.js +89 -0
  137. package/dist/src/security/redact.d.ts +6 -0
  138. package/dist/src/security/redact.js +48 -0
  139. package/dist/src/verification/index.d.ts +2 -0
  140. package/dist/src/verification/index.js +1 -0
  141. package/dist/src/verification/verify.d.ts +33 -0
  142. package/dist/src/verification/verify.js +114 -0
  143. package/package.json +49 -0
  144. package/proflow.module.json +45 -0
@@ -0,0 +1,11 @@
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[]): Promise<RequirementProbe[]>;
11
+ export declare function probeRequirement(moduleRef: string, requirement: ModuleRequirement, modules: readonly ResolvedModule[]): Promise<RequirementProbe>;
@@ -0,0 +1,224 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import { connect } from "node:net";
3
+ import { join } from "node:path";
4
+ import { versionSatisfies } from "../modules.js";
5
+ function compareRef(a, b) {
6
+ return a < b ? -1 : a > b ? 1 : 0;
7
+ }
8
+ export async function probeAllRequirements(modules) {
9
+ const sorted = [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
10
+ const results = [];
11
+ for (const module of sorted) {
12
+ for (const requirement of module.requirements) {
13
+ results.push(await probeRequirement(module.moduleRef, requirement, modules));
14
+ }
15
+ }
16
+ return results;
17
+ }
18
+ export async function probeRequirement(moduleRef, requirement, modules) {
19
+ switch (requirement.kind) {
20
+ case "runtime":
21
+ return probeRuntime(moduleRef, requirement);
22
+ case "executable":
23
+ return probeExecutable(moduleRef, requirement);
24
+ case "filesystem":
25
+ return probeFilesystem(moduleRef, requirement);
26
+ case "port":
27
+ return probePort(moduleRef, requirement);
28
+ case "network":
29
+ return probeNetwork(moduleRef, requirement);
30
+ case "module-contract":
31
+ return probeModuleContract(moduleRef, requirement, modules);
32
+ case "human":
33
+ return {
34
+ moduleRef,
35
+ requirement,
36
+ status: "ACTION_REQUIRED",
37
+ message: requirement.action,
38
+ };
39
+ }
40
+ }
41
+ function probeRuntime(moduleRef, requirement) {
42
+ if (requirement.runtime !== "node") {
43
+ return {
44
+ moduleRef,
45
+ requirement,
46
+ status: "PASS",
47
+ message: `${requirement.runtime} runtime availability is not probed by platform-cli`,
48
+ };
49
+ }
50
+ const version = process.version.startsWith("v")
51
+ ? process.version.slice(1)
52
+ : process.version;
53
+ const satisfied = versionSatisfies(version, requirement.versionRange);
54
+ return satisfied
55
+ ? {
56
+ moduleRef,
57
+ requirement,
58
+ status: "PASS",
59
+ message: `node ${version} satisfies ${requirement.versionRange}`,
60
+ }
61
+ : {
62
+ moduleRef,
63
+ requirement,
64
+ status: "FAIL",
65
+ message: `node ${version} does not satisfy ${requirement.versionRange}`,
66
+ };
67
+ }
68
+ function probeExecutable(moduleRef, requirement) {
69
+ const found = findOnPath(requirement.command);
70
+ return found
71
+ ? {
72
+ moduleRef,
73
+ requirement,
74
+ status: "PASS",
75
+ message: `executable ${requirement.command} found on PATH`,
76
+ }
77
+ : {
78
+ moduleRef,
79
+ requirement,
80
+ status: "FAIL",
81
+ message: `executable ${requirement.command} not found on PATH`,
82
+ };
83
+ }
84
+ // Bounded by construction: a synchronous scan of the finite PATH directory
85
+ // list. It spawns no subprocess, so it cannot hang.
86
+ function findOnPath(command) {
87
+ const path = process.env.PATH ?? "";
88
+ const directories = path.split(":").filter((entry) => entry !== "");
89
+ for (const directory of directories) {
90
+ const candidate = join(directory, command);
91
+ try {
92
+ accessSync(candidate, constants.X_OK);
93
+ return true;
94
+ }
95
+ catch {
96
+ // keep searching
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+ function probeFilesystem(moduleRef, requirement) {
102
+ const mode = requirement.access === "read"
103
+ ? constants.R_OK
104
+ : requirement.access === "write"
105
+ ? constants.W_OK
106
+ : constants.R_OK | constants.W_OK;
107
+ try {
108
+ accessSync(requirement.path, mode);
109
+ return {
110
+ moduleRef,
111
+ requirement,
112
+ status: "PASS",
113
+ message: `path ${requirement.path} is ${requirement.access} accessible`,
114
+ };
115
+ }
116
+ catch {
117
+ return {
118
+ moduleRef,
119
+ requirement,
120
+ status: "FAIL",
121
+ message: `path ${requirement.path} is not ${requirement.access} accessible`,
122
+ };
123
+ }
124
+ }
125
+ async function probePort(moduleRef, requirement) {
126
+ if (requirement.protocol === "udp") {
127
+ return {
128
+ moduleRef,
129
+ requirement,
130
+ status: "PASS",
131
+ message: `udp port ${requirement.port} is not probed by platform-cli`,
132
+ };
133
+ }
134
+ return new Promise((resolve) => {
135
+ let settled = false;
136
+ const socket = connect({ host: "127.0.0.1", port: requirement.port });
137
+ const finish = (result) => {
138
+ if (settled)
139
+ return;
140
+ settled = true;
141
+ socket.destroy();
142
+ resolve(result);
143
+ };
144
+ const timer = setTimeout(() => {
145
+ finish({
146
+ moduleRef,
147
+ requirement,
148
+ status: "FAIL",
149
+ message: `port ${requirement.port} probe timed out`,
150
+ });
151
+ }, 2_000);
152
+ socket.once("connect", () => {
153
+ clearTimeout(timer);
154
+ finish({
155
+ moduleRef,
156
+ requirement,
157
+ status: "FAIL",
158
+ message: `port ${requirement.port} is already in use`,
159
+ });
160
+ });
161
+ socket.once("error", () => {
162
+ clearTimeout(timer);
163
+ finish({
164
+ moduleRef,
165
+ requirement,
166
+ status: "PASS",
167
+ message: `port ${requirement.port} is available`,
168
+ });
169
+ });
170
+ });
171
+ }
172
+ async function probeNetwork(moduleRef, requirement) {
173
+ try {
174
+ const response = await fetch(requirement.url, {
175
+ method: "GET",
176
+ signal: AbortSignal.timeout(3_000),
177
+ });
178
+ return {
179
+ moduleRef,
180
+ requirement,
181
+ status: "PASS",
182
+ message: `network ${requirement.url} reachable (HTTP ${response.status})`,
183
+ };
184
+ }
185
+ catch {
186
+ return {
187
+ moduleRef,
188
+ requirement,
189
+ status: "FAIL",
190
+ message: `network ${requirement.url} unreachable`,
191
+ };
192
+ }
193
+ }
194
+ function probeModuleContract(moduleRef, requirement, modules) {
195
+ const providers = modules.filter((module) => module.provides.some((provide) => provide.contractRef === requirement.contractRef));
196
+ const matching = providers.filter((module) => module.provides.some((provide) => provide.contractRef === requirement.contractRef &&
197
+ versionSatisfies(provide.version, requirement.versionRange)));
198
+ if (matching.length > 0) {
199
+ const refs = matching
200
+ .map((module) => module.moduleRef)
201
+ .sort(compareRef)
202
+ .join(", ");
203
+ return {
204
+ moduleRef,
205
+ requirement,
206
+ status: "PASS",
207
+ message: `module-contract ${requirement.contractRef} satisfied by ${refs}`,
208
+ };
209
+ }
210
+ if (providers.length > 0) {
211
+ return {
212
+ moduleRef,
213
+ requirement,
214
+ status: "FAIL",
215
+ message: `module-contract ${requirement.contractRef} providers incompatible with ${requirement.versionRange}`,
216
+ };
217
+ }
218
+ return {
219
+ moduleRef,
220
+ requirement,
221
+ status: "FAIL",
222
+ message: `module-contract ${requirement.contractRef} has no provider`,
223
+ };
224
+ }
@@ -0,0 +1,2 @@
1
+ export type { BlockingAction, PlatformReadyInput, PlatformReadyResult, ReadyFinding, ReadyFindingCode, ReadyFindingSeverity, ResourceReality, } from "./ready.ts";
2
+ export { assessPlatformReady } from "./ready.ts";
@@ -0,0 +1 @@
1
+ export { assessPlatformReady } from "./ready.js";
@@ -0,0 +1,40 @@
1
+ import type { PlatformState, ResolvedModule, VerificationRecord } from "../contracts.ts";
2
+ import type { LifecycleRunResult } from "../lifecycle/index.ts";
3
+ export type ReadyFindingSeverity = "blocking" | "degraded";
4
+ export type ReadyFindingCode = "DEPENDENCY_UNRESOLVED" | "DEPENDENCY_INCOMPATIBLE" | "DEPENDENCY_CYCLE" | "MODULE_REF_UNRESOLVED" | "CONFIG_MISSING" | "RUNTIME_STATUS_UNAVAILABLE" | "RUNTIME_NOT_READY" | "RUNTIME_ACTION_REQUIRED" | "RUNTIME_CHECK_FAIL" | "RUNTIME_CHECK_WARN" | "VERIFICATION_MISSING" | "VERIFICATION_STALE" | "VERIFICATION_FAIL" | "BLOCKING_ACTION";
5
+ export interface ReadyFinding {
6
+ severity: ReadyFindingSeverity;
7
+ code: ReadyFindingCode;
8
+ moduleRef?: string;
9
+ message: string;
10
+ }
11
+ export interface BlockingAction {
12
+ moduleRef?: string;
13
+ action: string;
14
+ description?: string;
15
+ }
16
+ export interface ResourceReality {
17
+ moduleRef: string;
18
+ resourceIdentity?: string;
19
+ resourceVersion?: string;
20
+ }
21
+ export interface PlatformReadyInput {
22
+ modules: readonly ResolvedModule[];
23
+ status: readonly LifecycleRunResult[];
24
+ verification: readonly VerificationRecord[];
25
+ config?: Record<string, Record<string, string>>;
26
+ blockingActions?: readonly BlockingAction[];
27
+ resources?: readonly ResourceReality[];
28
+ }
29
+ export interface PlatformReadyResult {
30
+ state: PlatformState;
31
+ findings: ReadyFinding[];
32
+ }
33
+ /**
34
+ * Aggregates current reality into a single PlatformState. READY is derived only
35
+ * from live runtime status, current-version verification records, resolved
36
+ * dependencies, required config, and the absence of blocking human actions.
37
+ * Persisted PASS history, a bare SUCCEEDED operation, or a schema-valid but
38
+ * non-success structured result can never fabricate READY on their own.
39
+ */
40
+ export declare function assessPlatformReady(input: PlatformReadyInput): PlatformReadyResult;
@@ -0,0 +1,201 @@
1
+ import { PlatformError } from "../errors.js";
2
+ import { buildDependencyGraph, ModuleRefUnresolvedError, } from "../graph/index.js";
3
+ import { checkConfigReadiness } from "../preflight/index.js";
4
+ function compareRef(a, b) {
5
+ return a < b ? -1 : a > b ? 1 : 0;
6
+ }
7
+ function toDependencyCode(code) {
8
+ switch (code) {
9
+ case "DEPENDENCY_UNRESOLVED":
10
+ case "DEPENDENCY_INCOMPATIBLE":
11
+ case "DEPENDENCY_CYCLE":
12
+ return code;
13
+ default:
14
+ return undefined;
15
+ }
16
+ }
17
+ function isHumanAction(code) {
18
+ return code === "RUNTIME_ACTION_REQUIRED" || code === "BLOCKING_ACTION";
19
+ }
20
+ function deriveState(findings) {
21
+ let hardFailure = false;
22
+ let humanAction = false;
23
+ let degraded = false;
24
+ for (const finding of findings) {
25
+ if (finding.severity === "degraded") {
26
+ degraded = true;
27
+ continue;
28
+ }
29
+ if (isHumanAction(finding.code))
30
+ humanAction = true;
31
+ else
32
+ hardFailure = true;
33
+ }
34
+ if (hardFailure)
35
+ return "NOT_READY";
36
+ if (humanAction)
37
+ return "ACTION_REQUIRED";
38
+ if (degraded)
39
+ return "DEGRADED";
40
+ return "READY";
41
+ }
42
+ /**
43
+ * Aggregates current reality into a single PlatformState. READY is derived only
44
+ * from live runtime status, current-version verification records, resolved
45
+ * dependencies, required config, and the absence of blocking human actions.
46
+ * Persisted PASS history, a bare SUCCEEDED operation, or a schema-valid but
47
+ * non-success structured result can never fabricate READY on their own.
48
+ */
49
+ export function assessPlatformReady(input) {
50
+ const findings = [];
51
+ const modules = [...input.modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
52
+ // logical dependencies, cross-module compatibility, and moduleRef bindings
53
+ try {
54
+ buildDependencyGraph(modules, input.config === undefined ? {} : { config: input.config });
55
+ }
56
+ catch (error) {
57
+ if (error instanceof ModuleRefUnresolvedError) {
58
+ findings.push({
59
+ severity: "blocking",
60
+ code: "MODULE_REF_UNRESOLVED",
61
+ moduleRef: error.from,
62
+ message: error.message,
63
+ });
64
+ }
65
+ else if (error instanceof PlatformError) {
66
+ const code = toDependencyCode(error.code);
67
+ if (code === undefined)
68
+ throw error;
69
+ findings.push({ severity: "blocking", code, message: error.message });
70
+ }
71
+ else {
72
+ throw error;
73
+ }
74
+ }
75
+ // required config ready
76
+ for (const finding of checkConfigReadiness(modules, input.config)) {
77
+ findings.push({
78
+ severity: "blocking",
79
+ code: "CONFIG_MISSING",
80
+ ...(finding.moduleRef !== undefined
81
+ ? { moduleRef: finding.moduleRef }
82
+ : {}),
83
+ message: finding.message,
84
+ });
85
+ }
86
+ // required runtimes current READY (live status, never persisted)
87
+ const statusByRef = new Map(input.status.map((run) => [run.moduleRef, run]));
88
+ for (const module of modules) {
89
+ if (!module.lifecycle.includes("status"))
90
+ continue;
91
+ const run = statusByRef.get(module.moduleRef);
92
+ if (run === undefined ||
93
+ run.status !== "EXECUTED" ||
94
+ run.result === undefined) {
95
+ findings.push({
96
+ severity: "blocking",
97
+ code: "RUNTIME_STATUS_UNAVAILABLE",
98
+ moduleRef: module.moduleRef,
99
+ message: `no live status observed for ${module.moduleRef}`,
100
+ });
101
+ continue;
102
+ }
103
+ const result = run.result;
104
+ if (result.status === "ACTION_REQUIRED") {
105
+ const action = result.actionRequired;
106
+ findings.push({
107
+ severity: "blocking",
108
+ code: "RUNTIME_ACTION_REQUIRED",
109
+ moduleRef: module.moduleRef,
110
+ message: action !== undefined
111
+ ? `${module.moduleRef} requires action "${action.action}"`
112
+ : `${module.moduleRef} requires an unspecified human action`,
113
+ });
114
+ }
115
+ else if (result.status !== "SUCCEEDED") {
116
+ findings.push({
117
+ severity: "blocking",
118
+ code: "RUNTIME_NOT_READY",
119
+ moduleRef: module.moduleRef,
120
+ message: `runtime ${module.moduleRef} is ${result.status.toLowerCase()}`,
121
+ });
122
+ }
123
+ for (const runtimeCheck of result.checks ?? []) {
124
+ if (runtimeCheck.status === "FAIL") {
125
+ findings.push({
126
+ severity: "blocking",
127
+ code: "RUNTIME_CHECK_FAIL",
128
+ moduleRef: module.moduleRef,
129
+ message: `runtime check ${runtimeCheck.id} failed for ${module.moduleRef}`,
130
+ });
131
+ }
132
+ else if (runtimeCheck.status === "WARN") {
133
+ findings.push({
134
+ severity: "degraded",
135
+ code: "RUNTIME_CHECK_WARN",
136
+ moduleRef: module.moduleRef,
137
+ message: `runtime check ${runtimeCheck.id} warns for ${module.moduleRef}`,
138
+ });
139
+ }
140
+ }
141
+ }
142
+ // current-version verification PASS against current resource reality
143
+ const recordsByRef = new Map();
144
+ for (const record of input.verification) {
145
+ const list = recordsByRef.get(record.moduleRef) ?? [];
146
+ list.push(record);
147
+ recordsByRef.set(record.moduleRef, list);
148
+ }
149
+ const resourcesByRef = new Map((input.resources ?? []).map((resource) => [resource.moduleRef, resource]));
150
+ for (const module of modules) {
151
+ if (!module.lifecycle.includes("verify"))
152
+ continue;
153
+ const records = recordsByRef.get(module.moduleRef) ?? [];
154
+ const reality = resourcesByRef.get(module.moduleRef);
155
+ const current = records.filter((record) => {
156
+ if (record.moduleVersion !== module.moduleVersion)
157
+ return false;
158
+ if (reality === undefined)
159
+ return true;
160
+ if (reality.resourceIdentity !== undefined &&
161
+ record.resourceIdentity !== reality.resourceIdentity) {
162
+ return false;
163
+ }
164
+ if (reality.resourceVersion !== undefined &&
165
+ record.resourceVersion !== reality.resourceVersion) {
166
+ return false;
167
+ }
168
+ return true;
169
+ });
170
+ const latestCurrent = current[current.length - 1];
171
+ if (latestCurrent === undefined) {
172
+ const code = records.length === 0 ? "VERIFICATION_MISSING" : "VERIFICATION_STALE";
173
+ findings.push({
174
+ severity: "blocking",
175
+ code,
176
+ moduleRef: module.moduleRef,
177
+ message: `no current-version verification record for ${module.moduleRef}@${module.moduleVersion}`,
178
+ });
179
+ }
180
+ else if (latestCurrent.result !== "PASS") {
181
+ findings.push({
182
+ severity: "blocking",
183
+ code: "VERIFICATION_FAIL",
184
+ moduleRef: module.moduleRef,
185
+ message: `current-version verification FAIL for ${module.moduleRef}@${module.moduleVersion}`,
186
+ });
187
+ }
188
+ }
189
+ // blocking ACTION_REQUIRED count must be zero
190
+ for (const action of input.blockingActions ?? []) {
191
+ findings.push({
192
+ severity: "blocking",
193
+ code: "BLOCKING_ACTION",
194
+ ...(action.moduleRef !== undefined
195
+ ? { moduleRef: action.moduleRef }
196
+ : {}),
197
+ message: action.description ?? action.action,
198
+ });
199
+ }
200
+ return { state: deriveState(findings), findings };
201
+ }
@@ -0,0 +1,2 @@
1
+ export type { NpmCommandResult, NpmCommandRunner, RegistryDiscoveryResult, RegistryModuleCandidate, RegistryRejectedPackage, } from "./npm-registry.ts";
2
+ export { discoverRegistryInstallClosure, discoverRegistryModules, loadRegistryModuleDescriptor, PRO_FLOW_PACKAGE_PREFIX, PRO_FLOW_SCOPE, resolveScopeRegistry, systemNpmRunner, } from "./npm-registry.ts";
@@ -0,0 +1 @@
1
+ export { discoverRegistryInstallClosure, discoverRegistryModules, loadRegistryModuleDescriptor, PRO_FLOW_PACKAGE_PREFIX, PRO_FLOW_SCOPE, resolveScopeRegistry, systemNpmRunner, } from "./npm-registry.js";
@@ -0,0 +1,51 @@
1
+ import { type ModuleDescriptor, type ModuleInstallClass } from "@tomflow/proflow-module-contract";
2
+ export declare const PRO_FLOW_SCOPE = "@tomflow";
3
+ export declare const PRO_FLOW_PACKAGE_PREFIX = "@tomflow/proflow-";
4
+ export interface NpmCommandResult {
5
+ stdout: string;
6
+ stderr: string;
7
+ }
8
+ export interface NpmCommandRunner {
9
+ run(args: readonly string[], cwd: string): Promise<NpmCommandResult>;
10
+ }
11
+ export interface RegistryModuleCandidate {
12
+ packageName: string;
13
+ moduleVersion: string;
14
+ description?: string;
15
+ installClass: ModuleInstallClass;
16
+ descriptor: string;
17
+ manifest: "./proflow.module.json";
18
+ installRequires: string[];
19
+ nodeVersionRange?: string;
20
+ registry: string;
21
+ }
22
+ export interface RegistryRejectedPackage {
23
+ packageName: string;
24
+ moduleVersion?: string;
25
+ reason: "NOT_PROFLOW_MODULE" | "DEPRECATED" | "NODE_INCOMPATIBLE" | "METADATA_INVALID";
26
+ message: string;
27
+ }
28
+ export interface RegistryDiscoveryResult {
29
+ registry: string;
30
+ candidates: RegistryModuleCandidate[];
31
+ rejected: RegistryRejectedPackage[];
32
+ }
33
+ export declare function systemNpmRunner(): NpmCommandRunner;
34
+ export declare function resolveScopeRegistry(workspaceRoot: string, runner?: NpmCommandRunner): Promise<string>;
35
+ export declare function discoverRegistryModules(options: {
36
+ workspaceRoot: string;
37
+ packageName?: string;
38
+ runner?: NpmCommandRunner;
39
+ nodeVersion?: string;
40
+ }): Promise<RegistryDiscoveryResult>;
41
+ export declare function loadRegistryModuleDescriptor(options: {
42
+ workspaceRoot: string;
43
+ candidate: RegistryModuleCandidate;
44
+ runner?: NpmCommandRunner;
45
+ }): Promise<ModuleDescriptor>;
46
+ export declare function discoverRegistryInstallClosure(options: {
47
+ workspaceRoot: string;
48
+ packageName: string;
49
+ runner?: NpmCommandRunner;
50
+ nodeVersion?: string;
51
+ }): Promise<RegistryDiscoveryResult>;