@controlvector/cv-agent 1.4.0 → 1.5.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/dist/bundle.cjs +337 -17
- package/dist/bundle.cjs.map +4 -4
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +47 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/deploy-manifest.d.ts +67 -0
- package/dist/commands/deploy-manifest.d.ts.map +1 -0
- package/dist/commands/deploy-manifest.js +211 -0
- package/dist/commands/deploy-manifest.js.map +1 -0
- package/dist/commands/git-safety.d.ts +29 -0
- package/dist/commands/git-safety.d.ts.map +1 -0
- package/dist/commands/git-safety.js +116 -0
- package/dist/commands/git-safety.js.map +1 -0
- package/package.json +1 -1
package/dist/bundle.cjs
CHANGED
|
@@ -3037,7 +3037,7 @@ var {
|
|
|
3037
3037
|
} = import_index.default;
|
|
3038
3038
|
|
|
3039
3039
|
// src/commands/agent.ts
|
|
3040
|
-
var
|
|
3040
|
+
var import_node_child_process4 = require("node:child_process");
|
|
3041
3041
|
|
|
3042
3042
|
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
3043
3043
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
@@ -4089,6 +4089,277 @@ async function writeConfig(config) {
|
|
|
4089
4089
|
await import_fs2.promises.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
4090
4090
|
}
|
|
4091
4091
|
|
|
4092
|
+
// src/commands/git-safety.ts
|
|
4093
|
+
var import_node_child_process2 = require("node:child_process");
|
|
4094
|
+
function git(cmd, cwd) {
|
|
4095
|
+
return (0, import_node_child_process2.execSync)(cmd, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
|
|
4096
|
+
}
|
|
4097
|
+
function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
4098
|
+
const targetBranch = branch || "main";
|
|
4099
|
+
try {
|
|
4100
|
+
let statusOutput;
|
|
4101
|
+
try {
|
|
4102
|
+
statusOutput = git("git status --porcelain", workspaceRoot);
|
|
4103
|
+
} catch {
|
|
4104
|
+
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false, error: "git status failed" };
|
|
4105
|
+
}
|
|
4106
|
+
const lines = statusOutput.split("\n").filter(Boolean);
|
|
4107
|
+
if (lines.length === 0) {
|
|
4108
|
+
try {
|
|
4109
|
+
const unpushed = git(`git log origin/${targetBranch}..HEAD --oneline 2>/dev/null`, workspaceRoot);
|
|
4110
|
+
if (unpushed) {
|
|
4111
|
+
console.log(` [git-safety] Found unpushed commits \u2014 pushing now`);
|
|
4112
|
+
git(`git push origin ${targetBranch}`, workspaceRoot);
|
|
4113
|
+
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: true };
|
|
4114
|
+
}
|
|
4115
|
+
} catch {
|
|
4116
|
+
}
|
|
4117
|
+
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
|
|
4118
|
+
}
|
|
4119
|
+
let added = 0, modified = 0, deleted = 0;
|
|
4120
|
+
for (const line of lines) {
|
|
4121
|
+
const code = line.substring(0, 2);
|
|
4122
|
+
if (code.includes("?")) added++;
|
|
4123
|
+
else if (code.includes("D")) deleted++;
|
|
4124
|
+
else if (code.includes("M") || code.includes("A")) modified++;
|
|
4125
|
+
else added++;
|
|
4126
|
+
}
|
|
4127
|
+
console.log(
|
|
4128
|
+
` [git-safety] ${lines.length} uncommitted changes (${added} new, ${modified} modified, ${deleted} deleted) \u2014 committing now`
|
|
4129
|
+
);
|
|
4130
|
+
git("git add -A", workspaceRoot);
|
|
4131
|
+
const shortId = taskId.substring(0, 8);
|
|
4132
|
+
const commitMsg = `task: ${taskTitle} [${shortId}]
|
|
4133
|
+
|
|
4134
|
+
Auto-committed by cv-agent git safety net.
|
|
4135
|
+
Task ID: ${taskId}
|
|
4136
|
+
Files: ${added} added, ${modified} modified, ${deleted} deleted`;
|
|
4137
|
+
let commitSha;
|
|
4138
|
+
try {
|
|
4139
|
+
const commitOutput = git(`git commit -m ${JSON.stringify(commitMsg)}`, workspaceRoot);
|
|
4140
|
+
const shaMatch = commitOutput.match(/\[[\w/]+ ([a-f0-9]+)\]/);
|
|
4141
|
+
commitSha = shaMatch ? shaMatch[1] : void 0;
|
|
4142
|
+
} catch (e) {
|
|
4143
|
+
if (!e.message?.includes("nothing to commit")) {
|
|
4144
|
+
return {
|
|
4145
|
+
hadChanges: true,
|
|
4146
|
+
filesAdded: added,
|
|
4147
|
+
filesModified: modified,
|
|
4148
|
+
filesDeleted: deleted,
|
|
4149
|
+
pushed: false,
|
|
4150
|
+
error: `Commit failed: ${e.message}`
|
|
4151
|
+
};
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
try {
|
|
4155
|
+
git(`git push origin ${targetBranch}`, workspaceRoot);
|
|
4156
|
+
console.log(` [git-safety] Committed and pushed: ${commitSha || "ok"}`);
|
|
4157
|
+
} catch (pushErr) {
|
|
4158
|
+
console.log(` [git-safety] Push failed: ${pushErr.message}`);
|
|
4159
|
+
return {
|
|
4160
|
+
hadChanges: true,
|
|
4161
|
+
filesAdded: added,
|
|
4162
|
+
filesModified: modified,
|
|
4163
|
+
filesDeleted: deleted,
|
|
4164
|
+
commitSha,
|
|
4165
|
+
pushed: false,
|
|
4166
|
+
error: `Push failed: ${pushErr.message}`
|
|
4167
|
+
};
|
|
4168
|
+
}
|
|
4169
|
+
return {
|
|
4170
|
+
hadChanges: true,
|
|
4171
|
+
filesAdded: added,
|
|
4172
|
+
filesModified: modified,
|
|
4173
|
+
filesDeleted: deleted,
|
|
4174
|
+
commitSha,
|
|
4175
|
+
pushed: true
|
|
4176
|
+
};
|
|
4177
|
+
} catch (err) {
|
|
4178
|
+
return {
|
|
4179
|
+
hadChanges: false,
|
|
4180
|
+
filesAdded: 0,
|
|
4181
|
+
filesModified: 0,
|
|
4182
|
+
filesDeleted: 0,
|
|
4183
|
+
pushed: false,
|
|
4184
|
+
error: err.message
|
|
4185
|
+
};
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
// src/commands/deploy-manifest.ts
|
|
4190
|
+
var import_node_child_process3 = require("node:child_process");
|
|
4191
|
+
var import_node_fs = require("node:fs");
|
|
4192
|
+
var import_node_path = require("node:path");
|
|
4193
|
+
function exec(cmd, cwd, timeoutMs = 3e5, env2) {
|
|
4194
|
+
try {
|
|
4195
|
+
const stdout = (0, import_node_child_process3.execSync)(cmd, {
|
|
4196
|
+
cwd,
|
|
4197
|
+
encoding: "utf8",
|
|
4198
|
+
timeout: timeoutMs,
|
|
4199
|
+
env: env2 ? { ...process.env, ...env2 } : void 0,
|
|
4200
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4201
|
+
});
|
|
4202
|
+
return { ok: true, stdout, stderr: "" };
|
|
4203
|
+
} catch (err) {
|
|
4204
|
+
return { ok: false, stdout: err.stdout || "", stderr: err.stderr || err.message || "" };
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
async function httpStatus(url, timeoutMs = 1e4) {
|
|
4208
|
+
try {
|
|
4209
|
+
const controller = new AbortController();
|
|
4210
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
4211
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
4212
|
+
clearTimeout(timer);
|
|
4213
|
+
return res.status;
|
|
4214
|
+
} catch {
|
|
4215
|
+
return 0;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
async function checkHealth(url, timeoutSeconds) {
|
|
4219
|
+
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
4220
|
+
while (Date.now() < deadline) {
|
|
4221
|
+
const status = await httpStatus(url);
|
|
4222
|
+
if (status >= 200 && status < 500) return true;
|
|
4223
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
4224
|
+
}
|
|
4225
|
+
return false;
|
|
4226
|
+
}
|
|
4227
|
+
function getHeadCommit(cwd) {
|
|
4228
|
+
try {
|
|
4229
|
+
return (0, import_node_child_process3.execSync)("git rev-parse HEAD", { cwd, encoding: "utf8", timeout: 5e3 }).trim();
|
|
4230
|
+
} catch {
|
|
4231
|
+
return null;
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
function loadDeployManifest(workspaceRoot) {
|
|
4235
|
+
const manifestPath = (0, import_node_path.join)(workspaceRoot, ".cva", "deploy.json");
|
|
4236
|
+
if (!(0, import_node_fs.existsSync)(manifestPath)) return null;
|
|
4237
|
+
try {
|
|
4238
|
+
const raw = (0, import_node_fs.readFileSync)(manifestPath, "utf8");
|
|
4239
|
+
const manifest = JSON.parse(raw);
|
|
4240
|
+
if (!manifest.version || manifest.version < 1) {
|
|
4241
|
+
console.log(" [deploy] Invalid manifest version");
|
|
4242
|
+
return null;
|
|
4243
|
+
}
|
|
4244
|
+
return manifest;
|
|
4245
|
+
} catch (err) {
|
|
4246
|
+
console.log(` [deploy] Failed to read .cva/deploy.json: ${err.message}`);
|
|
4247
|
+
return null;
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
async function postTaskDeploy(workspaceRoot, taskId, log) {
|
|
4251
|
+
const manifest = loadDeployManifest(workspaceRoot);
|
|
4252
|
+
if (!manifest) {
|
|
4253
|
+
return { deployed: false, steps: [{ step: "manifest", status: "skipped", message: "No .cva/deploy.json" }] };
|
|
4254
|
+
}
|
|
4255
|
+
const steps = [];
|
|
4256
|
+
const preDeployCommit = getHeadCommit(workspaceRoot);
|
|
4257
|
+
if (manifest.build?.steps) {
|
|
4258
|
+
for (const buildStep of manifest.build.steps) {
|
|
4259
|
+
const stepName = `build:${buildStep.name}`;
|
|
4260
|
+
console.log(` [deploy] Building: ${buildStep.name}`);
|
|
4261
|
+
log("lifecycle", `Building: ${buildStep.name}`);
|
|
4262
|
+
const start = Date.now();
|
|
4263
|
+
const cwd = (0, import_node_path.join)(workspaceRoot, buildStep.working_dir || ".");
|
|
4264
|
+
const timeoutMs = (buildStep.timeout_seconds || 300) * 1e3;
|
|
4265
|
+
const result = exec(buildStep.command, cwd, timeoutMs);
|
|
4266
|
+
if (!result.ok) {
|
|
4267
|
+
const msg = `Build failed: ${buildStep.name}
|
|
4268
|
+
${result.stderr.slice(-500)}`;
|
|
4269
|
+
steps.push({ step: stepName, status: "failed", message: msg, durationMs: Date.now() - start });
|
|
4270
|
+
log("error", msg);
|
|
4271
|
+
return { deployed: false, steps, error: msg };
|
|
4272
|
+
}
|
|
4273
|
+
steps.push({ step: stepName, status: "ok", durationMs: Date.now() - start });
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
if (manifest.migrate?.command) {
|
|
4277
|
+
console.log(" [deploy] Running migration...");
|
|
4278
|
+
log("lifecycle", "Applying database migration");
|
|
4279
|
+
const start = Date.now();
|
|
4280
|
+
const env2 = manifest.migrate.env || {};
|
|
4281
|
+
const result = exec(manifest.migrate.command, workspaceRoot, 6e4, env2);
|
|
4282
|
+
if (!result.ok) {
|
|
4283
|
+
const msg = `Migration failed:
|
|
4284
|
+
${result.stderr.slice(-500)}`;
|
|
4285
|
+
steps.push({ step: "migrate", status: "failed", message: msg, durationMs: Date.now() - start });
|
|
4286
|
+
log("error", msg);
|
|
4287
|
+
return { deployed: false, steps, error: msg };
|
|
4288
|
+
}
|
|
4289
|
+
steps.push({ step: "migrate", status: "ok", durationMs: Date.now() - start });
|
|
4290
|
+
}
|
|
4291
|
+
if (manifest.service && manifest.service.type !== "none" && manifest.service.restart_command) {
|
|
4292
|
+
console.log(` [deploy] Restarting service: ${manifest.service.name || "default"}`);
|
|
4293
|
+
log("lifecycle", `Restarting service: ${manifest.service.name || "default"}`);
|
|
4294
|
+
const start = Date.now();
|
|
4295
|
+
const result = exec(manifest.service.restart_command, workspaceRoot, 3e4);
|
|
4296
|
+
if (!result.ok) {
|
|
4297
|
+
const msg = `Restart failed:
|
|
4298
|
+
${result.stderr.slice(-300)}`;
|
|
4299
|
+
steps.push({ step: "restart", status: "failed", message: msg, durationMs: Date.now() - start });
|
|
4300
|
+
} else {
|
|
4301
|
+
steps.push({ step: "restart", status: "ok", durationMs: Date.now() - start });
|
|
4302
|
+
}
|
|
4303
|
+
const waitMs = (manifest.service.startup_wait_seconds || 5) * 1e3;
|
|
4304
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
4305
|
+
}
|
|
4306
|
+
if (manifest.verify) {
|
|
4307
|
+
console.log(" [deploy] Verifying deployment...");
|
|
4308
|
+
log("lifecycle", "Verifying deployment");
|
|
4309
|
+
const timeoutSec = manifest.verify.timeout_seconds || 30;
|
|
4310
|
+
if (manifest.verify.health_url) {
|
|
4311
|
+
const healthy = await checkHealth(manifest.verify.health_url, timeoutSec);
|
|
4312
|
+
if (!healthy) {
|
|
4313
|
+
const msg = `Health check failed: ${manifest.verify.health_url} did not respond within ${timeoutSec}s`;
|
|
4314
|
+
steps.push({ step: "verify:health", status: "failed", message: msg });
|
|
4315
|
+
log("error", msg);
|
|
4316
|
+
if (manifest.rollback?.auto_rollback_on_verify_failure && preDeployCommit) {
|
|
4317
|
+
await rollback(workspaceRoot, preDeployCommit, manifest, log);
|
|
4318
|
+
return { deployed: false, steps, error: msg, rolledBack: true };
|
|
4319
|
+
}
|
|
4320
|
+
return { deployed: false, steps, error: msg };
|
|
4321
|
+
}
|
|
4322
|
+
steps.push({ step: "verify:health", status: "ok" });
|
|
4323
|
+
}
|
|
4324
|
+
for (const test of manifest.verify.smoke_tests || []) {
|
|
4325
|
+
const status = await httpStatus(test.url);
|
|
4326
|
+
if (!test.expected_status.includes(status)) {
|
|
4327
|
+
const msg = `Smoke test "${test.name}" failed: got ${status}, expected ${test.expected_status.join("|")}`;
|
|
4328
|
+
steps.push({ step: `verify:${test.name}`, status: "failed", message: msg });
|
|
4329
|
+
log("error", msg);
|
|
4330
|
+
if (manifest.rollback?.auto_rollback_on_verify_failure && preDeployCommit) {
|
|
4331
|
+
await rollback(workspaceRoot, preDeployCommit, manifest, log);
|
|
4332
|
+
return { deployed: false, steps, error: msg, rolledBack: true };
|
|
4333
|
+
}
|
|
4334
|
+
return { deployed: false, steps, error: msg };
|
|
4335
|
+
}
|
|
4336
|
+
steps.push({ step: `verify:${test.name}`, status: "ok" });
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
console.log(" [deploy] Deployment verified");
|
|
4340
|
+
log("lifecycle", "Deployment verified successfully");
|
|
4341
|
+
return { deployed: true, steps };
|
|
4342
|
+
}
|
|
4343
|
+
async function rollback(workspaceRoot, targetCommit, manifest, log) {
|
|
4344
|
+
console.log(` [deploy] Rolling back to ${targetCommit.substring(0, 8)}`);
|
|
4345
|
+
log("lifecycle", `Rolling back to ${targetCommit.substring(0, 8)}`);
|
|
4346
|
+
try {
|
|
4347
|
+
exec(`git checkout ${targetCommit}`, workspaceRoot, 1e4);
|
|
4348
|
+
} catch {
|
|
4349
|
+
log("error", "Rollback: git checkout failed");
|
|
4350
|
+
return;
|
|
4351
|
+
}
|
|
4352
|
+
if (manifest.build?.steps) {
|
|
4353
|
+
for (const step of manifest.build.steps) {
|
|
4354
|
+
exec(step.command, (0, import_node_path.join)(workspaceRoot, step.working_dir || "."), (step.timeout_seconds || 300) * 1e3);
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
if (manifest.service?.restart_command) {
|
|
4358
|
+
exec(manifest.service.restart_command, workspaceRoot, 3e4);
|
|
4359
|
+
}
|
|
4360
|
+
log("lifecycle", "Rollback complete");
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4092
4363
|
// src/commands/agent.ts
|
|
4093
4364
|
var AUTH_ERROR_PATTERNS = [
|
|
4094
4365
|
"Not logged in",
|
|
@@ -4110,7 +4381,7 @@ function containsAuthError(text) {
|
|
|
4110
4381
|
}
|
|
4111
4382
|
async function checkClaudeAuth() {
|
|
4112
4383
|
try {
|
|
4113
|
-
const output = (0,
|
|
4384
|
+
const output = (0, import_node_child_process4.execSync)("claude --version 2>&1", {
|
|
4114
4385
|
encoding: "utf8",
|
|
4115
4386
|
timeout: 1e4,
|
|
4116
4387
|
env: { ...process.env }
|
|
@@ -4321,7 +4592,7 @@ async function launchAutoApproveMode(prompt, options) {
|
|
|
4321
4592
|
if (sessionId && !isContinue) {
|
|
4322
4593
|
args.push("--session-id", sessionId);
|
|
4323
4594
|
}
|
|
4324
|
-
const child = (0,
|
|
4595
|
+
const child = (0, import_node_child_process4.spawn)("claude", args, {
|
|
4325
4596
|
cwd: options.cwd,
|
|
4326
4597
|
stdio: ["inherit", "pipe", "pipe"],
|
|
4327
4598
|
env: options.spawnEnv || { ...process.env }
|
|
@@ -4455,7 +4726,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
4455
4726
|
}
|
|
4456
4727
|
async function launchRelayMode(prompt, options) {
|
|
4457
4728
|
return new Promise((resolve, reject) => {
|
|
4458
|
-
const child = (0,
|
|
4729
|
+
const child = (0, import_node_child_process4.spawn)("claude", [], {
|
|
4459
4730
|
cwd: options.cwd,
|
|
4460
4731
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4461
4732
|
env: options.spawnEnv || { ...process.env }
|
|
@@ -4681,19 +4952,19 @@ async function handleSelfUpdate(task, state, creds) {
|
|
|
4681
4952
|
let output = "";
|
|
4682
4953
|
if (source === "npm" || source.startsWith("npm:")) {
|
|
4683
4954
|
const pkg = source === "npm" ? "@controlvector/cv-agent@latest" : source.replace("npm:", "");
|
|
4684
|
-
output = (0,
|
|
4955
|
+
output = (0, import_node_child_process4.execSync)(`npm install -g ${pkg} 2>&1`, { encoding: "utf8", timeout: 12e4 });
|
|
4685
4956
|
} else if (source.startsWith("git:")) {
|
|
4686
4957
|
const repoPath = source.replace("git:", "");
|
|
4687
|
-
output = (0,
|
|
4958
|
+
output = (0, import_node_child_process4.execSync)(`cd ${repoPath} && git pull && npm install && npm run build && npm link 2>&1`, {
|
|
4688
4959
|
encoding: "utf8",
|
|
4689
4960
|
timeout: 3e5
|
|
4690
4961
|
});
|
|
4691
4962
|
} else {
|
|
4692
|
-
output = (0,
|
|
4963
|
+
output = (0, import_node_child_process4.execSync)(`npm install -g @controlvector/cv-agent@latest 2>&1`, { encoding: "utf8", timeout: 12e4 });
|
|
4693
4964
|
}
|
|
4694
4965
|
let newVersion = "unknown";
|
|
4695
4966
|
try {
|
|
4696
|
-
newVersion = (0,
|
|
4967
|
+
newVersion = (0, import_node_child_process4.execSync)("cva --version 2>/dev/null || echo unknown", { encoding: "utf8" }).trim();
|
|
4697
4968
|
} catch {
|
|
4698
4969
|
}
|
|
4699
4970
|
postTaskEvent(creds, task.id, {
|
|
@@ -4719,7 +4990,7 @@ async function handleSelfUpdate(task, state, creds) {
|
|
|
4719
4990
|
if (task.input?.constraints?.includes("restart")) {
|
|
4720
4991
|
console.log(source_default.yellow("Restarting agent with updated binary..."));
|
|
4721
4992
|
const args = process.argv.slice(1).join(" ");
|
|
4722
|
-
(0,
|
|
4993
|
+
(0, import_node_child_process4.execSync)(`nohup cva ${args} > /tmp/cva-restart.log 2>&1 &`, { stdio: "ignore" });
|
|
4723
4994
|
process.exit(0);
|
|
4724
4995
|
}
|
|
4725
4996
|
} catch (err) {
|
|
@@ -4751,7 +5022,7 @@ async function runAgent(options) {
|
|
|
4751
5022
|
process.exit(1);
|
|
4752
5023
|
}
|
|
4753
5024
|
try {
|
|
4754
|
-
(0,
|
|
5025
|
+
(0, import_node_child_process4.execSync)("claude --version", { stdio: "pipe", timeout: 5e3 });
|
|
4755
5026
|
} catch {
|
|
4756
5027
|
console.log();
|
|
4757
5028
|
console.log(source_default.red("Claude Code CLI not found.") + " Install it first:");
|
|
@@ -4780,7 +5051,7 @@ async function runAgent(options) {
|
|
|
4780
5051
|
currentAuthStatus = "api_key_fallback";
|
|
4781
5052
|
}
|
|
4782
5053
|
try {
|
|
4783
|
-
(0,
|
|
5054
|
+
(0, import_node_child_process4.execSync)("cv --version", { stdio: "pipe", timeout: 5e3 });
|
|
4784
5055
|
} catch {
|
|
4785
5056
|
console.log(source_default.yellow("!") + " cv-git CLI not found. Claude Code will fall back to raw git commands.");
|
|
4786
5057
|
console.log(` Install it: ${source_default.cyan("npm install -g @controlvector/cv-git")}`);
|
|
@@ -4800,7 +5071,7 @@ async function runAgent(options) {
|
|
|
4800
5071
|
}
|
|
4801
5072
|
let detectedRepoId;
|
|
4802
5073
|
try {
|
|
4803
|
-
const remoteUrl = (0,
|
|
5074
|
+
const remoteUrl = (0, import_node_child_process4.execSync)("git remote get-url origin 2>/dev/null", {
|
|
4804
5075
|
cwd: workingDir,
|
|
4805
5076
|
encoding: "utf8",
|
|
4806
5077
|
timeout: 5e3
|
|
@@ -4970,6 +5241,33 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
4970
5241
|
});
|
|
4971
5242
|
}
|
|
4972
5243
|
console.log(source_default.gray("\n" + "-".repeat(60)));
|
|
5244
|
+
if (result.exitCode === 0 || result.exitCode === 1) {
|
|
5245
|
+
try {
|
|
5246
|
+
const gitSafety = gitSafetyNet(
|
|
5247
|
+
options.workingDir,
|
|
5248
|
+
task.title,
|
|
5249
|
+
task.id,
|
|
5250
|
+
task.branch
|
|
5251
|
+
);
|
|
5252
|
+
if (gitSafety.hadChanges) {
|
|
5253
|
+
sendTaskLog(
|
|
5254
|
+
creds,
|
|
5255
|
+
state.executorId,
|
|
5256
|
+
task.id,
|
|
5257
|
+
"git",
|
|
5258
|
+
`Safety net: committed ${gitSafety.filesAdded + gitSafety.filesModified} files (${gitSafety.commitSha || "ok"})`,
|
|
5259
|
+
{ added: gitSafety.filesAdded, modified: gitSafety.filesModified, deleted: gitSafety.filesDeleted }
|
|
5260
|
+
);
|
|
5261
|
+
} else if (gitSafety.pushed) {
|
|
5262
|
+
sendTaskLog(creds, state.executorId, task.id, "git", "Safety net: pushed unpushed commits");
|
|
5263
|
+
}
|
|
5264
|
+
if (gitSafety.error) {
|
|
5265
|
+
sendTaskLog(creds, state.executorId, task.id, "info", `Git safety warning: ${gitSafety.error}`);
|
|
5266
|
+
}
|
|
5267
|
+
} catch (gitErr) {
|
|
5268
|
+
sendTaskLog(creds, state.executorId, task.id, "info", `Git safety net error: ${gitErr.message}`);
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
4973
5271
|
const postGitState = capturePostTaskState(options.workingDir, preGitState);
|
|
4974
5272
|
const payload = buildCompletionPayload(result.exitCode, preGitState, postGitState, startTime, result.output);
|
|
4975
5273
|
const elapsed = formatDuration(Date.now() - startTime);
|
|
@@ -4978,6 +5276,28 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
|
|
|
4978
5276
|
...postGitState.filesModified,
|
|
4979
5277
|
...postGitState.filesDeleted
|
|
4980
5278
|
];
|
|
5279
|
+
let deployResult;
|
|
5280
|
+
if (result.exitCode === 0) {
|
|
5281
|
+
try {
|
|
5282
|
+
const logFn = (type, msg) => {
|
|
5283
|
+
sendTaskLog(creds, state.executorId, task.id, type, msg);
|
|
5284
|
+
};
|
|
5285
|
+
deployResult = await postTaskDeploy(options.workingDir, task.id, logFn);
|
|
5286
|
+
if (deployResult.deployed) {
|
|
5287
|
+
sendTaskLog(creds, state.executorId, task.id, "lifecycle", "Post-task deploy: verified");
|
|
5288
|
+
} else if (deployResult.error) {
|
|
5289
|
+
sendTaskLog(
|
|
5290
|
+
creds,
|
|
5291
|
+
state.executorId,
|
|
5292
|
+
task.id,
|
|
5293
|
+
"error",
|
|
5294
|
+
`Post-task deploy failed: ${deployResult.error}${deployResult.rolledBack ? " (rolled back)" : ""}`
|
|
5295
|
+
);
|
|
5296
|
+
}
|
|
5297
|
+
} catch (deployErr) {
|
|
5298
|
+
sendTaskLog(creds, state.executorId, task.id, "info", `Deploy manifest error: ${deployErr.message}`);
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
4981
5301
|
postTaskEvent(creds, task.id, {
|
|
4982
5302
|
event_type: "completed",
|
|
4983
5303
|
content: {
|
|
@@ -5228,7 +5548,7 @@ function authCommand() {
|
|
|
5228
5548
|
}
|
|
5229
5549
|
|
|
5230
5550
|
// src/commands/remote.ts
|
|
5231
|
-
var
|
|
5551
|
+
var import_node_child_process5 = require("node:child_process");
|
|
5232
5552
|
function getGitHost(apiUrl) {
|
|
5233
5553
|
return apiUrl.replace(/^https?:\/\//, "").replace(/^api\./, "git.");
|
|
5234
5554
|
}
|
|
@@ -5243,16 +5563,16 @@ async function remoteAdd(ownerRepo) {
|
|
|
5243
5563
|
}
|
|
5244
5564
|
const remoteUrl = `https://${gitHost}/${owner}/${repo}.git`;
|
|
5245
5565
|
try {
|
|
5246
|
-
const existing = (0,
|
|
5566
|
+
const existing = (0, import_node_child_process5.execSync)("git remote get-url cvhub", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
5247
5567
|
if (existing === remoteUrl) {
|
|
5248
5568
|
console.log(source_default.gray(`Remote 'cvhub' already set to ${remoteUrl}`));
|
|
5249
5569
|
return;
|
|
5250
5570
|
}
|
|
5251
|
-
(0,
|
|
5571
|
+
(0, import_node_child_process5.execSync)(`git remote set-url cvhub ${remoteUrl}`, { timeout: 5e3 });
|
|
5252
5572
|
console.log(source_default.green("Updated") + ` remote 'cvhub' -> ${remoteUrl}`);
|
|
5253
5573
|
} catch {
|
|
5254
5574
|
try {
|
|
5255
|
-
(0,
|
|
5575
|
+
(0, import_node_child_process5.execSync)(`git remote add cvhub ${remoteUrl}`, { timeout: 5e3 });
|
|
5256
5576
|
console.log(source_default.green("Added") + ` remote 'cvhub' -> ${remoteUrl}`);
|
|
5257
5577
|
} catch (err) {
|
|
5258
5578
|
console.log(source_default.red("Failed to add remote:") + ` ${err.message}`);
|
|
@@ -5444,7 +5764,7 @@ function statusCommand() {
|
|
|
5444
5764
|
|
|
5445
5765
|
// src/index.ts
|
|
5446
5766
|
var program2 = new Command();
|
|
5447
|
-
program2.name("cva").description("CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch").version(true ? "1.
|
|
5767
|
+
program2.name("cva").description("CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch").version(true ? "1.5.0" : "1.1.0");
|
|
5448
5768
|
program2.addCommand(agentCommand());
|
|
5449
5769
|
program2.addCommand(authCommand());
|
|
5450
5770
|
program2.addCommand(remoteCommand());
|