dsh-plugin-agentflow 0.2.3 → 0.3.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 +18 -11
- package/lib/client.js +362 -2
- package/package.json +13 -4
- package/runtime/boards.mjs +35 -1
- package/scripts/build-client.mjs +26 -14
- package/scripts/sync-runtime.mjs +26 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-agentflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "DeepSeek Harness tool plugin: run AgentFlow (material-canvas agent orchestrator) boards from the model",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"bugs": "https://github.com/CC19990113/dsh-plugin-agentflow/issues",
|
|
29
29
|
"main": "index.js",
|
|
30
30
|
"scripts": {
|
|
31
|
-
"check": "node --check index.js && node --check lib/bridge.js && node --check scripts/sync-runtime.mjs",
|
|
32
|
-
"test": "node test/bridge.selftest.mjs",
|
|
31
|
+
"check": "node --check index.js && node --check lib/bridge.js && node --check scripts/sync-runtime.mjs && tsc --noEmit",
|
|
32
|
+
"test": "node test/bridge.selftest.mjs && node test/client.selftest.mjs",
|
|
33
33
|
"build": "node scripts/build-client.mjs",
|
|
34
34
|
"sync": "node scripts/sync-runtime.mjs"
|
|
35
35
|
},
|
|
@@ -61,7 +61,16 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"
|
|
64
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
65
|
+
"@types/react": "^18.3",
|
|
66
|
+
"@types/react-dom": "^18.3",
|
|
67
|
+
"@vitejs/plugin-react": "^6.1.2",
|
|
68
|
+
"@xyflow/react": "^12.12.0",
|
|
69
|
+
"antd": "^6.6.6",
|
|
70
|
+
"lucide-react": "^1.60.0",
|
|
71
|
+
"tailwindcss": "^4.3.3",
|
|
72
|
+
"typescript": "^7.0.2",
|
|
73
|
+
"vite": "^8.3.4"
|
|
65
74
|
},
|
|
66
75
|
"exports": {
|
|
67
76
|
".": "./index.js",
|
package/runtime/boards.mjs
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import fs from 'node:fs/promises';
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
import { dataDir, writeJsonAtomic, exclusive } from './store.mjs';
|
|
16
|
+
import { orchestratorPersona, orchestratorCriteria } from './preset-gen.mjs';
|
|
16
17
|
|
|
17
18
|
export const FLOW_BOARD_ID = 'flow-agentflow';
|
|
18
19
|
|
|
@@ -247,6 +248,38 @@ function slugify(name) {
|
|
|
247
248
|
return s || 'board';
|
|
248
249
|
}
|
|
249
250
|
|
|
251
|
+
/**
|
|
252
|
+
* 新建画布的默认根节点(调度卡 / 对话入口)。
|
|
253
|
+
*
|
|
254
|
+
* 产品约定:画布必须有一张且只有一张调度根节点,它承载人格、是对话入口。
|
|
255
|
+
* 新建时直接给一张,用户不用先手动派生再改类型;子节点仍然由用户自己派生。
|
|
256
|
+
* 根节点在后端删不掉(DELETE 命中 orchestrator 返回 400),所以不会退化成空画布。
|
|
257
|
+
*/
|
|
258
|
+
function defaultRootNode(name) {
|
|
259
|
+
return {
|
|
260
|
+
id: 'orchestrator',
|
|
261
|
+
label: `${name} · 调度`,
|
|
262
|
+
type: 'orchestrator',
|
|
263
|
+
provider: '',
|
|
264
|
+
model: '',
|
|
265
|
+
reasoningEffort: '',
|
|
266
|
+
fallback: [],
|
|
267
|
+
fallbackEfforts: {},
|
|
268
|
+
task: orchestratorPersona({ name }),
|
|
269
|
+
criteria: orchestratorCriteria(),
|
|
270
|
+
skills: [],
|
|
271
|
+
tools: [],
|
|
272
|
+
verify: '',
|
|
273
|
+
content: '',
|
|
274
|
+
status: 'idle',
|
|
275
|
+
usage: null,
|
|
276
|
+
error: null,
|
|
277
|
+
verdict: null,
|
|
278
|
+
x: 80,
|
|
279
|
+
y: 80,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
250
283
|
/** 新建画布:presetId 有值就用它当 id(冲突报 409),否则由名字生成唯一 id。 */
|
|
251
284
|
export async function createBoard({ name, presetId } = {}) {
|
|
252
285
|
const safeName = String(name || '').trim() || '未命名画布';
|
|
@@ -273,7 +306,8 @@ export async function createBoard({ name, presetId } = {}) {
|
|
|
273
306
|
}
|
|
274
307
|
|
|
275
308
|
const now = Date.now();
|
|
276
|
-
|
|
309
|
+
// 新建画布默认带一张调度根节点(对话入口),子节点由用户自己派生
|
|
310
|
+
const board = { id, name: safeName, nodes: [defaultRootNode(safeName)], edges: [] };
|
|
277
311
|
const meta = { id, name: safeName, presetId: wantedPreset, createdAt: now, updatedAt: now };
|
|
278
312
|
await writeJsonAtomic(boardFile(id), board);
|
|
279
313
|
await writeIndex({
|
package/scripts/build-client.mjs
CHANGED
|
@@ -3,27 +3,36 @@
|
|
|
3
3
|
// DSH 的 web 加载器用 window.__ModuleLoader__.load({ id, factory }) 登记 client bundle,
|
|
4
4
|
// 求值时调用 factory(require):模块解析函数由加载器注入(没有全局 require),返回值
|
|
5
5
|
// 就是插件模块的导出。官方 dsh-context 由 tsdown 的 banner/intro/footer 生成同一形态,
|
|
6
|
-
//
|
|
7
|
-
|
|
6
|
+
// 所以这里让 Vite 打 CJS(react 等留 external),外面再套这层 factory。
|
|
7
|
+
//
|
|
8
|
+
// 构建细节在 vite.config.ts:单入口 src/index.tsx、lib cjs、产物 .client.build.cjs。
|
|
9
|
+
// 本脚本只负责调用 Vite API、套 wrapper、写 lib/client.js。
|
|
10
|
+
import { gzipSync } from 'node:zlib';
|
|
8
11
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
9
12
|
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { build, createLogger } from 'vite';
|
|
10
15
|
|
|
11
|
-
const root = path.dirname(
|
|
16
|
+
const root = path.dirname(fileURLToPath(import.meta.url));
|
|
12
17
|
const dir = path.resolve(root, '..');
|
|
18
|
+
const bundlePath = path.join(dir, '.client.build.cjs');
|
|
19
|
+
|
|
20
|
+
// 产物直接落在仓库根(与旧 esbuild 一致);Vite 对 outDir === root 总会提示一句,
|
|
21
|
+
// 而 emptyOutDir 已关、不会覆盖源码 —— 过滤这一条,别让有用告警被它淹没。
|
|
22
|
+
const logger = createLogger('warn');
|
|
23
|
+
const warn = logger.warn.bind(logger);
|
|
24
|
+
logger.warn = (msg, options) => {
|
|
25
|
+
if (typeof msg === 'string' && msg.includes('build.outDir must not be the same directory of root')) return;
|
|
26
|
+
warn(msg, options);
|
|
27
|
+
};
|
|
13
28
|
|
|
14
29
|
await build({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
platform: 'browser',
|
|
19
|
-
jsx: 'automatic',
|
|
20
|
-
minify: true,
|
|
21
|
-
external: ['react', 'react-dom', 'react/jsx-runtime', '@deepseek-ai/*'],
|
|
22
|
-
outfile: path.join(dir, '.client.build.cjs'),
|
|
23
|
-
logLevel: 'warning',
|
|
30
|
+
configFile: path.join(dir, 'vite.config.ts'),
|
|
31
|
+
logLevel: 'warn',
|
|
32
|
+
customLogger: logger,
|
|
24
33
|
});
|
|
25
34
|
|
|
26
|
-
const body = await readFile(
|
|
35
|
+
const body = await readFile(bundlePath, 'utf8');
|
|
27
36
|
|
|
28
37
|
// 与官方 tsdown 的 banner / intro / footer 逐字一致
|
|
29
38
|
const wrapped = `window.__ModuleLoader__.load({ id: "dsh-plugin-agentflow", factory: (require) => {
|
|
@@ -33,4 +42,7 @@ ${body}return module.exports; } });
|
|
|
33
42
|
|
|
34
43
|
await mkdir(path.join(dir, 'lib'), { recursive: true });
|
|
35
44
|
await writeFile(path.join(dir, 'lib/client.js'), wrapped);
|
|
36
|
-
|
|
45
|
+
|
|
46
|
+
const bytes = Buffer.byteLength(wrapped);
|
|
47
|
+
const gzipped = gzipSync(wrapped).length;
|
|
48
|
+
console.log(`lib/client.js ${(bytes / 1024).toFixed(1)} KB(gzip ${(gzipped / 1024).toFixed(1)} KB)`);
|
package/scripts/sync-runtime.mjs
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sync-runtime.mjs —— 把本机 AgentFlow 引擎快照进插件的 runtime/。
|
|
2
|
+
// sync-runtime.mjs —— 把本机 AgentFlow 引擎快照进插件的 runtime/ 与 src/canvas/。
|
|
3
3
|
//
|
|
4
4
|
// npm run sync
|
|
5
5
|
// AGENTFLOW_SRC=~/src/agent-flow npm run sync
|
|
6
6
|
//
|
|
7
|
-
// 只收运行时真正需要的文件:
|
|
7
|
+
// runtime/ 只收运行时真正需要的文件:
|
|
8
8
|
// - 顶层 *.mjs(server / engine / llm / boards / preset-gen …)
|
|
9
9
|
// - index.html(单文件旧版前端兜底)
|
|
10
10
|
// - web/dist/**(React 版前端构建产物)
|
|
11
|
-
// 不收 data/(运行时数据)、node_modules/、test
|
|
11
|
+
// 不收 data/(运行时数据)、node_modules/、test/。
|
|
12
12
|
//
|
|
13
|
-
//
|
|
13
|
+
// src/canvas/ 收 React 版前端的源码(web/src/**):画布不再走 iframe,而是编进
|
|
14
|
+
// 客户端 bundle、由 src/host/CanvasHost 直接渲染,所以源码要随插件一起走。
|
|
15
|
+
//
|
|
16
|
+
// 两处都是清空重建,保证与引擎来源一一对应,不残留旧文件。
|
|
14
17
|
|
|
15
18
|
import fs from 'node:fs/promises';
|
|
16
19
|
import os from 'node:os';
|
|
@@ -19,11 +22,15 @@ import { fileURLToPath } from 'node:url';
|
|
|
19
22
|
|
|
20
23
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
24
|
const DEST = path.join(ROOT, 'runtime');
|
|
25
|
+
const CANVAS_DEST = path.join(ROOT, 'src', 'canvas');
|
|
22
26
|
const SRC = path.resolve(process.env.AGENTFLOW_SRC || path.join(os.homedir(), '.dsh', 'agent-flow'));
|
|
23
27
|
|
|
24
28
|
/** 少了一个就跑不起来,宁可同步失败也别生成半残的 runtime */
|
|
25
29
|
const REQUIRED = ['server.mjs', 'engine.mjs', 'boards.mjs', 'llm.mjs', 'index.html', 'web/dist/index.html'];
|
|
26
30
|
|
|
31
|
+
/** 画布源码:内联渲染后同样缺一不可 */
|
|
32
|
+
const REQUIRED_CANVAS = ['web/src/App.tsx', 'web/src/main.tsx', 'web/src/index.css'];
|
|
33
|
+
|
|
27
34
|
function exists(file) {
|
|
28
35
|
return fs.access(file).then(() => true, () => false);
|
|
29
36
|
}
|
|
@@ -56,6 +63,11 @@ async function main() {
|
|
|
56
63
|
throw new Error(`引擎缺少必需文件:${path.join(SRC, name)}(用 AGENTFLOW_SRC 指定引擎目录)`);
|
|
57
64
|
}
|
|
58
65
|
}
|
|
66
|
+
for (const name of REQUIRED_CANVAS) {
|
|
67
|
+
if (!(await exists(path.join(SRC, name)))) {
|
|
68
|
+
throw new Error(`引擎缺少画布源码:${path.join(SRC, name)}(用 AGENTFLOW_SRC 指定引擎目录)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
59
71
|
|
|
60
72
|
// 清空重建:runtime/ 是生成物,不手改
|
|
61
73
|
await fs.rm(DEST, { recursive: true, force: true });
|
|
@@ -76,6 +88,16 @@ async function main() {
|
|
|
76
88
|
console.log(` 引擎:${mjs.length} 个 .mjs`);
|
|
77
89
|
console.log(' 前端:index.html + web/dist/**');
|
|
78
90
|
console.log(` 合计:${stats.files} 个文件,${mb(stats.bytes)}`);
|
|
91
|
+
|
|
92
|
+
// 清空重建:src/canvas/ 是 web/src 的镜像,宿主桥接改动请在引擎源码里改
|
|
93
|
+
await fs.rm(CANVAS_DEST, { recursive: true, force: true });
|
|
94
|
+
await fs.mkdir(CANVAS_DEST, { recursive: true });
|
|
95
|
+
await fs.cp(path.join(SRC, 'web', 'src'), CANVAS_DEST, { recursive: true });
|
|
96
|
+
|
|
97
|
+
const canvasStats = await measure(CANVAS_DEST);
|
|
98
|
+
console.log(`画布源码已生成:${CANVAS_DEST}`);
|
|
99
|
+
console.log(` 来源:${path.join(SRC, 'web', 'src')}`);
|
|
100
|
+
console.log(` 合计:${canvasStats.files} 个文件,${mb(canvasStats.bytes)}`);
|
|
79
101
|
}
|
|
80
102
|
|
|
81
103
|
main().catch((err) => {
|