testronaut 1.0.10 → 1.0.11

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.
Files changed (2) hide show
  1. package/bin/cli.js +133 -4
  2. package/package.json +3 -1
package/bin/cli.js CHANGED
@@ -5,6 +5,8 @@ import { fileURLToPath } from 'url';
5
5
  import { initializeTestronautProject } from './init.js';
6
6
  import { createWelcomeMission } from './createWelcomeMission.js';
7
7
  import { generateHtmlReport } from '../tools/generateHtmlReport.js';
8
+ import inquirer from 'inquirer';
9
+ import fetch from 'node-fetch';
8
10
 
9
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
12
 
@@ -21,6 +23,8 @@ const HELP_TEXT = `
21
23
  Usage:
22
24
  npx testronaut Run all missions in the ./missions directory
23
25
  npx testronaut <file> Run a specific mission file (e.g., login.mission.js)
26
+ npx testronaut login Log in and store session token
27
+ npx testronaut upload Upload the most recent report JSON
24
28
 
25
29
  Options:
26
30
  --init Scaffold project folders and a welcome mission
@@ -28,11 +32,11 @@ Options:
28
32
 
29
33
  Examples:
30
34
  npx testronaut
31
- npx testronaut login.mission.js
35
+ npx testronaut login
36
+ npx testronaut upload
32
37
  npx testronaut --init
33
38
  `;
34
39
 
35
-
36
40
  if (args.includes('--init')) {
37
41
  await initializeTestronautProject();
38
42
  await createWelcomeMission();
@@ -44,6 +48,18 @@ if (args.includes('--help')) {
44
48
  process.exit(0);
45
49
  }
46
50
 
51
+ // Handle the login command
52
+ if (args.includes('login')) {
53
+ await handleLogin();
54
+ process.exit(0);
55
+ }
56
+
57
+ // Handle the upload command
58
+ if (args.includes('upload')) {
59
+ await uploadReport();
60
+ process.exit(0);
61
+ }
62
+
47
63
  if (!fs.existsSync(missionsDir)) {
48
64
  console.error('❌ No `missions` directory found.');
49
65
  process.exit(1);
@@ -91,7 +107,6 @@ const flatMissions = allResults.flatMap(entry => {
91
107
  }));
92
108
  });
93
109
 
94
-
95
110
  const report = {
96
111
  runId,
97
112
  startTime: startTime.toISOString(),
@@ -107,4 +122,118 @@ const report = {
107
122
  const outputDir = './missions/mission_reports';
108
123
  fs.mkdirSync(outputDir, { recursive: true });
109
124
  fs.writeFileSync(`${outputDir}/${runId}.json`, JSON.stringify(report, null, 2));
110
- generateHtmlReport(report, `${outputDir}/${runId}.html`);
125
+ generateHtmlReport(report, `${outputDir}/${runId}.html`);
126
+
127
+ // Function to handle login and store session token
128
+ async function handleLogin() {
129
+ // Check if API key is provided as an argument
130
+ let apiKey = args.find(arg => arg.startsWith('--apiKey='))?.split('=')[1];
131
+
132
+ // If API key is not provided, prompt the user for it
133
+ if (!apiKey) {
134
+ const responses = await inquirer.prompt([
135
+ {
136
+ type: 'password',
137
+ name: 'apiKey',
138
+ message: 'Enter your API key:',
139
+ mask: '*'
140
+ }
141
+ ]);
142
+ apiKey = responses.apiKey;
143
+ }
144
+
145
+ // Create URLSearchParams to send as x-www-form-urlencoded
146
+ const formData = new URLSearchParams();
147
+ formData.append('apiKey', apiKey);
148
+ console.log('🔑 Authenticating with API key...');
149
+
150
+ // Call your authentication endpoint to get a session token
151
+ try {
152
+ const response = await fetch('http://testronaut.app/api/user/cli', { // Replace with your actual URL
153
+ method: 'POST',
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ },
157
+ body: formData.toString(),
158
+ });
159
+
160
+ const data = await response.json();
161
+
162
+ if (data.error) {
163
+ console.error('❌ Authentication failed:', data.error);
164
+ process.exit(1);
165
+ }
166
+
167
+ // Define the config path
168
+ const configPath = path.resolve(process.cwd(), 'testronaut-config.json');
169
+
170
+ // Read the existing config file
171
+ let config = {};
172
+ if (fs.existsSync(configPath)) {
173
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
174
+ }
175
+
176
+ // Overwrite or add the sessionToken field
177
+ config.sessionToken = data.sessionToken;
178
+
179
+ // Write the updated config back to testronaut-config.json
180
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
181
+ console.log('✅ Login successful! Session token saved to testronaut-config.json.');
182
+ } catch (error) {
183
+ console.error('❌ Error during login:', error);
184
+ process.exit(1);
185
+ }
186
+ }
187
+
188
+ // Upload the most recent report
189
+ async function uploadReport() {
190
+ const configPath = path.resolve(process.cwd(), 'testronaut-config.json');
191
+ if (!fs.existsSync(configPath)) {
192
+ console.error('❌ Configuration file not found.');
193
+ process.exit(1);
194
+ }
195
+
196
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
197
+ const sessionToken = config.sessionToken;
198
+
199
+ if (!sessionToken) {
200
+ console.error('❌ No session token found.');
201
+ process.exit(1);
202
+ }
203
+
204
+ // Get the most recent report from the 'missions/mission_reports/' directory
205
+ const reportDir = path.resolve(process.cwd(), 'missions/mission_reports');
206
+ const files = fs.readdirSync(reportDir).filter(file => file.endsWith('.json'));
207
+
208
+ if (files.length === 0) {
209
+ console.error('❌ No report files found.');
210
+ process.exit(1);
211
+ }
212
+
213
+ // Sort files by timestamp (most recent first)
214
+ files.sort((a, b) => parseInt(b.split('_')[1]) - parseInt(a.split('_')[1]));
215
+ const latestReport = path.join(reportDir, files[0]);
216
+
217
+ // Read the content of the most recent report
218
+ const reportData = fs.readFileSync(latestReport, 'utf8');
219
+
220
+ // Upload the report using the session token
221
+ try {
222
+ const response = await fetch(`http://testronaut.app/api/user/cli/${sessionToken}`, { // Replace with your API endpoint
223
+ method: 'POST',
224
+ headers: {
225
+ 'Content-Type': 'application/json',
226
+ },
227
+ body: reportData, // Sending the report JSON as the request body
228
+ });
229
+
230
+ const data = await response.json();
231
+ if (data.error) {
232
+ console.error('❌ Failed to upload the report:', data.error);
233
+ } else {
234
+ console.log('✅ Report uploaded successfully!');
235
+ }
236
+ } catch (error) {
237
+ console.error('❌ Error during upload:', error);
238
+ }
239
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testronaut",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "main": "./runner/testronaut.js",
5
5
  "bin": {
6
6
  "testronaut": "./bin/cli.js"
@@ -22,6 +22,8 @@
22
22
  "@dqbd/tiktoken": "^1.0.21",
23
23
  "cheerio": "^1.1.0",
24
24
  "dotenv": "^17.2.0",
25
+ "inquirer": "^12.9.1",
26
+ "node-fetch": "^3.3.2",
25
27
  "openai": "^5.10.2",
26
28
  "playwright": "^1.54.1",
27
29
  "prompts": "^2.4.2"