donobu 5.27.1 → 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.
|
@@ -107,8 +107,33 @@ export declare class DonobuFlow {
|
|
|
107
107
|
private onUnexpectedException;
|
|
108
108
|
/**
|
|
109
109
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
110
|
+
*
|
|
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.
|
|
110
118
|
*/
|
|
111
119
|
private onComplete;
|
|
120
|
+
/**
|
|
121
|
+
* Persists the current browser session state if the flow's config has
|
|
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.
|
|
129
|
+
*
|
|
130
|
+
* The browser context typically survives all-pages-closed (the read
|
|
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.
|
|
135
|
+
*/
|
|
136
|
+
private persistTerminalSessionStateIfNeeded;
|
|
112
137
|
/**
|
|
113
138
|
* Attempt to POST a JSON body containing given flow ID to the given
|
|
114
139
|
* ${@link callbackUrl} if the URL is non-null. Note that there is no retying
|
|
@@ -215,9 +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);
|
|
227
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
220
228
|
this.metadata.result = { failed: result.reason };
|
|
229
|
+
this.metadata.state = 'FAILED';
|
|
221
230
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
222
231
|
}
|
|
223
232
|
}
|
|
@@ -226,12 +235,13 @@ class DonobuFlow {
|
|
|
226
235
|
* are internal retries). This method will mark the flow as a failure.
|
|
227
236
|
*/
|
|
228
237
|
async onPersistentGptFailure(error) {
|
|
229
|
-
this.metadata.state = 'FAILED';
|
|
230
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();
|
|
231
240
|
this.metadata.result = {
|
|
232
241
|
failed: `Stopped flow due to the ${this.gptClient?.config.type} GPT platform throwing an internal error!`,
|
|
233
242
|
context: error.message,
|
|
234
243
|
};
|
|
244
|
+
this.metadata.state = 'FAILED';
|
|
235
245
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
236
246
|
}
|
|
237
247
|
/**
|
|
@@ -239,14 +249,15 @@ class DonobuFlow {
|
|
|
239
249
|
* usage quota or credits have been exhausted (HTTP 402).
|
|
240
250
|
*/
|
|
241
251
|
async onInsufficientQuota(error) {
|
|
242
|
-
this.metadata.state = 'FAILED';
|
|
243
252
|
const platform = error.gptPlatform;
|
|
244
253
|
const isDonobu = platform === 'DONOBU';
|
|
245
254
|
const failedMessage = isDonobu
|
|
246
255
|
? 'Your Donobu AI credits have been exhausted. Please add more credits to your account to continue running flows.'
|
|
247
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`;
|
|
248
257
|
Logger_1.appLogger.error(failedMessage, error);
|
|
258
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
249
259
|
this.metadata.result = { failed: failedMessage };
|
|
260
|
+
this.metadata.state = 'FAILED';
|
|
250
261
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
251
262
|
}
|
|
252
263
|
/**
|
|
@@ -336,23 +347,49 @@ class DonobuFlow {
|
|
|
336
347
|
* method will mark the flow as a failure.
|
|
337
348
|
*/
|
|
338
349
|
async onUnexpectedException(error) {
|
|
339
|
-
this.metadata.state = 'FAILED';
|
|
340
350
|
Logger_1.appLogger.error('Stopped flow due to exception!', error);
|
|
351
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
341
352
|
this.metadata.result = {
|
|
342
353
|
failed: 'Internal error 🙈',
|
|
343
354
|
};
|
|
355
|
+
this.metadata.state = 'FAILED';
|
|
344
356
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
345
357
|
}
|
|
346
358
|
/**
|
|
347
359
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
360
|
+
*
|
|
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.
|
|
348
368
|
*/
|
|
349
369
|
async onComplete() {
|
|
350
|
-
|
|
370
|
+
DonobuFlow.invokeFlowFinishedCallback(this.metadata.callbackUrl, this.metadata.id);
|
|
371
|
+
this.controlPanel.close();
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Persists the current browser session state if the flow's config has
|
|
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.
|
|
382
|
+
*
|
|
383
|
+
* The browser context typically survives all-pages-closed (the read
|
|
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.
|
|
388
|
+
*/
|
|
389
|
+
async persistTerminalSessionStateIfNeeded() {
|
|
351
390
|
if (this.metadata.web?.browser?.persistState) {
|
|
352
391
|
await this.targetInspector.persistSessionState(this.persistence, this.metadata.id);
|
|
353
392
|
}
|
|
354
|
-
DonobuFlow.invokeFlowFinishedCallback(this.metadata.callbackUrl, this.metadata.id);
|
|
355
|
-
this.controlPanel.close();
|
|
356
393
|
}
|
|
357
394
|
/**
|
|
358
395
|
* Attempt to POST a JSON body containing given flow ID to the given
|
|
@@ -623,8 +660,12 @@ Message: ${dialog.message()}`;
|
|
|
623
660
|
// the result object before the final state is set. If we did not do
|
|
624
661
|
// this, then someone polling for a flow's state may see the flow
|
|
625
662
|
// finished but still see a null result.
|
|
663
|
+
//
|
|
664
|
+
// The same rationale applies to the browser session state — see
|
|
665
|
+
// persistTerminalSessionStateIfNeeded for the full ordering note.
|
|
626
666
|
if ((0, FlowMetadata_1.isComplete)(nextState)) {
|
|
627
667
|
this.metadata.result = await this.createResultJson(nextState);
|
|
668
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
628
669
|
}
|
|
629
670
|
const lastState = this.metadata.state;
|
|
630
671
|
this.metadata.state = nextState;
|
|
@@ -107,8 +107,33 @@ export declare class DonobuFlow {
|
|
|
107
107
|
private onUnexpectedException;
|
|
108
108
|
/**
|
|
109
109
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
110
|
+
*
|
|
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.
|
|
110
118
|
*/
|
|
111
119
|
private onComplete;
|
|
120
|
+
/**
|
|
121
|
+
* Persists the current browser session state if the flow's config has
|
|
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.
|
|
129
|
+
*
|
|
130
|
+
* The browser context typically survives all-pages-closed (the read
|
|
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.
|
|
135
|
+
*/
|
|
136
|
+
private persistTerminalSessionStateIfNeeded;
|
|
112
137
|
/**
|
|
113
138
|
* Attempt to POST a JSON body containing given flow ID to the given
|
|
114
139
|
* ${@link callbackUrl} if the URL is non-null. Note that there is no retying
|
|
@@ -215,9 +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);
|
|
227
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
220
228
|
this.metadata.result = { failed: result.reason };
|
|
229
|
+
this.metadata.state = 'FAILED';
|
|
221
230
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
222
231
|
}
|
|
223
232
|
}
|
|
@@ -226,12 +235,13 @@ class DonobuFlow {
|
|
|
226
235
|
* are internal retries). This method will mark the flow as a failure.
|
|
227
236
|
*/
|
|
228
237
|
async onPersistentGptFailure(error) {
|
|
229
|
-
this.metadata.state = 'FAILED';
|
|
230
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();
|
|
231
240
|
this.metadata.result = {
|
|
232
241
|
failed: `Stopped flow due to the ${this.gptClient?.config.type} GPT platform throwing an internal error!`,
|
|
233
242
|
context: error.message,
|
|
234
243
|
};
|
|
244
|
+
this.metadata.state = 'FAILED';
|
|
235
245
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
236
246
|
}
|
|
237
247
|
/**
|
|
@@ -239,14 +249,15 @@ class DonobuFlow {
|
|
|
239
249
|
* usage quota or credits have been exhausted (HTTP 402).
|
|
240
250
|
*/
|
|
241
251
|
async onInsufficientQuota(error) {
|
|
242
|
-
this.metadata.state = 'FAILED';
|
|
243
252
|
const platform = error.gptPlatform;
|
|
244
253
|
const isDonobu = platform === 'DONOBU';
|
|
245
254
|
const failedMessage = isDonobu
|
|
246
255
|
? 'Your Donobu AI credits have been exhausted. Please add more credits to your account to continue running flows.'
|
|
247
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`;
|
|
248
257
|
Logger_1.appLogger.error(failedMessage, error);
|
|
258
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
249
259
|
this.metadata.result = { failed: failedMessage };
|
|
260
|
+
this.metadata.state = 'FAILED';
|
|
250
261
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
251
262
|
}
|
|
252
263
|
/**
|
|
@@ -336,23 +347,49 @@ class DonobuFlow {
|
|
|
336
347
|
* method will mark the flow as a failure.
|
|
337
348
|
*/
|
|
338
349
|
async onUnexpectedException(error) {
|
|
339
|
-
this.metadata.state = 'FAILED';
|
|
340
350
|
Logger_1.appLogger.error('Stopped flow due to exception!', error);
|
|
351
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
341
352
|
this.metadata.result = {
|
|
342
353
|
failed: 'Internal error 🙈',
|
|
343
354
|
};
|
|
355
|
+
this.metadata.state = 'FAILED';
|
|
344
356
|
await this.persistence.setFlowMetadata(this.metadata);
|
|
345
357
|
}
|
|
346
358
|
/**
|
|
347
359
|
* This method is called when a flow is complete (i.e. when {@link DonobuFlow.run} should return).
|
|
360
|
+
*
|
|
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.
|
|
348
368
|
*/
|
|
349
369
|
async onComplete() {
|
|
350
|
-
|
|
370
|
+
DonobuFlow.invokeFlowFinishedCallback(this.metadata.callbackUrl, this.metadata.id);
|
|
371
|
+
this.controlPanel.close();
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Persists the current browser session state if the flow's config has
|
|
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.
|
|
382
|
+
*
|
|
383
|
+
* The browser context typically survives all-pages-closed (the read
|
|
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.
|
|
388
|
+
*/
|
|
389
|
+
async persistTerminalSessionStateIfNeeded() {
|
|
351
390
|
if (this.metadata.web?.browser?.persistState) {
|
|
352
391
|
await this.targetInspector.persistSessionState(this.persistence, this.metadata.id);
|
|
353
392
|
}
|
|
354
|
-
DonobuFlow.invokeFlowFinishedCallback(this.metadata.callbackUrl, this.metadata.id);
|
|
355
|
-
this.controlPanel.close();
|
|
356
393
|
}
|
|
357
394
|
/**
|
|
358
395
|
* Attempt to POST a JSON body containing given flow ID to the given
|
|
@@ -623,8 +660,12 @@ Message: ${dialog.message()}`;
|
|
|
623
660
|
// the result object before the final state is set. If we did not do
|
|
624
661
|
// this, then someone polling for a flow's state may see the flow
|
|
625
662
|
// finished but still see a null result.
|
|
663
|
+
//
|
|
664
|
+
// The same rationale applies to the browser session state — see
|
|
665
|
+
// persistTerminalSessionStateIfNeeded for the full ordering note.
|
|
626
666
|
if ((0, FlowMetadata_1.isComplete)(nextState)) {
|
|
627
667
|
this.metadata.result = await this.createResultJson(nextState);
|
|
668
|
+
await this.persistTerminalSessionStateIfNeeded();
|
|
628
669
|
}
|
|
629
670
|
const lastState = this.metadata.state;
|
|
630
671
|
this.metadata.state = nextState;
|