@tomflow/proflow-platform-cli 0.1.33 → 0.1.35

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @tomflow/proflow-platform-cli
2
2
 
3
+ ## 0.1.35
4
+
5
+ ### Patch Changes
6
+
7
+ - Render retryable package-manager warnings as warnings instead of failed subprocess events.
8
+
9
+ ## 0.1.34
10
+
11
+ ### Patch Changes
12
+
13
+ - Upgrade the ProFlow terminal experience, stream Registry and package-manager progress, publish structured Chinese Module documentation, and provide numbered package-owned Setup assistants.
14
+
3
15
  ## 0.1.33
4
16
 
5
17
  ### Patch Changes
package/DOCS.md CHANGED
@@ -1,11 +1,39 @@
1
- # platform-cli
1
+ # platform-cli — ProFlow 管理命令行
2
2
 
3
- Platform CLI(平台命令行)是 ProFlow 的统一管理入口,负责发现模块、安装依赖、汇总状态和按依赖顺序启动或停止模块。
3
+ ## 模块定位与作用
4
4
 
5
- ## 推荐流程
5
+ ProFlow 的薄管理面,负责从 Registry 发现 Module、同步 npm 包、解析依赖顺序,并转发和聚合七标准命令。
6
6
 
7
- `platform install` → `platform status` → `platform docs` → `platform setup` → `platform start`
7
+ ## 主要能力
8
8
 
9
- ## 边界
9
+ - 安装、卸载并验证完整 Module 包集合。
10
+ - 聚合 status、docs 和 setup,不解释 Module 私有业务。
11
+ - 按依赖顺序启动、按逆依赖顺序停止,并支持幂等重试。
10
12
 
11
- Platform CLI 只负责调用和汇总,不读取模块私有配置,也不替模块解释外部服务。具体配置由对应模块的 Setup(配置向导)完成。
13
+ ## 提供的 API 与 Public Contract
14
+
15
+ - 不提供领域业务 Contract。
16
+ - 提供 `platform` CLI;`runCli()` 返回强类型 `CliOutcome`,终端层负责中文渲染。
17
+
18
+ ## 依赖的 Module、Contract 和外部资源
19
+
20
+ - 依赖 npm Registry、当前 Workspace package manager 和所有 discovered Module 的公开 adapter。
21
+ - 不 deep import Module 私有代码,不读取 Module 私有配置。
22
+
23
+ ## 运行形态与生命周期
24
+
25
+ 类型为 CLI,命令执行结束后退出;不是业务 Runtime 或长期守护进程。
26
+
27
+ ## 使用方式
28
+
29
+ 推荐顺序为 `platform install`、`status`、`docs`、`setup`、`start`、`stop`、`uninstall`。
30
+
31
+ ## 职责边界与限制
32
+
33
+ 不代理业务 API、不保存 Module setup 输入、不建立 preflight/doctor 第二真源,也不提供 JSON 输出选项。
34
+
35
+ ## 术语
36
+
37
+ - Discovery(发现):从 Registry 与 Workspace 识别 governed Module。
38
+ - Aggregation(聚合):保留各 Module 结果并以统一终端形式展示。
39
+ - Dependency Order(依赖顺序):Provider 先于 Consumer 的生命周期顺序。
@@ -6,7 +6,7 @@ export declare const behaviorAdapter: {
6
6
  readonly ok: true;
7
7
  readonly status: "SUCCEEDED";
8
8
  readonly moduleRef: "platform-cli";
9
- readonly moduleVersion: "0.1.33";
9
+ readonly moduleVersion: "0.1.35";
10
10
  };
11
11
  observedEffects: never[];
12
12
  }>;
@@ -16,7 +16,7 @@ export declare const behaviorAdapter: {
16
16
  readonly ok: true;
17
17
  readonly status: "SUCCEEDED";
18
18
  readonly moduleRef: "platform-cli";
19
- readonly moduleVersion: "0.1.33";
19
+ readonly moduleVersion: "0.1.35";
20
20
  };
21
21
  observedEffects: never[];
22
22
  }>;
@@ -26,7 +26,7 @@ export declare const behaviorAdapter: {
26
26
  ok: true;
27
27
  status: "SUCCEEDED";
28
28
  moduleRef: "platform-cli";
29
- moduleVersion: "0.1.33";
29
+ moduleVersion: "0.1.35";
30
30
  data: {
31
31
  readonly setupStatus: "READY";
32
32
  readonly runtimeStatus: "NOT_APPLICABLE";
@@ -40,7 +40,7 @@ export declare const behaviorAdapter: {
40
40
  readonly ok: true;
41
41
  readonly status: "SUCCEEDED";
42
42
  readonly moduleRef: "platform-cli";
43
- readonly moduleVersion: "0.1.33";
43
+ readonly moduleVersion: "0.1.35";
44
44
  };
45
45
  observedEffects: never[];
46
46
  }>;
@@ -50,7 +50,7 @@ export declare const behaviorAdapter: {
50
50
  ok: true;
51
51
  status: "SUCCEEDED";
52
52
  moduleRef: "platform-cli";
53
- moduleVersion: "0.1.33";
53
+ moduleVersion: "0.1.35";
54
54
  data: {
55
55
  docs: string;
56
56
  };
@@ -63,7 +63,7 @@ export declare const behaviorAdapter: {
63
63
  readonly ok: true;
64
64
  readonly status: "SUCCEEDED";
65
65
  readonly moduleRef: "platform-cli";
66
- readonly moduleVersion: "0.1.33";
66
+ readonly moduleVersion: "0.1.35";
67
67
  };
68
68
  observedEffects: never[];
69
69
  }>;
@@ -73,7 +73,7 @@ export declare const behaviorAdapter: {
73
73
  readonly ok: true;
74
74
  readonly status: "SUCCEEDED";
75
75
  readonly moduleRef: "platform-cli";
76
- readonly moduleVersion: "0.1.33";
76
+ readonly moduleVersion: "0.1.35";
77
77
  };
78
78
  observedEffects: never[];
79
79
  }>;
@@ -3,7 +3,7 @@ export declare const descriptor: {
3
3
  readonly contractVersion: "1.0.0";
4
4
  readonly moduleRef: "platform-cli";
5
5
  readonly packageName: "@tomflow/proflow-platform-cli";
6
- readonly moduleVersion: "0.1.33";
6
+ readonly moduleVersion: "0.1.35";
7
7
  readonly kind: "cli";
8
8
  readonly templateVersion: "1.0.0";
9
9
  readonly platformCompatibility: ">=1.0.0 <2.0.0";
@@ -3,7 +3,7 @@ export const descriptor = {
3
3
  contractVersion: "1.0.0",
4
4
  moduleRef: "platform-cli",
5
5
  packageName: "@tomflow/proflow-platform-cli",
6
- moduleVersion: "0.1.33",
6
+ moduleVersion: "0.1.35",
7
7
  kind: "cli",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
package/dist/src/cli.d.ts CHANGED
@@ -21,4 +21,8 @@ export interface CliRuntimeOptions {
21
21
  onProgress?: PlatformProgressReporter;
22
22
  }
23
23
  export declare function runCli(argv: readonly string[], runtime?: CliRuntimeOptions): Promise<CliOutcome>;
24
- export declare function renderHumanResult(result: CliOutcome): string;
24
+ export interface HumanRenderOptions {
25
+ color?: boolean;
26
+ width?: number;
27
+ }
28
+ export declare function renderHumanResult(result: CliOutcome, options?: HumanRenderOptions): string;
package/dist/src/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
2
3
  import { readFile, realpath, stat } from "node:fs/promises";
3
4
  import { resolve } from "node:path";
4
5
  import { moduleDocsDataSchema, moduleSetupPlanDataSchema, moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
@@ -230,21 +231,50 @@ async function validateInstalledPackageSet(root, candidates, previousManaged) {
230
231
  }
231
232
  }
232
233
  async function handleInstall(root, runtime) {
234
+ const startedAt = Date.now();
233
235
  reportProgress(runtime.onProgress, {
234
236
  command: "install",
235
237
  phase: "workspace",
238
+ kind: "phase",
236
239
  status: "STARTED",
237
240
  message: "正在校验 Workspace",
238
241
  });
239
242
  const metadata = await ensureWorkspaceMetadata(root);
243
+ reportProgress(runtime.onProgress, {
244
+ command: "install",
245
+ phase: "workspace",
246
+ kind: "phase",
247
+ status: "SUCCEEDED",
248
+ message: "Workspace 校验完成",
249
+ elapsedMs: Date.now() - startedAt,
250
+ });
240
251
  reportProgress(runtime.onProgress, {
241
252
  command: "install",
242
253
  phase: "registry",
254
+ kind: "phase",
243
255
  status: "STARTED",
244
- message: "正在发现 Registry 模块",
256
+ message: "正在连接 Registry",
245
257
  });
246
258
  const discovered = await discoverRegistryModules({
247
259
  workspaceRoot: root,
260
+ onSearchComplete: (total) => reportProgress(runtime.onProgress, {
261
+ command: "install",
262
+ phase: "registry",
263
+ kind: "detail",
264
+ status: "STARTED",
265
+ total,
266
+ message: `Registry 返回 ${total} 个候选模块,正在核验`,
267
+ }),
268
+ onPackageChecked: ({ current, total, packageName }) => reportProgress(runtime.onProgress, {
269
+ command: "install",
270
+ phase: "registry",
271
+ kind: "detail",
272
+ status: "SUCCEEDED",
273
+ current,
274
+ total,
275
+ moduleRef: packageName.replace(PRO_FLOW_PACKAGE_PREFIX, ""),
276
+ message: `核验 ${packageName.replace(PRO_FLOW_PACKAGE_PREFIX, "")}`,
277
+ }),
248
278
  ...(runtime.registryRunner === undefined
249
279
  ? {}
250
280
  : { runner: runtime.registryRunner }),
@@ -252,6 +282,7 @@ async function handleInstall(root, runtime) {
252
282
  reportProgress(runtime.onProgress, {
253
283
  command: "install",
254
284
  phase: "registry",
285
+ kind: "phase",
255
286
  status: "SUCCEEDED",
256
287
  message: `已发现 ${discovered.candidates.length} 个注册模块`,
257
288
  });
@@ -264,6 +295,7 @@ async function handleInstall(root, runtime) {
264
295
  reportProgress(runtime.onProgress, {
265
296
  command: "install",
266
297
  phase: "packages",
298
+ kind: "phase",
267
299
  status: "STARTED",
268
300
  message: "正在同步依赖",
269
301
  });
@@ -273,6 +305,7 @@ async function handleInstall(root, runtime) {
273
305
  packageName: item.packageName,
274
306
  version: item.moduleVersion,
275
307
  })),
308
+ onOutput: (line) => reportPackageManagerOutput(runtime, "install", line),
276
309
  ...(runtime.packageRunner === undefined
277
310
  ? {}
278
311
  : { runner: runtime.packageRunner }),
@@ -283,12 +316,20 @@ async function handleInstall(root, runtime) {
283
316
  reportProgress(runtime.onProgress, {
284
317
  command: "install",
285
318
  phase: "packages",
319
+ kind: "phase",
286
320
  status: "SUCCEEDED",
287
321
  message: "依赖同步完成",
288
322
  });
289
323
  await recordPnpmPolicyOwnership(root, pnpmPolicyBefore);
290
324
  await validateInstalledPackageSet(root, discovered.candidates, previousManaged);
291
325
  const { catalog, modules } = await buildContext(root);
326
+ reportProgress(runtime.onProgress, {
327
+ command: "install",
328
+ phase: "validation",
329
+ kind: "phase",
330
+ status: "SUCCEEDED",
331
+ message: `已验证 ${modules.length} 个已安装模块`,
332
+ });
292
333
  const moduleInstall = await installModulesThin(catalog, modules, root, runtime.onProgress);
293
334
  return outcome("install", batchStatus(moduleInstall), root, {
294
335
  registry: discovered.registry,
@@ -302,6 +343,22 @@ async function handleInstall(root, runtime) {
302
343
  next: "platform status",
303
344
  });
304
345
  }
346
+ const usefulPackageOutput = /(?:resolved|downloaded|reused|linked|added|removed|packages:|progress:|warn|warning|error|ERR_|done|completed|audited|changed)/i;
347
+ function reportPackageManagerOutput(runtime, command, output) {
348
+ if (!usefulPackageOutput.test(output.line))
349
+ return;
350
+ reportProgress(runtime.onProgress, {
351
+ command,
352
+ phase: "packages",
353
+ kind: "subprocess",
354
+ status: /\b(?:warn|warning)\b/i.test(output.line)
355
+ ? "WARNING"
356
+ : /(?:error|ERR_)/i.test(output.line)
357
+ ? "FAILED"
358
+ : "STARTED",
359
+ message: output.line,
360
+ });
361
+ }
305
362
  async function workspaceProFlowDependencies(root) {
306
363
  try {
307
364
  const parsed = JSON.parse(await readFile(resolve(root, "package.json"), "utf8"));
@@ -339,6 +396,7 @@ async function handleUninstall(root, runtime) {
339
396
  const mutation = await removeWorkspacePackages({
340
397
  workspaceRoot: root,
341
398
  packageNames,
399
+ onOutput: (line) => reportPackageManagerOutput(runtime, "uninstall", line),
342
400
  ...(runtime.packageRunner === undefined
343
401
  ? {}
344
402
  : { runner: runtime.packageRunner }),
@@ -447,7 +505,32 @@ function errorOutcome(command, error) {
447
505
  };
448
506
  }
449
507
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
450
- function renderStatus(data) {
508
+ const ansi = {
509
+ reset: "\u001b[0m",
510
+ bold: "\u001b[1m",
511
+ dim: "\u001b[2m",
512
+ cyan: "\u001b[36m",
513
+ blue: "\u001b[34m",
514
+ green: "\u001b[32m",
515
+ yellow: "\u001b[33m",
516
+ red: "\u001b[31m",
517
+ };
518
+ function humanTheme(options = {}) {
519
+ const enabled = options.color === true;
520
+ const paint = (tone, value) => enabled ? `${ansi[tone]}${value}${ansi.reset}` : value;
521
+ return {
522
+ width: Math.max(60, options.width ?? 100),
523
+ paint,
524
+ title: (value) => paint("bold", paint("cyan", value)),
525
+ section: (value) => paint("bold", value),
526
+ command: (value) => paint("blue", value),
527
+ muted: (value) => paint("dim", value),
528
+ success: (value) => paint("green", value),
529
+ warning: (value) => paint("yellow", value),
530
+ failure: (value) => paint("red", value),
531
+ };
532
+ }
533
+ function renderStatus(data, theme) {
451
534
  if (!isRecord(data) || !Array.isArray(data.modules))
452
535
  return "未发现模块。";
453
536
  const setupLabels = {
@@ -462,8 +545,15 @@ function renderStatus(data) {
462
545
  NOT_APPLICABLE: "无独立进程",
463
546
  };
464
547
  const modules = data.modules.filter(isRecord);
465
- const lines = ["ProFlow 状态", ""];
466
- for (const raw of modules) {
548
+ const unhealthy = modules.filter((item) => item.setupStatus !== "READY" || item.runtimeStatus === "FAILED");
549
+ const running = modules.filter((item) => item.setupStatus === "READY" && item.runtimeStatus === "RUNNING");
550
+ const healthy = modules.filter((item) => item.setupStatus === "READY" &&
551
+ item.runtimeStatus !== "RUNNING" &&
552
+ item.runtimeStatus !== "FAILED");
553
+ const lines = [theme.title("ProFlow 状态"), ""];
554
+ if (unhealthy.length > 0)
555
+ lines.push(theme.section("需要处理"), "");
556
+ for (const raw of unhealthy) {
467
557
  const moduleRef = String(raw.moduleRef);
468
558
  const setupStatus = String(raw.setupStatus);
469
559
  const runtimeStatus = String(raw.runtimeStatus);
@@ -478,43 +568,69 @@ function renderStatus(data) {
478
568
  : runtimeStatus === "NOT_APPLICABLE"
479
569
  ? "○"
480
570
  : "✓";
481
- lines.push(`${icon} ${moduleRef.padEnd(30)} ${String(raw.version).padEnd(9)} ${setupLabels[setupStatus] ?? "未知"} · ${runtimeLabels[runtimeStatus] ?? "未知"}`);
571
+ const tone = failed ? theme.failure : theme.warning;
572
+ lines.push(`${tone(icon)} ${theme.section(moduleRef)} ${theme.muted(String(raw.version))}`);
573
+ lines.push(` ${tone(setupLabels[setupStatus] ?? "未知")} · ${runtimeLabels[runtimeStatus] ?? "未知"}`);
482
574
  if (failed) {
483
- lines.push(` 原因:${setupStatus === "FAILED" ? "模块配置检查失败" : "模块运行状态异常"}`, ` 下一步:platform setup --module ${moduleRef}`);
575
+ lines.push(` 原因:${setupStatus === "FAILED" ? "模块配置检查失败" : "模块运行状态异常"}`, ` 下一步:${theme.command(`platform setup --module ${moduleRef}`)}`);
484
576
  }
485
577
  else if (actionRequired) {
486
- lines.push(" 原因:模块尚未完成配置", ` 下一步:platform setup --module ${moduleRef}`);
578
+ lines.push(" 原因:模块尚未完成配置", ` 下一步:${theme.command(`platform setup --module ${moduleRef}`)}`);
487
579
  }
580
+ lines.push("");
488
581
  }
582
+ const compact = (title, entries, symbol) => {
583
+ if (entries.length === 0)
584
+ return;
585
+ lines.push(theme.section(title), "");
586
+ for (const item of entries) {
587
+ const runtime = runtimeLabels[String(item.runtimeStatus)] ?? "未知";
588
+ lines.push(`${theme.success(symbol)} ${String(item.moduleRef).padEnd(32)} ${theme.muted(String(item.version).padEnd(9))} ${runtime}`);
589
+ }
590
+ lines.push("");
591
+ };
592
+ compact("运行中", running, "●");
593
+ compact("已就绪", healthy, "○");
489
594
  const ready = modules.filter((item) => item.setupStatus === "READY").length;
490
595
  const action = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED").length;
491
596
  const failed = modules.filter((item) => item.setupStatus === "FAILED").length;
492
- lines.push("", `汇总:${ready} 已就绪 · ${action} 需要操作 · ${failed} 失败`);
597
+ lines.push(theme.section("汇总"), `${theme.success(`${ready} 已就绪`)} · ${theme.warning(`${action} 需要操作`)} · ${failed > 0 ? theme.failure(`${failed} 失败`) : `${failed} 失败`}`);
493
598
  return lines.join("\n");
494
599
  }
495
- function renderTerminalMarkdown(source) {
600
+ function renderTerminalMarkdown(source, theme) {
601
+ let inCode = false;
496
602
  return source
497
603
  .split(/\r?\n/)
498
604
  .map((line) => {
499
- const heading = line.match(/^#{1,6}\s+(.+)$/);
500
- if (heading?.[1])
501
- return heading[1].replaceAll("`", "");
502
- const bullet = line.match(/^\s*-\s+(.+)$/);
605
+ if (line.trim().startsWith("```")) {
606
+ inCode = !inCode;
607
+ return "";
608
+ }
609
+ if (inCode)
610
+ return ` ${theme.command(line)}`;
611
+ const heading = line.match(/^(#{1,6})\s+(.+)$/);
612
+ if (heading?.[2])
613
+ return heading[1]?.length === 1
614
+ ? theme.title(heading[2].replaceAll("`", ""))
615
+ : theme.section(heading[2].replaceAll("`", ""));
616
+ const bullet = line.match(/^\s*[-*]\s+(.+)$/);
503
617
  if (bullet?.[1])
504
- return ` ${bullet[1].replaceAll("`", "")}`;
505
- return line.replaceAll("`", "");
618
+ return ` ${theme.paint("cyan", "")} ${bullet[1]}`;
619
+ if (/^\|.*\|$/.test(line))
620
+ return theme.muted(line);
621
+ return line.replace(/`([^`]+)`/g, (_match, code) => theme.command(code));
506
622
  })
507
623
  .join("\n")
508
624
  .replace(/\n{3,}/g, "\n\n")
509
625
  .trim();
510
626
  }
511
- function renderDocs(data) {
627
+ function renderDocs(data, theme) {
512
628
  if (!isRecord(data) || !Array.isArray(data.modules))
513
629
  return "未发现模块文档。";
514
- const lines = ["ProFlow 模块文档"];
630
+ const lines = [theme.title("ProFlow 模块文档")];
515
631
  for (const raw of data.modules)
516
632
  if (isRecord(raw))
517
- lines.push("", "─".repeat(72), `${String(raw.moduleRef)} ${String(raw.version)}`, "", renderTerminalMarkdown(String(raw.docs ?? "")));
633
+ lines.push("", theme.muted("─".repeat(Math.min(theme.width, 96))), `${theme.section(String(raw.moduleRef))} ${theme.muted(String(raw.version))}`, "", renderTerminalMarkdown(String(raw.docs ?? ""), theme));
518
634
  if (Array.isArray(data.errors) && data.errors.length > 0)
519
635
  lines.push("", "文档读取失败:", ...data.errors
520
636
  .filter(isRecord)
@@ -555,10 +671,10 @@ const setupCommands = {
555
671
  inputs: "Custom GPT URL",
556
672
  },
557
673
  };
558
- function renderSetup(data) {
674
+ function renderSetup(data, theme) {
559
675
  if (!isRecord(data) || !Array.isArray(data.results))
560
676
  return "没有需要执行的配置步骤。";
561
- const lines = ["ProFlow 配置", ""];
677
+ const lines = [theme.title("ProFlow 配置"), ""];
562
678
  const skipped = Array.isArray(data.skipped) ? data.skipped : [];
563
679
  let ready = skipped.filter((item) => isRecord(item) && item.reason === "READY").length;
564
680
  let needsAction = 0;
@@ -576,19 +692,31 @@ function renderSetup(data) {
576
692
  blocked += 1;
577
693
  else
578
694
  needsAction += 1;
579
- lines.push(`${status === "FAILED" ? "✕" : "◆"} ${moduleRef}`);
695
+ lines.push(`${status === "FAILED" ? theme.failure("✕") : theme.warning("◆")} ${theme.section(moduleRef)}`);
580
696
  const plan = moduleSetupPlanDataSchema.safeParse(raw.result.data);
581
697
  if (plan.success) {
582
698
  for (const [index, step] of plan.data.steps.entries()) {
583
- lines.push(` ${index + 1}. ${step.title}`);
584
- lines.push(` 运行:${step.execution.interactive}`);
585
- lines.push(` 自动执行:${step.execution.nonInteractive}`);
699
+ const marker = step.state === "DONE"
700
+ ? theme.success("✓")
701
+ : step.state === "BLOCKED"
702
+ ? theme.failure("✕")
703
+ : index ===
704
+ plan.data.steps.findIndex((item) => item.state === "TODO")
705
+ ? theme.warning("→")
706
+ : theme.muted("○");
707
+ lines.push(` ${marker} ${String(index + 1).padStart(2, "0")} ${step.title}`);
708
+ if (step.description)
709
+ lines.push(` ${step.description}`);
710
+ lines.push(` 运行:${theme.command(step.execution.interactive)}`);
711
+ lines.push(` AI:${theme.command(step.execution.nonInteractive)}`);
586
712
  if (step.requiredInputs.length > 0)
587
- lines.push(` 需要:${step.requiredInputs.map((item) => item.description).join("、")}`);
588
- lines.push(` 验证:${step.verify}`);
589
- lines.push(` 完成:${step.successCondition}`);
713
+ lines.push(` 需要:${step.requiredInputs.map((item) => item.description).join("、")}`);
714
+ if (step.humanAction)
715
+ lines.push(` 人工操作:${step.humanAction}`);
716
+ lines.push(` 验证:${theme.command(step.verify)}`);
717
+ lines.push(` 完成:${step.successCondition}`);
590
718
  if (step.blockedReason)
591
- lines.push(` 原因:${step.blockedReason}`);
719
+ lines.push(` 原因:${theme.failure(step.blockedReason)}`);
592
720
  }
593
721
  lines.push("");
594
722
  continue;
@@ -619,34 +747,49 @@ function renderSetup(data) {
619
747
  }
620
748
  if (needsAction === 0 && blocked === 0)
621
749
  lines.push("全部模块均已就绪。");
622
- lines.push(`汇总:${ready} 个已就绪,${needsAction} 个需要操作,${blocked} 个系统阻塞`);
750
+ lines.push(theme.section("汇总"), `汇总:${theme.success(`${ready} 个已就绪`)},${theme.warning(`${needsAction} 个需要操作`)},${blocked > 0 ? theme.failure(`${blocked} 个系统阻塞`) : `${blocked} 个系统阻塞`}`);
623
751
  return lines.join("\n").trimEnd();
624
752
  }
625
- export function renderHumanResult(result) {
753
+ export function renderHumanResult(result, options = {}) {
754
+ const theme = humanTheme(options);
626
755
  if (result.command === "setup" &&
627
756
  isRecord(result.data) &&
628
757
  Array.isArray(result.data.results))
629
- return renderSetup(result.data);
758
+ return renderSetup(result.data, theme);
630
759
  if (result.command === "start" && isRecord(result.data)) {
631
760
  const blockers = Array.isArray(result.data.blockers)
632
761
  ? result.data.blockers.filter(isRecord)
633
762
  : [];
634
763
  if (blockers.length > 0)
635
764
  return [
636
- "平台未启动:存在未就绪模块",
765
+ theme.failure("平台未启动:存在未就绪模块"),
637
766
  "",
638
- ...blockers.map((item) => `${String(item.moduleRef).padEnd(28)} ${String(item.setupStatus) === "FAILED" ? "失败" : "需要操作"} 配置尚未完成`),
767
+ ...blockers.flatMap((item) => [
768
+ `${String(item.setupStatus) === "FAILED" ? theme.failure("✕") : theme.warning("◆")} ${theme.section(String(item.moduleRef))} ${String(item.setupStatus) === "FAILED" ? theme.failure("失败") : theme.warning("需要操作")}`,
769
+ ` 原因:${typeof item.reason === "string" ? item.reason : "模块配置尚未完成"}`,
770
+ ` 下一步:${theme.command(`platform setup --module ${String(item.moduleRef)}`)}`,
771
+ ]),
639
772
  "",
640
- `处理方式:platform setup${result.workspaceRoot ? ` --workspace "${result.workspaceRoot}"` : ""}`,
773
+ `处理方式:${theme.command(`platform setup${result.workspaceRoot ? ` --workspace "${result.workspaceRoot}"` : ""}`)}`,
641
774
  ].join("\n");
642
775
  }
643
776
  if (result.status === "FAILED" && result.error)
644
- return `${result.command} 失败:${result.error.message}`;
777
+ return [
778
+ theme.failure(`╭─ ${result.command} 执行失败`),
779
+ `${theme.failure("│")} ${theme.section(result.error.code)}`,
780
+ `${theme.failure("│")} ${result.error.message}`,
781
+ theme.failure("╰─ 请根据提示修复后重试"),
782
+ ].join("\n");
645
783
  if (result.command === "help")
646
784
  return [
647
- "ProFlow 平台命令行",
785
+ theme.title("ProFlow 平台命令行"),
786
+ "管理 ProFlow 模块的安装、配置、文档和运行生命周期。",
648
787
  "",
649
- "platform install 安装并初始化全部 ProFlow 模块",
788
+ theme.section("用法"),
789
+ ` ${theme.command("platform <command> [--workspace <路径>]")}`,
790
+ "",
791
+ theme.section("命令"),
792
+ `${theme.command("platform install")} 安装并初始化全部 ProFlow 模块`,
650
793
  "platform uninstall 卸载模块包,保留 Workspace 数据",
651
794
  "platform status 查看模块配置与运行状态",
652
795
  "platform setup 自动配置并列出全部剩余步骤",
@@ -654,20 +797,32 @@ export function renderHumanResult(result) {
654
797
  "platform start 完成全量检查后启动平台",
655
798
  "platform stop 按逆依赖顺序停止平台",
656
799
  "",
657
- "常用参数:--workspace <路径>;setup 还支持 --module <模块名>",
658
- "推荐流程:install status → docs → setup → start → status → stop",
659
- "人工配置示例:pnpm exec -- proflow-chatgpt-carrier setup",
660
- "AI 配置示例:pnpm exec -- proflow-chatgpt-carrier setup --carrier-url <url>",
661
- "状态图例:已就绪=配置完成;需要操作=需运行 setup;失败=存在系统阻塞;无独立进程=无需启动",
800
+ theme.section("公共参数"),
801
+ " --workspace <路径> 指定 ProFlow Workspace",
802
+ " setup --module <模块名> 只查看指定模块的配置步骤",
803
+ "",
804
+ theme.section("推荐流程"),
805
+ ` ${theme.command("install → status → docs → setup → start → status → stop")}`,
806
+ "",
807
+ theme.section("人工配置示例"),
808
+ ` 人工:${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
809
+ ` AI: ${theme.command("pnpm exec -- proflow-chatgpt-carrier setup --carrier-url <url>")}`,
810
+ "",
811
+ theme.section("状态图例"),
812
+ ` ${theme.success("已就绪")} 配置完成 ${theme.warning("需要操作")} 运行 setup`,
813
+ ` ${theme.failure("失败")} 存在阻塞 无独立进程 无需启动`,
814
+ "",
815
+ theme.section("遇到问题"),
816
+ ` 先运行 ${theme.command("platform status")};配置问题运行 ${theme.command("platform setup")}。`,
662
817
  ].join("\n");
663
818
  if (result.command === "version" && isRecord(result.data))
664
819
  return String(result.data.version ?? platformCliDescriptor.moduleVersion);
665
820
  if (result.command === "status")
666
- return renderStatus(result.data);
821
+ return renderStatus(result.data, theme);
667
822
  if (result.command === "setup")
668
- return renderSetup(result.data);
823
+ return renderSetup(result.data, theme);
669
824
  if (result.command === "docs")
670
- return renderDocs(result.data);
825
+ return renderDocs(result.data, theme);
671
826
  if ((result.command === "start" || result.command === "stop") &&
672
827
  isRecord(result.data)) {
673
828
  const skippedItems = Array.isArray(result.data.skipped)
@@ -684,8 +839,10 @@ export function renderHumanResult(result) {
684
839
  !skippedRefs.has(String(item.moduleRef))).length;
685
840
  const failed = operations.find((item) => isRecord(item.result) && item.result.status !== "SUCCEEDED");
686
841
  return [
687
- `平台${result.command === "start" ? "启动" : "停止"}${failed ? "未完成" : "完成"}`,
688
- `成功:${succeeded},跳过:${skipped}${failed ? `,失败:${String(failed.moduleRef)}` : ",失败:0"}`,
842
+ failed
843
+ ? theme.failure(`平台${result.command === "start" ? "启动" : "停止"}未完成`)
844
+ : theme.success(`✓ 平台${result.command === "start" ? "启动" : "停止"}完成`),
845
+ `${theme.success(`成功:${succeeded}`)} ${theme.muted(`跳过:${skipped}`)} ${failed ? theme.failure(`失败:${String(failed.moduleRef)}`) : theme.muted("失败:0")}`,
689
846
  ].join("\n");
690
847
  }
691
848
  const labels = {
@@ -694,14 +851,40 @@ export function renderHumanResult(result) {
694
851
  start: "启动",
695
852
  stop: "停止",
696
853
  };
697
- return `${labels[result.command] ?? result.command}${result.status === "SUCCEEDED" ? "成功" : "未完成"}${result.workspaceRoot ? `\nWorkspace:${result.workspaceRoot}` : ""}`;
854
+ return `${result.status === "SUCCEEDED" ? theme.success("✓") : theme.failure("✕")} ${labels[result.command] ?? result.command}${result.status === "SUCCEEDED" ? "成功" : "未完成"}${result.workspaceRoot ? `\n${theme.muted("Workspace")} ${result.workspaceRoot}` : ""}`;
698
855
  }
699
856
  if (import.meta.main) {
700
857
  const argv = process.argv.slice(2);
701
858
  const reporter = createTerminalProgressReporter();
702
859
  const result = await runCli(argv, { onProgress: reporter });
703
860
  reporter.close();
704
- process.stdout.write(`${renderHumanResult(result)}\n`);
861
+ const color = process.stdout.isTTY === true &&
862
+ process.env.NO_COLOR === undefined &&
863
+ process.env.TERM !== "dumb";
864
+ const rendered = `${renderHumanResult(result, {
865
+ color,
866
+ width: process.stdout.columns,
867
+ })}\n`;
868
+ if (result.command === "docs" &&
869
+ process.stdout.isTTY === true &&
870
+ rendered.split("\n").length > (process.stdout.rows ?? 24)) {
871
+ await writeThroughPager(rendered);
872
+ }
873
+ else
874
+ process.stdout.write(rendered);
705
875
  if (result.status !== "SUCCEEDED")
706
876
  process.exitCode = 1;
707
877
  }
878
+ async function writeThroughPager(content) {
879
+ await new Promise((resolvePromise) => {
880
+ const pager = spawn("less", ["-FIRX"], {
881
+ stdio: ["pipe", "inherit", "inherit"],
882
+ });
883
+ pager.once("error", () => {
884
+ process.stdout.write(content);
885
+ resolvePromise();
886
+ });
887
+ pager.once("close", () => resolvePromise());
888
+ pager.stdin.end(content);
889
+ });
890
+ }
@@ -6,7 +6,11 @@ export interface WorkspacePackageManagerSelection {
6
6
  declared?: string;
7
7
  }
8
8
  export interface PackageCommandRunner {
9
- run(command: string, args: readonly string[], cwd: string): Promise<string>;
9
+ run(command: string, args: readonly string[], cwd: string, onOutput?: (line: PackageCommandOutput) => void): Promise<string>;
10
+ }
11
+ export interface PackageCommandOutput {
12
+ stream: "stdout" | "stderr";
13
+ line: string;
10
14
  }
11
15
  export declare function systemPackageCommandRunner(): PackageCommandRunner;
12
16
  export declare function readWorkspacePackageManagerSelection(workspaceRoot: string): Promise<WorkspacePackageManagerSelection>;
@@ -24,11 +28,13 @@ export declare function syncWorkspacePackages(options: {
24
28
  packages: readonly WorkspacePackageTarget[];
25
29
  runner?: PackageCommandRunner;
26
30
  executableAvailable?: (command: string) => boolean;
31
+ onOutput?: (line: PackageCommandOutput) => void;
27
32
  }): Promise<WorkspacePackageMutationResult>;
28
33
  export declare function removeWorkspacePackages(options: {
29
34
  workspaceRoot: string;
30
35
  packageNames: readonly string[];
31
36
  runner?: PackageCommandRunner;
32
37
  executableAvailable?: (command: string) => boolean;
38
+ onOutput?: (line: PackageCommandOutput) => void;
33
39
  }): Promise<WorkspacePackageMutationResult>;
34
40
  export declare function observeWorkspaceInstalledVersion(workspaceRoot: string, packageName: string): Promise<string | undefined>;
@@ -1,20 +1,58 @@
1
- import { execFile } from "node:child_process";
1
+ import { spawn } from "node:child_process";
2
2
  import { accessSync, constants } from "node:fs";
3
3
  import { access, readFile } from "node:fs/promises";
4
4
  import { delimiter, join } from "node:path";
5
- import { promisify } from "node:util";
6
5
  import { PlatformError } from "../errors.js";
7
6
  import { atomicWrite } from "../paths.js";
8
- const execFileAsync = promisify(execFile);
9
7
  export function systemPackageCommandRunner() {
10
8
  return {
11
- async run(command, args, cwd) {
12
- const result = await execFileAsync(command, [...args], {
13
- cwd,
14
- encoding: "utf8",
15
- maxBuffer: 4 * 1024 * 1024,
9
+ async run(command, args, cwd, onOutput) {
10
+ return new Promise((resolvePromise, rejectPromise) => {
11
+ const child = spawn(command, [...args], {
12
+ cwd,
13
+ stdio: ["ignore", "pipe", "pipe"],
14
+ });
15
+ let stdout = "";
16
+ let stderr = "";
17
+ const pending = {
18
+ stdout: "",
19
+ stderr: "",
20
+ };
21
+ const consume = (stream, chunk) => {
22
+ const text = chunk.toString("utf8");
23
+ if (stream === "stdout")
24
+ stdout += text;
25
+ else
26
+ stderr += text;
27
+ const parts = `${pending[stream]}${text}`.split(/\r?\n|\r/);
28
+ pending[stream] = parts.pop() ?? "";
29
+ for (const line of parts) {
30
+ const normalized = line.trim();
31
+ if (normalized)
32
+ onOutput?.({ stream, line: normalized });
33
+ }
34
+ };
35
+ const flush = () => {
36
+ for (const stream of ["stdout", "stderr"]) {
37
+ const line = pending[stream].trim();
38
+ if (line)
39
+ onOutput?.({ stream, line });
40
+ pending[stream] = "";
41
+ }
42
+ };
43
+ child.stdout.on("data", (chunk) => consume("stdout", chunk));
44
+ child.stderr.on("data", (chunk) => consume("stderr", chunk));
45
+ child.once("error", rejectPromise);
46
+ child.once("close", (code, signal) => {
47
+ flush();
48
+ if (code === 0)
49
+ resolvePromise(stdout);
50
+ else {
51
+ const detail = stderr.trim() || stdout.trim();
52
+ rejectPromise(new Error(`${command} exited with ${code ?? signal ?? "unknown"}${detail ? `: ${detail}` : ""}`));
53
+ }
54
+ });
16
55
  });
17
- return result.stdout;
18
56
  },
19
57
  };
20
58
  }
@@ -125,7 +163,7 @@ export async function syncWorkspacePackages(options) {
125
163
  await writeManagedPackageSet(options.workspaceRoot, targets);
126
164
  const args = await packageManagerInstallArgs(runner, manager, options.workspaceRoot);
127
165
  try {
128
- await runner.run(manager.name, args, options.workspaceRoot);
166
+ await runner.run(manager.name, args, options.workspaceRoot, options.onOutput);
129
167
  }
130
168
  catch (error) {
131
169
  throw new PlatformError("COMMAND_FAILED", `${manager.name} install failed: ${packageMutationError(error)}`);
@@ -177,7 +215,7 @@ export async function removeWorkspacePackages(options) {
177
215
  const manager = await requireWorkspacePackageManager(options.workspaceRoot, options.executableAvailable ?? findExecutable);
178
216
  const args = await batchPackageManagerArgs(runner, manager, options.workspaceRoot, "remove", packageNames);
179
217
  try {
180
- await runner.run(manager.name, args, options.workspaceRoot);
218
+ await runner.run(manager.name, args, options.workspaceRoot, options.onOutput);
181
219
  }
182
220
  catch (error) {
183
221
  throw new PlatformError("UNINSTALL_FAILED", `${manager.name} remove failed: ${packageMutationError(error)}`);
@@ -14,6 +14,7 @@ export interface ModuleBatchResult {
14
14
  blockers?: Array<{
15
15
  moduleRef: string;
16
16
  setupStatus: ModuleSetupStatus;
17
+ reason?: string;
17
18
  }>;
18
19
  skipped?: Array<{
19
20
  moduleRef: string;
@@ -250,7 +250,11 @@ export async function startModulesThin(catalog, modules, workspaceRoot, reporter
250
250
  message: `${module.moduleRef}`,
251
251
  });
252
252
  if (!succeeded(status.result)) {
253
- blockers.push({ moduleRef: module.moduleRef, setupStatus: "FAILED" });
253
+ blockers.push({
254
+ moduleRef: module.moduleRef,
255
+ setupStatus: "FAILED",
256
+ reason: status.result.error?.message ?? "模块状态检查失败",
257
+ });
254
258
  continue;
255
259
  }
256
260
  const observed = moduleStatusObservationSchema.parse(status.result.data);
@@ -1,10 +1,13 @@
1
- export type PlatformProgressStatus = "STARTED" | "SUCCEEDED" | "ACTION_REQUIRED" | "FAILED" | "SKIPPED";
1
+ export type PlatformProgressStatus = "STARTED" | "SUCCEEDED" | "WARNING" | "ACTION_REQUIRED" | "FAILED" | "SKIPPED";
2
+ export type PlatformProgressKind = "phase" | "detail" | "subprocess";
2
3
  export interface PlatformProgressEvent {
3
4
  command: string;
4
5
  phase: string;
6
+ kind?: PlatformProgressKind;
5
7
  current?: number;
6
8
  total?: number;
7
9
  moduleRef?: string;
10
+ elapsedMs?: number;
8
11
  status: PlatformProgressStatus;
9
12
  message: string;
10
13
  }
@@ -34,4 +34,10 @@ export declare function discoverRegistryModules(options: {
34
34
  packageName?: string;
35
35
  runner?: NpmCommandRunner;
36
36
  nodeVersion?: string;
37
+ onSearchComplete?: (total: number) => void;
38
+ onPackageChecked?: (input: {
39
+ current: number;
40
+ total: number;
41
+ packageName: string;
42
+ }) => void;
37
43
  }): Promise<RegistryDiscoveryResult>;
@@ -42,16 +42,32 @@ export async function discoverRegistryModules(options) {
42
42
  const names = options.packageName === undefined
43
43
  ? await searchPackageNames(options.workspaceRoot, registry, runner)
44
44
  : [validateRequestedPackageName(options.packageName)];
45
+ options.onSearchComplete?.(names.length);
45
46
  const candidates = [];
46
47
  const rejected = [];
47
- for (const packageName of names) {
48
- const manifest = await viewManifest(options.workspaceRoot, registry, packageName, runner);
49
- const assessed = assessManifest(manifest, registry, options.nodeVersion ?? process.versions.node);
50
- if ("candidate" in assessed)
51
- candidates.push(assessed.candidate);
52
- else
53
- rejected.push(assessed.rejected);
54
- }
48
+ let nextIndex = 0;
49
+ let completed = 0;
50
+ const worker = async () => {
51
+ while (nextIndex < names.length) {
52
+ const index = nextIndex++;
53
+ const packageName = names[index];
54
+ if (packageName === undefined)
55
+ return;
56
+ const manifest = await viewManifest(options.workspaceRoot, registry, packageName, runner);
57
+ const assessed = assessManifest(manifest, registry, options.nodeVersion ?? process.versions.node);
58
+ if ("candidate" in assessed)
59
+ candidates.push(assessed.candidate);
60
+ else
61
+ rejected.push(assessed.rejected);
62
+ completed += 1;
63
+ options.onPackageChecked?.({
64
+ current: completed,
65
+ total: names.length,
66
+ packageName,
67
+ });
68
+ }
69
+ };
70
+ await Promise.all(Array.from({ length: Math.min(4, names.length) }, () => worker()));
55
71
  candidates.sort((left, right) => left.packageName.localeCompare(right.packageName));
56
72
  rejected.sort((left, right) => left.packageName.localeCompare(right.packageName));
57
73
  return { registry, candidates, rejected };
@@ -1,40 +1,58 @@
1
1
  import { clearLine, cursorTo } from "node:readline";
2
2
  const colors = {
3
+ cyan: "\u001b[36m",
4
+ blue: "\u001b[34m",
3
5
  green: "\u001b[32m",
4
6
  red: "\u001b[31m",
5
7
  yellow: "\u001b[33m",
8
+ bold: "\u001b[1m",
6
9
  dim: "\u001b[2m",
7
10
  reset: "\u001b[0m",
8
11
  };
9
12
  const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
10
13
  function progressLine(event, color, spinner = "") {
11
- const prefix = event.current && event.total ? `${event.current}/${event.total} · ` : "";
14
+ const prefix = event.current && event.total
15
+ ? `[${String(event.current).padStart(String(event.total).length, "0")}/${event.total}] `
16
+ : "";
17
+ const elapsed = event.elapsedMs !== undefined
18
+ ? ` · ${(event.elapsedMs / 1000).toFixed(1)}s`
19
+ : "";
12
20
  const suffix = event.status === "SUCCEEDED"
13
21
  ? "完成"
14
- : event.status === "ACTION_REQUIRED"
15
- ? "待处理"
16
- : event.status === "FAILED"
17
- ? "失败"
18
- : event.status === "SKIPPED"
19
- ? "跳过"
20
- : "";
21
- const symbol = event.status === "STARTED"
22
- ? spinner
23
- : event.status === "SUCCEEDED"
24
- ? ""
25
- : event.status === "FAILED"
26
- ? "✕"
27
- : event.status === "ACTION_REQUIRED"
28
- ? ""
29
- : "";
22
+ : event.status === "WARNING"
23
+ ? "警告"
24
+ : event.status === "ACTION_REQUIRED"
25
+ ? "待处理"
26
+ : event.status === "FAILED"
27
+ ? "失败"
28
+ : event.status === "SKIPPED"
29
+ ? "跳过"
30
+ : "";
31
+ const symbol = event.kind === "subprocess"
32
+ ? ""
33
+ : event.status === "STARTED"
34
+ ? spinner
35
+ : event.status === "SUCCEEDED"
36
+ ? ""
37
+ : event.status === "WARNING"
38
+ ? "!"
39
+ : event.status === "FAILED"
40
+ ? "✕"
41
+ : event.status === "ACTION_REQUIRED"
42
+ ? "◆"
43
+ : "○";
30
44
  if (!color)
31
- return `${symbol ? `${symbol} ` : ""}${prefix}${event.message}${suffix ? ` · ${suffix}` : ""}`;
45
+ return `${symbol ? `${symbol} ` : ""}${prefix}${event.message}${suffix ? ` · ${suffix}` : ""}${elapsed}`;
32
46
  const tone = event.status === "SUCCEEDED"
33
47
  ? colors.green
34
- : event.status === "FAILED"
35
- ? colors.red
36
- : colors.yellow;
37
- return `${tone}${symbol}${colors.reset} ${colors.dim}${prefix}${colors.reset}${event.message}${suffix ? ` · ${tone}${suffix}${colors.reset}` : ""}`;
48
+ : event.status === "WARNING"
49
+ ? colors.yellow
50
+ : event.status === "FAILED"
51
+ ? colors.red
52
+ : event.kind === "subprocess"
53
+ ? colors.blue
54
+ : colors.yellow;
55
+ return `${tone}${symbol}${colors.reset} ${colors.dim}${prefix}${colors.reset}${event.message}${suffix ? ` · ${tone}${suffix}${colors.reset}` : ""}${event.elapsedMs === undefined ? "" : `${colors.dim}${elapsed}${colors.reset}`}`;
38
56
  }
39
57
  export function createTerminalProgressReporter(stream = process.stderr) {
40
58
  const interactive = stream.isTTY === true;
@@ -58,7 +76,10 @@ export function createTerminalProgressReporter(stream = process.stderr) {
58
76
  };
59
77
  const reporter = ((event) => {
60
78
  clearSpinner();
61
- active = event.status === "STARTED" ? event : undefined;
79
+ const previousActive = active;
80
+ if (event.kind !== "subprocess") {
81
+ active = event.status === "STARTED" ? event : undefined;
82
+ }
62
83
  const line = progressLine(event, color, interactive ? spinnerFrames[frame++ % spinnerFrames.length] : "›");
63
84
  if (!interactive) {
64
85
  stream.write(`${line}\n`);
@@ -67,18 +88,22 @@ export function createTerminalProgressReporter(stream = process.stderr) {
67
88
  cursorTo(stream, 0);
68
89
  clearLine(stream, 0);
69
90
  stream.write(line);
91
+ if (event.kind === "subprocess") {
92
+ stream.write("\n");
93
+ active = previousActive;
94
+ if (active) {
95
+ paint();
96
+ timer = setInterval(paint, 80);
97
+ timer.unref();
98
+ }
99
+ return;
100
+ }
70
101
  if (event.status === "STARTED") {
71
102
  timer = setInterval(paint, 80);
72
103
  timer.unref();
73
104
  return;
74
105
  }
75
- const compactModuleProgress = event.current !== undefined &&
76
- event.total !== undefined &&
77
- event.current < event.total &&
78
- event.status !== "FAILED" &&
79
- event.status !== "ACTION_REQUIRED";
80
- if (!compactModuleProgress)
81
- stream.write("\n");
106
+ stream.write("\n");
82
107
  });
83
108
  reporter.close = () => {
84
109
  clearSpinner();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-platform-cli",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -24,11 +24,11 @@
24
24
  "SETUP.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@tomflow/proflow-module-contract": "^0.1.10"
27
+ "@tomflow/proflow-module-contract": "^0.1.11"
28
28
  },
29
29
  "devDependencies": {
30
- "@tomflow/proflow-module-template": "^0.1.8",
31
- "@tomflow/proflow-deployment-conformance": "^0.1.8"
30
+ "@tomflow/proflow-deployment-conformance": "^0.1.9",
31
+ "@tomflow/proflow-module-template": "^0.1.9"
32
32
  },
33
33
  "description": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration.",
34
34
  "keywords": [
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "platform-cli",
5
5
  "packageName": "@tomflow/proflow-platform-cli",
6
- "moduleVersion": "0.1.33",
6
+ "moduleVersion": "0.1.35",
7
7
  "kind": "cli",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",