@tomflow/proflow-platform-cli 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -43
- package/dist/deployment/adapter.d.ts +5 -5
- package/dist/deployment/adapter.js +2 -1
- package/dist/deployment/descriptor.d.ts +2 -3
- package/dist/deployment/descriptor.js +2 -3
- package/dist/src/binding/production-bindings.d.ts +17 -15
- package/dist/src/binding/production-bindings.js +18 -59
- package/dist/src/cli.d.ts +7 -9
- package/dist/src/cli.js +329 -1237
- package/dist/src/contracts.d.ts +4 -97
- package/dist/src/discovery/discover.js +0 -1
- package/dist/src/docs/aggregate.d.ts +18 -0
- package/dist/src/docs/aggregate.js +44 -0
- package/dist/src/docs/docs.d.ts +0 -31
- package/dist/src/docs/docs.js +0 -61
- package/dist/src/docs/index.d.ts +2 -1
- package/dist/src/docs/index.js +1 -1
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +0 -18
- package/dist/src/graph/graph.d.ts +1 -4
- package/dist/src/graph/graph.js +1 -13
- package/dist/src/graph/index.d.ts +2 -2
- package/dist/src/graph/index.js +1 -1
- package/dist/src/index.d.ts +4 -24
- package/dist/src/index.js +2 -16
- package/dist/src/install/package-manager.d.ts +21 -0
- package/dist/src/install/package-manager.js +153 -0
- package/dist/src/lifecycle/dispatch.d.ts +0 -30
- package/dist/src/lifecycle/dispatch.js +0 -120
- package/dist/src/lifecycle/index.d.ts +4 -3
- package/dist/src/lifecycle/index.js +2 -2
- package/dist/src/lifecycle/thin.d.ts +11 -0
- package/dist/src/lifecycle/thin.js +68 -0
- package/dist/src/paths.d.ts +0 -14
- package/dist/src/paths.js +0 -39
- package/dist/src/persistence/config.d.ts +0 -6
- package/dist/src/persistence/config.js +0 -14
- package/dist/src/persistence/guards.d.ts +0 -6
- package/dist/src/persistence/guards.js +0 -114
- package/dist/src/persistence/index.d.ts +4 -6
- package/dist/src/persistence/index.js +2 -5
- package/dist/src/persistence/workspace-metadata.d.ts +7 -0
- package/dist/src/persistence/workspace-metadata.js +39 -0
- package/dist/src/registry/index.d.ts +1 -1
- package/dist/src/registry/index.js +1 -1
- package/dist/src/registry/npm-registry.d.ts +0 -14
- package/dist/src/registry/npm-registry.js +1 -132
- package/package.json +2 -6
- package/proflow.module.json +2 -3
- package/CHANGELOG.md +0 -79
- package/dist/src/apply/apply.d.ts +0 -26
- package/dist/src/apply/apply.js +0 -314
- package/dist/src/apply/cleanup.d.ts +0 -12
- package/dist/src/apply/cleanup.js +0 -37
- package/dist/src/apply/current.d.ts +0 -5
- package/dist/src/apply/current.js +0 -95
- package/dist/src/apply/driver.d.ts +0 -28
- package/dist/src/apply/driver.js +0 -239
- package/dist/src/apply/execute.d.ts +0 -36
- package/dist/src/apply/execute.js +0 -152
- package/dist/src/apply/index.d.ts +0 -8
- package/dist/src/apply/index.js +0 -4
- package/dist/src/apply/reality.d.ts +0 -20
- package/dist/src/apply/reality.js +0 -105
- package/dist/src/binding/global-binding.d.ts +0 -69
- package/dist/src/binding/global-binding.js +0 -349
- package/dist/src/doctor/doctor.d.ts +0 -41
- package/dist/src/doctor/doctor.js +0 -80
- package/dist/src/doctor/index.d.ts +0 -2
- package/dist/src/doctor/index.js +0 -1
- package/dist/src/install/bootstrap.d.ts +0 -10
- package/dist/src/install/bootstrap.js +0 -79
- package/dist/src/install/environment.d.ts +0 -30
- package/dist/src/install/environment.js +0 -230
- package/dist/src/install/index.d.ts +0 -6
- package/dist/src/install/index.js +0 -3
- package/dist/src/install/install.d.ts +0 -19
- package/dist/src/install/install.js +0 -172
- package/dist/src/lifecycle/service-process.d.ts +0 -11
- package/dist/src/lifecycle/service-process.js +0 -356
- package/dist/src/manifest/index.d.ts +0 -2
- package/dist/src/manifest/index.js +0 -1
- package/dist/src/manifest/manifest.d.ts +0 -61
- package/dist/src/manifest/manifest.js +0 -167
- package/dist/src/observer/deployment-summary.d.ts +0 -31
- package/dist/src/observer/deployment-summary.js +0 -34
- package/dist/src/persistence/atomic.d.ts +0 -2
- package/dist/src/persistence/atomic.js +0 -16
- package/dist/src/persistence/effective-config.d.ts +0 -3
- package/dist/src/persistence/effective-config.js +0 -20
- package/dist/src/persistence/generated.d.ts +0 -2
- package/dist/src/persistence/generated.js +0 -4
- package/dist/src/persistence/plans.d.ts +0 -5
- package/dist/src/persistence/plans.js +0 -34
- package/dist/src/persistence/state.d.ts +0 -7
- package/dist/src/persistence/state.js +0 -34
- package/dist/src/persistence/verification.d.ts +0 -5
- package/dist/src/persistence/verification.js +0 -47
- package/dist/src/planner/assemble.d.ts +0 -16
- package/dist/src/planner/assemble.js +0 -107
- package/dist/src/planner/check.d.ts +0 -20
- package/dist/src/planner/check.js +0 -107
- package/dist/src/planner/fingerprint.d.ts +0 -12
- package/dist/src/planner/fingerprint.js +0 -164
- package/dist/src/planner/index.d.ts +0 -13
- package/dist/src/planner/index.js +0 -7
- package/dist/src/planner/plan.d.ts +0 -15
- package/dist/src/planner/plan.js +0 -144
- package/dist/src/planner/repair.d.ts +0 -28
- package/dist/src/planner/repair.js +0 -131
- package/dist/src/planner/stale.d.ts +0 -7
- package/dist/src/planner/stale.js +0 -23
- package/dist/src/planner/steps.d.ts +0 -37
- package/dist/src/planner/steps.js +0 -165
- package/dist/src/planner/target-catalog.d.ts +0 -13
- package/dist/src/planner/target-catalog.js +0 -48
- package/dist/src/planner/upgrade.d.ts +0 -18
- package/dist/src/planner/upgrade.js +0 -114
- package/dist/src/preflight/config.d.ts +0 -11
- package/dist/src/preflight/config.js +0 -88
- package/dist/src/preflight/findings.d.ts +0 -26
- package/dist/src/preflight/findings.js +0 -1
- package/dist/src/preflight/index.d.ts +0 -7
- package/dist/src/preflight/index.js +0 -3
- package/dist/src/preflight/preflight.d.ts +0 -10
- package/dist/src/preflight/preflight.js +0 -190
- package/dist/src/preflight/requirements.d.ts +0 -11
- package/dist/src/preflight/requirements.js +0 -231
- package/dist/src/ready/index.d.ts +0 -2
- package/dist/src/ready/index.js +0 -1
- package/dist/src/ready/ready.d.ts +0 -40
- package/dist/src/ready/ready.js +0 -201
- package/dist/src/security/index.d.ts +0 -3
- package/dist/src/security/index.js +0 -2
- package/dist/src/security/lock.d.ts +0 -9
- package/dist/src/security/lock.js +0 -127
- package/dist/src/security/redact.d.ts +0 -6
- package/dist/src/security/redact.js +0 -48
- package/dist/src/verification/index.d.ts +0 -2
- package/dist/src/verification/index.js +0 -1
- package/dist/src/verification/verify.d.ts +0 -33
- package/dist/src/verification/verify.js +0 -114
|
@@ -1,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,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;
|