@controlvector/cv-agent 1.9.2 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.cjs +228 -42
- package/dist/bundle.cjs.map +4 -4
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +106 -16
- package/dist/commands/agent.js.map +1 -1
- package/dist/utils/api.d.ts +2 -1
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +20 -2
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/event-queue.d.ts +66 -0
- package/dist/utils/event-queue.d.ts.map +1 -0
- package/dist/utils/event-queue.js +193 -0
- package/dist/utils/event-queue.js.map +1 -0
- package/package.json +1 -1
package/dist/bundle.cjs
CHANGED
|
@@ -959,8 +959,8 @@ var require_command = __commonJS({
|
|
|
959
959
|
"node_modules/commander/lib/command.js"(exports2) {
|
|
960
960
|
var EventEmitter = require("node:events").EventEmitter;
|
|
961
961
|
var childProcess = require("node:child_process");
|
|
962
|
-
var
|
|
963
|
-
var
|
|
962
|
+
var path3 = require("node:path");
|
|
963
|
+
var fs4 = require("node:fs");
|
|
964
964
|
var process3 = require("node:process");
|
|
965
965
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
966
966
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1892,11 +1892,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1892
1892
|
let launchWithNode = false;
|
|
1893
1893
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1894
1894
|
function findFile(baseDir, baseName) {
|
|
1895
|
-
const localBin =
|
|
1896
|
-
if (
|
|
1897
|
-
if (sourceExt.includes(
|
|
1895
|
+
const localBin = path3.resolve(baseDir, baseName);
|
|
1896
|
+
if (fs4.existsSync(localBin)) return localBin;
|
|
1897
|
+
if (sourceExt.includes(path3.extname(baseName))) return void 0;
|
|
1898
1898
|
const foundExt = sourceExt.find(
|
|
1899
|
-
(ext) =>
|
|
1899
|
+
(ext) => fs4.existsSync(`${localBin}${ext}`)
|
|
1900
1900
|
);
|
|
1901
1901
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1902
1902
|
return void 0;
|
|
@@ -1908,21 +1908,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1908
1908
|
if (this._scriptPath) {
|
|
1909
1909
|
let resolvedScriptPath;
|
|
1910
1910
|
try {
|
|
1911
|
-
resolvedScriptPath =
|
|
1911
|
+
resolvedScriptPath = fs4.realpathSync(this._scriptPath);
|
|
1912
1912
|
} catch (err) {
|
|
1913
1913
|
resolvedScriptPath = this._scriptPath;
|
|
1914
1914
|
}
|
|
1915
|
-
executableDir =
|
|
1916
|
-
|
|
1915
|
+
executableDir = path3.resolve(
|
|
1916
|
+
path3.dirname(resolvedScriptPath),
|
|
1917
1917
|
executableDir
|
|
1918
1918
|
);
|
|
1919
1919
|
}
|
|
1920
1920
|
if (executableDir) {
|
|
1921
1921
|
let localFile = findFile(executableDir, executableFile);
|
|
1922
1922
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1923
|
-
const legacyName =
|
|
1923
|
+
const legacyName = path3.basename(
|
|
1924
1924
|
this._scriptPath,
|
|
1925
|
-
|
|
1925
|
+
path3.extname(this._scriptPath)
|
|
1926
1926
|
);
|
|
1927
1927
|
if (legacyName !== this._name) {
|
|
1928
1928
|
localFile = findFile(
|
|
@@ -1933,7 +1933,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1933
1933
|
}
|
|
1934
1934
|
executableFile = localFile || executableFile;
|
|
1935
1935
|
}
|
|
1936
|
-
launchWithNode = sourceExt.includes(
|
|
1936
|
+
launchWithNode = sourceExt.includes(path3.extname(executableFile));
|
|
1937
1937
|
let proc;
|
|
1938
1938
|
if (process3.platform !== "win32") {
|
|
1939
1939
|
if (launchWithNode) {
|
|
@@ -2773,7 +2773,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2773
2773
|
* @return {Command}
|
|
2774
2774
|
*/
|
|
2775
2775
|
nameFromFilename(filename) {
|
|
2776
|
-
this._name =
|
|
2776
|
+
this._name = path3.basename(filename, path3.extname(filename));
|
|
2777
2777
|
return this;
|
|
2778
2778
|
}
|
|
2779
2779
|
/**
|
|
@@ -2787,9 +2787,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2787
2787
|
* @param {string} [path]
|
|
2788
2788
|
* @return {(string|null|Command)}
|
|
2789
2789
|
*/
|
|
2790
|
-
executableDir(
|
|
2791
|
-
if (
|
|
2792
|
-
this._executableDir =
|
|
2790
|
+
executableDir(path4) {
|
|
2791
|
+
if (path4 === void 0) return this._executableDir;
|
|
2792
|
+
this._executableDir = path4;
|
|
2793
2793
|
return this;
|
|
2794
2794
|
}
|
|
2795
2795
|
/**
|
|
@@ -4117,10 +4117,138 @@ function setupCommand() {
|
|
|
4117
4117
|
|
|
4118
4118
|
// src/commands/agent.ts
|
|
4119
4119
|
var import_node_child_process5 = require("node:child_process");
|
|
4120
|
+
var os3 = __toESM(require("node:os"));
|
|
4121
|
+
var path2 = __toESM(require("node:path"));
|
|
4122
|
+
|
|
4123
|
+
// src/utils/event-queue.ts
|
|
4124
|
+
var import_node_fs2 = require("node:fs");
|
|
4125
|
+
var path = __toESM(require("node:path"));
|
|
4126
|
+
var EventQueue = class {
|
|
4127
|
+
buffer = [];
|
|
4128
|
+
sequence = 0;
|
|
4129
|
+
drainPromise = null;
|
|
4130
|
+
closed = false;
|
|
4131
|
+
spillDirty = false;
|
|
4132
|
+
spillPath;
|
|
4133
|
+
poster;
|
|
4134
|
+
maxRetries;
|
|
4135
|
+
baseDelayMs;
|
|
4136
|
+
onError;
|
|
4137
|
+
constructor(opts) {
|
|
4138
|
+
this.spillPath = opts.spillPath;
|
|
4139
|
+
this.poster = opts.poster;
|
|
4140
|
+
this.maxRetries = opts.maxRetries ?? 5;
|
|
4141
|
+
this.baseDelayMs = opts.baseDelayMs ?? 1e3;
|
|
4142
|
+
this.onError = opts.onError;
|
|
4143
|
+
}
|
|
4144
|
+
/** Load any events that were spilled to disk by a previous run. */
|
|
4145
|
+
async loadSpill() {
|
|
4146
|
+
try {
|
|
4147
|
+
const raw = await import_node_fs2.promises.readFile(this.spillPath, "utf8");
|
|
4148
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
4149
|
+
for (const line of lines) {
|
|
4150
|
+
const ev = JSON.parse(line);
|
|
4151
|
+
this.buffer.push(ev);
|
|
4152
|
+
if (ev.sequence_number >= this.sequence) {
|
|
4153
|
+
this.sequence = ev.sequence_number + 1;
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
} catch (err) {
|
|
4157
|
+
if (err.code !== "ENOENT") throw err;
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
/** Assign the next sequence number. Callers that want to stamp manually can read this. */
|
|
4161
|
+
nextSequence() {
|
|
4162
|
+
return this.sequence++;
|
|
4163
|
+
}
|
|
4164
|
+
/** Enqueue an event. Returns the assigned sequence_number. */
|
|
4165
|
+
enqueue(event) {
|
|
4166
|
+
if (this.closed) {
|
|
4167
|
+
throw new Error("EventQueue is closed");
|
|
4168
|
+
}
|
|
4169
|
+
const seq = event.sequence_number ?? this.nextSequence();
|
|
4170
|
+
if (seq >= this.sequence) this.sequence = seq + 1;
|
|
4171
|
+
this.buffer.push({ ...event, sequence_number: seq });
|
|
4172
|
+
this.spillDirty = true;
|
|
4173
|
+
this.drain().catch(() => {
|
|
4174
|
+
});
|
|
4175
|
+
return seq;
|
|
4176
|
+
}
|
|
4177
|
+
/** Drain the buffer, retrying transient failures. Serial — concurrent calls share the same promise. */
|
|
4178
|
+
drain() {
|
|
4179
|
+
if (this.drainPromise) return this.drainPromise;
|
|
4180
|
+
this.drainPromise = this.drainInternal().finally(() => {
|
|
4181
|
+
this.drainPromise = null;
|
|
4182
|
+
});
|
|
4183
|
+
return this.drainPromise;
|
|
4184
|
+
}
|
|
4185
|
+
async drainInternal() {
|
|
4186
|
+
try {
|
|
4187
|
+
while (this.buffer.length > 0) {
|
|
4188
|
+
const ev = this.buffer[0];
|
|
4189
|
+
let delivered = false;
|
|
4190
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
4191
|
+
try {
|
|
4192
|
+
await this.poster(ev);
|
|
4193
|
+
delivered = true;
|
|
4194
|
+
break;
|
|
4195
|
+
} catch (err) {
|
|
4196
|
+
this.onError?.(err instanceof Error ? err : new Error(String(err)), ev, attempt);
|
|
4197
|
+
if (attempt < this.maxRetries - 1) {
|
|
4198
|
+
await sleep(this.baseDelayMs * Math.pow(2, attempt));
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
if (delivered) {
|
|
4203
|
+
this.buffer.shift();
|
|
4204
|
+
this.spillDirty = true;
|
|
4205
|
+
} else {
|
|
4206
|
+
break;
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
} finally {
|
|
4210
|
+
if (this.spillDirty) {
|
|
4211
|
+
await this.writeSpill().catch(() => {
|
|
4212
|
+
});
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
}
|
|
4216
|
+
/** Flush: drain remaining events, then persist anything still pending to disk. */
|
|
4217
|
+
async flush() {
|
|
4218
|
+
await this.drain();
|
|
4219
|
+
await this.writeSpill();
|
|
4220
|
+
}
|
|
4221
|
+
/** Mark the queue closed and flush. Safe to call multiple times. */
|
|
4222
|
+
async close() {
|
|
4223
|
+
if (this.closed) return;
|
|
4224
|
+
this.closed = true;
|
|
4225
|
+
await this.flush();
|
|
4226
|
+
}
|
|
4227
|
+
/** Number of events still pending delivery. */
|
|
4228
|
+
size() {
|
|
4229
|
+
return this.buffer.length;
|
|
4230
|
+
}
|
|
4231
|
+
async writeSpill() {
|
|
4232
|
+
try {
|
|
4233
|
+
if (this.buffer.length === 0) {
|
|
4234
|
+
await import_node_fs2.promises.rm(this.spillPath, { force: true });
|
|
4235
|
+
} else {
|
|
4236
|
+
await import_node_fs2.promises.mkdir(path.dirname(this.spillPath), { recursive: true });
|
|
4237
|
+
const body = this.buffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
4238
|
+
await import_node_fs2.promises.writeFile(this.spillPath, body, "utf8");
|
|
4239
|
+
}
|
|
4240
|
+
this.spillDirty = false;
|
|
4241
|
+
} catch {
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
};
|
|
4245
|
+
function sleep(ms) {
|
|
4246
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
4247
|
+
}
|
|
4120
4248
|
|
|
4121
4249
|
// src/utils/api.ts
|
|
4122
|
-
async function apiCall(creds, method,
|
|
4123
|
-
const url = `${creds.CV_HUB_API}${
|
|
4250
|
+
async function apiCall(creds, method, path3, body) {
|
|
4251
|
+
const url = `${creds.CV_HUB_API}${path3}`;
|
|
4124
4252
|
const headers = {
|
|
4125
4253
|
"Authorization": `Bearer ${creds.CV_HUB_PAT}`,
|
|
4126
4254
|
"Content-Type": "application/json"
|
|
@@ -4131,7 +4259,7 @@ async function apiCall(creds, method, path, body) {
|
|
|
4131
4259
|
body: body ? JSON.stringify(body) : void 0
|
|
4132
4260
|
});
|
|
4133
4261
|
}
|
|
4134
|
-
async function registerExecutor(creds, machineName, workingDir, repositoryId, metadata) {
|
|
4262
|
+
async function registerExecutor(creds, machineName, workingDir, repositoryId, metadata, repoOwnerSlug) {
|
|
4135
4263
|
const body = {
|
|
4136
4264
|
name: `cva:${machineName}`,
|
|
4137
4265
|
machine_name: machineName,
|
|
@@ -4150,7 +4278,23 @@ async function registerExecutor(creds, machineName, workingDir, repositoryId, me
|
|
|
4150
4278
|
if (metadata?.tags) body.tags = metadata.tags;
|
|
4151
4279
|
if (metadata?.owner_project) body.owner_project = metadata.owner_project;
|
|
4152
4280
|
if (metadata?.integration) body.integration = metadata.integration;
|
|
4153
|
-
|
|
4281
|
+
let res = await apiCall(creds, "POST", "/api/v1/executors", body);
|
|
4282
|
+
if (res.status === 400 && repoOwnerSlug) {
|
|
4283
|
+
try {
|
|
4284
|
+
const errData = await res.json();
|
|
4285
|
+
const orgs = errData.error?.organizations;
|
|
4286
|
+
if (orgs && orgs.length > 0) {
|
|
4287
|
+
const match = orgs.find(
|
|
4288
|
+
(o) => o.slug.toLowerCase() === repoOwnerSlug.toLowerCase()
|
|
4289
|
+
);
|
|
4290
|
+
if (match) {
|
|
4291
|
+
body.organization_id = match.id;
|
|
4292
|
+
res = await apiCall(creds, "POST", "/api/v1/executors", body);
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
} catch {
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4154
4298
|
if (!res.ok) {
|
|
4155
4299
|
const err = await res.text();
|
|
4156
4300
|
throw new Error(`Failed to register executor: ${res.status} ${err}`);
|
|
@@ -4593,7 +4737,7 @@ async function writeConfig(config) {
|
|
|
4593
4737
|
|
|
4594
4738
|
// src/commands/git-safety.ts
|
|
4595
4739
|
var import_node_child_process3 = require("node:child_process");
|
|
4596
|
-
var
|
|
4740
|
+
var import_node_fs3 = require("node:fs");
|
|
4597
4741
|
var import_node_path2 = require("node:path");
|
|
4598
4742
|
var import_node_os3 = require("node:os");
|
|
4599
4743
|
var DANGEROUS_PATTERNS = [
|
|
@@ -4629,7 +4773,7 @@ function checkWorkspaceSafety(workspaceRoot) {
|
|
|
4629
4773
|
if (home.startsWith(resolved + "/")) {
|
|
4630
4774
|
return `Workspace (${resolved}) is a parent of HOME. Refusing to auto-commit.`;
|
|
4631
4775
|
}
|
|
4632
|
-
if (!(0,
|
|
4776
|
+
if (!(0, import_node_fs3.existsSync)((0, import_node_path2.join)(resolved, ".git"))) {
|
|
4633
4777
|
return `No .git directory in ${resolved}. Not a git repository.`;
|
|
4634
4778
|
}
|
|
4635
4779
|
return null;
|
|
@@ -4771,7 +4915,7 @@ Files: ${added} added, ${modified} modified, ${deleted} deleted`;
|
|
|
4771
4915
|
|
|
4772
4916
|
// src/commands/deploy-manifest.ts
|
|
4773
4917
|
var import_node_child_process4 = require("node:child_process");
|
|
4774
|
-
var
|
|
4918
|
+
var import_node_fs4 = require("node:fs");
|
|
4775
4919
|
var import_node_path3 = require("node:path");
|
|
4776
4920
|
function exec(cmd, cwd, timeoutMs = 3e5, env2) {
|
|
4777
4921
|
try {
|
|
@@ -4816,9 +4960,9 @@ function getHeadCommit(cwd) {
|
|
|
4816
4960
|
}
|
|
4817
4961
|
function loadDeployManifest(workspaceRoot) {
|
|
4818
4962
|
const manifestPath = (0, import_node_path3.join)(workspaceRoot, ".cva", "deploy.json");
|
|
4819
|
-
if (!(0,
|
|
4963
|
+
if (!(0, import_node_fs4.existsSync)(manifestPath)) return null;
|
|
4820
4964
|
try {
|
|
4821
|
-
const raw = (0,
|
|
4965
|
+
const raw = (0, import_node_fs4.readFileSync)(manifestPath, "utf8");
|
|
4822
4966
|
const manifest = JSON.parse(raw);
|
|
4823
4967
|
if (!manifest.version || manifest.version < 1) {
|
|
4824
4968
|
console.log(" [deploy] Invalid manifest version");
|
|
@@ -5185,6 +5329,7 @@ async function launchAutoApproveMode(prompt, options) {
|
|
|
5185
5329
|
const pendingQuestionIds = [];
|
|
5186
5330
|
let fullOutput = "";
|
|
5187
5331
|
let lastProgressBytes = 0;
|
|
5332
|
+
let totalBytesStreamed = 0;
|
|
5188
5333
|
const runOnce = (inputPrompt, isContinue) => {
|
|
5189
5334
|
return new Promise((resolve2, reject) => {
|
|
5190
5335
|
const args = isContinue ? ["-p", inputPrompt, "--continue", "--allowedTools", ...ALLOWED_TOOLS] : ["-p", inputPrompt, "--allowedTools", ...ALLOWED_TOOLS];
|
|
@@ -5206,8 +5351,20 @@ async function launchAutoApproveMode(prompt, options) {
|
|
|
5206
5351
|
process.stdout.write(data);
|
|
5207
5352
|
fullOutput += text;
|
|
5208
5353
|
if (fullOutput.length > MAX_OUTPUT_BYTES) {
|
|
5354
|
+
const dropped = fullOutput.length - MAX_OUTPUT_BYTES;
|
|
5209
5355
|
fullOutput = fullOutput.slice(-MAX_OUTPUT_BYTES);
|
|
5356
|
+
options.eventQueue?.enqueue({
|
|
5357
|
+
event_type: "output",
|
|
5358
|
+
content: {
|
|
5359
|
+
chunk: `
|
|
5360
|
+
[... ${dropped} bytes truncated from output buffer ...]
|
|
5361
|
+
`,
|
|
5362
|
+
truncated_bytes: dropped,
|
|
5363
|
+
byte_offset: totalBytesStreamed
|
|
5364
|
+
}
|
|
5365
|
+
});
|
|
5210
5366
|
}
|
|
5367
|
+
totalBytesStreamed += text.length;
|
|
5211
5368
|
if (Date.now() - spawnTime < 1e4) {
|
|
5212
5369
|
const authError = containsAuthError(fullOutput + stderr);
|
|
5213
5370
|
if (authError) {
|
|
@@ -5258,10 +5415,9 @@ ${source_default.red("!")} Claude Code auth failure detected: "${authError}"`);
|
|
|
5258
5415
|
).catch(() => {
|
|
5259
5416
|
});
|
|
5260
5417
|
}
|
|
5261
|
-
|
|
5418
|
+
options.eventQueue?.enqueue({
|
|
5262
5419
|
event_type: "output",
|
|
5263
|
-
content: { chunk, byte_offset:
|
|
5264
|
-
}).catch(() => {
|
|
5420
|
+
content: { chunk, byte_offset: totalBytesStreamed }
|
|
5265
5421
|
});
|
|
5266
5422
|
}
|
|
5267
5423
|
}
|
|
@@ -5337,6 +5493,7 @@ async function launchRelayMode(prompt, options) {
|
|
|
5337
5493
|
let lastProgressBytes = 0;
|
|
5338
5494
|
let authFailure = false;
|
|
5339
5495
|
const spawnTime = Date.now();
|
|
5496
|
+
let totalBytesStreamed = 0;
|
|
5340
5497
|
child.stdin?.write(prompt + "\n");
|
|
5341
5498
|
let lastRedirectCheck = Date.now();
|
|
5342
5499
|
let lineBuffer = "";
|
|
@@ -5346,8 +5503,20 @@ async function launchRelayMode(prompt, options) {
|
|
|
5346
5503
|
stdoutBuffer += text;
|
|
5347
5504
|
fullOutput += text;
|
|
5348
5505
|
if (fullOutput.length > MAX_OUTPUT_BYTES) {
|
|
5506
|
+
const dropped = fullOutput.length - MAX_OUTPUT_BYTES;
|
|
5349
5507
|
fullOutput = fullOutput.slice(-MAX_OUTPUT_BYTES);
|
|
5508
|
+
options.eventQueue?.enqueue({
|
|
5509
|
+
event_type: "output",
|
|
5510
|
+
content: {
|
|
5511
|
+
chunk: `
|
|
5512
|
+
[... ${dropped} bytes truncated from output buffer ...]
|
|
5513
|
+
`,
|
|
5514
|
+
truncated_bytes: dropped,
|
|
5515
|
+
byte_offset: totalBytesStreamed
|
|
5516
|
+
}
|
|
5517
|
+
});
|
|
5350
5518
|
}
|
|
5519
|
+
totalBytesStreamed += text.length;
|
|
5351
5520
|
if (Date.now() - spawnTime < 1e4 && !authFailure) {
|
|
5352
5521
|
const authError = containsAuthError(fullOutput + stderr);
|
|
5353
5522
|
if (authError) {
|
|
@@ -5406,10 +5575,9 @@ ${source_default.red("!")} Claude Code auth failure detected: "${authError}"`);
|
|
|
5406
5575
|
{ output_chunk: chunk }
|
|
5407
5576
|
).catch(() => {
|
|
5408
5577
|
});
|
|
5409
|
-
|
|
5578
|
+
options.eventQueue?.enqueue({
|
|
5410
5579
|
event_type: "output",
|
|
5411
|
-
content: { chunk, byte_offset:
|
|
5412
|
-
}).catch(() => {
|
|
5580
|
+
content: { chunk, byte_offset: totalBytesStreamed }
|
|
5413
5581
|
});
|
|
5414
5582
|
}
|
|
5415
5583
|
if (Date.now() - lastRedirectCheck > 1e4) {
|
|
@@ -5727,16 +5895,16 @@ async function runAgent(options) {
|
|
|
5727
5895
|
}
|
|
5728
5896
|
const gitignorePath = require("path").join(workingDir, ".gitignore");
|
|
5729
5897
|
try {
|
|
5730
|
-
const
|
|
5898
|
+
const fs4 = require("fs");
|
|
5731
5899
|
const credPatterns = ".env\n.env.*\n.claude/\n.claude.json\n.credentials*\n*.pem\n*.key\n.ssh/\n.gnupg/\n.npm/\n.config/\n.zsh_history\n.bash_history\nnode_modules/\n.DS_Store\n";
|
|
5732
5900
|
let existing = "";
|
|
5733
5901
|
try {
|
|
5734
|
-
existing =
|
|
5902
|
+
existing = fs4.readFileSync(gitignorePath, "utf-8");
|
|
5735
5903
|
} catch {
|
|
5736
5904
|
}
|
|
5737
5905
|
if (!existing.includes(".claude/")) {
|
|
5738
5906
|
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
5739
|
-
|
|
5907
|
+
fs4.writeFileSync(gitignorePath, existing + prefix + "# Credentials (auto-added by cv-agent)\n" + credPatterns);
|
|
5740
5908
|
console.log(source_default.gray(" Bootstrap: .gitignore credential protection added"));
|
|
5741
5909
|
}
|
|
5742
5910
|
} catch {
|
|
@@ -5774,6 +5942,7 @@ async function runAgent(options) {
|
|
|
5774
5942
|
}
|
|
5775
5943
|
}
|
|
5776
5944
|
let detectedRepoId;
|
|
5945
|
+
let detectedOwnerSlug;
|
|
5777
5946
|
try {
|
|
5778
5947
|
const remoteUrl = (0, import_node_child_process5.execSync)("git remote get-url origin 2>/dev/null", {
|
|
5779
5948
|
cwd: workingDir,
|
|
@@ -5785,6 +5954,7 @@ async function runAgent(options) {
|
|
|
5785
5954
|
);
|
|
5786
5955
|
if (cvHubMatch) {
|
|
5787
5956
|
const [, repoOwner, repoSlug] = cvHubMatch;
|
|
5957
|
+
detectedOwnerSlug = repoOwner;
|
|
5788
5958
|
try {
|
|
5789
5959
|
const repoData = await resolveRepoId(creds, repoOwner, repoSlug);
|
|
5790
5960
|
if (repoData?.id) {
|
|
@@ -5796,6 +5966,9 @@ async function runAgent(options) {
|
|
|
5796
5966
|
}
|
|
5797
5967
|
} catch {
|
|
5798
5968
|
}
|
|
5969
|
+
if (options.org) {
|
|
5970
|
+
detectedOwnerSlug = options.org;
|
|
5971
|
+
}
|
|
5799
5972
|
const wsConfig = await readWorkspaceConfig(workingDir);
|
|
5800
5973
|
const globalConfig = await readConfig();
|
|
5801
5974
|
const role = options.role || wsConfig.role || globalConfig.role || "development";
|
|
@@ -5836,7 +6009,7 @@ async function runAgent(options) {
|
|
|
5836
6009
|
console.log(source_default.yellow(` Guard: ${guardIcon} ${dispatchGuard}`));
|
|
5837
6010
|
}
|
|
5838
6011
|
const executor = await withRetry(
|
|
5839
|
-
() => registerExecutor(creds, machineName, workingDir, detectedRepoId, executorMeta),
|
|
6012
|
+
() => registerExecutor(creds, machineName, workingDir, detectedRepoId, executorMeta, detectedOwnerSlug),
|
|
5840
6013
|
"Executor registration"
|
|
5841
6014
|
);
|
|
5842
6015
|
const mode = options.autoApprove ? "auto-approve" : "relay";
|
|
@@ -5956,6 +6129,14 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
5956
6129
|
console.log(source_default.gray(` Git remote: ${remoteInfo.remoteName} -> ${remoteInfo.remoteUrl}`));
|
|
5957
6130
|
}
|
|
5958
6131
|
const prompt = buildClaudePrompt(task);
|
|
6132
|
+
const spillPath = path2.join(os3.tmpdir(), "cva-event-queue", `${task.id}.ndjson`);
|
|
6133
|
+
const eventQueue = new EventQueue({
|
|
6134
|
+
spillPath,
|
|
6135
|
+
poster: async (event) => {
|
|
6136
|
+
await postTaskEvent(creds, task.id, event);
|
|
6137
|
+
}
|
|
6138
|
+
});
|
|
6139
|
+
await eventQueue.loadSpill();
|
|
5959
6140
|
try {
|
|
5960
6141
|
const mode = options.autoApprove ? "auto-approve" : "relay";
|
|
5961
6142
|
console.log(`\u{1F680} ${source_default.bold.green("RUNNING")} \u2014 Launching Claude Code (${mode})...`);
|
|
@@ -5971,7 +6152,8 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
5971
6152
|
taskId: task.id,
|
|
5972
6153
|
executorId: state.executorId,
|
|
5973
6154
|
spawnEnv: claudeEnv,
|
|
5974
|
-
machineName: state.machineName
|
|
6155
|
+
machineName: state.machineName,
|
|
6156
|
+
eventQueue
|
|
5975
6157
|
});
|
|
5976
6158
|
} else {
|
|
5977
6159
|
result = await launchRelayMode(prompt, {
|
|
@@ -5980,7 +6162,8 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
5980
6162
|
executorId: state.executorId,
|
|
5981
6163
|
taskId: task.id,
|
|
5982
6164
|
spawnEnv: claudeEnv,
|
|
5983
|
-
machineName: state.machineName
|
|
6165
|
+
machineName: state.machineName,
|
|
6166
|
+
eventQueue
|
|
5984
6167
|
});
|
|
5985
6168
|
}
|
|
5986
6169
|
console.log(source_default.gray("\n" + "-".repeat(60)));
|
|
@@ -6041,14 +6224,13 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
6041
6224
|
sendTaskLog(creds, state.executorId, task.id, "info", `Deploy manifest error: ${deployErr.message}`);
|
|
6042
6225
|
}
|
|
6043
6226
|
}
|
|
6044
|
-
|
|
6227
|
+
eventQueue.enqueue({
|
|
6045
6228
|
event_type: "completed",
|
|
6046
6229
|
content: {
|
|
6047
6230
|
exit_code: result.exitCode,
|
|
6048
6231
|
duration_seconds: Math.round((Date.now() - startTime) / 1e3),
|
|
6049
6232
|
files_changed: allChangedFiles.length
|
|
6050
6233
|
}
|
|
6051
|
-
}).catch(() => {
|
|
6052
6234
|
});
|
|
6053
6235
|
if (result.exitCode === 0) {
|
|
6054
6236
|
if (allChangedFiles.length > 0) {
|
|
@@ -6073,14 +6255,13 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
6073
6255
|
console.log();
|
|
6074
6256
|
console.log(`\u2705 ${source_default.bold.green("COMPLETED")} \u2014 Duration: ${elapsed}`);
|
|
6075
6257
|
printBanner("COMPLETED", elapsed, allChangedFiles, postGitState.headSha);
|
|
6076
|
-
|
|
6258
|
+
eventQueue.enqueue({
|
|
6077
6259
|
event_type: "output_final",
|
|
6078
6260
|
content: {
|
|
6079
6261
|
output: result.output.slice(-MAX_OUTPUT_FINAL_BYTES),
|
|
6080
6262
|
exit_code: result.exitCode,
|
|
6081
6263
|
duration_seconds: Math.round((Date.now() - startTime) / 1e3)
|
|
6082
6264
|
}
|
|
6083
|
-
}).catch(() => {
|
|
6084
6265
|
});
|
|
6085
6266
|
await withRetry(
|
|
6086
6267
|
() => completeTask(creds, state.executorId, task.id, payload),
|
|
@@ -6169,6 +6350,10 @@ ${source_default.red("!")} Task error: ${err.message}`);
|
|
|
6169
6350
|
} finally {
|
|
6170
6351
|
clearInterval(heartbeatTimer);
|
|
6171
6352
|
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
6353
|
+
try {
|
|
6354
|
+
await eventQueue.close();
|
|
6355
|
+
} catch {
|
|
6356
|
+
}
|
|
6172
6357
|
state.currentTaskId = null;
|
|
6173
6358
|
state.lastTaskEnd = Date.now();
|
|
6174
6359
|
}
|
|
@@ -6192,6 +6377,7 @@ function agentCommand() {
|
|
|
6192
6377
|
cmd.option("--dispatch-guard <guard>", "Dispatch guard: open, confirm, locked (default: open)");
|
|
6193
6378
|
cmd.option("--tags <tags>", "Comma-separated tags for this executor");
|
|
6194
6379
|
cmd.option("--owner-project <project>", "Project this executor belongs to");
|
|
6380
|
+
cmd.option("--org <slug>", "Organization slug (auto-detected from repo owner if omitted)");
|
|
6195
6381
|
cmd.action(async (opts) => {
|
|
6196
6382
|
await runAgent(opts);
|
|
6197
6383
|
});
|
|
@@ -6515,7 +6701,7 @@ function statusCommand() {
|
|
|
6515
6701
|
|
|
6516
6702
|
// src/index.ts
|
|
6517
6703
|
var program2 = new Command();
|
|
6518
|
-
program2.name("cva").description('CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch.\n\nRun "cva setup" to get started.').version(true ? "1.
|
|
6704
|
+
program2.name("cva").description('CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch.\n\nRun "cva setup" to get started.').version(true ? "1.10.1" : "1.6.0");
|
|
6519
6705
|
program2.addCommand(setupCommand());
|
|
6520
6706
|
program2.addCommand(agentCommand());
|
|
6521
6707
|
program2.addCommand(authCommand());
|