donobu 5.57.1 → 5.60.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/dist/cli/donobu-cli.js +4 -0
- package/dist/codegen/CodeGenerator.js +83 -38
- package/dist/codegen/TargetCodeGenerator.d.ts +54 -0
- package/dist/codegen/TargetCodeGenerator.js +21 -0
- package/dist/esm/cli/donobu-cli.js +4 -0
- package/dist/esm/codegen/CodeGenerator.js +83 -38
- package/dist/esm/codegen/TargetCodeGenerator.d.ts +54 -0
- package/dist/esm/codegen/TargetCodeGenerator.js +21 -0
- package/dist/esm/lib/ai/cache/assertCache.d.ts +14 -1
- package/dist/esm/lib/ai/cache/cache.d.ts +12 -4
- package/dist/esm/lib/ai/cache/cache.js +40 -24
- package/dist/esm/lib/ai/cache/cacheEntryBuilder.d.ts +3 -3
- package/dist/esm/lib/ai/cache/cacheEntryBuilder.js +4 -6
- package/dist/esm/lib/page/extendPage.js +2 -0
- package/dist/esm/lib/test/testExtension.js +7 -0
- package/dist/esm/lib/test/utils/selfHealing.js +14 -0
- package/dist/esm/main.d.ts +7 -1
- package/dist/esm/main.js +7 -1
- package/dist/esm/managers/DonobuFlowsManager.js +17 -4
- package/dist/esm/managers/DonobuStack.js +9 -0
- package/dist/esm/managers/TestsManager.js +5 -0
- package/dist/esm/managers/ToolManager.js +11 -0
- package/dist/esm/models/CreateDonobuFlow.js +1 -1
- package/dist/esm/models/RunConfig.js +1 -1
- package/dist/esm/models/ToolTemplateDataSource.d.ts +5 -0
- package/dist/esm/targets/TargetRuntimePlugin.d.ts +13 -0
- package/dist/lib/ai/cache/assertCache.d.ts +14 -1
- package/dist/lib/ai/cache/cache.d.ts +12 -4
- package/dist/lib/ai/cache/cache.js +40 -24
- package/dist/lib/ai/cache/cacheEntryBuilder.d.ts +3 -3
- package/dist/lib/ai/cache/cacheEntryBuilder.js +4 -6
- package/dist/lib/page/extendPage.js +2 -0
- package/dist/lib/test/testExtension.js +7 -0
- package/dist/lib/test/utils/selfHealing.js +14 -0
- package/dist/main.d.ts +7 -1
- package/dist/main.js +7 -1
- package/dist/managers/DonobuFlowsManager.js +17 -4
- package/dist/managers/DonobuStack.js +9 -0
- package/dist/managers/TestsManager.js +5 -0
- package/dist/managers/ToolManager.js +11 -0
- package/dist/models/CreateDonobuFlow.js +1 -1
- package/dist/models/RunConfig.js +1 -1
- package/dist/models/ToolTemplateDataSource.d.ts +5 -0
- package/dist/targets/TargetRuntimePlugin.d.ts +13 -0
- package/package.json +1 -1
|
@@ -53,15 +53,28 @@ export declare function buildAssertExecutor(steps: PlaywrightAssertionStep[]): A
|
|
|
53
53
|
* on the same domain always resolves to the same cached entry.
|
|
54
54
|
*/
|
|
55
55
|
export type AssertCacheKey = {
|
|
56
|
+
/**
|
|
57
|
+
* Target device discriminator; defaults to `'web'`. Plugin targets supply
|
|
58
|
+
* their own opaque value — core treats any non-`'web'` value as an opaque
|
|
59
|
+
* string.
|
|
60
|
+
*/
|
|
61
|
+
deviceType?: string;
|
|
56
62
|
pageUrl: string;
|
|
57
63
|
assertion: string;
|
|
58
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* A structured assertion step. Web entries store
|
|
67
|
+
* {@link PlaywrightAssertionStep}s, which core compiles into Playwright
|
|
68
|
+
* `expect` calls. Non-web (plugin-target) entries store their own step
|
|
69
|
+
* shapes — opaque to core; the owning plugin interprets them on replay.
|
|
70
|
+
*/
|
|
71
|
+
export type AssertStep = PlaywrightAssertionStep | Record<string, unknown>;
|
|
59
72
|
/**
|
|
60
73
|
* Serialised cache entry stored on disk. Contains structured assertion
|
|
61
74
|
* steps rather than raw code strings.
|
|
62
75
|
*/
|
|
63
76
|
export type AssertCacheEntry = AssertCacheKey & {
|
|
64
|
-
steps:
|
|
77
|
+
steps: AssertStep[];
|
|
65
78
|
};
|
|
66
79
|
/**
|
|
67
80
|
* Entry hydrated with an executable runner.
|
|
@@ -9,8 +9,12 @@ import type { AssertCacheEntry, AssertCacheEntryWithRunner, AssertCacheKey, Loca
|
|
|
9
9
|
* For mobile entries, `pageUrl` is the sentinel `"mobile://app"`.
|
|
10
10
|
*/
|
|
11
11
|
export type PageAiCacheKey = {
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Target device discriminator; defaults to `'web'`. Plugin targets supply
|
|
14
|
+
* their own opaque value (e.g. mobile `'android'`/`'ios'`, API `'api'`) — core
|
|
15
|
+
* treats any non-`'web'` value as an opaque string.
|
|
16
|
+
*/
|
|
17
|
+
deviceType?: string;
|
|
14
18
|
pageUrl: string;
|
|
15
19
|
instruction: string | null;
|
|
16
20
|
schema: Record<string, unknown> | null;
|
|
@@ -39,9 +43,13 @@ export type PageAiCacheContents = {
|
|
|
39
43
|
locators?: LocateCacheEntry[];
|
|
40
44
|
};
|
|
41
45
|
export type PageAiCacheExecutionContext = {
|
|
46
|
+
/** Legacy cutout for the built-in web target. */
|
|
42
47
|
page?: DonobuExtendedPage;
|
|
43
|
-
/**
|
|
44
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Plugin targets supply their own handle under a target-specific key,
|
|
50
|
+
* opaque to core; the plugin's cache replay casts to its concrete shape.
|
|
51
|
+
*/
|
|
52
|
+
[targetHandle: string]: unknown;
|
|
45
53
|
};
|
|
46
54
|
export type PageAiCacheExecutor = (context: PageAiCacheExecutionContext) => Promise<unknown>;
|
|
47
55
|
/**
|
|
@@ -108,15 +108,30 @@ const cloneEntryWithRunner = (entry) => ({
|
|
|
108
108
|
// ---------------------------------------------------------------------------
|
|
109
109
|
// Assert cache helpers
|
|
110
110
|
// ---------------------------------------------------------------------------
|
|
111
|
-
const assertCacheKeysMatch = (left, right) => left.
|
|
111
|
+
const assertCacheKeysMatch = (left, right) => (left.deviceType ?? 'web') === (right.deviceType ?? 'web') &&
|
|
112
|
+
left.pageUrl === right.pageUrl &&
|
|
113
|
+
left.assertion === right.assertion;
|
|
112
114
|
const materializeAssertEntry = (entry) => ({
|
|
113
115
|
...entry,
|
|
114
|
-
|
|
116
|
+
// Only web steps compile into Playwright `expect` calls. Non-web entries
|
|
117
|
+
// are interpreted by their owning plugin, which reads `steps` directly.
|
|
118
|
+
run: (entry.deviceType ?? 'web') === 'web'
|
|
119
|
+
? (0, assertCache_1.buildAssertExecutor)(entry.steps)
|
|
120
|
+
: async () => {
|
|
121
|
+
throw new Error(`No assert executor for deviceType '${entry.deviceType}'; the owning plugin interprets cached steps directly.`);
|
|
122
|
+
},
|
|
115
123
|
});
|
|
116
|
-
|
|
124
|
+
// Drops the live runner to produce a JSON-safe shape for storage/serialization.
|
|
125
|
+
const stripAssertRunner = (entry) => ({
|
|
126
|
+
...(entry.deviceType && entry.deviceType !== 'web'
|
|
127
|
+
? { deviceType: entry.deviceType }
|
|
128
|
+
: {}),
|
|
117
129
|
pageUrl: entry.pageUrl,
|
|
118
130
|
assertion: entry.assertion,
|
|
119
131
|
steps: entry.steps,
|
|
132
|
+
});
|
|
133
|
+
const cloneAssertEntry = (entry) => ({
|
|
134
|
+
...stripAssertRunner(entry),
|
|
120
135
|
run: entry.run,
|
|
121
136
|
});
|
|
122
137
|
// ---------------------------------------------------------------------------
|
|
@@ -152,7 +167,17 @@ const serializeEntry = (entry) => {
|
|
|
152
167
|
const renderCacheModule = (entries, assertions, locators) => {
|
|
153
168
|
const serializedEntries = entries.map(serializeEntry).join(', ');
|
|
154
169
|
const assertionsPart = assertions && assertions.length > 0
|
|
155
|
-
? `, assertions: [${assertions
|
|
170
|
+
? `, assertions: [${assertions
|
|
171
|
+
.map((a) => JSON.stringify({
|
|
172
|
+
// Only emit deviceType for non-web entries (backwards compat).
|
|
173
|
+
...(a.deviceType && a.deviceType !== 'web'
|
|
174
|
+
? { deviceType: a.deviceType }
|
|
175
|
+
: {}),
|
|
176
|
+
pageUrl: a.pageUrl,
|
|
177
|
+
assertion: a.assertion,
|
|
178
|
+
steps: a.steps,
|
|
179
|
+
}))
|
|
180
|
+
.join(', ')}]`
|
|
156
181
|
: '';
|
|
157
182
|
const locatorsPart = locators && locators.length > 0
|
|
158
183
|
? `, locators: [${locators.map((l) => JSON.stringify({ pageUrl: l.pageUrl, description: l.description, result: l.result })).join(', ')}]`
|
|
@@ -227,11 +252,7 @@ class FilePageAiCache {
|
|
|
227
252
|
state,
|
|
228
253
|
result: {
|
|
229
254
|
caches: state.caches.map((entry) => stripRunner(entry)),
|
|
230
|
-
assertions: state.assertions.map((a) => (
|
|
231
|
-
pageUrl: a.pageUrl,
|
|
232
|
-
assertion: a.assertion,
|
|
233
|
-
steps: a.steps,
|
|
234
|
-
})),
|
|
255
|
+
assertions: state.assertions.map((a) => stripAssertRunner(a)),
|
|
235
256
|
locators: state.locators.map((l) => ({
|
|
236
257
|
pageUrl: l.pageUrl,
|
|
237
258
|
description: l.description,
|
|
@@ -370,11 +391,11 @@ class FilePageAiCache {
|
|
|
370
391
|
throw new Error(`Invalid cache entry: run must be a function for ${entry.pageUrl ?? 'unknown page'}.`);
|
|
371
392
|
}
|
|
372
393
|
const normalized = {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
394
|
+
// Preserve any non-web device type verbatim — values are opaque to
|
|
395
|
+
// core (web is the built-in; plugin targets supply their own).
|
|
396
|
+
...(entry.deviceType && entry.deviceType !== 'web'
|
|
397
|
+
? { deviceType: String(entry.deviceType) }
|
|
398
|
+
: {}),
|
|
378
399
|
pageUrl: String(entry.pageUrl ?? ''),
|
|
379
400
|
instruction: entry.instruction === null || entry.instruction === undefined
|
|
380
401
|
? null
|
|
@@ -406,6 +427,9 @@ class FilePageAiCache {
|
|
|
406
427
|
? entry.steps
|
|
407
428
|
: [];
|
|
408
429
|
return materializeAssertEntry({
|
|
430
|
+
...(entry.deviceType && entry.deviceType !== 'web'
|
|
431
|
+
? { deviceType: String(entry.deviceType) }
|
|
432
|
+
: {}),
|
|
409
433
|
pageUrl: String(entry.pageUrl ?? ''),
|
|
410
434
|
assertion: String(entry.assertion ?? ''),
|
|
411
435
|
steps,
|
|
@@ -431,11 +455,7 @@ class FilePageAiCache {
|
|
|
431
455
|
}
|
|
432
456
|
async writeCacheFile(state) {
|
|
433
457
|
const serializedCaches = state.caches.map((entry) => stripRunner(entry));
|
|
434
|
-
const serializedAssertions = state.assertions.map((e) => (
|
|
435
|
-
pageUrl: e.pageUrl,
|
|
436
|
-
assertion: e.assertion,
|
|
437
|
-
steps: e.steps,
|
|
438
|
-
}));
|
|
458
|
+
const serializedAssertions = state.assertions.map((e) => stripAssertRunner(e));
|
|
439
459
|
const serializedLocators = state.locators.map((e) => ({
|
|
440
460
|
pageUrl: e.pageUrl,
|
|
441
461
|
description: e.description,
|
|
@@ -517,11 +537,7 @@ class InMemoryPageAiCache {
|
|
|
517
537
|
async snapshot() {
|
|
518
538
|
return {
|
|
519
539
|
caches: this.cache.map((entry) => stripRunner(entry)),
|
|
520
|
-
assertions: this.assertions.map((a) => (
|
|
521
|
-
pageUrl: a.pageUrl,
|
|
522
|
-
assertion: a.assertion,
|
|
523
|
-
steps: a.steps,
|
|
524
|
-
})),
|
|
540
|
+
assertions: this.assertions.map((a) => stripAssertRunner(a)),
|
|
525
541
|
locators: this.locators.map((l) => ({
|
|
526
542
|
pageUrl: l.pageUrl,
|
|
527
543
|
description: l.description,
|
|
@@ -2,9 +2,9 @@ import type { FlowMetadata } from '../../../models/FlowMetadata';
|
|
|
2
2
|
import type { ProposedToolCall } from '../../../models/ProposedToolCall';
|
|
3
3
|
import type { PageAiCacheEntry } from './cache';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Builds web (Playwright) cache entries. Shared by the web runtime (`PageAi`)
|
|
6
|
+
* and the web code generator so both emit matching `page.run(...)` replay
|
|
7
|
+
* source. Non-web targets build their own cache entries in their own plugins.
|
|
8
8
|
*/
|
|
9
9
|
export declare class PageAiCacheEntryBuilder {
|
|
10
10
|
/**
|
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PageAiCacheEntryBuilder = void 0;
|
|
4
4
|
const CodeGenerator_1 = require("../../../codegen/CodeGenerator");
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Builds web (Playwright) cache entries. Shared by the web runtime (`PageAi`)
|
|
7
|
+
* and the web code generator so both emit matching `page.run(...)` replay
|
|
8
|
+
* source. Non-web targets build their own cache entries in their own plugins.
|
|
9
9
|
*/
|
|
10
10
|
class PageAiCacheEntryBuilder {
|
|
11
11
|
/**
|
|
@@ -20,9 +20,7 @@ class PageAiCacheEntryBuilder {
|
|
|
20
20
|
maxToolCalls: metadata.maxToolCalls,
|
|
21
21
|
envVars: metadata.envVars,
|
|
22
22
|
runSource: `async ({ page }) => {${toolCallCache
|
|
23
|
-
.map((toolCall) =>
|
|
24
|
-
return (0, CodeGenerator_1.convertProposedToolCallToPlaywrightCode)(toolCall);
|
|
25
|
-
})
|
|
23
|
+
.map((toolCall) => (0, CodeGenerator_1.convertProposedToolCallToPlaywrightCode)(toolCall))
|
|
26
24
|
.join('\n\n')}}`,
|
|
27
25
|
};
|
|
28
26
|
}
|
|
@@ -266,6 +266,8 @@ Valid options:
|
|
|
266
266
|
const cached = await cache.getAssert({ pageUrl, assertion });
|
|
267
267
|
if (cached) {
|
|
268
268
|
aiInvocationCacheHit = true;
|
|
269
|
+
// The key above carries no deviceType, so it only matches web
|
|
270
|
+
// entries — whose steps are Playwright-shaped by construction.
|
|
269
271
|
aiInvocationAssertSteps = cached.steps;
|
|
270
272
|
Logger_1.appLogger.debug(`Assert cache HIT for: "${assertion}" - running cached Playwright assertion`);
|
|
271
273
|
const envData = await resolveEnvData();
|
|
@@ -1109,6 +1109,12 @@ function getSanitizedTestName(testInfo) {
|
|
|
1109
1109
|
}
|
|
1110
1110
|
/** Builds a TestMetadata from the fields available on a FlowMetadata. */
|
|
1111
1111
|
function flowMetadataToTestMetadata(testId, flowMeta) {
|
|
1112
|
+
const targetConfig = {};
|
|
1113
|
+
if (flowMeta.target &&
|
|
1114
|
+
flowMeta.target !== 'web' &&
|
|
1115
|
+
flowMeta.target in flowMeta) {
|
|
1116
|
+
targetConfig[flowMeta.target] = flowMeta[flowMeta.target];
|
|
1117
|
+
}
|
|
1112
1118
|
return {
|
|
1113
1119
|
id: testId,
|
|
1114
1120
|
name: flowMeta.name,
|
|
@@ -1125,6 +1131,7 @@ function flowMetadataToTestMetadata(testId, flowMeta) {
|
|
|
1125
1131
|
suiteId: null,
|
|
1126
1132
|
nextRunMode: 'DETERMINISTIC',
|
|
1127
1133
|
provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
|
|
1134
|
+
...targetConfig,
|
|
1128
1135
|
};
|
|
1129
1136
|
}
|
|
1130
1137
|
//# sourceMappingURL=testExtension.js.map
|
|
@@ -267,6 +267,20 @@ to someone who is tasked with completing it as a goal.
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
async function buildToolList(donobuFlowMetadata, toolRegistry) {
|
|
270
|
+
if (donobuFlowMetadata.target !== 'web') {
|
|
271
|
+
const targetDefaultTools = toolRegistry
|
|
272
|
+
.defaultTools()
|
|
273
|
+
.filter((tool) => tool.supportedTargets.length === 0 ||
|
|
274
|
+
tool.supportedTargets.includes(donobuFlowMetadata.target))
|
|
275
|
+
.map((tool) => tool.name);
|
|
276
|
+
const defaultTools = new Set([
|
|
277
|
+
...(donobuFlowMetadata.allowedTools
|
|
278
|
+
? donobuFlowMetadata.allowedTools
|
|
279
|
+
: []),
|
|
280
|
+
...targetDefaultTools,
|
|
281
|
+
]);
|
|
282
|
+
return [...defaultTools];
|
|
283
|
+
}
|
|
270
284
|
const baseTools = [
|
|
271
285
|
AnalyzePageTextTool_1.AnalyzePageTextTool.NAME,
|
|
272
286
|
AssertTool_1.AssertTool.NAME,
|
package/dist/esm/main.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { GptClient } from './clients/GptClient';
|
|
|
6
6
|
export { type GptClientPlugin, GptClientPluginRegistry, } from './clients/GptClientPlugin';
|
|
7
7
|
export { OpenAiGptClient } from './clients/OpenAiGptClient';
|
|
8
8
|
export { VercelAiGptClient } from './clients/VercelAiGptClient';
|
|
9
|
+
export * from './codegen/TargetCodeGenerator';
|
|
9
10
|
export { env } from './envVars';
|
|
10
11
|
export * from './exceptions/DonobuException';
|
|
11
12
|
export * from './exceptions/FlowIdCollisionException';
|
|
@@ -14,6 +15,7 @@ export * from './exceptions/GptApiKeysNotSetupException';
|
|
|
14
15
|
export * from './exceptions/GptPlatformAuthenticationFailedException';
|
|
15
16
|
export * from './exceptions/InvalidParamValueException';
|
|
16
17
|
export * from './exceptions/ToolCallFailedException';
|
|
18
|
+
export * from './lib/ai/cache/assertCache';
|
|
17
19
|
export * from './lib/ai/cache/cache';
|
|
18
20
|
export * from './lib/ai/cache/cacheLocator';
|
|
19
21
|
export type * from './lib/page/DonobuExtendedPage';
|
|
@@ -29,7 +31,7 @@ export { type DonobuStack, setupDonobuStack } from './managers/DonobuStack';
|
|
|
29
31
|
export { InteractionVisualizer } from './managers/InteractionVisualizer';
|
|
30
32
|
export { type LoadedPlugins, type PluginDependencies, PluginLoader, } from './managers/PluginLoader';
|
|
31
33
|
export type * from './managers/TargetInspector';
|
|
32
|
-
export { ToolManager } from './managers/ToolManager';
|
|
34
|
+
export { buildToolInterpolationDataSource, ToolManager, } from './managers/ToolManager';
|
|
33
35
|
export { createDefaultToolRegistry, type ToolRegistry, } from './managers/ToolRegistry';
|
|
34
36
|
export * from './managers/WebTargetInspector';
|
|
35
37
|
export type * from './models/AiQuery';
|
|
@@ -55,6 +57,7 @@ export type * from './models/ToolCall';
|
|
|
55
57
|
export type * from './models/ToolCallContext';
|
|
56
58
|
export * from './models/ToolCallResult';
|
|
57
59
|
export * from './models/ToolSchema';
|
|
60
|
+
export type * from './models/ToolTemplateDataSource';
|
|
58
61
|
export type { VideoSegment } from './models/VideoSegment';
|
|
59
62
|
export type { EnvPersistence } from './persistence/env/EnvPersistence';
|
|
60
63
|
export { type FileUploadAggregateStatus, type FileUploadPlatformStatus, getFileUploadAggregateStatus, shutdownFileUploadWorkers, } from './persistence/files/fileUploadWorkerRegistry';
|
|
@@ -65,17 +68,20 @@ export type { TestsPersistence } from './persistence/tests/TestsPersistence';
|
|
|
65
68
|
export type * from './targets/TargetProvider';
|
|
66
69
|
export type { TargetRuntime } from './targets/TargetRuntime';
|
|
67
70
|
export { type TargetRuntimeParams, type TargetRuntimePlugin, TargetRuntimePluginRegistry, } from './targets/TargetRuntimePlugin';
|
|
71
|
+
export * from './tools/InputFakerTool';
|
|
68
72
|
export * from './tools/ReplayableInteraction';
|
|
69
73
|
export * from './tools/Tool';
|
|
70
74
|
export * from './utils/buildProvenance';
|
|
71
75
|
export * from './utils/createTool';
|
|
72
76
|
export * from './utils/FlowLogBuffer';
|
|
77
|
+
export * from './utils/JsonPath';
|
|
73
78
|
export * from './utils/JsonSchemaUtils';
|
|
74
79
|
export * from './utils/JsonUtils';
|
|
75
80
|
export * from './utils/Logger';
|
|
76
81
|
export * from './utils/MiscUtils';
|
|
77
82
|
export * from './utils/PlaywrightUtils';
|
|
78
83
|
export * from './utils/TargetUtils';
|
|
84
|
+
export * from './utils/TemplateInterpolator';
|
|
79
85
|
/**
|
|
80
86
|
* Starts a Donobu API server at the given port. The server assumes that the
|
|
81
87
|
* Playwright browsers have been installed.
|
package/dist/esm/main.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.TargetRuntimePluginRegistry = exports.PersistencePluginRegistry = exports.shutdownFileUploadWorkers = exports.getFileUploadAggregateStatus = exports.createDefaultToolRegistry = exports.ToolManager = exports.PluginLoader = exports.InteractionVisualizer = exports.setupDonobuStack = exports.prepareToolCallsForRerun = exports.DonobuFlowsManager = exports.distillAllowedEnvVariableNames = exports.DonobuFlow = exports.AdminApiController = exports.env = exports.VercelAiGptClient = exports.OpenAiGptClient = exports.GptClientPluginRegistry = exports.GptClient = exports.GoogleGenerativeAiGptClient = exports.fixAssertFields = exports.DonobuGptClient = exports.AnthropicGptClient = void 0;
|
|
17
|
+
exports.TargetRuntimePluginRegistry = exports.PersistencePluginRegistry = exports.shutdownFileUploadWorkers = exports.getFileUploadAggregateStatus = exports.createDefaultToolRegistry = exports.ToolManager = exports.buildToolInterpolationDataSource = exports.PluginLoader = exports.InteractionVisualizer = exports.setupDonobuStack = exports.prepareToolCallsForRerun = exports.DonobuFlowsManager = exports.distillAllowedEnvVariableNames = exports.DonobuFlow = exports.AdminApiController = exports.env = exports.VercelAiGptClient = exports.OpenAiGptClient = exports.GptClientPluginRegistry = exports.GptClient = exports.GoogleGenerativeAiGptClient = exports.fixAssertFields = exports.DonobuGptClient = exports.AnthropicGptClient = void 0;
|
|
18
18
|
exports.startDonobuServer = startDonobuServer;
|
|
19
19
|
const commander_1 = require("commander");
|
|
20
20
|
const v4_1 = require("zod/v4");
|
|
@@ -39,6 +39,7 @@ var OpenAiGptClient_1 = require("./clients/OpenAiGptClient");
|
|
|
39
39
|
Object.defineProperty(exports, "OpenAiGptClient", { enumerable: true, get: function () { return OpenAiGptClient_1.OpenAiGptClient; } });
|
|
40
40
|
var VercelAiGptClient_1 = require("./clients/VercelAiGptClient");
|
|
41
41
|
Object.defineProperty(exports, "VercelAiGptClient", { enumerable: true, get: function () { return VercelAiGptClient_1.VercelAiGptClient; } });
|
|
42
|
+
__exportStar(require("./codegen/TargetCodeGenerator"), exports);
|
|
42
43
|
var envVars_2 = require("./envVars");
|
|
43
44
|
Object.defineProperty(exports, "env", { enumerable: true, get: function () { return envVars_2.env; } });
|
|
44
45
|
__exportStar(require("./exceptions/DonobuException"), exports);
|
|
@@ -48,6 +49,7 @@ __exportStar(require("./exceptions/GptApiKeysNotSetupException"), exports);
|
|
|
48
49
|
__exportStar(require("./exceptions/GptPlatformAuthenticationFailedException"), exports);
|
|
49
50
|
__exportStar(require("./exceptions/InvalidParamValueException"), exports);
|
|
50
51
|
__exportStar(require("./exceptions/ToolCallFailedException"), exports);
|
|
52
|
+
__exportStar(require("./lib/ai/cache/assertCache"), exports);
|
|
51
53
|
__exportStar(require("./lib/ai/cache/cache"), exports);
|
|
52
54
|
__exportStar(require("./lib/ai/cache/cacheLocator"), exports);
|
|
53
55
|
__exportStar(require("./lib/page/extendPage"), exports);
|
|
@@ -70,6 +72,7 @@ Object.defineProperty(exports, "InteractionVisualizer", { enumerable: true, get:
|
|
|
70
72
|
var PluginLoader_1 = require("./managers/PluginLoader");
|
|
71
73
|
Object.defineProperty(exports, "PluginLoader", { enumerable: true, get: function () { return PluginLoader_1.PluginLoader; } });
|
|
72
74
|
var ToolManager_1 = require("./managers/ToolManager");
|
|
75
|
+
Object.defineProperty(exports, "buildToolInterpolationDataSource", { enumerable: true, get: function () { return ToolManager_1.buildToolInterpolationDataSource; } });
|
|
73
76
|
Object.defineProperty(exports, "ToolManager", { enumerable: true, get: function () { return ToolManager_1.ToolManager; } });
|
|
74
77
|
var ToolRegistry_1 = require("./managers/ToolRegistry");
|
|
75
78
|
Object.defineProperty(exports, "createDefaultToolRegistry", { enumerable: true, get: function () { return ToolRegistry_1.createDefaultToolRegistry; } });
|
|
@@ -95,17 +98,20 @@ var PersistencePlugin_1 = require("./persistence/PersistencePlugin");
|
|
|
95
98
|
Object.defineProperty(exports, "PersistencePluginRegistry", { enumerable: true, get: function () { return PersistencePlugin_1.PersistencePluginRegistry; } });
|
|
96
99
|
var TargetRuntimePlugin_1 = require("./targets/TargetRuntimePlugin");
|
|
97
100
|
Object.defineProperty(exports, "TargetRuntimePluginRegistry", { enumerable: true, get: function () { return TargetRuntimePlugin_1.TargetRuntimePluginRegistry; } });
|
|
101
|
+
__exportStar(require("./tools/InputFakerTool"), exports);
|
|
98
102
|
__exportStar(require("./tools/ReplayableInteraction"), exports);
|
|
99
103
|
__exportStar(require("./tools/Tool"), exports);
|
|
100
104
|
__exportStar(require("./utils/buildProvenance"), exports);
|
|
101
105
|
__exportStar(require("./utils/createTool"), exports);
|
|
102
106
|
__exportStar(require("./utils/FlowLogBuffer"), exports);
|
|
107
|
+
__exportStar(require("./utils/JsonPath"), exports);
|
|
103
108
|
__exportStar(require("./utils/JsonSchemaUtils"), exports);
|
|
104
109
|
__exportStar(require("./utils/JsonUtils"), exports);
|
|
105
110
|
__exportStar(require("./utils/Logger"), exports);
|
|
106
111
|
__exportStar(require("./utils/MiscUtils"), exports);
|
|
107
112
|
__exportStar(require("./utils/PlaywrightUtils"), exports);
|
|
108
113
|
__exportStar(require("./utils/TargetUtils"), exports);
|
|
114
|
+
__exportStar(require("./utils/TemplateInterpolator"), exports);
|
|
109
115
|
const DEFAULT_PORT = 31000;
|
|
110
116
|
/**
|
|
111
117
|
* Starts a Donobu API server at the given port. The server assumes that the
|
|
@@ -42,6 +42,7 @@ const fs = __importStar(require("fs/promises"));
|
|
|
42
42
|
const os_1 = require("os");
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const CodeGenerator_1 = require("../codegen/CodeGenerator");
|
|
45
|
+
const TargetCodeGenerator_1 = require("../codegen/TargetCodeGenerator");
|
|
45
46
|
const ActiveFlowNotFoundException_1 = require("../exceptions/ActiveFlowNotFoundException");
|
|
46
47
|
const BrowserStateNotFoundException_1 = require("../exceptions/BrowserStateNotFoundException");
|
|
47
48
|
const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
|
|
@@ -471,10 +472,22 @@ class DonobuFlowsManager {
|
|
|
471
472
|
const originalToolCalls = (await this.getToolCalls(flowId)).filter((tc) => tc.outcome?.isSuccessful);
|
|
472
473
|
const proposedToolCalls = await prepareToolCallsForRerun(originalToolCalls, effectiveOptions, this.toolRegistry);
|
|
473
474
|
const scriptVariant = effectiveOptions.playwrightScriptVariant === 'classic' ? 'classic' : 'ai';
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
475
|
+
// Dispatch to the target's code generator (web is built in; plugin targets
|
|
476
|
+
// register their own). A target with no generator (e.g. mobile, which owns
|
|
477
|
+
// its codegen entirely in-plugin) has no spec output via this endpoint.
|
|
478
|
+
const generator = (0, TargetCodeGenerator_1.getCodeGenerator)(flowMetadata.target);
|
|
479
|
+
if (!generator) {
|
|
480
|
+
throw new Error(`Code generation is not supported for target "${flowMetadata.target}".`);
|
|
481
|
+
}
|
|
482
|
+
const ctx = {
|
|
483
|
+
flowMetadata,
|
|
484
|
+
toolCalls: proposedToolCalls,
|
|
485
|
+
options: effectiveOptions,
|
|
486
|
+
toolRegistry: this.toolRegistry,
|
|
487
|
+
};
|
|
488
|
+
return scriptVariant === 'classic'
|
|
489
|
+
? generator.generateClassicScript(ctx)
|
|
490
|
+
: generator.generateAiScript(ctx);
|
|
478
491
|
}
|
|
479
492
|
/**
|
|
480
493
|
* Generates a complete Playwright project structure for multiple flows with dependency management.
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.setupDonobuStack = setupDonobuStack;
|
|
4
4
|
const GptClientFactory_1 = require("../clients/GptClientFactory");
|
|
5
5
|
const GptClientPlugin_1 = require("../clients/GptClientPlugin");
|
|
6
|
+
const TargetCodeGenerator_1 = require("../codegen/TargetCodeGenerator");
|
|
6
7
|
const envVars_1 = require("../envVars");
|
|
7
8
|
const AgentsPersistenceSqlite_1 = require("../persistence/agents/AgentsPersistenceSqlite");
|
|
8
9
|
const DonobuSqliteDb_1 = require("../persistence/DonobuSqliteDb");
|
|
@@ -78,6 +79,14 @@ async function loadDefaultPlugins() {
|
|
|
78
79
|
const pluginLoader = await PluginLoader_1.PluginLoader.create();
|
|
79
80
|
const plugins = await pluginLoader.loadAllPlugins();
|
|
80
81
|
plugins.targetRuntimePlugins.unshift(WebTargetRuntime_1.webTargetRuntimePlugin);
|
|
82
|
+
// Register each target's code generator (if any) so the code generator can
|
|
83
|
+
// emit spec files for plugin targets without knowing about them. The web
|
|
84
|
+
// generator is registered by the code generator module itself.
|
|
85
|
+
for (const plugin of plugins.targetRuntimePlugins) {
|
|
86
|
+
if (plugin.codeGenerator) {
|
|
87
|
+
(0, TargetCodeGenerator_1.registerCodeGenerator)(plugin.codeGenerator);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
81
90
|
return plugins;
|
|
82
91
|
}
|
|
83
92
|
//# sourceMappingURL=DonobuStack.js.map
|
|
@@ -23,6 +23,10 @@ class TestsManager {
|
|
|
23
23
|
targetWebsite: params.web.targetWebsite,
|
|
24
24
|
}
|
|
25
25
|
: undefined;
|
|
26
|
+
const targetConfig = {};
|
|
27
|
+
if (params.target && params.target !== 'web' && params.target in params) {
|
|
28
|
+
targetConfig[params.target] = params[params.target];
|
|
29
|
+
}
|
|
26
30
|
const testMetadata = {
|
|
27
31
|
id: testId,
|
|
28
32
|
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
@@ -41,6 +45,7 @@ class TestsManager {
|
|
|
41
45
|
videoDisabled: params.videoDisabled,
|
|
42
46
|
tags: [],
|
|
43
47
|
provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'),
|
|
48
|
+
...targetConfig,
|
|
44
49
|
};
|
|
45
50
|
// If the test is part of a suite, write it to the same persistence layer
|
|
46
51
|
// as the suite — otherwise the suite_id foreign key fails (in SQLite)
|
|
@@ -173,12 +173,14 @@ exports.ToolManager = ToolManager;
|
|
|
173
173
|
function buildToolInterpolationDataSource(envData, previousToolCalls) {
|
|
174
174
|
const dataSource = {
|
|
175
175
|
env: envData,
|
|
176
|
+
vars: {},
|
|
176
177
|
calls: [],
|
|
177
178
|
};
|
|
178
179
|
const interpolatedPastToolCalls = [];
|
|
179
180
|
for (const toolCall of previousToolCalls) {
|
|
180
181
|
const tempDataSource = {
|
|
181
182
|
env: envData,
|
|
183
|
+
vars: dataSource.vars,
|
|
182
184
|
calls: [...interpolatedPastToolCalls],
|
|
183
185
|
};
|
|
184
186
|
const processedParameters = (0, TemplateInterpolator_1.interpolateObject)(toolCall.parameters, tempDataSource);
|
|
@@ -188,6 +190,15 @@ function buildToolInterpolationDataSource(envData, previousToolCalls) {
|
|
|
188
190
|
result: toolCall.outcome.forLlm,
|
|
189
191
|
});
|
|
190
192
|
dataSource.calls = [...interpolatedPastToolCalls];
|
|
193
|
+
const variables = toolCall.outcome?.metadata?.variables;
|
|
194
|
+
if (variables &&
|
|
195
|
+
typeof variables === 'object' &&
|
|
196
|
+
!Array.isArray(variables)) {
|
|
197
|
+
dataSource.vars = {
|
|
198
|
+
...dataSource.vars,
|
|
199
|
+
...variables,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
191
202
|
}
|
|
192
203
|
return dataSource;
|
|
193
204
|
}
|
|
@@ -40,7 +40,7 @@ not need this, omit it and let the SDK mint an ID.`),
|
|
|
40
40
|
})
|
|
41
41
|
.optional()
|
|
42
42
|
.describe('Web target configuration. Required when target is "web".'),
|
|
43
|
-
// Plugin-provided target configurations (e.g. mobile) flow through via
|
|
43
|
+
// Plugin-provided target configurations (e.g. mobile, api) flow through via
|
|
44
44
|
// .passthrough() and are validated by the corresponding TargetRuntimePlugin.
|
|
45
45
|
// -- Target-invariant fields --
|
|
46
46
|
name: v4_1.z
|
|
@@ -25,7 +25,7 @@ exports.RunConfigSchema = v4_1.z.looseObject({
|
|
|
25
25
|
.string()
|
|
26
26
|
.describe('The target type (e.g. "web", "mobile"). The value names the key containing the target-specific configuration.'),
|
|
27
27
|
web: WebTargetConfigSchema.optional().describe('Web target configuration. Present when target is "web".'),
|
|
28
|
-
// Plugin-provided target configurations (e.g. mobile) flow through via
|
|
28
|
+
// Plugin-provided target configurations (e.g. mobile, api) flow through via
|
|
29
29
|
// the loose object behavior and are validated by the corresponding
|
|
30
30
|
// TargetRuntimePlugin.
|
|
31
31
|
envVars: v4_1.z
|
|
@@ -19,6 +19,11 @@ export type ToolTemplateDataSource = {
|
|
|
19
19
|
* The environment variables available to the current Donobu flow.
|
|
20
20
|
*/
|
|
21
21
|
env: Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Values published by prior tool calls (via `outcome.metadata.variables`)
|
|
24
|
+
* for reuse in later tool params.
|
|
25
|
+
*/
|
|
26
|
+
vars: Record<string, unknown>;
|
|
22
27
|
/**
|
|
23
28
|
* The historical tool calls for the current Donobu flow.
|
|
24
29
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EnvPick } from 'env-struct';
|
|
2
2
|
import type { BrowserContext } from 'playwright';
|
|
3
|
+
import type { TargetCodeGenerator } from '../codegen/TargetCodeGenerator';
|
|
3
4
|
import type { env } from '../envVars';
|
|
4
5
|
import type { InteractionVisualizer } from '../managers/InteractionVisualizer';
|
|
5
6
|
import type { BrowserStateReference } from '../models/BrowserStateFlowReference';
|
|
@@ -77,6 +78,18 @@ export interface TargetRuntimePlugin {
|
|
|
77
78
|
* the flow completes or errors.
|
|
78
79
|
*/
|
|
79
80
|
createRuntime(params: TargetRuntimeParams): Promise<TargetRuntime>;
|
|
81
|
+
/**
|
|
82
|
+
* Optional code generator for this target, registered with the code
|
|
83
|
+
* generator registry at startup. Owns this target's full spec/cache output
|
|
84
|
+
* and is what the unified "flow as code" path dispatches to.
|
|
85
|
+
*
|
|
86
|
+
* Omit when a target doesn't plug into core's codegen — either it produces no
|
|
87
|
+
* replay artifacts, or it owns codegen entirely in-plugin via its own path
|
|
88
|
+
* (e.g. mobile today builds `device.run(...)` cache entries through its own
|
|
89
|
+
* builder and never routes through core). Flows of a target with no
|
|
90
|
+
* registered generator have no output via the unified endpoint.
|
|
91
|
+
*/
|
|
92
|
+
readonly codeGenerator?: TargetCodeGenerator;
|
|
80
93
|
}
|
|
81
94
|
/**
|
|
82
95
|
* An immutable registry of target runtime plugins, keyed by target type.
|
|
@@ -53,15 +53,28 @@ export declare function buildAssertExecutor(steps: PlaywrightAssertionStep[]): A
|
|
|
53
53
|
* on the same domain always resolves to the same cached entry.
|
|
54
54
|
*/
|
|
55
55
|
export type AssertCacheKey = {
|
|
56
|
+
/**
|
|
57
|
+
* Target device discriminator; defaults to `'web'`. Plugin targets supply
|
|
58
|
+
* their own opaque value — core treats any non-`'web'` value as an opaque
|
|
59
|
+
* string.
|
|
60
|
+
*/
|
|
61
|
+
deviceType?: string;
|
|
56
62
|
pageUrl: string;
|
|
57
63
|
assertion: string;
|
|
58
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* A structured assertion step. Web entries store
|
|
67
|
+
* {@link PlaywrightAssertionStep}s, which core compiles into Playwright
|
|
68
|
+
* `expect` calls. Non-web (plugin-target) entries store their own step
|
|
69
|
+
* shapes — opaque to core; the owning plugin interprets them on replay.
|
|
70
|
+
*/
|
|
71
|
+
export type AssertStep = PlaywrightAssertionStep | Record<string, unknown>;
|
|
59
72
|
/**
|
|
60
73
|
* Serialised cache entry stored on disk. Contains structured assertion
|
|
61
74
|
* steps rather than raw code strings.
|
|
62
75
|
*/
|
|
63
76
|
export type AssertCacheEntry = AssertCacheKey & {
|
|
64
|
-
steps:
|
|
77
|
+
steps: AssertStep[];
|
|
65
78
|
};
|
|
66
79
|
/**
|
|
67
80
|
* Entry hydrated with an executable runner.
|
|
@@ -9,8 +9,12 @@ import type { AssertCacheEntry, AssertCacheEntryWithRunner, AssertCacheKey, Loca
|
|
|
9
9
|
* For mobile entries, `pageUrl` is the sentinel `"mobile://app"`.
|
|
10
10
|
*/
|
|
11
11
|
export type PageAiCacheKey = {
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Target device discriminator; defaults to `'web'`. Plugin targets supply
|
|
14
|
+
* their own opaque value (e.g. mobile `'android'`/`'ios'`, API `'api'`) — core
|
|
15
|
+
* treats any non-`'web'` value as an opaque string.
|
|
16
|
+
*/
|
|
17
|
+
deviceType?: string;
|
|
14
18
|
pageUrl: string;
|
|
15
19
|
instruction: string | null;
|
|
16
20
|
schema: Record<string, unknown> | null;
|
|
@@ -39,9 +43,13 @@ export type PageAiCacheContents = {
|
|
|
39
43
|
locators?: LocateCacheEntry[];
|
|
40
44
|
};
|
|
41
45
|
export type PageAiCacheExecutionContext = {
|
|
46
|
+
/** Legacy cutout for the built-in web target. */
|
|
42
47
|
page?: DonobuExtendedPage;
|
|
43
|
-
/**
|
|
44
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Plugin targets supply their own handle under a target-specific key,
|
|
50
|
+
* opaque to core; the plugin's cache replay casts to its concrete shape.
|
|
51
|
+
*/
|
|
52
|
+
[targetHandle: string]: unknown;
|
|
45
53
|
};
|
|
46
54
|
export type PageAiCacheExecutor = (context: PageAiCacheExecutionContext) => Promise<unknown>;
|
|
47
55
|
/**
|