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.
- package/package.json +1 -1
- package/dist/CloudCruise.d.ts +0 -25
- package/dist/CloudCruise.js +0 -81
- package/dist/events/types.d.ts +0 -152
- package/dist/events/types.js +0 -22
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -13
- package/dist/runs/RunsClient.d.ts +0 -42
- package/dist/runs/RunsClient.js +0 -200
- package/dist/runs/types.d.ts +0 -155
- package/dist/runs/types.js +0 -4
- package/dist/utils/asyncQueue.d.ts +0 -9
- package/dist/utils/asyncQueue.js +0 -43
- package/dist/utils/connectionManager.d.ts +0 -29
- package/dist/utils/connectionManager.js +0 -234
- package/dist/utils/env.d.ts +0 -2
- package/dist/utils/env.js +0 -9
- package/dist/utils/events.d.ts +0 -24
- package/dist/utils/events.js +0 -40
- package/dist/utils/sse.d.ts +0 -24
- package/dist/utils/sse.js +0 -122
- package/dist/vault/VaultClient.d.ts +0 -42
- package/dist/vault/VaultClient.js +0 -91
- package/dist/vault/types.d.ts +0 -50
- package/dist/vault/types.js +0 -4
- package/dist/vault/utils.d.ts +0 -33
- package/dist/vault/utils.js +0 -99
- package/dist/webhook/WebhookClient.d.ts +0 -15
- package/dist/webhook/WebhookClient.js +0 -18
- package/dist/webhook/types.d.ts +0 -7
- package/dist/webhook/types.js +0 -8
- package/dist/webhook/utils.d.ts +0 -3
- package/dist/webhook/utils.js +0 -49
- package/dist/workflows/WorkflowsClient.d.ts +0 -19
- package/dist/workflows/WorkflowsClient.js +0 -97
- package/dist/workflows/types.d.ts +0 -41
- package/dist/workflows/types.js +0 -15
package/dist/utils/sse.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Open an SSE connection using either native EventSource (browser, cookie auth)
|
|
3
|
-
* or fetch streaming (Node 18+ and modern browsers) when custom headers are needed.
|
|
4
|
-
*/
|
|
5
|
-
export function openSSE(url, handlers, opts) {
|
|
6
|
-
const { onOpen, onEvent, onError, onClose } = handlers;
|
|
7
|
-
const { headers, withCredentials, signal } = opts ?? {};
|
|
8
|
-
const shouldUseEventSource = () => typeof window !== 'undefined' &&
|
|
9
|
-
'EventSource' in window &&
|
|
10
|
-
withCredentials === true &&
|
|
11
|
-
(!headers || Object.keys(headers).length === 0);
|
|
12
|
-
const parseJSON = (text) => {
|
|
13
|
-
try {
|
|
14
|
-
return text ? JSON.parse(text) : undefined;
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return text || undefined;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
const createEventSourceConnection = () => {
|
|
21
|
-
const ES = window.EventSource;
|
|
22
|
-
const es = new ES(url, { withCredentials: true });
|
|
23
|
-
const onOpenHandler = () => onOpen?.();
|
|
24
|
-
const onErrorHandler = (e) => onError?.(e instanceof Error ? e : new Error('EventSource error'));
|
|
25
|
-
const onRunEvent = (e) => {
|
|
26
|
-
const raw = String(e.data ?? '');
|
|
27
|
-
const parsed = parseJSON(raw);
|
|
28
|
-
onEvent?.({ event: 'run.event', data: parsed, id: e.lastEventId, raw });
|
|
29
|
-
};
|
|
30
|
-
const onPing = (e) => {
|
|
31
|
-
const raw = String(e.data ?? '');
|
|
32
|
-
const parsed = parseJSON(raw);
|
|
33
|
-
onEvent?.({ event: 'ping', data: parsed, id: e.lastEventId, raw });
|
|
34
|
-
};
|
|
35
|
-
es.addEventListener('open', onOpenHandler);
|
|
36
|
-
es.addEventListener('error', onErrorHandler);
|
|
37
|
-
es.addEventListener('run.event', onRunEvent);
|
|
38
|
-
es.addEventListener('ping', onPing);
|
|
39
|
-
if (signal) {
|
|
40
|
-
const onAbort = () => {
|
|
41
|
-
es.close();
|
|
42
|
-
onClose?.();
|
|
43
|
-
};
|
|
44
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
close() {
|
|
48
|
-
es.close();
|
|
49
|
-
onClose?.();
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
const createFetchConnection = () => {
|
|
54
|
-
const controller = new AbortController();
|
|
55
|
-
const abort = () => controller.abort();
|
|
56
|
-
if (signal)
|
|
57
|
-
signal.addEventListener('abort', abort, { once: true });
|
|
58
|
-
const parseFrame = (frame) => {
|
|
59
|
-
let event = 'message';
|
|
60
|
-
let data = '';
|
|
61
|
-
let id;
|
|
62
|
-
for (const line of frame.split(/\r?\n/)) {
|
|
63
|
-
if (!line || line.startsWith(':'))
|
|
64
|
-
continue;
|
|
65
|
-
const idx = line.indexOf(':');
|
|
66
|
-
const field = idx === -1 ? line : line.slice(0, idx);
|
|
67
|
-
const value = idx === -1 ? '' : line.slice(idx + 1).trimStart();
|
|
68
|
-
if (field === 'event')
|
|
69
|
-
event = value;
|
|
70
|
-
else if (field === 'data')
|
|
71
|
-
data += (data ? '\n' : '') + value;
|
|
72
|
-
else if (field === 'id')
|
|
73
|
-
id = value;
|
|
74
|
-
}
|
|
75
|
-
const parsed = parseJSON(data);
|
|
76
|
-
return { event, data: parsed, id, raw: frame };
|
|
77
|
-
};
|
|
78
|
-
(async () => {
|
|
79
|
-
try {
|
|
80
|
-
const res = await fetch(url, {
|
|
81
|
-
method: 'GET',
|
|
82
|
-
headers: {
|
|
83
|
-
Accept: 'text/event-stream',
|
|
84
|
-
'Cache-Control': 'no-cache',
|
|
85
|
-
...(headers ?? {}),
|
|
86
|
-
},
|
|
87
|
-
credentials: withCredentials ? 'include' : 'same-origin',
|
|
88
|
-
signal: controller.signal,
|
|
89
|
-
});
|
|
90
|
-
if (!res.ok || !res.body)
|
|
91
|
-
throw new Error(`SSE HTTP ${res.status}`);
|
|
92
|
-
onOpen?.();
|
|
93
|
-
const reader = res.body.getReader();
|
|
94
|
-
const decoder = new TextDecoder();
|
|
95
|
-
let buffer = '';
|
|
96
|
-
while (true) {
|
|
97
|
-
const { done, value } = await reader.read();
|
|
98
|
-
if (done)
|
|
99
|
-
break;
|
|
100
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
101
|
-
const parts = (buffer + chunk).split(/\r?\n\r?\n/);
|
|
102
|
-
buffer = parts.pop() ?? '';
|
|
103
|
-
for (const frame of parts) {
|
|
104
|
-
const evt = parseFrame(frame);
|
|
105
|
-
onEvent?.(evt);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
onClose?.();
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
112
|
-
}
|
|
113
|
-
})();
|
|
114
|
-
return {
|
|
115
|
-
close() {
|
|
116
|
-
abort();
|
|
117
|
-
onClose?.();
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
return shouldUseEventSource() ? createEventSourceConnection() : createFetchConnection();
|
|
122
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { GetVaultEntriesFilters, VaultEntry } from './types.js';
|
|
2
|
-
export declare class VaultClient {
|
|
3
|
-
private readonly makeRequest;
|
|
4
|
-
private readonly encryptionKey;
|
|
5
|
-
constructor(makeRequest: <T = any>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: any) => Promise<T>, encryptionKey: string);
|
|
6
|
-
/**
|
|
7
|
-
* Creates a new vault entry
|
|
8
|
-
*/
|
|
9
|
-
create(domain: string, permissioned_user_id: string, options?: Partial<Omit<VaultEntry, 'id' | 'created_at' | 'domain' | 'permissioned_user_id'>>): Promise<VaultEntry>;
|
|
10
|
-
/**
|
|
11
|
-
* Gets vault entries, optionally filtered
|
|
12
|
-
* @param filters - Optional filters for the request
|
|
13
|
-
* @param filters.permissioned_user_id - Filter by user ID
|
|
14
|
-
* @param filters.domain - Filter by domain
|
|
15
|
-
* @param filters.decryptCredentials - Whether to decrypt sensitive fields (default: true)
|
|
16
|
-
*/
|
|
17
|
-
get(filters?: GetVaultEntriesFilters): Promise<VaultEntry[]>;
|
|
18
|
-
/**
|
|
19
|
-
* Updates an existing vault entry
|
|
20
|
-
* @param updates - Vault entry updates including required fields
|
|
21
|
-
* @param updates.permissioned_user_id - Required: User identifier for the vault entry
|
|
22
|
-
* @param updates.user_name - Required: Username or email
|
|
23
|
-
* @param updates.password - Required: User password
|
|
24
|
-
* @param updates.domain - Required: Target domain for the credentials
|
|
25
|
-
*/
|
|
26
|
-
update(updates: Partial<VaultEntry> & {
|
|
27
|
-
permissioned_user_id: string;
|
|
28
|
-
user_name: string;
|
|
29
|
-
password: string;
|
|
30
|
-
domain: string;
|
|
31
|
-
}): Promise<VaultEntry>;
|
|
32
|
-
/**
|
|
33
|
-
* Deletes a vault entry by domain and permissioned user ID
|
|
34
|
-
* @param params - Object containing domain and permissioned_user_id
|
|
35
|
-
* @param params.domain - The domain of the vault entry to delete
|
|
36
|
-
* @param params.permissioned_user_id - The permissioned user ID of the vault entry to delete
|
|
37
|
-
*/
|
|
38
|
-
delete(params: {
|
|
39
|
-
domain: string;
|
|
40
|
-
permissioned_user_id: string;
|
|
41
|
-
}): Promise<void>;
|
|
42
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { encryptSensitiveFields, decryptSensitiveFields } from './utils.js';
|
|
2
|
-
export class VaultClient {
|
|
3
|
-
makeRequest;
|
|
4
|
-
encryptionKey;
|
|
5
|
-
constructor(makeRequest, encryptionKey) {
|
|
6
|
-
this.makeRequest = makeRequest;
|
|
7
|
-
this.encryptionKey = encryptionKey;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Creates a new vault entry
|
|
11
|
-
*/
|
|
12
|
-
async create(domain, permissioned_user_id, options) {
|
|
13
|
-
const entry = {
|
|
14
|
-
domain,
|
|
15
|
-
permissioned_user_id,
|
|
16
|
-
...options
|
|
17
|
-
};
|
|
18
|
-
let processedEntry = { ...entry };
|
|
19
|
-
// Encrypt sensitive fields
|
|
20
|
-
processedEntry = await encryptSensitiveFields(processedEntry, this.encryptionKey);
|
|
21
|
-
const response = await this.makeRequest('POST', '/vault', processedEntry);
|
|
22
|
-
// Decrypt response using encryption key
|
|
23
|
-
return await decryptSensitiveFields(response, this.encryptionKey);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Gets vault entries, optionally filtered
|
|
27
|
-
* @param filters - Optional filters for the request
|
|
28
|
-
* @param filters.permissioned_user_id - Filter by user ID
|
|
29
|
-
* @param filters.domain - Filter by domain
|
|
30
|
-
* @param filters.decryptCredentials - Whether to decrypt sensitive fields (default: true)
|
|
31
|
-
*/
|
|
32
|
-
async get(filters) {
|
|
33
|
-
let path = '/vault';
|
|
34
|
-
if (filters && (filters.permissioned_user_id || filters.domain)) {
|
|
35
|
-
const params = new URLSearchParams();
|
|
36
|
-
if (filters.permissioned_user_id) {
|
|
37
|
-
params.append('permissioned_user_id', filters.permissioned_user_id);
|
|
38
|
-
}
|
|
39
|
-
if (filters.domain) {
|
|
40
|
-
params.append('domain', filters.domain);
|
|
41
|
-
}
|
|
42
|
-
path += `?${params.toString()}`;
|
|
43
|
-
}
|
|
44
|
-
const response = await this.makeRequest('GET', path);
|
|
45
|
-
let entries = Array.isArray(response) ? response : [response];
|
|
46
|
-
// Conditionally decrypt sensitive fields based on decryptCredentials flag
|
|
47
|
-
const shouldDecrypt = filters?.decryptCredentials !== false;
|
|
48
|
-
if (shouldDecrypt) {
|
|
49
|
-
entries = await Promise.all(entries.map(entry => decryptSensitiveFields(entry, this.encryptionKey)));
|
|
50
|
-
}
|
|
51
|
-
return entries;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Updates an existing vault entry
|
|
55
|
-
* @param updates - Vault entry updates including required fields
|
|
56
|
-
* @param updates.permissioned_user_id - Required: User identifier for the vault entry
|
|
57
|
-
* @param updates.user_name - Required: Username or email
|
|
58
|
-
* @param updates.password - Required: User password
|
|
59
|
-
* @param updates.domain - Required: Target domain for the credentials
|
|
60
|
-
*/
|
|
61
|
-
async update(updates) {
|
|
62
|
-
// Validate required fields
|
|
63
|
-
if (!updates.permissioned_user_id) {
|
|
64
|
-
throw new Error('permissioned_user_id is required for vault updates');
|
|
65
|
-
}
|
|
66
|
-
if (!updates.user_name) {
|
|
67
|
-
throw new Error('user_name is required for vault updates');
|
|
68
|
-
}
|
|
69
|
-
if (!updates.password) {
|
|
70
|
-
throw new Error('password is required for vault updates');
|
|
71
|
-
}
|
|
72
|
-
if (!updates.domain) {
|
|
73
|
-
throw new Error('domain is required for vault updates');
|
|
74
|
-
}
|
|
75
|
-
let processedEntry = { ...updates };
|
|
76
|
-
// Encrypt sensitive fields
|
|
77
|
-
processedEntry = await encryptSensitiveFields(processedEntry, this.encryptionKey);
|
|
78
|
-
const response = await this.makeRequest('PUT', '/vault', processedEntry);
|
|
79
|
-
// Decrypt response using encryption key
|
|
80
|
-
return await decryptSensitiveFields(response, this.encryptionKey);
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Deletes a vault entry by domain and permissioned user ID
|
|
84
|
-
* @param params - Object containing domain and permissioned_user_id
|
|
85
|
-
* @param params.domain - The domain of the vault entry to delete
|
|
86
|
-
* @param params.permissioned_user_id - The permissioned user ID of the vault entry to delete
|
|
87
|
-
*/
|
|
88
|
-
async delete(params) {
|
|
89
|
-
await this.makeRequest('DELETE', '/vault', params);
|
|
90
|
-
}
|
|
91
|
-
}
|
package/dist/vault/types.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CloudCruise Vault API Type Definitions
|
|
3
|
-
*/
|
|
4
|
-
export interface VaultPostPutHeadersInBody {
|
|
5
|
-
name: string;
|
|
6
|
-
value: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ProxyConfig {
|
|
9
|
-
enable: boolean;
|
|
10
|
-
target_ip?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface VaultEntry {
|
|
13
|
-
id: string;
|
|
14
|
-
domain: string;
|
|
15
|
-
permissioned_user_id: string;
|
|
16
|
-
workspace_id?: string;
|
|
17
|
-
user_id?: string;
|
|
18
|
-
password?: string;
|
|
19
|
-
user_name?: string;
|
|
20
|
-
tfa_secret?: string;
|
|
21
|
-
user_agent?: string;
|
|
22
|
-
user_alias?: string;
|
|
23
|
-
location?: string;
|
|
24
|
-
ip_address?: string;
|
|
25
|
-
session_id?: string;
|
|
26
|
-
allow_multiple_sessions?: boolean;
|
|
27
|
-
prevent_concurrency_during_login?: boolean | null;
|
|
28
|
-
max_concurrency?: number | null;
|
|
29
|
-
expiry_time_from_last_use?: string | null;
|
|
30
|
-
expiry_time_from_session_data_set?: string | null;
|
|
31
|
-
tfa_method?: 'AUTHENTICATOR' | 'EMAIL' | 'SMS' | null;
|
|
32
|
-
cookies?: any;
|
|
33
|
-
local_storage?: any;
|
|
34
|
-
session_storage?: any;
|
|
35
|
-
persist_cookies?: boolean | null;
|
|
36
|
-
persist_local_storage?: boolean | null;
|
|
37
|
-
persist_session_storage?: boolean | null;
|
|
38
|
-
cookie_domain_to_store?: string | null;
|
|
39
|
-
proxy?: ProxyConfig;
|
|
40
|
-
proxy_string?: string | null;
|
|
41
|
-
headers?: VaultPostPutHeadersInBody[];
|
|
42
|
-
created_at?: string | null;
|
|
43
|
-
session_data_set_at?: string | null;
|
|
44
|
-
effective_expires_at?: string | null;
|
|
45
|
-
}
|
|
46
|
-
export interface GetVaultEntriesFilters {
|
|
47
|
-
permissioned_user_id?: string;
|
|
48
|
-
domain?: string;
|
|
49
|
-
decryptCredentials?: boolean;
|
|
50
|
-
}
|
package/dist/vault/types.js
DELETED
package/dist/vault/utils.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AES-256-GCM Encryption utilities for CloudCruise vault data
|
|
3
|
-
* Uses 12-byte IV and returns concatenated hex: iv(24 hex) + ciphertext + tag(32 hex)
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Encrypts sensitive data using AES-256-GCM
|
|
7
|
-
* @param data - Data to encrypt (will be JSON stringified)
|
|
8
|
-
* @param keyHex - Hex-encoded encryption key
|
|
9
|
-
* @returns Concatenated hex string: iv(24 hex) + ciphertext + tag(32 hex)
|
|
10
|
-
*/
|
|
11
|
-
export declare function encryptData(data: any, keyHex: string): Promise<string>;
|
|
12
|
-
/**
|
|
13
|
-
* Decrypts data using AES-256-GCM
|
|
14
|
-
* @param encryptedHex - Concatenated hex: iv(24 hex) + ciphertext + tag(32 hex)
|
|
15
|
-
* @param keyHex - Hex-encoded encryption key
|
|
16
|
-
* @returns Decrypted and parsed data
|
|
17
|
-
*/
|
|
18
|
-
export declare function decryptData(encryptedHex: string, keyHex: string): Promise<any>;
|
|
19
|
-
/**
|
|
20
|
-
* Encrypts sensitive fields in a vault entry
|
|
21
|
-
* Fields encrypted: user_name, password, tfa_secret (if present)
|
|
22
|
-
* @param entry - Vault entry with potentially sensitive data
|
|
23
|
-
* @param encryptionKey - Hex-encoded encryption key
|
|
24
|
-
* @returns Entry with encrypted sensitive fields
|
|
25
|
-
*/
|
|
26
|
-
export declare function encryptSensitiveFields(entry: any, encryptionKey: string): Promise<any>;
|
|
27
|
-
/**
|
|
28
|
-
* Decrypts sensitive fields in a vault entry
|
|
29
|
-
* @param entry - Vault entry with encrypted sensitive fields
|
|
30
|
-
* @param encryptionKey - Hex-encoded encryption key
|
|
31
|
-
* @returns Entry with decrypted sensitive fields
|
|
32
|
-
*/
|
|
33
|
-
export declare function decryptSensitiveFields(entry: any, encryptionKey: string): Promise<any>;
|
package/dist/vault/utils.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
|
2
|
-
/**
|
|
3
|
-
* AES-256-GCM Encryption utilities for CloudCruise vault data
|
|
4
|
-
* Uses 12-byte IV and returns concatenated hex: iv(24 hex) + ciphertext + tag(32 hex)
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Encrypts sensitive data using AES-256-GCM
|
|
8
|
-
* @param data - Data to encrypt (will be JSON stringified)
|
|
9
|
-
* @param keyHex - Hex-encoded encryption key
|
|
10
|
-
* @returns Concatenated hex string: iv(24 hex) + ciphertext + tag(32 hex)
|
|
11
|
-
*/
|
|
12
|
-
export async function encryptData(data, keyHex) {
|
|
13
|
-
try {
|
|
14
|
-
const key = Buffer.from(keyHex, 'hex');
|
|
15
|
-
const iv = randomBytes(12); // 12-byte IV for GCM
|
|
16
|
-
const jsonData = JSON.stringify(data);
|
|
17
|
-
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
18
|
-
let encrypted = cipher.update(jsonData, 'utf8', 'hex');
|
|
19
|
-
encrypted += cipher.final('hex');
|
|
20
|
-
const tag = cipher.getAuthTag().toString('hex');
|
|
21
|
-
return iv.toString('hex') + encrypted + tag;
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
throw new Error(`Encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Decrypts data using AES-256-GCM
|
|
29
|
-
* @param encryptedHex - Concatenated hex: iv(24 hex) + ciphertext + tag(32 hex)
|
|
30
|
-
* @param keyHex - Hex-encoded encryption key
|
|
31
|
-
* @returns Decrypted and parsed data
|
|
32
|
-
*/
|
|
33
|
-
export async function decryptData(encryptedHex, keyHex) {
|
|
34
|
-
try {
|
|
35
|
-
if (typeof encryptedHex !== 'string' || encryptedHex.length < 56) {
|
|
36
|
-
throw new Error('Invalid encrypted payload');
|
|
37
|
-
}
|
|
38
|
-
const key = Buffer.from(keyHex, 'hex');
|
|
39
|
-
const iv = Buffer.from(encryptedHex.slice(0, 24), 'hex');
|
|
40
|
-
const tag = Buffer.from(encryptedHex.slice(-32), 'hex');
|
|
41
|
-
const encrypted = encryptedHex.slice(24, -32);
|
|
42
|
-
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
43
|
-
decipher.setAuthTag(tag);
|
|
44
|
-
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
45
|
-
decrypted += decipher.final('utf8');
|
|
46
|
-
return JSON.parse(decrypted);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Encrypts sensitive fields in a vault entry
|
|
54
|
-
* Fields encrypted: user_name, password, tfa_secret (if present)
|
|
55
|
-
* @param entry - Vault entry with potentially sensitive data
|
|
56
|
-
* @param encryptionKey - Hex-encoded encryption key
|
|
57
|
-
* @returns Entry with encrypted sensitive fields
|
|
58
|
-
*/
|
|
59
|
-
export async function encryptSensitiveFields(entry, encryptionKey) {
|
|
60
|
-
const encryptedEntry = { ...entry };
|
|
61
|
-
if (entry.user_name !== undefined) {
|
|
62
|
-
encryptedEntry.user_name = await encryptData(entry.user_name, encryptionKey);
|
|
63
|
-
}
|
|
64
|
-
if (entry.password !== undefined) {
|
|
65
|
-
encryptedEntry.password = await encryptData(entry.password, encryptionKey);
|
|
66
|
-
}
|
|
67
|
-
if (entry.tfa_secret !== undefined) {
|
|
68
|
-
encryptedEntry.tfa_secret = await encryptData(entry.tfa_secret, encryptionKey);
|
|
69
|
-
}
|
|
70
|
-
return encryptedEntry;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Decrypts sensitive fields in a vault entry
|
|
74
|
-
* @param entry - Vault entry with encrypted sensitive fields
|
|
75
|
-
* @param encryptionKey - Hex-encoded encryption key
|
|
76
|
-
* @returns Entry with decrypted sensitive fields
|
|
77
|
-
*/
|
|
78
|
-
export async function decryptSensitiveFields(entry, encryptionKey) {
|
|
79
|
-
const decryptedEntry = { ...entry };
|
|
80
|
-
if (typeof entry.user_name === 'string') {
|
|
81
|
-
try {
|
|
82
|
-
decryptedEntry.user_name = await decryptData(entry.user_name, encryptionKey);
|
|
83
|
-
}
|
|
84
|
-
catch { }
|
|
85
|
-
}
|
|
86
|
-
if (typeof entry.password === 'string') {
|
|
87
|
-
try {
|
|
88
|
-
decryptedEntry.password = await decryptData(entry.password, encryptionKey);
|
|
89
|
-
}
|
|
90
|
-
catch { }
|
|
91
|
-
}
|
|
92
|
-
if (typeof entry.tfa_secret === 'string') {
|
|
93
|
-
try {
|
|
94
|
-
decryptedEntry.tfa_secret = await decryptData(entry.tfa_secret, encryptionKey);
|
|
95
|
-
}
|
|
96
|
-
catch { }
|
|
97
|
-
}
|
|
98
|
-
return decryptedEntry;
|
|
99
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { WebhookVerificationOptions } from './types.js';
|
|
2
|
-
import type { EventType, WebhookMessage } from '../events/types.js';
|
|
3
|
-
export declare class WebhookClient {
|
|
4
|
-
constructor();
|
|
5
|
-
/**
|
|
6
|
-
* Verifies the signature of an incoming webhook payload.
|
|
7
|
-
*
|
|
8
|
-
* @param receivedData - Raw request body supplied by the webhook sender.
|
|
9
|
-
* @param receivedSignature - Value from the `x-hmac-signature` request header. e.g req.headers["x-hmac-signature"]
|
|
10
|
-
* @param secretKey - Webhook secret configured in the CloudCruise portal.
|
|
11
|
-
* @param options - Optional overrides controlling signature verification behavior.
|
|
12
|
-
* @returns Verified webhook payload when the signature matches.
|
|
13
|
-
*/
|
|
14
|
-
verifySignature<E extends EventType = EventType>(receivedData: any, receivedSignature: string, secretKey: string, options?: WebhookVerificationOptions): WebhookMessage<E>;
|
|
15
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { verifyMessage } from './utils.js';
|
|
2
|
-
export class WebhookClient {
|
|
3
|
-
constructor() {
|
|
4
|
-
// No makeRequest needed for webhook verification
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Verifies the signature of an incoming webhook payload.
|
|
8
|
-
*
|
|
9
|
-
* @param receivedData - Raw request body supplied by the webhook sender.
|
|
10
|
-
* @param receivedSignature - Value from the `x-hmac-signature` request header. e.g req.headers["x-hmac-signature"]
|
|
11
|
-
* @param secretKey - Webhook secret configured in the CloudCruise portal.
|
|
12
|
-
* @param options - Optional overrides controlling signature verification behavior.
|
|
13
|
-
* @returns Verified webhook payload when the signature matches.
|
|
14
|
-
*/
|
|
15
|
-
verifySignature(receivedData, receivedSignature, secretKey, options) {
|
|
16
|
-
return verifyMessage(receivedData, receivedSignature, secretKey, options);
|
|
17
|
-
}
|
|
18
|
-
}
|
package/dist/webhook/types.d.ts
DELETED
package/dist/webhook/types.js
DELETED
package/dist/webhook/utils.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { EventType, WebhookMessage } from '../events/types.js';
|
|
2
|
-
import { type WebhookVerificationOptions } from './types.js';
|
|
3
|
-
export declare function verifyMessage<E extends EventType = EventType>(receivedData: any, receivedSignature: string, secretKey: string, options?: WebhookVerificationOptions): WebhookMessage<E>;
|
package/dist/webhook/utils.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { VerificationError } from './types.js';
|
|
3
|
-
function verifyHmac(receivedData, receivedSignature, secretKey) {
|
|
4
|
-
const hmac = crypto.createHmac("sha256", secretKey);
|
|
5
|
-
hmac.update(receivedData);
|
|
6
|
-
const calculatedSignature = hmac.digest("hex");
|
|
7
|
-
const formattedReceivedSignature = receivedSignature.split("=")[1];
|
|
8
|
-
if (formattedReceivedSignature.length !== calculatedSignature.length) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
return crypto.timingSafeEqual(Buffer.from(calculatedSignature, "hex"), Buffer.from(formattedReceivedSignature, "hex"));
|
|
12
|
-
}
|
|
13
|
-
export function verifyMessage(receivedData, receivedSignature, secretKey, options) {
|
|
14
|
-
if (!receivedData) {
|
|
15
|
-
throw new VerificationError("Received request without body", 400);
|
|
16
|
-
}
|
|
17
|
-
if (!receivedSignature) {
|
|
18
|
-
throw new VerificationError("Missing HMAC signature", 400);
|
|
19
|
-
}
|
|
20
|
-
if (!secretKey) {
|
|
21
|
-
throw new VerificationError("Missing secret key", 400);
|
|
22
|
-
}
|
|
23
|
-
let dataJson;
|
|
24
|
-
let dataString;
|
|
25
|
-
if (typeof receivedData === 'string') {
|
|
26
|
-
dataString = receivedData;
|
|
27
|
-
try {
|
|
28
|
-
dataJson = JSON.parse(receivedData);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
throw new VerificationError(`Failed to decode JSON: ${error instanceof Error ? error.message : 'Unknown error'}`, 400);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
dataJson = receivedData;
|
|
36
|
-
dataString = JSON.stringify(receivedData);
|
|
37
|
-
}
|
|
38
|
-
const expiresAt = dataJson.expires_at;
|
|
39
|
-
if (!expiresAt) {
|
|
40
|
-
throw new VerificationError("No expiration date sent", 400);
|
|
41
|
-
}
|
|
42
|
-
if (!verifyHmac(dataString, receivedSignature, secretKey)) {
|
|
43
|
-
throw new VerificationError("Invalid HMAC signature", 401);
|
|
44
|
-
}
|
|
45
|
-
if (!options?.allowExpired && Date.now() / 1000 > expiresAt) {
|
|
46
|
-
throw new VerificationError("Webhook message expired", 400);
|
|
47
|
-
}
|
|
48
|
-
return dataJson;
|
|
49
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Workflow, WorkflowMetadata } from './types.js';
|
|
2
|
-
export declare class WorkflowsClient {
|
|
3
|
-
private readonly makeRequest;
|
|
4
|
-
constructor(makeRequest: <T = any>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: any) => Promise<T>);
|
|
5
|
-
/**
|
|
6
|
-
* Retrieves all workflows of the workspace the API key is associated with
|
|
7
|
-
*/
|
|
8
|
-
getAllWorkflows(): Promise<Workflow[]>;
|
|
9
|
-
/**
|
|
10
|
-
* Retrieves the JSON schema of the input variables for a specific workflow
|
|
11
|
-
* @param workflowId - The ID of the workflow
|
|
12
|
-
*/
|
|
13
|
-
getWorkflowMetadata(workflowId: string): Promise<WorkflowMetadata>;
|
|
14
|
-
/**
|
|
15
|
-
* Validates a payload against a workflow's input schema.
|
|
16
|
-
* Throws InputValidationError if invalid; resolves if valid.
|
|
17
|
-
*/
|
|
18
|
-
validateWorkflowInput(workflowId: string, payload: Record<string, any>): Promise<void>;
|
|
19
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { InputValidationError } from './types.js';
|
|
2
|
-
export class WorkflowsClient {
|
|
3
|
-
makeRequest;
|
|
4
|
-
constructor(makeRequest) {
|
|
5
|
-
this.makeRequest = makeRequest;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Retrieves all workflows of the workspace the API key is associated with
|
|
9
|
-
*/
|
|
10
|
-
async getAllWorkflows() {
|
|
11
|
-
return await this.makeRequest('GET', '/workflows');
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Retrieves the JSON schema of the input variables for a specific workflow
|
|
15
|
-
* @param workflowId - The ID of the workflow
|
|
16
|
-
*/
|
|
17
|
-
async getWorkflowMetadata(workflowId) {
|
|
18
|
-
const path = `/workflows/${workflowId}/metadata`;
|
|
19
|
-
return await this.makeRequest('GET', path);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Validates a payload against a workflow's input schema.
|
|
23
|
-
* Throws InputValidationError if invalid; resolves if valid.
|
|
24
|
-
*/
|
|
25
|
-
async validateWorkflowInput(workflowId, payload) {
|
|
26
|
-
const { input_schema } = await this.getWorkflowMetadata(workflowId);
|
|
27
|
-
const schema = input_schema ?? {};
|
|
28
|
-
const properties = schema.properties ?? {};
|
|
29
|
-
const required = schema.required ?? [];
|
|
30
|
-
const disallowExtras = schema.additionalProperties === false;
|
|
31
|
-
// Check only required keys for presence and type
|
|
32
|
-
const missingRequired = required.filter((key) => payload[key] === undefined);
|
|
33
|
-
const invalidTypes = [];
|
|
34
|
-
const detectType = (v) => {
|
|
35
|
-
if (v === null)
|
|
36
|
-
return 'null';
|
|
37
|
-
if (Array.isArray(v))
|
|
38
|
-
return 'array';
|
|
39
|
-
if (typeof v === 'number')
|
|
40
|
-
return Number.isInteger(v) ? 'integer' : 'number';
|
|
41
|
-
return typeof v;
|
|
42
|
-
};
|
|
43
|
-
const allowedTypes = new Set(['array', 'boolean', 'integer', 'number', 'object', 'string', 'null']);
|
|
44
|
-
const expectedTypesOf = (def) => {
|
|
45
|
-
if (!def)
|
|
46
|
-
return [];
|
|
47
|
-
// Normalize to array-of-strings from either string | string[] | { type: string | string[] }
|
|
48
|
-
const raw = (typeof def === 'object' && !Array.isArray(def)) ? def.type : def;
|
|
49
|
-
if (!raw)
|
|
50
|
-
return [];
|
|
51
|
-
const arr = Array.isArray(raw) ? raw : [raw];
|
|
52
|
-
return arr
|
|
53
|
-
.map((t) => String(t).toLowerCase())
|
|
54
|
-
.filter((t) => allowedTypes.has(t));
|
|
55
|
-
};
|
|
56
|
-
const matches = (expected, actual) => {
|
|
57
|
-
if (expected.length === 0)
|
|
58
|
-
return true; // unknown => don't enforce
|
|
59
|
-
if (expected.includes(actual))
|
|
60
|
-
return true;
|
|
61
|
-
if (actual === 'integer' && expected.includes('number'))
|
|
62
|
-
return true;
|
|
63
|
-
return false;
|
|
64
|
-
};
|
|
65
|
-
// Validate types for:
|
|
66
|
-
// - all required keys that are present
|
|
67
|
-
// - optional keys if they exist in the payload
|
|
68
|
-
for (const [key, schemaDef] of Object.entries(properties)) {
|
|
69
|
-
if (payload[key] === undefined)
|
|
70
|
-
continue; // optional and not provided
|
|
71
|
-
const expected = expectedTypesOf(schemaDef);
|
|
72
|
-
const actual = detectType(payload[key]);
|
|
73
|
-
if (!matches(expected, actual)) {
|
|
74
|
-
const exp = expected.length ? expected : ['any'];
|
|
75
|
-
invalidTypes.push({ field: key, expected_display: exp.join(' | '), actual });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// If additionalProperties is false, collect unknown keys present in payload
|
|
79
|
-
const unknownKeys = disallowExtras
|
|
80
|
-
? Object.keys(payload).filter((k) => !(k in properties))
|
|
81
|
-
: [];
|
|
82
|
-
if (missingRequired.length || invalidTypes.length || unknownKeys.length) {
|
|
83
|
-
const parts = [];
|
|
84
|
-
if (missingRequired.length)
|
|
85
|
-
parts.push(`missing required: ${missingRequired.join(', ')}`);
|
|
86
|
-
if (invalidTypes.length) {
|
|
87
|
-
parts.push(invalidTypes
|
|
88
|
-
.map((e) => `${e.field}: expected ${e.expected_display}, got ${e.actual}`)
|
|
89
|
-
.join('; '));
|
|
90
|
-
}
|
|
91
|
-
if (unknownKeys.length)
|
|
92
|
-
parts.push(`unknown keys: ${unknownKeys.join(', ')}`);
|
|
93
|
-
const message = `Workflow input validation failed: ${parts.join(' | ')}`;
|
|
94
|
-
throw new InputValidationError(message, missingRequired, invalidTypes, unknownKeys);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|