testdriverai 7.1.0 → 7.1.1
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/.env.example +2 -0
- package/.github/workflows/linux-tests.yml +28 -0
- package/agent/index.js +18 -45
- package/agent/interface.js +13 -2
- package/agent/lib/commands.js +1 -1
- package/agent/lib/redraw.js +1 -1
- package/agent/lib/sandbox.js +30 -2
- package/agent/lib/valid-version.js +2 -2
- package/debugger/index.html +1 -1
- package/docs/docs.json +86 -125
- package/docs/v6/getting-started/self-hosting.mdx +3 -2
- package/docs/v7/_drafts/agents.mdx +852 -0
- package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
- package/docs/v7/{guides → _drafts}/caching-selectors.mdx +125 -17
- package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
- package/docs/v7/_drafts/error-handling.mdx +501 -0
- package/docs/v7/_drafts/implementation-plan.mdx +994 -0
- package/docs/v7/_drafts/init-command.mdx +95 -0
- package/docs/v7/_drafts/optimal-sdk-design.mdx +1348 -0
- package/docs/v7/_drafts/plugin-migration.mdx +222 -0
- package/docs/v7/_drafts/prompt-cache.mdx +200 -0
- package/docs/{QUICK_START_TEST_RECORDING.md → v7/_drafts/quick-start-test-recording.mdx} +3 -3
- package/docs/v7/_drafts/sdk-logging.mdx +222 -0
- package/docs/v7/_drafts/sdk-migration.mdx +474 -0
- package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
- package/docs/v7/{guides → _drafts}/self-hosting.mdx +1 -1
- package/docs/v7/{guides → _drafts}/troubleshooting.mdx +2 -2
- package/docs/v7/{guides → _drafts}/vitest-plugin.mdx +4 -4
- package/docs/v7/api/{ai.mdx → act.mdx} +24 -24
- package/docs/v7/api/client.mdx +1 -1
- package/docs/v7/api/dashcam.mdx +2 -2
- package/docs/v7/api/elements.mdx +143 -41
- package/docs/v7/api/find.mdx +258 -0
- package/docs/v7/api/type.mdx +51 -7
- package/docs/v7/features/ai-native.mdx +427 -0
- package/docs/v7/features/easy-to-write.mdx +351 -0
- package/docs/v7/features/enterprise.mdx +540 -0
- package/docs/v7/features/fast.mdx +424 -0
- package/docs/v7/features/observable.mdx +623 -0
- package/docs/v7/features/powerful.mdx +531 -0
- package/docs/v7/features/scalable.mdx +417 -0
- package/docs/v7/features/stable.mdx +514 -0
- package/docs/v7/getting-started/configuration.mdx +1 -1
- package/docs/v7/getting-started/generating-tests.mdx +525 -0
- package/docs/v7/getting-started/installation.mdx +486 -0
- package/docs/v7/getting-started/quickstart.mdx +51 -5
- package/docs/v7/getting-started/running-and-debugging.mdx +511 -0
- package/docs/v7/getting-started/setting-up-in-ci.mdx +612 -0
- package/docs/v7/getting-started/writing-tests.mdx +535 -0
- package/docs/v7/overview/what-is-testdriver.mdx +398 -0
- package/docs/v7/playwright.mdx +3 -3
- package/docs/v7/presets/chrome.mdx +16 -0
- package/docs/v7/presets/electron.mdx +18 -0
- package/docs/v7/presets/vscode.mdx +19 -0
- package/examples/run-tests-with-recording.sh +70 -0
- package/examples/screenshot-example.js +63 -0
- package/examples/sdk-awesome-logs-demo.js +177 -0
- package/examples/sdk-cache-thresholds.js +96 -0
- package/examples/sdk-element-properties.js +155 -0
- package/examples/sdk-simple-example.js +65 -0
- package/examples/test-recording-example.test.js +166 -0
- package/interfaces/cli/commands/init.js +358 -0
- package/interfaces/vitest-plugin.mjs +214 -10
- package/{src → lib}/core/Dashcam.js +41 -4
- package/{src → lib}/vitest/hooks.mjs +118 -100
- package/lib/vitest/setup.mjs +44 -0
- package/package.json +9 -10
- package/sdk.d.ts +15 -2
- package/sdk.js +70 -18
- package/{self-hosted.yml → setup/aws/self-hosted.yml} +1 -1
- package/{testdriver/acceptance-sdk → test/manual}/test-console-logs.test.mjs +1 -1
- package/test/manual/test-find-api.js +73 -0
- package/test/manual/test-init.sh +54 -0
- package/test/manual/test-prompt-cache.js +96 -0
- package/test/manual/test-provision-auth.mjs +22 -0
- package/test/manual/test-sandbox-render.js +28 -0
- package/test/manual/test-sdk-methods.js +15 -0
- package/test/manual/test-sdk-refactor.js +53 -0
- package/test/manual/test-stack-trace.mjs +57 -0
- package/test/testdriver/assert.test.mjs +41 -0
- package/{testdriver/acceptance-sdk → test/testdriver}/auto-cache-key-demo.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/drag-and-drop.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/element-not-found.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/exec-js.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/exec-output.test.mjs +3 -3
- package/{testdriver/acceptance-sdk → test/testdriver}/exec-pwsh.test.mjs +3 -3
- package/{testdriver/acceptance-sdk → test/testdriver}/focus-window.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/formatted-logging.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/hover-image.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/hover-text-with-description.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/hover-text.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/match-image.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/press-keys.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/prompt.test.mjs +2 -2
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll-keyboard.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-image.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-text.test.mjs +1 -1
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll.test.mjs +1 -1
- package/{src/vitest/lifecycle.mjs → test/testdriver/setup/lifecycleHelpers.mjs} +84 -99
- package/test/testdriver/setup/testHelpers.mjs +653 -0
- package/{testdriver/acceptance-sdk → test/testdriver}/type.test.mjs +1 -1
- package/vitest.config.mjs +11 -57
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/acceptance-linux.yml +0 -75
- package/.github/workflows/acceptance-sdk-tests.yml +0 -133
- package/.github/workflows/acceptance-tests.yml +0 -130
- package/.github/workflows/lint.yml +0 -27
- package/.github/workflows/publish-canary.yml +0 -40
- package/.github/workflows/publish-latest.yml +0 -61
- package/.github/workflows/test-install.yml +0 -29
- package/.vscode/extensions.json +0 -3
- package/.vscode/launch.json +0 -22
- package/.vscode/settings.json +0 -14
- package/AGENTS.md +0 -550
- package/CODEOWNERS +0 -2
- package/_testdriver/acceptance/assert.yaml +0 -7
- package/_testdriver/acceptance/dashcam.yaml +0 -9
- package/_testdriver/acceptance/drag-and-drop.yaml +0 -49
- package/_testdriver/acceptance/embed.yaml +0 -9
- package/_testdriver/acceptance/exec-js.yaml +0 -29
- package/_testdriver/acceptance/exec-output.yaml +0 -43
- package/_testdriver/acceptance/exec-shell.yaml +0 -40
- package/_testdriver/acceptance/focus-window.yaml +0 -16
- package/_testdriver/acceptance/hover-image.yaml +0 -18
- package/_testdriver/acceptance/hover-text-with-description.yaml +0 -29
- package/_testdriver/acceptance/hover-text.yaml +0 -14
- package/_testdriver/acceptance/if-else.yaml +0 -31
- package/_testdriver/acceptance/match-image.yaml +0 -15
- package/_testdriver/acceptance/press-keys.yaml +0 -35
- package/_testdriver/acceptance/prompt.yaml +0 -11
- package/_testdriver/acceptance/remember.yaml +0 -27
- package/_testdriver/acceptance/screenshots/cart.png +0 -0
- package/_testdriver/acceptance/scroll-keyboard.yaml +0 -34
- package/_testdriver/acceptance/scroll-until-image.yaml +0 -26
- package/_testdriver/acceptance/scroll-until-text.yaml +0 -20
- package/_testdriver/acceptance/scroll.yaml +0 -33
- package/_testdriver/acceptance/snippets/login.yaml +0 -29
- package/_testdriver/acceptance/snippets/match-cart.yaml +0 -8
- package/_testdriver/acceptance/type.yaml +0 -29
- package/_testdriver/behavior/failure.yaml +0 -7
- package/_testdriver/behavior/hover-text.yaml +0 -13
- package/_testdriver/behavior/lifecycle/postrun.yaml +0 -10
- package/_testdriver/behavior/lifecycle/prerun.yaml +0 -8
- package/_testdriver/behavior/lifecycle/provision.yaml +0 -8
- package/_testdriver/behavior/secrets.yaml +0 -7
- package/_testdriver/edge-cases/dashcam-chrome.yaml +0 -8
- package/_testdriver/edge-cases/exec-pwsh-multiline.yaml +0 -10
- 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/prompt-in-middle.yaml +0 -23
- package/_testdriver/edge-cases/prompt-nested.yaml +0 -7
- package/_testdriver/edge-cases/success-test.yaml +0 -9
- package/_testdriver/examples/android/example.yaml +0 -12
- package/_testdriver/examples/android/lifecycle/postrun.yaml +0 -11
- package/_testdriver/examples/android/lifecycle/provision.yaml +0 -47
- package/_testdriver/examples/android/readme.md +0 -7
- package/_testdriver/examples/chrome-extension/lifecycle/provision.yaml +0 -74
- package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
- package/_testdriver/examples/desktop/lifecycle/provision.yaml +0 -64
- package/_testdriver/examples/vscode-extension/lifecycle/provision.yaml +0 -73
- package/_testdriver/examples/web/lifecycle/postrun.yaml +0 -7
- package/_testdriver/examples/web/lifecycle/prerun.yaml +0 -22
- package/_testdriver/lifecycle/postrun.yaml +0 -8
- package/_testdriver/lifecycle/prerun.yaml +0 -15
- package/_testdriver/lifecycle/provision.yaml +0 -25
- package/docs/v7/guides/ci-cd/azure.mdx +0 -587
- package/docs/v7/guides/ci-cd/circleci.mdx +0 -523
- package/docs/v7/guides/ci-cd/github-actions.mdx +0 -457
- package/docs/v7/guides/ci-cd/gitlab.mdx +0 -498
- package/docs/v7/guides/ci-cd/jenkins.mdx +0 -664
- package/docs/v7/guides/ci-cd/travis.mdx +0 -438
- package/scripts/view-test-results.mjs +0 -96
- package/src/vitest/extended.mjs +0 -108
- package/src/vitest/index.mjs +0 -64
- package/src/vitest/utils.mjs +0 -150
- package/styles/.vale-config/2-MDX.ini +0 -5
- package/styles/Microsoft/AMPM.yml +0 -9
- package/styles/Microsoft/Accessibility.yml +0 -30
- package/styles/Microsoft/Acronyms.yml +0 -64
- package/styles/Microsoft/Adverbs.yml +0 -272
- package/styles/Microsoft/Auto.yml +0 -11
- package/styles/Microsoft/Avoid.yml +0 -14
- package/styles/Microsoft/Contractions.yml +0 -50
- package/styles/Microsoft/Dashes.yml +0 -13
- package/styles/Microsoft/DateFormat.yml +0 -8
- package/styles/Microsoft/DateNumbers.yml +0 -40
- package/styles/Microsoft/DateOrder.yml +0 -8
- package/styles/Microsoft/Ellipses.yml +0 -9
- package/styles/Microsoft/FirstPerson.yml +0 -16
- package/styles/Microsoft/Foreign.yml +0 -13
- package/styles/Microsoft/Gender.yml +0 -8
- package/styles/Microsoft/GenderBias.yml +0 -42
- package/styles/Microsoft/GeneralURL.yml +0 -11
- package/styles/Microsoft/HeadingAcronyms.yml +0 -7
- package/styles/Microsoft/HeadingColons.yml +0 -8
- package/styles/Microsoft/HeadingPunctuation.yml +0 -13
- package/styles/Microsoft/Headings.yml +0 -28
- package/styles/Microsoft/Hyphens.yml +0 -14
- package/styles/Microsoft/Negative.yml +0 -13
- package/styles/Microsoft/Ordinal.yml +0 -13
- package/styles/Microsoft/OxfordComma.yml +0 -8
- package/styles/Microsoft/Passive.yml +0 -183
- package/styles/Microsoft/Percentages.yml +0 -7
- package/styles/Microsoft/Plurals.yml +0 -7
- package/styles/Microsoft/Quotes.yml +0 -7
- package/styles/Microsoft/RangeTime.yml +0 -13
- package/styles/Microsoft/Semicolon.yml +0 -8
- package/styles/Microsoft/SentenceLength.yml +0 -6
- package/styles/Microsoft/Spacing.yml +0 -8
- package/styles/Microsoft/Suspended.yml +0 -7
- package/styles/Microsoft/Terms.yml +0 -42
- package/styles/Microsoft/URLFormat.yml +0 -9
- package/styles/Microsoft/Units.yml +0 -16
- package/styles/Microsoft/Vocab.yml +0 -25
- package/styles/Microsoft/We.yml +0 -11
- package/styles/Microsoft/Wordiness.yml +0 -127
- package/styles/Microsoft/meta.json +0 -4
- package/styles/alex/Ablist.yml +0 -274
- package/styles/alex/Condescending.yml +0 -16
- package/styles/alex/Gendered.yml +0 -110
- package/styles/alex/LGBTQ.yml +0 -55
- package/styles/alex/OCD.yml +0 -10
- package/styles/alex/Press.yml +0 -12
- package/styles/alex/ProfanityLikely.yml +0 -1289
- package/styles/alex/ProfanityMaybe.yml +0 -282
- package/styles/alex/ProfanityUnlikely.yml +0 -251
- package/styles/alex/README.md +0 -27
- package/styles/alex/Race.yml +0 -85
- package/styles/alex/Suicide.yml +0 -26
- package/styles/alex/meta.json +0 -4
- package/styles/config/vocabularies/Docs/accept.txt +0 -47
- package/styles/config/vocabularies/Docs/reject.txt +0 -4
- package/styles/proselint/Airlinese.yml +0 -8
- package/styles/proselint/AnimalLabels.yml +0 -48
- package/styles/proselint/Annotations.yml +0 -9
- package/styles/proselint/Apologizing.yml +0 -8
- package/styles/proselint/Archaisms.yml +0 -52
- package/styles/proselint/But.yml +0 -8
- package/styles/proselint/Cliches.yml +0 -782
- package/styles/proselint/CorporateSpeak.yml +0 -30
- package/styles/proselint/Currency.yml +0 -5
- package/styles/proselint/Cursing.yml +0 -15
- package/styles/proselint/DateCase.yml +0 -7
- package/styles/proselint/DateMidnight.yml +0 -7
- package/styles/proselint/DateRedundancy.yml +0 -10
- package/styles/proselint/DateSpacing.yml +0 -7
- package/styles/proselint/DenizenLabels.yml +0 -52
- package/styles/proselint/Diacritical.yml +0 -95
- package/styles/proselint/GenderBias.yml +0 -45
- package/styles/proselint/GroupTerms.yml +0 -39
- package/styles/proselint/Hedging.yml +0 -8
- package/styles/proselint/Hyperbole.yml +0 -6
- package/styles/proselint/Jargon.yml +0 -11
- package/styles/proselint/LGBTOffensive.yml +0 -13
- package/styles/proselint/LGBTTerms.yml +0 -15
- package/styles/proselint/Malapropisms.yml +0 -8
- package/styles/proselint/Needless.yml +0 -358
- package/styles/proselint/Nonwords.yml +0 -38
- package/styles/proselint/Oxymorons.yml +0 -22
- package/styles/proselint/P-Value.yml +0 -6
- package/styles/proselint/RASSyndrome.yml +0 -30
- package/styles/proselint/README.md +0 -12
- package/styles/proselint/Skunked.yml +0 -13
- package/styles/proselint/Spelling.yml +0 -17
- package/styles/proselint/Typography.yml +0 -11
- package/styles/proselint/Uncomparables.yml +0 -50
- package/styles/proselint/Very.yml +0 -6
- package/styles/proselint/meta.json +0 -15
- package/styles/write-good/Cliches.yml +0 -702
- package/styles/write-good/E-Prime.yml +0 -32
- package/styles/write-good/Illusions.yml +0 -11
- package/styles/write-good/Passive.yml +0 -183
- package/styles/write-good/README.md +0 -27
- package/styles/write-good/So.yml +0 -5
- package/styles/write-good/ThereIs.yml +0 -6
- package/styles/write-good/TooWordy.yml +0 -221
- package/styles/write-good/Weasel.yml +0 -29
- package/styles/write-good/meta.json +0 -4
- package/test/dashcam.test.js +0 -137
- package/test/mcp-example-test.yaml +0 -27
- package/test/test_parser.js +0 -47
- package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +0 -61
- package/testdriver/acceptance-sdk/README.md +0 -128
- package/testdriver/acceptance-sdk/TEST_REPORTING.md +0 -245
- package/testdriver/acceptance-sdk/assert.test.mjs +0 -26
- package/testdriver/acceptance-sdk/hooks-example.test.mjs +0 -38
- package/testdriver/acceptance-sdk/presets-example.test.mjs +0 -87
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +0 -420
- package/testdriver/acceptance-sdk/sully-ai.test.mjs +0 -234
- package/testdriver/acceptance-sdk/type-checking-demo.js +0 -49
- package/vale.ini +0 -18
- package/vitest.config.example.js +0 -19
- package/vitest.config.mjs.bak +0 -44
- /package/docs/{ARCHITECTURE.md → v7/_drafts/architecture.mdx} +0 -0
- /package/docs/{AWESOME_LOGS_QUICK_REF.md → v7/_drafts/awesome-logs-quick-ref.mdx} +0 -0
- /package/docs/v7/{guides → _drafts}/best-practices.mdx +0 -0
- /package/docs/v7/{guides → _drafts}/caching-ai.mdx +0 -0
- /package/docs/v7/{guides → _drafts}/caching.mdx +0 -0
- /package/docs/{MIGRATION.md → v7/_drafts/cli-to-sdk-migration.mdx} +0 -0
- /package/{CONTRIBUTING.md → docs/v7/_drafts/contributing.mdx} +0 -0
- /package/docs/v7/{progressive-apis/CORE.md → _drafts/core.mdx} +0 -0
- /package/docs/v7/{guides → _drafts}/debugging.mdx +0 -0
- /package/docs/v7/{guides → _drafts}/faq.mdx +0 -0
- /package/docs/v7/{progressive-apis/HOOKS.md → _drafts/hooks.mdx} +0 -0
- /package/docs/v7/{guides → _drafts}/migration.mdx +0 -0
- /package/docs/v7/{guides → _drafts}/performance.mdx +0 -0
- /package/docs/{PRESETS.md → v7/_drafts/presets.mdx} +0 -0
- /package/docs/v7/{progressive-apis/PROGRESSIVE_DISCLOSURE.md → _drafts/progressive-disclosure.mdx} +0 -0
- /package/docs/v7/{progressive-apis/PROVISION.md → _drafts/provision.mdx} +0 -0
- /package/docs/{SDK_AWESOME_LOGS.md → v7/_drafts/sdk-awesome-logs.mdx} +0 -0
- /package/docs/{sdk-browser-rendering.md → v7/_drafts/sdk-browser-rendering.mdx} +0 -0
- /package/docs/{TEST_RECORDING.md → v7/_drafts/test-recording.mdx} +0 -0
- /package/docs/v7/{guides → _drafts}/vitest.mdx +0 -0
- /package/docs/v7/{README.md → overview/readme.mdx} +0 -0
- /package/{src → lib}/core/index.d.ts +0 -0
- /package/{src → lib}/core/index.js +0 -0
- /package/{src → lib}/presets/index.mjs +0 -0
- /package/{src → lib}/vitest/hooks.d.ts +0 -0
- /package/{debug-locate-response.js → test/manual/debug-locate-response.js} +0 -0
- /package/{verify-element-api.js → test/manual/verify-element-api.js} +0 -0
- /package/{verify-types.js → test/manual/verify-types.js} +0 -0
- /package/{testdriver/acceptance-sdk → test/testdriver}/chrome-extension.test.mjs +0 -0
- /package/{testdriver/acceptance-sdk → test/testdriver}/setup/globalTeardown.mjs +0 -0
- /package/{testdriver/acceptance-sdk → test/testdriver}/setup/vitestSetup.mjs +0 -0
|
@@ -17,6 +17,7 @@ class Dashcam {
|
|
|
17
17
|
* @param {string} [options.apiKey] - Dashcam API key
|
|
18
18
|
* @param {boolean} [options.autoStart=false] - Auto-start recording
|
|
19
19
|
* @param {Array} [options.logs=[]] - Log configurations to add
|
|
20
|
+
* @param {string} [options.title] - Recording title (defaults to generated title)
|
|
20
21
|
*/
|
|
21
22
|
constructor(client, options = {}) {
|
|
22
23
|
if (!client) {
|
|
@@ -31,6 +32,30 @@ class Dashcam {
|
|
|
31
32
|
this.recording = false;
|
|
32
33
|
this._authenticated = false;
|
|
33
34
|
this.startTime = null; // Track when dashcam recording started
|
|
35
|
+
this.title = options.title || this._generateDefaultTitle();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate a default title for the recording
|
|
40
|
+
* Uses test context if available, otherwise falls back to timestamp
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
_generateDefaultTitle() {
|
|
44
|
+
// Check for Vitest context
|
|
45
|
+
if (this.client.__vitestContext) {
|
|
46
|
+
const task = this.client.__vitestContext;
|
|
47
|
+
const testName = task.name || 'Test';
|
|
48
|
+
const fileName = task.file?.name || task.file?.filepath;
|
|
49
|
+
if (fileName) {
|
|
50
|
+
const baseName = fileName.split('/').pop().replace(/\.(test|spec)\.(js|mjs|ts|tsx)$/, '');
|
|
51
|
+
return `${baseName} - ${testName}`;
|
|
52
|
+
}
|
|
53
|
+
return testName;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Fallback to timestamp
|
|
57
|
+
const now = new Date();
|
|
58
|
+
return `Recording ${now.toISOString().replace(/T/, ' ').replace(/\..+/, '')}`;
|
|
34
59
|
}
|
|
35
60
|
|
|
36
61
|
/**
|
|
@@ -46,7 +71,7 @@ class Dashcam {
|
|
|
46
71
|
* @private
|
|
47
72
|
*/
|
|
48
73
|
_getApiRoot() {
|
|
49
|
-
return this.client.config?.TD_API_ROOT || '
|
|
74
|
+
return this.client.config?.TD_API_ROOT || 'https://testdriver-api.onrender.com';
|
|
50
75
|
}
|
|
51
76
|
|
|
52
77
|
/**
|
|
@@ -291,10 +316,11 @@ class Dashcam {
|
|
|
291
316
|
|
|
292
317
|
// Start dashcam record and redirect output with TD_API_ROOT
|
|
293
318
|
const outputFile = 'C:\\Users\\testdriver\\.dashcam-cli\\dashcam-start.log';
|
|
319
|
+
const titleArg = this.title ? ` --title="${this.title.replace(/"/g, '\"')}"` : '';
|
|
294
320
|
const startScript = `
|
|
295
321
|
try {
|
|
296
322
|
$env:TD_API_ROOT="${apiRoot}"
|
|
297
|
-
$process = Start-Process "cmd.exe" -ArgumentList "/c", "${dashcamPath} record > ${outputFile} 2>&1" -PassThru
|
|
323
|
+
$process = Start-Process "cmd.exe" -ArgumentList "/c", "${dashcamPath} record${titleArg} > ${outputFile} 2>&1" -PassThru
|
|
298
324
|
Write-Output "Process started with PID: $($process.Id)"
|
|
299
325
|
Start-Sleep -Seconds 2
|
|
300
326
|
if ($process.HasExited) {
|
|
@@ -327,7 +353,8 @@ class Dashcam {
|
|
|
327
353
|
} else {
|
|
328
354
|
// Linux/Mac with TD_API_ROOT
|
|
329
355
|
this._log('info', 'Starting dashcam recording on Linux/Mac...');
|
|
330
|
-
|
|
356
|
+
const titleArg = this.title ? ` --title="${this.title.replace(/"/g, '\"')}"` : '';
|
|
357
|
+
await this.client.exec(shell, `TD_API_ROOT="${apiRoot}" dashcam record${titleArg} >/dev/null 2>&1 &`);
|
|
331
358
|
this._log('info', 'Dashcam recording started');
|
|
332
359
|
}
|
|
333
360
|
|
|
@@ -337,7 +364,7 @@ class Dashcam {
|
|
|
337
364
|
// Update the session with dashcam start time for interaction timestamp synchronization
|
|
338
365
|
if (this.client && this.client.agent && this.client.agent.session) {
|
|
339
366
|
try {
|
|
340
|
-
const apiRoot = this.apiRoot || process.env.TD_API_ROOT || 'https://
|
|
367
|
+
const apiRoot = this.apiRoot || process.env.TD_API_ROOT || 'https://console.testdriver.ai';
|
|
341
368
|
const response = await fetch(`${apiRoot}/api/v7.0.0/testdriver/session/${this.client.agent.session}/update-dashcam-time`, {
|
|
342
369
|
method: 'POST',
|
|
343
370
|
headers: {
|
|
@@ -358,6 +385,16 @@ class Dashcam {
|
|
|
358
385
|
}
|
|
359
386
|
}
|
|
360
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Set the recording title
|
|
390
|
+
* This can be called before start() to customize the title
|
|
391
|
+
* @param {string} title - Custom recording title
|
|
392
|
+
*/
|
|
393
|
+
setTitle(title) {
|
|
394
|
+
this.title = title;
|
|
395
|
+
this._log('info', `Set dashcam recording title: ${title}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
361
398
|
/**
|
|
362
399
|
* Stop dashcam recording and retrieve replay URL
|
|
363
400
|
* @returns {Promise<string|null>} Replay URL if available
|
|
@@ -18,102 +18,106 @@
|
|
|
18
18
|
import fs from 'fs';
|
|
19
19
|
import os from 'os';
|
|
20
20
|
import path from 'path';
|
|
21
|
+
import { vi } from 'vitest';
|
|
21
22
|
import TestDriverSDK from '../../sdk.js';
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* Set up console spies using Vitest's vi.spyOn to intercept console logs
|
|
26
|
+
* and forward them to the sandbox for Dashcam visibility.
|
|
27
|
+
* This is test-isolated and doesn't cause conflicts with concurrent tests.
|
|
26
28
|
* @param {TestDriver} client - TestDriver client instance
|
|
27
29
|
* @param {string} taskId - Unique task identifier for this test
|
|
28
30
|
*/
|
|
29
|
-
function
|
|
30
|
-
// Store original console methods
|
|
31
|
-
const originalConsole = {
|
|
32
|
-
log: console.log,
|
|
33
|
-
error: console.error,
|
|
34
|
-
warn: console.warn,
|
|
35
|
-
info: console.info,
|
|
36
|
-
};
|
|
31
|
+
function setupConsoleSpy(client, taskId) {
|
|
37
32
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
:
|
|
33
|
+
// Debug logging for console spy setup
|
|
34
|
+
const debugConsoleSpy = process.env.TD_DEBUG_CONSOLE_SPY === 'true';
|
|
35
|
+
if (debugConsoleSpy) {
|
|
36
|
+
process.stdout.write(`[DEBUG setupConsoleSpy] taskId: ${taskId}\n`);
|
|
37
|
+
process.stdout.write(`[DEBUG setupConsoleSpy] client.sandbox exists: ${!!client.sandbox}\n`);
|
|
38
|
+
process.stdout.write(`[DEBUG setupConsoleSpy] client.sandbox?.instanceSocketConnected: ${client.sandbox?.instanceSocketConnected}\n`);
|
|
39
|
+
process.stdout.write(`[DEBUG setupConsoleSpy] client.sandbox?.send: ${typeof client.sandbox?.send}\n`);
|
|
40
|
+
}
|
|
42
41
|
|
|
43
|
-
//
|
|
44
|
-
|
|
42
|
+
// Track forwarding stats
|
|
43
|
+
let forwardedCount = 0;
|
|
44
|
+
let skippedCount = 0;
|
|
45
45
|
|
|
46
|
-
//
|
|
47
|
-
|
|
46
|
+
// Helper to forward logs to sandbox
|
|
47
|
+
const forwardToSandbox = (args) => {
|
|
48
|
+
const message = args
|
|
49
|
+
.map((arg) =>
|
|
50
|
+
typeof arg === "object"
|
|
51
|
+
? JSON.stringify(arg, null, 2)
|
|
52
|
+
: String(arg),
|
|
53
|
+
)
|
|
54
|
+
.join(" ");
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
// Send to sandbox for immediate visibility in dashcam
|
|
57
|
+
if (client.sandbox && client.sandbox.instanceSocketConnected) {
|
|
58
|
+
try {
|
|
59
|
+
client.sandbox.send({
|
|
60
|
+
type: "output",
|
|
61
|
+
output: Buffer.from(message, "utf8").toString("base64"),
|
|
62
|
+
});
|
|
63
|
+
forwardedCount++;
|
|
64
|
+
if (debugConsoleSpy && forwardedCount <= 3) {
|
|
65
|
+
process.stdout.write(`[DEBUG forwardToSandbox] Forwarded message #${forwardedCount}: "${message.substring(0, 50)}..."\n`);
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (debugConsoleSpy) {
|
|
69
|
+
process.stdout.write(`[DEBUG forwardToSandbox] Error sending: ${err.message}\n`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
skippedCount++;
|
|
74
|
+
if (debugConsoleSpy && skippedCount <= 3) {
|
|
75
|
+
process.stdout.write(`[DEBUG forwardToSandbox] SKIPPED (sandbox not connected): "${message.substring(0, 50)}..."\n`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
54
79
|
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
// Create spies for each console method
|
|
81
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
|
|
82
|
+
// Call through to original
|
|
83
|
+
logSpy.mock.calls; // Track calls
|
|
84
|
+
process.stdout.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
|
|
85
|
+
forwardToSandbox(args);
|
|
86
|
+
});
|
|
57
87
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
? JSON.stringify(arg, null, 2)
|
|
63
|
-
: String(arg),
|
|
64
|
-
)
|
|
65
|
-
.join(" ");
|
|
88
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
|
89
|
+
process.stderr.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
|
|
90
|
+
forwardToSandbox(args);
|
|
91
|
+
});
|
|
66
92
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
type: "output",
|
|
72
|
-
output: Buffer.from(message, "utf8").toString("base64"),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
};
|
|
93
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation((...args) => {
|
|
94
|
+
process.stderr.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
|
|
95
|
+
forwardToSandbox(args);
|
|
96
|
+
});
|
|
77
97
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
console.info = createInterceptor("info", originalConsole.info);
|
|
98
|
+
const infoSpy = vi.spyOn(console, 'info').mockImplementation((...args) => {
|
|
99
|
+
process.stdout.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
|
|
100
|
+
forwardToSandbox(args);
|
|
101
|
+
});
|
|
83
102
|
|
|
84
|
-
// Store
|
|
85
|
-
client.
|
|
86
|
-
taskId,
|
|
87
|
-
original: originalConsole,
|
|
88
|
-
};
|
|
103
|
+
// Store spies on client for cleanup
|
|
104
|
+
client._consoleSpies = { logSpy, errorSpy, warnSpy, infoSpy };
|
|
89
105
|
|
|
90
|
-
|
|
91
|
-
originalConsole.log(
|
|
92
|
-
`[useTestDriver] Console interceptor enabled for task: ${taskId}`,
|
|
93
|
-
);
|
|
106
|
+
console.log(`[testdriver] Console spy set up for task: ${taskId}`);
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
/**
|
|
97
|
-
*
|
|
110
|
+
* Clean up console spies and restore original console methods
|
|
98
111
|
* @param {TestDriver} client - TestDriver client instance
|
|
99
112
|
*/
|
|
100
|
-
function
|
|
101
|
-
if (client.
|
|
102
|
-
const {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
console.info = original.info;
|
|
109
|
-
|
|
110
|
-
// Use original console for cleanup message
|
|
111
|
-
original.log(
|
|
112
|
-
`[useTestDriver] Console interceptor removed for task: ${taskId}`,
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
// Clean up reference
|
|
116
|
-
delete client._consoleInterceptor;
|
|
113
|
+
function cleanupConsoleSpy(client) {
|
|
114
|
+
if (client._consoleSpies) {
|
|
115
|
+
const { logSpy, errorSpy, warnSpy, infoSpy } = client._consoleSpies;
|
|
116
|
+
logSpy.mockRestore();
|
|
117
|
+
errorSpy.mockRestore();
|
|
118
|
+
warnSpy.mockRestore();
|
|
119
|
+
infoSpy.mockRestore();
|
|
120
|
+
delete client._consoleSpies;
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
|
|
@@ -176,16 +180,27 @@ export function TestDriver(context, options = {}) {
|
|
|
176
180
|
|
|
177
181
|
// Auto-connect if enabled (default: true)
|
|
178
182
|
const autoConnect = config.autoConnect !== undefined ? config.autoConnect : true;
|
|
183
|
+
const debugConsoleSpy = process.env.TD_DEBUG_CONSOLE_SPY === 'true';
|
|
184
|
+
|
|
179
185
|
if (autoConnect) {
|
|
180
186
|
testdriver.__connectionPromise = (async () => {
|
|
181
|
-
try {
|
|
182
187
|
console.log('[testdriver] Connecting to sandbox...');
|
|
188
|
+
if (debugConsoleSpy) {
|
|
189
|
+
console.log('[DEBUG] Before auth - sandbox.instanceSocketConnected:', testdriver.sandbox?.instanceSocketConnected);
|
|
190
|
+
}
|
|
191
|
+
|
|
183
192
|
await testdriver.auth();
|
|
184
193
|
await testdriver.connect();
|
|
194
|
+
|
|
185
195
|
console.log('[testdriver] ✅ Connected to sandbox');
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
if (debugConsoleSpy) {
|
|
198
|
+
console.log('[DEBUG] After connect - sandbox.instanceSocketConnected:', testdriver.sandbox?.instanceSocketConnected);
|
|
199
|
+
console.log('[DEBUG] After connect - sandbox.send:', typeof testdriver.sandbox?.send);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Set up console spy using vi.spyOn (test-isolated)
|
|
203
|
+
setupConsoleSpy(testdriver, context.task.id);
|
|
189
204
|
|
|
190
205
|
// Create the log file on the remote machine
|
|
191
206
|
const shell = testdriver.os === "windows" ? "pwsh" : "sh";
|
|
@@ -202,23 +217,9 @@ export function TestDriver(context, options = {}) {
|
|
|
202
217
|
|
|
203
218
|
// Add automatic log tracking when dashcam starts
|
|
204
219
|
// Store original start method
|
|
205
|
-
|
|
206
|
-
testdriver.dashcam.
|
|
207
|
-
|
|
208
|
-
await originalDashcamStart();
|
|
209
|
-
|
|
210
|
-
// Add log file tracking after dashcam starts
|
|
211
|
-
try {
|
|
212
|
-
await testdriver.dashcam.addFileLog(logPath, "TestDriver Log");
|
|
213
|
-
console.log('[testdriver] ✅ Added log file to dashcam tracking');
|
|
214
|
-
} catch (error) {
|
|
215
|
-
console.warn('[testdriver] ⚠️ Failed to add log tracking:', error.message);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.error('[testdriver] Error during setup:', error);
|
|
220
|
-
throw error;
|
|
221
|
-
}
|
|
220
|
+
|
|
221
|
+
await testdriver.dashcam.addFileLog(logPath, "TestDriver Log");
|
|
222
|
+
|
|
222
223
|
})();
|
|
223
224
|
}
|
|
224
225
|
|
|
@@ -227,10 +228,18 @@ export function TestDriver(context, options = {}) {
|
|
|
227
228
|
const cleanup = async () => {
|
|
228
229
|
console.log('[testdriver] Cleaning up TestDriver client...');
|
|
229
230
|
try {
|
|
230
|
-
// Stop dashcam if it was started
|
|
231
|
+
// Stop dashcam if it was started - with timeout to prevent hanging
|
|
231
232
|
if (testdriver._dashcam && testdriver._dashcam.recording) {
|
|
232
233
|
try {
|
|
233
|
-
|
|
234
|
+
// Add a timeout wrapper to prevent dashcam.stop from hanging indefinitely
|
|
235
|
+
const stopWithTimeout = Promise.race([
|
|
236
|
+
testdriver.dashcam.stop(),
|
|
237
|
+
new Promise((_, reject) =>
|
|
238
|
+
setTimeout(() => reject(new Error('Dashcam stop timed out after 30s')), 30000)
|
|
239
|
+
)
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
const dashcamUrl = await stopWithTimeout;
|
|
234
243
|
console.log('🎥 Dashcam URL:', dashcamUrl);
|
|
235
244
|
|
|
236
245
|
// Write dashcam URL to file for the reporter (cross-process communication)
|
|
@@ -272,17 +281,26 @@ export function TestDriver(context, options = {}) {
|
|
|
272
281
|
if (error.name === 'NotFoundError' || error.responseData?.error === 'NotFoundError') {
|
|
273
282
|
console.log(' ℹ️ Sandbox session already terminated - dashcam stop skipped');
|
|
274
283
|
}
|
|
284
|
+
// Mark as not recording to prevent retries
|
|
285
|
+
if (testdriver._dashcam) {
|
|
286
|
+
testdriver._dashcam.recording = false;
|
|
287
|
+
}
|
|
275
288
|
}
|
|
276
289
|
}
|
|
277
290
|
|
|
278
|
-
//
|
|
279
|
-
|
|
291
|
+
// Clean up console spies
|
|
292
|
+
cleanupConsoleSpy(testdriver);
|
|
280
293
|
|
|
281
294
|
// Wait for connection to finish if it was initiated
|
|
282
295
|
if (testdriver.__connectionPromise) {
|
|
283
296
|
await testdriver.__connectionPromise.catch(() => {}); // Ignore connection errors during cleanup
|
|
284
297
|
}
|
|
285
|
-
|
|
298
|
+
|
|
299
|
+
// Disconnect with timeout
|
|
300
|
+
await Promise.race([
|
|
301
|
+
testdriver.disconnect(),
|
|
302
|
+
new Promise((resolve) => setTimeout(resolve, 5000)) // 5s timeout for disconnect
|
|
303
|
+
]);
|
|
286
304
|
console.log('✅ Client disconnected');
|
|
287
305
|
} catch (error) {
|
|
288
306
|
console.error('Error disconnecting client:', error);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest Setup File for TestDriver
|
|
3
|
+
*
|
|
4
|
+
* This file is run by Vitest before each test file to set up
|
|
5
|
+
* the TestDriver plugin state and global helpers.
|
|
6
|
+
*
|
|
7
|
+
* Usage in vitest.config.mjs:
|
|
8
|
+
* ```js
|
|
9
|
+
* export default defineConfig({
|
|
10
|
+
* test: {
|
|
11
|
+
* setupFiles: ['testdriverai/vitest/setup'],
|
|
12
|
+
* },
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
clearDashcamUrls,
|
|
19
|
+
clearSuiteTestRun,
|
|
20
|
+
getDashcamUrl,
|
|
21
|
+
getPluginState,
|
|
22
|
+
getSuiteTestRun,
|
|
23
|
+
pluginState,
|
|
24
|
+
registerDashcamUrl,
|
|
25
|
+
setSuiteTestRun,
|
|
26
|
+
} from '../../interfaces/vitest-plugin.mjs';
|
|
27
|
+
|
|
28
|
+
// Set up global TestDriver plugin interface
|
|
29
|
+
// This allows tests and the SDK to communicate with the reporter
|
|
30
|
+
globalThis.__testdriverPlugin = {
|
|
31
|
+
state: pluginState,
|
|
32
|
+
registerDashcamUrl,
|
|
33
|
+
getDashcamUrl,
|
|
34
|
+
clearDashcamUrls,
|
|
35
|
+
getPluginState,
|
|
36
|
+
getSuiteTestRun,
|
|
37
|
+
setSuiteTestRun,
|
|
38
|
+
clearSuiteTestRun,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Log that setup is complete (only in debug mode)
|
|
42
|
+
if (process.env.TD_LOG_LEVEL?.toLowerCase() === 'debug') {
|
|
43
|
+
console.log('[TestDriver] Setup file initialized, global plugin interface available');
|
|
44
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testdriverai",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.1",
|
|
4
4
|
"description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
|
|
5
5
|
"main": "sdk.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./sdk.js",
|
|
8
|
-
"./core": "./
|
|
9
|
-
"./vitest": "./
|
|
10
|
-
"./vitest/hooks": "./src/vitest/hooks.mjs",
|
|
11
|
-
"./vitest/extended": "./src/vitest/extended.mjs",
|
|
12
|
-
"./vitest/lifecycle": "./src/vitest/lifecycle.mjs",
|
|
13
|
-
"./vitest/utils": "./src/vitest/utils.mjs",
|
|
8
|
+
"./core": "./lib/core/index.js",
|
|
9
|
+
"./vitest": "./interfaces/vitest-plugin.mjs",
|
|
14
10
|
"./vitest/plugin": "./interfaces/vitest-plugin.mjs",
|
|
15
|
-
"./
|
|
11
|
+
"./vitest/setup": "./lib/vitest/setup.mjs",
|
|
12
|
+
"./vitest/hooks": "./lib/vitest/hooks.mjs",
|
|
13
|
+
"./presets": "./lib/presets/index.mjs"
|
|
16
14
|
},
|
|
17
15
|
"bin": {
|
|
18
16
|
"testdriverai": "bin/testdriverai.js"
|
|
@@ -61,7 +59,7 @@
|
|
|
61
59
|
"chalk": "^4.1.2",
|
|
62
60
|
"cli-progress": "^3.12.0",
|
|
63
61
|
"diff": "^8.0.2",
|
|
64
|
-
"dotenv": "^16.
|
|
62
|
+
"dotenv": "^16.6.1",
|
|
65
63
|
"eventemitter2": "^6.4.9",
|
|
66
64
|
"jimp": "^0.22.12",
|
|
67
65
|
"js-yaml": "^4.1.0",
|
|
@@ -99,7 +97,8 @@
|
|
|
99
97
|
"mocha": "^10.8.2",
|
|
100
98
|
"node-addon-api": "^8.0.0",
|
|
101
99
|
"prettier": "3.3.3",
|
|
102
|
-
"
|
|
100
|
+
"testdriverai": "^6.1.11",
|
|
101
|
+
"vitest": "^4.0.15"
|
|
103
102
|
},
|
|
104
103
|
"optionalDependencies": {
|
|
105
104
|
"@esbuild/linux-x64": "^0.21.5"
|
package/sdk.d.ts
CHANGED
|
@@ -1014,13 +1014,26 @@ export default class TestDriverSDK {
|
|
|
1014
1014
|
*
|
|
1015
1015
|
* @example
|
|
1016
1016
|
* // Simple execution
|
|
1017
|
-
* await client.
|
|
1017
|
+
* await client.act('Click the submit button');
|
|
1018
1018
|
*
|
|
1019
1019
|
* @example
|
|
1020
1020
|
* // With validation loop
|
|
1021
|
-
* const result = await client.
|
|
1021
|
+
* const result = await client.act('Fill out the contact form', { validateAndLoop: true });
|
|
1022
1022
|
* console.log(result); // AI's final assessment
|
|
1023
1023
|
*/
|
|
1024
|
+
act(
|
|
1025
|
+
task: string,
|
|
1026
|
+
options?: { validateAndLoop?: boolean },
|
|
1027
|
+
): Promise<string | void>;
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* @deprecated Use act() instead
|
|
1031
|
+
* Execute a natural language task using AI
|
|
1032
|
+
*
|
|
1033
|
+
* @param task - Natural language description of what to do
|
|
1034
|
+
* @param options - Execution options
|
|
1035
|
+
* @returns Final AI response if validateAndLoop is true
|
|
1036
|
+
*/
|
|
1024
1037
|
ai(
|
|
1025
1038
|
task: string,
|
|
1026
1039
|
options?: { validateAndLoop?: boolean },
|