@tomflow/proflow-platform-cli 0.1.20 → 0.1.22
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 +1 -1
- package/SETUP.md +8 -25
- package/dist/deployment/adapter.d.ts +7 -8
- package/dist/deployment/adapter.js +7 -1
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/src/cli.d.ts +3 -1
- package/dist/src/cli.js +235 -103
- package/dist/src/command.d.ts +1 -0
- package/dist/src/command.js +3 -0
- package/dist/src/install/pnpm-policy.d.ts +3 -0
- package/dist/src/install/pnpm-policy.js +68 -0
- package/dist/src/lifecycle/thin.d.ts +16 -7
- package/dist/src/lifecycle/thin.js +218 -32
- package/dist/src/progress.d.ts +12 -0
- package/dist/src/progress.js +1 -0
- package/dist/src/terminal.d.ts +2 -0
- package/dist/src/terminal.js +44 -0
- package/package.json +5 -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.22
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Replace JSON terminal protocols with typed CLI outcomes, add real-time Chinese progress and lifecycle summaries, publish DOCS content, standardize executable setup plans and package-owned setup commands, and preserve only user-owned pnpm policy during uninstall.
|
|
8
|
+
|
|
9
|
+
## 0.1.21
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Republish the contract closure packages with rebuilt distribution artifacts so runtime descriptors and CLI version output match package metadata.
|
|
14
|
+
|
|
3
15
|
## 0.1.20
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ There is no Platform-derived configuration status, missing-input list, overall r
|
|
|
51
51
|
|
|
52
52
|
`platform setup` scans all discovered Modules in dependency order. It skips `READY` Modules, invokes `Module.setup` for every non-ready Module, continues after `ACTION_REQUIRED` or `FAILED`, and returns one complete aggregate.
|
|
53
53
|
|
|
54
|
-
Targeted `platform setup --module <moduleRef
|
|
54
|
+
Targeted `platform setup --module <moduleRef>` re-observes one Module. Human or AI input is collected by the package-owned setup command shown by `platform setup`; Platform does not accept raw JSON input.
|
|
55
55
|
|
|
56
56
|
## Docs
|
|
57
57
|
|
package/SETUP.md
CHANGED
|
@@ -1,27 +1,10 @@
|
|
|
1
1
|
# @tomflow/proflow-platform-cli — Module Setup
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
**Success condition:**
|
|
14
|
-
|
|
15
|
-
`platform status` reports `platform-cli setup=READY`. No user/private path, token, loopback endpoint, or cross-Module fact is requested.
|
|
16
|
-
|
|
17
|
-
## Step 2 — Run the full workspace setup guide
|
|
18
|
-
|
|
19
|
-
**Executable:**
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
platform setup
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Success condition:**
|
|
26
|
-
|
|
27
|
-
All automatically solvable Module setup work is completed in the same run, and every remaining human/external action is listed together. Re-running the command re-observes reality and continues until all required Modules report `setupStatus=READY`.
|
|
3
|
+
## STEP-PLATFORM-CLI-01 — 完成模块自动配置
|
|
4
|
+
|
|
5
|
+
Responsible: AI
|
|
6
|
+
Interactive executable: `platform setup --module platform-cli`
|
|
7
|
+
Non-interactive executable: `platform setup --module platform-cli`
|
|
8
|
+
Required inputs: none
|
|
9
|
+
Verify: `platform status`
|
|
10
|
+
Success condition: `platform-cli.setupStatus=READY`.
|
|
@@ -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.22";
|
|
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.22";
|
|
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.22";
|
|
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.22";
|
|
44
44
|
};
|
|
45
45
|
observedEffects: never[];
|
|
46
46
|
}>;
|
|
@@ -50,10 +50,9 @@ 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.22";
|
|
54
54
|
data: {
|
|
55
55
|
docs: string;
|
|
56
|
-
setup: string;
|
|
57
56
|
};
|
|
58
57
|
};
|
|
59
58
|
observedEffects: never[];
|
|
@@ -64,7 +63,7 @@ export declare const behaviorAdapter: {
|
|
|
64
63
|
readonly ok: true;
|
|
65
64
|
readonly status: "SUCCEEDED";
|
|
66
65
|
readonly moduleRef: "platform-cli";
|
|
67
|
-
readonly moduleVersion: "0.1.
|
|
66
|
+
readonly moduleVersion: "0.1.22";
|
|
68
67
|
};
|
|
69
68
|
observedEffects: never[];
|
|
70
69
|
}>;
|
|
@@ -74,7 +73,7 @@ export declare const behaviorAdapter: {
|
|
|
74
73
|
readonly ok: true;
|
|
75
74
|
readonly status: "SUCCEEDED";
|
|
76
75
|
readonly moduleRef: "platform-cli";
|
|
77
|
-
readonly moduleVersion: "0.1.
|
|
76
|
+
readonly moduleVersion: "0.1.22";
|
|
78
77
|
};
|
|
79
78
|
observedEffects: never[];
|
|
80
79
|
}>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { descriptor } from "./descriptor.js";
|
|
2
3
|
const base = {
|
|
3
4
|
contract: "deployment.result.v1",
|
|
@@ -28,7 +29,12 @@ export const behaviorAdapter = {
|
|
|
28
29
|
observedEffects: [],
|
|
29
30
|
}),
|
|
30
31
|
docs: async (_context) => ({
|
|
31
|
-
result: {
|
|
32
|
+
result: {
|
|
33
|
+
...base,
|
|
34
|
+
data: {
|
|
35
|
+
docs: readFileSync(new URL("../DOCS.md", import.meta.url), "utf8"),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
32
38
|
observedEffects: [],
|
|
33
39
|
}),
|
|
34
40
|
start: async (_context) => ({
|
|
@@ -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.22";
|
|
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.22",
|
|
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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { type PackageCommandRunner } from "./install/package-manager.ts";
|
|
3
|
+
import { type PlatformProgressReporter } from "./progress.ts";
|
|
3
4
|
import { type NpmCommandRunner } from "./registry/index.ts";
|
|
4
5
|
export type CliStatus = "SUCCEEDED" | "ACTION_REQUIRED" | "BLOCKED" | "FAILED";
|
|
5
6
|
export interface CliOutcome {
|
|
@@ -17,6 +18,7 @@ export interface CliRuntimeOptions {
|
|
|
17
18
|
registryRunner?: NpmCommandRunner;
|
|
18
19
|
packageRunner?: PackageCommandRunner;
|
|
19
20
|
executableAvailable?: (command: string) => boolean;
|
|
21
|
+
onProgress?: PlatformProgressReporter;
|
|
20
22
|
}
|
|
21
|
-
export declare function runCli(argv: readonly string[], runtime?: CliRuntimeOptions): Promise<
|
|
23
|
+
export declare function runCli(argv: readonly string[], runtime?: CliRuntimeOptions): Promise<CliOutcome>;
|
|
22
24
|
export declare function renderHumanResult(result: CliOutcome): string;
|
package/dist/src/cli.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import { moduleStatusObservationSchema } from "@tomflow/proflow-module-contract";
|
|
4
|
+
import { moduleDocsDataSchema, moduleSetupPlanDataSchema, moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
|
|
5
5
|
import { descriptor as platformCliDescriptor } from "../deployment/descriptor.js";
|
|
6
6
|
import { AutoModuleCatalog, discoverModules } from "./discovery/discover.js";
|
|
7
7
|
import { InstalledModuleCatalog } from "./discovery/installed.js";
|
|
8
8
|
import { PlatformError } from "./errors.js";
|
|
9
9
|
import { observeWorkspaceInstalledVersion, removeWorkspacePackages, syncWorkspacePackages, } from "./install/package-manager.js";
|
|
10
|
+
import { cleanOwnedPnpmPolicy, observeMinimumReleaseAgeExclude, recordPnpmPolicyOwnership, } from "./install/pnpm-policy.js";
|
|
10
11
|
import { installModulesThin, observeDocs, observeStatuses, setupModulesThin, startModulesThin, stopModulesThin, uninstallModulesThin, } from "./lifecycle/index.js";
|
|
11
12
|
import { ensureWorkspaceMetadata } from "./persistence/workspace-metadata.js";
|
|
13
|
+
import { reportProgress } from "./progress.js";
|
|
12
14
|
import { discoverRegistryModules, PRO_FLOW_PACKAGE_PREFIX, } from "./registry/index.js";
|
|
15
|
+
import { createTerminalProgressReporter } from "./terminal.js";
|
|
13
16
|
const COMMANDS = [
|
|
14
17
|
"install",
|
|
15
18
|
"uninstall",
|
|
@@ -20,36 +23,23 @@ const COMMANDS = [
|
|
|
20
23
|
"stop",
|
|
21
24
|
];
|
|
22
25
|
function parseArgs(argv) {
|
|
23
|
-
let
|
|
26
|
+
let workspace, moduleRef;
|
|
24
27
|
let special;
|
|
25
28
|
const positional = [];
|
|
26
29
|
for (let index = 0; index < argv.length; index += 1) {
|
|
27
30
|
const value = argv[index];
|
|
28
31
|
if (value === undefined)
|
|
29
32
|
continue;
|
|
30
|
-
if (value === "--json")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
if (value === "--workspace" ||
|
|
35
|
-
value === "--module" ||
|
|
36
|
-
value === "--input") {
|
|
33
|
+
if (value === "--json")
|
|
34
|
+
throw new PlatformError("INVALID_REQUEST", "不支持的选项 --json");
|
|
35
|
+
if (value === "--workspace" || value === "--module") {
|
|
37
36
|
const next = argv[index + 1];
|
|
38
|
-
if (!next ||
|
|
37
|
+
if (!next || next.startsWith("-"))
|
|
39
38
|
throw new PlatformError("INVALID_REQUEST", `${value} requires a value`);
|
|
40
39
|
if (value === "--workspace")
|
|
41
40
|
workspace = next;
|
|
42
41
|
else if (value === "--module")
|
|
43
42
|
moduleRef = next;
|
|
44
|
-
else {
|
|
45
|
-
try {
|
|
46
|
-
input = JSON.parse(next);
|
|
47
|
-
inputSeen = true;
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
throw new PlatformError("INVALID_REQUEST", `--input must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
43
|
index += 1;
|
|
54
44
|
continue;
|
|
55
45
|
}
|
|
@@ -68,29 +58,24 @@ function parseArgs(argv) {
|
|
|
68
58
|
if (special !== undefined) {
|
|
69
59
|
if (workspace !== undefined ||
|
|
70
60
|
moduleRef !== undefined ||
|
|
71
|
-
inputSeen ||
|
|
72
61
|
positional.length > 0)
|
|
73
62
|
throw new PlatformError("INVALID_REQUEST", `${special} flag cannot be combined with command options`);
|
|
74
|
-
return { command: special
|
|
63
|
+
return { command: special };
|
|
75
64
|
}
|
|
76
65
|
if (positional.length === 0)
|
|
77
|
-
return { command: "help"
|
|
66
|
+
return { command: "help" };
|
|
78
67
|
if (positional.length !== 1)
|
|
79
68
|
throw new PlatformError("INVALID_REQUEST", "commands accept no positional arguments");
|
|
80
69
|
const raw = positional[0] ?? "";
|
|
81
70
|
if (!COMMANDS.includes(raw))
|
|
82
71
|
throw new PlatformError("INVALID_REQUEST", `unknown command ${raw}`);
|
|
83
72
|
const command = raw;
|
|
84
|
-
if (
|
|
85
|
-
throw new PlatformError("INVALID_REQUEST", "--module
|
|
86
|
-
if (inputSeen && moduleRef === undefined)
|
|
87
|
-
throw new PlatformError("INVALID_REQUEST", "--input requires --module");
|
|
73
|
+
if (moduleRef !== undefined && command !== "setup")
|
|
74
|
+
throw new PlatformError("INVALID_REQUEST", "--module is only valid with setup");
|
|
88
75
|
return {
|
|
89
76
|
command,
|
|
90
|
-
json,
|
|
91
77
|
...(workspace === undefined ? {} : { workspace }),
|
|
92
78
|
...(moduleRef === undefined ? {} : { moduleRef }),
|
|
93
|
-
...(inputSeen ? { input } : {}),
|
|
94
79
|
};
|
|
95
80
|
}
|
|
96
81
|
function outcome(command, status, workspaceRoot, data) {
|
|
@@ -144,9 +129,9 @@ function batchStatus(result) {
|
|
|
144
129
|
return "ACTION_REQUIRED";
|
|
145
130
|
return "FAILED";
|
|
146
131
|
}
|
|
147
|
-
async function handleStatus(root) {
|
|
132
|
+
async function handleStatus(root, runtime) {
|
|
148
133
|
const { catalog, modules } = await buildContext(root);
|
|
149
|
-
const observed = await observeStatuses(catalog, modules, root);
|
|
134
|
+
const observed = await observeStatuses(catalog, modules, root, runtime.onProgress);
|
|
150
135
|
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
151
136
|
const output = observed.map((item) => {
|
|
152
137
|
if (item.result.status !== "SUCCEEDED")
|
|
@@ -166,16 +151,32 @@ async function handleStatus(root) {
|
|
|
166
151
|
});
|
|
167
152
|
return outcome("status", "SUCCEEDED", root, { modules: output });
|
|
168
153
|
}
|
|
169
|
-
async function handleDocs(root) {
|
|
170
|
-
const { catalog, modules } = await buildContext(root);
|
|
171
|
-
const docs = await observeDocs(catalog,
|
|
172
|
-
const byRef = new Map(
|
|
173
|
-
|
|
174
|
-
|
|
154
|
+
async function handleDocs(root, runtime) {
|
|
155
|
+
const { catalog, modules: resolvedModules } = await buildContext(root);
|
|
156
|
+
const docs = await observeDocs(catalog, resolvedModules, root, runtime.onProgress);
|
|
157
|
+
const byRef = new Map(resolvedModules.map((module) => [module.moduleRef, module]));
|
|
158
|
+
const modules = [];
|
|
159
|
+
const errors = [];
|
|
160
|
+
for (const item of docs) {
|
|
161
|
+
const parsed = moduleDocsDataSchema.safeParse(item.result.data);
|
|
162
|
+
if (item.result.status !== "SUCCEEDED" || !parsed.success) {
|
|
163
|
+
errors.push({
|
|
164
|
+
moduleRef: item.moduleRef,
|
|
165
|
+
reason: item.result.status !== "SUCCEEDED"
|
|
166
|
+
? (item.result.error?.message ?? "文档读取失败")
|
|
167
|
+
: "Module.docs 返回格式无效",
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
modules.push({
|
|
175
172
|
moduleRef: item.moduleRef,
|
|
176
173
|
version: byRef.get(item.moduleRef)?.moduleVersion,
|
|
177
|
-
docs:
|
|
178
|
-
})
|
|
174
|
+
docs: parsed.data.docs,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return outcome("docs", errors.length === 0 ? "SUCCEEDED" : "FAILED", root, {
|
|
178
|
+
modules,
|
|
179
|
+
errors,
|
|
179
180
|
});
|
|
180
181
|
}
|
|
181
182
|
async function validateInstalledPackageSet(root, candidates, previousManaged) {
|
|
@@ -203,6 +204,19 @@ async function validateInstalledPackageSet(root, candidates, previousManaged) {
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
async function handleInstall(root, runtime) {
|
|
207
|
+
reportProgress(runtime.onProgress, {
|
|
208
|
+
command: "install",
|
|
209
|
+
phase: "workspace",
|
|
210
|
+
status: "STARTED",
|
|
211
|
+
message: "正在校验 Workspace",
|
|
212
|
+
});
|
|
213
|
+
const metadata = await ensureWorkspaceMetadata(root);
|
|
214
|
+
reportProgress(runtime.onProgress, {
|
|
215
|
+
command: "install",
|
|
216
|
+
phase: "registry",
|
|
217
|
+
status: "STARTED",
|
|
218
|
+
message: "正在发现 Registry 模块",
|
|
219
|
+
});
|
|
206
220
|
const discovered = await discoverRegistryModules({
|
|
207
221
|
workspaceRoot: root,
|
|
208
222
|
...(runtime.registryRunner === undefined
|
|
@@ -214,6 +228,7 @@ async function handleInstall(root, runtime) {
|
|
|
214
228
|
if (discovered.candidates.length === 0)
|
|
215
229
|
throw new PlatformError("PACKAGE_NOT_FOUND", "no ProFlow packages were discovered in the configured scope");
|
|
216
230
|
const previousManaged = await workspaceProFlowDependencies(root);
|
|
231
|
+
const pnpmPolicyBefore = await observeMinimumReleaseAgeExclude(root);
|
|
217
232
|
const mutation = await syncWorkspacePackages({
|
|
218
233
|
workspaceRoot: root,
|
|
219
234
|
packages: discovered.candidates.map((item) => ({
|
|
@@ -227,10 +242,16 @@ async function handleInstall(root, runtime) {
|
|
|
227
242
|
? {}
|
|
228
243
|
: { executableAvailable: runtime.executableAvailable }),
|
|
229
244
|
});
|
|
245
|
+
reportProgress(runtime.onProgress, {
|
|
246
|
+
command: "install",
|
|
247
|
+
phase: "packages",
|
|
248
|
+
status: "SUCCEEDED",
|
|
249
|
+
message: "依赖同步完成",
|
|
250
|
+
});
|
|
251
|
+
await recordPnpmPolicyOwnership(root, pnpmPolicyBefore);
|
|
230
252
|
await validateInstalledPackageSet(root, discovered.candidates, previousManaged);
|
|
231
|
-
const metadata = await ensureWorkspaceMetadata(root);
|
|
232
253
|
const { catalog, modules } = await buildContext(root);
|
|
233
|
-
const moduleInstall = await installModulesThin(catalog, modules, root);
|
|
254
|
+
const moduleInstall = await installModulesThin(catalog, modules, root, runtime.onProgress);
|
|
234
255
|
return outcome("install", batchStatus(moduleInstall), root, {
|
|
235
256
|
registry: discovered.registry,
|
|
236
257
|
packageManager: mutation.packageManager,
|
|
@@ -271,7 +292,7 @@ async function workspaceProFlowDependencies(root) {
|
|
|
271
292
|
async function handleUninstall(root, runtime) {
|
|
272
293
|
const packageNames = await workspaceProFlowDependencies(root);
|
|
273
294
|
const { catalog, modules } = await buildContext(root);
|
|
274
|
-
const moduleUninstall = await uninstallModulesThin(catalog, modules, root);
|
|
295
|
+
const moduleUninstall = await uninstallModulesThin(catalog, modules, root, runtime.onProgress);
|
|
275
296
|
if (!moduleUninstall.completed)
|
|
276
297
|
return outcome("uninstall", batchStatus(moduleUninstall), root, {
|
|
277
298
|
modules: moduleUninstall,
|
|
@@ -287,40 +308,39 @@ async function handleUninstall(root, runtime) {
|
|
|
287
308
|
? {}
|
|
288
309
|
: { executableAvailable: runtime.executableAvailable }),
|
|
289
310
|
});
|
|
311
|
+
const cleanedPnpmPolicy = await cleanOwnedPnpmPolicy(root);
|
|
290
312
|
return outcome("uninstall", "SUCCEEDED", root, {
|
|
291
313
|
modules: moduleUninstall,
|
|
292
314
|
packageManager: mutation.packageManager,
|
|
293
315
|
removed: packageNames,
|
|
316
|
+
cleanedPnpmPolicy,
|
|
294
317
|
preserved: [".proflow"],
|
|
295
318
|
});
|
|
296
319
|
}
|
|
297
|
-
async function handleSetup(root, parsed) {
|
|
320
|
+
async function handleSetup(root, parsed, runtime) {
|
|
298
321
|
const { catalog, modules } = await buildContext(root);
|
|
299
322
|
const target = parsed.moduleRef === undefined
|
|
300
323
|
? undefined
|
|
301
|
-
: {
|
|
302
|
-
|
|
303
|
-
...(Object.hasOwn(parsed, "input") ? { input: parsed.input } : {}),
|
|
304
|
-
};
|
|
305
|
-
const result = await setupModulesThin(catalog, modules, root, target);
|
|
324
|
+
: { moduleRef: parsed.moduleRef };
|
|
325
|
+
const result = await setupModulesThin(catalog, modules, root, target, runtime.onProgress);
|
|
306
326
|
return outcome("setup", batchStatus(result), root, result);
|
|
307
327
|
}
|
|
308
|
-
async function handleStart(root) {
|
|
328
|
+
async function handleStart(root, runtime) {
|
|
309
329
|
const { catalog, modules } = await buildContext(root);
|
|
310
|
-
const result = await startModulesThin(catalog, modules, root);
|
|
330
|
+
const result = await startModulesThin(catalog, modules, root, runtime.onProgress);
|
|
311
331
|
return outcome("start", batchStatus(result), root, result);
|
|
312
332
|
}
|
|
313
|
-
async function handleStop(root) {
|
|
333
|
+
async function handleStop(root, runtime) {
|
|
314
334
|
const { catalog, modules } = await buildContext(root);
|
|
315
|
-
const result = await stopModulesThin(catalog, modules, root);
|
|
335
|
+
const result = await stopModulesThin(catalog, modules, root, runtime.onProgress);
|
|
316
336
|
return outcome("stop", batchStatus(result), root, result);
|
|
317
337
|
}
|
|
318
338
|
function helpOutcome() {
|
|
319
339
|
return outcome("help", "SUCCEEDED", undefined, {
|
|
320
|
-
usage: "platform <install|uninstall|status|setup|docs|start|stop> [--workspace <path>]
|
|
340
|
+
usage: "platform <install|uninstall|status|setup|docs|start|stop> [--workspace <path>]",
|
|
321
341
|
commands: [...COMMANDS],
|
|
322
342
|
install: "platform install [--workspace <path>]",
|
|
323
|
-
setup: "platform setup [--workspace <path>] [--module <moduleRef>
|
|
343
|
+
setup: "platform setup [--workspace <path>] [--module <moduleRef>]",
|
|
324
344
|
});
|
|
325
345
|
}
|
|
326
346
|
export async function runCli(argv, runtime = {}) {
|
|
@@ -329,35 +349,35 @@ export async function runCli(argv, runtime = {}) {
|
|
|
329
349
|
parsed = parseArgs(argv);
|
|
330
350
|
}
|
|
331
351
|
catch (error) {
|
|
332
|
-
return
|
|
352
|
+
return errorOutcome("unknown", error);
|
|
333
353
|
}
|
|
334
354
|
try {
|
|
335
355
|
if (parsed.command === "help")
|
|
336
|
-
return
|
|
356
|
+
return helpOutcome();
|
|
337
357
|
if (parsed.command === "version")
|
|
338
|
-
return
|
|
358
|
+
return outcome("version", "SUCCEEDED", undefined, {
|
|
339
359
|
version: platformCliDescriptor.moduleVersion,
|
|
340
|
-
})
|
|
360
|
+
});
|
|
341
361
|
const root = await canonicalWorkspace(runtime.cwd ?? process.cwd(), parsed.workspace);
|
|
342
362
|
switch (parsed.command) {
|
|
343
363
|
case "install":
|
|
344
|
-
return
|
|
364
|
+
return await handleInstall(root, runtime);
|
|
345
365
|
case "uninstall":
|
|
346
|
-
return
|
|
366
|
+
return await handleUninstall(root, runtime);
|
|
347
367
|
case "status":
|
|
348
|
-
return
|
|
368
|
+
return await handleStatus(root, runtime);
|
|
349
369
|
case "setup":
|
|
350
|
-
return
|
|
370
|
+
return await handleSetup(root, parsed, runtime);
|
|
351
371
|
case "docs":
|
|
352
|
-
return
|
|
372
|
+
return await handleDocs(root, runtime);
|
|
353
373
|
case "start":
|
|
354
|
-
return
|
|
374
|
+
return await handleStart(root, runtime);
|
|
355
375
|
case "stop":
|
|
356
|
-
return
|
|
376
|
+
return await handleStop(root, runtime);
|
|
357
377
|
}
|
|
358
378
|
}
|
|
359
379
|
catch (error) {
|
|
360
|
-
return
|
|
380
|
+
return errorOutcome(parsed.command, error);
|
|
361
381
|
}
|
|
362
382
|
}
|
|
363
383
|
function errorOutcome(command, error) {
|
|
@@ -379,28 +399,81 @@ function errorOutcome(command, error) {
|
|
|
379
399
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
380
400
|
function renderStatus(data) {
|
|
381
401
|
if (!isRecord(data) || !Array.isArray(data.modules))
|
|
382
|
-
return "
|
|
402
|
+
return "未发现模块。";
|
|
403
|
+
const setupLabels = {
|
|
404
|
+
READY: "已就绪",
|
|
405
|
+
ACTION_REQUIRED: "需要操作",
|
|
406
|
+
FAILED: "失败",
|
|
407
|
+
};
|
|
408
|
+
const runtimeLabels = {
|
|
409
|
+
RUNNING: "运行中",
|
|
410
|
+
STOPPED: "已停止",
|
|
411
|
+
FAILED: "运行失败",
|
|
412
|
+
NOT_APPLICABLE: "无独立进程",
|
|
413
|
+
};
|
|
383
414
|
return [
|
|
384
|
-
"ProFlow
|
|
415
|
+
"ProFlow 模块状态",
|
|
385
416
|
"",
|
|
417
|
+
"模块 版本 配置状态 运行状态",
|
|
386
418
|
...data.modules
|
|
387
419
|
.filter(isRecord)
|
|
388
|
-
.map((raw) => `${String(raw.moduleRef)}
|
|
420
|
+
.map((raw) => `${String(raw.moduleRef).padEnd(28)} ${String(raw.version).padEnd(10)} ${(setupLabels[String(raw.setupStatus)] ?? "未知").padEnd(10)} ${runtimeLabels[String(raw.runtimeStatus)] ?? "未知"}`),
|
|
421
|
+
"",
|
|
422
|
+
"需要操作:运行 platform setup",
|
|
423
|
+
"失败:运行 platform setup 查看原因和修复步骤",
|
|
389
424
|
].join("\n");
|
|
390
425
|
}
|
|
391
426
|
function renderDocs(data) {
|
|
392
427
|
if (!isRecord(data) || !Array.isArray(data.modules))
|
|
393
|
-
return "
|
|
394
|
-
const lines = ["ProFlow
|
|
428
|
+
return "未发现模块文档。";
|
|
429
|
+
const lines = ["ProFlow 模块文档"];
|
|
395
430
|
for (const raw of data.modules)
|
|
396
431
|
if (isRecord(raw))
|
|
397
|
-
lines.push("", `## ${String(raw.moduleRef)} @ ${String(raw.version)}`,
|
|
432
|
+
lines.push("", `## ${String(raw.moduleRef)} @ ${String(raw.version)}`, String(raw.docs ?? ""));
|
|
433
|
+
if (Array.isArray(data.errors) && data.errors.length > 0)
|
|
434
|
+
lines.push("", "文档读取失败:", ...data.errors
|
|
435
|
+
.filter(isRecord)
|
|
436
|
+
.map((item) => `- ${String(item.moduleRef)}:${String(item.reason)}`));
|
|
398
437
|
return lines.join("\n");
|
|
399
438
|
}
|
|
439
|
+
const setupCommands = {
|
|
440
|
+
"chatgpt-carrier": {
|
|
441
|
+
ai: "proflow-chatgpt-carrier setup --carrier-url <url>",
|
|
442
|
+
inputs: "Custom GPT URL",
|
|
443
|
+
},
|
|
444
|
+
"dev-tunnel": {
|
|
445
|
+
ai: "proflow-dev-tunnel setup --tunnel-id <id> --public-base-url <url>",
|
|
446
|
+
inputs: "Tunnel ID、公开 HTTPS URL",
|
|
447
|
+
},
|
|
448
|
+
"model-provider-api": {
|
|
449
|
+
ai: "proflow-model-provider-api setup --provider-base-url <url>",
|
|
450
|
+
inputs: "模型服务 Base URL",
|
|
451
|
+
},
|
|
452
|
+
"model-runtime": {
|
|
453
|
+
ai: "proflow-model-runtime setup --fast-model <id> --reason-model <id>",
|
|
454
|
+
inputs: "FAST 模型 ID、REASON 模型 ID",
|
|
455
|
+
},
|
|
456
|
+
"execution-browser-extension": {
|
|
457
|
+
ai: "proflow-execution-browser-extension setup --extension-id <id>",
|
|
458
|
+
inputs: "Chrome Extension ID",
|
|
459
|
+
},
|
|
460
|
+
"agent-controller-dev": {
|
|
461
|
+
ai: "proflow-agent-controller-dev setup --carrier-url <url>",
|
|
462
|
+
inputs: "Custom GPT URL",
|
|
463
|
+
},
|
|
464
|
+
"agent-product": {
|
|
465
|
+
ai: "proflow-agent-product setup --carrier-url <url>",
|
|
466
|
+
inputs: "Custom GPT URL",
|
|
467
|
+
},
|
|
468
|
+
"agent-test-ops": {
|
|
469
|
+
ai: "proflow-agent-test-ops setup --carrier-url <url>",
|
|
470
|
+
inputs: "Custom GPT URL",
|
|
471
|
+
},
|
|
472
|
+
};
|
|
400
473
|
function renderSetup(data) {
|
|
401
474
|
if (!isRecord(data) || !Array.isArray(data.results))
|
|
402
|
-
return "
|
|
403
|
-
const lines = ["ProFlow
|
|
475
|
+
return "没有需要执行的配置步骤。";
|
|
476
|
+
const lines = ["ProFlow 配置", ""];
|
|
404
477
|
let automatic = 0;
|
|
405
478
|
let pending = 0;
|
|
406
479
|
for (const raw of data.results) {
|
|
@@ -413,31 +486,52 @@ function renderSetup(data) {
|
|
|
413
486
|
continue;
|
|
414
487
|
}
|
|
415
488
|
pending += 1;
|
|
416
|
-
lines.push(
|
|
417
|
-
const
|
|
489
|
+
lines.push(moduleRef);
|
|
490
|
+
const plan = moduleSetupPlanDataSchema.safeParse(raw.result.data);
|
|
491
|
+
if (plan.success) {
|
|
492
|
+
for (const [index, step] of plan.data.steps.entries()) {
|
|
493
|
+
lines.push(` [${step.state} ${index + 1}/${plan.data.steps.length}] ${step.title}`);
|
|
494
|
+
lines.push(` 人工执行:${step.execution.interactive}`);
|
|
495
|
+
lines.push(` AI 执行:${step.execution.nonInteractive}`);
|
|
496
|
+
if (step.requiredInputs.length > 0)
|
|
497
|
+
lines.push(` 需要输入:${step.requiredInputs.map((item) => item.description).join("、")}`);
|
|
498
|
+
lines.push(` 验证:${step.verify}`);
|
|
499
|
+
lines.push(` 完成条件:${step.successCondition}`);
|
|
500
|
+
if (step.blockedReason)
|
|
501
|
+
lines.push(` 原因:${step.blockedReason}`);
|
|
502
|
+
}
|
|
503
|
+
lines.push("");
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
lines.push(` [${status === "FAILED" ? "BLOCKED" : "TODO"} 1/1] ${status === "FAILED" ? "等待上游服务信息" : "完成模块配置"}`);
|
|
418
507
|
const error = raw.result.error;
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
508
|
+
if (status !== "FAILED") {
|
|
509
|
+
const commands = setupCommands[moduleRef] ?? {
|
|
510
|
+
ai: `proflow-${moduleRef} setup`,
|
|
511
|
+
inputs: "按命令提示提供",
|
|
512
|
+
};
|
|
513
|
+
lines.push(` 人工执行:proflow-${moduleRef} setup`);
|
|
514
|
+
lines.push(` AI 执行:${commands.ai}`);
|
|
515
|
+
lines.push(` 需要输入:${commands.inputs}`);
|
|
516
|
+
lines.push(` 验证:proflow-${moduleRef} verify`);
|
|
517
|
+
lines.push(" 完成条件:配置状态变为“已就绪”");
|
|
424
518
|
}
|
|
425
519
|
else if (isRecord(error)) {
|
|
426
520
|
const code = error.code === undefined ? "FAILED" : String(error.code);
|
|
427
521
|
const message = error.message === undefined
|
|
428
522
|
? "Module setup failed."
|
|
429
523
|
: String(error.message);
|
|
430
|
-
lines.push(`
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
lines.push(JSON.stringify(raw.result.data, null, 2));
|
|
524
|
+
lines.push(` 原因:${code} — ${message}`);
|
|
525
|
+
lines.push(` 执行:platform setup --module ${moduleRef}`);
|
|
526
|
+
lines.push(" 完成条件:配置状态变为“已就绪”");
|
|
434
527
|
}
|
|
435
528
|
lines.push("");
|
|
436
529
|
}
|
|
437
530
|
if (pending === 0)
|
|
438
|
-
lines.push("
|
|
531
|
+
lines.push("全部模块均已就绪。 ");
|
|
439
532
|
if (automatic > 0)
|
|
440
|
-
lines.push(
|
|
533
|
+
lines.push(`本次自动完成:${automatic} 个模块`);
|
|
534
|
+
lines.push(`汇总:${automatic} 个已就绪,${pending} 个待处理`);
|
|
441
535
|
return lines.join("\n").trimEnd();
|
|
442
536
|
}
|
|
443
537
|
export function renderHumanResult(result) {
|
|
@@ -445,17 +539,38 @@ export function renderHumanResult(result) {
|
|
|
445
539
|
isRecord(result.data) &&
|
|
446
540
|
Array.isArray(result.data.results))
|
|
447
541
|
return renderSetup(result.data);
|
|
448
|
-
if (result.
|
|
449
|
-
|
|
542
|
+
if (result.command === "start" && isRecord(result.data)) {
|
|
543
|
+
const blockers = Array.isArray(result.data.blockers)
|
|
544
|
+
? result.data.blockers.filter(isRecord)
|
|
545
|
+
: [];
|
|
546
|
+
if (blockers.length > 0)
|
|
547
|
+
return [
|
|
548
|
+
"平台未启动:存在未就绪模块",
|
|
549
|
+
"",
|
|
550
|
+
...blockers.map((item) => `${String(item.moduleRef).padEnd(28)} ${String(item.setupStatus) === "FAILED" ? "失败" : "需要操作"} 配置尚未完成`),
|
|
551
|
+
"",
|
|
552
|
+
`处理方式:platform setup${result.workspaceRoot ? ` --workspace "${result.workspaceRoot}"` : ""}`,
|
|
553
|
+
].join("\n");
|
|
554
|
+
}
|
|
555
|
+
if (result.status === "FAILED" && result.error)
|
|
556
|
+
return `${result.command} 失败:${result.error.message}`;
|
|
450
557
|
if (result.command === "help")
|
|
451
558
|
return [
|
|
452
|
-
"ProFlow
|
|
559
|
+
"ProFlow 平台命令行",
|
|
453
560
|
"",
|
|
454
|
-
|
|
561
|
+
"platform install 安装并初始化全部 ProFlow 模块",
|
|
562
|
+
"platform uninstall 卸载模块包,保留 Workspace 数据",
|
|
563
|
+
"platform status 查看模块配置与运行状态",
|
|
564
|
+
"platform setup 自动配置并列出全部剩余步骤",
|
|
565
|
+
"platform docs 阅读模块能力文档",
|
|
566
|
+
"platform start 完成全量检查后启动平台",
|
|
567
|
+
"platform stop 按逆依赖顺序停止平台",
|
|
455
568
|
"",
|
|
456
|
-
"
|
|
457
|
-
"
|
|
458
|
-
"
|
|
569
|
+
"常用参数:--workspace <路径>;setup 还支持 --module <模块名>",
|
|
570
|
+
"推荐流程:install → status → docs → setup → start → status → stop",
|
|
571
|
+
"人工配置示例:proflow-chatgpt-carrier setup",
|
|
572
|
+
"AI 配置示例:proflow-chatgpt-carrier setup --carrier-url <url>",
|
|
573
|
+
"状态图例:已就绪=配置完成;需要操作=需运行 setup;失败=存在系统阻塞;无独立进程=无需启动",
|
|
459
574
|
].join("\n");
|
|
460
575
|
if (result.command === "version" && isRecord(result.data))
|
|
461
576
|
return String(result.data.version ?? platformCliDescriptor.moduleVersion);
|
|
@@ -465,19 +580,36 @@ export function renderHumanResult(result) {
|
|
|
465
580
|
return renderSetup(result.data);
|
|
466
581
|
if (result.command === "docs")
|
|
467
582
|
return renderDocs(result.data);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
583
|
+
if ((result.command === "start" || result.command === "stop") &&
|
|
584
|
+
isRecord(result.data)) {
|
|
585
|
+
const skipped = Array.isArray(result.data.skipped)
|
|
586
|
+
? result.data.skipped.length
|
|
587
|
+
: 0;
|
|
588
|
+
const results = Array.isArray(result.data.results)
|
|
589
|
+
? result.data.results.filter(isRecord)
|
|
590
|
+
: [];
|
|
591
|
+
const operations = results.filter((item) => item.command === result.command);
|
|
592
|
+
const succeeded = operations.filter((item) => isRecord(item.result) && item.result.status === "SUCCEEDED").length;
|
|
593
|
+
const failed = operations.find((item) => isRecord(item.result) && item.result.status !== "SUCCEEDED");
|
|
594
|
+
return [
|
|
595
|
+
`平台${result.command === "start" ? "启动" : "停止"}${failed ? "未完成" : "完成"}`,
|
|
596
|
+
`成功:${succeeded},跳过:${skipped}${failed ? `,失败:${String(failed.moduleRef)}` : ",失败:0"}`,
|
|
597
|
+
].join("\n");
|
|
598
|
+
}
|
|
599
|
+
const labels = {
|
|
600
|
+
install: "安装",
|
|
601
|
+
uninstall: "卸载",
|
|
602
|
+
start: "启动",
|
|
603
|
+
stop: "停止",
|
|
604
|
+
};
|
|
605
|
+
return `${labels[result.command] ?? result.command}${result.status === "SUCCEEDED" ? "成功" : "未完成"}${result.workspaceRoot ? `\nWorkspace:${result.workspaceRoot}` : ""}`;
|
|
475
606
|
}
|
|
476
607
|
if (import.meta.main) {
|
|
477
608
|
const argv = process.argv.slice(2);
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
609
|
+
const result = await runCli(argv, {
|
|
610
|
+
onProgress: createTerminalProgressReporter(),
|
|
611
|
+
});
|
|
612
|
+
process.stdout.write(`${renderHumanResult(result)}\n`);
|
|
481
613
|
if (result.status !== "SUCCEEDED")
|
|
482
614
|
process.exitCode = 1;
|
|
483
615
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type CliOutcome, type CliRuntimeOptions, type CliStatus, runCli, } from "./cli.ts";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function observeMinimumReleaseAgeExclude(root: string): Promise<string[]>;
|
|
2
|
+
export declare function recordPnpmPolicyOwnership(root: string, before: readonly string[]): Promise<void>;
|
|
3
|
+
export declare function cleanOwnedPnpmPolicy(root: string): Promise<string[]>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { atomicWrite } from "../paths.js";
|
|
4
|
+
const policyFileName = "pnpm-workspace.yaml";
|
|
5
|
+
const ownershipFile = (root) => join(root, ".proflow", "deployment", "pnpm-policy-ownership.json");
|
|
6
|
+
async function text(root) {
|
|
7
|
+
try {
|
|
8
|
+
return await readFile(join(root, policyFileName), "utf8");
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function observeMinimumReleaseAgeExclude(root) {
|
|
15
|
+
const source = await text(root);
|
|
16
|
+
if (!source)
|
|
17
|
+
return [];
|
|
18
|
+
const lines = source.split(/\r?\n/);
|
|
19
|
+
const start = lines.findIndex((line) => /^minimumReleaseAgeExclude:\s*$/.test(line));
|
|
20
|
+
if (start < 0)
|
|
21
|
+
return [];
|
|
22
|
+
const values = [];
|
|
23
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
24
|
+
const line = lines[index] ?? "";
|
|
25
|
+
if (/^\S/.test(line) && line.trim() !== "")
|
|
26
|
+
break;
|
|
27
|
+
const match = line.match(/^\s+-\s+['"]?([^'"]+)['"]?\s*$/);
|
|
28
|
+
if (match?.[1])
|
|
29
|
+
values.push(match[1]);
|
|
30
|
+
}
|
|
31
|
+
return values;
|
|
32
|
+
}
|
|
33
|
+
export async function recordPnpmPolicyOwnership(root, before) {
|
|
34
|
+
const after = await observeMinimumReleaseAgeExclude(root);
|
|
35
|
+
const introduced = after.filter((value) => !before.includes(value));
|
|
36
|
+
if (introduced.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
await mkdir(join(root, ".proflow", "deployment"), { recursive: true });
|
|
39
|
+
await atomicWrite(ownershipFile(root), `${JSON.stringify({ contract: "proflow.pnpm-policy-ownership.v1", introduced }, null, 2)}\n`);
|
|
40
|
+
}
|
|
41
|
+
export async function cleanOwnedPnpmPolicy(root) {
|
|
42
|
+
let introduced;
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(await readFile(ownershipFile(root), "utf8"));
|
|
45
|
+
introduced =
|
|
46
|
+
typeof parsed === "object" &&
|
|
47
|
+
parsed !== null &&
|
|
48
|
+
Array.isArray(Reflect.get(parsed, "introduced"))
|
|
49
|
+
? Reflect.get(parsed, "introduced").filter((value) => typeof value === "string")
|
|
50
|
+
: [];
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
if (introduced.length === 0)
|
|
56
|
+
return [];
|
|
57
|
+
const source = await text(root);
|
|
58
|
+
if (!source)
|
|
59
|
+
return [];
|
|
60
|
+
const owned = new Set(introduced);
|
|
61
|
+
const lines = source.split(/\r?\n/);
|
|
62
|
+
const next = lines.filter((line) => {
|
|
63
|
+
const match = line.match(/^\s+-\s+['"]?([^'"]+)['"]?\s*$/);
|
|
64
|
+
return !match?.[1] || !owned.has(match[1]);
|
|
65
|
+
});
|
|
66
|
+
await atomicWrite(join(root, policyFileName), next.join("\n"));
|
|
67
|
+
return introduced;
|
|
68
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ModuleSetupStatus } from "@tomflow/proflow-module-contract";
|
|
2
2
|
import type { ResolvedModule } from "../contracts.ts";
|
|
3
3
|
import type { ModuleCatalog } from "../modules.ts";
|
|
4
|
+
import { type PlatformProgressReporter } from "../progress.ts";
|
|
4
5
|
import { type ModuleDispatchResult } from "./dispatch.ts";
|
|
5
6
|
export interface ModuleBatchResult {
|
|
6
7
|
phase: "install" | "uninstall" | "setup" | "start" | "stop";
|
|
@@ -10,14 +11,22 @@ export interface ModuleBatchResult {
|
|
|
10
11
|
moduleRef: string;
|
|
11
12
|
setupStatus: ModuleSetupStatus;
|
|
12
13
|
};
|
|
14
|
+
blockers?: Array<{
|
|
15
|
+
moduleRef: string;
|
|
16
|
+
setupStatus: ModuleSetupStatus;
|
|
17
|
+
}>;
|
|
18
|
+
skipped?: Array<{
|
|
19
|
+
moduleRef: string;
|
|
20
|
+
reason: "RUNNING" | "STOPPED" | "NOT_APPLICABLE";
|
|
21
|
+
}>;
|
|
13
22
|
}
|
|
14
|
-
export declare function observeStatuses(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string): Promise<ModuleDispatchResult[]>;
|
|
15
|
-
export declare function observeDocs(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string): Promise<ModuleDispatchResult[]>;
|
|
16
|
-
export declare const installModulesThin: (catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string) => Promise<ModuleBatchResult>;
|
|
17
|
-
export declare const uninstallModulesThin: (catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string) => Promise<ModuleBatchResult>;
|
|
18
|
-
export declare
|
|
23
|
+
export declare function observeStatuses(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter): Promise<ModuleDispatchResult[]>;
|
|
24
|
+
export declare function observeDocs(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter): Promise<ModuleDispatchResult[]>;
|
|
25
|
+
export declare const installModulesThin: (catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter) => Promise<ModuleBatchResult>;
|
|
26
|
+
export declare const uninstallModulesThin: (catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter) => Promise<ModuleBatchResult>;
|
|
27
|
+
export declare function stopModulesThin(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter): Promise<ModuleBatchResult>;
|
|
19
28
|
export declare function setupModulesThin(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, target?: {
|
|
20
29
|
moduleRef: string;
|
|
21
30
|
input?: unknown;
|
|
22
|
-
}): Promise<ModuleBatchResult>;
|
|
23
|
-
export declare function startModulesThin(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string): Promise<ModuleBatchResult>;
|
|
31
|
+
}, reporter?: PlatformProgressReporter): Promise<ModuleBatchResult>;
|
|
32
|
+
export declare function startModulesThin(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter): Promise<ModuleBatchResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
|
|
2
2
|
import { PlatformError } from "../errors.js";
|
|
3
3
|
import { buildDependencyGraph } from "../graph/graph.js";
|
|
4
|
+
import { reportProgress } from "../progress.js";
|
|
4
5
|
import { dispatchModuleCommand, } from "./dispatch.js";
|
|
5
6
|
const succeeded = (result) => result.status === "SUCCEEDED";
|
|
6
7
|
const context = (workspaceRoot, input) => input === undefined ? { workspaceRoot } : { workspaceRoot, input };
|
|
@@ -12,38 +13,160 @@ function ordered(modules, reverse = false) {
|
|
|
12
13
|
.map((ref) => byRef.get(ref))
|
|
13
14
|
.filter((item) => item !== undefined);
|
|
14
15
|
}
|
|
15
|
-
export async function observeStatuses(catalog, modules, workspaceRoot) {
|
|
16
|
+
export async function observeStatuses(catalog, modules, workspaceRoot, reporter) {
|
|
16
17
|
const results = [];
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const modulesInOrder = [...modules].sort((a, b) => a.moduleRef.localeCompare(b.moduleRef));
|
|
19
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
20
|
+
reportProgress(reporter, {
|
|
21
|
+
command: "status",
|
|
22
|
+
phase: "status",
|
|
23
|
+
current: index + 1,
|
|
24
|
+
total: modulesInOrder.length,
|
|
25
|
+
moduleRef: module.moduleRef,
|
|
26
|
+
status: "STARTED",
|
|
27
|
+
message: module.moduleRef,
|
|
28
|
+
});
|
|
29
|
+
const result = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
30
|
+
results.push(result);
|
|
31
|
+
reportProgress(reporter, {
|
|
32
|
+
command: "status",
|
|
33
|
+
phase: "status",
|
|
34
|
+
current: index + 1,
|
|
35
|
+
total: modulesInOrder.length,
|
|
36
|
+
moduleRef: module.moduleRef,
|
|
37
|
+
status: succeeded(result.result) ? "SUCCEEDED" : "FAILED",
|
|
38
|
+
message: module.moduleRef,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
19
41
|
return results;
|
|
20
42
|
}
|
|
21
|
-
export async function observeDocs(catalog, modules, workspaceRoot) {
|
|
43
|
+
export async function observeDocs(catalog, modules, workspaceRoot, reporter) {
|
|
22
44
|
const results = [];
|
|
23
|
-
|
|
24
|
-
|
|
45
|
+
const modulesInOrder = [...modules].sort((a, b) => a.moduleRef.localeCompare(b.moduleRef));
|
|
46
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
47
|
+
reportProgress(reporter, {
|
|
48
|
+
command: "docs",
|
|
49
|
+
phase: "docs",
|
|
50
|
+
current: index + 1,
|
|
51
|
+
total: modulesInOrder.length,
|
|
52
|
+
moduleRef: module.moduleRef,
|
|
53
|
+
status: "STARTED",
|
|
54
|
+
message: module.moduleRef,
|
|
55
|
+
});
|
|
56
|
+
const result = await dispatchModuleCommand(catalog, module, "docs", context(workspaceRoot));
|
|
57
|
+
results.push(result);
|
|
58
|
+
reportProgress(reporter, {
|
|
59
|
+
command: "docs",
|
|
60
|
+
phase: "docs",
|
|
61
|
+
current: index + 1,
|
|
62
|
+
total: modulesInOrder.length,
|
|
63
|
+
moduleRef: module.moduleRef,
|
|
64
|
+
status: succeeded(result.result) ? "SUCCEEDED" : "FAILED",
|
|
65
|
+
message: module.moduleRef,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
25
68
|
return results;
|
|
26
69
|
}
|
|
27
|
-
async function runOrdered(catalog, modules, workspaceRoot, command, reverse) {
|
|
70
|
+
async function runOrdered(catalog, modules, workspaceRoot, command, reverse, reporter) {
|
|
28
71
|
const results = [];
|
|
29
|
-
|
|
72
|
+
const modulesInOrder = ordered(modules, reverse);
|
|
73
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
74
|
+
reportProgress(reporter, {
|
|
75
|
+
command,
|
|
76
|
+
phase: command,
|
|
77
|
+
current: index + 1,
|
|
78
|
+
total: modulesInOrder.length,
|
|
79
|
+
moduleRef: module.moduleRef,
|
|
80
|
+
status: "STARTED",
|
|
81
|
+
message: `${module.moduleRef}`,
|
|
82
|
+
});
|
|
30
83
|
const result = await dispatchModuleCommand(catalog, module, command, context(workspaceRoot));
|
|
31
84
|
results.push(result);
|
|
85
|
+
reportProgress(reporter, {
|
|
86
|
+
command,
|
|
87
|
+
phase: command,
|
|
88
|
+
current: index + 1,
|
|
89
|
+
total: modulesInOrder.length,
|
|
90
|
+
moduleRef: module.moduleRef,
|
|
91
|
+
status: succeeded(result.result) ? "SUCCEEDED" : "FAILED",
|
|
92
|
+
message: `${module.moduleRef}`,
|
|
93
|
+
});
|
|
32
94
|
if (!succeeded(result.result))
|
|
33
95
|
return { phase: command, results, completed: false };
|
|
34
96
|
}
|
|
35
97
|
return { phase: command, results, completed: true };
|
|
36
98
|
}
|
|
37
|
-
export const installModulesThin = (catalog, modules, workspaceRoot) => runOrdered(catalog, modules, workspaceRoot, "install", false);
|
|
38
|
-
export const uninstallModulesThin = (catalog, modules, workspaceRoot) => runOrdered(catalog, modules, workspaceRoot, "uninstall", true);
|
|
39
|
-
export
|
|
40
|
-
|
|
99
|
+
export const installModulesThin = (catalog, modules, workspaceRoot, reporter) => runOrdered(catalog, modules, workspaceRoot, "install", false, reporter);
|
|
100
|
+
export const uninstallModulesThin = (catalog, modules, workspaceRoot, reporter) => runOrdered(catalog, modules, workspaceRoot, "uninstall", true, reporter);
|
|
101
|
+
export async function stopModulesThin(catalog, modules, workspaceRoot, reporter) {
|
|
102
|
+
const results = [];
|
|
103
|
+
const skipped = [];
|
|
104
|
+
const modulesInOrder = ordered(modules, true);
|
|
105
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
106
|
+
reportProgress(reporter, {
|
|
107
|
+
command: "stop",
|
|
108
|
+
phase: "status",
|
|
109
|
+
current: index + 1,
|
|
110
|
+
total: modulesInOrder.length,
|
|
111
|
+
moduleRef: module.moduleRef,
|
|
112
|
+
status: "STARTED",
|
|
113
|
+
message: `${module.moduleRef}`,
|
|
114
|
+
});
|
|
115
|
+
const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
116
|
+
results.push(status);
|
|
117
|
+
if (!succeeded(status.result))
|
|
118
|
+
return { phase: "stop", results, completed: false, skipped };
|
|
119
|
+
const observed = moduleStatusObservationSchema.parse(status.result.data);
|
|
120
|
+
if (observed.runtimeStatus === "STOPPED" ||
|
|
121
|
+
observed.runtimeStatus === "NOT_APPLICABLE") {
|
|
122
|
+
skipped.push({
|
|
123
|
+
moduleRef: module.moduleRef,
|
|
124
|
+
reason: observed.runtimeStatus,
|
|
125
|
+
});
|
|
126
|
+
reportProgress(reporter, {
|
|
127
|
+
command: "stop",
|
|
128
|
+
phase: "stop",
|
|
129
|
+
current: index + 1,
|
|
130
|
+
total: modulesInOrder.length,
|
|
131
|
+
moduleRef: module.moduleRef,
|
|
132
|
+
status: "SKIPPED",
|
|
133
|
+
message: `${module.moduleRef}`,
|
|
134
|
+
});
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const stopped = await dispatchModuleCommand(catalog, module, "stop", context(workspaceRoot));
|
|
138
|
+
results.push(stopped);
|
|
139
|
+
reportProgress(reporter, {
|
|
140
|
+
command: "stop",
|
|
141
|
+
phase: "stop",
|
|
142
|
+
current: index + 1,
|
|
143
|
+
total: modulesInOrder.length,
|
|
144
|
+
moduleRef: module.moduleRef,
|
|
145
|
+
status: succeeded(stopped.result) ? "SUCCEEDED" : "FAILED",
|
|
146
|
+
message: `${module.moduleRef}`,
|
|
147
|
+
});
|
|
148
|
+
if (!succeeded(stopped.result))
|
|
149
|
+
return { phase: "stop", results, completed: false, skipped };
|
|
150
|
+
}
|
|
151
|
+
return { phase: "stop", results, completed: true, skipped };
|
|
152
|
+
}
|
|
153
|
+
export async function setupModulesThin(catalog, modules, workspaceRoot, target, reporter) {
|
|
41
154
|
const results = [];
|
|
42
155
|
let matched = target === undefined;
|
|
43
156
|
let completed = true;
|
|
44
|
-
|
|
157
|
+
const modulesInOrder = ordered(modules);
|
|
158
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
45
159
|
if (target !== undefined && module.moduleRef !== target.moduleRef)
|
|
46
160
|
continue;
|
|
161
|
+
reportProgress(reporter, {
|
|
162
|
+
command: "setup",
|
|
163
|
+
phase: "setup",
|
|
164
|
+
current: index + 1,
|
|
165
|
+
total: modulesInOrder.length,
|
|
166
|
+
moduleRef: module.moduleRef,
|
|
167
|
+
status: "STARTED",
|
|
168
|
+
message: module.moduleRef,
|
|
169
|
+
});
|
|
47
170
|
matched = true;
|
|
48
171
|
const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
49
172
|
if (!succeeded(status.result)) {
|
|
@@ -54,10 +177,33 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target)
|
|
|
54
177
|
continue;
|
|
55
178
|
}
|
|
56
179
|
const observed = moduleStatusObservationSchema.parse(status.result.data);
|
|
57
|
-
if (observed.setupStatus === "READY" && target?.input === undefined)
|
|
180
|
+
if (observed.setupStatus === "READY" && target?.input === undefined) {
|
|
181
|
+
reportProgress(reporter, {
|
|
182
|
+
command: "setup",
|
|
183
|
+
phase: "setup",
|
|
184
|
+
current: index + 1,
|
|
185
|
+
total: modulesInOrder.length,
|
|
186
|
+
moduleRef: module.moduleRef,
|
|
187
|
+
status: "SKIPPED",
|
|
188
|
+
message: module.moduleRef,
|
|
189
|
+
});
|
|
58
190
|
continue;
|
|
191
|
+
}
|
|
59
192
|
const setup = await dispatchModuleCommand(catalog, module, "setup", context(workspaceRoot, target?.input));
|
|
60
193
|
results.push(setup);
|
|
194
|
+
reportProgress(reporter, {
|
|
195
|
+
command: "setup",
|
|
196
|
+
phase: "setup",
|
|
197
|
+
current: index + 1,
|
|
198
|
+
total: modulesInOrder.length,
|
|
199
|
+
moduleRef: module.moduleRef,
|
|
200
|
+
status: succeeded(setup.result)
|
|
201
|
+
? "SUCCEEDED"
|
|
202
|
+
: setup.result.status === "ACTION_REQUIRED"
|
|
203
|
+
? "SKIPPED"
|
|
204
|
+
: "FAILED",
|
|
205
|
+
message: module.moduleRef,
|
|
206
|
+
});
|
|
61
207
|
if (!succeeded(setup.result)) {
|
|
62
208
|
completed = false;
|
|
63
209
|
if (target !== undefined)
|
|
@@ -68,31 +214,71 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target)
|
|
|
68
214
|
throw new PlatformError("INVALID_REQUEST", `setup target module ${target?.moduleRef ?? ""} was not discovered`);
|
|
69
215
|
return { phase: "setup", results, completed };
|
|
70
216
|
}
|
|
71
|
-
export async function startModulesThin(catalog, modules, workspaceRoot) {
|
|
217
|
+
export async function startModulesThin(catalog, modules, workspaceRoot, reporter) {
|
|
72
218
|
const results = [];
|
|
73
|
-
|
|
219
|
+
const modulesInOrder = ordered(modules);
|
|
220
|
+
const blockers = [];
|
|
221
|
+
const runtimeByRef = new Map();
|
|
222
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
223
|
+
reportProgress(reporter, {
|
|
224
|
+
command: "start",
|
|
225
|
+
phase: "status",
|
|
226
|
+
current: index + 1,
|
|
227
|
+
total: modulesInOrder.length,
|
|
228
|
+
moduleRef: module.moduleRef,
|
|
229
|
+
status: "STARTED",
|
|
230
|
+
message: `${module.moduleRef}`,
|
|
231
|
+
});
|
|
74
232
|
const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
};
|
|
233
|
+
results.push(status);
|
|
234
|
+
if (!succeeded(status.result)) {
|
|
235
|
+
blockers.push({ moduleRef: module.moduleRef, setupStatus: "FAILED" });
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
81
238
|
const observed = moduleStatusObservationSchema.parse(status.result.data);
|
|
239
|
+
runtimeByRef.set(module.moduleRef, observed.runtimeStatus);
|
|
82
240
|
if (observed.setupStatus !== "READY")
|
|
83
|
-
|
|
241
|
+
blockers.push({
|
|
242
|
+
moduleRef: module.moduleRef,
|
|
243
|
+
setupStatus: observed.setupStatus,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (results.some((item) => !succeeded(item.result)) || blockers.length > 0)
|
|
247
|
+
return {
|
|
248
|
+
phase: "start",
|
|
249
|
+
results,
|
|
250
|
+
completed: false,
|
|
251
|
+
...(blockers[0] ? { blockedBy: blockers[0], blockers } : {}),
|
|
252
|
+
};
|
|
253
|
+
const skipped = [];
|
|
254
|
+
for (const [index, module] of modulesInOrder.entries()) {
|
|
255
|
+
const runtimeStatus = runtimeByRef.get(module.moduleRef);
|
|
256
|
+
if (runtimeStatus === "RUNNING" || runtimeStatus === "NOT_APPLICABLE") {
|
|
257
|
+
skipped.push({ moduleRef: module.moduleRef, reason: runtimeStatus });
|
|
258
|
+
reportProgress(reporter, {
|
|
259
|
+
command: "start",
|
|
84
260
|
phase: "start",
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
261
|
+
current: index + 1,
|
|
262
|
+
total: modulesInOrder.length,
|
|
263
|
+
moduleRef: module.moduleRef,
|
|
264
|
+
status: "SKIPPED",
|
|
265
|
+
message: `${module.moduleRef}`,
|
|
266
|
+
});
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
92
269
|
const started = await dispatchModuleCommand(catalog, module, "start", context(workspaceRoot));
|
|
93
270
|
results.push(started);
|
|
271
|
+
reportProgress(reporter, {
|
|
272
|
+
command: "start",
|
|
273
|
+
phase: "start",
|
|
274
|
+
current: index + 1,
|
|
275
|
+
total: modulesInOrder.length,
|
|
276
|
+
moduleRef: module.moduleRef,
|
|
277
|
+
status: succeeded(started.result) ? "SUCCEEDED" : "FAILED",
|
|
278
|
+
message: `${module.moduleRef}`,
|
|
279
|
+
});
|
|
94
280
|
if (!succeeded(started.result))
|
|
95
|
-
return { phase: "start", results, completed: false };
|
|
281
|
+
return { phase: "start", results, completed: false, skipped };
|
|
96
282
|
}
|
|
97
|
-
return { phase: "start", results, completed: true };
|
|
283
|
+
return { phase: "start", results, completed: true, skipped };
|
|
98
284
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type PlatformProgressStatus = "STARTED" | "SUCCEEDED" | "ACTION_REQUIRED" | "FAILED" | "SKIPPED";
|
|
2
|
+
export interface PlatformProgressEvent {
|
|
3
|
+
command: string;
|
|
4
|
+
phase: string;
|
|
5
|
+
current?: number;
|
|
6
|
+
total?: number;
|
|
7
|
+
moduleRef?: string;
|
|
8
|
+
status: PlatformProgressStatus;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
export type PlatformProgressReporter = (event: PlatformProgressEvent) => void;
|
|
12
|
+
export declare const reportProgress: (reporter: PlatformProgressReporter | undefined, event: PlatformProgressEvent) => void | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const reportProgress = (reporter, event) => reporter?.(event);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { clearLine, cursorTo } from "node:readline";
|
|
2
|
+
const colors = {
|
|
3
|
+
green: "\u001b[32m",
|
|
4
|
+
red: "\u001b[31m",
|
|
5
|
+
yellow: "\u001b[33m",
|
|
6
|
+
dim: "\u001b[2m",
|
|
7
|
+
reset: "\u001b[0m",
|
|
8
|
+
};
|
|
9
|
+
function progressLine(event, color) {
|
|
10
|
+
const prefix = event.current && event.total
|
|
11
|
+
? `[${String(event.current).padStart(2, "0")}/${event.total}] `
|
|
12
|
+
: "";
|
|
13
|
+
const suffix = event.status === "SUCCEEDED"
|
|
14
|
+
? "完成"
|
|
15
|
+
: event.status === "FAILED"
|
|
16
|
+
? "失败"
|
|
17
|
+
: event.status === "SKIPPED"
|
|
18
|
+
? "跳过"
|
|
19
|
+
: "";
|
|
20
|
+
if (!color || suffix === "")
|
|
21
|
+
return `${prefix}${event.message}${suffix ? `… ${suffix}` : "…"}`;
|
|
22
|
+
const tone = event.status === "SUCCEEDED"
|
|
23
|
+
? colors.green
|
|
24
|
+
: event.status === "FAILED"
|
|
25
|
+
? colors.red
|
|
26
|
+
: colors.yellow;
|
|
27
|
+
return `${colors.dim}${prefix}${colors.reset}${event.message}… ${tone}${suffix}${colors.reset}`;
|
|
28
|
+
}
|
|
29
|
+
export function createTerminalProgressReporter(stream = process.stderr) {
|
|
30
|
+
const interactive = stream.isTTY === true;
|
|
31
|
+
const color = interactive && !("NO_COLOR" in process.env);
|
|
32
|
+
return (event) => {
|
|
33
|
+
const line = progressLine(event, color);
|
|
34
|
+
if (!interactive) {
|
|
35
|
+
stream.write(`${line}\n`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
cursorTo(stream, 0);
|
|
39
|
+
clearLine(stream, 0);
|
|
40
|
+
stream.write(line);
|
|
41
|
+
if (event.status !== "STARTED")
|
|
42
|
+
stream.write("\n");
|
|
43
|
+
};
|
|
44
|
+
}
|
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.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./dist/src/index.js",
|
|
10
10
|
"./cli": "./dist/src/cli.js",
|
|
11
|
+
"./command": "./dist/src/command.js",
|
|
11
12
|
"./deployment/adapter": "./dist/deployment/adapter.js",
|
|
12
13
|
"./deployment/descriptor": "./dist/deployment/descriptor.js"
|
|
13
14
|
},
|
|
@@ -23,11 +24,11 @@
|
|
|
23
24
|
"SETUP.md"
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
26
|
-
"@tomflow/proflow-module-contract": "^0.1.
|
|
27
|
+
"@tomflow/proflow-module-contract": "^0.1.8"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@tomflow/proflow-
|
|
30
|
-
"@tomflow/proflow-
|
|
30
|
+
"@tomflow/proflow-deployment-conformance": "^0.1.6",
|
|
31
|
+
"@tomflow/proflow-module-template": "^0.1.6"
|
|
31
32
|
},
|
|
32
33
|
"description": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration.",
|
|
33
34
|
"keywords": [
|
package/proflow.module.json
CHANGED