@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.cjs
CHANGED
|
@@ -259,18 +259,41 @@ init_cjs_shims();
|
|
|
259
259
|
var SyncHttpClient = class {
|
|
260
260
|
/**
|
|
261
261
|
* Initialize the Sync HTTP client
|
|
262
|
-
* @param baseUrl -
|
|
263
|
-
* @param
|
|
264
|
-
* @param
|
|
262
|
+
* @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8002")
|
|
263
|
+
* @param config.timeout - Request timeout in milliseconds (default: 60000)
|
|
264
|
+
* @param config.jwtProvider - Provider for obtaining JWT tokens
|
|
265
265
|
*/
|
|
266
|
-
constructor(
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
constructor({
|
|
267
|
+
baseUrl = "http://localhost:8002",
|
|
268
|
+
timeout = 6e4,
|
|
269
|
+
jwtProvider
|
|
270
|
+
}) {
|
|
271
|
+
if (!jwtProvider) {
|
|
272
|
+
throw new Error("SyncHttpClient requires a JWT provider");
|
|
273
|
+
}
|
|
269
274
|
this.timeout = timeout;
|
|
270
|
-
this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
this.jwtProvider = jwtProvider;
|
|
276
|
+
this.configureBase(baseUrl);
|
|
277
|
+
}
|
|
278
|
+
updateBaseUrl(baseUrl) {
|
|
279
|
+
this.configureBase(baseUrl);
|
|
280
|
+
}
|
|
281
|
+
configureBase(uri) {
|
|
282
|
+
let processedUri = uri;
|
|
283
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
284
|
+
processedUri = `http://${uri}`;
|
|
285
|
+
}
|
|
286
|
+
const url = new URL(processedUri);
|
|
287
|
+
if (url.pathname && url.pathname !== "/") {
|
|
288
|
+
this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
|
|
289
|
+
} else {
|
|
290
|
+
this.baseUrl = `${url.protocol}//${url.host}`;
|
|
291
|
+
}
|
|
292
|
+
if (this.baseUrl.endsWith("/")) {
|
|
293
|
+
this.apiBase = `${this.baseUrl}sync/v1/`;
|
|
294
|
+
} else {
|
|
295
|
+
this.apiBase = `${this.baseUrl}/sync/v1/`;
|
|
296
|
+
}
|
|
274
297
|
}
|
|
275
298
|
/**
|
|
276
299
|
* Check the status of a snapshot
|
|
@@ -279,13 +302,20 @@ var SyncHttpClient = class {
|
|
|
279
302
|
* @throws Error on HTTP errors or connection errors
|
|
280
303
|
*/
|
|
281
304
|
async checkSnapshotStatus(snapshotHash) {
|
|
282
|
-
|
|
305
|
+
if (!snapshotHash) {
|
|
306
|
+
throw new Error("Snapshot hash must be provided");
|
|
307
|
+
}
|
|
308
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
309
|
+
const url = `${this.apiBase}snapshots`;
|
|
283
310
|
try {
|
|
284
311
|
const controller = new AbortController2();
|
|
285
312
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
286
313
|
const response = await fetch_wrapper_default(url, {
|
|
287
314
|
method: "POST",
|
|
288
|
-
headers:
|
|
315
|
+
headers: {
|
|
316
|
+
Authorization: `Bearer ${jwt}`,
|
|
317
|
+
"Content-Type": "application/json"
|
|
318
|
+
},
|
|
289
319
|
body: JSON.stringify({ snapshot_hash: snapshotHash }),
|
|
290
320
|
signal: controller.signal
|
|
291
321
|
});
|
|
@@ -317,13 +347,20 @@ var SyncHttpClient = class {
|
|
|
317
347
|
* @throws Error on HTTP errors or connection errors
|
|
318
348
|
*/
|
|
319
349
|
async createSnapshot(snapshotHash, files) {
|
|
320
|
-
|
|
350
|
+
if (!snapshotHash) {
|
|
351
|
+
throw new Error("Snapshot hash must be provided");
|
|
352
|
+
}
|
|
353
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
354
|
+
const url = `${this.apiBase}snapshots`;
|
|
321
355
|
try {
|
|
322
356
|
const controller = new AbortController2();
|
|
323
357
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
324
358
|
const response = await fetch_wrapper_default(url, {
|
|
325
359
|
method: "POST",
|
|
326
|
-
headers:
|
|
360
|
+
headers: {
|
|
361
|
+
Authorization: `Bearer ${jwt}`,
|
|
362
|
+
"Content-Type": "application/json"
|
|
363
|
+
},
|
|
327
364
|
body: JSON.stringify({
|
|
328
365
|
snapshot_hash: snapshotHash,
|
|
329
366
|
files
|
|
@@ -364,7 +401,8 @@ var SyncHttpClient = class {
|
|
|
364
401
|
* @throws Error on HTTP errors or connection errors
|
|
365
402
|
*/
|
|
366
403
|
async uploadFileContent(filesContent) {
|
|
367
|
-
const
|
|
404
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
405
|
+
const url = `${this.apiBase}files/content`;
|
|
368
406
|
try {
|
|
369
407
|
const controller = new AbortController2();
|
|
370
408
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
@@ -374,8 +412,9 @@ var SyncHttpClient = class {
|
|
|
374
412
|
const blob = new Blob([content], { type: "application/octet-stream" });
|
|
375
413
|
formData.append(fileHash, blob, fileData.path);
|
|
376
414
|
}
|
|
377
|
-
const uploadHeaders = {
|
|
378
|
-
|
|
415
|
+
const uploadHeaders = {
|
|
416
|
+
Authorization: `Bearer ${jwt}`
|
|
417
|
+
};
|
|
379
418
|
const response = await fetch_wrapper_default(url, {
|
|
380
419
|
method: "POST",
|
|
381
420
|
headers: uploadHeaders,
|
|
@@ -432,13 +471,13 @@ var SyncHttpClient = class {
|
|
|
432
471
|
* @throws Error on HTTP errors or connection errors
|
|
433
472
|
*/
|
|
434
473
|
async health() {
|
|
435
|
-
const url = `${this.
|
|
474
|
+
const url = `${this.apiBase}health`;
|
|
436
475
|
try {
|
|
437
476
|
const controller = new AbortController2();
|
|
438
477
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
439
478
|
const response = await fetch_wrapper_default(url, {
|
|
440
479
|
method: "GET",
|
|
441
|
-
|
|
480
|
+
// No authentication required for health check
|
|
442
481
|
signal: controller.signal
|
|
443
482
|
});
|
|
444
483
|
clearTimeout(timeoutId);
|
|
@@ -470,10 +509,28 @@ init_cjs_shims();
|
|
|
470
509
|
var RetrievalHttpClient = class {
|
|
471
510
|
/**
|
|
472
511
|
* Initialize the Retrieval HTTP client
|
|
473
|
-
* @param
|
|
474
|
-
* @param timeout - Request timeout in milliseconds (default: 60000)
|
|
512
|
+
* @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8004")
|
|
513
|
+
* @param config.timeout - Request timeout in milliseconds (default: 60000)
|
|
514
|
+
* @param config.jwtProvider - Provider for obtaining JWT tokens
|
|
475
515
|
*/
|
|
476
|
-
constructor(
|
|
516
|
+
constructor({
|
|
517
|
+
baseUrl = "http://localhost:8004",
|
|
518
|
+
timeout = 6e4,
|
|
519
|
+
jwtProvider
|
|
520
|
+
}) {
|
|
521
|
+
if (!jwtProvider) {
|
|
522
|
+
throw new Error("RetrievalHttpClient requires a JWT provider");
|
|
523
|
+
}
|
|
524
|
+
this.timeout = timeout;
|
|
525
|
+
this.jwtProvider = jwtProvider;
|
|
526
|
+
this.configureBase(baseUrl);
|
|
527
|
+
console.debug(`Initialized HTTP client for ${this.baseUrl}`);
|
|
528
|
+
console.debug(`API base: ${this.apiBase}`);
|
|
529
|
+
}
|
|
530
|
+
updateBaseUrl(baseUrl) {
|
|
531
|
+
this.configureBase(baseUrl);
|
|
532
|
+
}
|
|
533
|
+
configureBase(uri) {
|
|
477
534
|
let processedUri = uri;
|
|
478
535
|
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
479
536
|
processedUri = `http://${uri}`;
|
|
@@ -484,14 +541,11 @@ var RetrievalHttpClient = class {
|
|
|
484
541
|
} else {
|
|
485
542
|
this.baseUrl = `${url.protocol}//${url.host}`;
|
|
486
543
|
}
|
|
487
|
-
this.timeout = timeout;
|
|
488
544
|
if (this.baseUrl.endsWith("/")) {
|
|
489
545
|
this.apiBase = `${this.baseUrl}api/retrieval/`;
|
|
490
546
|
} else {
|
|
491
547
|
this.apiBase = `${this.baseUrl}/api/retrieval/`;
|
|
492
548
|
}
|
|
493
|
-
console.debug(`Initialized HTTP client for ${this.baseUrl}`);
|
|
494
|
-
console.debug(`API base: ${this.apiBase}`);
|
|
495
549
|
}
|
|
496
550
|
/**
|
|
497
551
|
* Check the health status of the Retrieval service
|
|
@@ -540,25 +594,22 @@ var RetrievalHttpClient = class {
|
|
|
540
594
|
* @param snapshotHash - SHA256 hash of the codebase snapshot
|
|
541
595
|
* @param queryText - Natural language query for retrieval
|
|
542
596
|
* @param budgetTokens - Maximum token budget for results (default: 3000)
|
|
543
|
-
* @param jwt - JWT token for authorization (required)
|
|
544
597
|
* @param options - Optional retrieval parameters
|
|
545
598
|
* @returns Retrieval results with formatted output
|
|
546
599
|
* @throws Error on query failures
|
|
547
600
|
*/
|
|
548
|
-
async query(snapshotHash, queryText, budgetTokens = 3e3,
|
|
601
|
+
async query(snapshotHash, queryText, budgetTokens = 3e3, options) {
|
|
549
602
|
if (!snapshotHash) {
|
|
550
603
|
throw new Error("Snapshot hash must be provided");
|
|
551
604
|
}
|
|
552
605
|
if (!queryText) {
|
|
553
606
|
throw new Error("Query text must be provided");
|
|
554
607
|
}
|
|
555
|
-
if (!jwt) {
|
|
556
|
-
throw new Error("JWT must be provided");
|
|
557
|
-
}
|
|
558
608
|
if (budgetTokens < 100) {
|
|
559
609
|
throw new Error("Budget tokens must be at least 100");
|
|
560
610
|
}
|
|
561
611
|
const startTime = Date.now();
|
|
612
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
562
613
|
const queryEndpoint = `${this.apiBase}query`;
|
|
563
614
|
try {
|
|
564
615
|
const controller = new AbortController2();
|
|
@@ -615,13 +666,11 @@ var RetrievalHttpClient = class {
|
|
|
615
666
|
* @returns Snapshot status information
|
|
616
667
|
* @throws Error on status check failures
|
|
617
668
|
*/
|
|
618
|
-
async checkSnapshotStatus(snapshotHash
|
|
669
|
+
async checkSnapshotStatus(snapshotHash) {
|
|
619
670
|
if (!snapshotHash) {
|
|
620
671
|
throw new Error("Snapshot hash must be provided");
|
|
621
672
|
}
|
|
622
|
-
|
|
623
|
-
throw new Error("JWT must be provided");
|
|
624
|
-
}
|
|
673
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
625
674
|
const statusEndpoint = `${this.apiBase}snapshots/${snapshotHash}/status`;
|
|
626
675
|
try {
|
|
627
676
|
const controller = new AbortController2();
|
|
@@ -666,10 +715,8 @@ var RetrievalHttpClient = class {
|
|
|
666
715
|
* @returns true if cache cleared successfully
|
|
667
716
|
* @throws Error on cache clear failures
|
|
668
717
|
*/
|
|
669
|
-
async clearCache(
|
|
670
|
-
|
|
671
|
-
throw new Error("JWT must be provided");
|
|
672
|
-
}
|
|
718
|
+
async clearCache() {
|
|
719
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
673
720
|
const cacheEndpoint = `${this.apiBase}cache`;
|
|
674
721
|
try {
|
|
675
722
|
const controller = new AbortController2();
|
|
@@ -704,10 +751,8 @@ var RetrievalHttpClient = class {
|
|
|
704
751
|
* @returns Cache statistics
|
|
705
752
|
* @throws Error on stats retrieval failures
|
|
706
753
|
*/
|
|
707
|
-
async getCacheStats(
|
|
708
|
-
|
|
709
|
-
throw new Error("JWT must be provided");
|
|
710
|
-
}
|
|
754
|
+
async getCacheStats() {
|
|
755
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
711
756
|
const statsEndpoint = `${this.apiBase}cache/stats`;
|
|
712
757
|
try {
|
|
713
758
|
const controller = new AbortController2();
|
|
@@ -753,7 +798,7 @@ var RetrievalHttpClient = class {
|
|
|
753
798
|
* @param formatter - Output format "standard" or "compact"
|
|
754
799
|
* @returns Retrieval results
|
|
755
800
|
*/
|
|
756
|
-
async queryWithOptions(snapshotHash, queryText,
|
|
801
|
+
async queryWithOptions(snapshotHash, queryText, budgetTokens = 3e3, flowStrength = 1.5, blendAlpha = 0.8, hopDepth = 2, maxIterations = 12, split = 0.8, formatter = "standard") {
|
|
757
802
|
const options = {
|
|
758
803
|
flow_strength: flowStrength,
|
|
759
804
|
blend_alpha: blendAlpha,
|
|
@@ -762,7 +807,164 @@ var RetrievalHttpClient = class {
|
|
|
762
807
|
split,
|
|
763
808
|
formatter
|
|
764
809
|
};
|
|
765
|
-
return this.query(snapshotHash, queryText, budgetTokens,
|
|
810
|
+
return this.query(snapshotHash, queryText, budgetTokens, options);
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Close the HTTP client connection (no-op for fetch)
|
|
814
|
+
*/
|
|
815
|
+
close() {
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
// src/clients/ast-client.ts
|
|
820
|
+
init_cjs_shims();
|
|
821
|
+
var ASTHttpClient = class {
|
|
822
|
+
/**
|
|
823
|
+
* Initialize the AST HTTP client
|
|
824
|
+
* @param config.baseUrl - URI/URL for connecting to the HTTP server (e.g., "http://localhost:8003")
|
|
825
|
+
* @param config.timeout - Request timeout in milliseconds (default: 60000)
|
|
826
|
+
* @param config.jwtProvider - Provider for obtaining JWT tokens
|
|
827
|
+
*/
|
|
828
|
+
constructor({
|
|
829
|
+
baseUrl = "http://localhost:8003",
|
|
830
|
+
timeout = 6e4,
|
|
831
|
+
jwtProvider
|
|
832
|
+
}) {
|
|
833
|
+
if (!jwtProvider) {
|
|
834
|
+
throw new Error("ASTHttpClient requires a JWT provider");
|
|
835
|
+
}
|
|
836
|
+
this.timeout = timeout;
|
|
837
|
+
this.jwtProvider = jwtProvider;
|
|
838
|
+
this.configureBase(baseUrl);
|
|
839
|
+
}
|
|
840
|
+
updateBaseUrl(baseUrl) {
|
|
841
|
+
this.configureBase(baseUrl);
|
|
842
|
+
}
|
|
843
|
+
configureBase(uri) {
|
|
844
|
+
let processedUri = uri;
|
|
845
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
846
|
+
processedUri = `http://${uri}`;
|
|
847
|
+
}
|
|
848
|
+
const url = new URL(processedUri);
|
|
849
|
+
if (url.pathname && url.pathname !== "/") {
|
|
850
|
+
this.baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
|
|
851
|
+
} else {
|
|
852
|
+
this.baseUrl = `${url.protocol}//${url.host}`;
|
|
853
|
+
}
|
|
854
|
+
if (this.baseUrl.endsWith("/")) {
|
|
855
|
+
this.apiBase = `${this.baseUrl}api/ast/`;
|
|
856
|
+
} else {
|
|
857
|
+
this.apiBase = `${this.baseUrl}/api/ast/`;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Get file visitor rules in v2 format optimized for Chokidar v4
|
|
862
|
+
* @returns Visitor rules with format, include_extensions, include_filenames, exclude_dirnames
|
|
863
|
+
* @throws Error on HTTP errors or connection errors
|
|
864
|
+
*/
|
|
865
|
+
async getVisitorRulesV2() {
|
|
866
|
+
const jwt = await this.jwtProvider.getJWT();
|
|
867
|
+
const url = `${this.apiBase}visitor-rules`;
|
|
868
|
+
try {
|
|
869
|
+
const controller = new AbortController2();
|
|
870
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
871
|
+
const response = await fetch_wrapper_default(url, {
|
|
872
|
+
method: "GET",
|
|
873
|
+
headers: {
|
|
874
|
+
Authorization: `Bearer ${jwt}`
|
|
875
|
+
},
|
|
876
|
+
signal: controller.signal
|
|
877
|
+
});
|
|
878
|
+
clearTimeout(timeoutId);
|
|
879
|
+
if (!response.ok) {
|
|
880
|
+
const errorText = await response.text();
|
|
881
|
+
throw new Error(
|
|
882
|
+
`Failed to get visitor rules v2 with status ${response.status}: ${errorText}`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
const data = await response.json();
|
|
886
|
+
if (data.format !== "segments+exts@v2") {
|
|
887
|
+
throw new Error(`Unexpected visitor rules format: ${data.format}`);
|
|
888
|
+
}
|
|
889
|
+
return data;
|
|
890
|
+
} catch (error) {
|
|
891
|
+
if (error.name === "AbortError") {
|
|
892
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
893
|
+
}
|
|
894
|
+
console.error(`Failed to get visitor rules v2: ${error.message}`);
|
|
895
|
+
throw error;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Check the health status of the AST service
|
|
900
|
+
* @returns Health status information
|
|
901
|
+
* @throws Error on HTTP errors or connection errors
|
|
902
|
+
*/
|
|
903
|
+
async health() {
|
|
904
|
+
const url = `${this.apiBase}health`;
|
|
905
|
+
try {
|
|
906
|
+
const controller = new AbortController2();
|
|
907
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
908
|
+
const response = await fetch_wrapper_default(url, {
|
|
909
|
+
method: "GET",
|
|
910
|
+
// No authentication required for health check
|
|
911
|
+
signal: controller.signal
|
|
912
|
+
});
|
|
913
|
+
clearTimeout(timeoutId);
|
|
914
|
+
if (!response.ok) {
|
|
915
|
+
const errorText = await response.text();
|
|
916
|
+
throw new Error(
|
|
917
|
+
`Health check failed with status ${response.status}: ${errorText}`
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
const data = await response.json();
|
|
921
|
+
return data;
|
|
922
|
+
} catch (error) {
|
|
923
|
+
if (error.name === "AbortError") {
|
|
924
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
925
|
+
}
|
|
926
|
+
console.error(`Health check failed: ${error.message}`);
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Compile visitor rules v2 for use with Chokidar v4
|
|
932
|
+
* This is a helper method that compiles the rules into a format
|
|
933
|
+
* that can be directly used with Chokidar's ignored option
|
|
934
|
+
* @param rules - The visitor rules v2 from the server
|
|
935
|
+
* @returns Compiled rules with Sets and RegExp for efficient matching
|
|
936
|
+
*/
|
|
937
|
+
static compileRulesV2(rules) {
|
|
938
|
+
if (rules.format !== "segments+exts@v2") {
|
|
939
|
+
throw new Error(`Unsupported rules format: ${rules.format}`);
|
|
940
|
+
}
|
|
941
|
+
const exts = new Set(rules.include_extensions.map((e) => e.toLowerCase()));
|
|
942
|
+
const names = new Set(rules.include_filenames.map((n) => n.toLowerCase()));
|
|
943
|
+
const dirs = new Set(rules.exclude_dirnames.map((n) => n.toLowerCase()));
|
|
944
|
+
const escapeRe = (s) => s.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
|
|
945
|
+
const dirRe = new RegExp(
|
|
946
|
+
`(?:^|[\\/])(?:${[...dirs].map(escapeRe).join("|")})(?:[\\/]|$)`,
|
|
947
|
+
"i"
|
|
948
|
+
);
|
|
949
|
+
return { exts, names, dirRe };
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Build Chokidar v4 `ignored` predicate from compiled rules
|
|
953
|
+
* @param compiled - Compiled rules from compileRulesV2
|
|
954
|
+
* @returns Predicate function that returns true to ignore, false to watch
|
|
955
|
+
*/
|
|
956
|
+
static buildIgnoredPredicate(compiled) {
|
|
957
|
+
return (p, stats) => {
|
|
958
|
+
const posix = p.replace(/\\/g, "/");
|
|
959
|
+
if (compiled.dirRe.test(posix)) return true;
|
|
960
|
+
if (stats?.isDirectory?.()) return false;
|
|
961
|
+
const base = posix.split("/").pop()?.toLowerCase() || "";
|
|
962
|
+
if (compiled.names.has(base)) return false;
|
|
963
|
+
const extIndex = base.lastIndexOf(".");
|
|
964
|
+
const ext = extIndex > -1 ? base.slice(extIndex).toLowerCase() : "";
|
|
965
|
+
if (!compiled.exts.has(ext)) return true;
|
|
966
|
+
return false;
|
|
967
|
+
};
|
|
766
968
|
}
|
|
767
969
|
/**
|
|
768
970
|
* Close the HTTP client connection (no-op for fetch)
|
|
@@ -771,7 +973,246 @@ var RetrievalHttpClient = class {
|
|
|
771
973
|
}
|
|
772
974
|
};
|
|
773
975
|
|
|
976
|
+
// src/utils/jwt-factory.ts
|
|
977
|
+
init_cjs_shims();
|
|
978
|
+
var DEFAULT_MIN_TTL_MS = 5e3;
|
|
979
|
+
var JWTFactory = class {
|
|
980
|
+
constructor(authClient, sourceToken, options = {}) {
|
|
981
|
+
this.authClient = authClient;
|
|
982
|
+
this.sourceToken = sourceToken;
|
|
983
|
+
if (!sourceToken) {
|
|
984
|
+
throw new Error("JWTFactory requires a non-empty source token");
|
|
985
|
+
}
|
|
986
|
+
this.minTtlMs = options.minTtlMs ?? DEFAULT_MIN_TTL_MS;
|
|
987
|
+
this.onTokenRefreshed = options.onTokenRefreshed;
|
|
988
|
+
}
|
|
989
|
+
setSourceToken(token) {
|
|
990
|
+
if (!token) {
|
|
991
|
+
throw new Error("JWTFactory requires a non-empty source token");
|
|
992
|
+
}
|
|
993
|
+
if (token !== this.sourceToken) {
|
|
994
|
+
this.sourceToken = token;
|
|
995
|
+
this.cache = void 0;
|
|
996
|
+
this.currentServerUrl = void 0;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
invalidate() {
|
|
1000
|
+
this.cache = void 0;
|
|
1001
|
+
this.currentServerUrl = void 0;
|
|
1002
|
+
}
|
|
1003
|
+
async getJWT(forceRefresh = false) {
|
|
1004
|
+
const now = Date.now();
|
|
1005
|
+
if (!forceRefresh && this.cache) {
|
|
1006
|
+
if (now < this.cache.expiresAt) {
|
|
1007
|
+
if (now < this.cache.refreshAt) {
|
|
1008
|
+
return this.cache.token;
|
|
1009
|
+
}
|
|
1010
|
+
} else {
|
|
1011
|
+
this.cache = void 0;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return this.refresh(now, forceRefresh);
|
|
1015
|
+
}
|
|
1016
|
+
async refresh(now, forceRefresh) {
|
|
1017
|
+
if (!this.refreshPromise) {
|
|
1018
|
+
this.refreshPromise = this.fetchNewToken().finally(() => {
|
|
1019
|
+
this.refreshPromise = void 0;
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
try {
|
|
1023
|
+
return await this.refreshPromise;
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
if (!forceRefresh && this.cache && now < this.cache.expiresAt) {
|
|
1026
|
+
console.warn(
|
|
1027
|
+
"Failed to refresh JWT, using cached token until expiry. Reason:",
|
|
1028
|
+
error
|
|
1029
|
+
);
|
|
1030
|
+
return this.cache.token;
|
|
1031
|
+
}
|
|
1032
|
+
throw error;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
async fetchNewToken() {
|
|
1036
|
+
const response = await this.authClient.authenticate(this.sourceToken);
|
|
1037
|
+
const fetchedAt = Date.now();
|
|
1038
|
+
const expiresAt = this.resolveExpiryMs(
|
|
1039
|
+
response.jwt,
|
|
1040
|
+
response.expires_at,
|
|
1041
|
+
fetchedAt
|
|
1042
|
+
);
|
|
1043
|
+
const payload = decodeJWT(response.jwt);
|
|
1044
|
+
const serverUrl = this.extractServerUrl(payload);
|
|
1045
|
+
const halfLife = Math.max((expiresAt - fetchedAt) / 2, this.minTtlMs);
|
|
1046
|
+
const refreshAt = fetchedAt + halfLife;
|
|
1047
|
+
this.cache = {
|
|
1048
|
+
token: response.jwt,
|
|
1049
|
+
expiresAt,
|
|
1050
|
+
refreshAt,
|
|
1051
|
+
fetchedAt,
|
|
1052
|
+
serverUrl
|
|
1053
|
+
};
|
|
1054
|
+
this.currentServerUrl = serverUrl;
|
|
1055
|
+
this.emitTokenRefreshed({
|
|
1056
|
+
token: response.jwt,
|
|
1057
|
+
expiresAt,
|
|
1058
|
+
serverUrl
|
|
1059
|
+
});
|
|
1060
|
+
return response.jwt;
|
|
1061
|
+
}
|
|
1062
|
+
resolveExpiryMs(jwt, expiresAt, referenceTime) {
|
|
1063
|
+
if (expiresAt) {
|
|
1064
|
+
const parsed = Date.parse(expiresAt);
|
|
1065
|
+
if (!Number.isNaN(parsed) && parsed > referenceTime + this.minTtlMs) {
|
|
1066
|
+
return parsed;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
const decoded = decodeJWT(jwt);
|
|
1070
|
+
if (decoded?.exp) {
|
|
1071
|
+
const expMs = decoded.exp * 1e3;
|
|
1072
|
+
if (expMs > referenceTime + this.minTtlMs) {
|
|
1073
|
+
return expMs;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return referenceTime + Math.max(this.minTtlMs, 6e4);
|
|
1077
|
+
}
|
|
1078
|
+
extractServerUrl(payload) {
|
|
1079
|
+
const possibleUrl = payload?.server_url;
|
|
1080
|
+
if (typeof possibleUrl === "string" && possibleUrl.trim()) {
|
|
1081
|
+
return possibleUrl.trim();
|
|
1082
|
+
}
|
|
1083
|
+
return void 0;
|
|
1084
|
+
}
|
|
1085
|
+
emitTokenRefreshed(info) {
|
|
1086
|
+
if (this.onTokenRefreshed) {
|
|
1087
|
+
try {
|
|
1088
|
+
this.onTokenRefreshed(info);
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
console.warn("JWTFactory onTokenRefreshed callback failed:", error);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
getServerUrl() {
|
|
1095
|
+
return this.currentServerUrl ?? this.cache?.serverUrl;
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
// src/coderule-clients.ts
|
|
1100
|
+
init_cjs_shims();
|
|
1101
|
+
var DEFAULT_AUTH_BASE_URL = "https://r.coderule.ai:16803";
|
|
1102
|
+
var DEFAULT_SERVICE_BASE_URL = "https://s1.coderule.ai:16803";
|
|
1103
|
+
var DEFAULT_TIMEOUTS = {
|
|
1104
|
+
auth: 3e4,
|
|
1105
|
+
ast: 6e4,
|
|
1106
|
+
retrieval: 6e4,
|
|
1107
|
+
sync: 6e4
|
|
1108
|
+
};
|
|
1109
|
+
function resolveOverrides(options) {
|
|
1110
|
+
return {
|
|
1111
|
+
auth: options.auth,
|
|
1112
|
+
ast: options.ast,
|
|
1113
|
+
retrieval: options.retrieval,
|
|
1114
|
+
sync: options.sync
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
function resolveTimeout(service, overrides) {
|
|
1118
|
+
return overrides[service]?.timeout ?? DEFAULT_TIMEOUTS[service];
|
|
1119
|
+
}
|
|
1120
|
+
var CoderuleClients = class {
|
|
1121
|
+
constructor(options) {
|
|
1122
|
+
if (!options?.token) {
|
|
1123
|
+
throw new Error("CoderuleClients requires a non-empty token");
|
|
1124
|
+
}
|
|
1125
|
+
const overrides = resolveOverrides(options);
|
|
1126
|
+
const baseUrl = options.baseUrl;
|
|
1127
|
+
const authBase = overrides.auth?.baseUrl ?? baseUrl ?? DEFAULT_AUTH_BASE_URL;
|
|
1128
|
+
const authTimeout = resolveTimeout("auth", overrides);
|
|
1129
|
+
this.auth = new AuthHttpClient(authBase, authTimeout);
|
|
1130
|
+
const userTokenCallback = options.jwtFactory?.onTokenRefreshed;
|
|
1131
|
+
const jwtOptions = {
|
|
1132
|
+
...options.jwtFactory,
|
|
1133
|
+
onTokenRefreshed: (info) => {
|
|
1134
|
+
userTokenCallback?.(info);
|
|
1135
|
+
if (info.serverUrl) {
|
|
1136
|
+
this.applyServerUrl(info.serverUrl);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
this.jwtFactory = new JWTFactory(this.auth, options.token, jwtOptions);
|
|
1141
|
+
this.serviceBaseLocked = {
|
|
1142
|
+
ast: Boolean(overrides.ast?.baseUrl),
|
|
1143
|
+
retrieval: Boolean(overrides.retrieval?.baseUrl),
|
|
1144
|
+
sync: Boolean(overrides.sync?.baseUrl)
|
|
1145
|
+
};
|
|
1146
|
+
const defaultServiceBase = baseUrl ?? DEFAULT_SERVICE_BASE_URL;
|
|
1147
|
+
this.ast = new ASTHttpClient({
|
|
1148
|
+
baseUrl: overrides.ast?.baseUrl ?? defaultServiceBase,
|
|
1149
|
+
timeout: resolveTimeout("ast", overrides),
|
|
1150
|
+
jwtProvider: this.jwtFactory
|
|
1151
|
+
});
|
|
1152
|
+
this.retrieval = new RetrievalHttpClient({
|
|
1153
|
+
baseUrl: overrides.retrieval?.baseUrl ?? defaultServiceBase,
|
|
1154
|
+
timeout: resolveTimeout("retrieval", overrides),
|
|
1155
|
+
jwtProvider: this.jwtFactory
|
|
1156
|
+
});
|
|
1157
|
+
this.sync = new SyncHttpClient({
|
|
1158
|
+
baseUrl: overrides.sync?.baseUrl ?? defaultServiceBase,
|
|
1159
|
+
timeout: resolveTimeout("sync", overrides),
|
|
1160
|
+
jwtProvider: this.jwtFactory
|
|
1161
|
+
});
|
|
1162
|
+
const initialServerUrl = this.jwtFactory.getServerUrl();
|
|
1163
|
+
if (initialServerUrl) {
|
|
1164
|
+
this.applyServerUrl(initialServerUrl);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
get jwt() {
|
|
1168
|
+
return this.jwtFactory;
|
|
1169
|
+
}
|
|
1170
|
+
async getJWT(forceRefresh = false) {
|
|
1171
|
+
return this.jwtFactory.getJWT(forceRefresh);
|
|
1172
|
+
}
|
|
1173
|
+
setToken(token) {
|
|
1174
|
+
this.jwtFactory.setSourceToken(token);
|
|
1175
|
+
}
|
|
1176
|
+
close() {
|
|
1177
|
+
this.ast.close();
|
|
1178
|
+
this.retrieval.close();
|
|
1179
|
+
this.sync.close();
|
|
1180
|
+
this.auth.close();
|
|
1181
|
+
}
|
|
1182
|
+
applyServerUrl(serverUrl) {
|
|
1183
|
+
const trimmed = serverUrl.trim();
|
|
1184
|
+
if (!trimmed || this.lastServerUrl === trimmed) {
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
this.lastServerUrl = trimmed;
|
|
1188
|
+
try {
|
|
1189
|
+
if (!this.serviceBaseLocked.ast) {
|
|
1190
|
+
this.ast.updateBaseUrl(trimmed);
|
|
1191
|
+
}
|
|
1192
|
+
} catch (error) {
|
|
1193
|
+
console.warn("Failed to update AST client base URL:", error);
|
|
1194
|
+
}
|
|
1195
|
+
try {
|
|
1196
|
+
if (!this.serviceBaseLocked.retrieval) {
|
|
1197
|
+
this.retrieval.updateBaseUrl(trimmed);
|
|
1198
|
+
}
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
console.warn("Failed to update Retrieval client base URL:", error);
|
|
1201
|
+
}
|
|
1202
|
+
try {
|
|
1203
|
+
if (!this.serviceBaseLocked.sync) {
|
|
1204
|
+
this.sync.updateBaseUrl(trimmed);
|
|
1205
|
+
}
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
console.warn("Failed to update Sync client base URL:", error);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
exports.ASTHttpClient = ASTHttpClient;
|
|
774
1213
|
exports.AuthHttpClient = AuthHttpClient;
|
|
1214
|
+
exports.CoderuleClients = CoderuleClients;
|
|
1215
|
+
exports.JWTFactory = JWTFactory;
|
|
775
1216
|
exports.RetrievalHttpClient = RetrievalHttpClient;
|
|
776
1217
|
exports.SyncHttpClient = SyncHttpClient;
|
|
777
1218
|
exports.decodeJWT = decodeJWT;
|