cloudcruise 0.0.4 → 1.0.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.
@@ -1,155 +0,0 @@
1
- /**
2
- * CloudCruise Runs API Type Definitions
3
- */
4
- import type { EventType, RunEventMessage, EventPayloadMap, ExecutionQueuedPayload, ExecutionStartPayload, ExecutionStepPayload, InteractionWaitingPayload, InteractionFinishedPayload, AgentErrorAnalysisPayload, ExecutionRequeuedPayload, EndRunPayload, EndRunError, ExecutionStoppedEarlyPayload, FileUploadedPayload, ScreenshotUploadedPayload } from '../events/types.js';
5
- export type { EventType };
6
- export type { ExecutionQueuedPayload, ExecutionStartPayload, ExecutionStepPayload, InteractionWaitingPayload, InteractionFinishedPayload, AgentErrorAnalysisPayload, ExecutionRequeuedPayload, EndRunPayload, EndRunError, ExecutionStoppedEarlyPayload, FileUploadedPayload, ScreenshotUploadedPayload, EventPayloadMap, };
7
- export interface DryRun {
8
- enabled: boolean;
9
- add_to_output?: Record<string, any>;
10
- }
11
- export interface Metadata {
12
- metadata: Record<string, any>;
13
- }
14
- export interface RunSpecificWebhook {
15
- url: string;
16
- event_types_subscribed: EventType[];
17
- secret: string;
18
- validity: number;
19
- }
20
- export type PayloadWebhook = Metadata | RunSpecificWebhook;
21
- export interface StartRunRequest {
22
- workflow_id: string;
23
- run_input_variables: Record<string, any>;
24
- dry_run?: DryRun;
25
- webhook?: PayloadWebhook;
26
- additional_context?: Record<string, any>;
27
- client_id?: string;
28
- }
29
- export interface StartRunResponse {
30
- session_id: string;
31
- }
32
- export type UserInteractionData = Record<string, any>;
33
- export interface VideoUrl {
34
- timestamp: string;
35
- session_id: string;
36
- signed_screen_recording_url: string;
37
- signed_screen_recording_url_expires: string;
38
- }
39
- export interface RunError {
40
- prompt?: string | null;
41
- message?: string | null;
42
- error_id?: string | null;
43
- full_url?: string | null;
44
- llm_model?: string | null;
45
- created_at?: string | null;
46
- error_code?: string | null;
47
- action_type?: string | null;
48
- action_display_name?: string | null;
49
- }
50
- export interface SignedFileUrl {
51
- signed_file_url: string;
52
- file_name: string;
53
- timestamp: string;
54
- signed_file_url_expires: string;
55
- metadata: Record<string, any>;
56
- }
57
- export interface SignedScreenshotUrl {
58
- signed_screenshot_url: string;
59
- node_display_name: string;
60
- timestamp: string;
61
- signed_screenshot_url_expires: string;
62
- error_screenshot: boolean;
63
- full_length_screenshot?: boolean;
64
- retry_index?: number;
65
- }
66
- export interface WorkflowError {
67
- message: string;
68
- error_id: string;
69
- full_url?: string | null;
70
- created_at?: string | null;
71
- error_code?: string | null;
72
- action_type?: string | null;
73
- action_display_name?: string | null;
74
- }
75
- export interface RunResult {
76
- session_id: string;
77
- status: EventType;
78
- input_variables: Record<string, any>;
79
- data: Record<string, any>;
80
- video_urls: VideoUrl[];
81
- file_urls: SignedFileUrl[];
82
- screenshot_urls: SignedScreenshotUrl[];
83
- errors: RunError[] | null;
84
- }
85
- export interface GetRunResult {
86
- data: Record<string, any> | null;
87
- session_id: string;
88
- errors: WorkflowError[];
89
- status: EventType;
90
- input_variables: Record<string, any>;
91
- workflow_id: string | null;
92
- session_retries: number | null;
93
- encrypted_variables: string[] | null;
94
- video_urls: VideoUrl[] | null;
95
- screenshot_urls?: SignedScreenshotUrl[] | null;
96
- file_urls: SignedFileUrl[] | null;
97
- }
98
- export interface WebhookEvent {
99
- success: boolean;
100
- response: string;
101
- error: string;
102
- }
103
- export interface WebhookReplayResponse {
104
- status: string;
105
- info: string;
106
- nr_success: number;
107
- nr_failed: number;
108
- webhook_events: WebhookEvent[];
109
- }
110
- /**
111
- * Streaming (SSE) types
112
- */
113
- export type SseEventName = 'run.event' | 'ping';
114
- export type RunEventEnvelope<E extends EventType = EventType> = RunEventMessage<E>;
115
- export interface PingEnvelope {
116
- event: 'ping';
117
- data: {
118
- ts: number;
119
- } | Record<string, any>;
120
- }
121
- export type SseMessage<E extends EventType = EventType> = RunEventEnvelope<E> | PingEnvelope;
122
- export interface RunStreamOptions {
123
- signal?: AbortSignal;
124
- withCredentials?: boolean;
125
- headers?: Record<string, string>;
126
- reconnect?: {
127
- enabled?: boolean;
128
- delays?: number[];
129
- jitter?: number;
130
- };
131
- }
132
- export type RunEventMap = {
133
- [K in EventType]: RunEventEnvelope<K>;
134
- };
135
- export type RunHandleEventMap = {
136
- 'open': undefined;
137
- 'close': undefined;
138
- 'reconnect': {
139
- attemptDelayMs: number;
140
- };
141
- 'error': unknown;
142
- 'end': {
143
- type: EventType;
144
- };
145
- 'run.event': SseMessage;
146
- 'ping': PingEnvelope;
147
- 'message': SseMessage | PingEnvelope;
148
- } & RunEventMap;
149
- export interface RunHandle {
150
- sessionId: string;
151
- on<K extends keyof RunHandleEventMap>(event: K, handler: (e: RunHandleEventMap[K]) => void): () => void;
152
- wait(): Promise<GetRunResult>;
153
- close(): void;
154
- [Symbol.asyncIterator](): AsyncIterator<SseMessage>;
155
- }
@@ -1,4 +0,0 @@
1
- /**
2
- * CloudCruise Runs API Type Definitions
3
- */
4
- export {};
@@ -1,9 +0,0 @@
1
- export declare class AsyncEventQueue<T> implements AsyncIterable<T> {
2
- private items;
3
- private pending;
4
- private done;
5
- push(item: T): void;
6
- close(): void;
7
- next(): Promise<IteratorResult<T>>;
8
- [Symbol.asyncIterator](): AsyncIterator<T>;
9
- }
@@ -1,43 +0,0 @@
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
- }
@@ -1,29 +0,0 @@
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 {};
@@ -1,234 +0,0 @@
1
- import { openSSE } from './sse.js';
2
- import { SimpleEventEmitter } from './events.js';
3
- import { AsyncEventQueue } from './asyncQueue.js';
4
- import { EventType } from '../events/types.js';
5
- function isFinalEvent(eventType) {
6
- return (eventType === EventType.ExecutionSuccess ||
7
- eventType === EventType.ExecutionFailed ||
8
- eventType === EventType.ExecutionStopped);
9
- }
10
- export class ConnectionManager {
11
- baseUrl;
12
- apiKey;
13
- clientId;
14
- conn = null;
15
- connecting = false;
16
- connected = false;
17
- reconnecting = false;
18
- reconnectDelays = [1000, 3000, 10000];
19
- sessions = new Map();
20
- constructor(baseUrl, apiKey) {
21
- this.baseUrl = baseUrl.replace(/\/$/, '');
22
- this.apiKey = apiKey;
23
- }
24
- async ensureClientId() {
25
- if (this.clientId)
26
- return this.clientId;
27
- this.clientId = this.generateClientId();
28
- return this.clientId;
29
- }
30
- /*
31
- try to use crypto.randomUUID if the platform is supported. otherwise fallback to other methods: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid/2117523#2117523
32
- */
33
- generateClientId() {
34
- const cryptoObj = globalThis.crypto;
35
- // Preferred: native CSPRNG-backed UUID
36
- if (cryptoObj?.randomUUID) {
37
- return cryptoObj.randomUUID();
38
- }
39
- // Fallback: RFC4122 v4 built from CSPRNG bytes
40
- if (cryptoObj?.getRandomValues) {
41
- const bytes = new Uint8Array(16);
42
- cryptoObj.getRandomValues(bytes);
43
- bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
44
- bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx
45
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));
46
- return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10).join('')}`;
47
- }
48
- // Last resort: non-CSPRNG timestamp + Math.random
49
- let d = Date.now();
50
- let d2 = typeof performance !== 'undefined' && typeof performance.now === 'function'
51
- ? Math.floor(performance.now() * 1000)
52
- : 0;
53
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
54
- let r = Math.random() * 16;
55
- if (d > 0) {
56
- r = ((d + r) % 16) | 0;
57
- d = Math.floor(d / 16);
58
- }
59
- else {
60
- r = ((d2 + r) % 16) | 0;
61
- d2 = Math.floor(d2 / 16);
62
- }
63
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
64
- return v.toString(16);
65
- });
66
- }
67
- async connectIfNeeded() {
68
- if (this.connected || this.connecting)
69
- return;
70
- if (!this.clientId)
71
- await this.ensureClientId();
72
- await this.openMuxConnection();
73
- }
74
- subscribe(sessionId, opts) {
75
- // Kick off connection if not already connected
76
- try {
77
- void this.connectIfNeeded();
78
- }
79
- catch { }
80
- // Ensure channel exists
81
- let channel = this.sessions.get(sessionId);
82
- if (!channel) {
83
- channel = {
84
- sessionId,
85
- emitter: new SimpleEventEmitter(),
86
- subscribers: new Set(),
87
- ended: false
88
- };
89
- this.sessions.set(sessionId, channel);
90
- }
91
- // Create per-handle queue
92
- const queue = new AsyncEventQueue();
93
- channel.subscribers.add(queue);
94
- // Propagate abort to handle close
95
- if (opts?.signal) {
96
- const onAbort = () => sub.close();
97
- opts.signal.addEventListener('abort', onAbort, { once: true });
98
- }
99
- const sub = {
100
- on: (event, handler) => channel.emitter.on(event, handler),
101
- close: () => {
102
- if (!channel)
103
- return;
104
- queue.close();
105
- channel.subscribers.delete(queue);
106
- // If no more subscribers and channel is ended, remove it
107
- if (channel.subscribers.size === 0 && channel.ended) {
108
- this.sessions.delete(sessionId);
109
- }
110
- },
111
- [Symbol.asyncIterator]() {
112
- return queue[Symbol.asyncIterator]();
113
- }
114
- };
115
- return sub;
116
- }
117
- async openMuxConnection() {
118
- if (this.connecting || this.connected)
119
- return;
120
- if (!this.clientId)
121
- await this.ensureClientId();
122
- this.connecting = true;
123
- const headers = {
124
- 'cc-key': this.apiKey
125
- };
126
- const url = `${this.baseUrl}/run/clients/${this.clientId}/events`;
127
- const emitAll = (event, payload) => {
128
- for (const ch of this.sessions.values()) {
129
- ch.emitter.emit(event, payload);
130
- }
131
- };
132
- try {
133
- this.conn = openSSE(url, {
134
- onOpen: () => {
135
- this.connected = true;
136
- this.connecting = false;
137
- emitAll('open');
138
- },
139
- onEvent: (evt) => {
140
- if (evt.event === 'ping') {
141
- emitAll('ping', evt);
142
- return;
143
- }
144
- if (evt.event === 'run.event') {
145
- const raw = evt;
146
- const data = raw.data?.data;
147
- if (!data) {
148
- return;
149
- }
150
- // Extract session_id - it's always present in payload
151
- const payload = data.payload;
152
- const sessionId = payload?.session_id;
153
- if (!sessionId) {
154
- return;
155
- }
156
- const channel = this.sessions.get(sessionId);
157
- if (!channel) {
158
- return;
159
- }
160
- const msg = {
161
- event: 'run.event',
162
- data: data,
163
- timestamp: raw.timestamp || new Date().toISOString(),
164
- expires_at: raw.expires_at || new Date(Date.now() + 3600000).toISOString()
165
- };
166
- // fan-out to all subscribers
167
- for (const q of channel.subscribers)
168
- q.push(msg);
169
- channel.emitter.emit('run.event', msg);
170
- const eventType = data.event;
171
- if (isFinalEvent(eventType)) {
172
- channel.ended = true;
173
- channel.emitter.emit('end', { type: eventType });
174
- for (const q of channel.subscribers)
175
- q.close();
176
- channel.subscribers.clear();
177
- // Remove the channel after notifying
178
- this.sessions.delete(sessionId);
179
- }
180
- return;
181
- }
182
- const u = evt;
183
- },
184
- onError: (err) => {
185
- // Surface error to all channels and attempt reconnect
186
- emitAll('error', err);
187
- if (!this.reconnecting)
188
- this.scheduleReconnect();
189
- },
190
- onClose: () => {
191
- this.connected = false;
192
- this.connecting = false;
193
- emitAll('close');
194
- if (!this.reconnecting)
195
- this.scheduleReconnect();
196
- }
197
- }, {
198
- headers,
199
- withCredentials: false
200
- });
201
- }
202
- catch (e) {
203
- this.connected = false;
204
- this.connecting = false;
205
- if (!this.reconnecting)
206
- this.scheduleReconnect();
207
- }
208
- }
209
- scheduleReconnect() {
210
- if (this.reconnecting)
211
- return;
212
- this.reconnecting = true;
213
- (async () => {
214
- for (const delay of this.reconnectDelays) {
215
- // Notify listeners about reconnect attempt
216
- for (const ch of this.sessions.values())
217
- ch.emitter.emit('reconnect', { attemptDelayMs: delay });
218
- await new Promise(r => setTimeout(r, delay));
219
- try {
220
- await this.openMuxConnection();
221
- if (this.connected) {
222
- this.reconnecting = false;
223
- return;
224
- }
225
- }
226
- catch {
227
- // continue to next delay
228
- }
229
- }
230
- // Give up after exhausting delays; next event/subscribe will try again
231
- this.reconnecting = false;
232
- })();
233
- }
234
- }
@@ -1,2 +0,0 @@
1
- export type CloudCruiseEnvVar = 'CLOUDCRUISE_API_KEY' | 'CLOUDCRUISE_BASE_URL' | 'CLOUDCRUISE_ENCRYPTION_KEY';
2
- export declare function getEnv(key: CloudCruiseEnvVar): string | undefined;
package/dist/utils/env.js DELETED
@@ -1,9 +0,0 @@
1
- export function getEnv(key) {
2
- if (typeof process !== 'undefined' && process.env && process.env[key]) {
3
- return process.env[key];
4
- }
5
- else if (typeof globalThis !== 'undefined' && globalThis[key]) {
6
- return globalThis[key];
7
- }
8
- return undefined;
9
- }
@@ -1,24 +0,0 @@
1
- export type EventHandler<T = unknown> = (event: T) => void;
2
- /**
3
- * Event emitter that supports both typed and untyped usage.
4
- *
5
- * - Use without type parameter for untyped events (backward compatible)
6
- * - Use with EventMap type parameter for type-safe events
7
- *
8
- * @example
9
- * // Untyped usage
10
- * const emitter = new SimpleEventEmitter();
11
- * emitter.on('foo', (data) => console.log(data));
12
- *
13
- * @example
14
- * // Typed usage
15
- * type Events = { foo: string; bar: number };
16
- * const emitter = new SimpleEventEmitter<Events>();
17
- * emitter.on('foo', (data) => console.log(data)); // data is string
18
- */
19
- export declare class SimpleEventEmitter<EventMap extends Record<string, any> = Record<string, unknown>> {
20
- private listeners;
21
- on<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): () => void;
22
- emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void;
23
- clear(): void;
24
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * Event emitter that supports both typed and untyped usage.
3
- *
4
- * - Use without type parameter for untyped events (backward compatible)
5
- * - Use with EventMap type parameter for type-safe events
6
- *
7
- * @example
8
- * // Untyped usage
9
- * const emitter = new SimpleEventEmitter();
10
- * emitter.on('foo', (data) => console.log(data));
11
- *
12
- * @example
13
- * // Typed usage
14
- * type Events = { foo: string; bar: number };
15
- * const emitter = new SimpleEventEmitter<Events>();
16
- * emitter.on('foo', (data) => console.log(data)); // data is string
17
- */
18
- export class SimpleEventEmitter {
19
- listeners = new Map();
20
- on(event, handler) {
21
- if (!this.listeners.has(event))
22
- this.listeners.set(event, new Set());
23
- this.listeners.get(event).add(handler);
24
- return () => {
25
- const set = this.listeners.get(event);
26
- if (set)
27
- set.delete(handler);
28
- };
29
- }
30
- emit(event, payload) {
31
- const set = this.listeners.get(event);
32
- if (!set)
33
- return;
34
- for (const handler of set)
35
- handler(payload);
36
- }
37
- clear() {
38
- this.listeners.clear();
39
- }
40
- }
@@ -1,24 +0,0 @@
1
- export interface SSEHandlers {
2
- onOpen?: () => void;
3
- onEvent?: (evt: {
4
- event: string;
5
- data?: unknown;
6
- id?: string;
7
- raw?: string;
8
- }) => void;
9
- onError?: (error: Error) => void;
10
- onClose?: () => void;
11
- }
12
- export interface SSEOptions {
13
- headers?: HeadersInit;
14
- withCredentials?: boolean;
15
- signal?: AbortSignal;
16
- }
17
- export interface SSEConnection {
18
- close(): void;
19
- }
20
- /**
21
- * Open an SSE connection using either native EventSource (browser, cookie auth)
22
- * or fetch streaming (Node 18+ and modern browsers) when custom headers are needed.
23
- */
24
- export declare function openSSE(url: string, handlers: SSEHandlers, opts?: SSEOptions): SSEConnection;