@coderule/clients 1.1.0 → 1.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Coderule TypeScript Client SDK
2
2
 
3
- TypeScript HTTP clients for core Coderule microservices (Auth, Sync, Retrieval) with automatic fetch polyfill support.
3
+ TypeScript HTTP clients for core Coderule microservices (Auth, Sync, Retrieval, AST) with automatic fetch polyfill support.
4
4
 
5
5
  ## Requirements
6
6
 
@@ -26,132 +26,190 @@ npm run build
26
26
 
27
27
  ## Usage Examples
28
28
 
29
- ### Authentication Service
29
+ ### Unified client access
30
30
 
31
31
  ```typescript
32
- import { AuthHttpClient } from '@coderule/clients';
32
+ import { CoderuleClients } from '@coderule/clients';
33
+
34
+ const clients = new CoderuleClients({
35
+ token: process.env.CODERULE_TOKEN!,
36
+ auth: { baseUrl: 'https://r.coderule.ai:16803' }, // optional, defaults to this host
37
+ });
38
+
39
+ const snapshotStatus = await clients.sync.checkSnapshotStatus(snapshotHash);
40
+ const retrieval = await clients.retrieval.query(snapshotHash, 'Find the entrypoint');
41
+ const visitorRules = await clients.ast.getVisitorRulesV2();
42
+
43
+ clients.close();
44
+ ```
45
+
46
+ The unified client automatically exchanges your long-lived token for short-lived
47
+ JWTs using the Auth service. Tokens are cached and refreshed halfway through
48
+ their lifetime. Whenever the Auth service returns a JWT with a different
49
+ `server_url`, the AST, Retrieval, and Sync clients update their base URLs on
50
+ the fly.
51
+
52
+ You can override individual service hosts or timeouts:
53
+
54
+ ```typescript
55
+ const clients = new CoderuleClients({
56
+ token,
57
+ auth: { baseUrl: 'https://internal-auth.example.com' },
58
+ retrieval: { timeout: 90_000 },
59
+ sync: { baseUrl: 'http://localhost:8002' }, // static override keeps sync on localhost
60
+ });
61
+ ```
62
+
63
+ ### Manual wiring
64
+
65
+ If you prefer to manage dependency injection yourself, wire the Auth client and
66
+ JWT factory and pass the provider into the other clients.
67
+
68
+ ```typescript
69
+ import {
70
+ AuthHttpClient,
71
+ JWTFactory,
72
+ RetrievalHttpClient,
73
+ SyncHttpClient,
74
+ ASTHttpClient,
75
+ } from '@coderule/clients';
33
76
 
77
+ const authClient = new AuthHttpClient('http://localhost:8001');
78
+ const jwtFactory = new JWTFactory(authClient, longLivedToken);
79
+
80
+ const syncClient = new SyncHttpClient({
81
+ baseUrl: 'http://localhost:8002',
82
+ jwtProvider: jwtFactory,
83
+ });
84
+
85
+ const astClient = new ASTHttpClient({
86
+ baseUrl: 'http://localhost:8003',
87
+ jwtProvider: jwtFactory,
88
+ });
89
+
90
+ const retrievalClient = new RetrievalHttpClient({
91
+ baseUrl: 'http://localhost:8004',
92
+ jwtProvider: jwtFactory,
93
+ });
94
+
95
+ const status = await syncClient.checkSnapshotStatus(snapshotHash);
96
+ const queryResult = await retrievalClient.query(snapshotHash, 'Find the entrypoint');
97
+ const rules = await astClient.getVisitorRulesV2();
98
+ ```
99
+
100
+ ### Authentication service
101
+
102
+ ```typescript
34
103
  const authClient = new AuthHttpClient('http://localhost:8001');
35
104
 
36
105
  try {
37
- // Authenticate and get JWT
38
106
  const authResponse = await authClient.authenticate('your-token');
39
- const jwt = authResponse.jwt;
40
107
  console.log(`JWT expires at: ${authResponse.expires_at}`);
41
108
 
42
- // Check health
43
109
  const health = await authClient.health();
44
110
  console.log(`Auth service status: ${health.status}`);
45
- } catch (error) {
46
- console.error('Authentication failed:', error);
47
111
  } finally {
48
112
  authClient.close();
49
113
  }
50
114
  ```
51
115
 
52
- ### Sync Service
116
+ ### Sync service
53
117
 
54
118
  ```typescript
55
- import { SyncHttpClient } from '@coderule/clients';
56
-
57
- const syncClient = new SyncHttpClient('http://localhost:8002', jwt);
119
+ const syncClient = new SyncHttpClient({
120
+ baseUrl: 'http://localhost:8002',
121
+ jwtProvider: jwtFactory,
122
+ });
123
+
124
+ const files = [
125
+ { file_path: 'src/main.ts', content: 'const x = 1;' },
126
+ { file_path: 'src/utils.ts', content: 'export function util() {}' },
127
+ { file_path: 'package.json', content: '{"name": "test"}' },
128
+ ];
129
+
130
+ const filesInfo = [];
131
+ const fileHashes = [];
132
+ const fileContents = new Map();
133
+
134
+ for (const file of files) {
135
+ const fileHash = SyncHttpClient.calculateFileHash(file.file_path, file.content);
136
+ filesInfo.push({ file_path: file.file_path, file_hash: fileHash });
137
+ fileContents.set(fileHash, {
138
+ path: file.file_path,
139
+ content: Buffer.from(file.content),
140
+ });
141
+ fileHashes.push(fileHash);
142
+ }
58
143
 
59
- try {
60
- // Prepare files for sync (you need to provide the content)
61
- const files = [
62
- { file_path: 'src/main.ts', content: 'const x = 1;' },
63
- { file_path: 'src/utils.ts', content: 'export function util() {}' },
64
- { file_path: 'package.json', content: '{"name": "test"}' }
65
- ];
66
-
67
- // Calculate file hashes
68
- const filesInfo = [];
69
- const fileHashes = [];
70
- const fileContents = new Map();
71
-
72
- for (const file of files) {
73
- const fileHash = SyncHttpClient.calculateFileHash(file.file_path, file.content);
74
- filesInfo.push({
75
- file_path: file.file_path,
76
- file_hash: fileHash
77
- });
78
- fileContents.set(fileHash, {
79
- path: file.file_path,
80
- content: Buffer.from(file.content)
81
- });
82
- fileHashes.push(fileHash);
83
- }
144
+ const snapshotHash = SyncHttpClient.calculateSnapshotHash(fileHashes);
84
145
 
85
- // Calculate snapshot hash
86
- const snapshotHash = SyncHttpClient.calculateSnapshotHash(fileHashes);
87
- console.log(`Snapshot hash: ${snapshotHash}`);
88
-
89
- // Check if snapshot exists
90
- const status = await syncClient.checkSnapshotStatus(snapshotHash);
91
- console.log(`Snapshot status: ${status.status}`);
92
-
93
- if (status.status === 'NOT_FOUND') {
94
- // Create snapshot
95
- const result = await syncClient.createSnapshot(snapshotHash, filesInfo);
96
-
97
- // Upload missing files if needed
98
- if (result.status === 'MISSING_CONTENT' && result.missing_files) {
99
- const missingContent = new Map();
100
- for (const missingFile of result.missing_files) {
101
- if (fileContents.has(missingFile.file_hash)) {
102
- missingContent.set(missingFile.file_hash, fileContents.get(missingFile.file_hash));
103
- }
146
+ const status = await syncClient.checkSnapshotStatus(snapshotHash);
147
+ if (status.status === 'NOT_FOUND') {
148
+ const result = await syncClient.createSnapshot(snapshotHash, filesInfo);
149
+
150
+ if (result.status === 'MISSING_CONTENT' && result.missing_files) {
151
+ const missingContent = new Map();
152
+ for (const missingFile of result.missing_files) {
153
+ if (fileContents.has(missingFile.file_hash)) {
154
+ missingContent.set(
155
+ missingFile.file_hash,
156
+ fileContents.get(missingFile.file_hash)!,
157
+ );
104
158
  }
159
+ }
105
160
 
106
- if (missingContent.size > 0) {
107
- await syncClient.uploadFileContent(missingContent);
108
- // Retry snapshot creation
109
- await syncClient.createSnapshot(snapshotHash, filesInfo);
110
- }
161
+ if (missingContent.size > 0) {
162
+ await syncClient.uploadFileContent(missingContent);
163
+ await syncClient.createSnapshot(snapshotHash, filesInfo);
111
164
  }
112
165
  }
113
- } catch (error) {
114
- console.error('Sync failed:', error);
115
- } finally {
116
- syncClient.close();
117
166
  }
118
167
  ```
119
168
 
120
- ### Retrieval Service
169
+ ### AST service
121
170
 
122
171
  ```typescript
123
- import { RetrievalHttpClient } from '@coderule/clients';
172
+ const astClient = new ASTHttpClient({
173
+ baseUrl: 'http://localhost:8003',
174
+ jwtProvider: jwtFactory,
175
+ });
124
176
 
125
- const retrievalClient = new RetrievalHttpClient('http://localhost:8004');
177
+ const health = await astClient.health();
178
+ console.log(`AST service status: ${health.status}`);
126
179
 
127
- try {
128
- // Execute a retrieval query
129
- const result = await retrievalClient.query(
130
- snapshotHash,
131
- 'Find the main authentication logic',
132
- 3000, // budget tokens
133
- jwt,
134
- {
135
- formatter: 'compact',
136
- flow_strength: 1.5,
137
- blend_alpha: 0.8
138
- }
139
- );
180
+ const rules = await astClient.getVisitorRulesV2();
181
+ console.log(`Rules format: ${rules.format}`);
140
182
 
141
- console.log(result.formatted_output);
183
+ const compiled = ASTHttpClient.compileRulesV2(rules);
184
+ const ignoredPredicate = ASTHttpClient.buildIgnoredPredicate(compiled);
185
+ ```
142
186
 
143
- // Check snapshot status
144
- const status = await retrievalClient.checkSnapshotStatus(snapshotHash, jwt);
145
- console.log(`Snapshot indexing status: ${status.status}`);
187
+ ### Retrieval service
146
188
 
147
- // Get cache statistics
148
- const cacheStats = await retrievalClient.getCacheStats(jwt);
149
- console.log(`Cached snapshots: ${cacheStats.cached_snapshots}`);
150
- } catch (error) {
151
- console.error('Retrieval failed:', error);
152
- } finally {
153
- retrievalClient.close();
154
- }
189
+ ```typescript
190
+ const retrievalClient = new RetrievalHttpClient({
191
+ baseUrl: 'http://localhost:8004',
192
+ jwtProvider: jwtFactory,
193
+ });
194
+
195
+ const result = await retrievalClient.query(
196
+ snapshotHash,
197
+ 'Find the main authentication logic',
198
+ 3000,
199
+ {
200
+ formatter: 'compact',
201
+ flow_strength: 1.5,
202
+ blend_alpha: 0.8,
203
+ },
204
+ );
205
+
206
+ console.log(result.formatted_output);
207
+
208
+ const status = await retrievalClient.checkSnapshotStatus(snapshotHash);
209
+ console.log(`Snapshot indexing status: ${status.status}`);
210
+
211
+ const cacheStats = await retrievalClient.getCacheStats();
212
+ console.log(`Cached snapshots: ${cacheStats.cached_snapshots}`);
155
213
  ```
156
214
 
157
215
  ## Utility Methods
@@ -195,8 +253,24 @@ All clients support timeout configuration in milliseconds:
195
253
 
196
254
  ```typescript
197
255
  // Custom timeout of 30 seconds
198
- const client = new RetrievalHttpClient('http://localhost:8004', 30000);
256
+ const retrievalClient = new RetrievalHttpClient({
257
+ baseUrl: 'http://localhost:8004',
258
+ timeout: 30_000,
259
+ jwtProvider: jwtFactory,
260
+ });
199
261
  ```
262
+ `CoderuleClients` also accepts per-service overrides via the `auth`, `ast`, `retrieval`,
263
+ and `sync` options. `JWTFactory` exposes an `onTokenRefreshed` callback if you need to
264
+ observe token rotations (for example, to log the current `server_url`).
265
+
266
+ ## Breaking Changes (v1.4.0)
267
+
268
+ - **Clients now require a JWT provider**: `SyncHttpClient`, `RetrievalHttpClient`, and
269
+ `ASTHttpClient` no longer accept JWT strings per method. Inject a `JWTFactory` or any
270
+ `JWTProvider` implementation through the constructor instead.
271
+ - **Unified entry point**: `CoderuleClients` centralises token exchange. Auth defaults to
272
+ `https://r.coderule.ai:16803`, and other clients automatically adopt the `server_url`
273
+ provided by the Auth service unless explicitly overridden.
200
274
 
201
275
  ## Notes
202
276
 
@@ -204,7 +278,10 @@ const client = new RetrievalHttpClient('http://localhost:8004', 30000);
204
278
  - **Optional dependency**: `node-fetch` is only required for Node.js versions < 18
205
279
  - **Zero dependencies for Node.js 18+**: Uses native fetch API
206
280
  - Clients automatically handle JSON serialization/deserialization
207
- - JWT tokens are required for most operations (except health checks)
281
+ - JWT handling is centralised through a `JWTProvider` (`JWTFactory` or `CoderuleClients`)
282
+ - AST, Retrieval, and Sync clients update their base URLs when the JWT refresh
283
+ returns a different `server_url` (unless a manual override is configured)
284
+ - Health check endpoints don't require authentication
208
285
  - All clients have a `close()` method for consistency, though fetch doesn't maintain persistent connections
209
286
 
210
287
  ## Building from Source
@@ -221,4 +298,4 @@ npm run build
221
298
 
222
299
  ## License
223
300
 
224
- ISC
301
+ ISC