@controlvector/cv-agent 1.1.1 → 1.3.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 CHANGED
@@ -3643,9 +3643,8 @@ async function apiCall(creds, method, path, body) {
3643
3643
  body: body ? JSON.stringify(body) : void 0
3644
3644
  });
3645
3645
  }
3646
- async function registerExecutor(creds, machineName, workingDir) {
3647
- const hostname2 = (await import("node:os")).hostname();
3648
- const res = await apiCall(creds, "POST", "/api/v1/executors", {
3646
+ async function registerExecutor(creds, machineName, workingDir, repositoryId) {
3647
+ const body = {
3649
3648
  name: `cva:${machineName}`,
3650
3649
  machine_name: machineName,
3651
3650
  type: "claude_code",
@@ -3654,7 +3653,11 @@ async function registerExecutor(creds, machineName, workingDir) {
3654
3653
  tools: ["bash", "read", "write", "edit", "glob", "grep"],
3655
3654
  maxConcurrentTasks: 1
3656
3655
  }
3657
- });
3656
+ };
3657
+ if (repositoryId) {
3658
+ body.repository_id = repositoryId;
3659
+ }
3660
+ const res = await apiCall(creds, "POST", "/api/v1/executors", body);
3658
3661
  if (!res.ok) {
3659
3662
  const err = await res.text();
3660
3663
  throw new Error(`Failed to register executor: ${res.status} ${err}`);
@@ -3662,6 +3665,16 @@ async function registerExecutor(creds, machineName, workingDir) {
3662
3665
  const data = await res.json();
3663
3666
  return { id: data.executor.id, name: data.executor.name };
3664
3667
  }
3668
+ async function resolveRepoId(creds, owner, repo) {
3669
+ try {
3670
+ const res = await apiCall(creds, "GET", `/api/v1/repos/${owner}/${repo}`);
3671
+ if (!res.ok) return null;
3672
+ const data = await res.json();
3673
+ return data.repository ? { id: data.repository.id, slug: data.repository.slug } : null;
3674
+ } catch {
3675
+ return null;
3676
+ }
3677
+ }
3665
3678
  async function markOffline(creds, executorId) {
3666
3679
  await apiCall(creds, "POST", `/api/v1/executors/${executorId}/offline`).catch(() => {
3667
3680
  });
@@ -4206,6 +4219,7 @@ var PERMISSION_PATTERNS = [
4206
4219
  /\? \(y\/n\)/
4207
4220
  ];
4208
4221
  var MAX_OUTPUT_BYTES = 200 * 1024;
4222
+ var MAX_OUTPUT_FINAL_BYTES = 50 * 1024;
4209
4223
  var OUTPUT_PROGRESS_INTERVAL = 4096;
4210
4224
  async function launchAutoApproveMode(prompt, options) {
4211
4225
  const sessionId = options.taskId ? options.taskId.replace(/-/g, "").slice(0, 32).padEnd(32, "0").replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, "$1-$2-$3-$4-$5") : void 0;
@@ -4266,6 +4280,11 @@ async function launchAutoApproveMode(prompt, options) {
4266
4280
  ).catch(() => {
4267
4281
  });
4268
4282
  }
4283
+ postTaskEvent(options.creds, options.taskId, {
4284
+ event_type: "output",
4285
+ content: { chunk, byte_offset: lastProgressBytes }
4286
+ }).catch(() => {
4287
+ });
4269
4288
  }
4270
4289
  }
4271
4290
  });
@@ -4376,6 +4395,11 @@ async function launchRelayMode(prompt, options) {
4376
4395
  { output_chunk: chunk }
4377
4396
  ).catch(() => {
4378
4397
  });
4398
+ postTaskEvent(options.creds, options.taskId, {
4399
+ event_type: "output",
4400
+ content: { chunk, byte_offset: lastProgressBytes }
4401
+ }).catch(() => {
4402
+ });
4379
4403
  }
4380
4404
  if (Date.now() - lastRedirectCheck > 1e4) {
4381
4405
  try {
@@ -4495,6 +4519,68 @@ async function pollForEventResponse(creds, taskId, eventId, timeoutMs) {
4495
4519
  }
4496
4520
  return null;
4497
4521
  }
4522
+ async function handleSelfUpdate(task, state, creds) {
4523
+ const startTime = Date.now();
4524
+ try {
4525
+ await startTask(creds, state.executorId, task.id);
4526
+ sendTaskLog(creds, state.executorId, task.id, "lifecycle", "Self-update started");
4527
+ const source = (task.input?.description || task.description || "npm").trim();
4528
+ let output = "";
4529
+ if (source === "npm" || source.startsWith("npm:")) {
4530
+ const pkg = source === "npm" ? "@controlvector/cv-agent@latest" : source.replace("npm:", "");
4531
+ output = (0, import_node_child_process2.execSync)(`npm install -g ${pkg} 2>&1`, { encoding: "utf8", timeout: 12e4 });
4532
+ } else if (source.startsWith("git:")) {
4533
+ const repoPath = source.replace("git:", "");
4534
+ output = (0, import_node_child_process2.execSync)(`cd ${repoPath} && git pull && npm install && npm run build && npm link 2>&1`, {
4535
+ encoding: "utf8",
4536
+ timeout: 3e5
4537
+ });
4538
+ } else {
4539
+ output = (0, import_node_child_process2.execSync)(`npm install -g @controlvector/cv-agent@latest 2>&1`, { encoding: "utf8", timeout: 12e4 });
4540
+ }
4541
+ let newVersion = "unknown";
4542
+ try {
4543
+ newVersion = (0, import_node_child_process2.execSync)("cva --version 2>/dev/null || echo unknown", { encoding: "utf8" }).trim();
4544
+ } catch {
4545
+ }
4546
+ postTaskEvent(creds, task.id, {
4547
+ event_type: "output_final",
4548
+ content: { output: output.slice(-1e4), new_version: newVersion }
4549
+ }).catch(() => {
4550
+ });
4551
+ sendTaskLog(
4552
+ creds,
4553
+ state.executorId,
4554
+ task.id,
4555
+ "lifecycle",
4556
+ `Self-update completed. New version: ${newVersion}`,
4557
+ { output: output.slice(-5e3) },
4558
+ 100
4559
+ );
4560
+ await completeTask(creds, state.executorId, task.id, {
4561
+ summary: `Updated to ${newVersion}`,
4562
+ exit_code: 0,
4563
+ stats: { duration_seconds: Math.round((Date.now() - startTime) / 1e3) }
4564
+ });
4565
+ state.completedCount++;
4566
+ if (task.input?.constraints?.includes("restart")) {
4567
+ console.log(source_default.yellow("Restarting agent with updated binary..."));
4568
+ const args = process.argv.slice(1).join(" ");
4569
+ (0, import_node_child_process2.execSync)(`nohup cva ${args} > /tmp/cva-restart.log 2>&1 &`, { stdio: "ignore" });
4570
+ process.exit(0);
4571
+ }
4572
+ } catch (err) {
4573
+ sendTaskLog(creds, state.executorId, task.id, "error", `Self-update failed: ${err.message}`);
4574
+ try {
4575
+ await failTask(creds, state.executorId, task.id, err.message);
4576
+ } catch {
4577
+ }
4578
+ state.failedCount++;
4579
+ } finally {
4580
+ state.currentTaskId = null;
4581
+ state.lastTaskEnd = Date.now();
4582
+ }
4583
+ }
4498
4584
  async function runAgent(options) {
4499
4585
  const creds = await readCredentials();
4500
4586
  if (!creds.CV_HUB_API) {
@@ -4539,8 +4625,31 @@ async function runAgent(options) {
4539
4625
  console.log();
4540
4626
  }
4541
4627
  }
4628
+ let detectedRepoId;
4629
+ try {
4630
+ const remoteUrl = (0, import_node_child_process2.execSync)("git remote get-url origin 2>/dev/null", {
4631
+ cwd: workingDir,
4632
+ encoding: "utf8",
4633
+ timeout: 5e3
4634
+ }).trim();
4635
+ const cvHubMatch = remoteUrl.match(
4636
+ /git\.hub\.controlvector\.io[:/]([^/]+)\/([^/.]+)/
4637
+ );
4638
+ if (cvHubMatch) {
4639
+ const [, repoOwner, repoSlug] = cvHubMatch;
4640
+ try {
4641
+ const repoData = await resolveRepoId(creds, repoOwner, repoSlug);
4642
+ if (repoData?.id) {
4643
+ detectedRepoId = repoData.id;
4644
+ console.log(source_default.gray(` Repo: ${repoOwner}/${repoSlug}`));
4645
+ }
4646
+ } catch {
4647
+ }
4648
+ }
4649
+ } catch {
4650
+ }
4542
4651
  const executor = await withRetry(
4543
- () => registerExecutor(creds, machineName, workingDir),
4652
+ () => registerExecutor(creds, machineName, workingDir, detectedRepoId),
4544
4653
  "Executor registration"
4545
4654
  );
4546
4655
  const mode = options.autoApprove ? "auto-approve" : "relay";
@@ -4599,6 +4708,10 @@ ${source_default.red("!")} Error: ${err.message}`);
4599
4708
  async function executeTask(task, state, creds, options) {
4600
4709
  const startTime = Date.now();
4601
4710
  state.currentTaskId = task.id;
4711
+ if (task.task_type === "_system_update") {
4712
+ await handleSelfUpdate(task, state, creds);
4713
+ return;
4714
+ }
4602
4715
  process.stdout.write("\r\x1B[K");
4603
4716
  console.log(`\u{1F4E5} ${source_default.bold.cyan("RECEIVED")} \u2014 Task: ${task.title} (${task.priority})`);
4604
4717
  console.log(source_default.bold("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
@@ -4705,6 +4818,15 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
4705
4818
  console.log();
4706
4819
  console.log(`\u2705 ${source_default.bold.green("COMPLETED")} \u2014 Duration: ${elapsed}`);
4707
4820
  printBanner("COMPLETED", elapsed, allChangedFiles, postGitState.headSha);
4821
+ postTaskEvent(creds, task.id, {
4822
+ event_type: "output_final",
4823
+ content: {
4824
+ output: result.output.slice(-MAX_OUTPUT_FINAL_BYTES),
4825
+ exit_code: result.exitCode,
4826
+ duration_seconds: Math.round((Date.now() - startTime) / 1e3)
4827
+ }
4828
+ }).catch(() => {
4829
+ });
4708
4830
  await withRetry(
4709
4831
  () => completeTask(creds, state.executorId, task.id, payload),
4710
4832
  "Report completion"
@@ -5081,7 +5203,7 @@ function statusCommand() {
5081
5203
 
5082
5204
  // src/index.ts
5083
5205
  var program2 = new Command();
5084
- program2.name("cva").description("CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch").version(true ? "1.1.1" : "1.1.0");
5206
+ program2.name("cva").description("CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch").version(true ? "1.3.0" : "1.1.0");
5085
5207
  program2.addCommand(agentCommand());
5086
5208
  program2.addCommand(authCommand());
5087
5209
  program2.addCommand(remoteCommand());