donobu 2.48.1 → 2.49.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.
Files changed (93) hide show
  1. package/dist/apis/ToolsApi.d.ts.map +1 -1
  2. package/dist/apis/ToolsApi.js +26 -2
  3. package/dist/apis/ToolsApi.js.map +1 -1
  4. package/dist/assets/generated/version +1 -1
  5. package/dist/esm/apis/ToolsApi.d.ts.map +1 -1
  6. package/dist/esm/apis/ToolsApi.js +26 -2
  7. package/dist/esm/apis/ToolsApi.js.map +1 -1
  8. package/dist/esm/assets/generated/version +1 -1
  9. package/dist/esm/lib/PageAi.d.ts.map +1 -1
  10. package/dist/esm/lib/PageAi.js +10 -11
  11. package/dist/esm/lib/PageAi.js.map +1 -1
  12. package/dist/esm/lib/testExtension.js +1 -1
  13. package/dist/esm/lib/testExtension.js.map +1 -1
  14. package/dist/esm/lib/utils/triageTestFailure.d.ts.map +1 -1
  15. package/dist/esm/lib/utils/triageTestFailure.js +4 -4
  16. package/dist/esm/lib/utils/triageTestFailure.js.map +1 -1
  17. package/dist/esm/managers/DonobuFlow.d.ts.map +1 -1
  18. package/dist/esm/managers/DonobuFlow.js +148 -156
  19. package/dist/esm/managers/DonobuFlow.js.map +1 -1
  20. package/dist/esm/managers/DonobuFlowsManager.d.ts +25 -59
  21. package/dist/esm/managers/DonobuFlowsManager.d.ts.map +1 -1
  22. package/dist/esm/managers/DonobuFlowsManager.js +322 -301
  23. package/dist/esm/managers/DonobuFlowsManager.js.map +1 -1
  24. package/dist/esm/managers/DonobuStack.js +1 -1
  25. package/dist/esm/managers/DonobuStack.js.map +1 -1
  26. package/dist/esm/managers/GptConfigsManager.js +36 -3
  27. package/dist/esm/managers/GptConfigsManager.js.map +1 -1
  28. package/dist/esm/managers/ToolManager.d.ts +2 -0
  29. package/dist/esm/managers/ToolManager.d.ts.map +1 -1
  30. package/dist/esm/managers/ToolManager.js +9 -5
  31. package/dist/esm/managers/ToolManager.js.map +1 -1
  32. package/dist/esm/persistence/DonobuSqliteDb.d.ts +2 -3
  33. package/dist/esm/persistence/DonobuSqliteDb.d.ts.map +1 -1
  34. package/dist/esm/persistence/DonobuSqliteDb.js +37 -6
  35. package/dist/esm/persistence/DonobuSqliteDb.js.map +1 -1
  36. package/dist/esm/persistence/env/EnvPersistenceFactoryImpl.d.ts.map +1 -1
  37. package/dist/esm/persistence/env/EnvPersistenceFactoryImpl.js +1 -1
  38. package/dist/esm/persistence/env/EnvPersistenceFactoryImpl.js.map +1 -1
  39. package/dist/esm/persistence/flows/FlowsPersistenceFactoryImpl.d.ts.map +1 -1
  40. package/dist/esm/persistence/flows/FlowsPersistenceFactoryImpl.js +2 -2
  41. package/dist/esm/persistence/flows/FlowsPersistenceFactoryImpl.js.map +1 -1
  42. package/dist/esm/persistence/flows/FlowsPersistenceSqlite.d.ts.map +1 -1
  43. package/dist/esm/persistence/flows/FlowsPersistenceSqlite.js +21 -16
  44. package/dist/esm/persistence/flows/FlowsPersistenceSqlite.js.map +1 -1
  45. package/dist/esm/tools/AssertPageTextTool.js +1 -1
  46. package/dist/esm/tools/AssertPageTextTool.js.map +1 -1
  47. package/dist/esm/utils/BrowserUtils.d.ts +1 -0
  48. package/dist/esm/utils/BrowserUtils.d.ts.map +1 -1
  49. package/dist/esm/utils/BrowserUtils.js +2 -5
  50. package/dist/esm/utils/BrowserUtils.js.map +1 -1
  51. package/dist/lib/PageAi.d.ts.map +1 -1
  52. package/dist/lib/PageAi.js +10 -11
  53. package/dist/lib/PageAi.js.map +1 -1
  54. package/dist/lib/testExtension.js +1 -1
  55. package/dist/lib/testExtension.js.map +1 -1
  56. package/dist/lib/utils/triageTestFailure.d.ts.map +1 -1
  57. package/dist/lib/utils/triageTestFailure.js +4 -4
  58. package/dist/lib/utils/triageTestFailure.js.map +1 -1
  59. package/dist/managers/DonobuFlow.d.ts.map +1 -1
  60. package/dist/managers/DonobuFlow.js +148 -156
  61. package/dist/managers/DonobuFlow.js.map +1 -1
  62. package/dist/managers/DonobuFlowsManager.d.ts +25 -59
  63. package/dist/managers/DonobuFlowsManager.d.ts.map +1 -1
  64. package/dist/managers/DonobuFlowsManager.js +322 -301
  65. package/dist/managers/DonobuFlowsManager.js.map +1 -1
  66. package/dist/managers/DonobuStack.js +1 -1
  67. package/dist/managers/DonobuStack.js.map +1 -1
  68. package/dist/managers/GptConfigsManager.js +36 -3
  69. package/dist/managers/GptConfigsManager.js.map +1 -1
  70. package/dist/managers/ToolManager.d.ts +2 -0
  71. package/dist/managers/ToolManager.d.ts.map +1 -1
  72. package/dist/managers/ToolManager.js +9 -5
  73. package/dist/managers/ToolManager.js.map +1 -1
  74. package/dist/persistence/DonobuSqliteDb.d.ts +2 -3
  75. package/dist/persistence/DonobuSqliteDb.d.ts.map +1 -1
  76. package/dist/persistence/DonobuSqliteDb.js +37 -6
  77. package/dist/persistence/DonobuSqliteDb.js.map +1 -1
  78. package/dist/persistence/env/EnvPersistenceFactoryImpl.d.ts.map +1 -1
  79. package/dist/persistence/env/EnvPersistenceFactoryImpl.js +1 -1
  80. package/dist/persistence/env/EnvPersistenceFactoryImpl.js.map +1 -1
  81. package/dist/persistence/flows/FlowsPersistenceFactoryImpl.d.ts.map +1 -1
  82. package/dist/persistence/flows/FlowsPersistenceFactoryImpl.js +2 -2
  83. package/dist/persistence/flows/FlowsPersistenceFactoryImpl.js.map +1 -1
  84. package/dist/persistence/flows/FlowsPersistenceSqlite.d.ts.map +1 -1
  85. package/dist/persistence/flows/FlowsPersistenceSqlite.js +21 -16
  86. package/dist/persistence/flows/FlowsPersistenceSqlite.js.map +1 -1
  87. package/dist/tools/AssertPageTextTool.js +1 -1
  88. package/dist/tools/AssertPageTextTool.js.map +1 -1
  89. package/dist/utils/BrowserUtils.d.ts +1 -0
  90. package/dist/utils/BrowserUtils.d.ts.map +1 -1
  91. package/dist/utils/BrowserUtils.js +2 -5
  92. package/dist/utils/BrowserUtils.js.map +1 -1
  93. package/package.json +1 -1
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.DonobuFlowsManager = void 0;
37
+ exports.distillAllowedEnvVariableNames = distillAllowedEnvVariableNames;
37
38
  const BrowserUtils_1 = require("../utils/BrowserUtils");
38
39
  const DonobuFlow_1 = require("./DonobuFlow");
39
40
  const GoToWebpageTool_1 = require("../tools/GoToWebpageTool");
@@ -46,7 +47,7 @@ const UnknownToolException_1 = require("../exceptions/UnknownToolException");
46
47
  const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
47
48
  const FlowNotFoundException_1 = require("../exceptions/FlowNotFoundException");
48
49
  const ToolRequiresGptException_1 = require("../exceptions/ToolRequiresGptException");
49
- const fs = __importStar(require("fs"));
50
+ const fs = __importStar(require("fs/promises"));
50
51
  const path = __importStar(require("path"));
51
52
  const os_1 = require("os");
52
53
  const BrowserStateNotFoundException_1 = require("../exceptions/BrowserStateNotFoundException");
@@ -94,19 +95,19 @@ class DonobuFlowsManager {
94
95
  : await this.createGptClient(flowParams.gptConfigNameOverride ?? null);
95
96
  const initialRunMode = flowParams.initialRunMode ??
96
97
  (gptClientData.gptClient ? 'AUTONOMOUS' : 'INSTRUCT');
97
- const browserConfig = flowParams.browser ?? this.getDefaultBrowserConfig();
98
+ const browserConfig = flowParams.browser ?? getDefaultBrowserConfig(this.environ);
98
99
  if (browserConfig.using.type === 'device' &&
99
100
  !BrowserUtils_1.BrowserUtils.getSupportedDevices().has(browserConfig.using.deviceName ?? '')) {
100
101
  throw new InvalidParamValueException_1.InvalidParamValueException('deviceName', browserConfig.using.deviceName);
101
102
  }
102
- await this.validateFlowParams(flowParams, gptClientData.gptClient, initialRunMode);
103
+ await validateFlowParams(flowParams, gptClientData.gptClient, initialRunMode);
103
104
  const isControlPanelEnabled = !(browserConfig.using.type === 'device'
104
105
  ? browserConfig.using.headless
105
106
  : false) &&
106
107
  (flowParams.isControlPanelEnabled ?? true);
107
- const allowedTools = await this.setupAllowedTools(flowParams, gptClientData !== null);
108
+ const allowedTools = await setupAllowedTools(flowParams, gptClientData !== null);
108
109
  const toolManager = new ToolManager_1.ToolManager(allowedTools);
109
- const toolCallsOnStart = this.prepareInitialToolCalls(flowParams);
110
+ const toolCallsOnStart = prepareInitialToolCalls(flowParams);
110
111
  const messageDuration = flowParams.defaultMessageDuration ??
111
112
  DonobuFlowsManager.DEFAULT_MESSAGE_DURATION;
112
113
  const interactionVisualizer = new InteractionVisualizer_1.InteractionVisualizer(messageDuration);
@@ -119,7 +120,7 @@ class DonobuFlowsManager {
119
120
  if (maxToolCalls !== null && maxToolCalls < 0) {
120
121
  throw new InvalidParamValueException_1.InvalidParamValueException('maxToolCalls', String(flowParams.maxToolCalls));
121
122
  }
122
- const allowedEnvVarsByName = DonobuFlowsManager.distillAllowedEnvVariableNames(flowParams.overallObjective, flowParams.envVars);
123
+ const allowedEnvVarsByName = distillAllowedEnvVariableNames(flowParams.overallObjective, flowParams.envVars);
123
124
  const flowMetadata = {
124
125
  id: flowId,
125
126
  createdWithDonobuVersion: MiscUtils_1.MiscUtils.DONOBU_VERSION,
@@ -149,7 +150,7 @@ class DonobuFlowsManager {
149
150
  };
150
151
  const tempVideoDir = flowMetadata.videoDisabled
151
152
  ? undefined
152
- : this.createTempDirectoryForFlow(flowMetadata.id);
153
+ : await createTempDirectoryForFlow(flowMetadata.id);
153
154
  try {
154
155
  const flowsPersistence = await this.flowsPersistenceFactory.createPersistenceLayer();
155
156
  const envData = await this.buildEnvData(flowMetadata.envVars ?? []);
@@ -189,12 +190,12 @@ class DonobuFlowsManager {
189
190
  }
190
191
  if (tempVideoDir) {
191
192
  try {
192
- await this.setFlowVideo(donobuFlow.metadata.id, tempVideoDir, flowsPersistence);
193
+ await setFlowVideo(donobuFlow.metadata.id, tempVideoDir, flowsPersistence);
193
194
  }
194
195
  catch (error) {
195
196
  Logger_1.appLogger.error('Failed to save flow video:', error);
196
197
  }
197
- this.removeTempDirectoryForFlow(donobuFlow.metadata.id);
198
+ await removeTempDirectoryForFlow(donobuFlow.metadata.id);
198
199
  }
199
200
  });
200
201
  return flowHandle;
@@ -205,13 +206,13 @@ class DonobuFlowsManager {
205
206
  }
206
207
  }
207
208
  catch (error) {
208
- this.removeTempDirectoryForFlow(flowMetadata.id);
209
+ await removeTempDirectoryForFlow(flowMetadata.id);
209
210
  throw error;
210
211
  }
211
212
  });
212
213
  }
213
214
  async renameFlow(flowId, name) {
214
- this.validateFlowName(name);
215
+ validateFlowName(name);
215
216
  const activeFlowHandle = this.isLocallyRunning()
216
217
  ? this.activeFlows.get(flowId)
217
218
  : null;
@@ -304,7 +305,7 @@ class DonobuFlowsManager {
304
305
  */
305
306
  async getFlows(query) {
306
307
  // Parse the composite page token or create initial state.
307
- const paginationState = this.parseCompositePageToken(query.pageToken);
308
+ const paginationState = parseCompositePageToken(query.pageToken);
308
309
  const requestedLimit = Math.min(Math.max(1, query.limit || 100), 100);
309
310
  // Results container.
310
311
  const combinedResults = [];
@@ -364,7 +365,7 @@ class DonobuFlowsManager {
364
365
  return {
365
366
  items: cleanResults,
366
367
  nextPageToken: hasMore
367
- ? this.createCompositePageToken(paginationState)
368
+ ? createCompositePageToken(paginationState)
368
369
  : undefined,
369
370
  };
370
371
  }
@@ -666,257 +667,48 @@ class DonobuFlowsManager {
666
667
  return result;
667
668
  }
668
669
  /**
669
- * Returns a browser config that points to BrowserBase if the
670
- * BROWSERBASE_PROJECT_ID and BROWSERBASE_API_KEY environment variables are
671
- * present, otherwise, returns back a basic config that uses local Chromium.
672
- */
673
- getDefaultBrowserConfig() {
674
- const browserBaseProjectId = this.environ.data.BROWSERBASE_PROJECT_ID;
675
- if (browserBaseProjectId && this.environ.data.BROWSERBASE_API_KEY) {
676
- return {
677
- initialState: undefined,
678
- persistState: false,
679
- using: {
680
- type: 'browserBase',
681
- sessionArgs: {
682
- projectId: browserBaseProjectId,
683
- browserSettings: {
684
- advancedStealth: false, // Advanced Stealth is only available on BrowserBase enterprise plans.
685
- },
686
- keepAlive: false,
687
- proxies: false,
688
- },
689
- },
690
- };
691
- }
692
- else {
693
- return {
694
- initialState: undefined,
695
- persistState: false,
696
- using: {
697
- type: 'device',
698
- deviceName: 'Desktop Chromium',
699
- headless: false,
700
- },
701
- };
702
- }
703
- }
704
- /**
705
- * Extracts environment variable names from the given objective and combines
706
- * it with the given explicitly allowed variables.
707
- *
708
- * This function performs two operations:
709
- * 1. Extracts environment variable references (in the form `$.env.VARIABLE_NAME`) from the overall objective.
710
- * 2. Combines these with any explicitly allowed environment variable names.
711
- *
712
- * The resulting array contains unique environment variable names without duplicates.
713
- *
714
- * @param overallObjective - The objective text that may contain environment variable references.
715
- * @param explicitlyAllowedEnvVariableNames - Additional environment variable names explicitly allowed.
716
- * @returns An array of unique environment variable names that are allowed to be accessed.
717
- *
718
- * @example
719
- * // Returns ["API_KEY", "USER_NAME", "DEBUG_MODE"]
720
- * distillAllowedEnvVariableNames(
721
- * "Use {{$.env.API_KEY}} to authenticate and greet {{$.env.USER_NAME}}",
722
- * ["API_KEY", "DEBUG_MODE"]
723
- * );
670
+ * Extracts dependencies for a single flow by analyzing its browser.initialState
724
671
  */
725
- static distillAllowedEnvVariableNames(overallObjective, explicitlyAllowedEnvVariableNames) {
726
- let allowedEnvVarsByName = overallObjective
727
- ? (0, TemplateInterpolator_1.extractInterpolationExpressions)(overallObjective)
728
- .filter((exp) => {
729
- return exp.startsWith('$.env.');
730
- })
731
- .map((exp) => {
732
- return exp.substring('$.env.'.length);
733
- })
734
- : [];
735
- // Concatonate that with the explicitly requested environment variables.
736
- allowedEnvVarsByName = Array.from(new Set(allowedEnvVarsByName.concat(explicitlyAllowedEnvVariableNames ?? [])));
737
- return allowedEnvVarsByName;
738
- }
739
- validateFlowName(flowName) {
740
- if (flowName && flowName.length > 255) {
741
- throw new InvalidParamValueException_1.InvalidParamValueException('name', flowName, 'the value cannot be longer than 255 characters');
742
- }
743
- }
744
- async validateFlowParams(flowParams, gptClient, initialRunMode) {
745
- const validTargetWebsiteProtocols = ['https:', 'http:'];
746
- const validCallbackUrlProtocols = ['https:', 'http:'];
747
- const parsedTargetWebsite = DonobuFlowsManager.parseUrl(flowParams.targetWebsite);
748
- const parsedCallbackUrl = DonobuFlowsManager.parseUrl(flowParams.callbackUrl);
749
- if (parsedTargetWebsite &&
750
- !validTargetWebsiteProtocols.some((protocol) => protocol === parsedTargetWebsite.protocol)) {
751
- throw new InvalidParamValueException_1.InvalidParamValueException('targetWebsite', flowParams.targetWebsite, 'the URL must start with a supported protocol (example: "https://")');
752
- }
753
- else if (flowParams.targetWebsite && !parsedTargetWebsite) {
754
- throw new InvalidParamValueException_1.InvalidParamValueException('targetWebsite', flowParams.targetWebsite, 'the URL is malformed');
755
- }
756
- if (parsedCallbackUrl &&
757
- !validCallbackUrlProtocols.some((protocol) => protocol === parsedCallbackUrl.protocol)) {
758
- throw new InvalidParamValueException_1.InvalidParamValueException('callbackUrl', flowParams.callbackUrl, 'the URL must start with a supported protocol (example: "https://")');
759
- }
760
- else if (flowParams.callbackUrl && !parsedCallbackUrl) {
761
- throw new InvalidParamValueException_1.InvalidParamValueException('callbackUrl', flowParams.callbackUrl, 'the URL is malformed');
672
+ async extractFlowDependencies(flow, nameToIdMap) {
673
+ const initialState = flow.browser?.initialState;
674
+ if (!initialState) {
675
+ return [];
762
676
  }
763
- this.validateFlowName(flowParams.name);
764
- switch (initialRunMode) {
765
- case 'AUTONOMOUS':
766
- if ((flowParams.overallObjective?.trim().length ?? 0) === 0) {
767
- throw new InvalidParamValueException_1.InvalidParamValueException('overallObjective', flowParams.overallObjective, `'initialRunMode' has a value of '${initialRunMode}'`);
677
+ switch (initialState.type) {
678
+ case 'id':
679
+ // Direct flow ID reference
680
+ try {
681
+ await this.getFlowById(initialState.value);
682
+ return [initialState.value];
768
683
  }
769
- if (!gptClient) {
770
- throw new InvalidParamValueException_1.InvalidParamValueException('initialRunMode', initialRunMode, `no GPT client is available`);
684
+ catch (_error) {
685
+ Logger_1.appLogger.warn(`Flow dependency not found: flow with ID "${initialState.value}" does not exist`);
686
+ return [];
771
687
  }
772
- break;
773
- case 'INSTRUCT':
774
- break;
775
- case 'DETERMINISTIC':
776
- break;
688
+ case 'name':
689
+ // Flow name reference - resolve to ID
690
+ const flowId = nameToIdMap.get(initialState.value);
691
+ if (flowId) {
692
+ return [flowId];
693
+ }
694
+ // If not in our current map, try to find it by querying all flows
695
+ try {
696
+ const dependentFlow = await this.getFlowByName(initialState.value);
697
+ nameToIdMap.set(initialState.value, dependentFlow.id);
698
+ return [dependentFlow.id];
699
+ }
700
+ catch (_error) {
701
+ Logger_1.appLogger.warn(`Flow dependency not found: flow with name "${initialState.value}" does not exist`);
702
+ return [];
703
+ }
704
+ case 'json':
705
+ // Direct JSON state - no dependencies
706
+ return [];
777
707
  default:
778
- throw new InvalidParamValueException_1.InvalidParamValueException('initialRunMode', initialRunMode);
779
- }
780
- if (!gptClient) {
781
- await this.checkIfAnyToolsRequireGpt(flowParams.allowedTools ?? null, flowParams.toolCallsOnStart ?? null);
782
- }
783
- }
784
- async setupAllowedTools(flowParams, hasGptClient) {
785
- const customTools = flowParams.customTools?.map((tool) => new CustomToolRunnerTool_1.CustomToolRunnerTool(tool)) ??
786
- [];
787
- const toolsNeededOnStart = flowParams.toolCallsOnStart?.map((t) => t.name) ?? [];
788
- let prepackagedTools;
789
- if (flowParams.allowedTools?.length) {
790
- const allowedTools = [...toolsNeededOnStart, ...flowParams.allowedTools];
791
- // The user has specified a list of tools to use.
792
- prepackagedTools = (await ToolManager_1.ToolManager.allTools()).filter((tool) => allowedTools.includes(tool.name));
793
- }
794
- else {
795
- // The user has not specified a list of tools to use, so use the default.
796
- const defaultTools = await ToolManager_1.ToolManager.defaultTools();
797
- const allowedTools = [
798
- ...toolsNeededOnStart,
799
- ...defaultTools.map((t) => t.name),
800
- ];
801
- prepackagedTools = (await ToolManager_1.ToolManager.allTools()).filter((tool) => allowedTools.includes(tool.name));
802
- }
803
- // If there is no GPT client, only include tools that do not require it.
804
- const prepackagedToolsWithGptFiltered = prepackagedTools.filter((tool) => {
805
- return hasGptClient || !tool.requiresGpt;
806
- });
807
- return [...customTools, ...prepackagedToolsWithGptFiltered];
808
- }
809
- prepareInitialToolCalls(flowParams) {
810
- if (!flowParams.toolCallsOnStart?.length && flowParams.targetWebsite) {
811
- return [
812
- {
813
- name: GoToWebpageTool_1.GoToWebpageTool.NAME,
814
- parameters: {
815
- rationale: 'Initializing web navigation.',
816
- url: flowParams.targetWebsite.toString(),
817
- },
818
- },
819
- ];
820
- }
821
- if (flowParams.toolCallsOnStart) {
822
- return flowParams.toolCallsOnStart;
823
- }
824
- else {
825
- return [];
826
- }
827
- }
828
- /**
829
- * This method creates a temporary directory for the flow with the given ID,
830
- * returning the path to the directory.
831
- */
832
- createTempDirectoryForFlow(flowId) {
833
- const tempDir = path.join((0, os_1.tmpdir)(), flowId);
834
- fs.mkdirSync(tempDir);
835
- return tempDir;
836
- }
837
- removeTempDirectoryForFlow(flowId) {
838
- try {
839
- const tempDir = path.join((0, os_1.tmpdir)(), flowId);
840
- fs.rmSync(tempDir, { recursive: true, force: true });
841
- }
842
- catch (error) {
843
- Logger_1.appLogger.error('Failed to remove temporary directory:', error);
844
- }
845
- }
846
- /**
847
- * Searches the given directory for the largest video file and sets it as the
848
- * flow's video. This should be called after the flow has completed.
849
- */
850
- async setFlowVideo(flowId, flowTempDir, flowsPersistence) {
851
- const files = fs
852
- .readdirSync(flowTempDir)
853
- .filter((file) => file.endsWith('.webm'))
854
- .map((file) => path.join(flowTempDir, file));
855
- const videoPath = files.length
856
- ? files.reduce((a, b) => fs.statSync(a).size > fs.statSync(b).size ? a : b)
857
- : null;
858
- if (videoPath) {
859
- const videoBytes = fs.readFileSync(videoPath);
860
- await flowsPersistence.setVideo(flowId, videoBytes);
861
- }
862
- }
863
- isLocallyRunning() {
864
- return this.deploymentEnvironment === 'LOCAL';
865
- }
866
- async checkIfAnyToolsRequireGpt(requestedTools, toolCallsOnStart) {
867
- const toolMap = new Map((await ToolManager_1.ToolManager.allTools()).map((tool) => [tool.name, tool]));
868
- const requestedToolsRequiringGpt = [...(requestedTools ?? [])].filter((name) => toolMap.get(name)?.requiresGpt);
869
- if (requestedToolsRequiringGpt.length) {
870
- throw new ToolRequiresGptException_1.ToolRequiresGptException(requestedToolsRequiringGpt[0]);
871
- }
872
- const toolCallsRequiringGpt = toolCallsOnStart?.filter((call) => toolMap.get(call.name)?.requiresGpt);
873
- if (toolCallsRequiringGpt?.length) {
874
- throw new ToolRequiresGptException_1.ToolRequiresGptException(toolCallsRequiringGpt[0].name);
875
- }
876
- }
877
- /**
878
- * Parses the given argument as a URL, returning null on failure.
879
- */
880
- static parseUrl(url) {
881
- if (!url) {
882
- return null;
883
- }
884
- try {
885
- return new URL(url);
886
- }
887
- catch (_) {
888
- return null;
889
- }
890
- }
891
- /**
892
- * Parse a composite page token into pagination state
893
- */
894
- parseCompositePageToken(token) {
895
- if (!token) {
896
- return {
897
- sourceTokens: {},
898
- exhaustedSources: [],
899
- cursorTimestamp: null,
900
- };
901
- }
902
- try {
903
- return JSON.parse(Buffer.from(token, 'base64').toString('utf8'));
904
- }
905
- catch (_error) {
906
- // If token parsing fails, start fresh
907
- return {
908
- sourceTokens: {},
909
- exhaustedSources: [],
910
- cursorTimestamp: null,
911
- };
708
+ Logger_1.appLogger.warn(`Unknown browser state reference type: ${initialState.type}`);
709
+ return [];
912
710
  }
913
711
  }
914
- /**
915
- * Create a composite page token from pagination state
916
- */
917
- createCompositePageToken(state) {
918
- return Buffer.from(JSON.stringify(state)).toString('base64');
919
- }
920
712
  /**
921
713
  * Resolves the complete set of flow IDs including all transitive dependencies.
922
714
  * Uses breadth-first search to discover all dependencies recursively.
@@ -954,54 +746,11 @@ class DonobuFlowsManager {
954
746
  }
955
747
  catch (_error) {
956
748
  // If a flow doesn't exist, skip it but continue processing
957
- console.warn(`Flow ${currentFlowId} not found, skipping dependency resolution for this flow`);
749
+ Logger_1.appLogger.warn(`Flow ${currentFlowId} not found, skipping dependency resolution for this flow`);
958
750
  }
959
751
  }
960
752
  return Array.from(resolvedFlowIds);
961
753
  }
962
- /**
963
- * Extracts dependencies for a single flow by analyzing its browser.initialState
964
- */
965
- async extractFlowDependencies(flow, nameToIdMap) {
966
- const initialState = flow.browser?.initialState;
967
- if (!initialState) {
968
- return [];
969
- }
970
- switch (initialState.type) {
971
- case 'id':
972
- // Direct flow ID reference
973
- try {
974
- await this.getFlowById(initialState.value);
975
- return [initialState.value];
976
- }
977
- catch (_error) {
978
- console.warn(`Flow dependency not found: flow with ID "${initialState.value}" does not exist`);
979
- return [];
980
- }
981
- case 'name':
982
- // Flow name reference - resolve to ID
983
- const flowId = nameToIdMap.get(initialState.value);
984
- if (flowId) {
985
- return [flowId];
986
- }
987
- // If not in our current map, try to find it by querying all flows
988
- try {
989
- const dependentFlow = await this.getFlowByName(initialState.value);
990
- nameToIdMap.set(initialState.value, dependentFlow.id);
991
- return [dependentFlow.id];
992
- }
993
- catch (_error) {
994
- console.warn(`Flow dependency not found: flow with name "${initialState.value}" does not exist`);
995
- return [];
996
- }
997
- case 'json':
998
- // Direct JSON state - no dependencies
999
- return [];
1000
- default:
1001
- console.warn(`Unknown browser state reference type: ${initialState.type}`);
1002
- return [];
1003
- }
1004
- }
1005
754
  /**
1006
755
  * Determines if a new flow ID should replace an existing flow ID in the name mapping.
1007
756
  */
@@ -1016,9 +765,281 @@ class DonobuFlowsManager {
1016
765
  return true;
1017
766
  }
1018
767
  }
768
+ isLocallyRunning() {
769
+ return this.deploymentEnvironment === 'LOCAL';
770
+ }
1019
771
  }
1020
772
  exports.DonobuFlowsManager = DonobuFlowsManager;
1021
773
  DonobuFlowsManager.DEFAULT_MESSAGE_DURATION = 2247;
1022
774
  DonobuFlowsManager.DEFAULT_MAX_TOOL_CALLS = 50;
1023
775
  DonobuFlowsManager.DEFAULT_BROWSER_STATE_FILENAME = 'browserstate.json';
776
+ /**
777
+ * Extracts environment variable names from the given objective and combines
778
+ * it with the given explicitly allowed variables.
779
+ *
780
+ * This function performs two operations:
781
+ * 1. Extracts environment variable references (in the form `$.env.VARIABLE_NAME`) from the overall objective.
782
+ * 2. Combines these with any explicitly allowed environment variable names.
783
+ *
784
+ * The resulting array contains unique environment variable names without duplicates.
785
+ *
786
+ * @param overallObjective - The objective text that may contain environment variable references.
787
+ * @param explicitlyAllowedEnvVariableNames - Additional environment variable names explicitly allowed.
788
+ * @returns An array of unique environment variable names that are allowed to be accessed.
789
+ *
790
+ * @example
791
+ * // Returns ["API_KEY", "USER_NAME", "DEBUG_MODE"]
792
+ * distillAllowedEnvVariableNames(
793
+ * "Use {{$.env.API_KEY}} to authenticate and greet {{$.env.USER_NAME}}",
794
+ * ["API_KEY", "DEBUG_MODE"]
795
+ * );
796
+ */
797
+ function distillAllowedEnvVariableNames(overallObjective, explicitlyAllowedEnvVariableNames) {
798
+ let allowedEnvVarsByName = overallObjective
799
+ ? (0, TemplateInterpolator_1.extractInterpolationExpressions)(overallObjective)
800
+ .filter((exp) => {
801
+ return exp.startsWith('$.env.');
802
+ })
803
+ .map((exp) => {
804
+ return exp.substring('$.env.'.length);
805
+ })
806
+ : [];
807
+ // Concatonate that with the explicitly requested environment variables.
808
+ allowedEnvVarsByName = Array.from(new Set(allowedEnvVarsByName.concat(explicitlyAllowedEnvVariableNames ?? [])));
809
+ return allowedEnvVarsByName;
810
+ }
811
+ /**
812
+ * Returns a browser config that points to BrowserBase if the
813
+ * BROWSERBASE_PROJECT_ID and BROWSERBASE_API_KEY environment variables are
814
+ * present, otherwise, returns back a basic config that uses local Chromium.
815
+ */
816
+ function getDefaultBrowserConfig(environ) {
817
+ const browserBaseProjectId = environ.data.BROWSERBASE_PROJECT_ID;
818
+ if (browserBaseProjectId && environ.data.BROWSERBASE_API_KEY) {
819
+ return {
820
+ initialState: undefined,
821
+ persistState: false,
822
+ using: {
823
+ type: 'browserBase',
824
+ sessionArgs: {
825
+ projectId: browserBaseProjectId,
826
+ browserSettings: {
827
+ advancedStealth: false, // Advanced Stealth is only available on BrowserBase enterprise plans.
828
+ },
829
+ keepAlive: false,
830
+ proxies: false,
831
+ },
832
+ },
833
+ };
834
+ }
835
+ else {
836
+ return {
837
+ initialState: undefined,
838
+ persistState: false,
839
+ using: {
840
+ type: 'device',
841
+ deviceName: 'Desktop Chromium',
842
+ headless: false,
843
+ },
844
+ };
845
+ }
846
+ }
847
+ /**
848
+ * Parses the given argument as a URL, returning null on failure.
849
+ */
850
+ function parseUrl(url) {
851
+ if (!url) {
852
+ return null;
853
+ }
854
+ try {
855
+ return new URL(url);
856
+ }
857
+ catch (_) {
858
+ return null;
859
+ }
860
+ }
861
+ /**
862
+ * Create a composite page token from pagination state
863
+ */
864
+ function createCompositePageToken(state) {
865
+ return Buffer.from(JSON.stringify(state)).toString('base64');
866
+ }
867
+ function validateFlowName(flowName) {
868
+ if (flowName && flowName.length > 255) {
869
+ throw new InvalidParamValueException_1.InvalidParamValueException('name', flowName, 'the value cannot be longer than 255 characters');
870
+ }
871
+ }
872
+ async function validateFlowParams(flowParams, gptClient, initialRunMode) {
873
+ const validTargetWebsiteProtocols = ['https:', 'http:'];
874
+ const validCallbackUrlProtocols = ['https:', 'http:'];
875
+ const parsedTargetWebsite = parseUrl(flowParams.targetWebsite);
876
+ const parsedCallbackUrl = parseUrl(flowParams.callbackUrl);
877
+ if (parsedTargetWebsite &&
878
+ !validTargetWebsiteProtocols.some((protocol) => protocol === parsedTargetWebsite.protocol)) {
879
+ throw new InvalidParamValueException_1.InvalidParamValueException('targetWebsite', flowParams.targetWebsite, 'the URL must start with a supported protocol (example: "https://")');
880
+ }
881
+ else if (flowParams.targetWebsite && !parsedTargetWebsite) {
882
+ throw new InvalidParamValueException_1.InvalidParamValueException('targetWebsite', flowParams.targetWebsite, 'the URL is malformed');
883
+ }
884
+ if (parsedCallbackUrl &&
885
+ !validCallbackUrlProtocols.some((protocol) => protocol === parsedCallbackUrl.protocol)) {
886
+ throw new InvalidParamValueException_1.InvalidParamValueException('callbackUrl', flowParams.callbackUrl, 'the URL must start with a supported protocol (example: "https://")');
887
+ }
888
+ else if (flowParams.callbackUrl && !parsedCallbackUrl) {
889
+ throw new InvalidParamValueException_1.InvalidParamValueException('callbackUrl', flowParams.callbackUrl, 'the URL is malformed');
890
+ }
891
+ validateFlowName(flowParams.name);
892
+ switch (initialRunMode) {
893
+ case 'AUTONOMOUS':
894
+ if ((flowParams.overallObjective?.trim().length ?? 0) === 0) {
895
+ throw new InvalidParamValueException_1.InvalidParamValueException('overallObjective', flowParams.overallObjective, `'initialRunMode' has a value of '${initialRunMode}'`);
896
+ }
897
+ if (!gptClient) {
898
+ throw new InvalidParamValueException_1.InvalidParamValueException('initialRunMode', initialRunMode, `no GPT client is available`);
899
+ }
900
+ break;
901
+ case 'INSTRUCT':
902
+ break;
903
+ case 'DETERMINISTIC':
904
+ break;
905
+ default:
906
+ throw new InvalidParamValueException_1.InvalidParamValueException('initialRunMode', initialRunMode);
907
+ }
908
+ if (!gptClient) {
909
+ await throwIfAnyToolsRequireGpt(flowParams.allowedTools ?? null, flowParams.toolCallsOnStart ?? null);
910
+ }
911
+ }
912
+ async function setupAllowedTools(flowParams, hasGptClient) {
913
+ const customTools = flowParams.customTools?.map((tool) => new CustomToolRunnerTool_1.CustomToolRunnerTool(tool)) ?? [];
914
+ const toolsNeededOnStart = flowParams.toolCallsOnStart?.map((t) => t.name) ?? [];
915
+ let prepackagedTools;
916
+ if (flowParams.allowedTools?.length) {
917
+ const allowedTools = [...toolsNeededOnStart, ...flowParams.allowedTools];
918
+ // The user has specified a list of tools to use.
919
+ prepackagedTools = (await ToolManager_1.ToolManager.allTools()).filter((tool) => allowedTools.includes(tool.name));
920
+ }
921
+ else {
922
+ // The user has not specified a list of tools to use, so use the default.
923
+ const defaultTools = await ToolManager_1.ToolManager.defaultTools();
924
+ const allowedTools = [
925
+ ...toolsNeededOnStart,
926
+ ...defaultTools.map((t) => t.name),
927
+ ];
928
+ prepackagedTools = (await ToolManager_1.ToolManager.allTools()).filter((tool) => allowedTools.includes(tool.name));
929
+ }
930
+ // If there is no GPT client, only include tools that do not require it.
931
+ const prepackagedToolsWithGptFiltered = prepackagedTools.filter((tool) => {
932
+ return hasGptClient || !tool.requiresGpt;
933
+ });
934
+ const minimalTools = await ToolManager_1.ToolManager.minimalTools();
935
+ const existingToolNames = new Set([...customTools, ...prepackagedToolsWithGptFiltered].map((tool) => tool.name));
936
+ const missingMinimalTools = minimalTools.filter((tool) => !existingToolNames.has(tool.name));
937
+ const combinedTools = [
938
+ ...customTools,
939
+ ...prepackagedToolsWithGptFiltered,
940
+ ...missingMinimalTools,
941
+ ];
942
+ const dedupedTools = Array.from(combinedTools
943
+ .reduce((uniqueTools, tool) => {
944
+ if (!uniqueTools.has(tool.name)) {
945
+ uniqueTools.set(tool.name, tool);
946
+ }
947
+ return uniqueTools;
948
+ }, new Map())
949
+ .values());
950
+ return dedupedTools.sort((a, b) => a.name.localeCompare(b.name));
951
+ }
952
+ function prepareInitialToolCalls(flowParams) {
953
+ if (!flowParams.toolCallsOnStart?.length && flowParams.targetWebsite) {
954
+ return [
955
+ {
956
+ name: GoToWebpageTool_1.GoToWebpageTool.NAME,
957
+ parameters: {
958
+ rationale: 'Initializing web navigation.',
959
+ url: flowParams.targetWebsite.toString(),
960
+ },
961
+ },
962
+ ];
963
+ }
964
+ if (flowParams.toolCallsOnStart) {
965
+ return flowParams.toolCallsOnStart;
966
+ }
967
+ else {
968
+ return [];
969
+ }
970
+ }
971
+ /**
972
+ * This method creates a temporary directory for the flow with the given ID,
973
+ * returning the path to the directory.
974
+ */
975
+ async function createTempDirectoryForFlow(flowId) {
976
+ const tempDir = path.join((0, os_1.tmpdir)(), flowId);
977
+ await fs.mkdir(tempDir);
978
+ return tempDir;
979
+ }
980
+ async function removeTempDirectoryForFlow(flowId) {
981
+ try {
982
+ const tempDir = path.join((0, os_1.tmpdir)(), flowId);
983
+ await fs.rm(tempDir, { recursive: true, force: true });
984
+ }
985
+ catch (error) {
986
+ Logger_1.appLogger.error('Failed to remove temporary directory:', error);
987
+ }
988
+ }
989
+ /**
990
+ * Searches the given directory for the largest video file and sets it as the
991
+ * flow's video. This should be called after the flow has completed.
992
+ */
993
+ async function setFlowVideo(flowId, flowTempDir, flowsPersistence) {
994
+ const files = (await fs.readdir(flowTempDir))
995
+ .filter((file) => file.endsWith('.webm'))
996
+ .map((file) => path.join(flowTempDir, file));
997
+ if (!files.length) {
998
+ return;
999
+ }
1000
+ const filesWithSizes = await Promise.all(files.map(async (filePath) => ({
1001
+ filePath,
1002
+ size: (await fs.stat(filePath)).size,
1003
+ })));
1004
+ const largestFile = filesWithSizes.reduce((largest, current) => current.size > largest.size ? current : largest);
1005
+ const videoPath = largestFile?.filePath ?? null;
1006
+ if (videoPath) {
1007
+ const videoBytes = await fs.readFile(videoPath);
1008
+ await flowsPersistence.setVideo(flowId, videoBytes);
1009
+ }
1010
+ }
1011
+ async function throwIfAnyToolsRequireGpt(requestedTools, toolCallsOnStart) {
1012
+ const toolMap = new Map((await ToolManager_1.ToolManager.allTools()).map((tool) => [tool.name, tool]));
1013
+ const requestedToolsRequiringGpt = [...(requestedTools ?? [])].filter((name) => toolMap.get(name)?.requiresGpt);
1014
+ if (requestedToolsRequiringGpt.length) {
1015
+ throw new ToolRequiresGptException_1.ToolRequiresGptException(requestedToolsRequiringGpt[0]);
1016
+ }
1017
+ const toolCallsRequiringGpt = toolCallsOnStart?.filter((call) => toolMap.get(call.name)?.requiresGpt);
1018
+ if (toolCallsRequiringGpt?.length) {
1019
+ throw new ToolRequiresGptException_1.ToolRequiresGptException(toolCallsRequiringGpt[0].name);
1020
+ }
1021
+ }
1022
+ /**
1023
+ * Parse a composite page token into pagination state
1024
+ */
1025
+ function parseCompositePageToken(token) {
1026
+ if (!token) {
1027
+ return {
1028
+ sourceTokens: {},
1029
+ exhaustedSources: [],
1030
+ cursorTimestamp: null,
1031
+ };
1032
+ }
1033
+ try {
1034
+ return JSON.parse(Buffer.from(token, 'base64').toString('utf8'));
1035
+ }
1036
+ catch (_error) {
1037
+ // If token parsing fails, start fresh
1038
+ return {
1039
+ sourceTokens: {},
1040
+ exhaustedSources: [],
1041
+ cursorTimestamp: null,
1042
+ };
1043
+ }
1044
+ }
1024
1045
  //# sourceMappingURL=DonobuFlowsManager.js.map