@coderule/clients 1.0.0 → 1.2.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 CHANGED
@@ -1,16 +1,926 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
1
+ import * as crypto from 'crypto';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
15
  };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fetch = exports.RetrievalHttpClient = exports.SyncHttpClient = exports.decodeJWT = exports.AuthHttpClient = void 0;
7
- var auth_client_1 = require("./clients/auth-client");
8
- Object.defineProperty(exports, "AuthHttpClient", { enumerable: true, get: function () { return auth_client_1.AuthHttpClient; } });
9
- Object.defineProperty(exports, "decodeJWT", { enumerable: true, get: function () { return auth_client_1.decodeJWT; } });
10
- var sync_client_1 = require("./clients/sync-client");
11
- Object.defineProperty(exports, "SyncHttpClient", { enumerable: true, get: function () { return sync_client_1.SyncHttpClient; } });
12
- var retrieval_client_1 = require("./clients/retrieval-client");
13
- Object.defineProperty(exports, "RetrievalHttpClient", { enumerable: true, get: function () { return retrieval_client_1.RetrievalHttpClient; } });
14
- var fetch_wrapper_1 = require("./utils/fetch-wrapper");
15
- Object.defineProperty(exports, "fetch", { enumerable: true, get: function () { return __importDefault(fetch_wrapper_1).default; } });
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from))
23
+ if (!__hasOwnProp.call(to, key) && key !== except)
24
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
25
+ }
26
+ return to;
27
+ };
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // node_modules/tsup/assets/esm_shims.js
31
+ var init_esm_shims = __esm({
32
+ "node_modules/tsup/assets/esm_shims.js"() {
33
+ }
34
+ });
35
+
36
+ // src/polyfills/fetch-polyfill.ts
37
+ var fetch_polyfill_exports = {};
38
+ __export(fetch_polyfill_exports, {
39
+ AbortController: () => AbortController,
40
+ FormData: () => FormData,
41
+ Headers: () => Headers,
42
+ Request: () => Request,
43
+ Response: () => Response,
44
+ fetch: () => fetch
45
+ });
46
+ var nodeFetch, NodeHeaders, NodeRequest, NodeResponse, NodeFormData, NodeAbortController, fetch, Headers, Request, Response, FormData, AbortController;
47
+ var init_fetch_polyfill = __esm({
48
+ "src/polyfills/fetch-polyfill.ts"() {
49
+ init_esm_shims();
50
+ try {
51
+ const module = __require("node-fetch");
52
+ nodeFetch = module.default || module;
53
+ NodeHeaders = module.Headers;
54
+ NodeRequest = module.Request;
55
+ NodeResponse = module.Response;
56
+ try {
57
+ NodeFormData = // eslint-disable-next-line @typescript-eslint/no-require-imports
58
+ module.FormData || __require("formdata-polyfill/esm.min.js").FormData;
59
+ } catch {
60
+ NodeFormData = class FormData {
61
+ constructor() {
62
+ throw new Error(
63
+ "FormData is not available. Install formdata-polyfill if needed."
64
+ );
65
+ }
66
+ };
67
+ }
68
+ NodeAbortController = globalThis.AbortController || module.AbortController;
69
+ } catch {
70
+ throw new Error(
71
+ "For Node.js < 18, please install node-fetch: npm install node-fetch@2"
72
+ );
73
+ }
74
+ fetch = nodeFetch;
75
+ Headers = NodeHeaders;
76
+ Request = NodeRequest;
77
+ Response = NodeResponse;
78
+ FormData = NodeFormData;
79
+ AbortController = NodeAbortController;
80
+ }
81
+ });
82
+
83
+ // src/index.ts
84
+ init_esm_shims();
85
+
86
+ // src/clients/auth-client.ts
87
+ init_esm_shims();
88
+
89
+ // src/utils/fetch-wrapper.ts
90
+ init_esm_shims();
91
+ var hasNativeFetch = typeof globalThis.fetch !== "undefined";
92
+ var fetchModule;
93
+ if (hasNativeFetch) {
94
+ fetchModule = {
95
+ fetch: globalThis.fetch,
96
+ Headers: globalThis.Headers,
97
+ Request: globalThis.Request,
98
+ Response: globalThis.Response,
99
+ FormData: globalThis.FormData,
100
+ AbortController: globalThis.AbortController
101
+ };
102
+ } else {
103
+ try {
104
+ fetchModule = (init_fetch_polyfill(), __toCommonJS(fetch_polyfill_exports));
105
+ } catch {
106
+ throw new Error(
107
+ `This library requires Node.js 18+ or the node-fetch package.
108
+ Please either:
109
+ 1. Upgrade to Node.js 18 or later, or
110
+ 2. Install node-fetch: npm install node-fetch@2`
111
+ );
112
+ }
113
+ }
114
+ var fetch2 = fetchModule.fetch;
115
+ fetchModule.Headers;
116
+ fetchModule.Request;
117
+ fetchModule.Response;
118
+ var FormData2 = fetchModule.FormData;
119
+ var AbortController2 = fetchModule.AbortController;
120
+ var fetch_wrapper_default = fetch2;
121
+
122
+ // src/clients/auth-client.ts
123
+ var AuthHttpClient = class {
124
+ /**
125
+ * Initialize the Auth HTTP client
126
+ * @param baseUrl - Base URL of the Auth service (e.g., "http://localhost:8001")
127
+ * @param timeout - Request timeout in milliseconds (default: 30000)
128
+ */
129
+ constructor(baseUrl, timeout = 3e4) {
130
+ this.baseUrl = baseUrl.replace(/\/$/, "");
131
+ this.timeout = timeout;
132
+ }
133
+ /**
134
+ * Authenticate a token and receive a JWT
135
+ * @param token - Authentication token
136
+ * @returns Response containing JWT and expiration
137
+ * @throws Error on HTTP errors or connection errors
138
+ */
139
+ async authenticate(token) {
140
+ const url = `${this.baseUrl}/api/auth/authenticate`;
141
+ try {
142
+ const controller = new AbortController2();
143
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
144
+ const response = await fetch_wrapper_default(url, {
145
+ method: "POST",
146
+ headers: {
147
+ "Content-Type": "application/json"
148
+ },
149
+ body: JSON.stringify({ token }),
150
+ signal: controller.signal
151
+ });
152
+ clearTimeout(timeoutId);
153
+ if (!response.ok) {
154
+ const errorText = await response.text();
155
+ throw new Error(
156
+ `Authentication failed with status ${response.status}: ${errorText}`
157
+ );
158
+ }
159
+ const data = await response.json();
160
+ console.debug(
161
+ `Authentication successful, JWT expires at ${data.expires_at}`
162
+ );
163
+ return data;
164
+ } catch (error) {
165
+ if (error.name === "AbortError") {
166
+ throw new Error(`Request timeout after ${this.timeout}ms`);
167
+ }
168
+ console.error(`Authentication request failed: ${error.message}`);
169
+ throw error;
170
+ }
171
+ }
172
+ /**
173
+ * Check the health status of the Auth service
174
+ * @returns Health status information
175
+ * @throws Error on HTTP errors or connection errors
176
+ */
177
+ async health() {
178
+ const url = `${this.baseUrl}/api/auth/health`;
179
+ try {
180
+ const controller = new AbortController2();
181
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
182
+ const response = await fetch_wrapper_default(url, {
183
+ method: "GET",
184
+ signal: controller.signal
185
+ });
186
+ clearTimeout(timeoutId);
187
+ if (!response.ok) {
188
+ const errorText = await response.text();
189
+ throw new Error(
190
+ `Health check failed with status ${response.status}: ${errorText}`
191
+ );
192
+ }
193
+ const data = await response.json();
194
+ console.debug(`Health check: ${data.status}`);
195
+ return data;
196
+ } catch (error) {
197
+ if (error.name === "AbortError") {
198
+ throw new Error(`Request timeout after ${this.timeout}ms`);
199
+ }
200
+ console.error(`Health check request failed: ${error.message}`);
201
+ throw error;
202
+ }
203
+ }
204
+ /**
205
+ * Close the HTTP client connection (no-op for fetch)
206
+ */
207
+ close() {
208
+ }
209
+ };
210
+ function decodeJWT(jwtToken) {
211
+ try {
212
+ const parts = jwtToken.split(".");
213
+ if (parts.length !== 3) {
214
+ return null;
215
+ }
216
+ const payload = parts[1];
217
+ const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
218
+ const base64 = paddedPayload.replace(/-/g, "+").replace(/_/g, "/");
219
+ const jsonString = Buffer.from(base64, "base64").toString("utf8");
220
+ const payloadObj = JSON.parse(jsonString);
221
+ if (payloadObj.exp) {
222
+ const now = Math.floor(Date.now() / 1e3);
223
+ if (payloadObj.exp < now) {
224
+ console.debug("JWT token is expired");
225
+ return null;
226
+ }
227
+ }
228
+ return payloadObj;
229
+ } catch (error) {
230
+ console.error("Failed to decode JWT:", error);
231
+ return null;
232
+ }
233
+ }
234
+
235
+ // src/clients/sync-client.ts
236
+ init_esm_shims();
237
+ var SyncHttpClient = class {
238
+ /**
239
+ * Initialize the Sync HTTP client
240
+ * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8002")
241
+ * @param timeout - Request timeout in milliseconds (default: 60000)
242
+ */
243
+ constructor(uri = "http://localhost:8002", timeout = 6e4) {
244
+ let processedUri = uri;
245
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
246
+ processedUri = `http://${uri}`;
247
+ }
248
+ const url = new URL(processedUri);
249
+ if (url.pathname && url.pathname !== "/") {
250
+ this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
251
+ } else {
252
+ this.baseUrl = `${url.protocol}//${url.host}`;
253
+ }
254
+ this.timeout = timeout;
255
+ if (this.baseUrl.endsWith("/")) {
256
+ this.apiBase = `${this.baseUrl}sync/v1/`;
257
+ } else {
258
+ this.apiBase = `${this.baseUrl}/sync/v1/`;
259
+ }
260
+ }
261
+ /**
262
+ * Check the status of a snapshot
263
+ * @param snapshotHash - SHA256 hash of the snapshot
264
+ * @param jwt - JWT token for authorization (required)
265
+ * @returns Snapshot status information
266
+ * @throws Error on HTTP errors or connection errors
267
+ */
268
+ async checkSnapshotStatus(snapshotHash, jwt) {
269
+ if (!jwt) {
270
+ throw new Error("JWT must be provided");
271
+ }
272
+ const url = `${this.apiBase}snapshots`;
273
+ try {
274
+ const controller = new AbortController2();
275
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
276
+ const response = await fetch_wrapper_default(url, {
277
+ method: "POST",
278
+ headers: {
279
+ Authorization: `Bearer ${jwt}`,
280
+ "Content-Type": "application/json"
281
+ },
282
+ body: JSON.stringify({ snapshot_hash: snapshotHash }),
283
+ signal: controller.signal
284
+ });
285
+ clearTimeout(timeoutId);
286
+ if (response.status === 404) {
287
+ return { status: "NOT_FOUND" };
288
+ }
289
+ if (!response.ok) {
290
+ const errorText = await response.text();
291
+ throw new Error(
292
+ `Failed to check snapshot with status ${response.status}: ${errorText}`
293
+ );
294
+ }
295
+ const data = await response.json();
296
+ return data;
297
+ } catch (error) {
298
+ if (error.name === "AbortError") {
299
+ throw new Error(`Request timeout after ${this.timeout}ms`);
300
+ }
301
+ console.error(`Request failed: ${error.message}`);
302
+ throw error;
303
+ }
304
+ }
305
+ /**
306
+ * Create a new snapshot or get its status if it exists
307
+ * @param snapshotHash - SHA256 hash of the snapshot
308
+ * @param files - List of file information with 'file_path' and 'file_hash'
309
+ * @param jwt - JWT token for authorization (required)
310
+ * @returns Snapshot creation result or status
311
+ * @throws Error on HTTP errors or connection errors
312
+ */
313
+ async createSnapshot(snapshotHash, files, jwt) {
314
+ if (!jwt) {
315
+ throw new Error("JWT must be provided");
316
+ }
317
+ const url = `${this.apiBase}snapshots`;
318
+ try {
319
+ const controller = new AbortController2();
320
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
321
+ const response = await fetch_wrapper_default(url, {
322
+ method: "POST",
323
+ headers: {
324
+ Authorization: `Bearer ${jwt}`,
325
+ "Content-Type": "application/json"
326
+ },
327
+ body: JSON.stringify({
328
+ snapshot_hash: snapshotHash,
329
+ files
330
+ }),
331
+ signal: controller.signal
332
+ });
333
+ clearTimeout(timeoutId);
334
+ if (response.status === 422) {
335
+ const data2 = await response.json();
336
+ console.info(
337
+ `Snapshot ${snapshotHash.substring(0, 8)}... missing ${data2.missing_files?.length || 0} files`
338
+ );
339
+ return data2;
340
+ }
341
+ if (!response.ok) {
342
+ const errorText = await response.text();
343
+ throw new Error(
344
+ `Failed to create snapshot with status ${response.status}: ${errorText}`
345
+ );
346
+ }
347
+ const data = await response.json();
348
+ console.info(
349
+ `Snapshot ${snapshotHash.substring(0, 8)}... status: ${data.status}`
350
+ );
351
+ return data;
352
+ } catch (error) {
353
+ if (error.name === "AbortError") {
354
+ throw new Error(`Request timeout after ${this.timeout}ms`);
355
+ }
356
+ console.error(`Request failed: ${error.message}`);
357
+ throw error;
358
+ }
359
+ }
360
+ /**
361
+ * Upload file content to the service
362
+ * @param filesContent - Map of file_hash to object with 'path' and 'content'
363
+ * @param jwt - JWT token for authorization (required)
364
+ * @returns Upload result with counts
365
+ * @throws Error on HTTP errors or connection errors
366
+ */
367
+ async uploadFileContent(filesContent, jwt) {
368
+ if (!jwt) {
369
+ throw new Error("JWT must be provided");
370
+ }
371
+ const url = `${this.apiBase}files/content`;
372
+ try {
373
+ const controller = new AbortController2();
374
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
375
+ const formData = new FormData2();
376
+ for (const [fileHash, fileData] of filesContent.entries()) {
377
+ const content = typeof fileData.content === "string" ? Buffer.from(fileData.content) : fileData.content;
378
+ const blob = new Blob([content], { type: "application/octet-stream" });
379
+ formData.append(fileHash, blob, fileData.path);
380
+ }
381
+ const uploadHeaders = {
382
+ Authorization: `Bearer ${jwt}`
383
+ };
384
+ const response = await fetch_wrapper_default(url, {
385
+ method: "POST",
386
+ headers: uploadHeaders,
387
+ body: formData,
388
+ signal: controller.signal
389
+ });
390
+ clearTimeout(timeoutId);
391
+ if (!response.ok) {
392
+ const errorText = await response.text();
393
+ throw new Error(
394
+ `Failed to upload files with status ${response.status}: ${errorText}`
395
+ );
396
+ }
397
+ const data = await response.json();
398
+ console.info(
399
+ `Uploaded ${data.uploaded_count || 0} files, ${data.failed_count || 0} failed`
400
+ );
401
+ return data;
402
+ } catch (error) {
403
+ if (error.name === "AbortError") {
404
+ throw new Error(`Request timeout after ${this.timeout}ms`);
405
+ }
406
+ console.error(`Request failed: ${error.message}`);
407
+ throw error;
408
+ }
409
+ }
410
+ /**
411
+ * Calculate file hash from path and content
412
+ * @param filePath - Relative file path
413
+ * @param content - File content as Buffer or string
414
+ * @returns SHA256 hash of the file
415
+ */
416
+ static calculateFileHash(filePath, content) {
417
+ const contentBuffer = typeof content === "string" ? Buffer.from(content) : content;
418
+ const fileData = Buffer.concat([
419
+ Buffer.from(`${filePath}
420
+ `, "utf-8"),
421
+ contentBuffer
422
+ ]);
423
+ return crypto.createHash("sha256").update(fileData).digest("hex");
424
+ }
425
+ /**
426
+ * Calculate snapshot hash from file hashes
427
+ * @param fileHashes - Array of file SHA256 hashes
428
+ * @returns SHA256 hash of the snapshot
429
+ */
430
+ static calculateSnapshotHash(fileHashes) {
431
+ const snapshotData = fileHashes.sort().join("\n");
432
+ return crypto.createHash("sha256").update(snapshotData, "utf-8").digest("hex");
433
+ }
434
+ /**
435
+ * Check the health status of the Sync service
436
+ * @returns Health status information
437
+ * @throws Error on HTTP errors or connection errors
438
+ */
439
+ async health() {
440
+ const url = `${this.apiBase}health`;
441
+ try {
442
+ const controller = new AbortController2();
443
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
444
+ const response = await fetch_wrapper_default(url, {
445
+ method: "GET",
446
+ // No authentication required for health check
447
+ signal: controller.signal
448
+ });
449
+ clearTimeout(timeoutId);
450
+ if (!response.ok) {
451
+ const errorText = await response.text();
452
+ throw new Error(
453
+ `Health check failed with status ${response.status}: ${errorText}`
454
+ );
455
+ }
456
+ const data = await response.json();
457
+ return data;
458
+ } catch (error) {
459
+ if (error.name === "AbortError") {
460
+ throw new Error(`Request timeout after ${this.timeout}ms`);
461
+ }
462
+ console.error(`Request failed: ${error.message}`);
463
+ throw error;
464
+ }
465
+ }
466
+ /**
467
+ * Close the HTTP client connection (no-op for fetch)
468
+ */
469
+ close() {
470
+ }
471
+ };
472
+
473
+ // src/clients/retrieval-client.ts
474
+ init_esm_shims();
475
+ var RetrievalHttpClient = class {
476
+ /**
477
+ * Initialize the Retrieval HTTP client
478
+ * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
479
+ * @param timeout - Request timeout in milliseconds (default: 60000)
480
+ */
481
+ constructor(uri = "http://localhost:8004", timeout = 6e4) {
482
+ let processedUri = uri;
483
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
484
+ processedUri = `http://${uri}`;
485
+ }
486
+ const url = new URL(processedUri);
487
+ if (url.pathname && url.pathname !== "/") {
488
+ this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
489
+ } else {
490
+ this.baseUrl = `${url.protocol}//${url.host}`;
491
+ }
492
+ this.timeout = timeout;
493
+ if (this.baseUrl.endsWith("/")) {
494
+ this.apiBase = `${this.baseUrl}api/retrieval/`;
495
+ } else {
496
+ this.apiBase = `${this.baseUrl}/api/retrieval/`;
497
+ }
498
+ console.debug(`Initialized HTTP client for ${this.baseUrl}`);
499
+ console.debug(`API base: ${this.apiBase}`);
500
+ }
501
+ /**
502
+ * Check the health status of the Retrieval service
503
+ * @returns Health status information
504
+ * @throws Error on connection errors
505
+ */
506
+ async healthCheck() {
507
+ const healthEndpoint = `${this.apiBase}health`;
508
+ console.debug(`Checking server health: ${healthEndpoint}`);
509
+ try {
510
+ const controller = new AbortController2();
511
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
512
+ const response = await fetch_wrapper_default(healthEndpoint, {
513
+ method: "GET",
514
+ signal: controller.signal
515
+ });
516
+ clearTimeout(timeoutId);
517
+ if (!response.ok) {
518
+ const errorText = await response.text();
519
+ throw new Error(
520
+ `Health check failed with status ${response.status}: ${errorText}`
521
+ );
522
+ }
523
+ const healthInfo = await response.json();
524
+ console.debug(`HTTP retrieval server status:`, healthInfo);
525
+ return {
526
+ status: healthInfo.status || "UNKNOWN",
527
+ database: healthInfo.database || "UNKNOWN",
528
+ embedder: healthInfo.embedder || "UNKNOWN",
529
+ cache_size: healthInfo.cache_size || 0,
530
+ cache_ttl: healthInfo.cache_ttl || 0,
531
+ version: healthInfo.version || "unknown"
532
+ };
533
+ } catch (error) {
534
+ if (error.name === "AbortError") {
535
+ throw new Error(`Request timeout after ${this.timeout}ms`);
536
+ }
537
+ console.error(`Error checking HTTP server health: ${error.message}`);
538
+ throw new Error(
539
+ `Unable to connect to HTTP server ${this.baseUrl}: ${error.message}`
540
+ );
541
+ }
542
+ }
543
+ /**
544
+ * Execute a retrieval query on a snapshot
545
+ * @param snapshotHash - SHA256 hash of the codebase snapshot
546
+ * @param queryText - Natural language query for retrieval
547
+ * @param budgetTokens - Maximum token budget for results (default: 3000)
548
+ * @param jwt - JWT token for authorization (required)
549
+ * @param options - Optional retrieval parameters
550
+ * @returns Retrieval results with formatted output
551
+ * @throws Error on query failures
552
+ */
553
+ async query(snapshotHash, queryText, budgetTokens = 3e3, jwt, options) {
554
+ if (!snapshotHash) {
555
+ throw new Error("Snapshot hash must be provided");
556
+ }
557
+ if (!queryText) {
558
+ throw new Error("Query text must be provided");
559
+ }
560
+ if (!jwt) {
561
+ throw new Error("JWT must be provided");
562
+ }
563
+ if (budgetTokens < 100) {
564
+ throw new Error("Budget tokens must be at least 100");
565
+ }
566
+ const startTime = Date.now();
567
+ const queryEndpoint = `${this.apiBase}query`;
568
+ try {
569
+ const controller = new AbortController2();
570
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
571
+ const requestBody = {
572
+ snapshot_hash: snapshotHash,
573
+ query: queryText,
574
+ budget_tokens: budgetTokens
575
+ };
576
+ if (options) {
577
+ requestBody.options = options;
578
+ }
579
+ const response = await fetch_wrapper_default(queryEndpoint, {
580
+ method: "POST",
581
+ headers: {
582
+ "Content-Type": "application/json",
583
+ Authorization: `Bearer ${jwt}`
584
+ },
585
+ body: JSON.stringify(requestBody),
586
+ signal: controller.signal
587
+ });
588
+ clearTimeout(timeoutId);
589
+ if (response.status === 404) {
590
+ const errorData = await response.json();
591
+ throw new Error(
592
+ errorData.detail || "Snapshot not found or access denied"
593
+ );
594
+ }
595
+ if (!response.ok) {
596
+ const errorText = await response.text();
597
+ throw new Error(
598
+ `Query failed with status ${response.status}: ${errorText}`
599
+ );
600
+ }
601
+ const result = await response.json();
602
+ const elapsedTime = (Date.now() - startTime) / 1e3;
603
+ const formatter = options?.formatter || "standard";
604
+ console.debug(
605
+ `Retrieval query completed for snapshot ${snapshotHash.substring(0, 8)}... Formatter: ${formatter}. Total time: ${elapsedTime.toFixed(2)}s`
606
+ );
607
+ return result;
608
+ } catch (error) {
609
+ if (error.name === "AbortError") {
610
+ throw new Error(`Request timeout after ${this.timeout}ms`);
611
+ }
612
+ console.error(`Error executing retrieval query: ${error.message}`);
613
+ throw new Error(`Failed to execute retrieval query: ${error.message}`);
614
+ }
615
+ }
616
+ /**
617
+ * Check if a snapshot is indexed and ready for retrieval
618
+ * @param snapshotHash - SHA256 hash of the snapshot to check
619
+ * @param jwt - JWT token for authorization (required)
620
+ * @returns Snapshot status information
621
+ * @throws Error on status check failures
622
+ */
623
+ async checkSnapshotStatus(snapshotHash, jwt) {
624
+ if (!snapshotHash) {
625
+ throw new Error("Snapshot hash must be provided");
626
+ }
627
+ if (!jwt) {
628
+ throw new Error("JWT must be provided");
629
+ }
630
+ const statusEndpoint = `${this.apiBase}snapshots/${snapshotHash}/status`;
631
+ try {
632
+ const controller = new AbortController2();
633
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
634
+ const response = await fetch_wrapper_default(statusEndpoint, {
635
+ method: "GET",
636
+ headers: {
637
+ Authorization: `Bearer ${jwt}`
638
+ },
639
+ signal: controller.signal
640
+ });
641
+ clearTimeout(timeoutId);
642
+ if (response.status === 404) {
643
+ return {
644
+ snapshot_hash: snapshotHash,
645
+ status: "NOT_FOUND",
646
+ message: "Snapshot not found or access denied"
647
+ };
648
+ }
649
+ if (!response.ok) {
650
+ const errorText = await response.text();
651
+ throw new Error(
652
+ `Status check failed with status ${response.status}: ${errorText}`
653
+ );
654
+ }
655
+ const statusInfo = await response.json();
656
+ console.debug(
657
+ `Snapshot ${snapshotHash.substring(0, 8)}... status: ${statusInfo.status}`
658
+ );
659
+ return statusInfo;
660
+ } catch (error) {
661
+ if (error.name === "AbortError") {
662
+ throw new Error(`Request timeout after ${this.timeout}ms`);
663
+ }
664
+ console.error(`Error checking snapshot status: ${error.message}`);
665
+ throw new Error(`Failed to check snapshot status: ${error.message}`);
666
+ }
667
+ }
668
+ /**
669
+ * Clear the graph cache (admin operation)
670
+ * @param jwt - JWT token for authorization (required)
671
+ * @returns true if cache cleared successfully
672
+ * @throws Error on cache clear failures
673
+ */
674
+ async clearCache(jwt) {
675
+ if (!jwt) {
676
+ throw new Error("JWT must be provided");
677
+ }
678
+ const cacheEndpoint = `${this.apiBase}cache`;
679
+ try {
680
+ const controller = new AbortController2();
681
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
682
+ const response = await fetch_wrapper_default(cacheEndpoint, {
683
+ method: "DELETE",
684
+ headers: {
685
+ Authorization: `Bearer ${jwt}`
686
+ },
687
+ signal: controller.signal
688
+ });
689
+ clearTimeout(timeoutId);
690
+ if (!response.ok) {
691
+ const errorText = await response.text();
692
+ throw new Error(
693
+ `Cache clear failed with status ${response.status}: ${errorText}`
694
+ );
695
+ }
696
+ console.info("Graph cache cleared successfully");
697
+ return true;
698
+ } catch (error) {
699
+ if (error.name === "AbortError") {
700
+ throw new Error(`Request timeout after ${this.timeout}ms`);
701
+ }
702
+ console.error(`Error clearing cache: ${error.message}`);
703
+ throw new Error(`Failed to clear cache: ${error.message}`);
704
+ }
705
+ }
706
+ /**
707
+ * Get cache statistics
708
+ * @param jwt - JWT token for authorization (required)
709
+ * @returns Cache statistics
710
+ * @throws Error on stats retrieval failures
711
+ */
712
+ async getCacheStats(jwt) {
713
+ if (!jwt) {
714
+ throw new Error("JWT must be provided");
715
+ }
716
+ const statsEndpoint = `${this.apiBase}cache/stats`;
717
+ try {
718
+ const controller = new AbortController2();
719
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
720
+ const response = await fetch_wrapper_default(statsEndpoint, {
721
+ method: "GET",
722
+ headers: {
723
+ Authorization: `Bearer ${jwt}`
724
+ },
725
+ signal: controller.signal
726
+ });
727
+ clearTimeout(timeoutId);
728
+ if (!response.ok) {
729
+ const errorText = await response.text();
730
+ throw new Error(
731
+ `Stats retrieval failed with status ${response.status}: ${errorText}`
732
+ );
733
+ }
734
+ const stats = await response.json();
735
+ console.debug(
736
+ `Cache stats: ${stats.cached_snapshots || 0} snapshots cached`
737
+ );
738
+ return stats;
739
+ } catch (error) {
740
+ if (error.name === "AbortError") {
741
+ throw new Error(`Request timeout after ${this.timeout}ms`);
742
+ }
743
+ console.error(`Error getting cache stats: ${error.message}`);
744
+ throw new Error(`Failed to get cache stats: ${error.message}`);
745
+ }
746
+ }
747
+ /**
748
+ * Convenience method for querying with specific options
749
+ * @param snapshotHash - SHA256 hash of the codebase snapshot
750
+ * @param queryText - Natural language query for retrieval
751
+ * @param jwt - JWT token for authorization (required)
752
+ * @param budgetTokens - Maximum token budget for results
753
+ * @param flowStrength - Flow intensity multiplier
754
+ * @param blendAlpha - Blending factor for scores
755
+ * @param hopDepth - Depth of local neighborhood
756
+ * @param maxIterations - Maximum flood-walk iterations
757
+ * @param split - Fraction of budget for full chunks
758
+ * @param formatter - Output format "standard" or "compact"
759
+ * @returns Retrieval results
760
+ */
761
+ async queryWithOptions(snapshotHash, queryText, jwt, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
762
+ const options = {
763
+ flow_strength: flowStrength,
764
+ blend_alpha: blendAlpha,
765
+ hop_depth: hopDepth,
766
+ max_iterations: maxIterations,
767
+ split,
768
+ formatter
769
+ };
770
+ return this.query(snapshotHash, queryText, budgetTokens, jwt, options);
771
+ }
772
+ /**
773
+ * Close the HTTP client connection (no-op for fetch)
774
+ */
775
+ close() {
776
+ }
777
+ };
778
+
779
+ // src/clients/ast-client.ts
780
+ init_esm_shims();
781
+ var ASTHttpClient = class {
782
+ /**
783
+ * Initialize the AST HTTP client
784
+ * @param uri - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8003")
785
+ * @param timeout - Request timeout in milliseconds (default: 60000)
786
+ */
787
+ constructor(uri = "http://localhost:8003", timeout = 6e4) {
788
+ let processedUri = uri;
789
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
790
+ processedUri = `http://${uri}`;
791
+ }
792
+ const url = new URL(processedUri);
793
+ if (url.pathname && url.pathname !== "/") {
794
+ this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
795
+ } else {
796
+ this.baseUrl = `${url.protocol}//${url.host}`;
797
+ }
798
+ this.timeout = timeout;
799
+ if (this.baseUrl.endsWith("/")) {
800
+ this.apiBase = `${this.baseUrl}api/ast/`;
801
+ } else {
802
+ this.apiBase = `${this.baseUrl}/api/ast/`;
803
+ }
804
+ }
805
+ /**
806
+ * Get file visitor rules in v2 format optimized for Chokidar v4
807
+ * @param jwt - JWT token for authorization (required)
808
+ * @returns Visitor rules with format, include_extensions, include_filenames, exclude_dirnames
809
+ * @throws Error on HTTP errors or connection errors
810
+ */
811
+ async getVisitorRulesV2(jwt) {
812
+ if (!jwt) {
813
+ throw new Error("JWT must be provided");
814
+ }
815
+ const url = `${this.apiBase}visitor-rules`;
816
+ try {
817
+ const controller = new AbortController2();
818
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
819
+ const response = await fetch_wrapper_default(url, {
820
+ method: "GET",
821
+ headers: {
822
+ Authorization: `Bearer ${jwt}`
823
+ },
824
+ signal: controller.signal
825
+ });
826
+ clearTimeout(timeoutId);
827
+ if (!response.ok) {
828
+ const errorText = await response.text();
829
+ throw new Error(
830
+ `Failed to get visitor rules v2 with status ${response.status}: ${errorText}`
831
+ );
832
+ }
833
+ const data = await response.json();
834
+ if (data.format !== "segments+exts@v2") {
835
+ throw new Error(`Unexpected visitor rules format: ${data.format}`);
836
+ }
837
+ return data;
838
+ } catch (error) {
839
+ if (error.name === "AbortError") {
840
+ throw new Error(`Request timeout after ${this.timeout}ms`);
841
+ }
842
+ console.error(`Failed to get visitor rules v2: ${error.message}`);
843
+ throw error;
844
+ }
845
+ }
846
+ /**
847
+ * Check the health status of the AST service
848
+ * @returns Health status information
849
+ * @throws Error on HTTP errors or connection errors
850
+ */
851
+ async health() {
852
+ const url = `${this.apiBase}health`;
853
+ try {
854
+ const controller = new AbortController2();
855
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
856
+ const response = await fetch_wrapper_default(url, {
857
+ method: "GET",
858
+ // No authentication required for health check
859
+ signal: controller.signal
860
+ });
861
+ clearTimeout(timeoutId);
862
+ if (!response.ok) {
863
+ const errorText = await response.text();
864
+ throw new Error(
865
+ `Health check failed with status ${response.status}: ${errorText}`
866
+ );
867
+ }
868
+ const data = await response.json();
869
+ return data;
870
+ } catch (error) {
871
+ if (error.name === "AbortError") {
872
+ throw new Error(`Request timeout after ${this.timeout}ms`);
873
+ }
874
+ console.error(`Health check failed: ${error.message}`);
875
+ throw error;
876
+ }
877
+ }
878
+ /**
879
+ * Compile visitor rules v2 for use with Chokidar v4
880
+ * This is a helper method that compiles the rules into a format
881
+ * that can be directly used with Chokidar's ignored option
882
+ * @param rules - The visitor rules v2 from the server
883
+ * @returns Compiled rules with Sets and RegExp for efficient matching
884
+ */
885
+ static compileRulesV2(rules) {
886
+ if (rules.format !== "segments+exts@v2") {
887
+ throw new Error(`Unsupported rules format: ${rules.format}`);
888
+ }
889
+ const exts = new Set(rules.include_extensions.map((e) => e.toLowerCase()));
890
+ const names = new Set(rules.include_filenames.map((n) => n.toLowerCase()));
891
+ const dirs = new Set(rules.exclude_dirnames.map((n) => n.toLowerCase()));
892
+ const escapeRe = (s) => s.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
893
+ const dirRe = new RegExp(
894
+ `(?:^|[\\/])(?:${[...dirs].map(escapeRe).join("|")})(?:[\\/]|$)`,
895
+ "i"
896
+ );
897
+ return { exts, names, dirRe };
898
+ }
899
+ /**
900
+ * Build Chokidar v4 `ignored` predicate from compiled rules
901
+ * @param compiled - Compiled rules from compileRulesV2
902
+ * @returns Predicate function that returns true to ignore, false to watch
903
+ */
904
+ static buildIgnoredPredicate(compiled) {
905
+ return (p, stats) => {
906
+ const posix = p.replace(/\\/g, "/");
907
+ if (compiled.dirRe.test(posix)) return true;
908
+ if (stats?.isDirectory?.()) return false;
909
+ const base = posix.split("/").pop()?.toLowerCase() || "";
910
+ if (compiled.names.has(base)) return false;
911
+ const extIndex = base.lastIndexOf(".");
912
+ const ext = extIndex > -1 ? base.slice(extIndex).toLowerCase() : "";
913
+ if (!compiled.exts.has(ext)) return true;
914
+ return false;
915
+ };
916
+ }
917
+ /**
918
+ * Close the HTTP client connection (no-op for fetch)
919
+ */
920
+ close() {
921
+ }
922
+ };
923
+
924
+ export { ASTHttpClient, AuthHttpClient, RetrievalHttpClient, SyncHttpClient, decodeJWT, fetch_wrapper_default as fetch };
925
+ //# sourceMappingURL=index.js.map
16
926
  //# sourceMappingURL=index.js.map