@thisispamela/sdk 1.0.3 → 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/src/index.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import axios, { AxiosInstance, AxiosError } from 'axios';
6
- import * as crypto from 'crypto';
7
6
  import {
8
7
  PamelaError,
9
8
  AuthenticationError,
@@ -12,68 +11,13 @@ import {
12
11
  ValidationError,
13
12
  CallError,
14
13
  } from './errors';
15
-
16
- export interface PamelaClientConfig {
17
- apiKey: string;
18
- baseUrl?: string;
19
- }
20
-
21
- export interface CreateCallRequest {
22
- to: string;
23
- country?: string;
24
- locale?: string;
25
- task: string;
26
- instructions?: string;
27
- voice?: 'male' | 'female' | 'auto';
28
- agent_name?: string;
29
- caller_name?: string;
30
- end_user_id?: string;
31
- metadata?: Record<string, any>;
32
- tools?: Array<Record<string, any>>;
33
- webhooks?: {
34
- webhook_url?: string;
35
- tool_webhook_url?: string;
36
- };
37
- }
38
-
39
- export interface CallResponse {
40
- id: string;
41
- status: string;
42
- call_session_id: string;
43
- created_at: string;
44
- }
45
-
46
- export interface CallStatus {
47
- id: string;
48
- status: string;
49
- to: string;
50
- from_: string; // Backend uses from_ (Python keyword)
51
- country: string;
52
- created_at: string;
53
- started_at?: string;
54
- completed_at?: string;
55
- duration_seconds?: number;
56
- transcript?: Array<Record<string, any>>;
57
- summary?: string;
58
- metadata: Record<string, any>;
59
- end_user_id?: string; // Marketplace end-user ID for privacy isolation
60
- }
61
-
62
- export interface ToolDefinition {
63
- name: string;
64
- description: string;
65
- input_schema: Record<string, any>;
66
- output_schema?: Record<string, any>;
67
- timeout_ms?: number;
68
- }
69
-
70
- export interface WebhookPayload {
71
- event: string;
72
- call_id: string;
73
- call_session_id: string;
74
- timestamp: string;
75
- data: Record<string, any>;
76
- }
14
+ import type {
15
+ PamelaClientConfig,
16
+ CreateCallRequest,
17
+ CallResponse,
18
+ CallStatus,
19
+ ToolDefinition,
20
+ } from './types';
77
21
 
78
22
  const mapAxiosError = (error: AxiosError, endpoint?: string): PamelaError => {
79
23
  const statusCode = error.response?.status;
@@ -225,6 +169,14 @@ export class PamelaClient {
225
169
  return response.data;
226
170
  }
227
171
 
172
+ /**
173
+ * Force hangup an in-progress call.
174
+ */
175
+ async hangupCall(callId: string): Promise<{ success: boolean; call_id: string; status: string }> {
176
+ const response = await this.client.post(`/calls/${callId}/hangup`);
177
+ return response.data;
178
+ }
179
+
228
180
  /**
229
181
  * Register a tool.
230
182
  */
@@ -249,25 +201,6 @@ export class PamelaClient {
249
201
  return response.data;
250
202
  }
251
203
 
252
- /**
253
- * Verify webhook signature.
254
- */
255
- static verifyWebhookSignature(
256
- payload: string | object,
257
- signature: string,
258
- secret: string
259
- ): boolean {
260
- const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);
261
- const expectedSignature = crypto
262
- .createHmac('sha256', secret)
263
- .update(payloadString)
264
- .digest('hex');
265
-
266
- return crypto.timingSafeEqual(
267
- Buffer.from(signature),
268
- Buffer.from(expectedSignature)
269
- );
270
- }
271
204
  }
272
205
 
273
206
  // Export as both default and named export for flexibility
@@ -281,4 +214,6 @@ export {
281
214
  ValidationError,
282
215
  CallError,
283
216
  };
217
+ export * from './types';
218
+ export * from './webhooks';
284
219
 
package/src/types.ts ADDED
@@ -0,0 +1,126 @@
1
+ export interface PamelaClientConfig {
2
+ apiKey: string;
3
+ baseUrl?: string;
4
+ }
5
+
6
+ export interface CreateCallRequest {
7
+ to: string;
8
+ country?: string;
9
+ locale?: string;
10
+ task: string;
11
+ instructions?: string;
12
+ max_duration_seconds?: number;
13
+ voice?: 'male' | 'female' | 'auto';
14
+ agent_name?: string;
15
+ caller_name?: string;
16
+ end_user_id?: string;
17
+ metadata?: Record<string, any>;
18
+ tools?: Array<Record<string, any>>;
19
+ webhooks?: {
20
+ webhook_url?: string;
21
+ tool_webhook_url?: string;
22
+ };
23
+ }
24
+
25
+ export type CallStatusValue =
26
+ | 'initiating'
27
+ | 'queued'
28
+ | 'ringing'
29
+ | 'in_progress'
30
+ | 'completed'
31
+ | 'failed'
32
+ | 'cancelled'
33
+ | 'timeout_terminated'
34
+ | 'in-progress';
35
+
36
+ export interface CallResponse {
37
+ id: string;
38
+ status: CallStatusValue | string;
39
+ call_session_id: string;
40
+ created_at: string;
41
+ }
42
+
43
+ export interface CallStatus {
44
+ id: string;
45
+ status: CallStatusValue | string;
46
+ to: string;
47
+ from_: string; // Backend uses from_ (Python keyword)
48
+ country: string;
49
+ created_at: string;
50
+ started_at?: string;
51
+ completed_at?: string;
52
+ duration_seconds?: number;
53
+ max_duration_seconds?: number;
54
+ transcript?: Array<Record<string, any>>;
55
+ summary?: string;
56
+ metadata: Record<string, any>;
57
+ end_user_id?: string; // Marketplace end-user ID for privacy isolation
58
+ }
59
+
60
+ export interface ToolDefinition {
61
+ name: string;
62
+ description: string;
63
+ input_schema: Record<string, any>;
64
+ output_schema?: Record<string, any>;
65
+ timeout_ms?: number;
66
+ }
67
+
68
+ export interface WebhookPayload {
69
+ event: string;
70
+ call_id: string;
71
+ call_session_id: string;
72
+ timestamp: string;
73
+ data: Record<string, any>;
74
+ }
75
+
76
+ /** Standard error codes (aligned with backend models/errors.py) */
77
+ export const ErrorCodes = {
78
+ // Auth errors (1000-1999)
79
+ AUTH_REQUIRED: 1001,
80
+ AUTH_INVALID: 1002,
81
+ AUTH_EXPIRED: 1003,
82
+ AUTH_INSUFFICIENT_PERMISSIONS: 1004,
83
+ AUTH_FORBIDDEN: 1005,
84
+
85
+ // Validation errors (2000-2999)
86
+ VALIDATION_ERROR: 2001,
87
+ INVALID_PHONE: 2002,
88
+ INVALID_EMAIL: 2003,
89
+ INVALID_PASSWORD: 2004,
90
+ MISSING_REQUIRED_FIELD: 2005,
91
+ INVALID_JSON: 2006,
92
+ INVALID_REQUEST_BODY: 2007,
93
+
94
+ // Not found errors (3000-3999)
95
+ NOT_FOUND: 3001,
96
+ USER_NOT_FOUND: 3002,
97
+ CONVERSATION_NOT_FOUND: 3003,
98
+ CALL_SESSION_NOT_FOUND: 3004,
99
+ RESOURCE_NOT_FOUND: 3005,
100
+ MESSAGE_NOT_FOUND: 3006,
101
+
102
+ // Conflict errors (4000-4999)
103
+ RESOURCE_ALREADY_EXISTS: 4001,
104
+ EMAIL_ALREADY_REGISTERED: 4002,
105
+ ACCOUNT_LINKING_REQUIRED: 4003,
106
+
107
+ // Server errors (5000-5999)
108
+ INTERNAL_ERROR: 5001,
109
+ EXTERNAL_SERVICE_ERROR: 5002,
110
+ DATABASE_ERROR: 5003,
111
+ CONFIGURATION_ERROR: 5004,
112
+
113
+ // Rate limiting (6000-6999)
114
+ RATE_LIMIT_EXCEEDED: 6001,
115
+ QUOTA_EXCEEDED: 6002,
116
+
117
+ // B2B errors (7000-7999)
118
+ B2B_PARTNER_NOT_FOUND: 7001,
119
+ B2B_PROJECT_NOT_FOUND: 7002,
120
+ B2B_CALL_NOT_FOUND: 7003,
121
+ B2B_NO_PHONE_NUMBER: 7004,
122
+ B2B_UNSUPPORTED_COUNTRY: 7005,
123
+ B2B_TOOL_EXECUTION_FAILED: 7006,
124
+ B2B_WEBHOOK_DELIVERY_FAILED: 7007,
125
+ SUBSCRIPTION_EXPIRED: 7008,
126
+ } as const;
@@ -0,0 +1,55 @@
1
+ import * as crypto from 'crypto';
2
+
3
+ export const verifyWebhookSignature = (
4
+ payload: string | object,
5
+ signature: string | undefined | null,
6
+ secret: string
7
+ ): boolean => {
8
+ if (!signature) {
9
+ return false;
10
+ }
11
+
12
+ if (!secret) {
13
+ throw new Error('Webhook secret cannot be empty');
14
+ }
15
+
16
+ const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);
17
+ const expectedSignature = crypto
18
+ .createHmac('sha256', secret)
19
+ .update(payloadString)
20
+ .digest('hex');
21
+
22
+ return crypto.timingSafeEqual(
23
+ Buffer.from(signature),
24
+ Buffer.from(expectedSignature)
25
+ );
26
+ };
27
+
28
+ export const parseToolWebhook = (payload: Record<string, any>): {
29
+ tool_name: string;
30
+ arguments: Record<string, any>;
31
+ call_id: string;
32
+ correlation_id: string;
33
+ call_session_id?: string;
34
+ partner_id?: string;
35
+ project_id?: string;
36
+ } => {
37
+ const requiredFields = ['tool_name', 'arguments', 'call_id', 'correlation_id'];
38
+ const missing = requiredFields.filter((field) => !(field in payload));
39
+
40
+ if (missing.length > 0) {
41
+ throw new Error(`Missing required fields: ${missing.join(', ')}`);
42
+ }
43
+
44
+ return {
45
+ tool_name: String(payload.tool_name),
46
+ arguments: typeof payload.arguments === 'object' && payload.arguments
47
+ ? payload.arguments
48
+ : {},
49
+ call_id: String(payload.call_id),
50
+ correlation_id: String(payload.correlation_id),
51
+ call_session_id: payload.call_session_id,
52
+ partner_id: payload.partner_id,
53
+ project_id: payload.project_id,
54
+ };
55
+ };
package/tsup.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts', 'src/webhooks.ts', 'src/types.ts', 'src/errors.ts'],
5
+ format: ['cjs', 'esm'],
6
+ dts: true,
7
+ clean: true,
8
+ sourcemap: true,
9
+ target: 'es2020',
10
+ });