donobu
Version:
Create browser automations with an LLM agent and replay them as Playwright scripts.
254 lines • 14.2 kB
TypeScript
import type { EnvPick } from 'env-struct';
import type { BrowserContext } from 'playwright';
import type { z } from 'zod/v4';
import type { GptClient } from '../clients/GptClient';
import type { GptClientFactory } from '../clients/GptClientFactory';
import type { GeneratedProject } from '../codegen/CodeGenerator';
import type { env } from '../envVars';
import type { AiQuery } from '../models/AiQuery';
import type { BrowserStateReference } from '../models/BrowserStateFlowReference';
import type { BrowserStorageState } from '../models/BrowserStorageState';
import type { CodeGenerationOptions } from '../models/CodeGenerationOptions';
import type { ControlPanelFactory } from '../models/ControlPanel';
import type { CreateDonobuFlow } from '../models/CreateDonobuFlow';
import type { DonobuAgent } from '../models/DonobuAgentType';
import type { DonobuDeploymentEnvironment } from '../models/DonobuDeploymentEnvironment';
import type { FlowHandle } from '../models/FlowHandle';
import type { FlowMetadata } from '../models/FlowMetadata';
import type { FlowsQuery } from '../models/FlowMetadata';
import type { PaginatedResult } from '../models/PaginatedResult';
import type { ProposedToolCall } from '../models/ProposedToolCall';
import type { RunConfig } from '../models/RunConfig';
import type { RunMode } from '../models/RunMode';
import type { ToolCall } from '../models/ToolCall';
import type { FlowsPersistenceRegistry } from '../persistence/flows/FlowsPersistenceRegistry';
import type { TestsPersistenceRegistry } from '../persistence/tests/TestsPersistenceRegistry';
import type { TargetRuntimePluginRegistry } from '../targets/TargetRuntimePlugin';
import type { Tool } from '../tools/Tool';
import type { FlowLogSnapshot } from '../utils/FlowLogBuffer';
import type { AgentsManager } from './AgentsManager';
import { DonobuFlow } from './DonobuFlow';
import type { EnvDataManager } from './EnvDataManager';
import type { GptConfigsManager } from './GptConfigsManager';
import type { ToolRegistry } from './ToolRegistry';
/**
* Response wrapper for `createFlow` calls.
*/
export type { FlowHandle };
export declare class DonobuFlowsManager {
private readonly deploymentEnvironment;
private readonly gptClientFactory;
private readonly gptConfigsManager;
private readonly agentsManager;
private readonly flowsPersistenceRegistry;
private readonly envDataManager;
private readonly controlPanelFactory;
private readonly environ;
private readonly toolRegistry;
private readonly targetRuntimePlugins;
private readonly testsPersistenceRegistry;
static readonly DEFAULT_MESSAGE_DURATION = 2247;
static readonly DEFAULT_MAX_TOOL_CALLS = 50;
static readonly DEFAULT_BROWSER_STATE_FILENAME = "browserstate.json";
static readonly FLOW_NAME_MAX_LENGTH = 255;
private readonly flowRuntime;
private readonly flowCatalog;
constructor(deploymentEnvironment: DonobuDeploymentEnvironment, gptClientFactory: GptClientFactory, gptConfigsManager: GptConfigsManager, agentsManager: AgentsManager, flowsPersistenceRegistry: FlowsPersistenceRegistry, envDataManager: EnvDataManager, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME'>, toolRegistry: ToolRegistry, targetRuntimePlugins: TargetRuntimePluginRegistry, testsPersistenceRegistry: TestsPersistenceRegistry);
/**
* Create a flow with the given parameters and invoke its `DonobuFlow#run`
* method, adding it to list of active flows.
*
* If `flowParams.testId` is set, the new flow is persisted to the same
* layer as the referenced test so the `flow_metadata.test_id` foreign
* key holds. Otherwise the primary layer is used.
*
* @param gptClient If present, will use this as the associated GPT client for
* this flow instead of instantiating a new one. If so, the
* gptConfigNameOverride field will be ignored.
* @param browserContextOverride If present, will use this as the browser
* context instead of instantiating a new one. If so, most browser
* related parameters are will be ignored.
*/
createFlow(flowParams: CreateDonobuFlow, gptClient?: GptClient, browserContextOverride?: BrowserContext): Promise<FlowHandle>;
getFlowLogs(flowId: string): Promise<FlowLogSnapshot>;
renameFlow(flowId: string, name: string | null): Promise<FlowMetadata>;
/**
* Converts a prior flow's tool calls into a list of tool calls to invoke when
* starting a new flow as a rerun (i.e. without agentic decisioning).
*
* @param priorFlowMetadata The metadata of the flow to prepare as a rerun.
* @param options The code generation options to use for the rerun.
*
* @returns A list of tool calls to invoke when starting the flow.
*/
getToolCallsForRerun(priorFlowMetadata: FlowMetadata, options: CodeGenerationOptions): Promise<ProposedToolCall[]>;
/**
* Loads the given flow by ID and returns a `CreateDonobuFlow` object that can be passed to `createFlow`
* to execute the flow as a rerun (i.e. without agentic decisioning).
*
* @param flowId The ID of the flow to prepare as a rerun.
* @returns Parameters that can be passed to createFlow to execute the flow as a rerun.
*/
getFlowAsRerun(flowId: string, options: CodeGenerationOptions): Promise<CreateDonobuFlow>;
/**
* Takes a RunConfig object, or anything derived from it (FlowMetadata,
* TestMetadata), plus some additional parameters, and returns a
* CreateDonobuFlow object that can be passed to `createFlow` to execute the
* flow.
*
* @param name The name for the new flow
* @param runMode The run mode to be used for the flow
* @param config The RunConfig object
* @param toolCallsOnStart An ordered series of tool calls to invoke when
* starting the flow
*
* @returns A CreateDonobuFlow object that can be passed to createFlow to
* execute the flow.
*/
getFlowFromConfigAndToolCalls(name: string, runMode: RunMode, config: RunConfig, toolCallsOnStart: ProposedToolCall[]): CreateDonobuFlow;
/** Add a proposed tool call the tool call queue for the given flow by ID. */
proposeToolCall(flowId: string, toolName: string, parameters: Record<string, unknown>): Promise<void>;
/**
* If the application is running in a non-hosted context, returns a direct,
* raw, `DonobuFlow` object by ID. If there is no flow in an active state
* with the given ID, or the application is running on some far flung server,
* then `ActiveFlowNotFoundException` is thrown. Mutations made to the
* returned object will be reflected by the active flow, and vice versa.
*/
getActiveFlow(flowId: string): DonobuFlow;
/**
* Get flows metadata across multiple persistence layers with pagination and filtering.
*/
getFlows(query: FlowsQuery): Promise<PaginatedResult<FlowMetadata>>;
/**
* Returns the metadata for the given flow by ID. If the flow is active, the
* returned metadata object is shared with the underlying flow and changes
* made to this metadata object will be visible to the flow. If the flow is
* not active, a copy of the persisted metadata is returned.
*/
getFlowById(flowId: string): Promise<FlowMetadata>;
/**
* Returns the metadata for the given flow by name.
*/
getFlowByName(flowName: string): Promise<FlowMetadata>;
/** Returns all the tool calls made by the given flow by ID. */
getToolCalls(flowId: string): Promise<ToolCall[]>;
/** Returns all AI query records for the given flow by ID. */
getAiQueries(flowId: string): Promise<AiQuery[]>;
/**
* Attempts to delete a flow by ID. If the flow is active, then
* `CannotDeleteRunningFlowException` is thrown. If the flow is not active,
* then the flow's persisted data is deleted.
*/
deleteFlowById(flowId: string): Promise<void>;
/**
* Attempts to cancel a flow by ID. If the flow is active, the flow is ended
* with a state of `FAILED`. If the flow is not active, this method has no
* effect.
*/
cancelFlow(flowId: string): Promise<FlowMetadata>;
/** Creates a Node.js Microsoft Playwright script to replay the given flow. */
getFlowAsPlaywrightScript(flowId: string, options?: CodeGenerationOptions): Promise<string>;
/**
* Generates a complete Playwright project structure for multiple flows with dependency management.
* Automatically includes any missing dependencies to ensure a complete dependency graph.
*/
getFlowsAsPlaywrightProject(flowIds: string[], options: CodeGenerationOptions): Promise<GeneratedProject>;
/**
* Resolves a GPT client using the provided GPT configuration name, falling back
* to environment-provided configs (BASE64_GPT_CONFIG, DONOBU_API_KEY,
* AWS_BEDROCK_MODEL_NAME, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY,
* OPENAI_API_KEY, OLLAMA_MODEL_NAME) and finally the config bound to the
* 'flow-runner' agent.
* Returns a null client if no configuration source is available.
*
* Public for testing only.
**/
createGptClient(gptConfigName?: string): Promise<{
gptConfigName: string | null;
agentName: DonobuAgent | null;
gptClient: GptClient | null;
}>;
/**
* Loads the browser state associated with the given flow. Throws
* {@link BrowserStateNotFoundException} if it is not found.
*/
getBrowserStorageState(browserStateRef: BrowserStateReference): Promise<BrowserStorageState>;
/**
* Picks the flows persistence layer to use when creating a new flow.
*
* - If `testId` is null/undefined: use the primary flows layer.
* - If `testId` is set: look up the test's layer key and use the matching
* flows layer. If no flows layer matches the test's key (rare —
* asymmetric registry config), fall back to the primary layer; the FK
* won't hold but the flow is at least persisted.
* - If `testId` is set but no test exists with that ID anywhere: fall
* back to the primary layer (the SQLite FK will reject if applicable;
* non-DB layers will accept the dangling reference).
*/
/**
* Best-effort check that no flow with the given ID exists in any persistence
* layer. Throws {@link FlowIdCollisionException} on hit. Note: this is RACY
* — concurrent callers with the same ID can both pass the check.
*/
private assertFlowIdAvailable;
private resolveLayerForFlowCreate;
private findTestLayerKey;
private isLocallyRunning;
}
/**
* Extracts environment variable names from the given objective and combines
* it with the given explicitly allowed variables.
*
* This function performs two operations:
* 1. Extracts environment variable references (in the form `$.env.VARIABLE_NAME`) from the overall objective.
* 2. Combines these with any explicitly allowed environment variable names.
*
* The resulting array contains unique environment variable names without duplicates.
*
* @param overallObjective - The objective text that may contain environment variable references.
* @param explicitlyAllowedEnvVariableNames - Additional environment variable names explicitly allowed.
* @returns An array of unique environment variable names that are allowed to be accessed.
*
* @example
* // Returns ["API_KEY", "USER_NAME", "DEBUG_MODE"]
* distillAllowedEnvVariableNames(
* "Use {{$.env.API_KEY}} to authenticate and greet {{$.env.USER_NAME}}",
* ["API_KEY", "DEBUG_MODE"]
* );
*/
export declare function distillAllowedEnvVariableNames(overallObjective: string | null | undefined, explicitlyAllowedEnvVariableNames: string[] | null | undefined): string[];
/**
* Resolves the final set of tools a flow is permitted to use.
*
* The resolution follows this priority:
* 1. If the caller explicitly listed tools, use those (filtered to the target).
* 2. Otherwise, fall back to target-appropriate defaults (curated subset for
* web, all target-compatible tools for plugin-provided targets).
* 3. Tools required by `toolCallsOnStart` are always included.
* 4. Custom (user-defined) tools are prepended.
* 5. GPT-requiring tools are stripped when no GPT client is available.
* 6. Minimal tools (objective bookkeeping) are guaranteed present.
* 7. The result is deduplicated and sorted by name.
*/
/** @internal Exported for testing. */
export declare function setupAllowedTools(flowParams: Pick<CreateDonobuFlow, 'customTools' | 'toolCallsOnStart' | 'allowedTools'>, hasGptClient: boolean, targetType: string, toolRegistry: ToolRegistry): Promise<Tool<z.ZodObject, z.ZodObject>[]>;
/**
* Convert an *executed* list of {@link ToolCall}s into a list of
* {@link ProposedToolCall}s that can be replayed in a fresh session.
*
* For each call, we look up the {@link Tool} in the given registry and
* delegate to its `prepareForRerun` override. Tools that need selector
* remapping (e.g. {@link ClickTool}, {@link InputTextTool}, the mobile
* interaction tools) each own their own logic; tools with no replay-
* specific needs inherit the passthrough default from {@link Tool}.
*
* When a tool is not registered (stale flow, removed tool) or
* `prepareForRerun` throws (e.g. missing selector metadata), the call
* is passed through or skipped with an `appLogger.warn` — "best-effort
* replay" behavior is preserved.
*
* @returns A list of {@link ProposedToolCall}s ready for replay.
*/
export declare function prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions, toolRegistry: ToolRegistry): Promise<ProposedToolCall[]>;
//# sourceMappingURL=DonobuFlowsManager.d.ts.map