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