@tomflow/proflow-platform-cli 0.1.15 → 0.1.17
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/DOCS.md +27 -0
- package/SETUP.md +7 -0
- package/dist/deployment/adapter.d.ts +58 -34
- package/dist/deployment/adapter.js +28 -12
- package/dist/deployment/descriptor.d.ts +5 -16
- package/dist/deployment/descriptor.js +10 -21
- package/dist/src/cli.js +131 -137
- package/dist/src/contracts.d.ts +0 -2
- package/dist/src/discovery/discover.d.ts +1 -3
- package/dist/src/discovery/discover.js +1 -10
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +0 -2
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.js +0 -1
- package/dist/src/lifecycle/dispatch.d.ts +4 -11
- package/dist/src/lifecycle/dispatch.js +28 -56
- package/dist/src/lifecycle/index.d.ts +4 -4
- package/dist/src/lifecycle/index.js +2 -2
- package/dist/src/lifecycle/thin.d.ts +19 -7
- package/dist/src/lifecycle/thin.js +80 -55
- package/dist/src/persistence/index.d.ts +0 -2
- package/dist/src/persistence/index.js +0 -1
- package/package.json +7 -5
- package/proflow.module.json +5 -20
- package/dist/src/binding/production-bindings.d.ts +0 -37
- package/dist/src/binding/production-bindings.js +0 -75
- package/dist/src/docs/aggregate.d.ts +0 -18
- package/dist/src/docs/aggregate.js +0 -44
- package/dist/src/docs/docs.d.ts +0 -16
- package/dist/src/docs/docs.js +0 -68
- package/dist/src/docs/index.d.ts +0 -2
- package/dist/src/docs/index.js +0 -1
- package/dist/src/persistence/config.d.ts +0 -6
- package/dist/src/persistence/config.js +0 -54
- package/dist/src/persistence/guards.d.ts +0 -1
- package/dist/src/persistence/guards.js +0 -7
package/dist/src/cli.js
CHANGED
|
@@ -3,28 +3,24 @@ import { readFile, realpath, stat } from "node:fs/promises";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { moduleStatusObservationSchema } from "@tomflow/proflow-module-contract";
|
|
5
5
|
import { descriptor as platformCliDescriptor } from "../deployment/descriptor.js";
|
|
6
|
-
import { buildProductionBindings, importRawAdapter, } from "./binding/production-bindings.js";
|
|
7
6
|
import { AutoModuleCatalog, discoverModules } from "./discovery/discover.js";
|
|
8
7
|
import { InstalledModuleCatalog } from "./discovery/installed.js";
|
|
9
|
-
import { aggregateModuleDocs } from "./docs/aggregate.js";
|
|
10
8
|
import { PlatformError } from "./errors.js";
|
|
11
9
|
import { observeWorkspaceInstalledVersion, removeWorkspacePackages, syncWorkspacePackages, } from "./install/package-manager.js";
|
|
12
|
-
import { observeStatuses,
|
|
13
|
-
import { workspacePaths } from "./paths.js";
|
|
14
|
-
import { loadConfig } from "./persistence/config.js";
|
|
10
|
+
import { installModulesThin, observeDocs, observeStatuses, setupModulesThin, startModulesThin, stopModulesThin, uninstallModulesThin, } from "./lifecycle/index.js";
|
|
15
11
|
import { ensureWorkspaceMetadata } from "./persistence/workspace-metadata.js";
|
|
16
12
|
import { discoverRegistryModules, PRO_FLOW_PACKAGE_PREFIX, } from "./registry/index.js";
|
|
17
13
|
const COMMANDS = [
|
|
18
|
-
"modules",
|
|
19
|
-
"docs",
|
|
20
14
|
"install",
|
|
21
15
|
"uninstall",
|
|
16
|
+
"status",
|
|
17
|
+
"setup",
|
|
18
|
+
"docs",
|
|
22
19
|
"start",
|
|
23
20
|
"stop",
|
|
24
21
|
];
|
|
25
22
|
function parseArgs(argv) {
|
|
26
|
-
let json = false;
|
|
27
|
-
let workspace;
|
|
23
|
+
let json = false, workspace, moduleRef, input, inputSeen = false;
|
|
28
24
|
let special;
|
|
29
25
|
const positional = [];
|
|
30
26
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -35,24 +31,33 @@ function parseArgs(argv) {
|
|
|
35
31
|
json = true;
|
|
36
32
|
continue;
|
|
37
33
|
}
|
|
38
|
-
if (value === "--workspace"
|
|
34
|
+
if (value === "--workspace" ||
|
|
35
|
+
value === "--module" ||
|
|
36
|
+
value === "--input") {
|
|
39
37
|
const next = argv[index + 1];
|
|
40
|
-
if (!next || next.startsWith("-"))
|
|
41
|
-
throw new PlatformError("INVALID_REQUEST",
|
|
38
|
+
if (!next || (value !== "--input" && next.startsWith("-")))
|
|
39
|
+
throw new PlatformError("INVALID_REQUEST", `${value} requires a value`);
|
|
40
|
+
if (value === "--workspace")
|
|
41
|
+
workspace = next;
|
|
42
|
+
else if (value === "--module")
|
|
43
|
+
moduleRef = next;
|
|
44
|
+
else {
|
|
45
|
+
try {
|
|
46
|
+
input = JSON.parse(next);
|
|
47
|
+
inputSeen = true;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new PlatformError("INVALID_REQUEST", `--input must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
42
52
|
}
|
|
43
|
-
workspace = next;
|
|
44
53
|
index += 1;
|
|
45
54
|
continue;
|
|
46
55
|
}
|
|
47
56
|
if (value === "--help" || value === "-h") {
|
|
48
|
-
if (special !== undefined && special !== "help")
|
|
49
|
-
throw new PlatformError("INVALID_REQUEST", "help and version flags cannot be combined");
|
|
50
57
|
special = "help";
|
|
51
58
|
continue;
|
|
52
59
|
}
|
|
53
60
|
if (value === "--version" || value === "-v") {
|
|
54
|
-
if (special !== undefined && special !== "version")
|
|
55
|
-
throw new PlatformError("INVALID_REQUEST", "help and version flags cannot be combined");
|
|
56
61
|
special = "version";
|
|
57
62
|
continue;
|
|
58
63
|
}
|
|
@@ -61,15 +66,15 @@ function parseArgs(argv) {
|
|
|
61
66
|
positional.push(value);
|
|
62
67
|
}
|
|
63
68
|
if (special !== undefined) {
|
|
64
|
-
if (workspace !== undefined ||
|
|
65
|
-
|
|
69
|
+
if (workspace !== undefined ||
|
|
70
|
+
moduleRef !== undefined ||
|
|
71
|
+
inputSeen ||
|
|
72
|
+
positional.length > 0)
|
|
73
|
+
throw new PlatformError("INVALID_REQUEST", `${special} flag cannot be combined with command options`);
|
|
66
74
|
return { command: special, json };
|
|
67
75
|
}
|
|
68
|
-
if (positional.length === 0)
|
|
69
|
-
if (workspace !== undefined)
|
|
70
|
-
throw new PlatformError("INVALID_REQUEST", "--workspace is only valid with install");
|
|
76
|
+
if (positional.length === 0)
|
|
71
77
|
return { command: "help", json };
|
|
72
|
-
}
|
|
73
78
|
if (positional.length !== 1)
|
|
74
79
|
throw new PlatformError("INVALID_REQUEST", "commands accept no positional arguments");
|
|
75
80
|
const raw = positional[0] ?? "";
|
|
@@ -78,9 +83,17 @@ function parseArgs(argv) {
|
|
|
78
83
|
const command = raw;
|
|
79
84
|
if (workspace !== undefined && command !== "install")
|
|
80
85
|
throw new PlatformError("INVALID_REQUEST", "--workspace is only valid with install");
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
if ((moduleRef !== undefined || inputSeen) && command !== "setup")
|
|
87
|
+
throw new PlatformError("INVALID_REQUEST", "--module/--input are only valid with setup");
|
|
88
|
+
if (inputSeen && moduleRef === undefined)
|
|
89
|
+
throw new PlatformError("INVALID_REQUEST", "--input requires --module");
|
|
90
|
+
return {
|
|
91
|
+
command,
|
|
92
|
+
json,
|
|
93
|
+
...(workspace === undefined ? {} : { workspace }),
|
|
94
|
+
...(moduleRef === undefined ? {} : { moduleRef }),
|
|
95
|
+
...(inputSeen ? { input } : {}),
|
|
96
|
+
};
|
|
84
97
|
}
|
|
85
98
|
function outcome(command, status, workspaceRoot, data) {
|
|
86
99
|
return {
|
|
@@ -99,32 +112,12 @@ async function canonicalWorkspace(cwd, explicit) {
|
|
|
99
112
|
catch (error) {
|
|
100
113
|
throw new PlatformError("WORKSPACE_NOT_FOUND", `Workspace does not exist: ${candidate} (${error instanceof Error ? error.message : String(error)})`);
|
|
101
114
|
}
|
|
102
|
-
if (!info.isDirectory())
|
|
115
|
+
if (!info.isDirectory())
|
|
103
116
|
throw new PlatformError("WORKSPACE_NOT_FOUND", `Workspace is not a directory: ${candidate}`);
|
|
104
|
-
}
|
|
105
117
|
return realpath(candidate);
|
|
106
118
|
}
|
|
107
|
-
async function loadConfigMap(root, modules) {
|
|
108
|
-
const paths = workspacePaths(root);
|
|
109
|
-
const configByModuleRef = new Map();
|
|
110
|
-
for (const module of modules) {
|
|
111
|
-
const config = await loadConfig(paths, module.moduleRef);
|
|
112
|
-
if (config === undefined)
|
|
113
|
-
continue;
|
|
114
|
-
configByModuleRef.set(module.moduleRef, config);
|
|
115
|
-
}
|
|
116
|
-
return configByModuleRef;
|
|
117
|
-
}
|
|
118
119
|
async function buildContext(root) {
|
|
119
|
-
const
|
|
120
|
-
const configByModuleRef = await loadConfigMap(root, discovered);
|
|
121
|
-
const bindings = await buildProductionBindings({
|
|
122
|
-
workspaceRoot: root,
|
|
123
|
-
modules: discovered,
|
|
124
|
-
configByModuleRef,
|
|
125
|
-
importAdapter: (packageName, source) => importRawAdapter(packageName, source, root),
|
|
126
|
-
});
|
|
127
|
-
const catalog = new AutoModuleCatalog(root, bindings);
|
|
120
|
+
const catalog = new AutoModuleCatalog(root);
|
|
128
121
|
const modules = await discoverModules({ workspaceRoot: root, catalog });
|
|
129
122
|
return { catalog, modules };
|
|
130
123
|
}
|
|
@@ -137,62 +130,62 @@ function statusFromModule(value) {
|
|
|
137
130
|
? "BLOCKED"
|
|
138
131
|
: "FAILED";
|
|
139
132
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
function batchStatus(result) {
|
|
134
|
+
if (result.completed)
|
|
135
|
+
return "SUCCEEDED";
|
|
136
|
+
if (result.blockedBy)
|
|
137
|
+
return result.blockedBy.setupStatus === "ACTION_REQUIRED"
|
|
138
|
+
? "ACTION_REQUIRED"
|
|
139
|
+
: "FAILED";
|
|
140
|
+
return statusFromModule(result.results.at(-1)?.result.status ?? "FAILED");
|
|
145
141
|
}
|
|
146
|
-
async function
|
|
142
|
+
async function handleStatus(root) {
|
|
147
143
|
const { catalog, modules } = await buildContext(root);
|
|
148
|
-
const observed = await observeStatuses(catalog, modules);
|
|
144
|
+
const observed = await observeStatuses(catalog, modules, root);
|
|
149
145
|
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
150
146
|
const output = observed.map((item) => {
|
|
151
|
-
if (item.result.status !== "SUCCEEDED")
|
|
147
|
+
if (item.result.status !== "SUCCEEDED")
|
|
152
148
|
throw new PlatformError("COMMAND_FAILED", `module ${item.moduleRef} status observation did not return SUCCEEDED`);
|
|
153
|
-
}
|
|
154
149
|
const parsed = moduleStatusObservationSchema.safeParse(item.result.data);
|
|
155
|
-
if (!parsed.success)
|
|
156
|
-
throw new PlatformError("COMMAND_FAILED", `module ${item.moduleRef} returned
|
|
157
|
-
}
|
|
150
|
+
if (!parsed.success)
|
|
151
|
+
throw new PlatformError("COMMAND_FAILED", `module ${item.moduleRef} returned invalid status: ${parsed.error.message}`);
|
|
158
152
|
const module = byRef.get(item.moduleRef);
|
|
159
|
-
if (module
|
|
153
|
+
if (!module)
|
|
160
154
|
throw new PlatformError("COMMAND_FAILED", `unknown module ${item.moduleRef}`);
|
|
161
155
|
return {
|
|
162
156
|
moduleRef: item.moduleRef,
|
|
163
157
|
version: module.moduleVersion,
|
|
164
|
-
|
|
165
|
-
...(parsed.data.missingConfig === undefined
|
|
166
|
-
? {}
|
|
167
|
-
: { missingConfig: parsed.data.missingConfig }),
|
|
158
|
+
setupStatus: parsed.data.setupStatus,
|
|
168
159
|
runtimeStatus: parsed.data.runtimeStatus,
|
|
169
160
|
};
|
|
170
161
|
});
|
|
171
|
-
return outcome("
|
|
162
|
+
return outcome("status", "SUCCEEDED", root, { modules: output });
|
|
172
163
|
}
|
|
173
164
|
async function handleDocs(root) {
|
|
174
|
-
const catalog =
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
return outcome("docs", "SUCCEEDED", root, {
|
|
165
|
+
const { catalog, modules } = await buildContext(root);
|
|
166
|
+
const docs = await observeDocs(catalog, modules, root);
|
|
167
|
+
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
168
|
+
return outcome("docs", "SUCCEEDED", root, {
|
|
169
|
+
modules: docs.map((item) => ({
|
|
170
|
+
moduleRef: item.moduleRef,
|
|
171
|
+
version: byRef.get(item.moduleRef)?.moduleVersion,
|
|
172
|
+
docs: item.result.data,
|
|
173
|
+
})),
|
|
174
|
+
});
|
|
178
175
|
}
|
|
179
176
|
async function validateInstalledPackageSet(root, candidates, previousManaged) {
|
|
180
177
|
const expectedNames = candidates.map((item) => item.packageName).sort();
|
|
181
178
|
const declaredNames = await workspaceProFlowDependencies(root);
|
|
182
|
-
if (JSON.stringify(declaredNames) !== JSON.stringify(expectedNames))
|
|
179
|
+
if (JSON.stringify(declaredNames) !== JSON.stringify(expectedNames))
|
|
183
180
|
throw new PlatformError("COMMAND_FAILED", `managed dependency set mismatch: expected ${expectedNames.join(", ")}, observed ${declaredNames.join(", ")}`);
|
|
184
|
-
}
|
|
185
181
|
for (const candidate of candidates) {
|
|
186
182
|
const observed = await observeWorkspaceInstalledVersion(root, candidate.packageName);
|
|
187
|
-
if (observed !== candidate.moduleVersion)
|
|
183
|
+
if (observed !== candidate.moduleVersion)
|
|
188
184
|
throw new PlatformError("COMMAND_FAILED", `installed version mismatch for ${candidate.packageName}: expected ${candidate.moduleVersion}, observed ${observed ?? "missing"}`);
|
|
189
|
-
}
|
|
190
185
|
}
|
|
191
|
-
for (const stale of previousManaged.filter((name) => !expectedNames.includes(name)))
|
|
192
|
-
if ((await observeWorkspaceInstalledVersion(root, stale)) !== undefined)
|
|
186
|
+
for (const stale of previousManaged.filter((name) => !expectedNames.includes(name)))
|
|
187
|
+
if ((await observeWorkspaceInstalledVersion(root, stale)) !== undefined)
|
|
193
188
|
throw new PlatformError("COMMAND_FAILED", `stale managed package remains installed after synchronization: ${stale}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
189
|
const catalog = new InstalledModuleCatalog(root);
|
|
197
190
|
const sources = await catalog.sources();
|
|
198
191
|
const modules = await discoverModules({ catalog, sources });
|
|
@@ -200,9 +193,8 @@ async function validateInstalledPackageSet(root, candidates, previousManaged) {
|
|
|
200
193
|
for (const candidate of candidates) {
|
|
201
194
|
const module = byPackage.get(candidate.packageName);
|
|
202
195
|
if (module === undefined ||
|
|
203
|
-
module.moduleVersion !== candidate.moduleVersion)
|
|
196
|
+
module.moduleVersion !== candidate.moduleVersion)
|
|
204
197
|
throw new PlatformError("DESCRIPTOR_INVALID", `installed descriptor mismatch for ${candidate.packageName}@${candidate.moduleVersion}`);
|
|
205
|
-
}
|
|
206
198
|
}
|
|
207
199
|
}
|
|
208
200
|
async function handleInstall(root, runtime) {
|
|
@@ -212,12 +204,10 @@ async function handleInstall(root, runtime) {
|
|
|
212
204
|
? {}
|
|
213
205
|
: { runner: runtime.registryRunner }),
|
|
214
206
|
});
|
|
215
|
-
if (discovered.rejected.length > 0)
|
|
207
|
+
if (discovered.rejected.length > 0)
|
|
216
208
|
throw new PlatformError("REGISTRY_RESPONSE_INVALID", `registry contains rejected ProFlow packages: ${discovered.rejected.map((item) => `${item.packageName}:${item.reason}`).join(", ")}`);
|
|
217
|
-
|
|
218
|
-
if (discovered.candidates.length === 0) {
|
|
209
|
+
if (discovered.candidates.length === 0)
|
|
219
210
|
throw new PlatformError("PACKAGE_NOT_FOUND", "no ProFlow packages were discovered in the configured scope");
|
|
220
|
-
}
|
|
221
211
|
const previousManaged = await workspaceProFlowDependencies(root);
|
|
222
212
|
const mutation = await syncWorkspacePackages({
|
|
223
213
|
workspaceRoot: root,
|
|
@@ -234,7 +224,9 @@ async function handleInstall(root, runtime) {
|
|
|
234
224
|
});
|
|
235
225
|
await validateInstalledPackageSet(root, discovered.candidates, previousManaged);
|
|
236
226
|
const metadata = await ensureWorkspaceMetadata(root);
|
|
237
|
-
|
|
227
|
+
const { catalog, modules } = await buildContext(root);
|
|
228
|
+
const moduleInstall = await installModulesThin(catalog, modules, root);
|
|
229
|
+
return outcome("install", batchStatus(moduleInstall), root, {
|
|
238
230
|
registry: discovered.registry,
|
|
239
231
|
packageManager: mutation.packageManager,
|
|
240
232
|
packages: discovered.candidates.map((item) => ({
|
|
@@ -242,7 +234,8 @@ async function handleInstall(root, runtime) {
|
|
|
242
234
|
version: item.moduleVersion,
|
|
243
235
|
})),
|
|
244
236
|
workspace: metadata,
|
|
245
|
-
|
|
237
|
+
modules: moduleInstall,
|
|
238
|
+
next: "platform status",
|
|
246
239
|
});
|
|
247
240
|
}
|
|
248
241
|
async function workspaceProFlowDependencies(root) {
|
|
@@ -256,10 +249,9 @@ async function workspaceProFlowDependencies(root) {
|
|
|
256
249
|
const value = record[field];
|
|
257
250
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
258
251
|
continue;
|
|
259
|
-
for (const name of Object.keys(value))
|
|
252
|
+
for (const name of Object.keys(value))
|
|
260
253
|
if (name.startsWith(PRO_FLOW_PACKAGE_PREFIX))
|
|
261
254
|
names.add(name);
|
|
262
|
-
}
|
|
263
255
|
}
|
|
264
256
|
return [...names].sort();
|
|
265
257
|
}
|
|
@@ -273,6 +265,13 @@ async function workspaceProFlowDependencies(root) {
|
|
|
273
265
|
}
|
|
274
266
|
async function handleUninstall(root, runtime) {
|
|
275
267
|
const packageNames = await workspaceProFlowDependencies(root);
|
|
268
|
+
const { catalog, modules } = await buildContext(root);
|
|
269
|
+
const moduleUninstall = await uninstallModulesThin(catalog, modules, root);
|
|
270
|
+
if (!moduleUninstall.completed)
|
|
271
|
+
return outcome("uninstall", batchStatus(moduleUninstall), root, {
|
|
272
|
+
modules: moduleUninstall,
|
|
273
|
+
removed: [],
|
|
274
|
+
});
|
|
276
275
|
const mutation = await removeWorkspacePackages({
|
|
277
276
|
workspaceRoot: root,
|
|
278
277
|
packageNames,
|
|
@@ -284,26 +283,39 @@ async function handleUninstall(root, runtime) {
|
|
|
284
283
|
: { executableAvailable: runtime.executableAvailable }),
|
|
285
284
|
});
|
|
286
285
|
return outcome("uninstall", "SUCCEEDED", root, {
|
|
286
|
+
modules: moduleUninstall,
|
|
287
287
|
packageManager: mutation.packageManager,
|
|
288
288
|
removed: packageNames,
|
|
289
289
|
preserved: [".proflow"],
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
|
+
async function handleSetup(root, parsed) {
|
|
293
|
+
const { catalog, modules } = await buildContext(root);
|
|
294
|
+
const target = parsed.moduleRef === undefined
|
|
295
|
+
? undefined
|
|
296
|
+
: {
|
|
297
|
+
moduleRef: parsed.moduleRef,
|
|
298
|
+
...(Object.hasOwn(parsed, "input") ? { input: parsed.input } : {}),
|
|
299
|
+
};
|
|
300
|
+
const result = await setupModulesThin(catalog, modules, root, target);
|
|
301
|
+
return outcome("setup", batchStatus(result), root, result);
|
|
302
|
+
}
|
|
292
303
|
async function handleStart(root) {
|
|
293
304
|
const { catalog, modules } = await buildContext(root);
|
|
294
|
-
const result = await
|
|
295
|
-
return outcome("start",
|
|
305
|
+
const result = await startModulesThin(catalog, modules, root);
|
|
306
|
+
return outcome("start", batchStatus(result), root, result);
|
|
296
307
|
}
|
|
297
308
|
async function handleStop(root) {
|
|
298
309
|
const { catalog, modules } = await buildContext(root);
|
|
299
|
-
const result = await stopModulesThin(catalog, modules);
|
|
300
|
-
return outcome("stop",
|
|
310
|
+
const result = await stopModulesThin(catalog, modules, root);
|
|
311
|
+
return outcome("stop", batchStatus(result), root, result);
|
|
301
312
|
}
|
|
302
313
|
function helpOutcome() {
|
|
303
314
|
return outcome("help", "SUCCEEDED", undefined, {
|
|
304
|
-
usage: "platform <
|
|
315
|
+
usage: "platform <install|uninstall|status|setup|docs|start|stop> [--json]",
|
|
305
316
|
commands: [...COMMANDS],
|
|
306
317
|
install: "platform install [--workspace <path>]",
|
|
318
|
+
setup: "platform setup [--module <moduleRef> --input '<json>']",
|
|
307
319
|
});
|
|
308
320
|
}
|
|
309
321
|
export async function runCli(argv, runtime = {}) {
|
|
@@ -317,24 +329,25 @@ export async function runCli(argv, runtime = {}) {
|
|
|
317
329
|
try {
|
|
318
330
|
if (parsed.command === "help")
|
|
319
331
|
return JSON.stringify(helpOutcome());
|
|
320
|
-
if (parsed.command === "version")
|
|
332
|
+
if (parsed.command === "version")
|
|
321
333
|
return JSON.stringify(outcome("version", "SUCCEEDED", undefined, {
|
|
322
334
|
version: platformCliDescriptor.moduleVersion,
|
|
323
335
|
}));
|
|
324
|
-
}
|
|
325
336
|
const cwd = await canonicalWorkspace(runtime.cwd ?? process.cwd());
|
|
326
337
|
const root = parsed.command === "install" && parsed.workspace !== undefined
|
|
327
338
|
? await canonicalWorkspace(cwd, parsed.workspace)
|
|
328
339
|
: cwd;
|
|
329
340
|
switch (parsed.command) {
|
|
330
|
-
case "modules":
|
|
331
|
-
return JSON.stringify(await handleModules(root));
|
|
332
|
-
case "docs":
|
|
333
|
-
return JSON.stringify(await handleDocs(root));
|
|
334
341
|
case "install":
|
|
335
342
|
return JSON.stringify(await handleInstall(root, runtime));
|
|
336
343
|
case "uninstall":
|
|
337
344
|
return JSON.stringify(await handleUninstall(root, runtime));
|
|
345
|
+
case "status":
|
|
346
|
+
return JSON.stringify(await handleStatus(root));
|
|
347
|
+
case "setup":
|
|
348
|
+
return JSON.stringify(await handleSetup(root, parsed));
|
|
349
|
+
case "docs":
|
|
350
|
+
return JSON.stringify(await handleDocs(root));
|
|
338
351
|
case "start":
|
|
339
352
|
return JSON.stringify(await handleStart(root));
|
|
340
353
|
case "stop":
|
|
@@ -346,13 +359,12 @@ export async function runCli(argv, runtime = {}) {
|
|
|
346
359
|
}
|
|
347
360
|
}
|
|
348
361
|
function errorOutcome(command, error) {
|
|
349
|
-
if (error instanceof PlatformError)
|
|
362
|
+
if (error instanceof PlatformError)
|
|
350
363
|
return {
|
|
351
364
|
command,
|
|
352
365
|
status: "FAILED",
|
|
353
366
|
error: { code: error.code, message: error.message },
|
|
354
367
|
};
|
|
355
|
-
}
|
|
356
368
|
return {
|
|
357
369
|
command,
|
|
358
370
|
status: "FAILED",
|
|
@@ -362,62 +374,44 @@ function errorOutcome(command, error) {
|
|
|
362
374
|
},
|
|
363
375
|
};
|
|
364
376
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
function renderModules(data) {
|
|
377
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
378
|
+
function renderStatus(data) {
|
|
369
379
|
if (!isRecord(data) || !Array.isArray(data.modules))
|
|
370
380
|
return "No modules.";
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
.join(",")
|
|
379
|
-
: "-";
|
|
380
|
-
lines.push(`${String(raw.moduleRef)} ${String(raw.version)} config=${String(raw.configStatus)} runtime=${String(raw.runtimeStatus)} missing=${missing}`);
|
|
381
|
-
}
|
|
382
|
-
return lines.join("\n");
|
|
381
|
+
return [
|
|
382
|
+
"ProFlow Modules",
|
|
383
|
+
"",
|
|
384
|
+
...data.modules
|
|
385
|
+
.filter(isRecord)
|
|
386
|
+
.map((raw) => `${String(raw.moduleRef)} ${String(raw.version)} setup=${String(raw.setupStatus)} runtime=${String(raw.runtimeStatus)}`),
|
|
387
|
+
].join("\n");
|
|
383
388
|
}
|
|
384
389
|
function renderDocs(data) {
|
|
385
390
|
if (!isRecord(data) || !Array.isArray(data.modules))
|
|
386
391
|
return "No module docs.";
|
|
387
392
|
const lines = ["ProFlow Docs"];
|
|
388
|
-
for (const raw of data.modules)
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
lines.push("", `## ${String(raw.moduleRef)} @ ${String(raw.version)}`);
|
|
392
|
-
if (!Array.isArray(raw.documents))
|
|
393
|
-
continue;
|
|
394
|
-
for (const document of raw.documents) {
|
|
395
|
-
if (!isRecord(document))
|
|
396
|
-
continue;
|
|
397
|
-
lines.push("", `### ${String(document.id)}`, String(document.content ?? ""));
|
|
398
|
-
}
|
|
399
|
-
}
|
|
393
|
+
for (const raw of data.modules)
|
|
394
|
+
if (isRecord(raw))
|
|
395
|
+
lines.push("", `## ${String(raw.moduleRef)} @ ${String(raw.version)}`, JSON.stringify(raw.docs ?? {}, null, 2));
|
|
400
396
|
return lines.join("\n");
|
|
401
397
|
}
|
|
402
398
|
export function renderHumanResult(result) {
|
|
403
|
-
if (result.status === "FAILED")
|
|
399
|
+
if (result.status === "FAILED")
|
|
404
400
|
return `${result.command.toUpperCase()} FAILED${result.error ? ` [${result.error.code}] ${result.error.message}` : ""}`;
|
|
405
|
-
|
|
406
|
-
if (result.command === "help") {
|
|
401
|
+
if (result.command === "help")
|
|
407
402
|
return [
|
|
408
403
|
"ProFlow Platform CLI",
|
|
409
404
|
"",
|
|
410
405
|
...COMMANDS.map((command) => `platform ${command}`),
|
|
411
406
|
"",
|
|
412
407
|
"platform install --workspace <path>",
|
|
408
|
+
"platform setup --module <moduleRef> --input '<json>'",
|
|
413
409
|
"append --json for machine-readable output",
|
|
414
410
|
].join("\n");
|
|
415
|
-
|
|
416
|
-
if (result.command === "version" && isRecord(result.data)) {
|
|
411
|
+
if (result.command === "version" && isRecord(result.data))
|
|
417
412
|
return String(result.data.version ?? platformCliDescriptor.moduleVersion);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return renderModules(result.data);
|
|
413
|
+
if (result.command === "status")
|
|
414
|
+
return renderStatus(result.data);
|
|
421
415
|
if (result.command === "docs")
|
|
422
416
|
return renderDocs(result.data);
|
|
423
417
|
return [
|
package/dist/src/contracts.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ export interface ResolvedModule {
|
|
|
10
10
|
requires: ModuleDescriptor["requires"];
|
|
11
11
|
requirements: ModuleRequirement[];
|
|
12
12
|
configSlots: ConfigSlot[];
|
|
13
|
-
lifecycle: string[];
|
|
14
|
-
verification: ModuleDescriptor["verification"];
|
|
15
13
|
effects: DeploymentEffect[];
|
|
16
14
|
source: {
|
|
17
15
|
type: "workspace" | "installed";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { DeploymentAdapterBinding } from "../binding/production-bindings.ts";
|
|
2
1
|
import type { ResolvedModule } from "../contracts.ts";
|
|
3
2
|
import type { ModuleCatalog, ModuleSource } from "../modules.ts";
|
|
4
3
|
export interface DiscoverOptions {
|
|
@@ -9,8 +8,7 @@ export interface DiscoverOptions {
|
|
|
9
8
|
export declare class AutoModuleCatalog implements ModuleCatalog {
|
|
10
9
|
private readonly workspace;
|
|
11
10
|
private readonly installed;
|
|
12
|
-
|
|
13
|
-
constructor(root?: string, bindings?: ReadonlyMap<string, DeploymentAdapterBinding>);
|
|
11
|
+
constructor(root?: string);
|
|
14
12
|
sources(): Promise<ModuleSource[]>;
|
|
15
13
|
loadDescriptor(source: ModuleSource): Promise<unknown>;
|
|
16
14
|
loadAdapter(source: ModuleSource): Promise<unknown>;
|
|
@@ -7,11 +7,9 @@ import { readPackageJson } from "./workspace.js";
|
|
|
7
7
|
export class AutoModuleCatalog {
|
|
8
8
|
workspace;
|
|
9
9
|
installed;
|
|
10
|
-
|
|
11
|
-
constructor(root, bindings) {
|
|
10
|
+
constructor(root) {
|
|
12
11
|
this.workspace = new WorkspaceModuleCatalog(root);
|
|
13
12
|
this.installed = new InstalledModuleCatalog(root);
|
|
14
|
-
this.bindings = bindings ?? new Map();
|
|
15
13
|
}
|
|
16
14
|
async sources() {
|
|
17
15
|
const workspaceSources = await this.workspace.sources();
|
|
@@ -32,11 +30,6 @@ export class AutoModuleCatalog {
|
|
|
32
30
|
: this.installed.loadDescriptor(source);
|
|
33
31
|
}
|
|
34
32
|
async loadAdapter(source) {
|
|
35
|
-
// A production binder may supply a bound adapter for a service module
|
|
36
|
-
// (createBehaviorAdapter(realService)); it wins over the unbound default.
|
|
37
|
-
const binding = this.bindings.get(source.packageName);
|
|
38
|
-
if (binding !== undefined)
|
|
39
|
-
return binding;
|
|
40
33
|
return source.type === "workspace"
|
|
41
34
|
? this.workspace.loadAdapter(source)
|
|
42
35
|
: this.installed.loadAdapter(source);
|
|
@@ -96,8 +89,6 @@ function toResolvedModule(descriptor, source) {
|
|
|
96
89
|
requires: descriptor.requires,
|
|
97
90
|
requirements: descriptor.requirements,
|
|
98
91
|
configSlots: descriptor.configSlots,
|
|
99
|
-
lifecycle: descriptor.lifecycle.supported,
|
|
100
|
-
verification: descriptor.verification,
|
|
101
92
|
effects: descriptor.effects,
|
|
102
93
|
source: resolvedSource,
|
|
103
94
|
};
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const platformErrorCodes: readonly ["INVALID_REQUEST", "DESCRIPTOR_INVALID", "DUPLICATE_IDENTITY", "DEPENDENCY_UNRESOLVED", "DEPENDENCY_INCOMPATIBLE", "DEPENDENCY_CYCLE", "
|
|
1
|
+
export declare const platformErrorCodes: readonly ["INVALID_REQUEST", "DESCRIPTOR_INVALID", "DUPLICATE_IDENTITY", "DEPENDENCY_UNRESOLVED", "DEPENDENCY_INCOMPATIBLE", "DEPENDENCY_CYCLE", "WORKSPACE_NOT_FOUND", "WORKSPACE_INSTANCE_INVALID", "PACKAGE_MANAGER_UNSUPPORTED", "PACKAGE_MANAGER_CONFLICT", "PACKAGE_MANAGER_UNAVAILABLE", "UNINSTALL_FAILED", "REGISTRY_UNAVAILABLE", "REGISTRY_AUTH_REQUIRED", "REGISTRY_RESPONSE_INVALID", "PACKAGE_NOT_FOUND", "PACKAGE_NOT_PROFLOW", "COMMAND_FAILED"];
|
|
2
2
|
export type PlatformErrorCode = (typeof platformErrorCodes)[number];
|
|
3
3
|
export declare class PlatformError extends Error {
|
|
4
4
|
readonly code: PlatformErrorCode;
|
package/dist/src/errors.js
CHANGED
|
@@ -5,13 +5,11 @@ export const platformErrorCodes = [
|
|
|
5
5
|
"DEPENDENCY_UNRESOLVED",
|
|
6
6
|
"DEPENDENCY_INCOMPATIBLE",
|
|
7
7
|
"DEPENDENCY_CYCLE",
|
|
8
|
-
"CONFIG_INVALID",
|
|
9
8
|
"WORKSPACE_NOT_FOUND",
|
|
10
9
|
"WORKSPACE_INSTANCE_INVALID",
|
|
11
10
|
"PACKAGE_MANAGER_UNSUPPORTED",
|
|
12
11
|
"PACKAGE_MANAGER_CONFLICT",
|
|
13
12
|
"PACKAGE_MANAGER_UNAVAILABLE",
|
|
14
|
-
"LIFECYCLE_UNSUPPORTED",
|
|
15
13
|
"UNINSTALL_FAILED",
|
|
16
14
|
"REGISTRY_UNAVAILABLE",
|
|
17
15
|
"REGISTRY_AUTH_REQUIRED",
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export type { CliOutcome, CliRuntimeOptions, CliStatus, } from "./cli.ts";
|
|
2
2
|
export { renderHumanResult, runCli } from "./cli.ts";
|
|
3
3
|
export { AutoModuleCatalog, discoverModules } from "./discovery/discover.ts";
|
|
4
|
-
export type { AggregatedDocument, AggregatedModuleDocs, } from "./docs/aggregate.ts";
|
|
5
|
-
export { aggregateModuleDocs } from "./docs/aggregate.ts";
|
|
6
4
|
export type { PlatformErrorCode } from "./errors.ts";
|
|
7
5
|
export { PlatformError } from "./errors.ts";
|
|
8
6
|
export type { ModuleCatalog, ModuleSource } from "./modules.ts";
|
package/dist/src/index.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ModuleCommandContext, type ModuleManagementCommand, type ModuleOperationResult } from "@tomflow/proflow-module-contract";
|
|
2
2
|
import type { ResolvedModule } from "../contracts.ts";
|
|
3
3
|
import type { ModuleCatalog } from "../modules.ts";
|
|
4
|
-
export interface
|
|
4
|
+
export interface ModuleDispatchResult {
|
|
5
5
|
moduleRef: string;
|
|
6
|
-
|
|
6
|
+
command: ModuleManagementCommand;
|
|
7
7
|
result: ModuleOperationResult;
|
|
8
8
|
observedEffects: string[];
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
* Dispatches a single lifecycle primitive against one module through its
|
|
12
|
-
* public deployment adapter. The descriptor is the source of truth for what is
|
|
13
|
-
* supported: a primitive not declared in `lifecycle` is rejected with
|
|
14
|
-
* `LIFECYCLE_UNSUPPORTED` rather than faked. The adapter result is always
|
|
15
|
-
* runtime-validated against the Module Operation Result schema.
|
|
16
|
-
*/
|
|
17
|
-
export declare function dispatchLifecycle(catalog: ModuleCatalog, module: ResolvedModule, primitive: LifecyclePrimitive): Promise<LifecycleDispatchResult>;
|
|
10
|
+
export declare function dispatchModuleCommand(catalog: ModuleCatalog, module: ResolvedModule, command: ModuleManagementCommand, context: ModuleCommandContext): Promise<ModuleDispatchResult>;
|