@controlvector/cv-agent 1.7.1 → 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 +112 -30
- package/dist/bundle.cjs.map +2 -2
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +130 -34
- package/dist/commands/setup.js.map +1 -1
- package/package.json +1 -1
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
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
console.log(source_default.
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
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:");
|
|
@@ -3827,6 +3895,20 @@ async function runSetup() {
|
|
|
3827
3895
|
}
|
|
3828
3896
|
} catch {
|
|
3829
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
|
+
}
|
|
3830
3912
|
try {
|
|
3831
3913
|
(0, import_node_child_process.execSync)("git log --oneline -1", { cwd, stdio: "pipe" });
|
|
3832
3914
|
const status = (0, import_node_child_process.execSync)("git status --porcelain", { cwd, encoding: "utf8" }).trim();
|
|
@@ -6070,7 +6152,7 @@ function statusCommand() {
|
|
|
6070
6152
|
|
|
6071
6153
|
// src/index.ts
|
|
6072
6154
|
var program2 = new Command();
|
|
6073
|
-
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.
|
|
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");
|
|
6074
6156
|
program2.addCommand(setupCommand());
|
|
6075
6157
|
program2.addCommand(agentCommand());
|
|
6076
6158
|
program2.addCommand(authCommand());
|