@tomflow/proflow-platform-cli 0.1.36 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @tomflow/proflow-platform-cli
2
2
 
3
+ ## 0.1.37
4
+
5
+ ### Patch Changes
6
+
7
+ - Ship the Phase 3 browser, tunnel, model automation, platform readiness, and documentation closure as one verifiable platform snapshot.
8
+
3
9
  ## 0.1.36
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -32,7 +32,7 @@ Platform does not interpret Module-private configuration or recreate a cross-Mod
32
32
 
33
33
  ## Install / Uninstall
34
34
 
35
- `platform install` validates or creates the minimal Workspace metadata, discovers the complete governed package set from the Registry, synchronizes that set, validates the installed descriptors, then invokes `Module.install` in dependency order. It does not perform human setup work.
35
+ `platform install` validates or creates the minimal Workspace metadata, discovers the complete governed package set from the Registry, synchronizes that set, validates the installed descriptors, then invokes `Module.install` in the frozen Deployment Install Order. This product deployment order is distinct from Runtime dependency topology: Chrome is first, followed by Browser Extension, Agent packages, Gateway/Tunnel, runtime/data foundations and governance packages. It does not perform human setup work.
36
36
 
37
37
  `platform uninstall` invokes `Module.uninstall` in reverse dependency order before package removal. `.proflow` is Workspace/user data and is preserved unless an owning Module explicitly removes its own artifacts.
38
38
 
@@ -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.36";
9
+ readonly moduleVersion: "0.1.37";
10
10
  };
11
11
  observedEffects: never[];
12
12
  }>;
@@ -16,7 +16,7 @@ export declare const behaviorAdapter: {
16
16
  readonly ok: true;
17
17
  readonly status: "SUCCEEDED";
18
18
  readonly moduleRef: "platform-cli";
19
- readonly moduleVersion: "0.1.36";
19
+ readonly moduleVersion: "0.1.37";
20
20
  };
21
21
  observedEffects: never[];
22
22
  }>;
@@ -26,7 +26,7 @@ export declare const behaviorAdapter: {
26
26
  ok: true;
27
27
  status: "SUCCEEDED";
28
28
  moduleRef: "platform-cli";
29
- moduleVersion: "0.1.36";
29
+ moduleVersion: "0.1.37";
30
30
  data: {
31
31
  readonly setupStatus: "READY";
32
32
  readonly runtimeStatus: "NOT_APPLICABLE";
@@ -40,7 +40,7 @@ export declare const behaviorAdapter: {
40
40
  readonly ok: true;
41
41
  readonly status: "SUCCEEDED";
42
42
  readonly moduleRef: "platform-cli";
43
- readonly moduleVersion: "0.1.36";
43
+ readonly moduleVersion: "0.1.37";
44
44
  };
45
45
  observedEffects: never[];
46
46
  }>;
@@ -50,7 +50,7 @@ export declare const behaviorAdapter: {
50
50
  ok: true;
51
51
  status: "SUCCEEDED";
52
52
  moduleRef: "platform-cli";
53
- moduleVersion: "0.1.36";
53
+ moduleVersion: "0.1.37";
54
54
  data: {
55
55
  docs: string;
56
56
  };
@@ -63,7 +63,7 @@ export declare const behaviorAdapter: {
63
63
  readonly ok: true;
64
64
  readonly status: "SUCCEEDED";
65
65
  readonly moduleRef: "platform-cli";
66
- readonly moduleVersion: "0.1.36";
66
+ readonly moduleVersion: "0.1.37";
67
67
  };
68
68
  observedEffects: never[];
69
69
  }>;
@@ -73,7 +73,7 @@ export declare const behaviorAdapter: {
73
73
  readonly ok: true;
74
74
  readonly status: "SUCCEEDED";
75
75
  readonly moduleRef: "platform-cli";
76
- readonly moduleVersion: "0.1.36";
76
+ readonly moduleVersion: "0.1.37";
77
77
  };
78
78
  observedEffects: never[];
79
79
  }>;
@@ -3,7 +3,7 @@ export declare const descriptor: {
3
3
  readonly contractVersion: "1.0.0";
4
4
  readonly moduleRef: "platform-cli";
5
5
  readonly packageName: "@tomflow/proflow-platform-cli";
6
- readonly moduleVersion: "0.1.36";
6
+ readonly moduleVersion: "0.1.37";
7
7
  readonly kind: "cli";
8
8
  readonly templateVersion: "1.0.0";
9
9
  readonly platformCompatibility: ">=1.0.0 <2.0.0";
@@ -3,7 +3,7 @@ export const descriptor = {
3
3
  contractVersion: "1.0.0",
4
4
  moduleRef: "platform-cli",
5
5
  packageName: "@tomflow/proflow-platform-cli",
6
- moduleVersion: "0.1.36",
6
+ moduleVersion: "0.1.37",
7
7
  kind: "cli",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
package/dist/src/cli.js CHANGED
@@ -604,10 +604,10 @@ function renderStatus(data, theme) {
604
604
  };
605
605
  compact("运行中", running, "●");
606
606
  compact("已就绪", healthy, "○");
607
- const ready = modules.filter((item) => item.setupStatus === "READY").length;
608
- const action = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED").length;
609
- const failed = modules.filter((item) => item.setupStatus === "FAILED").length;
610
- const blocked = modules.filter((item) => item.setupStatus === "BLOCKED").length;
607
+ const ready = healthy.length + running.length;
608
+ const action = modules.filter((item) => item.setupStatus === "ACTION_REQUIRED" && item.runtimeStatus !== "FAILED").length;
609
+ const failed = failedModules.length;
610
+ const blocked = modules.filter((item) => item.setupStatus === "BLOCKED" && item.runtimeStatus !== "FAILED").length;
611
611
  lines.push(theme.section("汇总"), `${theme.success(`${ready} 已就绪`)} · ${theme.warning(`${action} 需要操作`)} · ${theme.info(`${blocked} 等待依赖`)} · ${failed > 0 ? theme.failure(`${failed} 失败`) : `${failed} 失败`}`);
612
612
  return lines.join("\n");
613
613
  }
@@ -653,36 +653,36 @@ function renderDocs(data, theme) {
653
653
  }
654
654
  const setupCommands = {
655
655
  "chatgpt-carrier": {
656
- ai: "pnpm exec -- proflow-chatgpt-carrier setup --carrier-url <url>",
657
- inputs: "Custom GPT URL",
656
+ ai: "pnpm exec -- proflow-chatgpt-carrier setup",
657
+ inputs: "",
658
658
  },
659
659
  "dev-tunnel": {
660
- ai: "pnpm exec -- proflow-dev-tunnel setup --tunnel-id <id> --public-base-url <url>",
661
- inputs: "Tunnel ID、公开 HTTPS URL",
660
+ ai: "pnpm exec -- proflow-dev-tunnel setup",
661
+ inputs: "",
662
662
  },
663
663
  "model-provider-api": {
664
- ai: "pnpm exec -- proflow-model-provider-api setup --provider-base-url <url>",
665
- inputs: "模型服务 Base URL",
664
+ ai: "pnpm exec -- proflow-model-provider-api setup",
665
+ inputs: "无(等待 Deployment resolver 提供 endpoint)",
666
666
  },
667
667
  "model-runtime": {
668
- ai: "pnpm exec -- proflow-model-runtime setup --fast-model <id> --reason-model <id>",
669
- inputs: "FAST 模型 ID、REASON 模型 ID",
668
+ ai: "pnpm exec -- proflow-model-runtime setup",
669
+ inputs: "无(仅等价合格候选歧义时选择)",
670
670
  },
671
671
  "execution-browser-extension": {
672
- ai: "pnpm exec -- proflow-execution-browser-extension setup --extension-id <id>",
673
- inputs: "Chrome Extension ID",
672
+ ai: "pnpm exec -- proflow-execution-browser-extension setup",
673
+ inputs: "",
674
674
  },
675
675
  "agent-controller-dev": {
676
- ai: "pnpm exec -- proflow-agent-controller-dev setup --carrier-url <url>",
677
- inputs: "Custom GPT URL",
676
+ ai: "pnpm exec -- proflow-agent-controller-dev setup",
677
+ inputs: "",
678
678
  },
679
679
  "agent-product": {
680
- ai: "pnpm exec -- proflow-agent-product setup --carrier-url <url>",
681
- inputs: "Custom GPT URL",
680
+ ai: "pnpm exec -- proflow-agent-product setup",
681
+ inputs: "",
682
682
  },
683
683
  "agent-test-ops": {
684
- ai: "pnpm exec -- proflow-agent-test-ops setup --carrier-url <url>",
685
- inputs: "Custom GPT URL",
684
+ ai: "pnpm exec -- proflow-agent-test-ops setup",
685
+ inputs: "",
686
686
  },
687
687
  };
688
688
  function renderSetup(data, theme) {
@@ -693,10 +693,12 @@ function renderSetup(data, theme) {
693
693
  let ready = skipped.filter((item) => isRecord(item) && item.reason === "READY").length;
694
694
  let needsAction = 0;
695
695
  let blocked = 0;
696
+ const renderedModuleRefs = new Set();
696
697
  for (const raw of data.results) {
697
698
  if (!isRecord(raw) || !isRecord(raw.result))
698
699
  continue;
699
700
  const moduleRef = String(raw.moduleRef ?? raw.result.moduleRef ?? "unknown");
701
+ renderedModuleRefs.add(moduleRef);
700
702
  const status = String(raw.result.status ?? "UNKNOWN");
701
703
  if (status === "SUCCEEDED") {
702
704
  ready += 1;
@@ -759,6 +761,16 @@ function renderSetup(data, theme) {
759
761
  }
760
762
  lines.push("");
761
763
  }
764
+ const dependencyBlockers = Array.isArray(data.blockers)
765
+ ? data.blockers.filter(isRecord)
766
+ : [];
767
+ for (const item of dependencyBlockers) {
768
+ const moduleRef = String(item.moduleRef ?? "unknown");
769
+ if (renderedModuleRefs.has(moduleRef))
770
+ continue;
771
+ blocked += 1;
772
+ lines.push(`${theme.info("◇")} ${theme.section(moduleRef)}`, ` 原因:${typeof item.reason === "string" ? item.reason : "等待上游 Module 就绪"}`, ` 下一步:${theme.command(typeof item.nextCommand === "string" ? item.nextCommand : `platform setup --module ${moduleRef}`)}`, "");
773
+ }
762
774
  if (needsAction === 0 && blocked === 0)
763
775
  lines.push("全部模块均已就绪。");
764
776
  lines.push(theme.section("汇总"), `汇总:${theme.success(`${ready} 个已就绪`)},${theme.warning(`${needsAction} 个需要操作`)},${blocked > 0 ? theme.failure(`${blocked} 个系统阻塞`) : `${blocked} 个系统阻塞`}`);
@@ -830,7 +842,6 @@ function renderHelp(theme, command) {
830
842
  theme.section("人工配置示例"),
831
843
  ` 人工:${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
832
844
  ` AI: ${theme.command("pnpm exec -- proflow-chatgpt-carrier setup")}`,
833
- ` ${theme.command("--carrier-url <url>")}`,
834
845
  "",
835
846
  theme.section("状态图例"),
836
847
  ` ${theme.success("已就绪")} 配置与验证完成`,
@@ -0,0 +1,2 @@
1
+ export declare const FROZEN_DEPLOYMENT_INSTALL_ORDER: readonly ["chrome-runtime", "execution-browser-extension", "agent-controller-dev", "agent-product", "agent-test-ops", "agent-gateway", "dev-tunnel", "agent-runtime", "platform-host", "execution-runtime", "execution-local", "model-provider-api", "model-runtime", "task-orchestration", "task-store-sqlite", "task-migration-runner", "execution-contracts", "model-contracts", "module-contract", "module-skill", "module-template", "deployment-conformance", "platform-cli", "chatgpt-carrier"];
2
+ export declare function orderModuleRefsForInstall(moduleRefs: readonly string[]): string[];
@@ -0,0 +1,40 @@
1
+ export const FROZEN_DEPLOYMENT_INSTALL_ORDER = [
2
+ "chrome-runtime",
3
+ "execution-browser-extension",
4
+ "agent-controller-dev",
5
+ "agent-product",
6
+ "agent-test-ops",
7
+ "agent-gateway",
8
+ "dev-tunnel",
9
+ "agent-runtime",
10
+ "platform-host",
11
+ "execution-runtime",
12
+ "execution-local",
13
+ "model-provider-api",
14
+ "model-runtime",
15
+ "task-orchestration",
16
+ "task-store-sqlite",
17
+ "task-migration-runner",
18
+ "execution-contracts",
19
+ "model-contracts",
20
+ "module-contract",
21
+ "module-skill",
22
+ "module-template",
23
+ "deployment-conformance",
24
+ "platform-cli",
25
+ "chatgpt-carrier",
26
+ ];
27
+ const installRank = new Map(FROZEN_DEPLOYMENT_INSTALL_ORDER.map((moduleRef, index) => [moduleRef, index]));
28
+ export function orderModuleRefsForInstall(moduleRefs) {
29
+ return [...moduleRefs].sort((left, right) => {
30
+ const leftRank = installRank.get(left);
31
+ const rightRank = installRank.get(right);
32
+ if (leftRank !== undefined && rightRank !== undefined)
33
+ return leftRank - rightRank;
34
+ if (leftRank !== undefined)
35
+ return -1;
36
+ if (rightRank !== undefined)
37
+ return 1;
38
+ return left.localeCompare(right);
39
+ });
40
+ }
@@ -1,4 +1,5 @@
1
1
  import { moduleStatusObservationSchema, } from "@tomflow/proflow-module-contract";
2
+ import { orderModuleRefsForInstall } from "../deployment-order.js";
2
3
  import { PlatformError } from "../errors.js";
3
4
  import { buildDependencyGraph } from "../graph/graph.js";
4
5
  import { reportProgress } from "../progress.js";
@@ -13,6 +14,16 @@ function ordered(modules, reverse = false) {
13
14
  .map((ref) => byRef.get(ref))
14
15
  .filter((item) => item !== undefined);
15
16
  }
17
+ function orderedForInstall(modules) {
18
+ // Keep dependency validation, but do not let dependency depth/alphabetical order
19
+ // dictate the user-facing package installation trajectory.
20
+ buildDependencyGraph(modules);
21
+ const refs = orderModuleRefsForInstall(modules.map((module) => module.moduleRef));
22
+ const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
23
+ return refs
24
+ .map((ref) => byRef.get(ref))
25
+ .filter((item) => item !== undefined);
26
+ }
16
27
  export async function observeStatuses(catalog, modules, workspaceRoot, reporter) {
17
28
  const results = [];
18
29
  const modulesInOrder = [...modules].sort((a, b) => a.moduleRef.localeCompare(b.moduleRef));
@@ -69,7 +80,9 @@ export async function observeDocs(catalog, modules, workspaceRoot, reporter) {
69
80
  }
70
81
  async function runOrdered(catalog, modules, workspaceRoot, command, reverse, reporter) {
71
82
  const results = [];
72
- const modulesInOrder = ordered(modules, reverse);
83
+ const modulesInOrder = command === "install"
84
+ ? orderedForInstall(modules)
85
+ : ordered(modules, reverse);
73
86
  for (const [index, module] of modulesInOrder.entries()) {
74
87
  reportProgress(reporter, {
75
88
  command,
@@ -162,12 +175,37 @@ export async function stopModulesThin(catalog, modules, workspaceRoot, reporter)
162
175
  export async function setupModulesThin(catalog, modules, workspaceRoot, target, reporter) {
163
176
  const results = [];
164
177
  const skipped = [];
165
- let matched = target === undefined;
178
+ const blockers = [];
179
+ const graph = buildDependencyGraph(modules);
180
+ const byRef = new Map(modules.map((module) => [module.moduleRef, module]));
181
+ const dependenciesByRef = new Map();
182
+ for (const edge of graph.edges) {
183
+ const dependencies = dependenciesByRef.get(edge.from) ?? [];
184
+ dependencies.push(edge.to);
185
+ dependenciesByRef.set(edge.from, dependencies);
186
+ }
187
+ if (target !== undefined && !byRef.has(target.moduleRef))
188
+ throw new PlatformError("INVALID_REQUEST", `setup target module ${target.moduleRef} was not discovered`);
189
+ const visitedRefs = new Set();
190
+ const includeWithDependencies = (moduleRef) => {
191
+ if (visitedRefs.has(moduleRef))
192
+ return;
193
+ visitedRefs.add(moduleRef);
194
+ for (const dependency of dependenciesByRef.get(moduleRef) ?? [])
195
+ includeWithDependencies(dependency);
196
+ };
197
+ if (target !== undefined)
198
+ includeWithDependencies(target.moduleRef);
199
+ else
200
+ for (const moduleRef of graph.order)
201
+ visitedRefs.add(moduleRef);
202
+ const modulesInOrder = graph.order
203
+ .filter((moduleRef) => visitedRefs.has(moduleRef))
204
+ .map((moduleRef) => byRef.get(moduleRef))
205
+ .filter((module) => module !== undefined);
206
+ const setupStatusByRef = new Map();
166
207
  let completed = true;
167
- const modulesInOrder = ordered(modules);
168
208
  for (const [index, module] of modulesInOrder.entries()) {
169
- if (target !== undefined && module.moduleRef !== target.moduleRef)
170
- continue;
171
209
  reportProgress(reporter, {
172
210
  command: "setup",
173
211
  phase: "setup",
@@ -177,17 +215,20 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
177
215
  status: "STARTED",
178
216
  message: module.moduleRef,
179
217
  });
180
- matched = true;
181
218
  const status = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
182
219
  if (!succeeded(status.result)) {
183
220
  results.push(status);
221
+ setupStatusByRef.set(module.moduleRef, "FAILED");
184
222
  completed = false;
185
- if (target !== undefined)
223
+ if (target?.moduleRef === module.moduleRef)
186
224
  break;
187
225
  continue;
188
226
  }
189
227
  const observed = moduleStatusObservationSchema.parse(status.result.data);
190
- if (observed.setupStatus === "READY" && target?.input === undefined) {
228
+ setupStatusByRef.set(module.moduleRef, observed.setupStatus);
229
+ if (target !== undefined && module.moduleRef !== target.moduleRef)
230
+ continue;
231
+ if (observed.setupStatus === "READY" && target === undefined) {
191
232
  skipped.push({ moduleRef: module.moduleRef, reason: "READY" });
192
233
  reportProgress(reporter, {
193
234
  command: "setup",
@@ -200,6 +241,29 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
200
241
  });
201
242
  continue;
202
243
  }
244
+ const unavailableDependencies = (dependenciesByRef.get(module.moduleRef) ?? []).filter((moduleRef) => setupStatusByRef.get(moduleRef) !== "READY");
245
+ if (unavailableDependencies.length > 0) {
246
+ const first = unavailableDependencies[0];
247
+ blockers.push({
248
+ moduleRef: module.moduleRef,
249
+ setupStatus: "BLOCKED",
250
+ reason: `等待依赖模块就绪:${unavailableDependencies.join(", ")}`,
251
+ nextCommand: `platform setup --module ${first}`,
252
+ });
253
+ completed = false;
254
+ reportProgress(reporter, {
255
+ command: "setup",
256
+ phase: "setup",
257
+ current: index + 1,
258
+ total: modulesInOrder.length,
259
+ moduleRef: module.moduleRef,
260
+ status: "SKIPPED",
261
+ message: `${module.moduleRef} 等待 ${unavailableDependencies.join(", ")}`,
262
+ });
263
+ if (target !== undefined)
264
+ break;
265
+ continue;
266
+ }
203
267
  const setup = await dispatchModuleCommand(catalog, module, "setup", context(workspaceRoot, target?.input));
204
268
  results.push(setup);
205
269
  reportProgress(reporter, {
@@ -217,13 +281,25 @@ export async function setupModulesThin(catalog, modules, workspaceRoot, target,
217
281
  });
218
282
  if (!succeeded(setup.result)) {
219
283
  completed = false;
220
- if (target !== undefined)
221
- break;
222
284
  }
285
+ const reconciled = await dispatchModuleCommand(catalog, module, "status", context(workspaceRoot));
286
+ if (succeeded(reconciled.result))
287
+ setupStatusByRef.set(module.moduleRef, moduleStatusObservationSchema.parse(reconciled.result.data).setupStatus);
288
+ else {
289
+ results.push(reconciled);
290
+ setupStatusByRef.set(module.moduleRef, "FAILED");
291
+ completed = false;
292
+ }
293
+ if (target !== undefined && !succeeded(setup.result))
294
+ break;
223
295
  }
224
- if (!matched)
225
- throw new PlatformError("INVALID_REQUEST", `setup target module ${target?.moduleRef ?? ""} was not discovered`);
226
- return { phase: "setup", results, completed, skipped };
296
+ return {
297
+ phase: "setup",
298
+ results,
299
+ completed,
300
+ ...(blockers[0] ? { blockedBy: blockers[0], blockers } : {}),
301
+ skipped,
302
+ };
227
303
  }
228
304
  export async function startModulesThin(catalog, modules, workspaceRoot, reporter) {
229
305
  const results = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-platform-cli",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -24,11 +24,11 @@
24
24
  "SETUP.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@tomflow/proflow-module-contract": "^0.1.12"
27
+ "@tomflow/proflow-module-contract": "^0.1.13"
28
28
  },
29
29
  "devDependencies": {
30
- "@tomflow/proflow-deployment-conformance": "^0.1.10",
31
- "@tomflow/proflow-module-template": "^0.1.9"
30
+ "@tomflow/proflow-module-template": "^0.1.10",
31
+ "@tomflow/proflow-deployment-conformance": "^0.1.11"
32
32
  },
33
33
  "description": "Thin Platform CLI for Module discovery, documentation, package synchronization and lifecycle orchestration.",
34
34
  "keywords": [
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "platform-cli",
5
5
  "packageName": "@tomflow/proflow-platform-cli",
6
- "moduleVersion": "0.1.36",
6
+ "moduleVersion": "0.1.37",
7
7
  "kind": "cli",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",