gitarsenal-cli 1.9.5 → 1.9.7
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/.venv_status.json +1 -1
- package/bin/gitarsenal.js +206 -11
- package/config.json +5 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +2 -1
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-08-
|
|
1
|
+
{"created":"2025-08-03T18:14:08.672Z","packages":["modal","gitingest","requests"],"uv_version":"uv 0.8.4 (e176e1714 2025-07-30)"}
|
package/bin/gitarsenal.js
CHANGED
|
@@ -13,6 +13,8 @@ const pkg = require('../package.json');
|
|
|
13
13
|
const boxen = require('boxen');
|
|
14
14
|
const { spawn } = require('child_process');
|
|
15
15
|
const fs = require('fs');
|
|
16
|
+
const https = require('https');
|
|
17
|
+
const http = require('http');
|
|
16
18
|
|
|
17
19
|
// Function to activate virtual environment
|
|
18
20
|
function activateVirtualEnvironment() {
|
|
@@ -102,6 +104,148 @@ function activateVirtualEnvironment() {
|
|
|
102
104
|
return true;
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
// Function to send user data to web application
|
|
108
|
+
async function sendUserData(userId, userName, command, repoUrl = null, success = true, error = null) {
|
|
109
|
+
try {
|
|
110
|
+
const userData = {
|
|
111
|
+
userId,
|
|
112
|
+
userName,
|
|
113
|
+
command,
|
|
114
|
+
repoUrl,
|
|
115
|
+
success,
|
|
116
|
+
error,
|
|
117
|
+
timestamp: new Date().toISOString()
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const data = JSON.stringify(userData);
|
|
121
|
+
|
|
122
|
+
// Get webhook URL from config or use default
|
|
123
|
+
let webhookUrl = 'https://gitarsenal.dev/api/users/cli-log';
|
|
124
|
+
const configPath = path.join(__dirname, '..', 'config.json');
|
|
125
|
+
if (fs.existsSync(configPath)) {
|
|
126
|
+
try {
|
|
127
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
128
|
+
if (config.webhookUrl) {
|
|
129
|
+
webhookUrl = config.webhookUrl;
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// Use default URL if config can't be read
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const urlObj = new URL(webhookUrl);
|
|
137
|
+
const options = {
|
|
138
|
+
hostname: urlObj.hostname,
|
|
139
|
+
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
140
|
+
path: urlObj.pathname,
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: {
|
|
143
|
+
'Content-Type': 'application/json',
|
|
144
|
+
'Content-Length': Buffer.byteLength(data)
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
const client = urlObj.protocol === 'https:' ? https : http;
|
|
150
|
+
const req = client.request(options, (res) => {
|
|
151
|
+
let responseData = '';
|
|
152
|
+
res.on('data', (chunk) => {
|
|
153
|
+
responseData += chunk;
|
|
154
|
+
});
|
|
155
|
+
res.on('end', () => {
|
|
156
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
157
|
+
console.log(chalk.green('✅ User activity logged to GitArsenal dashboard'));
|
|
158
|
+
resolve(responseData);
|
|
159
|
+
} else {
|
|
160
|
+
console.log(chalk.yellow('⚠️ Failed to log user activity (non-critical)'));
|
|
161
|
+
resolve(responseData);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
req.on('error', (err) => {
|
|
167
|
+
console.log(chalk.yellow('⚠️ Could not connect to GitArsenal dashboard (non-critical)'));
|
|
168
|
+
resolve();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
req.write(data);
|
|
172
|
+
req.end();
|
|
173
|
+
});
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.log(chalk.yellow('⚠️ Error logging user activity (non-critical)'));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Function to collect user credentials
|
|
180
|
+
async function collectUserCredentials(options) {
|
|
181
|
+
let userId = options.userId;
|
|
182
|
+
let userName = options.userName;
|
|
183
|
+
|
|
184
|
+
// Check for config file first
|
|
185
|
+
const configPath = path.join(__dirname, '..', 'config.json');
|
|
186
|
+
if (fs.existsSync(configPath)) {
|
|
187
|
+
try {
|
|
188
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
189
|
+
if (config.userId && config.userName) {
|
|
190
|
+
userId = config.userId;
|
|
191
|
+
userName = config.userName;
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.log(chalk.yellow('⚠️ Could not read config file'));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// If not provided via CLI or config, prompt for them
|
|
199
|
+
if (!userId || !userName) {
|
|
200
|
+
console.log(chalk.blue('\n🔐 GitArsenal User Identification'));
|
|
201
|
+
console.log(chalk.gray('Help us track your usage and improve GitArsenal!'));
|
|
202
|
+
console.log(chalk.gray('Your credentials will be saved locally for future use.'));
|
|
203
|
+
|
|
204
|
+
const credentials = await inquirer.prompt([
|
|
205
|
+
{
|
|
206
|
+
type: 'input',
|
|
207
|
+
name: 'userId',
|
|
208
|
+
message: 'Enter your user ID (or email):',
|
|
209
|
+
default: userId || 'anonymous',
|
|
210
|
+
validate: (input) => input.trim() !== '' ? true : 'User ID is required'
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
type: 'input',
|
|
214
|
+
name: 'userName',
|
|
215
|
+
message: 'Enter your name:',
|
|
216
|
+
default: userName || 'Anonymous User',
|
|
217
|
+
validate: (input) => input.trim() !== '' ? true : 'Name is required'
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
type: 'confirm',
|
|
221
|
+
name: 'saveConfig',
|
|
222
|
+
message: 'Save these credentials for future use?',
|
|
223
|
+
default: true
|
|
224
|
+
}
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
userId = credentials.userId;
|
|
228
|
+
userName = credentials.userName;
|
|
229
|
+
|
|
230
|
+
// Save to config file if requested
|
|
231
|
+
if (credentials.saveConfig) {
|
|
232
|
+
try {
|
|
233
|
+
const config = {
|
|
234
|
+
userId,
|
|
235
|
+
userName,
|
|
236
|
+
webhookUrl: 'https://gitarsenal.dev/api/users/cli-log'
|
|
237
|
+
};
|
|
238
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
239
|
+
console.log(chalk.green('✅ Credentials saved to config file'));
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.log(chalk.yellow('⚠️ Could not save config file'));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return { userId, userName };
|
|
247
|
+
}
|
|
248
|
+
|
|
105
249
|
// Activate virtual environment
|
|
106
250
|
activateVirtualEnvironment();
|
|
107
251
|
|
|
@@ -140,6 +284,8 @@ const containerCmd = program
|
|
|
140
284
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
141
285
|
.option('-m, --manual', 'Disable automatic setup command detection')
|
|
142
286
|
.option('--show-examples', 'Show usage examples')
|
|
287
|
+
.option('--user-id <id>', 'User ID for tracking')
|
|
288
|
+
.option('--user-name <name>', 'User name for tracking')
|
|
143
289
|
.action(async (options) => {
|
|
144
290
|
await runContainerCommand(options);
|
|
145
291
|
});
|
|
@@ -189,6 +335,8 @@ program
|
|
|
189
335
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
190
336
|
.option('-m, --manual', 'Disable automatic setup command detection')
|
|
191
337
|
.option('--show-examples', 'Show usage examples')
|
|
338
|
+
.option('--user-id <id>', 'User ID for tracking')
|
|
339
|
+
.option('--user-name <name>', 'User name for tracking')
|
|
192
340
|
.action(async (options) => {
|
|
193
341
|
// If options are provided directly, run the container command
|
|
194
342
|
if (options.repo || options.showExamples || process.argv.length <= 3) {
|
|
@@ -208,12 +356,18 @@ async function runContainerCommand(options) {
|
|
|
208
356
|
return;
|
|
209
357
|
}
|
|
210
358
|
|
|
359
|
+
// Collect user credentials
|
|
360
|
+
const userCredentials = await collectUserCredentials(options);
|
|
361
|
+
const { userId, userName } = userCredentials;
|
|
362
|
+
|
|
211
363
|
// Check for required dependencies
|
|
212
364
|
const spinner = ora('Checking dependencies...').start();
|
|
213
365
|
const dependenciesOk = await checkDependencies();
|
|
214
366
|
|
|
215
367
|
if (!dependenciesOk) {
|
|
216
368
|
spinner.fail('Missing dependencies. Please install them and try again.');
|
|
369
|
+
// Log failed dependency check
|
|
370
|
+
await sendUserData(userId, userName, 'dependency-check', null, false, 'Missing dependencies');
|
|
217
371
|
process.exit(1);
|
|
218
372
|
}
|
|
219
373
|
spinner.succeed('Dependencies checked');
|
|
@@ -345,14 +499,28 @@ async function runContainerCommand(options) {
|
|
|
345
499
|
}
|
|
346
500
|
}
|
|
347
501
|
|
|
502
|
+
// Log command start
|
|
503
|
+
const commandString = `gitarsenal container ${repoUrl ? `--repo ${repoUrl}` : ''} ${gpuType ? `--gpu ${gpuType}` : ''}`;
|
|
504
|
+
await sendUserData(userId, userName, commandString, repoUrl, true);
|
|
505
|
+
|
|
348
506
|
// Run the container
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
507
|
+
try {
|
|
508
|
+
await runContainer({
|
|
509
|
+
repoUrl,
|
|
510
|
+
gpuType,
|
|
511
|
+
volumeName,
|
|
512
|
+
setupCommands,
|
|
513
|
+
useApi
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Log successful completion
|
|
517
|
+
await sendUserData(userId, userName, commandString, repoUrl, true);
|
|
518
|
+
|
|
519
|
+
} catch (containerError) {
|
|
520
|
+
// Log error
|
|
521
|
+
await sendUserData(userId, userName, commandString, repoUrl, false, containerError.message);
|
|
522
|
+
throw containerError;
|
|
523
|
+
}
|
|
356
524
|
|
|
357
525
|
} catch (error) {
|
|
358
526
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
@@ -362,6 +530,10 @@ async function runContainerCommand(options) {
|
|
|
362
530
|
|
|
363
531
|
async function handleKeysAdd(options) {
|
|
364
532
|
try {
|
|
533
|
+
// Collect user credentials for keys operations
|
|
534
|
+
const userCredentials = await collectUserCredentials(options);
|
|
535
|
+
const { userId, userName } = userCredentials;
|
|
536
|
+
|
|
365
537
|
const spinner = ora('Adding API key...').start();
|
|
366
538
|
|
|
367
539
|
let service = options.service;
|
|
@@ -413,11 +585,15 @@ async function handleKeysAdd(options) {
|
|
|
413
585
|
output += data.toString();
|
|
414
586
|
});
|
|
415
587
|
|
|
416
|
-
pythonProcess.on('close', (code) => {
|
|
588
|
+
pythonProcess.on('close', async (code) => {
|
|
417
589
|
if (code === 0) {
|
|
418
590
|
spinner.succeed(`API key for ${service} added successfully`);
|
|
591
|
+
// Log successful key addition
|
|
592
|
+
await sendUserData(userId, userName, `gitarsenal keys add --service ${service}`, null, true);
|
|
419
593
|
} else {
|
|
420
594
|
spinner.fail(`Failed to add API key: ${output}`);
|
|
595
|
+
// Log failed key addition
|
|
596
|
+
await sendUserData(userId, userName, `gitarsenal keys add --service ${service}`, null, false, output);
|
|
421
597
|
}
|
|
422
598
|
});
|
|
423
599
|
|
|
@@ -429,6 +605,10 @@ async function handleKeysAdd(options) {
|
|
|
429
605
|
|
|
430
606
|
async function handleKeysList() {
|
|
431
607
|
try {
|
|
608
|
+
// Collect user credentials for keys operations
|
|
609
|
+
const userCredentials = await collectUserCredentials({});
|
|
610
|
+
const { userId, userName } = userCredentials;
|
|
611
|
+
|
|
432
612
|
const spinner = ora('Fetching API keys...').start();
|
|
433
613
|
|
|
434
614
|
// Call Python script to list keys
|
|
@@ -440,11 +620,13 @@ async function handleKeysList() {
|
|
|
440
620
|
'list'
|
|
441
621
|
], { stdio: 'inherit' });
|
|
442
622
|
|
|
443
|
-
pythonProcess.on('close', (code) => {
|
|
623
|
+
pythonProcess.on('close', async (code) => {
|
|
444
624
|
if (code !== 0) {
|
|
445
625
|
spinner.fail('Failed to list API keys');
|
|
626
|
+
await sendUserData(userId, userName, 'gitarsenal keys list', null, false, 'Failed to list API keys');
|
|
446
627
|
} else {
|
|
447
628
|
spinner.stop();
|
|
629
|
+
await sendUserData(userId, userName, 'gitarsenal keys list', null, true);
|
|
448
630
|
}
|
|
449
631
|
});
|
|
450
632
|
|
|
@@ -456,6 +638,10 @@ async function handleKeysList() {
|
|
|
456
638
|
|
|
457
639
|
async function handleKeysView(options) {
|
|
458
640
|
try {
|
|
641
|
+
// Collect user credentials for keys operations
|
|
642
|
+
const userCredentials = await collectUserCredentials(options);
|
|
643
|
+
const { userId, userName } = userCredentials;
|
|
644
|
+
|
|
459
645
|
const spinner = ora('Viewing API key...').start();
|
|
460
646
|
|
|
461
647
|
let service = options.service;
|
|
@@ -483,11 +669,13 @@ async function handleKeysView(options) {
|
|
|
483
669
|
'--service', service
|
|
484
670
|
], { stdio: 'inherit' });
|
|
485
671
|
|
|
486
|
-
pythonProcess.on('close', (code) => {
|
|
672
|
+
pythonProcess.on('close', async (code) => {
|
|
487
673
|
if (code !== 0) {
|
|
488
674
|
spinner.fail(`Failed to view API key for ${service}`);
|
|
675
|
+
await sendUserData(userId, userName, `gitarsenal keys view --service ${service}`, null, false, `Failed to view API key for ${service}`);
|
|
489
676
|
} else {
|
|
490
677
|
spinner.stop();
|
|
678
|
+
await sendUserData(userId, userName, `gitarsenal keys view --service ${service}`, null, true);
|
|
491
679
|
}
|
|
492
680
|
});
|
|
493
681
|
|
|
@@ -499,6 +687,10 @@ async function handleKeysView(options) {
|
|
|
499
687
|
|
|
500
688
|
async function handleKeysDelete(options) {
|
|
501
689
|
try {
|
|
690
|
+
// Collect user credentials for keys operations
|
|
691
|
+
const userCredentials = await collectUserCredentials(options);
|
|
692
|
+
const { userId, userName } = userCredentials;
|
|
693
|
+
|
|
502
694
|
const spinner = ora('Deleting API key...').start();
|
|
503
695
|
|
|
504
696
|
let service = options.service;
|
|
@@ -529,6 +721,7 @@ async function handleKeysDelete(options) {
|
|
|
529
721
|
|
|
530
722
|
if (!confirmAnswer.confirm) {
|
|
531
723
|
console.log(chalk.yellow('Operation cancelled by user.'));
|
|
724
|
+
await sendUserData(userId, userName, `gitarsenal keys delete --service ${service}`, null, false, 'Operation cancelled by user');
|
|
532
725
|
return;
|
|
533
726
|
}
|
|
534
727
|
|
|
@@ -553,11 +746,13 @@ async function handleKeysDelete(options) {
|
|
|
553
746
|
output += data.toString();
|
|
554
747
|
});
|
|
555
748
|
|
|
556
|
-
pythonProcess.on('close', (code) => {
|
|
749
|
+
pythonProcess.on('close', async (code) => {
|
|
557
750
|
if (code === 0) {
|
|
558
751
|
spinner.succeed(`API key for ${service} deleted successfully`);
|
|
752
|
+
await sendUserData(userId, userName, `gitarsenal keys delete --service ${service}`, null, true);
|
|
559
753
|
} else {
|
|
560
754
|
spinner.fail(`Failed to delete API key: ${output}`);
|
|
755
|
+
await sendUserData(userId, userName, `gitarsenal keys delete --service ${service}`, null, false, output);
|
|
561
756
|
}
|
|
562
757
|
});
|
|
563
758
|
|
package/config.json
ADDED
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -32,9 +32,10 @@ async function checkAndInstallUv() {
|
|
|
32
32
|
if (platform === 'darwin') {
|
|
33
33
|
// macOS - prioritize Homebrew
|
|
34
34
|
installMethods = [
|
|
35
|
-
'brew install uv',
|
|
36
35
|
'curl -LsSf https://astral.sh/uv/install.sh | sh',
|
|
36
|
+
'pip install uv',
|
|
37
37
|
'pip3 install uv',
|
|
38
|
+
'brew install uv',
|
|
38
39
|
'cargo install uv'
|
|
39
40
|
];
|
|
40
41
|
} else if (platform === 'win32') {
|