@yanglaofish/fishtools 0.2.0 → 0.4.0

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.
Files changed (83) hide show
  1. package/README.md +43 -17
  2. package/manual/README.md +43 -17
  3. package/manual/docs/examples/pipeline-reader/LICENSE +21 -0
  4. package/manual/docs/examples/pipeline-reader/index.js +14 -0
  5. package/manual/docs/examples/pipeline-reader/package.json +7 -0
  6. package/manual/docs/examples/pipeline-writer/LICENSE +21 -0
  7. package/manual/docs/examples/pipeline-writer/index.js +17 -0
  8. package/manual/docs/examples/pipeline-writer/package.json +7 -0
  9. package/manual/docs/examples/script-plugin/LICENSE +21 -0
  10. package/manual/docs/examples/script-plugin/README.md +64 -0
  11. package/manual/docs/examples/script-plugin/index.js +39 -0
  12. package/manual/docs/examples/script-plugin/package.json +9 -0
  13. package/manual/docs/examples/script-plugin/scripts/benchmark.py +23 -0
  14. package/manual/docs/plugin-development.md +75 -14
  15. package/manual/docs/releases.md +30 -1
  16. package/manual/docs/research/2026-10-11-plugin-kernel-capabilities.md +167 -0
  17. package/manual/docs/roadmap.md +8 -1
  18. package/manual/docs/superpowers/plans/2026-10-11-control-observation.md +1 -1
  19. package/manual/docs/superpowers/plans/2026-10-11-public-plugin-fixes.md +40 -0
  20. package/manual/docs/superpowers/plans/2026-10-11-terminal-hierarchy.md +40 -0
  21. package/manual/docs/superpowers/plans/2026-10-12-plugin-developer-experience.md +50 -0
  22. package/manual/docs/superpowers/reviews/2026-10-12-plugin-release-review.md +20 -0
  23. package/manual/docs/superpowers/specs/2026-10-11-collapsed-plugin-output.md +23 -0
  24. package/manual/docs/superpowers/specs/2026-10-12-plugin-developer-experience.md +22 -0
  25. package/manual/docs/verification.md +100 -0
  26. package/manual/packages/core/src/application.js +235 -0
  27. package/manual/packages/core/src/engine.js +93 -0
  28. package/manual/packages/core/src/host.js +22 -5
  29. package/manual/packages/core/src/runtime.js +135 -0
  30. package/manual/packages/core/src/terminal/run-feed.js +78 -0
  31. package/manual/packages/core/src/terminal/session.js +438 -0
  32. package/manual/packages/core/src/terminal/view.js +283 -0
  33. package/manual/tests/application.test.js +30 -0
  34. package/node_modules/@mytools/core/bin/fst.js +27 -5
  35. package/node_modules/@mytools/core/package.json +2 -0
  36. package/node_modules/@mytools/core/src/application.js +46 -21
  37. package/node_modules/@mytools/core/src/contracts.js +27 -9
  38. package/node_modules/@mytools/core/src/documentation.js +1 -1
  39. package/node_modules/@mytools/core/src/engine.js +1 -1
  40. package/node_modules/@mytools/core/src/environments.js +39 -1
  41. package/node_modules/@mytools/core/src/host.js +22 -5
  42. package/node_modules/@mytools/core/src/markdown-log.js +1 -1
  43. package/node_modules/@mytools/core/src/plugin-installations.js +84 -0
  44. package/node_modules/@mytools/core/src/plugin-tools.js +133 -0
  45. package/node_modules/@mytools/core/src/plugins.js +16 -6
  46. package/node_modules/@mytools/core/src/process.js +30 -8
  47. package/node_modules/@mytools/core/src/schema-path.js +31 -0
  48. package/node_modules/@mytools/core/src/terminal/model.js +1 -1
  49. package/node_modules/@mytools/core/src/terminal/navigation.js +17 -0
  50. package/node_modules/@mytools/core/src/terminal/presentation.js +3 -3
  51. package/node_modules/@mytools/core/src/terminal/run-feed.js +78 -0
  52. package/node_modules/@mytools/core/src/terminal/run-presentation.js +60 -0
  53. package/node_modules/@mytools/core/src/terminal/run-viewer.js +30 -12
  54. package/node_modules/@mytools/core/src/terminal/session.js +118 -50
  55. package/node_modules/@mytools/core/src/terminal/theme.js +1 -0
  56. package/node_modules/@mytools/core/src/terminal/view.js +125 -28
  57. package/node_modules/@mytools/core/src/terminal/workflow-menu.js +50 -0
  58. package/node_modules/@mytools/core/src/validation.js +24 -9
  59. package/node_modules/@mytools/core/src/version.js +1 -1
  60. package/node_modules/@mytools/core/src/workflows.js +13 -15
  61. package/node_modules/@mytools/pack/dependencies.js +8 -1
  62. package/node_modules/@mytools/pack/index.js +4 -2
  63. package/node_modules/@mytools/web/public/THIRD_PARTY_NOTICES.txt +871 -870
  64. package/node_modules/{tar → minipass-flush/node_modules/minipass}/node_modules/yallist/README.md +7 -8
  65. package/node_modules/minipass-flush/node_modules/minipass/node_modules/yallist/package.json +29 -0
  66. package/node_modules/yallist/README.md +8 -7
  67. package/node_modules/yallist/package.json +51 -12
  68. package/package.json +15 -15
  69. package/node_modules/tar/node_modules/yallist/package.json +0 -68
  70. /package/node_modules/{yallist → minipass-flush/node_modules/minipass/node_modules/yallist}/LICENSE +0 -0
  71. /package/node_modules/{yallist → minipass-flush/node_modules/minipass/node_modules/yallist}/iterator.js +0 -0
  72. /package/node_modules/{yallist → minipass-flush/node_modules/minipass/node_modules/yallist}/yallist.js +0 -0
  73. /package/node_modules/{tar/node_modules/yallist → yallist}/LICENSE.md +0 -0
  74. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/commonjs/index.d.ts +0 -0
  75. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/commonjs/index.d.ts.map +0 -0
  76. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/commonjs/index.js +0 -0
  77. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/commonjs/index.js.map +0 -0
  78. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/commonjs/package.json +0 -0
  79. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/esm/index.d.ts +0 -0
  80. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/esm/index.d.ts.map +0 -0
  81. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/esm/index.js +0 -0
  82. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/esm/index.js.map +0 -0
  83. /package/node_modules/{tar/node_modules/yallist → yallist}/dist/esm/package.json +0 -0
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  交互终端支持选择能力、配置参数和流程编排;官方 Web 插件默认提供,原子 CLI 子命令用于自动化。三种入口共用应用服务。YAML、依赖锁和版本解析由系统维护,不要求普通用户手写。
8
8
 
9
- 0.2.0 增加条件分支、并行组、提前结束、实时运行观察,以及随安装提供的离线开发手册。本文对应此版本源码;发布与验证状态见 [版本记录](manual/docs/releases.md) 和 [验收记录](manual/docs/verification.md)。
9
+ 0.4.0 增加插件创建、离线校验、源码引用和安装批次回收,支持解释器候选、插件状态目录与输出编码,并改善 schema 诊断。输出不再按 schema 补默认值,插件须明确返回业务字段。此前版本的终端菜单、执行摘要、条件控制和离线开发手册继续可用。发布与验证状态见 [版本记录](manual/docs/releases.md) 和 [验收记录](manual/docs/verification.md)。
10
10
 
11
11
  插件接入层使用 JavaScript/TypeScript,通过通用进程接口调用 Python 脚本和系统命令。首款官方插件负责 Python/pip、NPU 与系统环境检查,使用与第三方插件相同的公开接口,按工作流依赖选入分发包。插件声明 Python 等环境使用宿主机还是自带资源;环境检查与业务兼容性由插件作者负责。包内始终携带编排器所需的 Node,完整便携包也携带 npm。
12
12
 
@@ -19,7 +19,7 @@ npm install -g @yanglaofish/fishtools
19
19
  fst
20
20
  ```
21
21
 
22
- 也可从 GitHub Release 下载 `.tgz` 后执行 `npm install -g ./yanglaofish-fishtools-0.2.0.tgz`。没有 Node/npm 的机器使用 Release 中的 Windows `.zip` 或 Linux `.tar.gz` 自包含包,解压后运行 `fst.cmd` 或 `./fst`。发布结果以 [GitHub Releases](https://github.com/yanglaofish/Fishtools/releases) 为准;当前仓库为私有,下载附件需要仓库访问权限。
22
+ 也可从 GitHub Release 下载 `.tgz` 后执行 `npm install -g ./yanglaofish-fishtools-0.4.0.tgz`。没有 Node/npm 的机器使用 Release 中的 Windows `.zip` 或 Linux `.tar.gz` 自包含包,解压后运行 `fst.cmd` 或 `./fst`。发布结果以 [GitHub Releases](https://github.com/yanglaofish/Fishtools/releases) 为准;当前仓库为私有,下载附件需要仓库访问权限。
23
23
 
24
24
  `fst` 在交互终端里启动终端工作台(TUI);`fst web` 启动 Web 并打印访问地址;`fst run hello` 直接执行工作流。首次使用在 `~/.fishtools` 创建工作区,也可通过 `FISHTOOLS_HOME` 指定。安装包已包含官方插件、页面和生产依赖,不需要源码仓库或前端构建。
25
25
 
@@ -41,23 +41,32 @@ fst --project ./my-project pack hello --target linux-x64-glibc --web
41
41
 
42
42
  启动 `fst` 后输入 `/` 会展开操作列表,使用上下箭头选择、Tab 补全、Enter 确认,Escape 返回。普通输入与插件命令忙碌页使用 Ctrl+C 取消;运行观察面板内 Ctrl+C 只关闭面板。在主提示符处 Ctrl+C 请求退出,有未保存修改或后台运行时会先确认。
43
43
 
44
- 首页展示 FISHtools 字符标识、欢迎介绍、当前工作区以及插件和工作流数量;小窗口自动使用紧凑标题。首次使用可以输入 `/run hello`,按提示输入文本(也可保留默认值),体验“转为大写 → 追加标记 → 保存文件”;执行后输入 `/logs` 阅读日志。首页只推荐当前工作区实际存在的工作流,空工作区提示用 `/new` 创建。
44
+ 首页展示 FISHtools 字符标识、欢迎介绍、当前工作区以及插件和工作流数量;小窗口自动使用紧凑标题。首次使用可以输入 `/run hello`,按提示输入文本(也可保留默认值),体验“转为大写 → 追加标记 → 保存文件”。运行过程与输入输出留在主面板,期间仍可打开配置或执行其他工作流。PgUp/PgDn 或鼠标滚轮浏览会话中的过程,向下翻至末尾恢复跟随。首页只推荐当前工作区实际存在的工作流;空工作区从 `/workflows` 新建。
45
45
 
46
- `/plugins` 直接列出当前已加载的插件名称、版本和状态。上下选择后可查看用途、编排能力、输入输出、环境声明及斜杠命令;有配置的插件提供配置入口,自行安装的插件提供移除入口。内置插件标明来源,避免误以为没有安装。`/help` 在终端内提供可翻页的用法说明。
46
+ 顶部的“位置”显示主面板或当前操作入口,菜单标题显示正在操作的对象。提交的命令、菜单选择与实际操作结果保留在本次会话中;返回时只更新当前位置,不追加通用返回消息,也不清空执行过程。这些导航记录只保留在当前会话。
47
+
48
+ 主面板查看当前执行,`/logs` 回看某次执行的步骤状态、输入输出和日志,也能重新打开正在运行的任务详情。查看记录不会重新执行工作流。旧 `/runs` 和 F4 仅作为兼容方式转到同一个 `/logs` 入口,不再提供独立列表。
49
+
50
+ 主面板默认按步骤显示“执行中/成功/失败”,每一步标明能力与插件;**Ctrl+O 展开/收起详情**,查看各步输入输出、作者日志和子进程 stdout/stderr。错误摘要与插件 `print()` 的用户提示直接展示,普通技术日志默认折叠。折叠只改变展示,不丢弃日志或结果。
51
+
52
+ 主输入框用 **↑ / ↓** 回看本次启动中已提交的命令,向下回到底部恢复未提交草稿。补全菜单打开时,方向键仍选择候选;配置表单内容不进入命令历史。最多保留最近 100 条,相邻重复命令合并,退出后不保存。
53
+
54
+ `/plugin` 直接列出当前已加载的插件名称、版本和状态。上下选择后可查看用途、编排能力、输入输出、环境声明及斜杠命令;有配置的插件提供配置入口,自行安装的插件提供移除入口。内置插件标明来源,避免误以为没有安装。`/help` 在终端内提供可翻页的用法说明。顶层补全标明 `[内置]` 或 `[插件]`,插件能力收进插件名入口的二级菜单。
47
55
 
48
56
  | 操作 | 功能 |
49
57
  | --- | --- |
50
- | `/workflows`、`/new` | 选择已有工作流,或新建流程 |
51
- | `/add`、`/steps` | 添加能力、子工作流、条件分支、并行组或提前结束;编辑内部步骤、参数和执行条件 |
52
- | `/outputs`、`/save` | 选择汇总输出,保存为 Web 可继续编辑的同一份流程 |
53
- | `/run` | 逐字段填写运行参数并执行,打开步骤、输入输出和实时日志观察面板 |
54
- | `/runs` | 选择当前会话的运行,重新打开观察面板;关闭面板后任务继续 |
55
- | `/logs` | 用键盘或鼠标滚轮阅读持久化历史日志,设置整个项目保留最近 N 次日志 |
56
- | `/plugins` | 浏览已安装插件、用途及能力,进入配置、安装和移除操作 |
58
+ | `/workflows` | 浏览、新建;进入某个流程后添加/编辑步骤、设置输出、保存、运行或删除 |
59
+ | `/run 工作流ID` | 可按 ID 或中文标题补全,填写参数后执行已保存版本;只输入 `/run` 打开已保存工作流列表 |
60
+ | `/logs` | 回看执行记录,运行中的任务优先;可切换总览、输入输出与日志,支持键盘或鼠标滚轮阅读 |
61
+ | `/plugin` | 浏览已安装插件、用途及能力,进入插件配置、安装和移除操作 |
57
62
  | `/pack` | 勾选工作流并选择目标平台打包 |
58
- | `/config` | 编辑并保存插件声明的配置 |
59
- | `/docs` | 阅读随安装提供的开发规范、控制流说明与示例 |
60
- | `/web`、`/help`、`/exit` | 启动可选 Web、查看说明、退出 |
63
+ | `/config` | 配置 Fishtools:查看工作区信息,设置整个项目的日志保留次数 |
64
+ | `/tools` | 启动 Web 或阅读离线资料 |
65
+ | `/help`、`/exit` | 查看帮助、退出 |
66
+
67
+ 旧 `/new`、`/add`、`/steps`、`/outputs`、`/save`、`/plugins`、`/docs`、`/web` 和 `/runs` 仍可直接输入,顶层补全主要呈现上表的对象入口。工作流编辑菜单返回时保留当前草稿,切换流程或退出会确认未保存修改。删除会将已保存文件移入 `.flow/deleted-workflows/工作流ID-编号.yaml`,仍被其他流程引用时拒绝删除;恢复时将备份复制回 `workflows/原工作流ID.yaml`,先确认同名文件不存在,避免覆盖。
68
+
69
+ `/run` 不要求保存当前编辑草稿,也不会丢弃它。要运行尚未保存的修改,请在 `/workflows` 的编辑菜单选择“运行工作流”,再选择“保存并运行”;已有磁盘版本时也可选择“运行已保存版本”并保留草稿。“返回编辑”会留在当前工作流,不会反复弹出保存确认。只查看参数或拒绝删除步骤不会产生未保存修改。
61
70
 
62
71
  参数来源可选固定值、每次运行时填写或前序步骤的输出。对象逐字段编辑,数组逐项编辑;复杂 schema 可以输入 JSON 值。终端本身无需 Web 插件或 LLM。
63
72
 
@@ -72,12 +81,14 @@ fst --project ./my-project pack hello --target linux-x64-glibc --web
72
81
  | `a` | 切换当前步骤与全部日志 |
73
82
  | ↑↓、鼠标滚轮、PageUp/PageDown、Home/End | 滚动、翻页、跳转开头或尾部;手动滚动暂停日志跟随,End 恢复跟随 |
74
83
  | `f` | 切换日志跟随 |
75
- | Esc、q、Enter、Ctrl+C | 关闭面板,任务继续运行;可用 `/runs` 返回 |
84
+ | Enter | 在总览中查看所选步骤详情,不退出面板 |
85
+ | `j` | 在易读字段与原始 JSON 诊断之间切换 |
86
+ | Esc、q、Ctrl+C | 返回上一级,运行中的任务继续;输入 `/logs` 可重新打开 |
76
87
  | `x`,再按 `y` | 确认取消整个运行 |
77
88
 
78
- `/runs` 显示当前会话的运行,包含已完成任务;重启后的历史用 `/logs` 查看。历史阅读器支持方向键、滚轮、PageUp/PageDown、Home/End,Esc、Enter 或 q 返回。主提示符 `/exit` 或 Ctrl+C 遇到后台任务时,会说明退出将取消任务并请求确认,默认不退出。
89
+ 运行记录同时列出正在执行和保留的历史记录,运行中任务优先;同一个工作流的多次执行分别显示时间和记录编号。查看详情后返回列表,可以继续选择另一次执行。本次启动期间每次运行分别保存阅读位置;重启后可通过 `/logs` 查看保存的记录,主面板会话展示从新会话开始。日志保留设置在 `/config`。主提示符 `/exit` 或 Ctrl+C 遇到后台任务时,会说明退出将取消任务并请求确认,默认不退出。
79
90
 
80
- 插件可以注册自己的命令:官方示例提供 `/text.upper`、交互式 `/text.ask`、`/env.check` 和 `/env.report`。`check` 在环境未通过时抛错,保持旧流程行为;`report` 正常返回 `ok:false` 和检查明细,适合交给工作流条件判断。安装或移除插件会同步刷新 `/` 菜单;命令和工作流共用日志、取消和结果机制。
91
+ 插件可以注册自己的命令:官方示例的 `/examples` 入口包含文本转大写与交互文本输入,`/environment` 入口包含环境检查与报告。原 `/text.upper`、`/text.ask`、`/env.check`、`/env.report` 仍可直接调用。`check` 在环境未通过时抛错;`report` 正常返回 `ok:false` 和检查明细,适合交给工作流条件判断。安装或移除插件会同步刷新 `/` 菜单;命令和工作流共用日志、取消和结果机制。
81
92
 
82
93
  在字符串参数中输入 `@` 可选择本地文件或目录,引用结果为绝对路径;`@@文字` 表示以 @ 开头的普通文字。`/run hello @"输入 参数.json"` 则明确读取文件中的 JSON 对象作为运行输入。文件路径相对启动时的目录(在 `/help` 中显示),不会自动上传文件,也不会把文件内容当作命令。路径补全无需额外安装 `fd`。
83
94
 
@@ -85,6 +96,21 @@ fst --project ./my-project pack hello --target linux-x64-glibc --web
85
96
 
86
97
  ## 源码开发启动
87
98
 
99
+ ### 源码预览
100
+
101
+ 已有源码依赖时,在仓库根目录运行 `npm run preview`;Windows 也可双击 `fst-preview.cmd`。首页标识“本地源码预览”。工作区位于 `.cache/log-preview-win32` 或 `.cache/log-preview-linux`,不会升级全局 `fst`,也不会改动用户默认工作区。
102
+
103
+ - 输入 **`/logs`**,查看已生成的成功、缺少解释器失败示例。
104
+ - 输入 `/run preview-pipeline`,体验三个不同插件“准备文本 → 转大写 → 保存报告”。文本和演示等待时间可以调整;默认查看步骤状态,Ctrl+O 展开输入输出,保存位置由插件直接展示。
105
+ - 输入 `/run preview-stream`,Tab 补全、Enter 执行,Ctrl+O 展开后查看持续输出;用 PgUp/PgDn 或滚轮回看。
106
+ - 输出期间输入 `/config` 查看工具设置,Esc 返回后过程仍在;再执行 `/run preview-success`,体验同一会话里的多个工作流。
107
+ - `/workflows` 进入管理与编辑;`/examples` 或 `/local-log-preview` 进入插件能力菜单。
108
+ - `/logs` 选择某次执行后,`1/2/3` 切换总览、输入输出、日志;`j` 查看原始诊断,`q` 返回。
109
+
110
+ 示例流程只在首次启动时创建,之后保留本地修改;故意失败示例不检查或改变机器的实际 Python 安装。
111
+
112
+ ### 常规源码启动
113
+
88
114
  开发机器安装 **Node.js 24.15.0** 和 npm,在仓库根目录执行:
89
115
 
90
116
  ```sh
package/manual/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  交互终端支持选择能力、配置参数和流程编排;官方 Web 插件默认提供,原子 CLI 子命令用于自动化。三种入口共用应用服务。YAML、依赖锁和版本解析由系统维护,不要求普通用户手写。
8
8
 
9
- 0.2.0 增加条件分支、并行组、提前结束、实时运行观察,以及随安装提供的离线开发手册。本文对应此版本源码;发布与验证状态见 [版本记录](docs/releases.md) 和 [验收记录](docs/verification.md)。
9
+ 0.4.0 增加插件创建、离线校验、源码引用和安装批次回收,支持解释器候选、插件状态目录与输出编码,并改善 schema 诊断。输出不再按 schema 补默认值,插件须明确返回业务字段。此前版本的终端菜单、执行摘要、条件控制和离线开发手册继续可用。发布与验证状态见 [版本记录](docs/releases.md) 和 [验收记录](docs/verification.md)。
10
10
 
11
11
  插件接入层使用 JavaScript/TypeScript,通过通用进程接口调用 Python 脚本和系统命令。首款官方插件负责 Python/pip、NPU 与系统环境检查,使用与第三方插件相同的公开接口,按工作流依赖选入分发包。插件声明 Python 等环境使用宿主机还是自带资源;环境检查与业务兼容性由插件作者负责。包内始终携带编排器所需的 Node,完整便携包也携带 npm。
12
12
 
@@ -19,7 +19,7 @@ npm install -g @yanglaofish/fishtools
19
19
  fst
20
20
  ```
21
21
 
22
- 也可从 GitHub Release 下载 `.tgz` 后执行 `npm install -g ./yanglaofish-fishtools-0.2.0.tgz`。没有 Node/npm 的机器使用 Release 中的 Windows `.zip` 或 Linux `.tar.gz` 自包含包,解压后运行 `fst.cmd` 或 `./fst`。发布结果以 [GitHub Releases](https://github.com/yanglaofish/Fishtools/releases) 为准;当前仓库为私有,下载附件需要仓库访问权限。
22
+ 也可从 GitHub Release 下载 `.tgz` 后执行 `npm install -g ./yanglaofish-fishtools-0.4.0.tgz`。没有 Node/npm 的机器使用 Release 中的 Windows `.zip` 或 Linux `.tar.gz` 自包含包,解压后运行 `fst.cmd` 或 `./fst`。发布结果以 [GitHub Releases](https://github.com/yanglaofish/Fishtools/releases) 为准;当前仓库为私有,下载附件需要仓库访问权限。
23
23
 
24
24
  `fst` 在交互终端里启动终端工作台(TUI);`fst web` 启动 Web 并打印访问地址;`fst run hello` 直接执行工作流。首次使用在 `~/.fishtools` 创建工作区,也可通过 `FISHTOOLS_HOME` 指定。安装包已包含官方插件、页面和生产依赖,不需要源码仓库或前端构建。
25
25
 
@@ -41,23 +41,32 @@ fst --project ./my-project pack hello --target linux-x64-glibc --web
41
41
 
42
42
  启动 `fst` 后输入 `/` 会展开操作列表,使用上下箭头选择、Tab 补全、Enter 确认,Escape 返回。普通输入与插件命令忙碌页使用 Ctrl+C 取消;运行观察面板内 Ctrl+C 只关闭面板。在主提示符处 Ctrl+C 请求退出,有未保存修改或后台运行时会先确认。
43
43
 
44
- 首页展示 FISHtools 字符标识、欢迎介绍、当前工作区以及插件和工作流数量;小窗口自动使用紧凑标题。首次使用可以输入 `/run hello`,按提示输入文本(也可保留默认值),体验“转为大写 → 追加标记 → 保存文件”;执行后输入 `/logs` 阅读日志。首页只推荐当前工作区实际存在的工作流,空工作区提示用 `/new` 创建。
44
+ 首页展示 FISHtools 字符标识、欢迎介绍、当前工作区以及插件和工作流数量;小窗口自动使用紧凑标题。首次使用可以输入 `/run hello`,按提示输入文本(也可保留默认值),体验“转为大写 → 追加标记 → 保存文件”。运行过程与输入输出留在主面板,期间仍可打开配置或执行其他工作流。PgUp/PgDn 或鼠标滚轮浏览会话中的过程,向下翻至末尾恢复跟随。首页只推荐当前工作区实际存在的工作流;空工作区从 `/workflows` 新建。
45
45
 
46
- `/plugins` 直接列出当前已加载的插件名称、版本和状态。上下选择后可查看用途、编排能力、输入输出、环境声明及斜杠命令;有配置的插件提供配置入口,自行安装的插件提供移除入口。内置插件标明来源,避免误以为没有安装。`/help` 在终端内提供可翻页的用法说明。
46
+ 顶部的“位置”显示主面板或当前操作入口,菜单标题显示正在操作的对象。提交的命令、菜单选择与实际操作结果保留在本次会话中;返回时只更新当前位置,不追加通用返回消息,也不清空执行过程。这些导航记录只保留在当前会话。
47
+
48
+ 主面板查看当前执行,`/logs` 回看某次执行的步骤状态、输入输出和日志,也能重新打开正在运行的任务详情。查看记录不会重新执行工作流。旧 `/runs` 和 F4 仅作为兼容方式转到同一个 `/logs` 入口,不再提供独立列表。
49
+
50
+ 主面板默认按步骤显示“执行中/成功/失败”,每一步标明能力与插件;**Ctrl+O 展开/收起详情**,查看各步输入输出、作者日志和子进程 stdout/stderr。错误摘要与插件 `print()` 的用户提示直接展示,普通技术日志默认折叠。折叠只改变展示,不丢弃日志或结果。
51
+
52
+ 主输入框用 **↑ / ↓** 回看本次启动中已提交的命令,向下回到底部恢复未提交草稿。补全菜单打开时,方向键仍选择候选;配置表单内容不进入命令历史。最多保留最近 100 条,相邻重复命令合并,退出后不保存。
53
+
54
+ `/plugin` 直接列出当前已加载的插件名称、版本和状态。上下选择后可查看用途、编排能力、输入输出、环境声明及斜杠命令;有配置的插件提供配置入口,自行安装的插件提供移除入口。内置插件标明来源,避免误以为没有安装。`/help` 在终端内提供可翻页的用法说明。顶层补全标明 `[内置]` 或 `[插件]`,插件能力收进插件名入口的二级菜单。
47
55
 
48
56
  | 操作 | 功能 |
49
57
  | --- | --- |
50
- | `/workflows`、`/new` | 选择已有工作流,或新建流程 |
51
- | `/add`、`/steps` | 添加能力、子工作流、条件分支、并行组或提前结束;编辑内部步骤、参数和执行条件 |
52
- | `/outputs`、`/save` | 选择汇总输出,保存为 Web 可继续编辑的同一份流程 |
53
- | `/run` | 逐字段填写运行参数并执行,打开步骤、输入输出和实时日志观察面板 |
54
- | `/runs` | 选择当前会话的运行,重新打开观察面板;关闭面板后任务继续 |
55
- | `/logs` | 用键盘或鼠标滚轮阅读持久化历史日志,设置整个项目保留最近 N 次日志 |
56
- | `/plugins` | 浏览已安装插件、用途及能力,进入配置、安装和移除操作 |
58
+ | `/workflows` | 浏览、新建;进入某个流程后添加/编辑步骤、设置输出、保存、运行或删除 |
59
+ | `/run 工作流ID` | 可按 ID 或中文标题补全,填写参数后执行已保存版本;只输入 `/run` 打开已保存工作流列表 |
60
+ | `/logs` | 回看执行记录,运行中的任务优先;可切换总览、输入输出与日志,支持键盘或鼠标滚轮阅读 |
61
+ | `/plugin` | 浏览已安装插件、用途及能力,进入插件配置、安装和移除操作 |
57
62
  | `/pack` | 勾选工作流并选择目标平台打包 |
58
- | `/config` | 编辑并保存插件声明的配置 |
59
- | `/docs` | 阅读随安装提供的开发规范、控制流说明与示例 |
60
- | `/web`、`/help`、`/exit` | 启动可选 Web、查看说明、退出 |
63
+ | `/config` | 配置 Fishtools:查看工作区信息,设置整个项目的日志保留次数 |
64
+ | `/tools` | 启动 Web 或阅读离线资料 |
65
+ | `/help`、`/exit` | 查看帮助、退出 |
66
+
67
+ 旧 `/new`、`/add`、`/steps`、`/outputs`、`/save`、`/plugins`、`/docs`、`/web` 和 `/runs` 仍可直接输入,顶层补全主要呈现上表的对象入口。工作流编辑菜单返回时保留当前草稿,切换流程或退出会确认未保存修改。删除会将已保存文件移入 `.flow/deleted-workflows/工作流ID-编号.yaml`,仍被其他流程引用时拒绝删除;恢复时将备份复制回 `workflows/原工作流ID.yaml`,先确认同名文件不存在,避免覆盖。
68
+
69
+ `/run` 不要求保存当前编辑草稿,也不会丢弃它。要运行尚未保存的修改,请在 `/workflows` 的编辑菜单选择“运行工作流”,再选择“保存并运行”;已有磁盘版本时也可选择“运行已保存版本”并保留草稿。“返回编辑”会留在当前工作流,不会反复弹出保存确认。只查看参数或拒绝删除步骤不会产生未保存修改。
61
70
 
62
71
  参数来源可选固定值、每次运行时填写或前序步骤的输出。对象逐字段编辑,数组逐项编辑;复杂 schema 可以输入 JSON 值。终端本身无需 Web 插件或 LLM。
63
72
 
@@ -72,12 +81,14 @@ fst --project ./my-project pack hello --target linux-x64-glibc --web
72
81
  | `a` | 切换当前步骤与全部日志 |
73
82
  | ↑↓、鼠标滚轮、PageUp/PageDown、Home/End | 滚动、翻页、跳转开头或尾部;手动滚动暂停日志跟随,End 恢复跟随 |
74
83
  | `f` | 切换日志跟随 |
75
- | Esc、q、Enter、Ctrl+C | 关闭面板,任务继续运行;可用 `/runs` 返回 |
84
+ | Enter | 在总览中查看所选步骤详情,不退出面板 |
85
+ | `j` | 在易读字段与原始 JSON 诊断之间切换 |
86
+ | Esc、q、Ctrl+C | 返回上一级,运行中的任务继续;输入 `/logs` 可重新打开 |
76
87
  | `x`,再按 `y` | 确认取消整个运行 |
77
88
 
78
- `/runs` 显示当前会话的运行,包含已完成任务;重启后的历史用 `/logs` 查看。历史阅读器支持方向键、滚轮、PageUp/PageDown、Home/End,Esc、Enter 或 q 返回。主提示符 `/exit` 或 Ctrl+C 遇到后台任务时,会说明退出将取消任务并请求确认,默认不退出。
89
+ 运行记录同时列出正在执行和保留的历史记录,运行中任务优先;同一个工作流的多次执行分别显示时间和记录编号。查看详情后返回列表,可以继续选择另一次执行。本次启动期间每次运行分别保存阅读位置;重启后可通过 `/logs` 查看保存的记录,主面板会话展示从新会话开始。日志保留设置在 `/config`。主提示符 `/exit` 或 Ctrl+C 遇到后台任务时,会说明退出将取消任务并请求确认,默认不退出。
79
90
 
80
- 插件可以注册自己的命令:官方示例提供 `/text.upper`、交互式 `/text.ask`、`/env.check` 和 `/env.report`。`check` 在环境未通过时抛错,保持旧流程行为;`report` 正常返回 `ok:false` 和检查明细,适合交给工作流条件判断。安装或移除插件会同步刷新 `/` 菜单;命令和工作流共用日志、取消和结果机制。
91
+ 插件可以注册自己的命令:官方示例的 `/examples` 入口包含文本转大写与交互文本输入,`/environment` 入口包含环境检查与报告。原 `/text.upper`、`/text.ask`、`/env.check`、`/env.report` 仍可直接调用。`check` 在环境未通过时抛错;`report` 正常返回 `ok:false` 和检查明细,适合交给工作流条件判断。安装或移除插件会同步刷新 `/` 菜单;命令和工作流共用日志、取消和结果机制。
81
92
 
82
93
  在字符串参数中输入 `@` 可选择本地文件或目录,引用结果为绝对路径;`@@文字` 表示以 @ 开头的普通文字。`/run hello @"输入 参数.json"` 则明确读取文件中的 JSON 对象作为运行输入。文件路径相对启动时的目录(在 `/help` 中显示),不会自动上传文件,也不会把文件内容当作命令。路径补全无需额外安装 `fd`。
83
94
 
@@ -85,6 +96,21 @@ fst --project ./my-project pack hello --target linux-x64-glibc --web
85
96
 
86
97
  ## 源码开发启动
87
98
 
99
+ ### 源码预览
100
+
101
+ 已有源码依赖时,在仓库根目录运行 `npm run preview`;Windows 也可双击 `fst-preview.cmd`。首页标识“本地源码预览”。工作区位于 `.cache/log-preview-win32` 或 `.cache/log-preview-linux`,不会升级全局 `fst`,也不会改动用户默认工作区。
102
+
103
+ - 输入 **`/logs`**,查看已生成的成功、缺少解释器失败示例。
104
+ - 输入 `/run preview-pipeline`,体验三个不同插件“准备文本 → 转大写 → 保存报告”。文本和演示等待时间可以调整;默认查看步骤状态,Ctrl+O 展开输入输出,保存位置由插件直接展示。
105
+ - 输入 `/run preview-stream`,Tab 补全、Enter 执行,Ctrl+O 展开后查看持续输出;用 PgUp/PgDn 或滚轮回看。
106
+ - 输出期间输入 `/config` 查看工具设置,Esc 返回后过程仍在;再执行 `/run preview-success`,体验同一会话里的多个工作流。
107
+ - `/workflows` 进入管理与编辑;`/examples` 或 `/local-log-preview` 进入插件能力菜单。
108
+ - `/logs` 选择某次执行后,`1/2/3` 切换总览、输入输出、日志;`j` 查看原始诊断,`q` 返回。
109
+
110
+ 示例流程只在首次启动时创建,之后保留本地修改;故意失败示例不检查或改变机器的实际 Python 安装。
111
+
112
+ ### 常规源码启动
113
+
88
114
  开发机器安装 **Node.js 24.15.0** 和 npm,在仓库根目录执行:
89
115
 
90
116
  ```sh
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mytools contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ import {setTimeout} from 'node:timers/promises';
2
+
3
+ export default {
4
+ id:'fishtools-example-reader',version:'0.1.0',title:'文本准备示例',platforms:['win32','linux'],
5
+ actions:{prepare:{
6
+ title:'准备文本',description:'整理输入文本,输出给另一个插件处理。等待时间仅用于观察执行状态,可设为 0。',
7
+ input:{type:'object',required:['text'],properties:{text:{type:'string',title:'待处理文本'},delayMs:{type:'integer',minimum:0,maximum:10000,default:1000,title:'演示等待毫秒数'}}},
8
+ output:{type:'object',required:['text'],properties:{text:{type:'string'}}},
9
+ async run({text,delayMs=1000},{log,signal}){
10
+ await log('准备文本参数',{characters:text.length,delayMs});
11
+ await setTimeout(delayMs,undefined,{signal});return {text:text.trim()};
12
+ },
13
+ }},
14
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "fishtools-example-reader",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "index.js",
6
+ "license": "MIT"
7
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mytools contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ import {writeFile} from 'node:fs/promises';
2
+ import {setTimeout} from 'node:timers/promises';
3
+ import path from 'node:path';
4
+
5
+ export default {
6
+ id:'fishtools-example-writer',version:'0.1.0',title:'报告保存示例',platforms:['win32','linux'],
7
+ actions:{save:{
8
+ title:'保存报告',description:'把上个插件的结果写入文件,通过 print 告诉用户保存位置。',
9
+ input:{type:'object',required:['text'],properties:{text:{type:'string',title:'报告文本'},delayMs:{type:'integer',minimum:0,maximum:10000,default:1000,title:'演示等待毫秒数'}}},
10
+ output:{type:'object',required:['file','text'],properties:{file:{type:'string'},text:{type:'string'}}},
11
+ async run({text,delayMs=1000},{log,print,outputDir,signal}){
12
+ const file=path.join(outputDir,'report.txt');await log('写入报告',{file,characters:text.length});
13
+ await setTimeout(delayMs,undefined,{signal});await writeFile(file,text,'utf8');
14
+ await print('报告已保存',{file});return {file,text};
15
+ },
16
+ }},
17
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "fishtools-example-writer",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "index.js",
6
+ "license": "MIT"
7
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mytools contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,64 @@
1
+ # Python 脚本插件示例
2
+
3
+ 这是可独立安装的 ESM 插件包。npm 依赖为零,包内 Python 脚本只使用标准库。它演示嵌套输入、宿主解释器选择、脚本资源定位和业务条件分支;指标是确定性的教学计算,不是硬件性能基准。
4
+
5
+ ## 安装与运行
6
+
7
+ 先确认目标机器安装了 Python 3。插件声明 `host` 环境,按顺序查找 `python3`、`python`;不会下载 Python 或将解释器打入包。复制本目录到目标机器,在 Fishtools 项目中执行:
8
+
9
+ ```sh
10
+ fst plugin validate ./script-plugin
11
+ fst --project ./my-project plugin add ./script-plugin
12
+ ```
13
+
14
+ 创建输入文件 `benchmark.json`:
15
+
16
+ ```json
17
+ {"benchmark_config":{"samples":4,"threshold":7}}
18
+ ```
19
+
20
+ 将 `fishtools-python-benchmark-example:benchmark` 加入工作流,输入绑定 `benchmark_config`。如果 Python 不在 PATH 中,运行时会报告找不到 `python3`、`python`。在项目 `flow.project.json` 的 `environments` 中为该插件指定解释器绝对路径,例如:
21
+
22
+ ```json
23
+ {
24
+ "environments": {
25
+ "fishtools-python-benchmark-example": {
26
+ "python": {"source": "host", "executable": "C:/Python312/python.exe"}
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ 上面仅展示相关字段;编辑已有项目文件时保留原有 `plugins`、`config` 等字段。Linux/macOS 可将路径换成 `/usr/bin/python3` 等实际位置。显式路径不可用会报错,不回退到候选命令。解释器需要 Python 3;目标环境和脚本的兼容性由部署者验证。
33
+
34
+ ## 业务结果与分支
35
+
36
+ 脚本计算 `1²` 到 `samples²` 的平均值。`samples=4` 得到 `7.5`;`threshold=7` 返回 `ok:true`,`threshold=8` 返回 `ok:false`。两种结果都表示脚本正常完成,输出包含 `metrics.samples`、`metrics.average` 和 `reason`。动作提供 `conditions.passed`,可在工作流中选择通过和未通过分支:
37
+
38
+ ```yaml
39
+ schemaVersion: 1
40
+ id: python-benchmark-example
41
+ steps:
42
+ - id: measure
43
+ action: fishtools-python-benchmark-example:benchmark
44
+ with:
45
+ benchmark_config:
46
+ samples: 4
47
+ threshold: 8
48
+ - id: result
49
+ branch:
50
+ condition:
51
+ left: {$ref: steps.measure.output.ok}
52
+ op: eq
53
+ right: true
54
+ then:
55
+ - id: accepted
56
+ stop: {reason: 业务指标通过}
57
+ else:
58
+ - id: rejected
59
+ stop: {reason: 业务指标未通过}
60
+ ```
61
+
62
+ 将工作流保存为项目的 `workflows/python-benchmark-example.yaml` 后,可执行 `fst --project ./my-project workflow check python-benchmark-example` 和 `fst --project ./my-project run python-benchmark-example`。`ok:false` 应由业务分支处理;脚本非零退出或输出不是 JSON 才作为执行错误抛出。
63
+
64
+ 动作通过 `environments.python.executable` 取得最终解释器,通过 `resourceDir` 找包内 `scripts/benchmark.py`,再用上下文 `runProcess` 执行。宿主绑定的 `runProcess` 自动继承取消信号并将 stdout/stderr 写入运行日志。`package.json.files` 包含脚本,`npm pack` 生成的归档可独立安装。
@@ -0,0 +1,39 @@
1
+ import path from 'node:path';
2
+
3
+ // 直接导出插件定义,不依赖尚未独立发布的内部 SDK。
4
+ export default {
5
+ id:'fishtools-python-benchmark-example',version:'1.0.0',sdk:'^0.1.0',
6
+ title:'Python 脚本示例',description:'调用包内标准库脚本并区分业务未通过与执行失败。',
7
+ platforms:['win32','linux','darwin'],architectures:['x64','arm64'],
8
+ environments:{python:{source:'host',executable:['python3','python']}},
9
+ actions:{
10
+ benchmark:{
11
+ title:'计算样本指标',description:'用 Python 计算前 N 个正整数的平方均值,并与阈值比较。',
12
+ input:{type:'object',properties:{
13
+ benchmark_config:{type:'object',title:'计算配置',properties:{
14
+ samples:{type:'integer',minimum:1,maximum:100000,title:'样本数'},
15
+ threshold:{type:'number',minimum:0,title:'最低平方均值'},
16
+ },required:['samples','threshold'],additionalProperties:false},
17
+ },required:['benchmark_config'],additionalProperties:false},
18
+ output:{type:'object',properties:{
19
+ ok:{type:'boolean',title:'业务通过'},
20
+ metrics:{type:'object',properties:{
21
+ samples:{type:'integer'},average:{type:'number'},
22
+ },required:['samples','average'],additionalProperties:false},
23
+ reason:{type:'string',title:'判定原因'},
24
+ },required:['ok','metrics','reason'],additionalProperties:false},
25
+ conditions:{passed:{title:'业务通过',path:'ok',op:'eq',value:true}},
26
+ async run({benchmark_config},{environments,resourceDir,runProcess,signal}){
27
+ signal?.throwIfAborted();
28
+ const script=path.join(resourceDir,'scripts','benchmark.py');
29
+ // 宿主绑定的 runProcess 会自动继承当前调用的取消信号并记录输出。
30
+ const result=await runProcess(environments.python.executable,[script,JSON.stringify(benchmark_config)],{timeout:30000});
31
+ if(result.exitCode!==0)throw Object.assign(new Error(`Python 脚本执行失败(退出码 ${result.exitCode}):${result.stderr}`),{
32
+ code:'PYTHON_FAILED',details:{exitCode:result.exitCode,stderr:result.stderr},
33
+ });
34
+ try{return JSON.parse(result.stdout);}
35
+ catch(cause){throw Object.assign(new Error('Python 脚本返回无效 JSON',{cause}),{code:'PYTHON_OUTPUT_INVALID'});}
36
+ },
37
+ },
38
+ },
39
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "fishtools-python-benchmark-example",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "index.js",
6
+ "files": ["index.js", "scripts", "README.md", "LICENSE"],
7
+ "license": "MIT",
8
+ "engines": {"node": ">=24.15.0"}
9
+ }
@@ -0,0 +1,23 @@
1
+ """用 Python 标准库计算确定性的示例指标。"""
2
+
3
+ import json
4
+ import sys
5
+
6
+
7
+ def main():
8
+ config = json.loads(sys.argv[1])
9
+ samples = config["samples"]
10
+ threshold = config["threshold"]
11
+ average = sum(value * value for value in range(1, samples + 1)) / samples
12
+ ok = average >= threshold
13
+ result = {
14
+ "ok": ok,
15
+ "metrics": {"samples": samples, "average": average},
16
+ "reason": "达到阈值" if ok else "平均值低于阈值",
17
+ }
18
+ # 默认 ASCII JSON 转义避免 Windows 控制台编码影响进程间协议。
19
+ print(json.dumps(result, ensure_ascii=True, separators=(",", ":")))
20
+
21
+
22
+ if __name__ == "__main__":
23
+ main()