@recursorsdk/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.
package/dist/index.js ADDED
@@ -0,0 +1,480 @@
1
+ export { RecursorWebSocket } from "./websocket.js";
2
+ export class RecursorSDK {
3
+ constructor(options) {
4
+ const envBase = process.env.RECURSOR_API_URL ?? "http://localhost:8000/api/v1";
5
+ this.baseUrl = (options?.baseUrl ?? envBase).replace(/\/$/, "");
6
+ this.apiKey = options?.apiKey ?? process.env.RECURSOR_API_KEY;
7
+ this.accessToken = options?.accessToken ?? process.env.RECURSOR_ACCESS_TOKEN;
8
+ this.timeoutMs = options?.timeoutMs ?? 10000;
9
+ }
10
+ setAccessToken(token) {
11
+ this.accessToken = token;
12
+ }
13
+ setApiKey(key) {
14
+ this.apiKey = key;
15
+ }
16
+ headers() {
17
+ const h = { "Content-Type": "application/json" };
18
+ if (this.accessToken) {
19
+ h["Authorization"] = `Bearer ${this.accessToken}`;
20
+ }
21
+ else if (this.apiKey) {
22
+ h["X-API-Key"] = this.apiKey;
23
+ }
24
+ return h;
25
+ }
26
+ async get(path, params) {
27
+ const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
28
+ if (params)
29
+ Object.entries(params).forEach(([k, v]) => {
30
+ if (v !== undefined && v !== null)
31
+ url.searchParams.set(k, String(v));
32
+ });
33
+ const ctrl = new AbortController();
34
+ const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
35
+ try {
36
+ const res = await fetch(url, { headers: this.headers(), signal: ctrl.signal });
37
+ if (!res.ok)
38
+ throw new Error(`HTTP ${res.status}`);
39
+ return (await res.json());
40
+ }
41
+ finally {
42
+ clearTimeout(t);
43
+ }
44
+ }
45
+ async post(path, body, method = "POST") {
46
+ const url = `${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`;
47
+ const ctrl = new AbortController();
48
+ const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
49
+ try {
50
+ const res = await fetch(url, {
51
+ method,
52
+ headers: this.headers(),
53
+ body: body ? JSON.stringify(body) : undefined,
54
+ signal: ctrl.signal,
55
+ });
56
+ if (!res.ok) {
57
+ const errorText = await res.text();
58
+ throw new Error(`HTTP ${res.status}: ${errorText}`);
59
+ }
60
+ // Handle 204 No Content
61
+ if (res.status === 204)
62
+ return undefined;
63
+ return (await res.json());
64
+ }
65
+ finally {
66
+ clearTimeout(t);
67
+ }
68
+ }
69
+ async put(path, body) {
70
+ return this.post(path, body, "PUT");
71
+ }
72
+ async patch(path, body) {
73
+ return this.post(path, body, "PATCH");
74
+ }
75
+ async delete(path) {
76
+ return this.post(path, undefined, "DELETE");
77
+ }
78
+ async checkHealth() {
79
+ try {
80
+ const res = await fetch(`${this.baseUrl.replace('/api/v1', '')}/v1/status/health`);
81
+ return res.ok;
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ async detectIntent(args) {
88
+ const payload = {
89
+ user_request: (args.user_request ?? "").trim().slice(0, 4000),
90
+ current_file: args.current_file ?? null,
91
+ user_id: args.user_id ?? null,
92
+ project_id: args.project_id ?? null,
93
+ tags: args.tags ?? [],
94
+ similar_limit: args.similar_limit ?? 5,
95
+ organization_id: args.organization_id ?? null,
96
+ };
97
+ return await this.post("/client/code_intelligence/detect-intent", payload);
98
+ }
99
+ // LLM Gateway
100
+ async getLLMGatewayPolicy() {
101
+ return await this.get("/recursor/llm/gateway/policy");
102
+ }
103
+ async gatewayChat(args) {
104
+ const body = {
105
+ provider: args.provider ?? "openai",
106
+ model: args.model ?? undefined,
107
+ messages: (args.messages ?? []).map(m => ({ role: m.role, content: String(m.content).slice(0, 4000) })),
108
+ call_provider: !!(args.call_provider ?? false),
109
+ user_id: args.user_id ?? undefined,
110
+ organization_id: args.organization_id ?? undefined,
111
+ };
112
+ return await this.post("/recursor/llm/gateway/chat", body);
113
+ }
114
+ // Robotics Gateway
115
+ async getRoboticsGatewayPolicy() {
116
+ return await this.get("/recursor/robotics/gateway/policy");
117
+ }
118
+ async roboticsGatewayObserve(args) {
119
+ const body = {
120
+ state: args.state,
121
+ command: args.command ?? undefined,
122
+ environment: args.environment ?? [],
123
+ user_id: args.user_id ?? undefined,
124
+ organization_id: args.organization_id ?? undefined,
125
+ };
126
+ return await this.post("/recursor/robotics/gateway/observe", body);
127
+ }
128
+ // AV Gateway
129
+ async getAvGatewayPolicy() {
130
+ return await this.get("/recursor/av/gateway/policy");
131
+ }
132
+ async avGatewayObserve(args) {
133
+ const body = {
134
+ sensors: args.sensors,
135
+ state: args.state,
136
+ action: args.action,
137
+ timestamp: args.timestamp,
138
+ vehicle_id: args.vehicle_id,
139
+ user_id: args.user_id ?? undefined,
140
+ organization_id: args.organization_id ?? undefined,
141
+ };
142
+ return await this.post("/recursor/av/gateway/observe", body);
143
+ }
144
+ async getIntentHistory(limit = 50, project_id) {
145
+ const params = { limit: Math.max(1, Math.min(limit, 200)) };
146
+ if (project_id)
147
+ params.project_id = project_id;
148
+ return await this.get("/client/code_intelligence/intent-history", params);
149
+ }
150
+ async createCorrection(args) {
151
+ const body = {
152
+ input_text: (args.input_text ?? "").slice(0, 4000),
153
+ output_text: (args.output_text ?? "").slice(0, 4000),
154
+ expected_output: (args.expected_output ?? args.output_text ?? "").slice(0, 4000),
155
+ context: args.context ?? {},
156
+ correction_type: args.correction_type ?? null,
157
+ };
158
+ const path = args.organization_id
159
+ ? `/client/corrections/?organization_id=${encodeURIComponent(args.organization_id)}`
160
+ : "/client/corrections/";
161
+ return await this.post(path, body);
162
+ }
163
+ async listCorrections(args) {
164
+ const params = {
165
+ page: Math.max(1, args?.page ?? 1),
166
+ page_size: Math.max(1, Math.min(args?.page_size ?? 50, 100)),
167
+ include_inactive: !!(args?.include_inactive ?? false),
168
+ };
169
+ if (args?.organization_id)
170
+ params.organization_id = args.organization_id;
171
+ return await this.get("/client/corrections/", params);
172
+ }
173
+ async searchCorrections(query, limit = 10, organization_id) {
174
+ const params = {
175
+ query: (query ?? "").trim().slice(0, 4000),
176
+ limit: Math.max(1, Math.min(limit, 50)),
177
+ };
178
+ if (organization_id)
179
+ params.organization_id = organization_id;
180
+ return await this.get("/client/corrections/search", params);
181
+ }
182
+ async getAnalyticsDashboard(user_id, period = "30d", project_id) {
183
+ const params = { user_id, period };
184
+ if (project_id)
185
+ params.project_id = project_id;
186
+ return await this.get("/client/code_intelligence/analytics/dashboard", params);
187
+ }
188
+ async getTimeSaved(user_id, period = "30d", project_id) {
189
+ const params = { user_id, period };
190
+ if (project_id)
191
+ params.project_id = project_id;
192
+ return await this.get("/client/code_intelligence/analytics/time-saved", params);
193
+ }
194
+ async getQualityMetrics(user_id, period = "30d", project_id) {
195
+ const params = { user_id, period };
196
+ if (project_id)
197
+ params.project_id = project_id;
198
+ return await this.get("/client/code_intelligence/analytics/quality", params);
199
+ }
200
+ async getAIAgentMetrics(user_id, project_id) {
201
+ const params = { user_id };
202
+ if (project_id)
203
+ params.project_id = project_id;
204
+ return await this.get("/client/code_intelligence/analytics/ai-agent", params);
205
+ }
206
+ async correctCode(code, language, project_profile) {
207
+ const body = { code, language, project_profile: project_profile ?? {} };
208
+ return await this.post("/client/code_intelligence/correct/code", body);
209
+ }
210
+ async correctConfig(config, config_type) {
211
+ const body = { config, config_type };
212
+ return await this.post("/client/code_intelligence/correct/config", body);
213
+ }
214
+ async correctDocumentation(markdown, doc_type = "README") {
215
+ const body = { markdown, doc_type };
216
+ return await this.post("/client/code_intelligence/correct/documentation", body);
217
+ }
218
+ async applyAutoCorrections(user_id, model_name, corrections) {
219
+ const body = { user_id, model_name, corrections };
220
+ return await this.post("/client/code_intelligence/auto-correct", body);
221
+ }
222
+ async getTrustScore(user_id, model_name) {
223
+ const data = await this.get("/client/code_intelligence/trust-score", { user_id, model_name });
224
+ const v = data.trust_score;
225
+ return typeof v === "number" ? v : Number(v ?? 0);
226
+ }
227
+ async submitFeedback(prediction_id, accepted) {
228
+ await this.post("/client/code_intelligence/feedback", { prediction_id, accepted: !!accepted });
229
+ }
230
+ async getAutoCorrectStats(user_id) {
231
+ return await this.get("/client/code_intelligence/stats", { user_id });
232
+ }
233
+ async getPatterns(user_id) {
234
+ const params = user_id ? { user_id } : undefined;
235
+ const data = await this.get("/client/code_intelligence/patterns", params);
236
+ return Array.isArray(data) ? data : [];
237
+ }
238
+ // ==================== Authentication & User Management ====================
239
+ async register(userData) {
240
+ return await this.post("/client/auth/register", userData);
241
+ }
242
+ async login(credentials) {
243
+ const response = await this.post("/client/auth/login", credentials);
244
+ // Automatically set access token for future requests
245
+ if (response.access_token) {
246
+ this.setAccessToken(response.access_token);
247
+ // Update WebSocket token if connected
248
+ if (this.wsClient) {
249
+ this.wsClient.updateToken(response.access_token);
250
+ }
251
+ }
252
+ return response;
253
+ }
254
+ async logout() {
255
+ await this.post("/client/auth/logout", {});
256
+ }
257
+ async refreshToken(refreshToken) {
258
+ return await this.post("/client/auth/refresh", { refresh_token: refreshToken });
259
+ }
260
+ async getProfile() {
261
+ return await this.get("/client/auth/me");
262
+ }
263
+ async updateProfile(updates) {
264
+ return await this.put("/client/auth/me", updates);
265
+ }
266
+ async changePassword(passwordChange) {
267
+ await this.post("/client/auth/change-password", passwordChange);
268
+ }
269
+ async generateApiKey() {
270
+ return await this.post("/client/auth/api-key", {});
271
+ }
272
+ async revokeApiKey() {
273
+ await this.delete("/client/auth/api-key");
274
+ }
275
+ async getPasswordRequirements() {
276
+ return await this.get("/client/auth/password-requirements");
277
+ }
278
+ // ==================== Project Management ====================
279
+ async createProject(projectData) {
280
+ return await this.post("/client/projects/", projectData);
281
+ }
282
+ async getProject(projectId) {
283
+ return await this.get(`/client/projects/${projectId}`);
284
+ }
285
+ async listProjects(organizationId) {
286
+ const path = organizationId ? `/client/projects/org/${organizationId}` : "/client/projects/";
287
+ return await this.get(path);
288
+ }
289
+ async updateProject(projectId, updates) {
290
+ return await this.patch(`/client/projects/${projectId}`, updates);
291
+ }
292
+ async deleteProject(projectId) {
293
+ await this.delete(`/client/projects/${projectId}`);
294
+ }
295
+ async regenerateProjectApiKey(projectId) {
296
+ return await this.post(`/client/projects/${projectId}/api-key`, {});
297
+ }
298
+ async getMcpConfig(projectId) {
299
+ return await this.get(`/client/projects/${projectId}/mcp-config`);
300
+ }
301
+ async getMcpStats(projectId) {
302
+ return await this.get(`/client/projects/${projectId}/mcp-stats`);
303
+ }
304
+ // ==================== Organizations & Teams ====================
305
+ async createOrganization(orgData) {
306
+ return await this.post("/client/organizations/", orgData);
307
+ }
308
+ async listOrganizations() {
309
+ return await this.get("/client/organizations/");
310
+ }
311
+ async getOrganization(orgId) {
312
+ return await this.get(`/client/organizations/${orgId}`);
313
+ }
314
+ async updateOrganization(orgId, updates) {
315
+ return await this.put(`/client/organizations/${orgId}`, updates);
316
+ }
317
+ async addMemberToOrganization(orgId, userId) {
318
+ await this.post(`/client/organizations/${orgId}/members`, { user_id: userId });
319
+ }
320
+ async removeMemberFromOrganization(orgId, userId) {
321
+ await this.post(`/client/organizations/${orgId}/members/${userId}`, {});
322
+ }
323
+ // ==================== Billing & Usage ====================
324
+ async getUsage() {
325
+ return await this.get("/client/billing/usage");
326
+ }
327
+ async getUsageHistory(days = 30, resourceType) {
328
+ const params = { days };
329
+ if (resourceType)
330
+ params.resource_type = resourceType;
331
+ return await this.get("/client/billing/usage/history", params);
332
+ }
333
+ async listBillingPlans() {
334
+ const data = await this.get("/client/billing/plans");
335
+ return Array.isArray(data.plans) ? data.plans : [];
336
+ }
337
+ async getSubscription() {
338
+ return await this.get("/client/billing/subscription");
339
+ }
340
+ // ==================== Notifications ====================
341
+ async listNotifications() {
342
+ const data = await this.get("/client/notifications");
343
+ return Array.isArray(data.notifications) ? data.notifications : [];
344
+ }
345
+ async markNotificationAsRead(notificationId) {
346
+ return await this.post(`/client/notifications/${notificationId}/read`, {});
347
+ }
348
+ async markAllNotificationsAsRead() {
349
+ await this.post("/client/notifications/read-all", {});
350
+ }
351
+ async deleteNotification(notificationId) {
352
+ await this.delete(`/client/notifications/${notificationId}`);
353
+ }
354
+ // ==================== Settings ====================
355
+ async getSettings() {
356
+ return await this.get("/client/settings");
357
+ }
358
+ async updateAccount(updates) {
359
+ return await this.put("/client/settings/account", updates);
360
+ }
361
+ async updatePreferences(preferences) {
362
+ return await this.put("/client/settings/preferences", preferences);
363
+ }
364
+ async getGuidelines() {
365
+ return await this.get("/client/settings/guidelines");
366
+ }
367
+ async changePasswordViaSettings(passwordChange) {
368
+ await this.post("/client/settings/password", passwordChange);
369
+ }
370
+ async deleteAccount(confirm) {
371
+ await this.delete("/client/settings/account");
372
+ }
373
+ // ==================== Activity Logs ====================
374
+ async listActivityLogs(page = 1, pageSize = 50) {
375
+ return await this.get("/client/activity", { page, page_size: pageSize });
376
+ }
377
+ async exportActivityLogs() {
378
+ const url = `${this.baseUrl}/client/activity/export`;
379
+ const res = await fetch(url, { headers: this.headers() });
380
+ if (!res.ok)
381
+ throw new Error(`HTTP ${res.status}`);
382
+ return await res.blob();
383
+ }
384
+ // ==================== Corrections (Additional Methods) ====================
385
+ async getCorrection(correctionId) {
386
+ return await this.get(`/client/corrections/${correctionId}`);
387
+ }
388
+ async updateCorrection(correctionId, updates) {
389
+ return await this.put(`/client/corrections/${correctionId}`, updates);
390
+ }
391
+ async getCorrectionStats() {
392
+ return await this.get("/client/corrections/stats");
393
+ }
394
+ // ==================== WebSocket Support ====================
395
+ /**
396
+ * Create WebSocket connection for real-time updates
397
+ * Requires access token (use login() first or setAccessToken())
398
+ */
399
+ async createWebSocket() {
400
+ if (!this.accessToken) {
401
+ throw new Error("Access token required for WebSocket connection. Use login() first or setAccessToken()");
402
+ }
403
+ if (!this.wsClient) {
404
+ // Dynamic import for WebSocket
405
+ const { RecursorWebSocket } = await import("./websocket.js");
406
+ this.wsClient = new RecursorWebSocket(this.baseUrl, this.accessToken);
407
+ }
408
+ return this.wsClient;
409
+ }
410
+ /**
411
+ * Connect WebSocket and return client
412
+ */
413
+ async connectWebSocket() {
414
+ const ws = await this.createWebSocket();
415
+ await ws.connect();
416
+ return ws;
417
+ }
418
+ /**
419
+ * Disconnect WebSocket if connected
420
+ */
421
+ disconnectWebSocket() {
422
+ if (this.wsClient) {
423
+ this.wsClient.disconnect();
424
+ this.wsClient = undefined;
425
+ }
426
+ }
427
+ // ==================== Memory Operations ====================
428
+ /**
429
+ * Create a conversation summary
430
+ */
431
+ async createConversationSummary(data) {
432
+ return this.post("/client/memory/conversations/summaries", data);
433
+ }
434
+ /**
435
+ * Get a conversation summary by ID
436
+ */
437
+ async getConversationSummary(conversationId) {
438
+ return this.get(`/client/memory/conversations/summaries/${conversationId}`);
439
+ }
440
+ /**
441
+ * List recent conversation summaries
442
+ */
443
+ async listConversationSummaries(params) {
444
+ return this.get("/client/memory/conversations/summaries", params);
445
+ }
446
+ /**
447
+ * Record an architectural change
448
+ */
449
+ async recordArchitecturalChange(data) {
450
+ return this.post("/client/memory/architectural/changes", data);
451
+ }
452
+ /**
453
+ * List recent architectural changes
454
+ */
455
+ async listArchitecturalChanges(params) {
456
+ return this.get("/client/memory/architectural/changes", params);
457
+ }
458
+ // ==================== Rotatable Memory Operations ====================
459
+ /**
460
+ * Query rotatable memory patterns
461
+ */
462
+ async queryRotatableMemory(params) {
463
+ return this.post("/client/memory/rotatable/query", params || {});
464
+ }
465
+ /**
466
+ * Record pattern usage and update effectiveness
467
+ */
468
+ async recordPatternUsage(patternId, successful) {
469
+ return this.post("/client/memory/rotatable/usage", {
470
+ pattern_id: patternId,
471
+ successful,
472
+ });
473
+ }
474
+ /**
475
+ * Get rotatable memory statistics
476
+ */
477
+ async getRotatableMemoryStats() {
478
+ return this.get("/client/memory/rotatable/stats");
479
+ }
480
+ }
@@ -0,0 +1,2 @@
1
+ export declare function activate(): Promise<void>;
2
+ export declare function deactivate(): void;
@@ -0,0 +1,24 @@
1
+ import { RecursorSDK } from "../index";
2
+ export async function activate() {
3
+ const sdk = new RecursorSDK();
4
+ try {
5
+ const policy = await sdk.getLLMGatewayPolicy();
6
+ const messages = [
7
+ { role: "system", content: "Agent start: enforce governance and consistency" },
8
+ ];
9
+ await sdk.gatewayChat({ provider: "openai", model: undefined, messages, call_provider: false });
10
+ // Optionally pre-warm robotics/av policies for domain projects
11
+ try {
12
+ await sdk.getRoboticsGatewayPolicy();
13
+ }
14
+ catch { }
15
+ try {
16
+ await sdk.getAvGatewayPolicy();
17
+ }
18
+ catch { }
19
+ }
20
+ catch (e) {
21
+ // noop – extension should not crash if server unavailable
22
+ }
23
+ }
24
+ export function deactivate() { }
@@ -0,0 +1 @@
1
+ export declare function initAgentSession(userId?: string, orgId?: string): Promise<void>;
@@ -0,0 +1,14 @@
1
+ import { RecursorSDK } from "../index";
2
+ export async function initAgentSession(userId, orgId) {
3
+ const sdk = new RecursorSDK();
4
+ try {
5
+ const policy = await sdk.getLLMGatewayPolicy();
6
+ const messages = [
7
+ { role: "system", content: "Windsurf agent: route through Recursor gateway" },
8
+ ];
9
+ await sdk.gatewayChat({ provider: "openai", model: undefined, messages, call_provider: false, user_id: userId, organization_id: orgId });
10
+ }
11
+ catch (e) {
12
+ // silent fail to avoid disrupting agent
13
+ }
14
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * WebSocket Client for Recursor SDK
3
+ * Provides real-time updates via WebSocket connection
4
+ */
5
+ export interface WebSocketMessage {
6
+ type: string;
7
+ data?: unknown;
8
+ user_id?: string;
9
+ timestamp?: string;
10
+ message?: string;
11
+ }
12
+ export type WebSocketEventHandler = (data: unknown) => void;
13
+ export declare class RecursorWebSocket {
14
+ private ws;
15
+ private reconnectAttempts;
16
+ private maxReconnectAttempts;
17
+ private reconnectDelay;
18
+ private pingInterval;
19
+ private isConnecting;
20
+ private shouldReconnect;
21
+ private eventHandlers;
22
+ private baseUrl;
23
+ private accessToken;
24
+ constructor(baseUrl: string, accessToken: string);
25
+ /**
26
+ * Connect to WebSocket server
27
+ */
28
+ connect(): Promise<void>;
29
+ /**
30
+ * Attempt to reconnect with exponential backoff
31
+ */
32
+ private attemptReconnect;
33
+ /**
34
+ * Start ping interval to keep connection alive
35
+ */
36
+ private startPing;
37
+ /**
38
+ * Stop ping interval
39
+ */
40
+ private stopPing;
41
+ /**
42
+ * Send message to server
43
+ */
44
+ send(message: WebSocketMessage): void;
45
+ /**
46
+ * Subscribe to event type
47
+ */
48
+ on(event: string, handler: WebSocketEventHandler): void;
49
+ /**
50
+ * Unsubscribe from event type
51
+ */
52
+ off(event: string, handler: WebSocketEventHandler): void;
53
+ /**
54
+ * Handle incoming message
55
+ */
56
+ private handleMessage;
57
+ /**
58
+ * Emit event to handlers
59
+ */
60
+ private emit;
61
+ /**
62
+ * Disconnect WebSocket
63
+ */
64
+ disconnect(): void;
65
+ /**
66
+ * Check if WebSocket is connected
67
+ */
68
+ isConnected(): boolean;
69
+ /**
70
+ * Update access token (useful after token refresh)
71
+ */
72
+ updateToken(newToken: string): void;
73
+ }