cloudcruise 0.0.1 → 0.0.2-alpha.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.
- package/README.md +45 -1
- package/dist/CloudCruise.d.ts +25 -0
- package/dist/CloudCruise.js +81 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +11 -0
- package/dist/runs/RunsClient.d.ts +42 -0
- package/dist/runs/RunsClient.js +192 -0
- package/dist/runs/types.d.ts +149 -0
- package/dist/runs/types.js +4 -0
- package/dist/utils/asyncQueue.d.ts +9 -0
- package/dist/utils/asyncQueue.js +43 -0
- package/dist/utils/connectionManager.d.ts +29 -0
- package/dist/utils/connectionManager.js +224 -0
- package/dist/utils/env.d.ts +2 -0
- package/dist/utils/env.js +9 -0
- package/dist/utils/events.d.ts +7 -0
- package/dist/utils/events.js +23 -0
- package/dist/utils/sse.d.ts +24 -0
- package/dist/utils/sse.js +122 -0
- package/dist/vault/VaultClient.d.ts +42 -0
- package/dist/vault/VaultClient.js +91 -0
- package/dist/vault/types.d.ts +43 -0
- package/dist/vault/types.js +4 -0
- package/dist/vault/utils.d.ts +33 -0
- package/dist/vault/utils.js +99 -0
- package/dist/webhook/WebhookClient.d.ts +5 -0
- package/dist/webhook/WebhookClient.js +14 -0
- package/dist/webhook/types.d.ts +13 -0
- package/dist/webhook/types.js +8 -0
- package/dist/webhook/utils.d.ts +2 -0
- package/dist/webhook/utils.js +49 -0
- package/dist/workflows/WorkflowsClient.d.ts +19 -0
- package/dist/workflows/WorkflowsClient.js +97 -0
- package/dist/workflows/types.d.ts +41 -0
- package/dist/workflows/types.js +15 -0
- package/package.json +15 -2
package/README.md
CHANGED
|
@@ -1 +1,45 @@
|
|
|
1
|
-
# cloudcruise-js
|
|
1
|
+
# cloudcruise-js
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+

|
|
5
|
+
[](https://github.com/CloudCruise/cloudcruise-js)
|
|
6
|
+
[](https://discord.com/invite/MHjbUqedZF)
|
|
7
|
+
[](https://www.ycombinator.com/companies/cloudcruise)
|
|
8
|
+
|
|
9
|
+
The official CloudCruise JavaScript/TypeScript client library for automated browser workflows, credential management, and real-time monitoring.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install cloudcruise
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Clients
|
|
18
|
+
|
|
19
|
+
| Client | Description | Documentation |
|
|
20
|
+
| -------------------------------- | -------------------------------------------------------- | ---------------------------------------------- |
|
|
21
|
+
| [**Vault**](./src/vault) | Encrypted credential storage with AES-256-GCM encryption | [📖 Vault Docs](./src/vault/README.md) |
|
|
22
|
+
| [**Workflows**](./src/workflows) | Workflow definitions, metadata, and input validation | [📖 Workflows Docs](./src/workflows/README.md) |
|
|
23
|
+
| [**Runs**](./src/runs) | Workflow execution with real-time SSE streaming | [📖 Runs Docs](./src/runs/README.md) |
|
|
24
|
+
| [**Webhook**](./src/webhook) | Secure webhook payload verification with HMAC | [📖 Webhook Docs](./src/webhook/README.md) |
|
|
25
|
+
|
|
26
|
+
## Documentation
|
|
27
|
+
|
|
28
|
+
- [**API Documentation**](https://docs.cloudcruise.com) - Complete API reference and guides
|
|
29
|
+
- [**CloudCruise Platform**](https://cloudcruise.com) - Learn more about CloudCruise
|
|
30
|
+
|
|
31
|
+
## Development
|
|
32
|
+
|
|
33
|
+
This project uses TypeScript and supports Node.js 18+. See [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed development instructions.
|
|
34
|
+
|
|
35
|
+
Quick start:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install # Install dependencies
|
|
39
|
+
npm run build # Build TypeScript to JavaScript
|
|
40
|
+
npm run dev # Run in watch mode
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { VaultClient } from './vault/VaultClient.js';
|
|
2
|
+
import { WorkflowsClient } from './workflows/WorkflowsClient.js';
|
|
3
|
+
import { RunsClient } from './runs/RunsClient.js';
|
|
4
|
+
import { WebhookClient } from './webhook/WebhookClient.js';
|
|
5
|
+
export interface CloudCruiseParams {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
encryptionKey?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class CloudCruise {
|
|
11
|
+
private readonly apiKey;
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly encryptionKey;
|
|
14
|
+
readonly vault: VaultClient;
|
|
15
|
+
readonly workflows: WorkflowsClient;
|
|
16
|
+
readonly runs: RunsClient;
|
|
17
|
+
readonly webhook: WebhookClient;
|
|
18
|
+
private readonly connectionManager;
|
|
19
|
+
constructor(params?: CloudCruiseParams);
|
|
20
|
+
/**
|
|
21
|
+
* Makes an HTTP request to the CloudCruise API
|
|
22
|
+
* Automatically adds the cc-key header for authentication
|
|
23
|
+
*/
|
|
24
|
+
private makeRequest;
|
|
25
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getEnv } from './utils/env.js';
|
|
2
|
+
import { VaultClient } from './vault/VaultClient.js';
|
|
3
|
+
import { WorkflowsClient } from './workflows/WorkflowsClient.js';
|
|
4
|
+
import { RunsClient } from './runs/RunsClient.js';
|
|
5
|
+
import { WebhookClient } from './webhook/WebhookClient.js';
|
|
6
|
+
import { ConnectionManager } from './utils/connectionManager.js';
|
|
7
|
+
export class CloudCruise {
|
|
8
|
+
apiKey;
|
|
9
|
+
baseUrl;
|
|
10
|
+
encryptionKey;
|
|
11
|
+
vault;
|
|
12
|
+
workflows;
|
|
13
|
+
runs;
|
|
14
|
+
webhook;
|
|
15
|
+
connectionManager;
|
|
16
|
+
constructor(params) {
|
|
17
|
+
const apiKey = params?.apiKey ?? getEnv('CLOUDCRUISE_API_KEY');
|
|
18
|
+
const baseUrl = params?.baseUrl ?? getEnv('CLOUDCRUISE_BASE_URL') ?? 'https://api.cloudcruise.com';
|
|
19
|
+
const encryptionKey = params?.encryptionKey ?? getEnv('CLOUDCRUISE_ENCRYPTION_KEY');
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
throw new Error('Missing apiKey. Provide via params.apiKey or CLOUDCRUISE_API_KEY env var.');
|
|
22
|
+
}
|
|
23
|
+
if (!encryptionKey) {
|
|
24
|
+
throw new Error('Missing encryptionKey. Provide via params.encryptionKey or CLOUDCRUISE_ENCRYPTION_KEY env var.');
|
|
25
|
+
}
|
|
26
|
+
this.apiKey = apiKey;
|
|
27
|
+
this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
28
|
+
this.encryptionKey = encryptionKey;
|
|
29
|
+
// Initialize namespace clients
|
|
30
|
+
this.connectionManager = new ConnectionManager(this.baseUrl, this.apiKey);
|
|
31
|
+
this.vault = new VaultClient(this.makeRequest.bind(this), this.encryptionKey);
|
|
32
|
+
this.workflows = new WorkflowsClient(this.makeRequest.bind(this));
|
|
33
|
+
this.runs = new RunsClient(this.connectionManager, this.makeRequest.bind(this), this.workflows);
|
|
34
|
+
this.webhook = new WebhookClient();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Makes an HTTP request to the CloudCruise API
|
|
38
|
+
* Automatically adds the cc-key header for authentication
|
|
39
|
+
*/
|
|
40
|
+
async makeRequest(method, path, body) {
|
|
41
|
+
const url = `${this.baseUrl}${path}`;
|
|
42
|
+
const headers = {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
'cc-key': this.apiKey
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
const response = await fetch(url, {
|
|
48
|
+
method,
|
|
49
|
+
headers,
|
|
50
|
+
body: body ? JSON.stringify(body) : undefined
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const errorText = await response.text();
|
|
54
|
+
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
55
|
+
try {
|
|
56
|
+
const errorJson = JSON.parse(errorText);
|
|
57
|
+
errorMessage = errorJson.message || errorJson.error || errorMessage;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Use HTTP status if we can't parse error response
|
|
61
|
+
}
|
|
62
|
+
throw new Error(errorMessage);
|
|
63
|
+
}
|
|
64
|
+
const contentType = response.headers.get('content-type');
|
|
65
|
+
if (contentType && contentType.includes('application/json')) {
|
|
66
|
+
return await response.json();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return await response.text();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`Request failed: ${String(error)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudCruise JavaScript/TypeScript SDK
|
|
3
|
+
* Official client library for the CloudCruise Platform
|
|
4
|
+
*/
|
|
5
|
+
export { CloudCruise } from './CloudCruise.js';
|
|
6
|
+
export type { CloudCruiseParams } from './CloudCruise.js';
|
|
7
|
+
export { VaultClient } from './vault/VaultClient.js';
|
|
8
|
+
export { WorkflowsClient } from './workflows/WorkflowsClient.js';
|
|
9
|
+
export { RunsClient } from './runs/RunsClient.js';
|
|
10
|
+
export { WebhookClient } from './webhook/WebhookClient.js';
|
|
11
|
+
export type { VaultEntry, GetVaultEntriesFilters, ProxyConfig, VaultPostPutHeadersInBody } from './vault/types.js';
|
|
12
|
+
export type { Workflow, WorkflowInputSchema, WorkflowMetadata } from './workflows/types.js';
|
|
13
|
+
export type { EventType, DryRun, Metadata, RunSpecificWebhook, PayloadWebhook, StartRunRequest, StartRunResponse, UserInteractionData, VideoUrl, SignedFileUrl, SignedScreenshotUrl, RunError, WorkflowError, RunResult, GetRunResult, WebhookEvent, WebhookReplayResponse, RunHandle, RunStreamOptions, SseEventName, SseMessage, RunEventEnvelope } from './runs/types.js';
|
|
14
|
+
export type { WebhookPayload, WebhookVerificationOptions } from './webhook/types.js';
|
|
15
|
+
export { VerificationError } from './webhook/types.js';
|
|
16
|
+
export { InputValidationError } from './workflows/types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudCruise JavaScript/TypeScript SDK
|
|
3
|
+
* Official client library for the CloudCruise Platform
|
|
4
|
+
*/
|
|
5
|
+
export { CloudCruise } from './CloudCruise.js';
|
|
6
|
+
export { VaultClient } from './vault/VaultClient.js';
|
|
7
|
+
export { WorkflowsClient } from './workflows/WorkflowsClient.js';
|
|
8
|
+
export { RunsClient } from './runs/RunsClient.js';
|
|
9
|
+
export { WebhookClient } from './webhook/WebhookClient.js';
|
|
10
|
+
export { VerificationError } from './webhook/types.js';
|
|
11
|
+
export { InputValidationError } from './workflows/types.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { StartRunRequest, UserInteractionData, GetRunResult, WebhookReplayResponse, RunHandle, RunStreamOptions } from './types.js';
|
|
2
|
+
import { ConnectionManager } from '../utils/connectionManager.js';
|
|
3
|
+
export declare class RunsClient {
|
|
4
|
+
private readonly makeRequest;
|
|
5
|
+
private readonly workflows?;
|
|
6
|
+
private readonly connectionManager;
|
|
7
|
+
constructor(connectionManager: ConnectionManager, makeRequest: <T = any>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: any) => Promise<T>, workflows?: {
|
|
8
|
+
validateWorkflowInput: (workflowId: string, payload: Record<string, any>) => Promise<void>;
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Queues a new run and returns a RunHandle.
|
|
12
|
+
* The handle exposes sessionId immediately and subscribes to SSE under the hood.
|
|
13
|
+
*/
|
|
14
|
+
start(request: StartRunRequest, options?: RunStreamOptions): Promise<RunHandle>;
|
|
15
|
+
/**
|
|
16
|
+
* Subscribes to SSE events for a given session. Returns a handle with helpers.
|
|
17
|
+
*/
|
|
18
|
+
subscribeToSession(sessionId: string, options?: RunStreamOptions): RunHandle;
|
|
19
|
+
/**
|
|
20
|
+
* Submits user interaction data during an active run
|
|
21
|
+
* @param sessionId - The unique identifier for the workflow execution session
|
|
22
|
+
* @param data - User input data as key-value pairs
|
|
23
|
+
*/
|
|
24
|
+
submitUserInteraction(sessionId: string, data: UserInteractionData): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves comprehensive results and execution details for a specific run
|
|
27
|
+
* @param sessionId - The unique identifier for the workflow execution session
|
|
28
|
+
* @returns Promise resolving to complete run results
|
|
29
|
+
*/
|
|
30
|
+
getResults(sessionId: string): Promise<GetRunResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Interrupts a running browser agent run
|
|
33
|
+
* @param sessionId - The unique identifier for the workflow execution session
|
|
34
|
+
*/
|
|
35
|
+
interrupt(sessionId: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Replays all webhooks that were sent during a session
|
|
38
|
+
* @param sessionId - The ID of the session to replay webhooks for
|
|
39
|
+
* @returns Promise resolving to webhook replay results
|
|
40
|
+
*/
|
|
41
|
+
replayWebhooks(sessionId: string): Promise<WebhookReplayResponse>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { AsyncEventQueue } from '../utils/asyncQueue.js';
|
|
2
|
+
import { SimpleEventEmitter } from '../utils/events.js';
|
|
3
|
+
export class RunsClient {
|
|
4
|
+
makeRequest;
|
|
5
|
+
workflows;
|
|
6
|
+
connectionManager;
|
|
7
|
+
constructor(connectionManager, makeRequest, workflows) {
|
|
8
|
+
this.makeRequest = makeRequest;
|
|
9
|
+
this.workflows = workflows;
|
|
10
|
+
this.connectionManager = connectionManager;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Queues a new run and returns a RunHandle.
|
|
14
|
+
* The handle exposes sessionId immediately and subscribes to SSE under the hood.
|
|
15
|
+
*/
|
|
16
|
+
async start(request, options) {
|
|
17
|
+
if (this.workflows) {
|
|
18
|
+
await this.workflows.validateWorkflowInput(request.workflow_id, request.run_input_variables);
|
|
19
|
+
}
|
|
20
|
+
// Ensure client_id and connection are ready to avoid missing early events
|
|
21
|
+
const clientId = await this.connectionManager.ensureClientId();
|
|
22
|
+
await this.connectionManager.connectIfNeeded();
|
|
23
|
+
request.client_id = clientId;
|
|
24
|
+
const { session_id } = await this.makeRequest('POST', '/run', request);
|
|
25
|
+
return this.subscribeToSession(session_id, options);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Subscribes to SSE events for a given session. Returns a handle with helpers.
|
|
29
|
+
*/
|
|
30
|
+
subscribeToSession(sessionId, options) {
|
|
31
|
+
const emitter = new SimpleEventEmitter();
|
|
32
|
+
const stream = new AsyncEventQueue();
|
|
33
|
+
let ended = false;
|
|
34
|
+
let closed = false;
|
|
35
|
+
let sub = null;
|
|
36
|
+
const reconnectCfg = {
|
|
37
|
+
enabled: options?.reconnect?.enabled ?? true,
|
|
38
|
+
delays: options?.reconnect?.delays ?? [1000, 3000, 10000],
|
|
39
|
+
};
|
|
40
|
+
const isTerminalEvent = (status) => status === 'execution.success' || status === 'execution.failed' || status === 'execution.stopped';
|
|
41
|
+
const emit = (event, payload) => {
|
|
42
|
+
emitter.emit(event, payload);
|
|
43
|
+
// Mirror only SSE messages to 'message' for catch-all consumers
|
|
44
|
+
if (event === 'run.event' || event === 'ping') {
|
|
45
|
+
emitter.emit('message', payload);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const endAndCleanup = (status) => {
|
|
49
|
+
if (ended)
|
|
50
|
+
return;
|
|
51
|
+
ended = true;
|
|
52
|
+
closed = true;
|
|
53
|
+
try {
|
|
54
|
+
sub?.close();
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
emit('end', { type: status });
|
|
58
|
+
stream.close();
|
|
59
|
+
emitter.clear();
|
|
60
|
+
};
|
|
61
|
+
const connect = () => {
|
|
62
|
+
sub = this.connectionManager.subscribe(sessionId, { signal: options?.signal });
|
|
63
|
+
const s = sub;
|
|
64
|
+
s.on('open', () => emit('open', undefined));
|
|
65
|
+
s.on('ping', (evt) => emit('ping', evt));
|
|
66
|
+
s.on('run.event', (msg) => {
|
|
67
|
+
const sseMsg = msg;
|
|
68
|
+
if (sseMsg.event !== 'run.event')
|
|
69
|
+
return;
|
|
70
|
+
stream.push(sseMsg);
|
|
71
|
+
emit('run.event', sseMsg);
|
|
72
|
+
const eventType = sseMsg.data.event;
|
|
73
|
+
if (typeof eventType === 'string' && isTerminalEvent(eventType)) {
|
|
74
|
+
endAndCleanup(eventType);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
s.on('error', (err) => {
|
|
78
|
+
emit('error', err);
|
|
79
|
+
if (!reconnectCfg.enabled || ended || closed)
|
|
80
|
+
return;
|
|
81
|
+
(async () => {
|
|
82
|
+
for (const base of reconnectCfg.delays) {
|
|
83
|
+
if (ended || closed)
|
|
84
|
+
return;
|
|
85
|
+
await new Promise(r => setTimeout(r, base));
|
|
86
|
+
if (ended || closed)
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
const snapshot = await this.getResults(sessionId);
|
|
90
|
+
const status = snapshot?.status;
|
|
91
|
+
if (isTerminalEvent(status)) {
|
|
92
|
+
endAndCleanup(status);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch { }
|
|
97
|
+
emit('reconnect', { attemptDelayMs: base });
|
|
98
|
+
return; // manager handles reconnect of mux
|
|
99
|
+
}
|
|
100
|
+
})();
|
|
101
|
+
});
|
|
102
|
+
s.on('reconnect', (e) => emit('reconnect', e));
|
|
103
|
+
s.on('end', (e) => {
|
|
104
|
+
const t = e?.type;
|
|
105
|
+
if (t && typeof t === 'string' && isTerminalEvent(t)) {
|
|
106
|
+
endAndCleanup(t);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// End without explicit type; still clean up
|
|
110
|
+
endAndCleanup('execution.stopped');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
connect();
|
|
115
|
+
const client = this;
|
|
116
|
+
const handle = {
|
|
117
|
+
sessionId,
|
|
118
|
+
on: (event, handler) => emitter.on(event, handler),
|
|
119
|
+
async wait() {
|
|
120
|
+
if (ended) {
|
|
121
|
+
return await client.getResults(sessionId);
|
|
122
|
+
}
|
|
123
|
+
return await new Promise((resolve, reject) => {
|
|
124
|
+
const offEnd = handle.on('end', async () => {
|
|
125
|
+
offErr();
|
|
126
|
+
try {
|
|
127
|
+
const result = await client.getResults(sessionId);
|
|
128
|
+
resolve(result);
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
reject(e);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
const offErr = handle.on('error', (e) => {
|
|
135
|
+
offEnd();
|
|
136
|
+
reject(e instanceof Error ? e : new Error('SSE error'));
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
close() {
|
|
141
|
+
closed = true;
|
|
142
|
+
try {
|
|
143
|
+
sub?.close();
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
stream.close();
|
|
147
|
+
emitter.clear();
|
|
148
|
+
},
|
|
149
|
+
async *[Symbol.asyncIterator]() {
|
|
150
|
+
for await (const msg of stream) {
|
|
151
|
+
yield msg;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
return handle;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Submits user interaction data during an active run
|
|
159
|
+
* @param sessionId - The unique identifier for the workflow execution session
|
|
160
|
+
* @param data - User input data as key-value pairs
|
|
161
|
+
*/
|
|
162
|
+
async submitUserInteraction(sessionId, data) {
|
|
163
|
+
const path = `/run/${sessionId}/user_interaction`;
|
|
164
|
+
await this.makeRequest('POST', path, data);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Retrieves comprehensive results and execution details for a specific run
|
|
168
|
+
* @param sessionId - The unique identifier for the workflow execution session
|
|
169
|
+
* @returns Promise resolving to complete run results
|
|
170
|
+
*/
|
|
171
|
+
async getResults(sessionId) {
|
|
172
|
+
const path = `/run/${sessionId}`;
|
|
173
|
+
return await this.makeRequest('GET', path);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Interrupts a running browser agent run
|
|
177
|
+
* @param sessionId - The unique identifier for the workflow execution session
|
|
178
|
+
*/
|
|
179
|
+
async interrupt(sessionId) {
|
|
180
|
+
const path = `/run/${sessionId}/interrupt`;
|
|
181
|
+
await this.makeRequest('POST', path);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Replays all webhooks that were sent during a session
|
|
185
|
+
* @param sessionId - The ID of the session to replay webhooks for
|
|
186
|
+
* @returns Promise resolving to webhook replay results
|
|
187
|
+
*/
|
|
188
|
+
async replayWebhooks(sessionId) {
|
|
189
|
+
const path = `/webhooks/${sessionId}/replay`;
|
|
190
|
+
return await this.makeRequest('POST', path);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudCruise Runs API Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
export type EventType = 'execution.queued' | 'execution.start' | 'execution.step' | 'execution.pause' | 'execution.stopped' | 'execution.failed' | 'execution.success' | 'execution.requeued' | 'file.uploaded' | 'screenshot.uploaded' | 'video.uploaded' | 'interaction.waiting' | 'interaction.finished' | 'interaction.failed';
|
|
5
|
+
export interface DryRun {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
add_to_output?: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
export interface Metadata {
|
|
10
|
+
metadata: Record<string, any>;
|
|
11
|
+
}
|
|
12
|
+
export interface RunSpecificWebhook {
|
|
13
|
+
url: string;
|
|
14
|
+
event_types_subscribed: EventType[];
|
|
15
|
+
secret: string;
|
|
16
|
+
validity: number;
|
|
17
|
+
}
|
|
18
|
+
export type PayloadWebhook = Metadata | RunSpecificWebhook;
|
|
19
|
+
export interface StartRunRequest {
|
|
20
|
+
workflow_id: string;
|
|
21
|
+
run_input_variables: Record<string, any>;
|
|
22
|
+
dry_run?: DryRun;
|
|
23
|
+
webhook?: PayloadWebhook;
|
|
24
|
+
additional_context?: Record<string, any>;
|
|
25
|
+
client_id?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface StartRunResponse {
|
|
28
|
+
session_id: string;
|
|
29
|
+
}
|
|
30
|
+
export type UserInteractionData = Record<string, any>;
|
|
31
|
+
export interface VideoUrl {
|
|
32
|
+
timestamp: string;
|
|
33
|
+
session_id: string;
|
|
34
|
+
signed_screen_recording_url: string;
|
|
35
|
+
signed_screen_recording_url_expires: string;
|
|
36
|
+
}
|
|
37
|
+
export interface RunError {
|
|
38
|
+
prompt?: string | null;
|
|
39
|
+
message?: string | null;
|
|
40
|
+
error_id?: string | null;
|
|
41
|
+
full_url?: string | null;
|
|
42
|
+
llm_model?: string | null;
|
|
43
|
+
created_at?: string | null;
|
|
44
|
+
error_code?: string | null;
|
|
45
|
+
action_type?: string | null;
|
|
46
|
+
action_display_name?: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface SignedFileUrl {
|
|
49
|
+
signed_file_url: string;
|
|
50
|
+
file_name: string;
|
|
51
|
+
timestamp: string;
|
|
52
|
+
signed_file_url_expires: string;
|
|
53
|
+
metadata: Record<string, any>;
|
|
54
|
+
}
|
|
55
|
+
export interface SignedScreenshotUrl {
|
|
56
|
+
signed_screenshot_url: string;
|
|
57
|
+
node_display_name: string;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
signed_screenshot_url_expires: string;
|
|
60
|
+
error_screenshot: boolean;
|
|
61
|
+
full_length_screenshot?: boolean;
|
|
62
|
+
retry_index?: number;
|
|
63
|
+
}
|
|
64
|
+
export interface WorkflowError {
|
|
65
|
+
message: string;
|
|
66
|
+
error_id: string;
|
|
67
|
+
full_url?: string | null;
|
|
68
|
+
created_at?: string | null;
|
|
69
|
+
error_code?: string | null;
|
|
70
|
+
action_type?: string | null;
|
|
71
|
+
action_display_name?: string | null;
|
|
72
|
+
}
|
|
73
|
+
export interface RunResult {
|
|
74
|
+
session_id: string;
|
|
75
|
+
status: EventType;
|
|
76
|
+
input_variables: Record<string, any>;
|
|
77
|
+
data: Record<string, any>;
|
|
78
|
+
video_urls: VideoUrl[];
|
|
79
|
+
file_urls: SignedFileUrl[];
|
|
80
|
+
screenshot_urls: SignedScreenshotUrl[];
|
|
81
|
+
errors: RunError[] | null;
|
|
82
|
+
}
|
|
83
|
+
export interface GetRunResult {
|
|
84
|
+
data: Record<string, any> | null;
|
|
85
|
+
session_id: string;
|
|
86
|
+
errors: WorkflowError[];
|
|
87
|
+
status: EventType;
|
|
88
|
+
input_variables: Record<string, any>;
|
|
89
|
+
workflow_id: string | null;
|
|
90
|
+
session_retries: number | null;
|
|
91
|
+
encrypted_variables: string[] | null;
|
|
92
|
+
video_urls: VideoUrl[] | null;
|
|
93
|
+
screenshot_urls?: SignedScreenshotUrl[] | null;
|
|
94
|
+
file_urls: SignedFileUrl[] | null;
|
|
95
|
+
}
|
|
96
|
+
export interface WebhookEvent {
|
|
97
|
+
success: boolean;
|
|
98
|
+
response: string;
|
|
99
|
+
error: string;
|
|
100
|
+
}
|
|
101
|
+
export interface WebhookReplayResponse {
|
|
102
|
+
status: string;
|
|
103
|
+
info: string;
|
|
104
|
+
nr_success: number;
|
|
105
|
+
nr_failed: number;
|
|
106
|
+
webhook_events: WebhookEvent[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Streaming (SSE) types
|
|
110
|
+
*/
|
|
111
|
+
export type SseEventName = 'run.event' | 'ping';
|
|
112
|
+
export interface RunEventEnvelope {
|
|
113
|
+
event: 'run.event';
|
|
114
|
+
data: {
|
|
115
|
+
event: EventType | string;
|
|
116
|
+
payload: {
|
|
117
|
+
session_id: string;
|
|
118
|
+
[key: string]: any;
|
|
119
|
+
};
|
|
120
|
+
expires_at: number;
|
|
121
|
+
timestamp: number;
|
|
122
|
+
};
|
|
123
|
+
timestamp?: string;
|
|
124
|
+
expires_at?: string;
|
|
125
|
+
}
|
|
126
|
+
export interface PingEnvelope {
|
|
127
|
+
event: 'ping';
|
|
128
|
+
data: {
|
|
129
|
+
ts: number;
|
|
130
|
+
} | Record<string, any>;
|
|
131
|
+
}
|
|
132
|
+
export type SseMessage = RunEventEnvelope | PingEnvelope;
|
|
133
|
+
export interface RunStreamOptions {
|
|
134
|
+
signal?: AbortSignal;
|
|
135
|
+
withCredentials?: boolean;
|
|
136
|
+
headers?: Record<string, string>;
|
|
137
|
+
reconnect?: {
|
|
138
|
+
enabled?: boolean;
|
|
139
|
+
delays?: number[];
|
|
140
|
+
jitter?: number;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export interface RunHandle {
|
|
144
|
+
sessionId: string;
|
|
145
|
+
on(event: 'open' | 'reconnect' | 'error' | 'end' | SseEventName | 'message', handler: (e: unknown) => void): () => void;
|
|
146
|
+
wait(): Promise<GetRunResult>;
|
|
147
|
+
close(): void;
|
|
148
|
+
[Symbol.asyncIterator](): AsyncIterator<SseMessage>;
|
|
149
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class AsyncEventQueue {
|
|
2
|
+
items = [];
|
|
3
|
+
pending = null;
|
|
4
|
+
done = false;
|
|
5
|
+
push(item) {
|
|
6
|
+
if (this.done)
|
|
7
|
+
return;
|
|
8
|
+
if (this.pending) {
|
|
9
|
+
const resolve = this.pending;
|
|
10
|
+
this.pending = null;
|
|
11
|
+
resolve({ value: item, done: false });
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
this.items.push(item);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
close() {
|
|
18
|
+
if (this.done)
|
|
19
|
+
return;
|
|
20
|
+
this.done = true;
|
|
21
|
+
if (this.pending) {
|
|
22
|
+
const resolve = this.pending;
|
|
23
|
+
this.pending = null;
|
|
24
|
+
resolve({ value: undefined, done: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async next() {
|
|
28
|
+
if (this.items.length) {
|
|
29
|
+
return { value: this.items.shift(), done: false };
|
|
30
|
+
}
|
|
31
|
+
if (this.done) {
|
|
32
|
+
return { value: undefined, done: true };
|
|
33
|
+
}
|
|
34
|
+
return await new Promise(resolve => {
|
|
35
|
+
this.pending = resolve;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
[Symbol.asyncIterator]() {
|
|
39
|
+
return {
|
|
40
|
+
next: () => this.next(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SseMessage } from '../runs/types.js';
|
|
2
|
+
type Listener = (e: unknown) => void;
|
|
3
|
+
interface SubscribeOptions {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
}
|
|
6
|
+
export interface SessionSubscription {
|
|
7
|
+
on(event: string, handler: Listener): () => void;
|
|
8
|
+
close(): void;
|
|
9
|
+
[Symbol.asyncIterator](): AsyncIterator<SseMessage>;
|
|
10
|
+
}
|
|
11
|
+
export declare class ConnectionManager {
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly apiKey;
|
|
14
|
+
private clientId;
|
|
15
|
+
private conn;
|
|
16
|
+
private connecting;
|
|
17
|
+
private connected;
|
|
18
|
+
private reconnecting;
|
|
19
|
+
private readonly reconnectDelays;
|
|
20
|
+
private readonly sessions;
|
|
21
|
+
constructor(baseUrl: string, apiKey: string);
|
|
22
|
+
ensureClientId(): Promise<string>;
|
|
23
|
+
private generateClientId;
|
|
24
|
+
connectIfNeeded(): Promise<void>;
|
|
25
|
+
subscribe(sessionId: string, opts?: SubscribeOptions): SessionSubscription;
|
|
26
|
+
private openMuxConnection;
|
|
27
|
+
private scheduleReconnect;
|
|
28
|
+
}
|
|
29
|
+
export {};
|