@tomflow/proflow-platform-cli 0.1.35 → 0.1.36

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,11 @@
1
1
  # @tomflow/proflow-platform-cli
2
2
 
3
+ ## 0.1.36
4
+
5
+ ### Patch Changes
6
+
7
+ - Distinguish dependency blocking from verified failure, expose Module-owned status diagnostics, remove automatic docs paging, consolidate start checks, add contextual help, and clarify uninstall output.
8
+
3
9
  ## 0.1.35
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -41,7 +41,7 @@ Platform does not interpret Module-private configuration or recreate a cross-Mod
41
41
  `platform status` validates and aggregates only Module-owned observations:
42
42
 
43
43
  ```text
44
- setupStatus: READY | ACTION_REQUIRED | FAILED
44
+ setupStatus: READY | ACTION_REQUIRED | BLOCKED | FAILED
45
45
  runtimeStatus: RUNNING | STOPPED | FAILED | NOT_APPLICABLE
46
46
  ```
47
47
 
@@ -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.35";
9
+ readonly moduleVersion: "0.1.36";
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.35";
19
+ readonly moduleVersion: "0.1.36";
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.35";
29
+ moduleVersion: "0.1.36";
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.35";
43
+ readonly moduleVersion: "0.1.36";
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.35";
53
+ moduleVersion: "0.1.36";
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.35";
66
+ readonly moduleVersion: "0.1.36";
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.35";
76
+ readonly moduleVersion: "0.1.36";
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.35";
6
+ readonly moduleVersion: "0.1.36";
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.35",
6
+ moduleVersion: "0.1.36",
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
@@ -2,6 +2,8 @@
2
2
  import { type PackageCommandRunner } from "./install/package-manager.ts";
3
3
  import { type PlatformProgressReporter } from "./progress.ts";
4
4
  import { type NpmCommandRunner } from "./registry/index.ts";
5
+ declare const COMMANDS: readonly ["install", "uninstall", "status", "setup", "docs", "start", "stop"];
6
+ type Command = (typeof COMMANDS)[number];
5
7
  export type CliStatus = "SUCCEEDED" | "ACTION_REQUIRED" | "BLOCKED" | "FAILED";
6
8
  export interface CliOutcome {
7
9
  command: string;
@@ -11,6 +13,8 @@ export interface CliOutcome {
11
13
  error?: {
12
14
  code: string;
13
15
  message: string;
16
+ category?: "USAGE" | "OPERATION";
17
+ helpCommand?: Command;
14
18
  };
15
19
  }
16
20
  export interface CliRuntimeOptions {
@@ -26,3 +30,4 @@ export interface HumanRenderOptions {
26
30
  width?: number;
27
31
  }
28
32
  export declare function renderHumanResult(result: CliOutcome, options?: HumanRenderOptions): string;
33
+ export {};
package/dist/src/cli.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { spawn } from "node:child_process";
3
2
  import { readFile, realpath, stat } from "node:fs/promises";
4
3
  import { resolve } from "node:path";
5
4
  import { moduleDocsDataSchema, moduleSetupPlanDataSchema, moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
@@ -120,7 +119,9 @@ function batchStatus(result) {
120
119
  if (result.blockedBy)
121
120
  return result.blockedBy.setupStatus === "ACTION_REQUIRED"
122
121
  ? "ACTION_REQUIRED"
123
- : "FAILED";
122
+ : result.blockedBy.setupStatus === "BLOCKED"
123
+ ? "BLOCKED"
124
+ : "FAILED";
124
125
  const statuses = result.results.map((item) => statusFromModule(item.result.status));
125
126
  if (statuses.includes("FAILED"))
126
127
  return "FAILED";
@@ -154,6 +155,9 @@ async function handleStatus(root, runtime) {
154
155
  version: module.moduleVersion,
155
156
  setupStatus: parsed.data.setupStatus,
156
157
  runtimeStatus: parsed.data.runtimeStatus,
158
+ ...(parsed.data.issues === undefined
159
+ ? {}
160
+ : { issues: parsed.data.issues }),
157
161
  };
158
162
  });
159
163
  reportProgress(runtime.onProgress, {
@@ -457,7 +461,8 @@ export async function runCli(argv, runtime = {}) {
457
461
  parsed = parseArgs(argv);
458
462
  }
459
463
  catch (error) {
460
- return errorOutcome("unknown", error);
464
+ const helpCommand = argv.find((value) => COMMANDS.includes(value));
465
+ return errorOutcome("unknown", error, "USAGE", helpCommand);
461
466
  }
462
467
  try {
463
468
  if (parsed.command === "help")
@@ -488,12 +493,17 @@ export async function runCli(argv, runtime = {}) {
488
493
  return errorOutcome(parsed.command, error);
489
494
  }
490
495
  }
491
- function errorOutcome(command, error) {
496
+ function errorOutcome(command, error, category = "OPERATION", helpCommand) {
492
497
  if (error instanceof PlatformError)
493
498
  return {
494
499
  command,
495
500
  status: "FAILED",
496
- error: { code: error.code, message: error.message },
501
+ error: {
502
+ code: error.code,
503
+ message: error.message,
504
+ category,
505
+ ...(helpCommand === undefined ? {} : { helpCommand }),
506
+ },
497
507
  };
498
508
  return {
499
509
  command,
@@ -501,6 +511,7 @@ function errorOutcome(command, error) {
501
511
  error: {
502
512
  code: "COMMAND_FAILED",
503
513
  message: error instanceof Error ? error.message : String(error),
514
+ category,
504
515
  },
505
516
  };
506
517
  }
@@ -524,6 +535,7 @@ function humanTheme(options = {}) {
524
535
  title: (value) => paint("bold", paint("cyan", value)),
525
536
  section: (value) => paint("bold", value),
526
537
  command: (value) => paint("blue", value),
538
+ info: (value) => paint("blue", value),
527
539
  muted: (value) => paint("dim", value),
528
540
  success: (value) => paint("green", value),
529
541
  warning: (value) => paint("yellow", value),
@@ -536,6 +548,7 @@ function renderStatus(data, theme) {
536
548
  const setupLabels = {
537
549
  READY: "已就绪",
538
550
  ACTION_REQUIRED: "需要操作",
551
+ BLOCKED: "等待依赖",
539
552
  FAILED: "失败",
540
553
  };
541
554
  const runtimeLabels = {
@@ -545,40 +558,40 @@ function renderStatus(data, theme) {
545
558
  NOT_APPLICABLE: "无独立进程",
546
559
  };
547
560
  const modules = data.modules.filter(isRecord);
548
- const unhealthy = modules.filter((item) => item.setupStatus !== "READY" || item.runtimeStatus === "FAILED");
561
+ const failedModules = modules.filter((item) => item.setupStatus === "FAILED" || item.runtimeStatus === "FAILED");
562
+ const actionModules = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED" && item.runtimeStatus !== "FAILED");
563
+ const blockedModules = modules.filter((item) => item.setupStatus === "BLOCKED" && item.runtimeStatus !== "FAILED");
549
564
  const running = modules.filter((item) => item.setupStatus === "READY" && item.runtimeStatus === "RUNNING");
550
565
  const healthy = modules.filter((item) => item.setupStatus === "READY" &&
551
566
  item.runtimeStatus !== "RUNNING" &&
552
567
  item.runtimeStatus !== "FAILED");
553
568
  const lines = [theme.title("ProFlow 状态"), ""];
554
- if (unhealthy.length > 0)
555
- lines.push(theme.section("需要处理"), "");
556
- for (const raw of unhealthy) {
557
- const moduleRef = String(raw.moduleRef);
558
- const setupStatus = String(raw.setupStatus);
559
- const runtimeStatus = String(raw.runtimeStatus);
560
- const failed = setupStatus === "FAILED" || runtimeStatus === "FAILED";
561
- const actionRequired = setupStatus === "ACTION_REQUIRED";
562
- const icon = failed
563
- ? ""
564
- : actionRequired
565
- ? "◆"
566
- : runtimeStatus === "RUNNING"
567
- ? "●"
568
- : runtimeStatus === "NOT_APPLICABLE"
569
- ? "○"
570
- : "✓";
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] ?? "未知"}`);
574
- if (failed) {
575
- lines.push(` 原因:${setupStatus === "FAILED" ? "模块配置检查失败" : "模块运行状态异常"}`, ` 下一步:${theme.command(`platform setup --module ${moduleRef}`)}`);
576
- }
577
- else if (actionRequired) {
578
- lines.push(" 原因:模块尚未完成配置", ` 下一步:${theme.command(`platform setup --module ${moduleRef}`)}`);
569
+ const renderProblems = (title, entries, tone, icon) => {
570
+ if (entries.length === 0)
571
+ return;
572
+ lines.push(theme.section(title), "");
573
+ for (const raw of entries) {
574
+ const moduleRef = String(raw.moduleRef);
575
+ const setupStatus = String(raw.setupStatus);
576
+ const runtimeStatus = String(raw.runtimeStatus);
577
+ lines.push(`${tone(icon)} ${theme.section(moduleRef)} ${theme.muted(String(raw.version))}`);
578
+ lines.push(` ${tone(setupLabels[setupStatus] ?? "未知")} · ${runtimeLabels[runtimeStatus] ?? "未知"}`);
579
+ const issues = Array.isArray(raw.issues)
580
+ ? raw.issues.filter(isRecord)
581
+ : [];
582
+ for (const issue of issues) {
583
+ lines.push(` 原因:${String(issue.message)}`);
584
+ if (Array.isArray(issue.relatedModuleRefs) &&
585
+ issue.relatedModuleRefs.length > 0)
586
+ lines.push(` 依赖:${issue.relatedModuleRefs.map(String).join("、")}`);
587
+ lines.push(` 下一步:${theme.command(String(issue.nextCommand))}`);
588
+ }
589
+ lines.push("");
579
590
  }
580
- lines.push("");
581
- }
591
+ };
592
+ renderProblems("失败", failedModules, theme.failure, "✕");
593
+ renderProblems("需要操作", actionModules, theme.warning, "◆");
594
+ renderProblems("等待依赖", blockedModules, theme.info, "◇");
582
595
  const compact = (title, entries, symbol) => {
583
596
  if (entries.length === 0)
584
597
  return;
@@ -594,7 +607,8 @@ function renderStatus(data, theme) {
594
607
  const ready = modules.filter((item) => item.setupStatus === "READY").length;
595
608
  const action = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED").length;
596
609
  const failed = modules.filter((item) => item.setupStatus === "FAILED").length;
597
- lines.push(theme.section("汇总"), `${theme.success(`${ready} 已就绪`)} · ${theme.warning(`${action} 需要操作`)} · ${failed > 0 ? theme.failure(`${failed} 失败`) : `${failed} 失败`}`);
610
+ const blocked = modules.filter((item) => item.setupStatus === "BLOCKED").length;
611
+ lines.push(theme.section("汇总"), `${theme.success(`${ready} 已就绪`)} · ${theme.warning(`${action} 需要操作`)} · ${theme.info(`${blocked} 等待依赖`)} · ${failed > 0 ? theme.failure(`${failed} 失败`) : `${failed} 失败`}`);
598
612
  return lines.join("\n");
599
613
  }
600
614
  function renderTerminalMarkdown(source, theme) {
@@ -750,6 +764,86 @@ function renderSetup(data, theme) {
750
764
  lines.push(theme.section("汇总"), `汇总:${theme.success(`${ready} 个已就绪`)},${theme.warning(`${needsAction} 个需要操作`)},${blocked > 0 ? theme.failure(`${blocked} 个系统阻塞`) : `${blocked} 个系统阻塞`}`);
751
765
  return lines.join("\n").trimEnd();
752
766
  }
767
+ function renderHelp(theme, command) {
768
+ const commandHelp = {
769
+ install: {
770
+ usage: "platform install [--workspace <路径>]",
771
+ description: "安装并初始化全部 ProFlow 模块",
772
+ },
773
+ uninstall: {
774
+ usage: "platform uninstall [--workspace <路径>]",
775
+ description: "卸载模块包,保留 Workspace 数据",
776
+ },
777
+ status: {
778
+ usage: "platform status [--workspace <路径>]",
779
+ description: "查看模块配置与运行状态",
780
+ },
781
+ setup: {
782
+ usage: "platform setup [--workspace <路径>] [--module <模块名>]",
783
+ description: "自动配置并列出全部剩余步骤",
784
+ },
785
+ docs: {
786
+ usage: "platform docs [--workspace <路径>]",
787
+ description: "阅读全部模块能力文档",
788
+ },
789
+ start: {
790
+ usage: "platform start [--workspace <路径>]",
791
+ description: "完成全量检查后启动平台",
792
+ },
793
+ stop: {
794
+ usage: "platform stop [--workspace <路径>]",
795
+ description: "按逆依赖顺序停止平台",
796
+ },
797
+ };
798
+ if (command) {
799
+ const item = commandHelp[command];
800
+ return [
801
+ theme.title(`platform ${command}`),
802
+ item.description,
803
+ "",
804
+ theme.section("用法"),
805
+ ` ${theme.command(item.usage)}`,
806
+ "",
807
+ theme.section("帮助"),
808
+ ` ${theme.command(`platform ${command} --help`)}`,
809
+ ].join("\n");
810
+ }
811
+ return [
812
+ theme.title("ProFlow 平台命令行"),
813
+ "管理 ProFlow 模块的安装、配置、文档和运行生命周期。",
814
+ "",
815
+ theme.section("用法"),
816
+ ` ${theme.command("platform <command> [--workspace <路径>]")}`,
817
+ "",
818
+ theme.section("命令"),
819
+ ...Object.entries(commandHelp).map(([name, item]) => ` ${theme.command(`platform ${name}`.padEnd(21))}${item.description}`),
820
+ "",
821
+ theme.section("公共参数"),
822
+ " --workspace <路径> 指定 ProFlow Workspace",
823
+ " setup --module <模块名> 只查看指定模块的配置步骤",
824
+ " -h, --help 显示帮助",
825
+ " -v, --version 显示版本",
826
+ "",
827
+ theme.section("推荐流程"),
828
+ ` ${theme.command("install → status → docs → setup → start → status → stop")}`,
829
+ "",
830
+ theme.section("人工配置示例"),
831
+ ` 人工:${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
832
+ ` AI: ${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
833
+ ` ${theme.command("--carrier-url <url>")}`,
834
+ "",
835
+ theme.section("状态图例"),
836
+ ` ${theme.success("已就绪")} 配置与验证完成`,
837
+ ` ${theme.warning("需要操作")} 需要执行 setup 步骤`,
838
+ ` ${theme.info("等待依赖")} 等待上游 Module 或外部服务`,
839
+ ` ${theme.failure("失败")} 已确认配置或运行故障`,
840
+ " 运行中 服务进程正在运行",
841
+ " 无独立进程 该模块无需启动",
842
+ "",
843
+ theme.section("遇到问题"),
844
+ ` 先运行 ${theme.command("platform status")};配置问题运行 ${theme.command("platform setup")}。`,
845
+ ].join("\n");
846
+ }
753
847
  export function renderHumanResult(result, options = {}) {
754
848
  const theme = humanTheme(options);
755
849
  if (result.command === "setup" &&
@@ -760,18 +854,35 @@ export function renderHumanResult(result, options = {}) {
760
854
  const blockers = Array.isArray(result.data.blockers)
761
855
  ? result.data.blockers.filter(isRecord)
762
856
  : [];
763
- if (blockers.length > 0)
764
- return [
765
- theme.failure("平台未启动:存在未就绪模块"),
766
- "",
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
- ]),
772
- "",
773
- `处理方式:${theme.command(`platform setup${result.workspaceRoot ? ` --workspace "${result.workspaceRoot}"` : ""}`)}`,
774
- ].join("\n");
857
+ if (blockers.length > 0) {
858
+ const groups = [
859
+ { status: "FAILED", title: "失败", icon: "✕", tone: theme.failure },
860
+ {
861
+ status: "ACTION_REQUIRED",
862
+ title: "需要操作",
863
+ icon: "",
864
+ tone: theme.warning,
865
+ },
866
+ { status: "BLOCKED", title: "等待依赖", icon: "◇", tone: theme.info },
867
+ ];
868
+ const lines = [theme.failure("平台未启动:存在未就绪模块"), ""];
869
+ for (const group of groups) {
870
+ const entries = blockers.filter((item) => item.setupStatus === group.status);
871
+ if (entries.length === 0)
872
+ continue;
873
+ lines.push(theme.section(group.title));
874
+ for (const item of entries)
875
+ lines.push(`${group.tone(group.icon)} ${theme.section(String(item.moduleRef))}`, ` 原因:${typeof item.reason === "string" ? item.reason : "模块尚未就绪"}`, ` 下一步:${theme.command(typeof item.nextCommand === "string" ? item.nextCommand : `platform setup --module ${String(item.moduleRef)}`)}`);
876
+ lines.push("");
877
+ }
878
+ const readyCount = Array.isArray(result.data.results)
879
+ ? result.data.results.filter((item) => isRecord(item) &&
880
+ isRecord(item.result) &&
881
+ item.result.status === "SUCCEEDED").length - blockers.length
882
+ : 0;
883
+ lines.push(theme.section("检查汇总"), `${Math.max(0, readyCount)} 已就绪 · ${blockers.length} 未就绪 · 0 个模块已启动`, `处理方式:${theme.command(`platform setup${result.workspaceRoot ? ` --workspace "${result.workspaceRoot}"` : ""}`)}`);
884
+ return lines.join("\n");
885
+ }
775
886
  }
776
887
  if (result.status === "FAILED" && result.error)
777
888
  return [
@@ -779,42 +890,12 @@ export function renderHumanResult(result, options = {}) {
779
890
  `${theme.failure("│")} ${theme.section(result.error.code)}`,
780
891
  `${theme.failure("│")} ${result.error.message}`,
781
892
  theme.failure("╰─ 请根据提示修复后重试"),
893
+ ...(result.error.category === "USAGE"
894
+ ? ["", renderHelp(theme, result.error.helpCommand)]
895
+ : []),
782
896
  ].join("\n");
783
897
  if (result.command === "help")
784
- return [
785
- theme.title("ProFlow 平台命令行"),
786
- "管理 ProFlow 模块的安装、配置、文档和运行生命周期。",
787
- "",
788
- theme.section("用法"),
789
- ` ${theme.command("platform <command> [--workspace <路径>]")}`,
790
- "",
791
- theme.section("命令"),
792
- `${theme.command("platform install")} 安装并初始化全部 ProFlow 模块`,
793
- "platform uninstall 卸载模块包,保留 Workspace 数据",
794
- "platform status 查看模块配置与运行状态",
795
- "platform setup 自动配置并列出全部剩余步骤",
796
- "platform docs 阅读模块能力文档",
797
- "platform start 完成全量检查后启动平台",
798
- "platform stop 按逆依赖顺序停止平台",
799
- "",
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")}。`,
817
- ].join("\n");
898
+ return renderHelp(theme);
818
899
  if (result.command === "version" && isRecord(result.data))
819
900
  return String(result.data.version ?? platformCliDescriptor.moduleVersion);
820
901
  if (result.command === "status")
@@ -851,6 +932,8 @@ export function renderHumanResult(result, options = {}) {
851
932
  start: "启动",
852
933
  stop: "停止",
853
934
  };
935
+ if (result.command === "uninstall" && result.status === "SUCCEEDED")
936
+ return `${theme.success("✓ 已经卸载")}${result.workspaceRoot ? `\n${theme.muted("Workspace")} ${result.workspaceRoot}` : ""}`;
854
937
  return `${result.status === "SUCCEEDED" ? theme.success("✓") : theme.failure("✕")} ${labels[result.command] ?? result.command}${result.status === "SUCCEEDED" ? "成功" : "未完成"}${result.workspaceRoot ? `\n${theme.muted("Workspace")} ${result.workspaceRoot}` : ""}`;
855
938
  }
856
939
  if (import.meta.main) {
@@ -865,26 +948,7 @@ if (import.meta.main) {
865
948
  color,
866
949
  width: process.stdout.columns,
867
950
  })}\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);
951
+ process.stdout.write(rendered);
875
952
  if (result.status !== "SUCCEEDED")
876
953
  process.exitCode = 1;
877
954
  }
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
- }
@@ -15,6 +15,7 @@ export interface ModuleBatchResult {
15
15
  moduleRef: string;
16
16
  setupStatus: ModuleSetupStatus;
17
17
  reason?: string;
18
+ nextCommand?: string;
18
19
  }>;
19
20
  skipped?: Array<{
20
21
  moduleRef: string;
@@ -78,7 +78,9 @@ async function runOrdered(catalog, modules, workspaceRoot, command, reverse, rep
78
78
  total: modulesInOrder.length,
79
79
  moduleRef: module.moduleRef,
80
80
  status: "STARTED",
81
- message: `${module.moduleRef}`,
81
+ message: command === "uninstall"
82
+ ? `正在卸载 ${module.moduleRef}`
83
+ : `${module.moduleRef}`,
82
84
  });
83
85
  const result = await dispatchModuleCommand(catalog, module, command, context(workspaceRoot));
84
86
  results.push(result);
@@ -232,22 +234,26 @@ export async function startModulesThin(catalog, modules, workspaceRoot, reporter
232
234
  reportProgress(reporter, {
233
235
  command: "start",
234
236
  phase: "status",
237
+ kind: "detail",
238
+ retention: "REPLACE",
235
239
  current: index + 1,
236
240
  total: modulesInOrder.length,
237
241
  moduleRef: module.moduleRef,
238
242
  status: "STARTED",
239
- message: `${module.moduleRef}`,
243
+ message: `正在检查模块状态 · ${module.moduleRef}`,
240
244
  });
241
245
  const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
242
246
  results.push(status);
243
247
  reportProgress(reporter, {
244
248
  command: "start",
245
249
  phase: "status",
250
+ kind: "detail",
251
+ retention: "REPLACE",
246
252
  current: index + 1,
247
253
  total: modulesInOrder.length,
248
254
  moduleRef: module.moduleRef,
249
255
  status: succeeded(status.result) ? "SUCCEEDED" : "FAILED",
250
- message: `${module.moduleRef}`,
256
+ message: `正在检查模块状态 · ${module.moduleRef}`,
251
257
  });
252
258
  if (!succeeded(status.result)) {
253
259
  blockers.push({
@@ -259,11 +265,19 @@ export async function startModulesThin(catalog, modules, workspaceRoot, reporter
259
265
  }
260
266
  const observed = moduleStatusObservationSchema.parse(status.result.data);
261
267
  runtimeByRef.set(module.moduleRef, observed.runtimeStatus);
262
- if (observed.setupStatus !== "READY")
268
+ if (observed.setupStatus !== "READY") {
269
+ const issue = observed.issues?.find((item) => item.scope === "SETUP");
263
270
  blockers.push({
264
271
  moduleRef: module.moduleRef,
265
272
  setupStatus: observed.setupStatus,
273
+ ...(issue
274
+ ? {
275
+ reason: issue.message,
276
+ nextCommand: issue.nextCommand,
277
+ }
278
+ : {}),
266
279
  });
280
+ }
267
281
  }
268
282
  if (results.some((item) => !succeeded(item.result)) || blockers.length > 0)
269
283
  return {
@@ -1,9 +1,11 @@
1
1
  export type PlatformProgressStatus = "STARTED" | "SUCCEEDED" | "WARNING" | "ACTION_REQUIRED" | "FAILED" | "SKIPPED";
2
2
  export type PlatformProgressKind = "phase" | "detail" | "subprocess";
3
+ export type PlatformProgressRetention = "KEEP" | "REPLACE";
3
4
  export interface PlatformProgressEvent {
4
5
  command: string;
5
6
  phase: string;
6
7
  kind?: PlatformProgressKind;
8
+ retention?: PlatformProgressRetention;
7
9
  current?: number;
8
10
  total?: number;
9
11
  moduleRef?: string;
@@ -17,17 +17,19 @@ function progressLine(event, color, spinner = "") {
17
17
  const elapsed = event.elapsedMs !== undefined
18
18
  ? ` · ${(event.elapsedMs / 1000).toFixed(1)}s`
19
19
  : "";
20
- const suffix = event.status === "SUCCEEDED"
21
- ? "完成"
22
- : event.status === "WARNING"
23
- ? "警告"
24
- : event.status === "ACTION_REQUIRED"
25
- ? "待处理"
26
- : event.status === "FAILED"
27
- ? "失败"
28
- : event.status === "SKIPPED"
29
- ? "跳过"
30
- : "";
20
+ const suffix = event.command === "uninstall" && event.status === "SUCCEEDED"
21
+ ? "已经卸载"
22
+ : event.status === "SUCCEEDED"
23
+ ? "完成"
24
+ : event.status === "WARNING"
25
+ ? "警告"
26
+ : event.status === "ACTION_REQUIRED"
27
+ ? "待处理"
28
+ : event.status === "FAILED"
29
+ ? "失败"
30
+ : event.status === "SKIPPED"
31
+ ? "跳过"
32
+ : "";
31
33
  const symbol = event.kind === "subprocess"
32
34
  ? "│"
33
35
  : event.status === "STARTED"
@@ -85,6 +87,12 @@ export function createTerminalProgressReporter(stream = process.stderr) {
85
87
  stream.write(`${line}\n`);
86
88
  return;
87
89
  }
90
+ if (event.retention === "REPLACE" && event.status !== "STARTED") {
91
+ cursorTo(stream, 0);
92
+ clearLine(stream, 0);
93
+ active = undefined;
94
+ return;
95
+ }
88
96
  cursorTo(stream, 0);
89
97
  clearLine(stream, 0);
90
98
  stream.write(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-platform-cli",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -24,10 +24,10 @@
24
24
  "SETUP.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@tomflow/proflow-module-contract": "^0.1.11"
27
+ "@tomflow/proflow-module-contract": "^0.1.12"
28
28
  },
29
29
  "devDependencies": {
30
- "@tomflow/proflow-deployment-conformance": "^0.1.9",
30
+ "@tomflow/proflow-deployment-conformance": "^0.1.10",
31
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.",
@@ -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.35",
6
+ "moduleVersion": "0.1.36",
7
7
  "kind": "cli",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",