@controlvector/cv-agent 0.1.1 → 1.1.0
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 +164 -0
- package/dist/bundle.cjs +286 -39
- package/dist/bundle.cjs.map +3 -3
- package/dist/commands/agent-git.d.ts +2 -1
- package/dist/commands/agent-git.d.ts.map +1 -1
- package/dist/commands/agent-git.js +2 -11
- package/dist/commands/agent-git.js.map +1 -1
- package/dist/commands/agent.d.ts +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +222 -44
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/auth.js +2 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/api.d.ts +18 -0
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +36 -0
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/output-parser.d.ts +26 -0
- package/dist/utils/output-parser.d.ts.map +1 -0
- package/dist/utils/output-parser.js +65 -0
- package/dist/utils/output-parser.js.map +1 -0
- package/package.json +1 -1
|
@@ -24,6 +24,7 @@ export interface PostTaskState {
|
|
|
24
24
|
}
|
|
25
25
|
export interface TaskCompletionPayload {
|
|
26
26
|
summary: string;
|
|
27
|
+
output: string;
|
|
27
28
|
commit: {
|
|
28
29
|
sha: string | null;
|
|
29
30
|
branch: string | null;
|
|
@@ -48,7 +49,7 @@ export interface TaskCompletionPayload {
|
|
|
48
49
|
export declare function gitExec(cmd: string, cwd: string): string | null;
|
|
49
50
|
export declare function capturePreTaskState(cwd: string): PreTaskState;
|
|
50
51
|
export declare function capturePostTaskState(cwd: string, preState: PreTaskState): PostTaskState;
|
|
51
|
-
export declare function buildCompletionPayload(exitCode: number, preState: PreTaskState, postState: PostTaskState, startTime: number): TaskCompletionPayload;
|
|
52
|
+
export declare function buildCompletionPayload(exitCode: number, preState: PreTaskState, postState: PostTaskState, startTime: number, output?: string): TaskCompletionPayload;
|
|
52
53
|
export declare function verifyGitRemote(cwd: string, task: {
|
|
53
54
|
owner?: string;
|
|
54
55
|
repo?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-git.d.ts","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAMD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAO7D;AAMD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"agent-git.d.ts","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAMD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAO7D;AAMD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,aAAa,CAmGvF;AAMD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,MAAW,GAClB,qBAAqB,CAsDvB;AAMD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACvC,OAAO,EAAE,MAAM,GACd;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmClD"}
|
|
@@ -48,7 +48,6 @@ function capturePostTaskState(cwd, preState) {
|
|
|
48
48
|
let linesDeleted = 0;
|
|
49
49
|
let commitMessages = [];
|
|
50
50
|
if (preState.headSha && headSha && preState.headSha !== headSha) {
|
|
51
|
-
// HEAD changed — diff between pre and post
|
|
52
51
|
const diffOutput = gitExec(`git diff --name-status ${preState.headSha}..${headSha}`, cwd);
|
|
53
52
|
if (diffOutput) {
|
|
54
53
|
for (const line of diffOutput.split('\n')) {
|
|
@@ -69,7 +68,6 @@ function capturePostTaskState(cwd, preState) {
|
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
|
-
// Line stats
|
|
73
71
|
const statOutput = gitExec(`git diff --shortstat ${preState.headSha}..${headSha}`, cwd);
|
|
74
72
|
if (statOutput) {
|
|
75
73
|
const addMatch = statOutput.match(/(\d+) insertion/);
|
|
@@ -77,21 +75,18 @@ function capturePostTaskState(cwd, preState) {
|
|
|
77
75
|
linesAdded = addMatch ? parseInt(addMatch[1], 10) : 0;
|
|
78
76
|
linesDeleted = delMatch ? parseInt(delMatch[1], 10) : 0;
|
|
79
77
|
}
|
|
80
|
-
// Commit messages
|
|
81
78
|
const logOutput = gitExec(`git log --oneline ${preState.headSha}..${headSha}`, cwd);
|
|
82
79
|
if (logOutput) {
|
|
83
80
|
commitMessages = logOutput.split('\n').filter(Boolean);
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
else if (!preState.headSha && headSha) {
|
|
87
|
-
// Repo was empty, everything is new
|
|
88
84
|
const allFiles = gitExec('git ls-files', cwd);
|
|
89
85
|
if (allFiles) {
|
|
90
86
|
filesAdded = allFiles.split('\n').filter(Boolean);
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
89
|
else {
|
|
94
|
-
// HEAD unchanged — check for uncommitted changes
|
|
95
90
|
const statusOutput = gitExec('git status --porcelain', cwd);
|
|
96
91
|
if (statusOutput) {
|
|
97
92
|
for (const line of statusOutput.split('\n')) {
|
|
@@ -108,7 +103,6 @@ function capturePostTaskState(cwd, preState) {
|
|
|
108
103
|
}
|
|
109
104
|
}
|
|
110
105
|
}
|
|
111
|
-
// Push status
|
|
112
106
|
let pushStatus = 'not_pushed';
|
|
113
107
|
let pushRemote = null;
|
|
114
108
|
const remote = gitExec('git remote get-url origin', cwd);
|
|
@@ -140,7 +134,7 @@ function capturePostTaskState(cwd, preState) {
|
|
|
140
134
|
// ============================================================================
|
|
141
135
|
// Structured completion payload
|
|
142
136
|
// ============================================================================
|
|
143
|
-
function buildCompletionPayload(exitCode, preState, postState, startTime) {
|
|
137
|
+
function buildCompletionPayload(exitCode, preState, postState, startTime, output = '') {
|
|
144
138
|
const durationSec = Math.floor((Date.now() - startTime) / 1000);
|
|
145
139
|
const durationStr = durationSec >= 60
|
|
146
140
|
? `${Math.floor(durationSec / 60)}m ${durationSec % 60}s`
|
|
@@ -166,6 +160,7 @@ function buildCompletionPayload(exitCode, preState, postState, startTime) {
|
|
|
166
160
|
}
|
|
167
161
|
return {
|
|
168
162
|
summary: parts.join(' '),
|
|
163
|
+
output,
|
|
169
164
|
commit: {
|
|
170
165
|
sha: postState.headSha,
|
|
171
166
|
branch: postState.branch,
|
|
@@ -196,7 +191,6 @@ function verifyGitRemote(cwd, task, gitHost) {
|
|
|
196
191
|
const expectedRemote = `https://${gitHost}/${task.owner}/${task.repo}.git`;
|
|
197
192
|
const currentRemote = gitExec('git remote get-url origin', cwd);
|
|
198
193
|
if (!currentRemote) {
|
|
199
|
-
// No remote — add origin
|
|
200
194
|
try {
|
|
201
195
|
(0, node_child_process_1.execSync)(`git remote add origin ${expectedRemote}`, { cwd, timeout: 5000 });
|
|
202
196
|
}
|
|
@@ -204,18 +198,15 @@ function verifyGitRemote(cwd, task, gitHost) {
|
|
|
204
198
|
return { remoteName: 'origin', remoteUrl: expectedRemote };
|
|
205
199
|
}
|
|
206
200
|
if (currentRemote === expectedRemote) {
|
|
207
|
-
// Already correct
|
|
208
201
|
return { remoteName: 'origin', remoteUrl: expectedRemote };
|
|
209
202
|
}
|
|
210
203
|
if (currentRemote.includes(gitHost)) {
|
|
211
|
-
// Wrong CV-Hub namespace — fix origin
|
|
212
204
|
try {
|
|
213
205
|
(0, node_child_process_1.execSync)(`git remote set-url origin ${expectedRemote}`, { cwd, timeout: 5000 });
|
|
214
206
|
}
|
|
215
207
|
catch { }
|
|
216
208
|
return { remoteName: 'origin', remoteUrl: expectedRemote };
|
|
217
209
|
}
|
|
218
|
-
// External remote (GitHub etc) — add cv-hub as secondary
|
|
219
210
|
const cvRemote = gitExec('git remote get-url cv-hub', cwd);
|
|
220
211
|
if (!cvRemote) {
|
|
221
212
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-git.js","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;
|
|
1
|
+
{"version":3,"file":"agent-git.js","sourceRoot":"","sources":["../../src/commands/agent-git.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* cva agent command
|
|
3
3
|
*
|
|
4
|
-
* Listens for tasks dispatched
|
|
4
|
+
* Listens for tasks dispatched via CV-Hub and executes them
|
|
5
5
|
* with Claude Code. The agent registers as an executor, polls for tasks,
|
|
6
6
|
* and reports results back.
|
|
7
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAm4BpC,wBAAgB,YAAY,IAAI,OAAO,CActC"}
|
package/dist/commands/agent.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* cva agent command
|
|
4
4
|
*
|
|
5
|
-
* Listens for tasks dispatched
|
|
5
|
+
* Listens for tasks dispatched via CV-Hub and executes them
|
|
6
6
|
* with Claude Code. The agent registers as an executor, polls for tasks,
|
|
7
7
|
* and reports results back.
|
|
8
8
|
*
|
|
@@ -26,6 +26,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
26
26
|
const chalk_1 = __importDefault(require("chalk"));
|
|
27
27
|
const credentials_js_1 = require("../utils/credentials.js");
|
|
28
28
|
const api_js_1 = require("../utils/api.js");
|
|
29
|
+
const output_parser_js_1 = require("../utils/output-parser.js");
|
|
29
30
|
const agent_git_js_1 = require("./agent-git.js");
|
|
30
31
|
const display_js_1 = require("../utils/display.js");
|
|
31
32
|
const retry_js_1 = require("../utils/retry.js");
|
|
@@ -34,7 +35,7 @@ const retry_js_1 = require("../utils/retry.js");
|
|
|
34
35
|
// ============================================================================
|
|
35
36
|
function buildClaudePrompt(task) {
|
|
36
37
|
let prompt = '';
|
|
37
|
-
prompt += `You are executing a task dispatched
|
|
38
|
+
prompt += `You are executing a task dispatched via CV-Hub.\n\n`;
|
|
38
39
|
prompt += `## Task: ${task.title}\n`;
|
|
39
40
|
prompt += `Task ID: ${task.id}\n`;
|
|
40
41
|
prompt += `Priority: ${task.priority}\n`;
|
|
@@ -149,35 +150,108 @@ const PERMISSION_PATTERNS = [
|
|
|
149
150
|
// ============================================================================
|
|
150
151
|
// Auto-approve mode launcher (proven, -p + --allowedTools)
|
|
151
152
|
// ============================================================================
|
|
153
|
+
/** Max output buffer size (200KB) — truncate to last 200KB if exceeded */
|
|
154
|
+
const MAX_OUTPUT_BYTES = 200 * 1024;
|
|
155
|
+
/** Interval (in bytes) at which to post progress events with output chunks */
|
|
156
|
+
const OUTPUT_PROGRESS_INTERVAL = 4096;
|
|
152
157
|
async function launchAutoApproveMode(prompt, options) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
158
|
+
// Use a stable session ID so we can --continue if a question needs follow-up
|
|
159
|
+
const sessionId = options.taskId
|
|
160
|
+
? options.taskId.replace(/-/g, '').slice(0, 32).padEnd(32, '0')
|
|
161
|
+
.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5')
|
|
162
|
+
: undefined;
|
|
163
|
+
const pendingQuestionIds = [];
|
|
164
|
+
let fullOutput = '';
|
|
165
|
+
let lastProgressBytes = 0;
|
|
166
|
+
const runOnce = (inputPrompt, isContinue) => {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
const args = isContinue
|
|
169
|
+
? ['-p', inputPrompt, '--continue', '--allowedTools', ...ALLOWED_TOOLS]
|
|
170
|
+
: ['-p', inputPrompt, '--allowedTools', ...ALLOWED_TOOLS];
|
|
171
|
+
if (sessionId && !isContinue) {
|
|
172
|
+
args.push('--session-id', sessionId);
|
|
173
|
+
}
|
|
174
|
+
const child = (0, node_child_process_1.spawn)('claude', args, {
|
|
175
|
+
cwd: options.cwd,
|
|
176
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
177
|
+
env: { ...process.env },
|
|
178
|
+
});
|
|
179
|
+
_activeChild = child;
|
|
180
|
+
let stderr = '';
|
|
181
|
+
let lineBuffer = '';
|
|
182
|
+
child.stdout?.on('data', (data) => {
|
|
183
|
+
const text = data.toString();
|
|
184
|
+
process.stdout.write(data);
|
|
185
|
+
// Accumulate full output (capped at MAX_OUTPUT_BYTES)
|
|
186
|
+
fullOutput += text;
|
|
187
|
+
if (fullOutput.length > MAX_OUTPUT_BYTES) {
|
|
188
|
+
fullOutput = fullOutput.slice(-MAX_OUTPUT_BYTES);
|
|
189
|
+
}
|
|
190
|
+
if (options.creds && options.taskId) {
|
|
191
|
+
lineBuffer += text;
|
|
192
|
+
const lines = lineBuffer.split('\n');
|
|
193
|
+
lineBuffer = lines.pop() ?? '';
|
|
194
|
+
for (const line of lines) {
|
|
195
|
+
const event = (0, output_parser_js_1.parseClaudeCodeOutput)(line);
|
|
196
|
+
if (event) {
|
|
197
|
+
(0, api_js_1.postTaskEvent)(options.creds, options.taskId, {
|
|
198
|
+
event_type: event.eventType,
|
|
199
|
+
content: event.content,
|
|
200
|
+
needs_response: event.needsResponse,
|
|
201
|
+
}).then((created) => {
|
|
202
|
+
if (event.needsResponse && created?.id) {
|
|
203
|
+
pendingQuestionIds.push(created.id);
|
|
204
|
+
}
|
|
205
|
+
}).catch(() => { });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Post periodic progress with output chunk
|
|
209
|
+
if (fullOutput.length - lastProgressBytes >= OUTPUT_PROGRESS_INTERVAL) {
|
|
210
|
+
const chunk = fullOutput.slice(lastProgressBytes).slice(-OUTPUT_PROGRESS_INTERVAL);
|
|
211
|
+
lastProgressBytes = fullOutput.length;
|
|
212
|
+
if (options.executorId) {
|
|
213
|
+
(0, api_js_1.sendTaskLog)(options.creds, options.executorId, options.taskId, 'progress', 'Claude Code output', { output_chunk: chunk }).catch(() => { });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
child.stderr?.on('data', (data) => {
|
|
219
|
+
stderr += data.toString();
|
|
220
|
+
process.stderr.write(data);
|
|
221
|
+
});
|
|
222
|
+
child.on('close', (code, signal) => {
|
|
223
|
+
_activeChild = null;
|
|
224
|
+
resolve({ exitCode: signal === 'SIGKILL' ? 137 : (code ?? 1), stderr, output: fullOutput });
|
|
225
|
+
});
|
|
226
|
+
child.on('error', (err) => {
|
|
227
|
+
_activeChild = null;
|
|
228
|
+
reject(err);
|
|
229
|
+
});
|
|
166
230
|
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
231
|
+
};
|
|
232
|
+
// Initial run
|
|
233
|
+
let result = await runOnce(prompt, false);
|
|
234
|
+
// If there were unanswered questions and the task isn't aborted,
|
|
235
|
+
// attempt to continue with responses (up to 3 follow-ups)
|
|
236
|
+
if (options.creds && options.taskId && result.exitCode === 0) {
|
|
237
|
+
let followUps = 0;
|
|
238
|
+
while (pendingQuestionIds.length > 0 && followUps < 3) {
|
|
239
|
+
const questionId = pendingQuestionIds.shift();
|
|
240
|
+
console.log(chalk_1.default.gray(` [auto-approve] Waiting for planner response to question...`));
|
|
241
|
+
const response = await pollForEventResponse(options.creds, options.taskId, questionId, 300_000);
|
|
242
|
+
if (response) {
|
|
243
|
+
const responseText = typeof response === 'string' ? response : JSON.stringify(response);
|
|
244
|
+
console.log(chalk_1.default.gray(` [auto-approve] Planner responded, continuing with --continue`));
|
|
245
|
+
result = await runOnce(responseText, true);
|
|
246
|
+
followUps++;
|
|
171
247
|
}
|
|
172
248
|
else {
|
|
173
|
-
|
|
249
|
+
console.log(chalk_1.default.yellow(` [auto-approve] No response received, continuing without.`));
|
|
250
|
+
break;
|
|
174
251
|
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
reject(err);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
181
255
|
}
|
|
182
256
|
// ============================================================================
|
|
183
257
|
// Relay mode launcher (interactive, permission prompts relayed to CV-Hub)
|
|
@@ -193,25 +267,93 @@ async function launchRelayMode(prompt, options) {
|
|
|
193
267
|
_activeChild = child;
|
|
194
268
|
let stderr = '';
|
|
195
269
|
let stdoutBuffer = '';
|
|
270
|
+
let fullOutput = '';
|
|
271
|
+
let lastProgressBytes = 0;
|
|
196
272
|
// Send the task prompt as initial input
|
|
197
273
|
child.stdin?.write(prompt + '\n');
|
|
198
|
-
|
|
274
|
+
let lastRedirectCheck = Date.now();
|
|
275
|
+
let lineBuffer = '';
|
|
276
|
+
// Tee stdout to terminal while scanning for permission patterns + structured markers
|
|
199
277
|
child.stdout?.on('data', async (data) => {
|
|
200
278
|
const text = data.toString();
|
|
201
279
|
process.stdout.write(data); // Tee to terminal
|
|
202
280
|
stdoutBuffer += text;
|
|
203
|
-
//
|
|
281
|
+
// Accumulate full output (capped at MAX_OUTPUT_BYTES)
|
|
282
|
+
fullOutput += text;
|
|
283
|
+
if (fullOutput.length > MAX_OUTPUT_BYTES) {
|
|
284
|
+
fullOutput = fullOutput.slice(-MAX_OUTPUT_BYTES);
|
|
285
|
+
}
|
|
286
|
+
// Parse line-by-line for structured markers
|
|
287
|
+
lineBuffer += text;
|
|
288
|
+
const lines = lineBuffer.split('\n');
|
|
289
|
+
lineBuffer = lines.pop() ?? '';
|
|
290
|
+
for (const line of lines) {
|
|
291
|
+
const event = (0, output_parser_js_1.parseClaudeCodeOutput)(line);
|
|
292
|
+
if (event) {
|
|
293
|
+
try {
|
|
294
|
+
const created = await (0, api_js_1.postTaskEvent)(options.creds, options.taskId, {
|
|
295
|
+
event_type: event.eventType,
|
|
296
|
+
content: event.content,
|
|
297
|
+
needs_response: event.needsResponse,
|
|
298
|
+
});
|
|
299
|
+
// If question, wait for response via task events
|
|
300
|
+
if (event.needsResponse && created?.id) {
|
|
301
|
+
console.log(chalk_1.default.gray(` [stream] Question detected, waiting for planner response...`));
|
|
302
|
+
const response = await pollForEventResponse(options.creds, options.taskId, created.id, 300_000);
|
|
303
|
+
if (response) {
|
|
304
|
+
const responseText = typeof response === 'string' ? response : JSON.stringify(response);
|
|
305
|
+
child.stdin?.write(responseText + '\n');
|
|
306
|
+
console.log(chalk_1.default.gray(` [stream] Planner responded.`));
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
child.stdin?.write('[No response received within timeout. Continue with your best judgment.]\n');
|
|
310
|
+
console.log(chalk_1.default.yellow(` [stream] Question timed out.`));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Non-fatal: don't block execution
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// Post periodic progress with output chunk
|
|
320
|
+
if (fullOutput.length - lastProgressBytes >= OUTPUT_PROGRESS_INTERVAL) {
|
|
321
|
+
const chunk = fullOutput.slice(lastProgressBytes).slice(-OUTPUT_PROGRESS_INTERVAL);
|
|
322
|
+
lastProgressBytes = fullOutput.length;
|
|
323
|
+
(0, api_js_1.sendTaskLog)(options.creds, options.executorId, options.taskId, 'progress', 'Claude Code output', { output_chunk: chunk }).catch(() => { });
|
|
324
|
+
}
|
|
325
|
+
// Periodic redirect check (every 10 seconds)
|
|
326
|
+
if (Date.now() - lastRedirectCheck > 10_000) {
|
|
327
|
+
try {
|
|
328
|
+
const redirects = await (0, api_js_1.getRedirects)(options.creds, options.taskId, new Date(lastRedirectCheck).toISOString());
|
|
329
|
+
for (const redirect of redirects) {
|
|
330
|
+
const instruction = redirect.content?.instruction;
|
|
331
|
+
if (instruction) {
|
|
332
|
+
child.stdin?.write(`\n[REDIRECT FROM PLANNER]: ${instruction}\n`);
|
|
333
|
+
console.log(chalk_1.default.gray(` [stream] Redirect received from planner.`));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
// Non-fatal
|
|
339
|
+
}
|
|
340
|
+
lastRedirectCheck = Date.now();
|
|
341
|
+
}
|
|
342
|
+
// Check for permission prompts (existing relay logic)
|
|
204
343
|
for (const pattern of PERMISSION_PATTERNS) {
|
|
205
344
|
const match = stdoutBuffer.match(pattern);
|
|
206
345
|
if (match) {
|
|
207
346
|
const promptText = match[0];
|
|
208
347
|
stdoutBuffer = ''; // Reset buffer after match
|
|
209
348
|
try {
|
|
210
|
-
// Log the approval request
|
|
211
349
|
await (0, api_js_1.sendTaskLog)(options.creds, options.executorId, options.taskId, 'info', `Permission prompt: ${promptText}`, { prompt_text: promptText });
|
|
212
|
-
// Create prompt on CV-Hub for user response
|
|
213
350
|
const { prompt_id } = await (0, api_js_1.createTaskPrompt)(options.creds, options.executorId, options.taskId, promptText, 'approval', ['y', 'n']);
|
|
214
|
-
//
|
|
351
|
+
// Also emit as approval_request event
|
|
352
|
+
(0, api_js_1.postTaskEvent)(options.creds, options.taskId, {
|
|
353
|
+
event_type: 'approval_request',
|
|
354
|
+
content: { prompt_text: promptText },
|
|
355
|
+
needs_response: true,
|
|
356
|
+
}).catch(() => { });
|
|
215
357
|
const timeoutMs = 5 * 60 * 1000;
|
|
216
358
|
const startPoll = Date.now();
|
|
217
359
|
let answered = false;
|
|
@@ -232,17 +374,15 @@ async function launchRelayMode(prompt, options) {
|
|
|
232
374
|
}
|
|
233
375
|
}
|
|
234
376
|
if (!answered) {
|
|
235
|
-
// Timeout — deny
|
|
236
377
|
child.stdin?.write('n\n');
|
|
237
378
|
console.log(chalk_1.default.yellow(` [relay] Prompt timed out, denying.`));
|
|
238
379
|
}
|
|
239
380
|
}
|
|
240
381
|
catch (err) {
|
|
241
|
-
// If prompt relay fails, deny to be safe
|
|
242
382
|
child.stdin?.write('n\n');
|
|
243
383
|
console.log(chalk_1.default.yellow(` [relay] Prompt relay error: ${err.message}, denying.`));
|
|
244
384
|
}
|
|
245
|
-
break;
|
|
385
|
+
break;
|
|
246
386
|
}
|
|
247
387
|
}
|
|
248
388
|
// Keep buffer manageable (only last 2KB)
|
|
@@ -258,10 +398,10 @@ async function launchRelayMode(prompt, options) {
|
|
|
258
398
|
child.on('close', (code, signal) => {
|
|
259
399
|
_activeChild = null;
|
|
260
400
|
if (signal === 'SIGKILL') {
|
|
261
|
-
resolve({ exitCode: 137, stderr });
|
|
401
|
+
resolve({ exitCode: 137, stderr, output: fullOutput });
|
|
262
402
|
}
|
|
263
403
|
else {
|
|
264
|
-
resolve({ exitCode: code ?? 1, stderr });
|
|
404
|
+
resolve({ exitCode: code ?? 1, stderr, output: fullOutput });
|
|
265
405
|
}
|
|
266
406
|
});
|
|
267
407
|
child.on('error', (err) => {
|
|
@@ -271,6 +411,25 @@ async function launchRelayMode(prompt, options) {
|
|
|
271
411
|
});
|
|
272
412
|
}
|
|
273
413
|
// ============================================================================
|
|
414
|
+
// Event response polling
|
|
415
|
+
// ============================================================================
|
|
416
|
+
async function pollForEventResponse(creds, taskId, eventId, timeoutMs) {
|
|
417
|
+
const start = Date.now();
|
|
418
|
+
while (Date.now() - start < timeoutMs) {
|
|
419
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
420
|
+
try {
|
|
421
|
+
const result = await (0, api_js_1.getEventResponse)(creds, taskId, eventId);
|
|
422
|
+
if (result.response !== null) {
|
|
423
|
+
return result.response;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// Continue polling
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
// ============================================================================
|
|
274
433
|
// Main agent loop
|
|
275
434
|
// ============================================================================
|
|
276
435
|
async function runAgent(options) {
|
|
@@ -311,7 +470,7 @@ async function runAgent(options) {
|
|
|
311
470
|
}
|
|
312
471
|
const machineName = options.machine || await (0, credentials_js_1.getMachineName)();
|
|
313
472
|
const pollInterval = Math.max(3, parseInt(options.pollInterval, 10)) * 1000;
|
|
314
|
-
const workingDir = options.workingDir;
|
|
473
|
+
const workingDir = options.workingDir === '.' ? process.cwd() : options.workingDir;
|
|
315
474
|
if (!options.machine) {
|
|
316
475
|
const credCheck = await (0, credentials_js_1.readCredentials)();
|
|
317
476
|
if (!credCheck.CV_HUB_MACHINE_NAME) {
|
|
@@ -373,6 +532,8 @@ async function executeTask(task, state, creds, options) {
|
|
|
373
532
|
state.currentTaskId = task.id;
|
|
374
533
|
// Clear status line
|
|
375
534
|
process.stdout.write('\r\x1b[K');
|
|
535
|
+
// Task received
|
|
536
|
+
console.log(`📥 ${chalk_1.default.bold.cyan('RECEIVED')} — Task: ${task.title} (${task.priority})`);
|
|
376
537
|
// Task header
|
|
377
538
|
console.log(chalk_1.default.bold('┌─────────────────────────────────────────────────────────────┐'));
|
|
378
539
|
console.log(chalk_1.default.bold(`│ Task: ${(task.title || '').substring(0, 53).padEnd(53)}│`));
|
|
@@ -424,14 +585,19 @@ async function executeTask(task, state, creds, options) {
|
|
|
424
585
|
const prompt = buildClaudePrompt(task);
|
|
425
586
|
try {
|
|
426
587
|
const mode = options.autoApprove ? 'auto-approve' : 'relay';
|
|
427
|
-
console.log(chalk_1.default.
|
|
588
|
+
console.log(`🚀 ${chalk_1.default.bold.green('RUNNING')} — Launching Claude Code (${mode})...`);
|
|
428
589
|
if (options.autoApprove) {
|
|
429
590
|
console.log(chalk_1.default.gray(' Allowed tools: ') + ALLOWED_TOOLS.join(', '));
|
|
430
591
|
}
|
|
431
592
|
console.log(chalk_1.default.gray('-'.repeat(60)));
|
|
432
593
|
let result;
|
|
433
594
|
if (options.autoApprove) {
|
|
434
|
-
result = await launchAutoApproveMode(prompt, {
|
|
595
|
+
result = await launchAutoApproveMode(prompt, {
|
|
596
|
+
cwd: options.workingDir,
|
|
597
|
+
creds,
|
|
598
|
+
taskId: task.id,
|
|
599
|
+
executorId: state.executorId,
|
|
600
|
+
});
|
|
435
601
|
}
|
|
436
602
|
else {
|
|
437
603
|
result = await launchRelayMode(prompt, {
|
|
@@ -444,19 +610,29 @@ async function executeTask(task, state, creds, options) {
|
|
|
444
610
|
console.log(chalk_1.default.gray('\n' + '-'.repeat(60)));
|
|
445
611
|
// Capture post-task git state
|
|
446
612
|
const postGitState = (0, agent_git_js_1.capturePostTaskState)(options.workingDir, preGitState);
|
|
447
|
-
const payload = (0, agent_git_js_1.buildCompletionPayload)(result.exitCode, preGitState, postGitState, startTime);
|
|
613
|
+
const payload = (0, agent_git_js_1.buildCompletionPayload)(result.exitCode, preGitState, postGitState, startTime, result.output);
|
|
448
614
|
const elapsed = (0, display_js_1.formatDuration)(Date.now() - startTime);
|
|
449
615
|
const allChangedFiles = [
|
|
450
616
|
...postGitState.filesAdded,
|
|
451
617
|
...postGitState.filesModified,
|
|
452
618
|
...postGitState.filesDeleted,
|
|
453
619
|
];
|
|
620
|
+
// Emit completion event via task events
|
|
621
|
+
(0, api_js_1.postTaskEvent)(creds, task.id, {
|
|
622
|
+
event_type: 'completed',
|
|
623
|
+
content: {
|
|
624
|
+
exit_code: result.exitCode,
|
|
625
|
+
duration_seconds: Math.round((Date.now() - startTime) / 1000),
|
|
626
|
+
files_changed: allChangedFiles.length,
|
|
627
|
+
},
|
|
628
|
+
}).catch(() => { });
|
|
454
629
|
if (result.exitCode === 0) {
|
|
455
630
|
if (allChangedFiles.length > 0) {
|
|
456
631
|
(0, api_js_1.sendTaskLog)(creds, state.executorId, task.id, 'git', `${allChangedFiles.length} file(s) changed (+${postGitState.linesAdded}/-${postGitState.linesDeleted})`, { added: postGitState.filesAdded.slice(0, 10), modified: postGitState.filesModified.slice(0, 10), deleted: postGitState.filesDeleted.slice(0, 10) });
|
|
457
632
|
}
|
|
458
633
|
(0, api_js_1.sendTaskLog)(creds, state.executorId, task.id, 'lifecycle', `Claude Code completed successfully (${elapsed})`, undefined, 100);
|
|
459
634
|
console.log();
|
|
635
|
+
console.log(`✅ ${chalk_1.default.bold.green('COMPLETED')} — Duration: ${elapsed}`);
|
|
460
636
|
(0, display_js_1.printBanner)('COMPLETED', elapsed, allChangedFiles, postGitState.headSha);
|
|
461
637
|
await (0, retry_js_1.withRetry)(() => (0, api_js_1.completeTask)(creds, state.executorId, task.id, payload), 'Report completion');
|
|
462
638
|
state.completedCount++;
|
|
@@ -465,6 +641,7 @@ async function executeTask(task, state, creds, options) {
|
|
|
465
641
|
else if (result.exitCode === 137) {
|
|
466
642
|
(0, api_js_1.sendTaskLog)(creds, state.executorId, task.id, 'lifecycle', 'Task aborted by user (Ctrl+C)');
|
|
467
643
|
console.log();
|
|
644
|
+
console.log(`⏹ ${chalk_1.default.bold.yellow('ABORTED')} — Duration: ${elapsed}`);
|
|
468
645
|
(0, display_js_1.printBanner)('ABORTED', elapsed, [], null);
|
|
469
646
|
try {
|
|
470
647
|
await (0, api_js_1.failTask)(creds, state.executorId, task.id, 'Aborted by user (Ctrl+C)');
|
|
@@ -476,6 +653,7 @@ async function executeTask(task, state, creds, options) {
|
|
|
476
653
|
const stderrTail = result.stderr.trim().slice(-500);
|
|
477
654
|
(0, api_js_1.sendTaskLog)(creds, state.executorId, task.id, 'lifecycle', `Claude Code exited with code ${result.exitCode} (${elapsed})`, stderrTail ? { stderr_tail: stderrTail } : undefined);
|
|
478
655
|
console.log();
|
|
656
|
+
console.log(`❌ ${chalk_1.default.bold.red('FAILED')} — Duration: ${elapsed} (exit code ${result.exitCode})`);
|
|
479
657
|
(0, display_js_1.printBanner)('FAILED', elapsed, allChangedFiles, postGitState.headSha);
|
|
480
658
|
const errorDetail = result.stderr.trim()
|
|
481
659
|
? `${result.stderr.trim().slice(-1500)}\n\nExit code ${result.exitCode} after ${elapsed}.`
|
|
@@ -510,10 +688,10 @@ async function executeTask(task, state, creds, options) {
|
|
|
510
688
|
// ============================================================================
|
|
511
689
|
function agentCommand() {
|
|
512
690
|
const cmd = new commander_1.Command('agent');
|
|
513
|
-
cmd.description('Listen for tasks dispatched
|
|
514
|
-
cmd.option('--machine <name>', '
|
|
515
|
-
cmd.option('--poll-interval <seconds>', 'How often to check for tasks', '5');
|
|
516
|
-
cmd.option('--working-dir <path>', 'Working directory for Claude Code',
|
|
691
|
+
cmd.description('Listen for tasks dispatched via CV-Hub and execute them with Claude Code');
|
|
692
|
+
cmd.option('--machine <name>', 'Override auto-detected machine name');
|
|
693
|
+
cmd.option('--poll-interval <seconds>', 'How often to check for tasks, minimum 3 (default: 5)', '5');
|
|
694
|
+
cmd.option('--working-dir <path>', 'Working directory for Claude Code (default: current directory)', '.');
|
|
517
695
|
cmd.option('--auto-approve', 'Pre-approve all tool permissions (uses -p mode)', false);
|
|
518
696
|
cmd.action(async (opts) => {
|
|
519
697
|
await runAgent(opts);
|