@controlvector/cv-agent 1.9.1 → 1.10.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 +255 -507
- package/dist/bundle.cjs.map +4 -4
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +104 -109
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/git-safety.d.ts +6 -12
- package/dist/commands/git-safety.d.ts.map +1 -1
- package/dist/commands/git-safety.js +22 -114
- package/dist/commands/git-safety.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +45 -321
- package/dist/commands/setup.js.map +1 -1
- package/dist/utils/api.d.ts +1 -0
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/event-queue.d.ts +66 -0
- package/dist/utils/event-queue.d.ts.map +1 -0
- package/dist/utils/event-queue.js +193 -0
- package/dist/utils/event-queue.js.map +1 -0
- package/package.json +1 -1
|
@@ -6,106 +6,30 @@
|
|
|
6
6
|
* Ensures all changes are committed and pushed, because Claude Code
|
|
7
7
|
* cannot be trusted to do this reliably.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - REFUSES to run if workspace has no .git directory
|
|
12
|
-
* - Filters out sensitive files from staging (.claude/, .zsh_history, etc.)
|
|
13
|
-
* - Logs warnings instead of silently committing dangerous paths
|
|
9
|
+
* Evidence: ANAX Artifact Registry task (2026-04-03) — Claude Code wrote
|
|
10
|
+
* an entire module and didn't git-add a single file.
|
|
14
11
|
*/
|
|
15
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
13
|
exports.gitSafetyNet = gitSafetyNet;
|
|
17
14
|
const node_child_process_1 = require("node:child_process");
|
|
18
|
-
const node_fs_1 = require("node:fs");
|
|
19
|
-
const node_path_1 = require("node:path");
|
|
20
|
-
const node_os_1 = require("node:os");
|
|
21
|
-
/** Files/directories that must NEVER be committed by the safety net */
|
|
22
|
-
const DANGEROUS_PATTERNS = [
|
|
23
|
-
'.claude/',
|
|
24
|
-
'.claude.json',
|
|
25
|
-
'.credentials',
|
|
26
|
-
'.zsh_history',
|
|
27
|
-
'.bash_history',
|
|
28
|
-
'.zsh_sessions/',
|
|
29
|
-
'.ssh/',
|
|
30
|
-
'.gnupg/',
|
|
31
|
-
'.npm/',
|
|
32
|
-
'.config/',
|
|
33
|
-
'.CFUserTextEncoding',
|
|
34
|
-
'Library/',
|
|
35
|
-
'Applications/',
|
|
36
|
-
'.Trash/',
|
|
37
|
-
'.DS_Store',
|
|
38
|
-
'node_modules/',
|
|
39
|
-
'.env',
|
|
40
|
-
'.env.local',
|
|
41
|
-
'.env.production',
|
|
42
|
-
];
|
|
43
|
-
function git(cmd, cwd) {
|
|
44
|
-
return (0, node_child_process_1.execSync)(cmd, { cwd, encoding: 'utf8', timeout: 30_000 }).trim();
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Check if a workspace is safe for auto-commit operations.
|
|
48
|
-
* Returns an error string if unsafe, null if safe.
|
|
49
|
-
*/
|
|
50
|
-
function checkWorkspaceSafety(workspaceRoot) {
|
|
51
|
-
const resolved = (0, node_path_1.resolve)(workspaceRoot);
|
|
52
|
-
const home = (0, node_path_1.resolve)((0, node_os_1.homedir)());
|
|
53
|
-
// NEVER auto-commit from user's HOME directory
|
|
54
|
-
if (resolved === home) {
|
|
55
|
-
return `Workspace is user HOME directory (${home}). Refusing to auto-commit to prevent indexing personal files.`;
|
|
56
|
-
}
|
|
57
|
-
// NEVER auto-commit from a parent of HOME (e.g., /Users or /)
|
|
58
|
-
if (home.startsWith(resolved + '/')) {
|
|
59
|
-
return `Workspace (${resolved}) is a parent of HOME. Refusing to auto-commit.`;
|
|
60
|
-
}
|
|
61
|
-
// Must have a .git directory
|
|
62
|
-
if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(resolved, '.git'))) {
|
|
63
|
-
return `No .git directory in ${resolved}. Not a git repository.`;
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
15
|
/**
|
|
68
|
-
*
|
|
69
|
-
* Returns only the safe lines.
|
|
16
|
+
* Execute a git command, return stdout or throw.
|
|
70
17
|
*/
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
const blocked = [];
|
|
74
|
-
for (const line of statusLines) {
|
|
75
|
-
const filePath = line.substring(3).trim(); // Remove status prefix (e.g., "?? " or " M ")
|
|
76
|
-
const isDangerous = DANGEROUS_PATTERNS.some(pattern => filePath.startsWith(pattern) || filePath.includes('/' + pattern));
|
|
77
|
-
if (isDangerous) {
|
|
78
|
-
blocked.push(filePath);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
safe.push(line);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return { safe, blocked };
|
|
18
|
+
function git(cmd, cwd) {
|
|
19
|
+
return (0, node_child_process_1.execSync)(cmd, { cwd, encoding: 'utf8', timeout: 30_000 }).trim();
|
|
85
20
|
}
|
|
86
21
|
/**
|
|
87
22
|
* Ensure all changes are committed and pushed after Claude Code exits.
|
|
88
23
|
*
|
|
89
|
-
*
|
|
90
|
-
* -
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* - Only stages files that pass the safety filter
|
|
24
|
+
* 1. Check for uncommitted changes (untracked, modified, deleted)
|
|
25
|
+
* 2. If any: git add -A, commit with task metadata, push
|
|
26
|
+
* 3. If no local changes but unpushed commits: push them
|
|
27
|
+
* 4. Return structured result for task reporting
|
|
94
28
|
*/
|
|
95
29
|
function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
96
30
|
const targetBranch = branch || 'main';
|
|
97
|
-
// ── Safety check: refuse to run in dangerous directories ──────────
|
|
98
|
-
const safetyError = checkWorkspaceSafety(workspaceRoot);
|
|
99
|
-
if (safetyError) {
|
|
100
|
-
console.log(` [git-safety] BLOCKED: ${safetyError}`);
|
|
101
|
-
return {
|
|
102
|
-
hadChanges: false, filesAdded: 0, filesModified: 0,
|
|
103
|
-
filesDeleted: 0, pushed: false, skipped: true,
|
|
104
|
-
skipReason: safetyError,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
31
|
try {
|
|
108
|
-
// 1. Check for uncommitted changes
|
|
32
|
+
// 1. Check for ANY uncommitted changes
|
|
109
33
|
let statusOutput;
|
|
110
34
|
try {
|
|
111
35
|
statusOutput = git('git status --porcelain', workspaceRoot);
|
|
@@ -113,8 +37,8 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
113
37
|
catch {
|
|
114
38
|
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false, error: 'git status failed' };
|
|
115
39
|
}
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
40
|
+
const lines = statusOutput.split('\n').filter(Boolean);
|
|
41
|
+
if (lines.length === 0) {
|
|
118
42
|
// No local changes — check for unpushed commits
|
|
119
43
|
try {
|
|
120
44
|
const unpushed = git(`git log origin/${targetBranch}..HEAD --oneline 2>/dev/null`, workspaceRoot);
|
|
@@ -125,22 +49,13 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
125
49
|
}
|
|
126
50
|
}
|
|
127
51
|
catch {
|
|
128
|
-
// No remote tracking or push failed
|
|
52
|
+
// No remote tracking or push failed — not critical
|
|
129
53
|
}
|
|
130
54
|
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
|
|
131
55
|
}
|
|
132
|
-
// 2.
|
|
133
|
-
const { safe: safeLines, blocked } = filterDangerousFiles(allLines);
|
|
134
|
-
if (blocked.length > 0) {
|
|
135
|
-
console.log(` [git-safety] Blocked ${blocked.length} sensitive file(s) from staging: ${blocked.slice(0, 5).join(', ')}${blocked.length > 5 ? '...' : ''}`);
|
|
136
|
-
}
|
|
137
|
-
if (safeLines.length === 0) {
|
|
138
|
-
console.log(` [git-safety] All ${allLines.length} changed files were blocked by safety filter`);
|
|
139
|
-
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
|
|
140
|
-
}
|
|
141
|
-
// 3. Parse safe changes
|
|
56
|
+
// 2. Parse what Claude Code left behind
|
|
142
57
|
let added = 0, modified = 0, deleted = 0;
|
|
143
|
-
for (const line of
|
|
58
|
+
for (const line of lines) {
|
|
144
59
|
const code = line.substring(0, 2);
|
|
145
60
|
if (code.includes('?'))
|
|
146
61
|
added++;
|
|
@@ -149,21 +64,13 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
149
64
|
else if (code.includes('M') || code.includes('A'))
|
|
150
65
|
modified++;
|
|
151
66
|
else
|
|
152
|
-
added++;
|
|
67
|
+
added++; // Treat unknown status as added
|
|
153
68
|
}
|
|
154
|
-
console.log(` [git-safety] ${
|
|
69
|
+
console.log(` [git-safety] ${lines.length} uncommitted changes ` +
|
|
155
70
|
`(${added} new, ${modified} modified, ${deleted} deleted) — committing now`);
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
git(`git add -- ${JSON.stringify(filePath)}`, workspaceRoot);
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
// Individual file add failed — skip it
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// 5. Commit with task metadata
|
|
71
|
+
// 3. Stage everything
|
|
72
|
+
git('git add -A', workspaceRoot);
|
|
73
|
+
// 4. Commit with task metadata
|
|
167
74
|
const shortId = taskId.substring(0, 8);
|
|
168
75
|
const commitMsg = `task: ${taskTitle} [${shortId}]\n\nAuto-committed by cv-agent git safety net.\nTask ID: ${taskId}\nFiles: ${added} added, ${modified} modified, ${deleted} deleted`;
|
|
169
76
|
let commitSha;
|
|
@@ -173,6 +80,7 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
173
80
|
commitSha = shaMatch ? shaMatch[1] : undefined;
|
|
174
81
|
}
|
|
175
82
|
catch (e) {
|
|
83
|
+
// Commit may fail if there's nothing to commit after add
|
|
176
84
|
if (!e.message?.includes('nothing to commit')) {
|
|
177
85
|
return {
|
|
178
86
|
hadChanges: true, filesAdded: added, filesModified: modified,
|
|
@@ -180,7 +88,7 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
180
88
|
};
|
|
181
89
|
}
|
|
182
90
|
}
|
|
183
|
-
//
|
|
91
|
+
// 5. Push
|
|
184
92
|
try {
|
|
185
93
|
git(`git push origin ${targetBranch}`, workspaceRoot);
|
|
186
94
|
console.log(` [git-safety] Committed and pushed: ${commitSha || 'ok'}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-safety.js","sourceRoot":"","sources":["../../src/commands/git-safety.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"git-safety.js","sourceRoot":"","sources":["../../src/commands/git-safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmQpC,wBAAgB,YAAY,IAAI,OAAO,CAKtC"}
|
package/dist/commands/setup.js
CHANGED
|
@@ -60,104 +60,6 @@ async function validateToken(hubUrl, token) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
// ============================================================================
|
|
63
|
-
// OAuth Device Authorization Flow (RFC 8628)
|
|
64
|
-
// ============================================================================
|
|
65
|
-
const DEVICE_CLIENT_ID = 'cv-agent-cli';
|
|
66
|
-
const DEVICE_SCOPES = 'repo:read repo:write profile offline_access';
|
|
67
|
-
const POLL_INTERVAL_MS = 5000;
|
|
68
|
-
const MAX_POLL_ATTEMPTS = 180; // 15 minutes
|
|
69
|
-
async function deviceAuthFlow(hubUrl, appUrl) {
|
|
70
|
-
// Step 1: Request device authorization
|
|
71
|
-
const authRes = await fetch(`${hubUrl}/oauth/device/authorize`, {
|
|
72
|
-
method: 'POST',
|
|
73
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
74
|
-
body: new URLSearchParams({
|
|
75
|
-
client_id: DEVICE_CLIENT_ID,
|
|
76
|
-
scope: DEVICE_SCOPES,
|
|
77
|
-
}),
|
|
78
|
-
});
|
|
79
|
-
if (!authRes.ok) {
|
|
80
|
-
const err = await authRes.json().catch(() => ({}));
|
|
81
|
-
throw new Error(err.error_description || `Device auth failed: ${authRes.status}`);
|
|
82
|
-
}
|
|
83
|
-
const auth = await authRes.json();
|
|
84
|
-
// Step 2: Display code and open browser
|
|
85
|
-
console.log(chalk_1.default.bold(' ┌──────────────────────────────────────────┐'));
|
|
86
|
-
console.log(chalk_1.default.bold(' │ CV-Hub Device Authorization │'));
|
|
87
|
-
console.log(chalk_1.default.bold(' ├──────────────────────────────────────────┤'));
|
|
88
|
-
console.log(chalk_1.default.bold(' │ │'));
|
|
89
|
-
console.log(chalk_1.default.bold(' │ Open this URL in your browser: │'));
|
|
90
|
-
console.log(chalk_1.default.bold(` │ ${chalk_1.default.cyan(auth.verification_uri).padEnd(51)}│`));
|
|
91
|
-
console.log(chalk_1.default.bold(' │ │'));
|
|
92
|
-
console.log(chalk_1.default.bold(' │ Then enter this code: │'));
|
|
93
|
-
console.log(chalk_1.default.bold(` │ ${chalk_1.default.white.bold(auth.user_code)} │`));
|
|
94
|
-
console.log(chalk_1.default.bold(' │ │'));
|
|
95
|
-
console.log(chalk_1.default.bold(` │ ${chalk_1.default.gray(`Expires in ${Math.floor(auth.expires_in / 60)} minutes`).padEnd(51)}│`));
|
|
96
|
-
console.log(chalk_1.default.bold(' └──────────────────────────────────────────┘'));
|
|
97
|
-
console.log();
|
|
98
|
-
// Try to open browser
|
|
99
|
-
try {
|
|
100
|
-
const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
101
|
-
(0, node_child_process_1.execSync)(`${openCmd} "${auth.verification_uri_complete}" 2>/dev/null`, { timeout: 5000 });
|
|
102
|
-
console.log(chalk_1.default.gray(' Browser opened. Waiting for authorization...'));
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
console.log(chalk_1.default.gray(' Open the URL above in your browser.'));
|
|
106
|
-
}
|
|
107
|
-
// Step 3: Poll for token
|
|
108
|
-
let interval = Math.max(auth.interval * 1000, POLL_INTERVAL_MS);
|
|
109
|
-
const expireTime = Date.now() + auth.expires_in * 1000;
|
|
110
|
-
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
|
|
111
|
-
await new Promise(r => setTimeout(r, interval));
|
|
112
|
-
if (Date.now() > expireTime) {
|
|
113
|
-
throw new Error('Authorization timed out');
|
|
114
|
-
}
|
|
115
|
-
const remaining = Math.ceil((expireTime - Date.now()) / 1000);
|
|
116
|
-
const mins = Math.floor(remaining / 60);
|
|
117
|
-
const secs = remaining % 60;
|
|
118
|
-
process.stdout.write(`\r Waiting for authorization... (${mins}:${secs.toString().padStart(2, '0')} remaining) `);
|
|
119
|
-
const tokenRes = await fetch(`${hubUrl}/oauth/token`, {
|
|
120
|
-
method: 'POST',
|
|
121
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
122
|
-
body: new URLSearchParams({
|
|
123
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
124
|
-
device_code: auth.device_code,
|
|
125
|
-
client_id: DEVICE_CLIENT_ID,
|
|
126
|
-
}),
|
|
127
|
-
});
|
|
128
|
-
const tokenData = await tokenRes.json();
|
|
129
|
-
if (tokenData.access_token) {
|
|
130
|
-
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
131
|
-
// Get username
|
|
132
|
-
let uname = 'user';
|
|
133
|
-
try {
|
|
134
|
-
const userRes = await fetch(`${hubUrl}/oauth/userinfo`, {
|
|
135
|
-
headers: { Authorization: `Bearer ${tokenData.access_token}` },
|
|
136
|
-
});
|
|
137
|
-
if (userRes.ok) {
|
|
138
|
-
const userInfo = await userRes.json();
|
|
139
|
-
uname = userInfo.preferred_username || userInfo.name || 'user';
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch { /* use default */ }
|
|
143
|
-
return { token: tokenData.access_token, username: uname };
|
|
144
|
-
}
|
|
145
|
-
if (tokenData.error === 'slow_down') {
|
|
146
|
-
interval += 5000;
|
|
147
|
-
}
|
|
148
|
-
else if (tokenData.error === 'access_denied') {
|
|
149
|
-
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
150
|
-
throw new Error('Authorization denied by user');
|
|
151
|
-
}
|
|
152
|
-
else if (tokenData.error === 'expired_token') {
|
|
153
|
-
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
154
|
-
throw new Error('Authorization expired');
|
|
155
|
-
}
|
|
156
|
-
// authorization_pending → keep polling
|
|
157
|
-
}
|
|
158
|
-
throw new Error('Authorization timed out');
|
|
159
|
-
}
|
|
160
|
-
// ============================================================================
|
|
161
63
|
// Preflight Checks
|
|
162
64
|
// ============================================================================
|
|
163
65
|
function checkBinary(cmd) {
|
|
@@ -230,25 +132,44 @@ async function runSetup() {
|
|
|
230
132
|
if (!token) {
|
|
231
133
|
console.log(' Let\'s connect you to CV-Hub.');
|
|
232
134
|
console.log();
|
|
233
|
-
|
|
234
|
-
|
|
135
|
+
const autoName = `${(0, node_os_1.hostname)()}-${new Date().toISOString().slice(0, 10)}`;
|
|
136
|
+
const tokenUrl = `${appUrl}/settings/tokens/new?name=${encodeURIComponent(autoName)}&scopes=agent,repo`;
|
|
137
|
+
console.log(chalk_1.default.gray(` Opening: ${tokenUrl}`));
|
|
138
|
+
// Try to open browser
|
|
235
139
|
try {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
writeSharedCreds({ hub_url: hubUrl, token, username, created_at: new Date().toISOString() });
|
|
242
|
-
// Also save to cv-hub credentials for backward compatibility
|
|
243
|
-
await (0, credentials_js_1.writeCredentialField)('CV_HUB_PAT', token);
|
|
244
|
-
await (0, credentials_js_1.writeCredentialField)('CV_HUB_API', hubUrl);
|
|
140
|
+
const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
141
|
+
(0, node_child_process_1.execSync)(`${openCmd} "${tokenUrl}" 2>/dev/null`, { timeout: 5000 });
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
console.log(chalk_1.default.gray(' (Could not open browser — copy the URL above)'));
|
|
245
145
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
146
|
+
console.log();
|
|
147
|
+
// Prompt for token
|
|
148
|
+
const readline = await import('node:readline');
|
|
149
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
150
|
+
token = await new Promise((resolve) => {
|
|
151
|
+
rl.question(' Paste your token here: ', (answer) => {
|
|
152
|
+
rl.close();
|
|
153
|
+
resolve(answer.trim());
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
if (!token) {
|
|
157
|
+
console.log(chalk_1.default.red(' No token provided. Run cva setup again.'));
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
// Validate
|
|
161
|
+
const user = await validateToken(hubUrl, token);
|
|
162
|
+
if (!user) {
|
|
163
|
+
console.log(chalk_1.default.red(' Token validation failed. Check your token and try again.'));
|
|
250
164
|
process.exit(1);
|
|
251
165
|
}
|
|
166
|
+
username = user;
|
|
167
|
+
console.log(chalk_1.default.green(' ✓') + ` Authenticated as ${chalk_1.default.bold(user)}`);
|
|
168
|
+
// Save to shared credentials
|
|
169
|
+
writeSharedCreds({ hub_url: hubUrl, token, username, created_at: new Date().toISOString() });
|
|
170
|
+
// Also save to cv-hub credentials for backward compatibility
|
|
171
|
+
await (0, credentials_js_1.writeCredentialField)('CV_HUB_PAT', token);
|
|
172
|
+
await (0, credentials_js_1.writeCredentialField)('CV_HUB_API', hubUrl);
|
|
252
173
|
}
|
|
253
174
|
console.log();
|
|
254
175
|
// ── Step 3: Claude.ai MCP Connector ────────────────────────────────
|
|
@@ -261,90 +182,19 @@ async function runSetup() {
|
|
|
261
182
|
console.log(chalk_1.default.gray(' (You can do this later — setup will continue.)'));
|
|
262
183
|
console.log();
|
|
263
184
|
// ── Step 4: Repository Setup ───────────────────────────────────────
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
185
|
+
const cwd = process.cwd();
|
|
186
|
+
const isGitRepo = (0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, '.git'));
|
|
187
|
+
const hasCVDir = (0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, '.cv'));
|
|
188
|
+
const hasClaudeMd = (0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, 'CLAUDE.md'));
|
|
189
|
+
const repoName = (0, node_path_1.basename)(cwd);
|
|
267
190
|
if (isGitRepo) {
|
|
268
191
|
console.log(chalk_1.default.green(' ✓') + ` Git repo found: ${repoName}`);
|
|
269
192
|
}
|
|
270
193
|
else {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
console.log(' No git repository found in this directory.');
|
|
275
|
-
console.log();
|
|
276
|
-
console.log(` ${chalk_1.default.cyan('a')} — Initialize a new project here: ${cwd}`);
|
|
277
|
-
console.log(` ${chalk_1.default.cyan('b')} — Clone an existing repo from CV-Hub`);
|
|
278
|
-
console.log();
|
|
279
|
-
const choice = await new Promise((resolve) => {
|
|
280
|
-
rl.question(' Choose [a/b]: ', (answer) => {
|
|
281
|
-
rl.close();
|
|
282
|
-
resolve(answer.trim().toLowerCase() || 'a');
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
if (choice === 'b' && token) {
|
|
286
|
-
// Clone from CV-Hub
|
|
287
|
-
try {
|
|
288
|
-
console.log(chalk_1.default.gray(' Fetching your repositories...'));
|
|
289
|
-
const controller = new AbortController();
|
|
290
|
-
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
291
|
-
const res = await fetch(`${hubUrl}/api/v1/repos?limit=50`, {
|
|
292
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
293
|
-
signal: controller.signal,
|
|
294
|
-
});
|
|
295
|
-
clearTimeout(timeout);
|
|
296
|
-
if (res.ok) {
|
|
297
|
-
const data = await res.json();
|
|
298
|
-
const repos = data.repositories || [];
|
|
299
|
-
if (repos.length === 0) {
|
|
300
|
-
console.log(chalk_1.default.yellow(' No repos found on CV-Hub. Initializing a new project instead.'));
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
console.log();
|
|
304
|
-
console.log(' Your CV-Hub repositories:');
|
|
305
|
-
const displayRepos = repos.slice(0, 20);
|
|
306
|
-
displayRepos.forEach((r, i) => {
|
|
307
|
-
const desc = r.description ? chalk_1.default.gray(` — ${r.description.substring(0, 40)}`) : '';
|
|
308
|
-
console.log(` ${chalk_1.default.cyan(String(i + 1).padStart(2))}. ${r.slug || r.name}${desc}`);
|
|
309
|
-
});
|
|
310
|
-
console.log();
|
|
311
|
-
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
312
|
-
const selection = await new Promise((resolve) => {
|
|
313
|
-
rl2.question(` Select a repo [1-${displayRepos.length}]: `, (answer) => {
|
|
314
|
-
rl2.close();
|
|
315
|
-
resolve(answer.trim());
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
const idx = parseInt(selection, 10) - 1;
|
|
319
|
-
if (idx >= 0 && idx < displayRepos.length) {
|
|
320
|
-
const repo = displayRepos[idx];
|
|
321
|
-
const gitHost = 'git.hub.controlvector.io';
|
|
322
|
-
const slug = repo.slug || repo.name;
|
|
323
|
-
const cloneUrl = `https://${username}:${token}@${gitHost}/${username}/${slug}.git`;
|
|
324
|
-
console.log(chalk_1.default.gray(` Cloning ${username}/${slug}...`));
|
|
325
|
-
(0, node_child_process_1.execSync)(`git clone ${cloneUrl} ${slug}`, { cwd, stdio: 'pipe', timeout: 60_000 });
|
|
326
|
-
cwd = (0, node_path_1.join)(cwd, slug);
|
|
327
|
-
process.chdir(cwd);
|
|
328
|
-
repoName = slug;
|
|
329
|
-
isGitRepo = true;
|
|
330
|
-
console.log(chalk_1.default.green(' ✓') + ` Cloned ${username}/${slug}`);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
catch (err) {
|
|
336
|
-
console.log(chalk_1.default.yellow(` Could not fetch repos: ${err.message}. Initializing instead.`));
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
// If we haven't cloned, init a new repo
|
|
340
|
-
if (!isGitRepo) {
|
|
341
|
-
console.log(' Initializing git repository...');
|
|
342
|
-
(0, node_child_process_1.execSync)('git init && git checkout -b main', { cwd, stdio: 'pipe' });
|
|
343
|
-
console.log(chalk_1.default.green(' ✓') + ' Git repo initialized');
|
|
344
|
-
}
|
|
194
|
+
console.log(' Initializing git repository...');
|
|
195
|
+
(0, node_child_process_1.execSync)('git init && git checkout -b main', { cwd, stdio: 'pipe' });
|
|
196
|
+
console.log(chalk_1.default.green(' ✓') + ' Git repo initialized');
|
|
345
197
|
}
|
|
346
|
-
const hasClaudeMd = (0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, 'CLAUDE.md'));
|
|
347
|
-
const hasCVDir = (0, node_fs_1.existsSync)((0, node_path_1.join)(cwd, '.cv'));
|
|
348
198
|
if (!hasClaudeMd) {
|
|
349
199
|
const template = `# ${repoName}\n\n## Overview\n[Describe your project here]\n\n## Tech Stack\n[What languages/frameworks does this project use?]\n\n## Build & Run\n[How to build and run this project]\n`;
|
|
350
200
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(cwd, 'CLAUDE.md'), template);
|
|
@@ -363,25 +213,6 @@ async function runSetup() {
|
|
|
363
213
|
try {
|
|
364
214
|
const existingRemote = (0, node_child_process_1.execSync)('git remote get-url cv-hub 2>/dev/null || echo ""', { cwd, encoding: 'utf8' }).trim();
|
|
365
215
|
if (!existingRemote) {
|
|
366
|
-
// Create repo on CV-Hub (non-fatal if it already exists or fails)
|
|
367
|
-
try {
|
|
368
|
-
const controller = new AbortController();
|
|
369
|
-
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
370
|
-
await fetch(`${hubUrl}/api/v1/user/repos`, {
|
|
371
|
-
method: 'POST',
|
|
372
|
-
headers: {
|
|
373
|
-
Authorization: `Bearer ${token}`,
|
|
374
|
-
'Content-Type': 'application/json',
|
|
375
|
-
},
|
|
376
|
-
body: JSON.stringify({ name: repoName, auto_init: false }),
|
|
377
|
-
signal: controller.signal,
|
|
378
|
-
});
|
|
379
|
-
clearTimeout(timeout);
|
|
380
|
-
console.log(chalk_1.default.green(' ✓') + ` Repo created on CV-Hub: ${username}/${repoName}`);
|
|
381
|
-
}
|
|
382
|
-
catch {
|
|
383
|
-
// May already exist — that's fine
|
|
384
|
-
}
|
|
385
216
|
(0, node_child_process_1.execSync)(`git remote add cv-hub ${remoteUrl}`, { cwd, stdio: 'pipe' });
|
|
386
217
|
console.log(chalk_1.default.green(' ✓') + ` Remote added: cv-hub → ${remoteUrl}`);
|
|
387
218
|
}
|
|
@@ -392,126 +223,19 @@ async function runSetup() {
|
|
|
392
223
|
catch {
|
|
393
224
|
// Remote setup non-fatal
|
|
394
225
|
}
|
|
395
|
-
// Configure git credentials for CV-Hub pushes
|
|
396
|
-
try {
|
|
397
|
-
const credStorePath = (0, node_path_1.join)((0, node_os_1.homedir)(), '.git-credentials');
|
|
398
|
-
const credLine = `https://${username}:${token}@${gitHost}`;
|
|
399
|
-
let existing = '';
|
|
400
|
-
try {
|
|
401
|
-
existing = (0, node_fs_1.readFileSync)(credStorePath, 'utf-8');
|
|
402
|
-
}
|
|
403
|
-
catch { /* doesn't exist yet */ }
|
|
404
|
-
if (!existing.includes(gitHost)) {
|
|
405
|
-
(0, node_fs_1.writeFileSync)(credStorePath, existing + credLine + '\n', { mode: 0o600 });
|
|
406
|
-
}
|
|
407
|
-
(0, node_child_process_1.execSync)(`git config --global credential.helper store`, { stdio: 'pipe' });
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
// Non-fatal
|
|
411
|
-
}
|
|
412
|
-
// Initial commit if repo has no commits
|
|
413
|
-
try {
|
|
414
|
-
(0, node_child_process_1.execSync)('git log --oneline -1', { cwd, stdio: 'pipe' });
|
|
415
|
-
// Has commits — check for uncommitted new files
|
|
416
|
-
const status = (0, node_child_process_1.execSync)('git status --porcelain', { cwd, encoding: 'utf8' }).trim();
|
|
417
|
-
if (status) {
|
|
418
|
-
(0, node_child_process_1.execSync)('git add -A', { cwd, stdio: 'pipe' });
|
|
419
|
-
(0, node_child_process_1.execSync)('git commit -m "chore: cv-agent setup"', { cwd, stdio: 'pipe' });
|
|
420
|
-
console.log(chalk_1.default.green(' ✓') + ' Changes committed');
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
catch {
|
|
424
|
-
// No commits yet — make initial commit
|
|
425
|
-
try {
|
|
426
|
-
(0, node_child_process_1.execSync)('git add -A', { cwd, stdio: 'pipe' });
|
|
427
|
-
(0, node_child_process_1.execSync)('git commit -m "Initial commit via cv-agent"', { cwd, stdio: 'pipe' });
|
|
428
|
-
console.log(chalk_1.default.green(' ✓') + ' Initial commit created');
|
|
429
|
-
}
|
|
430
|
-
catch { /* empty repo with nothing to commit */ }
|
|
431
|
-
}
|
|
432
|
-
// Push to CV-Hub
|
|
433
|
-
try {
|
|
434
|
-
(0, node_child_process_1.execSync)('git push -u cv-hub main 2>&1', { cwd, stdio: 'pipe', timeout: 30_000 });
|
|
435
|
-
console.log(chalk_1.default.green(' ✓') + ' Pushed to CV-Hub');
|
|
436
|
-
}
|
|
437
|
-
catch {
|
|
438
|
-
// Push may fail if repo doesn't exist yet or auth issue — non-fatal
|
|
439
|
-
console.log(chalk_1.default.gray(' (Push skipped — you can push later with: git push cv-hub main)'));
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
console.log();
|
|
443
|
-
// ── Step 5: Agent Daemon ────────────────────────────────────────────
|
|
444
|
-
let agentStatus = '';
|
|
445
|
-
const pidFile = (0, node_path_1.join)((0, node_os_1.homedir)(), '.config', 'controlvector', 'agent.pid');
|
|
446
|
-
// Check if agent is already running
|
|
447
|
-
let agentRunning = false;
|
|
448
|
-
try {
|
|
449
|
-
const pid = parseInt((0, node_fs_1.readFileSync)(pidFile, 'utf-8').trim(), 10);
|
|
450
|
-
if (pid > 0) {
|
|
451
|
-
process.kill(pid, 0); // throws if not running
|
|
452
|
-
agentRunning = true;
|
|
453
|
-
agentStatus = `running (PID ${pid})`;
|
|
454
|
-
console.log(chalk_1.default.green(' ✓') + ` Agent already running (PID ${pid})`);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
catch {
|
|
458
|
-
// PID file missing or process dead
|
|
459
|
-
}
|
|
460
|
-
if (!agentRunning) {
|
|
461
|
-
const readline = await import('node:readline');
|
|
462
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
463
|
-
const answer = await new Promise((resolve) => {
|
|
464
|
-
rl.question(' Start the CV-Agent daemon? (Y/n): ', (a) => {
|
|
465
|
-
rl.close();
|
|
466
|
-
resolve(a.trim().toLowerCase() || 'y');
|
|
467
|
-
});
|
|
468
|
-
});
|
|
469
|
-
if (answer === 'y' || answer === 'yes' || answer === '') {
|
|
470
|
-
const machName = (0, node_os_1.hostname)().toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
|
|
471
|
-
console.log(chalk_1.default.gray(` Starting agent as "${machName}"...`));
|
|
472
|
-
try {
|
|
473
|
-
const { spawn: spawnChild } = await import('node:child_process');
|
|
474
|
-
const child = spawnChild('cva', [
|
|
475
|
-
'agent', '--auto-approve', '--machine', machName, '--working-dir', cwd,
|
|
476
|
-
], {
|
|
477
|
-
detached: true,
|
|
478
|
-
stdio: 'ignore',
|
|
479
|
-
env: { ...process.env },
|
|
480
|
-
});
|
|
481
|
-
child.unref();
|
|
482
|
-
if (child.pid) {
|
|
483
|
-
(0, node_fs_1.mkdirSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.config', 'controlvector'), { recursive: true });
|
|
484
|
-
(0, node_fs_1.writeFileSync)(pidFile, String(child.pid), { mode: 0o600 });
|
|
485
|
-
agentStatus = `running (PID ${child.pid})`;
|
|
486
|
-
console.log(chalk_1.default.green(' ✓') + ` Agent started (PID ${child.pid}) — executor "${machName}"`);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
catch (err) {
|
|
490
|
-
console.log(chalk_1.default.yellow(` Could not start agent: ${err.message}`));
|
|
491
|
-
console.log(chalk_1.default.gray(' Start manually with: cva agent --auto-approve'));
|
|
492
|
-
agentStatus = 'not started';
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
agentStatus = 'not started';
|
|
497
|
-
console.log(chalk_1.default.gray(' Start anytime with: cva agent --auto-approve'));
|
|
498
|
-
}
|
|
499
226
|
}
|
|
500
227
|
console.log();
|
|
501
|
-
// ── Step
|
|
228
|
+
// ── Step 5: Summary ────────────────────────────────────────────────
|
|
502
229
|
console.log(chalk_1.default.bold(' Setup Complete'));
|
|
503
230
|
console.log(chalk_1.default.gray(' ' + '─'.repeat(40)));
|
|
504
231
|
console.log(` ${chalk_1.default.green('✓')} Authenticated as: ${chalk_1.default.cyan(username)}`);
|
|
505
232
|
console.log(` ${chalk_1.default.green('✓')} Repository: ${chalk_1.default.cyan(repoName)}`);
|
|
506
233
|
console.log(` ${chalk_1.default.green('✓')} CLAUDE.md: present`);
|
|
507
|
-
if (agentStatus.startsWith('running')) {
|
|
508
|
-
console.log(` ${chalk_1.default.green('✓')} Agent daemon: ${chalk_1.default.cyan(agentStatus)}`);
|
|
509
|
-
}
|
|
510
234
|
console.log(chalk_1.default.gray(' ' + '─'.repeat(40)));
|
|
511
235
|
console.log();
|
|
512
236
|
console.log(' What\'s next:');
|
|
513
|
-
console.log(
|
|
514
|
-
console.log(
|
|
237
|
+
console.log(` ${chalk_1.default.cyan('cva agent --auto-approve')} — Start listening for tasks`);
|
|
238
|
+
console.log(` Or open Claude.ai and dispatch a task to this repo.`);
|
|
515
239
|
console.log();
|
|
516
240
|
console.log(chalk_1.default.gray(` Dashboard: ${appUrl}`));
|
|
517
241
|
console.log();
|