@tomflow/proflow-platform-cli 0.1.38 → 0.1.40
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/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 +2 -0
- package/dist/src/cli.js +132 -60
- package/dist/src/install/package-manager.js +1 -1
- package/dist/src/lifecycle/thin.d.ts +1 -1
- package/dist/src/lifecycle/thin.js +39 -10
- package/dist/src/registry/npm-registry.d.ts +2 -0
- package/dist/src/registry/npm-registry.js +6 -2
- package/dist/src/setup/interaction.d.ts +14 -0
- package/dist/src/setup/interaction.js +126 -0
- package/package.json +4 -3
- 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.40
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Ignore governed non-Module tool packages during Registry module discovery so Fresh install remains fixed at 23 active modules.
|
|
8
|
+
|
|
9
|
+
## 0.1.39
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Add professional setup orchestration, Provider FAST/THINK input, recoverable managed Dev Tunnel provisioning, Browser installation guidance, and precise status diagnostics.
|
|
14
|
+
|
|
3
15
|
## 0.1.38
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -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.40";
|
|
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.40";
|
|
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.40";
|
|
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.40";
|
|
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.40";
|
|
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.40";
|
|
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.40";
|
|
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.40";
|
|
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.40",
|
|
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,7 @@
|
|
|
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
|
+
import { type SetupInteraction } from "./setup/interaction.ts";
|
|
5
6
|
declare const COMMANDS: readonly ["install", "uninstall", "status", "setup", "docs", "start", "stop"];
|
|
6
7
|
type Command = (typeof COMMANDS)[number];
|
|
7
8
|
export type CliStatus = "SUCCEEDED" | "ACTION_REQUIRED" | "BLOCKED" | "FAILED";
|
|
@@ -23,6 +24,7 @@ export interface CliRuntimeOptions {
|
|
|
23
24
|
packageRunner?: PackageCommandRunner;
|
|
24
25
|
executableAvailable?: (command: string) => boolean;
|
|
25
26
|
onProgress?: PlatformProgressReporter;
|
|
27
|
+
setupInteraction?: SetupInteraction;
|
|
26
28
|
}
|
|
27
29
|
export declare function runCli(argv: readonly string[], runtime?: CliRuntimeOptions): Promise<CliOutcome>;
|
|
28
30
|
export interface HumanRenderOptions {
|
package/dist/src/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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 { moduleDocsDataSchema, moduleSetupPlanDataSchema, moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
|
|
4
|
+
import { moduleDocsDataSchema, moduleSetupPlanDataSchema, moduleStatusObservationSchema, readModuleSharedFacts, } 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";
|
|
@@ -12,6 +12,7 @@ import { installModulesThin, observeDocs, observeStatuses, setupModulesThin, sta
|
|
|
12
12
|
import { ensureWorkspaceMetadata } from "./persistence/workspace-metadata.js";
|
|
13
13
|
import { reportProgress } from "./progress.js";
|
|
14
14
|
import { discoverRegistryModules, PRO_FLOW_PACKAGE_PREFIX, } from "./registry/index.js";
|
|
15
|
+
import { createClackSetupInteraction, } from "./setup/interaction.js";
|
|
15
16
|
import { createTerminalProgressReporter } from "./terminal.js";
|
|
16
17
|
const COMMANDS = [
|
|
17
18
|
"install",
|
|
@@ -70,8 +71,8 @@ function parseArgs(argv) {
|
|
|
70
71
|
if (!COMMANDS.includes(raw))
|
|
71
72
|
throw new PlatformError("INVALID_REQUEST", `unknown command ${raw}`);
|
|
72
73
|
const command = raw;
|
|
73
|
-
if (moduleRef !== undefined && command !== "setup")
|
|
74
|
-
throw new PlatformError("INVALID_REQUEST", "--module is only valid with setup");
|
|
74
|
+
if (moduleRef !== undefined && command !== "setup" && command !== "docs")
|
|
75
|
+
throw new PlatformError("INVALID_REQUEST", "--module is only valid with setup or docs");
|
|
75
76
|
return {
|
|
76
77
|
command,
|
|
77
78
|
...(workspace === undefined ? {} : { workspace }),
|
|
@@ -171,7 +172,7 @@ async function handleStatus(root, runtime) {
|
|
|
171
172
|
});
|
|
172
173
|
return outcome("status", "SUCCEEDED", root, { modules: output });
|
|
173
174
|
}
|
|
174
|
-
async function handleDocs(root, runtime) {
|
|
175
|
+
async function handleDocs(root, parsed, runtime) {
|
|
175
176
|
reportProgress(runtime.onProgress, {
|
|
176
177
|
command: "docs",
|
|
177
178
|
phase: "docs",
|
|
@@ -179,7 +180,28 @@ async function handleDocs(root, runtime) {
|
|
|
179
180
|
message: "正在整理模块文档",
|
|
180
181
|
});
|
|
181
182
|
const { catalog, modules: resolvedModules } = await buildContext(root);
|
|
182
|
-
const
|
|
183
|
+
const selectedModules = parsed.moduleRef === undefined
|
|
184
|
+
? resolvedModules
|
|
185
|
+
: resolvedModules.filter((item) => item.moduleRef === parsed.moduleRef);
|
|
186
|
+
if (parsed.moduleRef !== undefined && selectedModules.length === 0)
|
|
187
|
+
throw new PlatformError("INVALID_REQUEST", `docs target module ${parsed.moduleRef} was not discovered`);
|
|
188
|
+
if (parsed.moduleRef === undefined) {
|
|
189
|
+
reportProgress(runtime.onProgress, {
|
|
190
|
+
command: "docs",
|
|
191
|
+
phase: "docs",
|
|
192
|
+
status: "SUCCEEDED",
|
|
193
|
+
message: `已整理 ${selectedModules.length} 个模块的文档索引`,
|
|
194
|
+
});
|
|
195
|
+
return outcome("docs", "SUCCEEDED", root, {
|
|
196
|
+
indexOnly: true,
|
|
197
|
+
modules: selectedModules.map((module) => ({
|
|
198
|
+
moduleRef: module.moduleRef,
|
|
199
|
+
version: module.moduleVersion,
|
|
200
|
+
})),
|
|
201
|
+
errors: [],
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const docs = await observeDocs(catalog, selectedModules, root);
|
|
183
205
|
const byRef = new Map(resolvedModules.map((module) => [module.moduleRef, module]));
|
|
184
206
|
const modules = [];
|
|
185
207
|
const errors = [];
|
|
@@ -270,7 +292,7 @@ async function handleInstall(root, runtime) {
|
|
|
270
292
|
kind: "detail",
|
|
271
293
|
status: "STARTED",
|
|
272
294
|
total,
|
|
273
|
-
message: `Registry 返回 ${total}
|
|
295
|
+
message: `Registry 返回 ${total} 个候选模块,正在做安装前核验`,
|
|
274
296
|
}),
|
|
275
297
|
onPackageChecked: ({ current, total, packageName }) => reportProgress(runtime.onProgress, {
|
|
276
298
|
command: "install",
|
|
@@ -280,7 +302,7 @@ async function handleInstall(root, runtime) {
|
|
|
280
302
|
current,
|
|
281
303
|
total,
|
|
282
304
|
moduleRef: packageName.replace(PRO_FLOW_PACKAGE_PREFIX, ""),
|
|
283
|
-
message:
|
|
305
|
+
message: `安装前核验 ${packageName.replace(PRO_FLOW_PACKAGE_PREFIX, "")}`,
|
|
284
306
|
}),
|
|
285
307
|
...(runtime.registryRunner === undefined
|
|
286
308
|
? {}
|
|
@@ -335,7 +357,7 @@ async function handleInstall(root, runtime) {
|
|
|
335
357
|
phase: "validation",
|
|
336
358
|
kind: "phase",
|
|
337
359
|
status: "SUCCEEDED",
|
|
338
|
-
message:
|
|
360
|
+
message: `安装完成,${modules.length} 个模块已从本地安装物验证`,
|
|
339
361
|
});
|
|
340
362
|
const moduleInstall = await installModulesThin(catalog, modules, root, runtime.onProgress);
|
|
341
363
|
return outcome("install", batchStatus(moduleInstall), root, {
|
|
@@ -428,17 +450,78 @@ async function handleSetup(root, parsed, runtime) {
|
|
|
428
450
|
message: "正在分析模块配置",
|
|
429
451
|
});
|
|
430
452
|
const { catalog, modules } = await buildContext(root);
|
|
431
|
-
const
|
|
453
|
+
const interaction = runtime.setupInteraction;
|
|
454
|
+
const inputByModule = new Map();
|
|
455
|
+
const approvedSteps = new Set();
|
|
456
|
+
const confirmSetupStep = interaction?.confirmStep
|
|
457
|
+
? async (moduleRef) => {
|
|
458
|
+
if (approvedSteps.has(moduleRef))
|
|
459
|
+
return;
|
|
460
|
+
if (!(await interaction.confirmStep?.(moduleRef)))
|
|
461
|
+
throw new PlatformError("INVALID_REQUEST", "配置已取消");
|
|
462
|
+
approvedSteps.add(moduleRef);
|
|
463
|
+
}
|
|
464
|
+
: undefined;
|
|
465
|
+
if (interaction)
|
|
466
|
+
interaction.begin();
|
|
467
|
+
let target = parsed.moduleRef === undefined
|
|
432
468
|
? undefined
|
|
433
469
|
: { moduleRef: parsed.moduleRef };
|
|
434
|
-
|
|
470
|
+
let result = await setupModulesThin(catalog, modules, root, target, runtime.onProgress, confirmSetupStep);
|
|
471
|
+
for (let attempt = 0; interaction && !result.completed && attempt < 8; attempt += 1) {
|
|
472
|
+
const actionable = [...result.results]
|
|
473
|
+
.reverse()
|
|
474
|
+
.find((item) => item.result.status === "ACTION_REQUIRED");
|
|
475
|
+
const action = actionable?.result.actionRequired;
|
|
476
|
+
if (!actionable || !action)
|
|
477
|
+
break;
|
|
478
|
+
let modelIds;
|
|
479
|
+
if (actionable.moduleRef === "model-runtime") {
|
|
480
|
+
const facts = await readModuleSharedFacts({ workspaceRoot: root }, "model-provider-api");
|
|
481
|
+
const inventory = facts?.models;
|
|
482
|
+
if (Array.isArray(inventory))
|
|
483
|
+
modelIds = inventory.flatMap((item) => {
|
|
484
|
+
if (typeof item === "string")
|
|
485
|
+
return [item];
|
|
486
|
+
if (isRecord(item) && typeof item.id === "string")
|
|
487
|
+
return [item.id];
|
|
488
|
+
return [];
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
const collected = await interaction.collect({
|
|
492
|
+
moduleRef: actionable.moduleRef,
|
|
493
|
+
action: action.action,
|
|
494
|
+
description: action.description,
|
|
495
|
+
...(modelIds?.length ? { modelIds } : {}),
|
|
496
|
+
});
|
|
497
|
+
if (!collected)
|
|
498
|
+
break;
|
|
499
|
+
const previous = inputByModule.get(actionable.moduleRef) ?? {};
|
|
500
|
+
const input = { ...previous, ...collected };
|
|
501
|
+
inputByModule.set(actionable.moduleRef, input);
|
|
502
|
+
const targeted = await setupModulesThin(catalog, modules, root, { moduleRef: actionable.moduleRef, input }, runtime.onProgress, confirmSetupStep);
|
|
503
|
+
if (parsed.moduleRef !== undefined || !targeted.completed) {
|
|
504
|
+
result = targeted;
|
|
505
|
+
target = { moduleRef: actionable.moduleRef, input };
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
target = undefined;
|
|
509
|
+
result = await setupModulesThin(catalog, modules, root, undefined, runtime.onProgress, confirmSetupStep);
|
|
510
|
+
}
|
|
511
|
+
if (interaction && result.completed)
|
|
512
|
+
interaction.finish("配置已完成");
|
|
435
513
|
reportProgress(runtime.onProgress, {
|
|
436
514
|
command: "setup",
|
|
437
515
|
phase: "setup",
|
|
438
516
|
status: result.completed ? "SUCCEEDED" : "ACTION_REQUIRED",
|
|
439
517
|
message: result.completed ? "模块配置已就绪" : "配置清单已生成",
|
|
440
518
|
});
|
|
441
|
-
return outcome("setup", batchStatus(result), root,
|
|
519
|
+
return outcome("setup", batchStatus(result), root, {
|
|
520
|
+
...result,
|
|
521
|
+
...(parsed.moduleRef === undefined
|
|
522
|
+
? {}
|
|
523
|
+
: { targetModuleRef: parsed.moduleRef }),
|
|
524
|
+
});
|
|
442
525
|
}
|
|
443
526
|
async function handleStart(root, runtime) {
|
|
444
527
|
const { catalog, modules } = await buildContext(root);
|
|
@@ -485,7 +568,7 @@ export async function runCli(argv, runtime = {}) {
|
|
|
485
568
|
case "setup":
|
|
486
569
|
return await handleSetup(root, parsed, runtime);
|
|
487
570
|
case "docs":
|
|
488
|
-
return await handleDocs(root, runtime);
|
|
571
|
+
return await handleDocs(root, parsed, runtime);
|
|
489
572
|
case "start":
|
|
490
573
|
return await handleStart(root, runtime);
|
|
491
574
|
case "stop":
|
|
@@ -658,6 +741,18 @@ function renderTerminalMarkdown(source, theme) {
|
|
|
658
741
|
function renderDocs(data, theme) {
|
|
659
742
|
if (!isRecord(data) || !Array.isArray(data.modules))
|
|
660
743
|
return "未发现模块文档。";
|
|
744
|
+
if (data.indexOnly === true) {
|
|
745
|
+
const modules = data.modules.filter(isRecord);
|
|
746
|
+
return [
|
|
747
|
+
theme.title("ProFlow 帮助"),
|
|
748
|
+
"",
|
|
749
|
+
"先运行 platform status 查看当前状态;需要某项详细说明时使用:",
|
|
750
|
+
` ${theme.command("platform docs --module <模块名>")}`,
|
|
751
|
+
"",
|
|
752
|
+
theme.section(`可用文档(${modules.length})`),
|
|
753
|
+
...modules.map((item) => ` ${String(item.moduleRef).padEnd(34)} ${theme.muted(String(item.version ?? ""))}`),
|
|
754
|
+
].join("\n");
|
|
755
|
+
}
|
|
661
756
|
const lines = [theme.title("ProFlow 模块文档")];
|
|
662
757
|
for (const raw of data.modules)
|
|
663
758
|
if (isRecord(raw))
|
|
@@ -668,36 +763,13 @@ function renderDocs(data, theme) {
|
|
|
668
763
|
.map((item) => `- ${String(item.moduleRef)}:${String(item.reason)}`));
|
|
669
764
|
return lines.join("\n");
|
|
670
765
|
}
|
|
671
|
-
const
|
|
672
|
-
"
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
"model-provider-api": {
|
|
677
|
-
ai: "platform setup --module model-provider-api",
|
|
678
|
-
inputs: "无(等待 Deployment resolver 提供 endpoint)",
|
|
679
|
-
},
|
|
680
|
-
"model-runtime": {
|
|
681
|
-
ai: "platform setup --module model-runtime",
|
|
682
|
-
inputs: "无(仅等价合格候选歧义时选择)",
|
|
683
|
-
},
|
|
684
|
-
"execution-browser-extension": {
|
|
685
|
-
ai: "platform setup --module execution-browser-extension",
|
|
686
|
-
inputs: "无",
|
|
687
|
-
},
|
|
688
|
-
"agent-controller-dev": {
|
|
689
|
-
ai: "platform setup --module agent-controller-dev",
|
|
690
|
-
inputs: "无",
|
|
691
|
-
},
|
|
692
|
-
"agent-product": {
|
|
693
|
-
ai: "platform setup --module agent-product",
|
|
694
|
-
inputs: "无",
|
|
695
|
-
},
|
|
696
|
-
"agent-test-ops": {
|
|
697
|
-
ai: "platform setup --module agent-test-ops",
|
|
698
|
-
inputs: "无",
|
|
699
|
-
},
|
|
766
|
+
const setupModuleLabels = {
|
|
767
|
+
"execution-browser-extension": "浏览器扩展",
|
|
768
|
+
"dev-tunnel": "远程连接",
|
|
769
|
+
"model-provider-api": "模型服务",
|
|
770
|
+
"model-runtime": "FAST / THINK 模型",
|
|
700
771
|
};
|
|
772
|
+
const setupModuleLabel = (moduleRef) => setupModuleLabels[moduleRef] ?? moduleRef;
|
|
701
773
|
function renderSetup(data, theme) {
|
|
702
774
|
if (!isRecord(data) || !Array.isArray(data.results))
|
|
703
775
|
return "没有需要执行的配置步骤。";
|
|
@@ -721,10 +793,14 @@ function renderSetup(data, theme) {
|
|
|
721
793
|
blocked += 1;
|
|
722
794
|
else
|
|
723
795
|
needsAction += 1;
|
|
724
|
-
lines.push(`${status === "FAILED" ? theme.failure("✕") : theme.warning("◆")} ${theme.section(moduleRef)}`);
|
|
796
|
+
lines.push(`${status === "FAILED" ? theme.failure("✕") : theme.warning("◆")} ${theme.section(setupModuleLabel(moduleRef))}`);
|
|
725
797
|
const plan = moduleSetupPlanDataSchema.safeParse(raw.result.data);
|
|
726
798
|
if (plan.success) {
|
|
799
|
+
const currentStep = plan.data.steps.find((step) => step.state === "BLOCKED") ??
|
|
800
|
+
plan.data.steps.find((step) => step.state === "TODO");
|
|
727
801
|
for (const [index, step] of plan.data.steps.entries()) {
|
|
802
|
+
if (step !== currentStep && step.state !== "DONE")
|
|
803
|
+
continue;
|
|
728
804
|
const marker = step.state === "DONE"
|
|
729
805
|
? theme.success("✓")
|
|
730
806
|
: step.state === "BLOCKED"
|
|
@@ -736,13 +812,10 @@ function renderSetup(data, theme) {
|
|
|
736
812
|
lines.push(` ${marker} ${String(index + 1).padStart(2, "0")} ${step.title}`);
|
|
737
813
|
if (step.description)
|
|
738
814
|
lines.push(` ${step.description}`);
|
|
739
|
-
lines.push(` 运行:${theme.command(step.execution.interactive)}`);
|
|
740
|
-
lines.push(` AI:${theme.command(step.execution.nonInteractive)}`);
|
|
741
815
|
if (step.requiredInputs.length > 0)
|
|
742
816
|
lines.push(` 需要:${step.requiredInputs.map((item) => item.description).join("、")}`);
|
|
743
817
|
if (step.humanAction)
|
|
744
818
|
lines.push(` 人工操作:${step.humanAction}`);
|
|
745
|
-
lines.push(` 验证:${theme.command(step.verify)}`);
|
|
746
819
|
lines.push(` 完成:${step.successCondition}`);
|
|
747
820
|
if (step.blockedReason)
|
|
748
821
|
lines.push(` 原因:${theme.failure(step.blockedReason)}`);
|
|
@@ -753,15 +826,7 @@ function renderSetup(data, theme) {
|
|
|
753
826
|
lines.push(` 1. ${status === "FAILED" ? "等待上游服务信息" : "完成模块配置"}`);
|
|
754
827
|
const error = raw.result.error;
|
|
755
828
|
if (status !== "FAILED") {
|
|
756
|
-
|
|
757
|
-
ai: `platform setup --module ${moduleRef}`,
|
|
758
|
-
inputs: "按命令提示提供",
|
|
759
|
-
};
|
|
760
|
-
lines.push(` 人工执行:${commands.ai}`);
|
|
761
|
-
lines.push(` AI 执行:${commands.ai}`);
|
|
762
|
-
lines.push(` 需要输入:${commands.inputs}`);
|
|
763
|
-
lines.push(" 验证:platform status");
|
|
764
|
-
lines.push(" 完成条件:配置状态变为“已就绪”");
|
|
829
|
+
lines.push(" 请在当前 Platform 向导中完成这一项。完成后会自动验证。");
|
|
765
830
|
}
|
|
766
831
|
else if (isRecord(error)) {
|
|
767
832
|
const code = error.code === undefined ? "FAILED" : String(error.code);
|
|
@@ -769,8 +834,7 @@ function renderSetup(data, theme) {
|
|
|
769
834
|
? "Module setup failed."
|
|
770
835
|
: String(error.message);
|
|
771
836
|
lines.push(` 原因:${code} — ${message}`);
|
|
772
|
-
lines.push(
|
|
773
|
-
lines.push(" 完成条件:配置状态变为“已就绪”");
|
|
837
|
+
lines.push(" 修复当前问题后重新运行 platform setup。");
|
|
774
838
|
}
|
|
775
839
|
lines.push("");
|
|
776
840
|
}
|
|
@@ -784,8 +848,11 @@ function renderSetup(data, theme) {
|
|
|
784
848
|
blocked += 1;
|
|
785
849
|
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}`)}`, "");
|
|
786
850
|
}
|
|
851
|
+
const targetModuleRef = typeof data.targetModuleRef === "string" ? data.targetModuleRef : undefined;
|
|
787
852
|
if (needsAction === 0 && blocked === 0)
|
|
788
|
-
lines.push(
|
|
853
|
+
lines.push(targetModuleRef
|
|
854
|
+
? `${setupModuleLabel(targetModuleRef)}配置已就绪。`
|
|
855
|
+
: "全部模块均已就绪。");
|
|
789
856
|
lines.push(theme.section("汇总"), `汇总:${theme.success(`${ready} 个已就绪`)},${theme.warning(`${needsAction} 个需要操作`)},${blocked > 0 ? theme.failure(`${blocked} 个系统阻塞`) : `${blocked} 个系统阻塞`}`);
|
|
790
857
|
return lines.join("\n").trimEnd();
|
|
791
858
|
}
|
|
@@ -808,8 +875,8 @@ function renderHelp(theme, command) {
|
|
|
808
875
|
description: "自动配置并列出全部剩余步骤",
|
|
809
876
|
},
|
|
810
877
|
docs: {
|
|
811
|
-
usage: "platform docs [--workspace <路径>]",
|
|
812
|
-
description: "
|
|
878
|
+
usage: "platform docs [--workspace <路径>] [--module <模块名>]",
|
|
879
|
+
description: "查看文档索引,或阅读一个模块的详细文档",
|
|
813
880
|
},
|
|
814
881
|
start: {
|
|
815
882
|
usage: "platform start [--workspace <路径>]",
|
|
@@ -974,7 +1041,12 @@ export function renderHumanResult(result, options = {}) {
|
|
|
974
1041
|
if (import.meta.main) {
|
|
975
1042
|
const argv = process.argv.slice(2);
|
|
976
1043
|
const reporter = createTerminalProgressReporter();
|
|
977
|
-
const result = await runCli(argv, {
|
|
1044
|
+
const result = await runCli(argv, {
|
|
1045
|
+
onProgress: reporter,
|
|
1046
|
+
...(process.stdin.isTTY && process.stdout.isTTY
|
|
1047
|
+
? { setupInteraction: createClackSetupInteraction() }
|
|
1048
|
+
: {}),
|
|
1049
|
+
});
|
|
978
1050
|
reporter.close();
|
|
979
1051
|
const color = process.stdout.isTTY === true &&
|
|
980
1052
|
process.env.NO_COLOR === undefined &&
|
|
@@ -71,7 +71,7 @@ export async function readWorkspacePackageManagerSelection(workspaceRoot) {
|
|
|
71
71
|
return { name, source: "declared", declared };
|
|
72
72
|
}
|
|
73
73
|
if (lockManagers.size > 1) {
|
|
74
|
-
throw new PlatformError("PACKAGE_MANAGER_CONFLICT",
|
|
74
|
+
throw new PlatformError("PACKAGE_MANAGER_CONFLICT", `检测到多个包管理器的 lockfile(${[...lockManagers].sort().join(", ")})。Platform 不会自动删除或替你选择;请先保留现场并明确工作区应使用的唯一包管理器。`);
|
|
75
75
|
}
|
|
76
76
|
const [fromLockfile] = lockManagers;
|
|
77
77
|
if (fromLockfile !== undefined) {
|
|
@@ -30,5 +30,5 @@ export declare function stopModulesThin(catalog: ModuleCatalog, modules: readonl
|
|
|
30
30
|
export declare function setupModulesThin(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, target?: {
|
|
31
31
|
moduleRef: string;
|
|
32
32
|
input?: unknown;
|
|
33
|
-
}, reporter?: PlatformProgressReporter): Promise<ModuleBatchResult>;
|
|
33
|
+
}, reporter?: PlatformProgressReporter, beforeSetup?: (moduleRef: string) => Promise<void>): Promise<ModuleBatchResult>;
|
|
34
34
|
export declare function startModulesThin(catalog: ModuleCatalog, modules: readonly ResolvedModule[], workspaceRoot: string, reporter?: PlatformProgressReporter): Promise<ModuleBatchResult>;
|
|
@@ -172,7 +172,7 @@ export async function stopModulesThin(catalog, modules, workspaceRoot, reporter)
|
|
|
172
172
|
}
|
|
173
173
|
return { phase: "stop", results, completed: true, skipped };
|
|
174
174
|
}
|
|
175
|
-
export async function setupModulesThin(catalog, modules, workspaceRoot, target, reporter) {
|
|
175
|
+
export async function setupModulesThin(catalog, modules, workspaceRoot, target, reporter, beforeSetup) {
|
|
176
176
|
const results = [];
|
|
177
177
|
const skipped = [];
|
|
178
178
|
const blockers = [];
|
|
@@ -180,8 +180,40 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
180
180
|
const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
|
|
181
181
|
if (target !== undefined && !byRef.has(target.moduleRef))
|
|
182
182
|
throw new PlatformError("INVALID_REQUEST", `setup target module ${target.moduleRef} was not discovered`);
|
|
183
|
-
const
|
|
184
|
-
|
|
183
|
+
const setupPriority = [
|
|
184
|
+
"execution-browser-extension",
|
|
185
|
+
"dev-tunnel",
|
|
186
|
+
"model-provider-api",
|
|
187
|
+
"model-runtime",
|
|
188
|
+
];
|
|
189
|
+
const dependenciesByRef = new Map();
|
|
190
|
+
for (const edge of graph.edges) {
|
|
191
|
+
const dependencies = dependenciesByRef.get(edge.from) ?? [];
|
|
192
|
+
dependencies.push(edge.to);
|
|
193
|
+
dependenciesByRef.set(edge.from, dependencies);
|
|
194
|
+
}
|
|
195
|
+
const userOrder = [];
|
|
196
|
+
const included = new Set();
|
|
197
|
+
const appendWithDependencies = (moduleRef) => {
|
|
198
|
+
if (included.has(moduleRef) || !byRef.has(moduleRef))
|
|
199
|
+
return;
|
|
200
|
+
for (const dependency of graph.order)
|
|
201
|
+
if (dependenciesByRef.get(moduleRef)?.includes(dependency))
|
|
202
|
+
appendWithDependencies(dependency);
|
|
203
|
+
included.add(moduleRef);
|
|
204
|
+
userOrder.push(moduleRef);
|
|
205
|
+
};
|
|
206
|
+
if (target === undefined) {
|
|
207
|
+
for (const moduleRef of setupPriority)
|
|
208
|
+
appendWithDependencies(moduleRef);
|
|
209
|
+
for (const moduleRef of graph.order)
|
|
210
|
+
appendWithDependencies(moduleRef);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
appendWithDependencies(target.moduleRef);
|
|
214
|
+
}
|
|
215
|
+
const selectedRefs = new Set(target === undefined ? userOrder : [target.moduleRef]);
|
|
216
|
+
const modulesInOrder = userOrder
|
|
185
217
|
.filter((moduleRef) => selectedRefs.has(moduleRef))
|
|
186
218
|
.map((moduleRef) => byRef.get(moduleRef))
|
|
187
219
|
.filter((module) => module !== undefined);
|
|
@@ -200,9 +232,7 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
200
232
|
if (!succeeded(status.result)) {
|
|
201
233
|
results.push(status);
|
|
202
234
|
completed = false;
|
|
203
|
-
|
|
204
|
-
break;
|
|
205
|
-
continue;
|
|
235
|
+
break;
|
|
206
236
|
}
|
|
207
237
|
const observed = moduleStatusObservationSchema.parse(status.result.data);
|
|
208
238
|
if (observed.setupStatus === "READY" && target === undefined) {
|
|
@@ -218,6 +248,7 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
218
248
|
});
|
|
219
249
|
continue;
|
|
220
250
|
}
|
|
251
|
+
await beforeSetup?.(module.moduleRef);
|
|
221
252
|
const setup = await dispatchModuleCommand(catalog, module, "setup", context(workspaceRoot, target?.input));
|
|
222
253
|
results.push(setup);
|
|
223
254
|
reportProgress(reporter, {
|
|
@@ -243,8 +274,7 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
243
274
|
}
|
|
244
275
|
else {
|
|
245
276
|
const reconciledStatus = moduleStatusObservationSchema.parse(reconciled.result.data);
|
|
246
|
-
if (reconciledStatus.setupStatus
|
|
247
|
-
reconciledStatus.setupStatus === "FAILED") {
|
|
277
|
+
if (reconciledStatus.setupStatus !== "READY") {
|
|
248
278
|
const issue = reconciledStatus.issues?.find((item) => item.scope === "SETUP");
|
|
249
279
|
blockers.push({
|
|
250
280
|
moduleRef: module.moduleRef,
|
|
@@ -256,8 +286,7 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
|
|
|
256
286
|
completed = false;
|
|
257
287
|
}
|
|
258
288
|
}
|
|
259
|
-
if (
|
|
260
|
-
(!succeeded(setup.result) || blockers.length > 0))
|
|
289
|
+
if (!succeeded(setup.result) || blockers.length > 0)
|
|
261
290
|
break;
|
|
262
291
|
}
|
|
263
292
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const PRO_FLOW_SCOPE = "@tomflow";
|
|
2
2
|
export declare const PRO_FLOW_PACKAGE_PREFIX = "@tomflow/proflow-";
|
|
3
3
|
export declare const RETIRED_PRO_FLOW_PACKAGES: Set<string>;
|
|
4
|
+
export declare const PRO_FLOW_TOOL_PACKAGES: Set<string>;
|
|
4
5
|
export interface NpmCommandResult {
|
|
5
6
|
stdout: string;
|
|
6
7
|
stderr: string;
|
|
@@ -28,6 +29,7 @@ export interface RegistryDiscoveryResult {
|
|
|
28
29
|
candidates: RegistryModuleCandidate[];
|
|
29
30
|
rejected: RegistryRejectedPackage[];
|
|
30
31
|
retired: string[];
|
|
32
|
+
tools: string[];
|
|
31
33
|
}
|
|
32
34
|
export declare function systemNpmRunner(): NpmCommandRunner;
|
|
33
35
|
export declare function resolveScopeRegistry(workspaceRoot: string, runner?: NpmCommandRunner): Promise<string>;
|
|
@@ -9,6 +9,9 @@ export const PRO_FLOW_PACKAGE_PREFIX = "@tomflow/proflow-";
|
|
|
9
9
|
export const RETIRED_PRO_FLOW_PACKAGES = new Set([
|
|
10
10
|
"@tomflow/proflow-chatgpt-carrier",
|
|
11
11
|
]);
|
|
12
|
+
export const PRO_FLOW_TOOL_PACKAGES = new Set([
|
|
13
|
+
"@tomflow/proflow-devtunnel-cli",
|
|
14
|
+
]);
|
|
12
15
|
export function systemNpmRunner() {
|
|
13
16
|
return {
|
|
14
17
|
async run(args, cwd, timeoutMs) {
|
|
@@ -46,7 +49,8 @@ export async function discoverRegistryModules(options) {
|
|
|
46
49
|
? await searchPackageNames(options.workspaceRoot, registry, runner)
|
|
47
50
|
: [validateRequestedPackageName(options.packageName)];
|
|
48
51
|
const retired = names.filter((name) => RETIRED_PRO_FLOW_PACKAGES.has(name));
|
|
49
|
-
const
|
|
52
|
+
const tools = names.filter((name) => PRO_FLOW_TOOL_PACKAGES.has(name));
|
|
53
|
+
const activeNames = names.filter((name) => !RETIRED_PRO_FLOW_PACKAGES.has(name) && !PRO_FLOW_TOOL_PACKAGES.has(name));
|
|
50
54
|
options.onSearchComplete?.(activeNames.length);
|
|
51
55
|
const candidates = [];
|
|
52
56
|
const rejected = [];
|
|
@@ -75,7 +79,7 @@ export async function discoverRegistryModules(options) {
|
|
|
75
79
|
await Promise.all(Array.from({ length: Math.min(4, activeNames.length) }, () => worker()));
|
|
76
80
|
candidates.sort((left, right) => left.packageName.localeCompare(right.packageName));
|
|
77
81
|
rejected.sort((left, right) => left.packageName.localeCompare(right.packageName));
|
|
78
|
-
return { registry, candidates, rejected, retired };
|
|
82
|
+
return { registry, candidates, rejected, retired, tools };
|
|
79
83
|
}
|
|
80
84
|
async function searchPackageNames(workspaceRoot, registry, runner) {
|
|
81
85
|
const result = await runner.run([
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SetupActionRequest = {
|
|
2
|
+
moduleRef: string;
|
|
3
|
+
action: string;
|
|
4
|
+
description: string;
|
|
5
|
+
modelIds?: readonly string[];
|
|
6
|
+
};
|
|
7
|
+
export interface SetupInteraction {
|
|
8
|
+
begin(): void;
|
|
9
|
+
finish(message: string): void;
|
|
10
|
+
confirmStep?(moduleRef: string): Promise<boolean>;
|
|
11
|
+
collect(request: SetupActionRequest): Promise<Record<string, unknown> | undefined>;
|
|
12
|
+
}
|
|
13
|
+
export declare function validateProviderBaseUrl(value: string): string | undefined;
|
|
14
|
+
export declare function createClackSetupInteraction(): SetupInteraction;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { autocomplete, cancel, confirm, intro, isCancel, outro, password, text, } from "@clack/prompts";
|
|
2
|
+
const stepPrompts = {
|
|
3
|
+
"execution-browser-extension": "下一步将打开 Chrome,并引导你加载浏览器扩展。按 Enter 继续。",
|
|
4
|
+
"dev-tunnel": "下一步将检查远程连接;如需登录,会打开 GitHub 授权。按 Enter 继续。",
|
|
5
|
+
"model-provider-api": "下一步配置 OpenAI-compatible 模型服务地址。按 Enter 继续。",
|
|
6
|
+
"model-runtime": "下一步从真实模型列表选择 FAST 与 THINK。按 Enter 继续。",
|
|
7
|
+
};
|
|
8
|
+
function cancelled(value) {
|
|
9
|
+
if (!isCancel(value))
|
|
10
|
+
return false;
|
|
11
|
+
cancel("已取消配置,没有继续执行后续步骤。");
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
function privateIpv4(hostname) {
|
|
15
|
+
const parts = hostname.split(".").map(Number);
|
|
16
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part)))
|
|
17
|
+
return false;
|
|
18
|
+
const [first = -1, second = -1] = parts;
|
|
19
|
+
return (first === 10 ||
|
|
20
|
+
first === 127 ||
|
|
21
|
+
(first === 192 && second === 168) ||
|
|
22
|
+
(first === 172 && second >= 16 && second <= 31));
|
|
23
|
+
}
|
|
24
|
+
export function validateProviderBaseUrl(value) {
|
|
25
|
+
let url;
|
|
26
|
+
try {
|
|
27
|
+
url = new URL(value.trim());
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return "请输入包含 http:// 或 https:// 的完整 API Base URL。";
|
|
31
|
+
}
|
|
32
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
33
|
+
return "只支持 HTTP(S) OpenAI-compatible 服务。";
|
|
34
|
+
if (/\/chat\/completions\/?$/i.test(url.pathname))
|
|
35
|
+
return "请填写服务 Base URL,不要包含 /chat/completions。";
|
|
36
|
+
const local = url.hostname === "localhost" ||
|
|
37
|
+
url.hostname.endsWith(".local") ||
|
|
38
|
+
privateIpv4(url.hostname);
|
|
39
|
+
if (!local && url.protocol !== "https:")
|
|
40
|
+
return "公网模型服务必须使用 https://。";
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
async function providerEndpoint() {
|
|
44
|
+
const value = await text({
|
|
45
|
+
message: "OpenAI-compatible 服务 Base URL",
|
|
46
|
+
placeholder: "例如 https://api.deepseek.com 或 http://192.168.0.108:8080/v1",
|
|
47
|
+
validate: (candidate) => candidate === undefined || candidate.trim().length === 0
|
|
48
|
+
? "API Base URL 不能为空。"
|
|
49
|
+
: validateProviderBaseUrl(candidate),
|
|
50
|
+
});
|
|
51
|
+
if (cancelled(value))
|
|
52
|
+
return undefined;
|
|
53
|
+
const providerBaseUrl = String(value).trim().replace(/\/$/, "");
|
|
54
|
+
const approved = await confirm({
|
|
55
|
+
message: `使用 ${providerBaseUrl} 连接整个模型服务?`,
|
|
56
|
+
initialValue: true,
|
|
57
|
+
});
|
|
58
|
+
if (cancelled(approved) || approved !== true)
|
|
59
|
+
return undefined;
|
|
60
|
+
return { providerBaseUrl };
|
|
61
|
+
}
|
|
62
|
+
async function providerCredential() {
|
|
63
|
+
const value = await password({
|
|
64
|
+
message: "模型服务 API Key(输入不会显示)",
|
|
65
|
+
mask: "•",
|
|
66
|
+
validate: (candidate) => candidate === undefined || candidate.trim().length === 0
|
|
67
|
+
? "API Key 不能为空。"
|
|
68
|
+
: undefined,
|
|
69
|
+
});
|
|
70
|
+
if (cancelled(value))
|
|
71
|
+
return undefined;
|
|
72
|
+
return { providerCredential: String(value) };
|
|
73
|
+
}
|
|
74
|
+
async function modelRoles(modelIds) {
|
|
75
|
+
const options = modelIds.map((id) => ({ value: id, label: id }));
|
|
76
|
+
const fastModel = await autocomplete({
|
|
77
|
+
message: "选择 FAST 模型(快速、低延迟)",
|
|
78
|
+
placeholder: "输入关键字搜索模型",
|
|
79
|
+
options,
|
|
80
|
+
});
|
|
81
|
+
if (cancelled(fastModel))
|
|
82
|
+
return undefined;
|
|
83
|
+
const reasonModel = await autocomplete({
|
|
84
|
+
message: "选择 THINK 模型(复杂分析与推理)",
|
|
85
|
+
placeholder: "输入关键字搜索模型",
|
|
86
|
+
options,
|
|
87
|
+
});
|
|
88
|
+
if (cancelled(reasonModel))
|
|
89
|
+
return undefined;
|
|
90
|
+
const approved = await confirm({
|
|
91
|
+
message: `确认 FAST=${String(fastModel)},THINK=${String(reasonModel)}?`,
|
|
92
|
+
initialValue: true,
|
|
93
|
+
});
|
|
94
|
+
if (cancelled(approved) || approved !== true)
|
|
95
|
+
return undefined;
|
|
96
|
+
return { fastModel: String(fastModel), reasonModel: String(reasonModel) };
|
|
97
|
+
}
|
|
98
|
+
export function createClackSetupInteraction() {
|
|
99
|
+
return {
|
|
100
|
+
begin() {
|
|
101
|
+
intro("ProFlow 配置向导");
|
|
102
|
+
},
|
|
103
|
+
finish(message) {
|
|
104
|
+
outro(message);
|
|
105
|
+
},
|
|
106
|
+
async confirmStep(moduleRef) {
|
|
107
|
+
const message = stepPrompts[moduleRef];
|
|
108
|
+
if (!message)
|
|
109
|
+
return true;
|
|
110
|
+
const approved = await confirm({ message, initialValue: true });
|
|
111
|
+
return !cancelled(approved) && approved === true;
|
|
112
|
+
},
|
|
113
|
+
async collect(request) {
|
|
114
|
+
if (request.action === "provide-provider-endpoint") {
|
|
115
|
+
return providerEndpoint();
|
|
116
|
+
}
|
|
117
|
+
if (request.action === "provide-provider-credential") {
|
|
118
|
+
return providerCredential();
|
|
119
|
+
}
|
|
120
|
+
if (request.action.startsWith("select-") && request.modelIds?.length) {
|
|
121
|
+
return modelRoles(request.modelIds);
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
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.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,11 +24,12 @@
|
|
|
24
24
|
"SETUP.md"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@clack/prompts": "1.7.0",
|
|
27
28
|
"@tomflow/proflow-module-contract": "^0.1.13"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
|
-
"@tomflow/proflow-
|
|
31
|
-
"@tomflow/proflow-
|
|
31
|
+
"@tomflow/proflow-module-template": "^0.1.10",
|
|
32
|
+
"@tomflow/proflow-deployment-conformance": "^0.1.13"
|
|
32
33
|
},
|
|
33
34
|
"description": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration.",
|
|
34
35
|
"keywords": [
|
package/proflow.module.json
CHANGED