@yanglaofish/fishtools 0.5.3 → 0.6.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 +3 -3
- package/manual/README.md +3 -3
- package/manual/docs/modules.md +2 -0
- package/manual/docs/plans/2026-10-13-effective-inputs-and-tables.md +39 -0
- package/manual/docs/plans/2026-10-13-plugin-defaults.md +45 -0
- package/manual/docs/plans/2026-10-14-issue7-recovery.md +33 -0
- package/manual/docs/plans/2026-10-14-issue8-10-workflows.md +26 -0
- package/manual/docs/plans/2026-10-14-issue9-completion.md +47 -0
- package/manual/docs/plans/2026-10-14-managed-execution.md +58 -0
- package/manual/docs/plans/2026-10-14-plugin-discovery-history.md +41 -0
- package/manual/docs/plans/2026-10-14-structured-reports.md +77 -0
- package/manual/docs/plans/2026-10-14-unreleased-review.md +47 -0
- package/manual/docs/plugin-development.md +161 -8
- package/manual/docs/releases.md +25 -6
- package/manual/docs/superpowers/plans/2026-10-13-tui-readable-sessions.md +2 -2
- package/manual/docs/superpowers/reviews/2026-10-13-tui-sessions-review.md +2 -0
- package/manual/docs/superpowers/reviews/2026-10-14-0.6.0-release-review.md +19 -0
- package/manual/docs/verification.md +17 -0
- package/manual/packages/core/README.md +6 -0
- package/manual/packages/core/src/application.js +15 -9
- package/manual/packages/core/src/plugin-catalog.js +20 -2
- package/manual/packages/core/src/plugin-loading.js +24 -0
- package/manual/packages/core/src/version.js +1 -1
- package/manual/packages/fst-cli/README.md +5 -1
- package/manual/packages/fst-cli/src/terminal/command-menu.js +3 -2
- package/manual/packages/fst-cli/src/terminal/file-autocomplete.js +106 -0
- package/manual/packages/fst-cli/src/terminal/log-viewer.js +4 -3
- package/manual/packages/fst-cli/src/terminal/navigation.js +23 -1
- package/manual/packages/fst-cli/src/terminal/presentation.js +2 -1
- package/manual/packages/fst-cli/src/terminal/report-presentation.js +38 -0
- package/manual/packages/fst-cli/src/terminal/result-table.js +66 -0
- package/manual/packages/fst-cli/src/terminal/run-feed.js +42 -17
- package/manual/packages/fst-cli/src/terminal/run-presentation.js +51 -17
- package/manual/packages/fst-cli/src/terminal/run-viewer.js +15 -12
- package/manual/packages/fst-cli/src/terminal/schema.js +114 -4
- package/manual/packages/fst-cli/src/terminal/session-history.js +2 -2
- package/manual/packages/fst-cli/src/terminal/session.js +40 -15
- package/manual/packages/fst-cli/src/terminal/view.js +80 -68
- package/manual/packages/fst-cli/src/terminal/workflow-menu.js +3 -1
- package/manual/packages/fst-devtools/README.md +2 -0
- package/manual/packages/fst-devtools/src/plugin-quality.js +4 -1
- package/manual/packages/fst-devtools/src/plugin-tools.js +1 -0
- package/manual/packages/fst-devtools/src/process-usage.js +62 -0
- package/manual/packages/fst-platform/README.md +5 -0
- package/manual/packages/fst-platform/package.json +1 -0
- package/manual/packages/fst-platform/src/managed-process.js +36 -0
- package/manual/packages/fst-platform/src/process.js +7 -2
- package/manual/packages/fst-plugin/README.md +4 -0
- package/manual/packages/fst-plugin/src/contracts.js +3 -0
- package/manual/packages/fst-plugin/src/host.js +38 -23
- package/manual/packages/fst-plugin/src/runtime.js +14 -8
- package/manual/packages/fst-workflow/src/engine.js +1 -1
- package/manual/packages/fst-workspace/README.md +4 -0
- package/manual/packages/fst-workspace/src/plugin-registry.js +1 -1
- package/manual/packages/fst-workspace/src/plugins.js +5 -3
- package/manual/packages/fst-workspace/src/workflows.js +27 -9
- package/manual/packages/sdk/README.md +10 -0
- package/manual/packages/sdk/inputs.js +53 -0
- package/manual/packages/sdk/package.json +2 -0
- package/manual/packages/sdk/reports.js +53 -0
- package/node_modules/@mytools/core/README.md +6 -0
- package/node_modules/@mytools/core/src/application.js +15 -9
- package/node_modules/@mytools/core/src/plugin-catalog.js +20 -2
- package/node_modules/@mytools/core/src/plugin-loading.js +24 -0
- package/node_modules/@mytools/core/src/version.js +1 -1
- package/node_modules/@mytools/fst-cli/README.md +5 -1
- package/node_modules/@mytools/fst-cli/src/terminal/command-menu.js +3 -2
- package/node_modules/@mytools/fst-cli/src/terminal/file-autocomplete.js +106 -0
- package/node_modules/@mytools/fst-cli/src/terminal/log-viewer.js +4 -3
- package/node_modules/@mytools/fst-cli/src/terminal/navigation.js +23 -1
- package/node_modules/@mytools/fst-cli/src/terminal/presentation.js +2 -1
- package/node_modules/@mytools/fst-cli/src/terminal/report-presentation.js +38 -0
- package/node_modules/@mytools/fst-cli/src/terminal/result-table.js +66 -0
- package/node_modules/@mytools/fst-cli/src/terminal/run-feed.js +42 -17
- package/node_modules/@mytools/fst-cli/src/terminal/run-presentation.js +51 -17
- package/node_modules/@mytools/fst-cli/src/terminal/run-viewer.js +15 -12
- package/node_modules/@mytools/fst-cli/src/terminal/schema.js +114 -4
- package/node_modules/@mytools/fst-cli/src/terminal/session-history.js +2 -2
- package/node_modules/@mytools/fst-cli/src/terminal/session.js +40 -15
- package/node_modules/@mytools/fst-cli/src/terminal/view.js +80 -68
- package/node_modules/@mytools/fst-cli/src/terminal/workflow-menu.js +3 -1
- package/node_modules/@mytools/fst-devtools/README.md +2 -0
- package/node_modules/@mytools/fst-devtools/src/plugin-quality.js +4 -1
- package/node_modules/@mytools/fst-devtools/src/plugin-tools.js +1 -0
- package/node_modules/@mytools/fst-devtools/src/process-usage.js +62 -0
- package/node_modules/@mytools/fst-platform/README.md +5 -0
- package/node_modules/@mytools/fst-platform/package.json +1 -0
- package/node_modules/@mytools/fst-platform/src/managed-process.js +36 -0
- package/node_modules/@mytools/fst-platform/src/process.js +7 -2
- package/node_modules/@mytools/fst-plugin/README.md +4 -0
- package/node_modules/@mytools/fst-plugin/src/contracts.js +3 -0
- package/node_modules/@mytools/fst-plugin/src/host.js +38 -23
- package/node_modules/@mytools/fst-plugin/src/runtime.js +14 -8
- package/node_modules/@mytools/fst-workflow/src/engine.js +1 -1
- package/node_modules/@mytools/fst-workspace/README.md +4 -0
- package/node_modules/@mytools/fst-workspace/src/plugin-registry.js +1 -1
- package/node_modules/@mytools/fst-workspace/src/plugins.js +5 -3
- package/node_modules/@mytools/fst-workspace/src/workflows.js +27 -9
- package/node_modules/@mytools/sdk/README.md +10 -0
- package/node_modules/@mytools/sdk/inputs.js +53 -0
- package/node_modules/@mytools/sdk/package.json +2 -0
- package/node_modules/@mytools/sdk/reports.js +53 -0
- package/node_modules/@mytools/web/public/THIRD_PARTY_NOTICES.txt +1032 -1032
- package/package.json +1 -1
|
@@ -59,6 +59,120 @@ JSON 序列化可能丢弃对象中的 `undefined`、函数或 Symbol,并将
|
|
|
59
59
|
|
|
60
60
|
输入与配置校验继续应用 `default`;动作和交互命令的输出校验只验证实际返回值,不补默认值。旧插件若依靠输出 schema 自动生成 `ok` 等字段,必须在 `run` 返回值中明确赋值。诊断消息和 `details[].jsonPointer` 提供完整 JSON 指针(如 `/benchmark_config/model_name`),`schemaPath` 标明约束位置;`required` 放在顶层时只能报告顶层缺失,不会猜测作者原本想放在哪个子对象。
|
|
61
61
|
|
|
62
|
+
### 默认参数与开箱使用
|
|
63
|
+
|
|
64
|
+
- 常规选项存在适用的合理默认值时,作者必须在输入或配置 schema 的字段中声明 `default`,例如次数、输出格式、超时和功能开关。不要让用户反复填写这些常量。
|
|
65
|
+
- 只把确实无法预设的业务对象、地址、凭据或目标环境列为必填,并提供清楚的字段标题和说明。不可为了通过检查编造密码、目标路径或机器地址;也不要求每个字段都有默认值。
|
|
66
|
+
- `required` 与 `default` 可以同时声明:缺失时先补默认值,再校验。`false`、`0`、空字符串和显式 `null` 不视为缺失;它们仍须满足字段约束。
|
|
67
|
+
- 可选对象未传入时,不自动创建该对象或启用对应功能。只有确定需要默认启用整个对象时才给父对象声明 `default: {}`,随后才能补充其内部字段默认值。
|
|
68
|
+
- 普通调用和工作区配置依靠 schema `default`。`defaultConfig` 保持公开部署配置的语义,只用于生成运行包,不能代替普通安装后的 schema 默认值;严禁放入私有凭据。
|
|
69
|
+
- TUI 普通带参数的插件能力先展示当前参数(含默认值),提供“执行”“配置参数”“返回”;配置完成回到预览,执行前校验必填内容。预览按窗口宽度分为一至三列、最多四行,小窗口进一步收缩;仅在字段数量或内容确实显示不全时出现展开入口,可滚动阅读。无参数命令、完整快捷预设和显式参数文件保留直接调用。
|
|
70
|
+
- 此处编辑的是本次调用参数;跨次复用固定参数可通过 `/shortcuts` 保存。插件配置继续通过 `/plugin` 保存到工作区。
|
|
71
|
+
|
|
72
|
+
发布验收必须覆盖:最少必填输入可以运行、缺失参数确实需要用户提供、默认值符合字段约束、显式值覆盖默认值、可选能力不会因默认值意外启用。默认值是否适合业务由作者审查;宿主按 schema 验证实际输入,不凭参数数量警告用户。
|
|
73
|
+
|
|
74
|
+
### 能力发现与历史(0.6.0 起)
|
|
75
|
+
|
|
76
|
+
能力发现约定:作者注册的 `commands` 可通过非空斜杠前缀补全发现,空 `/` 的精简列表不代表全部能力。推荐用明确命名空间组织命令,例如 `envprobe.check`;命名空间唯一且无名称冲突时,`/envprobe` 可进入插件菜单。实际可执行命令名始终保留,从菜单执行后也会进入用户的命令历史。纯 `actions` 仍通过工作流和自定义快捷名称调用,不会擅自生成新的命令语法。
|
|
77
|
+
|
|
78
|
+
### 参数继承与来源(0.6.0 起)
|
|
79
|
+
|
|
80
|
+
固定 `default` 不能表达“跟随当前插件配置”。需要这种行为时,在输入对象的顶层字段增加 `x-fst` 声明,例如:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const config = {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: { timeoutMs: { type: 'integer', minimum: 1, default: 8000 } },
|
|
86
|
+
};
|
|
87
|
+
const input = {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
timeout: {
|
|
91
|
+
type: 'integer', title: '单项超时', minimum: 1,
|
|
92
|
+
'x-fst': { fromConfig: 'timeoutMs', unit: '毫秒' },
|
|
93
|
+
},
|
|
94
|
+
tools: {
|
|
95
|
+
type: 'array', title: '检查工具', items: { type: 'string' },
|
|
96
|
+
'x-fst': { unsetLabel: '全部工具' },
|
|
97
|
+
},
|
|
98
|
+
extraPaths: {
|
|
99
|
+
type: 'array', title: '额外目录', items: { type: 'string' },
|
|
100
|
+
'x-fst': { unsetLabel: '无额外目录' },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- `fromConfig` 是已声明的配置字段路径,例如 `network.timeoutMs`;宿主使用已校验、已应用配置默认值的实际配置。路径拼错、危险路径、格式不正确会在加载及 `plugin validate` 时报告。
|
|
107
|
+
- 取值顺序为:**显式调用输入 → 插件配置 → 输入字段 default → 保持省略**。`false`、`0`、空字符串、`null` 都是显式值,仍须通过输入约束。配置值不符合输入约束时执行失败,不静默退回固定默认值。
|
|
108
|
+
- `unsetLabel` 只是作者声明的省略含义,不注入业务值;插件仍负责实现“全部工具”等行为。`unit` 只用于展示,不改变类型或换算数值。
|
|
109
|
+
- TUI 展示“继承插件配置:8000 毫秒(timeoutMs)”“默认值”“本次输入”,编辑时可恢复继承。查看和编辑其他参数不会将继承值保存为固定输入;之后修改插件配置,下一次执行会使用新值。工作流绑定保存原始输入/引用,执行时解析继承。
|
|
110
|
+
- 当前只支持简单输入对象的顶层字段;嵌套对象内部、数组项、`$ref` 和条件/联合 schema 中的 `x-fst` 暂不支持,声明时明确报错。需要复杂结构时先保留原有 schema 与业务实现。
|
|
111
|
+
- 旧插件无需改动即可继续运行;没有声明的动态规则不会被推断。可选且未提供的字段会显示“未提供(插件未声明缺省行为)”,不能将其理解为插件没有内部默认值。上述协议从 0.6.0 起支持。
|
|
112
|
+
|
|
113
|
+
内核提供解析、来源展示和校验,插件负责声明真实规则及省略含义。开发验收应包含:修改配置后继承变化、显式值覆盖、恢复继承,以及显示值与实际收到的输入一致。应用接口 `app.previewInput('/命令名' 或 '插件ID:动作名', 原始输入)` 返回 `{value, fields}`;仅返回该输入引用到的配置值,不返回整份插件配置。运行记录中的输入仍为原始提交内容。
|
|
114
|
+
|
|
115
|
+
### 统一业务报告(0.6.0 起)
|
|
116
|
+
|
|
117
|
+
业务结论由插件生成,内核只校验和展示。SDK 的 `@mytools/sdk/reports` 提供 `createReport`、`reportSchema` 和 `inspectReport`。报告按摘要、提醒、建议、业务分组排列;提醒等级不会改变执行状态,建议不会自动执行。
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
import {createReport, reportSchema} from '@mytools/sdk/reports';
|
|
121
|
+
|
|
122
|
+
const output = {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {ok: {type: 'boolean'}, report: reportSchema},
|
|
125
|
+
required: ['ok', 'report'],
|
|
126
|
+
additionalProperties: false,
|
|
127
|
+
};
|
|
128
|
+
// 动作/命令执行函数返回此类对象,原有业务字段仍可供工作流引用。
|
|
129
|
+
const result = {
|
|
130
|
+
ok: false,
|
|
131
|
+
report: createReport({
|
|
132
|
+
title: '开发环境检查',
|
|
133
|
+
summary: '检查了两个工具,其中一个尚未安装。',
|
|
134
|
+
alerts: [{level: 'warning', message: '缺少构建工具。'}],
|
|
135
|
+
recommendations: ['确认本次任务需要该工具后,再手动安装。'],
|
|
136
|
+
sections: [{
|
|
137
|
+
title: '构建工具',
|
|
138
|
+
columns: ['name', 'found'],
|
|
139
|
+
titles: {name: '工具', found: '已安装'},
|
|
140
|
+
data: [{name: '工具甲', found: true}, {name: '工具乙', found: false}],
|
|
141
|
+
}],
|
|
142
|
+
}),
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
协议是普通 JSON:`kind: 'fst-report'`、`version: 1`、非空 `title` 必填,helper 自动填写前两项。可选 `summary` 是非空文本;`alerts` 是 `{level, message}` 数组,等级为 `info`、`warning`、`error`;`recommendations` 是非空文本数组。`sections` 内每组必填 `title`,可带相同的摘要/提醒/建议和任意 JSON `data`;`columns` 为唯一字段名数组,`titles` 为字段标题映射。v1 不支持递归分组,元数据不接受未知字段。纯文本中的换行保留,终端控制字符在展示时过滤。
|
|
147
|
+
|
|
148
|
+
新能力可直接返回报告并声明 `output: reportSchema`;已有能力推荐保留业务输出并增加 `report` 字段。长内容和未选列保留全文,窄屏退回字段阅读;主面板默认展示报告摘要、提醒、建议、分组和表格,无需先展开详情。报告整体缩进,结论和组标题再缩进一级,组内数据和表格再缩进一级。输入和实际执行指令始终可见;Ctrl+O 展开原始输出与诊断,运行详情中的 m 保留专用阅读。原始 JSON、工作流引用和落盘数据不变。声明 schema 或调用 helper 时会校验;读取损坏或未来版本报告时提示原因并回退字段展示。超出日志内存预算时明确标注摘要,并指向 `/logs` 的 `p` 完整落盘阅读,不将残缺预览误报为协议错误。只有显式 `kind` 标记才启用协议,旧文本、Markdown 和普通对象不需要改造。
|
|
149
|
+
|
|
150
|
+
### 结构化结果与表格阅读(0.6.0 起)
|
|
151
|
+
|
|
152
|
+
表格布局由内核负责,无需 LLM,也无需插件打印边框字符。插件应返回结构化业务数据,并在输出 schema 中提供准确字段标题。相同字段结构、至少包含两个标量字段的记录数组或字典可自动显示为表格;异构结构保留字段阅读。可选的 `x-fst-table` 指定希望优先阅读的列及顺序:
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
const output = {
|
|
156
|
+
type: 'array',
|
|
157
|
+
'x-fst-table': ['name', 'found', 'version'],
|
|
158
|
+
items: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
name: { type: 'string', title: '工具' },
|
|
162
|
+
found: { type: 'boolean', title: '已找到' },
|
|
163
|
+
version: { type: ['string', 'null'], title: '版本' },
|
|
164
|
+
details: { type: 'object', title: '详细信息' },
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
字典将同样的列提示放在字典 schema 上,行字段 schema 放在 `additionalProperties` 中。该提示是非强制阅读建议;无效格式退回通用推断,非标量字段仍按正文阅读。内核按中文显示宽度排版,窄窗口回退紧凑字段格式。自动推断时,重复的字典标识列合并、相同标量集中为共有字段;`false` 和 `0` 始终保留。空字段可折叠,原始 JSON 不变。
|
|
171
|
+
|
|
172
|
+
长单元格显示“见下文”,复杂值及装不下的字段在表后按条目补充全文。主面板、运行详情和 `m` 阅读随窗口宽度重排;`j` 仍可查看原始数据。表格改善对齐与横向比较,不保证所有窗口尺寸的总行数都更少。不要为了表格删减业务输出;旧插件也能获得通用表格布局,声明列提示可进一步改善阅读顺序。
|
|
173
|
+
|
|
174
|
+
TUI 负责通用结果阅读:紧凑输出合并同一对象内的短字段,折叠空字符串、空值和空容器,保留 `false`、`0`、非空错误及多行正文。详情按 `j` 查看未经折叠的原始 JSON,落盘数据不受影响。插件继续返回完整结构化结果,并通过 `print` 给出业务摘要;字段含义通过 schema `title` 描述,内核不根据插件名称猜测业务。直接插件命令也使用自己的输出 schema 展示标题。
|
|
175
|
+
|
|
62
176
|
无参数或无输出时,分别使用 `{type:'object', properties:{}, additionalProperties:false}`,动作返回 `{}`。JSON Schema 的布尔形式也受支持:`true` 接受任意值,`false` 拒绝所有值;为了生成字段表单与输出选择器,推荐明确声明对象和字段。缺省、`null`、数组、字符串和数值都不能代替 schema 声明。原有动作/子工作流的 `schemaVersion: 1` 格式保留;旧插件若要参与编排,需要补齐有效的 input/output 声明。正常返回且输出校验通过即执行成功,不要求返回额外的 `success:true`。
|
|
63
177
|
|
|
64
178
|
## 插件定义字段
|
|
@@ -172,13 +286,16 @@ fst plugin lint ./my-plugin --json
|
|
|
172
286
|
- `MISSING_TITLE` / `MISSING_DESCRIPTION`:动作、命令缺少非空标题或描述;动作别名命令可复用目标动作元信息。递归检查 input/output 的字段标题。
|
|
173
287
|
- `BOOLEAN_SCHEMA`:布尔 schema 合法,只提示字段级表单信息不足,不判定契约错误。
|
|
174
288
|
- `CONSOLE_LOG`:在可达 JavaScript 中识别直接 `console.log` 调用,包括顶层及辅助函数;引导动作和命令使用 `log` / `print`。注释和字符串不作为调用。
|
|
289
|
+
- `UNMANAGED_PROCESS`:定位能追溯到 `child_process`、`execa`、`cross-spawn`、`shelljs.exec` 或平台底层 `runProcess` 的直接调用,提示迁移到上下文 `ctx.runProcess`。覆盖常见 ESM 别名、CJS require、createRequire 和字面量动态 import;仅导入未调用不告警。它是迁移警告,不拒绝旧插件,不代表该代码实际执行过。动态计算、复杂别名、依赖内部及 Python/Shell 内部的执行不保证覆盖。
|
|
175
290
|
- `UNDECLARED_DEPENDENCY`:检查字面量 import、export-from、动态 import、CommonJS require,以及 ESM 中由 `node:module` 的 `createRequire(import.meta.url)` 建立的加载器。认可 package.json 的 dependencies、optionalDependencies、peerDependencies,devDependencies 不替代运行依赖;跳过 Node 内置模块。
|
|
176
291
|
|
|
177
292
|
源码扫描从 `package.json.main`(默认 index.js)沿本地依赖访问 `.js/.mjs/.cjs`,不执行扫描到的函数、不遍历无关测试文件、不跟随到包外或 node_modules。源码位置含行列号。特殊模块映射、解析失败或无法访问的本地文件给出检查不完整的警告;`.js` 按插件 ESM 契约解析。保守跳过文件内有局部重名或重赋值的 console/require,可能漏报其他作用域里的调用;动态拼接依赖和函数别名不保证识别。它不是完整依赖分析或安全扫描。
|
|
178
293
|
|
|
179
294
|
不提供 `--fix`;不会自动补平台、空标题或 additionalProperties:false,不猜测硬件关键词,不检查 run 的真实返回值。开放对象是合法契约,自动封闭可能破坏业务输入。
|
|
180
295
|
|
|
181
|
-
`link`
|
|
296
|
+
`link` 将源码绝对路径保存到插件库(普通 CLI 默认全局登记),不复制源码,不安装源码目录的 npm 依赖;需要依赖时先在插件目录自行安装。可用 `fst --watch` 在业务空闲后自动重启开发进程并重新加载完整模块图;未启用 watch 时手工重启。源码目录迁移后需要重新 link。正常 `add` 同名包可替换开发引用,`remove` 解除引用,不删除源码。分发收集源码的发布文件和生产依赖快照,不把开发路径链接带给最终用户。
|
|
297
|
+
|
|
298
|
+
已安装或链接的插件出现可捕获的导入、SDK 或单插件契约错误时,插件列表显示“不可用”、来源和原因;依赖它的插件也不可用,无关流程仍可执行。可进入插件管理移除故障插件,多个故障可逐个处理。修复源码后重新启动,或由 `--watch` 重新加载。新安装和链接仍严格检查;项目预置插件、明确的跨插件图冲突、运行时初始化错误,以及模块顶层退出进程或无限循环不在此恢复范围内。
|
|
182
299
|
|
|
183
300
|
### 安装批次清理与恢复
|
|
184
301
|
|
|
@@ -218,12 +335,14 @@ fst plugin gc --apply
|
|
|
218
335
|
|
|
219
336
|
可用 `fst network show` 查看当前规则、`fst network set 规则.json` 保存;TUI 的 `/config` 和 Web 设置页也可编辑。TUI/Web 会列出已安装插件与动作最终采用的模式,并提示指向已删除插件或动作的规则。保存代理、插件或日志设置前,会在 `.flow/backups/` 备份原 `flow.project.json`,界面和命令会显示具体路径;需要恢复时先关闭项目,再将备份复制回 `flow.project.json`。其他实例已修改代理规则时,旧实例会拒绝覆盖,重新读取后再保存。代理地址可写 `httpUrl`/`httpsUrl`,含账号密码的地址只能用 `httpEnv`/`httpsEnv` 引用环境变量,避免把凭据写入项目文件。可用 `noProxy` 或 `noProxyEnv` 排除地址。代理按每次动作调用解析;分发包只携带所选插件使用的规则和配置引用,不携带环境变量的值,目标机需自行提供。离线场景可设 `direct`。
|
|
220
337
|
|
|
221
|
-
覆盖保存已有工作流时,原始 YAML 会先写入 `.flow/workflow-backups/工作流ID-编号.yaml`;若要恢复,先停止项目并确认目标文件,再将备份复制回 `workflows/工作流ID.yaml
|
|
338
|
+
覆盖保存已有工作流时,原始 YAML 会先写入 `.flow/workflow-backups/工作流ID-编号.yaml`;若要恢复,先停止项目并确认目标文件,再将备份复制回 `workflows/工作流ID.yaml`。选择“备份后删除”会把原文件移入 `.flow/deleted-workflows/`,可按同样方式复制回去;明确选择“直接删除”不会生成这份备份,不能据此恢复。插件状态切换保留 `.flow/plugins.previous.json`;若更新与恢复都失败,当前宿主停止接受新的运行,重启前应先核对磁盘配置和插件状态。
|
|
222
339
|
|
|
223
340
|
动作通过 `ctx.http.fetch(url, options)` 发起 HTTP 请求,或通过 `ctx.runProcess(file,args,options)` 启动子进程;两者会应用本次动作的规则。插件直接使用其他网络库、直接创建子进程或自行覆盖代理环境时,应自行接入规则;宿主不拦截其网络调用。插件命令和服务使用插件级规则。
|
|
224
341
|
|
|
225
342
|
### 权限声明与执行前检查
|
|
226
343
|
|
|
344
|
+
统一执行接口不会增加系统权限。权限声明与预检仍在动作主体执行前检查,已确认不足时直接报错,不自动调用 sudo 或请求管理员密码。没有声明的旧插件仍按兼容规则运行;规范要求作者逐步补齐声明。
|
|
345
|
+
|
|
227
346
|
插件可以为所有动作声明默认权限,也可以为某个动作覆盖:
|
|
228
347
|
|
|
229
348
|
```js
|
|
@@ -276,7 +395,7 @@ Linux 支持 `user/root/resource`,Windows 支持 `user/administrator/resource`
|
|
|
276
395
|
|
|
277
396
|
`runProcess` 新增 `encoding`,默认 `utf8`,也可用 `utf16le`、`gbk` 等当前 Node TextDecoder 支持的编码;实时日志和返回文本采用同一编码,并正确处理跨块字符。示例:`runProcess('wsl',['-l','-v'],{encoding:'utf16le'})`,具体编码应以目标命令版本为准。Windows PowerShell 的编码和 CLIXML 是不同问题,宿主不自动猜编码或解析 CLIXML。输出上限仍按每个流的原始字节计量,默认 4 MiB;超限回收进程树并抛 `OUTPUT_LIMIT`,不返回伪装完整的截断结果。
|
|
278
397
|
|
|
279
|
-
|
|
398
|
+
主面板默认展示执行入口、步骤状态、输入和受管进程的实际程序、参数、目录、原始脚本。`fst-report` 业务报告及 print 用户反馈缩进展示;工作流步骤的报告也默认显示,最终输出转发同一报告时仅展示一次。Ctrl+O 展开或收起原始返回、内部日志和调用栈;stdout/stderr(包括感叹号透传)按原始返回处理,即使带有 `audience: 'user'` 也默认折叠。失败摘要始终直接显示。希望用户直接看到的进度或其他结果,显式使用 `await print('报告已保存', {file})`。`print` 沿用日志通道,以不带 stream 的 `type: 'log', audience: 'user'` 持久化,工作流中自动带上步骤归属。动作和交互命令都支持,不应直接用 `console.log` 向 TUI 打印。
|
|
280
399
|
|
|
281
400
|
若 Python 程序的部分输出需要直接展示,可使用 `runProcess(file, args, {onOutput: ({stream,message}) => stream === 'stdout' ? print(message) : undefined})`。回调按数据块触发;作者可先缓存成完整行再选择性 print。完整进程输出仍自动保存在诊断日志,print 的用户提示另外保留。写入 stderr 本身不等同执行失败,动作仍须按退出码决定抛错。
|
|
282
401
|
|
|
@@ -284,7 +403,41 @@ Linux 支持 `user/root/resource`,Windows 支持 `user/administrator/resource`
|
|
|
284
403
|
|
|
285
404
|
`runProcess` 默认无 Shell,参数用数组。支持 `cwd`、`env`、`signal`、`timeout`(默认 30 秒)、`maxBuffer`(stdout/stderr 各默认 4 MiB)和可选的 `onOutput({stream,message})`。普通非零退出返回 `{exitCode,stdout,stderr}`,动作应自行决定是否抛出业务错误;缺失程序、超时、取消、输出超限抛错,并等待进程树回收。不要把密码或令牌写入日志。
|
|
286
405
|
|
|
287
|
-
|
|
406
|
+
#### 统一执行入口与并行边界
|
|
407
|
+
|
|
408
|
+
动作和交互命令通过上下文 `ctx.runProcess` 执行外部程序,不直接使用 `node:child_process`、execa、cross-spawn 或 shelljs.exec;也不直接导入平台底层 runProcess。`!` 命令与插件共用内核的进程包装,统一日志、取消和回调顺序。每次调用的启动记录与 stdout/stderr 带同一个 `processId`,主面板指令和详细回显显示对应短编号;旧记录没有编号时仍可阅读。
|
|
409
|
+
|
|
410
|
+
当前插件本身是 JavaScript,在宿主 Node.js 中完成的纯计算不需要新进程。Python 或其他程序仍需要启动解释器:无论解释器来自系统还是随离线包携带,均使用同一接口,例如:
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
async function run(input, ctx) {
|
|
414
|
+
// environments 负责定位系统或随包解释器,runProcess 负责执行管理。
|
|
415
|
+
const result = await ctx.runProcess(ctx.environments.python.executable,
|
|
416
|
+
['check.py'], {cwd: ctx.resourceDir, signal: ctx.signal});
|
|
417
|
+
if (result.exitCode !== 0) throw new Error(`检查失败,退出码 ${result.exitCode}`);
|
|
418
|
+
return {stdout: result.stdout};
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
统一入口不增加全局串行队列。工作流继续用 `parallel.concurrency` 控制步骤并发;插件在单个动作里使用 Promise.all 启动多个程序时,应自行设定合理并发并 await 所有任务。每次调用的取消信号独立;运行或并行组主动取消时,仍按既有规则取消所属任务。当前没有全局进程数量限制或资源锁;同时修改相同系统资源时,插件应自行协调。
|
|
423
|
+
|
|
424
|
+
动作结束前必须等待所启动的程序,不使用 detached、后台 Shell 等方式脱离管理。长期后台程序的专门执行接口尚未提供。这里是开发契约与统一管理接口,不是安全沙箱:插件仍与内核同进程,静态检查不能强制拦截所有绕行,也不能逐条审核解释器内部命令。独立提权和不可信插件隔离不在本轮范围。
|
|
425
|
+
|
|
426
|
+
受管 `ctx.runProcess` 在启动前自动记录实际程序、参数数组和执行目录,Shell 另记录原始指令;自定义 `onStart({file,args,cwd,script?})` 在此记录后调用。启动记录写入失败或此时取消会阻止启动子进程。TUI 主面板默认展示这些执行指令;插件实际来源目录另随调用记录,展开诊断后可以阅读。通过其他方式执行的程序或任意文件访问不在该记录范围内;参数若含凭据,也会进入本地执行日志,作者应通过环境变量或其他受控渠道传递。
|
|
427
|
+
|
|
428
|
+
敏感参数必须显式设置日志策略,实际执行参数与自动记录的展示参数分开:
|
|
429
|
+
|
|
430
|
+
```js
|
|
431
|
+
await ctx.runProcess('tool', ['--endpoint', endpoint, '--token', ctx.config.token], {
|
|
432
|
+
logPolicy: {redactArgs: [3]}, // 从 0 起算,将第 4 个参数显示为 [已隐藏]
|
|
433
|
+
});
|
|
434
|
+
// 完全隐藏参数及原始脚本,仍记录程序、目录和调用编号:
|
|
435
|
+
await ctx.runProcess('tool', privateArgs, {logPolicy: false});
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`logPolicy` 对象还支持 `omitArgs: true` 和 `omitScript: true`。隐藏任意参数时会同时隐藏原始脚本,避免同一密钥从脚本文本泄露。下标越界或策略字段无效会在执行前报错,不包含策略原值。普通调用未声明策略时仍记录参数,不自动猜测业务字段是否敏感;自定义 onStart 回调仍接收真实参数,作者不得自行把它们重新写入日志。系统拒绝启动的自动错误不再附带 Execa 的完整命令行。
|
|
439
|
+
|
|
440
|
+
这只保护宿主自动记录的启动信息,不会清理插件主动 log/print、stdout/stderr、业务输入输出、自定义错误或已有日志。已有历史记录保持原样,修复不会自动清除其中的密钥;需要时由用户按日志管理流程处理。
|
|
288
441
|
|
|
289
442
|
工作流、直接动作和插件命令在执行前统一追加 `run.input`,保存本次提交的完整参数;界面及元数据仍仅持有有界预览。较大参数通过 `/logs` 的完整日志分页读取,配置表单不属于运行参数,不进入此记录。
|
|
290
443
|
|
|
@@ -302,7 +455,7 @@ Promise.race 只能让调用方停止等待,不能中断正在执行的操作
|
|
|
302
455
|
|
|
303
456
|
当前源码已提供 `ctx.fs.stat/readDirectory` 隔离只读探测,独立进程执行 I/O,超时/取消返回结构化状态,未回收进程继续占并发预算。它不替代插件直接使用的任意文件 I/O,也不保证内核 I/O 立即可中断。`runProcess` 的可执行文件查找仍可能访问 PATH 中的挂载目录,当前超时从进程启动后开始,不构成文件访问隔离。请将工作区、插件与日志目录放在可靠的本地存储上;接口见[完整能力指南](kernel-generation.md)。
|
|
304
457
|
|
|
305
|
-
###
|
|
458
|
+
### 查看日志体积
|
|
306
459
|
|
|
307
460
|
TUI `/logs` 与 `/config` 的“查看日志体积”按需统计当前日志根目录下 UUID 运行目录中的 run.md、events.jsonl、result.json、run.meta.json 文件长度,包括尚未结束的运行。显示字节数、文件数、当前项目的保留次数;读取失败时明确标注统计不完整。
|
|
308
461
|
|
|
@@ -322,7 +475,7 @@ TUI `/logs` 与 `/config` 的“查看日志体积”按需统计当前日志根
|
|
|
322
475
|
|
|
323
476
|
每次通过应用运行工作流或插件命令,都在 `.flow/runs/<runId>/` 写入 `run.md`、`events.jsonl` 与 `result.json`(指定 `--output` 时使用该目录;便携版写入可写工作区)。
|
|
324
477
|
|
|
325
|
-
- **运行中直接观察**:TUI
|
|
478
|
+
- **运行中直接观察**:TUI 主面板逐步更新状态,默认展示输入和实际指令,Ctrl+O 展开原始输出和诊断日志;`/logs` 可选择某次运行进入详细面板。Web 使用“查看运行”。按调用路径区分同一动作的不同调用。
|
|
326
479
|
- **历史日志直接阅读**:TUI `/logs` 选择记录后切换总览、输入输出和日志;方向键或鼠标滚轮滚动,PageUp/PageDown 翻页,Home/End 跳转,Esc 或 q 返回,Enter 查看步骤。重启后读取磁盘历史;run.md 也可用外部 Markdown 阅读器打开。
|
|
327
480
|
- **导出阅读内容**:`run.md` 按时间顺序展示步骤、作者日志、stdout/stderr、执行结果、失败原因、错误详情和调用栈,结束后附最终状态。它在执行过程中逐条追加,也可使用外部 Markdown 阅读器查看。调用栈保留原始换行。
|
|
328
481
|
|
|
@@ -331,9 +484,9 @@ TUI `/logs` 与 `/config` 的“查看日志体积”按需统计当前日志根
|
|
|
331
484
|
- 例如调用不存在的 Python 时,运行失败,message 含具体插件及“未找到程序”,错误链包含 `ENOENT` 和 `details.executable`。无需先运行环境检查插件才能捕获此错误。
|
|
332
485
|
- Python 脚本异常或依赖缺失通常通过非零退出码及 stderr 报告。作者必须检查返回值并抛错,例如 `throw Object.assign(new Error('Python 脚本失败'), {code:'PYTHON_FAILED', details:{exitCode:r.exitCode, stderr:r.stderr}})`;不要捕获后返回成功。此写法无需内部 SDK。
|
|
333
486
|
- 插件可 `await log('正在检查 Python', {phase:'python-check'})` 补充业务阶段。插件自身的 `console.log`、自行启动的子进程和未 await 的后台任务不会自动进入这套日志;动作上下文 `runProcess` 的 stdout/stderr 会实时写入。
|
|
334
|
-
-
|
|
487
|
+
- 核心会记录执行输入和受管进程的程序、参数、目录、原始脚本;敏感命令参数必须用上述 logPolicy 显式隐藏。核心不主动记录完整环境变量或插件配置,但它们被放入参数、业务输入输出、错误或日志时仍可能被保存。stderr、调用栈、作者 log/print 和返回结果应由作者按业务脱敏;已有日志不会自动清除。
|
|
335
488
|
|
|
336
|
-
定位顺序:按 runId 打开 run.md → 查看失败插件及步骤 → 查看原始错误码与调用栈 → 结合之前的日志和成功步骤输出复现。JSON 文件是给程序查询与界面使用的结构化记录,不要求用户通过 JSON
|
|
489
|
+
定位顺序:按 runId 打开 run.md → 查看失败插件及步骤 → 查看原始错误码与调用栈 → 结合之前的日志和成功步骤输出复现。JSON 文件是给程序查询与界面使用的结构化记录,不要求用户通过 JSON 阅读日志。此处是运行诊断;插件加载/初始化失败发生在运行创建之前,可恢复的已安装插件错误在插件列表中显示,其他启动错误仍由启动诊断报告,不会凭空产生工作流运行目录。
|
|
337
490
|
|
|
338
491
|
### 日志保留与清理
|
|
339
492
|
|
package/manual/docs/releases.md
CHANGED
|
@@ -11,7 +11,22 @@
|
|
|
11
11
|
- 一批明显的新能力或兼容性变化,再考虑增加中间一位,例如 `0.5.x → 0.6.0`,并在发布说明中解释升级影响。
|
|
12
12
|
- 仅文档、测试或开发流程调整,且不需要用户升级已安装产品时,不单独增加产品版本或发布 npm。
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
已发布版本不可覆盖。`0.5.2` 曾按用户指定目标集中发布;本轮参数继承、报告协议与受管执行等新能力合并为 `0.6.0`,后续小修复继续使用补丁版本。
|
|
15
|
+
|
|
16
|
+
## 0.6.0 参数预览、业务报告与执行恢复
|
|
17
|
+
|
|
18
|
+
- 选择插件能力后展示当前有效参数,支持直接执行或配置后返回预览;参数较多时分列展示,仅在内容确实省略时提供完整参数入口。
|
|
19
|
+
- 参数规则可声明继承插件配置及留空含义,预览和执行使用同一解析规则并显示来源,不用固定默认值覆盖用户配置。旧插件未声明的执行内部默认行为不会被内核猜测。
|
|
20
|
+
- 新增业务报告协议,支持摘要、告警、建议、分组、表格和 Markdown;主面板默认展示业务报告并缩进,详情和格式化阅读保留完整内容。旧插件使用通用紧凑排版。
|
|
21
|
+
- 输入和实际执行指令默认可见,Ctrl+O 折叠的是原始输出和技术诊断;插件菜单执行记录实际入口,输入插件名称前缀可补全插件及动作。
|
|
22
|
+
- 受管进程入口统一关联日志、取消与进程编号,保持并行能力;开发质量检查提示常见绕行执行。它不构成插件沙箱,也不新增自动提权。
|
|
23
|
+
- 敏感进程参数可用 `logPolicy` 显式脱敏或禁止记录,系统及执行库启动错误不再重新拼接完整参数。默认仍记录普通参数;插件作者负责标记敏感项、业务输出脱敏,已有日志不会自动清除。
|
|
24
|
+
- 修复 #7/#8:已安装插件的可捕获加载或契约错误降级为不可用,无关流程仍可运行;坏工作流文件显示路径和解析错误,不阻断正常列表。卸载与删除继续保守检查引用。
|
|
25
|
+
- 修复 #9:文件补全后台隔离探测、超时、取消和有界缓存;预算由整个 TUI 会话持有,反复进入输入框不会遗忘未回收进程。修复 #10:执行和打包前检查根及子流程输入 Schema,避免先执行前置动作才发现无效声明。
|
|
26
|
+
|
|
27
|
+
TUI 是主要交互入口,CLI 保留必要的直接运行、自动化、打包与故障恢复能力,不要求每项 TUI 操作都提供另一套 CLI。工作流格式和 SDK_VERSION 保持兼容;新版可选协议需要插件声明才能获得完整效果,本轮未修改插件源码。
|
|
28
|
+
|
|
29
|
+
已获用户本轮推送和发布授权。#11 已做有限的真实插件本机验证;跨机离线业务验收、首次用户体验、真实 NFS/SMB 故障验收仍待完成,不能由自动化门禁代替。正式发布结果见[验收记录](verification.md)。
|
|
15
30
|
|
|
16
31
|
## 0.5.3 全局插件、可读详情与会话恢复
|
|
17
32
|
|
|
@@ -32,6 +47,10 @@
|
|
|
32
47
|
|
|
33
48
|
验证与正式发布结果见[验收记录](verification.md)。
|
|
34
49
|
|
|
50
|
+
**2026-10-13 发布完成:** [正式发布流程](https://github.com/yanglaofish/Fishtools/actions/runs/38402983167)全部成功。
|
|
51
|
+
Windows / Ubuntu 在四个 Node 版本执行的八组完整验收各 586 项、零失败;两平台便携包与断网只读容器通过。
|
|
52
|
+
npm `latest` 为 **0.5.3**,公开包与正式实测归档逐字节相同;[Release](https://github.com/yanglaofish/Fishtools/releases/tag/v0.5.3)四个附件及服务端校验值核对完成,具体下载验收范围记入验收记录。
|
|
53
|
+
|
|
35
54
|
## 0.5.2 命令注册与快捷名称
|
|
36
55
|
|
|
37
56
|
- TUI 主输入框支持 `!` 后的系统命令,原样保留多行、引号、管道和重定向,纳入运行记录、取消、超时与完整日志。
|
|
@@ -126,20 +145,20 @@ npm `latest` 为 0.5.1;独立下载包校验值与注册表及 [Release](https
|
|
|
126
145
|
|
|
127
146
|
## 准备下一个版本
|
|
128
147
|
|
|
129
|
-
在开发分支完成修改、更新说明并验证后,按上面的约定选择版本号。以下为本轮 `0.
|
|
148
|
+
在开发分支完成修改、更新说明并验证后,按上面的约定选择版本号。以下为本轮 `0.6.0` 的准备命令:
|
|
130
149
|
|
|
131
150
|
```sh
|
|
132
|
-
npm version 0.
|
|
151
|
+
npm version 0.6.0 --no-git-tag-version
|
|
133
152
|
git add package.json package-lock.json packages/core/src/version.js
|
|
134
|
-
git commit -m "chore: prepare 0.
|
|
153
|
+
git commit -m "chore: prepare 0.6.0"
|
|
135
154
|
```
|
|
136
155
|
|
|
137
156
|
`npm version` 自动同步 CLI 的产品版本。合入 main 后,对经过审查的提交执行:
|
|
138
157
|
|
|
139
158
|
```sh
|
|
140
|
-
git tag -a v0.
|
|
159
|
+
git tag -a v0.6.0 -m "Fishtools 0.6.0"
|
|
141
160
|
git push origin main
|
|
142
|
-
git push origin v0.
|
|
161
|
+
git push origin v0.6.0
|
|
143
162
|
```
|
|
144
163
|
|
|
145
164
|
不要改动已经发布的 tag。主版本号尚为 0,升级前查看变更说明并备份工作区。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0.5.3:可读执行详情与完整会话
|
|
2
2
|
|
|
3
|
-
用户本轮确认需要:步骤附近的展开入口、能看懂的指令/文件/结果、主面板计时、恢复完整 TUI 会话、TUI 内平台提示,以及 `!ipconfig`
|
|
3
|
+
用户本轮确认需要:步骤附近的展开入口、能看懂的指令/文件/结果、主面板计时、恢复完整 TUI 会话、TUI 内平台提示,以及 `!ipconfig` 中文输出修复。本轮集中合入 0.5.3,现已发布,见[验收记录](../../verification.md)。
|
|
4
4
|
|
|
5
5
|
## 设计
|
|
6
6
|
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
- [x] logs 增加会话存储与分页,覆盖重开、竞争、归属、坏记录、链接及预算;TUI 接入保存恢复并验证输入、反馈、结果。
|
|
19
19
|
- [x] 实际执行 Windows `ipconfig` 验证中文,同时覆盖 Shell 自身输出及已有命令回归。
|
|
20
20
|
- [x] 进行独立 Standards / Spec 增量审查,修复有效发现并验证;两轴终轮均无剩余阻塞问题。最终归档完整验收与发布结果记入验收记录。
|
|
21
|
-
- [
|
|
21
|
+
- [x] 推送后等待两平台及 Node 支持范围的正式发布验收,核对 npm 和四个 Release 附件。公开 npm 与实测包逐字节一致;便携包由 CI 实际运行并核对 GitHub 服务端摘要,本机未完成的下载单独记录。
|
|
@@ -53,3 +53,5 @@
|
|
|
53
53
|
- 新增真实替换文件、同一原文件链接及不同设备/文件 ID 拒绝探针,链接创建无权限时明确跳过。Windows Node 24.0、24.11.1、24.15.0 定向回归各 54 项:52 通过、0 失败、2 项链接权限跳过;真实 CI 文件系统还需定向复跑确认。
|
|
54
54
|
- Windows / Node 24.11.1 的 Web 测试过早结束轮询,任务仍为 running。仅调整为 10 秒明确期限,原成功状态、输出、鉴权及路径断言保留;不修改产品执行逻辑。CI 新增 Windows 早期 Node 定向门禁,原全量矩阵保留。
|
|
55
55
|
- Standards 独立复审:硬问题 0、判断性气味 0;Spec 缺失需求、范围扩张、错误实现均为 0。Spec 的 16 个只读边界探针通过,包含实际诊断值、异设备高位、替换、链接及异常清理。实际失败文件系统定向 CI 和完整发行矩阵仍作为发布门禁。
|
|
56
|
+
- 后续 CI 环境变量移至步骤级的增量独立复核:两轴均无新增问题。最终主分支六组检查通过,Windows 24.0/24.11 专项各 54/54;Windows 24.0 真实路径设备编号仍为 0,与主句柄完整身份补证成功。最终发行八组各 586 项、零失败,便携包与断网只读容器通过;详见[验收记录](../../verification.md)。
|
|
57
|
+
- 0.5.3 已完成正式发布;公开 npm 包与上述实测归档逐字节一致,Release 四个附件齐全。npm 后台处理超时后仅续跑发布任务完成同步,发行源提交保持 `047a202fb92de100104f689014bb831283e1f00b`。便携包本机下载未完成,采用 CI 运行验收和 GitHub 实际文件摘要核对,具体方法如实记入验收记录。
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 0.6.0 发布复核
|
|
2
|
+
|
|
3
|
+
基线 `v0.5.3`,范围为之后的源码与发布前修正。需求依据为 `docs/plans/` 的插件默认参数、有效输入与表格、能力发现与历史、业务报告、受管执行及 #6–#11 处理记录。用户授权推送和发布;保留必要 CLI、以 TUI 为主,不新增内置剪贴板、不修改插件源码、不提交用户业务结果。
|
|
4
|
+
|
|
5
|
+
## Standards
|
|
6
|
+
|
|
7
|
+
发布复核发现两项文档问题:参数继承仍标本地未发布;删除工作流错误承诺一律备份。已分别标为 0.6.0 起及明确“备份后删除/直接删除”的恢复区别。模块入口、依赖和代码职责未发现发布阻断。修正后复核遗留 0 项。
|
|
8
|
+
|
|
9
|
+
## Spec
|
|
10
|
+
|
|
11
|
+
用户提供的 #9 复核先完成真实失败回归:单个输入框关闭会遗忘未回收进程预算。现由整个终端共享探测器,provider 仅取消自己的请求;终端退出后检查有界关闭报告,终端状态恢复后才报未回收错误。
|
|
12
|
+
|
|
13
|
+
整体复核补充两项 P2:已知故障插件版本不满足健康消费者要求时仍须拒绝;运行详情输入不能把 JSON 形态字符串解码成布尔或对象,且不能丢弃显式 null。均经历新增失败回归、修正、通过与独立复核。输出继续使用可读化展示。
|
|
14
|
+
|
|
15
|
+
修正后 Spec 可行动发现 0 项,遗留阻断 0 项,范围扩张 0 项。#6 仍需插件显式声明敏感参数,#11 现场验收待办不冒充已完成。
|
|
16
|
+
|
|
17
|
+
## 验证职责
|
|
18
|
+
|
|
19
|
+
审查与全量测试独立进行。完整测试、归档安装及远端多平台发布门禁的实际结果统一记录在 `docs/verification.md`。本次审查不等于已完成跨机真实插件业务验收。
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# 验收记录
|
|
2
2
|
|
|
3
|
+
## 0.6.0 参数、报告与恢复(2026-10-14)
|
|
4
|
+
|
|
5
|
+
- 发布范围为 `v0.5.3` 之后的本地演进,开发分支 `codex/plugin-defaults`。用户已明确授权修复、验证后推送并发布;本次包含参数继承、报告协议及受管执行等新能力,采用 0.6.0,后续小修继续使用补丁号。
|
|
6
|
+
- 独立 Standards / Spec 发布审查发现文档承诺不准确、故障插件已知版本检查遗漏、运行详情输入字符串及 null 展示错误,均已修正并复核,两个维度各 0 项遗留问题。完整记录见[发布复核](superpowers/reviews/2026-10-14-0.6.0-release-review.md)。
|
|
7
|
+
- 针对用户提供的 #9 后续 P2,使用真实阻塞子进程并模拟终止失败,确认旧输入框关闭会遗忘预算;改为整个终端共用一个探测器,未退出进程持续占预算。覆盖多次进入和取消、退出后重新取得名额、默认非注入路径共用探测器及最终关闭报告。相关终端及文件探测 203 项通过。
|
|
8
|
+
- 首次发布全量遇到 `live-output` CLI 用例共享全局插件库而争锁;该用例改为独立临时 `FISHTOOLS_HOME`,保留退出码及结果断言,不放宽生产锁。输入详情和相关运行回归 38 项通过,随后显式 null 补充回归 18 项通过。
|
|
9
|
+
- `npm run dist:npm` 构建成功,本地归档为 `dist/yanglaofish-fishtools-0.6.0.tgz`,9,913,943 字节,284 个生产包。构建时第三方 `use client` 指令提示不影响完成;未修改插件源码。
|
|
10
|
+
- TUI 为主交互入口,保留必要 CLI;复制沿用终端原生能力,不新增内置剪贴板功能。#11 的真实插件本机闭环已有记录,跨机业务离线验收、首次用户体验及真实 NFS/SMB 故障场景仍待现场验证。发布流水线的容器与便携包测试不能替代这些验收。
|
|
11
|
+
- 最终完整测试使用上述实际 npm 归档:694 项,691 通过,0 失败,3 项既有 Windows 条件跳过,约 202 秒;日志为 `.cache/release-0.6.0-final.log`。`git diff --check` 与 `node scripts/release-info.mjs v0.6.0` 通过。远端发布结果完成后补充,不提前宣称 npm 已发布。用户 `flow-results/` 保持未跟踪,不纳入提交或分发。
|
|
12
|
+
|
|
3
13
|
## 0.5.3 全局插件、结果保护与准确提示(2026-10-13)
|
|
4
14
|
|
|
5
15
|
- 分支 `codex/issue-4-node-compat`,固定基线 `46a897450e122054dcfab882de5be061b311570f`;读取 issue #4 正文、评论及截图和 issue #5 正文及截图,采用用户确认的普通插件全局安装范围。
|
|
@@ -22,6 +32,13 @@
|
|
|
22
32
|
- 同次 Windows / Node 24.11.1 唯一失败为 Web 测试过早断言仍运行中的任务。只把约 600 毫秒的固定轮询改为 10 秒内等待真实结束;成功状态、输出、鉴权和路由断言保留。
|
|
23
33
|
- 上述增量包含真实路径替换拒绝和链接拒绝探针。Windows 本地 Node 24.0、24.11.1、24.15.0 各执行同一组定向回归 **54 项:52 通过、0 失败、2 项文件链接权限跳过**。新增 Windows 24.0/24.11 定向 CI,保留原全量矩阵,真实失败文件系统及最终发行归档复验待完成。
|
|
24
34
|
- 新增定向 CI 首次提交因 job 级 `env` 无法使用 `runner.temp` 而未启动测试;按 GitHub 官方上下文范围把隔离目录变量移至测试步骤级,不改变测试内容和原全量门禁,重新执行远端验收。
|
|
35
|
+
- [最终主分支 CI](https://github.com/yanglaofish/Fishtools/actions/runs/38402152986)六组检查全部通过。Windows / Node 24.0、24.11 定向回归各 **54/54**,零失败、零跳过;24.0 实际路径设备编号仍为 `0`,句柄仍为 `742408122`,已覆盖原失败条件。
|
|
36
|
+
- [最终正式发行验收](https://github.com/yanglaofish/Fishtools/actions/runs/38402983167)以源提交 `047a202fb92de100104f689014bb831283e1f00b` 重建同一 npm 归档。Windows、Ubuntu 分别在 Node 22.22.2、24.0.0、24.11.1、24.15.0 执行完整测试,八组均为 **586 项、0 失败**;每组 Windows 582 通过、4 跳过,Ubuntu 581 通过、5 跳过。两平台便携包、无 Node 断网只读 Linux 容器也通过。
|
|
37
|
+
- 正式 npm 候选归档下载后 **9,855,227 字节**,SHA-256 `836bb95161f3ccab637d1f78c671312b4f9577b01fcfffa3fb930bf9177a3593`。用此归档中的真实 TUI 处理路径,在本机 Node 24.11.1 再次执行 `!ipconfig` 与 `!ipconfig /all`:均成功显示“Windows IP 配置”,替换字符 0,界面正文与同次完整 stdout 日志一致。公开 npm、Release 附件和最终校验结果完成后追加。
|
|
38
|
+
- 最终流程的首次发布步骤中,npm 已接受 `@yanglaofish/fishtools@0.5.3` 上传并提示后台处理;五分钟后仍未对外可见,脚本按既有门禁退出,尚未创建 Release。保留该发行 tag 和原验收归档,待注册表可见且完整性一致后仅续跑发布步骤。
|
|
39
|
+
- npm 对外可见且完整性与验收归档一致后,仅续跑发布任务;最终[发布流程](https://github.com/yanglaofish/Fishtools/actions/runs/38402983167)第二次执行成功,没有重跑已通过的测试或再次上传 npm。`latest` 为 **0.5.3**,公开 npm tarball 独立下载为 **9,855,227 字节**,SHA-512 与注册表一致,且与正式验收归档逐字节相同。
|
|
40
|
+
- [GitHub Release](https://github.com/yanglaofish/Fishtools/releases/tag/v0.5.3)已公开,四个附件齐全。npm 归档和 `SHA256SUMS` 独立下载校验;两套便携包本机下载因网络缓慢未完成,未将其计为完整下载通过。便携包使用已通过的双平台运行验收,以及 GitHub 实际上传文件的服务端 SHA-256 与下载校验文件逐项匹配来核验:Windows **52,711,988 字节**,SHA-256 `94dae599c372b54347969258340d36bc46427a00a0dafde04b1a6fa64a9114b7`;Linux **58,213,807 字节**,SHA-256 `52b5ddbb33cc1c32dc2c3d49eb422bba87f7c286bc62ca15e5511163ca66e36a`。
|
|
41
|
+
- 发行 tag 固定于 `047a202fb92de100104f689014bb831283e1f00b`;后续仅补充文档记录,不改变已发布产物。验证脚本分别记录本地字节校验和 GitHub 服务端摘要核验方式,未完成下载保留为临时备份,用户原有 `flow-results/` 没有移动或删除。
|
|
25
42
|
|
|
26
43
|
## 0.5.2 命令注册与快捷名称(2026-10-13)
|
|
27
44
|
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
|
|
28
28
|
`createApplication({projectDir,pluginRegistryDir})` 可让多个工作区读取同一插件库;CLI 默认接入用户级全局库,程序未传此选项时保留工作区模式。分发包始终使用自身工作区安装状态。`listPlugins()` 返回 scope/source 和待配置原因;`pluginDefinition(id)` 返回作者声明。`app.resultsDir` 是业务文件根,`app.outputDir` 保留为日志根;插件 `ctx.outputDir` 与 `ctx.logDir` 分别对应本次业务目录和日志目录。
|
|
29
29
|
|
|
30
|
+
已安装条目的加载或单插件契约错误隔离为 `runtime.status: 'unavailable'`,`listPlugins()` 返回 `loadError` 与来源,传递依赖者同样不可用。直接调用或检查引用它的流程抛出 `PLUGIN_UNAVAILABLE`;无关插件继续装配。纯卸载允许保留其他旧故障;新安装/链接严格检查。修复来源后须重新启动。此恢复不覆盖预置插件、明确的跨插件图错误和运行时初始化错误。
|
|
31
|
+
|
|
30
32
|
`app.startShell(script,options)` 执行临时命令,`app.startAction(id,input,context)` 调用任意动作,两者均纳入运行记录与关闭等待。`startCommand(name,input,{expectedPlugin,...context})` 可固定命令提供者,启动时再次检查,防止参数填写期间被其他同名能力替换。`configureCommandPresets` 和 `configureShortcuts` 在备份、冲突检查与变更锁内保存项目设置;命令快捷项在用户明确设置时绑定提供者 ID,普通安装/卸载和未改变目标的整组保存不会重新绑定;已有未绑定项必须明确提供 plugin 或在界面重新选择目标。注册项变化通过定向宿主更新生效,导出快照保留所需定义与有效快捷项。
|
|
31
33
|
|
|
32
34
|
本模块负责上述职责的实现、文档与回归;跨模块接口调整需要同时检查生产者与消费者。具体维护人员尚未指定。
|
|
@@ -37,6 +39,10 @@
|
|
|
37
39
|
node --test tests/modules.test.js tests/application.test.js tests/lifecycle.test.js tests/module-interfaces.test.js
|
|
38
40
|
```
|
|
39
41
|
|
|
42
|
+
## 输入预览
|
|
43
|
+
|
|
44
|
+
应用接口 `app.previewInput(target, input)` 读取目标动作或命令的当前有效输入及来源;`target` 为 `插件ID:动作名` 或 `/命令名`。由宿主使用当前有效配置解析,返回脱离原对象的 `{value, fields}`,不回写输入,也不暴露整份配置。预览允许缺参,执行时仍作完整校验。验证见 `tests/effective-inputs.test.js`。
|
|
45
|
+
|
|
40
46
|
## 开发监督进程
|
|
41
47
|
|
|
42
48
|
`createDevelopmentSupervisor({projectDir,entry,args,env,configFile,onEvent})` 启动文件监听 worker 和应用子进程,返回 `status()`、幂等 `close()`。父进程不加载插件运行模块;每次重启使用全新的 Node 进程,完整更新 ESM 导入依赖。`bindDevelopmentChild(app,{details,closePresentation})` 通过 IPC 报告就绪,收到重启请求后等待 `app.developmentStatus()` 的运行和管理操作均为空闲,再调用 `app.close()` 及界面清理。
|
|
@@ -7,6 +7,7 @@ import { createHost } from '@mytools/fst-plugin';
|
|
|
7
7
|
import { WorkflowStore } from '@mytools/fst-workspace/workflows';
|
|
8
8
|
import { runWorkflow } from '@mytools/fst-workflow';
|
|
9
9
|
import { runProcess } from '@mytools/fst-platform';
|
|
10
|
+
import {createProcessRunner} from '@mytools/fst-platform/managed-process';
|
|
10
11
|
import {runShell} from '@mytools/fst-platform/shell';
|
|
11
12
|
import {COMMANDS_PLUGIN_ID,commandsPluginRoot,createCommandsPlugin,validateCommandPresets} from '@mytools/commands';
|
|
12
13
|
import {validateShortcuts,describeShortcuts,bindShortcutCommands} from '@mytools/fst-workspace/shortcuts';
|
|
@@ -27,6 +28,7 @@ import {validateNetworkPolicy,inspectNetworkPolicy,resolveNetworkPolicy,buildPro
|
|
|
27
28
|
import {resolvePrivilegeRequirement,checkStaticPrivilege} from '@mytools/fst-plugin/privileges';
|
|
28
29
|
import {registerWorkspace,registryWorkflowReferences,registryPluginReferences} from '@mytools/fst-workspace/registry';
|
|
29
30
|
import {mergePluginEntries,preparePluginCatalog} from './plugin-catalog.js';
|
|
31
|
+
import {loadInstalledEntry} from './plugin-loading.js';
|
|
30
32
|
|
|
31
33
|
function declaredCommands(entries){
|
|
32
34
|
return entries.flatMap(entry=>{const plugin=entry.plugin??entry;return Object.keys(plugin.commands??{}).map(name=>({name,plugin:plugin.id}));});
|
|
@@ -73,8 +75,9 @@ export async function createApplication({projectDir=process.cwd(),workspace,plug
|
|
|
73
75
|
developmentStatus:()=>({running:launching+writingRuns.size+operations.size,busy,closing:!!closing}),
|
|
74
76
|
catalog:()=>host.catalog(),
|
|
75
77
|
pluginDefinition:id=>host.definitions.get(id)??pending.get(id)?.entry.plugin,
|
|
76
|
-
listPlugins:()=>catalogEntries.map(entry=>{const p=entry.plugin??entry,scope=entry.scope??'preset',installed=scope==='global'?state:scope==='workspace'?(shared?localState:state):{};return {id:p.id,version:p.version,title:p.title,description:p.description,scope,source:entry.root,platforms:p.platforms,architectures:p.architectures,environments:p.environments,config:p.config,requirements:p.requirements,privileges:pluginPrivileges(p),runtime:{...(pending.has(p.id)?{status:'unconfigured'}:host.runtimeStatus().find(r=>r.id===p.id)),...(p.runtime?{updatePolicy:p.runtime.updatePolicy??'maintenance'}:{})},...(pending.has(p.id)?{configurationError:pending.get(p.id).message}:{}),removable:!!(installed.dependencies?.[p.id]||installed.links?.[p.id]),development:!!installed.links?.[p.id],warnings:host.warnings.filter(item=>item.plugin===p.id).map(item=>item.message)};}),
|
|
78
|
+
listPlugins:()=>catalogEntries.map(entry=>{const p=entry.plugin??entry,scope=entry.scope??'preset',installed=scope==='global'?state:scope==='workspace'?(shared?localState:state):{};return {id:p.id,version:p.version,title:p.title,description:p.description,scope,source:entry.root,platforms:p.platforms,architectures:p.architectures,environments:p.environments,config:p.config,requirements:p.requirements,privileges:pluginPrivileges(p),runtime:{...(pending.has(p.id)?{status:pending.get(p.id).status??'unconfigured'}:host.runtimeStatus().find(r=>r.id===p.id)),...(p.runtime?{updatePolicy:p.runtime.updatePolicy??'maintenance'}:{})},...(pending.has(p.id)?(pending.get(p.id).status==='unavailable'?{loadError:pending.get(p.id).error}:{configurationError:pending.get(p.id).message}):{}),removable:!!(installed.dependencies?.[p.id]||installed.links?.[p.id]),development:!!installed.links?.[p.id],warnings:host.warnings.filter(item=>item.plugin===p.id).map(item=>item.message)};}),
|
|
77
79
|
listCommands:()=>host.commands(),
|
|
80
|
+
previewInput:(target,input)=>host.previewInput(target,input),
|
|
78
81
|
listShortcuts:()=>describeShortcuts(project.shortcuts??{},{actions:host.catalog(),commands:host.commands()}),
|
|
79
82
|
async configureShortcuts(value){return updateUserCommands({shortcuts:value});},
|
|
80
83
|
async configureCommandPresets(value,options={}){
|
|
@@ -86,7 +89,7 @@ export async function createApplication({projectDir=process.cwd(),workspace,plug
|
|
|
86
89
|
async removeWorkflow(id,options){return mutateWorkflow(()=>store.remove(id,options));},
|
|
87
90
|
async checkWorkflow(id){
|
|
88
91
|
host.assertHealthy();
|
|
89
|
-
const closure=await store.resolve([id],host);
|
|
92
|
+
const closure=await store.resolve([id],{...host,actions:{get(action){assertConfigured(action.slice(0,action.lastIndexOf(':')));return host.actions.get(action);}}});
|
|
90
93
|
return {...closure,privileges:workflowPrivileges(closure,id)};
|
|
91
94
|
},
|
|
92
95
|
subscribe(fn){emitter.on('event',fn);return ()=>emitter.off('event',fn);},
|
|
@@ -125,8 +128,7 @@ export async function createApplication({projectDir=process.cwd(),workspace,plug
|
|
|
125
128
|
const env=buildProcessEnv(policy);
|
|
126
129
|
return app.executeTracked('!命令',async(_host,context)=>{
|
|
127
130
|
const result=await runShell(script,{shell,temporaryDir:context.outputDir,cwd:projectDir,env,extendEnv:false,signal:context.signal,timeout,outputMode:'stream',summaryBytes:65536,
|
|
128
|
-
|
|
129
|
-
onOutput:({stream,message})=>context.log(message,undefined,{stream,audience:'user'})});
|
|
131
|
+
runProcess:createProcessRunner({signal:context.signal,log:context.log,audience:'user'})});
|
|
130
132
|
if(result.exitCode!==0)throw new FlowError(`命令执行失败,退出码 ${result.exitCode}`,{code:'EXECUTION_FAILED',details:result});
|
|
131
133
|
return result;
|
|
132
134
|
},{kind:'shell',title:'系统命令'});
|
|
@@ -331,11 +333,15 @@ export async function createApplication({projectDir=process.cwd(),workspace,plug
|
|
|
331
333
|
}catch(error){await fileProbe.close();await opened.release();await localOpened?.release();throw error;}
|
|
332
334
|
async function loadInstalled(current,base,excluded=new Set()){
|
|
333
335
|
const loaded=[];
|
|
334
|
-
for(const name of Object.keys(current.dependencies??{}))if(!excluded.has(name)&&(!maintenance||name!==omitInstalledPlugin))loaded.push(await
|
|
335
|
-
for(const [name,source] of Object.entries(current.links??{}))if(!excluded.has(name)&&(!maintenance||name!==omitInstalledPlugin))loaded.push(await
|
|
336
|
+
for(const name of Object.keys(current.dependencies??{}))if(!excluded.has(name)&&(!maintenance||name!==omitInstalledPlugin))loaded.push(await loadInstalledEntry(name,name,current.root));
|
|
337
|
+
for(const [name,source] of Object.entries(current.links??{}))if(!excluded.has(name)&&(!maintenance||name!==omitInstalledPlugin))loaded.push(await loadInstalledEntry(name,source,base,{linked:true}));
|
|
336
338
|
return loaded;
|
|
337
339
|
}
|
|
338
|
-
function assertConfigured(id){
|
|
340
|
+
function assertConfigured(id){
|
|
341
|
+
const failure=pending.get(id);if(!failure)return;
|
|
342
|
+
if(failure.status==='unavailable')throw new FlowError(`插件 ${id} 不可用(${failure.entry.root??'未知来源'}):${failure.message}`,{code:'PLUGIN_UNAVAILABLE',details:{plugin:id,source:failure.entry.root,error:failure.error}});
|
|
343
|
+
throw new FlowError(`插件 ${id} 尚未配置:${failure.message}`,{code:'PLUGIN_NOT_CONFIGURED',details:{plugin:id}});
|
|
344
|
+
}
|
|
339
345
|
for(const name of ['startRun','startCommand','startAction','startShell','startWeb','saveWorkflow','duplicateWorkflow','removeWorkflow','installPlugin','linkPlugin','removePlugin','configurePlugin','configureNetworkPolicy','configureLogs','configureCommandPresets','configureShortcuts','setMaintenance','pack']){
|
|
340
346
|
const original=app[name],isRun=['startRun','startCommand','startAction','startShell'].includes(name);
|
|
341
347
|
app[name]=async(...args)=>{
|
|
@@ -466,7 +472,7 @@ export async function createApplication({projectDir=process.cwd(),workspace,plug
|
|
|
466
472
|
}
|
|
467
473
|
async function findPluginWorkflowReferences(id){
|
|
468
474
|
const refs=[];
|
|
469
|
-
for(const workflow of await store.list()){
|
|
475
|
+
for(const workflow of await store.list({strict:true})){
|
|
470
476
|
const actions=[...workflowActions(workflow.steps)].filter(action=>action.startsWith(`${id}:`));
|
|
471
477
|
if(actions.length)refs.push({id:workflow.id,title:workflow.title??workflow.id,actions});
|
|
472
478
|
}
|
|
@@ -510,7 +516,7 @@ export async function createApplication({projectDir=process.cwd(),workspace,plug
|
|
|
510
516
|
const workflows=shared&&targetDir===registryDir?await registryWorkflowReferences(registryDir,name):await findPluginWorkflowReferences(name);
|
|
511
517
|
if(workflows.length&&!options.force)throw new FlowError(`插件 ${name} 仍被工作流引用:${workflows.map(w=>w.id).join('、')}`,{code:'PLUGIN_IN_USE',details:{workflows}});
|
|
512
518
|
}
|
|
513
|
-
const next=await installGeneration(targetDir,{...operation,sourceDir:projectDir,state:shared&&targetDir===registryDir?{...targetState,global:true}:targetState});
|
|
519
|
+
const next=await installGeneration(targetDir,{...operation,sourceDir:projectDir,state:shared&&targetDir===registryDir?{...targetState,global:true}:targetState,recoverInstalled:loadInstalledEntry});
|
|
514
520
|
const nextLocal=shared?(targetDir===projectDir?next.loaded:localEntries):next.loaded,nextGlobal=shared?(targetDir===registryDir?next.loaded:globalEntries):[];
|
|
515
521
|
const nextState=targetDir===registryDir?next.state:state;
|
|
516
522
|
const nextCatalog=entriesWithPresets(mergePluginEntries(initial,nextLocal,nextGlobal,nextState.disabledLegacy,{shared}),latest.commandPresets??{}),activeConfig={...project.config,...config},effectiveConfig={...latest.config,...config};
|
|
@@ -18,9 +18,28 @@ export function mergePluginEntries(initial,local,global,disabledLegacy=[],{share
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function preparePluginCatalog(entries,config){
|
|
21
|
-
const pending=new Map()
|
|
21
|
+
const pending=new Map(),unavailable=new Set();
|
|
22
|
+
for(const entry of entries)if(entry.unavailable){
|
|
23
|
+
const id=entry.plugin.id;unavailable.add(id);
|
|
24
|
+
pending.set(id,{entry,status:'unavailable',message:entry.unavailable.message,error:entry.unavailable});
|
|
25
|
+
}
|
|
26
|
+
// 故障依赖的消费者显式不可用,直到用户修复来源并重新启动。
|
|
27
|
+
let changed;
|
|
28
|
+
do{
|
|
29
|
+
changed=false;
|
|
30
|
+
for(const entry of entries){
|
|
31
|
+
const plugin=entry.plugin??entry;
|
|
32
|
+
const missing=Object.keys(plugin.dependencies??{}).filter(id=>pending.has(id));
|
|
33
|
+
if(!pending.has(plugin.id)&&missing.length){
|
|
34
|
+
const message=`依赖插件不可用:${missing.join('、')}`;
|
|
35
|
+
pending.set(plugin.id,{entry,status:'unavailable',message,error:{message,phase:'dependency',dependencies:missing}});changed=true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}while(changed);
|
|
39
|
+
validatePluginDefinitions(entries,{unavailable,blocked:new Set(pending.keys())});
|
|
22
40
|
for(const entry of entries){
|
|
23
41
|
const plugin=entry.plugin??entry;
|
|
42
|
+
if(pending.has(plugin.id))continue;
|
|
24
43
|
if(plugin.config!==undefined)assertSchema(plugin.config,`${plugin.id} 配置`);
|
|
25
44
|
try{validate(plugin.config,config[plugin.id]??{},`${plugin.id} 配置`);}
|
|
26
45
|
catch(error){
|
|
@@ -29,7 +48,6 @@ export function preparePluginCatalog(entries,config){
|
|
|
29
48
|
}
|
|
30
49
|
}
|
|
31
50
|
// 缺配置的服务提供者暂不启用,相关消费者也等待;无关插件正常装配。
|
|
32
|
-
let changed;
|
|
33
51
|
do{
|
|
34
52
|
changed=false;
|
|
35
53
|
for(const entry of entries){
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import {loadPlugin} from '@mytools/fst-plugin/loader';
|
|
3
|
+
import {validatePluginDefinition} from '@mytools/fst-plugin';
|
|
4
|
+
import {assertSchema} from '@mytools/sdk/schema';
|
|
5
|
+
import {FlowError} from '@mytools/sdk';
|
|
6
|
+
import {errorRecord} from '@mytools/sdk/diagnostics';
|
|
7
|
+
|
|
8
|
+
// 已安装插件加载失败时保留注册身份,避免错误导出破坏管理和卸载入口。
|
|
9
|
+
export async function loadInstalledEntry(id,spec,base,{linked=false}={}){
|
|
10
|
+
let loaded,phase='load';
|
|
11
|
+
const source=linked?path.resolve(base,spec):path.join(base,'node_modules',id);
|
|
12
|
+
try{
|
|
13
|
+
loaded=await loadPlugin(spec,base);phase='contract';
|
|
14
|
+
const plugin=validatePluginDefinition(loaded);
|
|
15
|
+
if(plugin.id!==id)throw new FlowError(`插件导出标识 ${plugin.id} 与已安装标识 ${id} 不一致`,{code:'VALIDATION'});
|
|
16
|
+
if(plugin.config!==undefined)assertSchema(plugin.config,`${id} 配置`);
|
|
17
|
+
return loaded;
|
|
18
|
+
}catch(error){
|
|
19
|
+
const original=loaded?.plugin,plugin={id};
|
|
20
|
+
for(const key of ['version','title','description'])if(typeof original?.[key]==='string')plugin[key]=original[key];
|
|
21
|
+
if(original?.dependencies&&typeof original.dependencies==='object'&&!Array.isArray(original.dependencies))plugin.dependencies=original.dependencies;
|
|
22
|
+
return {plugin,root:loaded?.root??source,unavailable:{...errorRecord(error),phase}};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// 由 npm version 的生命周期脚本同步,内部插件 API 版本独立维护。
|
|
2
|
-
export const VERSION="0.
|
|
2
|
+
export const VERSION="0.6.0";
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
fst 命令入口、交互终端、输入呈现和运行观察。通过公开应用和模块接口工作,业务执行不在界面中重复实现。
|
|
8
8
|
|
|
9
|
+
支持 SDK 的 `fst-report` v1 业务报告:主面板默认显示结论、分组和表格,以缩进区分报告标题、结论和组内数据。执行入口、输入及实际指令始终可见;Ctrl+O 展开原始输出与诊断,运行详情中的 `m` 提供专用阅读,`j` 保留原始 JSON。报告的提醒等级只代表插件的业务判断,不改变运行状态。旧文本/Markdown/结构化输出保持兼容;损坏和未知版本报告提示后回退字段阅读。
|
|
10
|
+
|
|
9
11
|
内部依赖:`@mytools/sdk`、`@mytools/core`、`@mytools/fst-workspace`、`@mytools/fst-platform`、`@mytools/fst-plugin`、`@mytools/fst-devtools`、`@mytools/fst-logs`。
|
|
10
12
|
## 公开入口
|
|
11
13
|
|
|
@@ -35,7 +37,9 @@ fst 命令入口、交互终端、输入呈现和运行观察。通过公开应
|
|
|
35
37
|
|
|
36
38
|
TUI 支持 `!` 系统命令、`/commands` 常用命令注册和 `/shortcuts` 任意插件能力快捷名。主输入框与命令编辑框使用 Shift+Enter / Alt+Enter 换行、Enter 提交;真实多行粘贴保持换行及缩进。使用说明可通过 `fst docs commands` 离线阅读。
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
空 `/` 只列精简入口;输入前缀后搜索全部已注册命令,例如 `/env` 可找到 `envprobe.*`,`/envprobe.` 可继续缩小范围。唯一且没有名称冲突的命令命名空间也可打开所属插件能力菜单;原插件 ID 入口保留。从插件管理或能力菜单实际启动命令后,其完整命令名进入上下键历史及持久会话;取消参数或启动失败不新增执行条目,参数保留在运行记录中,不拼入可召回命令。
|
|
41
|
+
|
|
42
|
+
运行和步骤附近提示 Ctrl+O 展开原始输出与诊断;输入及受管进程的实际程序、参数、目录和原始命令默认可见。stdout/stderr(包括感叹号透传)默认折叠,print 用户反馈和业务报告缩进展示。`/logs` 中按 m 单独格式化阅读输出,j 仍保留原始 JSON。总耗时在执行期间每秒更新,结束或关闭界面后回收刷新计时器。平台声明提示直接显示在 TUI。
|
|
39
43
|
|
|
40
44
|
`/history` 或 `/tools` → 历史会话恢复输入、反馈与结果,不重跑指令;与已有插件或快捷名 `history` 冲突时保留原入口。通过 `fst-logs/sessions` 在所属工作区保存有界快照与完整追加交互记录,只读包改用可写输出目录。较早的内容分页读取,运行日志仍独立保存并遵循其保留策略。恢复创建当前会话的继续记录,不覆盖来源会话;未提交草稿和配置表单不保存。
|
|
41
45
|
|