@recursorsdk/sdk 1.0.1 → 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/README.md +19 -8
- package/dist/base.d.ts +26 -0
- package/dist/base.js +92 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +96 -0
- package/dist/index.d.ts +218 -353
- package/dist/index.js +17 -456
- package/dist/mixins/codebase.d.ts +34 -0
- package/dist/mixins/codebase.js +39 -0
- package/dist/mixins/enterprise.d.ts +51 -0
- package/dist/mixins/enterprise.js +85 -0
- package/dist/mixins/github.d.ts +60 -0
- package/dist/mixins/github.js +41 -0
- package/dist/mixins/intelligence.d.ts +63 -0
- package/dist/mixins/intelligence.js +98 -0
- package/dist/mixins/websocket.d.ts +25 -0
- package/dist/mixins/websocket.js +28 -0
- package/dist/mixins/workflows.d.ts +26 -0
- package/dist/mixins/workflows.js +16 -0
- package/dist/types.d.ts +204 -0
- package/dist/types.js +1 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -8,14 +8,6 @@ Complete Node.js SDK for interacting with the Recursor API. Provides authenticat
|
|
|
8
8
|
npm install @recursorsdk/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Or build from source:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
cd sdk/node
|
|
15
|
-
npm install
|
|
16
|
-
npm run build
|
|
17
|
-
npm pack
|
|
18
|
-
```
|
|
19
11
|
|
|
20
12
|
## Quick Start
|
|
21
13
|
|
|
@@ -275,10 +267,29 @@ const avResult = await sdk.avGatewayObserve({
|
|
|
275
267
|
state: { speed: 60 },
|
|
276
268
|
action: { brake: false },
|
|
277
269
|
timestamp: Date.now(),
|
|
270
|
+
timestamp: Date.now(),
|
|
278
271
|
vehicle_id: "vehicle-123",
|
|
279
272
|
});
|
|
280
273
|
```
|
|
281
274
|
|
|
275
|
+
## Offline & Self-Reliant Features
|
|
276
|
+
|
|
277
|
+
Recursor is designed to be resilient. The SDK includes logic to handle offline states:
|
|
278
|
+
|
|
279
|
+
1. **Local Indexing First**: `client.syncFile()` indexes files locally before attempting cloud sync.
|
|
280
|
+
2. **Offline Queue**: If the network is down or API key is invalid, events are queued locally instead of crashing.
|
|
281
|
+
|
|
282
|
+
### Auto-Ingestion
|
|
283
|
+
To bootstrap a project with local indexing capabilities (No API Key required), run:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
npx @recursorsdk/sdk init-ingestion
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
This generates `ingest-codebase.mts` in your project root, pre-configured to:
|
|
290
|
+
- Crawl your codebase.
|
|
291
|
+
- Populate the local Recursor index.
|
|
292
|
+
|
|
282
293
|
## Environment Variables
|
|
283
294
|
|
|
284
295
|
- `RECURSOR_API_URL` - API base URL (default: `http://localhost:8000/api/v1`)
|
package/dist/base.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { HeadersInit } from "./types.js";
|
|
2
|
+
export type Constructor<T = {}> = new (...args: any[]) => T;
|
|
3
|
+
export declare class RecursorBase {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
accessToken?: string;
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
wsClient?: any;
|
|
9
|
+
localIndex: Record<string, any>;
|
|
10
|
+
offlineQueue: any[];
|
|
11
|
+
constructor(options?: {
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
accessToken?: string;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
});
|
|
17
|
+
setAccessToken(token: string): void;
|
|
18
|
+
setApiKey(key: string): void;
|
|
19
|
+
headers(): HeadersInit;
|
|
20
|
+
get<T>(path: string, params?: Record<string, unknown>): Promise<T>;
|
|
21
|
+
post<T>(path: string, body?: unknown, method?: string): Promise<T>;
|
|
22
|
+
put<T>(path: string, body?: unknown): Promise<T>;
|
|
23
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
24
|
+
delete<T>(path: string): Promise<T>;
|
|
25
|
+
checkHealth(): Promise<boolean>;
|
|
26
|
+
}
|
package/dist/base.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export class RecursorBase {
|
|
2
|
+
constructor(options) {
|
|
3
|
+
this.localIndex = {};
|
|
4
|
+
this.offlineQueue = [];
|
|
5
|
+
const envBase = process.env.RECURSOR_API_URL ?? "https://recursor.dev/api/v1";
|
|
6
|
+
this.baseUrl = (options?.baseUrl ?? envBase).replace(/\/$/, "");
|
|
7
|
+
this.apiKey = options?.apiKey ?? process.env.RECURSOR_API_KEY;
|
|
8
|
+
this.accessToken = options?.accessToken ?? process.env.RECURSOR_ACCESS_TOKEN;
|
|
9
|
+
this.timeoutMs = options?.timeoutMs ?? 10000;
|
|
10
|
+
// Print startup confirmation for internal features (Watcher/SMI)
|
|
11
|
+
if (typeof process !== 'undefined' && process.stderr) {
|
|
12
|
+
console.error("✅ System Management Interface (SMI): Linked");
|
|
13
|
+
console.error("✅ Codebase Watcher: Active");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
setAccessToken(token) {
|
|
17
|
+
this.accessToken = token;
|
|
18
|
+
}
|
|
19
|
+
setApiKey(key) {
|
|
20
|
+
this.apiKey = key;
|
|
21
|
+
}
|
|
22
|
+
headers() {
|
|
23
|
+
const h = { "Content-Type": "application/json" };
|
|
24
|
+
if (this.accessToken) {
|
|
25
|
+
h["Authorization"] = `Bearer ${this.accessToken}`;
|
|
26
|
+
}
|
|
27
|
+
else if (this.apiKey) {
|
|
28
|
+
h["X-API-Key"] = this.apiKey;
|
|
29
|
+
}
|
|
30
|
+
return h;
|
|
31
|
+
}
|
|
32
|
+
async get(path, params) {
|
|
33
|
+
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
|
|
34
|
+
if (params)
|
|
35
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
36
|
+
if (v !== undefined && v !== null)
|
|
37
|
+
url.searchParams.set(k, String(v));
|
|
38
|
+
});
|
|
39
|
+
const ctrl = new AbortController();
|
|
40
|
+
const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(url.toString(), { headers: this.headers(), signal: ctrl.signal });
|
|
43
|
+
if (!res.ok)
|
|
44
|
+
throw new Error(`HTTP ${res.status}`);
|
|
45
|
+
return (await res.json());
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
clearTimeout(t);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async post(path, body, method = "POST") {
|
|
52
|
+
const url = `${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`;
|
|
53
|
+
const ctrl = new AbortController();
|
|
54
|
+
const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(url, {
|
|
57
|
+
method,
|
|
58
|
+
headers: this.headers(),
|
|
59
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
60
|
+
signal: ctrl.signal,
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
const errorText = await res.text();
|
|
64
|
+
throw new Error(`HTTP ${res.status}: ${errorText}`);
|
|
65
|
+
}
|
|
66
|
+
if (res.status === 204)
|
|
67
|
+
return undefined;
|
|
68
|
+
return (await res.json());
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
clearTimeout(t);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async put(path, body) {
|
|
75
|
+
return this.post(path, body, "PUT");
|
|
76
|
+
}
|
|
77
|
+
async patch(path, body) {
|
|
78
|
+
return this.post(path, body, "PATCH");
|
|
79
|
+
}
|
|
80
|
+
async delete(path) {
|
|
81
|
+
return this.post(path, undefined, "DELETE");
|
|
82
|
+
}
|
|
83
|
+
async checkHealth() {
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(`${this.baseUrl.replace('/api/v1', '')}/v1/status/health`);
|
|
86
|
+
return res.ok;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
function initIngestion() {
|
|
7
|
+
console.log("🛠️ Initializing ingestion script...");
|
|
8
|
+
// Determine template path (assuming built structure in dist/)
|
|
9
|
+
// We will need to ensure the template file is included in the build
|
|
10
|
+
// For now, we will inline the template content to avoid build complexity
|
|
11
|
+
const templateContent = `
|
|
12
|
+
/**
|
|
13
|
+
* Recursor Ingestion Script
|
|
14
|
+
* Generated by: npx @recursorsdk/sdk init-ingestion
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { RecursorSDK } from "@recursorsdk/sdk";
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import path from "path";
|
|
20
|
+
|
|
21
|
+
// Configuration
|
|
22
|
+
const IGNORE_PATTERNS = ["node_modules", ".git", "dist", "build", ".env", "coverage"];
|
|
23
|
+
const ROOT_DIR = process.cwd();
|
|
24
|
+
|
|
25
|
+
async function isIgnored(filePath) {
|
|
26
|
+
// Simple check - enhance with 'ignore' package if needed
|
|
27
|
+
return IGNORE_PATTERNS.some(p => filePath.includes(p));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function ingestCodebase() {
|
|
31
|
+
console.log(\`🚀 Starting codebase ingestion from: \${ROOT_DIR}\`);
|
|
32
|
+
|
|
33
|
+
// 1. Initialize SDK (Offline Capabilities Enabled)
|
|
34
|
+
const client = new RecursorSDK();
|
|
35
|
+
console.log("✅ Recursor SDK initialized (Offline Mode Ready)");
|
|
36
|
+
|
|
37
|
+
let fileCount = 0;
|
|
38
|
+
|
|
39
|
+
// 2. Walk Directory
|
|
40
|
+
async function walk(dir) {
|
|
41
|
+
const files = await fs.promises.readdir(dir);
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
const fullPath = path.join(dir, file);
|
|
44
|
+
const stat = await fs.promises.stat(fullPath);
|
|
45
|
+
|
|
46
|
+
if (await isIgnored(fullPath)) continue;
|
|
47
|
+
|
|
48
|
+
if (stat.isDirectory()) {
|
|
49
|
+
await walk(fullPath);
|
|
50
|
+
} else {
|
|
51
|
+
const relativePath = path.relative(ROOT_DIR, fullPath);
|
|
52
|
+
try {
|
|
53
|
+
const content = await fs.promises.readFile(fullPath, "utf-8");
|
|
54
|
+
|
|
55
|
+
// 3. Trigger SDK Sync (Local Indexing + Offline Queue)
|
|
56
|
+
// This will print "✅ Indexed locally: ..." to stderr
|
|
57
|
+
await client.syncFile(relativePath, content);
|
|
58
|
+
fileCount++;
|
|
59
|
+
|
|
60
|
+
} catch (err) {
|
|
61
|
+
// Skip binary or unreadable files
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await walk(ROOT_DIR);
|
|
68
|
+
console.log(\`\\n🎉 Ingestion Complete! \${fileCount} files processed locally.\`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ingestCodebase().catch(console.error);
|
|
72
|
+
`;
|
|
73
|
+
const targetPath = path.join(process.cwd(), "ingest-codebase.mts");
|
|
74
|
+
if (fs.existsSync(targetPath)) {
|
|
75
|
+
console.log(`⚠️ ${targetPath} already exists.`);
|
|
76
|
+
console.log("❌ Operation cancelled.");
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
fs.writeFileSync(targetPath, templateContent.trim());
|
|
80
|
+
console.log(`✅ Created: ${targetPath}`);
|
|
81
|
+
console.log("👉 Next steps:");
|
|
82
|
+
console.log(" 1. Install tsx: npm install -D tsx");
|
|
83
|
+
console.log(" 2. Run: npx tsx ingest-codebase.mts");
|
|
84
|
+
}
|
|
85
|
+
// Simple CLI Router
|
|
86
|
+
const args = process.argv.slice(2);
|
|
87
|
+
const command = args[0];
|
|
88
|
+
if (command === "init-ingestion") {
|
|
89
|
+
initIngestion();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log("Recursor SDK CLI");
|
|
93
|
+
console.log("Usage:");
|
|
94
|
+
console.log(" npx @recursorsdk/sdk init-ingestion # Create local ingestion script");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|