@protoboxai/sdk 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.
Files changed (46) hide show
  1. package/README.md +51 -0
  2. package/dist/adapters/openai.d.ts +106 -0
  3. package/dist/adapters/openai.js +185 -0
  4. package/dist/client.d.ts +60 -0
  5. package/dist/client.js +307 -0
  6. package/dist/errors/index.d.ts +179 -0
  7. package/dist/errors/index.js +319 -0
  8. package/dist/index.d.ts +23 -0
  9. package/dist/index.js +62 -0
  10. package/dist/live/index.d.ts +5 -0
  11. package/dist/live/index.js +10 -0
  12. package/dist/live/live-chat.d.ts +71 -0
  13. package/dist/live/live-chat.js +95 -0
  14. package/dist/live/typed-emitter.d.ts +15 -0
  15. package/dist/live/typed-emitter.js +40 -0
  16. package/dist/live/types.d.ts +24 -0
  17. package/dist/live/types.js +6 -0
  18. package/dist/modules/auth.d.ts +76 -0
  19. package/dist/modules/auth.js +59 -0
  20. package/dist/modules/chat.d.ts +164 -0
  21. package/dist/modules/chat.js +168 -0
  22. package/dist/modules/health.d.ts +45 -0
  23. package/dist/modules/health.js +22 -0
  24. package/dist/modules/knowledge.d.ts +202 -0
  25. package/dist/modules/knowledge.js +147 -0
  26. package/dist/modules/mcp.d.ts +138 -0
  27. package/dist/modules/mcp.js +110 -0
  28. package/dist/modules/prompts.d.ts +128 -0
  29. package/dist/modules/prompts.js +93 -0
  30. package/dist/modules/tools.d.ts +222 -0
  31. package/dist/modules/tools.js +302 -0
  32. package/dist/modules/toolsets.d.ts +173 -0
  33. package/dist/modules/toolsets.js +216 -0
  34. package/dist/modules/workspace.d.ts +48 -0
  35. package/dist/modules/workspace.js +49 -0
  36. package/dist/types/api.d.ts +4 -0
  37. package/dist/types/api.js +21 -0
  38. package/dist/types/config.d.ts +81 -0
  39. package/dist/types/config.js +3 -0
  40. package/dist/types/tool-calls.d.ts +60 -0
  41. package/dist/types/tool-calls.js +8 -0
  42. package/dist/types/tools.d.ts +321 -0
  43. package/dist/types/tools.js +9 -0
  44. package/dist/types/toolsets.d.ts +151 -0
  45. package/dist/types/toolsets.js +8 -0
  46. package/package.json +52 -0
package/dist/client.js ADDED
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChanlSDK = void 0;
4
+ const workspace_1 = require("./modules/workspace");
5
+ const auth_1 = require("./modules/auth");
6
+ const health_1 = require("./modules/health");
7
+ const prompts_1 = require("./modules/prompts");
8
+ const tools_1 = require("./modules/tools");
9
+ const toolsets_1 = require("./modules/toolsets");
10
+ const mcp_1 = require("./modules/mcp");
11
+ const knowledge_1 = require("./modules/knowledge");
12
+ const chat_1 = require("./modules/chat");
13
+ class ChanlSDK {
14
+ constructor(config) {
15
+ const server = config.server;
16
+ const baseUrl = config.baseUrl ||
17
+ (typeof server === 'string' && server.length > 0 ? server : '');
18
+ if (!baseUrl) {
19
+ throw new Error('ChanlSDK: baseUrl or server is required');
20
+ }
21
+ this.config = {
22
+ timeout: 30000,
23
+ debug: false,
24
+ retry: {
25
+ maxRetries: 3,
26
+ retryDelay: 1000,
27
+ backoffMultiplier: 2,
28
+ },
29
+ ...config,
30
+ baseUrl,
31
+ };
32
+ // Initialize modules
33
+ this.workspace = new workspace_1.WorkspaceModule(this);
34
+ this.auth = new auth_1.AuthModule(this);
35
+ this.health = new health_1.HealthModule(this);
36
+ this.prompts = new prompts_1.PromptsModule(this);
37
+ this.tools = new tools_1.ToolsModule(this);
38
+ this.toolsets = new toolsets_1.ToolsetsModule(this);
39
+ this.mcp = new mcp_1.McpModule(this);
40
+ this.knowledge = new knowledge_1.KnowledgeModule(this);
41
+ this.chat = new chat_1.ChatModule(this);
42
+ }
43
+ /**
44
+ * Make a request to the API
45
+ */
46
+ /**
47
+ * Rewrite `/api/v1/...` paths for local OSS servers (flat routes).
48
+ */
49
+ resolveRequestPath(path) {
50
+ const explicit = this.config.apiPrefix;
51
+ if (explicit !== undefined) {
52
+ const trimmed = explicit.replace(/^\/+|\/+$/g, '');
53
+ if (trimmed === '') {
54
+ if (path.startsWith('/api/v1/')) {
55
+ return path.replace(/^\/api\/v1/, '') || '/';
56
+ }
57
+ return path;
58
+ }
59
+ if (path.startsWith('/api/v1/')) {
60
+ return `/${trimmed}${path.slice('/api/v1'.length)}`;
61
+ }
62
+ return path;
63
+ }
64
+ if (this.config.deployment === 'local' && path.startsWith('/api/v1/')) {
65
+ return path.replace(/^\/api\/v1/, '') || '/';
66
+ }
67
+ return path;
68
+ }
69
+ async request(method, url, data, config) {
70
+ // Check if data is FormData (Node.js form-data package or native FormData)
71
+ const isNodeFormData = data != null &&
72
+ typeof data
73
+ .getHeaders === "function";
74
+ const isNativeFormData = typeof FormData !== "undefined" && data instanceof FormData;
75
+ const isFormData = isNodeFormData || isNativeFormData;
76
+ const path = this.resolveRequestPath(url);
77
+ // Build full URL
78
+ const fullUrl = new URL(path, this.config.baseUrl).toString();
79
+ // Build headers
80
+ const headers = {
81
+ ...this.config.headers,
82
+ ...config?.headers,
83
+ };
84
+ // Set Content-Type for non-FormData requests (FormData sets its own boundary)
85
+ if (!isFormData && !headers["Content-Type"]) {
86
+ headers["Content-Type"] = "application/json";
87
+ }
88
+ // Remove Content-Type for FormData to let fetch/runtime set it with boundary
89
+ if (isFormData) {
90
+ delete headers["Content-Type"];
91
+ }
92
+ // Add authentication
93
+ if (this.config.apiKey) {
94
+ headers["X-API-Key"] = this.config.apiKey;
95
+ }
96
+ else if (this.config.jwtToken) {
97
+ headers["Authorization"] = `Bearer ${this.config.jwtToken}`;
98
+ }
99
+ // Add workspace context (flat local APIs do not use this header)
100
+ const skipWorkspaceHeader = this.config.deployment === "local" || this.config.apiPrefix === "";
101
+ if (this.config.workspaceId && !skipWorkspaceHeader) {
102
+ headers["x-workspace-id"] = this.config.workspaceId;
103
+ }
104
+ // Setup timeout via AbortController
105
+ const timeout = config?.timeout ?? this.config.timeout ?? 30000;
106
+ const controller = new AbortController();
107
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
108
+ // Build fetch options
109
+ const fetchOptions = {
110
+ method,
111
+ headers,
112
+ signal: controller.signal,
113
+ };
114
+ // Attach body if present
115
+ if (data != null) {
116
+ if (isFormData) {
117
+ // Pass FormData directly (works for both Node.js form-data and native FormData)
118
+ fetchOptions.body = data;
119
+ }
120
+ else {
121
+ fetchOptions.body = JSON.stringify(data);
122
+ }
123
+ }
124
+ try {
125
+ const response = await fetch(fullUrl, fetchOptions);
126
+ if (!response.ok) {
127
+ // Try to parse error body for a meaningful message
128
+ let errorMessage = "API Error";
129
+ try {
130
+ const errorBody = (await response.json());
131
+ errorMessage =
132
+ errorBody.error?.message || errorBody.message || "API Error";
133
+ }
134
+ catch {
135
+ // JSON parse failed, use status text
136
+ errorMessage = response.statusText || "API Error";
137
+ }
138
+ throw new Error(errorMessage);
139
+ }
140
+ // Handle 204 No Content
141
+ if (response.status === 204) {
142
+ return {};
143
+ }
144
+ const json = await response.json();
145
+ const body = json;
146
+ // Normalize response shape to { success, data, message, timestamp }.
147
+ // API responses come in several shapes depending on the service/gateway:
148
+ // 1. { success, data, message, timestamp } — standard ResponseInterceptor
149
+ // 2. { data: { ... } } — partial wrapper (no success)
150
+ // 3. { agents, pagination } — raw unwrapped
151
+ let normalized;
152
+ if (body['success'] !== undefined) {
153
+ // Case 1: fully wrapped
154
+ normalized = json;
155
+ }
156
+ else if (body['data'] !== undefined && Object.keys(body).length <= 3) {
157
+ // Case 2: has { data } wrapper but no success — add success
158
+ normalized = {
159
+ success: true,
160
+ data: body['data'],
161
+ message: body['message'] || 'Success',
162
+ timestamp: body['timestamp'] || new Date().toISOString(),
163
+ };
164
+ }
165
+ else {
166
+ // Case 3: raw unwrapped — wrap entire body as data
167
+ normalized = {
168
+ success: true,
169
+ data: json,
170
+ message: 'Success',
171
+ timestamp: new Date().toISOString(),
172
+ };
173
+ }
174
+ // Normalize _id → id in response data
175
+ if (normalized.data !== undefined) {
176
+ normalized.data = this.normalizeIds(normalized.data);
177
+ }
178
+ // Unwrap single-entity responses: { agent: {...} } → {...}
179
+ // API get-by-id endpoints return { entityName: { ...entity } }.
180
+ // Unwrap so consumers get the entity directly as `data`.
181
+ if (normalized.data !== null && typeof normalized.data === 'object' && !Array.isArray(normalized.data)) {
182
+ const dataObj = normalized.data;
183
+ const keys = Object.keys(dataObj);
184
+ const singleKey = keys.length === 1 ? keys[0] : undefined;
185
+ if (singleKey) {
186
+ const value = dataObj[singleKey];
187
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
188
+ normalized.data = value;
189
+ }
190
+ }
191
+ }
192
+ return normalized;
193
+ }
194
+ catch (error) {
195
+ if (error instanceof DOMException && error.name === "AbortError") {
196
+ throw new Error(`Request timeout after ${timeout}ms`);
197
+ }
198
+ // Re-throw errors we already created above, or wrap unknown errors
199
+ if (error instanceof Error) {
200
+ throw error;
201
+ }
202
+ throw new Error("Network Error");
203
+ }
204
+ finally {
205
+ clearTimeout(timeoutId);
206
+ }
207
+ }
208
+ /**
209
+ * Make a streaming request that returns the raw Response.
210
+ *
211
+ * Unlike `request()`, this does NOT parse JSON or apply timeout.
212
+ * The caller reads `response.body` via `getReader()`.
213
+ * Used for SSE / chunked-transfer endpoints (e.g., chat streaming).
214
+ */
215
+ async requestStream(method, url, data) {
216
+ const fullUrl = new URL(url, this.config.baseUrl).toString();
217
+ const headers = {
218
+ ...this.config.headers,
219
+ 'Content-Type': 'application/json',
220
+ 'Accept': 'text/event-stream',
221
+ };
222
+ if (this.config.apiKey) {
223
+ headers['X-API-Key'] = this.config.apiKey;
224
+ }
225
+ else if (this.config.jwtToken) {
226
+ headers['Authorization'] = `Bearer ${this.config.jwtToken}`;
227
+ }
228
+ if (this.config.workspaceId) {
229
+ headers['x-workspace-id'] = this.config.workspaceId;
230
+ }
231
+ const fetchOptions = {
232
+ method,
233
+ headers,
234
+ };
235
+ if (data != null) {
236
+ fetchOptions.body = JSON.stringify(data);
237
+ }
238
+ const response = await fetch(fullUrl, fetchOptions);
239
+ if (!response.ok) {
240
+ let errorMessage = 'API Error';
241
+ try {
242
+ const errorBody = (await response.json());
243
+ errorMessage =
244
+ errorBody.error?.message || errorBody.message || 'API Error';
245
+ }
246
+ catch {
247
+ errorMessage = response.statusText || 'API Error';
248
+ }
249
+ throw new Error(errorMessage);
250
+ }
251
+ return response;
252
+ }
253
+ /**
254
+ * Recursively normalize _id → id in response data.
255
+ * Skips if id already exists to avoid overwriting.
256
+ */
257
+ normalizeIds(data) {
258
+ if (data === null || data === undefined || typeof data !== "object") {
259
+ return data;
260
+ }
261
+ if (Array.isArray(data)) {
262
+ return data.map((item) => this.normalizeIds(item));
263
+ }
264
+ const obj = data;
265
+ const result = {};
266
+ for (const [key, value] of Object.entries(obj)) {
267
+ if (key === "_id" && !("id" in obj)) {
268
+ result["id"] = value;
269
+ }
270
+ else if (key === "_id" && "id" in obj) {
271
+ // Skip _id when id already exists
272
+ continue;
273
+ }
274
+ else {
275
+ result[key] = this.normalizeIds(value);
276
+ }
277
+ }
278
+ return result;
279
+ }
280
+ /**
281
+ * Update authentication credentials
282
+ */
283
+ updateAuth(authConfig) {
284
+ if (authConfig.apiKey) {
285
+ this.config.apiKey = authConfig.apiKey;
286
+ delete this.config.jwtToken;
287
+ }
288
+ else if (authConfig.jwtToken) {
289
+ this.config.jwtToken = authConfig.jwtToken;
290
+ delete this.config.apiKey;
291
+ }
292
+ }
293
+ /**
294
+ * Get current configuration
295
+ */
296
+ getConfig() {
297
+ return { ...this.config };
298
+ }
299
+ /**
300
+ * Enable/disable debug logging
301
+ */
302
+ setDebug(enabled) {
303
+ this.config.debug = enabled;
304
+ }
305
+ }
306
+ exports.ChanlSDK = ChanlSDK;
307
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Custom Error Classes for @chanl/sdk
3
+ *
4
+ * TDD Phase: RED - These errors define the error handling contract
5
+ *
6
+ * Design Principles:
7
+ * - Error transparency: Include code, message, and context for debugging
8
+ * - HTTP status awareness: Map API response codes to specific error types
9
+ * - Serializable details: Allow JSON serialization for logging
10
+ */
11
+ /**
12
+ * Options type for ChanlError constructor
13
+ * Uses explicit `| undefined` to be compatible with exactOptionalPropertyTypes
14
+ */
15
+ interface ChanlErrorOptions {
16
+ code?: string | undefined;
17
+ statusCode?: number | undefined;
18
+ details?: Record<string, unknown> | undefined;
19
+ cause?: Error | undefined;
20
+ requestId?: string | undefined;
21
+ }
22
+ /**
23
+ * Base error class for all Chanl SDK errors
24
+ */
25
+ export declare class ChanlError extends Error {
26
+ /** Error code for programmatic handling */
27
+ readonly code: string;
28
+ /** HTTP status code if from API response */
29
+ readonly statusCode?: number;
30
+ /** Additional error details for debugging */
31
+ readonly details?: Record<string, unknown>;
32
+ /** Original error if wrapping another error */
33
+ readonly cause?: Error;
34
+ /** Request ID from API for tracing */
35
+ readonly requestId?: string;
36
+ constructor(message: string, options?: ChanlErrorOptions);
37
+ /**
38
+ * Convert error to JSON for logging/serialization
39
+ */
40
+ toJSON(): Record<string, unknown>;
41
+ }
42
+ /**
43
+ * Error thrown when a requested resource is not found (404)
44
+ */
45
+ export declare class NotFoundError extends ChanlError {
46
+ /** The type of resource that was not found */
47
+ readonly resourceType: string;
48
+ /** The identifier that was used to look up the resource */
49
+ readonly resourceId: string;
50
+ constructor(resourceType: string, resourceId: string, options?: {
51
+ details?: Record<string, unknown> | undefined;
52
+ requestId?: string | undefined;
53
+ });
54
+ }
55
+ /**
56
+ * Error thrown when input validation fails (400)
57
+ */
58
+ export declare class ValidationError extends ChanlError {
59
+ /** Field-level validation errors */
60
+ readonly fieldErrors?: Record<string, string[]>;
61
+ constructor(message: string, options?: {
62
+ fieldErrors?: Record<string, string[]> | undefined;
63
+ details?: Record<string, unknown> | undefined;
64
+ requestId?: string | undefined;
65
+ });
66
+ /**
67
+ * Get validation errors for a specific field
68
+ */
69
+ getFieldErrors(field: string): string[];
70
+ /**
71
+ * Check if a specific field has validation errors
72
+ */
73
+ hasFieldError(field: string): boolean;
74
+ }
75
+ /**
76
+ * Error thrown when authentication fails (401)
77
+ */
78
+ export declare class AuthenticationError extends ChanlError {
79
+ constructor(message?: string, options?: {
80
+ details?: Record<string, unknown> | undefined;
81
+ requestId?: string | undefined;
82
+ });
83
+ }
84
+ /**
85
+ * Error thrown when authorization fails (403)
86
+ */
87
+ export declare class AuthorizationError extends ChanlError {
88
+ /** The action that was attempted */
89
+ readonly action?: string;
90
+ /** The resource that was being accessed */
91
+ readonly resource?: string;
92
+ constructor(message?: string, options?: {
93
+ action?: string | undefined;
94
+ resource?: string | undefined;
95
+ details?: Record<string, unknown> | undefined;
96
+ requestId?: string | undefined;
97
+ });
98
+ }
99
+ /**
100
+ * Error thrown when there's a conflict (409)
101
+ */
102
+ export declare class ConflictError extends ChanlError {
103
+ /** The conflicting resource identifier */
104
+ readonly conflictingResource?: string;
105
+ constructor(message: string, options?: {
106
+ conflictingResource?: string | undefined;
107
+ details?: Record<string, unknown> | undefined;
108
+ requestId?: string | undefined;
109
+ });
110
+ }
111
+ /**
112
+ * Error thrown when rate limit is exceeded (429)
113
+ */
114
+ export declare class RateLimitError extends ChanlError {
115
+ /** When the rate limit resets (Unix timestamp) */
116
+ readonly retryAfter?: number;
117
+ constructor(message?: string, options?: {
118
+ retryAfter?: number | undefined;
119
+ details?: Record<string, unknown> | undefined;
120
+ requestId?: string | undefined;
121
+ });
122
+ }
123
+ /**
124
+ * Error thrown when API server fails (5xx)
125
+ */
126
+ export declare class ServerError extends ChanlError {
127
+ constructor(message?: string, options?: {
128
+ statusCode?: number | undefined;
129
+ details?: Record<string, unknown> | undefined;
130
+ requestId?: string | undefined;
131
+ });
132
+ }
133
+ /**
134
+ * Error thrown for network/connection issues
135
+ */
136
+ export declare class NetworkError extends ChanlError {
137
+ constructor(message?: string, options?: {
138
+ cause?: Error | undefined;
139
+ details?: Record<string, unknown> | undefined;
140
+ });
141
+ }
142
+ /**
143
+ * Error thrown when request times out
144
+ */
145
+ export declare class TimeoutError extends ChanlError {
146
+ /** Timeout duration in milliseconds */
147
+ readonly timeoutMs?: number;
148
+ constructor(message?: string, options?: {
149
+ timeoutMs?: number | undefined;
150
+ details?: Record<string, unknown> | undefined;
151
+ requestId?: string | undefined;
152
+ });
153
+ }
154
+ /**
155
+ * Type guard to check if error is a ChanlError
156
+ */
157
+ export declare function isChanlError(error: unknown): error is ChanlError;
158
+ /**
159
+ * Type guard to check if error is a NotFoundError
160
+ */
161
+ export declare function isNotFoundError(error: unknown): error is NotFoundError;
162
+ /**
163
+ * Type guard to check if error is a ValidationError
164
+ */
165
+ export declare function isValidationError(error: unknown): error is ValidationError;
166
+ /**
167
+ * Type guard to check if error is an AuthenticationError
168
+ */
169
+ export declare function isAuthenticationError(error: unknown): error is AuthenticationError;
170
+ /**
171
+ * Factory function to create appropriate error from API response
172
+ */
173
+ export declare function createErrorFromResponse(statusCode: number, message: string, options?: {
174
+ details?: Record<string, unknown> | undefined;
175
+ requestId?: string | undefined;
176
+ fieldErrors?: Record<string, string[]> | undefined;
177
+ }): ChanlError;
178
+ export {};
179
+ //# sourceMappingURL=index.d.ts.map