@syfthub/sdk 0.1.1 → 0.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.cjs +1300 -706
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1249 -454
- package/dist/index.d.ts +1249 -454
- package/dist/index.js +1296 -699
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
- package/README.md +0 -276
package/dist/index.js
CHANGED
|
@@ -124,11 +124,21 @@ var init_errors = __esm({
|
|
|
124
124
|
init_errors();
|
|
125
125
|
|
|
126
126
|
// src/utils.ts
|
|
127
|
+
var snakeToCamelCache = /* @__PURE__ */ new Map();
|
|
128
|
+
var camelToSnakeCache = /* @__PURE__ */ new Map();
|
|
127
129
|
function snakeToCamel(str) {
|
|
128
|
-
|
|
130
|
+
const cached = snakeToCamelCache.get(str);
|
|
131
|
+
if (cached !== void 0) return cached;
|
|
132
|
+
const result = str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
133
|
+
snakeToCamelCache.set(str, result);
|
|
134
|
+
return result;
|
|
129
135
|
}
|
|
130
136
|
function camelToSnake(str) {
|
|
131
|
-
|
|
137
|
+
const cached = camelToSnakeCache.get(str);
|
|
138
|
+
if (cached !== void 0) return cached;
|
|
139
|
+
const result = str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
140
|
+
camelToSnakeCache.set(str, result);
|
|
141
|
+
return result;
|
|
132
142
|
}
|
|
133
143
|
var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
134
144
|
function isISODateString(value) {
|
|
@@ -141,6 +151,9 @@ function transformKeys(obj, keyTransformer, parseDates = true) {
|
|
|
141
151
|
if (Array.isArray(obj)) {
|
|
142
152
|
return obj.map((item) => transformKeys(item, keyTransformer, parseDates));
|
|
143
153
|
}
|
|
154
|
+
if (obj instanceof Date) {
|
|
155
|
+
return obj.toISOString();
|
|
156
|
+
}
|
|
144
157
|
if (parseDates && isISODateString(obj)) {
|
|
145
158
|
return new Date(obj);
|
|
146
159
|
}
|
|
@@ -168,9 +181,61 @@ function buildSearchParams(params) {
|
|
|
168
181
|
}
|
|
169
182
|
return searchParams;
|
|
170
183
|
}
|
|
184
|
+
async function* readSSEEvents(response) {
|
|
185
|
+
if (!response.body) return;
|
|
186
|
+
const reader = response.body.getReader();
|
|
187
|
+
const decoder = new TextDecoder();
|
|
188
|
+
let buffer = "";
|
|
189
|
+
let currentEvent = null;
|
|
190
|
+
let currentData = "";
|
|
191
|
+
const flush = function* () {
|
|
192
|
+
if (currentData) {
|
|
193
|
+
yield { event: currentEvent ?? "message", data: currentData };
|
|
194
|
+
}
|
|
195
|
+
currentEvent = null;
|
|
196
|
+
currentData = "";
|
|
197
|
+
};
|
|
198
|
+
try {
|
|
199
|
+
while (true) {
|
|
200
|
+
const { done, value } = await reader.read();
|
|
201
|
+
if (done) break;
|
|
202
|
+
buffer += decoder.decode(value, { stream: true });
|
|
203
|
+
const lines = buffer.split("\n");
|
|
204
|
+
buffer = lines.pop() ?? "";
|
|
205
|
+
for (const line of lines) {
|
|
206
|
+
const trimmed = line.trim();
|
|
207
|
+
if (!trimmed) {
|
|
208
|
+
yield* flush();
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (trimmed.startsWith("event:")) {
|
|
212
|
+
currentEvent = trimmed.slice(6).trim();
|
|
213
|
+
} else if (trimmed.startsWith("data:")) {
|
|
214
|
+
if (currentData && currentEvent === null) {
|
|
215
|
+
yield* flush();
|
|
216
|
+
}
|
|
217
|
+
currentData = trimmed.slice(5).trim();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const trailing = buffer.trim();
|
|
222
|
+
if (trailing) {
|
|
223
|
+
if (trailing.startsWith("event:")) {
|
|
224
|
+
currentEvent = trailing.slice(6).trim();
|
|
225
|
+
} else if (trailing.startsWith("data:")) {
|
|
226
|
+
if (currentData && currentEvent === null) {
|
|
227
|
+
yield* flush();
|
|
228
|
+
}
|
|
229
|
+
currentData = trailing.slice(5).trim();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
yield* flush();
|
|
233
|
+
} finally {
|
|
234
|
+
reader.releaseLock();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
171
237
|
|
|
172
238
|
// src/http.ts
|
|
173
|
-
init_errors();
|
|
174
239
|
var HTTPClient = class {
|
|
175
240
|
/**
|
|
176
241
|
* Create a new HTTP client.
|
|
@@ -184,17 +249,32 @@ var HTTPClient = class {
|
|
|
184
249
|
}
|
|
185
250
|
accessToken = null;
|
|
186
251
|
refreshToken = null;
|
|
252
|
+
apiToken = null;
|
|
187
253
|
isRefreshing = false;
|
|
188
254
|
refreshPromise = null;
|
|
189
255
|
/**
|
|
190
|
-
* Set authentication tokens.
|
|
256
|
+
* Set JWT authentication tokens.
|
|
257
|
+
* Clears any API token if set.
|
|
191
258
|
*/
|
|
192
259
|
setTokens(access, refresh) {
|
|
193
260
|
this.accessToken = access;
|
|
194
261
|
this.refreshToken = refresh;
|
|
262
|
+
this.apiToken = null;
|
|
195
263
|
}
|
|
196
264
|
/**
|
|
197
|
-
*
|
|
265
|
+
* Set API token for authentication.
|
|
266
|
+
* Clears any JWT tokens if set.
|
|
267
|
+
*
|
|
268
|
+
* @param token - The API token (starts with "syft_")
|
|
269
|
+
*/
|
|
270
|
+
setApiToken(token) {
|
|
271
|
+
this.apiToken = token;
|
|
272
|
+
this.accessToken = null;
|
|
273
|
+
this.refreshToken = null;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get current JWT authentication tokens.
|
|
277
|
+
* Returns null if using API token authentication.
|
|
198
278
|
*/
|
|
199
279
|
getTokens() {
|
|
200
280
|
if (!this.accessToken || !this.refreshToken) {
|
|
@@ -207,17 +287,30 @@ var HTTPClient = class {
|
|
|
207
287
|
};
|
|
208
288
|
}
|
|
209
289
|
/**
|
|
210
|
-
*
|
|
290
|
+
* Check if using API token authentication.
|
|
291
|
+
*/
|
|
292
|
+
isUsingApiToken() {
|
|
293
|
+
return this.apiToken !== null;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Clear all authentication (JWT and API tokens).
|
|
211
297
|
*/
|
|
212
298
|
clearTokens() {
|
|
213
299
|
this.accessToken = null;
|
|
214
300
|
this.refreshToken = null;
|
|
301
|
+
this.apiToken = null;
|
|
215
302
|
}
|
|
216
303
|
/**
|
|
217
|
-
* Check if the client has valid
|
|
304
|
+
* Check if the client has valid authentication (JWT or API token).
|
|
218
305
|
*/
|
|
219
306
|
hasTokens() {
|
|
220
|
-
return this.accessToken !== null;
|
|
307
|
+
return this.accessToken !== null || this.apiToken !== null;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get the current bearer token (API token or JWT access token).
|
|
311
|
+
*/
|
|
312
|
+
getBearerToken() {
|
|
313
|
+
return this.apiToken ?? this.accessToken;
|
|
221
314
|
}
|
|
222
315
|
/**
|
|
223
316
|
* Make a GET request.
|
|
@@ -263,8 +356,9 @@ var HTTPClient = class {
|
|
|
263
356
|
}
|
|
264
357
|
}
|
|
265
358
|
const headers = {};
|
|
266
|
-
|
|
267
|
-
|
|
359
|
+
const bearerToken = this.getBearerToken();
|
|
360
|
+
if (includeAuth && bearerToken) {
|
|
361
|
+
headers["Authorization"] = `Bearer ${bearerToken}`;
|
|
268
362
|
}
|
|
269
363
|
let requestBody;
|
|
270
364
|
if (body !== void 0) {
|
|
@@ -292,7 +386,7 @@ var HTTPClient = class {
|
|
|
292
386
|
signal: controller.signal
|
|
293
387
|
});
|
|
294
388
|
clearTimeout(timeoutId);
|
|
295
|
-
if (response.status === 401 && includeAuth && this.refreshToken) {
|
|
389
|
+
if (response.status === 401 && includeAuth && this.refreshToken && !this.apiToken) {
|
|
296
390
|
await this.attemptTokenRefresh();
|
|
297
391
|
return this.request(method, path, {
|
|
298
392
|
...options,
|
|
@@ -479,6 +573,110 @@ var HTTPClient = class {
|
|
|
479
573
|
// src/client.ts
|
|
480
574
|
init_errors();
|
|
481
575
|
|
|
576
|
+
// src/resources/api-tokens.ts
|
|
577
|
+
var APITokensResource = class {
|
|
578
|
+
constructor(http) {
|
|
579
|
+
this.http = http;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Create a new API token.
|
|
583
|
+
*
|
|
584
|
+
* IMPORTANT: The returned token is only shown ONCE!
|
|
585
|
+
* Make sure to save it immediately - it cannot be retrieved later.
|
|
586
|
+
*
|
|
587
|
+
* @param input - Token creation options
|
|
588
|
+
* @returns The created token with the full token value
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* const result = await client.apiTokens.create({
|
|
592
|
+
* name: 'CI/CD Pipeline',
|
|
593
|
+
* scopes: ['write'],
|
|
594
|
+
* expiresAt: new Date('2025-12-31'),
|
|
595
|
+
* });
|
|
596
|
+
*
|
|
597
|
+
* // SAVE THIS TOKEN - it will not be shown again!
|
|
598
|
+
* console.log(result.token);
|
|
599
|
+
*/
|
|
600
|
+
async create(input) {
|
|
601
|
+
return this.http.post("/api/v1/auth/tokens", input);
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* List all API tokens for the current user.
|
|
605
|
+
*
|
|
606
|
+
* By default, only active tokens are returned.
|
|
607
|
+
* Note: The full token value is never returned - only the prefix.
|
|
608
|
+
*
|
|
609
|
+
* @param options - List options
|
|
610
|
+
* @returns List of tokens and total count
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* // List active tokens
|
|
614
|
+
* const { tokens, total } = await client.apiTokens.list();
|
|
615
|
+
*
|
|
616
|
+
* // Include revoked tokens
|
|
617
|
+
* const all = await client.apiTokens.list({ includeInactive: true });
|
|
618
|
+
*/
|
|
619
|
+
async list(options = {}) {
|
|
620
|
+
const params = {};
|
|
621
|
+
if (options.includeInactive !== void 0) {
|
|
622
|
+
params.include_inactive = options.includeInactive;
|
|
623
|
+
}
|
|
624
|
+
if (options.skip !== void 0) {
|
|
625
|
+
params.skip = options.skip;
|
|
626
|
+
}
|
|
627
|
+
if (options.limit !== void 0) {
|
|
628
|
+
params.limit = options.limit;
|
|
629
|
+
}
|
|
630
|
+
return this.http.get("/api/v1/auth/tokens", params);
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Get a single API token by ID.
|
|
634
|
+
*
|
|
635
|
+
* Note: The full token value is never returned - only the prefix.
|
|
636
|
+
*
|
|
637
|
+
* @param tokenId - The token ID
|
|
638
|
+
* @returns The token details
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* const token = await client.apiTokens.get(123);
|
|
642
|
+
* console.log(token.name, token.lastUsedAt);
|
|
643
|
+
*/
|
|
644
|
+
async get(tokenId) {
|
|
645
|
+
return this.http.get(`/api/v1/auth/tokens/${tokenId}`);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Update an API token's name.
|
|
649
|
+
*
|
|
650
|
+
* Only the name can be updated. Scopes and expiration cannot be
|
|
651
|
+
* changed after creation.
|
|
652
|
+
*
|
|
653
|
+
* @param tokenId - The token ID
|
|
654
|
+
* @param input - Update options
|
|
655
|
+
* @returns The updated token
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* const updated = await client.apiTokens.update(123, {
|
|
659
|
+
* name: 'New Name',
|
|
660
|
+
* });
|
|
661
|
+
*/
|
|
662
|
+
async update(tokenId, input) {
|
|
663
|
+
return this.http.patch(`/api/v1/auth/tokens/${tokenId}`, input);
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Revoke an API token.
|
|
667
|
+
*
|
|
668
|
+
* The token becomes immediately unusable. This action cannot be undone.
|
|
669
|
+
*
|
|
670
|
+
* @param tokenId - The token ID to revoke
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* await client.apiTokens.revoke(123);
|
|
674
|
+
*/
|
|
675
|
+
async revoke(tokenId) {
|
|
676
|
+
await this.http.delete(`/api/v1/auth/tokens/${tokenId}`);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
482
680
|
// src/resources/auth.ts
|
|
483
681
|
init_errors();
|
|
484
682
|
var AuthResource = class {
|
|
@@ -546,8 +744,13 @@ var AuthResource = class {
|
|
|
546
744
|
const response = await this.http.post("/api/v1/auth/register", input, {
|
|
547
745
|
includeAuth: false
|
|
548
746
|
});
|
|
549
|
-
|
|
550
|
-
|
|
747
|
+
if (response.accessToken && response.refreshToken) {
|
|
748
|
+
this.http.setTokens(response.accessToken, response.refreshToken);
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
user: response.user,
|
|
752
|
+
requiresEmailVerification: response.requiresEmailVerification
|
|
753
|
+
};
|
|
551
754
|
}
|
|
552
755
|
/**
|
|
553
756
|
* Login with username/email and password.
|
|
@@ -621,6 +824,115 @@ var AuthResource = class {
|
|
|
621
824
|
newPassword
|
|
622
825
|
});
|
|
623
826
|
}
|
|
827
|
+
/**
|
|
828
|
+
* Get the platform's authentication configuration.
|
|
829
|
+
*
|
|
830
|
+
* No authentication required. Use this to determine whether email
|
|
831
|
+
* verification or password reset is available.
|
|
832
|
+
*
|
|
833
|
+
* @returns AuthConfig with feature flags
|
|
834
|
+
*/
|
|
835
|
+
async getAuthConfig() {
|
|
836
|
+
return this.http.get("/api/v1/auth/config", void 0, { includeAuth: false });
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Verify a registration OTP and receive auth tokens.
|
|
840
|
+
*
|
|
841
|
+
* After registering when email verification is required, call this with
|
|
842
|
+
* the 6-digit code sent to the user's email.
|
|
843
|
+
*
|
|
844
|
+
* Idempotent: if the user is already verified, tokens are issued immediately.
|
|
845
|
+
*
|
|
846
|
+
* @param input - Email and 6-digit code
|
|
847
|
+
* @returns The authenticated User
|
|
848
|
+
* @throws {APIError} If the code is invalid or max attempts exceeded
|
|
849
|
+
*/
|
|
850
|
+
async verifyOtp(input) {
|
|
851
|
+
const response = await this.http.post("/api/v1/auth/register/verify-otp", input, {
|
|
852
|
+
includeAuth: false
|
|
853
|
+
});
|
|
854
|
+
this.http.setTokens(response.accessToken, response.refreshToken);
|
|
855
|
+
return response.user;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Resend the registration OTP code.
|
|
859
|
+
*
|
|
860
|
+
* Rate-limited. Always returns successfully to prevent email enumeration.
|
|
861
|
+
*
|
|
862
|
+
* @param email - Email address to resend the OTP to
|
|
863
|
+
*/
|
|
864
|
+
async resendOtp(email) {
|
|
865
|
+
await this.http.post(
|
|
866
|
+
"/api/v1/auth/register/resend-otp",
|
|
867
|
+
{ email },
|
|
868
|
+
{
|
|
869
|
+
includeAuth: false
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Request a password reset OTP.
|
|
875
|
+
*
|
|
876
|
+
* Always returns successfully to prevent email enumeration.
|
|
877
|
+
* If SMTP is not configured on the server, this is a no-op.
|
|
878
|
+
*
|
|
879
|
+
* @param input - Email address for password reset
|
|
880
|
+
*/
|
|
881
|
+
async requestPasswordReset(input) {
|
|
882
|
+
await this.http.post("/api/v1/auth/password-reset/request", input, {
|
|
883
|
+
includeAuth: false
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Confirm a password reset with OTP and set a new password.
|
|
888
|
+
*
|
|
889
|
+
* @param input - Email, 6-digit code, and new password
|
|
890
|
+
* @throws {APIError} If the code is invalid or max attempts exceeded
|
|
891
|
+
*/
|
|
892
|
+
async confirmPasswordReset(input) {
|
|
893
|
+
await this.http.post("/api/v1/auth/password-reset/confirm", input, {
|
|
894
|
+
includeAuth: false
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Get a peer token for NATS communication with tunneling spaces.
|
|
899
|
+
*
|
|
900
|
+
* Peer tokens are short-lived credentials that allow the aggregator to
|
|
901
|
+
* communicate with tunneling SyftAI Spaces via NATS pub/sub.
|
|
902
|
+
*
|
|
903
|
+
* @param targetUsernames - Usernames of the tunneling spaces to communicate with
|
|
904
|
+
* @returns PeerTokenResponse with token, channel, expiry, and NATS URL
|
|
905
|
+
* @throws {AuthenticationError} If not authenticated
|
|
906
|
+
*
|
|
907
|
+
* @example
|
|
908
|
+
* const peer = await client.auth.getPeerToken(['alice', 'bob']);
|
|
909
|
+
* console.log(`Peer channel: ${peer.peerChannel}, expires in ${peer.expiresIn}s`);
|
|
910
|
+
*/
|
|
911
|
+
async getPeerToken(targetUsernames) {
|
|
912
|
+
return this.http.post("/api/v1/peer-token", {
|
|
913
|
+
target_usernames: targetUsernames
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Get a guest peer token for NATS communication without authentication.
|
|
918
|
+
*
|
|
919
|
+
* Guest peer tokens are rate-limited by IP address. They use the same
|
|
920
|
+
* response format as authenticated peer tokens.
|
|
921
|
+
*
|
|
922
|
+
* @param targetUsernames - Usernames of the tunneling spaces to communicate with
|
|
923
|
+
* @returns PeerTokenResponse with token, channel, expiry, and NATS URL
|
|
924
|
+
*
|
|
925
|
+
* @example
|
|
926
|
+
* const peer = await client.auth.getGuestPeerToken(['alice']);
|
|
927
|
+
* console.log(`Guest peer channel: ${peer.peerChannel}`);
|
|
928
|
+
*/
|
|
929
|
+
async getGuestPeerToken(targetUsernames) {
|
|
930
|
+
return this.http.post(
|
|
931
|
+
"/api/v1/nats/guest-peer-token",
|
|
932
|
+
{ target_usernames: targetUsernames },
|
|
933
|
+
{ includeAuth: false }
|
|
934
|
+
);
|
|
935
|
+
}
|
|
624
936
|
/**
|
|
625
937
|
* Get a satellite token for a specific audience (target service).
|
|
626
938
|
*
|
|
@@ -665,6 +977,59 @@ var AuthResource = class {
|
|
|
665
977
|
return { audience: aud, token: response.targetToken };
|
|
666
978
|
})
|
|
667
979
|
);
|
|
980
|
+
for (const [i, result] of results.entries()) {
|
|
981
|
+
if (result.status === "fulfilled") {
|
|
982
|
+
tokenMap.set(result.value.audience, result.value.token);
|
|
983
|
+
} else {
|
|
984
|
+
console.warn(
|
|
985
|
+
`[SyftHub] Failed to fetch satellite token for "${uniqueAudiences[i]}":`,
|
|
986
|
+
result.reason
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
return tokenMap;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Get a guest satellite token for a specific audience (target service).
|
|
994
|
+
*
|
|
995
|
+
* Guest tokens allow unauthenticated users to access policy-free endpoints.
|
|
996
|
+
* No authentication is required to call this method.
|
|
997
|
+
*
|
|
998
|
+
* @param audience - Target service identifier (username of the service owner)
|
|
999
|
+
* @returns Satellite token response with token and expiry
|
|
1000
|
+
* @throws {ValidationError} If audience is invalid or inactive
|
|
1001
|
+
*
|
|
1002
|
+
* @example
|
|
1003
|
+
* // Get a guest token for querying alice's policy-free endpoints
|
|
1004
|
+
* const tokenResponse = await client.auth.getGuestSatelliteToken('alice');
|
|
1005
|
+
*/
|
|
1006
|
+
async getGuestSatelliteToken(audience) {
|
|
1007
|
+
return this.http.get(
|
|
1008
|
+
"/api/v1/token/guest",
|
|
1009
|
+
{ aud: audience },
|
|
1010
|
+
{ includeAuth: false }
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Get guest satellite tokens for multiple audiences in parallel.
|
|
1015
|
+
*
|
|
1016
|
+
* No authentication is required to call this method.
|
|
1017
|
+
*
|
|
1018
|
+
* @param audiences - Array of unique audience identifiers (usernames)
|
|
1019
|
+
* @returns Map of audience to satellite token
|
|
1020
|
+
*
|
|
1021
|
+
* @example
|
|
1022
|
+
* const tokens = await client.auth.getGuestSatelliteTokens(['alice', 'bob']);
|
|
1023
|
+
*/
|
|
1024
|
+
async getGuestSatelliteTokens(audiences) {
|
|
1025
|
+
const uniqueAudiences = [...new Set(audiences)];
|
|
1026
|
+
const tokenMap = /* @__PURE__ */ new Map();
|
|
1027
|
+
const results = await Promise.allSettled(
|
|
1028
|
+
uniqueAudiences.map(async (aud) => {
|
|
1029
|
+
const response = await this.getGuestSatelliteToken(aud);
|
|
1030
|
+
return { audience: aud, token: response.targetToken };
|
|
1031
|
+
})
|
|
1032
|
+
);
|
|
668
1033
|
for (const result of results) {
|
|
669
1034
|
if (result.status === "fulfilled") {
|
|
670
1035
|
tokenMap.set(result.value.audience, result.value.token);
|
|
@@ -675,38 +1040,137 @@ var AuthResource = class {
|
|
|
675
1040
|
/**
|
|
676
1041
|
* Get transaction tokens for multiple endpoint owners.
|
|
677
1042
|
*
|
|
678
|
-
* Transaction tokens are
|
|
679
|
-
*
|
|
680
|
-
*
|
|
1043
|
+
* @deprecated Transaction tokens are no longer needed. Payments are handled
|
|
1044
|
+
* via the MPP 402 flow. This method is kept for backward compatibility and
|
|
1045
|
+
* always returns empty tokens/errors.
|
|
681
1046
|
*
|
|
682
|
-
*
|
|
1047
|
+
* @param ownerUsernames - Array of endpoint owner usernames (ignored)
|
|
1048
|
+
* @returns TransactionTokensResponse with empty tokens and errors
|
|
1049
|
+
*/
|
|
1050
|
+
async getTransactionTokens(ownerUsernames) {
|
|
1051
|
+
return { tokens: {}, errors: {} };
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Get the current access token (JWT or API token).
|
|
683
1055
|
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
1056
|
+
* This is used by the chat flow to pass the user's Hub token to the
|
|
1057
|
+
* aggregator for the MPP payment callback.
|
|
1058
|
+
*
|
|
1059
|
+
* @returns The current access token, or null if not authenticated
|
|
1060
|
+
*/
|
|
1061
|
+
getAccessToken() {
|
|
1062
|
+
const tokens = this.http.getTokens();
|
|
1063
|
+
return tokens?.accessToken ?? null;
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
// src/resources/aggregators.ts
|
|
1068
|
+
var AggregatorsResource = class {
|
|
1069
|
+
constructor(http) {
|
|
1070
|
+
this.http = http;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* List all aggregator configurations for the current user.
|
|
1074
|
+
*
|
|
1075
|
+
* @returns Array of UserAggregator objects
|
|
686
1076
|
* @throws {AuthenticationError} If not authenticated
|
|
687
1077
|
*
|
|
688
1078
|
* @example
|
|
689
|
-
*
|
|
690
|
-
* const
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
1079
|
+
* const aggregators = await client.users.aggregators.list();
|
|
1080
|
+
* for (const agg of aggregators) {
|
|
1081
|
+
* if (agg.isDefault) {
|
|
1082
|
+
* console.log(`Default: ${agg.name}`);
|
|
1083
|
+
* }
|
|
694
1084
|
* }
|
|
695
1085
|
*/
|
|
696
|
-
async
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1086
|
+
async list() {
|
|
1087
|
+
return this.http.get("/api/v1/users/me/aggregators");
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Get a specific aggregator configuration by ID.
|
|
1091
|
+
*
|
|
1092
|
+
* @param aggregatorId - The aggregator ID
|
|
1093
|
+
* @returns The UserAggregator object
|
|
1094
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1095
|
+
* @throws {NotFoundError} If aggregator not found
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* const agg = await client.users.aggregators.get(1);
|
|
1099
|
+
* console.log(`${agg.name}: ${agg.url}`);
|
|
1100
|
+
*/
|
|
1101
|
+
async get(aggregatorId) {
|
|
1102
|
+
return this.http.get(`/api/v1/users/me/aggregators/${aggregatorId}`);
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Create a new aggregator configuration.
|
|
1106
|
+
*
|
|
1107
|
+
* The first aggregator created is automatically set as the default.
|
|
1108
|
+
*
|
|
1109
|
+
* @param input - Aggregator creation input
|
|
1110
|
+
* @returns The created UserAggregator object
|
|
1111
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1112
|
+
* @throws {ValidationError} If input is invalid
|
|
1113
|
+
*
|
|
1114
|
+
* @example
|
|
1115
|
+
* const agg = await client.users.aggregators.create({
|
|
1116
|
+
* name: 'My Custom Aggregator',
|
|
1117
|
+
* url: 'https://my-aggregator.example.com'
|
|
1118
|
+
* });
|
|
1119
|
+
* console.log(`Created: ${agg.id}`);
|
|
1120
|
+
*/
|
|
1121
|
+
async create(input) {
|
|
1122
|
+
return this.http.post("/api/v1/users/me/aggregators", input);
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Update an aggregator configuration.
|
|
1126
|
+
*
|
|
1127
|
+
* Only provided fields will be updated.
|
|
1128
|
+
*
|
|
1129
|
+
* @param aggregatorId - The aggregator ID to update
|
|
1130
|
+
* @param input - Fields to update
|
|
1131
|
+
* @returns The updated UserAggregator object
|
|
1132
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1133
|
+
* @throws {NotFoundError} If aggregator not found
|
|
1134
|
+
* @throws {ValidationError} If input is invalid
|
|
1135
|
+
*
|
|
1136
|
+
* @example
|
|
1137
|
+
* const agg = await client.users.aggregators.update(1, {
|
|
1138
|
+
* name: 'Updated Name'
|
|
1139
|
+
* });
|
|
1140
|
+
*/
|
|
1141
|
+
async update(aggregatorId, input) {
|
|
1142
|
+
return this.http.put(`/api/v1/users/me/aggregators/${aggregatorId}`, input);
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Delete an aggregator configuration.
|
|
1146
|
+
*
|
|
1147
|
+
* @param aggregatorId - The aggregator ID to delete
|
|
1148
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1149
|
+
* @throws {NotFoundError} If aggregator not found
|
|
1150
|
+
*
|
|
1151
|
+
* @example
|
|
1152
|
+
* await client.users.aggregators.delete(1);
|
|
1153
|
+
*/
|
|
1154
|
+
async delete(aggregatorId) {
|
|
1155
|
+
await this.http.delete(`/api/v1/users/me/aggregators/${aggregatorId}`);
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Set an aggregator as the default.
|
|
1159
|
+
*
|
|
1160
|
+
* Only one aggregator can be the default at a time. Setting a new default
|
|
1161
|
+
* automatically unsets the previous one.
|
|
1162
|
+
*
|
|
1163
|
+
* @param aggregatorId - The aggregator ID to set as default
|
|
1164
|
+
* @returns The updated UserAggregator object with isDefault=true
|
|
1165
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1166
|
+
* @throws {NotFoundError} If aggregator not found
|
|
1167
|
+
*
|
|
1168
|
+
* @example
|
|
1169
|
+
* const agg = await client.users.aggregators.setDefault(2);
|
|
1170
|
+
* console.log(`${agg.name} is now the default`);
|
|
1171
|
+
*/
|
|
1172
|
+
async setDefault(aggregatorId) {
|
|
1173
|
+
return this.http.patch(`/api/v1/users/me/aggregators/${aggregatorId}/default`);
|
|
710
1174
|
}
|
|
711
1175
|
};
|
|
712
1176
|
|
|
@@ -715,6 +1179,34 @@ var UsersResource = class {
|
|
|
715
1179
|
constructor(http) {
|
|
716
1180
|
this.http = http;
|
|
717
1181
|
}
|
|
1182
|
+
_aggregators;
|
|
1183
|
+
/**
|
|
1184
|
+
* Access aggregator management operations.
|
|
1185
|
+
*
|
|
1186
|
+
* @returns AggregatorsResource for managing user's aggregator configurations
|
|
1187
|
+
*
|
|
1188
|
+
* @example
|
|
1189
|
+
* // List aggregators
|
|
1190
|
+
* const aggregators = await client.users.aggregators.list();
|
|
1191
|
+
* for (const agg of aggregators) {
|
|
1192
|
+
* console.log(`${agg.name}: ${agg.url}`);
|
|
1193
|
+
* }
|
|
1194
|
+
*
|
|
1195
|
+
* // Create aggregator
|
|
1196
|
+
* const agg = await client.users.aggregators.create({
|
|
1197
|
+
* name: 'My Aggregator',
|
|
1198
|
+
* url: 'https://my-aggregator.example.com'
|
|
1199
|
+
* });
|
|
1200
|
+
*
|
|
1201
|
+
* // Set as default
|
|
1202
|
+
* await client.users.aggregators.setDefault(agg.id);
|
|
1203
|
+
*/
|
|
1204
|
+
get aggregators() {
|
|
1205
|
+
if (!this._aggregators) {
|
|
1206
|
+
this._aggregators = new AggregatorsResource(this.http);
|
|
1207
|
+
}
|
|
1208
|
+
return this._aggregators;
|
|
1209
|
+
}
|
|
718
1210
|
/**
|
|
719
1211
|
* Update the current user's profile.
|
|
720
1212
|
*
|
|
@@ -774,6 +1266,50 @@ var UsersResource = class {
|
|
|
774
1266
|
async getAccountingCredentials() {
|
|
775
1267
|
return this.http.get("/api/v1/users/me/accounting");
|
|
776
1268
|
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Send a heartbeat to indicate this SyftAI Space is alive.
|
|
1271
|
+
*
|
|
1272
|
+
* The heartbeat mechanism allows SyftAI Spaces to signal their availability
|
|
1273
|
+
* to SyftHub. This should be called periodically (before the TTL expires)
|
|
1274
|
+
* to maintain the "active" status.
|
|
1275
|
+
*
|
|
1276
|
+
* @param input - Heartbeat parameters
|
|
1277
|
+
* @param input.url - Full URL of this space (e.g., "https://myspace.example.com").
|
|
1278
|
+
* The server extracts the domain from this URL.
|
|
1279
|
+
* @param input.ttlSeconds - Time-to-live in seconds (1-3600). The server caps this
|
|
1280
|
+
* at a maximum of 600 seconds (10 minutes). Default is 300
|
|
1281
|
+
* seconds (5 minutes).
|
|
1282
|
+
* @returns HeartbeatResponse containing status, expiry time, domain, and effective TTL
|
|
1283
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1284
|
+
* @throws {ValidationError} If URL or TTL is invalid
|
|
1285
|
+
*
|
|
1286
|
+
* @example
|
|
1287
|
+
* // Send heartbeat with default TTL (300 seconds)
|
|
1288
|
+
* const response = await client.users.sendHeartbeat({
|
|
1289
|
+
* url: 'https://myspace.example.com'
|
|
1290
|
+
* });
|
|
1291
|
+
* console.log(`Next heartbeat before: ${response.expiresAt}`);
|
|
1292
|
+
*
|
|
1293
|
+
* @example
|
|
1294
|
+
* // Send heartbeat with custom TTL
|
|
1295
|
+
* const response = await client.users.sendHeartbeat({
|
|
1296
|
+
* url: 'https://myspace.example.com',
|
|
1297
|
+
* ttlSeconds: 600 // Maximum allowed
|
|
1298
|
+
* });
|
|
1299
|
+
*/
|
|
1300
|
+
async sendHeartbeat(input) {
|
|
1301
|
+
const response = await this.http.post("/api/v1/users/me/heartbeat", {
|
|
1302
|
+
url: input.url,
|
|
1303
|
+
ttl_seconds: input.ttlSeconds ?? 300
|
|
1304
|
+
});
|
|
1305
|
+
return {
|
|
1306
|
+
status: response.status,
|
|
1307
|
+
receivedAt: new Date(response.received_at),
|
|
1308
|
+
expiresAt: new Date(response.expires_at),
|
|
1309
|
+
domain: response.domain,
|
|
1310
|
+
ttlSeconds: response.ttl_seconds
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
777
1313
|
};
|
|
778
1314
|
|
|
779
1315
|
// src/pagination.ts
|
|
@@ -902,14 +1438,12 @@ var MyEndpointsResource = class {
|
|
|
902
1438
|
* Create a new endpoint.
|
|
903
1439
|
*
|
|
904
1440
|
* @param input - Endpoint creation details
|
|
905
|
-
* @param organizationId - Optional organization ID (for org-owned endpoints)
|
|
906
1441
|
* @returns The created Endpoint
|
|
907
1442
|
* @throws {AuthenticationError} If not authenticated
|
|
908
1443
|
* @throws {ValidationError} If input validation fails
|
|
909
1444
|
*/
|
|
910
|
-
async create(input
|
|
911
|
-
|
|
912
|
-
return this.http.post("/api/v1/endpoints", body);
|
|
1445
|
+
async create(input) {
|
|
1446
|
+
return this.http.post("/api/v1/endpoints", input);
|
|
913
1447
|
}
|
|
914
1448
|
/**
|
|
915
1449
|
* Get a specific endpoint by path.
|
|
@@ -970,7 +1504,6 @@ var MyEndpointsResource = class {
|
|
|
970
1504
|
* 3. Is ATOMIC: either all endpoints sync successfully, or none do
|
|
971
1505
|
*
|
|
972
1506
|
* Important Notes:
|
|
973
|
-
* - Organization endpoints are NOT affected
|
|
974
1507
|
* - Stars on existing endpoints will be lost (reset to 0)
|
|
975
1508
|
* - Endpoint IDs will change (new IDs assigned)
|
|
976
1509
|
* - Maximum 100 endpoints per sync request
|
|
@@ -1044,17 +1577,23 @@ var HubResource = class {
|
|
|
1044
1577
|
/**
|
|
1045
1578
|
* Browse all public endpoints.
|
|
1046
1579
|
*
|
|
1047
|
-
* @param options -
|
|
1580
|
+
* @param options - Filter and pagination options
|
|
1048
1581
|
* @returns PageIterator that lazily fetches endpoints
|
|
1582
|
+
*
|
|
1583
|
+
* @example
|
|
1584
|
+
* // Browse only model endpoints
|
|
1585
|
+
* const models = await client.hub.browse({ endpointType: 'model' }).firstPage();
|
|
1049
1586
|
*/
|
|
1050
1587
|
browse(options) {
|
|
1051
1588
|
const pageSize = options?.pageSize ?? 20;
|
|
1052
1589
|
return new PageIterator(async (skip, limit) => {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1590
|
+
const params = { skip, limit };
|
|
1591
|
+
if (options?.endpointType !== void 0) {
|
|
1592
|
+
params["endpoint_type"] = options.endpointType;
|
|
1593
|
+
}
|
|
1594
|
+
return this.http.get("/api/v1/endpoints/public", params, {
|
|
1595
|
+
includeAuth: false
|
|
1596
|
+
});
|
|
1058
1597
|
}, pageSize);
|
|
1059
1598
|
}
|
|
1060
1599
|
/**
|
|
@@ -1062,19 +1601,57 @@ var HubResource = class {
|
|
|
1062
1601
|
*
|
|
1063
1602
|
* @param options - Filter and pagination options
|
|
1064
1603
|
* @returns PageIterator that lazily fetches endpoints
|
|
1604
|
+
*
|
|
1605
|
+
* @example
|
|
1606
|
+
* // Get trending models only
|
|
1607
|
+
* const models = await client.hub.trending({ endpointType: 'model' }).firstPage();
|
|
1065
1608
|
*/
|
|
1066
1609
|
trending(options) {
|
|
1067
1610
|
const pageSize = options?.pageSize ?? 20;
|
|
1068
1611
|
return new PageIterator(async (skip, limit) => {
|
|
1069
1612
|
const params = { skip, limit };
|
|
1613
|
+
if (options?.endpointType !== void 0) {
|
|
1614
|
+
params["endpoint_type"] = options.endpointType;
|
|
1615
|
+
}
|
|
1070
1616
|
if (options?.minStars !== void 0) {
|
|
1071
|
-
params["
|
|
1617
|
+
params["min_stars"] = options.minStars;
|
|
1072
1618
|
}
|
|
1073
1619
|
return this.http.get("/api/v1/endpoints/trending", params, {
|
|
1074
1620
|
includeAuth: false
|
|
1075
1621
|
});
|
|
1076
1622
|
}, pageSize);
|
|
1077
1623
|
}
|
|
1624
|
+
/**
|
|
1625
|
+
* List endpoints accessible to unauthenticated (guest) users.
|
|
1626
|
+
*
|
|
1627
|
+
* Guest-accessible endpoints are public, active, and have no policies attached.
|
|
1628
|
+
* No authentication is required to call this method.
|
|
1629
|
+
*
|
|
1630
|
+
* @param options - Filter and pagination options
|
|
1631
|
+
* @returns PageIterator that lazily fetches guest-accessible endpoints
|
|
1632
|
+
*
|
|
1633
|
+
* @example
|
|
1634
|
+
* // List all guest-accessible endpoints
|
|
1635
|
+
* for await (const endpoint of client.hub.guestAccessible()) {
|
|
1636
|
+
* console.log(`${endpoint.ownerUsername}/${endpoint.slug}: ${endpoint.name}`);
|
|
1637
|
+
* }
|
|
1638
|
+
*
|
|
1639
|
+
* @example
|
|
1640
|
+
* // List only guest-accessible models
|
|
1641
|
+
* const models = await client.hub.guestAccessible({ endpointType: 'model' }).firstPage();
|
|
1642
|
+
*/
|
|
1643
|
+
guestAccessible(options) {
|
|
1644
|
+
const pageSize = options?.pageSize ?? 20;
|
|
1645
|
+
return new PageIterator(async (skip, limit) => {
|
|
1646
|
+
const params = { skip, limit };
|
|
1647
|
+
if (options?.endpointType !== void 0) {
|
|
1648
|
+
params["endpoint_type"] = options.endpointType;
|
|
1649
|
+
}
|
|
1650
|
+
return this.http.get("/api/v1/endpoints/guest-accessible", params, {
|
|
1651
|
+
includeAuth: false
|
|
1652
|
+
});
|
|
1653
|
+
}, pageSize);
|
|
1654
|
+
}
|
|
1078
1655
|
/**
|
|
1079
1656
|
* Search for endpoints using semantic search.
|
|
1080
1657
|
*
|
|
@@ -1170,6 +1747,26 @@ var HubResource = class {
|
|
|
1170
1747
|
const endpointId = await this.resolveEndpointId(path);
|
|
1171
1748
|
await this.http.delete(`/api/v1/endpoints/${endpointId}/star`);
|
|
1172
1749
|
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Get the owner/slug paths of a collective's approved member endpoints,
|
|
1752
|
+
* optionally narrowed to a single shared-endpoint subset.
|
|
1753
|
+
*
|
|
1754
|
+
* Used by the chat resource to expand both `collective/<slug>` and
|
|
1755
|
+
* `collective/<slug>/<shared-slug>` data-source references into the
|
|
1756
|
+
* individual endpoint paths before building the aggregator request.
|
|
1757
|
+
*
|
|
1758
|
+
* @param slug - The collective slug (e.g. "genomics-research")
|
|
1759
|
+
* @param sharedSlug - Optional curated-subset slug. When provided, only the
|
|
1760
|
+
* intersection of the subset's configured endpoints with the collective's
|
|
1761
|
+
* currently approved members is returned. Omit for "all approved members".
|
|
1762
|
+
* @returns Array of "owner/slug" path strings
|
|
1763
|
+
* @throws {NotFoundError} If the collective (or shared endpoint) does not exist
|
|
1764
|
+
*/
|
|
1765
|
+
async getCollectiveEndpointPaths(slug, sharedSlug) {
|
|
1766
|
+
const base = `/api/v1/collectives/by-slug/${encodeURIComponent(slug)}`;
|
|
1767
|
+
const path = sharedSlug ? `${base}/shared-endpoints/${encodeURIComponent(sharedSlug)}/endpoint-paths` : `${base}/endpoint-paths`;
|
|
1768
|
+
return this.http.get(path, {}, { includeAuth: false });
|
|
1769
|
+
}
|
|
1173
1770
|
/**
|
|
1174
1771
|
* Check if you have starred an endpoint.
|
|
1175
1772
|
*
|
|
@@ -1187,489 +1784,408 @@ var HubResource = class {
|
|
|
1187
1784
|
}
|
|
1188
1785
|
};
|
|
1189
1786
|
|
|
1190
|
-
// src/models/common.ts
|
|
1191
|
-
var Visibility = {
|
|
1192
|
-
/** Visible to everyone, no authentication required */
|
|
1193
|
-
PUBLIC: "public",
|
|
1194
|
-
/** Only visible to the owner and collaborators */
|
|
1195
|
-
PRIVATE: "private",
|
|
1196
|
-
/** Visible to authenticated users within the organization */
|
|
1197
|
-
INTERNAL: "internal"
|
|
1198
|
-
};
|
|
1199
|
-
var EndpointType = {
|
|
1200
|
-
/** Machine learning model endpoint */
|
|
1201
|
-
MODEL: "model",
|
|
1202
|
-
/** Data source endpoint */
|
|
1203
|
-
DATA_SOURCE: "data_source"
|
|
1204
|
-
};
|
|
1205
|
-
var UserRole = {
|
|
1206
|
-
/** Administrator with full access */
|
|
1207
|
-
ADMIN: "admin",
|
|
1208
|
-
/** Regular user */
|
|
1209
|
-
USER: "user",
|
|
1210
|
-
/** Guest user with limited access */
|
|
1211
|
-
GUEST: "guest"
|
|
1212
|
-
};
|
|
1213
|
-
var OrganizationRole = {
|
|
1214
|
-
/** Organization owner with full control */
|
|
1215
|
-
OWNER: "owner",
|
|
1216
|
-
/** Administrator with management privileges */
|
|
1217
|
-
ADMIN: "admin",
|
|
1218
|
-
/** Regular member */
|
|
1219
|
-
MEMBER: "member"
|
|
1220
|
-
};
|
|
1221
|
-
|
|
1222
|
-
// src/models/endpoint.ts
|
|
1223
|
-
function getEndpointOwnerType(endpoint) {
|
|
1224
|
-
return endpoint.organizationId !== null ? "organization" : "user";
|
|
1225
|
-
}
|
|
1226
|
-
function getEndpointPublicPath(endpoint) {
|
|
1227
|
-
return `${endpoint.ownerUsername}/${endpoint.slug}`;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
// src/models/accounting.ts
|
|
1231
|
-
var TransactionStatus = {
|
|
1232
|
-
/** Transaction created, awaiting confirmation */
|
|
1233
|
-
PENDING: "pending",
|
|
1234
|
-
/** Transaction confirmed, funds transferred */
|
|
1235
|
-
COMPLETED: "completed",
|
|
1236
|
-
/** Transaction cancelled, no funds transferred */
|
|
1237
|
-
CANCELLED: "cancelled"
|
|
1238
|
-
};
|
|
1239
|
-
var CreatorType = {
|
|
1240
|
-
/** System-initiated transaction */
|
|
1241
|
-
SYSTEM: "system",
|
|
1242
|
-
/** Sender-initiated transaction */
|
|
1243
|
-
SENDER: "sender",
|
|
1244
|
-
/** Recipient-initiated transaction (delegated) */
|
|
1245
|
-
RECIPIENT: "recipient"
|
|
1246
|
-
};
|
|
1247
|
-
function parseTransaction(response) {
|
|
1248
|
-
return {
|
|
1249
|
-
...response,
|
|
1250
|
-
createdAt: new Date(response.createdAt),
|
|
1251
|
-
resolvedAt: response.resolvedAt ? new Date(response.resolvedAt) : null
|
|
1252
|
-
};
|
|
1253
|
-
}
|
|
1254
|
-
function isTransactionPending(tx) {
|
|
1255
|
-
return tx.status === TransactionStatus.PENDING;
|
|
1256
|
-
}
|
|
1257
|
-
function isTransactionCompleted(tx) {
|
|
1258
|
-
return tx.status === TransactionStatus.COMPLETED;
|
|
1259
|
-
}
|
|
1260
|
-
function isTransactionCancelled(tx) {
|
|
1261
|
-
return tx.status === TransactionStatus.CANCELLED;
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
1787
|
// src/resources/accounting.ts
|
|
1265
|
-
init_errors();
|
|
1266
|
-
async function handleResponseError(response) {
|
|
1267
|
-
if (response.ok) return;
|
|
1268
|
-
let detail;
|
|
1269
|
-
try {
|
|
1270
|
-
const body = await response.json();
|
|
1271
|
-
detail = body.detail ?? body.message ?? JSON.stringify(body);
|
|
1272
|
-
} catch {
|
|
1273
|
-
detail = await response.text() || `HTTP ${response.status}`;
|
|
1274
|
-
}
|
|
1275
|
-
switch (response.status) {
|
|
1276
|
-
case 401:
|
|
1277
|
-
throw new AuthenticationError(`Authentication failed: ${detail}`);
|
|
1278
|
-
case 403:
|
|
1279
|
-
throw new AuthorizationError(`Permission denied: ${detail}`);
|
|
1280
|
-
case 404:
|
|
1281
|
-
throw new NotFoundError(`Not found: ${detail}`);
|
|
1282
|
-
case 422:
|
|
1283
|
-
throw new ValidationError(`Validation error: ${detail}`);
|
|
1284
|
-
default:
|
|
1285
|
-
throw new APIError(`Accounting API error: ${detail}`, response.status);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
function createBasicAuth(email, password) {
|
|
1289
|
-
const credentials = `${email}:${password}`;
|
|
1290
|
-
const encoded = typeof btoa !== "undefined" ? btoa(credentials) : Buffer.from(credentials).toString("base64");
|
|
1291
|
-
return `Basic ${encoded}`;
|
|
1292
|
-
}
|
|
1293
1788
|
var AccountingResource = class {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
password;
|
|
1297
|
-
timeout;
|
|
1298
|
-
authHeader;
|
|
1299
|
-
constructor(options) {
|
|
1300
|
-
this.baseUrl = options.url.replace(/\/$/, "");
|
|
1301
|
-
this.email = options.email;
|
|
1302
|
-
this.password = options.password;
|
|
1303
|
-
this.timeout = options.timeout ?? 3e4;
|
|
1304
|
-
this.authHeader = createBasicAuth(this.email, this.password);
|
|
1305
|
-
}
|
|
1306
|
-
// ===========================================================================
|
|
1307
|
-
// Private HTTP Methods
|
|
1308
|
-
// ===========================================================================
|
|
1309
|
-
/**
|
|
1310
|
-
* Make an authenticated request to the accounting service.
|
|
1311
|
-
*/
|
|
1312
|
-
async request(method, path, options) {
|
|
1313
|
-
const url = new URL(path, this.baseUrl);
|
|
1314
|
-
if (options?.params) {
|
|
1315
|
-
for (const [key, value] of Object.entries(options.params)) {
|
|
1316
|
-
url.searchParams.set(key, String(value));
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
const controller = new AbortController();
|
|
1320
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1321
|
-
try {
|
|
1322
|
-
const response = await fetch(url.toString(), {
|
|
1323
|
-
method,
|
|
1324
|
-
headers: {
|
|
1325
|
-
Authorization: this.authHeader,
|
|
1326
|
-
"Content-Type": "application/json",
|
|
1327
|
-
Accept: "application/json"
|
|
1328
|
-
},
|
|
1329
|
-
body: options?.body ? JSON.stringify(options.body) : void 0,
|
|
1330
|
-
signal: controller.signal
|
|
1331
|
-
});
|
|
1332
|
-
await handleResponseError(response);
|
|
1333
|
-
if (response.status === 204) {
|
|
1334
|
-
return {};
|
|
1335
|
-
}
|
|
1336
|
-
return await response.json();
|
|
1337
|
-
} catch (error) {
|
|
1338
|
-
if (error instanceof SyftHubError) {
|
|
1339
|
-
throw error;
|
|
1340
|
-
}
|
|
1341
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
1342
|
-
throw new APIError("Request timeout", 408);
|
|
1343
|
-
}
|
|
1344
|
-
throw new APIError(
|
|
1345
|
-
`Accounting request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1346
|
-
0
|
|
1347
|
-
);
|
|
1348
|
-
} finally {
|
|
1349
|
-
clearTimeout(timeoutId);
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
|
-
* Make a request using Bearer token auth (for delegated transactions).
|
|
1354
|
-
*/
|
|
1355
|
-
async requestWithToken(method, path, token, options) {
|
|
1356
|
-
const url = new URL(path, this.baseUrl);
|
|
1357
|
-
const controller = new AbortController();
|
|
1358
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1359
|
-
try {
|
|
1360
|
-
const response = await fetch(url.toString(), {
|
|
1361
|
-
method,
|
|
1362
|
-
headers: {
|
|
1363
|
-
Authorization: `Bearer ${token}`,
|
|
1364
|
-
"Content-Type": "application/json",
|
|
1365
|
-
Accept: "application/json"
|
|
1366
|
-
},
|
|
1367
|
-
body: options?.body ? JSON.stringify(options.body) : void 0,
|
|
1368
|
-
signal: controller.signal
|
|
1369
|
-
});
|
|
1370
|
-
await handleResponseError(response);
|
|
1371
|
-
if (response.status === 204) {
|
|
1372
|
-
return {};
|
|
1373
|
-
}
|
|
1374
|
-
return await response.json();
|
|
1375
|
-
} catch (error) {
|
|
1376
|
-
if (error instanceof SyftHubError) {
|
|
1377
|
-
throw error;
|
|
1378
|
-
}
|
|
1379
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
1380
|
-
throw new APIError("Request timeout", 408);
|
|
1381
|
-
}
|
|
1382
|
-
throw new APIError(
|
|
1383
|
-
`Accounting request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1384
|
-
0
|
|
1385
|
-
);
|
|
1386
|
-
} finally {
|
|
1387
|
-
clearTimeout(timeoutId);
|
|
1388
|
-
}
|
|
1789
|
+
constructor(http) {
|
|
1790
|
+
this.http = http;
|
|
1389
1791
|
}
|
|
1390
1792
|
// ===========================================================================
|
|
1391
|
-
//
|
|
1793
|
+
// Wallet Operations
|
|
1392
1794
|
// ===========================================================================
|
|
1393
1795
|
/**
|
|
1394
|
-
* Get the current user's
|
|
1796
|
+
* Get the current user's wallet information.
|
|
1395
1797
|
*
|
|
1396
|
-
* @returns
|
|
1397
|
-
* @throws {AuthenticationError} If
|
|
1398
|
-
* @throws {APIError} On other errors
|
|
1798
|
+
* @returns WalletInfo with address and existence status
|
|
1799
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1399
1800
|
*
|
|
1400
1801
|
* @example
|
|
1401
1802
|
* ```typescript
|
|
1402
|
-
* const
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1803
|
+
* const wallet = await client.accounting.getWallet();
|
|
1804
|
+
* if (wallet.exists) {
|
|
1805
|
+
* console.log(`Wallet address: ${wallet.address}`);
|
|
1806
|
+
* } else {
|
|
1807
|
+
* console.log('No wallet configured');
|
|
1808
|
+
* }
|
|
1405
1809
|
* ```
|
|
1406
1810
|
*/
|
|
1407
|
-
async
|
|
1408
|
-
return this.
|
|
1811
|
+
async getWallet() {
|
|
1812
|
+
return this.http.get("/api/v1/wallet/");
|
|
1409
1813
|
}
|
|
1410
1814
|
/**
|
|
1411
|
-
*
|
|
1815
|
+
* Get the current user's wallet balance and recent transactions.
|
|
1412
1816
|
*
|
|
1413
|
-
* @
|
|
1414
|
-
* @
|
|
1415
|
-
* @throws {AuthenticationError} If current password is wrong
|
|
1416
|
-
* @throws {ValidationError} If new password doesn't meet requirements
|
|
1817
|
+
* @returns WalletBalance with balance, currency, and recent transactions
|
|
1818
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1417
1819
|
*
|
|
1418
1820
|
* @example
|
|
1419
1821
|
* ```typescript
|
|
1420
|
-
* await accounting.
|
|
1822
|
+
* const balance = await client.accounting.getBalance();
|
|
1823
|
+
* console.log(`Balance: ${balance.balance} ${balance.currency}`);
|
|
1824
|
+
* console.log(`Wallet configured: ${balance.wallet_configured}`);
|
|
1421
1825
|
* ```
|
|
1422
1826
|
*/
|
|
1423
|
-
async
|
|
1424
|
-
|
|
1425
|
-
body: {
|
|
1426
|
-
oldPassword: currentPassword,
|
|
1427
|
-
newPassword
|
|
1428
|
-
}
|
|
1429
|
-
});
|
|
1827
|
+
async getBalance() {
|
|
1828
|
+
return this.http.get("/api/v1/wallet/balance");
|
|
1430
1829
|
}
|
|
1431
1830
|
/**
|
|
1432
|
-
*
|
|
1831
|
+
* Get the current user's wallet transactions.
|
|
1433
1832
|
*
|
|
1434
|
-
* @
|
|
1435
|
-
* @throws {AuthenticationError} If
|
|
1833
|
+
* @returns Array of WalletTransaction objects
|
|
1834
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1436
1835
|
*
|
|
1437
1836
|
* @example
|
|
1438
1837
|
* ```typescript
|
|
1439
|
-
* await accounting.
|
|
1838
|
+
* const transactions = await client.accounting.getTransactions();
|
|
1839
|
+
* for (const tx of transactions) {
|
|
1840
|
+
* console.log(`${tx.created_at}: ${tx.amount} ${tx.status}`);
|
|
1841
|
+
* }
|
|
1440
1842
|
* ```
|
|
1441
1843
|
*/
|
|
1442
|
-
async
|
|
1443
|
-
|
|
1444
|
-
body: { organization }
|
|
1445
|
-
});
|
|
1844
|
+
async getTransactions() {
|
|
1845
|
+
return this.http.get("/api/v1/wallet/transactions");
|
|
1446
1846
|
}
|
|
1447
|
-
// ===========================================================================
|
|
1448
|
-
// Transaction Listing
|
|
1449
|
-
// ===========================================================================
|
|
1450
1847
|
/**
|
|
1451
|
-
*
|
|
1848
|
+
* Create a new wallet for the current user.
|
|
1452
1849
|
*
|
|
1453
|
-
*
|
|
1850
|
+
* Generates a new wallet with a fresh keypair. The wallet address
|
|
1851
|
+
* is returned and stored on the server.
|
|
1454
1852
|
*
|
|
1455
|
-
* @
|
|
1456
|
-
* @
|
|
1853
|
+
* @returns Object with the new wallet address
|
|
1854
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1855
|
+
* @throws {ValidationError} If user already has a wallet
|
|
1457
1856
|
*
|
|
1458
1857
|
* @example
|
|
1459
1858
|
* ```typescript
|
|
1460
|
-
*
|
|
1461
|
-
*
|
|
1462
|
-
* console.log(`${tx.createdAt}: ${tx.amount} from ${tx.senderEmail}`);
|
|
1463
|
-
* }
|
|
1464
|
-
*
|
|
1465
|
-
* // Get first page only
|
|
1466
|
-
* const firstPage = await accounting.getTransactions().firstPage();
|
|
1467
|
-
*
|
|
1468
|
-
* // Get all transactions
|
|
1469
|
-
* const allTxs = await accounting.getTransactions().all();
|
|
1859
|
+
* const result = await client.accounting.createWallet();
|
|
1860
|
+
* console.log(`New wallet address: ${result.address}`);
|
|
1470
1861
|
* ```
|
|
1471
1862
|
*/
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
return new PageIterator(async (skip, limit) => {
|
|
1475
|
-
const response = await this.request("GET", "/transactions", {
|
|
1476
|
-
params: { skip, limit }
|
|
1477
|
-
});
|
|
1478
|
-
return response.map(parseTransaction);
|
|
1479
|
-
}, pageSize);
|
|
1863
|
+
async createWallet() {
|
|
1864
|
+
return this.http.post("/api/v1/wallet/create", {});
|
|
1480
1865
|
}
|
|
1481
1866
|
/**
|
|
1482
|
-
*
|
|
1867
|
+
* Import an existing wallet using a private key.
|
|
1483
1868
|
*
|
|
1484
|
-
* @param
|
|
1485
|
-
* @returns
|
|
1486
|
-
* @throws {
|
|
1869
|
+
* @param privateKey - The private key to import
|
|
1870
|
+
* @returns Object with the imported wallet address
|
|
1871
|
+
* @throws {AuthenticationError} If not authenticated
|
|
1872
|
+
* @throws {ValidationError} If the private key is invalid
|
|
1487
1873
|
*
|
|
1488
1874
|
* @example
|
|
1489
1875
|
* ```typescript
|
|
1490
|
-
* const
|
|
1491
|
-
* console.log(`
|
|
1876
|
+
* const result = await client.accounting.importWallet('0x...');
|
|
1877
|
+
* console.log(`Imported wallet address: ${result.address}`);
|
|
1492
1878
|
* ```
|
|
1493
1879
|
*/
|
|
1494
|
-
async
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1880
|
+
async importWallet(privateKey) {
|
|
1881
|
+
return this.http.post("/api/v1/wallet/import", {
|
|
1882
|
+
private_key: privateKey
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
};
|
|
1886
|
+
function createAccountingResource(options) {
|
|
1887
|
+
throw new Error(
|
|
1888
|
+
"createAccountingResource() is deprecated. The wallet API is now accessed through the SyftHubClient. Use client.initAccounting() after login instead."
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// src/resources/agent.ts
|
|
1893
|
+
init_errors();
|
|
1894
|
+
var AgentSessionError = class extends SyftHubError {
|
|
1895
|
+
constructor(message, code) {
|
|
1896
|
+
super(message);
|
|
1897
|
+
this.code = code;
|
|
1898
|
+
this.name = "AgentSessionError";
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
var AgentResource = class {
|
|
1902
|
+
constructor(auth, aggregatorUrl) {
|
|
1903
|
+
this.auth = auth;
|
|
1904
|
+
this.aggregatorUrl = aggregatorUrl;
|
|
1500
1905
|
}
|
|
1501
|
-
// ===========================================================================
|
|
1502
|
-
// Direct Transaction Operations
|
|
1503
|
-
// ===========================================================================
|
|
1504
1906
|
/**
|
|
1505
|
-
*
|
|
1907
|
+
* Start a new agent session.
|
|
1506
1908
|
*
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1510
|
-
* @param input - Transaction details
|
|
1511
|
-
* @returns Transaction in PENDING status
|
|
1512
|
-
* @throws {ValidationError} If amount <= 0 or insufficient balance
|
|
1513
|
-
*
|
|
1514
|
-
* @example
|
|
1515
|
-
* ```typescript
|
|
1516
|
-
* const tx = await accounting.createTransaction({
|
|
1517
|
-
* recipientEmail: 'bob@example.com',
|
|
1518
|
-
* amount: 10.0,
|
|
1519
|
-
* appName: 'syftai-space',
|
|
1520
|
-
* appEpPath: 'alice/my-model'
|
|
1521
|
-
* });
|
|
1522
|
-
* console.log(`Created transaction ${tx.id}: ${tx.status}`);
|
|
1523
|
-
*
|
|
1524
|
-
* // Later, confirm or cancel
|
|
1525
|
-
* await accounting.confirmTransaction(tx.id);
|
|
1526
|
-
* ```
|
|
1909
|
+
* @param options - Session options including prompt, endpoint, and config
|
|
1910
|
+
* @returns An AgentSessionClient for interacting with the session
|
|
1527
1911
|
*/
|
|
1528
|
-
async
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1912
|
+
async startSession(options) {
|
|
1913
|
+
let owner;
|
|
1914
|
+
let slug;
|
|
1915
|
+
if (typeof options.endpoint === "string") {
|
|
1916
|
+
const parts = options.endpoint.split("/");
|
|
1917
|
+
if (parts.length !== 2) {
|
|
1918
|
+
throw new AgentSessionError(
|
|
1919
|
+
`Endpoint must be in 'owner/slug' format, got: ${options.endpoint}`
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
owner = parts[0];
|
|
1923
|
+
slug = parts[1];
|
|
1924
|
+
} else {
|
|
1925
|
+
owner = options.endpoint.owner;
|
|
1926
|
+
slug = options.endpoint.slug;
|
|
1927
|
+
}
|
|
1928
|
+
const satResponse = await this.auth.getSatelliteToken(owner);
|
|
1929
|
+
const peerResponse = await this.auth.getPeerToken([owner]);
|
|
1930
|
+
const wsUrl = this.aggregatorUrl.replace(/^http/, "ws") + "/agent/session";
|
|
1931
|
+
const ws = new WebSocket(wsUrl);
|
|
1932
|
+
await new Promise((resolve, reject) => {
|
|
1933
|
+
const onOpen = () => {
|
|
1934
|
+
ws.removeEventListener("error", onError);
|
|
1935
|
+
resolve();
|
|
1936
|
+
};
|
|
1937
|
+
const onError = (_e) => {
|
|
1938
|
+
ws.removeEventListener("open", onOpen);
|
|
1939
|
+
reject(new AgentSessionError("Failed to connect to agent WebSocket"));
|
|
1940
|
+
};
|
|
1941
|
+
ws.addEventListener("open", onOpen, { once: true });
|
|
1942
|
+
ws.addEventListener("error", onError, { once: true });
|
|
1943
|
+
if (options.signal) {
|
|
1944
|
+
options.signal.addEventListener(
|
|
1945
|
+
"abort",
|
|
1946
|
+
() => {
|
|
1947
|
+
ws.close();
|
|
1948
|
+
reject(new AgentSessionError("Session start aborted"));
|
|
1949
|
+
},
|
|
1950
|
+
{ once: true }
|
|
1951
|
+
);
|
|
1538
1952
|
}
|
|
1539
1953
|
});
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
*/
|
|
1559
|
-
async confirmTransaction(transactionId) {
|
|
1560
|
-
const response = await this.request(
|
|
1561
|
-
"POST",
|
|
1562
|
-
`/transactions/${transactionId}/confirm`
|
|
1954
|
+
const startPayload = {
|
|
1955
|
+
prompt: options.prompt,
|
|
1956
|
+
endpoint: { owner, slug },
|
|
1957
|
+
satellite_token: satResponse.targetToken,
|
|
1958
|
+
peer_token: peerResponse.peerToken,
|
|
1959
|
+
peer_channel: peerResponse.peerChannel
|
|
1960
|
+
};
|
|
1961
|
+
if (options.config) {
|
|
1962
|
+
startPayload.config = options.config;
|
|
1963
|
+
}
|
|
1964
|
+
if (options.messages) {
|
|
1965
|
+
startPayload.messages = options.messages;
|
|
1966
|
+
}
|
|
1967
|
+
ws.send(
|
|
1968
|
+
JSON.stringify({
|
|
1969
|
+
type: "session.start",
|
|
1970
|
+
payload: startPayload
|
|
1971
|
+
})
|
|
1563
1972
|
);
|
|
1564
|
-
|
|
1973
|
+
const response = await new Promise((resolve, reject) => {
|
|
1974
|
+
const onMessage = (event) => {
|
|
1975
|
+
try {
|
|
1976
|
+
const data = JSON.parse(event.data);
|
|
1977
|
+
if (data.type === "session.created") {
|
|
1978
|
+
ws.removeEventListener("message", onMessage);
|
|
1979
|
+
resolve({
|
|
1980
|
+
session_id: data.session_id || data.payload?.session_id
|
|
1981
|
+
});
|
|
1982
|
+
} else if (data.type === "agent.error") {
|
|
1983
|
+
ws.removeEventListener("message", onMessage);
|
|
1984
|
+
reject(
|
|
1985
|
+
new AgentSessionError(
|
|
1986
|
+
data.payload?.message || "Session start failed",
|
|
1987
|
+
data.payload?.code
|
|
1988
|
+
)
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1991
|
+
} catch {
|
|
1992
|
+
reject(new AgentSessionError("Failed to parse session response"));
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
ws.addEventListener("message", onMessage);
|
|
1996
|
+
});
|
|
1997
|
+
return new AgentSessionClient(ws, response.session_id);
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
var AgentSessionClient = class {
|
|
2001
|
+
constructor(ws, sessionId) {
|
|
2002
|
+
this.ws = ws;
|
|
2003
|
+
this.sessionId = sessionId;
|
|
2004
|
+
this.ws.addEventListener("message", (event) => {
|
|
2005
|
+
this._handleMessage(event);
|
|
2006
|
+
});
|
|
2007
|
+
this.ws.addEventListener("close", () => {
|
|
2008
|
+
this._handleClose();
|
|
2009
|
+
});
|
|
2010
|
+
this.ws.addEventListener("error", () => {
|
|
2011
|
+
this._state = "error";
|
|
2012
|
+
this._handleClose();
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
_state = "running";
|
|
2016
|
+
_sequenceCounter = 0;
|
|
2017
|
+
_messageQueue = [];
|
|
2018
|
+
_messageResolvers = [];
|
|
2019
|
+
_closed = false;
|
|
2020
|
+
/** Current session state */
|
|
2021
|
+
get state() {
|
|
2022
|
+
return this._state;
|
|
1565
2023
|
}
|
|
1566
2024
|
/**
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
* Cancels the transaction without transferring funds.
|
|
1570
|
-
* Can be called by either the sender or recipient.
|
|
1571
|
-
*
|
|
1572
|
-
* @param transactionId - The transaction ID to cancel
|
|
1573
|
-
* @returns Transaction in CANCELLED status
|
|
1574
|
-
* @throws {NotFoundError} If transaction not found
|
|
1575
|
-
* @throws {ValidationError} If transaction is not in PENDING status
|
|
2025
|
+
* Async generator yielding agent events.
|
|
1576
2026
|
*
|
|
1577
2027
|
* @example
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1581
|
-
* ```
|
|
2028
|
+
* for await (const event of session.events()) {
|
|
2029
|
+
* console.log(event.type, event.payload);
|
|
2030
|
+
* }
|
|
1582
2031
|
*/
|
|
1583
|
-
async
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
2032
|
+
async *events() {
|
|
2033
|
+
while (!this._closed) {
|
|
2034
|
+
const event = await this._nextEvent();
|
|
2035
|
+
if (event === null) break;
|
|
2036
|
+
yield event;
|
|
2037
|
+
}
|
|
1589
2038
|
}
|
|
1590
|
-
// ===========================================================================
|
|
1591
|
-
// Delegated Transaction Operations
|
|
1592
|
-
// ===========================================================================
|
|
1593
2039
|
/**
|
|
1594
|
-
*
|
|
1595
|
-
*
|
|
1596
|
-
* Creates a JWT token that authorizes the recipient to create a
|
|
1597
|
-
* transaction on behalf of the sender (current user). The token
|
|
1598
|
-
* is short-lived (typically ~5 minutes).
|
|
1599
|
-
*
|
|
1600
|
-
* Use this when you want to pre-authorize a payment that will be
|
|
1601
|
-
* initiated by the recipient (e.g., a service charging for usage).
|
|
1602
|
-
*
|
|
1603
|
-
* @param recipientEmail - Email of the authorized recipient
|
|
1604
|
-
* @returns JWT token string to share with recipient
|
|
1605
|
-
*
|
|
1606
|
-
* @example
|
|
1607
|
-
* ```typescript
|
|
1608
|
-
* // Sender creates token
|
|
1609
|
-
* const token = await accounting.createTransactionToken('service@example.com');
|
|
2040
|
+
* Register an event handler.
|
|
1610
2041
|
*
|
|
1611
|
-
*
|
|
1612
|
-
*
|
|
1613
|
-
* ```
|
|
2042
|
+
* @param eventType - The event type to listen for, or '*' for all events
|
|
2043
|
+
* @param handler - Callback function
|
|
1614
2044
|
*/
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2045
|
+
on(eventType, handler) {
|
|
2046
|
+
this.ws.addEventListener("message", (msgEvent) => {
|
|
2047
|
+
try {
|
|
2048
|
+
const data = JSON.parse(msgEvent.data);
|
|
2049
|
+
if (eventType === "*" || data.type === eventType) {
|
|
2050
|
+
handler(data);
|
|
2051
|
+
}
|
|
2052
|
+
} catch {
|
|
2053
|
+
}
|
|
1618
2054
|
});
|
|
1619
|
-
return response.token;
|
|
1620
2055
|
}
|
|
1621
|
-
/**
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
2056
|
+
/** Send a user message to the agent */
|
|
2057
|
+
sendMessage(content) {
|
|
2058
|
+
this._send({
|
|
2059
|
+
type: "user.message",
|
|
2060
|
+
payload: { content }
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
/** Confirm a tool call */
|
|
2064
|
+
confirm(toolCallId) {
|
|
2065
|
+
this._send({
|
|
2066
|
+
type: "user.confirm",
|
|
2067
|
+
payload: { tool_call_id: toolCallId }
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
/** Deny a tool call */
|
|
2071
|
+
deny(toolCallId, reason) {
|
|
2072
|
+
this._send({
|
|
2073
|
+
type: "user.deny",
|
|
2074
|
+
payload: { tool_call_id: toolCallId, reason }
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
/** Cancel the session */
|
|
2078
|
+
cancel() {
|
|
2079
|
+
this._state = "cancelled";
|
|
2080
|
+
this._send({ type: "user.cancel" });
|
|
2081
|
+
}
|
|
2082
|
+
/** Close the session and WebSocket */
|
|
2083
|
+
close() {
|
|
2084
|
+
if (this._closed) return;
|
|
2085
|
+
this._send({ type: "session.close" });
|
|
2086
|
+
this.ws.close();
|
|
2087
|
+
this._handleClose();
|
|
2088
|
+
}
|
|
2089
|
+
// ---- Internal ----
|
|
2090
|
+
_send(msg) {
|
|
2091
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
2092
|
+
this.ws.send(JSON.stringify(msg));
|
|
1652
2093
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
2094
|
+
}
|
|
2095
|
+
_handleMessage(event) {
|
|
2096
|
+
try {
|
|
2097
|
+
const data = JSON.parse(event.data);
|
|
2098
|
+
this._sequenceCounter++;
|
|
2099
|
+
switch (data.type) {
|
|
2100
|
+
case "agent.request_input":
|
|
2101
|
+
this._state = "awaiting_input";
|
|
2102
|
+
break;
|
|
2103
|
+
case "session.completed":
|
|
2104
|
+
this._state = "completed";
|
|
2105
|
+
break;
|
|
2106
|
+
case "session.failed":
|
|
2107
|
+
this._state = "failed";
|
|
2108
|
+
break;
|
|
2109
|
+
case "agent.error":
|
|
2110
|
+
if (!data.payload.recoverable) {
|
|
2111
|
+
this._state = "error";
|
|
2112
|
+
}
|
|
2113
|
+
break;
|
|
2114
|
+
default:
|
|
2115
|
+
if (this._state === "awaiting_input" || this._state === "connecting") {
|
|
2116
|
+
this._state = "running";
|
|
2117
|
+
}
|
|
1662
2118
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
2119
|
+
if (this._messageResolvers.length > 0) {
|
|
2120
|
+
const resolve = this._messageResolvers.shift();
|
|
2121
|
+
resolve(data);
|
|
2122
|
+
} else {
|
|
2123
|
+
this._messageQueue.push(data);
|
|
2124
|
+
}
|
|
2125
|
+
if (data.type === "session.completed" || data.type === "session.failed") {
|
|
2126
|
+
this._handleClose();
|
|
2127
|
+
}
|
|
2128
|
+
} catch {
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
_handleClose() {
|
|
2132
|
+
if (this._closed) return;
|
|
2133
|
+
this._closed = true;
|
|
2134
|
+
for (const resolve of this._messageResolvers) {
|
|
2135
|
+
resolve(null);
|
|
2136
|
+
}
|
|
2137
|
+
this._messageResolvers = [];
|
|
2138
|
+
}
|
|
2139
|
+
_nextEvent() {
|
|
2140
|
+
if (this._messageQueue.length > 0) {
|
|
2141
|
+
return Promise.resolve(this._messageQueue.shift());
|
|
2142
|
+
}
|
|
2143
|
+
if (this._closed) {
|
|
2144
|
+
return Promise.resolve(null);
|
|
2145
|
+
}
|
|
2146
|
+
return new Promise((resolve) => {
|
|
2147
|
+
this._messageResolvers.push(resolve);
|
|
2148
|
+
});
|
|
1665
2149
|
}
|
|
1666
2150
|
};
|
|
1667
|
-
function createAccountingResource(options) {
|
|
1668
|
-
return new AccountingResource(options);
|
|
1669
|
-
}
|
|
1670
2151
|
|
|
1671
2152
|
// src/resources/chat.ts
|
|
1672
2153
|
init_errors();
|
|
2154
|
+
|
|
2155
|
+
// src/models/common.ts
|
|
2156
|
+
var Visibility = {
|
|
2157
|
+
/** Visible to everyone, no authentication required */
|
|
2158
|
+
PUBLIC: "public",
|
|
2159
|
+
/** Only visible to the owner and collaborators */
|
|
2160
|
+
PRIVATE: "private",
|
|
2161
|
+
/** Behaves like private — only visible to the owner */
|
|
2162
|
+
INTERNAL: "internal"
|
|
2163
|
+
};
|
|
2164
|
+
var EndpointType = {
|
|
2165
|
+
/** Machine learning model endpoint */
|
|
2166
|
+
MODEL: "model",
|
|
2167
|
+
/** Data source endpoint */
|
|
2168
|
+
DATA_SOURCE: "data_source",
|
|
2169
|
+
/** Both model and data source endpoint */
|
|
2170
|
+
MODEL_DATA_SOURCE: "model_data_source",
|
|
2171
|
+
/** Agent endpoint with session-based interaction */
|
|
2172
|
+
AGENT: "agent"
|
|
2173
|
+
};
|
|
2174
|
+
var UserRole = {
|
|
2175
|
+
/** Administrator with full access */
|
|
2176
|
+
ADMIN: "admin",
|
|
2177
|
+
/** Regular user */
|
|
2178
|
+
USER: "user",
|
|
2179
|
+
/** Guest user with limited access */
|
|
2180
|
+
GUEST: "guest"
|
|
2181
|
+
};
|
|
2182
|
+
|
|
2183
|
+
// src/models/endpoint.ts
|
|
2184
|
+
function getEndpointPublicPath(endpoint) {
|
|
2185
|
+
return `${endpoint.ownerUsername}/${endpoint.slug}`;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
// src/resources/chat.ts
|
|
1673
2189
|
var AggregatorError = class extends SyftHubError {
|
|
1674
2190
|
constructor(message, status, detail) {
|
|
1675
2191
|
super(message);
|
|
@@ -1685,12 +2201,26 @@ var EndpointResolutionError = class extends SyftHubError {
|
|
|
1685
2201
|
this.name = "EndpointResolutionError";
|
|
1686
2202
|
}
|
|
1687
2203
|
};
|
|
1688
|
-
var ChatResource = class {
|
|
2204
|
+
var ChatResource = class _ChatResource {
|
|
1689
2205
|
constructor(hub, auth, aggregatorUrl) {
|
|
1690
2206
|
this.hub = hub;
|
|
1691
2207
|
this.auth = auth;
|
|
1692
2208
|
this.aggregatorUrl = aggregatorUrl;
|
|
1693
2209
|
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Check if an endpoint type matches the expected type.
|
|
2212
|
+
* A model_data_source endpoint matches both 'model' and 'data_source'.
|
|
2213
|
+
*/
|
|
2214
|
+
static typeMatches(actualType, expectedType) {
|
|
2215
|
+
if (actualType === expectedType) return true;
|
|
2216
|
+
if (actualType === EndpointType.MODEL_DATA_SOURCE) {
|
|
2217
|
+
return expectedType === EndpointType.MODEL || expectedType === EndpointType.DATA_SOURCE;
|
|
2218
|
+
}
|
|
2219
|
+
if (actualType === EndpointType.AGENT && expectedType === EndpointType.MODEL) {
|
|
2220
|
+
return true;
|
|
2221
|
+
}
|
|
2222
|
+
return false;
|
|
2223
|
+
}
|
|
1694
2224
|
/**
|
|
1695
2225
|
* Convert any endpoint format to EndpointRef with URL and owner info.
|
|
1696
2226
|
* The ownerUsername is critical for satellite token authentication.
|
|
@@ -1700,7 +2230,7 @@ var ChatResource = class {
|
|
|
1700
2230
|
return endpoint;
|
|
1701
2231
|
}
|
|
1702
2232
|
if (this.isEndpointPublic(endpoint)) {
|
|
1703
|
-
if (expectedType && endpoint.type
|
|
2233
|
+
if (expectedType && !_ChatResource.typeMatches(endpoint.type, expectedType)) {
|
|
1704
2234
|
throw new Error(
|
|
1705
2235
|
`Expected endpoint type '${expectedType}', got '${endpoint.type}' for '${endpoint.slug}'`
|
|
1706
2236
|
);
|
|
@@ -1755,12 +2285,15 @@ var ChatResource = class {
|
|
|
1755
2285
|
/**
|
|
1756
2286
|
* Get satellite tokens for all unique endpoint owners.
|
|
1757
2287
|
* Returns a map of owner username to satellite token.
|
|
2288
|
+
*
|
|
2289
|
+
* @param owners - Array of unique owner usernames
|
|
2290
|
+
* @param guestMode - If true, fetch guest tokens (no auth required)
|
|
1758
2291
|
*/
|
|
1759
|
-
async getSatelliteTokensForOwners(owners) {
|
|
2292
|
+
async getSatelliteTokensForOwners(owners, guestMode = false) {
|
|
1760
2293
|
if (owners.length === 0) {
|
|
1761
2294
|
return {};
|
|
1762
2295
|
}
|
|
1763
|
-
const tokenMap = await this.auth.getSatelliteTokens(owners);
|
|
2296
|
+
const tokenMap = guestMode ? await this.auth.getGuestSatelliteTokens(owners) : await this.auth.getSatelliteTokens(owners);
|
|
1764
2297
|
const result = {};
|
|
1765
2298
|
for (const [owner, token] of tokenMap) {
|
|
1766
2299
|
result[owner] = token;
|
|
@@ -1768,18 +2301,11 @@ var ChatResource = class {
|
|
|
1768
2301
|
return result;
|
|
1769
2302
|
}
|
|
1770
2303
|
/**
|
|
1771
|
-
* Get
|
|
1772
|
-
* Returns
|
|
1773
|
-
*
|
|
1774
|
-
* Transaction tokens are used for billing - they authorize the endpoint
|
|
1775
|
-
* owner to charge the current user for usage.
|
|
2304
|
+
* Get the user's Hub access token for MPP payment flow.
|
|
2305
|
+
* Returns null if in guest mode or not authenticated.
|
|
1776
2306
|
*/
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
return {};
|
|
1780
|
-
}
|
|
1781
|
-
const response = await this.auth.getTransactionTokens(owners);
|
|
1782
|
-
return response.tokens;
|
|
2307
|
+
getUserToken() {
|
|
2308
|
+
return this.auth.getAccessToken();
|
|
1783
2309
|
}
|
|
1784
2310
|
/**
|
|
1785
2311
|
* Type guard for EndpointRef.
|
|
@@ -1793,14 +2319,147 @@ var ChatResource = class {
|
|
|
1793
2319
|
isEndpointPublic(value) {
|
|
1794
2320
|
return typeof value === "object" && value !== null && "connect" in value && "ownerUsername" in value && Array.isArray(value.connect);
|
|
1795
2321
|
}
|
|
2322
|
+
static COLLECTIVE_PREFIX = "collective/";
|
|
2323
|
+
static TUNNELING_PREFIX = "tunneling:";
|
|
2324
|
+
/**
|
|
2325
|
+
* Expand any `collective/<slug>` (or `collective/<slug>/<shared-slug>`)
|
|
2326
|
+
* entries in the data-sources list into the individual `owner/slug` paths
|
|
2327
|
+
* of the collective's approved members.
|
|
2328
|
+
*
|
|
2329
|
+
* Path forms recognised:
|
|
2330
|
+
* - `collective/<slug>` → every approved member (backward-compatible)
|
|
2331
|
+
* - `collective/<slug>/all` → equivalent alias of the above
|
|
2332
|
+
* - `collective/<slug>/<shared-slug>` → the named subset, intersected with
|
|
2333
|
+
* the collective's currently approved members
|
|
2334
|
+
*
|
|
2335
|
+
* Non-collective entries pass through unchanged. String paths are
|
|
2336
|
+
* deduplicated so a regular endpoint that also belongs to a selected
|
|
2337
|
+
* collective is not queried twice.
|
|
2338
|
+
*/
|
|
2339
|
+
async expandCollectivePaths(dataSources) {
|
|
2340
|
+
const expanded = [];
|
|
2341
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
2342
|
+
for (const ds of dataSources) {
|
|
2343
|
+
if (typeof ds === "string" && ds.startsWith(_ChatResource.COLLECTIVE_PREFIX)) {
|
|
2344
|
+
const rest = ds.slice(_ChatResource.COLLECTIVE_PREFIX.length);
|
|
2345
|
+
const slashAt = rest.indexOf("/");
|
|
2346
|
+
const collectiveSlug = slashAt < 0 ? rest : rest.slice(0, slashAt);
|
|
2347
|
+
const rawShared = slashAt < 0 ? void 0 : rest.slice(slashAt + 1);
|
|
2348
|
+
const sharedSlug = rawShared && rawShared !== "all" ? rawShared : void 0;
|
|
2349
|
+
if (!collectiveSlug) {
|
|
2350
|
+
throw new EndpointResolutionError(`Malformed collective path: ${ds}`, ds);
|
|
2351
|
+
}
|
|
2352
|
+
let memberPaths;
|
|
2353
|
+
try {
|
|
2354
|
+
memberPaths = await this.hub.getCollectiveEndpointPaths(collectiveSlug, sharedSlug);
|
|
2355
|
+
} catch (error) {
|
|
2356
|
+
const target = sharedSlug ? `${collectiveSlug}/${sharedSlug}` : collectiveSlug;
|
|
2357
|
+
throw new EndpointResolutionError(
|
|
2358
|
+
`Failed to resolve collective '${target}': ${error instanceof Error ? error.message : String(error)}`,
|
|
2359
|
+
ds
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
for (const path of memberPaths) {
|
|
2363
|
+
if (!seenPaths.has(path)) {
|
|
2364
|
+
seenPaths.add(path);
|
|
2365
|
+
expanded.push(path);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
} else if (typeof ds === "string") {
|
|
2369
|
+
if (!seenPaths.has(ds)) {
|
|
2370
|
+
seenPaths.add(ds);
|
|
2371
|
+
expanded.push(ds);
|
|
2372
|
+
}
|
|
2373
|
+
} else {
|
|
2374
|
+
expanded.push(ds);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
return expanded;
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* Check if any endpoints use tunneling URLs and extract target usernames.
|
|
2381
|
+
*/
|
|
2382
|
+
collectTunnelingUsernames(modelRef, dataSourceRefs) {
|
|
2383
|
+
const usernames = /* @__PURE__ */ new Set();
|
|
2384
|
+
if (modelRef.url.startsWith(_ChatResource.TUNNELING_PREFIX)) {
|
|
2385
|
+
usernames.add(modelRef.url.slice(_ChatResource.TUNNELING_PREFIX.length));
|
|
2386
|
+
}
|
|
2387
|
+
for (const ds of dataSourceRefs) {
|
|
2388
|
+
if (ds.url.startsWith(_ChatResource.TUNNELING_PREFIX)) {
|
|
2389
|
+
usernames.add(ds.url.slice(_ChatResource.TUNNELING_PREFIX.length));
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
return [...usernames];
|
|
2393
|
+
}
|
|
2394
|
+
/**
|
|
2395
|
+
* Shared request preparation for complete() and stream().
|
|
2396
|
+
* Resolves endpoints, fetches tokens, and builds the aggregator request body.
|
|
2397
|
+
* Returns the request body and the resolved aggregator URL.
|
|
2398
|
+
*/
|
|
2399
|
+
async prepareRequest(options, stream) {
|
|
2400
|
+
const modelRef = await this.resolveEndpointRef(options.model, "model");
|
|
2401
|
+
const expandedDataSources = await this.expandCollectivePaths(options.dataSources ?? []);
|
|
2402
|
+
const dsRefs = [];
|
|
2403
|
+
for (const ds of expandedDataSources) {
|
|
2404
|
+
dsRefs.push(await this.resolveEndpointRef(ds, "data_source"));
|
|
2405
|
+
}
|
|
2406
|
+
const uniqueOwners = this.collectUniqueOwners(modelRef, dsRefs);
|
|
2407
|
+
const guestMode = options.guestMode ?? false;
|
|
2408
|
+
const endpointTokens = await this.getSatelliteTokensForOwners(uniqueOwners, guestMode);
|
|
2409
|
+
const userToken = guestMode ? null : this.getUserToken();
|
|
2410
|
+
let peerToken = options.peerToken;
|
|
2411
|
+
let peerChannel = options.peerChannel;
|
|
2412
|
+
if (!peerToken) {
|
|
2413
|
+
const tunnelingUsernames = this.collectTunnelingUsernames(modelRef, dsRefs);
|
|
2414
|
+
if (tunnelingUsernames.length > 0) {
|
|
2415
|
+
const peerResponse = guestMode ? await this.auth.getGuestPeerToken(tunnelingUsernames) : await this.auth.getPeerToken(tunnelingUsernames);
|
|
2416
|
+
peerToken = peerResponse.peerToken;
|
|
2417
|
+
peerChannel = peerResponse.peerChannel;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
const requestBody = this.buildRequestBody(
|
|
2421
|
+
options.prompt,
|
|
2422
|
+
modelRef,
|
|
2423
|
+
dsRefs,
|
|
2424
|
+
endpointTokens,
|
|
2425
|
+
userToken,
|
|
2426
|
+
{
|
|
2427
|
+
topK: options.topK,
|
|
2428
|
+
maxTokens: options.maxTokens,
|
|
2429
|
+
temperature: options.temperature,
|
|
2430
|
+
similarityThreshold: options.similarityThreshold,
|
|
2431
|
+
stream,
|
|
2432
|
+
messages: options.messages,
|
|
2433
|
+
peerToken,
|
|
2434
|
+
peerChannel
|
|
2435
|
+
}
|
|
2436
|
+
);
|
|
2437
|
+
const effectiveAggregatorUrl = (options.aggregatorUrl ?? this.aggregatorUrl).replace(
|
|
2438
|
+
/\/+$/,
|
|
2439
|
+
""
|
|
2440
|
+
);
|
|
2441
|
+
return { requestBody, effectiveAggregatorUrl };
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* Parse an error response from the aggregator into an AggregatorError.
|
|
2445
|
+
*/
|
|
2446
|
+
async handleAggregatorErrorResponse(response) {
|
|
2447
|
+
let message = `HTTP ${response.status}`;
|
|
2448
|
+
try {
|
|
2449
|
+
const data = await response.json();
|
|
2450
|
+
message = String(data["message"] ?? data["error"] ?? message);
|
|
2451
|
+
} catch {
|
|
2452
|
+
}
|
|
2453
|
+
throw new AggregatorError(`Aggregator error: ${message}`, response.status);
|
|
2454
|
+
}
|
|
1796
2455
|
/**
|
|
1797
2456
|
* Build the request body for the aggregator.
|
|
1798
2457
|
* Includes endpoint_tokens mapping for satellite token authentication.
|
|
1799
|
-
* Includes
|
|
2458
|
+
* Includes user_token for MPP payment callback authorization.
|
|
1800
2459
|
* User identity is derived from satellite tokens, not passed in request body.
|
|
1801
2460
|
*/
|
|
1802
|
-
buildRequestBody(prompt, modelRef, dataSourceRefs, endpointTokens,
|
|
1803
|
-
|
|
2461
|
+
buildRequestBody(prompt, modelRef, dataSourceRefs, endpointTokens, userToken, options) {
|
|
2462
|
+
const body = {
|
|
1804
2463
|
prompt,
|
|
1805
2464
|
model: {
|
|
1806
2465
|
url: modelRef.url,
|
|
@@ -1817,13 +2476,25 @@ var ChatResource = class {
|
|
|
1817
2476
|
owner_username: ds.ownerUsername ?? null
|
|
1818
2477
|
})),
|
|
1819
2478
|
endpoint_tokens: endpointTokens,
|
|
1820
|
-
transaction_tokens: transactionTokens,
|
|
1821
2479
|
top_k: options.topK ?? 5,
|
|
1822
2480
|
max_tokens: options.maxTokens ?? 1024,
|
|
1823
2481
|
temperature: options.temperature ?? 0.7,
|
|
1824
2482
|
similarity_threshold: options.similarityThreshold ?? 0.5,
|
|
1825
2483
|
stream: options.stream ?? false
|
|
1826
2484
|
};
|
|
2485
|
+
if (userToken) {
|
|
2486
|
+
body["user_token"] = userToken;
|
|
2487
|
+
}
|
|
2488
|
+
if (options.messages && options.messages.length > 0) {
|
|
2489
|
+
body.messages = options.messages.map((m) => ({ role: m.role, content: m.content }));
|
|
2490
|
+
}
|
|
2491
|
+
if (options.peerToken) {
|
|
2492
|
+
body["peer_token"] = options.peerToken;
|
|
2493
|
+
}
|
|
2494
|
+
if (options.peerChannel) {
|
|
2495
|
+
body["peer_channel"] = options.peerChannel;
|
|
2496
|
+
}
|
|
2497
|
+
return body;
|
|
1827
2498
|
}
|
|
1828
2499
|
/**
|
|
1829
2500
|
* Parse a SourceInfo from raw data.
|
|
@@ -1895,7 +2566,7 @@ var ChatResource = class {
|
|
|
1895
2566
|
* This method automatically:
|
|
1896
2567
|
* 1. Resolves endpoints and extracts owner information
|
|
1897
2568
|
* 2. Exchanges Hub tokens for satellite tokens (one per unique owner)
|
|
1898
|
-
* 3.
|
|
2569
|
+
* 3. Passes the user's Hub access token for MPP payment authorization
|
|
1899
2570
|
* 4. Sends tokens to the aggregator for forwarding to SyftAI-Space
|
|
1900
2571
|
*
|
|
1901
2572
|
* @param options - Chat completion options
|
|
@@ -1904,48 +2575,14 @@ var ChatResource = class {
|
|
|
1904
2575
|
* @throws {AggregatorError} If aggregator service fails
|
|
1905
2576
|
*/
|
|
1906
2577
|
async complete(options) {
|
|
1907
|
-
const
|
|
1908
|
-
const
|
|
1909
|
-
for (const ds of options.dataSources ?? []) {
|
|
1910
|
-
dsRefs.push(await this.resolveEndpointRef(ds, "data_source"));
|
|
1911
|
-
}
|
|
1912
|
-
const uniqueOwners = this.collectUniqueOwners(modelRef, dsRefs);
|
|
1913
|
-
const endpointTokens = await this.getSatelliteTokensForOwners(uniqueOwners);
|
|
1914
|
-
const transactionTokens = await this.getTransactionTokensForOwners(uniqueOwners);
|
|
1915
|
-
const requestBody = this.buildRequestBody(
|
|
1916
|
-
options.prompt,
|
|
1917
|
-
modelRef,
|
|
1918
|
-
dsRefs,
|
|
1919
|
-
endpointTokens,
|
|
1920
|
-
transactionTokens,
|
|
1921
|
-
{
|
|
1922
|
-
topK: options.topK,
|
|
1923
|
-
maxTokens: options.maxTokens,
|
|
1924
|
-
temperature: options.temperature,
|
|
1925
|
-
similarityThreshold: options.similarityThreshold,
|
|
1926
|
-
stream: false
|
|
1927
|
-
}
|
|
1928
|
-
);
|
|
1929
|
-
const effectiveAggregatorUrl = (options.aggregatorUrl ?? this.aggregatorUrl).replace(
|
|
1930
|
-
/\/+$/,
|
|
1931
|
-
""
|
|
1932
|
-
);
|
|
1933
|
-
const url = `${effectiveAggregatorUrl}/chat`;
|
|
1934
|
-
const response = await fetch(url, {
|
|
2578
|
+
const { requestBody, effectiveAggregatorUrl } = await this.prepareRequest(options, false);
|
|
2579
|
+
const response = await fetch(`${effectiveAggregatorUrl}/chat`, {
|
|
1935
2580
|
method: "POST",
|
|
1936
|
-
headers: {
|
|
1937
|
-
"Content-Type": "application/json"
|
|
1938
|
-
},
|
|
2581
|
+
headers: { "Content-Type": "application/json" },
|
|
1939
2582
|
body: JSON.stringify(requestBody)
|
|
1940
2583
|
});
|
|
1941
2584
|
if (!response.ok) {
|
|
1942
|
-
|
|
1943
|
-
try {
|
|
1944
|
-
const data2 = await response.json();
|
|
1945
|
-
message = String(data2["message"] ?? data2["error"] ?? message);
|
|
1946
|
-
} catch {
|
|
1947
|
-
}
|
|
1948
|
-
throw new AggregatorError(`Aggregator error: ${message}`, response.status);
|
|
2585
|
+
return this.handleAggregatorErrorResponse(response);
|
|
1949
2586
|
}
|
|
1950
2587
|
const data = await response.json();
|
|
1951
2588
|
const sourcesData = data["sources"];
|
|
@@ -1956,12 +2593,14 @@ var ChatResource = class {
|
|
|
1956
2593
|
const metadata = this.parseMetadata(metadataData ?? {});
|
|
1957
2594
|
const usageData = data["usage"];
|
|
1958
2595
|
const usage = usageData ? this.parseUsage(usageData) : void 0;
|
|
2596
|
+
const profitShare = data["profit_share"];
|
|
1959
2597
|
return {
|
|
1960
2598
|
response: String(data["response"] ?? ""),
|
|
1961
2599
|
sources,
|
|
1962
2600
|
retrievalInfo,
|
|
1963
2601
|
metadata,
|
|
1964
|
-
usage
|
|
2602
|
+
usage,
|
|
2603
|
+
profitShare
|
|
1965
2604
|
};
|
|
1966
2605
|
}
|
|
1967
2606
|
/**
|
|
@@ -1970,41 +2609,15 @@ var ChatResource = class {
|
|
|
1970
2609
|
* This method automatically:
|
|
1971
2610
|
* 1. Resolves endpoints and extracts owner information
|
|
1972
2611
|
* 2. Exchanges Hub tokens for satellite tokens (one per unique owner)
|
|
1973
|
-
* 3.
|
|
2612
|
+
* 3. Passes the user's Hub access token for MPP payment authorization
|
|
1974
2613
|
* 4. Sends tokens to the aggregator for forwarding to SyftAI-Space
|
|
1975
2614
|
*
|
|
1976
2615
|
* @param options - Chat completion options
|
|
1977
2616
|
* @yields ChatStreamEvent objects as they arrive
|
|
1978
2617
|
*/
|
|
1979
2618
|
async *stream(options) {
|
|
1980
|
-
const
|
|
1981
|
-
const
|
|
1982
|
-
for (const ds of options.dataSources ?? []) {
|
|
1983
|
-
dsRefs.push(await this.resolveEndpointRef(ds, "data_source"));
|
|
1984
|
-
}
|
|
1985
|
-
const uniqueOwners = this.collectUniqueOwners(modelRef, dsRefs);
|
|
1986
|
-
const endpointTokens = await this.getSatelliteTokensForOwners(uniqueOwners);
|
|
1987
|
-
const transactionTokens = await this.getTransactionTokensForOwners(uniqueOwners);
|
|
1988
|
-
const requestBody = this.buildRequestBody(
|
|
1989
|
-
options.prompt,
|
|
1990
|
-
modelRef,
|
|
1991
|
-
dsRefs,
|
|
1992
|
-
endpointTokens,
|
|
1993
|
-
transactionTokens,
|
|
1994
|
-
{
|
|
1995
|
-
topK: options.topK,
|
|
1996
|
-
maxTokens: options.maxTokens,
|
|
1997
|
-
temperature: options.temperature,
|
|
1998
|
-
similarityThreshold: options.similarityThreshold,
|
|
1999
|
-
stream: true
|
|
2000
|
-
}
|
|
2001
|
-
);
|
|
2002
|
-
const effectiveAggregatorUrl = (options.aggregatorUrl ?? this.aggregatorUrl).replace(
|
|
2003
|
-
/\/+$/,
|
|
2004
|
-
""
|
|
2005
|
-
);
|
|
2006
|
-
const url = `${effectiveAggregatorUrl}/chat/stream`;
|
|
2007
|
-
const response = await fetch(url, {
|
|
2619
|
+
const { requestBody, effectiveAggregatorUrl } = await this.prepareRequest(options, true);
|
|
2620
|
+
const response = await fetch(`${effectiveAggregatorUrl}/chat/stream`, {
|
|
2008
2621
|
method: "POST",
|
|
2009
2622
|
headers: {
|
|
2010
2623
|
"Content-Type": "application/json",
|
|
@@ -2014,55 +2627,22 @@ var ChatResource = class {
|
|
|
2014
2627
|
signal: options.signal
|
|
2015
2628
|
});
|
|
2016
2629
|
if (!response.ok) {
|
|
2017
|
-
|
|
2018
|
-
try {
|
|
2019
|
-
const data = await response.json();
|
|
2020
|
-
message = String(data["message"] ?? data["error"] ?? message);
|
|
2021
|
-
} catch {
|
|
2022
|
-
}
|
|
2023
|
-
throw new AggregatorError(`Aggregator error: ${message}`, response.status);
|
|
2630
|
+
return this.handleAggregatorErrorResponse(response);
|
|
2024
2631
|
}
|
|
2025
2632
|
if (!response.body) {
|
|
2026
2633
|
throw new AggregatorError("No response body from aggregator");
|
|
2027
2634
|
}
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
const { done, value } = await reader.read();
|
|
2036
|
-
if (done) break;
|
|
2037
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2038
|
-
const lines = buffer.split("\n");
|
|
2039
|
-
buffer = lines.pop() ?? "";
|
|
2040
|
-
for (const line of lines) {
|
|
2041
|
-
const trimmedLine = line.trim();
|
|
2042
|
-
if (!trimmedLine) {
|
|
2043
|
-
if (currentEvent && currentData) {
|
|
2044
|
-
try {
|
|
2045
|
-
const data = JSON.parse(currentData);
|
|
2046
|
-
const event = this.parseSSEEvent(currentEvent, data);
|
|
2047
|
-
if (event) {
|
|
2048
|
-
yield event;
|
|
2049
|
-
}
|
|
2050
|
-
} catch {
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
currentEvent = null;
|
|
2054
|
-
currentData = "";
|
|
2055
|
-
continue;
|
|
2056
|
-
}
|
|
2057
|
-
if (trimmedLine.startsWith("event:")) {
|
|
2058
|
-
currentEvent = trimmedLine.slice(6).trim();
|
|
2059
|
-
} else if (trimmedLine.startsWith("data:")) {
|
|
2060
|
-
currentData = trimmedLine.slice(5).trim();
|
|
2061
|
-
}
|
|
2635
|
+
for await (const { event: eventName, data: dataStr } of readSSEEvents(response)) {
|
|
2636
|
+
if (eventName === "message") continue;
|
|
2637
|
+
try {
|
|
2638
|
+
const data = JSON.parse(dataStr);
|
|
2639
|
+
const event = this.parseSSEEvent(eventName, data);
|
|
2640
|
+
if (event) {
|
|
2641
|
+
yield event;
|
|
2062
2642
|
}
|
|
2643
|
+
} catch {
|
|
2644
|
+
yield { type: "error", message: `Failed to parse SSE data: ${dataStr}` };
|
|
2063
2645
|
}
|
|
2064
|
-
} finally {
|
|
2065
|
-
reader.releaseLock();
|
|
2066
2646
|
}
|
|
2067
2647
|
}
|
|
2068
2648
|
/**
|
|
@@ -2088,8 +2668,24 @@ var ChatResource = class {
|
|
|
2088
2668
|
totalDocuments: Number(data["total_documents"] ?? 0),
|
|
2089
2669
|
timeMs: Number(data["time_ms"] ?? 0)
|
|
2090
2670
|
};
|
|
2671
|
+
case "reranking_start":
|
|
2672
|
+
return {
|
|
2673
|
+
type: "reranking_start",
|
|
2674
|
+
documents: Number(data["documents"] ?? 0)
|
|
2675
|
+
};
|
|
2676
|
+
case "reranking_complete":
|
|
2677
|
+
return {
|
|
2678
|
+
type: "reranking_complete",
|
|
2679
|
+
documents: Number(data["documents"] ?? 0),
|
|
2680
|
+
timeMs: Number(data["time_ms"] ?? 0)
|
|
2681
|
+
};
|
|
2091
2682
|
case "generation_start":
|
|
2092
2683
|
return { type: "generation_start" };
|
|
2684
|
+
case "generation_heartbeat":
|
|
2685
|
+
return {
|
|
2686
|
+
type: "generation_heartbeat",
|
|
2687
|
+
elapsedMs: Number(data["elapsed_ms"] ?? 0)
|
|
2688
|
+
};
|
|
2093
2689
|
case "token":
|
|
2094
2690
|
return {
|
|
2095
2691
|
type: "token",
|
|
@@ -2104,7 +2700,9 @@ var ChatResource = class {
|
|
|
2104
2700
|
const metadata = this.parseMetadata(metadataData ?? {});
|
|
2105
2701
|
const usageData = data["usage"];
|
|
2106
2702
|
const usage = usageData ? this.parseUsage(usageData) : void 0;
|
|
2107
|
-
|
|
2703
|
+
const profitShare = data["profit_share"];
|
|
2704
|
+
const response = data["response"];
|
|
2705
|
+
return { type: "done", sources, retrievalInfo, metadata, usage, profitShare, response };
|
|
2108
2706
|
}
|
|
2109
2707
|
case "error":
|
|
2110
2708
|
return {
|
|
@@ -2112,30 +2710,33 @@ var ChatResource = class {
|
|
|
2112
2710
|
message: String(data["message"] ?? "Unknown error")
|
|
2113
2711
|
};
|
|
2114
2712
|
default:
|
|
2713
|
+
console.warn(`[SyftHub] Unknown SSE event type received from aggregator: ${eventType}`);
|
|
2115
2714
|
return {
|
|
2116
2715
|
type: "error",
|
|
2117
2716
|
message: `Unknown event type: ${eventType}`
|
|
2118
2717
|
};
|
|
2119
2718
|
}
|
|
2120
2719
|
}
|
|
2121
|
-
|
|
2122
|
-
* Get model endpoints that have connection URLs configured.
|
|
2123
|
-
*
|
|
2124
|
-
* @param limit - Maximum number of results (default: 20)
|
|
2125
|
-
* @returns Array of EndpointPublic objects for models with URLs
|
|
2126
|
-
*/
|
|
2127
|
-
async getAvailableModels(limit = 20) {
|
|
2720
|
+
async getAvailableEndpoints(endpointType, limit) {
|
|
2128
2721
|
const results = [];
|
|
2129
2722
|
for await (const endpoint of this.hub.browse()) {
|
|
2130
2723
|
if (results.length >= limit) break;
|
|
2131
|
-
if (endpoint.type !==
|
|
2132
|
-
|
|
2133
|
-
if (hasUrl) {
|
|
2724
|
+
if (endpoint.type !== endpointType) continue;
|
|
2725
|
+
if (endpoint.connect.some((conn) => conn.enabled && conn.config["url"])) {
|
|
2134
2726
|
results.push(endpoint);
|
|
2135
2727
|
}
|
|
2136
2728
|
}
|
|
2137
2729
|
return results;
|
|
2138
2730
|
}
|
|
2731
|
+
/**
|
|
2732
|
+
* Get model endpoints that have connection URLs configured.
|
|
2733
|
+
*
|
|
2734
|
+
* @param limit - Maximum number of results (default: 20)
|
|
2735
|
+
* @returns Array of EndpointPublic objects for models with URLs
|
|
2736
|
+
*/
|
|
2737
|
+
async getAvailableModels(limit = 20) {
|
|
2738
|
+
return this.getAvailableEndpoints(EndpointType.MODEL, limit);
|
|
2739
|
+
}
|
|
2139
2740
|
/**
|
|
2140
2741
|
* Get data source endpoints that have connection URLs configured.
|
|
2141
2742
|
*
|
|
@@ -2143,16 +2744,7 @@ var ChatResource = class {
|
|
|
2143
2744
|
* @returns Array of EndpointPublic objects for data sources with URLs
|
|
2144
2745
|
*/
|
|
2145
2746
|
async getAvailableDataSources(limit = 20) {
|
|
2146
|
-
|
|
2147
|
-
for await (const endpoint of this.hub.browse()) {
|
|
2148
|
-
if (results.length >= limit) break;
|
|
2149
|
-
if (endpoint.type !== EndpointType.DATA_SOURCE) continue;
|
|
2150
|
-
const hasUrl = endpoint.connect.some((conn) => conn.enabled && conn.config["url"]);
|
|
2151
|
-
if (hasUrl) {
|
|
2152
|
-
results.push(endpoint);
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
return results;
|
|
2747
|
+
return this.getAvailableEndpoints(EndpointType.DATA_SOURCE, limit);
|
|
2156
2748
|
}
|
|
2157
2749
|
};
|
|
2158
2750
|
|
|
@@ -2338,45 +2930,22 @@ var SyftAIResource = class {
|
|
|
2338
2930
|
if (!response.body) {
|
|
2339
2931
|
throw new GenerationError("No response body from model", endpoint.slug);
|
|
2340
2932
|
}
|
|
2341
|
-
const
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
if (
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
const trimmedLine = line.trim();
|
|
2353
|
-
if (!trimmedLine || trimmedLine.startsWith("event:")) {
|
|
2354
|
-
continue;
|
|
2355
|
-
}
|
|
2356
|
-
if (trimmedLine.startsWith("data:")) {
|
|
2357
|
-
const dataStr = trimmedLine.slice(5).trim();
|
|
2358
|
-
if (dataStr === "[DONE]") {
|
|
2359
|
-
return;
|
|
2360
|
-
}
|
|
2361
|
-
try {
|
|
2362
|
-
const data = JSON.parse(dataStr);
|
|
2363
|
-
if (typeof data["content"] === "string") {
|
|
2364
|
-
yield data["content"];
|
|
2365
|
-
} else if (Array.isArray(data["choices"])) {
|
|
2366
|
-
for (const choice of data["choices"]) {
|
|
2367
|
-
const delta = choice["delta"];
|
|
2368
|
-
if (delta && typeof delta["content"] === "string") {
|
|
2369
|
-
yield delta["content"];
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
} catch {
|
|
2933
|
+
for await (const { data: dataStr } of readSSEEvents(response)) {
|
|
2934
|
+
if (dataStr === "[DONE]") return;
|
|
2935
|
+
try {
|
|
2936
|
+
const data = JSON.parse(dataStr);
|
|
2937
|
+
if (typeof data["content"] === "string") {
|
|
2938
|
+
yield data["content"];
|
|
2939
|
+
} else if (Array.isArray(data["choices"])) {
|
|
2940
|
+
for (const choice of data["choices"]) {
|
|
2941
|
+
const delta = choice["delta"];
|
|
2942
|
+
if (delta && typeof delta["content"] === "string") {
|
|
2943
|
+
yield delta["content"];
|
|
2374
2944
|
}
|
|
2375
2945
|
}
|
|
2376
2946
|
}
|
|
2947
|
+
} catch {
|
|
2377
2948
|
}
|
|
2378
|
-
} finally {
|
|
2379
|
-
reader.releaseLock();
|
|
2380
2949
|
}
|
|
2381
2950
|
}
|
|
2382
2951
|
};
|
|
@@ -2393,7 +2962,6 @@ function isBrowser() {
|
|
|
2393
2962
|
}
|
|
2394
2963
|
var SyftHubClient = class {
|
|
2395
2964
|
http;
|
|
2396
|
-
options;
|
|
2397
2965
|
aggregatorUrl;
|
|
2398
2966
|
// Lazy-initialized resources
|
|
2399
2967
|
_auth;
|
|
@@ -2401,9 +2969,10 @@ var SyftHubClient = class {
|
|
|
2401
2969
|
_myEndpoints;
|
|
2402
2970
|
_hub;
|
|
2403
2971
|
_accounting;
|
|
2404
|
-
|
|
2972
|
+
_agent;
|
|
2405
2973
|
_chat;
|
|
2406
2974
|
_syftai;
|
|
2975
|
+
_apiTokens;
|
|
2407
2976
|
/**
|
|
2408
2977
|
* Create a new SyftHub client.
|
|
2409
2978
|
*
|
|
@@ -2411,7 +2980,6 @@ var SyftHubClient = class {
|
|
|
2411
2980
|
* @throws {SyftHubError} If baseUrl is not provided and SYFTHUB_URL is not set (in non-browser environments)
|
|
2412
2981
|
*/
|
|
2413
2982
|
constructor(options = {}) {
|
|
2414
|
-
this.options = options;
|
|
2415
2983
|
let baseUrl = options.baseUrl ?? getEnv("SYFTHUB_URL");
|
|
2416
2984
|
if (!baseUrl && !isBrowser()) {
|
|
2417
2985
|
throw new SyftHubError(
|
|
@@ -2422,6 +2990,10 @@ var SyftHubClient = class {
|
|
|
2422
2990
|
const normalizedUrl = baseUrl ? baseUrl.replace(/\/+$/, "") : "";
|
|
2423
2991
|
this.http = new HTTPClient(normalizedUrl, options.timeout ?? 3e4);
|
|
2424
2992
|
this.aggregatorUrl = options.aggregatorUrl ?? getEnv("SYFTHUB_AGGREGATOR_URL") ?? `${normalizedUrl}/aggregator/api/v1`;
|
|
2993
|
+
const apiToken = options.apiToken ?? getEnv("SYFTHUB_API_TOKEN");
|
|
2994
|
+
if (apiToken) {
|
|
2995
|
+
this.http.setApiToken(apiToken);
|
|
2996
|
+
}
|
|
2425
2997
|
}
|
|
2426
2998
|
/**
|
|
2427
2999
|
* Authentication resource for login, register, and session management.
|
|
@@ -2477,13 +3049,33 @@ var SyftHubClient = class {
|
|
|
2477
3049
|
return this._hub;
|
|
2478
3050
|
}
|
|
2479
3051
|
/**
|
|
2480
|
-
*
|
|
3052
|
+
* Agent resource for bidirectional agent sessions via WebSocket.
|
|
3053
|
+
*
|
|
3054
|
+
* @example
|
|
3055
|
+
* const session = await client.agent.startSession({
|
|
3056
|
+
* prompt: 'Help me refactor this code',
|
|
3057
|
+
* endpoint: 'alice/code-assistant',
|
|
3058
|
+
* });
|
|
3059
|
+
*
|
|
3060
|
+
* for await (const event of session.events()) {
|
|
3061
|
+
* console.log(event.type, event.payload);
|
|
3062
|
+
* }
|
|
3063
|
+
*/
|
|
3064
|
+
get agent() {
|
|
3065
|
+
if (!this._agent) {
|
|
3066
|
+
this._agent = new AgentResource(this.auth, this.aggregatorUrl);
|
|
3067
|
+
}
|
|
3068
|
+
return this._agent;
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Accounting resource for wallet and payment operations.
|
|
2481
3072
|
*
|
|
2482
|
-
*
|
|
2483
|
-
*
|
|
3073
|
+
* Provides access to MPP wallet management (balance, transactions,
|
|
3074
|
+
* wallet creation/import). Uses the same SyftHub JWT authentication
|
|
3075
|
+
* as other resources.
|
|
2484
3076
|
*
|
|
2485
|
-
*
|
|
2486
|
-
*
|
|
3077
|
+
* You must call `initAccounting()` after login to initialize this resource,
|
|
3078
|
+
* or access it directly if already initialized.
|
|
2487
3079
|
*
|
|
2488
3080
|
* @throws {AuthenticationError} If not initialized
|
|
2489
3081
|
*
|
|
@@ -2493,7 +3085,8 @@ var SyftHubClient = class {
|
|
|
2493
3085
|
* await client.initAccounting();
|
|
2494
3086
|
*
|
|
2495
3087
|
* // Now accounting is available
|
|
2496
|
-
* const
|
|
3088
|
+
* const wallet = await client.accounting.getWallet();
|
|
3089
|
+
* const balance = await client.accounting.getBalance();
|
|
2497
3090
|
*/
|
|
2498
3091
|
get accounting() {
|
|
2499
3092
|
if (this._accounting) {
|
|
@@ -2504,14 +3097,13 @@ var SyftHubClient = class {
|
|
|
2504
3097
|
);
|
|
2505
3098
|
}
|
|
2506
3099
|
/**
|
|
2507
|
-
* Initialize accounting resource
|
|
3100
|
+
* Initialize the accounting (wallet) resource.
|
|
2508
3101
|
*
|
|
2509
|
-
*
|
|
2510
|
-
* and
|
|
3102
|
+
* The wallet API uses the same SyftHub authentication as other resources.
|
|
3103
|
+
* This method simply verifies authentication and creates the resource.
|
|
2511
3104
|
*
|
|
2512
3105
|
* @returns The initialized AccountingResource
|
|
2513
3106
|
* @throws {AuthenticationError} If not authenticated
|
|
2514
|
-
* @throws {ConfigurationError} If user has no accounting service configured
|
|
2515
3107
|
*
|
|
2516
3108
|
* @example
|
|
2517
3109
|
* // Login first, then initialize accounting
|
|
@@ -2519,49 +3111,20 @@ var SyftHubClient = class {
|
|
|
2519
3111
|
* await client.initAccounting();
|
|
2520
3112
|
*
|
|
2521
3113
|
* // Now accounting is available
|
|
2522
|
-
* const
|
|
3114
|
+
* const wallet = await client.accounting.getWallet();
|
|
3115
|
+
* const balance = await client.accounting.getBalance();
|
|
2523
3116
|
*/
|
|
2524
3117
|
async initAccounting() {
|
|
2525
3118
|
if (this._accounting) {
|
|
2526
3119
|
return this._accounting;
|
|
2527
3120
|
}
|
|
2528
|
-
if (this._accountingInitPromise) {
|
|
2529
|
-
return this._accountingInitPromise;
|
|
2530
|
-
}
|
|
2531
|
-
this._accountingInitPromise = this._doInitAccounting();
|
|
2532
|
-
try {
|
|
2533
|
-
this._accounting = await this._accountingInitPromise;
|
|
2534
|
-
return this._accounting;
|
|
2535
|
-
} finally {
|
|
2536
|
-
this._accountingInitPromise = null;
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
/**
|
|
2540
|
-
* Internal method to perform accounting initialization.
|
|
2541
|
-
*/
|
|
2542
|
-
async _doInitAccounting() {
|
|
2543
3121
|
if (!this.isAuthenticated) {
|
|
2544
3122
|
throw new AuthenticationError(
|
|
2545
3123
|
"Must be logged in to use accounting. Call client.auth.login() first."
|
|
2546
3124
|
);
|
|
2547
3125
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
throw new ConfigurationError(
|
|
2551
|
-
"No accounting service configured for this user. Contact your administrator to set up accounting."
|
|
2552
|
-
);
|
|
2553
|
-
}
|
|
2554
|
-
if (!creds.password) {
|
|
2555
|
-
throw new ConfigurationError(
|
|
2556
|
-
"Accounting password not available. This may indicate an issue with your account setup."
|
|
2557
|
-
);
|
|
2558
|
-
}
|
|
2559
|
-
return new AccountingResource({
|
|
2560
|
-
url: creds.url,
|
|
2561
|
-
email: creds.email,
|
|
2562
|
-
password: creds.password,
|
|
2563
|
-
timeout: this.options.timeout
|
|
2564
|
-
});
|
|
3126
|
+
this._accounting = new AccountingResource(this.http);
|
|
3127
|
+
return this._accounting;
|
|
2565
3128
|
}
|
|
2566
3129
|
/**
|
|
2567
3130
|
* Chat resource for RAG-augmented conversations via the Aggregator.
|
|
@@ -2625,6 +3188,40 @@ var SyftHubClient = class {
|
|
|
2625
3188
|
}
|
|
2626
3189
|
return this._syftai;
|
|
2627
3190
|
}
|
|
3191
|
+
/**
|
|
3192
|
+
* API Tokens resource for managing personal access tokens.
|
|
3193
|
+
*
|
|
3194
|
+
* API tokens provide an alternative to username/password authentication.
|
|
3195
|
+
* They are ideal for CI/CD pipelines, scripts, and programmatic access.
|
|
3196
|
+
*
|
|
3197
|
+
* @example
|
|
3198
|
+
* // Create a new token
|
|
3199
|
+
* const result = await client.apiTokens.create({
|
|
3200
|
+
* name: 'CI/CD Pipeline',
|
|
3201
|
+
* scopes: ['write'],
|
|
3202
|
+
* });
|
|
3203
|
+
* console.log('Save this token:', result.token);
|
|
3204
|
+
*
|
|
3205
|
+
* // List all tokens
|
|
3206
|
+
* const { tokens } = await client.apiTokens.list();
|
|
3207
|
+
*
|
|
3208
|
+
* // Revoke a token
|
|
3209
|
+
* await client.apiTokens.revoke(tokenId);
|
|
3210
|
+
*/
|
|
3211
|
+
get apiTokens() {
|
|
3212
|
+
if (!this._apiTokens) {
|
|
3213
|
+
this._apiTokens = new APITokensResource(this.http);
|
|
3214
|
+
}
|
|
3215
|
+
return this._apiTokens;
|
|
3216
|
+
}
|
|
3217
|
+
/**
|
|
3218
|
+
* Check if the client is using API token authentication.
|
|
3219
|
+
*
|
|
3220
|
+
* @returns True if authenticated with an API token (vs JWT)
|
|
3221
|
+
*/
|
|
3222
|
+
get isUsingApiToken() {
|
|
3223
|
+
return this.http.isUsingApiToken();
|
|
3224
|
+
}
|
|
2628
3225
|
/**
|
|
2629
3226
|
* Get current authentication tokens.
|
|
2630
3227
|
*
|
|
@@ -2666,7 +3263,7 @@ var SyftHubClient = class {
|
|
|
2666
3263
|
return this.http.hasTokens();
|
|
2667
3264
|
}
|
|
2668
3265
|
/**
|
|
2669
|
-
* Check if accounting has been initialized.
|
|
3266
|
+
* Check if the accounting (wallet) resource has been initialized.
|
|
2670
3267
|
*
|
|
2671
3268
|
* Use this to check if accounting is available before accessing
|
|
2672
3269
|
* the `accounting` property, which will throw if not initialized.
|
|
@@ -2675,7 +3272,7 @@ var SyftHubClient = class {
|
|
|
2675
3272
|
*
|
|
2676
3273
|
* @example
|
|
2677
3274
|
* if (client.isAccountingInitialized) {
|
|
2678
|
-
* const
|
|
3275
|
+
* const wallet = await client.accounting.getWallet();
|
|
2679
3276
|
* }
|
|
2680
3277
|
*/
|
|
2681
3278
|
get isAccountingInitialized() {
|
|
@@ -2693,6 +3290,6 @@ var SyftHubClient = class {
|
|
|
2693
3290
|
// src/index.ts
|
|
2694
3291
|
init_errors();
|
|
2695
3292
|
|
|
2696
|
-
export { APIError, AccountingAccountExistsError, AccountingResource, AccountingServiceUnavailableError, AggregatorError, AuthenticationError, AuthorizationError, ChatResource, ConfigurationError,
|
|
3293
|
+
export { APIError, APITokensResource, AccountingAccountExistsError, AccountingResource, AccountingServiceUnavailableError, AgentResource, AgentSessionClient, AgentSessionError, AggregatorError, AggregatorsResource, AuthenticationError, AuthorizationError, ChatResource, ConfigurationError, EndpointResolutionError, EndpointType, GenerationError, InvalidAccountingPasswordError, NetworkError, NotFoundError, PageIterator, RetrievalError, SyftAIResource, SyftHubClient, SyftHubError, UserAlreadyExistsError, UserRole, ValidationError, Visibility, createAccountingResource, getEndpointPublicPath };
|
|
2697
3294
|
//# sourceMappingURL=index.js.map
|
|
2698
3295
|
//# sourceMappingURL=index.js.map
|