@tomflow/proflow-platform-cli 0.1.1 → 0.1.3
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/CHANGELOG.md +12 -0
- package/README.md +24 -3
- package/dist/deployment/adapter.d.ts +5 -5
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/src/apply/apply.d.ts +2 -0
- package/dist/src/apply/apply.js +47 -4
- package/dist/src/apply/current.js +14 -0
- package/dist/src/apply/driver.d.ts +1 -0
- package/dist/src/apply/driver.js +40 -12
- package/dist/src/apply/execute.js +28 -0
- package/dist/src/binding/global-binding.d.ts +69 -0
- package/dist/src/binding/global-binding.js +349 -0
- package/dist/src/binding/production-bindings.d.ts +2 -12
- package/dist/src/binding/production-bindings.js +18 -4
- package/dist/src/cli.d.ts +13 -1
- package/dist/src/cli.js +661 -40
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +13 -0
- package/dist/src/install/environment.d.ts +2 -1
- package/dist/src/install/environment.js +34 -30
- package/dist/src/install/install.js +41 -0
- package/dist/src/install/package-manager.d.ts +4 -2
- package/dist/src/install/package-manager.js +73 -17
- package/dist/src/lifecycle/dispatch.js +52 -1
- package/dist/src/lifecycle/service-process.js +32 -2
- package/dist/src/persistence/guards.js +1 -0
- package/dist/src/planner/check.js +12 -1
- package/dist/src/planner/plan.d.ts +1 -0
- package/dist/src/planner/plan.js +14 -26
- package/dist/src/preflight/config.d.ts +1 -0
- package/dist/src/preflight/config.js +43 -0
- package/dist/src/preflight/preflight.d.ts +2 -0
- package/dist/src/preflight/preflight.js +18 -2
- package/dist/src/preflight/requirements.d.ts +2 -2
- package/dist/src/preflight/requirements.js +16 -9
- package/dist/src/registry/npm-registry.d.ts +1 -1
- package/dist/src/registry/npm-registry.js +2 -1
- package/dist/src/security/lock.js +40 -2
- package/package.json +4 -4
- package/proflow.module.json +1 -1
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { PlatformError } from "../errors.js";
|
|
2
2
|
import { buildDependencyGraph, ModuleRefUnresolvedError, } from "../graph/graph.js";
|
|
3
3
|
import { dispatchLifecycle } from "../lifecycle/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { loadLatestVerification } from "../persistence/index.js";
|
|
5
|
+
import { checkConfigReadiness, checkConfigReality } from "./config.js";
|
|
5
6
|
import { probeAllRequirements } from "./requirements.js";
|
|
6
7
|
function compareRef(a, b) {
|
|
7
8
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
8
9
|
}
|
|
10
|
+
async function humanVerifiedModules(modules, paths) {
|
|
11
|
+
const verified = new Set();
|
|
12
|
+
if (paths === undefined)
|
|
13
|
+
return verified;
|
|
14
|
+
for (const module of modules) {
|
|
15
|
+
const latest = await loadLatestVerification(paths, module.moduleRef);
|
|
16
|
+
if (latest?.result === "PASS" &&
|
|
17
|
+
latest.moduleVersion === module.moduleVersion) {
|
|
18
|
+
verified.add(module.moduleRef);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return verified;
|
|
22
|
+
}
|
|
9
23
|
export async function runPreflight(modules, options = {}) {
|
|
10
24
|
const findings = [];
|
|
11
25
|
let dependency;
|
|
@@ -33,7 +47,9 @@ export async function runPreflight(modules, options = {}) {
|
|
|
33
47
|
}
|
|
34
48
|
}
|
|
35
49
|
findings.push(...checkConfigReadiness(modules, options.config));
|
|
36
|
-
|
|
50
|
+
findings.push(...(await checkConfigReality(modules, options.config)));
|
|
51
|
+
const verifiedHumanModules = await humanVerifiedModules(modules, options.paths);
|
|
52
|
+
const requirementProbes = await probeAllRequirements(modules, verifiedHumanModules);
|
|
37
53
|
for (const probe of requirementProbes) {
|
|
38
54
|
if (probe.status === "ACTION_REQUIRED") {
|
|
39
55
|
findings.push({
|
|
@@ -7,5 +7,5 @@ export interface RequirementProbe {
|
|
|
7
7
|
status: ProbeStatus;
|
|
8
8
|
message: string;
|
|
9
9
|
}
|
|
10
|
-
export declare function probeAllRequirements(modules: readonly ResolvedModule[]): Promise<RequirementProbe[]>;
|
|
11
|
-
export declare function probeRequirement(moduleRef: string, requirement: ModuleRequirement, modules: readonly ResolvedModule[]): Promise<RequirementProbe>;
|
|
10
|
+
export declare function probeAllRequirements(modules: readonly ResolvedModule[], humanVerifiedModuleRefs?: ReadonlySet<string>): Promise<RequirementProbe[]>;
|
|
11
|
+
export declare function probeRequirement(moduleRef: string, requirement: ModuleRequirement, modules: readonly ResolvedModule[], humanVerifiedModuleRefs?: ReadonlySet<string>): Promise<RequirementProbe>;
|
|
@@ -5,17 +5,17 @@ import { versionSatisfies } from "../modules.js";
|
|
|
5
5
|
function compareRef(a, b) {
|
|
6
6
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
7
7
|
}
|
|
8
|
-
export async function probeAllRequirements(modules) {
|
|
8
|
+
export async function probeAllRequirements(modules, humanVerifiedModuleRefs = new Set()) {
|
|
9
9
|
const sorted = [...modules].sort((a, b) => compareRef(a.moduleRef, b.moduleRef));
|
|
10
10
|
const results = [];
|
|
11
11
|
for (const module of sorted) {
|
|
12
12
|
for (const requirement of module.requirements) {
|
|
13
|
-
results.push(await probeRequirement(module.moduleRef, requirement, modules));
|
|
13
|
+
results.push(await probeRequirement(module.moduleRef, requirement, modules, humanVerifiedModuleRefs));
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
return results;
|
|
17
17
|
}
|
|
18
|
-
export async function probeRequirement(moduleRef, requirement, modules) {
|
|
18
|
+
export async function probeRequirement(moduleRef, requirement, modules, humanVerifiedModuleRefs = new Set()) {
|
|
19
19
|
switch (requirement.kind) {
|
|
20
20
|
case "runtime":
|
|
21
21
|
return probeRuntime(moduleRef, requirement);
|
|
@@ -30,12 +30,19 @@ export async function probeRequirement(moduleRef, requirement, modules) {
|
|
|
30
30
|
case "module-contract":
|
|
31
31
|
return probeModuleContract(moduleRef, requirement, modules);
|
|
32
32
|
case "human":
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
return humanVerifiedModuleRefs.has(moduleRef)
|
|
34
|
+
? {
|
|
35
|
+
moduleRef,
|
|
36
|
+
requirement,
|
|
37
|
+
status: "PASS",
|
|
38
|
+
message: `human prerequisite verified for ${moduleRef}`,
|
|
39
|
+
}
|
|
40
|
+
: {
|
|
41
|
+
moduleRef,
|
|
42
|
+
requirement,
|
|
43
|
+
status: "ACTION_REQUIRED",
|
|
44
|
+
message: requirement.action,
|
|
45
|
+
};
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
function probeRuntime(moduleRef, requirement) {
|
|
@@ -6,7 +6,7 @@ export interface NpmCommandResult {
|
|
|
6
6
|
stderr: string;
|
|
7
7
|
}
|
|
8
8
|
export interface NpmCommandRunner {
|
|
9
|
-
run(args: readonly string[], cwd: string): Promise<NpmCommandResult>;
|
|
9
|
+
run(args: readonly string[], cwd: string, timeoutMs?: number): Promise<NpmCommandResult>;
|
|
10
10
|
}
|
|
11
11
|
export interface RegistryModuleCandidate {
|
|
12
12
|
packageName: string;
|
|
@@ -12,12 +12,13 @@ export const PRO_FLOW_SCOPE = "@tomflow";
|
|
|
12
12
|
export const PRO_FLOW_PACKAGE_PREFIX = "@tomflow/proflow-";
|
|
13
13
|
export function systemNpmRunner() {
|
|
14
14
|
return {
|
|
15
|
-
async run(args, cwd) {
|
|
15
|
+
async run(args, cwd, timeoutMs) {
|
|
16
16
|
try {
|
|
17
17
|
const result = await execFileAsync("npm", [...args], {
|
|
18
18
|
cwd,
|
|
19
19
|
encoding: "utf8",
|
|
20
20
|
maxBuffer: 4 * 1024 * 1024,
|
|
21
|
+
...(timeoutMs === undefined ? {} : { timeout: timeoutMs }),
|
|
21
22
|
});
|
|
22
23
|
return { stdout: result.stdout, stderr: result.stderr };
|
|
23
24
|
}
|
|
@@ -45,6 +45,32 @@ function lockedError(existing) {
|
|
|
45
45
|
: `pid=${existing.pid} planRef=${existing.planRef} since=${existing.createdAt} fingerprint=${existing.workspaceFingerprint}`;
|
|
46
46
|
return new PlatformError("WORKSPACE_LOCKED", `workspace apply already in progress (${details})`);
|
|
47
47
|
}
|
|
48
|
+
function isProvablyDeadPid(pid) {
|
|
49
|
+
try {
|
|
50
|
+
process.kill(pid, 0);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return errorHasCode(error, "ESRCH");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function reclaimProvablyStaleLock(paths, expectedFingerprint) {
|
|
58
|
+
const existing = await readWorkspaceLock(paths);
|
|
59
|
+
if (existing === undefined ||
|
|
60
|
+
existing.workspaceFingerprint !== expectedFingerprint ||
|
|
61
|
+
!isProvablyDeadPid(existing.pid)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await unlink(workspaceLockPath(paths));
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (errorHasCode(error, "ENOENT"))
|
|
70
|
+
return true;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
48
74
|
// v1 single-process exclusive lock: O_EXCL create, no distributed coordination,
|
|
49
75
|
// and no reclaim of a lock that cannot be proven stale.
|
|
50
76
|
export async function acquireWorkspaceLock(paths, planRef) {
|
|
@@ -61,10 +87,22 @@ export async function acquireWorkspaceLock(paths, planRef) {
|
|
|
61
87
|
handle = await open(file, "wx", 0o644);
|
|
62
88
|
}
|
|
63
89
|
catch (error) {
|
|
64
|
-
if (errorHasCode(error, "EEXIST"))
|
|
90
|
+
if (!errorHasCode(error, "EEXIST"))
|
|
91
|
+
throw error;
|
|
92
|
+
if (await reclaimProvablyStaleLock(paths, record.workspaceFingerprint)) {
|
|
93
|
+
try {
|
|
94
|
+
handle = await open(file, "wx", 0o644);
|
|
95
|
+
}
|
|
96
|
+
catch (retryError) {
|
|
97
|
+
if (errorHasCode(retryError, "EEXIST")) {
|
|
98
|
+
throw lockedError(await readWorkspaceLock(paths));
|
|
99
|
+
}
|
|
100
|
+
throw retryError;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
65
104
|
throw lockedError(await readWorkspaceLock(paths));
|
|
66
105
|
}
|
|
67
|
-
throw error;
|
|
68
106
|
}
|
|
69
107
|
try {
|
|
70
108
|
await handle.writeFile(`${JSON.stringify(record, null, 2)}\n`, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomflow/proflow-platform-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@tomflow/proflow-module-contract": "^0.1.
|
|
24
|
+
"@tomflow/proflow-module-contract": "^0.1.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@tomflow/proflow-deployment-conformance": "^0.1.
|
|
28
|
-
"@tomflow/proflow-module-template": "^0.1.
|
|
27
|
+
"@tomflow/proflow-deployment-conformance": "^0.1.2",
|
|
28
|
+
"@tomflow/proflow-module-template": "^0.1.2"
|
|
29
29
|
},
|
|
30
30
|
"description": "Deterministic platform-level deployment discovery, planning, lifecycle and verification CLI.",
|
|
31
31
|
"keywords": [
|
package/proflow.module.json
CHANGED