testdriverai 6.1.10 → 6.2.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/.github/workflows/acceptance-tests.yml +0 -2
- package/.github/workflows/acceptance-v6.yml +0 -2
- package/.github/workflows/lint.yml +1 -4
- package/.github/workflows/publish-canary.yml +0 -2
- package/.github/workflows/publish-latest.yml +0 -1
- package/.prettierignore +0 -1
- package/.vscode/settings.json +1 -4
- package/agent/events.js +10 -1
- package/agent/index.js +76 -104
- package/agent/interface.js +6 -43
- package/agent/lib/censorship.js +10 -15
- package/agent/lib/commander.js +18 -31
- package/agent/lib/commands.js +63 -81
- package/agent/lib/debugger-server.js +5 -0
- package/agent/lib/generator.js +2 -2
- package/agent/lib/redraw.js +1 -0
- package/agent/lib/sandbox.js +2 -0
- package/agent/lib/sdk.js +1 -2
- package/agent/lib/source-mapper.js +1 -1
- package/agent/lib/system.js +6 -1
- package/docs/account/enterprise.mdx +12 -8
- package/docs/account/pricing.mdx +2 -2
- package/docs/account/projects.mdx +0 -5
- package/docs/cli/overview.mdx +6 -6
- package/docs/commands/assert.mdx +0 -1
- package/docs/commands/hover-text.mdx +1 -3
- package/docs/commands/match-image.mdx +4 -5
- package/docs/commands/press-keys.mdx +8 -6
- package/docs/commands/scroll-until-image.mdx +7 -8
- package/docs/commands/scroll-until-text.mdx +6 -7
- package/docs/commands/wait-for-image.mdx +4 -5
- package/docs/commands/wait-for-text.mdx +5 -6
- package/docs/docs.json +40 -42
- package/docs/getting-started/vscode.mdx +56 -67
- package/docs/guide/environment-variables.mdx +5 -5
- package/docs/overview/comparison.mdx +39 -22
- package/docs/overview/quickstart.mdx +32 -84
- package/docs/styles.css +1 -10
- package/interfaces/cli/lib/base.js +6 -27
- package/interfaces/cli/utils/factory.js +4 -17
- package/interfaces/logger.js +5 -4
- package/interfaces/readline.js +1 -1
- package/package.json +3 -3
- package/schema.json +2 -22
- package/testdriver/acceptance/hover-text.yaml +1 -2
- package/testdriver/acceptance/prompt.yaml +1 -4
- package/testdriver/acceptance/scroll-until-image.yaml +0 -5
- package/testdriver/{lifecycle/prerun.yaml → examples/web/lifecycle/provision.yaml} +0 -6
- package/testdriver/lifecycle/provision.yaml +20 -0
- package/.github/workflows/self-hosted.yml +0 -102
- package/docs/apps/tauri-apps.mdx +0 -361
- package/docs/getting-started/playwright.mdx +0 -342
- package/docs/getting-started/self-hosting.mdx +0 -370
- package/docs/guide/dashcam.mdx +0 -118
- package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
- package/docs/images/content/vscode/ide-full.png +0 -0
- package/docs/images/content/vscode/running.png +0 -0
- package/interfaces/cli/commands/generate.js +0 -3
- package/setup/aws/cloudformation.yaml +0 -463
- package/setup/aws/spawn-runner.sh +0 -190
- package/testdriver/edge-cases/js-exception.yaml +0 -8
- package/testdriver/edge-cases/js-promise.yaml +0 -19
- package/testdriver/edge-cases/lifecycle/postrun.yaml +0 -10
- package/testdriver/edge-cases/success-test.yaml +0 -9
- package/testdriver/examples/web/lifecycle/postrun.yaml +0 -7
- package/testdriver/examples/web/lifecycle/prerun.yaml +0 -17
- package/testdriver/lifecycle/postrun.yaml +0 -7
package/.prettierignore
CHANGED
package/.vscode/settings.json
CHANGED
|
@@ -3,8 +3,5 @@
|
|
|
3
3
|
"source.organizeImports": "explicit"
|
|
4
4
|
},
|
|
5
5
|
"editor.formatOnSave": true,
|
|
6
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
7
|
-
"yaml.schemas": {
|
|
8
|
-
"https://raw.githubusercontent.com/testdriverai/testdriverai/main/schema.json": "file:///Users/kid/Desktop/td/internal/testdriverai/testdriver.yaml"
|
|
9
|
-
}
|
|
6
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
10
7
|
}
|
package/agent/events.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { EventEmitter2 } = require("eventemitter2");
|
|
2
|
+
const { censorSensitiveDataDeep } = require("./lib/censorship");
|
|
2
3
|
|
|
3
4
|
// Factory function to create a new emitter instance with censoring middleware
|
|
4
5
|
const createEmitter = () => {
|
|
@@ -12,6 +13,14 @@ const createEmitter = () => {
|
|
|
12
13
|
ignoreErrors: false,
|
|
13
14
|
});
|
|
14
15
|
|
|
16
|
+
// Override emit to censor sensitive data before emitting
|
|
17
|
+
const originalEmit = emitter.emit.bind(emitter);
|
|
18
|
+
emitter.emit = function (event, ...args) {
|
|
19
|
+
// Censor all arguments passed to emit
|
|
20
|
+
const censoredArgs = args.map(censorSensitiveDataDeep);
|
|
21
|
+
return originalEmit(event, ...censoredArgs);
|
|
22
|
+
};
|
|
23
|
+
|
|
15
24
|
return emitter;
|
|
16
25
|
};
|
|
17
26
|
|
|
@@ -37,7 +46,7 @@ const events = {
|
|
|
37
46
|
status: "status",
|
|
38
47
|
log: {
|
|
39
48
|
markdown: {
|
|
40
|
-
static: "log:markdown",
|
|
49
|
+
static: "log:markdown:static",
|
|
41
50
|
start: "log:markdown:start",
|
|
42
51
|
chunk: "log:markdown:chunk",
|
|
43
52
|
end: "log:markdown:end",
|
package/agent/index.js
CHANGED
|
@@ -63,18 +63,13 @@ class TestDriverAgent extends EventEmitter2 {
|
|
|
63
63
|
// Derive properties from cliArgs
|
|
64
64
|
const flags = cliArgs.options || {};
|
|
65
65
|
const firstArg = cliArgs.args && cliArgs.args[0];
|
|
66
|
-
|
|
67
|
-
// All commands (run, edit, generate) use the same pattern:
|
|
68
|
-
// first argument is the main file to work with
|
|
69
66
|
this.thisFile = firstArg || this.config.TD_DEFAULT_TEST_FILE;
|
|
70
|
-
|
|
71
67
|
this.resultFile = flags.resultFile || null;
|
|
72
68
|
this.newSandbox = flags.newSandbox || false;
|
|
73
69
|
this.healMode = flags.healMode || flags.heal || false;
|
|
74
70
|
this.sandboxId = flags["sandbox-id"] || null;
|
|
75
71
|
this.sandboxAmi = flags["sandbox-ami"] || null;
|
|
76
72
|
this.sandboxInstance = flags["sandbox-instance"] || null;
|
|
77
|
-
this.ip = flags.ip || null;
|
|
78
73
|
this.workingDir = flags.workingDir || process.cwd();
|
|
79
74
|
|
|
80
75
|
// Resolve thisFile to absolute path with proper extension
|
|
@@ -227,15 +222,7 @@ class TestDriverAgent extends EventEmitter2 {
|
|
|
227
222
|
if (skipPostrun) {
|
|
228
223
|
this.exit(true);
|
|
229
224
|
} else {
|
|
230
|
-
|
|
231
|
-
await this.summarize(error.message);
|
|
232
|
-
} catch (summarizeError) {
|
|
233
|
-
// If summarization fails, log it but don't let it prevent postrun from running
|
|
234
|
-
this.emitter.emit(
|
|
235
|
-
events.log.warn,
|
|
236
|
-
theme.yellow(`Failed to summarize: ${summarizeError.message}`),
|
|
237
|
-
);
|
|
238
|
-
}
|
|
225
|
+
await this.summarize(error.message);
|
|
239
226
|
// Always run postrun lifecycle script, even for fatal errors
|
|
240
227
|
return await this.exit(true, false, true);
|
|
241
228
|
}
|
|
@@ -432,7 +419,6 @@ class TestDriverAgent extends EventEmitter2 {
|
|
|
432
419
|
|
|
433
420
|
// Log current execution position for debugging
|
|
434
421
|
if (this.sourceMapper.currentFileSourceMap) {
|
|
435
|
-
this.emitter.emit(events.log.log, "");
|
|
436
422
|
this.emitter.emit(
|
|
437
423
|
events.log.log,
|
|
438
424
|
theme.dim(`${this.sourceMapper.getCurrentPositionDescription()}`),
|
|
@@ -490,13 +476,14 @@ class TestDriverAgent extends EventEmitter2 {
|
|
|
490
476
|
sourcePosition: sourcePosition,
|
|
491
477
|
});
|
|
492
478
|
|
|
493
|
-
|
|
479
|
+
await this.haveAIResolveError(
|
|
494
480
|
error,
|
|
495
481
|
yaml.dump({ commands: [yml] }),
|
|
496
482
|
depth,
|
|
497
483
|
true,
|
|
498
484
|
shouldSave,
|
|
499
485
|
);
|
|
486
|
+
throw error;
|
|
500
487
|
}
|
|
501
488
|
}
|
|
502
489
|
|
|
@@ -892,33 +879,30 @@ commands:
|
|
|
892
879
|
// based on the current state of the system (primarily the current screenshot)
|
|
893
880
|
// it will generate files that contain only "prompts"
|
|
894
881
|
// @todo revit the generate command
|
|
895
|
-
async generate(count
|
|
896
|
-
this.emitter.emit(
|
|
897
|
-
events.log.debug,
|
|
898
|
-
`generate called with count: ${count}, prompt: ${prompt}`,
|
|
899
|
-
);
|
|
900
|
-
|
|
901
|
-
await this.runLifecycle("prerun");
|
|
882
|
+
async generate(type, count, baseYaml, skipYaml = false) {
|
|
883
|
+
this.emitter.emit(events.log.debug, "generate called, %s", type);
|
|
902
884
|
|
|
903
885
|
this.emitter.emit(events.log.narration, theme.dim("thinking..."), true);
|
|
904
886
|
|
|
887
|
+
if (baseYaml && !skipYaml) {
|
|
888
|
+
await this.runLifecycle("prerun");
|
|
889
|
+
await this.run(baseYaml, false, false);
|
|
890
|
+
await this.runLifecycle("postrun");
|
|
891
|
+
}
|
|
892
|
+
|
|
905
893
|
let image = await this.system.captureScreenBase64();
|
|
906
894
|
|
|
907
895
|
const streamId = `generate-${Date.now()}`;
|
|
908
896
|
this.emitter.emit(events.log.markdown.start, streamId);
|
|
909
897
|
|
|
910
|
-
let mouse = await this.system.getMousePosition();
|
|
911
|
-
let activeWindow = await this.system.activeWin();
|
|
912
|
-
|
|
913
898
|
let message = await this.sdk.req(
|
|
914
899
|
"generate",
|
|
915
900
|
{
|
|
916
|
-
|
|
901
|
+
type,
|
|
917
902
|
image,
|
|
918
|
-
mousePosition:
|
|
919
|
-
activeWindow:
|
|
903
|
+
mousePosition: await this.system.getMousePosition(),
|
|
904
|
+
activeWindow: await this.system.activeWin(),
|
|
920
905
|
count,
|
|
921
|
-
stream: false,
|
|
922
906
|
},
|
|
923
907
|
(chunk) => {
|
|
924
908
|
if (chunk.type === "data") {
|
|
@@ -941,36 +925,35 @@ commands:
|
|
|
941
925
|
.replace(/['"`]/g, "")
|
|
942
926
|
.replace(/[^a-zA-Z0-9-]/g, "") // remove any non-alphanumeric chars except hyphens
|
|
943
927
|
.toLowerCase() + ".yaml";
|
|
944
|
-
|
|
945
928
|
let path1 = path.join(
|
|
946
929
|
this.workingDir,
|
|
947
930
|
"testdriver",
|
|
948
931
|
"generate",
|
|
949
932
|
fileName,
|
|
950
933
|
);
|
|
934
|
+
|
|
951
935
|
// create generate directory if it doesn't exist
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
fs.mkdirSync(generateDir);
|
|
955
|
-
console.log("Created generate directory:", generateDir);
|
|
956
|
-
} else {
|
|
957
|
-
console.log("Generate directory already exists:", generateDir);
|
|
936
|
+
if (!fs.existsSync(path.join(this.workingDir, "generate"))) {
|
|
937
|
+
fs.mkdirSync(path.join(this.workingDir, "generate"));
|
|
958
938
|
}
|
|
959
939
|
|
|
960
940
|
let list = testPrompt.steps;
|
|
961
941
|
|
|
942
|
+
if (baseYaml && fs.existsSync(baseYaml)) {
|
|
943
|
+
list.unshift({
|
|
944
|
+
step: {
|
|
945
|
+
command: "run",
|
|
946
|
+
file: baseYaml,
|
|
947
|
+
},
|
|
948
|
+
});
|
|
949
|
+
}
|
|
962
950
|
let contents = yaml.dump({
|
|
963
951
|
version: packageJson.version,
|
|
964
952
|
steps: list,
|
|
965
953
|
});
|
|
966
|
-
|
|
967
|
-
this.emitter.emit(events.log.debug, `writing file ${path1} ${contents}`);
|
|
968
|
-
|
|
969
954
|
fs.writeFileSync(path1, contents);
|
|
970
955
|
}
|
|
971
956
|
|
|
972
|
-
await this.runLifecycle("postrun");
|
|
973
|
-
|
|
974
957
|
this.exit(false);
|
|
975
958
|
}
|
|
976
959
|
|
|
@@ -1156,8 +1139,21 @@ ${yml}
|
|
|
1156
1139
|
|
|
1157
1140
|
// Create diff if file exists and content has changed
|
|
1158
1141
|
let diffResult = null;
|
|
1142
|
+
console.log("Checking for diff. File exists:", fileExists);
|
|
1143
|
+
console.log(
|
|
1144
|
+
"Content changed:",
|
|
1145
|
+
fileExists && existingContent !== regression,
|
|
1146
|
+
);
|
|
1147
|
+
if (fileExists) {
|
|
1148
|
+
console.log(
|
|
1149
|
+
"Existing content preview:",
|
|
1150
|
+
existingContent.substring(0, 100),
|
|
1151
|
+
);
|
|
1152
|
+
console.log("New content preview:", regression.substring(0, 100));
|
|
1153
|
+
}
|
|
1159
1154
|
|
|
1160
1155
|
if (fileExists && existingContent !== regression) {
|
|
1156
|
+
console.log("Creating diff - content has changed");
|
|
1161
1157
|
const patches = diff.structuredPatch(
|
|
1162
1158
|
filepath,
|
|
1163
1159
|
filepath,
|
|
@@ -1245,6 +1241,8 @@ ${yml}
|
|
|
1245
1241
|
diff: diffResult,
|
|
1246
1242
|
timestamp: endTime,
|
|
1247
1243
|
});
|
|
1244
|
+
} else {
|
|
1245
|
+
console.log("No diff result to emit");
|
|
1248
1246
|
}
|
|
1249
1247
|
|
|
1250
1248
|
// Emit file save completion event
|
|
@@ -1521,8 +1519,6 @@ ${regression}
|
|
|
1521
1519
|
}
|
|
1522
1520
|
|
|
1523
1521
|
async embed(file, depth, pushToHistory) {
|
|
1524
|
-
let inputFile = JSON.parse(JSON.stringify(file));
|
|
1525
|
-
|
|
1526
1522
|
this.analytics.track("embed", { file });
|
|
1527
1523
|
|
|
1528
1524
|
this.emitter.emit(
|
|
@@ -1532,7 +1528,7 @@ ${regression}
|
|
|
1532
1528
|
|
|
1533
1529
|
depth = depth + 1;
|
|
1534
1530
|
|
|
1535
|
-
this.emitter.emit(events.log.log, `${
|
|
1531
|
+
this.emitter.emit(events.log.log, `${file} (start)`);
|
|
1536
1532
|
|
|
1537
1533
|
// Use the new helper method to resolve file paths relative to testdriver directory
|
|
1538
1534
|
const currentFilePath = this.sourceMapper.currentFilePath || this.thisFile;
|
|
@@ -1585,7 +1581,7 @@ ${regression}
|
|
|
1585
1581
|
this.sourceMapper.restoreContext(previousContext);
|
|
1586
1582
|
}
|
|
1587
1583
|
|
|
1588
|
-
this.emitter.emit(events.log.log, `${
|
|
1584
|
+
this.emitter.emit(events.log.log, `${file} (end)`);
|
|
1589
1585
|
}
|
|
1590
1586
|
|
|
1591
1587
|
// Returns sandboxId to use (either from file if recent, or null)
|
|
@@ -1710,20 +1706,7 @@ ${regression}
|
|
|
1710
1706
|
const recentId = createNew ? null : this.getRecentSandboxId();
|
|
1711
1707
|
|
|
1712
1708
|
// Set sandbox ID for reconnection (only if not creating new and recent ID exists)
|
|
1713
|
-
if (
|
|
1714
|
-
let instance = await this.sandbox.send({
|
|
1715
|
-
type: "direct",
|
|
1716
|
-
resolution: this.config.TD_RESOLUTION,
|
|
1717
|
-
ci: this.config.CI,
|
|
1718
|
-
ip: this.ip,
|
|
1719
|
-
});
|
|
1720
|
-
|
|
1721
|
-
await this.renderSandbox(instance.instance, headless);
|
|
1722
|
-
await this.newSession();
|
|
1723
|
-
await this.runLifecycle("provision");
|
|
1724
|
-
|
|
1725
|
-
return;
|
|
1726
|
-
} else if (!createNew && recentId) {
|
|
1709
|
+
if (!createNew && recentId) {
|
|
1727
1710
|
this.emitter.emit(
|
|
1728
1711
|
events.log.narration,
|
|
1729
1712
|
theme.dim(`using recent sandbox: ${recentId}`),
|
|
@@ -1734,8 +1717,10 @@ ${regression}
|
|
|
1734
1717
|
events.log.narration,
|
|
1735
1718
|
theme.dim(`no recent sandbox found, creating a new one.`),
|
|
1736
1719
|
);
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// Only attempt to connect to existing sandbox if not in CI mode and not creating new
|
|
1723
|
+
if (this.sandboxId && !this.config.CI && !createNew) {
|
|
1739
1724
|
// Attempt to connect to known instance
|
|
1740
1725
|
this.emitter.emit(
|
|
1741
1726
|
events.log.narration,
|
|
@@ -1776,15 +1761,24 @@ ${regression}
|
|
|
1776
1761
|
return this.createNewSandbox();
|
|
1777
1762
|
});
|
|
1778
1763
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1764
|
+
console.log("New sandbox created:", newSandbox);
|
|
1765
|
+
|
|
1766
|
+
let data = {
|
|
1767
|
+
resolution: this.config.TD_RESOLUTION,
|
|
1768
|
+
url: newSandbox.url,
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
const encodedData = encodeURIComponent(JSON.stringify(data));
|
|
1772
|
+
|
|
1773
|
+
// Use the debugger URL instead of the VNC URL
|
|
1774
|
+
const urlToOpen = `${this.debuggerUrl}?data=${encodedData}`;
|
|
1775
|
+
|
|
1776
|
+
this.emitter.emit(events.showWindow, urlToOpen);
|
|
1777
|
+
|
|
1786
1778
|
await this.newSession();
|
|
1787
1779
|
await this.runLifecycle("provision");
|
|
1780
|
+
|
|
1781
|
+
console.log("provision run");
|
|
1788
1782
|
}
|
|
1789
1783
|
|
|
1790
1784
|
async start() {
|
|
@@ -1793,16 +1787,13 @@ ${regression}
|
|
|
1793
1787
|
events.log.log,
|
|
1794
1788
|
theme.green(`Howdy! I'm TestDriver v${packageJson.version}`),
|
|
1795
1789
|
);
|
|
1796
|
-
|
|
1797
|
-
// Emit test start event for the entire test execution
|
|
1798
|
-
this.emitter.emit(events.test.start, {
|
|
1799
|
-
filePath: this.thisFile,
|
|
1800
|
-
timestamp: Date.now(),
|
|
1801
|
-
});
|
|
1802
|
-
|
|
1803
1790
|
// Start the debugger server as early as possible to ensure event listeners are attached
|
|
1804
1791
|
if (!debuggerStarted) {
|
|
1805
1792
|
debuggerStarted = true; // Prevent multiple starts, especially when running test in parallel
|
|
1793
|
+
this.emitter.emit(
|
|
1794
|
+
events.log.narration,
|
|
1795
|
+
theme.green(`Starting debugger server...`),
|
|
1796
|
+
);
|
|
1806
1797
|
debuggerProcess = await createDebuggerProcess(
|
|
1807
1798
|
this.config,
|
|
1808
1799
|
this.emitter,
|
|
@@ -1810,7 +1801,6 @@ ${regression}
|
|
|
1810
1801
|
}
|
|
1811
1802
|
this.debuggerUrl = debuggerProcess.url || null; // Store the debugger URL
|
|
1812
1803
|
this.emitter.emit(events.log.log, `This is beta software!`);
|
|
1813
|
-
this.emitter.emit(events.log.log, ``);
|
|
1814
1804
|
this.emitter.emit(
|
|
1815
1805
|
events.log.log,
|
|
1816
1806
|
theme.yellow(`Join our Discord for help`),
|
|
@@ -1819,7 +1809,6 @@ ${regression}
|
|
|
1819
1809
|
events.log.log,
|
|
1820
1810
|
`https://discord.com/invite/cWDFW8DzPm`,
|
|
1821
1811
|
);
|
|
1822
|
-
this.emitter.emit(events.log.log, ``);
|
|
1823
1812
|
|
|
1824
1813
|
// make testdriver directory if it doesn't exist
|
|
1825
1814
|
let testdriverFolder = path.join(this.workingDir);
|
|
@@ -1833,10 +1822,7 @@ ${regression}
|
|
|
1833
1822
|
}
|
|
1834
1823
|
|
|
1835
1824
|
// if the directory for thisFile doesn't exist, create it
|
|
1836
|
-
if (
|
|
1837
|
-
this.cliArgs.command !== "sandbox" &&
|
|
1838
|
-
this.cliArgs.command !== "generate"
|
|
1839
|
-
) {
|
|
1825
|
+
if (this.cliArgs.command !== "sandbox") {
|
|
1840
1826
|
const dir = path.dirname(this.thisFile);
|
|
1841
1827
|
if (!fs.existsSync(dir)) {
|
|
1842
1828
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -1861,10 +1847,7 @@ ${regression}
|
|
|
1861
1847
|
await this.sdk.auth();
|
|
1862
1848
|
}
|
|
1863
1849
|
|
|
1864
|
-
if (
|
|
1865
|
-
this.cliArgs.command !== "sandbox" &&
|
|
1866
|
-
this.cliArgs.command !== "generate"
|
|
1867
|
-
) {
|
|
1850
|
+
if (this.cliArgs.command !== "sandbox") {
|
|
1868
1851
|
this.emitter.emit(
|
|
1869
1852
|
events.log.log,
|
|
1870
1853
|
theme.dim(`Working on ${this.thisFile}`),
|
|
@@ -1963,6 +1946,7 @@ Please check your network connection, TD_API_KEY, or the service status.`,
|
|
|
1963
1946
|
async createNewSandbox() {
|
|
1964
1947
|
const sandboxConfig = {
|
|
1965
1948
|
type: "create",
|
|
1949
|
+
os: "linux",
|
|
1966
1950
|
resolution: this.config.TD_RESOLUTION,
|
|
1967
1951
|
ci: this.config.CI,
|
|
1968
1952
|
};
|
|
@@ -1975,7 +1959,12 @@ Please check your network connection, TD_API_KEY, or the service status.`,
|
|
|
1975
1959
|
sandboxConfig.instanceType = this.sandboxInstance;
|
|
1976
1960
|
}
|
|
1977
1961
|
|
|
1962
|
+
console.log("sending create");
|
|
1963
|
+
|
|
1978
1964
|
let instance = await this.sandbox.send(sandboxConfig);
|
|
1965
|
+
|
|
1966
|
+
console.log("instance created", instance);
|
|
1967
|
+
|
|
1979
1968
|
return instance;
|
|
1980
1969
|
}
|
|
1981
1970
|
|
|
@@ -2061,20 +2050,6 @@ Please check your network connection, TD_API_KEY, or the service status.`,
|
|
|
2061
2050
|
// Use the current file path from sourceMapper to find the lifecycle directory
|
|
2062
2051
|
// If sourceMapper doesn't have a current file, use thisFile which should be the file being run
|
|
2063
2052
|
let currentFilePath = this.sourceMapper.currentFilePath || this.thisFile;
|
|
2064
|
-
|
|
2065
|
-
this.emitter.emit(events.log.log, ``);
|
|
2066
|
-
this.emitter.emit(events.log.log, "Running lifecycle: " + lifecycleName);
|
|
2067
|
-
|
|
2068
|
-
// If we still don't have a currentFilePath, fall back to the default testdriver directory
|
|
2069
|
-
if (!currentFilePath) {
|
|
2070
|
-
currentFilePath = path.join(
|
|
2071
|
-
this.workingDir,
|
|
2072
|
-
"testdriver",
|
|
2073
|
-
"testdriver.yaml",
|
|
2074
|
-
);
|
|
2075
|
-
console.log("No currentFilePath found, using fallback:", currentFilePath);
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
2053
|
// Ensure we have an absolute path
|
|
2079
2054
|
if (currentFilePath && !path.isAbsolute(currentFilePath)) {
|
|
2080
2055
|
currentFilePath = path.resolve(this.workingDir, currentFilePath);
|
|
@@ -2111,9 +2086,6 @@ Please check your network connection, TD_API_KEY, or the service status.`,
|
|
|
2111
2086
|
}
|
|
2112
2087
|
}
|
|
2113
2088
|
}
|
|
2114
|
-
|
|
2115
|
-
this.emitter.emit(events.log.log, lifecycleFile);
|
|
2116
|
-
|
|
2117
2089
|
if (lifecycleFile) {
|
|
2118
2090
|
// Store current source mapping state before running lifecycle file
|
|
2119
2091
|
const previousContext = this.sourceMapper.saveContext();
|
|
@@ -2183,7 +2155,7 @@ Please check your network connection, TD_API_KEY, or the service status.`,
|
|
|
2183
2155
|
}
|
|
2184
2156
|
|
|
2185
2157
|
// Move environment setup and special handling here
|
|
2186
|
-
if (["edit", "run"
|
|
2158
|
+
if (["edit", "run"].includes(commandName)) {
|
|
2187
2159
|
await this.buildEnv(options);
|
|
2188
2160
|
}
|
|
2189
2161
|
|
package/agent/interface.js
CHANGED
|
@@ -55,10 +55,6 @@ function createCommandDefinitions(agent) {
|
|
|
55
55
|
"sandbox-instance": Flags.string({
|
|
56
56
|
description: "Specify EC2 instance type for sandbox (e.g., i3.metal)",
|
|
57
57
|
}),
|
|
58
|
-
ip: Flags.string({
|
|
59
|
-
description:
|
|
60
|
-
"Connect directly to a sandbox at the specified IP address",
|
|
61
|
-
}),
|
|
62
58
|
summary: Flags.string({
|
|
63
59
|
description: "Specify output file for summarize results",
|
|
64
60
|
}),
|
|
@@ -72,6 +68,12 @@ function createCommandDefinitions(agent) {
|
|
|
72
68
|
const file = normalizeFilePath(args.file);
|
|
73
69
|
const testStartTime = Date.now();
|
|
74
70
|
|
|
71
|
+
// Emit test start event for the entire test execution
|
|
72
|
+
agent.emitter.emit(events.test.start, {
|
|
73
|
+
filePath: file,
|
|
74
|
+
timestamp: testStartTime,
|
|
75
|
+
});
|
|
76
|
+
|
|
75
77
|
try {
|
|
76
78
|
await agent.runLifecycle("prerun");
|
|
77
79
|
// When run() is called through run.js CLI command, shouldExit should be true
|
|
@@ -133,10 +135,6 @@ function createCommandDefinitions(agent) {
|
|
|
133
135
|
"sandbox-instance": Flags.string({
|
|
134
136
|
description: "Specify EC2 instance type for sandbox (e.g., i3.metal)",
|
|
135
137
|
}),
|
|
136
|
-
ip: Flags.string({
|
|
137
|
-
description:
|
|
138
|
-
"Connect directly to a sandbox at the specified IP address",
|
|
139
|
-
}),
|
|
140
138
|
summary: Flags.string({
|
|
141
139
|
description: "Specify output file for summarize results",
|
|
142
140
|
}),
|
|
@@ -204,41 +202,6 @@ function createCommandDefinitions(agent) {
|
|
|
204
202
|
console.log(`TestDriver.ai v${packageJson.version}`);
|
|
205
203
|
},
|
|
206
204
|
},
|
|
207
|
-
|
|
208
|
-
generate: {
|
|
209
|
-
description: "Generate test files based on current screen state",
|
|
210
|
-
args: {
|
|
211
|
-
prompt: Args.string({
|
|
212
|
-
description: "Multi-line text prompt describing what to generate",
|
|
213
|
-
required: false,
|
|
214
|
-
}),
|
|
215
|
-
},
|
|
216
|
-
flags: {
|
|
217
|
-
count: Flags.integer({
|
|
218
|
-
description: "Number of test files to generate",
|
|
219
|
-
default: 3,
|
|
220
|
-
}),
|
|
221
|
-
headless: Flags.boolean({
|
|
222
|
-
description: "Run in headless mode (no GUI)",
|
|
223
|
-
default: false,
|
|
224
|
-
}),
|
|
225
|
-
new: Flags.boolean({
|
|
226
|
-
description:
|
|
227
|
-
"Create a new sandbox instead of reconnecting to an existing one",
|
|
228
|
-
default: false,
|
|
229
|
-
}),
|
|
230
|
-
"sandbox-ami": Flags.string({
|
|
231
|
-
description: "Specify AMI ID for sandbox instance (e.g., ami-1234)",
|
|
232
|
-
}),
|
|
233
|
-
"sandbox-instance": Flags.string({
|
|
234
|
-
description: "Specify EC2 instance type for sandbox (e.g., i3.metal)",
|
|
235
|
-
}),
|
|
236
|
-
},
|
|
237
|
-
handler: async (args, flags) => {
|
|
238
|
-
// Call generate with the count and prompt
|
|
239
|
-
await agent.generate(flags.count || 3, args.prompt);
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
205
|
};
|
|
243
206
|
}
|
|
244
207
|
|
package/agent/lib/censorship.js
CHANGED
|
@@ -38,23 +38,18 @@ const censorSensitiveData = (message) => {
|
|
|
38
38
|
|
|
39
39
|
// Function to censor sensitive data in any value (recursive for objects/arrays)
|
|
40
40
|
const censorSensitiveDataDeep = (value) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
result[key] = censorSensitiveDataDeep(val);
|
|
50
|
-
}
|
|
51
|
-
return result;
|
|
41
|
+
if (typeof value === "string") {
|
|
42
|
+
return censorSensitiveData(value);
|
|
43
|
+
} else if (Array.isArray(value)) {
|
|
44
|
+
return value.map(censorSensitiveDataDeep);
|
|
45
|
+
} else if (value && typeof value === "object") {
|
|
46
|
+
const result = {};
|
|
47
|
+
for (const [key, val] of Object.entries(value)) {
|
|
48
|
+
result[key] = censorSensitiveDataDeep(val);
|
|
52
49
|
}
|
|
53
|
-
return
|
|
54
|
-
} catch {
|
|
55
|
-
// If we hit any error (like circular reference), just return a safe placeholder
|
|
56
|
-
return "[Object]";
|
|
50
|
+
return result;
|
|
57
51
|
}
|
|
52
|
+
return value;
|
|
58
53
|
};
|
|
59
54
|
|
|
60
55
|
// Function to update interpolation variables (for runtime updates)
|