donobu 5.27.2 → 5.27.3
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.
|
@@ -108,24 +108,30 @@ export declare class DonobuFlow {
|
|
|
108
108
|
/**
|
|
109
109
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
110
110
|
*
|
|
111
|
-
* Browser session state and the
|
|
112
|
-
* whichever code path produced the terminal state
|
|
113
|
-
* tool-driven completion;
|
|
114
|
-
*
|
|
111
|
+
* Browser session state and the terminal-state metadata write are
|
|
112
|
+
* committed by whichever code path produced the terminal state
|
|
113
|
+
* (transitionState for tool-driven completion; onTargetClosed /
|
|
114
|
+
* onPersistentGptFailure / onInsufficientQuota / onUnexpectedException
|
|
115
|
+
* for failure paths) — by the time we reach onComplete those have
|
|
116
|
+
* already happened. This method just runs the post-completion side
|
|
117
|
+
* effects.
|
|
115
118
|
*/
|
|
116
119
|
private onComplete;
|
|
117
120
|
/**
|
|
118
121
|
* Persists the current browser session state if the flow's config has
|
|
119
|
-
* `persistState` enabled. Must be called BEFORE
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
122
|
+
* `persistState` enabled. Must be called BEFORE the in-memory `state`
|
|
123
|
+
* is mutated to a terminal value at every site that produces a
|
|
124
|
+
* terminal state — otherwise FlowCatalog.getFlowById can read the
|
|
125
|
+
* live FlowMetadata object (LOCAL deployments) and a frontend that
|
|
126
|
+
* observes the terminal state will race the (potentially network-
|
|
127
|
+
* bound) upload here, getting a 404 from a subsequent browser-state
|
|
128
|
+
* fetch.
|
|
125
129
|
*
|
|
126
130
|
* The browser context typically survives all-pages-closed (the read
|
|
127
|
-
* goes against the context, not a specific page), so this is
|
|
128
|
-
*
|
|
131
|
+
* goes against the context, not a specific page), so this is safe to
|
|
132
|
+
* call from failure handlers like onTargetClosed. If the read does
|
|
133
|
+
* fail, persistSessionState catches and logs internally — it doesn't
|
|
134
|
+
* propagate.
|
|
129
135
|
*/
|
|
130
136
|
private persistTerminalSessionStateIfNeeded;
|
|
131
137
|
/**
|
|
@@ -215,10 +215,18 @@ class DonobuFlow {
|
|
|
215
215
|
async onTargetClosed() {
|
|
216
216
|
const result = await this.targetInspector.handleTargetClosed();
|
|
217
217
|
if (!result.recovered) {
|
|
218
|
-
|
|
218
|
+
// Persist browser state BEFORE flipping the in-memory `state` to
|
|
219
|
+
// a terminal value. FlowCatalog.getFlowById serves the *live*
|
|
220
|
+
// FlowMetadata object for LOCAL deployments, so the next frontend
|
|
221
|
+
// poll observes terminal state the moment we mutate `state`.
|
|
222
|
+
// If we do that before the (potentially network-bound) session
|
|
223
|
+
// upload, an eager browser-state fetch from the frontend (e.g.
|
|
224
|
+
// FlowDeveloperTools auto-loads on terminal) races the upload and
|
|
225
|
+
// 404s. Same rationale as the ordering in transitionState.
|
|
219
226
|
Logger_1.appLogger.error(result.reason);
|
|
220
|
-
this.metadata.result = { failed: result.reason };
|
|
221
227
|
await this.persistTerminalSessionStateIfNeeded();
|
|
228
|
+
this.metadata.result = { failed: result.reason };
|
|
229
|
+
this.metadata.state = 'FAILED';
|
|
222
230
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
223
231
|
}
|
|
224
232
|
}
|
|
@@ -227,13 +235,13 @@ class DonobuFlow {
|
|
|
227
235
|
* are internal retries). This method will mark the flow as a failure.
|
|
228
236
|
*/
|
|
229
237
|
async onPersistentGptFailure(error) {
|
|
230
|
-
this.metadata.state = 'FAILED';
|
|
231
238
|
Logger_1.appLogger.error(`Stopped flow due to the ${this.gptClient?.config.type} GPT platform throwing an internal error!`, error);
|
|
239
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
232
240
|
this.metadata.result = {
|
|
233
241
|
failed: `Stopped flow due to the ${this.gptClient?.config.type} GPT platform throwing an internal error!`,
|
|
234
242
|
context: error.message,
|
|
235
243
|
};
|
|
236
|
-
|
|
244
|
+
this.metadata.state = 'FAILED';
|
|
237
245
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
238
246
|
}
|
|
239
247
|
/**
|
|
@@ -241,15 +249,15 @@ class DonobuFlow {
|
|
|
241
249
|
* usage quota or credits have been exhausted (HTTP 402).
|
|
242
250
|
*/
|
|
243
251
|
async onInsufficientQuota(error) {
|
|
244
|
-
this.metadata.state = 'FAILED';
|
|
245
252
|
const platform = error.gptPlatform;
|
|
246
253
|
const isDonobu = platform === 'DONOBU';
|
|
247
254
|
const failedMessage = isDonobu
|
|
248
255
|
? 'Your Donobu AI credits have been exhausted. Please add more credits to your account to continue running flows.'
|
|
249
256
|
: `Your ${platform} API quota has been exhausted. Please check your account's billing and usage limits; this may happen if there is a lack of funds in the account`;
|
|
250
257
|
Logger_1.appLogger.error(failedMessage, error);
|
|
251
|
-
this.metadata.result = { failed: failedMessage };
|
|
252
258
|
await this.persistTerminalSessionStateIfNeeded();
|
|
259
|
+
this.metadata.result = { failed: failedMessage };
|
|
260
|
+
this.metadata.state = 'FAILED';
|
|
253
261
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
254
262
|
}
|
|
255
263
|
/**
|
|
@@ -339,21 +347,24 @@ class DonobuFlow {
|
|
|
339
347
|
* method will mark the flow as a failure.
|
|
340
348
|
*/
|
|
341
349
|
async onUnexpectedException(error) {
|
|
342
|
-
this.metadata.state = 'FAILED';
|
|
343
350
|
Logger_1.appLogger.error('Stopped flow due to exception!', error);
|
|
351
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
344
352
|
this.metadata.result = {
|
|
345
353
|
failed: 'Internal error 🙈',
|
|
346
354
|
};
|
|
347
|
-
|
|
355
|
+
this.metadata.state = 'FAILED';
|
|
348
356
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
349
357
|
}
|
|
350
358
|
/**
|
|
351
359
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
352
360
|
*
|
|
353
|
-
* Browser session state and the
|
|
354
|
-
* whichever code path produced the terminal state
|
|
355
|
-
* tool-driven completion;
|
|
356
|
-
*
|
|
361
|
+
* Browser session state and the terminal-state metadata write are
|
|
362
|
+
* committed by whichever code path produced the terminal state
|
|
363
|
+
* (transitionState for tool-driven completion; onTargetClosed /
|
|
364
|
+
* onPersistentGptFailure / onInsufficientQuota / onUnexpectedException
|
|
365
|
+
* for failure paths) — by the time we reach onComplete those have
|
|
366
|
+
* already happened. This method just runs the post-completion side
|
|
367
|
+
* effects.
|
|
357
368
|
*/
|
|
358
369
|
async onComplete() {
|
|
359
370
|
DonobuFlow.invokeFlowFinishedCallback(this.metadata.callbackUrl, this.metadata.id);
|
|
@@ -361,16 +372,19 @@ class DonobuFlow {
|
|
|
361
372
|
}
|
|
362
373
|
/**
|
|
363
374
|
* Persists the current browser session state if the flow's config has
|
|
364
|
-
* `persistState` enabled. Must be called BEFORE
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
375
|
+
* `persistState` enabled. Must be called BEFORE the in-memory `state`
|
|
376
|
+
* is mutated to a terminal value at every site that produces a
|
|
377
|
+
* terminal state — otherwise FlowCatalog.getFlowById can read the
|
|
378
|
+
* live FlowMetadata object (LOCAL deployments) and a frontend that
|
|
379
|
+
* observes the terminal state will race the (potentially network-
|
|
380
|
+
* bound) upload here, getting a 404 from a subsequent browser-state
|
|
381
|
+
* fetch.
|
|
370
382
|
*
|
|
371
383
|
* The browser context typically survives all-pages-closed (the read
|
|
372
|
-
* goes against the context, not a specific page), so this is
|
|
373
|
-
*
|
|
384
|
+
* goes against the context, not a specific page), so this is safe to
|
|
385
|
+
* call from failure handlers like onTargetClosed. If the read does
|
|
386
|
+
* fail, persistSessionState catches and logs internally — it doesn't
|
|
387
|
+
* propagate.
|
|
374
388
|
*/
|
|
375
389
|
async persistTerminalSessionStateIfNeeded() {
|
|
376
390
|
if (this.metadata.web?.browser?.persistState) {
|
|
@@ -647,10 +661,8 @@ Message: ${dialog.message()}`;
|
|
|
647
661
|
// this, then someone polling for a flow's state may see the flow
|
|
648
662
|
// finished but still see a null result.
|
|
649
663
|
//
|
|
650
|
-
// The same rationale applies to the browser session state
|
|
651
|
-
//
|
|
652
|
-
// terminal state) would otherwise race the upload that onComplete
|
|
653
|
-
// performs and 404 until the user manually refreshed.
|
|
664
|
+
// The same rationale applies to the browser session state — see
|
|
665
|
+
// persistTerminalSessionStateIfNeeded for the full ordering note.
|
|
654
666
|
if ((0, FlowMetadata_1.isComplete)(nextState)) {
|
|
655
667
|
this.metadata.result = await this.createResultJson(nextState);
|
|
656
668
|
await this.persistTerminalSessionStateIfNeeded();
|
|
@@ -108,24 +108,30 @@ export declare class DonobuFlow {
|
|
|
108
108
|
/**
|
|
109
109
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
110
110
|
*
|
|
111
|
-
* Browser session state and the
|
|
112
|
-
* whichever code path produced the terminal state
|
|
113
|
-
* tool-driven completion;
|
|
114
|
-
*
|
|
111
|
+
* Browser session state and the terminal-state metadata write are
|
|
112
|
+
* committed by whichever code path produced the terminal state
|
|
113
|
+
* (transitionState for tool-driven completion; onTargetClosed /
|
|
114
|
+
* onPersistentGptFailure / onInsufficientQuota / onUnexpectedException
|
|
115
|
+
* for failure paths) — by the time we reach onComplete those have
|
|
116
|
+
* already happened. This method just runs the post-completion side
|
|
117
|
+
* effects.
|
|
115
118
|
*/
|
|
116
119
|
private onComplete;
|
|
117
120
|
/**
|
|
118
121
|
* Persists the current browser session state if the flow's config has
|
|
119
|
-
* `persistState` enabled. Must be called BEFORE
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
122
|
+
* `persistState` enabled. Must be called BEFORE the in-memory `state`
|
|
123
|
+
* is mutated to a terminal value at every site that produces a
|
|
124
|
+
* terminal state — otherwise FlowCatalog.getFlowById can read the
|
|
125
|
+
* live FlowMetadata object (LOCAL deployments) and a frontend that
|
|
126
|
+
* observes the terminal state will race the (potentially network-
|
|
127
|
+
* bound) upload here, getting a 404 from a subsequent browser-state
|
|
128
|
+
* fetch.
|
|
125
129
|
*
|
|
126
130
|
* The browser context typically survives all-pages-closed (the read
|
|
127
|
-
* goes against the context, not a specific page), so this is
|
|
128
|
-
*
|
|
131
|
+
* goes against the context, not a specific page), so this is safe to
|
|
132
|
+
* call from failure handlers like onTargetClosed. If the read does
|
|
133
|
+
* fail, persistSessionState catches and logs internally — it doesn't
|
|
134
|
+
* propagate.
|
|
129
135
|
*/
|
|
130
136
|
private persistTerminalSessionStateIfNeeded;
|
|
131
137
|
/**
|
|
@@ -215,10 +215,18 @@ class DonobuFlow {
|
|
|
215
215
|
async onTargetClosed() {
|
|
216
216
|
const result = await this.targetInspector.handleTargetClosed();
|
|
217
217
|
if (!result.recovered) {
|
|
218
|
-
|
|
218
|
+
// Persist browser state BEFORE flipping the in-memory `state` to
|
|
219
|
+
// a terminal value. FlowCatalog.getFlowById serves the *live*
|
|
220
|
+
// FlowMetadata object for LOCAL deployments, so the next frontend
|
|
221
|
+
// poll observes terminal state the moment we mutate `state`.
|
|
222
|
+
// If we do that before the (potentially network-bound) session
|
|
223
|
+
// upload, an eager browser-state fetch from the frontend (e.g.
|
|
224
|
+
// FlowDeveloperTools auto-loads on terminal) races the upload and
|
|
225
|
+
// 404s. Same rationale as the ordering in transitionState.
|
|
219
226
|
Logger_1.appLogger.error(result.reason);
|
|
220
|
-
this.metadata.result = { failed: result.reason };
|
|
221
227
|
await this.persistTerminalSessionStateIfNeeded();
|
|
228
|
+
this.metadata.result = { failed: result.reason };
|
|
229
|
+
this.metadata.state = 'FAILED';
|
|
222
230
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
223
231
|
}
|
|
224
232
|
}
|
|
@@ -227,13 +235,13 @@ class DonobuFlow {
|
|
|
227
235
|
* are internal retries). This method will mark the flow as a failure.
|
|
228
236
|
*/
|
|
229
237
|
async onPersistentGptFailure(error) {
|
|
230
|
-
this.metadata.state = 'FAILED';
|
|
231
238
|
Logger_1.appLogger.error(`Stopped flow due to the ${this.gptClient?.config.type} GPT platform throwing an internal error!`, error);
|
|
239
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
232
240
|
this.metadata.result = {
|
|
233
241
|
failed: `Stopped flow due to the ${this.gptClient?.config.type} GPT platform throwing an internal error!`,
|
|
234
242
|
context: error.message,
|
|
235
243
|
};
|
|
236
|
-
|
|
244
|
+
this.metadata.state = 'FAILED';
|
|
237
245
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
238
246
|
}
|
|
239
247
|
/**
|
|
@@ -241,15 +249,15 @@ class DonobuFlow {
|
|
|
241
249
|
* usage quota or credits have been exhausted (HTTP 402).
|
|
242
250
|
*/
|
|
243
251
|
async onInsufficientQuota(error) {
|
|
244
|
-
this.metadata.state = 'FAILED';
|
|
245
252
|
const platform = error.gptPlatform;
|
|
246
253
|
const isDonobu = platform === 'DONOBU';
|
|
247
254
|
const failedMessage = isDonobu
|
|
248
255
|
? 'Your Donobu AI credits have been exhausted. Please add more credits to your account to continue running flows.'
|
|
249
256
|
: `Your ${platform} API quota has been exhausted. Please check your account's billing and usage limits; this may happen if there is a lack of funds in the account`;
|
|
250
257
|
Logger_1.appLogger.error(failedMessage, error);
|
|
251
|
-
this.metadata.result = { failed: failedMessage };
|
|
252
258
|
await this.persistTerminalSessionStateIfNeeded();
|
|
259
|
+
this.metadata.result = { failed: failedMessage };
|
|
260
|
+
this.metadata.state = 'FAILED';
|
|
253
261
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
254
262
|
}
|
|
255
263
|
/**
|
|
@@ -339,21 +347,24 @@ class DonobuFlow {
|
|
|
339
347
|
* method will mark the flow as a failure.
|
|
340
348
|
*/
|
|
341
349
|
async onUnexpectedException(error) {
|
|
342
|
-
this.metadata.state = 'FAILED';
|
|
343
350
|
Logger_1.appLogger.error('Stopped flow due to exception!', error);
|
|
351
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
344
352
|
this.metadata.result = {
|
|
345
353
|
failed: 'Internal error 🙈',
|
|
346
354
|
};
|
|
347
|
-
|
|
355
|
+
this.metadata.state = 'FAILED';
|
|
348
356
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
349
357
|
}
|
|
350
358
|
/**
|
|
351
359
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
352
360
|
*
|
|
353
|
-
* Browser session state and the
|
|
354
|
-
* whichever code path produced the terminal state
|
|
355
|
-
* tool-driven completion;
|
|
356
|
-
*
|
|
361
|
+
* Browser session state and the terminal-state metadata write are
|
|
362
|
+
* committed by whichever code path produced the terminal state
|
|
363
|
+
* (transitionState for tool-driven completion; onTargetClosed /
|
|
364
|
+
* onPersistentGptFailure / onInsufficientQuota / onUnexpectedException
|
|
365
|
+
* for failure paths) — by the time we reach onComplete those have
|
|
366
|
+
* already happened. This method just runs the post-completion side
|
|
367
|
+
* effects.
|
|
357
368
|
*/
|
|
358
369
|
async onComplete() {
|
|
359
370
|
DonobuFlow.invokeFlowFinishedCallback(this.metadata.callbackUrl, this.metadata.id);
|
|
@@ -361,16 +372,19 @@ class DonobuFlow {
|
|
|
361
372
|
}
|
|
362
373
|
/**
|
|
363
374
|
* Persists the current browser session state if the flow's config has
|
|
364
|
-
* `persistState` enabled. Must be called BEFORE
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
375
|
+
* `persistState` enabled. Must be called BEFORE the in-memory `state`
|
|
376
|
+
* is mutated to a terminal value at every site that produces a
|
|
377
|
+
* terminal state — otherwise FlowCatalog.getFlowById can read the
|
|
378
|
+
* live FlowMetadata object (LOCAL deployments) and a frontend that
|
|
379
|
+
* observes the terminal state will race the (potentially network-
|
|
380
|
+
* bound) upload here, getting a 404 from a subsequent browser-state
|
|
381
|
+
* fetch.
|
|
370
382
|
*
|
|
371
383
|
* The browser context typically survives all-pages-closed (the read
|
|
372
|
-
* goes against the context, not a specific page), so this is
|
|
373
|
-
*
|
|
384
|
+
* goes against the context, not a specific page), so this is safe to
|
|
385
|
+
* call from failure handlers like onTargetClosed. If the read does
|
|
386
|
+
* fail, persistSessionState catches and logs internally — it doesn't
|
|
387
|
+
* propagate.
|
|
374
388
|
*/
|
|
375
389
|
async persistTerminalSessionStateIfNeeded() {
|
|
376
390
|
if (this.metadata.web?.browser?.persistState) {
|
|
@@ -647,10 +661,8 @@ Message: ${dialog.message()}`;
|
|
|
647
661
|
// this, then someone polling for a flow's state may see the flow
|
|
648
662
|
// finished but still see a null result.
|
|
649
663
|
//
|
|
650
|
-
// The same rationale applies to the browser session state
|
|
651
|
-
//
|
|
652
|
-
// terminal state) would otherwise race the upload that onComplete
|
|
653
|
-
// performs and 404 until the user manually refreshed.
|
|
664
|
+
// The same rationale applies to the browser session state — see
|
|
665
|
+
// persistTerminalSessionStateIfNeeded for the full ordering note.
|
|
654
666
|
if ((0, FlowMetadata_1.isComplete)(nextState)) {
|
|
655
667
|
this.metadata.result = await this.createResultJson(nextState);
|
|
656
668
|
await this.persistTerminalSessionStateIfNeeded();
|