@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,37 @@
1
+ import type { HumanAction } from "@tomflow/proflow-module-contract";
2
+ import type { DeploymentStep, ResolvedModule } from "../contracts.ts";
3
+ import type { DependencyGraph } from "../graph/graph.ts";
4
+ export declare const CheckStrategy: {
5
+ readonly packageInstalled: "package:installed";
6
+ readonly packageAbsent: "package:absent";
7
+ readonly configMaterialized: "config:materialized";
8
+ readonly lifecycleRunning: "lifecycle:running";
9
+ readonly lifecycleStopped: "lifecycle:stopped";
10
+ readonly externalResourceConfigured: "external-resource:configured";
11
+ readonly humanVerified: "human:verified";
12
+ readonly verifyChecksPass: "verify:checks-pass";
13
+ readonly migrateComplete: "migrate:complete";
14
+ };
15
+ export declare const ExecuteStrategy: {
16
+ readonly packageInstall: "package:install";
17
+ readonly packageUpgrade: "package:upgrade";
18
+ readonly packageRemove: "package:remove";
19
+ readonly configWrite: "config:write";
20
+ readonly lifecycleStart: "lifecycle:start";
21
+ readonly lifecycleStop: "lifecycle:stop";
22
+ readonly lifecycleRestart: "lifecycle:restart";
23
+ readonly lifecycleUninstall: "lifecycle:uninstall";
24
+ readonly lifecycleMigrate: "lifecycle:migrate";
25
+ readonly externalResourceConfigure: "external-resource:configure";
26
+ };
27
+ export interface StepSequencer {
28
+ next(kind: string, moduleRef: string): string;
29
+ }
30
+ export declare function createSequencer(): StepSequencer;
31
+ export declare function upstreamDependencies(graph: DependencyGraph): Map<string, string[]>;
32
+ export declare function packageStep(seq: StepSequencer, module: ResolvedModule, verb: "install" | "upgrade" | "remove", dependencies: readonly string[]): DeploymentStep;
33
+ export declare function configStep(seq: StepSequencer, module: ResolvedModule): DeploymentStep;
34
+ export declare function externalResourceStep(seq: StepSequencer, module: ResolvedModule): DeploymentStep | undefined;
35
+ export declare function humanStep(seq: StepSequencer, module: ResolvedModule, action: string): DeploymentStep;
36
+ export declare function lifecycleStep(seq: StepSequencer, module: ResolvedModule, action: "start" | "stop" | "restart" | "migrate" | "uninstall"): DeploymentStep;
37
+ export declare function humanActionsFromModules(modules: readonly ResolvedModule[]): HumanAction[];
@@ -0,0 +1,165 @@
1
+ export const CheckStrategy = {
2
+ packageInstalled: "package:installed",
3
+ packageAbsent: "package:absent",
4
+ configMaterialized: "config:materialized",
5
+ lifecycleRunning: "lifecycle:running",
6
+ lifecycleStopped: "lifecycle:stopped",
7
+ externalResourceConfigured: "external-resource:configured",
8
+ humanVerified: "human:verified",
9
+ verifyChecksPass: "verify:checks-pass",
10
+ migrateComplete: "migrate:complete",
11
+ };
12
+ export const ExecuteStrategy = {
13
+ packageInstall: "package:install",
14
+ packageUpgrade: "package:upgrade",
15
+ packageRemove: "package:remove",
16
+ configWrite: "config:write",
17
+ lifecycleStart: "lifecycle:start",
18
+ lifecycleStop: "lifecycle:stop",
19
+ lifecycleRestart: "lifecycle:restart",
20
+ lifecycleUninstall: "lifecycle:uninstall",
21
+ lifecycleMigrate: "lifecycle:migrate",
22
+ externalResourceConfigure: "external-resource:configure",
23
+ };
24
+ export function createSequencer() {
25
+ let counter = 0;
26
+ return {
27
+ next(kind, moduleRef) {
28
+ counter += 1;
29
+ return `step-${String(counter).padStart(3, "0")}-${kind}-${moduleRef}`;
30
+ },
31
+ };
32
+ }
33
+ function compareRef(a, b) {
34
+ return a < b ? -1 : a > b ? 1 : 0;
35
+ }
36
+ export function upstreamDependencies(graph) {
37
+ const map = new Map();
38
+ for (const edge of graph.edges) {
39
+ const list = map.get(edge.from) ?? [];
40
+ list.push(edge.to);
41
+ map.set(edge.from, list);
42
+ }
43
+ for (const list of map.values())
44
+ list.sort(compareRef);
45
+ return map;
46
+ }
47
+ export function packageStep(seq, module, verb, dependencies) {
48
+ const removing = verb === "remove";
49
+ return {
50
+ stepRef: seq.next("package", module.moduleRef),
51
+ moduleRef: module.moduleRef,
52
+ kind: "package",
53
+ preconditions: removing
54
+ ? []
55
+ : dependencies.map((dependency) => `dependency ${dependency} installed and verified`),
56
+ expectedEffect: removing
57
+ ? `module package ${module.packageName}@${module.moduleVersion} removed`
58
+ : `module package ${module.packageName}@${module.moduleVersion} ${verb === "install" ? "installed" : "upgraded"}`,
59
+ checkStrategy: removing
60
+ ? CheckStrategy.packageAbsent
61
+ : CheckStrategy.packageInstalled,
62
+ executeStrategy: removing
63
+ ? ExecuteStrategy.packageRemove
64
+ : verb === "install"
65
+ ? ExecuteStrategy.packageInstall
66
+ : ExecuteStrategy.packageUpgrade,
67
+ postcondition: removing
68
+ ? `package ${module.packageName} is absent from the Workspace installation`
69
+ : `installed version ${module.moduleVersion} satisfies ${module.moduleRef}`,
70
+ };
71
+ }
72
+ export function configStep(seq, module) {
73
+ return {
74
+ stepRef: seq.next("config", module.moduleRef),
75
+ moduleRef: module.moduleRef,
76
+ kind: "config",
77
+ preconditions: [`module package ${module.packageName} installed`],
78
+ expectedEffect: `configuration materialized for ${module.moduleRef}`,
79
+ checkStrategy: CheckStrategy.configMaterialized,
80
+ executeStrategy: ExecuteStrategy.configWrite,
81
+ postcondition: `all required config slots materialized for ${module.moduleRef}`,
82
+ };
83
+ }
84
+ export function externalResourceStep(seq, module) {
85
+ if (!module.lifecycle.includes("start")) {
86
+ // Status/verify-only external resources (e.g. chatgpt-carrier,
87
+ // chrome-runtime, model-provider-api) have no activation primitive.
88
+ // Their configuration is satisfied by config + human prerequisite +
89
+ // verification steps, never by a forged `start` mutation.
90
+ return undefined;
91
+ }
92
+ return {
93
+ stepRef: seq.next("external-resource", module.moduleRef),
94
+ moduleRef: module.moduleRef,
95
+ kind: "external-resource",
96
+ preconditions: [
97
+ `module package ${module.packageName} installed`,
98
+ `configuration materialized for ${module.moduleRef}`,
99
+ ],
100
+ expectedEffect: `external resource ${module.moduleRef} configured`,
101
+ checkStrategy: CheckStrategy.externalResourceConfigured,
102
+ executeStrategy: ExecuteStrategy.externalResourceConfigure,
103
+ postcondition: `external resource ${module.moduleRef} configured and reachable`,
104
+ };
105
+ }
106
+ export function humanStep(seq, module, action) {
107
+ return {
108
+ stepRef: seq.next("human", module.moduleRef),
109
+ moduleRef: module.moduleRef,
110
+ kind: "human",
111
+ preconditions: [],
112
+ expectedEffect: `human action completed: ${action}`,
113
+ checkStrategy: CheckStrategy.humanVerified,
114
+ postcondition: `human action "${action}" verified`,
115
+ };
116
+ }
117
+ export function lifecycleStep(seq, module, action) {
118
+ const executeStrategy = action === "start"
119
+ ? ExecuteStrategy.lifecycleStart
120
+ : action === "stop"
121
+ ? ExecuteStrategy.lifecycleStop
122
+ : action === "restart"
123
+ ? ExecuteStrategy.lifecycleRestart
124
+ : action === "uninstall"
125
+ ? ExecuteStrategy.lifecycleUninstall
126
+ : ExecuteStrategy.lifecycleMigrate;
127
+ const checkStrategy = action === "migrate"
128
+ ? CheckStrategy.migrateComplete
129
+ : action === "stop" || action === "uninstall"
130
+ ? CheckStrategy.lifecycleStopped
131
+ : CheckStrategy.lifecycleRunning;
132
+ const settled = action === "start" || action === "restart"
133
+ ? "running"
134
+ : action === "stop" || action === "uninstall"
135
+ ? "stopped"
136
+ : "migrated";
137
+ return {
138
+ stepRef: seq.next("lifecycle", module.moduleRef),
139
+ moduleRef: module.moduleRef,
140
+ kind: "lifecycle",
141
+ preconditions: [],
142
+ expectedEffect: `lifecycle ${action} completed for ${module.moduleRef}`,
143
+ checkStrategy,
144
+ executeStrategy,
145
+ postcondition: `module ${module.moduleRef} is ${settled}`,
146
+ };
147
+ }
148
+ export function humanActionsFromModules(modules) {
149
+ const actions = [];
150
+ const seen = new Set();
151
+ for (const module of modules) {
152
+ for (const requirement of module.requirements) {
153
+ if (requirement.kind !== "human")
154
+ continue;
155
+ if (seen.has(requirement.action))
156
+ continue;
157
+ seen.add(requirement.action);
158
+ actions.push({
159
+ action: requirement.action,
160
+ description: `human requirement for ${module.moduleRef}`,
161
+ });
162
+ }
163
+ }
164
+ return actions;
165
+ }
@@ -0,0 +1,13 @@
1
+ import { type ModuleDescriptor } from "@tomflow/proflow-module-contract";
2
+ /**
3
+ * A local target catalog resolved from a real target workspace on disk. Every
4
+ * descriptor is loaded from that workspace's actual `deployment/descriptor`
5
+ * artifacts (workspace packages win over declared installed dependencies). It
6
+ * never performs registry/npm "world" discovery and never fabricates a
7
+ * descriptor from a bare targetVersion string.
8
+ */
9
+ export interface TargetCatalog {
10
+ root: string;
11
+ descriptors: ModuleDescriptor[];
12
+ }
13
+ export declare function resolveTargetCatalog(targetWorkspace: string): Promise<TargetCatalog>;
@@ -0,0 +1,48 @@
1
+ import { parseModuleDescriptor, } from "@tomflow/proflow-module-contract";
2
+ import { AutoModuleCatalog } from "../discovery/discover.js";
3
+ import { readPackageJson } from "../discovery/workspace.js";
4
+ import { PlatformError } from "../errors.js";
5
+ export async function resolveTargetCatalog(targetWorkspace) {
6
+ const catalog = new AutoModuleCatalog(targetWorkspace);
7
+ const sources = await catalog.sources();
8
+ const descriptors = [];
9
+ const seen = new Set();
10
+ for (const source of sources) {
11
+ const descriptor = await loadTargetDescriptor(catalog, source);
12
+ if (seen.has(descriptor.moduleRef)) {
13
+ throw new PlatformError("DUPLICATE_IDENTITY", `duplicate moduleRef "${descriptor.moduleRef}" in target workspace ${targetWorkspace}`);
14
+ }
15
+ seen.add(descriptor.moduleRef);
16
+ descriptors.push(descriptor);
17
+ }
18
+ return { root: targetWorkspace, descriptors };
19
+ }
20
+ async function loadTargetDescriptor(catalog, source) {
21
+ let raw;
22
+ try {
23
+ raw = await catalog.loadDescriptor(source);
24
+ }
25
+ catch (error) {
26
+ if (error instanceof PlatformError)
27
+ throw error;
28
+ throw new PlatformError("DESCRIPTOR_INVALID", `failed to load target descriptor for ${source.packageName}: ${errorMessage(error)}`);
29
+ }
30
+ let descriptor;
31
+ try {
32
+ descriptor = parseModuleDescriptor(raw);
33
+ }
34
+ catch (error) {
35
+ throw new PlatformError("DESCRIPTOR_INVALID", `invalid target descriptor for ${source.packageName}: ${errorMessage(error)}`);
36
+ }
37
+ if (source.type === "workspace" && source.path !== undefined) {
38
+ const packageJson = await readPackageJson(source.path);
39
+ if (packageJson.version !== undefined &&
40
+ packageJson.version !== descriptor.moduleVersion) {
41
+ throw new PlatformError("DESCRIPTOR_INVALID", `moduleVersion ${descriptor.moduleVersion} does not match package.json version ${packageJson.version} for ${descriptor.moduleRef}`);
42
+ }
43
+ }
44
+ return descriptor;
45
+ }
46
+ function errorMessage(error) {
47
+ return error instanceof Error ? error.message : String(error);
48
+ }
@@ -0,0 +1,18 @@
1
+ import { type ModuleDescriptor } from "@tomflow/proflow-module-contract";
2
+ import type { DeploymentPlan } from "../contracts.ts";
3
+ import type { PlanInput } from "./plan.ts";
4
+ export interface UpgradeAssessment {
5
+ compatible: boolean;
6
+ breakingChanges: string[];
7
+ templateVersionChanged: boolean;
8
+ migrationDeclared: boolean;
9
+ migrationRequired: boolean;
10
+ }
11
+ export declare function assessUpgrade(current: ModuleDescriptor, target: ModuleDescriptor): UpgradeAssessment;
12
+ /**
13
+ * Deterministic descriptor equality (key-order independent). Used to detect
14
+ * an already-satisfied upgrade target so we never emit a pretend
15
+ * `package:upgrade` mutation for an unchanged module.
16
+ */
17
+ export declare function descriptorsEqual(current: ModuleDescriptor, target: ModuleDescriptor): boolean;
18
+ export declare function planUpgrade(input: PlanInput): DeploymentPlan;
@@ -0,0 +1,105 @@
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
+ steps.push(packageStep(seq, module, "upgrade", dependencies));
75
+ if (module.configSlots.length > 0) {
76
+ steps.push(configStep(seq, module));
77
+ }
78
+ if (assessment?.migrationRequired === true) {
79
+ steps.push(lifecycleStep(seq, module, "migrate"));
80
+ }
81
+ if (module.kind === "service") {
82
+ if (module.lifecycle.includes("restart")) {
83
+ steps.push(lifecycleStep(seq, module, "restart"));
84
+ }
85
+ else if (module.lifecycle.includes("start")) {
86
+ steps.push(lifecycleStep(seq, module, "start"));
87
+ }
88
+ }
89
+ for (const requirement of module.requirements) {
90
+ if (requirement.kind === "human") {
91
+ steps.push(humanStep(seq, module, requirement.action));
92
+ }
93
+ }
94
+ }
95
+ return assemblePlan({
96
+ intent: "upgrade",
97
+ modules,
98
+ targets: input.targets ?? [],
99
+ config: input.config,
100
+ steps,
101
+ humanActions: humanActionsFromModules(modules),
102
+ now: input.now ?? new Date(),
103
+ targetDescriptors,
104
+ });
105
+ }
@@ -0,0 +1,10 @@
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[];
@@ -0,0 +1,45 @@
1
+ import { PlatformError } from "../errors.js";
2
+ import { isValidSecretRef } from "../security/redact.js";
3
+ function compareRef(a, b) {
4
+ return a < b ? -1 : a > b ? 1 : 0;
5
+ }
6
+ export function resolveModuleConfig(module, provided) {
7
+ const values = {};
8
+ const secretRefs = [];
9
+ const missing = [];
10
+ for (const slot of module.configSlots) {
11
+ let value = provided?.[slot.key];
12
+ if (value === undefined && slot.default !== undefined) {
13
+ value = String(slot.default);
14
+ }
15
+ if (slot.required && value === undefined) {
16
+ missing.push(slot.key);
17
+ continue;
18
+ }
19
+ if (value === undefined)
20
+ continue;
21
+ values[slot.key] = value;
22
+ if (slot.type === "secretRef") {
23
+ if (!isValidSecretRef(value))
24
+ throw new PlatformError("SECRET_REF_INVALID", `secretRef config "${slot.key}" for ${module.moduleRef} must be an opaque reference identity (e.g. secret://provider/name)`);
25
+ secretRefs.push(slot.key);
26
+ }
27
+ }
28
+ return { moduleRef: module.moduleRef, values, secretRefs, missing };
29
+ }
30
+ export function checkConfigReadiness(modules, config) {
31
+ const findings = [];
32
+ const sorted = [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
33
+ for (const module of sorted) {
34
+ const resolved = resolveModuleConfig(module, config?.[module.moduleRef]);
35
+ for (const key of resolved.missing) {
36
+ findings.push({
37
+ code: "CONFIG_MISSING",
38
+ severity: "error",
39
+ moduleRef: module.moduleRef,
40
+ message: `missing required config "${key}" for ${module.moduleRef}`,
41
+ });
42
+ }
43
+ }
44
+ return findings;
45
+ }
@@ -0,0 +1,26 @@
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
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
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";
@@ -0,0 +1,3 @@
1
+ export { checkConfigReadiness, resolveModuleConfig } from "./config.js";
2
+ export { runPreflight } from "./preflight.js";
3
+ export { probeAllRequirements, probeRequirement } from "./requirements.js";
@@ -0,0 +1,8 @@
1
+ import type { ResolvedModule } from "../contracts.ts";
2
+ import type { ModuleCatalog } from "../modules.ts";
3
+ import type { PreflightResult } from "./findings.ts";
4
+ export interface PreflightOptions {
5
+ config?: Record<string, Record<string, string>>;
6
+ catalog?: ModuleCatalog;
7
+ }
8
+ export declare function runPreflight(modules: readonly ResolvedModule[], options?: PreflightOptions): Promise<PreflightResult>;
@@ -0,0 +1,174 @@
1
+ import { PlatformError } from "../errors.js";
2
+ import { buildDependencyGraph, ModuleRefUnresolvedError, } from "../graph/graph.js";
3
+ import { dispatchLifecycle } from "../lifecycle/index.js";
4
+ import { checkConfigReadiness } from "./config.js";
5
+ import { probeAllRequirements } from "./requirements.js";
6
+ function compareRef(a, b) {
7
+ return a < b ? -1 : a > b ? 1 : 0;
8
+ }
9
+ export async function runPreflight(modules, options = {}) {
10
+ const findings = [];
11
+ let dependency;
12
+ try {
13
+ dependency = buildDependencyGraph(modules, options.config === undefined ? {} : { config: options.config });
14
+ }
15
+ catch (error) {
16
+ if (error instanceof ModuleRefUnresolvedError) {
17
+ findings.push({
18
+ code: "MODULE_REF_UNRESOLVED",
19
+ severity: "error",
20
+ moduleRef: error.from,
21
+ message: error.message,
22
+ });
23
+ }
24
+ else if (error instanceof PlatformError) {
25
+ findings.push({
26
+ code: error.code,
27
+ severity: "error",
28
+ message: error.message,
29
+ });
30
+ }
31
+ else {
32
+ throw error;
33
+ }
34
+ }
35
+ findings.push(...checkConfigReadiness(modules, options.config));
36
+ const requirementProbes = await probeAllRequirements(modules);
37
+ for (const probe of requirementProbes) {
38
+ if (probe.status === "ACTION_REQUIRED") {
39
+ findings.push({
40
+ code: "HUMAN_ACTION",
41
+ severity: "action",
42
+ moduleRef: probe.moduleRef,
43
+ message: probe.message,
44
+ });
45
+ }
46
+ else if (probe.status === "FAIL") {
47
+ findings.push({
48
+ code: "REQUIREMENT_FAIL",
49
+ severity: "warning",
50
+ moduleRef: probe.moduleRef,
51
+ message: probe.message,
52
+ });
53
+ }
54
+ }
55
+ const modulePreflight = await runModulePreflight(modules, options.catalog);
56
+ for (const result of modulePreflight) {
57
+ findings.push(...toFindings(result));
58
+ }
59
+ const status = deriveStatus(findings);
60
+ return {
61
+ ok: status === "READY",
62
+ status,
63
+ findings,
64
+ requirementProbes,
65
+ modulePreflight,
66
+ dependency,
67
+ };
68
+ }
69
+ async function runModulePreflight(modules, catalog) {
70
+ if (catalog === undefined)
71
+ return [];
72
+ const sorted = [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
73
+ const results = [];
74
+ for (const module of sorted) {
75
+ if (!module.lifecycle.includes("preflight"))
76
+ continue;
77
+ results.push(await preflightOne(catalog, module));
78
+ }
79
+ return results;
80
+ }
81
+ async function preflightOne(catalog, module) {
82
+ try {
83
+ const dispatched = await dispatchLifecycle(catalog, module, "preflight");
84
+ const result = dispatched.result;
85
+ return {
86
+ moduleRef: module.moduleRef,
87
+ status: result.status,
88
+ message: modulePreflightMessage(module.moduleRef, result),
89
+ };
90
+ }
91
+ catch (error) {
92
+ return {
93
+ moduleRef: module.moduleRef,
94
+ status: "UNBOUND",
95
+ message: `module ${module.moduleRef} preflight unavailable: adapter is not bound to a runtime (${errorMessage(error)})`,
96
+ };
97
+ }
98
+ }
99
+ function modulePreflightMessage(moduleRef, result) {
100
+ switch (result.status) {
101
+ case "SUCCEEDED":
102
+ return `module ${moduleRef} preflight succeeded`;
103
+ case "ACTION_REQUIRED": {
104
+ const action = result.actionRequired;
105
+ return action !== undefined
106
+ ? `module ${moduleRef} preflight requires action "${action.action}"`
107
+ : `module ${moduleRef} preflight requires an unspecified human action`;
108
+ }
109
+ case "BLOCKED":
110
+ case "FAILED": {
111
+ const error = result.error;
112
+ return error !== undefined
113
+ ? `module ${moduleRef} preflight ${result.status.toLowerCase()}: ${error.code}: ${error.message}`
114
+ : `module ${moduleRef} preflight ${result.status.toLowerCase()}`;
115
+ }
116
+ }
117
+ }
118
+ function toFindings(result) {
119
+ switch (result.status) {
120
+ case "SUCCEEDED":
121
+ return [];
122
+ case "ACTION_REQUIRED":
123
+ case "UNBOUND":
124
+ return [
125
+ {
126
+ code: "MODULE_PREFLIGHT_ACTION_REQUIRED",
127
+ severity: "action",
128
+ moduleRef: result.moduleRef,
129
+ message: result.message,
130
+ },
131
+ ];
132
+ case "BLOCKED":
133
+ return [
134
+ {
135
+ code: "MODULE_PREFLIGHT_BLOCKED",
136
+ severity: "error",
137
+ moduleRef: result.moduleRef,
138
+ message: result.message,
139
+ },
140
+ ];
141
+ case "FAILED":
142
+ return [
143
+ {
144
+ code: "MODULE_PREFLIGHT_FAILED",
145
+ severity: "error",
146
+ moduleRef: result.moduleRef,
147
+ message: result.message,
148
+ },
149
+ ];
150
+ }
151
+ }
152
+ function errorMessage(error) {
153
+ return error instanceof Error ? error.message : String(error);
154
+ }
155
+ function deriveStatus(findings) {
156
+ let hasError = false;
157
+ let hasAction = false;
158
+ let hasWarning = false;
159
+ for (const finding of findings) {
160
+ if (finding.severity === "error")
161
+ hasError = true;
162
+ else if (finding.severity === "action")
163
+ hasAction = true;
164
+ else if (finding.severity === "warning")
165
+ hasWarning = true;
166
+ }
167
+ if (hasError)
168
+ return "NOT_READY";
169
+ if (hasAction)
170
+ return "ACTION_REQUIRED";
171
+ if (hasWarning)
172
+ return "DEGRADED";
173
+ return "READY";
174
+ }