@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 +173 -96
- package/dist/index.cjs +485 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +123 -10
- package/dist/index.d.ts +123 -10
- package/dist/index.js +483 -45
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -237,18 +237,41 @@ init_esm_shims();
|
|
|
237
237
|
var SyncHttpClient = class {
|
|
238
238
|
/**
|
|
239
239
|
* Initialize the Sync HTTP client
|
|
240
|
-
* @param baseUrl -
|
|
241
|
-
* @param
|
|
242
|
-
* @param
|
|
240
|
+
* @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8002")
|
|
241
|
+
* @param config.timeout - Request timeout in milliseconds (default: 60000)
|
|
242
|
+
* @param config.jwtProvider - Provider for obtaining JWT tokens
|
|
243
243
|
*/
|
|
244
|
-
constructor(
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
constructor({
|
|
245
|
+
baseUrl = "http://localhost:8002",
|
|
246
|
+
timeout = 6e4,
|
|
247
|
+
jwtProvider
|
|
248
|
+
}) {
|
|
249
|
+
if (!jwtProvider) {
|
|
250
|
+
throw new Error("SyncHttpClient requires a JWT provider");
|
|
251
|
+
}
|
|
247
252
|
this.timeout = timeout;
|
|
248
|
-
this.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
253
|
+
this.jwtProvider = jwtProvider;
|
|
254
|
+
this.configureBase(baseUrl);
|
|
255
|
+
}
|
|
256
|
+
updateBaseUrl(baseUrl) {
|
|
257
|
+
this.configureBase(baseUrl);
|
|
258
|
+
}
|
|
259
|
+
configureBase(uri) {
|
|
260
|
+
let processedUri = uri;
|
|
261
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
262
|
+
processedUri = `http://${uri}`;
|
|
263
|
+
}
|
|
264
|
+
const url = new URL(processedUri);
|
|
265
|
+
if (url.pathname && url.pathname !== "/") {
|
|
266
|
+
this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
|
|
267
|
+
} else {
|
|
268
|
+
this.baseUrl = `${url.protocol}//${url.host}`;
|
|
269
|
+
}
|
|
270
|
+
if (this.baseUrl.endsWith("/")) {
|
|
271
|
+
this.apiBase = `${this.baseUrl}sync/v1/`;
|
|
272
|
+
} else {
|
|
273
|
+
this.apiBase = `${this.baseUrl}/sync/v1/`;
|
|
274
|
+
}
|
|
252
275
|
}
|
|
253
276
|
/**
|
|
254
277
|
* Check the status of a snapshot
|
|
@@ -257,13 +280,20 @@ var SyncHttpClient = class {
|
|
|
257
280
|
* @throws Error on HTTP errors or connection errors
|
|
258
281
|
*/
|
|
259
282
|
async checkSnapshotStatus(snapshotHash) {
|
|
260
|
-
|
|
283
|
+
if (!snapshotHash) {
|
|
284
|
+
throw new Error("Snapshot hash must be provided");
|
|
285
|
+
}
|
|
286
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
287
|
+
const url = `${this.apiBase}snapshots`;
|
|
261
288
|
try {
|
|
262
289
|
const controller = new AbortController2();
|
|
263
290
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
264
291
|
const response = await fetch_wrapper_default(url, {
|
|
265
292
|
method: "POST",
|
|
266
|
-
headers:
|
|
293
|
+
headers: {
|
|
294
|
+
Authorization: `Bearer ${jwt}`,
|
|
295
|
+
"Content-Type": "application/json"
|
|
296
|
+
},
|
|
267
297
|
body: JSON.stringify({ snapshot_hash: snapshotHash }),
|
|
268
298
|
signal: controller.signal
|
|
269
299
|
});
|
|
@@ -295,13 +325,20 @@ var SyncHttpClient = class {
|
|
|
295
325
|
* @throws Error on HTTP errors or connection errors
|
|
296
326
|
*/
|
|
297
327
|
async createSnapshot(snapshotHash, files) {
|
|
298
|
-
|
|
328
|
+
if (!snapshotHash) {
|
|
329
|
+
throw new Error("Snapshot hash must be provided");
|
|
330
|
+
}
|
|
331
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
332
|
+
const url = `${this.apiBase}snapshots`;
|
|
299
333
|
try {
|
|
300
334
|
const controller = new AbortController2();
|
|
301
335
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
302
336
|
const response = await fetch_wrapper_default(url, {
|
|
303
337
|
method: "POST",
|
|
304
|
-
headers:
|
|
338
|
+
headers: {
|
|
339
|
+
Authorization: `Bearer ${jwt}`,
|
|
340
|
+
"Content-Type": "application/json"
|
|
341
|
+
},
|
|
305
342
|
body: JSON.stringify({
|
|
306
343
|
snapshot_hash: snapshotHash,
|
|
307
344
|
files
|
|
@@ -342,7 +379,8 @@ var SyncHttpClient = class {
|
|
|
342
379
|
* @throws Error on HTTP errors or connection errors
|
|
343
380
|
*/
|
|
344
381
|
async uploadFileContent(filesContent) {
|
|
345
|
-
const
|
|
382
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
383
|
+
const url = `${this.apiBase}files/content`;
|
|
346
384
|
try {
|
|
347
385
|
const controller = new AbortController2();
|
|
348
386
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
@@ -352,8 +390,9 @@ var SyncHttpClient = class {
|
|
|
352
390
|
const blob = new Blob([content], { type: "application/octet-stream" });
|
|
353
391
|
formData.append(fileHash, blob, fileData.path);
|
|
354
392
|
}
|
|
355
|
-
const uploadHeaders = {
|
|
356
|
-
|
|
393
|
+
const uploadHeaders = {
|
|
394
|
+
Authorization: `Bearer ${jwt}`
|
|
395
|
+
};
|
|
357
396
|
const response = await fetch_wrapper_default(url, {
|
|
358
397
|
method: "POST",
|
|
359
398
|
headers: uploadHeaders,
|
|
@@ -410,13 +449,13 @@ var SyncHttpClient = class {
|
|
|
410
449
|
* @throws Error on HTTP errors or connection errors
|
|
411
450
|
*/
|
|
412
451
|
async health() {
|
|
413
|
-
const url = `${this.
|
|
452
|
+
const url = `${this.apiBase}health`;
|
|
414
453
|
try {
|
|
415
454
|
const controller = new AbortController2();
|
|
416
455
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
417
456
|
const response = await fetch_wrapper_default(url, {
|
|
418
457
|
method: "GET",
|
|
419
|
-
|
|
458
|
+
// No authentication required for health check
|
|
420
459
|
signal: controller.signal
|
|
421
460
|
});
|
|
422
461
|
clearTimeout(timeoutId);
|
|
@@ -448,10 +487,28 @@ init_esm_shims();
|
|
|
448
487
|
var RetrievalHttpClient = class {
|
|
449
488
|
/**
|
|
450
489
|
* Initialize the Retrieval HTTP client
|
|
451
|
-
* @param
|
|
452
|
-
* @param timeout - Request timeout in milliseconds (default: 60000)
|
|
490
|
+
* @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
|
|
491
|
+
* @param config.timeout - Request timeout in milliseconds (default: 60000)
|
|
492
|
+
* @param config.jwtProvider - Provider for obtaining JWT tokens
|
|
453
493
|
*/
|
|
454
|
-
constructor(
|
|
494
|
+
constructor({
|
|
495
|
+
baseUrl = "http://localhost:8004",
|
|
496
|
+
timeout = 6e4,
|
|
497
|
+
jwtProvider
|
|
498
|
+
}) {
|
|
499
|
+
if (!jwtProvider) {
|
|
500
|
+
throw new Error("RetrievalHttpClient requires a JWT provider");
|
|
501
|
+
}
|
|
502
|
+
this.timeout = timeout;
|
|
503
|
+
this.jwtProvider = jwtProvider;
|
|
504
|
+
this.configureBase(baseUrl);
|
|
505
|
+
console.debug(`Initialized HTTP client for ${this.baseUrl}`);
|
|
506
|
+
console.debug(`API base: ${this.apiBase}`);
|
|
507
|
+
}
|
|
508
|
+
updateBaseUrl(baseUrl) {
|
|
509
|
+
this.configureBase(baseUrl);
|
|
510
|
+
}
|
|
511
|
+
configureBase(uri) {
|
|
455
512
|
let processedUri = uri;
|
|
456
513
|
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
457
514
|
processedUri = `http://${uri}`;
|
|
@@ -462,14 +519,11 @@ var RetrievalHttpClient = class {
|
|
|
462
519
|
} else {
|
|
463
520
|
this.baseUrl = `${url.protocol}//${url.host}`;
|
|
464
521
|
}
|
|
465
|
-
this.timeout = timeout;
|
|
466
522
|
if (this.baseUrl.endsWith("/")) {
|
|
467
523
|
this.apiBase = `${this.baseUrl}api/retrieval/`;
|
|
468
524
|
} else {
|
|
469
525
|
this.apiBase = `${this.baseUrl}/api/retrieval/`;
|
|
470
526
|
}
|
|
471
|
-
console.debug(`Initialized HTTP client for ${this.baseUrl}`);
|
|
472
|
-
console.debug(`API base: ${this.apiBase}`);
|
|
473
527
|
}
|
|
474
528
|
/**
|
|
475
529
|
* Check the health status of the Retrieval service
|
|
@@ -518,25 +572,22 @@ var RetrievalHttpClient = class {
|
|
|
518
572
|
* @param snapshotHash - SHA256 hash of the codebase snapshot
|
|
519
573
|
* @param queryText - Natural language query for retrieval
|
|
520
574
|
* @param budgetTokens - Maximum token budget for results (default: 3000)
|
|
521
|
-
* @param jwt - JWT token for authorization (required)
|
|
522
575
|
* @param options - Optional retrieval parameters
|
|
523
576
|
* @returns Retrieval results with formatted output
|
|
524
577
|
* @throws Error on query failures
|
|
525
578
|
*/
|
|
526
|
-
async query(snapshotHash, queryText, budgetTokens = 3e3,
|
|
579
|
+
async query(snapshotHash, queryText, budgetTokens = 3e3, options) {
|
|
527
580
|
if (!snapshotHash) {
|
|
528
581
|
throw new Error("Snapshot hash must be provided");
|
|
529
582
|
}
|
|
530
583
|
if (!queryText) {
|
|
531
584
|
throw new Error("Query text must be provided");
|
|
532
585
|
}
|
|
533
|
-
if (!jwt) {
|
|
534
|
-
throw new Error("JWT must be provided");
|
|
535
|
-
}
|
|
536
586
|
if (budgetTokens < 100) {
|
|
537
587
|
throw new Error("Budget tokens must be at least 100");
|
|
538
588
|
}
|
|
539
589
|
const startTime = Date.now();
|
|
590
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
540
591
|
const queryEndpoint = `${this.apiBase}query`;
|
|
541
592
|
try {
|
|
542
593
|
const controller = new AbortController2();
|
|
@@ -593,13 +644,11 @@ var RetrievalHttpClient = class {
|
|
|
593
644
|
* @returns Snapshot status information
|
|
594
645
|
* @throws Error on status check failures
|
|
595
646
|
*/
|
|
596
|
-
async checkSnapshotStatus(snapshotHash
|
|
647
|
+
async checkSnapshotStatus(snapshotHash) {
|
|
597
648
|
if (!snapshotHash) {
|
|
598
649
|
throw new Error("Snapshot hash must be provided");
|
|
599
650
|
}
|
|
600
|
-
|
|
601
|
-
throw new Error("JWT must be provided");
|
|
602
|
-
}
|
|
651
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
603
652
|
const statusEndpoint = `${this.apiBase}snapshots/${snapshotHash}/status`;
|
|
604
653
|
try {
|
|
605
654
|
const controller = new AbortController2();
|
|
@@ -644,10 +693,8 @@ var RetrievalHttpClient = class {
|
|
|
644
693
|
* @returns true if cache cleared successfully
|
|
645
694
|
* @throws Error on cache clear failures
|
|
646
695
|
*/
|
|
647
|
-
async clearCache(
|
|
648
|
-
|
|
649
|
-
throw new Error("JWT must be provided");
|
|
650
|
-
}
|
|
696
|
+
async clearCache() {
|
|
697
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
651
698
|
const cacheEndpoint = `${this.apiBase}cache`;
|
|
652
699
|
try {
|
|
653
700
|
const controller = new AbortController2();
|
|
@@ -682,10 +729,8 @@ var RetrievalHttpClient = class {
|
|
|
682
729
|
* @returns Cache statistics
|
|
683
730
|
* @throws Error on stats retrieval failures
|
|
684
731
|
*/
|
|
685
|
-
async getCacheStats(
|
|
686
|
-
|
|
687
|
-
throw new Error("JWT must be provided");
|
|
688
|
-
}
|
|
732
|
+
async getCacheStats() {
|
|
733
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
689
734
|
const statsEndpoint = `${this.apiBase}cache/stats`;
|
|
690
735
|
try {
|
|
691
736
|
const controller = new AbortController2();
|
|
@@ -731,7 +776,7 @@ var RetrievalHttpClient = class {
|
|
|
731
776
|
* @param formatter - Output format "standard" or "compact"
|
|
732
777
|
* @returns Retrieval results
|
|
733
778
|
*/
|
|
734
|
-
async queryWithOptions(snapshotHash, queryText,
|
|
779
|
+
async queryWithOptions(snapshotHash, queryText, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
|
|
735
780
|
const options = {
|
|
736
781
|
flow_strength: flowStrength,
|
|
737
782
|
blend_alpha: blendAlpha,
|
|
@@ -740,7 +785,164 @@ var RetrievalHttpClient = class {
|
|
|
740
785
|
split,
|
|
741
786
|
formatter
|
|
742
787
|
};
|
|
743
|
-
return this.query(snapshotHash, queryText, budgetTokens,
|
|
788
|
+
return this.query(snapshotHash, queryText, budgetTokens, options);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Close the HTTP client connection (no-op for fetch)
|
|
792
|
+
*/
|
|
793
|
+
close() {
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
// src/clients/ast-client.ts
|
|
798
|
+
init_esm_shims();
|
|
799
|
+
var ASTHttpClient = class {
|
|
800
|
+
/**
|
|
801
|
+
* Initialize the AST HTTP client
|
|
802
|
+
* @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8003")
|
|
803
|
+
* @param config.timeout - Request timeout in milliseconds (default: 60000)
|
|
804
|
+
* @param config.jwtProvider - Provider for obtaining JWT tokens
|
|
805
|
+
*/
|
|
806
|
+
constructor({
|
|
807
|
+
baseUrl = "http://localhost:8003",
|
|
808
|
+
timeout = 6e4,
|
|
809
|
+
jwtProvider
|
|
810
|
+
}) {
|
|
811
|
+
if (!jwtProvider) {
|
|
812
|
+
throw new Error("ASTHttpClient requires a JWT provider");
|
|
813
|
+
}
|
|
814
|
+
this.timeout = timeout;
|
|
815
|
+
this.jwtProvider = jwtProvider;
|
|
816
|
+
this.configureBase(baseUrl);
|
|
817
|
+
}
|
|
818
|
+
updateBaseUrl(baseUrl) {
|
|
819
|
+
this.configureBase(baseUrl);
|
|
820
|
+
}
|
|
821
|
+
configureBase(uri) {
|
|
822
|
+
let processedUri = uri;
|
|
823
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
824
|
+
processedUri = `http://${uri}`;
|
|
825
|
+
}
|
|
826
|
+
const url = new URL(processedUri);
|
|
827
|
+
if (url.pathname && url.pathname !== "/") {
|
|
828
|
+
this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
|
|
829
|
+
} else {
|
|
830
|
+
this.baseUrl = `${url.protocol}//${url.host}`;
|
|
831
|
+
}
|
|
832
|
+
if (this.baseUrl.endsWith("/")) {
|
|
833
|
+
this.apiBase = `${this.baseUrl}api/ast/`;
|
|
834
|
+
} else {
|
|
835
|
+
this.apiBase = `${this.baseUrl}/api/ast/`;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Get file visitor rules in v2 format optimized for Chokidar v4
|
|
840
|
+
* @returns Visitor rules with format, include_extensions, include_filenames, exclude_dirnames
|
|
841
|
+
* @throws Error on HTTP errors or connection errors
|
|
842
|
+
*/
|
|
843
|
+
async getVisitorRulesV2() {
|
|
844
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
845
|
+
const url = `${this.apiBase}visitor-rules`;
|
|
846
|
+
try {
|
|
847
|
+
const controller = new AbortController2();
|
|
848
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
849
|
+
const response = await fetch_wrapper_default(url, {
|
|
850
|
+
method: "GET",
|
|
851
|
+
headers: {
|
|
852
|
+
Authorization: `Bearer ${jwt}`
|
|
853
|
+
},
|
|
854
|
+
signal: controller.signal
|
|
855
|
+
});
|
|
856
|
+
clearTimeout(timeoutId);
|
|
857
|
+
if (!response.ok) {
|
|
858
|
+
const errorText = await response.text();
|
|
859
|
+
throw new Error(
|
|
860
|
+
`Failed to get visitor rules v2 with status ${response.status}: ${errorText}`
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
const data = await response.json();
|
|
864
|
+
if (data.format !== "segments+exts@v2") {
|
|
865
|
+
throw new Error(`Unexpected visitor rules format: ${data.format}`);
|
|
866
|
+
}
|
|
867
|
+
return data;
|
|
868
|
+
} catch (error) {
|
|
869
|
+
if (error.name === "AbortError") {
|
|
870
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
871
|
+
}
|
|
872
|
+
console.error(`Failed to get visitor rules v2: ${error.message}`);
|
|
873
|
+
throw error;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Check the health status of the AST service
|
|
878
|
+
* @returns Health status information
|
|
879
|
+
* @throws Error on HTTP errors or connection errors
|
|
880
|
+
*/
|
|
881
|
+
async health() {
|
|
882
|
+
const url = `${this.apiBase}health`;
|
|
883
|
+
try {
|
|
884
|
+
const controller = new AbortController2();
|
|
885
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
886
|
+
const response = await fetch_wrapper_default(url, {
|
|
887
|
+
method: "GET",
|
|
888
|
+
// No authentication required for health check
|
|
889
|
+
signal: controller.signal
|
|
890
|
+
});
|
|
891
|
+
clearTimeout(timeoutId);
|
|
892
|
+
if (!response.ok) {
|
|
893
|
+
const errorText = await response.text();
|
|
894
|
+
throw new Error(
|
|
895
|
+
`Health check failed with status ${response.status}: ${errorText}`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
const data = await response.json();
|
|
899
|
+
return data;
|
|
900
|
+
} catch (error) {
|
|
901
|
+
if (error.name === "AbortError") {
|
|
902
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
903
|
+
}
|
|
904
|
+
console.error(`Health check failed: ${error.message}`);
|
|
905
|
+
throw error;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Compile visitor rules v2 for use with Chokidar v4
|
|
910
|
+
* This is a helper method that compiles the rules into a format
|
|
911
|
+
* that can be directly used with Chokidar's ignored option
|
|
912
|
+
* @param rules - The visitor rules v2 from the server
|
|
913
|
+
* @returns Compiled rules with Sets and RegExp for efficient matching
|
|
914
|
+
*/
|
|
915
|
+
static compileRulesV2(rules) {
|
|
916
|
+
if (rules.format !== "segments+exts@v2") {
|
|
917
|
+
throw new Error(`Unsupported rules format: ${rules.format}`);
|
|
918
|
+
}
|
|
919
|
+
const exts = new Set(rules.include_extensions.map((e) => e.toLowerCase()));
|
|
920
|
+
const names = new Set(rules.include_filenames.map((n) => n.toLowerCase()));
|
|
921
|
+
const dirs = new Set(rules.exclude_dirnames.map((n) => n.toLowerCase()));
|
|
922
|
+
const escapeRe = (s) => s.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
|
|
923
|
+
const dirRe = new RegExp(
|
|
924
|
+
`(?:^|[\\/])(?:${[...dirs].map(escapeRe).join("|")})(?:[\\/]|$)`,
|
|
925
|
+
"i"
|
|
926
|
+
);
|
|
927
|
+
return { exts, names, dirRe };
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Build Chokidar v4 `ignored` predicate from compiled rules
|
|
931
|
+
* @param compiled - Compiled rules from compileRulesV2
|
|
932
|
+
* @returns Predicate function that returns true to ignore, false to watch
|
|
933
|
+
*/
|
|
934
|
+
static buildIgnoredPredicate(compiled) {
|
|
935
|
+
return (p, stats) => {
|
|
936
|
+
const posix = p.replace(/\\/g, "/");
|
|
937
|
+
if (compiled.dirRe.test(posix)) return true;
|
|
938
|
+
if (stats?.isDirectory?.()) return false;
|
|
939
|
+
const base = posix.split("/").pop()?.toLowerCase() || "";
|
|
940
|
+
if (compiled.names.has(base)) return false;
|
|
941
|
+
const extIndex = base.lastIndexOf(".");
|
|
942
|
+
const ext = extIndex > -1 ? base.slice(extIndex).toLowerCase() : "";
|
|
943
|
+
if (!compiled.exts.has(ext)) return true;
|
|
944
|
+
return false;
|
|
945
|
+
};
|
|
744
946
|
}
|
|
745
947
|
/**
|
|
746
948
|
* Close the HTTP client connection (no-op for fetch)
|
|
@@ -749,6 +951,242 @@ var RetrievalHttpClient = class {
|
|
|
749
951
|
}
|
|
750
952
|
};
|
|
751
953
|
|
|
752
|
-
|
|
954
|
+
// src/utils/jwt-factory.ts
|
|
955
|
+
init_esm_shims();
|
|
956
|
+
var DEFAULT_MIN_TTL_MS = 5e3;
|
|
957
|
+
var JWTFactory = class {
|
|
958
|
+
constructor(authClient, sourceToken, options = {}) {
|
|
959
|
+
this.authClient = authClient;
|
|
960
|
+
this.sourceToken = sourceToken;
|
|
961
|
+
if (!sourceToken) {
|
|
962
|
+
throw new Error("JWTFactory requires a non-empty source token");
|
|
963
|
+
}
|
|
964
|
+
this.minTtlMs = options.minTtlMs ?? DEFAULT_MIN_TTL_MS;
|
|
965
|
+
this.onTokenRefreshed = options.onTokenRefreshed;
|
|
966
|
+
}
|
|
967
|
+
setSourceToken(token) {
|
|
968
|
+
if (!token) {
|
|
969
|
+
throw new Error("JWTFactory requires a non-empty source token");
|
|
970
|
+
}
|
|
971
|
+
if (token !== this.sourceToken) {
|
|
972
|
+
this.sourceToken = token;
|
|
973
|
+
this.cache = void 0;
|
|
974
|
+
this.currentServerUrl = void 0;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
invalidate() {
|
|
978
|
+
this.cache = void 0;
|
|
979
|
+
this.currentServerUrl = void 0;
|
|
980
|
+
}
|
|
981
|
+
async getJWT(forceRefresh = false) {
|
|
982
|
+
const now = Date.now();
|
|
983
|
+
if (!forceRefresh && this.cache) {
|
|
984
|
+
if (now < this.cache.expiresAt) {
|
|
985
|
+
if (now < this.cache.refreshAt) {
|
|
986
|
+
return this.cache.token;
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
this.cache = void 0;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return this.refresh(now, forceRefresh);
|
|
993
|
+
}
|
|
994
|
+
async refresh(now, forceRefresh) {
|
|
995
|
+
if (!this.refreshPromise) {
|
|
996
|
+
this.refreshPromise = this.fetchNewToken().finally(() => {
|
|
997
|
+
this.refreshPromise = void 0;
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
return await this.refreshPromise;
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
if (!forceRefresh && this.cache && now < this.cache.expiresAt) {
|
|
1004
|
+
console.warn(
|
|
1005
|
+
"Failed to refresh JWT, using cached token until expiry. Reason:",
|
|
1006
|
+
error
|
|
1007
|
+
);
|
|
1008
|
+
return this.cache.token;
|
|
1009
|
+
}
|
|
1010
|
+
throw error;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
async fetchNewToken() {
|
|
1014
|
+
const response = await this.authClient.authenticate(this.sourceToken);
|
|
1015
|
+
const fetchedAt = Date.now();
|
|
1016
|
+
const expiresAt = this.resolveExpiryMs(
|
|
1017
|
+
response.jwt,
|
|
1018
|
+
response.expires_at,
|
|
1019
|
+
fetchedAt
|
|
1020
|
+
);
|
|
1021
|
+
const payload = decodeJWT(response.jwt);
|
|
1022
|
+
const serverUrl = this.extractServerUrl(payload);
|
|
1023
|
+
const halfLife = Math.max((expiresAt - fetchedAt) / 2, this.minTtlMs);
|
|
1024
|
+
const refreshAt = fetchedAt + halfLife;
|
|
1025
|
+
this.cache = {
|
|
1026
|
+
token: response.jwt,
|
|
1027
|
+
expiresAt,
|
|
1028
|
+
refreshAt,
|
|
1029
|
+
fetchedAt,
|
|
1030
|
+
serverUrl
|
|
1031
|
+
};
|
|
1032
|
+
this.currentServerUrl = serverUrl;
|
|
1033
|
+
this.emitTokenRefreshed({
|
|
1034
|
+
token: response.jwt,
|
|
1035
|
+
expiresAt,
|
|
1036
|
+
serverUrl
|
|
1037
|
+
});
|
|
1038
|
+
return response.jwt;
|
|
1039
|
+
}
|
|
1040
|
+
resolveExpiryMs(jwt, expiresAt, referenceTime) {
|
|
1041
|
+
if (expiresAt) {
|
|
1042
|
+
const parsed = Date.parse(expiresAt);
|
|
1043
|
+
if (!Number.isNaN(parsed) && parsed > referenceTime + this.minTtlMs) {
|
|
1044
|
+
return parsed;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
const decoded = decodeJWT(jwt);
|
|
1048
|
+
if (decoded?.exp) {
|
|
1049
|
+
const expMs = decoded.exp * 1e3;
|
|
1050
|
+
if (expMs > referenceTime + this.minTtlMs) {
|
|
1051
|
+
return expMs;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return referenceTime + Math.max(this.minTtlMs, 6e4);
|
|
1055
|
+
}
|
|
1056
|
+
extractServerUrl(payload) {
|
|
1057
|
+
const possibleUrl = payload?.server_url;
|
|
1058
|
+
if (typeof possibleUrl === "string" && possibleUrl.trim()) {
|
|
1059
|
+
return possibleUrl.trim();
|
|
1060
|
+
}
|
|
1061
|
+
return void 0;
|
|
1062
|
+
}
|
|
1063
|
+
emitTokenRefreshed(info) {
|
|
1064
|
+
if (this.onTokenRefreshed) {
|
|
1065
|
+
try {
|
|
1066
|
+
this.onTokenRefreshed(info);
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
console.warn("JWTFactory onTokenRefreshed callback failed:", error);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
getServerUrl() {
|
|
1073
|
+
return this.currentServerUrl ?? this.cache?.serverUrl;
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
// src/coderule-clients.ts
|
|
1078
|
+
init_esm_shims();
|
|
1079
|
+
var DEFAULT_AUTH_BASE_URL = "https://r.coderule.ai:16803";
|
|
1080
|
+
var DEFAULT_SERVICE_BASE_URL = "https://s1.coderule.ai:16803";
|
|
1081
|
+
var DEFAULT_TIMEOUTS = {
|
|
1082
|
+
auth: 3e4,
|
|
1083
|
+
ast: 6e4,
|
|
1084
|
+
retrieval: 6e4,
|
|
1085
|
+
sync: 6e4
|
|
1086
|
+
};
|
|
1087
|
+
function resolveOverrides(options) {
|
|
1088
|
+
return {
|
|
1089
|
+
auth: options.auth,
|
|
1090
|
+
ast: options.ast,
|
|
1091
|
+
retrieval: options.retrieval,
|
|
1092
|
+
sync: options.sync
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
function resolveTimeout(service, overrides) {
|
|
1096
|
+
return overrides[service]?.timeout ?? DEFAULT_TIMEOUTS[service];
|
|
1097
|
+
}
|
|
1098
|
+
var CoderuleClients = class {
|
|
1099
|
+
constructor(options) {
|
|
1100
|
+
if (!options?.token) {
|
|
1101
|
+
throw new Error("CoderuleClients requires a non-empty token");
|
|
1102
|
+
}
|
|
1103
|
+
const overrides = resolveOverrides(options);
|
|
1104
|
+
const baseUrl = options.baseUrl;
|
|
1105
|
+
const authBase = overrides.auth?.baseUrl ?? baseUrl ?? DEFAULT_AUTH_BASE_URL;
|
|
1106
|
+
const authTimeout = resolveTimeout("auth", overrides);
|
|
1107
|
+
this.auth = new AuthHttpClient(authBase, authTimeout);
|
|
1108
|
+
const userTokenCallback = options.jwtFactory?.onTokenRefreshed;
|
|
1109
|
+
const jwtOptions = {
|
|
1110
|
+
...options.jwtFactory,
|
|
1111
|
+
onTokenRefreshed: (info) => {
|
|
1112
|
+
userTokenCallback?.(info);
|
|
1113
|
+
if (info.serverUrl) {
|
|
1114
|
+
this.applyServerUrl(info.serverUrl);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
this.jwtFactory = new JWTFactory(this.auth, options.token, jwtOptions);
|
|
1119
|
+
this.serviceBaseLocked = {
|
|
1120
|
+
ast: Boolean(overrides.ast?.baseUrl),
|
|
1121
|
+
retrieval: Boolean(overrides.retrieval?.baseUrl),
|
|
1122
|
+
sync: Boolean(overrides.sync?.baseUrl)
|
|
1123
|
+
};
|
|
1124
|
+
const defaultServiceBase = baseUrl ?? DEFAULT_SERVICE_BASE_URL;
|
|
1125
|
+
this.ast = new ASTHttpClient({
|
|
1126
|
+
baseUrl: overrides.ast?.baseUrl ?? defaultServiceBase,
|
|
1127
|
+
timeout: resolveTimeout("ast", overrides),
|
|
1128
|
+
jwtProvider: this.jwtFactory
|
|
1129
|
+
});
|
|
1130
|
+
this.retrieval = new RetrievalHttpClient({
|
|
1131
|
+
baseUrl: overrides.retrieval?.baseUrl ?? defaultServiceBase,
|
|
1132
|
+
timeout: resolveTimeout("retrieval", overrides),
|
|
1133
|
+
jwtProvider: this.jwtFactory
|
|
1134
|
+
});
|
|
1135
|
+
this.sync = new SyncHttpClient({
|
|
1136
|
+
baseUrl: overrides.sync?.baseUrl ?? defaultServiceBase,
|
|
1137
|
+
timeout: resolveTimeout("sync", overrides),
|
|
1138
|
+
jwtProvider: this.jwtFactory
|
|
1139
|
+
});
|
|
1140
|
+
const initialServerUrl = this.jwtFactory.getServerUrl();
|
|
1141
|
+
if (initialServerUrl) {
|
|
1142
|
+
this.applyServerUrl(initialServerUrl);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
get jwt() {
|
|
1146
|
+
return this.jwtFactory;
|
|
1147
|
+
}
|
|
1148
|
+
async getJWT(forceRefresh = false) {
|
|
1149
|
+
return this.jwtFactory.getJWT(forceRefresh);
|
|
1150
|
+
}
|
|
1151
|
+
setToken(token) {
|
|
1152
|
+
this.jwtFactory.setSourceToken(token);
|
|
1153
|
+
}
|
|
1154
|
+
close() {
|
|
1155
|
+
this.ast.close();
|
|
1156
|
+
this.retrieval.close();
|
|
1157
|
+
this.sync.close();
|
|
1158
|
+
this.auth.close();
|
|
1159
|
+
}
|
|
1160
|
+
applyServerUrl(serverUrl) {
|
|
1161
|
+
const trimmed = serverUrl.trim();
|
|
1162
|
+
if (!trimmed || this.lastServerUrl === trimmed) {
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
this.lastServerUrl = trimmed;
|
|
1166
|
+
try {
|
|
1167
|
+
if (!this.serviceBaseLocked.ast) {
|
|
1168
|
+
this.ast.updateBaseUrl(trimmed);
|
|
1169
|
+
}
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
console.warn("Failed to update AST client base URL:", error);
|
|
1172
|
+
}
|
|
1173
|
+
try {
|
|
1174
|
+
if (!this.serviceBaseLocked.retrieval) {
|
|
1175
|
+
this.retrieval.updateBaseUrl(trimmed);
|
|
1176
|
+
}
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
console.warn("Failed to update Retrieval client base URL:", error);
|
|
1179
|
+
}
|
|
1180
|
+
try {
|
|
1181
|
+
if (!this.serviceBaseLocked.sync) {
|
|
1182
|
+
this.sync.updateBaseUrl(trimmed);
|
|
1183
|
+
}
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
console.warn("Failed to update Sync client base URL:", error);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
export { ASTHttpClient, AuthHttpClient, CoderuleClients, JWTFactory, RetrievalHttpClient, SyncHttpClient, decodeJWT, fetch_wrapper_default as fetch };
|
|
753
1191
|
//# sourceMappingURL=index.js.map
|
|
754
1192
|
//# sourceMappingURL=index.js.map
|