@tomflow/proflow-platform-cli 0.1.35 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/dist/deployment/adapter.d.ts +7 -7
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/src/cli.d.ts +5 -0
- package/dist/src/cli.js +194 -119
- package/dist/src/deployment-order.d.ts +2 -0
- package/dist/src/deployment-order.js +40 -0
- package/dist/src/lifecycle/thin.d.ts +1 -0
- package/dist/src/lifecycle/thin.js +107 -17
- package/dist/src/progress.d.ts +2 -0
- package/dist/src/terminal.js +19 -11
- package/package.json +4 -4
- package/proflow.module.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @tomflow/proflow-platform-cli
|
|
2
2
|
|
|
3
|
+
## 0.1.37
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Ship the Phase 3 browser, tunnel, model automation, platform readiness, and documentation closure as one verifiable platform snapshot.
|
|
8
|
+
|
|
9
|
+
## 0.1.36
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 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.
|
|
14
|
+
|
|
3
15
|
## 0.1.35
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Platform does not interpret Module-private configuration or recreate a cross-Mod
|
|
|
32
32
|
|
|
33
33
|
## Install / Uninstall
|
|
34
34
|
|
|
35
|
-
`platform install` validates or creates the minimal Workspace metadata, discovers the complete governed package set from the Registry, synchronizes that set, validates the installed descriptors, then invokes `Module.install` in dependency
|
|
35
|
+
`platform install` validates or creates the minimal Workspace metadata, discovers the complete governed package set from the Registry, synchronizes that set, validates the installed descriptors, then invokes `Module.install` in the frozen Deployment Install Order. This product deployment order is distinct from Runtime dependency topology: Chrome is first, followed by Browser Extension, Agent packages, Gateway/Tunnel, runtime/data foundations and governance packages. It does not perform human setup work.
|
|
36
36
|
|
|
37
37
|
`platform uninstall` invokes `Module.uninstall` in reverse dependency order before package removal. `.proflow` is Workspace/user data and is preserved unless an owning Module explicitly removes its own artifacts.
|
|
38
38
|
|
|
@@ -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.
|
|
9
|
+
readonly moduleVersion: "0.1.37";
|
|
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.
|
|
19
|
+
readonly moduleVersion: "0.1.37";
|
|
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.
|
|
29
|
+
moduleVersion: "0.1.37";
|
|
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.
|
|
43
|
+
readonly moduleVersion: "0.1.37";
|
|
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.
|
|
53
|
+
moduleVersion: "0.1.37";
|
|
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.
|
|
66
|
+
readonly moduleVersion: "0.1.37";
|
|
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.
|
|
76
|
+
readonly moduleVersion: "0.1.37";
|
|
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.
|
|
6
|
+
readonly moduleVersion: "0.1.37";
|
|
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.
|
|
6
|
+
moduleVersion: "0.1.37",
|
|
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
|
-
: "
|
|
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
|
-
|
|
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: {
|
|
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
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
?
|
|
566
|
-
:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -591,10 +604,11 @@ function renderStatus(data, theme) {
|
|
|
591
604
|
};
|
|
592
605
|
compact("运行中", running, "●");
|
|
593
606
|
compact("已就绪", healthy, "○");
|
|
594
|
-
const ready =
|
|
595
|
-
const action = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED").length;
|
|
596
|
-
const failed =
|
|
597
|
-
|
|
607
|
+
const ready = healthy.length + running.length;
|
|
608
|
+
const action = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED" && item.runtimeStatus !== "FAILED").length;
|
|
609
|
+
const failed = failedModules.length;
|
|
610
|
+
const blocked = modules.filter((item) => item.setupStatus === "BLOCKED" && item.runtimeStatus !== "FAILED").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) {
|
|
@@ -639,36 +653,36 @@ function renderDocs(data, theme) {
|
|
|
639
653
|
}
|
|
640
654
|
const setupCommands = {
|
|
641
655
|
"chatgpt-carrier": {
|
|
642
|
-
ai: "pnpm exec -- proflow-chatgpt-carrier setup
|
|
643
|
-
inputs: "
|
|
656
|
+
ai: "pnpm exec -- proflow-chatgpt-carrier setup",
|
|
657
|
+
inputs: "无",
|
|
644
658
|
},
|
|
645
659
|
"dev-tunnel": {
|
|
646
|
-
ai: "pnpm exec -- proflow-dev-tunnel setup
|
|
647
|
-
inputs: "
|
|
660
|
+
ai: "pnpm exec -- proflow-dev-tunnel setup",
|
|
661
|
+
inputs: "无",
|
|
648
662
|
},
|
|
649
663
|
"model-provider-api": {
|
|
650
|
-
ai: "pnpm exec -- proflow-model-provider-api setup
|
|
651
|
-
inputs: "
|
|
664
|
+
ai: "pnpm exec -- proflow-model-provider-api setup",
|
|
665
|
+
inputs: "无(等待 Deployment resolver 提供 endpoint)",
|
|
652
666
|
},
|
|
653
667
|
"model-runtime": {
|
|
654
|
-
ai: "pnpm exec -- proflow-model-runtime setup
|
|
655
|
-
inputs: "
|
|
668
|
+
ai: "pnpm exec -- proflow-model-runtime setup",
|
|
669
|
+
inputs: "无(仅等价合格候选歧义时选择)",
|
|
656
670
|
},
|
|
657
671
|
"execution-browser-extension": {
|
|
658
|
-
ai: "pnpm exec -- proflow-execution-browser-extension setup
|
|
659
|
-
inputs: "
|
|
672
|
+
ai: "pnpm exec -- proflow-execution-browser-extension setup",
|
|
673
|
+
inputs: "无",
|
|
660
674
|
},
|
|
661
675
|
"agent-controller-dev": {
|
|
662
|
-
ai: "pnpm exec -- proflow-agent-controller-dev setup
|
|
663
|
-
inputs: "
|
|
676
|
+
ai: "pnpm exec -- proflow-agent-controller-dev setup",
|
|
677
|
+
inputs: "无",
|
|
664
678
|
},
|
|
665
679
|
"agent-product": {
|
|
666
|
-
ai: "pnpm exec -- proflow-agent-product setup
|
|
667
|
-
inputs: "
|
|
680
|
+
ai: "pnpm exec -- proflow-agent-product setup",
|
|
681
|
+
inputs: "无",
|
|
668
682
|
},
|
|
669
683
|
"agent-test-ops": {
|
|
670
|
-
ai: "pnpm exec -- proflow-agent-test-ops setup
|
|
671
|
-
inputs: "
|
|
684
|
+
ai: "pnpm exec -- proflow-agent-test-ops setup",
|
|
685
|
+
inputs: "无",
|
|
672
686
|
},
|
|
673
687
|
};
|
|
674
688
|
function renderSetup(data, theme) {
|
|
@@ -679,10 +693,12 @@ function renderSetup(data, theme) {
|
|
|
679
693
|
let ready = skipped.filter((item) => isRecord(item) && item.reason === "READY").length;
|
|
680
694
|
let needsAction = 0;
|
|
681
695
|
let blocked = 0;
|
|
696
|
+
const renderedModuleRefs = new Set();
|
|
682
697
|
for (const raw of data.results) {
|
|
683
698
|
if (!isRecord(raw) || !isRecord(raw.result))
|
|
684
699
|
continue;
|
|
685
700
|
const moduleRef = String(raw.moduleRef ?? raw.result.moduleRef ?? "unknown");
|
|
701
|
+
renderedModuleRefs.add(moduleRef);
|
|
686
702
|
const status = String(raw.result.status ?? "UNKNOWN");
|
|
687
703
|
if (status === "SUCCEEDED") {
|
|
688
704
|
ready += 1;
|
|
@@ -745,11 +761,100 @@ function renderSetup(data, theme) {
|
|
|
745
761
|
}
|
|
746
762
|
lines.push("");
|
|
747
763
|
}
|
|
764
|
+
const dependencyBlockers = Array.isArray(data.blockers)
|
|
765
|
+
? data.blockers.filter(isRecord)
|
|
766
|
+
: [];
|
|
767
|
+
for (const item of dependencyBlockers) {
|
|
768
|
+
const moduleRef = String(item.moduleRef ?? "unknown");
|
|
769
|
+
if (renderedModuleRefs.has(moduleRef))
|
|
770
|
+
continue;
|
|
771
|
+
blocked += 1;
|
|
772
|
+
lines.push(`${theme.info("◇")} ${theme.section(moduleRef)}`, ` 原因:${typeof item.reason === "string" ? item.reason : "等待上游 Module 就绪"}`, ` 下一步:${theme.command(typeof item.nextCommand === "string" ? item.nextCommand : `platform setup --module ${moduleRef}`)}`, "");
|
|
773
|
+
}
|
|
748
774
|
if (needsAction === 0 && blocked === 0)
|
|
749
775
|
lines.push("全部模块均已就绪。");
|
|
750
776
|
lines.push(theme.section("汇总"), `汇总:${theme.success(`${ready} 个已就绪`)},${theme.warning(`${needsAction} 个需要操作`)},${blocked > 0 ? theme.failure(`${blocked} 个系统阻塞`) : `${blocked} 个系统阻塞`}`);
|
|
751
777
|
return lines.join("\n").trimEnd();
|
|
752
778
|
}
|
|
779
|
+
function renderHelp(theme, command) {
|
|
780
|
+
const commandHelp = {
|
|
781
|
+
install: {
|
|
782
|
+
usage: "platform install [--workspace <路径>]",
|
|
783
|
+
description: "安装并初始化全部 ProFlow 模块",
|
|
784
|
+
},
|
|
785
|
+
uninstall: {
|
|
786
|
+
usage: "platform uninstall [--workspace <路径>]",
|
|
787
|
+
description: "卸载模块包,保留 Workspace 数据",
|
|
788
|
+
},
|
|
789
|
+
status: {
|
|
790
|
+
usage: "platform status [--workspace <路径>]",
|
|
791
|
+
description: "查看模块配置与运行状态",
|
|
792
|
+
},
|
|
793
|
+
setup: {
|
|
794
|
+
usage: "platform setup [--workspace <路径>] [--module <模块名>]",
|
|
795
|
+
description: "自动配置并列出全部剩余步骤",
|
|
796
|
+
},
|
|
797
|
+
docs: {
|
|
798
|
+
usage: "platform docs [--workspace <路径>]",
|
|
799
|
+
description: "阅读全部模块能力文档",
|
|
800
|
+
},
|
|
801
|
+
start: {
|
|
802
|
+
usage: "platform start [--workspace <路径>]",
|
|
803
|
+
description: "完成全量检查后启动平台",
|
|
804
|
+
},
|
|
805
|
+
stop: {
|
|
806
|
+
usage: "platform stop [--workspace <路径>]",
|
|
807
|
+
description: "按逆依赖顺序停止平台",
|
|
808
|
+
},
|
|
809
|
+
};
|
|
810
|
+
if (command) {
|
|
811
|
+
const item = commandHelp[command];
|
|
812
|
+
return [
|
|
813
|
+
theme.title(`platform ${command}`),
|
|
814
|
+
item.description,
|
|
815
|
+
"",
|
|
816
|
+
theme.section("用法"),
|
|
817
|
+
` ${theme.command(item.usage)}`,
|
|
818
|
+
"",
|
|
819
|
+
theme.section("帮助"),
|
|
820
|
+
` ${theme.command(`platform ${command} --help`)}`,
|
|
821
|
+
].join("\n");
|
|
822
|
+
}
|
|
823
|
+
return [
|
|
824
|
+
theme.title("ProFlow 平台命令行"),
|
|
825
|
+
"管理 ProFlow 模块的安装、配置、文档和运行生命周期。",
|
|
826
|
+
"",
|
|
827
|
+
theme.section("用法"),
|
|
828
|
+
` ${theme.command("platform <command> [--workspace <路径>]")}`,
|
|
829
|
+
"",
|
|
830
|
+
theme.section("命令"),
|
|
831
|
+
...Object.entries(commandHelp).map(([name, item]) => ` ${theme.command(`platform ${name}`.padEnd(21))}${item.description}`),
|
|
832
|
+
"",
|
|
833
|
+
theme.section("公共参数"),
|
|
834
|
+
" --workspace <路径> 指定 ProFlow Workspace",
|
|
835
|
+
" setup --module <模块名> 只查看指定模块的配置步骤",
|
|
836
|
+
" -h, --help 显示帮助",
|
|
837
|
+
" -v, --version 显示版本",
|
|
838
|
+
"",
|
|
839
|
+
theme.section("推荐流程"),
|
|
840
|
+
` ${theme.command("install → status → docs → setup → start → status → stop")}`,
|
|
841
|
+
"",
|
|
842
|
+
theme.section("人工配置示例"),
|
|
843
|
+
` 人工:${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
|
|
844
|
+
` AI: ${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
|
|
845
|
+
"",
|
|
846
|
+
theme.section("状态图例"),
|
|
847
|
+
` ${theme.success("已就绪")} 配置与验证完成`,
|
|
848
|
+
` ${theme.warning("需要操作")} 需要执行 setup 步骤`,
|
|
849
|
+
` ${theme.info("等待依赖")} 等待上游 Module 或外部服务`,
|
|
850
|
+
` ${theme.failure("失败")} 已确认配置或运行故障`,
|
|
851
|
+
" 运行中 服务进程正在运行",
|
|
852
|
+
" 无独立进程 该模块无需启动",
|
|
853
|
+
"",
|
|
854
|
+
theme.section("遇到问题"),
|
|
855
|
+
` 先运行 ${theme.command("platform status")};配置问题运行 ${theme.command("platform setup")}。`,
|
|
856
|
+
].join("\n");
|
|
857
|
+
}
|
|
753
858
|
export function renderHumanResult(result, options = {}) {
|
|
754
859
|
const theme = humanTheme(options);
|
|
755
860
|
if (result.command === "setup" &&
|
|
@@ -760,18 +865,35 @@ export function renderHumanResult(result, options = {}) {
|
|
|
760
865
|
const blockers = Array.isArray(result.data.blockers)
|
|
761
866
|
? result.data.blockers.filter(isRecord)
|
|
762
867
|
: [];
|
|
763
|
-
if (blockers.length > 0)
|
|
764
|
-
|
|
765
|
-
theme.failure
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
"",
|
|
773
|
-
|
|
774
|
-
|
|
868
|
+
if (blockers.length > 0) {
|
|
869
|
+
const groups = [
|
|
870
|
+
{ status: "FAILED", title: "失败", icon: "✕", tone: theme.failure },
|
|
871
|
+
{
|
|
872
|
+
status: "ACTION_REQUIRED",
|
|
873
|
+
title: "需要操作",
|
|
874
|
+
icon: "◆",
|
|
875
|
+
tone: theme.warning,
|
|
876
|
+
},
|
|
877
|
+
{ status: "BLOCKED", title: "等待依赖", icon: "◇", tone: theme.info },
|
|
878
|
+
];
|
|
879
|
+
const lines = [theme.failure("平台未启动:存在未就绪模块"), ""];
|
|
880
|
+
for (const group of groups) {
|
|
881
|
+
const entries = blockers.filter((item) => item.setupStatus === group.status);
|
|
882
|
+
if (entries.length === 0)
|
|
883
|
+
continue;
|
|
884
|
+
lines.push(theme.section(group.title));
|
|
885
|
+
for (const item of entries)
|
|
886
|
+
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)}`)}`);
|
|
887
|
+
lines.push("");
|
|
888
|
+
}
|
|
889
|
+
const readyCount = Array.isArray(result.data.results)
|
|
890
|
+
? result.data.results.filter((item) => isRecord(item) &&
|
|
891
|
+
isRecord(item.result) &&
|
|
892
|
+
item.result.status === "SUCCEEDED").length - blockers.length
|
|
893
|
+
: 0;
|
|
894
|
+
lines.push(theme.section("检查汇总"), `${Math.max(0, readyCount)} 已就绪 · ${blockers.length} 未就绪 · 0 个模块已启动`, `处理方式:${theme.command(`platform setup${result.workspaceRoot ? ` --workspace "${result.workspaceRoot}"` : ""}`)}`);
|
|
895
|
+
return lines.join("\n");
|
|
896
|
+
}
|
|
775
897
|
}
|
|
776
898
|
if (result.status === "FAILED" && result.error)
|
|
777
899
|
return [
|
|
@@ -779,42 +901,12 @@ export function renderHumanResult(result, options = {}) {
|
|
|
779
901
|
`${theme.failure("│")} ${theme.section(result.error.code)}`,
|
|
780
902
|
`${theme.failure("│")} ${result.error.message}`,
|
|
781
903
|
theme.failure("╰─ 请根据提示修复后重试"),
|
|
904
|
+
...(result.error.category === "USAGE"
|
|
905
|
+
? ["", renderHelp(theme, result.error.helpCommand)]
|
|
906
|
+
: []),
|
|
782
907
|
].join("\n");
|
|
783
908
|
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");
|
|
909
|
+
return renderHelp(theme);
|
|
818
910
|
if (result.command === "version" && isRecord(result.data))
|
|
819
911
|
return String(result.data.version ?? platformCliDescriptor.moduleVersion);
|
|
820
912
|
if (result.command === "status")
|
|
@@ -851,6 +943,8 @@ export function renderHumanResult(result, options = {}) {
|
|
|
851
943
|
start: "启动",
|
|
852
944
|
stop: "停止",
|
|
853
945
|
};
|
|
946
|
+
if (result.command === "uninstall" && result.status === "SUCCEEDED")
|
|
947
|
+
return `${theme.success("✓ 已经卸载")}${result.workspaceRoot ? `\n${theme.muted("Workspace")} ${result.workspaceRoot}` : ""}`;
|
|
854
948
|
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
949
|
}
|
|
856
950
|
if (import.meta.main) {
|
|
@@ -865,26 +959,7 @@ if (import.meta.main) {
|
|
|
865
959
|
color,
|
|
866
960
|
width: process.stdout.columns,
|
|
867
961
|
})}\n`;
|
|
868
|
-
|
|
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);
|
|
962
|
+
process.stdout.write(rendered);
|
|
875
963
|
if (result.status !== "SUCCEEDED")
|
|
876
964
|
process.exitCode = 1;
|
|
877
965
|
}
|
|
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
|
-
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const FROZEN_DEPLOYMENT_INSTALL_ORDER: readonly ["chrome-runtime", "execution-browser-extension", "agent-controller-dev", "agent-product", "agent-test-ops", "agent-gateway", "dev-tunnel", "agent-runtime", "platform-host", "execution-runtime", "execution-local", "model-provider-api", "model-runtime", "task-orchestration", "task-store-sqlite", "task-migration-runner", "execution-contracts", "model-contracts", "module-contract", "module-skill", "module-template", "deployment-conformance", "platform-cli", "chatgpt-carrier"];
|
|
2
|
+
export declare function orderModuleRefsForInstall(moduleRefs: readonly string[]): string[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const FROZEN_DEPLOYMENT_INSTALL_ORDER = [
|
|
2
|
+
"chrome-runtime",
|
|
3
|
+
"execution-browser-extension",
|
|
4
|
+
"agent-controller-dev",
|
|
5
|
+
"agent-product",
|
|
6
|
+
"agent-test-ops",
|
|
7
|
+
"agent-gateway",
|
|
8
|
+
"dev-tunnel",
|
|
9
|
+
"agent-runtime",
|
|
10
|
+
"platform-host",
|
|
11
|
+
"execution-runtime",
|
|
12
|
+
"execution-local",
|
|
13
|
+
"model-provider-api",
|
|
14
|
+
"model-runtime",
|
|
15
|
+
"task-orchestration",
|
|
16
|
+
"task-store-sqlite",
|
|
17
|
+
"task-migration-runner",
|
|
18
|
+
"execution-contracts",
|
|
19
|
+
"model-contracts",
|
|
20
|
+
"module-contract",
|
|
21
|
+
"module-skill",
|
|
22
|
+
"module-template",
|
|
23
|
+
"deployment-conformance",
|
|
24
|
+
"platform-cli",
|
|
25
|
+
"chatgpt-carrier",
|
|
26
|
+
];
|
|
27
|
+
const installRank = new Map(FROZEN_DEPLOYMENT_INSTALL_ORDER.map((moduleRef, index) => [moduleRef, index]));
|
|
28
|
+
export function orderModuleRefsForInstall(moduleRefs) {
|
|
29
|
+
return [...moduleRefs].sort((left, right) => {
|
|
30
|
+
const leftRank = installRank.get(left);
|
|
31
|
+
const rightRank = installRank.get(right);
|
|
32
|
+
if (leftRank !== undefined && rightRank !== undefined)
|
|
33
|
+
return leftRank - rightRank;
|
|
34
|
+
if (leftRank !== undefined)
|
|
35
|
+
return -1;
|
|
36
|
+
if (rightRank !== undefined)
|
|
37
|
+
return 1;
|
|
38
|
+
return left.localeCompare(right);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
|
|
2
|
+
import { orderModuleRefsForInstall } from "../deployment-order.js";
|
|
2
3
|
import { PlatformError } from "../errors.js";
|
|
3
4
|
import { buildDependencyGraph } from "../graph/graph.js";
|
|
4
5
|
import { reportProgress } from "../progress.js";
|
|
@@ -13,6 +14,16 @@ function ordered(modules, reverse = false) {
|
|
|
13
14
|
.map((ref) => byRef.get(ref))
|
|
14
15
|
.filter((item) => item !== undefined);
|
|
15
16
|
}
|
|
17
|
+
function orderedForInstall(modules) {
|
|
18
|
+
// Keep dependency validation, but do not let dependency depth/alphabetical order
|
|
19
|
+
// dictate the user-facing package installation trajectory.
|
|
20
|
+
buildDependencyGraph(modules);
|
|
21
|
+
const refs = orderModuleRefsForInstall(modules.map((module) => module.moduleRef));
|
|
22
|
+
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
23
|
+
return refs
|
|
24
|
+
.map((ref) => byRef.get(ref))
|
|
25
|
+
.filter((item) => item !== undefined);
|
|
26
|
+
}
|
|
16
27
|
export async function observeStatuses(catalog, modules, workspaceRoot, reporter) {
|
|
17
28
|
const results = [];
|
|
18
29
|
const modulesInOrder = [...modules].sort((a, b) => a.moduleRef.localeCompare(b.moduleRef));
|
|
@@ -69,7 +80,9 @@ export async function observeDocs(catalog, modules, workspaceRoot, reporter) {
|
|
|
69
80
|
}
|
|
70
81
|
async function runOrdered(catalog, modules, workspaceRoot, command, reverse, reporter) {
|
|
71
82
|
const results = [];
|
|
72
|
-
const modulesInOrder =
|
|
83
|
+
const modulesInOrder = command === "install"
|
|
84
|
+
? orderedForInstall(modules)
|
|
85
|
+
: ordered(modules, reverse);
|
|
73
86
|
for (const [index, module] of modulesInOrder.entries()) {
|
|
74
87
|
reportProgress(reporter, {
|
|
75
88
|
command,
|
|
@@ -78,7 +91,9 @@ async function runOrdered(catalog, modules, workspaceRoot, command, reverse, rep
|
|
|
78
91
|
total: modulesInOrder.length,
|
|
79
92
|
moduleRef: module.moduleRef,
|
|
80
93
|
status: "STARTED",
|
|
81
|
-
message:
|
|
94
|
+
message: command === "uninstall"
|
|
95
|
+
? `正在卸载 ${module.moduleRef}`
|
|
96
|
+
: `${module.moduleRef}`,
|
|
82
97
|
});
|
|
83
98
|
const result = await dispatchModuleCommand(catalog, module, command, context(workspaceRoot));
|
|
84
99
|
results.push(result);
|
|
@@ -160,12 +175,37 @@ export async function stopModulesThin(catalog, modules, workspaceRoot, reporter)
|
|
|
160
175
|
export async function setupModulesThin(catalog, modules, workspaceRoot, target, reporter) {
|
|
161
176
|
const results = [];
|
|
162
177
|
const skipped = [];
|
|
163
|
-
|
|
178
|
+
const blockers = [];
|
|
179
|
+
const graph = buildDependencyGraph(modules);
|
|
180
|
+
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
181
|
+
const dependenciesByRef = new Map();
|
|
182
|
+
for (const edge of graph.edges) {
|
|
183
|
+
const dependencies = dependenciesByRef.get(edge.from) ?? [];
|
|
184
|
+
dependencies.push(edge.to);
|
|
185
|
+
dependenciesByRef.set(edge.from, dependencies);
|
|
186
|
+
}
|
|
187
|
+
if (target !== undefined && !byRef.has(target.moduleRef))
|
|
188
|
+
throw new PlatformError("INVALID_REQUEST", `setup target module ${target.moduleRef} was not discovered`);
|
|
189
|
+
const visitedRefs = new Set();
|
|
190
|
+
const includeWithDependencies = (moduleRef) => {
|
|
191
|
+
if (visitedRefs.has(moduleRef))
|
|
192
|
+
return;
|
|
193
|
+
visitedRefs.add(moduleRef);
|
|
194
|
+
for (const dependency of dependenciesByRef.get(moduleRef) ?? [])
|
|
195
|
+
includeWithDependencies(dependency);
|
|
196
|
+
};
|
|
197
|
+
if (target !== undefined)
|
|
198
|
+
includeWithDependencies(target.moduleRef);
|
|
199
|
+
else
|
|
200
|
+
for (const moduleRef of graph.order)
|
|
201
|
+
visitedRefs.add(moduleRef);
|
|
202
|
+
const modulesInOrder = graph.order
|
|
203
|
+
.filter((moduleRef) => visitedRefs.has(moduleRef))
|
|
204
|
+
.map((moduleRef) => byRef.get(moduleRef))
|
|
205
|
+
.filter((module) => module !== undefined);
|
|
206
|
+
const setupStatusByRef = new Map();
|
|
164
207
|
let completed = true;
|
|
165
|
-
const modulesInOrder = ordered(modules);
|
|
166
208
|
for (const [index, module] of modulesInOrder.entries()) {
|
|
167
|
-
if (target !== undefined && module.moduleRef !== target.moduleRef)
|
|
168
|
-
continue;
|
|
169
209
|
reportProgress(reporter, {
|
|
170
210
|
command: "setup",
|
|
171
211
|
phase: "setup",
|
|
@@ -175,17 +215,20 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
175
215
|
status: "STARTED",
|
|
176
216
|
message: module.moduleRef,
|
|
177
217
|
});
|
|
178
|
-
matched = true;
|
|
179
218
|
const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
180
219
|
if (!succeeded(status.result)) {
|
|
181
220
|
results.push(status);
|
|
221
|
+
setupStatusByRef.set(module.moduleRef, "FAILED");
|
|
182
222
|
completed = false;
|
|
183
|
-
if (target
|
|
223
|
+
if (target?.moduleRef === module.moduleRef)
|
|
184
224
|
break;
|
|
185
225
|
continue;
|
|
186
226
|
}
|
|
187
227
|
const observed = moduleStatusObservationSchema.parse(status.result.data);
|
|
188
|
-
|
|
228
|
+
setupStatusByRef.set(module.moduleRef, observed.setupStatus);
|
|
229
|
+
if (target !== undefined && module.moduleRef !== target.moduleRef)
|
|
230
|
+
continue;
|
|
231
|
+
if (observed.setupStatus === "READY" && target === undefined) {
|
|
189
232
|
skipped.push({ moduleRef: module.moduleRef, reason: "READY" });
|
|
190
233
|
reportProgress(reporter, {
|
|
191
234
|
command: "setup",
|
|
@@ -198,6 +241,29 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
198
241
|
});
|
|
199
242
|
continue;
|
|
200
243
|
}
|
|
244
|
+
const unavailableDependencies = (dependenciesByRef.get(module.moduleRef) ?? []).filter((moduleRef) => setupStatusByRef.get(moduleRef) !== "READY");
|
|
245
|
+
if (unavailableDependencies.length > 0) {
|
|
246
|
+
const first = unavailableDependencies[0];
|
|
247
|
+
blockers.push({
|
|
248
|
+
moduleRef: module.moduleRef,
|
|
249
|
+
setupStatus: "BLOCKED",
|
|
250
|
+
reason: `等待依赖模块就绪:${unavailableDependencies.join(", ")}`,
|
|
251
|
+
nextCommand: `platform setup --module ${first}`,
|
|
252
|
+
});
|
|
253
|
+
completed = false;
|
|
254
|
+
reportProgress(reporter, {
|
|
255
|
+
command: "setup",
|
|
256
|
+
phase: "setup",
|
|
257
|
+
current: index + 1,
|
|
258
|
+
total: modulesInOrder.length,
|
|
259
|
+
moduleRef: module.moduleRef,
|
|
260
|
+
status: "SKIPPED",
|
|
261
|
+
message: `${module.moduleRef} 等待 ${unavailableDependencies.join(", ")}`,
|
|
262
|
+
});
|
|
263
|
+
if (target !== undefined)
|
|
264
|
+
break;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
201
267
|
const setup = await dispatchModuleCommand(catalog, module, "setup", context(workspaceRoot, target?.input));
|
|
202
268
|
results.push(setup);
|
|
203
269
|
reportProgress(reporter, {
|
|
@@ -215,13 +281,25 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
215
281
|
});
|
|
216
282
|
if (!succeeded(setup.result)) {
|
|
217
283
|
completed = false;
|
|
218
|
-
if (target !== undefined)
|
|
219
|
-
break;
|
|
220
284
|
}
|
|
285
|
+
const reconciled = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
286
|
+
if (succeeded(reconciled.result))
|
|
287
|
+
setupStatusByRef.set(module.moduleRef, moduleStatusObservationSchema.parse(reconciled.result.data).setupStatus);
|
|
288
|
+
else {
|
|
289
|
+
results.push(reconciled);
|
|
290
|
+
setupStatusByRef.set(module.moduleRef, "FAILED");
|
|
291
|
+
completed = false;
|
|
292
|
+
}
|
|
293
|
+
if (target !== undefined && !succeeded(setup.result))
|
|
294
|
+
break;
|
|
221
295
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
296
|
+
return {
|
|
297
|
+
phase: "setup",
|
|
298
|
+
results,
|
|
299
|
+
completed,
|
|
300
|
+
...(blockers[0] ? { blockedBy: blockers[0], blockers } : {}),
|
|
301
|
+
skipped,
|
|
302
|
+
};
|
|
225
303
|
}
|
|
226
304
|
export async function startModulesThin(catalog, modules, workspaceRoot, reporter) {
|
|
227
305
|
const results = [];
|
|
@@ -232,22 +310,26 @@ export async function startModulesThin(catalog, modules, workspaceRoot, reporter
|
|
|
232
310
|
reportProgress(reporter, {
|
|
233
311
|
command: "start",
|
|
234
312
|
phase: "status",
|
|
313
|
+
kind: "detail",
|
|
314
|
+
retention: "REPLACE",
|
|
235
315
|
current: index + 1,
|
|
236
316
|
total: modulesInOrder.length,
|
|
237
317
|
moduleRef: module.moduleRef,
|
|
238
318
|
status: "STARTED",
|
|
239
|
-
message:
|
|
319
|
+
message: `正在检查模块状态 · ${module.moduleRef}`,
|
|
240
320
|
});
|
|
241
321
|
const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
242
322
|
results.push(status);
|
|
243
323
|
reportProgress(reporter, {
|
|
244
324
|
command: "start",
|
|
245
325
|
phase: "status",
|
|
326
|
+
kind: "detail",
|
|
327
|
+
retention: "REPLACE",
|
|
246
328
|
current: index + 1,
|
|
247
329
|
total: modulesInOrder.length,
|
|
248
330
|
moduleRef: module.moduleRef,
|
|
249
331
|
status: succeeded(status.result) ? "SUCCEEDED" : "FAILED",
|
|
250
|
-
message:
|
|
332
|
+
message: `正在检查模块状态 · ${module.moduleRef}`,
|
|
251
333
|
});
|
|
252
334
|
if (!succeeded(status.result)) {
|
|
253
335
|
blockers.push({
|
|
@@ -259,11 +341,19 @@ export async function startModulesThin(catalog, modules, workspaceRoot, reporter
|
|
|
259
341
|
}
|
|
260
342
|
const observed = moduleStatusObservationSchema.parse(status.result.data);
|
|
261
343
|
runtimeByRef.set(module.moduleRef, observed.runtimeStatus);
|
|
262
|
-
if (observed.setupStatus !== "READY")
|
|
344
|
+
if (observed.setupStatus !== "READY") {
|
|
345
|
+
const issue = observed.issues?.find((item) => item.scope === "SETUP");
|
|
263
346
|
blockers.push({
|
|
264
347
|
moduleRef: module.moduleRef,
|
|
265
348
|
setupStatus: observed.setupStatus,
|
|
349
|
+
...(issue
|
|
350
|
+
? {
|
|
351
|
+
reason: issue.message,
|
|
352
|
+
nextCommand: issue.nextCommand,
|
|
353
|
+
}
|
|
354
|
+
: {}),
|
|
266
355
|
});
|
|
356
|
+
}
|
|
267
357
|
}
|
|
268
358
|
if (results.some((item) => !succeeded(item.result)) || blockers.length > 0)
|
|
269
359
|
return {
|
package/dist/src/progress.d.ts
CHANGED
|
@@ -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;
|
package/dist/src/terminal.js
CHANGED
|
@@ -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 === "
|
|
23
|
-
? "
|
|
24
|
-
: event.status === "
|
|
25
|
-
? "
|
|
26
|
-
: event.status === "
|
|
27
|
-
? "
|
|
28
|
-
: event.status === "
|
|
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.
|
|
3
|
+
"version": "0.1.37",
|
|
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.
|
|
27
|
+
"@tomflow/proflow-module-contract": "^0.1.13"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@tomflow/proflow-
|
|
31
|
-
"@tomflow/proflow-
|
|
30
|
+
"@tomflow/proflow-module-template": "^0.1.10",
|
|
31
|
+
"@tomflow/proflow-deployment-conformance": "^0.1.11"
|
|
32
32
|
},
|
|
33
33
|
"description": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration.",
|
|
34
34
|
"keywords": [
|
package/proflow.module.json
CHANGED