@tom2012/cc-web 2026.6.17-b → 2026.9.6-c

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 (239) hide show
  1. package/CHANGELOG.json +96 -21
  2. package/backend/dist/adapters/claude-adapter.d.ts +4 -0
  3. package/backend/dist/adapters/claude-adapter.d.ts.map +1 -1
  4. package/backend/dist/adapters/claude-adapter.js +18 -2
  5. package/backend/dist/adapters/claude-adapter.js.map +1 -1
  6. package/backend/dist/adapters/types.d.ts +8 -0
  7. package/backend/dist/adapters/types.d.ts.map +1 -1
  8. package/backend/dist/auth.d.ts.map +0 -0
  9. package/backend/dist/config.d.ts +0 -0
  10. package/backend/dist/config.js +0 -0
  11. package/backend/dist/config.js.map +0 -0
  12. package/backend/dist/hooks-manager.d.ts +0 -1
  13. package/backend/dist/hooks-manager.d.ts.map +1 -1
  14. package/backend/dist/hooks-manager.js +19 -19
  15. package/backend/dist/hooks-manager.js.map +1 -1
  16. package/backend/dist/index.d.ts.map +1 -1
  17. package/backend/dist/index.js +76 -1
  18. package/backend/dist/index.js.map +1 -1
  19. package/backend/dist/logger.d.ts.map +1 -1
  20. package/backend/dist/logger.js +3 -0
  21. package/backend/dist/logger.js.map +1 -1
  22. package/backend/dist/routes/auth.d.ts +0 -0
  23. package/backend/dist/routes/auth.d.ts.map +0 -0
  24. package/backend/dist/routes/auth.js.map +0 -0
  25. package/backend/dist/routes/projects.d.ts.map +0 -0
  26. package/backend/dist/routes/projects.js.map +0 -0
  27. package/backend/dist/session-manager.d.ts +0 -0
  28. package/backend/dist/session-manager.d.ts.map +0 -0
  29. package/backend/dist/session-manager.js +0 -0
  30. package/backend/dist/session-manager.js.map +0 -0
  31. package/backend/dist/terminal-manager.d.ts +10 -1
  32. package/backend/dist/terminal-manager.d.ts.map +1 -1
  33. package/backend/dist/terminal-manager.js +19 -2
  34. package/backend/dist/terminal-manager.js.map +1 -1
  35. package/backend/dist/types.d.ts +0 -0
  36. package/backend/dist/types.d.ts.map +0 -0
  37. package/backend/dist/types.js +0 -0
  38. package/backend/dist/types.js.map +0 -0
  39. package/frontend/dist/assets/{ChatOverlay-Dp1BzsYb.js → ChatOverlay-CymdKeQK.js} +1 -1
  40. package/frontend/dist/assets/{ClaudeMemPage-CpzMjJnb.js → ClaudeMemPage-BZ4UtUoz.js} +1 -1
  41. package/frontend/dist/assets/{GraphPreview-DDY1yVOX.js → GraphPreview-21EwV_e_.js} +1 -1
  42. package/frontend/dist/assets/{MobilePage-BnnE820D.js → MobilePage-99vv9cWh.js} +3 -3
  43. package/frontend/dist/assets/{NotebookPage-BOiYcs-l.js → NotebookPage-B-mcSRJm.js} +1 -1
  44. package/frontend/dist/assets/{OfficePreview-C5N2gr8-.js → OfficePreview-C3rHS2Te.js} +2 -2
  45. package/frontend/dist/assets/{PdfPreview-COG-9AQa.js → PdfPreview-B-toyaoA.js} +1 -1
  46. package/frontend/dist/assets/{ProjectPage-ARPH_J5o.js → ProjectPage-CShqyrUl.js} +4 -4
  47. package/frontend/dist/assets/{ProjectSettingsPage-CMWcT-5r.js → ProjectSettingsPage-FxkEGuUP.js} +1 -1
  48. package/frontend/dist/assets/{SettingsPage-BMrIDtg8.js → SettingsPage-B-bqNp6b.js} +1 -1
  49. package/frontend/dist/assets/{SkillHubPage-Bq93A7Rt.js → SkillHubPage-DN79j9Is.js} +1 -1
  50. package/frontend/dist/assets/{check-circle-2-BGT1eH1f.js → check-circle-2-DRT7EX5I.js} +1 -1
  51. package/frontend/dist/assets/{chevron-down-Bbg11n9i.js → chevron-down-DdtdiM4b.js} +1 -1
  52. package/frontend/dist/assets/{chevron-left-BmStQriC.js → chevron-left-Chv-EE6G.js} +1 -1
  53. package/frontend/dist/assets/{file-text-D9WjmbhK.js → file-text-D3ZDULSr.js} +1 -1
  54. package/frontend/dist/assets/{index-EFhMcMWk.js → index-BIGQ6n-i.js} +2 -2
  55. package/frontend/dist/assets/index-BpdaJxoa.css +1 -0
  56. package/frontend/dist/assets/{index-BRqvDxO6.js → index-CH1oLpmL.js} +1 -1
  57. package/frontend/dist/assets/{index-Bhp9CVTp.js → index-Dbm49BgL.js} +1 -1
  58. package/frontend/dist/assets/{index-COZnAgez.js → index-xJR85RJZ.js} +1 -1
  59. package/frontend/dist/assets/{jszip.min-Dyo__hKc.js → jszip.min-CMcZF4_M.js} +1 -1
  60. package/frontend/dist/assets/{save-CAjdgnp7.js → save-Dhw-I0uF.js} +1 -1
  61. package/frontend/dist/assets/{search-DmWhhICR.js → search-zoOED2R0.js} +1 -1
  62. package/frontend/dist/assets/{select-DAfsBGCS.js → select-B7xEE81_.js} +1 -1
  63. package/frontend/dist/assets/{tabs-zra46FZv.js → tabs-BCHX4LWy.js} +1 -1
  64. package/frontend/dist/assets/{textarea-BG4Tvy0e.js → textarea-CGPmR1Ae.js} +1 -1
  65. package/frontend/dist/index.html +2 -2
  66. package/package.json +36 -10
  67. package/backend/dist/backup/config.d.ts +0 -10
  68. package/backend/dist/backup/config.d.ts.map +0 -1
  69. package/backend/dist/backup/config.js +0 -128
  70. package/backend/dist/backup/config.js.map +0 -1
  71. package/backend/dist/backup/engine.d.ts +0 -22
  72. package/backend/dist/backup/engine.d.ts.map +0 -1
  73. package/backend/dist/backup/engine.js +0 -298
  74. package/backend/dist/backup/engine.js.map +0 -1
  75. package/backend/dist/backup/providers/dropbox.d.ts +0 -25
  76. package/backend/dist/backup/providers/dropbox.d.ts.map +0 -1
  77. package/backend/dist/backup/providers/dropbox.js +0 -320
  78. package/backend/dist/backup/providers/dropbox.js.map +0 -1
  79. package/backend/dist/backup/providers/google-drive.d.ts +0 -29
  80. package/backend/dist/backup/providers/google-drive.d.ts.map +0 -1
  81. package/backend/dist/backup/providers/google-drive.js +0 -303
  82. package/backend/dist/backup/providers/google-drive.js.map +0 -1
  83. package/backend/dist/backup/providers/index.d.ts +0 -3
  84. package/backend/dist/backup/providers/index.d.ts.map +0 -1
  85. package/backend/dist/backup/providers/index.js +0 -19
  86. package/backend/dist/backup/providers/index.js.map +0 -1
  87. package/backend/dist/backup/providers/onedrive.d.ts +0 -29
  88. package/backend/dist/backup/providers/onedrive.d.ts.map +0 -1
  89. package/backend/dist/backup/providers/onedrive.js +0 -317
  90. package/backend/dist/backup/providers/onedrive.js.map +0 -1
  91. package/backend/dist/backup/scheduler.d.ts +0 -6
  92. package/backend/dist/backup/scheduler.d.ts.map +0 -1
  93. package/backend/dist/backup/scheduler.js +0 -55
  94. package/backend/dist/backup/scheduler.js.map +0 -1
  95. package/backend/dist/backup/types.d.ts +0 -73
  96. package/backend/dist/backup/types.d.ts.map +0 -1
  97. package/backend/dist/backup/types.js +0 -4
  98. package/backend/dist/backup/types.js.map +0 -1
  99. package/backend/dist/chat-process-manager.d.ts +0 -45
  100. package/backend/dist/chat-process-manager.d.ts.map +0 -1
  101. package/backend/dist/chat-process-manager.js +0 -258
  102. package/backend/dist/chat-process-manager.js.map +0 -1
  103. package/backend/dist/flows/runner.d.ts +0 -37
  104. package/backend/dist/flows/runner.d.ts.map +0 -1
  105. package/backend/dist/flows/runner.js +0 -660
  106. package/backend/dist/flows/runner.js.map +0 -1
  107. package/backend/dist/flows/store.d.ts +0 -40
  108. package/backend/dist/flows/store.d.ts.map +0 -1
  109. package/backend/dist/flows/store.js +0 -284
  110. package/backend/dist/flows/store.js.map +0 -1
  111. package/backend/dist/flows/types.d.ts +0 -176
  112. package/backend/dist/flows/types.d.ts.map +0 -1
  113. package/backend/dist/flows/types.js +0 -25
  114. package/backend/dist/flows/types.js.map +0 -1
  115. package/backend/dist/information/condenser.d.ts +0 -12
  116. package/backend/dist/information/condenser.d.ts.map +0 -1
  117. package/backend/dist/information/condenser.js +0 -568
  118. package/backend/dist/information/condenser.js.map +0 -1
  119. package/backend/dist/information/conversation-sync.d.ts +0 -22
  120. package/backend/dist/information/conversation-sync.d.ts.map +0 -1
  121. package/backend/dist/information/conversation-sync.js +0 -500
  122. package/backend/dist/information/conversation-sync.js.map +0 -1
  123. package/backend/dist/information/types.d.ts +0 -36
  124. package/backend/dist/information/types.d.ts.map +0 -1
  125. package/backend/dist/information/types.js +0 -4
  126. package/backend/dist/information/types.js.map +0 -1
  127. package/backend/dist/memory-pool/buoyancy.d.ts +0 -2
  128. package/backend/dist/memory-pool/buoyancy.d.ts.map +0 -1
  129. package/backend/dist/memory-pool/buoyancy.js +0 -12
  130. package/backend/dist/memory-pool/buoyancy.js.map +0 -1
  131. package/backend/dist/memory-pool/global-pool-manager.d.ts +0 -12
  132. package/backend/dist/memory-pool/global-pool-manager.d.ts.map +0 -1
  133. package/backend/dist/memory-pool/global-pool-manager.js +0 -295
  134. package/backend/dist/memory-pool/global-pool-manager.js.map +0 -1
  135. package/backend/dist/memory-pool/pool-lock.d.ts +0 -2
  136. package/backend/dist/memory-pool/pool-lock.d.ts.map +0 -1
  137. package/backend/dist/memory-pool/pool-lock.js +0 -31
  138. package/backend/dist/memory-pool/pool-lock.js.map +0 -1
  139. package/backend/dist/memory-pool/pool-manager.d.ts +0 -57
  140. package/backend/dist/memory-pool/pool-manager.d.ts.map +0 -1
  141. package/backend/dist/memory-pool/pool-manager.js +0 -322
  142. package/backend/dist/memory-pool/pool-manager.js.map +0 -1
  143. package/backend/dist/memory-pool/templates.d.ts +0 -5
  144. package/backend/dist/memory-pool/templates.d.ts.map +0 -1
  145. package/backend/dist/memory-pool/templates.js +0 -234
  146. package/backend/dist/memory-pool/templates.js.map +0 -1
  147. package/backend/dist/memory-pool/types.d.ts +0 -61
  148. package/backend/dist/memory-pool/types.d.ts.map +0 -1
  149. package/backend/dist/memory-pool/types.js +0 -4
  150. package/backend/dist/memory-pool/types.js.map +0 -1
  151. package/backend/dist/plan-control/checker.d.ts +0 -3
  152. package/backend/dist/plan-control/checker.d.ts.map +0 -1
  153. package/backend/dist/plan-control/checker.js +0 -103
  154. package/backend/dist/plan-control/checker.js.map +0 -1
  155. package/backend/dist/plan-control/executor.d.ts +0 -93
  156. package/backend/dist/plan-control/executor.d.ts.map +0 -1
  157. package/backend/dist/plan-control/executor.js +0 -955
  158. package/backend/dist/plan-control/executor.js.map +0 -1
  159. package/backend/dist/plan-control/parser.d.ts +0 -12
  160. package/backend/dist/plan-control/parser.d.ts.map +0 -1
  161. package/backend/dist/plan-control/parser.js +0 -216
  162. package/backend/dist/plan-control/parser.js.map +0 -1
  163. package/backend/dist/plan-control/templates.d.ts +0 -4
  164. package/backend/dist/plan-control/templates.d.ts.map +0 -1
  165. package/backend/dist/plan-control/templates.js +0 -271
  166. package/backend/dist/plan-control/templates.js.map +0 -1
  167. package/backend/dist/plan-control/types.d.ts +0 -102
  168. package/backend/dist/plan-control/types.d.ts.map +0 -1
  169. package/backend/dist/plan-control/types.js +0 -12
  170. package/backend/dist/plan-control/types.js.map +0 -1
  171. package/backend/dist/routes/backup.d.ts +0 -4
  172. package/backend/dist/routes/backup.d.ts.map +0 -1
  173. package/backend/dist/routes/backup.js +0 -274
  174. package/backend/dist/routes/backup.js.map +0 -1
  175. package/backend/dist/routes/flows.d.ts +0 -5
  176. package/backend/dist/routes/flows.d.ts.map +0 -1
  177. package/backend/dist/routes/flows.js +0 -457
  178. package/backend/dist/routes/flows.js.map +0 -1
  179. package/backend/dist/routes/global-flows.d.ts +0 -3
  180. package/backend/dist/routes/global-flows.d.ts.map +0 -1
  181. package/backend/dist/routes/global-flows.js +0 -90
  182. package/backend/dist/routes/global-flows.js.map +0 -1
  183. package/backend/dist/routes/information.d.ts +0 -3
  184. package/backend/dist/routes/information.d.ts.map +0 -1
  185. package/backend/dist/routes/information.js +0 -159
  186. package/backend/dist/routes/information.js.map +0 -1
  187. package/backend/dist/routes/memory-pool.d.ts +0 -3
  188. package/backend/dist/routes/memory-pool.d.ts.map +0 -1
  189. package/backend/dist/routes/memory-pool.js +0 -837
  190. package/backend/dist/routes/memory-pool.js.map +0 -1
  191. package/backend/dist/routes/plan-control.d.ts +0 -15
  192. package/backend/dist/routes/plan-control.d.ts.map +0 -1
  193. package/backend/dist/routes/plan-control.js +0 -337
  194. package/backend/dist/routes/plan-control.js.map +0 -1
  195. package/backend/dist/routes/share.d.ts +0 -3
  196. package/backend/dist/routes/share.d.ts.map +0 -1
  197. package/backend/dist/routes/share.js +0 -167
  198. package/backend/dist/routes/share.js.map +0 -1
  199. package/backend/dist/routes/sounds.d.ts +0 -3
  200. package/backend/dist/routes/sounds.d.ts.map +0 -1
  201. package/backend/dist/routes/sounds.js +0 -285
  202. package/backend/dist/routes/sounds.js.map +0 -1
  203. package/backend/dist/tracks/__tests__/verify-starter-templates.d.ts +0 -24
  204. package/backend/dist/tracks/__tests__/verify-starter-templates.d.ts.map +0 -1
  205. package/backend/dist/tracks/__tests__/verify-starter-templates.js +0 -175
  206. package/backend/dist/tracks/__tests__/verify-starter-templates.js.map +0 -1
  207. package/backend/dist/tracks/__tests__/verify-track-cancel.d.ts +0 -23
  208. package/backend/dist/tracks/__tests__/verify-track-cancel.d.ts.map +0 -1
  209. package/backend/dist/tracks/__tests__/verify-track-cancel.js +0 -153
  210. package/backend/dist/tracks/__tests__/verify-track-cancel.js.map +0 -1
  211. package/backend/dist/tracks/__tests__/verify-track-t1.d.ts +0 -11
  212. package/backend/dist/tracks/__tests__/verify-track-t1.d.ts.map +0 -1
  213. package/backend/dist/tracks/__tests__/verify-track-t1.js +0 -293
  214. package/backend/dist/tracks/__tests__/verify-track-t1.js.map +0 -1
  215. package/backend/dist/tracks/__tests__/verify-track.d.ts +0 -20
  216. package/backend/dist/tracks/__tests__/verify-track.d.ts.map +0 -1
  217. package/backend/dist/tracks/__tests__/verify-track.js +0 -187
  218. package/backend/dist/tracks/__tests__/verify-track.js.map +0 -1
  219. package/backend/dist/tracks/registry.d.ts +0 -61
  220. package/backend/dist/tracks/registry.d.ts.map +0 -1
  221. package/backend/dist/tracks/registry.js +0 -202
  222. package/backend/dist/tracks/registry.js.map +0 -1
  223. package/backend/dist/tracks/track-runner.d.ts +0 -69
  224. package/backend/dist/tracks/track-runner.d.ts.map +0 -1
  225. package/backend/dist/tracks/track-runner.js +0 -158
  226. package/backend/dist/tracks/track-runner.js.map +0 -1
  227. package/backend/dist/tracks/train-loader.d.ts +0 -44
  228. package/backend/dist/tracks/train-loader.d.ts.map +0 -1
  229. package/backend/dist/tracks/train-loader.js +0 -32
  230. package/backend/dist/tracks/train-loader.js.map +0 -1
  231. package/backend/dist/tracks/types-train.d.ts +0 -12
  232. package/backend/dist/tracks/types-train.d.ts.map +0 -1
  233. package/backend/dist/tracks/types-train.js +0 -13
  234. package/backend/dist/tracks/types-train.js.map +0 -1
  235. package/backend/dist/usage-terminal.d.ts +0 -18
  236. package/backend/dist/usage-terminal.d.ts.map +0 -1
  237. package/backend/dist/usage-terminal.js +0 -191
  238. package/backend/dist/usage-terminal.js.map +0 -1
  239. package/frontend/dist/assets/index-GfCaMXPO.css +0 -1
@@ -1,955 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.PlanExecutor = void 0;
37
- // backend/src/plan-control/executor.ts
38
- const events_1 = require("events");
39
- const fs = __importStar(require("fs"));
40
- const path = __importStar(require("path"));
41
- const config_1 = require("../config");
42
- const types_1 = require("./types");
43
- const parser_1 = require("./parser");
44
- const checker_1 = require("./checker");
45
- class PlanExecutor extends events_1.EventEmitter {
46
- constructor(projectPath, deps, config) {
47
- super();
48
- this.state = null;
49
- this.ast = [];
50
- this.funcs = new Map();
51
- this.flatNodes = []; // all non-blank/comment nodes in order
52
- this.machineState = 'STOPPED';
53
- this.nodeCounter = 0;
54
- this.currentNodeId = null;
55
- this.fileWatcher = null;
56
- this.pollTimer = null;
57
- this.nudgeTimer = null;
58
- this.idleWaitTimer = null;
59
- this.nudgeCount = 0;
60
- this.preReplanLines = null;
61
- this.pendingPause = false;
62
- /** Track if-chain matched state per chain (keyed by the `if` node's line number). */
63
- this.ifChainMatched = new Map();
64
- this.projectPath = projectPath;
65
- this.pcDir = path.join(projectPath, '.plan-control');
66
- this.deps = deps;
67
- this.config = { ...types_1.DEFAULT_PLAN_CONFIG, ...config };
68
- }
69
- // ── Public API ──
70
- checkSyntax() {
71
- const source = this.readMainPc();
72
- if (!source)
73
- return { errors: [{ line: 0, message: 'main.pc 文件不存在' }], ast: [] };
74
- const ast = (0, parser_1.parseProgram)(source);
75
- const errors = (0, checker_1.check)(ast);
76
- return { errors, ast };
77
- }
78
- start() {
79
- const { errors, ast } = this.checkSyntax();
80
- if (errors.length > 0)
81
- throw new Error('语法检查失败,请先修复错误');
82
- this.ast = ast;
83
- this.funcs = (0, parser_1.collectFuncs)(ast);
84
- this.flattenAst();
85
- this.nodeCounter = 0;
86
- this.state = {
87
- status: 'running',
88
- current_line: this.findFirstExecutableLine(),
89
- executed_tasks: 0,
90
- estimated_tasks: (0, parser_1.estimateTasks)(ast),
91
- variables: {},
92
- call_stack: [],
93
- loop_stack: [],
94
- last_task_status: null,
95
- history: [],
96
- };
97
- this.processInitialVarAssigns();
98
- this.saveState();
99
- this.machineState = 'IDLE';
100
- this.broadcastStatus();
101
- this.tick();
102
- }
103
- resume() {
104
- if (!this.state) {
105
- this.tryRecoverState();
106
- if (!this.state)
107
- throw new Error('无执行状态可恢复');
108
- }
109
- const source = this.readMainPc();
110
- if (!source)
111
- throw new Error('main.pc 不存在');
112
- this.ast = (0, parser_1.parseProgram)(source);
113
- this.funcs = (0, parser_1.collectFuncs)(this.ast);
114
- this.flattenAst();
115
- if (this.state.history.length > 0) {
116
- const maxId = Math.max(...this.state.history.map(h => parseInt(h.node_id, 10)));
117
- this.nodeCounter = maxId;
118
- }
119
- this.state.status = 'running';
120
- this.saveState();
121
- this.machineState = 'IDLE';
122
- this.pendingPause = false;
123
- this.broadcastStatus();
124
- this.tick();
125
- }
126
- pause() {
127
- if (this.machineState === 'WAITING') {
128
- this.pendingPause = true;
129
- return;
130
- }
131
- this.enterPaused('用户暂停');
132
- }
133
- stop() {
134
- this.cleanup();
135
- if (this.state) {
136
- this.state.status = 'stopped';
137
- this.state.stop_line = this.state.current_line;
138
- this.state.stop_node_id = this.currentNodeId ?? undefined;
139
- this.saveState();
140
- }
141
- this.machineState = 'STOPPED';
142
- this.broadcastStatus();
143
- }
144
- getState() {
145
- return this.state;
146
- }
147
- getNodes() {
148
- const nodesDir = path.join(this.pcDir, 'nodes');
149
- if (!fs.existsSync(nodesDir))
150
- return [];
151
- return fs.readdirSync(nodesDir)
152
- .filter(f => f.startsWith('node-') && f.endsWith('.json'))
153
- .sort()
154
- .map(f => {
155
- try {
156
- return JSON.parse(fs.readFileSync(path.join(nodesDir, f), 'utf-8'));
157
- }
158
- catch {
159
- return null;
160
- }
161
- })
162
- .filter(Boolean);
163
- }
164
- getNode(nodeId) {
165
- const file = path.join(this.pcDir, 'nodes', `node-${nodeId}.json`);
166
- if (!fs.existsSync(file))
167
- return null;
168
- try {
169
- return JSON.parse(fs.readFileSync(file, 'utf-8'));
170
- }
171
- catch {
172
- return null;
173
- }
174
- }
175
- isInitialized() {
176
- return fs.existsSync(this.pcDir) && fs.existsSync(path.join(this.pcDir, 'plan-code.md'));
177
- }
178
- hasMainPc() {
179
- return fs.existsSync(path.join(this.pcDir, 'main.pc'));
180
- }
181
- // ── State Machine ──
182
- tick() {
183
- if (this.machineState !== 'IDLE' || !this.state)
184
- return;
185
- const node = this.findNodeAtLine(this.state.current_line);
186
- if (!node) {
187
- this.state.status = 'completed';
188
- this.saveState();
189
- this.machineState = 'COMPLETED';
190
- this.broadcastStatus();
191
- return;
192
- }
193
- if (node.type === 'task' || node.type === 'task_assign') {
194
- this.dispatchTask(node);
195
- }
196
- else {
197
- this.processControlFlow(node);
198
- this.advanceLine();
199
- this.saveState();
200
- this.broadcastStatus();
201
- setImmediate(() => this.tick());
202
- }
203
- }
204
- dispatchTask(node) {
205
- this.machineState = 'SENDING';
206
- const description = node.description ?? '';
207
- const resolvedDesc = (0, parser_1.interpolate)(description, this.getEffectiveVariables());
208
- const nodeId = this.nextNodeId();
209
- this.currentNodeId = nodeId;
210
- const resolvedCode = node.type === 'task_assign'
211
- ? `${node.varName} = task ${resolvedDesc}`
212
- : `task ${resolvedDesc}`;
213
- const prompt = this.buildPrompt(nodeId, resolvedDesc, node);
214
- const record = {
215
- id: nodeId,
216
- line: node.line,
217
- code: node.raw,
218
- resolved_code: resolvedCode,
219
- prompt,
220
- started_at: new Date().toISOString(),
221
- completed_at: null,
222
- nudge_count: 0,
223
- status: null,
224
- result: null,
225
- summary: null,
226
- };
227
- const nodesDir = path.join(this.pcDir, 'nodes');
228
- fs.mkdirSync(nodesDir, { recursive: true });
229
- (0, config_1.atomicWriteSync)(path.join(nodesDir, `node-${nodeId}.json`), JSON.stringify(record, null, 2));
230
- this.waitForIdle(() => {
231
- this.deps.writeToPty(prompt + '\r');
232
- this.machineState = 'WAITING';
233
- this.state.status = 'waiting';
234
- this.saveState();
235
- this.broadcastStatus();
236
- this.startFileWatch(nodeId);
237
- this.startNudgeTimer(nodeId);
238
- });
239
- }
240
- processControlFlow(node) {
241
- const state = this.state;
242
- switch (node.type) {
243
- case 'var_assign':
244
- if (node.varName && node.listItems) {
245
- this.setVariable(node.varName, node.listItems);
246
- }
247
- break;
248
- case 'if': {
249
- const matched = this.evaluateCondition(node.condition);
250
- if (!matched) {
251
- this.ifChainMatched.delete(node.line); // ensure clean state
252
- this.skipToNextBranch(node);
253
- return;
254
- }
255
- this.ifChainMatched.set(node.line, true);
256
- // If no elif/else follows, clean up when advancing past the block
257
- this.scheduleIfChainCleanup(node);
258
- break;
259
- }
260
- case 'elif': {
261
- const chainKey = this.findChainIfLine(node);
262
- if (this.ifChainMatched.get(chainKey)) {
263
- // Previous branch matched and executed — skip rest of chain
264
- this.ifChainMatched.delete(chainKey);
265
- this.skipIfChain(node);
266
- return;
267
- }
268
- // Previous condition failed — evaluate this elif
269
- const matched = this.evaluateCondition(node.condition);
270
- if (!matched) {
271
- this.skipToNextBranch(node);
272
- return;
273
- }
274
- this.ifChainMatched.set(chainKey, true);
275
- break;
276
- }
277
- case 'else': {
278
- const chainKey = this.findChainIfLine(node);
279
- if (this.ifChainMatched.get(chainKey)) {
280
- this.ifChainMatched.delete(chainKey);
281
- this.skipBlock(node);
282
- return;
283
- }
284
- // Previous conditions all failed — enter else body
285
- break;
286
- }
287
- case 'for': {
288
- const vars = this.getEffectiveVariables();
289
- const listVal = vars[node.iterRef];
290
- let list;
291
- if (Array.isArray(listVal)) {
292
- list = listVal;
293
- }
294
- else if (typeof listVal === 'string') {
295
- list = [listVal];
296
- }
297
- else if (listVal === null || listVal === undefined || typeof listVal === 'boolean') {
298
- this.enterPaused(`for 循环变量 \${${node.iterRef}} 不是列表(值: ${JSON.stringify(listVal)})`);
299
- return;
300
- }
301
- else {
302
- list = [];
303
- }
304
- if (list.length === 0) {
305
- this.skipBlock(node);
306
- return;
307
- }
308
- const endLine = this.findBlockEndLine(node);
309
- state.loop_stack.push({
310
- type: 'for',
311
- var: node.iterVar,
312
- list,
313
- index: 0,
314
- start_line: node.line,
315
- end_line: endLine,
316
- });
317
- this.setVariable(node.iterVar, list[0]);
318
- break;
319
- }
320
- case 'loop': {
321
- const endLine = this.findBlockEndLine(node);
322
- state.loop_stack.push({
323
- type: 'loop',
324
- var: node.loopCounter,
325
- count: node.loopCount,
326
- index: 0,
327
- start_line: node.line,
328
- end_line: endLine,
329
- });
330
- if (node.loopCounter) {
331
- this.setVariable(node.loopCounter, '1');
332
- }
333
- break;
334
- }
335
- case 'break': {
336
- const frame = state.loop_stack.pop();
337
- if (frame) {
338
- state.current_line = frame.end_line;
339
- }
340
- break;
341
- }
342
- case 'continue': {
343
- const frame = state.loop_stack[state.loop_stack.length - 1];
344
- if (frame) {
345
- frame.index++;
346
- if (this.isLoopDone(frame)) {
347
- state.loop_stack.pop();
348
- state.current_line = frame.end_line;
349
- }
350
- else {
351
- this.updateLoopVar(frame);
352
- state.current_line = frame.start_line;
353
- }
354
- }
355
- break;
356
- }
357
- case 'func':
358
- this.skipBlock(node);
359
- return;
360
- case 'call': {
361
- if (state.call_stack.length >= 20) {
362
- this.enterPaused('call_stack 深度超过限制 (20)');
363
- return;
364
- }
365
- const funcNode = this.funcs.get(node.funcName);
366
- if (!funcNode)
367
- break;
368
- const vars = this.getEffectiveVariables();
369
- const frame = {
370
- func: node.funcName,
371
- return_line: this.findNextLineAfter(node.line),
372
- local_vars: {},
373
- saved_last_task_status: state.last_task_status,
374
- };
375
- if (funcNode.params && node.args) {
376
- for (let i = 0; i < funcNode.params.length; i++) {
377
- const param = funcNode.params[i];
378
- const arg = node.args[i];
379
- if (arg) {
380
- if (arg.type === 'var') {
381
- frame.local_vars[param] = vars[arg.name] ?? null;
382
- }
383
- else {
384
- frame.local_vars[param] = arg.items;
385
- }
386
- }
387
- }
388
- }
389
- state.call_stack.push(frame);
390
- state.current_line = funcNode.line;
391
- break;
392
- }
393
- case 'return': {
394
- const callFrame = state.call_stack.pop();
395
- if (callFrame) {
396
- state.last_task_status = callFrame.saved_last_task_status;
397
- state.current_line = callFrame.return_line - 1;
398
- }
399
- break;
400
- }
401
- case 'comment':
402
- case 'blank':
403
- break;
404
- }
405
- }
406
- // ── File Watching ──
407
- startFileWatch(nodeId) {
408
- const nodeFile = path.join(this.pcDir, 'nodes', `node-${nodeId}.json`);
409
- this.nudgeCount = 0;
410
- try {
411
- this.fileWatcher = fs.watch(nodeFile, () => this.checkNodeResult(nodeId));
412
- }
413
- catch { /* fs.watch may not work on all platforms */ }
414
- const interval = setInterval(() => this.checkNodeResult(nodeId), this.config.watch_poll_interval);
415
- interval.unref();
416
- this.pollTimer = interval;
417
- }
418
- checkNodeResult(nodeId) {
419
- if (this.machineState === 'REPLANNING') {
420
- const record = this.getNode(nodeId);
421
- if (record && record.status === 'success') {
422
- this.cleanup();
423
- record.completed_at = new Date().toISOString();
424
- fs.writeFileSync(path.join(this.pcDir, 'nodes', `node-${nodeId}.json`), JSON.stringify(record, null, 2));
425
- this.handleReplanComplete(nodeId);
426
- }
427
- return;
428
- }
429
- if (this.machineState !== 'WAITING')
430
- return;
431
- const record = this.getNode(nodeId);
432
- if (!record || record.status === null)
433
- return;
434
- const validStatuses = ['success', 'failed', 'blocked', 'replan'];
435
- if (!validStatuses.includes(record.status)) {
436
- this.deps.writeToPty(`[PLAN-CONTROL] 错误:status 值 "${record.status}" 无效。有效值: ${validStatuses.join(', ')}\r`);
437
- return;
438
- }
439
- this.cleanup();
440
- record.completed_at = new Date().toISOString();
441
- fs.writeFileSync(path.join(this.pcDir, 'nodes', `node-${nodeId}.json`), JSON.stringify(record, null, 2));
442
- this.processResult(record);
443
- }
444
- processResult(record) {
445
- if (!this.state)
446
- return;
447
- this.machineState = 'PROCESSING';
448
- const node = this.findNodeAtLine(this.state.current_line);
449
- if (node?.type === 'task_assign' && node.varName) {
450
- this.setVariable(node.varName, record.result);
451
- }
452
- const normalizedStatus = record.status === 'replan' ? 'success' : record.status;
453
- this.state.last_task_status = normalizedStatus;
454
- this.state.executed_tasks++;
455
- this.state.history.push({
456
- node_id: record.id,
457
- line: record.line,
458
- status: record.status,
459
- timestamp: record.completed_at,
460
- });
461
- this.deps.broadcast({
462
- type: 'plan_node_update',
463
- node_id: record.id,
464
- status: record.status,
465
- summary: record.summary,
466
- });
467
- const needsReplan = record.request_replan || record.status === 'replan';
468
- if (needsReplan) {
469
- this.enterReplanning(record);
470
- return;
471
- }
472
- if (this.pendingPause) {
473
- this.pendingPause = false;
474
- this.enterPaused('用户暂停');
475
- return;
476
- }
477
- this.advanceLine();
478
- this.state.status = 'running';
479
- this.saveState();
480
- this.machineState = 'IDLE';
481
- this.broadcastStatus();
482
- this.tick();
483
- }
484
- // ── Nudge ──
485
- startNudgeTimer(nodeId) {
486
- const interval = this.config.nudge_idle_seconds * 1000 *
487
- Math.pow(this.config.nudge_interval_multiplier, this.nudgeCount);
488
- this.nudgeTimer = setTimeout(() => {
489
- if (this.machineState !== 'WAITING')
490
- return;
491
- const lastActivity = this.deps.getLastActivity();
492
- const now = Date.now();
493
- const idle = lastActivity ? (now - lastActivity) > this.config.nudge_idle_seconds * 1000 : true;
494
- if (!idle) {
495
- this.startNudgeTimer(nodeId);
496
- return;
497
- }
498
- this.nudgeCount++;
499
- if (this.nudgeCount > this.config.nudge_max_count) {
500
- const record = this.getNode(nodeId);
501
- if (record && !record.status) {
502
- record.status = 'blocked';
503
- record.summary = `${this.config.nudge_max_count} 次催促后仍无响应`;
504
- record.completed_at = new Date().toISOString();
505
- fs.writeFileSync(path.join(this.pcDir, 'nodes', `node-${nodeId}.json`), JSON.stringify(record, null, 2));
506
- this.cleanup();
507
- this.processResult(record);
508
- }
509
- return;
510
- }
511
- this.deps.writeToPty(`请继续当前任务。如果已完成,请按照 .plan-control/output-format.md 的格式更新 .plan-control/nodes/node-${nodeId}.json\r`);
512
- const record = this.getNode(nodeId);
513
- if (record) {
514
- record.nudge_count = this.nudgeCount;
515
- fs.writeFileSync(path.join(this.pcDir, 'nodes', `node-${nodeId}.json`), JSON.stringify(record, null, 2));
516
- }
517
- this.deps.broadcast({ type: 'plan_nudge', node_id: nodeId, nudge_count: this.nudgeCount });
518
- this.startNudgeTimer(nodeId);
519
- }, interval);
520
- if (this.nudgeTimer)
521
- this.nudgeTimer.unref();
522
- }
523
- // ── Replan ──
524
- enterReplanning(record) {
525
- if (!this.state)
526
- return;
527
- this.state.status = 'replanning';
528
- this.saveState();
529
- this.machineState = 'REPLANNING';
530
- const source = this.readMainPc();
531
- this.preReplanLines = source ? source.split('\n') : [];
532
- const prompt = `\n[PLAN-CONTROL] 计划调整请求
533
- 原因:${record.replan_reason || '未提供原因'}
534
- 请修改 .plan-control/main.pc,保留前 ${this.state.current_line} 行不变,调整后续计划。
535
- 修改完成后,请更新 .plan-control/nodes/node-${record.id}.json 的 status 为 "success"。
536
- [/PLAN-CONTROL]\n`;
537
- this.deps.writeToPty(prompt + '\r');
538
- this.deps.broadcast({ type: 'plan_replan', node_id: record.id, reason: record.replan_reason });
539
- this.broadcastStatus();
540
- this.cleanup(); // close previous watcher/timers before starting new ones
541
- this.startFileWatch(record.id);
542
- }
543
- handleReplanComplete(nodeId) {
544
- if (!this.state || !this.preReplanLines)
545
- return;
546
- const newSource = this.readMainPc();
547
- if (!newSource) {
548
- this.deps.writeToPty('[PLAN-CONTROL] 错误:main.pc 不存在\r');
549
- return;
550
- }
551
- const newLines = newSource.split('\n');
552
- const preserveCount = this.state.current_line;
553
- for (let i = 0; i < preserveCount && i < this.preReplanLines.length; i++) {
554
- if (newLines[i] !== this.preReplanLines[i]) {
555
- this.deps.writeToPty(`[PLAN-CONTROL] 错误:前 ${preserveCount} 行不可修改(第 ${i + 1} 行已变更),请恢复并仅修改第 ${preserveCount + 1} 行之后的内容\r`);
556
- return;
557
- }
558
- }
559
- const ast = (0, parser_1.parseProgram)(newSource);
560
- const errors = (0, checker_1.check)(ast);
561
- if (errors.length > 0) {
562
- const errText = errors.map(e => `第 ${e.line} 行: ${e.message}`).join('\n');
563
- this.deps.writeToPty(`[PLAN-CONTROL] 语法错误:\n${errText}\n请修复后重试。\r`);
564
- return;
565
- }
566
- this.preReplanLines = null;
567
- this.ast = ast;
568
- this.funcs = (0, parser_1.collectFuncs)(ast);
569
- this.flattenAst();
570
- this.state.estimated_tasks = (0, parser_1.estimateTasks)(ast);
571
- this.state.status = 'running';
572
- this.advanceLine();
573
- this.saveState();
574
- this.machineState = 'IDLE';
575
- this.broadcastStatus();
576
- this.tick();
577
- }
578
- // ── Crash Recovery ──
579
- tryRecoverState() {
580
- const stateFile = path.join(this.pcDir, 'state.json');
581
- if (!fs.existsSync(stateFile))
582
- return;
583
- try {
584
- this.state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
585
- }
586
- catch {
587
- return;
588
- }
589
- if (!this.state || this.state.status === 'completed') {
590
- this.state = null;
591
- return;
592
- }
593
- const source = this.readMainPc();
594
- if (!source)
595
- return;
596
- this.ast = (0, parser_1.parseProgram)(source);
597
- this.funcs = (0, parser_1.collectFuncs)(this.ast);
598
- this.flattenAst();
599
- if (this.state.history.length > 0) {
600
- this.nodeCounter = Math.max(...this.state.history.map(h => parseInt(h.node_id, 10)));
601
- }
602
- }
603
- // ── Helpers ──
604
- readMainPc() {
605
- const file = path.join(this.pcDir, 'main.pc');
606
- if (!fs.existsSync(file))
607
- return null;
608
- return fs.readFileSync(file, 'utf-8');
609
- }
610
- saveState() {
611
- if (!this.state)
612
- return;
613
- fs.mkdirSync(this.pcDir, { recursive: true });
614
- (0, config_1.atomicWriteSync)(path.join(this.pcDir, 'state.json'), JSON.stringify(this.state, null, 2));
615
- }
616
- nextNodeId() {
617
- this.nodeCounter++;
618
- return String(this.nodeCounter).padStart(3, '0');
619
- }
620
- getEffectiveVariables() {
621
- if (!this.state)
622
- return {};
623
- const vars = { ...this.state.variables };
624
- if (this.state.call_stack.length > 0) {
625
- const frame = this.state.call_stack[this.state.call_stack.length - 1];
626
- Object.assign(vars, frame.local_vars);
627
- }
628
- return vars;
629
- }
630
- setVariable(name, value) {
631
- if (!this.state)
632
- return;
633
- if (this.state.call_stack.length > 0) {
634
- this.state.call_stack[this.state.call_stack.length - 1].local_vars[name] = value;
635
- }
636
- else {
637
- this.state.variables[name] = value;
638
- }
639
- }
640
- evaluateCondition(condition) {
641
- if (!this.state)
642
- return false;
643
- if (['success', 'failed', 'blocked'].includes(condition)) {
644
- return this.state.last_task_status === condition;
645
- }
646
- const varMatch = condition.match(/^\$\{(.+)\}$/);
647
- if (varMatch) {
648
- const val = this.getEffectiveVariables()[varMatch[1]];
649
- if (val === null || val === undefined || val === false)
650
- return false;
651
- if (val === '')
652
- return false;
653
- if (Array.isArray(val) && val.length === 0)
654
- return false;
655
- return true;
656
- }
657
- return false;
658
- }
659
- findNodeAtLine(line) {
660
- return this.flatNodes.find(n => n.line === line);
661
- }
662
- flattenAst() {
663
- this.flatNodes = [];
664
- const walk = (nodes) => {
665
- for (const n of nodes) {
666
- if (n.type !== 'blank' && n.type !== 'comment') {
667
- this.flatNodes.push(n);
668
- }
669
- if (n.children.length > 0)
670
- walk(n.children);
671
- }
672
- };
673
- walk(this.ast);
674
- this.flatNodes.sort((a, b) => a.line - b.line);
675
- }
676
- findFirstExecutableLine() {
677
- for (const n of this.flatNodes) {
678
- if (n.type !== 'blank' && n.type !== 'comment')
679
- return n.line;
680
- }
681
- return 1;
682
- }
683
- advanceLine() {
684
- if (!this.state)
685
- return;
686
- let currentIdx = this.flatNodes.findIndex(n => n.line === this.state.current_line);
687
- // current_line may point to a blank/comment line (not in flatNodes).
688
- // Find the next flatNode after current_line instead of jumping to end.
689
- if (currentIdx < 0) {
690
- const nextIdx = this.flatNodes.findIndex(n => n.line > this.state.current_line);
691
- if (nextIdx >= 0) {
692
- this.state.current_line = this.flatNodes[nextIdx].line;
693
- this.checkLoopEnd();
694
- this.checkCallEnd();
695
- return;
696
- }
697
- if (this.checkLoopEnd())
698
- return;
699
- this.state.current_line = this.flatNodes.length > 0
700
- ? this.flatNodes[this.flatNodes.length - 1].line + 1
701
- : 1;
702
- return;
703
- }
704
- if (currentIdx + 1 >= this.flatNodes.length) {
705
- if (this.checkLoopEnd())
706
- return;
707
- this.state.current_line = this.flatNodes[this.flatNodes.length - 1].line + 1;
708
- return;
709
- }
710
- const nextNode = this.flatNodes[currentIdx + 1];
711
- this.state.current_line = nextNode.line;
712
- this.checkLoopEnd();
713
- this.checkCallEnd();
714
- }
715
- checkLoopEnd() {
716
- if (!this.state || this.state.loop_stack.length === 0)
717
- return false;
718
- const frame = this.state.loop_stack[this.state.loop_stack.length - 1];
719
- if (this.state.current_line > frame.end_line) {
720
- frame.index++;
721
- if (this.isLoopDone(frame)) {
722
- this.state.loop_stack.pop();
723
- return false;
724
- }
725
- this.updateLoopVar(frame);
726
- this.state.current_line = frame.start_line;
727
- const loopNode = this.findNodeAtLine(frame.start_line);
728
- if (loopNode && loopNode.children.length > 0) {
729
- this.state.current_line = loopNode.children[0].line;
730
- }
731
- return true;
732
- }
733
- return false;
734
- }
735
- /** Implicit return: if current_line left the function body, pop the call frame. */
736
- checkCallEnd() {
737
- if (!this.state || this.state.call_stack.length === 0)
738
- return;
739
- const frame = this.state.call_stack[this.state.call_stack.length - 1];
740
- const funcNode = this.funcs.get(frame.func);
741
- if (!funcNode)
742
- return;
743
- const endLine = this.findBlockEndLine(funcNode);
744
- if (this.state.current_line > endLine) {
745
- this.state.call_stack.pop();
746
- this.state.last_task_status = frame.saved_last_task_status;
747
- this.state.current_line = frame.return_line;
748
- }
749
- }
750
- isLoopDone(frame) {
751
- if (frame.type === 'for') {
752
- return frame.index >= (frame.list?.length ?? 0);
753
- }
754
- return frame.index >= (frame.count ?? 0);
755
- }
756
- updateLoopVar(frame) {
757
- if (frame.type === 'for' && frame.var && frame.list) {
758
- this.setVariable(frame.var, frame.list[frame.index]);
759
- }
760
- else if (frame.type === 'loop' && frame.var) {
761
- this.setVariable(frame.var, String(frame.index + 1));
762
- }
763
- }
764
- skipBlock(node) {
765
- if (!this.state)
766
- return;
767
- const endLine = this.findBlockEndLine(node);
768
- this.state.current_line = endLine;
769
- }
770
- /** Skip to the next elif/else sibling after a failed if/elif condition. */
771
- skipToNextBranch(node) {
772
- if (!this.state)
773
- return;
774
- const siblings = this.findSiblings(node);
775
- if (!siblings) {
776
- this.skipBlock(node);
777
- return;
778
- }
779
- const idx = siblings.findIndex(s => s.line === node.line);
780
- for (let i = idx + 1; i < siblings.length; i++) {
781
- if (siblings[i].type === 'elif' || siblings[i].type === 'else') {
782
- this.state.current_line = siblings[i].line;
783
- return;
784
- }
785
- }
786
- // No more branches — skip past the if block
787
- this.skipBlock(node);
788
- }
789
- skipIfChain(node) {
790
- if (!this.state)
791
- return;
792
- const siblings = this.findSiblings(node);
793
- if (!siblings)
794
- return;
795
- const idx = siblings.findIndex(s => s.line === node.line);
796
- let lastInChain = idx;
797
- for (let i = idx + 1; i < siblings.length; i++) {
798
- if (siblings[i].type === 'elif' || siblings[i].type === 'else') {
799
- lastInChain = i;
800
- }
801
- else
802
- break;
803
- }
804
- const lastNode = siblings[lastInChain];
805
- const endLine = this.findBlockEndLine(lastNode);
806
- this.state.current_line = endLine;
807
- }
808
- /** Schedule cleanup of ifChainMatched entry after the if-block's last branch ends. */
809
- scheduleIfChainCleanup(ifNode) {
810
- const siblings = this.findSiblings(ifNode);
811
- if (!siblings)
812
- return;
813
- // If there are no elif/else siblings, the Map entry would never be cleaned up
814
- const hasElif = siblings.some(s => s.line !== ifNode.line && (s.type === 'elif' || s.type === 'else'));
815
- if (!hasElif) {
816
- // No elif/else — clean up immediately after block body (entry only needed for elif/else)
817
- this.ifChainMatched.delete(ifNode.line);
818
- }
819
- }
820
- /** Find the `if` node's line that starts the chain containing this elif/else node. */
821
- findChainIfLine(node) {
822
- const siblings = this.findSiblings(node);
823
- if (siblings) {
824
- for (const s of siblings) {
825
- if (s.type === 'if')
826
- return s.line;
827
- }
828
- }
829
- return node.line; // fallback
830
- }
831
- findSiblings(node) {
832
- const find = (nodes) => {
833
- for (const n of nodes) {
834
- if (n.line === node.line)
835
- return nodes;
836
- const found = find(n.children);
837
- if (found)
838
- return found;
839
- }
840
- return null;
841
- };
842
- return find(this.ast);
843
- }
844
- findBlockEndLine(node) {
845
- if (node.children.length === 0)
846
- return node.line;
847
- const lastChild = node.children[node.children.length - 1];
848
- if (lastChild.children.length > 0)
849
- return this.findBlockEndLine(lastChild);
850
- return lastChild.line;
851
- }
852
- findNextLineAfter(line) {
853
- const idx = this.flatNodes.findIndex(n => n.line === line);
854
- if (idx < 0 || idx + 1 >= this.flatNodes.length)
855
- return line + 1;
856
- return this.flatNodes[idx + 1].line;
857
- }
858
- processInitialVarAssigns() {
859
- for (const node of this.ast) {
860
- if (node.type === 'var_assign' && node.varName && node.listItems) {
861
- this.state.variables[node.varName] = node.listItems;
862
- }
863
- }
864
- }
865
- waitForIdle(callback) {
866
- const check = () => {
867
- if (this.machineState !== 'SENDING')
868
- return; // cancelled by stop/pause
869
- const lastActivity = this.deps.getLastActivity();
870
- const now = Date.now();
871
- const idle = !lastActivity || (now - lastActivity) > this.config.send_idle_seconds * 1000;
872
- if (idle) {
873
- this.idleWaitTimer = null;
874
- callback();
875
- }
876
- else {
877
- this.idleWaitTimer = setTimeout(check, 5000);
878
- this.idleWaitTimer.unref();
879
- }
880
- };
881
- check();
882
- }
883
- buildPrompt(nodeId, resolvedDesc, node) {
884
- const state = this.state;
885
- let prompt = `[PLAN-CONTROL] 任务 #${nodeId}
886
- 指令:${resolvedDesc}
887
- 上下文:第${state.executed_tasks + 1}个任务(已完成${state.executed_tasks}个)`;
888
- // Attach recent task context (last 3 completed tasks with summary + result)
889
- if (state.history.length > 0) {
890
- const recent = state.history.slice(-3);
891
- prompt += '\n\n前序任务摘要:';
892
- for (const h of recent) {
893
- const rec = this.getNode(h.node_id);
894
- if (rec) {
895
- const resultStr = rec.result !== null && rec.result !== undefined
896
- ? (typeof rec.result === 'string' ? rec.result : JSON.stringify(rec.result))
897
- : '无';
898
- prompt += `\n #${h.node_id} (${h.status}) — ${rec.summary || '无摘要'} | result: ${resultStr}`;
899
- }
900
- else {
901
- prompt += `\n #${h.node_id} (${h.status})`;
902
- }
903
- }
904
- }
905
- prompt += `\n\n输出文件:.plan-control/nodes/node-${nodeId}.json`;
906
- prompt += `\n输出格式:见 .plan-control/output-format.md`;
907
- if (node.type === 'task_assign') {
908
- prompt += `\n返回要求:result 字段请填写为字符串列表、字符串或布尔值`;
909
- }
910
- prompt += `\nGit:执行前先 commit 当前工作,执行完成后再 commit 本次变更`;
911
- prompt += '\n[/PLAN-CONTROL]';
912
- return prompt;
913
- }
914
- enterPaused(reason) {
915
- if (!this.state)
916
- return;
917
- this.cleanup();
918
- this.state.status = 'paused';
919
- this.state.error = reason;
920
- this.saveState();
921
- this.machineState = 'PAUSED';
922
- this.broadcastStatus();
923
- }
924
- cleanup() {
925
- if (this.fileWatcher) {
926
- this.fileWatcher.close();
927
- this.fileWatcher = null;
928
- }
929
- if (this.pollTimer) {
930
- clearInterval(this.pollTimer);
931
- this.pollTimer = null;
932
- }
933
- if (this.idleWaitTimer) {
934
- clearTimeout(this.idleWaitTimer);
935
- this.idleWaitTimer = null;
936
- }
937
- if (this.nudgeTimer) {
938
- clearTimeout(this.nudgeTimer);
939
- this.nudgeTimer = null;
940
- }
941
- }
942
- broadcastStatus() {
943
- if (!this.state)
944
- return;
945
- this.deps.broadcast({
946
- type: 'plan_status',
947
- status: this.state.status,
948
- executed_tasks: this.state.executed_tasks,
949
- estimated_tasks: this.state.estimated_tasks,
950
- current_line: this.state.current_line,
951
- });
952
- }
953
- }
954
- exports.PlanExecutor = PlanExecutor;
955
- //# sourceMappingURL=executor.js.map