cloudcruise 1.0.1 → 1.1.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/CloudCruise.d.ts +1 -1
- package/dist/CloudCruise.js +4 -2
- package/dist/events/types.d.ts +34 -1
- package/dist/events/types.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/runs/RunsClient.d.ts +59 -0
- package/dist/runs/RunsClient.js +200 -0
- package/dist/vault/types.d.ts +2 -0
- package/dist/vault/utils.d.ts +2 -1
- package/dist/vault/utils.js +24 -1
- package/package.json +1 -1
package/dist/CloudCruise.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface CloudCruiseParams {
|
|
|
6
6
|
apiKey?: string;
|
|
7
7
|
/**
|
|
8
8
|
* CloudCruise API base URL. Authenticated requests are restricted to the
|
|
9
|
-
* production CloudCruise API origin.
|
|
9
|
+
* production CloudCruise API origin or the staging API origin.
|
|
10
10
|
*/
|
|
11
11
|
baseUrl?: string;
|
|
12
12
|
encryptionKey?: string;
|
package/dist/CloudCruise.js
CHANGED
|
@@ -5,6 +5,8 @@ import { RunsClient } from './runs/RunsClient.js';
|
|
|
5
5
|
import { WebhookClient } from './webhook/WebhookClient.js';
|
|
6
6
|
import { ConnectionManager } from './utils/connectionManager.js';
|
|
7
7
|
const DEFAULT_BASE_URL = 'https://api.cloudcruise.com';
|
|
8
|
+
const STAGING_BASE_URL = 'https://staging-api.cloudcruise.app';
|
|
9
|
+
const ALLOWED_BASE_URLS = new Set([DEFAULT_BASE_URL, STAGING_BASE_URL]);
|
|
8
10
|
const DEFAULT_API_HOST = new URL(DEFAULT_BASE_URL).host.toLowerCase();
|
|
9
11
|
function normalizeBaseUrl(baseUrl) {
|
|
10
12
|
let url;
|
|
@@ -27,9 +29,9 @@ function assertBaseUrlAllowed(baseUrl) {
|
|
|
27
29
|
if (host === DEFAULT_API_HOST && url.protocol !== 'https:') {
|
|
28
30
|
throw new Error(`Refusing to send CloudCruise API key to "${baseUrl}". The default CloudCruise API host requires https:.`);
|
|
29
31
|
}
|
|
30
|
-
if (baseUrl
|
|
32
|
+
if (!ALLOWED_BASE_URLS.has(baseUrl)) {
|
|
31
33
|
throw new Error(`Refusing to send CloudCruise API key to unapproved baseUrl "${baseUrl}". ` +
|
|
32
|
-
`Authenticated requests are restricted to ${
|
|
34
|
+
`Authenticated requests are restricted to: ${Array.from(ALLOWED_BASE_URLS).join(", ")}.`);
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
export class CloudCruise {
|
package/dist/events/types.d.ts
CHANGED
|
@@ -17,7 +17,8 @@ export declare enum EventType {
|
|
|
17
17
|
InteractionWaiting = "interaction.waiting",
|
|
18
18
|
InteractionFinished = "interaction.finished",
|
|
19
19
|
InteractionFailed = "interaction.failed",
|
|
20
|
-
AgentErrorAnalysis = "agent.error_analysis"
|
|
20
|
+
AgentErrorAnalysis = "agent.error_analysis",
|
|
21
|
+
ExecutionInputRequired = "execution.input_required"
|
|
21
22
|
}
|
|
22
23
|
export interface ExecutionQueuedPayload {
|
|
23
24
|
session_id: string;
|
|
@@ -61,6 +62,37 @@ export interface AgentErrorAnalysisPayload {
|
|
|
61
62
|
ai_analysis?: string;
|
|
62
63
|
root_cause_analysis?: string;
|
|
63
64
|
error_category?: string;
|
|
65
|
+
phase?: "modal_decision_dispatched" | "popup_dismiss_verified" | string;
|
|
66
|
+
session_id?: string;
|
|
67
|
+
modal_action?: string;
|
|
68
|
+
modal_action_label?: string;
|
|
69
|
+
response_time_ms?: number;
|
|
70
|
+
outcome?: "success" | "failure";
|
|
71
|
+
host?: string;
|
|
72
|
+
popup_signature?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface AvailableAction {
|
|
75
|
+
id: string;
|
|
76
|
+
label: string;
|
|
77
|
+
}
|
|
78
|
+
export interface PopupRetry {
|
|
79
|
+
attempt: number;
|
|
80
|
+
max_attempts: number;
|
|
81
|
+
}
|
|
82
|
+
export interface PopupContext {
|
|
83
|
+
error_description: string;
|
|
84
|
+
error_sub_type?: string;
|
|
85
|
+
full_url?: string;
|
|
86
|
+
available_actions: AvailableAction[];
|
|
87
|
+
retry: PopupRetry;
|
|
88
|
+
}
|
|
89
|
+
export type InputRequiredReason = "input_required" | "incorrect_form_input" | "multiple_matching_results" | "non_dismissible_popup";
|
|
90
|
+
export interface ExecutionInputRequiredPayload {
|
|
91
|
+
session_id: string;
|
|
92
|
+
input_variables: Record<string, any>;
|
|
93
|
+
screenshot_url: string | null;
|
|
94
|
+
reason?: InputRequiredReason;
|
|
95
|
+
popup_context?: PopupContext;
|
|
64
96
|
}
|
|
65
97
|
export interface ExecutionRequeuedPayload {
|
|
66
98
|
session_id: string;
|
|
@@ -132,6 +164,7 @@ export type EventPayloadMap = {
|
|
|
132
164
|
[EventType.VideoUploaded]: never;
|
|
133
165
|
[EventType.ExecutionPause]: never;
|
|
134
166
|
[EventType.InteractionFailed]: never;
|
|
167
|
+
[EventType.ExecutionInputRequired]: ExecutionInputRequiredPayload;
|
|
135
168
|
};
|
|
136
169
|
export type WebhookMessage<E extends EventType = EventType> = {
|
|
137
170
|
event: E;
|
package/dist/events/types.js
CHANGED
|
@@ -19,4 +19,5 @@ export var EventType;
|
|
|
19
19
|
EventType["InteractionFinished"] = "interaction.finished";
|
|
20
20
|
EventType["InteractionFailed"] = "interaction.failed";
|
|
21
21
|
EventType["AgentErrorAnalysis"] = "agent.error_analysis";
|
|
22
|
+
EventType["ExecutionInputRequired"] = "execution.input_required";
|
|
22
23
|
})(EventType || (EventType = {}));
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type { VaultEntry, GetVaultEntriesFilters, ProxyConfig, VaultPostPutHeade
|
|
|
12
12
|
export type { Workflow, WorkflowInputSchema, WorkflowMetadata } from './workflows/types.js';
|
|
13
13
|
export type { DryRun, Metadata, RunSpecificWebhook, PayloadWebhook, StartRunRequest, StartRunResponse, UserInteractionData, VideoUrl, SignedFileUrl, SignedScreenshotUrl, RunError, WorkflowError, RunResult, GetRunResult, WebhookEvent, WebhookReplayResponse, RunHandle, RunStreamOptions, SseEventName, SseMessage, RunEventEnvelope, RunHandleEventMap } from './runs/types.js';
|
|
14
14
|
export { EventType } from './events/types.js';
|
|
15
|
-
export type { WebhookMessage, RunEventMessage } from './events/types.js';
|
|
15
|
+
export type { WebhookMessage, RunEventMessage, AvailableAction, PopupRetry, PopupContext, InputRequiredReason, ExecutionInputRequiredPayload, AgentErrorAnalysisPayload } from './events/types.js';
|
|
16
16
|
export type { WebhookVerificationOptions } from './webhook/types.js';
|
|
17
17
|
export { VerificationError } from './webhook/types.js';
|
|
18
18
|
export { InputValidationError } from './workflows/types.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StartRunRequest, UserInteractionData, GetRunResult, WebhookReplayResponse, RunHandle, RunStreamOptions } from './types.js';
|
|
2
|
+
import type { PopupContext, ExecutionInputRequiredPayload } from '../events/types.js';
|
|
2
3
|
import { ConnectionManager } from '../utils/connectionManager.js';
|
|
3
4
|
export declare class RunsClient {
|
|
4
5
|
private readonly makeRequest;
|
|
@@ -22,6 +23,64 @@ export declare class RunsClient {
|
|
|
22
23
|
* @param data - User input data as key-value pairs
|
|
23
24
|
*/
|
|
24
25
|
submitUserInteraction(sessionId: string, data: UserInteractionData): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Responds to an execution.input_required event whose reason is
|
|
28
|
+
* "non_dismissible_popup" by picking one of the CTA buttons surfaced in
|
|
29
|
+
* popup_context.available_actions. The backend dispatches a synthetic
|
|
30
|
+
* click on the chosen button and resumes the workflow.
|
|
31
|
+
*
|
|
32
|
+
* Only valid while the session is waiting for input. The backing endpoint
|
|
33
|
+
* returns 400 if the wait already expired (the workspace setting
|
|
34
|
+
* input_required_timeout_seconds, default 15s, max 300s).
|
|
35
|
+
*
|
|
36
|
+
* @param sessionId - The session waiting for input.
|
|
37
|
+
* @param actionId - One of the ids in popup_context.available_actions.
|
|
38
|
+
*/
|
|
39
|
+
submitModalAction(sessionId: string, actionId: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Responds to an execution.input_required event whose reason is
|
|
42
|
+
* "input_required", "incorrect_form_input", or "multiple_matching_results"
|
|
43
|
+
* by supplying the corrected/required input variables. Backend resumes from
|
|
44
|
+
* the appropriate recovery node with the new values substituted in.
|
|
45
|
+
*
|
|
46
|
+
* Mutually exclusive with submitModalAction at the endpoint level.
|
|
47
|
+
*
|
|
48
|
+
* @param sessionId - The session waiting for input.
|
|
49
|
+
* @param inputVariables - Mapping of variable name to new value.
|
|
50
|
+
*/
|
|
51
|
+
submitInputVariables(sessionId: string, inputVariables: Record<string, any>): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Registers a listener that auto-responds ONLY to non-dismissible modal
|
|
54
|
+
* input_required events (reason === "non_dismissible_popup"). The decider
|
|
55
|
+
* receives the popup_context and must return one of the action ids in
|
|
56
|
+
* popup_context.available_actions.
|
|
57
|
+
*
|
|
58
|
+
* The SDK never picks an action on its own. The customer's decider IS the
|
|
59
|
+
* decision point. If decider throws, the listener swallows it and skips
|
|
60
|
+
* submission; the backend's input wait will time out naturally.
|
|
61
|
+
*
|
|
62
|
+
* Other input_required reasons (incorrect_form_input, etc.) are ignored
|
|
63
|
+
* here and should be routed to onInputVariablesRequired.
|
|
64
|
+
*
|
|
65
|
+
* @returns An unsubscribe callable.
|
|
66
|
+
*/
|
|
67
|
+
onPopupDecisionRequired(handle: RunHandle, decider: (ctx: PopupContext) => string | Promise<string>, onError?: (err: unknown) => void, options?: {
|
|
68
|
+
verbose?: boolean;
|
|
69
|
+
}): () => void;
|
|
70
|
+
/**
|
|
71
|
+
* Registers a listener that auto-responds ONLY to workflow-variable
|
|
72
|
+
* input_required events (reason in {"input_required",
|
|
73
|
+
* "incorrect_form_input", "multiple_matching_results"}). The decider
|
|
74
|
+
* receives the full payload and must return the input_variables dict.
|
|
75
|
+
*
|
|
76
|
+
* Counterpart to onPopupDecisionRequired. Modal events
|
|
77
|
+
* (reason === "non_dismissible_popup") are routed there and ignored here.
|
|
78
|
+
*
|
|
79
|
+
* @returns An unsubscribe callable.
|
|
80
|
+
*/
|
|
81
|
+
onInputVariablesRequired(handle: RunHandle, decider: (payload: ExecutionInputRequiredPayload) => Record<string, any> | Promise<Record<string, any>>, onError?: (err: unknown) => void, options?: {
|
|
82
|
+
verbose?: boolean;
|
|
83
|
+
}): () => void;
|
|
25
84
|
/**
|
|
26
85
|
* Retrieves comprehensive results and execution details for a specific run
|
|
27
86
|
* @param sessionId - The unique identifier for the workflow execution session
|
package/dist/runs/RunsClient.js
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
import { EventType } from '../events/types.js';
|
|
2
2
|
import { AsyncEventQueue } from '../utils/asyncQueue.js';
|
|
3
3
|
import { SimpleEventEmitter } from '../utils/events.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default error reporter for the recovery helpers. Writes to console.error
|
|
6
|
+
* so submission failures surface in customer logs even if no onError
|
|
7
|
+
* callback is provided. The runtime SimpleEventEmitter ignores async-handler
|
|
8
|
+
* returns, so without this default a rejection from /new_input_variables
|
|
9
|
+
* would become a silent unhandled rejection.
|
|
10
|
+
*/
|
|
11
|
+
function defaultRecoverySubmitErrorLog(operation) {
|
|
12
|
+
return (err) => {
|
|
13
|
+
try {
|
|
14
|
+
const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
15
|
+
console.error(`[CloudCruise SDK] ${operation} failed during recovery: ${msg}`);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// never throw from the error reporter
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Helper for `verbose: true` mode on the recovery helpers. Writes a single
|
|
24
|
+
* line to console.error so customers can watch the recovery loop without
|
|
25
|
+
* instrumenting their own decider.
|
|
26
|
+
*/
|
|
27
|
+
function verboseLog(operation, message) {
|
|
28
|
+
try {
|
|
29
|
+
console.error(`[CloudCruise SDK verbose] ${operation}: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// never throw
|
|
33
|
+
}
|
|
34
|
+
}
|
|
4
35
|
export class RunsClient {
|
|
5
36
|
makeRequest;
|
|
6
37
|
workflows;
|
|
@@ -171,6 +202,175 @@ export class RunsClient {
|
|
|
171
202
|
const path = `/run/${sessionId}/user_interaction`;
|
|
172
203
|
await this.makeRequest('POST', path, data);
|
|
173
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Responds to an execution.input_required event whose reason is
|
|
207
|
+
* "non_dismissible_popup" by picking one of the CTA buttons surfaced in
|
|
208
|
+
* popup_context.available_actions. The backend dispatches a synthetic
|
|
209
|
+
* click on the chosen button and resumes the workflow.
|
|
210
|
+
*
|
|
211
|
+
* Only valid while the session is waiting for input. The backing endpoint
|
|
212
|
+
* returns 400 if the wait already expired (the workspace setting
|
|
213
|
+
* input_required_timeout_seconds, default 15s, max 300s).
|
|
214
|
+
*
|
|
215
|
+
* @param sessionId - The session waiting for input.
|
|
216
|
+
* @param actionId - One of the ids in popup_context.available_actions.
|
|
217
|
+
*/
|
|
218
|
+
async submitModalAction(sessionId, actionId) {
|
|
219
|
+
const path = `/run/${sessionId}/new_input_variables`;
|
|
220
|
+
await this.makeRequest('POST', path, { modal_action: actionId });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Responds to an execution.input_required event whose reason is
|
|
224
|
+
* "input_required", "incorrect_form_input", or "multiple_matching_results"
|
|
225
|
+
* by supplying the corrected/required input variables. Backend resumes from
|
|
226
|
+
* the appropriate recovery node with the new values substituted in.
|
|
227
|
+
*
|
|
228
|
+
* Mutually exclusive with submitModalAction at the endpoint level.
|
|
229
|
+
*
|
|
230
|
+
* @param sessionId - The session waiting for input.
|
|
231
|
+
* @param inputVariables - Mapping of variable name to new value.
|
|
232
|
+
*/
|
|
233
|
+
async submitInputVariables(sessionId, inputVariables) {
|
|
234
|
+
const path = `/run/${sessionId}/new_input_variables`;
|
|
235
|
+
await this.makeRequest('POST', path, { input_variables: inputVariables });
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Registers a listener that auto-responds ONLY to non-dismissible modal
|
|
239
|
+
* input_required events (reason === "non_dismissible_popup"). The decider
|
|
240
|
+
* receives the popup_context and must return one of the action ids in
|
|
241
|
+
* popup_context.available_actions.
|
|
242
|
+
*
|
|
243
|
+
* The SDK never picks an action on its own. The customer's decider IS the
|
|
244
|
+
* decision point. If decider throws, the listener swallows it and skips
|
|
245
|
+
* submission; the backend's input wait will time out naturally.
|
|
246
|
+
*
|
|
247
|
+
* Other input_required reasons (incorrect_form_input, etc.) are ignored
|
|
248
|
+
* here and should be routed to onInputVariablesRequired.
|
|
249
|
+
*
|
|
250
|
+
* @returns An unsubscribe callable.
|
|
251
|
+
*/
|
|
252
|
+
onPopupDecisionRequired(handle, decider, onError, options) {
|
|
253
|
+
const verbose = options?.verbose === true;
|
|
254
|
+
const reportError = (err) => {
|
|
255
|
+
const reporter = onError ?? defaultRecoverySubmitErrorLog('submitModalAction');
|
|
256
|
+
try {
|
|
257
|
+
reporter(err);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// defense: a buggy onError must not crash the event loop
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
if (verbose)
|
|
264
|
+
verboseLog('onPopupDecisionRequired', 'listener registered');
|
|
265
|
+
const listener = async (event) => {
|
|
266
|
+
const payload = (event?.data?.payload ?? event?.payload);
|
|
267
|
+
if (!payload || payload.reason !== 'non_dismissible_popup' || !payload.popup_context) {
|
|
268
|
+
if (verbose) {
|
|
269
|
+
verboseLog('onPopupDecisionRequired', `skipping reason=${payload?.reason ?? 'undefined'}`);
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (verbose) {
|
|
274
|
+
const attempt = payload.popup_context.retry?.attempt;
|
|
275
|
+
const actions = payload.popup_context.available_actions?.map((a) => a.id) ?? [];
|
|
276
|
+
verboseLog('onPopupDecisionRequired', `event received attempt=${attempt} actions=${JSON.stringify(actions)}`);
|
|
277
|
+
}
|
|
278
|
+
let actionId;
|
|
279
|
+
try {
|
|
280
|
+
actionId = await decider(payload.popup_context);
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
if (verbose)
|
|
284
|
+
verboseLog('onPopupDecisionRequired', `decider raised: ${e}`);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (typeof actionId !== 'string' || actionId.length === 0) {
|
|
288
|
+
if (verbose)
|
|
289
|
+
verboseLog('onPopupDecisionRequired', `decider returned non-string/empty (${JSON.stringify(actionId)}); skipping`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const sid = payload.session_id || handle.sessionId;
|
|
293
|
+
if (verbose)
|
|
294
|
+
verboseLog('onPopupDecisionRequired', `submitting modal_action=${JSON.stringify(actionId)} for session=${sid}`);
|
|
295
|
+
try {
|
|
296
|
+
await this.submitModalAction(sid, actionId);
|
|
297
|
+
if (verbose)
|
|
298
|
+
verboseLog('onPopupDecisionRequired', `submit ok for action=${JSON.stringify(actionId)}`);
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
reportError(err);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const unsubscribe = handle.on(EventType.ExecutionInputRequired, listener);
|
|
305
|
+
return typeof unsubscribe === 'function' ? unsubscribe : () => { };
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Registers a listener that auto-responds ONLY to workflow-variable
|
|
309
|
+
* input_required events (reason in {"input_required",
|
|
310
|
+
* "incorrect_form_input", "multiple_matching_results"}). The decider
|
|
311
|
+
* receives the full payload and must return the input_variables dict.
|
|
312
|
+
*
|
|
313
|
+
* Counterpart to onPopupDecisionRequired. Modal events
|
|
314
|
+
* (reason === "non_dismissible_popup") are routed there and ignored here.
|
|
315
|
+
*
|
|
316
|
+
* @returns An unsubscribe callable.
|
|
317
|
+
*/
|
|
318
|
+
onInputVariablesRequired(handle, decider, onError, options) {
|
|
319
|
+
const verbose = options?.verbose === true;
|
|
320
|
+
const VARIABLE_REASONS = new Set([
|
|
321
|
+
'input_required',
|
|
322
|
+
'incorrect_form_input',
|
|
323
|
+
'multiple_matching_results',
|
|
324
|
+
]);
|
|
325
|
+
const reportError = (err) => {
|
|
326
|
+
const reporter = onError ?? defaultRecoverySubmitErrorLog('submitInputVariables');
|
|
327
|
+
try {
|
|
328
|
+
reporter(err);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// defense
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
if (verbose)
|
|
335
|
+
verboseLog('onInputVariablesRequired', 'listener registered');
|
|
336
|
+
const listener = async (event) => {
|
|
337
|
+
const payload = (event?.data?.payload ?? event?.payload);
|
|
338
|
+
if (!payload || !payload.reason || !VARIABLE_REASONS.has(payload.reason)) {
|
|
339
|
+
if (verbose)
|
|
340
|
+
verboseLog('onInputVariablesRequired', `skipping reason=${payload?.reason ?? 'undefined'}`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (verbose)
|
|
344
|
+
verboseLog('onInputVariablesRequired', `event received reason=${payload.reason}`);
|
|
345
|
+
let inputVars;
|
|
346
|
+
try {
|
|
347
|
+
inputVars = await decider(payload);
|
|
348
|
+
}
|
|
349
|
+
catch (e) {
|
|
350
|
+
if (verbose)
|
|
351
|
+
verboseLog('onInputVariablesRequired', `decider raised: ${e}`);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (!inputVars || typeof inputVars !== 'object' || Array.isArray(inputVars)) {
|
|
355
|
+
if (verbose)
|
|
356
|
+
verboseLog('onInputVariablesRequired', `decider returned non-object (${Array.isArray(inputVars) ? 'array' : typeof inputVars}); skipping`);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const sid = payload.session_id || handle.sessionId;
|
|
360
|
+
if (verbose)
|
|
361
|
+
verboseLog('onInputVariablesRequired', `submitting input_variables keys=${JSON.stringify(Object.keys(inputVars))} for session=${sid}`);
|
|
362
|
+
try {
|
|
363
|
+
await this.submitInputVariables(sid, inputVars);
|
|
364
|
+
if (verbose)
|
|
365
|
+
verboseLog('onInputVariablesRequired', 'submit ok');
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
reportError(err);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
const unsubscribe = handle.on(EventType.ExecutionInputRequired, listener);
|
|
372
|
+
return typeof unsubscribe === 'function' ? unsubscribe : () => { };
|
|
373
|
+
}
|
|
174
374
|
/**
|
|
175
375
|
* Retrieves comprehensive results and execution details for a specific run
|
|
176
376
|
* @param sessionId - The unique identifier for the workflow execution session
|
package/dist/vault/types.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface VaultEntry {
|
|
|
38
38
|
cookie_domain_to_store?: string | null;
|
|
39
39
|
proxy?: ProxyConfig;
|
|
40
40
|
proxy_string?: string | null;
|
|
41
|
+
proxy_setting?: 'random' | 'static' | 'country' | 'custom' | null;
|
|
42
|
+
proxy_value?: string | null;
|
|
41
43
|
headers?: VaultPostPutHeadersInBody[];
|
|
42
44
|
created_at?: string | null;
|
|
43
45
|
session_data_set_at?: string | null;
|
package/dist/vault/utils.d.ts
CHANGED
|
@@ -18,7 +18,8 @@ export declare function encryptData(data: any, keyHex: string): Promise<string>;
|
|
|
18
18
|
export declare function decryptData(encryptedHex: string, keyHex: string): Promise<any>;
|
|
19
19
|
/**
|
|
20
20
|
* Encrypts sensitive fields in a vault entry
|
|
21
|
-
* Fields encrypted: user_name, password, tfa_secret (if present)
|
|
21
|
+
* Fields encrypted: user_name, password, tfa_secret (if present), and
|
|
22
|
+
* proxy_value when proxy_setting is "custom" (the bring-your-own proxy URL).
|
|
22
23
|
* @param entry - Vault entry with potentially sensitive data
|
|
23
24
|
* @param encryptionKey - Hex-encoded encryption key
|
|
24
25
|
* @returns Entry with encrypted sensitive fields
|
package/dist/vault/utils.js
CHANGED
|
@@ -51,7 +51,8 @@ export async function decryptData(encryptedHex, keyHex) {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Encrypts sensitive fields in a vault entry
|
|
54
|
-
* Fields encrypted: user_name, password, tfa_secret (if present)
|
|
54
|
+
* Fields encrypted: user_name, password, tfa_secret (if present), and
|
|
55
|
+
* proxy_value when proxy_setting is "custom" (the bring-your-own proxy URL).
|
|
55
56
|
* @param entry - Vault entry with potentially sensitive data
|
|
56
57
|
* @param encryptionKey - Hex-encoded encryption key
|
|
57
58
|
* @returns Entry with encrypted sensitive fields
|
|
@@ -67,6 +68,22 @@ export async function encryptSensitiveFields(entry, encryptionKey) {
|
|
|
67
68
|
if (entry.tfa_secret !== undefined) {
|
|
68
69
|
encryptedEntry.tfa_secret = await encryptData(entry.tfa_secret, encryptionKey);
|
|
69
70
|
}
|
|
71
|
+
// proxy_value is meaningless to the backend without proxy_setting, which is
|
|
72
|
+
// also the discriminator that tells us whether to encrypt. Fail closed: a
|
|
73
|
+
// custom proxy URL (often with embedded credentials) must never be sent in
|
|
74
|
+
// plaintext because the caller forgot the mode on a partial update.
|
|
75
|
+
if (entry.proxy_value !== undefined &&
|
|
76
|
+
entry.proxy_value !== null &&
|
|
77
|
+
(entry.proxy_setting === undefined || entry.proxy_setting === null)) {
|
|
78
|
+
throw new Error("proxy_value requires proxy_setting. Pass proxy_setting: 'custom' for a " +
|
|
79
|
+
"bring-your-own proxy URL (so it is encrypted before sending), or " +
|
|
80
|
+
"'static'/'country' for a managed proxy.");
|
|
81
|
+
}
|
|
82
|
+
if (entry.proxy_setting === 'custom' &&
|
|
83
|
+
entry.proxy_value !== undefined &&
|
|
84
|
+
entry.proxy_value !== null) {
|
|
85
|
+
encryptedEntry.proxy_value = await encryptData(entry.proxy_value, encryptionKey);
|
|
86
|
+
}
|
|
70
87
|
return encryptedEntry;
|
|
71
88
|
}
|
|
72
89
|
/**
|
|
@@ -95,5 +112,11 @@ export async function decryptSensitiveFields(entry, encryptionKey) {
|
|
|
95
112
|
}
|
|
96
113
|
catch { }
|
|
97
114
|
}
|
|
115
|
+
if (entry.proxy_setting === 'custom' && typeof entry.proxy_value === 'string') {
|
|
116
|
+
try {
|
|
117
|
+
decryptedEntry.proxy_value = await decryptData(entry.proxy_value, encryptionKey);
|
|
118
|
+
}
|
|
119
|
+
catch { }
|
|
120
|
+
}
|
|
98
121
|
return decryptedEntry;
|
|
99
122
|
}
|