@trustgraph/client 1.7.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/auth-api.d.ts +26 -0
- package/dist/auth/auth-api.d.ts.map +1 -0
- package/dist/index.cjs +462 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +457 -64
- package/dist/index.esm.js.map +1 -1
- package/dist/models/messages.d.ts +31 -2
- package/dist/models/messages.d.ts.map +1 -1
- package/dist/models/namespaces.d.ts +2 -1
- package/dist/models/namespaces.d.ts.map +1 -1
- package/dist/socket/service-call-multi.d.ts +3 -1
- package/dist/socket/service-call-multi.d.ts.map +1 -1
- package/dist/socket/service-call.d.ts +1 -0
- package/dist/socket/service-call.d.ts.map +1 -1
- package/dist/socket/trustgraph-socket.d.ts +113 -14
- package/dist/socket/trustgraph-socket.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.esm.js
CHANGED
|
@@ -8,7 +8,8 @@ const TG_QUERY = TG + "query";
|
|
|
8
8
|
const TG_EDGE_COUNT = TG + "edgeCount";
|
|
9
9
|
const TG_SELECTED_EDGE = TG + "selectedEdge";
|
|
10
10
|
const TG_EDGE = TG + "edge";
|
|
11
|
-
const
|
|
11
|
+
const TG_CONCEPT = TG + "concept";
|
|
12
|
+
const TG_SCORE = TG + "score";
|
|
12
13
|
const TG_CONTENT = TG + "content";
|
|
13
14
|
const TG_REIFIES = TG + "reifies";
|
|
14
15
|
const TG_DOCUMENT = TG + "document";
|
|
@@ -74,18 +75,17 @@ class ServiceCallMulti {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
/**
|
|
77
|
-
* Called when socket
|
|
78
|
+
* Called when socket reconnects. Streaming requests cannot be resumed
|
|
79
|
+
* after a disconnect — the backend state is gone — so fail immediately.
|
|
78
80
|
*/
|
|
79
81
|
retryNow() {
|
|
80
82
|
if (this.complete)
|
|
81
83
|
return;
|
|
82
|
-
// Clear any pending backoff timer
|
|
83
84
|
clearTimeout(this.timeoutId);
|
|
84
85
|
this.timeoutId = undefined;
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
|
|
88
|
-
this.attempt();
|
|
86
|
+
this.complete = true;
|
|
87
|
+
delete this.socket.inflight[this.mid];
|
|
88
|
+
this.error("Connection lost during streaming request");
|
|
89
89
|
}
|
|
90
90
|
onTimeout() {
|
|
91
91
|
if (this.complete == true)
|
|
@@ -106,8 +106,11 @@ class ServiceCallMulti {
|
|
|
106
106
|
this.error("Ran out of retries");
|
|
107
107
|
return; // Exit early - no more attempts
|
|
108
108
|
}
|
|
109
|
-
// Check if WebSocket connection is available and
|
|
110
|
-
|
|
109
|
+
// Check if WebSocket connection is available, open, and authenticated.
|
|
110
|
+
const authReady = this.socket.isAuthReady?.() ?? true;
|
|
111
|
+
if (this.socket.ws &&
|
|
112
|
+
this.socket.ws.readyState === WebSocket.OPEN &&
|
|
113
|
+
authReady) {
|
|
111
114
|
try {
|
|
112
115
|
this.socket.ws.send(JSON.stringify(this.msg));
|
|
113
116
|
this.timeoutId = setTimeout(this.onTimeout.bind(this), this.timeout);
|
|
@@ -289,8 +292,14 @@ class ServiceCall {
|
|
|
289
292
|
this.error("Ran out of retries");
|
|
290
293
|
return; // Exit early - no more attempts
|
|
291
294
|
}
|
|
292
|
-
// Check if WebSocket connection is available and
|
|
293
|
-
|
|
295
|
+
// Check if WebSocket connection is available, open, and authenticated.
|
|
296
|
+
// The IAM gateway rejects all non-auth frames until the first-frame
|
|
297
|
+
// auth handshake has completed, so sends gated only on readyState
|
|
298
|
+
// would race the auth response.
|
|
299
|
+
const authReady = this.socket.isAuthReady?.() ?? true;
|
|
300
|
+
if (this.socket.ws &&
|
|
301
|
+
this.socket.ws.readyState === WebSocket.OPEN &&
|
|
302
|
+
authReady) {
|
|
294
303
|
try {
|
|
295
304
|
// Attempt to send the message as JSON
|
|
296
305
|
this.socket.ws.send(JSON.stringify(this.msg));
|
|
@@ -319,12 +328,23 @@ class ServiceCall {
|
|
|
319
328
|
// Configuration constants
|
|
320
329
|
const SOCKET_RECONNECTION_TIMEOUT = 2000; // 2 seconds between reconnection
|
|
321
330
|
// attempts
|
|
322
|
-
const SOCKET_URL = "/api/socket"; // WebSocket endpoint path
|
|
331
|
+
const SOCKET_URL = "/api/v1/socket"; // WebSocket endpoint path
|
|
323
332
|
/**
|
|
324
333
|
* Generates a random message ID using cryptographically secure random values
|
|
325
334
|
* @param length - Number of random characters to generate
|
|
326
335
|
* @returns Random string of specified length
|
|
327
336
|
*/
|
|
337
|
+
// Normalise the top-level `error` field from a WebSocket response frame.
|
|
338
|
+
// The gateway may send it as a plain string *or* as an object with a
|
|
339
|
+
// `message` field; callers always want a string.
|
|
340
|
+
function errorToString(err) {
|
|
341
|
+
if (typeof err === "string")
|
|
342
|
+
return err;
|
|
343
|
+
if (err && typeof err === "object" && "message" in err) {
|
|
344
|
+
return String(err.message);
|
|
345
|
+
}
|
|
346
|
+
return String(err);
|
|
347
|
+
}
|
|
328
348
|
function makeid(length) {
|
|
329
349
|
const array = new Uint32Array(length);
|
|
330
350
|
crypto.getRandomValues(array);
|
|
@@ -332,23 +352,46 @@ function makeid(length) {
|
|
|
332
352
|
return array.reduce((acc, current) => acc + characters[current % characters.length], "");
|
|
333
353
|
}
|
|
334
354
|
class BaseApi {
|
|
335
|
-
constructor(
|
|
355
|
+
constructor(token, socketUrl) {
|
|
356
|
+
// Legacy honour-system field. The gateway now derives identity from the
|
|
357
|
+
// authenticated token; this is retained as an empty string so the
|
|
358
|
+
// existing request-builder code paths that read `api.user` keep
|
|
359
|
+
// compiling. It will be removed when the request payloads are cleaned
|
|
360
|
+
// up.
|
|
361
|
+
this.user = "";
|
|
362
|
+
// Active workspace for outbound requests. Sent as the envelope
|
|
363
|
+
// `workspace` field (sibling of `flow`); the gateway reconciles it
|
|
364
|
+
// against the authenticated identity and, when empty, defaults to
|
|
365
|
+
// the token's bound workspace. Kept in sync with the active
|
|
366
|
+
// workspace by the state layer.
|
|
367
|
+
this.workspace = "";
|
|
336
368
|
this.inflight = {}; // Track active requests by
|
|
337
369
|
// message ID
|
|
338
370
|
this.reconnectAttempts = 0; // Track reconnection attempts
|
|
339
371
|
this.maxReconnectAttempts = 10; // Maximum reconnection attempts
|
|
340
372
|
this.reconnectionState = "idle"; // Connection state
|
|
373
|
+
// Auth-handshake state for the current socket. Reset on each open.
|
|
374
|
+
this.authState = "pending";
|
|
341
375
|
// Connection state tracking for UI
|
|
342
376
|
this.connectionStateListeners = [];
|
|
343
377
|
this.tag = makeid(16); // Generate unique client tag
|
|
344
378
|
this.id = 1; // Start message ID counter
|
|
345
|
-
this.token = token;
|
|
346
|
-
this.user = user; // Store user identifier
|
|
379
|
+
this.token = token;
|
|
347
380
|
this.socketUrl = socketUrl || SOCKET_URL; // Use provided URL or default
|
|
348
|
-
console.log("SOCKET: opening socket..."
|
|
381
|
+
console.log("SOCKET: opening socket...");
|
|
349
382
|
this.openSocket(); // Establish WebSocket connection
|
|
350
383
|
console.log("SOCKET: socket opened");
|
|
351
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* True once the WebSocket is OPEN and the first-frame auth handshake
|
|
387
|
+
* has been accepted by the gateway. ServiceCall consults this before
|
|
388
|
+
* sending request frames.
|
|
389
|
+
*/
|
|
390
|
+
isAuthReady() {
|
|
391
|
+
return (!!this.ws &&
|
|
392
|
+
this.ws.readyState === WebSocket.OPEN &&
|
|
393
|
+
this.authState === "ok");
|
|
394
|
+
}
|
|
352
395
|
/**
|
|
353
396
|
* Subscribe to connection state changes for UI updates
|
|
354
397
|
*/
|
|
@@ -369,7 +412,8 @@ class BaseApi {
|
|
|
369
412
|
*/
|
|
370
413
|
getConnectionState() {
|
|
371
414
|
const hasApiKey = !!this.token;
|
|
372
|
-
// Determine status based on WebSocket state
|
|
415
|
+
// Determine status based on WebSocket state, auth handshake state, and
|
|
416
|
+
// reconnection state.
|
|
373
417
|
let status;
|
|
374
418
|
if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
|
|
375
419
|
if (this.reconnectionState === "failed") {
|
|
@@ -386,7 +430,12 @@ class BaseApi {
|
|
|
386
430
|
status = "connecting";
|
|
387
431
|
}
|
|
388
432
|
else if (this.ws.readyState === WebSocket.OPEN) {
|
|
389
|
-
|
|
433
|
+
if (this.authState === "ok")
|
|
434
|
+
status = "authenticated";
|
|
435
|
+
else if (this.authState === "failed")
|
|
436
|
+
status = "auth-failed";
|
|
437
|
+
else
|
|
438
|
+
status = "authenticating";
|
|
390
439
|
}
|
|
391
440
|
else {
|
|
392
441
|
status = "connecting";
|
|
@@ -435,13 +484,12 @@ class BaseApi {
|
|
|
435
484
|
this.ws.removeEventListener("error", this.onError);
|
|
436
485
|
this.ws = undefined;
|
|
437
486
|
}
|
|
487
|
+
// Reset the auth handshake state for the new connection. The token
|
|
488
|
+
// is sent as the first frame after onOpen, never on the URL.
|
|
489
|
+
this.authState = "pending";
|
|
438
490
|
try {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
? `${this.socketUrl}?token=${this.token}`
|
|
442
|
-
: this.socketUrl;
|
|
443
|
-
console.log("SOCKET: connecting to", wsUrl.replace(/token=[^&]*/, "token=***"));
|
|
444
|
-
this.ws = new WebSocket(wsUrl);
|
|
491
|
+
console.log("SOCKET: connecting to", this.socketUrl);
|
|
492
|
+
this.ws = new WebSocket(this.socketUrl);
|
|
445
493
|
}
|
|
446
494
|
catch (e) {
|
|
447
495
|
console.error("[socket creation error]", e);
|
|
@@ -465,6 +513,34 @@ class BaseApi {
|
|
|
465
513
|
return;
|
|
466
514
|
try {
|
|
467
515
|
const obj = JSON.parse(message.data);
|
|
516
|
+
// Auth handshake frames are addressed by `type`, not by request id.
|
|
517
|
+
if (obj.type === "auth-ok") {
|
|
518
|
+
console.log("[socket] auth-ok", obj.default_workspace ?? "");
|
|
519
|
+
this.authState = "ok";
|
|
520
|
+
this.lastError = undefined;
|
|
521
|
+
this.notifyStateChange();
|
|
522
|
+
// Auth complete — release any requests that were waiting for the
|
|
523
|
+
// socket to become usable.
|
|
524
|
+
for (const mid in this.inflight) {
|
|
525
|
+
this.inflight[mid].retryNow();
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (obj.type === "auth-failed") {
|
|
530
|
+
console.warn("[socket] auth-failed", obj.error);
|
|
531
|
+
this.authState = "failed";
|
|
532
|
+
this.lastError = obj.error || "auth failure";
|
|
533
|
+
this.notifyStateChange();
|
|
534
|
+
// Per the IAM spec the server keeps the socket open so the client
|
|
535
|
+
// can re-authenticate without reconnecting. Don't auto-close.
|
|
536
|
+
// Surface the failure to any inflight requests so they don't sit
|
|
537
|
+
// forever — callers should clear the token and reauth.
|
|
538
|
+
for (const mid in this.inflight) {
|
|
539
|
+
this.inflight[mid].error(new Error("auth failure"));
|
|
540
|
+
}
|
|
541
|
+
this.inflight = {};
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
468
544
|
// Skip messages without ID (can't route them)
|
|
469
545
|
if (!obj.id)
|
|
470
546
|
return;
|
|
@@ -497,12 +573,19 @@ class BaseApi {
|
|
|
497
573
|
clearTimeout(this.reconnectTimer);
|
|
498
574
|
this.reconnectTimer = undefined;
|
|
499
575
|
}
|
|
500
|
-
//
|
|
501
|
-
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
576
|
+
// Send the auth frame as the first message. The server rejects all
|
|
577
|
+
// non-auth messages until it sends back auth-ok, so we hold off
|
|
578
|
+
// releasing inflight requests here — that happens in onMessage when
|
|
579
|
+
// auth-ok arrives.
|
|
580
|
+
this.authState = "pending";
|
|
581
|
+
try {
|
|
582
|
+
this.ws?.send(JSON.stringify({ type: "auth", token: this.token }));
|
|
505
583
|
}
|
|
584
|
+
catch (e) {
|
|
585
|
+
console.error("[socket] failed to send auth frame", e);
|
|
586
|
+
}
|
|
587
|
+
// Notify UI that we're now in the authenticating phase.
|
|
588
|
+
this.notifyStateChange();
|
|
506
589
|
}
|
|
507
590
|
// Handle socket errors
|
|
508
591
|
onError(event) {
|
|
@@ -605,7 +688,7 @@ class BaseApi {
|
|
|
605
688
|
const mid = this.getNextId();
|
|
606
689
|
// Set default values
|
|
607
690
|
if (timeout == undefined)
|
|
608
|
-
timeout =
|
|
691
|
+
timeout = 30000;
|
|
609
692
|
if (retries == undefined)
|
|
610
693
|
retries = 3;
|
|
611
694
|
// Construct the request message
|
|
@@ -617,6 +700,10 @@ class BaseApi {
|
|
|
617
700
|
// Add flow identifier if provided
|
|
618
701
|
if (flow)
|
|
619
702
|
msg.flow = flow;
|
|
703
|
+
// Stamp the active workspace onto the envelope. When empty the
|
|
704
|
+
// gateway falls back to the token's bound workspace.
|
|
705
|
+
if (this.workspace)
|
|
706
|
+
msg.workspace = this.workspace;
|
|
620
707
|
// Return a Promise that will be resolved/rejected by the ServiceCall
|
|
621
708
|
return new Promise((resolve, reject) => {
|
|
622
709
|
const call = new ServiceCall(mid, msg, resolve, reject, timeout, retries, this);
|
|
@@ -636,7 +723,7 @@ class BaseApi {
|
|
|
636
723
|
const mid = this.getNextId();
|
|
637
724
|
// Set defaults
|
|
638
725
|
if (timeout == undefined)
|
|
639
|
-
timeout =
|
|
726
|
+
timeout = 30000;
|
|
640
727
|
if (retries == undefined)
|
|
641
728
|
retries = 3;
|
|
642
729
|
// Construct request message
|
|
@@ -647,6 +734,8 @@ class BaseApi {
|
|
|
647
734
|
};
|
|
648
735
|
if (flow)
|
|
649
736
|
msg.flow = flow;
|
|
737
|
+
if (this.workspace)
|
|
738
|
+
msg.workspace = this.workspace;
|
|
650
739
|
return new Promise((resolve, reject) => {
|
|
651
740
|
const call = new ServiceCallMulti(mid, msg, resolve, reject, timeout, retries, this, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
652
741
|
receiver);
|
|
@@ -683,6 +772,166 @@ class BaseApi {
|
|
|
683
772
|
collectionManagement() {
|
|
684
773
|
return new CollectionManagementApi(this);
|
|
685
774
|
}
|
|
775
|
+
iam() {
|
|
776
|
+
return new IamApi(this);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* IamApi - Identity, user, workspace, and API key management over the
|
|
781
|
+
* authenticated socket. IAM is the one surface that lives outside
|
|
782
|
+
* workspace tenancy; these calls run as the `iam` service and the
|
|
783
|
+
* gateway injects the caller's identity from the connection's token.
|
|
784
|
+
*/
|
|
785
|
+
class IamApi {
|
|
786
|
+
constructor(api) {
|
|
787
|
+
this.api = api;
|
|
788
|
+
}
|
|
789
|
+
call(req) {
|
|
790
|
+
return this.api.makeRequest("iam", req);
|
|
791
|
+
}
|
|
792
|
+
// --- Identity ---
|
|
793
|
+
whoami() {
|
|
794
|
+
return this.call({
|
|
795
|
+
operation: "whoami",
|
|
796
|
+
}).then((r) => {
|
|
797
|
+
const user = r.user ?? {};
|
|
798
|
+
return {
|
|
799
|
+
id: String(user.id ?? ""),
|
|
800
|
+
username: String(user.username ?? ""),
|
|
801
|
+
name: String(user.name ?? ""),
|
|
802
|
+
email: String(user.email ?? ""),
|
|
803
|
+
default_workspace: String(user.default_workspace ?? ""),
|
|
804
|
+
roles: Array.isArray(user.roles) ? user.roles : [],
|
|
805
|
+
enabled: !!user.enabled,
|
|
806
|
+
};
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
// --- Users ---
|
|
810
|
+
listUsers(workspace) {
|
|
811
|
+
return this.call({
|
|
812
|
+
operation: "list-users",
|
|
813
|
+
...(workspace ? { workspace } : {}),
|
|
814
|
+
}).then((r) => r.users ?? []);
|
|
815
|
+
}
|
|
816
|
+
getUser(userId) {
|
|
817
|
+
return this.call({
|
|
818
|
+
operation: "get-user",
|
|
819
|
+
user_id: userId,
|
|
820
|
+
}).then((r) => r.user);
|
|
821
|
+
}
|
|
822
|
+
createUser(params) {
|
|
823
|
+
return this.call({
|
|
824
|
+
operation: "create-user",
|
|
825
|
+
user: {
|
|
826
|
+
username: params.username,
|
|
827
|
+
password: params.password,
|
|
828
|
+
name: params.name ?? "",
|
|
829
|
+
email: params.email ?? "",
|
|
830
|
+
enabled: true,
|
|
831
|
+
must_change_password: params.must_change_password ?? false,
|
|
832
|
+
roles: [],
|
|
833
|
+
},
|
|
834
|
+
...(params.workspace ? { workspace: params.workspace } : {}),
|
|
835
|
+
}).then((r) => r.user);
|
|
836
|
+
}
|
|
837
|
+
updateUser(userId, fields) {
|
|
838
|
+
return this.call({
|
|
839
|
+
operation: "update-user",
|
|
840
|
+
user_id: userId,
|
|
841
|
+
user: fields,
|
|
842
|
+
}).then((r) => r.user);
|
|
843
|
+
}
|
|
844
|
+
enableUser(userId) {
|
|
845
|
+
return this.call({
|
|
846
|
+
operation: "enable-user",
|
|
847
|
+
user_id: userId,
|
|
848
|
+
}).then(() => { });
|
|
849
|
+
}
|
|
850
|
+
disableUser(userId) {
|
|
851
|
+
return this.call({
|
|
852
|
+
operation: "disable-user",
|
|
853
|
+
user_id: userId,
|
|
854
|
+
}).then(() => { });
|
|
855
|
+
}
|
|
856
|
+
deleteUser(userId) {
|
|
857
|
+
return this.call({
|
|
858
|
+
operation: "delete-user",
|
|
859
|
+
user_id: userId,
|
|
860
|
+
}).then(() => { });
|
|
861
|
+
}
|
|
862
|
+
resetPassword(userId) {
|
|
863
|
+
return this.call({
|
|
864
|
+
operation: "reset-password",
|
|
865
|
+
user_id: userId,
|
|
866
|
+
}).then((r) => r.temporary_password);
|
|
867
|
+
}
|
|
868
|
+
changePassword(userId, currentPassword, newPassword) {
|
|
869
|
+
return this.call({
|
|
870
|
+
operation: "change-password",
|
|
871
|
+
user_id: userId,
|
|
872
|
+
password: currentPassword,
|
|
873
|
+
new_password: newPassword,
|
|
874
|
+
}).then(() => { });
|
|
875
|
+
}
|
|
876
|
+
// --- Workspaces ---
|
|
877
|
+
listMyWorkspaces() {
|
|
878
|
+
return this.call({
|
|
879
|
+
operation: "list-my-workspaces",
|
|
880
|
+
}).then((r) => (r.workspaces ?? []).map((w) => ({
|
|
881
|
+
id: String(w.id ?? ""),
|
|
882
|
+
name: String(w.name ?? ""),
|
|
883
|
+
enabled: !!w.enabled,
|
|
884
|
+
created: String(w.created ?? ""),
|
|
885
|
+
})));
|
|
886
|
+
}
|
|
887
|
+
listWorkspaces() {
|
|
888
|
+
return this.call({
|
|
889
|
+
operation: "list-workspaces",
|
|
890
|
+
}).then((r) => r.workspaces ?? []);
|
|
891
|
+
}
|
|
892
|
+
getWorkspace(id) {
|
|
893
|
+
return this.call({
|
|
894
|
+
operation: "get-workspace",
|
|
895
|
+
workspace_record: { id },
|
|
896
|
+
}).then((r) => r.workspace);
|
|
897
|
+
}
|
|
898
|
+
createWorkspace(id, name) {
|
|
899
|
+
return this.call({
|
|
900
|
+
operation: "create-workspace",
|
|
901
|
+
workspace_record: { id, name: name ?? id, enabled: true },
|
|
902
|
+
}).then((r) => r.workspace);
|
|
903
|
+
}
|
|
904
|
+
updateWorkspace(id, fields) {
|
|
905
|
+
return this.call({
|
|
906
|
+
operation: "update-workspace",
|
|
907
|
+
workspace_record: { id, ...fields },
|
|
908
|
+
}).then((r) => r.workspace);
|
|
909
|
+
}
|
|
910
|
+
disableWorkspace(id) {
|
|
911
|
+
return this.call({
|
|
912
|
+
operation: "disable-workspace",
|
|
913
|
+
workspace_record: { id },
|
|
914
|
+
}).then(() => { });
|
|
915
|
+
}
|
|
916
|
+
// --- API Keys ---
|
|
917
|
+
listApiKeys(userId) {
|
|
918
|
+
return this.call({
|
|
919
|
+
operation: "list-api-keys",
|
|
920
|
+
user_id: userId,
|
|
921
|
+
}).then((r) => r.api_keys ?? []);
|
|
922
|
+
}
|
|
923
|
+
createApiKey(userId, name, expires) {
|
|
924
|
+
return this.call({
|
|
925
|
+
operation: "create-api-key",
|
|
926
|
+
key: { user_id: userId, name, expires: expires ?? "" },
|
|
927
|
+
}).then((r) => ({ plaintext: r.api_key_plaintext, key: r.api_key }));
|
|
928
|
+
}
|
|
929
|
+
revokeApiKey(keyId) {
|
|
930
|
+
return this.call({
|
|
931
|
+
operation: "revoke-api-key",
|
|
932
|
+
key_id: keyId,
|
|
933
|
+
}).then(() => { });
|
|
934
|
+
}
|
|
686
935
|
}
|
|
687
936
|
/**
|
|
688
937
|
* LibrarianApi - Manages document storage and retrieval
|
|
@@ -921,7 +1170,7 @@ class LibrarianApi {
|
|
|
921
1170
|
const msg = message;
|
|
922
1171
|
// Check for top-level error
|
|
923
1172
|
if (msg.error) {
|
|
924
|
-
onError(msg.error);
|
|
1173
|
+
onError(errorToString(msg.error));
|
|
925
1174
|
return true;
|
|
926
1175
|
}
|
|
927
1176
|
const resp = msg.response;
|
|
@@ -1007,17 +1256,30 @@ class FlowsApi {
|
|
|
1007
1256
|
deleteConfig(keys) {
|
|
1008
1257
|
return this.api.makeRequest("config", {
|
|
1009
1258
|
operation: "delete",
|
|
1010
|
-
keys: keys,
|
|
1259
|
+
keys: [keys],
|
|
1011
1260
|
}, 30000);
|
|
1012
1261
|
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Lists available configuration keys for a given type
|
|
1264
|
+
*/
|
|
1265
|
+
list(type) {
|
|
1266
|
+
return this.api
|
|
1267
|
+
.makeRequest("config", {
|
|
1268
|
+
operation: "list",
|
|
1269
|
+
type: type,
|
|
1270
|
+
}, 60000)
|
|
1271
|
+
.then((r) => r);
|
|
1272
|
+
}
|
|
1013
1273
|
// Prompt management - specialized config operations for AI prompts
|
|
1014
1274
|
/**
|
|
1015
|
-
* Retrieves list of available prompt
|
|
1275
|
+
* Retrieves list of available prompt template IDs
|
|
1016
1276
|
*/
|
|
1017
1277
|
getPrompts() {
|
|
1018
|
-
return this.
|
|
1019
|
-
const
|
|
1020
|
-
return
|
|
1278
|
+
return this.list("prompt").then((r) => {
|
|
1279
|
+
const keys = r?.directory || [];
|
|
1280
|
+
return keys
|
|
1281
|
+
.filter((k) => k.startsWith("template."))
|
|
1282
|
+
.map((k) => k.slice("template.".length));
|
|
1021
1283
|
});
|
|
1022
1284
|
}
|
|
1023
1285
|
/**
|
|
@@ -1174,17 +1436,19 @@ class FlowApi {
|
|
|
1174
1436
|
const msg = message;
|
|
1175
1437
|
// Check for top-level error
|
|
1176
1438
|
if (msg.error) {
|
|
1177
|
-
error(msg.error);
|
|
1439
|
+
error(errorToString(msg.error));
|
|
1178
1440
|
return true;
|
|
1179
1441
|
}
|
|
1180
1442
|
const resp = msg.response || {};
|
|
1443
|
+
// Prefer message_type, fall back to chunk_type for older backends
|
|
1444
|
+
const msgType = resp.message_type || resp.chunk_type;
|
|
1181
1445
|
// Check for errors in response
|
|
1182
|
-
if (
|
|
1446
|
+
if (msgType === "error" || resp.error) {
|
|
1183
1447
|
error(resp.error?.message || "Unknown agent error");
|
|
1184
1448
|
return true; // End streaming on error
|
|
1185
1449
|
}
|
|
1186
|
-
// Handle explainability events
|
|
1187
|
-
if (
|
|
1450
|
+
// Handle explainability events
|
|
1451
|
+
if (msgType === "explain" && resp.explain_id && resp.explain_graph) {
|
|
1188
1452
|
onExplain?.({
|
|
1189
1453
|
explainId: resp.explain_id,
|
|
1190
1454
|
explainGraph: resp.explain_graph,
|
|
@@ -1192,7 +1456,7 @@ class FlowApi {
|
|
|
1192
1456
|
});
|
|
1193
1457
|
return false;
|
|
1194
1458
|
}
|
|
1195
|
-
// Handle streaming chunks by
|
|
1459
|
+
// Handle streaming chunks by message type
|
|
1196
1460
|
const content = resp.content || "";
|
|
1197
1461
|
const messageId = resp.message_id;
|
|
1198
1462
|
const messageComplete = !!resp.end_of_message;
|
|
@@ -1201,7 +1465,7 @@ class FlowApi {
|
|
|
1201
1465
|
const metadata = dialogComplete && (resp.in_token || resp.out_token || resp.model)
|
|
1202
1466
|
? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model }
|
|
1203
1467
|
: undefined;
|
|
1204
|
-
switch (
|
|
1468
|
+
switch (msgType) {
|
|
1205
1469
|
case "thought":
|
|
1206
1470
|
think(content, messageComplete, messageId, metadata);
|
|
1207
1471
|
break;
|
|
@@ -1225,7 +1489,7 @@ class FlowApi {
|
|
|
1225
1489
|
user: this.api.user,
|
|
1226
1490
|
collection: collection || "default",
|
|
1227
1491
|
streaming: true, // Always use streaming mode
|
|
1228
|
-
}, receiver,
|
|
1492
|
+
}, receiver, 180000, 1, this.flowId)
|
|
1229
1493
|
.catch((err) => {
|
|
1230
1494
|
const errorMessage = err instanceof Error ? err.message : err?.toString() || "Unknown error";
|
|
1231
1495
|
error(`Agent request failed: ${errorMessage}`);
|
|
@@ -1245,7 +1509,7 @@ class FlowApi {
|
|
|
1245
1509
|
const msg = message;
|
|
1246
1510
|
// Check for top-level error
|
|
1247
1511
|
if (msg.error) {
|
|
1248
|
-
onError(msg.error);
|
|
1512
|
+
onError(errorToString(msg.error));
|
|
1249
1513
|
return true;
|
|
1250
1514
|
}
|
|
1251
1515
|
const resp = (msg.response || {});
|
|
@@ -1283,7 +1547,7 @@ class FlowApi {
|
|
|
1283
1547
|
"max-subgraph-size": options?.maxSubgraphSize,
|
|
1284
1548
|
"max-path-length": options?.pathLength,
|
|
1285
1549
|
streaming: true,
|
|
1286
|
-
}, recv,
|
|
1550
|
+
}, recv, 180000, 1, this.flowId);
|
|
1287
1551
|
}
|
|
1288
1552
|
/**
|
|
1289
1553
|
* Performs Document RAG query with streaming response
|
|
@@ -1298,7 +1562,7 @@ class FlowApi {
|
|
|
1298
1562
|
const msg = message;
|
|
1299
1563
|
// Check for top-level error
|
|
1300
1564
|
if (msg.error) {
|
|
1301
|
-
onError(msg.error);
|
|
1565
|
+
onError(errorToString(msg.error));
|
|
1302
1566
|
return true;
|
|
1303
1567
|
}
|
|
1304
1568
|
const resp = (msg.response || {});
|
|
@@ -1331,7 +1595,7 @@ class FlowApi {
|
|
|
1331
1595
|
collection: collection || "default",
|
|
1332
1596
|
"doc-limit": docLimit,
|
|
1333
1597
|
streaming: true,
|
|
1334
|
-
}, recv,
|
|
1598
|
+
}, recv, 180000, 1, this.flowId);
|
|
1335
1599
|
}
|
|
1336
1600
|
/**
|
|
1337
1601
|
* Performs text completion with streaming response
|
|
@@ -1345,7 +1609,7 @@ class FlowApi {
|
|
|
1345
1609
|
const msg = message;
|
|
1346
1610
|
// Check for top-level error
|
|
1347
1611
|
if (msg.error) {
|
|
1348
|
-
onError(msg.error);
|
|
1612
|
+
onError(errorToString(msg.error));
|
|
1349
1613
|
return true;
|
|
1350
1614
|
}
|
|
1351
1615
|
const resp = (msg.response || {});
|
|
@@ -1382,7 +1646,7 @@ class FlowApi {
|
|
|
1382
1646
|
const msg = message;
|
|
1383
1647
|
// Check for top-level error
|
|
1384
1648
|
if (msg.error) {
|
|
1385
|
-
onError(msg.error);
|
|
1649
|
+
onError(errorToString(msg.error));
|
|
1386
1650
|
return true;
|
|
1387
1651
|
}
|
|
1388
1652
|
const resp = (msg.response || {});
|
|
@@ -1403,7 +1667,7 @@ class FlowApi {
|
|
|
1403
1667
|
};
|
|
1404
1668
|
this.api.makeRequestMulti("prompt", {
|
|
1405
1669
|
id: id,
|
|
1406
|
-
|
|
1670
|
+
variables: terms,
|
|
1407
1671
|
streaming: true,
|
|
1408
1672
|
}, recv, 30000, undefined, this.flowId);
|
|
1409
1673
|
}
|
|
@@ -1448,6 +1712,75 @@ class FlowApi {
|
|
|
1448
1712
|
}, 30000, undefined, this.flowId)
|
|
1449
1713
|
.then((r) => r.response);
|
|
1450
1714
|
}
|
|
1715
|
+
sparqlQuery(query, collection, limit, batchSize) {
|
|
1716
|
+
const columns = [];
|
|
1717
|
+
const rows = [];
|
|
1718
|
+
let queryType = "select";
|
|
1719
|
+
let askResult;
|
|
1720
|
+
let triples;
|
|
1721
|
+
let sparqlError = null;
|
|
1722
|
+
const termToString = (val) => {
|
|
1723
|
+
if (!val)
|
|
1724
|
+
return "";
|
|
1725
|
+
if (val.t === "i")
|
|
1726
|
+
return val.i;
|
|
1727
|
+
if (val.t === "l")
|
|
1728
|
+
return val.v;
|
|
1729
|
+
return "";
|
|
1730
|
+
};
|
|
1731
|
+
return this.api
|
|
1732
|
+
.makeRequestMulti("sparql", {
|
|
1733
|
+
query,
|
|
1734
|
+
collection: collection || "default",
|
|
1735
|
+
limit: limit ?? 10000,
|
|
1736
|
+
streaming: true,
|
|
1737
|
+
"batch-size": batchSize ?? 50,
|
|
1738
|
+
}, (resp) => {
|
|
1739
|
+
const msg = resp;
|
|
1740
|
+
const batch = msg.response;
|
|
1741
|
+
const isComplete = msg.complete === true;
|
|
1742
|
+
if (!batch)
|
|
1743
|
+
return isComplete;
|
|
1744
|
+
if (batch.error) {
|
|
1745
|
+
sparqlError = typeof batch.error === "string"
|
|
1746
|
+
? batch.error
|
|
1747
|
+
: batch.error.message || "SPARQL query error";
|
|
1748
|
+
return true;
|
|
1749
|
+
}
|
|
1750
|
+
queryType = batch["query-type"] || queryType;
|
|
1751
|
+
if (queryType === "ask") {
|
|
1752
|
+
askResult = batch["ask-result"];
|
|
1753
|
+
return true;
|
|
1754
|
+
}
|
|
1755
|
+
if (queryType === "construct" || queryType === "describe") {
|
|
1756
|
+
triples = batch.triples || [];
|
|
1757
|
+
return true;
|
|
1758
|
+
}
|
|
1759
|
+
if (batch.variables && columns.length === 0) {
|
|
1760
|
+
columns.push(...batch.variables);
|
|
1761
|
+
}
|
|
1762
|
+
if (batch.bindings) {
|
|
1763
|
+
for (const binding of batch.bindings) {
|
|
1764
|
+
const row = {};
|
|
1765
|
+
for (let i = 0; i < columns.length; i++) {
|
|
1766
|
+
row[columns[i]] = termToString(binding.values[i] ?? null);
|
|
1767
|
+
}
|
|
1768
|
+
rows.push(row);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
return isComplete;
|
|
1772
|
+
}, 60000, undefined, this.flowId)
|
|
1773
|
+
.then(() => {
|
|
1774
|
+
if (sparqlError)
|
|
1775
|
+
throw new Error(sparqlError);
|
|
1776
|
+
return { queryType, columns, rows, askResult, triples };
|
|
1777
|
+
})
|
|
1778
|
+
.catch((err) => {
|
|
1779
|
+
if (err instanceof Error)
|
|
1780
|
+
throw err;
|
|
1781
|
+
throw new Error(typeof err === "string" ? err : JSON.stringify(err));
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1451
1784
|
/**
|
|
1452
1785
|
* Loads a document into this flow for processing
|
|
1453
1786
|
*/
|
|
@@ -1596,17 +1929,19 @@ class ConfigApi {
|
|
|
1596
1929
|
deleteConfig(keys) {
|
|
1597
1930
|
return this.api.makeRequest("config", {
|
|
1598
1931
|
operation: "delete",
|
|
1599
|
-
keys: keys,
|
|
1932
|
+
keys: [keys],
|
|
1600
1933
|
}, 30000);
|
|
1601
1934
|
}
|
|
1602
1935
|
// Specialized prompt management methods
|
|
1603
1936
|
/**
|
|
1604
|
-
* Retrieves available prompt
|
|
1937
|
+
* Retrieves available prompt template IDs
|
|
1605
1938
|
*/
|
|
1606
1939
|
getPrompts() {
|
|
1607
|
-
return this.
|
|
1608
|
-
const
|
|
1609
|
-
return
|
|
1940
|
+
return this.list("prompt").then((r) => {
|
|
1941
|
+
const keys = r?.directory || [];
|
|
1942
|
+
return keys
|
|
1943
|
+
.filter((k) => k.startsWith("template."))
|
|
1944
|
+
.map((k) => k.slice("template.".length));
|
|
1610
1945
|
});
|
|
1611
1946
|
}
|
|
1612
1947
|
/**
|
|
@@ -1824,15 +2159,73 @@ class CollectionManagementApi {
|
|
|
1824
2159
|
}
|
|
1825
2160
|
}
|
|
1826
2161
|
/**
|
|
1827
|
-
* Factory function to create a new TrustGraph WebSocket connection
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
1831
|
-
* @param
|
|
2162
|
+
* Factory function to create a new TrustGraph WebSocket connection.
|
|
2163
|
+
* The token (JWT or API key) is sent as the first frame after connect;
|
|
2164
|
+
* the gateway derives the user identity and workspace from it.
|
|
2165
|
+
*
|
|
2166
|
+
* @param token - Bearer token (JWT from /auth/login or an API key)
|
|
2167
|
+
* @param socketUrl - Optional WebSocket URL (defaults to /api/v1/socket
|
|
2168
|
+
* for browser, provide full URL for Node.js)
|
|
1832
2169
|
*/
|
|
1833
|
-
const createTrustGraphSocket = (
|
|
1834
|
-
return new BaseApi(
|
|
2170
|
+
const createTrustGraphSocket = (token, socketUrl) => {
|
|
2171
|
+
return new BaseApi(token, socketUrl);
|
|
1835
2172
|
};
|
|
1836
2173
|
|
|
1837
|
-
|
|
2174
|
+
// HTTP auth client for the TrustGraph IAM gateway endpoints.
|
|
2175
|
+
//
|
|
2176
|
+
// These endpoints run before the WebSocket exists, so they use plain
|
|
2177
|
+
// fetch rather than the socket transport.
|
|
2178
|
+
const DEFAULT_BOOTSTRAP_STATUS_URL = "/api/v1/auth/bootstrap-status";
|
|
2179
|
+
const DEFAULT_LOGIN_URL = "/api/v1/auth/login";
|
|
2180
|
+
class AuthError extends Error {
|
|
2181
|
+
constructor(message, status) {
|
|
2182
|
+
super(message);
|
|
2183
|
+
this.name = "AuthError";
|
|
2184
|
+
this.status = status;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
class AuthApi {
|
|
2188
|
+
constructor(options = {}) {
|
|
2189
|
+
this.bootstrapStatusUrl =
|
|
2190
|
+
options.bootstrapStatusUrl ?? DEFAULT_BOOTSTRAP_STATUS_URL;
|
|
2191
|
+
this.loginUrl = options.loginUrl ?? DEFAULT_LOGIN_URL;
|
|
2192
|
+
this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);
|
|
2193
|
+
}
|
|
2194
|
+
async bootstrapStatus() {
|
|
2195
|
+
const resp = await this.fetchImpl(this.bootstrapStatusUrl, {
|
|
2196
|
+
method: "POST",
|
|
2197
|
+
headers: { "Content-Type": "application/json" },
|
|
2198
|
+
body: "{}",
|
|
2199
|
+
});
|
|
2200
|
+
if (!resp.ok) {
|
|
2201
|
+
throw new AuthError(`bootstrap-status failed: ${resp.status}`, resp.status);
|
|
2202
|
+
}
|
|
2203
|
+
const body = await resp.json();
|
|
2204
|
+
return { bootstrapAvailable: !!body.bootstrap_available };
|
|
2205
|
+
}
|
|
2206
|
+
async login(username, password, default_workspace) {
|
|
2207
|
+
const payload = { username, password };
|
|
2208
|
+
if (default_workspace)
|
|
2209
|
+
payload.default_workspace = default_workspace;
|
|
2210
|
+
const resp = await this.fetchImpl(this.loginUrl, {
|
|
2211
|
+
method: "POST",
|
|
2212
|
+
headers: { "Content-Type": "application/json" },
|
|
2213
|
+
body: JSON.stringify(payload),
|
|
2214
|
+
});
|
|
2215
|
+
if (resp.status === 401) {
|
|
2216
|
+
throw new AuthError("auth failure", 401);
|
|
2217
|
+
}
|
|
2218
|
+
if (!resp.ok) {
|
|
2219
|
+
throw new AuthError(`login failed: ${resp.status}`, resp.status);
|
|
2220
|
+
}
|
|
2221
|
+
const body = await resp.json();
|
|
2222
|
+
if (!body.jwt) {
|
|
2223
|
+
throw new AuthError("login response missing jwt");
|
|
2224
|
+
}
|
|
2225
|
+
return { jwt: body.jwt, jwtExpires: body.jwt_expires ?? "" };
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
const createAuthApi = (options) => new AuthApi(options);
|
|
2229
|
+
|
|
2230
|
+
export { AuthApi, AuthError, BaseApi, CollectionManagementApi, ConfigApi, FlowApi, FlowsApi, IamApi, KnowledgeApi, LibrarianApi, PROV, PROV_ACTIVITY, PROV_ENTITY, PROV_STARTED_AT_TIME, PROV_WAS_DERIVED_FROM, PROV_WAS_GENERATED_BY, RDF, RDFS, RDFS_LABEL, RDF_TYPE, SCHEMA, SCHEMA_AUTHOR, SCHEMA_DESCRIPTION, SCHEMA_KEYWORDS, SCHEMA_NAME, SKOS, SKOS_DEFINITION, TG, TG_CONCEPT, TG_CONTENT, TG_DOCUMENT, TG_EDGE, TG_EDGE_COUNT, TG_QUERY, TG_REIFIES, TG_SCORE, TG_SELECTED_EDGE, createAuthApi, createTrustGraphSocket };
|
|
1838
2231
|
//# sourceMappingURL=index.esm.js.map
|