openmatrix 0.2.21 → 0.2.23
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/dist/agents/agent-runner.d.ts +4 -0
- package/dist/agents/agent-runner.js +6 -0
- package/dist/orchestrator/executor.d.ts +2 -4
- package/dist/orchestrator/executor.js +17 -16
- package/dist/orchestrator/phase-executor.js +8 -1
- package/dist/orchestrator/scheduler.js +5 -2
- package/dist/orchestrator/state-machine.d.ts +12 -9
- package/dist/orchestrator/state-machine.js +9 -3
- package/dist/orchestrator/task-planner.js +1 -1
- package/dist/storage/state-manager.js +7 -13
- package/package.json +3 -3
- package/skills/deploy.md +108 -0
|
@@ -820,6 +820,12 @@ describe('MyFunction', () => {
|
|
|
820
820
|
getRunningCount() {
|
|
821
821
|
return this.runningAgents.size;
|
|
822
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* 标记任务完成,释放 runningAgents 槽位
|
|
825
|
+
*/
|
|
826
|
+
markTaskCompleted(taskId) {
|
|
827
|
+
this.runningAgents.delete(taskId);
|
|
828
|
+
}
|
|
823
829
|
/**
|
|
824
830
|
* 检查是否可以启动新 Agent
|
|
825
831
|
*/
|
|
@@ -46,12 +46,14 @@ export declare class OrchestratorExecutor {
|
|
|
46
46
|
private aiReviewer;
|
|
47
47
|
private meetingManager;
|
|
48
48
|
private config;
|
|
49
|
+
private _configReady;
|
|
49
50
|
private taskTimers;
|
|
50
51
|
constructor(stateManager: StateManager, approvalManager: ApprovalManager, config?: Partial<ExecutorConfig>);
|
|
51
52
|
/**
|
|
52
53
|
* 获取 PhaseExecutor 实例
|
|
53
54
|
*/
|
|
54
55
|
getPhaseExecutor(): PhaseExecutor;
|
|
56
|
+
private loadConfigFromState;
|
|
55
57
|
/**
|
|
56
58
|
* 执行一步 - 返回待执行的 Subagent 任务
|
|
57
59
|
*
|
|
@@ -65,10 +67,6 @@ export declare class OrchestratorExecutor {
|
|
|
65
67
|
* 仅在失败/异常时才暂停
|
|
66
68
|
*/
|
|
67
69
|
private checkApprovals;
|
|
68
|
-
/**
|
|
69
|
-
* 检查是否所有任务完成
|
|
70
|
-
*/
|
|
71
|
-
private checkAllCompleted;
|
|
72
70
|
/**
|
|
73
71
|
* 获取统计信息
|
|
74
72
|
*/
|
|
@@ -26,25 +26,19 @@ class OrchestratorExecutor {
|
|
|
26
26
|
aiReviewer;
|
|
27
27
|
meetingManager;
|
|
28
28
|
config;
|
|
29
|
+
_configReady;
|
|
29
30
|
taskTimers = new Map();
|
|
30
31
|
constructor(stateManager, approvalManager, config) {
|
|
31
32
|
this.stateManager = stateManager;
|
|
32
33
|
this.approvalManager = approvalManager;
|
|
33
34
|
this.meetingManager = new meeting_manager_js_1.MeetingManager(stateManager, approvalManager);
|
|
34
|
-
// 从 state.config 读取 taskTimeout,如果未定义则使用默认值
|
|
35
|
-
const stateConfig = stateManager.getState().then(s => s.config).catch(() => null);
|
|
36
35
|
const defaultTaskTimeout = 600000; // 10 分钟(毫秒)
|
|
37
36
|
this.config = {
|
|
38
37
|
maxConcurrent: 3,
|
|
39
38
|
taskTimeout: defaultTaskTimeout,
|
|
40
39
|
...config
|
|
41
40
|
};
|
|
42
|
-
|
|
43
|
-
stateConfig.then(cfg => {
|
|
44
|
-
if (cfg?.taskTimeout) {
|
|
45
|
-
this.config.taskTimeout = cfg.taskTimeout;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
41
|
+
this._configReady = this.loadConfigFromState(stateManager);
|
|
48
42
|
this.scheduler = new scheduler_js_1.Scheduler(stateManager, {
|
|
49
43
|
maxConcurrentTasks: this.config.maxConcurrent,
|
|
50
44
|
taskTimeout: this.config.taskTimeout
|
|
@@ -64,12 +58,24 @@ class OrchestratorExecutor {
|
|
|
64
58
|
getPhaseExecutor() {
|
|
65
59
|
return this.phaseExecutor;
|
|
66
60
|
}
|
|
61
|
+
async loadConfigFromState(stateManager) {
|
|
62
|
+
try {
|
|
63
|
+
const state = await stateManager.getState();
|
|
64
|
+
if (state.config?.taskTimeout) {
|
|
65
|
+
this.config.taskTimeout = state.config.taskTimeout;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// use default config
|
|
70
|
+
}
|
|
71
|
+
}
|
|
67
72
|
/**
|
|
68
73
|
* 执行一步 - 返回待执行的 Subagent 任务
|
|
69
74
|
*
|
|
70
75
|
* Skills 调用此方法获取任务,然后使用 Agent 工具执行
|
|
71
76
|
*/
|
|
72
77
|
async step() {
|
|
78
|
+
await this._configReady;
|
|
73
79
|
const state = await this.stateManager.getState();
|
|
74
80
|
// 1. 检查是否需要审批
|
|
75
81
|
const pendingApprovals = await this.checkApprovals(state);
|
|
@@ -163,14 +169,6 @@ class OrchestratorExecutor {
|
|
|
163
169
|
return state.config.approvalPoints.includes(approvalPoint);
|
|
164
170
|
});
|
|
165
171
|
}
|
|
166
|
-
/**
|
|
167
|
-
* 检查是否所有任务完成
|
|
168
|
-
*/
|
|
169
|
-
checkAllCompleted(tasks) {
|
|
170
|
-
if (tasks.length === 0)
|
|
171
|
-
return true;
|
|
172
|
-
return tasks.every(task => task.status === 'completed');
|
|
173
|
-
}
|
|
174
172
|
/**
|
|
175
173
|
* 获取统计信息
|
|
176
174
|
*/
|
|
@@ -327,6 +325,7 @@ class OrchestratorExecutor {
|
|
|
327
325
|
}
|
|
328
326
|
else if (currentPhase === 'accept') {
|
|
329
327
|
await this.scheduler.markTaskCompleted(taskId);
|
|
328
|
+
this.agentRunner.markTaskCompleted(taskId);
|
|
330
329
|
// Review 任务完成:自动解析报告并生成修复任务
|
|
331
330
|
if (task.assignedAgent === 'reviewer' && result.output) {
|
|
332
331
|
return await this.processReviewResult(taskId, result.output);
|
|
@@ -335,6 +334,7 @@ class OrchestratorExecutor {
|
|
|
335
334
|
}
|
|
336
335
|
else {
|
|
337
336
|
this.clearTaskTimeout(taskId);
|
|
337
|
+
this.agentRunner.markTaskCompleted(taskId);
|
|
338
338
|
await this.scheduler.markTaskFailed(taskId, result.error || 'Unknown error');
|
|
339
339
|
}
|
|
340
340
|
return {};
|
|
@@ -525,6 +525,7 @@ class OrchestratorExecutor {
|
|
|
525
525
|
console.error(`⏰ 任务超时: ${taskId} (${timeoutSeconds}s)`);
|
|
526
526
|
logger_js_1.logger.task.timeout(taskId, timeoutSeconds);
|
|
527
527
|
this.taskTimers.delete(taskId);
|
|
528
|
+
this.agentRunner.markTaskCompleted(taskId);
|
|
528
529
|
await this.scheduler.markTaskFailed(taskId, `Task timed out after ${timeoutSeconds}s`);
|
|
529
530
|
}, this.config.taskTimeout);
|
|
530
531
|
this.taskTimers.set(taskId, timer);
|
|
@@ -874,13 +874,20 @@ ACCEPT_FAILED
|
|
|
874
874
|
async processPhaseResult(task, phase, result) {
|
|
875
875
|
const now = new Date().toISOString();
|
|
876
876
|
if (result.success) {
|
|
877
|
+
// 计算阶段耗时
|
|
878
|
+
const phaseKey = phase;
|
|
879
|
+
const phaseData = task.phases?.[phaseKey];
|
|
880
|
+
const startedAt = phaseData?.startedAt;
|
|
881
|
+
const duration = startedAt
|
|
882
|
+
? Math.round((new Date(now).getTime() - new Date(startedAt).getTime()) / 1000)
|
|
883
|
+
: 0;
|
|
877
884
|
// 更新阶段状态
|
|
878
885
|
const updates = {
|
|
879
886
|
phases: {
|
|
880
887
|
...task.phases,
|
|
881
888
|
[phase]: {
|
|
882
889
|
status: 'completed',
|
|
883
|
-
duration
|
|
890
|
+
duration,
|
|
884
891
|
completedAt: now
|
|
885
892
|
}
|
|
886
893
|
}
|
|
@@ -262,14 +262,17 @@ class Scheduler {
|
|
|
262
262
|
this.cycleCacheKey = hash;
|
|
263
263
|
this.lastCycleCheck = now;
|
|
264
264
|
// 标记阻塞任务
|
|
265
|
+
const blockedIds = new Set();
|
|
265
266
|
for (const cycle of cycles) {
|
|
266
267
|
for (const taskId of cycle) {
|
|
268
|
+
if (blockedIds.has(taskId))
|
|
269
|
+
continue;
|
|
267
270
|
const task = tasks.find(t => t.id === taskId);
|
|
268
271
|
if (task && (task.status === 'pending' || task.status === 'retry_queue')) {
|
|
269
|
-
await this.
|
|
270
|
-
status: 'blocked',
|
|
272
|
+
await this.transitionTask(taskId, 'block', {
|
|
271
273
|
error: `循环依赖检测: ${cycle.join(' → ')}`
|
|
272
274
|
});
|
|
275
|
+
blockedIds.add(taskId);
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
278
|
}
|
|
@@ -3,16 +3,19 @@ import type { Task, TaskStatus } from '../types/index.js';
|
|
|
3
3
|
* 任务状态转换规则
|
|
4
4
|
*
|
|
5
5
|
* pending → scheduled → in_progress → verify → accept → completed
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* │ │ │ │
|
|
7
|
+
* ├──────► blocked ◄──────┘ ▼ ▼
|
|
8
|
+
* │ │ failed ◄─ failed
|
|
9
|
+
* │ ▼ │
|
|
10
|
+
* │ waiting ▼
|
|
11
|
+
* │ │ retry_queue
|
|
12
|
+
* │ │ │ │
|
|
13
|
+
* └────────────┴───────────────────┘ └──► blocked
|
|
14
|
+
*
|
|
15
|
+
* retry_queue → in_progress (直接重试)
|
|
16
|
+
* scheduled/blocked/waiting → failed
|
|
14
17
|
*/
|
|
15
|
-
export type TransitionEvent = 'schedule' | 'start' | 'develop_done' | 'verify_done' | 'accept_done' | '
|
|
18
|
+
export type TransitionEvent = 'schedule' | 'start' | 'develop_done' | 'verify_done' | 'accept_done' | 'block' | 'wait' | 'resume' | 'fail' | 'retry' | 'cancel';
|
|
16
19
|
export interface TransitionResult {
|
|
17
20
|
success: boolean;
|
|
18
21
|
fromStatus: TaskStatus;
|
|
@@ -9,6 +9,12 @@ const TRANSITIONS = [
|
|
|
9
9
|
{ from: 'scheduled', to: 'in_progress', event: 'start' },
|
|
10
10
|
// pending → in_progress (跳过 scheduled,直接开始)
|
|
11
11
|
{ from: 'pending', to: 'in_progress', event: 'start' },
|
|
12
|
+
// retry_queue → in_progress (重试后直接开始)
|
|
13
|
+
{ from: 'retry_queue', to: 'in_progress', event: 'start' },
|
|
14
|
+
// pending → blocked (循环依赖检测)
|
|
15
|
+
{ from: 'pending', to: 'blocked', event: 'block' },
|
|
16
|
+
// retry_queue → blocked (循环依赖检测)
|
|
17
|
+
{ from: 'retry_queue', to: 'blocked', event: 'block' },
|
|
12
18
|
// in_progress → verify
|
|
13
19
|
{ from: 'in_progress', to: 'verify', event: 'develop_done' },
|
|
14
20
|
// verify → accept
|
|
@@ -25,6 +31,9 @@ const TRANSITIONS = [
|
|
|
25
31
|
{ from: 'in_progress', to: 'failed', event: 'fail' },
|
|
26
32
|
{ from: 'verify', to: 'failed', event: 'fail' },
|
|
27
33
|
{ from: 'accept', to: 'failed', event: 'fail' },
|
|
34
|
+
{ from: 'scheduled', to: 'failed', event: 'fail' },
|
|
35
|
+
{ from: 'blocked', to: 'failed', event: 'fail' },
|
|
36
|
+
{ from: 'waiting', to: 'failed', event: 'fail' },
|
|
28
37
|
// failed → retry_queue
|
|
29
38
|
{ from: 'failed', to: 'retry_queue', event: 'retry' },
|
|
30
39
|
// retry_queue → pending (安全上限 100,由 executor 层控制具体 maxRetries)
|
|
@@ -110,10 +119,7 @@ class StateMachine {
|
|
|
110
119
|
'develop_done': '开发完成',
|
|
111
120
|
'verify_done': '验证完成',
|
|
112
121
|
'accept_done': '验收完成',
|
|
113
|
-
'need_verify': '需要验证',
|
|
114
|
-
'need_accept': '需要验收',
|
|
115
122
|
'block': '阻塞任务',
|
|
116
|
-
'unblock': '解除阻塞',
|
|
117
123
|
'wait': '等待外部',
|
|
118
124
|
'resume': '恢复执行',
|
|
119
125
|
'fail': '标记失败',
|
|
@@ -1368,7 +1368,7 @@ ${typeConfig.runCommand}
|
|
|
1368
1368
|
if (b.title.startsWith('系统集成:') && devTaskIds.length > 0) {
|
|
1369
1369
|
// 集成任务依赖当前阶段的所有开发任务
|
|
1370
1370
|
const currentDeps = b.dependencies;
|
|
1371
|
-
if (currentDeps.
|
|
1371
|
+
if (currentDeps.length === 0 || currentDeps.some(d => devTaskIds.includes(d))) {
|
|
1372
1372
|
integrationTaskId = b.taskId;
|
|
1373
1373
|
}
|
|
1374
1374
|
}
|
|
@@ -292,19 +292,13 @@ class StateManager {
|
|
|
292
292
|
// 从文件重新读取最新状态(不用缓存,避免 stale)
|
|
293
293
|
const state = await this.store.readJson('state.json') ?? await this.getState();
|
|
294
294
|
const stats = { ...state.statistics };
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if (!('verify' in stats))
|
|
303
|
-
stats.verify = 0;
|
|
304
|
-
if (!('accept' in stats))
|
|
305
|
-
stats.accept = 0;
|
|
306
|
-
if (!('retry_queue' in stats))
|
|
307
|
-
stats.retry_queue = 0;
|
|
295
|
+
// 确保扩展统计字段已初始化
|
|
296
|
+
stats.scheduled ??= 0;
|
|
297
|
+
stats.blocked ??= 0;
|
|
298
|
+
stats.waiting ??= 0;
|
|
299
|
+
stats.verify ??= 0;
|
|
300
|
+
stats.accept ??= 0;
|
|
301
|
+
stats.retry_queue ??= 0;
|
|
308
302
|
// Decrement old status count
|
|
309
303
|
if (oldStatus === 'pending')
|
|
310
304
|
stats.pending--;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openmatrix",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.23",
|
|
4
4
|
"description": "AI Agent task orchestration system with Claude Code Skills integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -41,18 +41,18 @@
|
|
|
41
41
|
"url": "https://github.com/bigfish1913/openmatrix/issues"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@types/node": "^22.0.0",
|
|
45
44
|
"chalk": "^5.6.2",
|
|
46
45
|
"chokidar": "^5.0.0",
|
|
47
46
|
"commander": "^14.0.3",
|
|
48
|
-
"typescript": "^5.3.3",
|
|
49
47
|
"winston": "^3.19.0"
|
|
50
48
|
},
|
|
51
49
|
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
52
51
|
"@typescript-eslint/eslint-plugin": "^8.58.1",
|
|
53
52
|
"@typescript-eslint/parser": "^8.58.1",
|
|
54
53
|
"@vitest/coverage-v8": "^1.6.1",
|
|
55
54
|
"eslint": "^10.2.0",
|
|
55
|
+
"typescript": "^5.3.3",
|
|
56
56
|
"vitest": "^1.6.0"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
package/skills/deploy.md
CHANGED
|
@@ -91,6 +91,80 @@ cat requirements.txt pyproject.toml 2>/dev/null
|
|
|
91
91
|
|
|
92
92
|
---
|
|
93
93
|
|
|
94
|
+
## Step 2.1: 检测并优化依赖源
|
|
95
|
+
|
|
96
|
+
检测项目使用的包管理器和镜像源,如果是国外慢源则建议替换为国内镜像:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# npm/yarn/pnpm 源
|
|
100
|
+
npm config get registry 2>/dev/null
|
|
101
|
+
cat .npmrc 2>/dev/null
|
|
102
|
+
cat .yarnrc 2>/dev/null
|
|
103
|
+
cat .yarnrc.yml 2>/dev/null
|
|
104
|
+
|
|
105
|
+
# pip 源
|
|
106
|
+
cat pip.conf 2>/dev/null
|
|
107
|
+
pip config get global.index-url 2>/dev/null
|
|
108
|
+
|
|
109
|
+
# Docker 镜像源
|
|
110
|
+
cat /etc/docker/daemon.json 2>/dev/null
|
|
111
|
+
cat ~/.docker/daemon.json 2>/dev/null
|
|
112
|
+
|
|
113
|
+
# Go 代理
|
|
114
|
+
go env GOPROXY 2>/dev/null
|
|
115
|
+
|
|
116
|
+
# Rust 源
|
|
117
|
+
cat ~/.cargo/config.toml 2>/dev/null
|
|
118
|
+
|
|
119
|
+
# Gradle/Maven 源
|
|
120
|
+
cat build.gradle 2>/dev/null | grep -i "repositories" -A 5
|
|
121
|
+
cat pom.xml 2>/dev/null | grep -i "mirror" -A 5
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**AI 分析源速度,如果检测到国外源,建议替换:**
|
|
125
|
+
|
|
126
|
+
| 包管理器 | 国外源 | 国内镜像 |
|
|
127
|
+
|---------|--------|---------|
|
|
128
|
+
| npm/yarn/pnpm | registry.npmjs.org | https://registry.npmmirror.com |
|
|
129
|
+
| pip | pypi.org | https://pypi.tuna.tsinghua.edu.cn/simple |
|
|
130
|
+
| Docker | docker.io | mirror.ccs.tencentyun.com |
|
|
131
|
+
| Go | proxy.golang.org | https://goproxy.cn,direct |
|
|
132
|
+
| Rust/Cargo | crates.io | https://rsproxy.cn/crates.io-index |
|
|
133
|
+
| Gradle/Maven | repo.maven.apache.org | https://maven.aliyun.com/repository/public |
|
|
134
|
+
|
|
135
|
+
**如果检测到慢源,输出建议并询问:**
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
AskUserQuestion:
|
|
139
|
+
header: "镜像源"
|
|
140
|
+
question: "检测到 npm 使用官方源(registry.npmjs.org),国内访问较慢。是否切换到国内镜像?"
|
|
141
|
+
|
|
142
|
+
options:
|
|
143
|
+
- label: "切换到淘宝镜像 (推荐)"
|
|
144
|
+
description: "npmmirror.com,国内速度快,同步延迟 < 10 分钟"
|
|
145
|
+
- label: "保持不变"
|
|
146
|
+
description: "当前源可用,不需要切换"
|
|
147
|
+
- label: "使用其他镜像"
|
|
148
|
+
description: "指定自定义镜像地址"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**用户确认后执行切换:**
|
|
152
|
+
```bash
|
|
153
|
+
# npm
|
|
154
|
+
npm config set registry https://registry.npmmirror.com
|
|
155
|
+
|
|
156
|
+
# pip
|
|
157
|
+
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
|
158
|
+
|
|
159
|
+
# Go
|
|
160
|
+
go env -w GOPROXY=https://goproxy.cn,direct
|
|
161
|
+
|
|
162
|
+
# Dockerfile 中的 apt 源(如果有)
|
|
163
|
+
# AI 读取 Dockerfile,将 archive.ubuntu.com 替换为 mirrors.aliyun.com
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
94
168
|
## Step 3: 检测系统环境
|
|
95
169
|
|
|
96
170
|
```bash
|
|
@@ -181,6 +255,40 @@ AskUserQuestion:
|
|
|
181
255
|
|
|
182
256
|
---
|
|
183
257
|
|
|
258
|
+
## Step 6.1: 生成部署任务清单
|
|
259
|
+
|
|
260
|
+
根据前面的分析和用户选择,将所有部署步骤写入 TodoWrite,逐条执行:
|
|
261
|
+
|
|
262
|
+
**示例(Docker 本地部署 + 源优化 + 脚本生成):**
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
TodoWrite({
|
|
266
|
+
todos: [
|
|
267
|
+
{ content: "切换 npm 源到淘宝镜像", activeForm: "正在切换 npm 镜像源", status: "pending" },
|
|
268
|
+
{ content: "构建 Docker 镜像", activeForm: "正在构建 Docker 镜像", status: "pending" },
|
|
269
|
+
{ content: "启动容器", activeForm: "正在启动容器", status: "pending" },
|
|
270
|
+
{ content: "验证部署:容器状态", activeForm: "正在检查容器状态", status: "pending" },
|
|
271
|
+
{ content: "验证部署:端口监听", activeForm: "正在检查端口", status: "pending" },
|
|
272
|
+
{ content: "验证部署:HTTP 连通", activeForm: "正在验证 HTTP 连通性", status: "pending" },
|
|
273
|
+
{ content: "生成 Taskfile.yml 一键脚本", activeForm: "正在生成一键脚本", status: "pending" },
|
|
274
|
+
{ content: "保存部署配置", activeForm: "正在保存部署配置", status: "pending" }
|
|
275
|
+
]
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**任务清单根据实际情况动态生成:**
|
|
280
|
+
- 需要切换源 → 加入源切换任务
|
|
281
|
+
- 需要生成 Dockerfile → 加入生成任务
|
|
282
|
+
- 多服务 → 每个服务一条任务
|
|
283
|
+
- 选择生成脚本 → 加入脚本生成任务
|
|
284
|
+
|
|
285
|
+
**执行规则:**
|
|
286
|
+
- 每次只执行一条,完成后立即标记 completed
|
|
287
|
+
- 失败时停止,输出错误信息
|
|
288
|
+
- 用户可以看到整体进度
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
184
292
|
## Step 7: 执行部署
|
|
185
293
|
|
|
186
294
|
根据用户选择,AI 执行对应命令:
|