@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 +150 -30
- package/dist/bundle.cjs.map +2 -2
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +178 -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:");
|
|
@@ -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.
|
|
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());
|