@tomflow/proflow-platform-cli 0.1.14 → 0.1.16

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 (142) hide show
  1. package/README.md +48 -43
  2. package/dist/deployment/adapter.d.ts +5 -5
  3. package/dist/deployment/adapter.js +2 -1
  4. package/dist/deployment/descriptor.d.ts +2 -3
  5. package/dist/deployment/descriptor.js +2 -3
  6. package/dist/src/binding/production-bindings.d.ts +17 -15
  7. package/dist/src/binding/production-bindings.js +18 -59
  8. package/dist/src/cli.d.ts +7 -9
  9. package/dist/src/cli.js +327 -1316
  10. package/dist/src/contracts.d.ts +4 -97
  11. package/dist/src/discovery/discover.js +0 -1
  12. package/dist/src/docs/aggregate.d.ts +18 -0
  13. package/dist/src/docs/aggregate.js +44 -0
  14. package/dist/src/docs/docs.d.ts +0 -31
  15. package/dist/src/docs/docs.js +0 -61
  16. package/dist/src/docs/index.d.ts +2 -1
  17. package/dist/src/docs/index.js +1 -1
  18. package/dist/src/errors.d.ts +1 -1
  19. package/dist/src/errors.js +0 -18
  20. package/dist/src/graph/graph.d.ts +1 -4
  21. package/dist/src/graph/graph.js +1 -13
  22. package/dist/src/graph/index.d.ts +2 -2
  23. package/dist/src/graph/index.js +1 -1
  24. package/dist/src/index.d.ts +4 -24
  25. package/dist/src/index.js +2 -16
  26. package/dist/src/install/package-manager.d.ts +21 -0
  27. package/dist/src/install/package-manager.js +153 -0
  28. package/dist/src/lifecycle/dispatch.d.ts +0 -31
  29. package/dist/src/lifecycle/dispatch.js +0 -125
  30. package/dist/src/lifecycle/index.d.ts +4 -3
  31. package/dist/src/lifecycle/index.js +2 -2
  32. package/dist/src/lifecycle/thin.d.ts +11 -0
  33. package/dist/src/lifecycle/thin.js +68 -0
  34. package/dist/src/paths.d.ts +0 -14
  35. package/dist/src/paths.js +0 -39
  36. package/dist/src/persistence/config.d.ts +0 -6
  37. package/dist/src/persistence/config.js +0 -14
  38. package/dist/src/persistence/guards.d.ts +0 -6
  39. package/dist/src/persistence/guards.js +0 -114
  40. package/dist/src/persistence/index.d.ts +4 -6
  41. package/dist/src/persistence/index.js +2 -5
  42. package/dist/src/persistence/workspace-metadata.d.ts +7 -0
  43. package/dist/src/persistence/workspace-metadata.js +39 -0
  44. package/dist/src/registry/index.d.ts +1 -1
  45. package/dist/src/registry/index.js +1 -1
  46. package/dist/src/registry/npm-registry.d.ts +0 -14
  47. package/dist/src/registry/npm-registry.js +1 -132
  48. package/package.json +5 -9
  49. package/proflow.module.json +2 -3
  50. package/CHANGELOG.md +0 -85
  51. package/dist/src/apply/apply.d.ts +0 -26
  52. package/dist/src/apply/apply.js +0 -314
  53. package/dist/src/apply/cleanup.d.ts +0 -12
  54. package/dist/src/apply/cleanup.js +0 -37
  55. package/dist/src/apply/current.d.ts +0 -5
  56. package/dist/src/apply/current.js +0 -95
  57. package/dist/src/apply/driver.d.ts +0 -28
  58. package/dist/src/apply/driver.js +0 -239
  59. package/dist/src/apply/execute.d.ts +0 -36
  60. package/dist/src/apply/execute.js +0 -152
  61. package/dist/src/apply/index.d.ts +0 -8
  62. package/dist/src/apply/index.js +0 -4
  63. package/dist/src/apply/reality.d.ts +0 -20
  64. package/dist/src/apply/reality.js +0 -105
  65. package/dist/src/binding/global-binding.d.ts +0 -69
  66. package/dist/src/binding/global-binding.js +0 -349
  67. package/dist/src/doctor/doctor.d.ts +0 -41
  68. package/dist/src/doctor/doctor.js +0 -80
  69. package/dist/src/doctor/index.d.ts +0 -2
  70. package/dist/src/doctor/index.js +0 -1
  71. package/dist/src/install/bootstrap.d.ts +0 -10
  72. package/dist/src/install/bootstrap.js +0 -79
  73. package/dist/src/install/environment.d.ts +0 -30
  74. package/dist/src/install/environment.js +0 -230
  75. package/dist/src/install/index.d.ts +0 -6
  76. package/dist/src/install/index.js +0 -3
  77. package/dist/src/install/install.d.ts +0 -19
  78. package/dist/src/install/install.js +0 -172
  79. package/dist/src/lifecycle/service-process.d.ts +0 -11
  80. package/dist/src/lifecycle/service-process.js +0 -356
  81. package/dist/src/manifest/index.d.ts +0 -2
  82. package/dist/src/manifest/index.js +0 -1
  83. package/dist/src/manifest/manifest.d.ts +0 -61
  84. package/dist/src/manifest/manifest.js +0 -167
  85. package/dist/src/observer/deployment-summary.d.ts +0 -31
  86. package/dist/src/observer/deployment-summary.js +0 -34
  87. package/dist/src/persistence/atomic.d.ts +0 -2
  88. package/dist/src/persistence/atomic.js +0 -16
  89. package/dist/src/persistence/effective-config.d.ts +0 -3
  90. package/dist/src/persistence/effective-config.js +0 -20
  91. package/dist/src/persistence/generated.d.ts +0 -2
  92. package/dist/src/persistence/generated.js +0 -4
  93. package/dist/src/persistence/plans.d.ts +0 -5
  94. package/dist/src/persistence/plans.js +0 -34
  95. package/dist/src/persistence/state.d.ts +0 -7
  96. package/dist/src/persistence/state.js +0 -34
  97. package/dist/src/persistence/verification.d.ts +0 -5
  98. package/dist/src/persistence/verification.js +0 -47
  99. package/dist/src/planner/assemble.d.ts +0 -16
  100. package/dist/src/planner/assemble.js +0 -107
  101. package/dist/src/planner/check.d.ts +0 -20
  102. package/dist/src/planner/check.js +0 -107
  103. package/dist/src/planner/fingerprint.d.ts +0 -12
  104. package/dist/src/planner/fingerprint.js +0 -164
  105. package/dist/src/planner/index.d.ts +0 -13
  106. package/dist/src/planner/index.js +0 -7
  107. package/dist/src/planner/plan.d.ts +0 -15
  108. package/dist/src/planner/plan.js +0 -144
  109. package/dist/src/planner/repair.d.ts +0 -28
  110. package/dist/src/planner/repair.js +0 -131
  111. package/dist/src/planner/stale.d.ts +0 -7
  112. package/dist/src/planner/stale.js +0 -23
  113. package/dist/src/planner/steps.d.ts +0 -37
  114. package/dist/src/planner/steps.js +0 -165
  115. package/dist/src/planner/target-catalog.d.ts +0 -13
  116. package/dist/src/planner/target-catalog.js +0 -48
  117. package/dist/src/planner/upgrade.d.ts +0 -18
  118. package/dist/src/planner/upgrade.js +0 -114
  119. package/dist/src/preflight/config.d.ts +0 -11
  120. package/dist/src/preflight/config.js +0 -88
  121. package/dist/src/preflight/findings.d.ts +0 -26
  122. package/dist/src/preflight/findings.js +0 -1
  123. package/dist/src/preflight/index.d.ts +0 -7
  124. package/dist/src/preflight/index.js +0 -3
  125. package/dist/src/preflight/preflight.d.ts +0 -10
  126. package/dist/src/preflight/preflight.js +0 -190
  127. package/dist/src/preflight/requirements.d.ts +0 -11
  128. package/dist/src/preflight/requirements.js +0 -231
  129. package/dist/src/ready/index.d.ts +0 -2
  130. package/dist/src/ready/index.js +0 -1
  131. package/dist/src/ready/ready.d.ts +0 -40
  132. package/dist/src/ready/ready.js +0 -201
  133. package/dist/src/security/index.d.ts +0 -3
  134. package/dist/src/security/index.js +0 -2
  135. package/dist/src/security/lock.d.ts +0 -9
  136. package/dist/src/security/lock.js +0 -127
  137. package/dist/src/security/redact.d.ts +0 -6
  138. package/dist/src/security/redact.js +0 -48
  139. package/dist/src/verification/index.d.ts +0 -2
  140. package/dist/src/verification/index.js +0 -1
  141. package/dist/src/verification/verify.d.ts +0 -33
  142. package/dist/src/verification/verify.js +0 -114
@@ -1,20 +0,0 @@
1
- import { loadConfig } from "./config.js";
2
- export async function mergeEffectiveConfig(paths, modules, provided) {
3
- const effective = {};
4
- for (const module of modules) {
5
- const stored = await loadConfig(paths, module.moduleRef);
6
- if (stored === undefined)
7
- continue;
8
- effective[module.moduleRef] = {
9
- ...stored.publicValues,
10
- ...stored.secretValues,
11
- };
12
- }
13
- for (const [moduleRef, values] of Object.entries(provided)) {
14
- effective[moduleRef] = {
15
- ...(effective[moduleRef] ?? {}),
16
- ...values,
17
- };
18
- }
19
- return effective;
20
- }
@@ -1,2 +0,0 @@
1
- import type { WorkspacePaths } from "../paths.ts";
2
- export declare function writeInstallDoc(paths: WorkspacePaths, content: string): Promise<void>;
@@ -1,4 +0,0 @@
1
- import { writeFileAtomic } from "./atomic.js";
2
- export async function writeInstallDoc(paths, content) {
3
- await writeFileAtomic(paths.installMd, content, 0o644);
4
- }
@@ -1,5 +0,0 @@
1
- import type { DeploymentPlan } from "../contracts.ts";
2
- import { type WorkspacePaths } from "../paths.ts";
3
- export declare function savePlan(paths: WorkspacePaths, plan: DeploymentPlan): Promise<void>;
4
- export declare function loadPlan(paths: WorkspacePaths, planRef: string): Promise<DeploymentPlan | undefined>;
5
- export declare function listPlans(paths: WorkspacePaths): Promise<string[]>;
@@ -1,34 +0,0 @@
1
- import { readdir } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { readJson } from "../paths.js";
4
- import { writeJsonAtomic } from "./atomic.js";
5
- import { assertSafeFileName, isDeploymentPlan } from "./guards.js";
6
- function planFilePath(paths, planRef) {
7
- assertSafeFileName(planRef, "planRef");
8
- return join(paths.plans, `${planRef}.json`);
9
- }
10
- // Plans are public DTOs: secretRef config values are opaque reference
11
- // identities (e.g. `secret://model-provider/default`), safe to persist verbatim.
12
- // No raw secret ever enters a plan, so no redaction is applied on save.
13
- export async function savePlan(paths, plan) {
14
- await writeJsonAtomic(planFilePath(paths, plan.planRef), plan);
15
- }
16
- export async function loadPlan(paths, planRef) {
17
- const value = await readJson(planFilePath(paths, planRef));
18
- if (value === undefined)
19
- return undefined;
20
- return isDeploymentPlan(value) ? value : undefined;
21
- }
22
- export async function listPlans(paths) {
23
- let entries;
24
- try {
25
- entries = await readdir(paths.plans);
26
- }
27
- catch {
28
- return [];
29
- }
30
- return entries
31
- .filter((entry) => entry.endsWith(".json"))
32
- .map((entry) => entry.slice(0, -".json".length))
33
- .sort();
34
- }
@@ -1,7 +0,0 @@
1
- import type { DeploymentState, PendingActionRecord } from "../contracts.ts";
2
- import { type WorkspacePaths } from "../paths.ts";
3
- export declare function emptyDeploymentState(): DeploymentState;
4
- export declare function clearPendingActions(state: DeploymentState, isResolved: (action: PendingActionRecord) => boolean): DeploymentState;
5
- export declare function clearCompletedPendingActions(state: DeploymentState): DeploymentState;
6
- export declare function loadDeploymentState(paths: WorkspacePaths): Promise<DeploymentState | undefined>;
7
- export declare function saveDeploymentState(paths: WorkspacePaths, state: DeploymentState): Promise<void>;
@@ -1,34 +0,0 @@
1
- import { readJson } from "../paths.js";
2
- import { writeJsonAtomic } from "./atomic.js";
3
- import { DEPLOYMENT_STATE_CONTRACT, isDeploymentState } from "./guards.js";
4
- export function emptyDeploymentState() {
5
- return {
6
- contract: DEPLOYMENT_STATE_CONTRACT,
7
- selectedModules: [],
8
- lastAppliedPlans: [],
9
- verificationIndex: [],
10
- pendingActions: [],
11
- updatedAt: new Date().toISOString(),
12
- };
13
- }
14
- export function clearPendingActions(state, isResolved) {
15
- return {
16
- ...state,
17
- pendingActions: state.pendingActions.filter((action) => !isResolved(action)),
18
- };
19
- }
20
- export function clearCompletedPendingActions(state) {
21
- const completed = new Set(state.lastAppliedPlans.map((entry) => entry.planRef));
22
- return clearPendingActions(state, (action) => completed.has(action.planRef));
23
- }
24
- // Loaded state is validated at the boundary; a missing or corrupt file resolves
25
- // to undefined so stale persisted facts can never impersonate current reality.
26
- export async function loadDeploymentState(paths) {
27
- const value = await readJson(paths.stateJson);
28
- if (value === undefined)
29
- return undefined;
30
- return isDeploymentState(value) ? value : undefined;
31
- }
32
- export async function saveDeploymentState(paths, state) {
33
- await writeJsonAtomic(paths.stateJson, state);
34
- }
@@ -1,5 +0,0 @@
1
- import type { VerificationRecord } from "../contracts.ts";
2
- import type { WorkspacePaths } from "../paths.ts";
3
- export declare function appendVerification(paths: WorkspacePaths, record: VerificationRecord): Promise<void>;
4
- export declare function loadVerificationHistory(paths: WorkspacePaths, moduleRef: string): Promise<VerificationRecord[]>;
5
- export declare function loadLatestVerification(paths: WorkspacePaths, moduleRef: string): Promise<VerificationRecord | undefined>;
@@ -1,47 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { writeFileAtomic } from "./atomic.js";
4
- import { assertSafeFileName, isVerificationRecord } from "./guards.js";
5
- function verificationFilePath(paths, moduleRef) {
6
- assertSafeFileName(moduleRef, "moduleRef");
7
- return join(paths.verification, `${moduleRef}.jsonl`);
8
- }
9
- async function readJsonlLines(file) {
10
- let raw;
11
- try {
12
- raw = await readFile(file, "utf8");
13
- }
14
- catch {
15
- return [];
16
- }
17
- const lines = [];
18
- for (const line of raw.split("\n")) {
19
- const trimmed = line.trim();
20
- if (trimmed.length === 0)
21
- continue;
22
- try {
23
- lines.push(JSON.parse(trimmed));
24
- }
25
- catch {
26
- // skip a corrupt line rather than discarding otherwise valid history
27
- }
28
- }
29
- return lines;
30
- }
31
- // Verification history is append-only: existing records are preserved and the
32
- // new record is appended, then the whole file is atomically replaced.
33
- export async function appendVerification(paths, record) {
34
- const file = verificationFilePath(paths, record.moduleRef);
35
- const records = (await readJsonlLines(file)).filter(isVerificationRecord);
36
- records.push(record);
37
- const content = `${records.map((item) => JSON.stringify(item)).join("\n")}\n`;
38
- await writeFileAtomic(file, content);
39
- }
40
- export async function loadVerificationHistory(paths, moduleRef) {
41
- const lines = await readJsonlLines(verificationFilePath(paths, moduleRef));
42
- return lines.filter(isVerificationRecord);
43
- }
44
- export async function loadLatestVerification(paths, moduleRef) {
45
- const history = await loadVerificationHistory(paths, moduleRef);
46
- return history[history.length - 1];
47
- }
@@ -1,16 +0,0 @@
1
- import type { HumanAction, ModuleDescriptor } from "@tomflow/proflow-module-contract";
2
- import type { DeploymentIntent, DeploymentPlan, DeploymentStep, ModuleTarget, ResolvedModule } from "../contracts.ts";
3
- export declare function toResolvedModule(descriptor: ModuleDescriptor): ResolvedModule;
4
- export declare function generatePlanRef(intent: DeploymentIntent, moduleTargets: readonly ModuleTarget[], now: Date): string;
5
- export declare function deepFreeze<T>(value: T): T;
6
- export interface AssembleArgs {
7
- intent: DeploymentIntent;
8
- modules: readonly ResolvedModule[];
9
- targets: readonly ModuleTarget[];
10
- config: Record<string, Record<string, string>> | undefined;
11
- steps: DeploymentStep[];
12
- humanActions: HumanAction[];
13
- now: Date;
14
- targetDescriptors?: readonly ModuleDescriptor[];
15
- }
16
- export declare function assemblePlan(args: AssembleArgs): DeploymentPlan;
@@ -1,107 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { buildDependencyGraph } from "../graph/graph.js";
3
- import { computeFingerprint } from "./fingerprint.js";
4
- function compareRef(a, b) {
5
- return a < b ? -1 : a > b ? 1 : 0;
6
- }
7
- export function toResolvedModule(descriptor) {
8
- return {
9
- moduleRef: descriptor.moduleRef,
10
- packageName: descriptor.packageName,
11
- moduleVersion: descriptor.moduleVersion,
12
- kind: descriptor.kind,
13
- installClass: descriptor.installClass,
14
- identity: descriptor.identity,
15
- documentation: descriptor.documentation,
16
- provides: descriptor.provides,
17
- requires: descriptor.requires,
18
- requirements: descriptor.requirements,
19
- configSlots: descriptor.configSlots,
20
- lifecycle: descriptor.lifecycle.supported,
21
- verification: descriptor.verification,
22
- effects: descriptor.effects,
23
- source: { type: "installed" },
24
- };
25
- }
26
- export function generatePlanRef(intent, moduleTargets, now) {
27
- const key = JSON.stringify({
28
- intent,
29
- targets: [...moduleTargets]
30
- .sort((a, b) => compareRef(a.moduleRef, b.moduleRef))
31
- .map((target) => ({
32
- moduleRef: target.moduleRef,
33
- targetVersion: target.targetVersion ?? null,
34
- })),
35
- });
36
- const hash = createHash("sha256").update(key).digest("hex").slice(0, 8);
37
- return `plan-${intent}-${now.getTime()}-${hash}`;
38
- }
39
- export function deepFreeze(value) {
40
- if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
41
- Object.freeze(value);
42
- for (const key of Object.keys(value)) {
43
- deepFreeze(value[key]);
44
- }
45
- }
46
- return value;
47
- }
48
- export function assemblePlan(args) {
49
- const graph = buildDependencyGraph(args.modules);
50
- const byRef = new Map(args.modules.map((module) => [module.moduleRef, module]));
51
- const targetByRef = new Map(args.targets.map((target) => [target.moduleRef, target]));
52
- const moduleTargets = [];
53
- for (const ref of graph.order) {
54
- const explicit = targetByRef.get(ref);
55
- const merged = {
56
- ...(explicit?.config ?? {}),
57
- ...(args.config?.[ref] ?? {}),
58
- };
59
- const target = { moduleRef: ref };
60
- if (explicit?.targetVersion !== undefined) {
61
- target.targetVersion = explicit.targetVersion;
62
- }
63
- if (Object.keys(merged).length > 0) {
64
- target.config = merged;
65
- }
66
- moduleTargets.push(target);
67
- }
68
- const verification = graph.order.map((ref) => ({
69
- moduleRef: ref,
70
- checks: (byRef.get(ref)?.verification.checks ?? []).map((check) => check.id),
71
- }));
72
- const effectMap = new Map();
73
- for (const module of args.modules) {
74
- for (const effect of module.effects) {
75
- const key = `${effect.kind}:${effect.description}`;
76
- if (!effectMap.has(key))
77
- effectMap.set(key, effect);
78
- }
79
- }
80
- const effects = [...effectMap.values()].sort((a, b) => compareRef(`${a.kind}:${a.description}`, `${b.kind}:${b.description}`));
81
- const humanByAction = new Map();
82
- for (const action of args.humanActions) {
83
- if (!humanByAction.has(action.action)) {
84
- humanByAction.set(action.action, action);
85
- }
86
- }
87
- const humanActions = [...humanByAction.values()].sort((a, b) => compareRef(a.action, b.action));
88
- const resolvedModules = [...args.modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
89
- const plan = {
90
- planRef: "",
91
- intent: args.intent,
92
- moduleTargets,
93
- resolvedModules,
94
- ...(args.targetDescriptors !== undefined
95
- ? { targetDescriptors: [...args.targetDescriptors] }
96
- : {}),
97
- steps: args.steps,
98
- effects,
99
- humanActions,
100
- verification,
101
- fingerprint: "",
102
- createdAt: args.now.toISOString(),
103
- };
104
- plan.planRef = generatePlanRef(args.intent, moduleTargets, args.now);
105
- plan.fingerprint = computeFingerprint(plan);
106
- return deepFreeze(plan);
107
- }
@@ -1,20 +0,0 @@
1
- import type { DeploymentPlan, DeploymentStep } from "../contracts.ts";
2
- export interface VerificationCheckResult {
3
- id: string;
4
- status: "PASS" | "FAIL";
5
- }
6
- export interface StepReality {
7
- installedVersion?: string;
8
- configValues?: Record<string, string>;
9
- processRunning?: boolean;
10
- resourceConfigured?: boolean;
11
- humanActionVerified?: boolean;
12
- verificationChecks?: readonly VerificationCheckResult[];
13
- migrated?: boolean;
14
- }
15
- export type StepCheckStatus = "SATISFIED" | "NOT_SATISFIED" | "UNKNOWN";
16
- export interface StepCheckResult {
17
- status: StepCheckStatus;
18
- reason: string;
19
- }
20
- export declare function evaluateStepCheck(step: DeploymentStep, plan: DeploymentPlan, reality: StepReality): StepCheckResult;
@@ -1,107 +0,0 @@
1
- import { CheckStrategy } from "./steps.js";
2
- function satisfied(reason) {
3
- return { status: "SATISFIED", reason };
4
- }
5
- function notSatisfied(reason) {
6
- return { status: "NOT_SATISFIED", reason };
7
- }
8
- function unknown(reason) {
9
- return { status: "UNKNOWN", reason };
10
- }
11
- function moduleOf(plan, step) {
12
- return plan.resolvedModules.find((module) => module.moduleRef === step.moduleRef);
13
- }
14
- export function evaluateStepCheck(step, plan, reality) {
15
- switch (step.checkStrategy) {
16
- case CheckStrategy.packageInstalled:
17
- return checkPackage(step, plan, reality);
18
- case CheckStrategy.packageAbsent:
19
- return reality.installedVersion === undefined
20
- ? satisfied(`${step.moduleRef} package is absent`)
21
- : notSatisfied(`${step.moduleRef} package is still installed at ${reality.installedVersion}`);
22
- case CheckStrategy.configMaterialized:
23
- return checkConfig(step, plan, reality);
24
- case CheckStrategy.lifecycleRunning:
25
- return reality.processRunning === true
26
- ? satisfied(`${step.moduleRef} is running`)
27
- : notSatisfied(`${step.moduleRef} is not observed running`);
28
- case CheckStrategy.lifecycleStopped:
29
- return reality.processRunning === false
30
- ? satisfied(`${step.moduleRef} is stopped`)
31
- : notSatisfied(`${step.moduleRef} is not observed stopped`);
32
- case CheckStrategy.externalResourceConfigured:
33
- return reality.resourceConfigured === true
34
- ? satisfied(`external resource ${step.moduleRef} configured`)
35
- : notSatisfied(`external resource ${step.moduleRef} not observed configured`);
36
- case CheckStrategy.humanVerified:
37
- return reality.humanActionVerified === true
38
- ? satisfied(`human action for ${step.moduleRef} verified`)
39
- : notSatisfied(`human action for ${step.moduleRef} not observed verified`);
40
- case CheckStrategy.migrateComplete:
41
- return reality.migrated === true
42
- ? satisfied(`${step.moduleRef} migrated`)
43
- : notSatisfied(`${step.moduleRef} not observed migrated`);
44
- case CheckStrategy.verifyChecksPass:
45
- return checkVerify(step, plan, reality);
46
- default:
47
- return unknown(`unknown check strategy ${step.checkStrategy}`);
48
- }
49
- }
50
- function checkPackage(step, plan, reality) {
51
- const module = moduleOf(plan, step);
52
- if (module === undefined) {
53
- return unknown(`module ${step.moduleRef} is not in the plan`);
54
- }
55
- if (reality.installedVersion === undefined) {
56
- return notSatisfied(`installed version for ${step.moduleRef} is not observed`);
57
- }
58
- if (reality.installedVersion !== module.moduleVersion) {
59
- return notSatisfied(`installed ${reality.installedVersion} != expected ${module.moduleVersion}`);
60
- }
61
- return satisfied(`installed ${module.moduleVersion} matches target`);
62
- }
63
- function checkConfig(step, plan, reality) {
64
- const module = moduleOf(plan, step);
65
- if (module === undefined) {
66
- return unknown(`module ${step.moduleRef} is not in the plan`);
67
- }
68
- if (reality.configValues === undefined) {
69
- return notSatisfied(`config for ${step.moduleRef} is not materialized`);
70
- }
71
- for (const slot of module.configSlots) {
72
- if (!slot.required)
73
- continue;
74
- const value = reality.configValues[slot.key];
75
- if (value === undefined || value === "") {
76
- return notSatisfied(`required config ${slot.key} is missing`);
77
- }
78
- }
79
- // A configure step is satisfied only when current persisted reality matches
80
- // the immutable target carried by this plan. Required-key presence alone is
81
- // insufficient: a failed/partial prior apply can leave every key present but
82
- // with values that the module-owned materializer rejected.
83
- const target = plan.moduleTargets.find((entry) => entry.moduleRef === step.moduleRef)?.config;
84
- for (const [key, expected] of Object.entries(target ?? {})) {
85
- const observed = reality.configValues[key];
86
- if (observed !== expected) {
87
- return notSatisfied(`config ${key} for ${step.moduleRef} is ${observed === undefined ? "not observed" : "not at the planned target"}`);
88
- }
89
- }
90
- return satisfied(`planned config materialized for ${step.moduleRef}`);
91
- }
92
- function checkVerify(step, plan, reality) {
93
- const module = moduleOf(plan, step);
94
- if (module === undefined) {
95
- return unknown(`module ${step.moduleRef} is not in the plan`);
96
- }
97
- if (reality.verificationChecks === undefined) {
98
- return notSatisfied(`verification for ${step.moduleRef} is not run`);
99
- }
100
- const byId = new Map(reality.verificationChecks.map((check) => [check.id, check.status]));
101
- for (const check of module.verification.checks) {
102
- if (byId.get(check.id) !== "PASS") {
103
- return notSatisfied(`verification check ${check.id} is not passing`);
104
- }
105
- }
106
- return satisfied(`verification checks pass for ${step.moduleRef}`);
107
- }
@@ -1,12 +0,0 @@
1
- import type { DeploymentPlan } from "../contracts.ts";
2
- export interface FingerprintSource {
3
- intent: string;
4
- targets: string;
5
- modules: string;
6
- steps: string;
7
- effects: string;
8
- humanActions: string;
9
- verification: string;
10
- }
11
- export declare function buildFingerprintSource(plan: DeploymentPlan): FingerprintSource;
12
- export declare function computeFingerprint(plan: DeploymentPlan): string;
@@ -1,164 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- function compareRef(a, b) {
3
- return a < b ? -1 : a > b ? 1 : 0;
4
- }
5
- function stableStringify(value) {
6
- if (value === null || value === undefined)
7
- return "null";
8
- if (typeof value !== "object")
9
- return JSON.stringify(value);
10
- if (Array.isArray(value)) {
11
- return `[${value.map((item) => stableStringify(item)).join(",")}]`;
12
- }
13
- const record = value;
14
- const keys = Object.keys(record).sort();
15
- return `{${keys
16
- .map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
17
- .join(",")}}`;
18
- }
19
- function sha256(value) {
20
- return createHash("sha256").update(value).digest("hex");
21
- }
22
- // Maps each moduleRef to the config keys that hold RAW secret values and must
23
- // be redacted from the fingerprint. secretRef slots are explicitly excluded:
24
- // their values are opaque reference identities (e.g. `secret://…`), which are
25
- // fingerprinted verbatim so a reference change (A→B) changes the fingerprint.
26
- function rawSecretKeyMap(modules) {
27
- const map = new Map();
28
- for (const module of modules) {
29
- const set = new Set();
30
- for (const slot of module.configSlots) {
31
- if (slot.type !== "secretRef" && slot.sensitive === true) {
32
- set.add(slot.key);
33
- }
34
- }
35
- map.set(module.moduleRef, set);
36
- }
37
- return map;
38
- }
39
- function canonicalModules(modules) {
40
- return [...modules]
41
- .sort((a, b) => compareRef(a.moduleRef, b.moduleRef))
42
- .map((module) => ({
43
- moduleRef: module.moduleRef,
44
- packageName: module.packageName,
45
- moduleVersion: module.moduleVersion,
46
- kind: module.kind,
47
- installClass: module.installClass,
48
- identity: module.identity,
49
- documentation: [...module.documentation]
50
- .sort((a, b) => compareRef(a.id, b.id))
51
- .map((entry) => ({
52
- id: entry.id,
53
- path: entry.path,
54
- description: entry.description ?? null,
55
- })),
56
- provides: [...module.provides]
57
- .sort((a, b) => compareRef(a.contractRef, b.contractRef))
58
- .map((provide) => ({
59
- contractRef: provide.contractRef,
60
- version: provide.version,
61
- })),
62
- requires: [...module.requires]
63
- .sort((a, b) => compareRef(a.contractRef, b.contractRef))
64
- .map((require) => ({
65
- contractRef: require.contractRef,
66
- versionRange: require.versionRange,
67
- optional: require.optional ?? false,
68
- })),
69
- requirements: [...module.requirements]
70
- .sort((a, b) => compareRef(stableStringify(a), stableStringify(b)))
71
- .map((requirement) => requirement),
72
- configSlots: [...module.configSlots]
73
- .sort((a, b) => compareRef(a.key, b.key))
74
- .map((slot) => ({
75
- key: slot.key,
76
- type: slot.type,
77
- required: slot.required,
78
- sensitive: slot.sensitive ?? false,
79
- default: slot.default === undefined ? null : stableStringify(slot.default),
80
- })),
81
- lifecycle: [...module.lifecycle].sort(compareRef),
82
- verification: [...module.verification.checks]
83
- .sort((a, b) => compareRef(a.id, b.id))
84
- .map((check) => ({
85
- id: check.id,
86
- lifecycle: check.lifecycle,
87
- description: check.description,
88
- })),
89
- effects: [...module.effects]
90
- .sort((a, b) => compareRef(`${a.kind}:${a.description}`, `${b.kind}:${b.description}`))
91
- .map((effect) => ({
92
- kind: effect.kind,
93
- description: effect.description,
94
- path: effect.path ?? null,
95
- retention: effect.retention,
96
- })),
97
- }));
98
- }
99
- function redactConfig(config, rawSecretKeys) {
100
- const result = {};
101
- for (const key of Object.keys(config).sort()) {
102
- result[key] = rawSecretKeys.has(key) ? "<redacted>" : (config[key] ?? "");
103
- }
104
- return result;
105
- }
106
- function redactedTargets(targets, rawSecretByRef) {
107
- return [...targets]
108
- .sort((a, b) => compareRef(a.moduleRef, b.moduleRef))
109
- .map((target) => {
110
- const rawSecretKeys = rawSecretByRef.get(target.moduleRef) ?? new Set();
111
- const config = target.config === undefined
112
- ? null
113
- : redactConfig(target.config, rawSecretKeys);
114
- return {
115
- moduleRef: target.moduleRef,
116
- targetVersion: target.targetVersion ?? null,
117
- config,
118
- };
119
- });
120
- }
121
- function canonicalSteps(steps) {
122
- return steps.map((step) => ({
123
- stepRef: step.stepRef,
124
- moduleRef: step.moduleRef,
125
- kind: step.kind,
126
- preconditions: [...step.preconditions].sort(compareRef),
127
- expectedEffect: step.expectedEffect,
128
- checkStrategy: step.checkStrategy,
129
- executeStrategy: step.executeStrategy ?? null,
130
- postcondition: step.postcondition,
131
- }));
132
- }
133
- export function buildFingerprintSource(plan) {
134
- const rawSecretByRef = rawSecretKeyMap(plan.resolvedModules);
135
- return {
136
- intent: plan.intent,
137
- targets: stableStringify(redactedTargets(plan.moduleTargets, rawSecretByRef)),
138
- modules: stableStringify(canonicalModules(plan.resolvedModules)),
139
- steps: stableStringify(canonicalSteps(plan.steps)),
140
- effects: stableStringify([...plan.effects]
141
- .sort((a, b) => compareRef(`${a.kind}:${a.description}`, `${b.kind}:${b.description}`))
142
- .map((effect) => ({
143
- kind: effect.kind,
144
- description: effect.description,
145
- path: effect.path ?? null,
146
- retention: effect.retention,
147
- }))),
148
- humanActions: stableStringify([...plan.humanActions]
149
- .sort((a, b) => compareRef(a.action, b.action))
150
- .map((action) => ({
151
- action: action.action,
152
- description: action.description,
153
- }))),
154
- verification: stableStringify([...plan.verification]
155
- .sort((a, b) => compareRef(a.moduleRef, b.moduleRef))
156
- .map((verification) => ({
157
- moduleRef: verification.moduleRef,
158
- checks: [...verification.checks].sort(compareRef),
159
- }))),
160
- };
161
- }
162
- export function computeFingerprint(plan) {
163
- return sha256(stableStringify(buildFingerprintSource(plan)));
164
- }
@@ -1,13 +0,0 @@
1
- export type { StepCheckResult, StepCheckStatus, StepReality, VerificationCheckResult, } from "./check.ts";
2
- export { evaluateStepCheck } from "./check.ts";
3
- export type { FingerprintSource } from "./fingerprint.ts";
4
- export { computeFingerprint } from "./fingerprint.ts";
5
- export type { PlanInput } from "./plan.ts";
6
- export { planDeployment } from "./plan.ts";
7
- export type { RepairFact, RepairFactCode, RepairPlanInput } from "./repair.ts";
8
- export { planRepair, repairFactCodes } from "./repair.ts";
9
- export type { StalenessResult } from "./stale.ts";
10
- export { checkPlanStale } from "./stale.ts";
11
- export { CheckStrategy, ExecuteStrategy } from "./steps.ts";
12
- export type { UpgradeAssessment } from "./upgrade.ts";
13
- export { assessUpgrade, planUpgrade } from "./upgrade.ts";
@@ -1,7 +0,0 @@
1
- export { evaluateStepCheck } from "./check.js";
2
- export { computeFingerprint } from "./fingerprint.js";
3
- export { planDeployment } from "./plan.js";
4
- export { planRepair, repairFactCodes } from "./repair.js";
5
- export { checkPlanStale } from "./stale.js";
6
- export { CheckStrategy, ExecuteStrategy } from "./steps.js";
7
- export { assessUpgrade, planUpgrade } from "./upgrade.js";
@@ -1,15 +0,0 @@
1
- import type { ModuleDescriptor } from "@tomflow/proflow-module-contract";
2
- import type { DeploymentIntent, DeploymentPlan, ModuleTarget, ResolvedModule } from "../contracts.ts";
3
- import type { RepairFact } from "./repair.ts";
4
- export interface PlanInput {
5
- intent: DeploymentIntent;
6
- modules?: readonly ResolvedModule[];
7
- targets?: readonly ModuleTarget[];
8
- config?: Record<string, Record<string, string>>;
9
- currentDescriptors?: readonly ModuleDescriptor[];
10
- targetDescriptors?: readonly ModuleDescriptor[];
11
- facts?: readonly RepairFact[];
12
- uninstallScope?: "module" | "platform-instance";
13
- now?: Date;
14
- }
15
- export declare function planDeployment(input: PlanInput): DeploymentPlan;