@yanglaofish/fishtools 0.4.0 → 0.5.1
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 +33 -5
- package/bin/fst.js +1 -1
- package/manual/README.md +33 -5
- package/manual/docs/deployment.md +3 -3
- package/manual/docs/kernel-generation.md +97 -0
- package/manual/docs/modules.md +57 -0
- package/manual/docs/plugin-development.md +120 -10
- package/manual/docs/releases.md +24 -0
- package/manual/docs/research/2026-10-11-plugin-kernel-capabilities.md +6 -6
- package/manual/docs/roadmap.md +14 -3
- package/manual/docs/superpowers/plans/2026-10-09-workflow-mvp.md +5 -5
- package/manual/docs/superpowers/plans/2026-10-11-public-plugin-fixes.md +2 -2
- package/manual/docs/superpowers/plans/2026-10-12-kernel-generation.md +22 -0
- package/manual/docs/superpowers/plans/2026-10-12-modular-kernel.md +40 -0
- package/manual/docs/superpowers/reviews/2026-10-12-kernel-generation-review.md +27 -0
- package/manual/docs/superpowers/reviews/2026-10-12-modular-kernel-review.md +32 -0
- package/manual/docs/superpowers/reviews/2026-10-13-0.5.0-followup-review.md +67 -0
- package/manual/docs/superpowers/reviews/2026-10-13-kernel-runtime-release-review.md +19 -0
- package/manual/docs/superpowers/specs/2026-10-12-action-network-policy-design.md +52 -0
- package/manual/docs/superpowers/specs/2026-10-12-kernel-generation-design.md +38 -0
- package/manual/docs/superpowers/specs/2026-10-12-modular-kernel-design.md +70 -0
- package/manual/docs/superpowers/specs/2026-10-12-plugin-readiness-design.md +21 -0
- package/manual/docs/superpowers/specs/2026-10-12-runtime-maintenance-granularity-design.md +35 -0
- package/manual/docs/superpowers/specs/2026-10-12-workflow-variation-design.md +19 -0
- package/manual/docs/verification.md +55 -2
- package/manual/packages/core/README.md +44 -0
- package/manual/packages/core/package.json +38 -0
- package/manual/packages/core/src/application.js +251 -85
- package/manual/packages/core/src/bundle.js +2 -0
- package/manual/packages/core/src/development-watch-worker.js +69 -0
- package/manual/packages/core/src/development.js +146 -0
- package/manual/packages/core/src/environments.js +2 -0
- package/manual/packages/core/src/export-snapshot.js +42 -0
- package/manual/packages/core/src/plugin-update.js +108 -0
- package/manual/packages/core/src/process.js +2 -0
- package/manual/packages/core/src/resources.js +31 -0
- package/manual/packages/core/src/version.js +2 -0
- package/manual/packages/fst-cli/LICENSE +21 -0
- package/manual/packages/fst-cli/README.md +59 -0
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/bin/fst.js +55 -23
- package/manual/packages/fst-cli/package.json +50 -0
- package/manual/packages/fst-cli/src/development.js +53 -0
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/model.js +1 -1
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/navigation.js +1 -1
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/presentation.js +9 -1
- package/manual/packages/fst-cli/src/terminal/run-feed.js +119 -0
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/run-viewer.js +9 -7
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/schema.js +1 -1
- package/manual/packages/{core → fst-cli}/src/terminal/session.js +137 -9
- package/manual/packages/{core → fst-cli}/src/terminal/view.js +20 -3
- package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/workflow-menu.js +17 -2
- package/manual/packages/fst-devtools/LICENSE +21 -0
- package/manual/packages/fst-devtools/README.md +28 -0
- package/manual/packages/fst-devtools/package.json +29 -0
- package/{node_modules/@mytools/core → manual/packages/fst-devtools}/src/documentation.js +3 -1
- package/manual/packages/fst-devtools/src/plugin-quality.js +136 -0
- package/{node_modules/@mytools/core → manual/packages/fst-devtools}/src/plugin-tools.js +24 -5
- package/manual/packages/fst-logs/LICENSE +21 -0
- package/manual/packages/fst-logs/README.md +67 -0
- package/manual/packages/fst-logs/package.json +26 -0
- package/manual/packages/fst-logs/src/log-bounds.js +91 -0
- package/manual/packages/fst-logs/src/run-logs.js +224 -0
- package/manual/packages/fst-logs/src/run-preview.js +18 -0
- package/manual/packages/fst-logs/src/step-summaries.js +28 -0
- package/manual/packages/fst-platform/LICENSE +21 -0
- package/manual/packages/fst-platform/README.md +73 -0
- package/manual/packages/fst-platform/package.json +33 -0
- package/manual/packages/fst-platform/src/bundle.js +17 -0
- package/manual/packages/fst-platform/src/environments.js +67 -0
- package/manual/packages/fst-platform/src/file-probe-engine.js +183 -0
- package/manual/packages/fst-platform/src/file-probe-worker.js +43 -0
- package/manual/packages/fst-platform/src/file-probe.js +6 -0
- package/manual/packages/fst-platform/src/json.js +5 -0
- package/manual/packages/fst-platform/src/network.js +199 -0
- package/manual/packages/fst-platform/src/npm.js +11 -0
- package/manual/packages/fst-platform/src/process.js +110 -0
- package/manual/packages/fst-plugin/LICENSE +21 -0
- package/manual/packages/fst-plugin/README.md +29 -0
- package/manual/packages/fst-plugin/package.json +29 -0
- package/{node_modules/@mytools/core → manual/packages/fst-plugin}/src/contracts.js +30 -3
- package/manual/packages/fst-plugin/src/host.js +235 -0
- package/manual/packages/fst-plugin/src/loader.js +21 -0
- package/manual/packages/fst-plugin/src/privileges.js +91 -0
- package/{node_modules/@mytools/core → manual/packages/fst-plugin}/src/runtime.js +27 -8
- package/manual/packages/fst-workflow/LICENSE +21 -0
- package/manual/packages/fst-workflow/README.md +28 -0
- package/manual/packages/fst-workflow/package.json +24 -0
- package/{node_modules/@mytools/core → manual/packages/fst-workflow}/src/control.js +3 -2
- package/manual/packages/{core → fst-workflow}/src/engine.js +2 -2
- package/manual/packages/fst-workspace/LICENSE +21 -0
- package/manual/packages/fst-workspace/README.md +32 -0
- package/manual/packages/fst-workspace/package.json +34 -0
- package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/bundle-workspace.js +1 -1
- package/manual/packages/fst-workspace/src/index.js +3 -0
- package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/plugins.js +6 -36
- package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/workflows.js +40 -7
- package/manual/packages/pack/LICENSE +21 -0
- package/manual/packages/pack/README.md +31 -0
- package/manual/packages/pack/archive.js +15 -0
- package/manual/packages/pack/dependencies.js +105 -0
- package/manual/packages/pack/environments.js +50 -0
- package/manual/packages/pack/index.js +103 -0
- package/manual/packages/pack/package.json +29 -0
- package/manual/packages/pack/runtime.js +61 -0
- package/manual/packages/sdk/README.md +31 -0
- package/manual/packages/sdk/conditions.js +1 -0
- package/manual/packages/sdk/package.json +23 -1
- package/manual/plugins/web/README.md +29 -0
- package/manual/tests/application.test.js +6 -4
- package/node_modules/@mytools/core/README.md +44 -0
- package/node_modules/@mytools/core/package.json +17 -17
- package/node_modules/@mytools/core/src/application.js +251 -85
- package/node_modules/@mytools/core/src/bundle.js +2 -17
- package/node_modules/@mytools/core/src/development-watch-worker.js +69 -0
- package/node_modules/@mytools/core/src/development.js +146 -0
- package/node_modules/@mytools/core/src/environments.js +2 -67
- package/node_modules/@mytools/core/src/export-snapshot.js +42 -0
- package/node_modules/@mytools/core/src/plugin-update.js +108 -0
- package/node_modules/@mytools/core/src/process.js +2 -80
- package/node_modules/@mytools/core/src/version.js +1 -1
- package/node_modules/@mytools/fst-cli/LICENSE +21 -0
- package/node_modules/@mytools/fst-cli/README.md +59 -0
- package/node_modules/@mytools/fst-cli/bin/fst.js +124 -0
- package/node_modules/@mytools/fst-cli/package.json +50 -0
- package/node_modules/@mytools/fst-cli/src/cli-run.js +12 -0
- package/node_modules/@mytools/fst-cli/src/development.js +53 -0
- package/node_modules/@mytools/fst-cli/src/terminal/conditions.js +32 -0
- package/node_modules/@mytools/fst-cli/src/terminal/log-viewer.js +45 -0
- package/node_modules/@mytools/fst-cli/src/terminal/model.js +104 -0
- package/node_modules/@mytools/fst-cli/src/terminal/navigation.js +17 -0
- package/node_modules/@mytools/fst-cli/src/terminal/presentation.js +50 -0
- package/node_modules/@mytools/fst-cli/src/terminal/run-feed.js +119 -0
- package/node_modules/@mytools/fst-cli/src/terminal/run-presentation.js +60 -0
- package/node_modules/@mytools/fst-cli/src/terminal/run-viewer.js +106 -0
- package/node_modules/@mytools/fst-cli/src/terminal/schema.js +52 -0
- package/node_modules/@mytools/{core → fst-cli}/src/terminal/session.js +137 -9
- package/node_modules/@mytools/fst-cli/src/terminal/theme.js +29 -0
- package/node_modules/@mytools/{core → fst-cli}/src/terminal/view.js +20 -3
- package/node_modules/@mytools/fst-cli/src/terminal/workflow-menu.js +65 -0
- package/node_modules/@mytools/fst-devtools/LICENSE +21 -0
- package/node_modules/@mytools/fst-devtools/README.md +28 -0
- package/node_modules/@mytools/fst-devtools/package.json +29 -0
- package/node_modules/@mytools/fst-devtools/src/documentation.js +67 -0
- package/node_modules/@mytools/fst-devtools/src/plugin-quality.js +136 -0
- package/node_modules/@mytools/fst-devtools/src/plugin-tools.js +152 -0
- package/node_modules/@mytools/fst-logs/LICENSE +21 -0
- package/node_modules/@mytools/fst-logs/README.md +67 -0
- package/node_modules/@mytools/fst-logs/package.json +26 -0
- package/node_modules/@mytools/fst-logs/src/log-bounds.js +91 -0
- package/node_modules/@mytools/fst-logs/src/markdown-log.js +55 -0
- package/node_modules/@mytools/fst-logs/src/run-logs.js +224 -0
- package/node_modules/@mytools/fst-logs/src/run-preview.js +18 -0
- package/node_modules/@mytools/fst-logs/src/step-summaries.js +28 -0
- package/node_modules/@mytools/fst-platform/LICENSE +21 -0
- package/node_modules/@mytools/fst-platform/README.md +73 -0
- package/node_modules/@mytools/fst-platform/package.json +33 -0
- package/node_modules/@mytools/fst-platform/src/bundle.js +17 -0
- package/node_modules/@mytools/fst-platform/src/environments.js +67 -0
- package/node_modules/@mytools/fst-platform/src/file-probe-engine.js +183 -0
- package/node_modules/@mytools/fst-platform/src/file-probe-worker.js +43 -0
- package/node_modules/@mytools/fst-platform/src/file-probe.js +6 -0
- package/node_modules/@mytools/fst-platform/src/json.js +5 -0
- package/node_modules/@mytools/fst-platform/src/network.js +199 -0
- package/node_modules/@mytools/fst-platform/src/npm.js +11 -0
- package/node_modules/@mytools/fst-platform/src/process.js +110 -0
- package/node_modules/@mytools/fst-plugin/LICENSE +21 -0
- package/node_modules/@mytools/fst-plugin/README.md +29 -0
- package/node_modules/@mytools/fst-plugin/package.json +29 -0
- package/node_modules/@mytools/fst-plugin/src/contracts.js +86 -0
- package/node_modules/@mytools/fst-plugin/src/host.js +235 -0
- package/node_modules/@mytools/fst-plugin/src/loader.js +21 -0
- package/node_modules/@mytools/fst-plugin/src/privileges.js +91 -0
- package/{manual/packages/core → node_modules/@mytools/fst-plugin}/src/runtime.js +27 -8
- package/node_modules/@mytools/fst-workflow/LICENSE +21 -0
- package/node_modules/@mytools/fst-workflow/README.md +28 -0
- package/node_modules/@mytools/fst-workflow/package.json +24 -0
- package/node_modules/@mytools/fst-workflow/src/control.js +64 -0
- package/node_modules/@mytools/{core → fst-workflow}/src/engine.js +2 -2
- package/node_modules/@mytools/fst-workspace/LICENSE +21 -0
- package/node_modules/@mytools/fst-workspace/README.md +32 -0
- package/node_modules/@mytools/fst-workspace/package.json +34 -0
- package/node_modules/@mytools/fst-workspace/src/bundle-workspace.js +39 -0
- package/node_modules/@mytools/fst-workspace/src/index.js +3 -0
- package/node_modules/@mytools/fst-workspace/src/plugin-installations.js +84 -0
- package/node_modules/@mytools/fst-workspace/src/plugins.js +35 -0
- package/node_modules/@mytools/fst-workspace/src/project.js +29 -0
- package/node_modules/@mytools/fst-workspace/src/workflows.js +150 -0
- package/node_modules/@mytools/fst-workspace/templates/control-demo.yaml +47 -0
- package/node_modules/@mytools/fst-workspace/templates/hello.yaml +35 -0
- package/node_modules/@mytools/fst-workspace/templates/python-check.yaml +26 -0
- package/node_modules/@mytools/fst-workspace/templates/system-check.yaml +11 -0
- package/node_modules/@mytools/pack/README.md +31 -0
- package/node_modules/@mytools/pack/dependencies.js +7 -2
- package/node_modules/@mytools/pack/environments.js +1 -1
- package/node_modules/@mytools/pack/index.js +33 -25
- package/node_modules/@mytools/pack/package.json +29 -1
- package/node_modules/@mytools/sdk/README.md +31 -0
- package/node_modules/@mytools/sdk/conditions.js +1 -0
- package/node_modules/@mytools/sdk/diagnostics.js +27 -0
- package/node_modules/@mytools/sdk/package.json +23 -1
- package/node_modules/@mytools/sdk/schema-path.js +31 -0
- package/node_modules/@mytools/sdk/validation.js +78 -0
- package/node_modules/@mytools/web/README.md +29 -0
- package/node_modules/@mytools/web/package.json +7 -0
- package/node_modules/@mytools/web/public/THIRD_PARTY_NOTICES.txt +1440 -1240
- package/node_modules/@mytools/web/public/assets/index-BaVzHycP.js +70 -0
- package/node_modules/@mytools/web/public/assets/index-DA8Ae92t.css +1 -0
- package/node_modules/@mytools/web/public/index.html +2 -2
- package/node_modules/@mytools/web/server.js +24 -17
- package/node_modules/@mytools/web/src/model.js +84 -0
- package/node_modules/acorn/CHANGELOG.md +1012 -0
- package/node_modules/acorn/LICENSE +21 -0
- package/node_modules/acorn/README.md +309 -0
- package/node_modules/acorn/bin/acorn +4 -0
- package/node_modules/acorn/dist/acorn.d.mts +897 -0
- package/node_modules/acorn/dist/acorn.d.ts +897 -0
- package/node_modules/acorn/dist/acorn.js +6370 -0
- package/node_modules/acorn/dist/acorn.mjs +6341 -0
- package/node_modules/acorn/dist/bin.js +90 -0
- package/node_modules/acorn/package.json +50 -0
- package/node_modules/{minipass-flush/node_modules/minipass → tar}/node_modules/yallist/README.md +8 -7
- package/node_modules/tar/node_modules/yallist/package.json +68 -0
- package/node_modules/yallist/README.md +7 -8
- package/node_modules/yallist/package.json +12 -51
- package/package.json +131 -115
- package/manual/packages/core/src/host.js +0 -139
- package/manual/packages/core/src/terminal/run-feed.js +0 -78
- package/node_modules/@mytools/core/src/host.js +0 -139
- package/node_modules/@mytools/core/src/run-logs.js +0 -63
- package/node_modules/@mytools/core/src/terminal/run-feed.js +0 -78
- package/node_modules/@mytools/web/public/assets/index-DT2B4tMV.js +0 -70
- package/node_modules/@mytools/web/public/assets/index-vuZvrUTT.css +0 -1
- package/node_modules/minipass-flush/node_modules/minipass/node_modules/yallist/package.json +0 -29
- /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/cli-run.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/conditions.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/log-viewer.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/run-presentation.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-cli}/src/terminal/theme.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-logs}/src/markdown-log.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/plugin-installations.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/src/project.js +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/control-demo.yaml +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/hello.yaml +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/python-check.yaml +0 -0
- /package/{node_modules/@mytools/core → manual/packages/fst-workspace}/templates/system-check.yaml +0 -0
- /package/{node_modules/@mytools/core/src → manual/packages/sdk}/diagnostics.js +0 -0
- /package/{node_modules/@mytools/core/src → manual/packages/sdk}/schema-path.js +0 -0
- /package/{node_modules/@mytools/core/src → manual/packages/sdk}/validation.js +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/LICENSE.md +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/commonjs/index.js +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/commonjs/index.js.map +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/commonjs/package.json +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/esm/index.d.ts +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/esm/index.d.ts.map +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/esm/index.js +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/esm/index.js.map +0 -0
- /package/node_modules/{yallist → tar/node_modules/yallist}/dist/esm/package.json +0 -0
- /package/node_modules/{minipass-flush/node_modules/minipass/node_modules/yallist → yallist}/LICENSE +0 -0
- /package/node_modules/{minipass-flush/node_modules/minipass/node_modules/yallist → yallist}/iterator.js +0 -0
- /package/node_modules/{minipass-flush/node_modules/minipass/node_modules/yallist → yallist}/yallist.js +0 -0
|
@@ -1,139 +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 {prepareEnvironments,platformWarnings} from './environments.js';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import {mkdir} from 'node:fs/promises';
|
|
9
|
-
import {createHash} from 'node:crypto';
|
|
10
|
-
import {createRuntime} from './runtime.js';
|
|
11
|
-
import {pluginError} from './diagnostics.js';
|
|
12
|
-
|
|
13
|
-
export async function createHost({ plugins = [], config = {}, services = {}, environmentOverrides={},runtimeEnabled=true } = {}) {
|
|
14
|
-
const ctx = new Context();
|
|
15
|
-
const actions = new Map();
|
|
16
|
-
const definitions = new Map();
|
|
17
|
-
const extensions = new Map();
|
|
18
|
-
const serviceNames = new Set();
|
|
19
|
-
const commandNames=new Set(),commands=new Map();
|
|
20
|
-
function bindContext(context){
|
|
21
|
-
const bound={...services,...context},execute=bound.runProcess;
|
|
22
|
-
// 面向用户的输出与诊断日志共用持久化通道,只区分默认可见性。
|
|
23
|
-
bound.print=(message,data)=>Promise.resolve(bound.log?.(message,data,{audience:'user'}));
|
|
24
|
-
if(typeof execute==='function')bound.runProcess=(file,args,options={})=>{
|
|
25
|
-
const signal=context.signal&&options.signal?AbortSignal.any([context.signal,options.signal]):context.signal??options.signal;
|
|
26
|
-
return execute(file,args,{...options,signal,onOutput:async({stream,message})=>{
|
|
27
|
-
await bound.log?.(message,undefined,{stream});
|
|
28
|
-
await options.onOutput?.({stream,message});
|
|
29
|
-
}});
|
|
30
|
-
};
|
|
31
|
-
return bound;
|
|
32
|
-
}
|
|
33
|
-
async function pluginContext(plugin,action,context){
|
|
34
|
-
const bound=bindContext(context),application=services.application;
|
|
35
|
-
const projectDir=application?.projectDir;
|
|
36
|
-
// 固定长度摘要避免 scoped 包名分隔符、大小写碰撞及合法长包名超出文件名上限。
|
|
37
|
-
const stateDir=projectDir&&!application.project.readOnly?path.join(projectDir,'.flow','plugin-state',createHash('sha256').update(plugin.id).digest('hex')):undefined;
|
|
38
|
-
if(stateDir)await mkdir(stateDir,{recursive:true});
|
|
39
|
-
for(const message of platformWarnings(plugin))await bound.log?.(message,{code:'PLATFORM_MISMATCH'},{audience:'user'});
|
|
40
|
-
const environments=await prepareEnvironments(plugin,action,environmentOverrides[plugin.id],{cwd:projectDir??process.cwd()});
|
|
41
|
-
context.signal?.throwIfAborted();
|
|
42
|
-
return {...bound,projectDir,stateDir,environments};
|
|
43
|
-
}
|
|
44
|
-
for (const entry of plugins) {
|
|
45
|
-
const plugin = entry.plugin ?? entry;
|
|
46
|
-
validateDeclarations(plugin);validateCommands(plugin,commandNames);
|
|
47
|
-
if (!plugin.id || definitions.has(plugin.id)) throw new FlowError(`插件标识缺失或重复:${plugin.id}`, {code:'VALIDATION'});
|
|
48
|
-
if (!semver.satisfies(SDK_VERSION, plugin.sdk ?? '^0.1.0')) throw new FlowError(`插件 ${plugin.id} 与 SDK ${SDK_VERSION} 不兼容`, {code:'VALIDATION'});
|
|
49
|
-
for(const [name,action] of Object.entries(plugin.actions??{})){
|
|
50
|
-
if(typeof action?.run!=='function')throw new FlowError(`动作 ${plugin.id}:${name} 缺少执行函数`);
|
|
51
|
-
}
|
|
52
|
-
for(const [name,extension] of Object.entries(plugin.services??{})){
|
|
53
|
-
if(serviceNames.has(name))throw new FlowError(`服务重复:${name}`);
|
|
54
|
-
if(typeof extension!=='function')throw new FlowError(`服务 ${name} 缺少执行函数`);
|
|
55
|
-
serviceNames.add(name);
|
|
56
|
-
}
|
|
57
|
-
definitions.set(plugin.id, {...plugin, root:entry.root});
|
|
58
|
-
}
|
|
59
|
-
const settingsById=new Map();
|
|
60
|
-
for (const plugin of definitions.values()) {
|
|
61
|
-
for (const [id, range] of Object.entries(plugin.dependencies ?? {})) {
|
|
62
|
-
if (!definitions.has(id) || !semver.satisfies(definitions.get(id).version,range)) throw new FlowError(`插件 ${plugin.id} 缺少兼容依赖 ${id}@${range}`,{code:'VALIDATION'});
|
|
63
|
-
}
|
|
64
|
-
const settings = validate(plugin.config, config[plugin.id] ?? {}, `${plugin.id} 配置`);
|
|
65
|
-
settingsById.set(plugin.id,settings);
|
|
66
|
-
}
|
|
67
|
-
const runtime=createRuntime(ctx,definitions,{enabled:runtimeEnabled});
|
|
68
|
-
try{
|
|
69
|
-
for (const plugin of definitions.values()) {
|
|
70
|
-
const settings=settingsById.get(plugin.id);
|
|
71
|
-
runtime.register(plugin,settings,scope=>{
|
|
72
|
-
for(const [name,command] of Object.entries(plugin.commands??{})){
|
|
73
|
-
const action=command.action?plugin.actions[command.action]:command;
|
|
74
|
-
commands.set(name,{...command,name,plugin:plugin.id,input:action.input,output:action.output,description:command.description??action.description??action.title??name,config:settings});
|
|
75
|
-
scope.on('dispose',()=>commands.delete(name));
|
|
76
|
-
}
|
|
77
|
-
for (const [name, action] of Object.entries(plugin.actions ?? {})) {
|
|
78
|
-
const id = `${plugin.id}:${name}`;
|
|
79
|
-
if (typeof action.run !== 'function') throw new FlowError(`动作 ${id} 缺少执行函数`);
|
|
80
|
-
actions.set(id, {...action, id, plugin:plugin.id, config:settings, root:plugin.root});
|
|
81
|
-
scope.on('dispose', () => actions.delete(id));
|
|
82
|
-
}
|
|
83
|
-
for (const [name, extension] of Object.entries(plugin.services ?? {})) {
|
|
84
|
-
if (extensions.has(name)) throw new FlowError(`服务重复:${name}`);
|
|
85
|
-
extensions.set(name, {run:extension,plugin:plugin.id});
|
|
86
|
-
scope.on('dispose',()=>extensions.delete(name));
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
await runtime.start();
|
|
91
|
-
}catch(error){
|
|
92
|
-
try{await runtime.close();}catch(cleanup){if(cleanup.message!==error.message)throw new AggregateError([error,cleanup],`${error.message};${cleanup.message}`);}
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
definitions, actions,
|
|
97
|
-
warnings:[...definitions.values()].flatMap(plugin=>platformWarnings(plugin).map(message=>({plugin:plugin.id,message}))),
|
|
98
|
-
hasRuntime:runtime.hasRuntime,
|
|
99
|
-
runtimeStatus:()=>runtime.status(),
|
|
100
|
-
commands(){return [...commands.values()].map(({run,config,...metadata})=>structuredClone(metadata));},
|
|
101
|
-
async invokeCommand(name,input,context={}){
|
|
102
|
-
runtime.assertOpen();
|
|
103
|
-
const command=commands.get(name);if(!command)throw new FlowError(`命令不存在:/${name}`);
|
|
104
|
-
if(command.action)return this.invoke(`${command.plugin}:${command.action}`,input,context);
|
|
105
|
-
if(!context.ui)throw new FlowError(`交互命令 /${name} 需要在 TUI 中调用`);
|
|
106
|
-
const plugin=definitions.get(command.plugin);
|
|
107
|
-
try{
|
|
108
|
-
const getService=runtime.bindServices(plugin.id);
|
|
109
|
-
context.signal?.throwIfAborted();
|
|
110
|
-
const result=await command.run(validate(command.input??emptyObject,input,`/${name} 输入`),{...await pluginContext(plugin,undefined,context),getService,config:command.config,resourceDir:plugin.root});
|
|
111
|
-
context.signal?.throwIfAborted();
|
|
112
|
-
return validate(command.output,JSON.parse(JSON.stringify(result??{})),`/${name} 输出`,{useDefaults:false});
|
|
113
|
-
}catch(cause){throw pluginError(cause,{plugin:plugin.id,command:`/${name}`});}
|
|
114
|
-
},
|
|
115
|
-
catalog() { return [...actions.values()].map(({run,config,root,...metadata})=>structuredClone(metadata)); },
|
|
116
|
-
async invoke(id, input, context = {}) {
|
|
117
|
-
runtime.assertOpen();
|
|
118
|
-
const action = actions.get(id);
|
|
119
|
-
if (!action) throw new FlowError(`动作不存在:${id}`, {code:'VALIDATION'});
|
|
120
|
-
try{
|
|
121
|
-
const getService=runtime.bindServices(action.plugin);
|
|
122
|
-
context.signal?.throwIfAborted();
|
|
123
|
-
const result = await action.run(validate(action.input ?? emptyObject,input,`${id} 输入`), {...await pluginContext(definitions.get(action.plugin),action,context),getService, config:action.config, resourceDir:action.root});
|
|
124
|
-
context.signal?.throwIfAborted();
|
|
125
|
-
// 通过 JSON 边界验证返回值,避免函数、循环引用等进入工作流状态。
|
|
126
|
-
const encoded = JSON.stringify(result);
|
|
127
|
-
if (encoded === undefined) throw new FlowError(`${id} 输出必须可 JSON 序列化`);
|
|
128
|
-
return validate(action.output, JSON.parse(encoded),`${id} 输出`,{useDefaults:false});
|
|
129
|
-
}catch(cause){throw pluginError(cause,{plugin:action.plugin,action:id});}
|
|
130
|
-
},
|
|
131
|
-
async service(name,options) {
|
|
132
|
-
runtime.assertOpen();
|
|
133
|
-
if (!extensions.has(name)) throw new FlowError(`未安装提供 ${name} 的插件`);
|
|
134
|
-
const extension=extensions.get(name);runtime.assertActive(extension.plugin);
|
|
135
|
-
return extension.run(services,options);
|
|
136
|
-
},
|
|
137
|
-
close:()=>runtime.close(),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
@@ -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
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import {readableValue,readableError,readableTimeline} from './run-presentation.js';
|
|
2
|
-
import {runStatuses} from './run-viewer.js';
|
|
3
|
-
|
|
4
|
-
// 主面板按事件到达顺序追加;运行快照另存,配置菜单不会清掉执行记录。
|
|
5
|
-
export class RunFeed{
|
|
6
|
-
constructor(refresh){this.refresh=refresh;this.runs=new Map();this.entries=[];this.subscriptions=new Map();this.expanded=false;}
|
|
7
|
-
track({app,runId,title,input}){
|
|
8
|
-
if(this.runs.has(runId))return;
|
|
9
|
-
const record=app.getRun(runId),item={app,record,title,input,count:0,ended:false,steps:new Map(),catalog:new Map((app.catalog?.()??[]).map(action=>[action.id,action]))};
|
|
10
|
-
this.runs.set(runId,item);
|
|
11
|
-
this.entries.push({type:'start',item});
|
|
12
|
-
if(!this.subscriptions.has(app))this.subscriptions.set(app,app.subscribe(event=>{
|
|
13
|
-
const item=this.runs.get(event.runId);if(!item||item.app!==app)return;
|
|
14
|
-
this.update(item,event);this.release(app);
|
|
15
|
-
if(event.type==='log'&&!item.ended)this.scheduleRefresh();
|
|
16
|
-
else this.flushRefresh();
|
|
17
|
-
}));
|
|
18
|
-
this.update(item);this.release(app);this.refresh();
|
|
19
|
-
}
|
|
20
|
-
scheduleRefresh(){
|
|
21
|
-
if(this.refreshTimer)return;
|
|
22
|
-
this.refreshTimer=setTimeout(()=>{this.refreshTimer=null;this.refresh();},100);
|
|
23
|
-
}
|
|
24
|
-
flushRefresh(){
|
|
25
|
-
if(this.refreshTimer){clearTimeout(this.refreshTimer);this.refreshTimer=null;}
|
|
26
|
-
this.refresh();
|
|
27
|
-
}
|
|
28
|
-
update(item,event){
|
|
29
|
-
if(event?.type&&!['run.succeeded','run.failed','run.cancelled','run.stopped'].includes(event.type))item.record.events.push(event);
|
|
30
|
-
else{
|
|
31
|
-
const record=item.app.getRun(item.record.id);if(!record)return;
|
|
32
|
-
item.record={...record,events:[...(record.events??[])]};
|
|
33
|
-
}
|
|
34
|
-
const {record}=item,events=record.events.slice(item.count);item.count=record.events.length;
|
|
35
|
-
for(const event of events)if(event.type.startsWith('step.'))item.steps.set(event.callPath,{...item.steps.get(event.callPath),...event});
|
|
36
|
-
if(events.length){
|
|
37
|
-
const last=this.entries.at(-1);
|
|
38
|
-
if(last?.type==='events'&&last.item===item)last.events.push(...events);
|
|
39
|
-
else this.entries.push({type:'events',item,events});
|
|
40
|
-
}
|
|
41
|
-
if(record.status!=='running'&&!item.ended){item.ended=true;this.entries.push({type:'result',item});}
|
|
42
|
-
}
|
|
43
|
-
release(app){
|
|
44
|
-
if([...this.runs.values()].some(item=>item.app===app&&!item.ended))return;
|
|
45
|
-
this.subscriptions.get(app)?.();this.subscriptions.delete(app);
|
|
46
|
-
}
|
|
47
|
-
message(text){this.entries.push({type:'message',text});this.refresh();}
|
|
48
|
-
compactEvents(entry,seen){
|
|
49
|
-
const {item}=entry,rows=[];
|
|
50
|
-
for(const event of entry.events){
|
|
51
|
-
const key=`${item.record.id}\0${event.callPath}`;
|
|
52
|
-
if(event.type.startsWith('step.')&&!seen.has(key)){
|
|
53
|
-
seen.add(key);const latest=item.steps.get(event.callPath),action=item.catalog.get(latest.action);
|
|
54
|
-
const status=latest.type.slice(5),symbols={started:'●',succeeded:'✓',failed:'✗',cancelled:'×',skipped:'–',stopped:'■'};
|
|
55
|
-
const name=action?.title??latest.action??latest.stepId??latest.callPath;
|
|
56
|
-
rows.push(`${symbols[status]??'·'} ${name} · ${status==='started'?'执行中':runStatuses[status]??status}${latest.durationMs!==undefined?' · '+latest.durationMs+' ms':''}${action?.plugin?' · '+action.plugin:''} · ${latest.callPath}`);
|
|
57
|
-
}
|
|
58
|
-
if(event.type==='log'&&event.audience==='user')rows.push(`[${event.callPath??item.record.workflowId}] ${event.message??''}${event.data!==undefined?'\n'+readableValue(event.data):''}`);
|
|
59
|
-
if(event.type==='step.failed'&&![...item.steps.values()].some(step=>step.type==='step.failed'&&step.callPath?.startsWith(event.callPath+'/')))rows.push(`✗ ${event.callPath}\n${readableError(event.error??{message:event.message})}`);
|
|
60
|
-
if(event.type==='step.stopped'||event.type==='step.skipped')rows.push(`${event.callPath}:${event.reason??event.message??''}`);
|
|
61
|
-
}
|
|
62
|
-
return rows.length?`${item.title??item.record.workflowId} · ${item.record.id.slice(0,8)}\n${rows.join('\n')}`:'';
|
|
63
|
-
}
|
|
64
|
-
text(){const seen=new Set();return this.entries.map(entry=>{
|
|
65
|
-
if(entry.type==='message')return entry.text;
|
|
66
|
-
const {record,title,input}=entry.item,label=`${title??record.workflowId} · ${record.id.slice(0,8)}`;
|
|
67
|
-
if(entry.type==='start')return [
|
|
68
|
-
`› ${record.workflowId.startsWith('/')?record.workflowId:'/run '+record.workflowId} · ${label}`,
|
|
69
|
-
`状态:${runStatuses[record.status]??record.status}`,
|
|
70
|
-
this.expanded&&input&&Object.keys(input).length?'输入\n'+readableValue(input):'',
|
|
71
|
-
].filter(Boolean).join('\n');
|
|
72
|
-
if(entry.type==='events')return this.expanded?`${label}\n${readableTimeline(entry.events,{values:true})}`:this.compactEvents(entry,seen);
|
|
73
|
-
return [`结果 · ${label} · ${runStatuses[record.status]??record.status}`,
|
|
74
|
-
this.expanded&&record.output!==undefined?readableValue(record.output):'',record.error&&(this.expanded||![...entry.item.steps.values()].some(step=>step.type==='step.failed'))?readableError(record.error,{details:this.expanded}):'',record.reason??'',
|
|
75
|
-
].filter(Boolean).join('\n');
|
|
76
|
-
}).filter(Boolean).join('\n\n');}
|
|
77
|
-
close(){if(this.refreshTimer)clearTimeout(this.refreshTimer);this.refreshTimer=null;for(const unsubscribe of this.subscriptions.values())unsubscribe();this.subscriptions.clear();}
|
|
78
|
-
}
|