@tomflow/proflow-platform-cli 0.1.16 → 0.1.18

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 (37) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/DOCS.md +27 -0
  3. package/SETUP.md +27 -0
  4. package/dist/deployment/adapter.d.ts +58 -34
  5. package/dist/deployment/adapter.js +28 -12
  6. package/dist/deployment/descriptor.d.ts +5 -16
  7. package/dist/deployment/descriptor.js +10 -21
  8. package/dist/src/cli.js +173 -140
  9. package/dist/src/contracts.d.ts +0 -2
  10. package/dist/src/discovery/discover.d.ts +1 -3
  11. package/dist/src/discovery/discover.js +1 -10
  12. package/dist/src/errors.d.ts +1 -1
  13. package/dist/src/errors.js +0 -2
  14. package/dist/src/index.d.ts +0 -2
  15. package/dist/src/index.js +0 -1
  16. package/dist/src/lifecycle/dispatch.d.ts +4 -11
  17. package/dist/src/lifecycle/dispatch.js +28 -56
  18. package/dist/src/lifecycle/index.d.ts +4 -4
  19. package/dist/src/lifecycle/index.js +2 -2
  20. package/dist/src/lifecycle/thin.d.ts +19 -7
  21. package/dist/src/lifecycle/thin.js +83 -53
  22. package/dist/src/persistence/index.d.ts +0 -2
  23. package/dist/src/persistence/index.js +0 -1
  24. package/package.json +7 -5
  25. package/proflow.module.json +5 -20
  26. package/dist/src/binding/production-bindings.d.ts +0 -37
  27. package/dist/src/binding/production-bindings.js +0 -75
  28. package/dist/src/docs/aggregate.d.ts +0 -18
  29. package/dist/src/docs/aggregate.js +0 -44
  30. package/dist/src/docs/docs.d.ts +0 -16
  31. package/dist/src/docs/docs.js +0 -68
  32. package/dist/src/docs/index.d.ts +0 -2
  33. package/dist/src/docs/index.js +0 -1
  34. package/dist/src/persistence/config.d.ts +0 -6
  35. package/dist/src/persistence/config.js +0 -54
  36. package/dist/src/persistence/guards.d.ts +0 -1
  37. 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, preflightAndStartModules, stopModulesThin, } from "./lifecycle/thin.js";
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", "--workspace requires a path");
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,26 +66,32 @@ function parseArgs(argv) {
61
66
  positional.push(value);
62
67
  }
63
68
  if (special !== undefined) {
64
- if (workspace !== undefined || positional.length > 0)
65
- throw new PlatformError("INVALID_REQUEST", `${special} flag cannot be combined with a command or --workspace`);
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] ?? "";
76
81
  if (!COMMANDS.includes(raw))
77
82
  throw new PlatformError("INVALID_REQUEST", `unknown command ${raw}`);
78
83
  const command = raw;
79
- if (workspace !== undefined && command !== "install")
80
- throw new PlatformError("INVALID_REQUEST", "--workspace is only valid with install");
81
- return workspace === undefined
82
- ? { command, json }
83
- : { command, workspace, json };
84
+ if ((moduleRef !== undefined || inputSeen) && command !== "setup")
85
+ throw new PlatformError("INVALID_REQUEST", "--module/--input are only valid with setup");
86
+ if (inputSeen && moduleRef === undefined)
87
+ throw new PlatformError("INVALID_REQUEST", "--input requires --module");
88
+ return {
89
+ command,
90
+ json,
91
+ ...(workspace === undefined ? {} : { workspace }),
92
+ ...(moduleRef === undefined ? {} : { moduleRef }),
93
+ ...(inputSeen ? { input } : {}),
94
+ };
84
95
  }
85
96
  function outcome(command, status, workspaceRoot, data) {
86
97
  return {
@@ -99,32 +110,12 @@ async function canonicalWorkspace(cwd, explicit) {
99
110
  catch (error) {
100
111
  throw new PlatformError("WORKSPACE_NOT_FOUND", `Workspace does not exist: ${candidate} (${error instanceof Error ? error.message : String(error)})`);
101
112
  }
102
- if (!info.isDirectory()) {
113
+ if (!info.isDirectory())
103
114
  throw new PlatformError("WORKSPACE_NOT_FOUND", `Workspace is not a directory: ${candidate}`);
104
- }
105
115
  return realpath(candidate);
106
116
  }
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
117
  async function buildContext(root) {
119
- const discovered = await discoverModules({ workspaceRoot: root });
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);
118
+ const catalog = new AutoModuleCatalog(root);
128
119
  const modules = await discoverModules({ workspaceRoot: root, catalog });
129
120
  return { catalog, modules };
130
121
  }
@@ -137,62 +128,69 @@ function statusFromModule(value) {
137
128
  ? "BLOCKED"
138
129
  : "FAILED";
139
130
  }
140
- function failedLifecycleStatus(results) {
141
- const last = results.at(-1);
142
- return last === undefined
143
- ? "SUCCEEDED"
144
- : statusFromModule(last.result.status);
131
+ function batchStatus(result) {
132
+ if (result.completed)
133
+ return "SUCCEEDED";
134
+ if (result.blockedBy)
135
+ return result.blockedBy.setupStatus === "ACTION_REQUIRED"
136
+ ? "ACTION_REQUIRED"
137
+ : "FAILED";
138
+ const statuses = result.results.map((item) => statusFromModule(item.result.status));
139
+ if (statuses.includes("FAILED"))
140
+ return "FAILED";
141
+ if (statuses.includes("BLOCKED"))
142
+ return "BLOCKED";
143
+ if (statuses.includes("ACTION_REQUIRED"))
144
+ return "ACTION_REQUIRED";
145
+ return "FAILED";
145
146
  }
146
- async function handleModules(root) {
147
+ async function handleStatus(root) {
147
148
  const { catalog, modules } = await buildContext(root);
148
- const observed = await observeStatuses(catalog, modules);
149
+ const observed = await observeStatuses(catalog, modules, root);
149
150
  const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
150
151
  const output = observed.map((item) => {
151
- if (item.result.status !== "SUCCEEDED") {
152
+ if (item.result.status !== "SUCCEEDED")
152
153
  throw new PlatformError("COMMAND_FAILED", `module ${item.moduleRef} status observation did not return SUCCEEDED`);
153
- }
154
154
  const parsed = moduleStatusObservationSchema.safeParse(item.result.data);
155
- if (!parsed.success) {
156
- throw new PlatformError("COMMAND_FAILED", `module ${item.moduleRef} returned an invalid status observation: ${parsed.error.message}`);
157
- }
155
+ if (!parsed.success)
156
+ throw new PlatformError("COMMAND_FAILED", `module ${item.moduleRef} returned invalid status: ${parsed.error.message}`);
158
157
  const module = byRef.get(item.moduleRef);
159
- if (module === undefined)
158
+ if (!module)
160
159
  throw new PlatformError("COMMAND_FAILED", `unknown module ${item.moduleRef}`);
161
160
  return {
162
161
  moduleRef: item.moduleRef,
163
162
  version: module.moduleVersion,
164
- configStatus: parsed.data.configStatus,
165
- ...(parsed.data.missingConfig === undefined
166
- ? {}
167
- : { missingConfig: parsed.data.missingConfig }),
163
+ setupStatus: parsed.data.setupStatus,
168
164
  runtimeStatus: parsed.data.runtimeStatus,
169
165
  };
170
166
  });
171
- return outcome("modules", "SUCCEEDED", root, { modules: output });
167
+ return outcome("status", "SUCCEEDED", root, { modules: output });
172
168
  }
173
169
  async function handleDocs(root) {
174
- const catalog = new AutoModuleCatalog(root);
175
- const modules = await discoverModules({ workspaceRoot: root, catalog });
176
- const docs = await aggregateModuleDocs(root, catalog, modules);
177
- return outcome("docs", "SUCCEEDED", root, { modules: docs });
170
+ const { catalog, modules } = await buildContext(root);
171
+ const docs = await observeDocs(catalog, modules, root);
172
+ const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
173
+ return outcome("docs", "SUCCEEDED", root, {
174
+ modules: docs.map((item) => ({
175
+ moduleRef: item.moduleRef,
176
+ version: byRef.get(item.moduleRef)?.moduleVersion,
177
+ docs: item.result.data,
178
+ })),
179
+ });
178
180
  }
179
181
  async function validateInstalledPackageSet(root, candidates, previousManaged) {
180
182
  const expectedNames = candidates.map((item) => item.packageName).sort();
181
183
  const declaredNames = await workspaceProFlowDependencies(root);
182
- if (JSON.stringify(declaredNames) !== JSON.stringify(expectedNames)) {
184
+ if (JSON.stringify(declaredNames) !== JSON.stringify(expectedNames))
183
185
  throw new PlatformError("COMMAND_FAILED", `managed dependency set mismatch: expected ${expectedNames.join(", ")}, observed ${declaredNames.join(", ")}`);
184
- }
185
186
  for (const candidate of candidates) {
186
187
  const observed = await observeWorkspaceInstalledVersion(root, candidate.packageName);
187
- if (observed !== candidate.moduleVersion) {
188
+ if (observed !== candidate.moduleVersion)
188
189
  throw new PlatformError("COMMAND_FAILED", `installed version mismatch for ${candidate.packageName}: expected ${candidate.moduleVersion}, observed ${observed ?? "missing"}`);
189
- }
190
190
  }
191
- for (const stale of previousManaged.filter((name) => !expectedNames.includes(name))) {
192
- if ((await observeWorkspaceInstalledVersion(root, stale)) !== undefined) {
191
+ for (const stale of previousManaged.filter((name) => !expectedNames.includes(name)))
192
+ if ((await observeWorkspaceInstalledVersion(root, stale)) !== undefined)
193
193
  throw new PlatformError("COMMAND_FAILED", `stale managed package remains installed after synchronization: ${stale}`);
194
- }
195
- }
196
194
  const catalog = new InstalledModuleCatalog(root);
197
195
  const sources = await catalog.sources();
198
196
  const modules = await discoverModules({ catalog, sources });
@@ -200,9 +198,8 @@ async function validateInstalledPackageSet(root, candidates, previousManaged) {
200
198
  for (const candidate of candidates) {
201
199
  const module = byPackage.get(candidate.packageName);
202
200
  if (module === undefined ||
203
- module.moduleVersion !== candidate.moduleVersion) {
201
+ module.moduleVersion !== candidate.moduleVersion)
204
202
  throw new PlatformError("DESCRIPTOR_INVALID", `installed descriptor mismatch for ${candidate.packageName}@${candidate.moduleVersion}`);
205
- }
206
203
  }
207
204
  }
208
205
  async function handleInstall(root, runtime) {
@@ -212,12 +209,10 @@ async function handleInstall(root, runtime) {
212
209
  ? {}
213
210
  : { runner: runtime.registryRunner }),
214
211
  });
215
- if (discovered.rejected.length > 0) {
212
+ if (discovered.rejected.length > 0)
216
213
  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) {
214
+ if (discovered.candidates.length === 0)
219
215
  throw new PlatformError("PACKAGE_NOT_FOUND", "no ProFlow packages were discovered in the configured scope");
220
- }
221
216
  const previousManaged = await workspaceProFlowDependencies(root);
222
217
  const mutation = await syncWorkspacePackages({
223
218
  workspaceRoot: root,
@@ -234,7 +229,9 @@ async function handleInstall(root, runtime) {
234
229
  });
235
230
  await validateInstalledPackageSet(root, discovered.candidates, previousManaged);
236
231
  const metadata = await ensureWorkspaceMetadata(root);
237
- return outcome("install", "SUCCEEDED", root, {
232
+ const { catalog, modules } = await buildContext(root);
233
+ const moduleInstall = await installModulesThin(catalog, modules, root);
234
+ return outcome("install", batchStatus(moduleInstall), root, {
238
235
  registry: discovered.registry,
239
236
  packageManager: mutation.packageManager,
240
237
  packages: discovered.candidates.map((item) => ({
@@ -242,7 +239,8 @@ async function handleInstall(root, runtime) {
242
239
  version: item.moduleVersion,
243
240
  })),
244
241
  workspace: metadata,
245
- next: "platform modules",
242
+ modules: moduleInstall,
243
+ next: "platform status",
246
244
  });
247
245
  }
248
246
  async function workspaceProFlowDependencies(root) {
@@ -256,10 +254,9 @@ async function workspaceProFlowDependencies(root) {
256
254
  const value = record[field];
257
255
  if (typeof value !== "object" || value === null || Array.isArray(value))
258
256
  continue;
259
- for (const name of Object.keys(value)) {
257
+ for (const name of Object.keys(value))
260
258
  if (name.startsWith(PRO_FLOW_PACKAGE_PREFIX))
261
259
  names.add(name);
262
- }
263
260
  }
264
261
  return [...names].sort();
265
262
  }
@@ -273,6 +270,13 @@ async function workspaceProFlowDependencies(root) {
273
270
  }
274
271
  async function handleUninstall(root, runtime) {
275
272
  const packageNames = await workspaceProFlowDependencies(root);
273
+ const { catalog, modules } = await buildContext(root);
274
+ const moduleUninstall = await uninstallModulesThin(catalog, modules, root);
275
+ if (!moduleUninstall.completed)
276
+ return outcome("uninstall", batchStatus(moduleUninstall), root, {
277
+ modules: moduleUninstall,
278
+ removed: [],
279
+ });
276
280
  const mutation = await removeWorkspacePackages({
277
281
  workspaceRoot: root,
278
282
  packageNames,
@@ -284,26 +288,39 @@ async function handleUninstall(root, runtime) {
284
288
  : { executableAvailable: runtime.executableAvailable }),
285
289
  });
286
290
  return outcome("uninstall", "SUCCEEDED", root, {
291
+ modules: moduleUninstall,
287
292
  packageManager: mutation.packageManager,
288
293
  removed: packageNames,
289
294
  preserved: [".proflow"],
290
295
  });
291
296
  }
297
+ async function handleSetup(root, parsed) {
298
+ const { catalog, modules } = await buildContext(root);
299
+ const target = parsed.moduleRef === undefined
300
+ ? undefined
301
+ : {
302
+ moduleRef: parsed.moduleRef,
303
+ ...(Object.hasOwn(parsed, "input") ? { input: parsed.input } : {}),
304
+ };
305
+ const result = await setupModulesThin(catalog, modules, root, target);
306
+ return outcome("setup", batchStatus(result), root, result);
307
+ }
292
308
  async function handleStart(root) {
293
309
  const { catalog, modules } = await buildContext(root);
294
- const result = await preflightAndStartModules(catalog, modules);
295
- return outcome("start", result.completed ? "SUCCEEDED" : failedLifecycleStatus(result.results), root, result);
310
+ const result = await startModulesThin(catalog, modules, root);
311
+ return outcome("start", batchStatus(result), root, result);
296
312
  }
297
313
  async function handleStop(root) {
298
314
  const { catalog, modules } = await buildContext(root);
299
- const result = await stopModulesThin(catalog, modules);
300
- return outcome("stop", result.completed ? "SUCCEEDED" : failedLifecycleStatus(result.results), root, result);
315
+ const result = await stopModulesThin(catalog, modules, root);
316
+ return outcome("stop", batchStatus(result), root, result);
301
317
  }
302
318
  function helpOutcome() {
303
319
  return outcome("help", "SUCCEEDED", undefined, {
304
- usage: "platform <modules|docs|install|uninstall|start|stop> [--json]",
320
+ usage: "platform <install|uninstall|status|setup|docs|start|stop> [--workspace <path>] [--json]",
305
321
  commands: [...COMMANDS],
306
322
  install: "platform install [--workspace <path>]",
323
+ setup: "platform setup [--workspace <path>] [--module <moduleRef> --input '<json>']",
307
324
  });
308
325
  }
309
326
  export async function runCli(argv, runtime = {}) {
@@ -317,24 +334,22 @@ export async function runCli(argv, runtime = {}) {
317
334
  try {
318
335
  if (parsed.command === "help")
319
336
  return JSON.stringify(helpOutcome());
320
- if (parsed.command === "version") {
337
+ if (parsed.command === "version")
321
338
  return JSON.stringify(outcome("version", "SUCCEEDED", undefined, {
322
339
  version: platformCliDescriptor.moduleVersion,
323
340
  }));
324
- }
325
- const cwd = await canonicalWorkspace(runtime.cwd ?? process.cwd());
326
- const root = parsed.command === "install" && parsed.workspace !== undefined
327
- ? await canonicalWorkspace(cwd, parsed.workspace)
328
- : cwd;
341
+ const root = await canonicalWorkspace(runtime.cwd ?? process.cwd(), parsed.workspace);
329
342
  switch (parsed.command) {
330
- case "modules":
331
- return JSON.stringify(await handleModules(root));
332
- case "docs":
333
- return JSON.stringify(await handleDocs(root));
334
343
  case "install":
335
344
  return JSON.stringify(await handleInstall(root, runtime));
336
345
  case "uninstall":
337
346
  return JSON.stringify(await handleUninstall(root, runtime));
347
+ case "status":
348
+ return JSON.stringify(await handleStatus(root));
349
+ case "setup":
350
+ return JSON.stringify(await handleSetup(root, parsed));
351
+ case "docs":
352
+ return JSON.stringify(await handleDocs(root));
338
353
  case "start":
339
354
  return JSON.stringify(await handleStart(root));
340
355
  case "stop":
@@ -346,13 +361,12 @@ export async function runCli(argv, runtime = {}) {
346
361
  }
347
362
  }
348
363
  function errorOutcome(command, error) {
349
- if (error instanceof PlatformError) {
364
+ if (error instanceof PlatformError)
350
365
  return {
351
366
  command,
352
367
  status: "FAILED",
353
368
  error: { code: error.code, message: error.message },
354
369
  };
355
- }
356
370
  return {
357
371
  command,
358
372
  status: "FAILED",
@@ -362,62 +376,81 @@ function errorOutcome(command, error) {
362
376
  },
363
377
  };
364
378
  }
365
- function isRecord(value) {
366
- return typeof value === "object" && value !== null && !Array.isArray(value);
367
- }
368
- function renderModules(data) {
379
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
380
+ function renderStatus(data) {
369
381
  if (!isRecord(data) || !Array.isArray(data.modules))
370
382
  return "No modules.";
371
- const lines = ["ProFlow Modules", ""];
372
- for (const raw of data.modules) {
373
- if (!isRecord(raw))
374
- continue;
375
- const missing = Array.isArray(raw.missingConfig)
376
- ? raw.missingConfig
377
- .filter((item) => typeof item === "string")
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");
383
+ return [
384
+ "ProFlow Modules",
385
+ "",
386
+ ...data.modules
387
+ .filter(isRecord)
388
+ .map((raw) => `${String(raw.moduleRef)} ${String(raw.version)} setup=${String(raw.setupStatus)} runtime=${String(raw.runtimeStatus)}`),
389
+ ].join("\n");
383
390
  }
384
391
  function renderDocs(data) {
385
392
  if (!isRecord(data) || !Array.isArray(data.modules))
386
393
  return "No module docs.";
387
394
  const lines = ["ProFlow Docs"];
388
- for (const raw of data.modules) {
389
- if (!isRecord(raw))
395
+ for (const raw of data.modules)
396
+ if (isRecord(raw))
397
+ lines.push("", `## ${String(raw.moduleRef)} @ ${String(raw.version)}`, JSON.stringify(raw.docs ?? {}, null, 2));
398
+ return lines.join("\n");
399
+ }
400
+ function renderSetup(data) {
401
+ if (!isRecord(data) || !Array.isArray(data.results))
402
+ return "No setup actions are required.";
403
+ const lines = ["ProFlow Setup", ""];
404
+ let automatic = 0;
405
+ let pending = 0;
406
+ for (const raw of data.results) {
407
+ if (!isRecord(raw) || !isRecord(raw.result))
390
408
  continue;
391
- lines.push("", `## ${String(raw.moduleRef)} @ ${String(raw.version)}`);
392
- if (!Array.isArray(raw.documents))
409
+ const moduleRef = String(raw.moduleRef ?? raw.result.moduleRef ?? "unknown");
410
+ const status = String(raw.result.status ?? "UNKNOWN");
411
+ if (status === "SUCCEEDED") {
412
+ automatic += 1;
393
413
  continue;
394
- for (const document of raw.documents) {
395
- if (!isRecord(document))
396
- continue;
397
- lines.push("", `### ${String(document.id)}`, String(document.content ?? ""));
398
414
  }
415
+ pending += 1;
416
+ lines.push(`## ${moduleRef} — ${status}`);
417
+ const actionRequired = raw.result.actionRequired;
418
+ if (isRecord(actionRequired)) {
419
+ if (actionRequired.action !== undefined)
420
+ lines.push(`Action: ${String(actionRequired.action)}`);
421
+ if (actionRequired.description !== undefined)
422
+ lines.push(String(actionRequired.description));
423
+ }
424
+ else if (raw.result.data !== undefined) {
425
+ lines.push(JSON.stringify(raw.result.data, null, 2));
426
+ }
427
+ lines.push("");
399
428
  }
400
- return lines.join("\n");
429
+ if (pending === 0)
430
+ lines.push("All discovered Modules are setup-ready.");
431
+ if (automatic > 0)
432
+ lines.push(`Automatically completed in this run: ${automatic}`);
433
+ return lines.join("\n").trimEnd();
401
434
  }
402
435
  export function renderHumanResult(result) {
403
- if (result.status === "FAILED") {
436
+ if (result.status === "FAILED")
404
437
  return `${result.command.toUpperCase()} FAILED${result.error ? ` [${result.error.code}] ${result.error.message}` : ""}`;
405
- }
406
- if (result.command === "help") {
438
+ if (result.command === "help")
407
439
  return [
408
440
  "ProFlow Platform CLI",
409
441
  "",
410
442
  ...COMMANDS.map((command) => `platform ${command}`),
411
443
  "",
412
444
  "platform install --workspace <path>",
445
+ "platform setup --module <moduleRef> --input '<json>'",
413
446
  "append --json for machine-readable output",
414
447
  ].join("\n");
415
- }
416
- if (result.command === "version" && isRecord(result.data)) {
448
+ if (result.command === "version" && isRecord(result.data))
417
449
  return String(result.data.version ?? platformCliDescriptor.moduleVersion);
418
- }
419
- if (result.command === "modules")
420
- return renderModules(result.data);
450
+ if (result.command === "status")
451
+ return renderStatus(result.data);
452
+ if (result.command === "setup")
453
+ return renderSetup(result.data);
421
454
  if (result.command === "docs")
422
455
  return renderDocs(result.data);
423
456
  return [
@@ -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
- private readonly bindings;
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
- bindings;
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
  };
@@ -1,4 +1,4 @@
1
- export declare const platformErrorCodes: readonly ["INVALID_REQUEST", "DESCRIPTOR_INVALID", "DUPLICATE_IDENTITY", "DEPENDENCY_UNRESOLVED", "DEPENDENCY_INCOMPATIBLE", "DEPENDENCY_CYCLE", "CONFIG_INVALID", "WORKSPACE_NOT_FOUND", "WORKSPACE_INSTANCE_INVALID", "PACKAGE_MANAGER_UNSUPPORTED", "PACKAGE_MANAGER_CONFLICT", "PACKAGE_MANAGER_UNAVAILABLE", "LIFECYCLE_UNSUPPORTED", "UNINSTALL_FAILED", "REGISTRY_UNAVAILABLE", "REGISTRY_AUTH_REQUIRED", "REGISTRY_RESPONSE_INVALID", "PACKAGE_NOT_FOUND", "PACKAGE_NOT_PROFLOW", "COMMAND_FAILED"];
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;
@@ -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",
@@ -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,4 +1,3 @@
1
1
  export { renderHumanResult, runCli } from "./cli.js";
2
2
  export { AutoModuleCatalog, discoverModules } from "./discovery/discover.js";
3
- export { aggregateModuleDocs } from "./docs/aggregate.js";
4
3
  export { PlatformError } from "./errors.js";
@@ -1,17 +1,10 @@
1
- import { type LifecyclePrimitive, type ModuleOperationResult } from "@tomflow/proflow-module-contract";
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 LifecycleDispatchResult {
4
+ export interface ModuleDispatchResult {
5
5
  moduleRef: string;
6
- primitive: LifecyclePrimitive;
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>;