foliko 2.0.6 → 2.0.8

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 (53) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/docs/public-api.md +3 -3
  3. package/examples/workflow.js +62 -57
  4. package/package.json +2 -3
  5. package/plugins/core/scheduler/CHANGELOG.md +31 -0
  6. package/plugins/core/scheduler/index.js +576 -638
  7. package/plugins/core/workflow/context.js +941 -0
  8. package/plugins/core/workflow/engine.js +66 -0
  9. package/plugins/core/workflow/examples/01-basic.js +42 -0
  10. package/plugins/core/workflow/examples/01-basic.json +30 -0
  11. package/plugins/core/workflow/examples/02-choice.js +75 -0
  12. package/plugins/core/workflow/examples/02-choice.json +59 -0
  13. package/plugins/core/workflow/examples/03-chain-style.js +114 -0
  14. package/plugins/core/workflow/examples/03-each.json +41 -0
  15. package/plugins/core/workflow/examples/04-parallel.js +52 -0
  16. package/plugins/core/workflow/examples/04-parallel.json +51 -0
  17. package/plugins/core/workflow/examples/05-chain-style.json +68 -0
  18. package/plugins/core/workflow/examples/05-each.js +65 -0
  19. package/plugins/core/workflow/examples/06-script.js +82 -0
  20. package/plugins/core/workflow/examples/07-module-export.js +43 -0
  21. package/plugins/core/workflow/examples/08-default-export.js +29 -0
  22. package/plugins/core/workflow/examples/09-next-with-args.js +50 -0
  23. package/plugins/core/workflow/examples/10-logger.js +34 -0
  24. package/plugins/core/workflow/examples/11-storage.js +68 -0
  25. package/plugins/core/workflow/examples/simple.js +77 -0
  26. package/plugins/core/workflow/examples/simple.json +75 -0
  27. package/plugins/core/workflow/index.js +124 -58
  28. package/plugins/core/workflow/js-runner.js +318 -0
  29. package/plugins/core/workflow/json-runner.js +323 -0
  30. package/plugins/core/workflow/stages/action.js +211 -0
  31. package/plugins/core/workflow/stages/choice.js +74 -0
  32. package/plugins/core/workflow/stages/delay.js +73 -0
  33. package/plugins/core/workflow/stages/each.js +123 -0
  34. package/plugins/core/workflow/stages/parallel.js +69 -0
  35. package/plugins/core/workflow/stages/try.js +142 -0
  36. package/plugins/executors/data-splitter/index.js +3 -2
  37. package/plugins/memory/index.js +10 -3
  38. package/sandbox/check-context.js +5 -0
  39. package/sandbox/test-context.js +27 -0
  40. package/sandbox/test-fixes.js +40 -0
  41. package/sandbox/test-hello-js.js +46 -0
  42. package/skills/workflows/SKILL.md +715 -613
  43. package/src/agent/chat.js +8 -1
  44. package/src/agent/main.js +5 -1
  45. package/src/cli/ui/chat-ui.js +4 -3
  46. package/src/common/json-safe.js +20 -0
  47. package/src/framework/framework.js +58 -2
  48. package/src/index.js +10 -0
  49. package/src/plugin/base.js +7 -4
  50. package/src/plugin/manager.js +95 -1
  51. package/src/utils/sandbox.js +1 -1
  52. package/skills/workflows/workflow-troubleshooting/DEBUGGING.md +0 -197
  53. package/skills/workflows/workflow-troubleshooting/SKILL.md +0 -331
@@ -238,7 +238,8 @@
238
238
  "Bash(cmd //c \"D:\\\\\\\\tmp\\\\\\\\run-diag3.bat\")",
239
239
  "Bash(npm start *)",
240
240
  "Bash(xxd)",
241
- "Bash(python fix_ext2.py)"
241
+ "Bash(python fix_ext2.py)",
242
+ "Bash(npx vitest *)"
242
243
  ]
243
244
  }
244
245
  }
@@ -718,13 +718,13 @@ framework.workflows.list();
718
718
 
719
719
  // 获取单个工作流定义
720
720
  framework.workflows.get('my-workflow');
721
- // { name, description, steps: [...], type }
721
+ // { flow, version, stages: [...], description }
722
722
 
723
723
  // 检查工作流是否存在
724
724
  framework.workflows.has('my-workflow'); // true/false
725
725
 
726
- // 获取工作流引擎(用于创建步骤)
727
- framework.workflows.getEngine(); // WorkflowEngine 实例
726
+ // 获取工作流引擎(v2 FlowEngine 实例,实际执行路径)
727
+ framework.workflows.getEngine();
728
728
  ```
729
729
 
730
730
  #### 执行工作流
@@ -1,106 +1,111 @@
1
1
  /**
2
- * 工作流示例
3
- * 展示如何使用工作流引擎
2
+ * 工作流示例(v2 格式)
3
+ * 展示如何使用工作流引擎的 JSON / JS / 内联 schema 三种入口
4
4
  */
5
5
 
6
6
  const foliko = require('../src');
7
7
  const { WorkflowPlugin } = require('../plugins/core/workflow');
8
8
 
9
9
  async function main() {
10
- console.log('=== Workflow Example ===\n');
10
+ console.log('=== Workflow v2 Example ===\n');
11
11
 
12
12
  const framework = foliko.createApp({ debug: true });
13
13
 
14
14
  // 加载工作流插件
15
15
  await framework.loadPlugin(new WorkflowPlugin());
16
16
 
17
- console.log('\n--- Testing Script Step ---');
17
+ // --------------------------------------------------------------
18
+ // 示例 1:内联 JSON(v2 schema: stages + action: "script")
19
+ // --------------------------------------------------------------
20
+ console.log('\n--- Example 1: action:script stages ---');
18
21
  const result1 = await framework.executeTool('execute_workflow', {
19
22
  workflow: JSON.stringify({
20
- steps: [
23
+ flow: 'calc',
24
+ version: '2.0',
25
+ description: '两个 script stage 顺序执行,结果通过 $.data 串联',
26
+ stages: [
21
27
  {
22
- type: 'script',
23
- name: 'Calculate',
24
- script: `
25
- const a = 10;
26
- const b = 20;
27
- context.variables.sum = a + b;
28
- return a + b;
29
- `,
28
+ name: 'sum',
29
+ action: 'script',
30
+ script: '$.data.sum = 10 + 20; return $.data.sum;',
30
31
  },
31
32
  {
32
- type: 'script',
33
- name: 'Multiply',
34
- script: `
35
- const sum = context.variables.sum;
36
- context.variables.product = sum * 2;
37
- return sum * 2;
38
- `,
33
+ name: 'double',
34
+ action: 'script',
35
+ script: 'const sum = $.data.sum; $.data.product = sum * 2; return $.data.product;',
39
36
  },
40
37
  ],
41
38
  }),
42
39
  input: {},
43
40
  });
44
- console.log('Result:', result1);
41
+ console.log('Result:', JSON.stringify(result1, null, 2));
45
42
 
46
- console.log('\n--- Testing Loop Step ---');
43
+ // --------------------------------------------------------------
44
+ // 示例 2:each 循环(v2 schema: each + as + stages)
45
+ // --------------------------------------------------------------
46
+ console.log('\n--- Example 2: each loop ---');
47
47
  const result2 = await framework.executeTool('execute_workflow', {
48
48
  workflow: JSON.stringify({
49
- steps: [
49
+ flow: 'loop-demo',
50
+ version: '2.0',
51
+ input: { items: ['a', 'b', 'c'] },
52
+ stages: [
50
53
  {
51
- type: 'loop',
52
- name: 'Loop 3 times',
53
- maxIterations: 3,
54
- loopVariable: 'i',
55
- steps: [
54
+ name: 'process-items',
55
+ each: '{{input.items}}',
56
+ as: 'item',
57
+ index: 'i',
58
+ stages: [
56
59
  {
57
- type: 'script',
58
- name: 'Log iteration',
59
- script: `console.log('Iteration ' + context.variables.i); return context.variables.i;`,
60
+ name: 'log',
61
+ action: 'script',
62
+ script: 'return `iter ${$.data.i} = ${$.data.item}`;',
60
63
  },
61
64
  ],
62
65
  },
63
66
  ],
64
67
  }),
65
- input: {},
68
+ input: { items: ['a', 'b', 'c'] },
66
69
  });
67
- console.log('Result:', result2);
70
+ console.log('Result:', JSON.stringify(result2, null, 2));
68
71
 
69
- console.log('\n--- Testing Condition Step ---');
72
+ // --------------------------------------------------------------
73
+ // 示例 3:choice 分支(v2 schema: choice[] + default + 同级 stages)
74
+ // --------------------------------------------------------------
75
+ console.log('\n--- Example 3: choice routing ---');
70
76
  const result3 = await framework.executeTool('execute_workflow', {
71
77
  workflow: JSON.stringify({
72
- steps: [
78
+ flow: 'route-by-value',
79
+ version: '2.0',
80
+ stages: [
73
81
  {
74
- type: 'script',
75
- name: 'Set value',
76
- outputVariable: 'value',
77
- script: `context.variables.value = 15; return 15;`,
82
+ name: 'set-value',
83
+ action: 'script',
84
+ script: '$.data.value = 15; return $.data.value;',
78
85
  },
79
86
  {
80
- type: 'condition',
81
- name: 'Check value',
82
- branches: [
83
- {
84
- name: 'Greater than 10',
85
- condition: 'context.variables.value > 10',
86
- steps: [
87
- { type: 'script', name: 'Log greater', script: `console.log('Value is greater than 10'); return 'greater';` },
88
- ],
89
- },
90
- {
91
- name: 'Less than or equal 10',
92
- condition: 'context.variables.value <= 10',
93
- steps: [
94
- { type: 'script', name: 'Log less', script: `console.log('Value is less than or equal 10'); return 'less-or-equal';` },
95
- ],
96
- },
87
+ name: 'classify',
88
+ choice: [
89
+ { when: '$.data.value > 10', do: 'big' },
90
+ { when: '$.data.value <= 10', do: 'small' },
97
91
  ],
92
+ default: 'big',
93
+ },
94
+ {
95
+ name: 'big',
96
+ action: 'script',
97
+ script: 'return "value is greater than 10";',
98
+ },
99
+ {
100
+ name: 'small',
101
+ action: 'script',
102
+ script: 'return "value is less than or equal to 10";',
98
103
  },
99
104
  ],
100
105
  }),
101
106
  input: {},
102
107
  });
103
- console.log('Result:', result3);
108
+ console.log('Result:', JSON.stringify(result3, null, 2));
104
109
 
105
110
  // 清理
106
111
  await foliko.destroy(framework);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -67,6 +67,7 @@
67
67
  "ai": "^6.0.146",
68
68
  "chalk": "^5.6.2",
69
69
  "cli-highlight": "^2.1.11",
70
+ "croner": "^9.1.0",
70
71
  "crypto": "^1.0.1",
71
72
  "diff": "^9.0.0",
72
73
  "dotenv": "^17.3.1",
@@ -79,9 +80,7 @@
79
80
  "mailparser": "^3.7.2",
80
81
  "marked": "^11.2.0",
81
82
  "marked-terminal": "6",
82
- "node-cron": "^4.2.1",
83
83
  "node-html-markdown": "^1.3.0",
84
- "node-schedule": "^2.1.1",
85
84
  "node-telegram-bot-api": "^0.67.0",
86
85
  "nodemailer": "^6.10.0",
87
86
  "pino": "7",
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ ## 2.0.0
4
+
5
+ ### Breaking Changes
6
+
7
+ - **依赖替换**:移除 `node-schedule` 和未使用的 `node-cron`;改用 [`croner`](https://github.com/Hexagon/croner) 作为唯一调度引擎
8
+ - 用户配置(`schedule_task` 工具入参)完全兼容,无需改动
9
+ - **持久化文件 schema 微调**:`tasks.json` 新增 `timezone` 字段;旧的 `cronTask` / `timer` 字段不再写盘(之前存的是非序列化对象,无意义)
10
+ - 旧版 `tasks.json` 可被新代码读取(多余字段被忽略),无需手动迁移
11
+ - **删除内部 API**:`_taskStats`、`_checkTasks`、`_startScheduler` 整段不再存在(之前是 no-op 或死代码)
12
+
13
+ ### 行为变更
14
+
15
+ - **事件循环阻塞自愈**:`croner` 用绝对时间戳校验触发时间。即使主线程被 LLM 流式响应 / 磁盘 IO 阻塞几十秒,进程恢复后**会自动 catch-up 错过的触发**(`catch: true`)
16
+ - **过期一次性任务补触发**:进程重启后,若一次性任务本应触发的时间点已过但在 24h 容忍期内,**立即补触发一次**;超过 24h 则丢弃并 warn
17
+ - **cron 任务重叠保护**:若上次任务尚未执行完(`protect: true`),下一次触发被跳过,避免并发
18
+ - **持久化原子化**:`saveTasks` 改为先写 `.tmp` 再 `rename`,崩溃中途不会留下半残 JSON
19
+ - **持久化 debounce**:500ms 内的多次 mutation 合并成一次写盘,减少 IO
20
+
21
+ ### 新增
22
+
23
+ - `timezone` 配置项(默认 `Asia/Shanghai`,可通过 `config.timezone` 或环境变量 `TZ` 覆盖)
24
+ - `unref: true` 调度器不阻塞进程退出(避免 CLI 关闭后定时任务不退出)
25
+ - `schedule_list` 工具输出新增 `nextRun`(croner 的 `nextRun()` getter)和 `timezone` 字段
26
+
27
+ ### 修复
28
+
29
+ - 每次 LLM 模式 cron tick 都会 `createSubAgent`,长期运行会漏内存。现在 `_schedulerSubAgent` 单例复用
30
+ - shell / 普通 任务的重复代码路径已合并,统一通过 `_executeTask` 分发
31
+ - 调度失败时不再静默 warn(`croner` 是硬依赖,启动时 require 失败直接抛)