@yanglaofish/fishtools 0.1.4 → 0.3.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.
- package/README.md +79 -25
- package/manual/LICENSE +21 -0
- package/manual/README.md +199 -0
- package/manual/THIRD_PARTY_NOTICES.md +32 -0
- package/manual/docs/examples/action-plugin/LICENSE +21 -0
- package/manual/docs/examples/action-plugin/README.md +38 -0
- package/manual/docs/examples/action-plugin/index.js +18 -0
- package/manual/docs/examples/action-plugin/input.json +1 -0
- package/manual/docs/examples/action-plugin/package.json +9 -0
- package/manual/docs/examples/pipeline-reader/LICENSE +21 -0
- package/manual/docs/examples/pipeline-reader/index.js +14 -0
- package/manual/docs/examples/pipeline-reader/package.json +7 -0
- package/manual/docs/examples/pipeline-writer/LICENSE +21 -0
- package/manual/docs/examples/pipeline-writer/index.js +17 -0
- package/manual/docs/examples/pipeline-writer/package.json +7 -0
- package/manual/docs/plans/2026-10-10-workbench-ui.md +27 -0
- package/{docs → manual/docs}/plugin-development.md +172 -20
- package/{docs → manual/docs}/releases.md +32 -2
- package/manual/docs/research/2026-10-11-plugin-kernel-capabilities.md +167 -0
- package/manual/docs/roadmap.md +90 -0
- package/manual/docs/superpowers/plans/2026-10-09-workflow-mvp.md +115 -0
- package/manual/docs/superpowers/plans/2026-10-10-cordis-services.md +34 -0
- package/manual/docs/superpowers/plans/2026-10-10-fst-terminal.md +33 -0
- package/manual/docs/superpowers/plans/2026-10-10-portable-plugin-contract.md +35 -0
- package/manual/docs/superpowers/plans/2026-10-11-control-observation.md +77 -0
- package/manual/docs/superpowers/plans/2026-10-11-public-plugin-fixes.md +40 -0
- package/manual/docs/superpowers/plans/2026-10-11-terminal-hierarchy.md +40 -0
- package/manual/docs/superpowers/specs/2026-10-09-workflow-cli-design.md +377 -0
- package/manual/docs/superpowers/specs/2026-10-10-cordis-services-design.md +88 -0
- package/manual/docs/superpowers/specs/2026-10-10-portable-plugin-contract.md +31 -0
- package/manual/docs/superpowers/specs/2026-10-10-run-logs.md +28 -0
- package/manual/docs/superpowers/specs/2026-10-11-collapsed-plugin-output.md +23 -0
- package/manual/docs/superpowers/specs/2026-10-11-control-observation.md +37 -0
- package/{docs → manual/docs}/verification.md +97 -1
- package/manual/examples/service-plugins/README.md +20 -0
- package/manual/examples/service-plugins/consumer/LICENSE +21 -0
- package/manual/examples/service-plugins/consumer/index.js +14 -0
- package/manual/examples/service-plugins/consumer/package.json +1 -0
- package/manual/examples/service-plugins/provider/LICENSE +21 -0
- package/manual/examples/service-plugins/provider/index.js +19 -0
- package/manual/examples/service-plugins/provider/package.json +1 -0
- package/manual/packages/core/LICENSE +21 -0
- package/manual/packages/core/src/application.js +211 -0
- package/manual/packages/core/src/engine.js +93 -0
- package/manual/packages/core/src/host.js +124 -0
- package/manual/packages/core/src/runtime.js +135 -0
- package/manual/packages/core/src/terminal/run-feed.js +78 -0
- package/manual/packages/core/src/terminal/session.js +438 -0
- package/manual/packages/core/src/terminal/view.js +283 -0
- package/manual/packages/sdk/LICENSE +21 -0
- package/manual/packages/sdk/index.js +14 -0
- package/manual/packages/sdk/package.json +1 -0
- package/manual/plugins/environment/LICENSE +21 -0
- package/manual/plugins/environment/index.js +92 -0
- package/manual/plugins/environment/package.json +1 -0
- package/manual/plugins/examples/LICENSE +21 -0
- package/manual/plugins/examples/index.js +23 -0
- package/manual/plugins/examples/package.json +1 -0
- package/manual/plugins/examples/scripts/echo.py +5 -0
- package/manual/tests/application.test.js +75 -0
- package/node_modules/@mytools/core/bin/fst.js +12 -4
- package/node_modules/@mytools/core/src/application.js +11 -5
- package/node_modules/@mytools/core/src/cli-run.js +6 -0
- package/node_modules/@mytools/core/src/contracts.js +15 -2
- package/node_modules/@mytools/core/src/control.js +63 -0
- package/node_modules/@mytools/core/src/documentation.js +65 -0
- package/node_modules/@mytools/core/src/engine.js +80 -26
- package/node_modules/@mytools/core/src/host.js +15 -2
- package/node_modules/@mytools/core/src/markdown-log.js +9 -6
- package/node_modules/@mytools/core/src/process.js +49 -15
- package/node_modules/@mytools/core/src/project.js +1 -1
- package/node_modules/@mytools/core/src/run-logs.js +1 -1
- package/node_modules/@mytools/core/src/terminal/conditions.js +32 -0
- package/node_modules/@mytools/core/src/terminal/log-viewer.js +7 -8
- package/node_modules/@mytools/core/src/terminal/model.js +33 -4
- package/node_modules/@mytools/core/src/terminal/navigation.js +17 -0
- package/node_modules/@mytools/core/src/terminal/presentation.js +8 -10
- package/node_modules/@mytools/core/src/terminal/run-feed.js +78 -0
- package/node_modules/@mytools/core/src/terminal/run-presentation.js +60 -0
- package/node_modules/@mytools/core/src/terminal/run-viewer.js +104 -0
- package/node_modules/@mytools/core/src/terminal/session.js +212 -57
- package/node_modules/@mytools/core/src/terminal/theme.js +29 -0
- package/node_modules/@mytools/core/src/terminal/view.js +185 -33
- package/node_modules/@mytools/core/src/terminal/workflow-menu.js +50 -0
- package/node_modules/@mytools/core/src/validation.js +17 -4
- package/node_modules/@mytools/core/src/version.js +1 -1
- package/node_modules/@mytools/core/src/workflows.js +41 -19
- package/node_modules/@mytools/core/templates/control-demo.yaml +47 -0
- package/node_modules/@mytools/environment/index.js +14 -2
- package/node_modules/@mytools/pack/index.js +6 -2
- package/node_modules/@mytools/web/public/THIRD_PARTY_NOTICES.txt +780 -780
- package/node_modules/@mytools/web/public/assets/index-DT2B4tMV.js +70 -0
- package/node_modules/@mytools/web/public/assets/{index-BeV6IqTW.css → index-vuZvrUTT.css} +1 -1
- package/node_modules/@mytools/web/public/index.html +2 -2
- package/package.json +2 -2
- package/node_modules/@mytools/web/public/assets/index-TgsBnZgG.js +0 -70
- /package/{docs → manual/docs}/deployment.md +0 -0
|
@@ -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,27 @@
|
|
|
1
|
+
# Fishtools 工作台易用性优化
|
|
2
|
+
|
|
3
|
+
> 历史实施记录。后续 TUI、便携工具及远端 CI 状态见 [当前路线图](../roadmap.md);本文末尾的“仍待验证”仅描述该次 UI 开发阶段。
|
|
4
|
+
|
|
5
|
+
## 目标
|
|
6
|
+
|
|
7
|
+
首版功能验收已完成;本轮让首次使用者能直接找到已有工作流,理解添加步骤、设置参数、运行与查看结果的路径。保持离线可用和 CLI/Web 共用执行核心。
|
|
8
|
+
|
|
9
|
+
## 已确定的设计
|
|
10
|
+
|
|
11
|
+
- 默认进入工作流总览,展示用途、步骤数、运行输入和输出;支持搜索、打开、新建。
|
|
12
|
+
- 编辑器保留能力库、顺序列表、参数检查器。窄屏时参数用可关闭的侧边弹窗展示,不落到页面最底部。
|
|
13
|
+
- 每个步骤提供可聚焦的“设置参数”入口;排序和删除始终可见。
|
|
14
|
+
- 使用深色文字、青绿色强调色、本地字体与轻量过渡,尊重减少动态效果设置。gpt-taste 的营销页结构、远程素材和滚动锁定与高效离线工作台不符,本轮不采用。
|
|
15
|
+
- 弹窗使用浏览器原生 dialog 管理焦点和 Escape;提交错误在当前弹窗显示。
|
|
16
|
+
- 保留数据、现有插件和工作流格式,不改变执行协议。
|
|
17
|
+
|
|
18
|
+
## 实施与验收
|
|
19
|
+
|
|
20
|
+
1. 添加首页、状态引导和简洁结果视图,整理视图组件。
|
|
21
|
+
2. 重做布局、表单和窄屏参数面板,保留键盘操作。
|
|
22
|
+
3. 构建,跑现有测试;浏览器实测首页、编辑、保存、运行和窄屏;检查无横向溢出。
|
|
23
|
+
4. 独立代码审查,补充验收记录,更新 npm 安装包并本地提交。
|
|
24
|
+
|
|
25
|
+
## 原有验收边界
|
|
26
|
+
|
|
27
|
+
Windows 本机、WSL、Linux 容器和 npm 安装已验证。真实 NPU、干净 Windows 虚机和远端 CI 仍待相应环境,不声明为已验证。
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
# 插件开发
|
|
2
2
|
|
|
3
|
-
本文是 Fishtools
|
|
3
|
+
本文是 Fishtools 的公共插件接入规范,面向第三方开发者。普通动作插件与官方环境检查插件使用同一套契约;不要求依赖 Web 前端,也不要求开发者直接操作 Cordis。
|
|
4
|
+
|
|
5
|
+
`print()` 用户输出接口、主面板默认折叠和 Ctrl+O 展开从 0.3.0 起纳入发行包;0.2.0 不包含这些能力。
|
|
6
|
+
|
|
7
|
+
## 从已安装的工具查阅规范
|
|
8
|
+
|
|
9
|
+
完整 npm 与便携发行包随附离线资料,包含本文、控制流规范、服务示例和可安装的[零依赖动作插件](examples/action-plugin/README.md)。无需克隆源码仓库:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
fst docs
|
|
13
|
+
fst docs plugins
|
|
14
|
+
fst docs control
|
|
15
|
+
fst docs plugins --path
|
|
16
|
+
fst docs --export ./fishtools-manual
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`fst docs <主题>` 输出正文,`--path` 输出本地路径;`--export` 导出全部资料到新目录,已有目标会拒绝覆盖。资料按当前安装版本定位,不依赖当前工作目录,不启动插件或服务。TUI 中通过 `/docs` 选择主题,在内置阅读器查看。
|
|
20
|
+
|
|
21
|
+
导出目录保留相对链接,可离线浏览规范与示例。`plugins/`、`packages/`、`tests/` 中附带的源码仅供阅读,保留其 LICENSE;它们不是另一份可执行宿主或完整源码工作区。实际可安装示例位于 `docs/examples/action-plugin` 和 `examples/service-plugins`。内部 SDK 不承诺已作为独立公共 npm 包发布,首次开发可直接使用零依赖示例。
|
|
22
|
+
|
|
23
|
+
## 0.2.0:条件控制与运行观察
|
|
24
|
+
|
|
25
|
+
本版本包含条件分支、并行组、提前结束和运行观察接口。以下说明对应 0.2.0 源码实现;执行模型见 [控制流规范](superpowers/specs/2026-10-11-control-observation.md),发布与验证范围以 [版本记录](releases.md) 和 [验收记录](verification.md) 为准。
|
|
26
|
+
|
|
27
|
+
- 可编排动作必须声明输入与输出 schema。正常返回且输出校验通过即“执行成功”,不强制作者再写成功判定。
|
|
28
|
+
- 检测完成与检测通过分开:检查类能力返回 `ok/passed/count/exists` 等结构化业务字段;异常代表执行失败。工作流决定结果不符合要求时分支、跳过还是提前结束。
|
|
29
|
+
- 动作可以提供 `conditions` 判定预设,例如 `conditions: {passed: {title:'检查通过', path:'ok', op:'eq', value:true}}`,`path` 相对于输出;无需提供可执行判定函数。
|
|
30
|
+
- 工作流使用条件分支、并行组、提前结束节点;插件不自行操作编排器的流程指针。并行调用必须正确响应 `signal`,共享文件/连接的并发安全由插件实现负责。
|
|
31
|
+
- `runProcess` 提供实时 stdout/stderr,使用 `await log(message,data)` 记录业务日志。运行面板按步骤调用路径区分输出,用户可切换总览/详情/日志;退出观察页面不等于取消任务。
|
|
32
|
+
- 保持旧环境 `check` 动作的失败语义,新增报告动作供条件编排使用,避免升级后旧流程意外继续运行。
|
|
4
33
|
|
|
5
34
|
## 开发约束与实际校验
|
|
6
35
|
|
|
@@ -9,18 +38,23 @@
|
|
|
9
38
|
| 约束 | 开发者要求 | 当前宿主检查 |
|
|
10
39
|
| --- | --- | --- |
|
|
11
40
|
| 标识与版本 | `id` 必须等于 npm 包名,`version` 必须等于包版本并使用 semver;升级时保持已有动作标识稳定 | 检查 ID 非空、重复;检查 SDK 和已声明插件依赖的版本兼容性;尚未自动比对包名/包版本 |
|
|
12
|
-
| 能力元数据 | 每个公开动作必须提供清楚的 `title`、`description`、`input`、`output`、`run` | 检查 `run`
|
|
13
|
-
| 输入输出 |
|
|
41
|
+
| 能力元数据 | 每个公开动作必须提供清楚的 `title`、`description`、`input`、`output`、`run` | 检查 `run` 是函数;编排前检查 input/output schema;标题和描述的质量由作者负责 |
|
|
42
|
+
| 输入输出 | 显式提供有效的 JSON Schema,推荐对象形式声明类型、必填项、默认值和字段说明;无参数或无输出也要声明空对象 schema | 编排前校验 schema 声明,调用时用 Ajv 验证实际输入输出;返回值经过 JSON 序列化边界 |
|
|
43
|
+
| 判定预设 | `conditions` 提供标题、相对输出路径、比较符和值;不提供可执行判定函数 | 加载时检查格式、比较符、危险路径及已声明的输出字段 |
|
|
14
44
|
| 配置 | 将机器地址、解释器位置等放入配置或动作参数;`defaultConfig` 只包含可公开默认值 | 加载时验证已声明的配置 schema;不自动识别敏感配置 |
|
|
15
45
|
| 依赖 | npm 库写入包的 `dependencies`,插件依赖写入插件定义的 `dependencies`;不得依赖开发机偶然安装的库 | 加载时检查插件依赖;打包收集生产 npm 依赖与工作流依赖闭包 |
|
|
16
46
|
| 子进程 | 必须使用上下文 `runProcess`,参数逐项传入数组,传递 `signal`;非零退出必须按业务处理 | 使用该接口时提供超时、取消、输出上限和进程树回收;无法约束插件绕过接口直接启动的进程 |
|
|
17
47
|
| 脚本与文件 | 自带资源必须位于包内并进入发布文件;用 `resourceDir` 找资源、`outputDir` 写结果,不能写死开发机路径 | 打包按 npm 发布文件规则收集资源;不会静态检查所有路径或保证脚本在目标机可运行 |
|
|
18
|
-
| 环境前提 |
|
|
48
|
+
| 环境前提 | 业务动作无法执行时应抛错;报告动作可以返回未通过的结构化结果,由工作流决定后续处理 | 官方 `check` 未通过会抛错,`report` 返回 `ok:false`;均不自动安装或修复 |
|
|
19
49
|
| 可取消与副作用 | 响应 `signal`;重复执行、覆盖文件、网络请求等副作用应在说明中写清 | 调用前后检查取消信号;同步阻塞代码无法被抢占,不提供自动回滚 |
|
|
20
50
|
| 日志与许可 | 使用 `log`,不得记录密码/令牌;保留所有适用的 LICENSE/NOTICE | 记录结构化日志、保留发布文件中的许可证;不自动脱敏或审计第三方许可证 |
|
|
21
51
|
|
|
22
52
|
JSON 序列化可能丢弃对象中的 `undefined`、函数或 Symbol,并将非有限数字转为 `null`;不能把“能够序列化”当作完整的类型保证。请给输出写明确的 schema,不返回循环引用、BigInt、函数或依赖隐式转换的对象。
|
|
23
53
|
|
|
54
|
+
每份 schema 独立编译并缓存,`$id` 不会在不同插件或安装批次之间共享注册。插件升级可以保留相同 `$id` 并调整约束;定义内部的 `$ref` 正常解析,不支持隐式引用另一个插件曾注册的 schema。
|
|
55
|
+
|
|
56
|
+
无参数或无输出时,分别使用 `{type:'object', properties:{}, additionalProperties:false}`,动作返回 `{}`。JSON Schema 的布尔形式也受支持:`true` 接受任意值,`false` 拒绝所有值;为了生成字段表单与输出选择器,推荐明确声明对象和字段。缺省、`null`、数组、字符串和数值都不能代替 schema 声明。原有动作/子工作流的 `schemaVersion: 1` 格式保留;旧插件若要参与编排,需要补齐有效的 input/output 声明。正常返回且输出校验通过即执行成功,不要求返回额外的 `success:true`。
|
|
57
|
+
|
|
24
58
|
## 插件定义字段
|
|
25
59
|
|
|
26
60
|
| 字段 | 发布要求与语义 |
|
|
@@ -79,14 +113,14 @@ export default {
|
|
|
79
113
|
};
|
|
80
114
|
```
|
|
81
115
|
|
|
82
|
-
包名/版本应与 `id`/`version` 一致。能力标识为 `my-flow-plugin:greet
|
|
116
|
+
包名/版本应与 `id`/`version` 一致。能力标识为 `my-flow-plugin:greet`,用户看到中文标题、描述、表单和输出字段。仓库内部可使用 `@mytools/sdk` 的 `definePlugin()` 和 `FlowError`;独立插件不能假设全局 Fishtools 安装会使这个内部包自动可解析。首次开发优先复制[零依赖完整示例](examples/action-plugin/README.md)。
|
|
83
117
|
|
|
84
118
|
## 安装与依赖
|
|
85
119
|
|
|
86
120
|
```sh
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
fst plugin add /absolute/path/to/my-flow-plugin
|
|
122
|
+
fst plugin list
|
|
123
|
+
fst plugin remove my-flow-plugin
|
|
90
124
|
```
|
|
91
125
|
|
|
92
126
|
也支持 npm 包标识或本地 `.tgz`;Web“插件管理”提供相同入口。本地路径以服务器所在机器为准。
|
|
@@ -108,25 +142,41 @@ npm run fst -- --project examples plugin remove my-flow-plugin
|
|
|
108
142
|
| `resourceDir` | 插件包目录,用于定位脚本 |
|
|
109
143
|
| `getService(name)` | 获取本插件 runtime.inject/provides 声明的已就绪服务;未声明或不可用时抛错 |
|
|
110
144
|
| `environments` | 按声明和来源选择解析的环境;包含解释器路径,随包环境另有资源根目录 |
|
|
111
|
-
| `runProcess(file,args,options)` |
|
|
112
|
-
| `log(message,data)` |
|
|
145
|
+
| `runProcess(file,args,options)` | 通用进程调用,自动关联当前日志与取消信号 |
|
|
146
|
+
| `log(message,data,meta?)` | 写结构化日志并返回落盘 Promise;常规调用保持 `log(message,data)` |
|
|
147
|
+
| `print(message,data?)` | 面向用户输出,默认主面板直接显示;同样返回落盘 Promise |
|
|
113
148
|
|
|
114
149
|
返回值必须可 JSON 序列化,输入输出经 Ajv 校验。插件在宿主进程中执行,属于可信代码;同步阻塞代码不能被取消信号抢占。
|
|
115
150
|
|
|
116
|
-
`
|
|
151
|
+
主面板默认展示步骤状态,Ctrl+O 展开或收起各步输入输出和技术日志。普通 `log` 与子进程 stdout/stderr 默认折叠;失败摘要始终直接显示。希望用户直接看到的进度或结果,显式使用 `await print('报告已保存', {file})`。`print` 沿用日志通道,以 `type: 'log', audience: 'user'` 持久化,工作流中自动带上步骤归属。动作和交互命令都支持,不应直接用 `console.log` 向 TUI 打印。
|
|
152
|
+
|
|
153
|
+
若 Python 程序的部分输出需要直接展示,可使用 `runProcess(file, args, {onOutput: ({stream,message}) => stream === 'stdout' ? print(message) : undefined})`。回调按数据块触发;作者可先缓存成完整行再选择性 print。完整进程输出仍自动保存在诊断日志,print 的用户提示另外保留。写入 stderr 本身不等同执行失败,动作仍须按退出码决定抛错。
|
|
154
|
+
|
|
155
|
+
跨插件示例源码:[文本准备插件](examples/pipeline-reader/index.js)、[报告保存插件](examples/pipeline-writer/index.js)。它们与基础示例的转大写动作组成 `preview-pipeline`,通过前一步输出绑定下一步输入;每个目录都是独立插件包,可参考现有安装方法接入。
|
|
156
|
+
|
|
157
|
+
`runProcess` 默认无 Shell,参数用数组。支持 `cwd`、`env`、`signal`、`timeout`(默认 30 秒)、`maxBuffer`(stdout/stderr 各默认 4 MiB)和可选的 `onOutput({stream,message})`。普通非零退出返回 `{exitCode,stdout,stderr}`,动作应自行决定是否抛出业务错误;缺失程序、超时、取消、输出超限抛错,并等待进程树回收。不要把密码或令牌写入日志。
|
|
158
|
+
|
|
159
|
+
相对程序路径及 `PATH` 中的相对目录以传入的 `cwd` 为基准;未传 `cwd` 时使用宿主当前目录。例如 `runProcess('./tools/check', [], {cwd: resourceDir})` 调用插件资源目录内的工具,不改变其他并发动作的工作目录。
|
|
160
|
+
|
|
161
|
+
通过动作上下文调用时,stdout/stderr 在子进程执行期间自动产生 `type:'log'` 事件,`stream` 为 `stdout` 或 `stderr`,工作流事件保留 `stepId` 和 `callPath`。例如同一动作分别位于 `main/group/left`、`main/group/right`,日志按各自调用归属显示。写入 stderr 本身不会把运行改成失败。
|
|
162
|
+
|
|
163
|
+
输出按 UTF-8 解码,跨数据块的中文和 emoji 不会被拆坏;回调消息是流数据块,不保证一条消息就是一整行。可选 `onOutput` 在自动日志落盘后执行,不需要再手动 `log` 同一份输出。回调可以返回 Promise,`runProcess` 会等待;回调抛错或拒绝会终止受管进程并向调用方报告错误。完整 stdout/stderr 返回缓冲仍受输出上限约束。不要启动未等待的回调后台任务。
|
|
164
|
+
|
|
165
|
+
宿主使用 `log(message, undefined, {stream})` 写入进程事件。插件的业务日志继续使用 `await log('检查完成', {count:3})`;调用路径由执行器固定,不能用元数据覆盖步骤归属。宿主同时绑定当前运行的取消信号,显式传入额外 `signal` 时任一信号取消都会终止进程。
|
|
117
166
|
|
|
118
167
|
### 用日志定位插件错误
|
|
119
168
|
|
|
120
169
|
每次通过应用运行工作流或插件命令,都在 `.flow/runs/<runId>/` 写入 `run.md`、`events.jsonl` 与 `result.json`(指定 `--output` 时使用该目录;便携版写入可写工作区)。
|
|
121
170
|
|
|
122
|
-
-
|
|
123
|
-
-
|
|
171
|
+
- **运行中直接观察**:TUI 主面板逐步更新状态,Ctrl+O 展开输入输出和诊断日志;`/logs` 可选择某次运行进入详细面板。Web 使用“查看运行”。按调用路径区分同一动作的不同调用。
|
|
172
|
+
- **历史日志直接阅读**:TUI `/logs` 选择记录后切换总览、输入输出和日志;方向键或鼠标滚轮滚动,PageUp/PageDown 翻页,Home/End 跳转,Esc 或 q 返回,Enter 查看步骤。重启后读取磁盘历史;run.md 也可用外部 Markdown 阅读器打开。
|
|
173
|
+
- **导出阅读内容**:`run.md` 按时间顺序展示步骤、作者日志、stdout/stderr、执行结果、失败原因、错误详情和调用栈,结束后附最终状态。它在执行过程中逐条追加,也可使用外部 Markdown 阅读器查看。调用栈保留原始换行。
|
|
124
174
|
|
|
125
|
-
- `events.jsonl`
|
|
126
|
-
- `result.json`
|
|
175
|
+
- `events.jsonl` 按顺序记录启动、步骤成功/失败/跳过/停止、作者日志、进程流输出和运行结束。`step.started` 含解析后的 input;失败事件包含插件 ID、动作或命令、错误码、message,以及 `error` 中的 stack 和 cause 链。工作流还有 stepId、callPath、耗时。
|
|
176
|
+
- `result.json` 保存最终状态、输出、错误和完整事件快照。正常提前结束为 `status:'stopped'`,保存字符串 reason,不生成 error。单步输出可在 `events` 的 `step.succeeded.output` 查看,即使后续失败或停止也保留;逐行事件文件不重复保存业务输出。
|
|
127
177
|
- 例如调用不存在的 Python 时,运行失败,message 含具体插件及“未找到程序”,错误链包含 `ENOENT` 和 `details.executable`。无需先运行环境检查插件才能捕获此错误。
|
|
128
|
-
- Python 脚本异常或依赖缺失通常通过非零退出码及 stderr 报告。作者必须检查返回值并抛错,例如 `throw new
|
|
129
|
-
- 插件可 `await log('正在检查 Python', {phase:'python-check'})`
|
|
178
|
+
- Python 脚本异常或依赖缺失通常通过非零退出码及 stderr 报告。作者必须检查返回值并抛错,例如 `throw Object.assign(new Error('Python 脚本失败'), {code:'PYTHON_FAILED', details:{exitCode:r.exitCode, stderr:r.stderr}})`;不要捕获后返回成功。此写法无需内部 SDK。
|
|
179
|
+
- 插件可 `await log('正在检查 Python', {phase:'python-check'})` 补充业务阶段。插件自身的 `console.log`、自行启动的子进程和未 await 的后台任务不会自动进入这套日志;动作上下文 `runProcess` 的 stdout/stderr 会实时写入。
|
|
130
180
|
- 核心不自动记录命令参数、环境变量和插件配置。错误消息、stderr、调用栈、作者 log 和返回结果仍可能包含敏感内容;作者应在返回或抛错前按业务脱敏。
|
|
131
181
|
|
|
132
182
|
定位顺序:按 runId 打开 run.md → 查看失败插件及步骤 → 查看原始错误码与调用栈 → 结合之前的日志和成功步骤输出复现。JSON 文件是给程序查询与界面使用的结构化记录,不要求用户通过 JSON 阅读日志。此处是运行诊断;插件加载/初始化失败发生在运行创建之前,目前由启动错误报告,不会凭空产生工作流运行目录。
|
|
@@ -139,12 +189,107 @@ TUI `/logs` → “设置日志保留次数”可指定整个项目合计保留
|
|
|
139
189
|
{"logs":{"keepRuns":10}}
|
|
140
190
|
```
|
|
141
191
|
|
|
142
|
-
未配置或设为 null 时不自动清理,0
|
|
192
|
+
未配置或设为 null 时不自动清理,0 和负数无效。保存保留策略时立即清理,之后每次运行结束自动执行,按结束时间从新到旧保留。成功、失败、取消及正常停止都计入已结束运行;正在执行或尚未写完日志的运行受到保护。
|
|
143
193
|
|
|
144
194
|
只移除过期运行的 run.md、events.jsonl 和 result.json;不递归删除运行目录,不删除插件生成的业务文件,不跟随日志目录或文件的符号链接。没有其他文件的空目录可移除。被清理的日志不再进入历史列表;业务文件仍可在原输出目录找到。自动清理失败会写入本次运行日志,不把业务成功改成失败。
|
|
145
195
|
|
|
146
196
|
记录按项目目录标识归属,共用 `--output` 时互不清理。无归属字段的旧记录仅在本项目默认 `.flow/runs` 内兼容读取和清理,共享输出目录中的旧记录不会自动认领。应用 API 的 `await run.done` 返回完成结果快照;并发任务的调用方应使用该快照,避免依赖可能已按保留策略移除的历史记录。
|
|
147
197
|
|
|
198
|
+
## 工作流条件与控制节点
|
|
199
|
+
|
|
200
|
+
`schemaVersion: 1` 保留顺序列表:同一层从上到下执行,每个步骤必须恰有一个执行目标。TUI 和 Web 都通过表单生成这些结构,不要求用户手写 YAML。
|
|
201
|
+
|
|
202
|
+
| 目标 | 结构与行为 |
|
|
203
|
+
| --- | --- |
|
|
204
|
+
| 动作 | `{action:'插件ID:动作', with:{...}}`,调用插件并校验输入输出 |
|
|
205
|
+
| 子工作流 | `{workflow:'./child.yaml', with:{...}}`,调用工作流目录中的子流程 |
|
|
206
|
+
| 条件分支 | `{branch:{condition, then:[步骤], else:[步骤]}}`,只执行命中的一侧;else 可省略 |
|
|
207
|
+
| 并行组 | `{parallel:{steps:[步骤], concurrency:2}}`,并发执行独立子步骤;并发数为 1–32 的整数,默认 4 |
|
|
208
|
+
| 提前结束 | `{stop:{reason:'环境不满足'}}`,正常结束整个顶层运行;原因必须为非空字符串 |
|
|
209
|
+
|
|
210
|
+
任一步骤可以增加 `when`。条件不成立时记 `step.skipped`,不调用插件,也不生成该步骤输出。未选择的分支同样标记跳过。节点可以嵌套,同一工作流内的步骤 ID 必须唯一,不能在不同分支重复使用。
|
|
211
|
+
|
|
212
|
+
### 条件与判定预设
|
|
213
|
+
|
|
214
|
+
比较条件例如 `{left:{$ref:'steps.check.output.ok'}, op:'eq', right:true}`。支持 `eq/ne`(严格类型的相等/不等)、`gt/gte/lt/lte`(数值比较)、`contains`(字符串包含或数组包含同类型值)、`exists`(字段存在,不需要 right)。组合条件使用 `{all:[条件]}`、`{any:[条件]}`、`{not:条件}`,不执行 JavaScript 表达式。
|
|
215
|
+
|
|
216
|
+
已声明字段在本次运行中缺失时,`exists` 为 false,其他比较也为 false。前向引用、并行兄弟引用、危险字段和静态可知不存在的字段会在执行前拒绝;不能用 `exists` 绕过非法引用。
|
|
217
|
+
|
|
218
|
+
插件可以给动作添加可选预设:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
conditions: {
|
|
222
|
+
passed: {title:'检查通过', path:'ok', op:'eq', value:true},
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`path` 是相对本动作输出的字段路径,必须能够从 output schema 找到。界面选择前序动作的预设后,将其展开为普通条件,例如 `steps.check.output.ok`。预设不改变动作是否执行成功,也不取代实际输出 schema 校验;普通文本转换动作无需额外声明“执行成功”预设。
|
|
227
|
+
|
|
228
|
+
### 引用范围、并行失败与停止
|
|
229
|
+
|
|
230
|
+
嵌套序列可以读取外层已完成步骤和本层前序步骤;并行兄弟不能互相读取结果。分支和并行组只将实际完成的直接子步骤汇总为 `子步骤 ID → 输出`,组外通过 `steps.group.output.child.field` 读取。嵌套组可以继续沿该结构读取;不能从外层直接引用组内子步骤的裸 ID。
|
|
231
|
+
|
|
232
|
+
跳过或未选中的步骤没有输出。普通参数绑定读到缺失输出会明确失败,应先用条件判断、分支或提前结束避免无效读取。打包依赖闭包包含所有分支和并行组中引用的插件及子工作流,不只包含本次命中的路径。
|
|
233
|
+
|
|
234
|
+
并行组出现首个失败或提前结束时,停止调度未开始的步骤、向已启动兄弟发出取消信号,并等待它们清理结束。插件应响应 `signal`;共享文件或连接的并发安全由插件负责。已经发生的业务副作用不会自动回滚。
|
|
235
|
+
|
|
236
|
+
`stop` 从嵌套分支、并行组或子工作流传播到整个运行,保留已完成步骤的事件和输出,记录 `step.stopped`、`workflow.stopped`、`run.stopped`。最终为 `stopped`,有 reason、无 error;不生成正常完成时的汇总输出。用户主动取消为 `cancelled`,执行异常为 `failed`。CLI 的 `run` 和 `command` 遇到正常停止返回退出码 0,并打印 `{status:'stopped', reason:'...'}`。
|
|
237
|
+
|
|
238
|
+
### 环境 report 与 check
|
|
239
|
+
|
|
240
|
+
`@mytools/environment:check` 保持原语义:缺少要求的解释器、包或设备时抛出 `ENVIRONMENT_FAILED`,旧流程因此停止执行。`@mytools/environment:report` 使用同样的检查,但未通过时正常返回包含 `ok:false`、system、checks 的报告;它提供 passed 预设,由工作流选择处理方式。两者都不会安装依赖或修复环境,也都会传播取消。
|
|
241
|
+
|
|
242
|
+
例如先报告,再在环境不满足时正常结束:
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
schemaVersion: 1
|
|
246
|
+
id: guarded-check
|
|
247
|
+
inputs:
|
|
248
|
+
type: object
|
|
249
|
+
required: [executable]
|
|
250
|
+
properties:
|
|
251
|
+
executable: {type: string, title: Python 解释器路径}
|
|
252
|
+
steps:
|
|
253
|
+
- id: inspect
|
|
254
|
+
action: '@mytools/environment:report'
|
|
255
|
+
with:
|
|
256
|
+
python:
|
|
257
|
+
executable: {$ref: inputs.executable}
|
|
258
|
+
- id: finish-if-unavailable
|
|
259
|
+
when:
|
|
260
|
+
left: {$ref: steps.inspect.output.ok}
|
|
261
|
+
op: eq
|
|
262
|
+
right: false
|
|
263
|
+
stop:
|
|
264
|
+
reason: 环境未满足要求,请查看检查明细
|
|
265
|
+
outputs:
|
|
266
|
+
report: {$ref: steps.inspect.output}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
报告通过时,stop 节点跳过并返回汇总报告;不通过时,最终状态为 stopped,可从 inspect 的步骤输出阅读检查明细。TUI 命令 `/env.check`、`/env.report` 分别对应这两个动作。
|
|
270
|
+
|
|
271
|
+
## TUI 与 Web 运行观察
|
|
272
|
+
|
|
273
|
+
TUI `/add` 选择动作、子工作流、条件分支、并行组或提前结束;`/steps` 可进入分支/并行组编辑内部步骤,设置执行条件、并发数和结束原因。条件表单支持选择字段或预设,也支持且、或、取反。`/save` 保存后,Web 可继续编辑同一份工作流。
|
|
274
|
+
|
|
275
|
+
TUI `/run 工作流ID` 支持 ID 和中文标题补全,执行过程、步骤输入输出与最终结果留在主面板。运行期间可继续输入命令或配置工具;多次执行分别记录,PgUp/PgDn 或鼠标滚轮回看。F4 或 `/runs` 打开详细观察面板,`/logs` 直接查看某次运行的日志;包括跨重启保留下来的记录。
|
|
276
|
+
|
|
277
|
+
| 面板键位 | 行为 |
|
|
278
|
+
| --- | --- |
|
|
279
|
+
| `1` / `2` / `3` | 步骤总览 / 输入输出 / 实时日志 |
|
|
280
|
+
| `[` / `]` | 选择上一个 / 下一个步骤调用 |
|
|
281
|
+
| `a` | 切换本步骤日志与全部日志 |
|
|
282
|
+
| ↑↓、鼠标滚轮、PageUp/PageDown、Home | 滚动或跳到开头;手动滚动暂停日志跟随 |
|
|
283
|
+
| End / `f` | End 跳至尾部并恢复跟随;f 切换跟随状态 |
|
|
284
|
+
| Enter | 在总览查看所选步骤详情 |
|
|
285
|
+
| `j` | 切换可读字段与原始 JSON / 完整诊断 |
|
|
286
|
+
| Esc、q、Ctrl+C | 返回上一级,运行中任务继续 |
|
|
287
|
+
| `x`,再按 `y` | 确认取消整个运行;确认提示中其他键返回 |
|
|
288
|
+
|
|
289
|
+
关闭观察面板不等于退出应用。主提示符 `/exit` 或 Ctrl+C 遇到后台运行时,会提示退出将取消这些任务,默认不退出。普通参数输入和插件命令忙碌页仍使用原有 Ctrl+C 取消行为。鼠标滚轮用于主面板和详细阅读面板;参数输入或菜单按各自的键盘提示操作,退出应用时恢复终端鼠标模式。
|
|
290
|
+
|
|
291
|
+
Web 通过“添加步骤 → 流程控制”添加分支、并行组或提前结束,点击节点填写条件、并发数、原因及内部步骤;普通步骤也可设置执行条件。保存后点击“运行工作流”,运行面板提供“步骤总览 / 输入输出 / 实时日志”和调用路径筛选。向上滚动日志会暂停跟随,可以点击“恢复自动跟随”继续查看尾部。关闭观察窗口后任务继续,顶部“查看运行”可重新打开;“停止运行”发送取消请求。只读运行包允许运行和观察,不允许修改流程。
|
|
292
|
+
|
|
148
293
|
## Python 脚本
|
|
149
294
|
|
|
150
295
|
JS 负责元数据和参数适配,业务可用 Python:
|
|
@@ -154,12 +299,14 @@ import path from 'node:path';
|
|
|
154
299
|
|
|
155
300
|
async function run({executable, text}, {resourceDir, runProcess, signal}) {
|
|
156
301
|
const result = await runProcess(executable,
|
|
157
|
-
[path.join(resourceDir, 'scripts', 'task.py'), text], {signal});
|
|
302
|
+
['-u', path.join(resourceDir, 'scripts', 'task.py'), text], {signal});
|
|
158
303
|
if (result.exitCode !== 0) throw new Error(result.stderr);
|
|
159
304
|
return JSON.parse(result.stdout);
|
|
160
305
|
}
|
|
161
306
|
```
|
|
162
307
|
|
|
308
|
+
需要实时进度时,使用 Python 的 `-u` 或 `print(..., flush=True)`,避免子进程自身的输出缓冲延迟消息。若 stdout 用于返回 JSON,把进度写到 stderr,避免进度文本混入最终 JSON。
|
|
309
|
+
|
|
163
310
|
将脚本纳入包 `files`。解释器和业务库使用目标机器环境,推荐先加入官方“检查目标环境”,把输出 `python.executable` 绑定到后续动作。支持 Python 版本、pip、distribution 包名及可选导入模块;版本范围用 semver(如 `>=3.9 <4`),不支持完整 PEP 440 预发行版本语义。
|
|
164
311
|
|
|
165
312
|
## 配置与许可
|
|
@@ -170,7 +317,7 @@ async function run({executable, text}, {resourceDir, runProcess, signal}) {
|
|
|
170
317
|
{"my-flow-plugin":{"endpoint":"http://127.0.0.1:9000"}}
|
|
171
318
|
```
|
|
172
319
|
|
|
173
|
-
私有配置不默认入包,`defaultConfig` 只放公开默认值。TUI `/
|
|
320
|
+
私有配置不默认入包,`defaultConfig` 只放公开默认值。TUI `/plugin` → 选择插件 → 设置插件配置,根据 schema 编辑并保存;`/config` 专用于 Fishtools 自身设置。CLI `fst configure 插件ID 配置.json` 继续配置插件。调用时 `--config` 优先于工作区配置,不自动回写或导出。
|
|
174
321
|
|
|
175
322
|
## 平台声明与责任边界
|
|
176
323
|
|
|
@@ -180,6 +327,10 @@ async function run({executable, text}, {resourceDir, runProcess, signal}) {
|
|
|
180
327
|
|
|
181
328
|
`commands` 的键不含 `/`,推荐使用 `插件短名.操作名`;不得与核心入口或其他插件命令重名。安装/移除后 TUI `/` 菜单立即更新,失败的安装不会替换旧宿主。
|
|
182
329
|
|
|
330
|
+
TUI 顶层按插件归组:例如 ID `@mytools/examples` 对应 `/examples`,进入后选择具体能力,列表标明 `[插件]`、插件名称及 ID。入口取插件 ID 的末段;同名插件使用完整 ID 转为连字符名称,遇到保留名或已有命令则加 `plugin-` 前缀。以当前补全列表中的实际入口为准,不要在插件间依赖这个 UI 别名。只有注册了 `commands` 的插件才产生能力入口,纯服务/纯动作插件仍可在 `/plugin` 管理或参与工作流。原始命令名和 CLI `fst command` 协议不变,旧插件无需迁移定义。新保留入口包括 `plugin`、`tools`,作者应继续使用带插件前缀的命令名。
|
|
331
|
+
|
|
332
|
+
TUI 负责展示当前工作区、操作入口、菜单标题以及提交命令和操作结果。返回时只更新当前位置,不追加通用返回消息。插件使用 `ui.input/select/show/confirm`,不要自行清屏或打印终端控制序列,以免破坏主面板中的运行过程。导航记录仅保留在本次会话,持久诊断请使用动作上下文的日志接口。`/run` 执行已保存的工作流;工作流草稿只在编辑菜单明确选择“保存并运行”后才写入,不由插件命令隐式保存。
|
|
333
|
+
|
|
183
334
|
```js
|
|
184
335
|
commands: {
|
|
185
336
|
'demo.greet': { description: '生成问候', action: 'greet' },
|
|
@@ -309,6 +460,7 @@ Fishtools 等待 `ctx.on('dispose', async () => ...)` 的清理完成并报告
|
|
|
309
460
|
- TUI `/plugins` 可进入维护模式并恢复服务;CLI 使用 `fst --maintenance plugin add ...`、`fst --maintenance configure ...`。维护模式不持久化,默认重启恢复服务。
|
|
310
461
|
- 维护模式停止 runtime 插件的动作和命令,普通插件仍可使用;声明校验仍执行,不能借维护模式加载无效插件定义。
|
|
311
462
|
- 现有 `services.web` 仍为兼容的启动工厂;应用持有其返回的 close 句柄。`app.close()` 先关闭这些句柄,再关闭业务宿主,重复关闭安全。
|
|
463
|
+
- 打包选择包含 Web 时,按 `services.web` 声明选取实际提供者,连同其传递插件依赖一起导出,不要求官方包名。完整便携包关闭 Web 选项时,仅省略非必需的 Web 提供者;工作流或其他插件确实依赖它时仍保留。
|
|
312
464
|
- 活动 Web 提供者不能移除、替换或修改配置;TUI 插件管理可先停止 Web。普通插件更新不会关闭标准发行版中的 Web 管理入口。外部安装的 Web 提供者若被 npm 重新生成到新目录,也需先停止。
|
|
313
465
|
- 没有实现运行中无缝热替换。服务撤销后消费者动作/命令被撤销;恢复后按 Cordis 生命周期重新激活。
|
|
314
466
|
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
# 版本发布
|
|
2
2
|
|
|
3
|
-
产品名称 Fishtools,公共 npm 包名 `@yanglaofish/fishtools`,启动命令 `fst`。`0.
|
|
3
|
+
产品名称 Fishtools,公共 npm 包名 `@yanglaofish/fishtools`,启动命令 `fst`。`0.x` 定位为早期可用版本。内部 `@mytools/*` 包随主包捆绑,不单独发布;内部插件 API 版本独立维护。
|
|
4
|
+
|
|
5
|
+
## 0.3.0 终端交互与插件输出
|
|
6
|
+
|
|
7
|
+
- 终端顶层按工作流、插件、工具和配置组织;命令历史保留未提交草稿,工作流编辑与执行互不覆盖。
|
|
8
|
+
- 主面板按步骤展示跨插件执行摘要;`Ctrl+O` 展开输入输出和诊断。插件可用 `print()` 发送用户可见内容,展开详情包含完整错误链。
|
|
9
|
+
- `/logs` 统一回看实时和历史运行;修复第三方插件进程调用、Web 依赖打包和 JSON Schema 升级。
|
|
10
|
+
- 离线手册补齐新增研究文档引用的源码,长日志刷新合并处理。
|
|
11
|
+
|
|
12
|
+
本版本沿用 0.2.0 的工作流格式与插件 API 版本;第三方插件可按需使用新增 `print()`。发布验收结果记录在下方及[验收记录](verification.md)。
|
|
13
|
+
|
|
14
|
+
## 0.2.0 控制流与运行观察
|
|
15
|
+
|
|
16
|
+
- 增加执行条件、条件分支、并行组、提前结束;TUI/Web 均可通过表单编辑嵌套步骤与参数引用。
|
|
17
|
+
- 正常返回且输出通过 schema 校验即动作成功;环境 `report` 返回业务结果供工作流判断,原 `check` 保留失败即抛错的行为。
|
|
18
|
+
- `/runs` 提供步骤总览、输入输出和实时日志;支持键盘、鼠标滚轮、暂停与恢复跟随。关闭观察面板后任务继续,取消需明确操作。
|
|
19
|
+
- `runProcess` 自动将 stdout/stderr 实时记录到当前步骤,Markdown 日志与结构化记录保留停止原因及错误来源。
|
|
20
|
+
- 新工作区带有 `control-demo` 示例;导出继续收集所有分支、并行子节点与子工作流的插件依赖。
|
|
21
|
+
- TUI 首页改为青绿色紧凑块字标识,统一菜单与日志配色,支持窄屏与 `NO_COLOR`;`/docs` 可直接阅读离线开发资料。
|
|
22
|
+
- npm 和便携包随附自包含开发手册、接口规范、参考源码、许可证与零依赖示例插件;`fst docs --export <新目录>` 导出整套资料,不需要访问源码仓库。
|
|
23
|
+
|
|
24
|
+
升级说明:参与工作流编排的旧插件需要补齐有效的 `input` 与 `output` JSON Schema;无参数/无输出可使用空对象 schema 并返回 `{}`。不需要额外编写“成功判定”函数。原有顺序工作流仍使用 `schemaVersion: 1`。发布是否完成以本版本的 GitHub Release 和下方验收记录为准。
|
|
4
25
|
|
|
5
26
|
## 发布入口
|
|
6
27
|
|
|
28
|
+
**发布前先完成本地试用并取得用户确认。** 自动化测试通过只代表具备试用条件,不代替用户对交互体验的验收。交付本地启动入口、说明改动及测试结果后,等待用户明确确认本轮可以发布,才创建版本 tag、发布 npm 或 GitHub Release。普通修复提交不触发发包。
|
|
29
|
+
|
|
30
|
+
`npm run preview` / 根目录 `fst-preview.cmd` 继续提供独立源码预览工作区;其运行不会修改全局安装或默认工作区。0.3.0 的对象菜单、主面板运行及公共插件修复已列入本次发布范围。
|
|
31
|
+
|
|
7
32
|
普通 push / PR 只验证代码。推送 `v版本号` tag 触发 `.github/workflows/release.yml`:
|
|
8
33
|
|
|
9
34
|
1. 检查 tag、根 package.json 和 CLI 版本一致。
|
|
@@ -39,7 +64,8 @@ git push origin v0.1.4
|
|
|
39
64
|
- Secret `NPM` 仅传入最后的发布步骤。需要有创建/发布 `@yanglaofish/fishtools` 的权限,以及 npm 自动发布要求的 Bypass 2FA 配置。缺失或权限不足时保留失败状态,修正 Secret 后重跑失败任务。
|
|
40
65
|
- npm 与 GitHub 不能进行同一个原子事务。若 npm 已成功、GitHub 暂时失败,使用 Actions 的 **Re-run failed jobs**;同版本归档 SHA-512 一致才跳过 npm 并继续 GitHub 发布。不同内容必须增加版本,不能覆盖。
|
|
41
66
|
- npm 首次发布可能先返回成功并提示正在处理,而注册表暂时仍返回 404。发布后会等待最多约 5 分钟再核对 SHA-512;若仍未可见,等待 `npm view @yanglaofish/fishtools@版本号` 能查询到后,再重跑失败任务,避免在处理阶段重复提交。
|
|
42
|
-
- GitHub Release
|
|
67
|
+
- GitHub Release 先创建草稿,附件齐备且 npm 可见后公开;重试可补齐草稿。已公开 Release 不自动覆盖附件。
|
|
68
|
+
- 若 npm 处理持续较久,可手动运行 `查询 npm 版本状态`(`npm-status.yml`)并填写版本。此工作流使用现有 Secret 只读查询官方生命周期接口,只记录状态字段,不输出凭据、不上传包。`validating` 表示仍在校验,需等注册表可见后再重跑发布。
|
|
43
69
|
- 也可使用 `gh workflow run release.yml --ref v0.1.3` 针对现有 tag 重新触发。由于完整重建可能产生不同归档,优先重跑失败任务,以复用原始已验证产物。
|
|
44
70
|
- 仓库当前为 private,Release 也沿用仓库访问权限;npm 包公开发布。未启用要求公开源码仓库的 npm provenance,不改变仓库可见性。
|
|
45
71
|
|
|
@@ -62,3 +88,7 @@ git push origin v0.1.4
|
|
|
62
88
|
`v0.1.3` 的 Windows/Linux 全量测试、自包含包执行和 Docker 验收全部通过。npm 于 2026-10-10 23:18(北京时间)接受 `@yanglaofish/fishtools@0.1.3`,首次发布后的处理延迟曾导致注册表返回 404。待注册表可见后重跑原发布任务,核对相同归档并跳过重复上传,于 23:24 完成 [GitHub Release](https://github.com/yanglaofish/Fishtools/releases/tag/v0.1.3),包含 npm、Windows、Linux 三个归档及 SHA256SUMS。[发布流程](https://github.com/yanglaofish/Fishtools/actions/runs/38062508265) 全部成功;后续发布脚本已增加有上限的注册表可见性等待。
|
|
63
89
|
|
|
64
90
|
首次发布时第三方 npm 镜像可能尚未同步归档。若遇到镜像 404,可指定官方源安装:`npm install -g @yanglaofish/fishtools --registry=https://registry.npmjs.org`。
|
|
91
|
+
|
|
92
|
+
## 0.2.0 发布结果
|
|
93
|
+
|
|
94
|
+
2026-10-11:双平台各 143 项测试、自包含归档与断网 Docker 验收通过。npm 接受上传后,生命周期状态为 `validating`,超过首次五分钟等待上限;注册表可见后核对相同 SHA-512,重跑原任务的失败步骤完成同步。当前官方注册表 `latest` 为 0.2.0,[发布流程](https://github.com/yanglaofish/Fishtools/actions/runs/38076094907)成功,[Release](https://github.com/yanglaofish/Fishtools/releases/tag/v0.2.0) 已包含全部三个归档和校验文件。
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# 插件内核公共能力研究:会话、执行、Hook 与授权
|
|
2
|
+
|
|
3
|
+
日期:2026-10-11。状态:研究与设计建议,**不是已实现的接口规范或发布承诺**。
|
|
4
|
+
|
|
5
|
+
Fishtools 核对基线:`838d572f0eff85f759b3c1118abb9dda616a4113`。
|
|
6
|
+
|
|
7
|
+
## 1. 来源与研究边界
|
|
8
|
+
|
|
9
|
+
- 用户提供的 [pengchengneo/Claude-Code](https://github.com/pengchengneo/Claude-Code) 自述为 npm source map 还原的非官方源码,README 声明源码版权归 Anthropic。查询时 GitHub 未识别到许可证;不能按 MIT 组件直接融合。
|
|
10
|
+
- 本次固定读取快照 `b78dd22a091b717c8938ab98c736bc04825a8ee8`。Git 克隆连接失败后,改用 GitHub API 读取关键文件;没有运行该仓库的脚本、依赖或二进制,没有把其代码复制进产品。
|
|
11
|
+
- 官方公开文档用于核对受支持的扩展机制;官方当前文档与还原快照不是同一版本,不能互相证明所有实现细节。
|
|
12
|
+
- 下文“建议”均为 Fishtools 的设计判断;具体 API 名称仍需设计确认。当前可用接口以 [插件开发规范](../plugin-development.md) 为准。
|
|
13
|
+
|
|
14
|
+
## 2. 值得借鉴的机制
|
|
15
|
+
|
|
16
|
+
| 观察到的机制 | 源码或官方证据 | 对 Fishtools 的意义 |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| 工具契约涵盖执行、输入输出、进度、权限与展示 | [Tool.ts](https://github.com/pengchengneo/Claude-Code/blob/b78dd22a091b717c8938ab98c736bc04825a8ee8/src/Tool.ts) | 插件动作除了能调用,还应能被统一展示、取消和审核;不把模型提示词、React 节点放进我们的通用动作契约 |
|
|
19
|
+
| 调用管线串起校验、前置 Hook、权限、执行及后置 Hook | [toolExecution.ts](https://github.com/pengchengneo/Claude-Code/blob/b78dd22a091b717c8938ab98c736bc04825a8ee8/src/services/tools/toolExecution.ts) | 工作流、斜杠命令与未来 Agent 调用同一能力时,应经过同一条受管调用路径 |
|
|
20
|
+
| 会话文件在首条用户/助手消息时才创建,之前缓存元数据 | [sessionStorage.ts](https://github.com/pengchengneo/Claude-Code/blob/b78dd22a091b717c8938ab98c736bc04825a8ee8/src/utils/sessionStorage.ts#L973) | 支持用户提出的空会话不落盘;Fishtools 的触发点应改为首次实质操作,不能照搬聊天消息标准 |
|
|
21
|
+
| 执行任务和观察界面有独立状态 | [task/framework.ts](https://github.com/pengchengneo/Claude-Code/blob/b78dd22a091b717c8938ab98c736bc04825a8ee8/src/utils/task/framework.ts) | 返回菜单不结束运行;日志、主面板及运行详情查看同一执行记录 |
|
|
22
|
+
| 权限有独立决策对象和交互组件 | [permissions.ts](https://github.com/pengchengneo/Claude-Code/blob/b78dd22a091b717c8938ab98c736bc04825a8ee8/src/utils/permissions/permissions.ts)、[PermissionRequest.tsx](https://github.com/pengchengneo/Claude-Code/blob/b78dd22a091b717c8938ab98c736bc04825a8ee8/src/components/permissions/PermissionRequest.tsx) | 判断结果不能只是一行文本;应有带请求 ID、操作范围、理由和有效期的决定,再由界面收集用户选择 |
|
|
23
|
+
| 官方 Mods 提供同进程 JavaScript/TypeScript 扩展 | [Mods overview](https://code.claude.com/docs/en/plugins/mods/overview) | 公开接口也能覆盖命令、界面和执行拦截;同进程扩展仍可直接访问宿主,不构成沙箱 |
|
|
24
|
+
| 官方事件接口区分观察、改写和接管,允许等待用户输入 | [React to events](https://code.claude.com/docs/en/plugins/mods/events) | 借鉴事件契约与异步交互;首版不必开放任意替换执行结果、自动重试或完整接管调用的能力 |
|
|
25
|
+
| 官方 API 将命令、工具和受管资源集中提供;界面文档区分多种状态生命周期 | [Mods API](https://code.claude.com/docs/en/plugins/mods/api)、[UI 与状态](https://code.claude.com/docs/en/plugins/mods/interface) | 分开运行临时数据、会话扩展状态和持久插件数据;由宿主管理交互焦点,避免插件互相抢占终端 |
|
|
26
|
+
|
|
27
|
+
该快照的权限界面仍有按内置工具对象选择组件的分支。Fishtools 应提供通用展示协议和默认渲染器,避免为第三方插件增加内核分支。源码中出现的隐藏特性不作为我们的产品目标。
|
|
28
|
+
|
|
29
|
+
## 3. 先明确四个对象
|
|
30
|
+
|
|
31
|
+
| 对象 | 定义 | 生命周期 |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| 工作区 Workspace | 工作流、插件配置、会话记录和结果的归属位置 | 跨启动保留;不是权限沙箱 |
|
|
34
|
+
| 会话 Session | 用户这一次连续工作的记录,可含多个工作流和直接插件调用 | 启动产生临时会话;有实际操作才保存;可从历史重新打开 |
|
|
35
|
+
| 执行 Run | 一次工作流或一次独立插件能力调用 | 有独立 ID、状态、输出和日志;固定归属启动它的会话 |
|
|
36
|
+
| 调用 Invocation | Run 内某一步插件调用或嵌套子调用 | 有调用路径、父调用、输入输出、进度及取消信号 |
|
|
37
|
+
|
|
38
|
+
由此确定入口分工:
|
|
39
|
+
|
|
40
|
+
- `/history`:查找、打开会话;恢复该会话主面板、执行记录和阅读位置。
|
|
41
|
+
- `/logs`:浏览当前会话内的执行,进一步查看某一步诊断。它是同一批记录的另一种视图。
|
|
42
|
+
- 主面板:按会话连续展示操作与执行摘要;配置菜单只是临时界面,不销毁运行过程。
|
|
43
|
+
- ↑↓:输入命令回溯,和会话历史是不同功能。
|
|
44
|
+
|
|
45
|
+
恢复历史不等于重跑工作流,也不等于恢复已经退出的 Python/Node 进程。LLM 插件可以恢复自己的对话状态;恢复何种模型上下文由该插件决定。
|
|
46
|
+
|
|
47
|
+
这也符合官方会话文档对“恢复记录”和“重新运行中断调用”的区分;具体保留策略仍由 Fishtools 自己定义。参见 [恢复会话的范围](https://code.claude.com/docs/en/sessions#what-a-resumed-session-restores)。
|
|
48
|
+
|
|
49
|
+
## 4. 公共能力清单与当前差距
|
|
50
|
+
|
|
51
|
+
“部分已有”只表示具备基础实现,不表示新接口已可调用。
|
|
52
|
+
|
|
53
|
+
| 公共能力 | 当前状态及证据 | 建议补齐的契约 |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| 插件加载、依赖、生命周期和共享服务 | 已有:[host.js](../../packages/core/src/host.js)、[runtime.js](../../packages/core/src/runtime.js) | 继续使用 Cordis;新增订阅、Hook、面板注册也跟随插件作用域注销 |
|
|
56
|
+
| 动作发现、调用与结果校验 | 已有 catalog、invoke、commands、input/output:[host.js](../../packages/core/src/host.js) | 稳定的第三方调用门面;身份和调用归属由内核赋值,统一经过 Hook/授权/日志 |
|
|
57
|
+
| 顺序、分支、并行、条件停止 | 已有:[engine.js](../../packages/core/src/engine.js) | 沿用现有控制节点和状态,不为 LLM 另造一套编排协议 |
|
|
58
|
+
| 会话与插件状态存储 | 尚无持久会话;[terminal/view.js](../../packages/core/src/terminal/view.js) 的命令历史在内存中 | 延迟落盘、会话索引、恢复、按插件命名空间存储数据和版本;工作区状态与会话状态分开 |
|
|
59
|
+
| 执行记录、日志和用户输出 | 已有 run、events、run.md、log/print:[application.js](../../packages/core/src/application.js)、[run-feed.js](../../packages/core/src/terminal/run-feed.js) | 统一事件版本与顺序号;进度更新、结果摘要、产物引用;历史与实时使用相同渲染输入 |
|
|
60
|
+
| 公共交互与界面扩展 | 插件交互命令已有 input/select/show/confirm:[terminal/session.js](../../packages/core/src/terminal/session.js) | 能力查询、结构化确认/表单/详情;由内核仲裁并发提问和焦点;动作与 Hook 可通过统一请求通道交互 |
|
|
61
|
+
| 执行 Hook | 仅有生命周期回调和运行事件订阅 | 注册前置决策与后置通知;过滤范围、超时、取消、顺序、异常归属和注销契约 |
|
|
62
|
+
| 授权请求、模式和审计 | 尚无统一机制 | 操作与资源范围声明、允许/拒绝/询问/无意见、用户规则与可选决策插件、真实 TUI 请求 |
|
|
63
|
+
| 受管进程与后台工作 | 已有 runProcess、取消/超时、stdout/stderr;运行可在主面板持续显示 | 给受管操作接入权限与调用归属;长期后台任务句柄随后按实际需要增加,不立刻建设新调度系统 |
|
|
64
|
+
| 分发和第三方开发支持 | 已有按工作流依赖打包、平台/环境声明、随包手册与示例 | 每项新契约同步类型、离线说明、仓库外插件示例和接入验收;不能只让内置插件使用 |
|
|
65
|
+
|
|
66
|
+
当前 [SDK](../../packages/sdk/index.js) 很小,且未作为独立公共 npm SDK 发布。不能把内部存在某个函数等同于第三方已经拥有稳定 API。
|
|
67
|
+
|
|
68
|
+
## 5. 会话、日志和持久化
|
|
69
|
+
|
|
70
|
+
以下是把已讨论方向落实为契约的建议:
|
|
71
|
+
|
|
72
|
+
1. `fst` 启动只产生内存中的临时会话。浏览帮助、插件列表、历史不会制造空记录;直接打开旧会话丢弃这个临时对象。
|
|
73
|
+
2. 第一次请求实际执行时,在可能发生副作用之前保存会话与请求。失败、取消和权限拒绝也有诊断意义。工作流保存/配置修改算作实质操作并记录摘要,是本次建议,仍需确认。
|
|
74
|
+
3. 每个运行在创建时固定 sessionId。用户之后切换历史页面,后台事件仍写回原会话,不能读一个可变的“当前会话”决定归属。
|
|
75
|
+
4. 会话记录引用其全部 Run;事件原始数据只保存一份,主面板、执行详情、Markdown 导出从中构建视图。保留 run.md 方便离线阅读,TUI 不直接展示原始 Markdown 标记。
|
|
76
|
+
5. 建议以整会话为日志保留单位,替代“整个项目最近 10 个 Run”。保留的会话不能被独立 Run 清理掏空;活动会话/执行不参加清理,业务产物仍不随日志自动删除。
|
|
77
|
+
6. 旧记录先兼容读取并标注“旧版执行记录”。迁移先备份、可回退,不在首次启动时直接删除旧文件;保留数量也不静默把 10 次解释成 10 个会话。
|
|
78
|
+
7. 序列化和追加写入是内部持久化方法,用户仍看可读内容。增量写入、崩溃后的末尾损坏处理、多个 fst 同时写入的锁/版本策略都需要明确。
|
|
79
|
+
8. 重启后未正常结束的本地执行显示“中断”,不伪装仍在执行。没有插件明确支持的恢复协议,就只恢复记录。
|
|
80
|
+
|
|
81
|
+
LLM 插件可以在自身会话命名空间保存消息、模型配置引用、压缩状态。内核只管理归属、读写、版本和清理,不解释 token 或提示词。敏感配置不应随日志和分发包无意传播;日志字段需提供显式隐藏/脱敏声明,不能宣称能识别所有任意文本中的秘密。
|
|
82
|
+
|
|
83
|
+
## 6. Hook 与权限需要分开
|
|
84
|
+
|
|
85
|
+
### 6.1 Hook 是执行扩展点
|
|
86
|
+
|
|
87
|
+
建议先有少量事件族:会话开始/恢复/结束;Run 开始/结束;动作执行前/成功/失败/取消;权限请求/决定。
|
|
88
|
+
|
|
89
|
+
- 通知 Hook 用于观察、统计、保存插件状态。异常需要记录,不能把已经成功的动作改成“没有执行”。
|
|
90
|
+
- 前置决策 Hook 可阻止或请求确认。声明为必要的决策 Hook 超时/失败时,该操作不能继续冒充已审核。
|
|
91
|
+
- 插件只能注册自己作用域内的 Hook,卸载自动注销;顺序固定并可诊断,不能依赖竞速最先返回的结果。
|
|
92
|
+
- 首版建议不开放任意输入改写/结果替换/自动重试。后续若支持改写,必须重新校验最终输入、重新评估权限并记录差异;后置 Hook 无法撤销已发生的副作用。
|
|
93
|
+
- 事件订阅只是观察通道;不能把目前 `application.subscribe` 更名成 Hook 就算完成执行拦截。
|
|
94
|
+
|
|
95
|
+
官方存在设置型 Hook 与同进程 Mods 两种机制;Fishtools 先做一种公共 JS 注册契约即可。运行脚本、HTTP 或 LLM 审核可通过插件实现,不必都进入内核。参见 [官方 Hooks](https://code.claude.com/docs/en/hooks) 与 [Mods 事件](https://code.claude.com/docs/en/plugins/mods/events)。
|
|
96
|
+
|
|
97
|
+
### 6.2 权限是受管操作的决策
|
|
98
|
+
|
|
99
|
+
建议的受管调用顺序:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
工作流 / 插件命令 / 第三方调用门面
|
|
103
|
+
→ 创建调用身份并记录请求
|
|
104
|
+
→ 校验输入
|
|
105
|
+
→ 执行前决策 Hook
|
|
106
|
+
→ 收集操作范围并进行权限决策
|
|
107
|
+
→ 需要时排队显示交互确认
|
|
108
|
+
→ 执行动作(受管子进程仍检查其具体操作)
|
|
109
|
+
→ 校验输出并记录成功/失败/取消
|
|
110
|
+
→ 后置通知
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
若前置 Hook 拒绝,也记录拒绝及来源,并走统一收尾。避免一个嵌套调用对相同、已授权范围重复弹窗,但新增路径/参数/目的地必须重新判断。
|
|
114
|
+
|
|
115
|
+
操作类别从文件读取、写入、删除、进程启动、网络访问等小集合起步;请求包含插件、动作、实际目标、参数、cwd 和目的。插件声明描述自己的操作,**不能据此给自己授予权限**。`rm -f` 只是某种命令表达,不能用几个字符串匹配代替所有删除请求。
|
|
116
|
+
|
|
117
|
+
规则裁决建议:用户明确拒绝优先;用户明确要求询问的操作不能被普通自动模式绕过;匹配允许范围后执行;其他请求按模式处理。可选审核插件必须由用户选择启用作为决策提供者,不能因安装了一个业务插件就让它审批其他插件。
|
|
118
|
+
|
|
119
|
+
| 建议模式 | 未命中明确规则时 | 无交互终端时 |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| 确认 | 展示 TUI 确认 | 返回需要授权的结构化结果,不等待 stdin |
|
|
122
|
+
| 自动 | 本地规则/用户启用的审核插件给出决定;无结论则询问 | 无法完成审批则拒绝此次操作并说明原因 |
|
|
123
|
+
| 宽松 | 自动允许普通受管请求;仍保留明确拒绝和必须询问规则 | 按用户明确选择的同一策略处理 |
|
|
124
|
+
|
|
125
|
+
这是 Fishtools 的候选语义,**不等同 Claude 的同名模式**。[官方权限模式](https://code.claude.com/docs/en/permission-modes) 用于比较,不逐项照搬。预览执行计划单独提供:显示已知步骤、条件、依赖和权限,未知运行值标记未知;不靠真实执行插件来“预览”。
|
|
126
|
+
|
|
127
|
+
确认界面应显示操作和范围,提供“允许本次 / 本会话允许此范围 / 拒绝”,以拒绝为安全默认;↑↓、Enter、Esc 可操作。会话授权不会无提示变为永久授权。每条请求有 ID;多步骤并行时由界面依次展示,后台输出继续记录。请求取消或用户切换权限模式后,旧审核结果不得批准已经失效的请求。恢复旧会话不静默恢复过去的宽松授权。
|
|
128
|
+
|
|
129
|
+
无沙箱的边界保持明确:以上约束公共调用路径。插件直接使用 Node fs、网络或 child_process 可以绕开受管接口。我们不因此限制用户安装谁的插件,也不声称能强制隔离任意插件代码。官方同进程 Mods 文档同样明确了宿主权限访问边界;这是架构条件,不是官方审核要求。
|
|
130
|
+
|
|
131
|
+
## 7. 展示与交互也必须是公共能力
|
|
132
|
+
|
|
133
|
+
建议沿用现有 `log` / `print`,补充以下语义而非让每个插件自己控制终端:
|
|
134
|
+
|
|
135
|
+
| 信息 | 默认展示 | 展开详情/持久化 |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| 动作状态 | 插件与能力名称、执行中/等待确认/结果、耗时 | 输入输出、调用路径 |
|
|
138
|
+
| 进度 | 更新同一条进度;无百分比时显示阶段 | 保留必要的阶段事件,避免每帧刷一条日志 |
|
|
139
|
+
| log、stdout/stderr | 折叠 | Ctrl+O / 执行详情查看、可滚动 |
|
|
140
|
+
| print | 直接显示,保持调用归属 | 同样可在历史重读 |
|
|
141
|
+
| 错误 | 直接显示原因和所属插件 | 堆栈、退出码、stderr、建议处理信息 |
|
|
142
|
+
| 产物 | 文件/报告名称和位置 | 结构化引用,和日志清理分开 |
|
|
143
|
+
|
|
144
|
+
声明式表单、选择、确认、结果字段、详情块是第一步;自定义面板/状态栏插槽可以后补。TUI/Web 各有适配器,CLI 无交互时返回明确结果。插件查询当前支持哪些交互,不导入内核私有组件,不直接抢 stdin 或清屏。
|
|
145
|
+
|
|
146
|
+
默认输出 schema 足以提供基本结果展示,复杂插件再注册可选展示元数据。不把“为每个插件写 React 渲染器”变成接入门槛。审批内容由宿主统一显示,业务插件不自行伪装内核授权面板。
|
|
147
|
+
|
|
148
|
+
## 8. 内核、插件与暂缓范围
|
|
149
|
+
|
|
150
|
+
**内核公共服务:** 插件注册与 Cordis 生命周期、动作契约、编排执行、会话/Run/事件归属、日志与产物引用、Hook 调度、权限决策机制、交互请求协议、打包闭包和手册。
|
|
151
|
+
|
|
152
|
+
**由插件实现:** 环境探测、Python 业务逻辑、LLM 模型与对话循环、LLM 审核提供者、MCP/外部 Agent 适配、SSH/Kubernetes 执行、具体报告和可选界面扩展。
|
|
153
|
+
|
|
154
|
+
**当前不扩张到:** 不可信代码沙箱、企业组织权限后台、多机内核调度、任意动作自动回滚、崩溃后继续旧进程、全能任意代码审计、复制 Claude 全套前端或隐藏功能。
|
|
155
|
+
|
|
156
|
+
继续复用已有 Cordis、pi-tui、Execa、Ajv。第三方源码组件仅在明确许可和实际需要下引入;这次研究不增加依赖、不换 UI 框架。
|
|
157
|
+
|
|
158
|
+
## 9. 建议实施顺序与验收
|
|
159
|
+
|
|
160
|
+
本节是下一阶段的候选目标,不覆盖已完成里程碑,也不表示用户已批准每个 API 细节。
|
|
161
|
+
|
|
162
|
+
1. **会话与记录统一。** 实现延迟落盘、history/logs 分工、主面板恢复、会话归属及保留策略。验收:空启动→恢复不产生新文件;一会话多个工作流;失败记录保留;重启阅读原位置;后台事件不串会话;旧日志有备份与迁移回退。
|
|
163
|
+
2. **公共展示与交互。** 补进度/产物/确认请求协议,所有第三方通过公共接口使用。验收:独立插件的 print、log、错误、进度在 Windows/WSL/无 Node 的便携 Linux 包中一致;切菜单不丢记录;并发提问不抢焦点。
|
|
164
|
+
3. **Hook 与权限闭环。** 在统一受管调用处接入前置决策、后置通知、授权和审计。验收:工作流/命令/外部调用行为一致;明确 deny 不被模式覆盖;更改目标重新审批;Hook 超时/取消和无 TTY 可预测;注销后不残留回调。
|
|
165
|
+
4. **第三方扩展验证。** 仓库外做业务插件和审核插件,再用一个最小 LLM/Agent 适配样例验证 catalog、调用、会话状态与交互是否足够。核心中不能出现对该插件 ID 的特殊判断。长期后台任务接口有真实需要时再扩展。
|
|
166
|
+
|
|
167
|
+
每阶段同步随包离线手册、类型说明和可安装示例;新方法只有在文档、实现和仓库外接入验收一致后才标记为可用。先本地 `fst-preview` 试用,用户确认后再讨论发布。
|