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