@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,10 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
- import { mkdtemp, readFile, rm } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
2
  import { promisify } from "node:util";
6
- import { gunzipSync } from "node:zlib";
7
- import { parseModuleDescriptor, proflowPackageMetadataSchema, } from "@tomflow/proflow-module-contract";
3
+ import { proflowPackageMetadataSchema } from "@tomflow/proflow-module-contract";
8
4
  import { PlatformError } from "../errors.js";
9
5
  import { versionSatisfies } from "../modules.js";
10
6
  const execFileAsync = promisify(execFile);
@@ -60,131 +56,6 @@ export async function discoverRegistryModules(options) {
60
56
  rejected.sort((left, right) => left.packageName.localeCompare(right.packageName));
61
57
  return { registry, candidates, rejected };
62
58
  }
63
- export async function loadRegistryModuleDescriptor(options) {
64
- const runner = options.runner ?? systemNpmRunner();
65
- const tempRoot = await mkdtemp(join(tmpdir(), "proflow-registry-manifest-"));
66
- try {
67
- const packed = await runner.run([
68
- "pack",
69
- `${options.candidate.packageName}@${options.candidate.moduleVersion}`,
70
- "--json",
71
- "--ignore-scripts",
72
- `--pack-destination=${tempRoot}`,
73
- `--registry=${options.candidate.registry}`,
74
- ], options.workspaceRoot);
75
- const filename = packFilename(packed.stdout, options.candidate.packageName);
76
- const archive = await readFile(join(tempRoot, filename));
77
- const manifestPath = `package/${options.candidate.manifest.replace(/^\.\//, "")}`;
78
- const rawManifest = readTarEntry(archive, manifestPath);
79
- const descriptor = parseModuleDescriptor(parseJson(rawManifest.toString("utf8"), `${options.candidate.packageName} module manifest`));
80
- if (descriptor.packageName !== options.candidate.packageName ||
81
- descriptor.moduleVersion !== options.candidate.moduleVersion) {
82
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", `Registry manifest identity/version mismatch for ${options.candidate.packageName}@${options.candidate.moduleVersion}`);
83
- }
84
- return descriptor;
85
- }
86
- finally {
87
- await rm(tempRoot, { recursive: true, force: true });
88
- }
89
- }
90
- function packFilename(stdout, packageName) {
91
- const parsed = parseJson(stdout, `npm pack ${packageName}`);
92
- if (!Array.isArray(parsed) || parsed.length !== 1 || !isRecord(parsed[0])) {
93
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", `npm pack response is invalid for ${packageName}`);
94
- }
95
- const filename = parsed[0].filename;
96
- if (typeof filename !== "string" ||
97
- filename === "" ||
98
- filename.includes("/") ||
99
- filename.includes("\\")) {
100
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", `npm pack filename is invalid for ${packageName}`);
101
- }
102
- return filename;
103
- }
104
- function readTarEntry(tgz, targetName) {
105
- let tar;
106
- try {
107
- tar = gunzipSync(tgz);
108
- }
109
- catch {
110
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", "npm package tarball is not valid gzip");
111
- }
112
- for (let offset = 0; offset + 512 <= tar.length;) {
113
- const header = tar.subarray(offset, offset + 512);
114
- if (header.every((byte) => byte === 0))
115
- break;
116
- const name = tarString(header, 0, 100);
117
- const prefix = tarString(header, 345, 155);
118
- const entryName = prefix === "" ? name : `${prefix}/${name}`;
119
- const sizeRaw = tarString(header, 124, 12).trim();
120
- const size = sizeRaw === "" ? 0 : Number.parseInt(sizeRaw, 8);
121
- if (!Number.isSafeInteger(size) || size < 0) {
122
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", "npm package tarball has an invalid entry size");
123
- }
124
- const dataStart = offset + 512;
125
- const dataEnd = dataStart + size;
126
- if (dataEnd > tar.length) {
127
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", "npm package tarball is truncated");
128
- }
129
- if (entryName === targetName) {
130
- if (size > 1024 * 1024) {
131
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", "ProFlow module manifest exceeds 1 MiB");
132
- }
133
- return tar.subarray(dataStart, dataEnd);
134
- }
135
- offset = dataStart + Math.ceil(size / 512) * 512;
136
- }
137
- throw new PlatformError("REGISTRY_RESPONSE_INVALID", `npm package tarball does not contain ${targetName}`);
138
- }
139
- function tarString(header, start, length) {
140
- const field = header.subarray(start, start + length);
141
- const end = field.indexOf(0);
142
- return field.subarray(0, end < 0 ? field.length : end).toString("utf8");
143
- }
144
- export async function discoverRegistryInstallClosure(options) {
145
- const runner = options.runner ?? systemNpmRunner();
146
- const queue = [validateRequestedPackageName(options.packageName)];
147
- const seen = new Set();
148
- const candidates = new Map();
149
- const rejected = new Map();
150
- let registry;
151
- while (queue.length > 0) {
152
- const packageName = queue.shift();
153
- if (!packageName)
154
- continue;
155
- if (seen.has(packageName))
156
- continue;
157
- seen.add(packageName);
158
- const discovered = await discoverRegistryModules({
159
- workspaceRoot: options.workspaceRoot,
160
- packageName,
161
- runner,
162
- ...(options.nodeVersion === undefined
163
- ? {}
164
- : { nodeVersion: options.nodeVersion }),
165
- });
166
- registry ??= discovered.registry;
167
- for (const item of discovered.rejected)
168
- rejected.set(item.packageName, item);
169
- const candidate = discovered.candidates[0];
170
- if (candidate === undefined) {
171
- throw new PlatformError("PACKAGE_NOT_PROFLOW", `package ${packageName} is not an installable ProFlow module`);
172
- }
173
- candidates.set(candidate.packageName, candidate);
174
- for (const dependency of candidate.installRequires) {
175
- if (!seen.has(dependency))
176
- queue.push(dependency);
177
- }
178
- }
179
- if (registry === undefined) {
180
- registry = await resolveScopeRegistry(options.workspaceRoot, runner);
181
- }
182
- return {
183
- registry,
184
- candidates: [...candidates.values()].sort((a, b) => a.packageName.localeCompare(b.packageName)),
185
- rejected: [...rejected.values()].sort((a, b) => a.packageName.localeCompare(b.packageName)),
186
- };
187
- }
188
59
  async function searchPackageNames(workspaceRoot, registry, runner) {
189
60
  const result = await runner.run([
190
61
  "search",
@@ -273,10 +144,8 @@ function assessManifest(manifest, registry, nodeVersion) {
273
144
  ...(typeof manifest.description === "string"
274
145
  ? { description: manifest.description }
275
146
  : {}),
276
- installClass: metadata.data.installClass,
277
147
  descriptor: metadata.data.descriptor,
278
148
  manifest: metadata.data.manifest,
279
- installRequires: [...metadata.data.installRequires],
280
149
  ...(nodeVersionRange === undefined ? {} : { nodeVersionRange }),
281
150
  registry,
282
151
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-platform-cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -21,13 +21,13 @@
21
21
  "README.md"
22
22
  ],
23
23
  "dependencies": {
24
- "@tomflow/proflow-module-contract": "^0.1.1"
24
+ "@tomflow/proflow-module-contract": "^0.1.3"
25
25
  },
26
26
  "devDependencies": {
27
- "@tomflow/proflow-deployment-conformance": "^0.1.2",
28
- "@tomflow/proflow-module-template": "^0.1.2"
27
+ "@tomflow/proflow-deployment-conformance": "^0.1.3",
28
+ "@tomflow/proflow-module-template": "^0.1.3"
29
29
  },
30
- "description": "Deterministic platform-level deployment discovery, planning, lifecycle and verification CLI.",
30
+ "description": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration.",
31
31
  "keywords": [
32
32
  "proflow",
33
33
  "proflow-module",
@@ -35,10 +35,6 @@
35
35
  ],
36
36
  "proflow": {
37
37
  "module": true,
38
- "installClass": "core",
39
- "installRequires": [
40
- "@tomflow/proflow-module-contract"
41
- ],
42
38
  "descriptor": "./dist/deployment/descriptor.js",
43
39
  "manifest": "./proflow.module.json"
44
40
  },
@@ -3,14 +3,13 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "platform-cli",
5
5
  "packageName": "@tomflow/proflow-platform-cli",
6
- "moduleVersion": "0.1.14",
6
+ "moduleVersion": "0.1.16",
7
7
  "kind": "cli",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",
10
- "installClass": "core",
11
10
  "identity": {
12
11
  "domain": "deployment-governance",
13
- "summary": "Deterministic platform-level deployment discovery, planning, lifecycle and verification CLI."
12
+ "summary": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration."
14
13
  },
15
14
  "provides": [],
16
15
  "requires": [],
package/CHANGELOG.md DELETED
@@ -1,85 +0,0 @@
1
- # @tomflow/proflow-platform-cli
2
-
3
- ## 0.1.14
4
-
5
- ### Patch Changes
6
-
7
- - Gate `platform start` on managed preflight, make platform-level `restart` a stop-then-start sequence without an extra preflight, and render lifecycle / verify / doctor results clearly for human users.
8
-
9
- ## 0.1.13
10
-
11
- ### Patch Changes
12
-
13
- - Fully clean Workspace uninstall artifacts by removing empty ProFlow layout directories and stale pnpm release-age entries across prior managed versions.
14
-
15
- ## 0.1.12
16
-
17
- ### Patch Changes
18
-
19
- - Clean pnpm release-age and empty-root lockfile artifacts after whole Workspace uninstall, and remove the `.proflow` root only when it is empty while preserving non-deployment data.
20
-
21
- ## 0.1.11
22
-
23
- ### Patch Changes
24
-
25
- - Use pnpm's global config form to disable scripts during package removal so Workspace uninstall works on pnpm 11.
26
-
27
- ## 0.1.10
28
-
29
- ### Patch Changes
30
-
31
- - Fail closed on malformed persisted module config instead of treating it as missing configuration.
32
-
33
- ## 0.1.9
34
-
35
- ### Patch Changes
36
-
37
- - Clarify whole-instance uninstall preservation boundaries in public CLI help and package documentation.
38
-
39
- ## 0.1.8
40
-
41
- ### Patch Changes
42
-
43
- - Align human-action completion across Apply resume, preflight, and manifest current reality by treating the latest current-version verification PASS as the durable completion proof while preserving append-only deployment history.
44
-
45
- ## 0.1.7
46
-
47
- ### Patch Changes
48
-
49
- - Rebuild upgrade apply staleness from the current persisted Workspace configuration so unchanged config remains fresh while configuration drift becomes PLAN_STALE instead of being misreported as missing required config.
50
-
51
- ## 0.1.6
52
-
53
- ### Patch Changes
54
-
55
- - Carry persisted workspace configuration into Registry-backed upgrade planning, with explicit --config values overlaying stored values, so newly published descriptors cannot falsely report existing required configuration as missing before package mutation.
56
-
57
- ## 0.1.5
58
-
59
- ### Patch Changes
60
-
61
- - Preserve module-provided ACTION_REQUIRED descriptions in startup preflight findings so missing Role readiness tells the user the missing registration fact, next action, and revalidation command.
62
-
63
- ## 0.1.4
64
-
65
- ### Patch Changes
66
-
67
- - Make upgrade planning emit configuration writes only when a real config target exists, and fail before package mutation when new required configuration is missing.
68
-
69
- ## 0.1.3
70
-
71
- ### Patch Changes
72
-
73
- - Real-1 final remediation and black-box release closure
74
-
75
- ## 0.1.2
76
-
77
- ### Patch Changes
78
-
79
- - Close the Real-1 global Platform control-plane contract across every published ProFlow package. Platform CLI now owns one durable global Workspace binding with canonical identity, cross-process operation locking, cwd/`--workspace` install targeting, cross-directory instance commands, whole-instance uninstall/rebind, and deterministic npm/yarn/pnpm Workspace package-manager selection. Every package-owned install entry, including newly generated Module Template packages, delegates to the Shell-global `platform` command and fails closed when that global CLI is unavailable; Deployment Conformance and the formal test plans mechanically enforce the same rule across all 24 packages.
80
-
81
- ## 0.1.1
82
-
83
- ### Patch Changes
84
-
85
- - Read installed package versions directly from the Workspace `node_modules` package manifest so postcondition checks cannot reuse stale Node module-resolution cache entries after an in-process package upgrade.
@@ -1,26 +0,0 @@
1
- import type { ApplyResult } from "../contracts.ts";
2
- import type { ModuleCatalog } from "../modules.ts";
3
- import { type WorkspacePaths } from "../paths.ts";
4
- import { type PlanInput } from "../planner/index.ts";
5
- import type { PackageManagerDriver } from "./driver.ts";
6
- import type { RealityObserver } from "./reality.ts";
7
- export interface ApplyContext {
8
- paths: WorkspacePaths;
9
- planRef: string;
10
- catalog: ModuleCatalog;
11
- /** Current stable assumptions the plan must still match (staleness gate). */
12
- current: PlanInput;
13
- driver: PackageManagerDriver;
14
- observer?: RealityObserver;
15
- /** Rebuilds installed descriptors/bindings after a package mutation. */
16
- refreshCatalog?: () => Promise<ModuleCatalog>;
17
- }
18
- /**
19
- * Applies a frozen deployment plan deterministically. Every step is re-checked
20
- * against current reality before any action: satisfied steps SKIP, human steps
21
- * and ACTION_REQUIRED mutations persist a structured pendingAction and STOP with
22
- * ACTION_REQUIRED, a BLOCKED/FAILED mutation STOPs with the matching outcome,
23
- * and only a fully applied plan is recorded in `lastAppliedPlans`. The exclusive
24
- * workspace lock is released in all paths.
25
- */
26
- export declare function applyPlan(context: ApplyContext): Promise<ApplyResult>;
@@ -1,314 +0,0 @@
1
- import { PlatformError } from "../errors.js";
2
- import { ensureLayout } from "../paths.js";
3
- import { emptyDeploymentState, loadDeploymentState, loadPlan, saveDeploymentState, } from "../persistence/index.js";
4
- import { checkPlanStale, ExecuteStrategy, evaluateStepCheck, } from "../planner/index.js";
5
- import { acquireWorkspaceLock } from "../security/index.js";
6
- import { executeStep } from "./execute.js";
7
- import { createRealityObserver } from "./reality.js";
8
- function nowIso() {
9
- return new Date().toISOString();
10
- }
11
- function failureMessage(error) {
12
- return error instanceof Error ? error.message : String(error);
13
- }
14
- /**
15
- * Applies a frozen deployment plan deterministically. Every step is re-checked
16
- * against current reality before any action: satisfied steps SKIP, human steps
17
- * and ACTION_REQUIRED mutations persist a structured pendingAction and STOP with
18
- * ACTION_REQUIRED, a BLOCKED/FAILED mutation STOPs with the matching outcome,
19
- * and only a fully applied plan is recorded in `lastAppliedPlans`. The exclusive
20
- * workspace lock is released in all paths.
21
- */
22
- export async function applyPlan(context) {
23
- const driver = context.driver;
24
- let catalog = context.catalog;
25
- let observer = context.observer ??
26
- createRealityObserver({
27
- paths: context.paths,
28
- catalog,
29
- driver,
30
- });
31
- await ensureLayout(context.paths);
32
- const lock = await acquireWorkspaceLock(context.paths, context.planRef);
33
- try {
34
- const plan = await loadPlan(context.paths, context.planRef);
35
- if (plan === undefined) {
36
- throw new PlatformError("PLAN_NOT_FOUND", `plan ${context.planRef} not found`);
37
- }
38
- const staleness = checkPlanStale(plan, context.current);
39
- if (staleness.stale) {
40
- return {
41
- planRef: plan.planRef,
42
- outcome: "BLOCKED",
43
- stepResults: [],
44
- completedAt: nowIso(),
45
- };
46
- }
47
- const state = (await loadDeploymentState(context.paths)) ?? emptyDeploymentState();
48
- const stepResults = [];
49
- for (const step of plan.steps) {
50
- const reality = await observer.observe(step, plan);
51
- if (reality === undefined) {
52
- // UNKNOWN: current reality could not be observed. Stop without
53
- // replaying any lifecycle/external effect, then doctor/repair.
54
- stepResults.push({
55
- stepRef: step.stepRef,
56
- moduleRef: step.moduleRef,
57
- status: "FAILED",
58
- message: `blocked: current reality for ${step.moduleRef} could not be observed`,
59
- });
60
- state.updatedAt = nowIso();
61
- await saveDeploymentState(context.paths, state);
62
- return {
63
- planRef: plan.planRef,
64
- outcome: "BLOCKED",
65
- stepResults,
66
- completedAt: nowIso(),
67
- };
68
- }
69
- const check = evaluateStepCheck(step, plan, reality);
70
- if (check.status === "SATISFIED") {
71
- // A previously-pending action for this step can no longer block a
72
- // resume: the step is satisfied in current reality, so clear it.
73
- state.pendingActions = state.pendingActions.filter((pending) => !(pending.planRef === plan.planRef &&
74
- pending.stepRef === step.stepRef));
75
- stepResults.push({
76
- stepRef: step.stepRef,
77
- moduleRef: step.moduleRef,
78
- status: "SKIP",
79
- message: check.reason,
80
- });
81
- continue;
82
- }
83
- if (step.kind === "human") {
84
- const completedAt = nowIso();
85
- state.pendingActions.push({
86
- planRef: plan.planRef,
87
- stepRef: step.stepRef,
88
- moduleRef: step.moduleRef,
89
- action: step.expectedEffect,
90
- createdAt: completedAt,
91
- });
92
- state.updatedAt = completedAt;
93
- await saveDeploymentState(context.paths, state);
94
- stepResults.push({
95
- stepRef: step.stepRef,
96
- moduleRef: step.moduleRef,
97
- status: "ACTION_REQUIRED",
98
- message: check.reason,
99
- });
100
- return {
101
- planRef: plan.planRef,
102
- outcome: "ACTION_REQUIRED",
103
- stepResults,
104
- completedAt,
105
- };
106
- }
107
- let outcome;
108
- try {
109
- outcome = await executeStep({ paths: context.paths, catalog, driver }, step, plan);
110
- }
111
- catch (error) {
112
- stepResults.push({
113
- stepRef: step.stepRef,
114
- moduleRef: step.moduleRef,
115
- status: "FAILED",
116
- message: failureMessage(error),
117
- });
118
- state.updatedAt = nowIso();
119
- await saveDeploymentState(context.paths, state);
120
- return {
121
- planRef: plan.planRef,
122
- outcome: "FAILED",
123
- stepResults,
124
- completedAt: nowIso(),
125
- };
126
- }
127
- switch (outcome.kind) {
128
- case "SUCCEEDED": {
129
- if (step.executeStrategy === ExecuteStrategy.lifecycleUninstall) {
130
- // A lifecycle uninstall is owned by the module adapter. Its typed
131
- // SUCCEEDED result is the authoritative teardown confirmation. An
132
- // idempotent uninstall can legitimately leave status UNBOUND/UNKNOWN,
133
- // so a second status probe must not reinterpret that successful
134
- // teardown as a failed lifecycle:stopped postcondition.
135
- stepResults.push({
136
- stepRef: step.stepRef,
137
- moduleRef: step.moduleRef,
138
- status: "EXECUTED",
139
- message: `${step.moduleRef} owner confirmed lifecycle uninstall`,
140
- });
141
- break;
142
- }
143
- // Postcondition re-check: only a genuinely successful mutation is
144
- // confirmed; an effect that cannot be confirmed stops the apply
145
- // rather than blindly repeating it.
146
- const postReality = await observer.observe(step, plan);
147
- if (postReality === undefined) {
148
- stepResults.push({
149
- stepRef: step.stepRef,
150
- moduleRef: step.moduleRef,
151
- status: "FAILED",
152
- message: `blocked: postcondition reality for ${step.moduleRef} could not be observed`,
153
- });
154
- state.updatedAt = nowIso();
155
- await saveDeploymentState(context.paths, state);
156
- return {
157
- planRef: plan.planRef,
158
- outcome: "BLOCKED",
159
- stepResults,
160
- completedAt: nowIso(),
161
- };
162
- }
163
- const post = evaluateStepCheck(step, plan, postReality);
164
- if (post.status !== "SATISFIED") {
165
- stepResults.push({
166
- stepRef: step.stepRef,
167
- moduleRef: step.moduleRef,
168
- status: "FAILED",
169
- message: `postcondition not satisfied after execution: ${post.reason}`,
170
- });
171
- state.updatedAt = nowIso();
172
- await saveDeploymentState(context.paths, state);
173
- return {
174
- planRef: plan.planRef,
175
- outcome: "FAILED",
176
- stepResults,
177
- completedAt: nowIso(),
178
- };
179
- }
180
- stepResults.push({
181
- stepRef: step.stepRef,
182
- moduleRef: step.moduleRef,
183
- status: "EXECUTED",
184
- message: post.reason,
185
- });
186
- if (step.kind === "package" && context.refreshCatalog !== undefined) {
187
- try {
188
- catalog = await context.refreshCatalog();
189
- if (context.observer === undefined) {
190
- observer = createRealityObserver({
191
- paths: context.paths,
192
- catalog,
193
- driver,
194
- });
195
- }
196
- }
197
- catch (error) {
198
- stepResults.push({
199
- stepRef: step.stepRef,
200
- moduleRef: step.moduleRef,
201
- status: "FAILED",
202
- message: `post-package catalog refresh failed: ${failureMessage(error)}`,
203
- });
204
- state.updatedAt = nowIso();
205
- await saveDeploymentState(context.paths, state);
206
- return {
207
- planRef: plan.planRef,
208
- outcome: "FAILED",
209
- stepResults,
210
- completedAt: nowIso(),
211
- };
212
- }
213
- }
214
- break;
215
- }
216
- case "ACTION_REQUIRED": {
217
- const completedAt = nowIso();
218
- state.pendingActions.push({
219
- planRef: plan.planRef,
220
- stepRef: step.stepRef,
221
- moduleRef: step.moduleRef,
222
- action: outcome.actionRequired.action,
223
- description: outcome.actionRequired.description,
224
- createdAt: completedAt,
225
- });
226
- state.updatedAt = completedAt;
227
- await saveDeploymentState(context.paths, state);
228
- stepResults.push({
229
- stepRef: step.stepRef,
230
- moduleRef: step.moduleRef,
231
- status: "ACTION_REQUIRED",
232
- message: outcome.actionRequired.action,
233
- });
234
- return {
235
- planRef: plan.planRef,
236
- outcome: "ACTION_REQUIRED",
237
- stepResults,
238
- completedAt,
239
- };
240
- }
241
- case "BLOCKED": {
242
- stepResults.push({
243
- stepRef: step.stepRef,
244
- moduleRef: step.moduleRef,
245
- status: "FAILED",
246
- message: `blocked: ${outcome.reason}`,
247
- });
248
- state.updatedAt = nowIso();
249
- await saveDeploymentState(context.paths, state);
250
- return {
251
- planRef: plan.planRef,
252
- outcome: "BLOCKED",
253
- stepResults,
254
- completedAt: nowIso(),
255
- };
256
- }
257
- case "FAILED": {
258
- stepResults.push({
259
- stepRef: step.stepRef,
260
- moduleRef: step.moduleRef,
261
- status: "FAILED",
262
- message: outcome.reason,
263
- });
264
- state.updatedAt = nowIso();
265
- await saveDeploymentState(context.paths, state);
266
- return {
267
- planRef: plan.planRef,
268
- outcome: "FAILED",
269
- stepResults,
270
- completedAt: nowIso(),
271
- };
272
- }
273
- }
274
- }
275
- const completedAt = nowIso();
276
- if (plan.intent === "uninstall") {
277
- const removed = new Set(plan.resolvedModules.map((module) => module.moduleRef));
278
- state.selectedModules = state.selectedModules.filter((module) => !removed.has(module.moduleRef));
279
- }
280
- else if ((plan.intent === "install" || plan.intent === "upgrade") &&
281
- plan.resolvedModules.every((module) => module.source.type === "registry")) {
282
- const byRef = new Map(state.selectedModules.map((module) => [module.moduleRef, module]));
283
- for (const module of plan.resolvedModules) {
284
- byRef.set(module.moduleRef, {
285
- moduleRef: module.moduleRef,
286
- moduleVersion: module.moduleVersion,
287
- });
288
- }
289
- state.selectedModules = [...byRef.values()].sort((left, right) => left.moduleRef.localeCompare(right.moduleRef));
290
- }
291
- else {
292
- state.selectedModules = plan.resolvedModules.map((module) => ({
293
- moduleRef: module.moduleRef,
294
- moduleVersion: module.moduleVersion,
295
- }));
296
- }
297
- state.lastAppliedPlans.push({
298
- planRef: plan.planRef,
299
- intent: plan.intent,
300
- appliedAt: completedAt,
301
- });
302
- state.updatedAt = completedAt;
303
- await saveDeploymentState(context.paths, state);
304
- return {
305
- planRef: plan.planRef,
306
- outcome: "COMPLETE",
307
- stepResults,
308
- completedAt,
309
- };
310
- }
311
- finally {
312
- await lock.release();
313
- }
314
- }
@@ -1,12 +0,0 @@
1
- import type { DeploymentEffect } from "@tomflow/proflow-module-contract";
2
- /**
3
- * Removes only package-declared ephemeral filesystem effects under the
4
- * Workspace-owned `.proflow` state tree. Persistent and explicit-purge effects
5
- * are never deleted by ordinary uninstall. Paths outside `.proflow` are left to
6
- * the package-owned uninstall adapter so a descriptor can never turn the
7
- * platform package manager into an arbitrary recursive-delete primitive.
8
- */
9
- export declare function cleanupRemovableFilesystemEffects(input: {
10
- workspaceRoot: string;
11
- effects: readonly DeploymentEffect[];
12
- }): Promise<string[]>;