testronaut 1.0.5 β†’ 1.0.6

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/README.md CHANGED
@@ -20,9 +20,13 @@
20
20
  ```
21
21
  npm install -g testronaut
22
22
  ```
23
- Or use with npx:
23
+ Then initialize with npx, following the instructions:
24
24
  ```
25
- npx testronaut
25
+ npx testronaut --init
26
+ ```
27
+ run the welcome mission:
28
+ ```
29
+ npx testronaut welcome.mission.js
26
30
  ```
27
31
  πŸ“ Directory Structure
28
32
  Your project should include a missions/ folder with mission files like:
@@ -98,7 +102,7 @@ testronaut
98
102
  Run a specific file:
99
103
 
100
104
  ```
101
- testronaut missions/login.mission.js
105
+ testronaut login.mission.js
102
106
  ```
103
107
 
104
108
  🧰 Available Helpers
package/bin/cli.js CHANGED
@@ -2,12 +2,43 @@
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
+ import { initializeTestronautProject } from './init.js';
6
+ import { createWelcomeMission } from './createWelcomeMission.js';
5
7
 
6
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
9
 
8
10
  const args = process.argv.slice(2);
9
11
  const missionsDir = path.resolve(process.cwd(), 'missions');
10
12
 
13
+ const HELP_TEXT = `
14
+ πŸ§‘β€πŸš€ testronaut - Autonomous Agent Mission Runner
15
+
16
+ Usage:
17
+ npx testronaut Run all missions in the ./missions directory
18
+ npx testronaut <file> Run a specific mission file (e.g., login.mission.js)
19
+
20
+ Options:
21
+ --init Scaffold project folders and a welcome mission
22
+ --help Show this help message
23
+
24
+ Examples:
25
+ npx testronaut
26
+ npx testronaut login.mission.js
27
+ npx testronaut --init
28
+ `;
29
+
30
+
31
+ if (args.includes('--init')) {
32
+ await initializeTestronautProject();
33
+ await createWelcomeMission();
34
+ process.exit(0);
35
+ }
36
+
37
+ if (args.includes('--help')) {
38
+ console.log(HELP_TEXT);
39
+ process.exit(0);
40
+ }
41
+
11
42
  if (!fs.existsSync(missionsDir)) {
12
43
  console.error('❌ No `missions` directory found.');
13
44
  process.exit(1);
@@ -0,0 +1,35 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export const createWelcomeMission = async () => {
5
+ const missionsDir = path.join(process.cwd(), 'missions');
6
+ const welcomePath = path.join(missionsDir, 'welcome.mission.js');
7
+
8
+ if (fs.existsSync(welcomePath)) {
9
+ console.log('βœ… welcome.mission.js already exists');
10
+ return;
11
+ }
12
+
13
+ const content = `import { runMissions } from 'testronaut';
14
+
15
+ const welcomeGoal = \`
16
+ Welcome to Testronaut!
17
+
18
+ This is your first mission.
19
+ All we ask is that you confirm this setup is working correctly.
20
+
21
+ To complete this mission, report SUCCESS immediately.
22
+ If you cannot reach this message, report FAILURE.
23
+ \`;
24
+
25
+ export async function executeMission() {
26
+ await runMissions({
27
+ mission: welcomeGoal
28
+ });
29
+ }
30
+
31
+ `;
32
+
33
+ fs.writeFileSync(welcomePath, content);
34
+ console.log('πŸš€ Created missions/welcome.mission.js');
35
+ }
package/bin/init.js ADDED
@@ -0,0 +1,77 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import prompts from 'prompts';
4
+ import { createWelcomeMission } from './createWelcomeMission.js';
5
+
6
+ export async function initializeTestronautProject() {
7
+ const root = process.cwd();
8
+ const missionsDir = path.join(root, 'missions');
9
+ const reportsDir = path.join(missionsDir, 'mission_reports');
10
+ const configPath = path.join(root, 'testronaut-config.json');
11
+ const envPath = path.join(root, '.env');
12
+
13
+ let config = {};
14
+ if (fs.existsSync(configPath)) {
15
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
16
+ }
17
+
18
+ // If not initialized before, walk through setup
19
+ if (!config.initialized) {
20
+ // Prompt for LLM
21
+ const response = await prompts({
22
+ type: 'select',
23
+ name: 'llmProvider',
24
+ message: 'Which LLM provider would you like to use?',
25
+ choices: [
26
+ { title: 'OpenAI (GPT-4o)', value: 'openai' },
27
+ // Add others like Anthropic, Mistral, etc. later
28
+ ]
29
+ });
30
+
31
+ config.model = response.llmProvider;
32
+ config.initialized = true;
33
+ config.outputDir = 'missions/mission_reports';
34
+ config.projectName = path.basename(root);
35
+ config.maxTurns = 20;
36
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
37
+ console.log('πŸ“ Created testronaut-config.json');
38
+
39
+ if (response.llmProvider === 'openai') {
40
+ // Display .env instructions
41
+ if (!fs.existsSync(envPath)) {
42
+ const exampleEnv = `# Add your OpenAI API key below\nOPENAI_API_KEY=sk-...\n`;
43
+ fs.writeFileSync(envPath, exampleEnv);
44
+ console.log('πŸ” Created .env file');
45
+ }
46
+
47
+ console.log(`
48
+ πŸ”§ Setup Complete!
49
+
50
+ πŸ‘‰ Please ensure your .env file contains your OpenAI API key like this:
51
+
52
+ OPENAI_API_KEY=sk-...
53
+
54
+ πŸ“Œ The key must have permissions to call the OpenAI Chat Completions API.
55
+
56
+ βœ… You’re now ready to write missions in \`missions/\` and run your first mission using:
57
+
58
+ testronaut welcome.mission.js
59
+ `);
60
+ }
61
+
62
+ createWelcomeMission();
63
+ } else {
64
+ console.log('πŸ” Already initialized. Skipping setup.');
65
+ }
66
+
67
+ // Create directories if they don't exist
68
+ if (!fs.existsSync(missionsDir)) {
69
+ fs.mkdirSync(missionsDir);
70
+ console.log('πŸ“ Created missions/');
71
+ }
72
+
73
+ if (!fs.existsSync(reportsDir)) {
74
+ fs.mkdirSync(reportsDir);
75
+ console.log('πŸ“ Created missions/mission_reports/');
76
+ }
77
+ }
package/core/turnLoop.js CHANGED
@@ -43,7 +43,7 @@ const pushDOMAssistant = async (browser, messages, agentMemory, { skipIfLastTool
43
43
 
44
44
  const domHtml = await CHROME_TOOL_MAP.get_dom(browser, { limit: 100000, exclude: true }, agentMemory);
45
45
  await tokenEstimate('gpt-4o', domHtml);
46
- fs.writeFileSync(`debug-expanded-${Date.now()}.html`, domHtml);
46
+ fs.writeFileSync(`missions/mission_reports/debug-expanded-${Date.now()}.html`, domHtml);
47
47
 
48
48
  messages.push({
49
49
  role: 'tool',
@@ -171,7 +171,7 @@ export const turnLoop = async (browser, messages, maxTurns, currentTurn = 0, ret
171
171
  const truncated = result.length > 100 ? result.slice(0, 100) + '…' : result;
172
172
 
173
173
  if (result.length > 100) {
174
- const logFile = `tool-result-${Date.now()}.log`;
174
+ const logFile = `missions/mission_reports/tool-result-${Date.now()}.log`;
175
175
  fs.writeFileSync(logFile, result);
176
176
  console.log(`[tool ] ← ${truncated} (full output written to ${logFile})`);
177
177
  } else {
@@ -197,7 +197,7 @@ export const turnLoop = async (browser, messages, maxTurns, currentTurn = 0, ret
197
197
  focus: [], // or try focusing on ['main'] to keep it light
198
198
  }, agentMemory);
199
199
  await tokenEstimate('gpt-4o', domHtml);
200
- fs.writeFileSync(`debug-expanded-${Date.now()}.html`, domHtml);
200
+ fs.writeFileSync(`missions/mission_reports/debug-expanded-${Date.now()}.html`, domHtml);
201
201
  await pushDOMAssistant(browser, messages, agentMemory, {
202
202
  skipIfLastTool: ['get_dom', 'check_text']
203
203
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testronaut",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "main": "./runner/testronaut.js",
5
5
  "bin": {
6
6
  "testronaut": "./bin/cli.js"
@@ -23,6 +23,7 @@
23
23
  "cheerio": "^1.1.0",
24
24
  "dotenv": "^17.2.0",
25
25
  "openai": "^5.10.2",
26
- "playwright": "^1.54.1"
26
+ "playwright": "^1.54.1",
27
+ "prompts": "^2.4.2"
27
28
  }
28
29
  }
@@ -92,7 +92,7 @@ export class ChromeBrowser {
92
92
  await el.click();
93
93
  await this.page.screenshot({ path: `screenshot_after_click_${Date.now()}.png` });
94
94
  await this.page.waitForTimeout(5000);
95
- await this.page.screenshot({ path: `screenshot_after_waiting_click_${Date.now()}.png` });
95
+ // await this.page.screenshot({ path: `screenshot_after_waiting_click_${Date.now()}.png` });
96
96
  return `clicked text "${text}"`;
97
97
 
98
98
  }
@@ -149,10 +149,10 @@ export class ChromeBrowser {
149
149
  await this.page.waitForTimeout(delayMs);
150
150
 
151
151
  const expandedDom = await this.page.content();
152
- const filename = `expand_menu_dump_${Date.now()}.html`;
152
+ const filename = `missions/mission_reports/expand_menu_dump_${Date.now()}.html`;
153
153
  fs.writeFileSync(filename, expandedDom);
154
154
  console.log(`[expand_menu] β†’ Dumped DOM after expand click to ${filename}`);
155
- await this.page.screenshot({ path: `screenshot_expand_menu_${Date.now()}.png` });
155
+ await this.page.screenshot({ path: `missions/mission_reports/screenshot_expand_menu_${Date.now()}.png` });
156
156
 
157
157
  return `expanded menu using ${selector}`;
158
158
  }