@yanglaofish/fishtools 0.3.0 → 0.5.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 (247) hide show
  1. package/README.md +33 -5
  2. package/bin/fst.js +1 -1
  3. package/manual/README.md +33 -5
  4. package/manual/docs/deployment.md +3 -3
  5. package/manual/docs/examples/script-plugin/LICENSE +21 -0
  6. package/manual/docs/examples/script-plugin/README.md +64 -0
  7. package/manual/docs/examples/script-plugin/index.js +39 -0
  8. package/manual/docs/examples/script-plugin/package.json +9 -0
  9. package/manual/docs/examples/script-plugin/scripts/benchmark.py +23 -0
  10. package/manual/docs/kernel-generation.md +97 -0
  11. package/manual/docs/modules.md +57 -0
  12. package/manual/docs/plugin-development.md +163 -12
  13. package/manual/docs/releases.md +23 -0
  14. package/manual/docs/research/2026-10-11-plugin-kernel-capabilities.md +6 -6
  15. package/manual/docs/roadmap.md +16 -3
  16. package/manual/docs/superpowers/plans/2026-10-09-workflow-mvp.md +5 -5
  17. package/manual/docs/superpowers/plans/2026-10-11-public-plugin-fixes.md +2 -2
  18. package/manual/docs/superpowers/plans/2026-10-12-kernel-generation.md +22 -0
  19. package/manual/docs/superpowers/plans/2026-10-12-modular-kernel.md +40 -0
  20. package/manual/docs/superpowers/plans/2026-10-12-plugin-developer-experience.md +50 -0
  21. package/manual/docs/superpowers/reviews/2026-10-12-kernel-generation-review.md +27 -0
  22. package/manual/docs/superpowers/reviews/2026-10-12-modular-kernel-review.md +32 -0
  23. package/manual/docs/superpowers/reviews/2026-10-12-plugin-release-review.md +20 -0
  24. package/manual/docs/superpowers/reviews/2026-10-13-kernel-runtime-release-review.md +19 -0
  25. package/manual/docs/superpowers/specs/2026-10-12-action-network-policy-design.md +52 -0
  26. package/manual/docs/superpowers/specs/2026-10-12-kernel-generation-design.md +38 -0
  27. package/manual/docs/superpowers/specs/2026-10-12-modular-kernel-design.md +70 -0
  28. package/manual/docs/superpowers/specs/2026-10-12-plugin-developer-experience.md +22 -0
  29. package/manual/docs/superpowers/specs/2026-10-12-plugin-readiness-design.md +21 -0
  30. package/manual/docs/superpowers/specs/2026-10-12-runtime-maintenance-granularity-design.md +35 -0
  31. package/manual/docs/superpowers/specs/2026-10-12-workflow-variation-design.md +19 -0
  32. package/manual/docs/verification.md +58 -1
  33. package/manual/packages/core/README.md +44 -0
  34. package/manual/packages/core/package.json +38 -0
  35. package/manual/packages/core/src/application.js +268 -87
  36. package/manual/packages/core/src/bundle.js +2 -0
  37. package/manual/packages/core/src/development-watch-worker.js +69 -0
  38. package/manual/packages/core/src/development.js +146 -0
  39. package/manual/packages/core/src/environments.js +2 -0
  40. package/manual/packages/core/src/export-snapshot.js +42 -0
  41. package/manual/packages/core/src/plugin-update.js +108 -0
  42. package/manual/packages/core/src/process.js +2 -0
  43. package/manual/packages/core/src/resources.js +31 -0
  44. package/manual/packages/core/src/version.js +2 -0
  45. package/manual/packages/fst-cli/LICENSE +21 -0
  46. package/manual/packages/fst-cli/README.md +54 -0
  47. package/manual/packages/fst-cli/bin/fst.js +124 -0
  48. package/manual/packages/fst-cli/package.json +49 -0
  49. package/manual/packages/fst-cli/src/development.js +53 -0
  50. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/model.js +1 -1
  51. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/navigation.js +1 -1
  52. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/presentation.js +9 -1
  53. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/run-feed.js +31 -5
  54. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/run-viewer.js +9 -7
  55. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/schema.js +1 -1
  56. package/manual/packages/{core → fst-cli}/src/terminal/session.js +137 -9
  57. package/manual/packages/{core → fst-cli}/src/terminal/view.js +20 -3
  58. package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/workflow-menu.js +17 -2
  59. package/manual/packages/fst-devtools/LICENSE +21 -0
  60. package/manual/packages/fst-devtools/README.md +28 -0
  61. package/manual/packages/fst-devtools/package.json +29 -0
  62. package/{node_modules/@mytools/core → manual/packages/fst-devtools}/src/documentation.js +3 -1
  63. package/manual/packages/fst-devtools/src/plugin-quality.js +136 -0
  64. package/manual/packages/fst-devtools/src/plugin-tools.js +152 -0
  65. package/manual/packages/fst-logs/LICENSE +21 -0
  66. package/manual/packages/fst-logs/README.md +56 -0
  67. package/manual/packages/fst-logs/package.json +24 -0
  68. package/manual/packages/fst-logs/src/log-bounds.js +91 -0
  69. package/manual/packages/fst-logs/src/run-logs.js +239 -0
  70. package/manual/packages/fst-platform/LICENSE +21 -0
  71. package/manual/packages/fst-platform/README.md +67 -0
  72. package/manual/packages/fst-platform/package.json +33 -0
  73. package/manual/packages/fst-platform/src/bundle.js +17 -0
  74. package/manual/packages/fst-platform/src/environments.js +67 -0
  75. package/manual/packages/fst-platform/src/file-probe-engine.js +183 -0
  76. package/manual/packages/fst-platform/src/file-probe-worker.js +43 -0
  77. package/manual/packages/fst-platform/src/file-probe.js +6 -0
  78. package/manual/packages/fst-platform/src/json.js +5 -0
  79. package/manual/packages/fst-platform/src/network.js +199 -0
  80. package/manual/packages/fst-platform/src/npm.js +11 -0
  81. package/manual/packages/fst-platform/src/process.js +110 -0
  82. package/manual/packages/fst-plugin/LICENSE +21 -0
  83. package/manual/packages/fst-plugin/README.md +29 -0
  84. package/manual/packages/fst-plugin/package.json +29 -0
  85. package/manual/packages/fst-plugin/src/contracts.js +86 -0
  86. package/manual/packages/fst-plugin/src/host.js +232 -0
  87. package/manual/packages/fst-plugin/src/loader.js +21 -0
  88. package/manual/packages/fst-plugin/src/privileges.js +91 -0
  89. package/manual/packages/{core → fst-plugin}/src/runtime.js +20 -4
  90. package/manual/packages/fst-workflow/LICENSE +21 -0
  91. package/manual/packages/fst-workflow/README.md +28 -0
  92. package/manual/packages/fst-workflow/package.json +24 -0
  93. package/{node_modules/@mytools/core → manual/packages/fst-workflow}/src/control.js +3 -2
  94. package/manual/packages/{core → fst-workflow}/src/engine.js +2 -2
  95. package/manual/packages/fst-workspace/LICENSE +21 -0
  96. package/manual/packages/fst-workspace/README.md +32 -0
  97. package/manual/packages/fst-workspace/package.json +34 -0
  98. package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/bundle-workspace.js +1 -1
  99. package/manual/packages/fst-workspace/src/index.js +3 -0
  100. package/manual/packages/fst-workspace/src/plugin-installations.js +84 -0
  101. package/manual/packages/fst-workspace/src/plugins.js +35 -0
  102. package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/workflows.js +41 -21
  103. package/manual/packages/pack/LICENSE +21 -0
  104. package/manual/packages/pack/README.md +31 -0
  105. package/manual/packages/pack/archive.js +15 -0
  106. package/manual/packages/pack/dependencies.js +105 -0
  107. package/manual/packages/pack/environments.js +50 -0
  108. package/manual/packages/pack/index.js +103 -0
  109. package/manual/packages/pack/package.json +29 -0
  110. package/manual/packages/pack/runtime.js +61 -0
  111. package/manual/packages/sdk/README.md +31 -0
  112. package/manual/packages/sdk/conditions.js +1 -0
  113. package/manual/packages/sdk/package.json +23 -1
  114. package/manual/packages/sdk/schema-path.js +31 -0
  115. package/{node_modules/@mytools/core/src → manual/packages/sdk}/validation.js +22 -9
  116. package/manual/plugins/web/README.md +29 -0
  117. package/manual/tests/application.test.js +6 -4
  118. package/node_modules/@mytools/core/README.md +44 -0
  119. package/node_modules/@mytools/core/package.json +17 -15
  120. package/node_modules/@mytools/core/src/application.js +268 -87
  121. package/node_modules/@mytools/core/src/bundle.js +2 -17
  122. package/node_modules/@mytools/core/src/development-watch-worker.js +69 -0
  123. package/node_modules/@mytools/core/src/development.js +146 -0
  124. package/node_modules/@mytools/core/src/environments.js +2 -29
  125. package/node_modules/@mytools/core/src/export-snapshot.js +42 -0
  126. package/node_modules/@mytools/core/src/plugin-update.js +108 -0
  127. package/node_modules/@mytools/core/src/process.js +2 -71
  128. package/node_modules/@mytools/core/src/version.js +1 -1
  129. package/node_modules/@mytools/fst-cli/LICENSE +21 -0
  130. package/node_modules/@mytools/fst-cli/README.md +54 -0
  131. package/node_modules/@mytools/fst-cli/bin/fst.js +124 -0
  132. package/node_modules/@mytools/fst-cli/package.json +49 -0
  133. package/node_modules/@mytools/fst-cli/src/cli-run.js +12 -0
  134. package/node_modules/@mytools/fst-cli/src/development.js +53 -0
  135. package/node_modules/@mytools/fst-cli/src/terminal/conditions.js +32 -0
  136. package/node_modules/@mytools/fst-cli/src/terminal/log-viewer.js +45 -0
  137. package/node_modules/@mytools/fst-cli/src/terminal/model.js +104 -0
  138. package/node_modules/@mytools/fst-cli/src/terminal/navigation.js +17 -0
  139. package/node_modules/@mytools/fst-cli/src/terminal/presentation.js +50 -0
  140. package/{manual/packages/core → node_modules/@mytools/fst-cli}/src/terminal/run-feed.js +31 -5
  141. package/node_modules/@mytools/fst-cli/src/terminal/run-presentation.js +60 -0
  142. package/node_modules/@mytools/fst-cli/src/terminal/run-viewer.js +106 -0
  143. package/node_modules/@mytools/fst-cli/src/terminal/schema.js +52 -0
  144. package/node_modules/@mytools/{core → fst-cli}/src/terminal/session.js +137 -9
  145. package/node_modules/@mytools/fst-cli/src/terminal/theme.js +29 -0
  146. package/node_modules/@mytools/{core → fst-cli}/src/terminal/view.js +20 -3
  147. package/node_modules/@mytools/fst-cli/src/terminal/workflow-menu.js +65 -0
  148. package/node_modules/@mytools/fst-devtools/LICENSE +21 -0
  149. package/node_modules/@mytools/fst-devtools/README.md +28 -0
  150. package/node_modules/@mytools/fst-devtools/package.json +29 -0
  151. package/node_modules/@mytools/fst-devtools/src/documentation.js +67 -0
  152. package/node_modules/@mytools/fst-devtools/src/plugin-quality.js +136 -0
  153. package/node_modules/@mytools/fst-devtools/src/plugin-tools.js +152 -0
  154. package/node_modules/@mytools/fst-logs/LICENSE +21 -0
  155. package/node_modules/@mytools/fst-logs/README.md +56 -0
  156. package/node_modules/@mytools/fst-logs/package.json +24 -0
  157. package/node_modules/@mytools/fst-logs/src/log-bounds.js +91 -0
  158. package/node_modules/@mytools/fst-logs/src/markdown-log.js +55 -0
  159. package/node_modules/@mytools/fst-logs/src/run-logs.js +239 -0
  160. package/node_modules/@mytools/fst-platform/LICENSE +21 -0
  161. package/node_modules/@mytools/fst-platform/README.md +67 -0
  162. package/node_modules/@mytools/fst-platform/package.json +33 -0
  163. package/node_modules/@mytools/fst-platform/src/bundle.js +17 -0
  164. package/node_modules/@mytools/fst-platform/src/environments.js +67 -0
  165. package/node_modules/@mytools/fst-platform/src/file-probe-engine.js +183 -0
  166. package/node_modules/@mytools/fst-platform/src/file-probe-worker.js +43 -0
  167. package/node_modules/@mytools/fst-platform/src/file-probe.js +6 -0
  168. package/node_modules/@mytools/fst-platform/src/json.js +5 -0
  169. package/node_modules/@mytools/fst-platform/src/network.js +199 -0
  170. package/node_modules/@mytools/fst-platform/src/npm.js +11 -0
  171. package/node_modules/@mytools/fst-platform/src/process.js +110 -0
  172. package/node_modules/@mytools/fst-plugin/LICENSE +21 -0
  173. package/node_modules/@mytools/fst-plugin/README.md +29 -0
  174. package/node_modules/@mytools/fst-plugin/package.json +29 -0
  175. package/node_modules/@mytools/fst-plugin/src/contracts.js +86 -0
  176. package/node_modules/@mytools/fst-plugin/src/host.js +232 -0
  177. package/node_modules/@mytools/fst-plugin/src/loader.js +21 -0
  178. package/node_modules/@mytools/fst-plugin/src/privileges.js +91 -0
  179. package/node_modules/@mytools/{core → fst-plugin}/src/runtime.js +20 -4
  180. package/node_modules/@mytools/fst-workflow/LICENSE +21 -0
  181. package/node_modules/@mytools/fst-workflow/README.md +28 -0
  182. package/node_modules/@mytools/fst-workflow/package.json +24 -0
  183. package/node_modules/@mytools/fst-workflow/src/control.js +64 -0
  184. package/node_modules/@mytools/{core → fst-workflow}/src/engine.js +2 -2
  185. package/node_modules/@mytools/fst-workspace/LICENSE +21 -0
  186. package/node_modules/@mytools/fst-workspace/README.md +32 -0
  187. package/node_modules/@mytools/fst-workspace/package.json +34 -0
  188. package/node_modules/@mytools/fst-workspace/src/bundle-workspace.js +39 -0
  189. package/node_modules/@mytools/fst-workspace/src/index.js +3 -0
  190. package/node_modules/@mytools/fst-workspace/src/plugin-installations.js +84 -0
  191. package/node_modules/@mytools/fst-workspace/src/plugins.js +35 -0
  192. package/node_modules/@mytools/fst-workspace/src/project.js +29 -0
  193. package/node_modules/@mytools/fst-workspace/src/workflows.js +150 -0
  194. package/node_modules/@mytools/fst-workspace/templates/control-demo.yaml +47 -0
  195. package/node_modules/@mytools/fst-workspace/templates/hello.yaml +35 -0
  196. package/node_modules/@mytools/fst-workspace/templates/python-check.yaml +26 -0
  197. package/node_modules/@mytools/fst-workspace/templates/system-check.yaml +11 -0
  198. package/node_modules/@mytools/pack/README.md +31 -0
  199. package/node_modules/@mytools/pack/dependencies.js +15 -3
  200. package/node_modules/@mytools/pack/environments.js +1 -1
  201. package/node_modules/@mytools/pack/index.js +33 -25
  202. package/node_modules/@mytools/pack/package.json +29 -1
  203. package/node_modules/@mytools/sdk/README.md +31 -0
  204. package/node_modules/@mytools/sdk/conditions.js +1 -0
  205. package/node_modules/@mytools/sdk/diagnostics.js +27 -0
  206. package/node_modules/@mytools/sdk/package.json +23 -1
  207. package/node_modules/@mytools/sdk/schema-path.js +31 -0
  208. package/node_modules/@mytools/sdk/validation.js +78 -0
  209. package/node_modules/@mytools/web/README.md +29 -0
  210. package/node_modules/@mytools/web/package.json +7 -0
  211. package/node_modules/@mytools/web/public/THIRD_PARTY_NOTICES.txt +1275 -1076
  212. package/node_modules/@mytools/web/public/assets/index-BaVzHycP.js +70 -0
  213. package/node_modules/@mytools/web/public/assets/index-DA8Ae92t.css +1 -0
  214. package/node_modules/@mytools/web/public/index.html +2 -2
  215. package/node_modules/@mytools/web/server.js +24 -17
  216. package/node_modules/@mytools/web/src/model.js +84 -0
  217. package/node_modules/acorn/CHANGELOG.md +1012 -0
  218. package/node_modules/acorn/LICENSE +21 -0
  219. package/node_modules/acorn/README.md +309 -0
  220. package/node_modules/acorn/bin/acorn +4 -0
  221. package/node_modules/acorn/dist/acorn.d.mts +897 -0
  222. package/node_modules/acorn/dist/acorn.d.ts +897 -0
  223. package/node_modules/acorn/dist/acorn.js +6370 -0
  224. package/node_modules/acorn/dist/acorn.mjs +6341 -0
  225. package/node_modules/acorn/dist/bin.js +90 -0
  226. package/node_modules/acorn/package.json +50 -0
  227. package/package.json +117 -101
  228. package/manual/packages/core/src/host.js +0 -124
  229. package/node_modules/@mytools/core/bin/fst.js +0 -70
  230. package/node_modules/@mytools/core/src/contracts.js +0 -41
  231. package/node_modules/@mytools/core/src/host.js +0 -124
  232. package/node_modules/@mytools/core/src/plugins.js +0 -55
  233. package/node_modules/@mytools/core/src/run-logs.js +0 -63
  234. package/node_modules/@mytools/web/public/assets/index-DT2B4tMV.js +0 -70
  235. package/node_modules/@mytools/web/public/assets/index-vuZvrUTT.css +0 -1
  236. /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/cli-run.js +0 -0
  237. /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/conditions.js +0 -0
  238. /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/log-viewer.js +0 -0
  239. /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/run-presentation.js +0 -0
  240. /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/theme.js +0 -0
  241. /package/{node_modules/@mytools/core → manual/packages/fst-logs}/src/markdown-log.js +0 -0
  242. /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/project.js +0 -0
  243. /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/control-demo.yaml +0 -0
  244. /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/hello.yaml +0 -0
  245. /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/python-check.yaml +0 -0
  246. /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/system-check.yaml +0 -0
  247. /package/{node_modules/@mytools/core/src → manual/packages/sdk}/diagnostics.js +0 -0
@@ -1,41 +0,0 @@
1
- import {FlowError} from '@mytools/sdk';
2
- import {conditionOperators} from './control.js';
3
-
4
- export const builtinCommands=[
5
- ['workflows','选择已有工作流'],['new','新建工作流'],['add','添加插件能力或子工作流'],
6
- ['steps','设置步骤参数、排序或删除'],['outputs','设置最终输出'],['save','保存工作流'],
7
- ['run','运行工作流,可附带 @参数.json'],['runs','运行记录:选择任意一次执行,查看进度、结果和日志'],['logs','查看运行日志'],['plugin','管理插件'],['plugins','管理插件(兼容旧入口)'],['config','配置 Fishtools'],['tools','Web 与离线文档'],
8
- ['pack','选择工作流打包'],['web','启动 Web 工作台'],['docs','阅读离线开发与使用文档'],['help','查看操作说明'],['exit','退出终端工作台'],
9
- ].map(([name,description])=>({name,description}));
10
-
11
- export function validateDeclarations(plugin){
12
- for(const [name,action] of Object.entries(plugin.actions??{})){
13
- if(action.conditions!==undefined&&(!action.conditions||typeof action.conditions!=='object'||Array.isArray(action.conditions)))throw new FlowError(`动作 ${name} 的 conditions 必须为对象`,{code:'VALIDATION'});
14
- for(const [key,preset] of Object.entries(action.conditions??{})){
15
- if(!preset||typeof preset.title!=='string'||!preset.title.trim()||typeof preset.path!=='string'||!preset.path||preset.path.split('.').some(p=>!p||['__proto__','constructor','prototype'].includes(p))||!conditionOperators.includes(preset.op)||(preset.op!=='exists'&&!Object.hasOwn(preset,'value')))throw new FlowError(`动作 ${name} 的判定预设 ${key} 无效`,{code:'VALIDATION'});
16
- let schema=action.output;
17
- for(const part of preset.path.split('.')){
18
- if(schema?.properties&&Object.hasOwn(schema.properties,part))schema=schema.properties[part];
19
- else if(schema?.type==='array'&&/^\d+$/.test(part))schema=schema.items;
20
- else throw new FlowError(`判定预设 ${key} 引用了未声明的输出字段 ${preset.path}`,{code:'VALIDATION'});
21
- }
22
- }
23
- }
24
- for(const key of ['platforms','architectures'])if(plugin[key]!==undefined&&(!Array.isArray(plugin[key])||!plugin[key].every(x=>typeof x==='string'&&x)))throw new FlowError(`${plugin.id} 的 ${key} 必须是字符串数组`);
25
- for(const declarations of [plugin.environments,...Object.values(plugin.actions??{}).map(a=>a.environments)]){
26
- for(const [id,e] of Object.entries(declarations??{})){
27
- if(!/^[\w-]+$/.test(id)||!e||!['host','bundled','tool'].includes(e.source))throw new FlowError(`环境声明无效:${plugin.id}/${id}`);
28
- if(e.allowedSources&&(!Array.isArray(e.allowedSources)||!e.allowedSources.includes(e.source)||!e.allowedSources.every(x=>['host','bundled','tool'].includes(x))))throw new FlowError(`环境来源范围无效:${id}`);
29
- if(e.source==='tool'&&id!=='node')throw new FlowError(`工具尚未提供环境:${id}`);
30
- }
31
- }
32
- }
33
-
34
- export function validateCommands(plugin,registered){
35
- for(const [name,c] of Object.entries(plugin.commands??{})){
36
- if(!/^[a-zA-Z][\w.-]*$/.test(name)||builtinCommands.some(x=>x.name===name)||registered.has(name))throw new FlowError(`命令名称无效或冲突:/${name}`);
37
- if(!c||Boolean(c.action)===Boolean(c.run)||c.run&&typeof c.run!=='function')throw new FlowError(`命令 /${name} 必须声明 action 或 run`);
38
- if(c.action&&!plugin.actions?.[c.action])throw new FlowError(`命令 /${name} 指向不存在的动作:${c.action}`);
39
- registered.add(name);
40
- }
41
- }
@@ -1,124 +0,0 @@
1
- import { Context } from 'cordis';
2
- import semver from 'semver';
3
- import { SDK_VERSION, FlowError } from '@mytools/sdk';
4
- import { validate, emptyObject } from './validation.js';
5
- import {validateDeclarations,validateCommands} from './contracts.js';
6
- import {resolveEnvironments} from './environments.js';
7
- import {createRuntime} from './runtime.js';
8
- import {pluginError} from './diagnostics.js';
9
-
10
- export async function createHost({ plugins = [], config = {}, services = {}, environmentOverrides={},runtimeEnabled=true } = {}) {
11
- const ctx = new Context();
12
- const actions = new Map();
13
- const definitions = new Map();
14
- const extensions = new Map();
15
- const serviceNames = new Set();
16
- const commandNames=new Set(),commands=new Map();
17
- function bindContext(context){
18
- const bound={...services,...context},execute=bound.runProcess;
19
- // 面向用户的输出与诊断日志共用持久化通道,只区分默认可见性。
20
- bound.print=(message,data)=>Promise.resolve(bound.log?.(message,data,{audience:'user'}));
21
- if(typeof execute==='function')bound.runProcess=(file,args,options={})=>{
22
- const signal=context.signal&&options.signal?AbortSignal.any([context.signal,options.signal]):context.signal??options.signal;
23
- return execute(file,args,{...options,signal,onOutput:async({stream,message})=>{
24
- await bound.log?.(message,undefined,{stream});
25
- await options.onOutput?.({stream,message});
26
- }});
27
- };
28
- return bound;
29
- }
30
- for (const entry of plugins) {
31
- const plugin = entry.plugin ?? entry;
32
- validateDeclarations(plugin);validateCommands(plugin,commandNames);
33
- if (!plugin.id || definitions.has(plugin.id)) throw new FlowError(`插件标识缺失或重复:${plugin.id}`, {code:'VALIDATION'});
34
- if (!semver.satisfies(SDK_VERSION, plugin.sdk ?? '^0.1.0')) throw new FlowError(`插件 ${plugin.id} 与 SDK ${SDK_VERSION} 不兼容`, {code:'VALIDATION'});
35
- for(const [name,action] of Object.entries(plugin.actions??{})){
36
- if(typeof action?.run!=='function')throw new FlowError(`动作 ${plugin.id}:${name} 缺少执行函数`);
37
- }
38
- for(const [name,extension] of Object.entries(plugin.services??{})){
39
- if(serviceNames.has(name))throw new FlowError(`服务重复:${name}`);
40
- if(typeof extension!=='function')throw new FlowError(`服务 ${name} 缺少执行函数`);
41
- serviceNames.add(name);
42
- }
43
- definitions.set(plugin.id, {...plugin, root:entry.root});
44
- }
45
- const settingsById=new Map();
46
- for (const plugin of definitions.values()) {
47
- for (const [id, range] of Object.entries(plugin.dependencies ?? {})) {
48
- if (!definitions.has(id) || !semver.satisfies(definitions.get(id).version,range)) throw new FlowError(`插件 ${plugin.id} 缺少兼容依赖 ${id}@${range}`,{code:'VALIDATION'});
49
- }
50
- const settings = validate(plugin.config, config[plugin.id] ?? {}, `${plugin.id} 配置`);
51
- settingsById.set(plugin.id,settings);
52
- }
53
- const runtime=createRuntime(ctx,definitions,{enabled:runtimeEnabled});
54
- try{
55
- for (const plugin of definitions.values()) {
56
- const settings=settingsById.get(plugin.id);
57
- runtime.register(plugin,settings,scope=>{
58
- for(const [name,command] of Object.entries(plugin.commands??{})){
59
- const action=command.action?plugin.actions[command.action]:command;
60
- commands.set(name,{...command,name,plugin:plugin.id,input:action.input,output:action.output,description:command.description??action.description??action.title??name,config:settings});
61
- scope.on('dispose',()=>commands.delete(name));
62
- }
63
- for (const [name, action] of Object.entries(plugin.actions ?? {})) {
64
- const id = `${plugin.id}:${name}`;
65
- if (typeof action.run !== 'function') throw new FlowError(`动作 ${id} 缺少执行函数`);
66
- actions.set(id, {...action, id, plugin:plugin.id, config:settings, root:plugin.root});
67
- scope.on('dispose', () => actions.delete(id));
68
- }
69
- for (const [name, extension] of Object.entries(plugin.services ?? {})) {
70
- if (extensions.has(name)) throw new FlowError(`服务重复:${name}`);
71
- extensions.set(name, {run:extension,plugin:plugin.id});
72
- scope.on('dispose',()=>extensions.delete(name));
73
- }
74
- });
75
- }
76
- await runtime.start();
77
- }catch(error){
78
- try{await runtime.close();}catch(cleanup){if(cleanup.message!==error.message)throw new AggregateError([error,cleanup],`${error.message};${cleanup.message}`);}
79
- throw error;
80
- }
81
- return {
82
- definitions, actions,
83
- hasRuntime:runtime.hasRuntime,
84
- runtimeStatus:()=>runtime.status(),
85
- commands(){return [...commands.values()].map(({run,config,...metadata})=>structuredClone(metadata));},
86
- async invokeCommand(name,input,context={}){
87
- runtime.assertOpen();
88
- const command=commands.get(name);if(!command)throw new FlowError(`命令不存在:/${name}`);
89
- if(command.action)return this.invoke(`${command.plugin}:${command.action}`,input,context);
90
- if(!context.ui)throw new FlowError(`交互命令 /${name} 需要在 TUI 中调用`);
91
- const plugin=definitions.get(command.plugin);
92
- try{
93
- const getService=runtime.bindServices(plugin.id);
94
- context.signal?.throwIfAborted();
95
- const result=await command.run(validate(command.input??emptyObject,input,`/${name} 输入`),{...bindContext(context),getService,config:command.config,resourceDir:plugin.root,environments:resolveEnvironments(plugin,undefined,environmentOverrides[plugin.id])});
96
- context.signal?.throwIfAborted();
97
- return validate(command.output,JSON.parse(JSON.stringify(result??{})),`/${name} 输出`);
98
- }catch(cause){throw pluginError(cause,{plugin:plugin.id,command:`/${name}`});}
99
- },
100
- catalog() { return [...actions.values()].map(({run,config,root,...metadata})=>structuredClone(metadata)); },
101
- async invoke(id, input, context = {}) {
102
- runtime.assertOpen();
103
- const action = actions.get(id);
104
- if (!action) throw new FlowError(`动作不存在:${id}`, {code:'VALIDATION'});
105
- try{
106
- const getService=runtime.bindServices(action.plugin);
107
- context.signal?.throwIfAborted();
108
- const result = await action.run(validate(action.input ?? emptyObject,input,`${id} 输入`), {...bindContext(context),getService, config:action.config, resourceDir:action.root,environments:resolveEnvironments(definitions.get(action.plugin),action,environmentOverrides[action.plugin])});
109
- context.signal?.throwIfAborted();
110
- // 通过 JSON 边界验证返回值,避免函数、循环引用等进入工作流状态。
111
- const encoded = JSON.stringify(result);
112
- if (encoded === undefined) throw new FlowError(`${id} 输出必须可 JSON 序列化`);
113
- return validate(action.output, JSON.parse(encoded),`${id} 输出`);
114
- }catch(cause){throw pluginError(cause,{plugin:action.plugin,action:id});}
115
- },
116
- async service(name,options) {
117
- runtime.assertOpen();
118
- if (!extensions.has(name)) throw new FlowError(`未安装提供 ${name} 的插件`);
119
- const extension=extensions.get(name);runtime.assertActive(extension.plugin);
120
- return extension.run(services,options);
121
- },
122
- close:()=>runtime.close(),
123
- };
124
- }
@@ -1,55 +0,0 @@
1
- import { createRequire } from 'node:module';
2
- import { pathToFileURL } from 'node:url';
3
- import { readFile, writeFile, mkdir, access } from 'node:fs/promises';
4
- import path from 'node:path';
5
- import { randomUUID } from 'node:crypto';
6
- import { runProcess } from './process.js';
7
- import { FlowError } from '@mytools/sdk';
8
-
9
- export async function readJson(file,fallback) {
10
- try { return JSON.parse(await readFile(file,'utf8')); }
11
- catch(e) { if(e.code==='ENOENT' && fallback!==undefined) return fallback; throw e; }
12
- }
13
- export async function loadPlugin(spec,base) {
14
- const req=createRequire(path.join(base,'package.json'));
15
- let entry;
16
- if(path.isAbsolute(spec) || spec.startsWith('.')) {
17
- const root=path.resolve(base,spec), pkg=await readJson(path.join(root,'package.json'));
18
- entry=path.resolve(root,pkg.main??'index.js');
19
- } else {
20
- try {entry=req.resolve(spec);} catch {entry=createRequire(import.meta.url).resolve(spec);}
21
- }
22
- let root=path.dirname(entry);
23
- while(root!==path.dirname(root)) {
24
- try {await access(path.join(root,'package.json'));break;} catch {root=path.dirname(root);}
25
- }
26
- const module=await import(pathToFileURL(entry).href);
27
- return {plugin:module.default,root};
28
- }
29
- export async function npm(args,options={}) {
30
- const candidates=[path.join(path.dirname(process.execPath),'node_modules/npm/bin/npm-cli.js'),process.env.npm_execpath].filter(Boolean);
31
- for(const cli of candidates) {
32
- try {await access(cli);} catch {continue;}
33
- return runProcess(process.execPath,[cli,...args],{timeout:180000,...options});
34
- }
35
- return runProcess('npm',args,{timeout:180000,...options});
36
- }
37
-
38
- export async function installGeneration(projectDir,{add,remove,state}) {
39
- const root=path.join(projectDir,'.flow','installations',randomUUID());
40
- await mkdir(root,{recursive:true});
41
- const dependencies={...(state?.dependencies??{})};
42
- if(remove) delete dependencies[remove];
43
- await writeFile(path.join(root,'package.json'),JSON.stringify({name:'flow-project-plugins',private:true,dependencies},null,2));
44
- let source=add;
45
- if(add) {
46
- try {await access(path.resolve(projectDir,add));source=path.resolve(projectDir,add);} catch {}
47
- if(source.startsWith('-')) throw new FlowError('插件来源不能以选项符号开头',{code:'VALIDATION'});
48
- }
49
- const result=await npm(['install',...(source?[source]:[]),'--save-exact','--ignore-scripts','--install-links','--no-audit','--no-fund'],{cwd:root});
50
- if(result.exitCode!==0) throw new FlowError(`插件安装失败:${result.stderr}`,{code:'INSTALL_FAILED'});
51
- const pkg=await readJson(path.join(root,'package.json'));
52
- const loaded=[];
53
- for(const name of Object.keys(pkg.dependencies??{})) loaded.push(await loadPlugin(name,root));
54
- return {state:{root,dependencies:pkg.dependencies??{}},loaded};
55
- }
@@ -1,63 +0,0 @@
1
- import path from 'node:path';
2
- import {readdir,readFile,lstat,unlink,rmdir} from 'node:fs/promises';
3
- import {FlowError} from '@mytools/sdk';
4
- import {markdownHeader,markdownEvent,markdownResult} from './markdown-log.js';
5
-
6
- const validId=id=>typeof id==='string'&&/^[a-f0-9-]{36}$/.test(id);
7
- export function validateKeepRuns(value){
8
- if(value!==null&&(!Number.isSafeInteger(value)||value<1))throw new FlowError('日志保留次数必须为正整数;null 表示不自动清理');
9
- return value;
10
- }
11
-
12
- export function createRunLogs(directory,{projectDir,allowLegacy=false}){
13
- const root=path.resolve(directory);let cleaning=Promise.resolve();
14
- const owner=process.platform==='win32'?projectDir.toLowerCase():projectDir;
15
- function location(id){if(!validId(id))throw new FlowError('运行 ID 无效');return path.join(root,id);}
16
- async function regular(file){try{return (await lstat(file)).isFile();}catch(error){if(error.code==='ENOENT')return false;throw error;}}
17
- async function read(id){
18
- const dir=location(id);if(!(await lstat(dir)).isDirectory()||(await lstat(dir)).isSymbolicLink())throw new FlowError('日志目录无效');
19
- const file=path.join(dir,'result.json');if(!await regular(file))throw new FlowError('运行记录不存在');
20
- const result=JSON.parse(await readFile(file,'utf8'));if(result.id!==id)throw new FlowError('运行记录 ID 不匹配');
21
- if(result.logOwner!==owner&&!(allowLegacy&&result.logOwner===undefined))throw new FlowError('运行记录不属于此项目');
22
- return result;
23
- }
24
- async function list(){
25
- let entries;try{entries=await readdir(root,{withFileTypes:true});}catch(error){if(error.code==='ENOENT')return [];throw error;}
26
- const results=[];
27
- for(const entry of entries){
28
- if(!entry.isDirectory()||!validId(entry.name))continue;
29
- try{
30
- const record=await read(entry.name);
31
- if(typeof record.workflowId==='string'&&['succeeded','failed','cancelled','stopped'].includes(record.status)&&Number.isFinite(Date.parse(record.finishedAt)))results.push(record);
32
- }catch(error){if(error.code==='EACCES'||error.code==='EPERM')throw error;}
33
- }
34
- return results.sort((a,b)=>Date.parse(b.finishedAt)-Date.parse(a.finishedAt)||b.id.localeCompare(a.id));
35
- }
36
- return {
37
- list,read,owner,
38
- async markdown(id){
39
- const dir=location(id);const stat=await lstat(dir);if(!stat.isDirectory()||stat.isSymbolicLink())throw new FlowError('日志目录无效');
40
- const run=await read(id),file=path.join(dir,'run.md');if(await regular(file))return readFile(file,'utf8');
41
- return markdownHeader(run)+(run.events??[]).map(markdownEvent).join('')+markdownResult(run);
42
- },
43
- prune(keepRuns,protectedIds){
44
- const job=cleaning.then(async()=>{
45
- if(keepRuns===null)return [];
46
- validateKeepRuns(keepRuns);
47
- const ended=await list(),removed=[];
48
- for(const run of ended.slice(keepRuns)){
49
- if(protectedIds.has(run.id))continue;
50
- const dir=location(run.id);
51
- // 只删除本工具的三个日志文件;不递归删除目录或插件生成的业务结果。
52
- for(const name of ['run.md','events.jsonl','result.json']){
53
- const file=path.join(dir,name);
54
- if(await regular(file))try{await unlink(file);}catch(error){if(error.code!=='ENOENT')throw error;}
55
- }
56
- try{await rmdir(dir);}catch(error){if(!['ENOTEMPTY','EEXIST','ENOENT'].includes(error.code))throw error;}
57
- removed.push(run.id);
58
- }
59
- return removed;
60
- });cleaning=job.catch(()=>{});return job;
61
- },
62
- };
63
- }