@controlvector/cv-agent 1.7.0 → 1.8.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
@@ -3665,6 +3665,92 @@ async function validateToken(hubUrl, token) {
3665
3665
  return null;
3666
3666
  }
3667
3667
  }
3668
+ var DEVICE_CLIENT_ID = "cv-agent-cli";
3669
+ var DEVICE_SCOPES = "repo:read repo:write profile offline_access";
3670
+ var POLL_INTERVAL_MS = 5e3;
3671
+ var MAX_POLL_ATTEMPTS = 180;
3672
+ async function deviceAuthFlow(hubUrl, appUrl) {
3673
+ const authRes = await fetch(`${hubUrl}/oauth/device/authorize`, {
3674
+ method: "POST",
3675
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3676
+ body: new URLSearchParams({
3677
+ client_id: DEVICE_CLIENT_ID,
3678
+ scope: DEVICE_SCOPES
3679
+ })
3680
+ });
3681
+ if (!authRes.ok) {
3682
+ const err = await authRes.json().catch(() => ({}));
3683
+ throw new Error(err.error_description || `Device auth failed: ${authRes.status}`);
3684
+ }
3685
+ const auth = await authRes.json();
3686
+ 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\u2510"));
3687
+ console.log(source_default.bold(" \u2502 CV-Hub Device Authorization \u2502"));
3688
+ console.log(source_default.bold(" \u251C\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\u2524"));
3689
+ console.log(source_default.bold(" \u2502 \u2502"));
3690
+ console.log(source_default.bold(" \u2502 Open this URL in your browser: \u2502"));
3691
+ console.log(source_default.bold(` \u2502 ${source_default.cyan(auth.verification_uri).padEnd(51)}\u2502`));
3692
+ console.log(source_default.bold(" \u2502 \u2502"));
3693
+ console.log(source_default.bold(" \u2502 Then enter this code: \u2502"));
3694
+ console.log(source_default.bold(` \u2502 ${source_default.white.bold(auth.user_code)} \u2502`));
3695
+ console.log(source_default.bold(" \u2502 \u2502"));
3696
+ console.log(source_default.bold(` \u2502 ${source_default.gray(`Expires in ${Math.floor(auth.expires_in / 60)} minutes`).padEnd(51)}\u2502`));
3697
+ console.log(source_default.bold(" \u2514\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\u2518"));
3698
+ console.log();
3699
+ try {
3700
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
3701
+ (0, import_node_child_process.execSync)(`${openCmd} "${auth.verification_uri_complete}" 2>/dev/null`, { timeout: 5e3 });
3702
+ console.log(source_default.gray(" Browser opened. Waiting for authorization..."));
3703
+ } catch {
3704
+ console.log(source_default.gray(" Open the URL above in your browser."));
3705
+ }
3706
+ let interval = Math.max(auth.interval * 1e3, POLL_INTERVAL_MS);
3707
+ const expireTime = Date.now() + auth.expires_in * 1e3;
3708
+ for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
3709
+ await new Promise((r) => setTimeout(r, interval));
3710
+ if (Date.now() > expireTime) {
3711
+ throw new Error("Authorization timed out");
3712
+ }
3713
+ const remaining = Math.ceil((expireTime - Date.now()) / 1e3);
3714
+ const mins = Math.floor(remaining / 60);
3715
+ const secs = remaining % 60;
3716
+ process.stdout.write(`\r Waiting for authorization... (${mins}:${secs.toString().padStart(2, "0")} remaining) `);
3717
+ const tokenRes = await fetch(`${hubUrl}/oauth/token`, {
3718
+ method: "POST",
3719
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3720
+ body: new URLSearchParams({
3721
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
3722
+ device_code: auth.device_code,
3723
+ client_id: DEVICE_CLIENT_ID
3724
+ })
3725
+ });
3726
+ const tokenData = await tokenRes.json();
3727
+ if (tokenData.access_token) {
3728
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
3729
+ let uname = "user";
3730
+ try {
3731
+ const userRes = await fetch(`${hubUrl}/oauth/userinfo`, {
3732
+ headers: { Authorization: `Bearer ${tokenData.access_token}` }
3733
+ });
3734
+ if (userRes.ok) {
3735
+ const userInfo = await userRes.json();
3736
+ uname = userInfo.preferred_username || userInfo.name || "user";
3737
+ }
3738
+ } catch {
3739
+ }
3740
+ return { token: tokenData.access_token, username: uname };
3741
+ }
3742
+ if (tokenData.error === "slow_down") {
3743
+ interval += 5e3;
3744
+ } else if (tokenData.error === "access_denied") {
3745
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
3746
+ throw new Error("Authorization denied by user");
3747
+ } else if (tokenData.error === "expired_token") {
3748
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
3749
+ throw new Error("Authorization expired");
3750
+ }
3751
+ }
3752
+ throw new Error("Authorization timed out");
3753
+ }
3668
3754
  function checkBinary(cmd) {
3669
3755
  try {
3670
3756
  return (0, import_node_child_process.execSync)(`${cmd} 2>&1`, { encoding: "utf8", timeout: 5e3 }).trim().split("\n")[0];
@@ -3724,38 +3810,20 @@ async function runSetup() {
3724
3810
  if (!token) {
3725
3811
  console.log(" Let's connect you to CV-Hub.");
3726
3812
  console.log();
3727
- const autoName = `${(0, import_node_os2.hostname)()}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
3728
- const tokenUrl = `${appUrl}/settings/tokens/new?name=${encodeURIComponent(autoName)}&scopes=agent,repo`;
3729
- console.log(source_default.gray(` Opening: ${tokenUrl}`));
3730
3813
  try {
3731
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
3732
- (0, import_node_child_process.execSync)(`${openCmd} "${tokenUrl}" 2>/dev/null`, { timeout: 5e3 });
3733
- } catch {
3734
- console.log(source_default.gray(" (Could not open browser \u2014 copy the URL above)"));
3735
- }
3736
- console.log();
3737
- const readline = await import("node:readline");
3738
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3739
- token = await new Promise((resolve) => {
3740
- rl.question(" Paste your token here: ", (answer) => {
3741
- rl.close();
3742
- resolve(answer.trim());
3743
- });
3744
- });
3745
- if (!token) {
3746
- console.log(source_default.red(" No token provided. Run cva setup again."));
3747
- process.exit(1);
3748
- }
3749
- const user = await validateToken(hubUrl, token);
3750
- if (!user) {
3751
- console.log(source_default.red(" Token validation failed. Check your token and try again."));
3814
+ const deviceResult = await deviceAuthFlow(hubUrl, appUrl);
3815
+ token = deviceResult.token;
3816
+ username = deviceResult.username;
3817
+ console.log(source_default.green(" \u2713") + ` Authenticated as ${source_default.bold(username)}`);
3818
+ writeSharedCreds({ hub_url: hubUrl, token, username, created_at: (/* @__PURE__ */ new Date()).toISOString() });
3819
+ await writeCredentialField("CV_HUB_PAT", token);
3820
+ await writeCredentialField("CV_HUB_API", hubUrl);
3821
+ } catch (err) {
3822
+ console.log(source_default.red(` Authentication failed: ${err.message}`));
3823
+ console.log(source_default.gray(" You can retry with: cva setup"));
3824
+ console.log(source_default.gray(" Or manually: cva auth login --token <your-pat>"));
3752
3825
  process.exit(1);
3753
3826
  }
3754
- username = user;
3755
- console.log(source_default.green(" \u2713") + ` Authenticated as ${source_default.bold(user)}`);
3756
- writeSharedCreds({ hub_url: hubUrl, token, username, created_at: (/* @__PURE__ */ new Date()).toISOString() });
3757
- await writeCredentialField("CV_HUB_PAT", token);
3758
- await writeCredentialField("CV_HUB_API", hubUrl);
3759
3827
  }
3760
3828
  console.log();
3761
3829
  console.log(" Claude.ai MCP connector:");
@@ -3804,6 +3872,22 @@ async function runSetup() {
3804
3872
  try {
3805
3873
  const existingRemote = (0, import_node_child_process.execSync)('git remote get-url cv-hub 2>/dev/null || echo ""', { cwd, encoding: "utf8" }).trim();
3806
3874
  if (!existingRemote) {
3875
+ try {
3876
+ const controller = new AbortController();
3877
+ const timeout = setTimeout(() => controller.abort(), 15e3);
3878
+ await fetch(`${hubUrl}/api/v1/user/repos`, {
3879
+ method: "POST",
3880
+ headers: {
3881
+ Authorization: `Bearer ${token}`,
3882
+ "Content-Type": "application/json"
3883
+ },
3884
+ body: JSON.stringify({ name: repoName, auto_init: false }),
3885
+ signal: controller.signal
3886
+ });
3887
+ clearTimeout(timeout);
3888
+ console.log(source_default.green(" \u2713") + ` Repo created on CV-Hub: ${username}/${repoName}`);
3889
+ } catch {
3890
+ }
3807
3891
  (0, import_node_child_process.execSync)(`git remote add cv-hub ${remoteUrl}`, { cwd, stdio: "pipe" });
3808
3892
  console.log(source_default.green(" \u2713") + ` Remote added: cv-hub \u2192 ${remoteUrl}`);
3809
3893
  } else {
@@ -3811,6 +3895,42 @@ async function runSetup() {
3811
3895
  }
3812
3896
  } catch {
3813
3897
  }
3898
+ try {
3899
+ const credStorePath = (0, import_node_path.join)((0, import_node_os2.homedir)(), ".git-credentials");
3900
+ const credLine = `https://${username}:${token}@${gitHost}`;
3901
+ let existing = "";
3902
+ try {
3903
+ existing = (0, import_node_fs.readFileSync)(credStorePath, "utf-8");
3904
+ } catch {
3905
+ }
3906
+ if (!existing.includes(gitHost)) {
3907
+ (0, import_node_fs.writeFileSync)(credStorePath, existing + credLine + "\n", { mode: 384 });
3908
+ }
3909
+ (0, import_node_child_process.execSync)(`git config --global credential.helper store`, { stdio: "pipe" });
3910
+ } catch {
3911
+ }
3912
+ try {
3913
+ (0, import_node_child_process.execSync)("git log --oneline -1", { cwd, stdio: "pipe" });
3914
+ const status = (0, import_node_child_process.execSync)("git status --porcelain", { cwd, encoding: "utf8" }).trim();
3915
+ if (status) {
3916
+ (0, import_node_child_process.execSync)("git add -A", { cwd, stdio: "pipe" });
3917
+ (0, import_node_child_process.execSync)('git commit -m "chore: cv-agent setup"', { cwd, stdio: "pipe" });
3918
+ console.log(source_default.green(" \u2713") + " Changes committed");
3919
+ }
3920
+ } catch {
3921
+ try {
3922
+ (0, import_node_child_process.execSync)("git add -A", { cwd, stdio: "pipe" });
3923
+ (0, import_node_child_process.execSync)('git commit -m "Initial commit via cv-agent"', { cwd, stdio: "pipe" });
3924
+ console.log(source_default.green(" \u2713") + " Initial commit created");
3925
+ } catch {
3926
+ }
3927
+ }
3928
+ try {
3929
+ (0, import_node_child_process.execSync)("git push -u cv-hub main 2>&1", { cwd, stdio: "pipe", timeout: 3e4 });
3930
+ console.log(source_default.green(" \u2713") + " Pushed to CV-Hub");
3931
+ } catch {
3932
+ console.log(source_default.gray(" (Push skipped \u2014 you can push later with: git push cv-hub main)"));
3933
+ }
3814
3934
  }
3815
3935
  console.log();
3816
3936
  console.log(source_default.bold(" Setup Complete"));
@@ -6032,7 +6152,7 @@ function statusCommand() {
6032
6152
 
6033
6153
  // src/index.ts
6034
6154
  var program2 = new Command();
6035
- 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.7.0" : "1.6.0");
6155
+ 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.8.0" : "1.6.0");
6036
6156
  program2.addCommand(setupCommand());
6037
6157
  program2.addCommand(agentCommand());
6038
6158
  program2.addCommand(authCommand());