@trustgraph/client 1.7.2 → 2.0.3
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 +345 -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 +340 -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 +58 -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,49 @@ 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 and workspace discovery over the authenticated
|
|
783
|
+
* socket. IAM is the one surface that lives outside workspace tenancy;
|
|
784
|
+
* these calls run as the `iam` service and the gateway injects the
|
|
785
|
+
* caller's identity from the connection's token.
|
|
786
|
+
*/
|
|
787
|
+
class IamApi {
|
|
788
|
+
constructor(api) {
|
|
789
|
+
this.api = api;
|
|
790
|
+
}
|
|
791
|
+
// The caller's own user record, including their home workspace.
|
|
792
|
+
whoami() {
|
|
793
|
+
return this.api
|
|
794
|
+
.makeRequest("iam", { operation: "whoami" })
|
|
795
|
+
.then((r) => {
|
|
796
|
+
const user = r.user ?? {};
|
|
797
|
+
return {
|
|
798
|
+
id: String(user.id ?? ""),
|
|
799
|
+
username: String(user.username ?? ""),
|
|
800
|
+
name: String(user.name ?? ""),
|
|
801
|
+
email: String(user.email ?? ""),
|
|
802
|
+
default_workspace: String(user.default_workspace ?? ""),
|
|
803
|
+
roles: Array.isArray(user.roles) ? user.roles : [],
|
|
804
|
+
enabled: !!user.enabled,
|
|
805
|
+
};
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
// The workspaces the caller has access to (one for an ordinary user,
|
|
809
|
+
// all for an admin — driven entirely by what the gateway returns).
|
|
810
|
+
listMyWorkspaces() {
|
|
811
|
+
return this.api
|
|
812
|
+
.makeRequest("iam", { operation: "list-my-workspaces" })
|
|
813
|
+
.then((r) => (r.workspaces ?? []).map((w) => ({
|
|
814
|
+
id: String(w.id ?? ""),
|
|
815
|
+
name: String(w.name ?? ""),
|
|
816
|
+
enabled: !!w.enabled,
|
|
817
|
+
created: String(w.created ?? ""),
|
|
818
|
+
})));
|
|
819
|
+
}
|
|
688
820
|
}
|
|
689
821
|
/**
|
|
690
822
|
* LibrarianApi - Manages document storage and retrieval
|
|
@@ -923,7 +1055,7 @@ class LibrarianApi {
|
|
|
923
1055
|
const msg = message;
|
|
924
1056
|
// Check for top-level error
|
|
925
1057
|
if (msg.error) {
|
|
926
|
-
onError(msg.error);
|
|
1058
|
+
onError(errorToString(msg.error));
|
|
927
1059
|
return true;
|
|
928
1060
|
}
|
|
929
1061
|
const resp = msg.response;
|
|
@@ -1009,17 +1141,30 @@ class FlowsApi {
|
|
|
1009
1141
|
deleteConfig(keys) {
|
|
1010
1142
|
return this.api.makeRequest("config", {
|
|
1011
1143
|
operation: "delete",
|
|
1012
|
-
keys: keys,
|
|
1144
|
+
keys: [keys],
|
|
1013
1145
|
}, 30000);
|
|
1014
1146
|
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Lists available configuration keys for a given type
|
|
1149
|
+
*/
|
|
1150
|
+
list(type) {
|
|
1151
|
+
return this.api
|
|
1152
|
+
.makeRequest("config", {
|
|
1153
|
+
operation: "list",
|
|
1154
|
+
type: type,
|
|
1155
|
+
}, 60000)
|
|
1156
|
+
.then((r) => r);
|
|
1157
|
+
}
|
|
1015
1158
|
// Prompt management - specialized config operations for AI prompts
|
|
1016
1159
|
/**
|
|
1017
|
-
* Retrieves list of available prompt
|
|
1160
|
+
* Retrieves list of available prompt template IDs
|
|
1018
1161
|
*/
|
|
1019
1162
|
getPrompts() {
|
|
1020
|
-
return this.
|
|
1021
|
-
const
|
|
1022
|
-
return
|
|
1163
|
+
return this.list("prompt").then((r) => {
|
|
1164
|
+
const keys = r?.directory || [];
|
|
1165
|
+
return keys
|
|
1166
|
+
.filter((k) => k.startsWith("template."))
|
|
1167
|
+
.map((k) => k.slice("template.".length));
|
|
1023
1168
|
});
|
|
1024
1169
|
}
|
|
1025
1170
|
/**
|
|
@@ -1176,17 +1321,19 @@ class FlowApi {
|
|
|
1176
1321
|
const msg = message;
|
|
1177
1322
|
// Check for top-level error
|
|
1178
1323
|
if (msg.error) {
|
|
1179
|
-
error(msg.error);
|
|
1324
|
+
error(errorToString(msg.error));
|
|
1180
1325
|
return true;
|
|
1181
1326
|
}
|
|
1182
1327
|
const resp = msg.response || {};
|
|
1328
|
+
// Prefer message_type, fall back to chunk_type for older backends
|
|
1329
|
+
const msgType = resp.message_type || resp.chunk_type;
|
|
1183
1330
|
// Check for errors in response
|
|
1184
|
-
if (
|
|
1331
|
+
if (msgType === "error" || resp.error) {
|
|
1185
1332
|
error(resp.error?.message || "Unknown agent error");
|
|
1186
1333
|
return true; // End streaming on error
|
|
1187
1334
|
}
|
|
1188
|
-
// Handle explainability events
|
|
1189
|
-
if (
|
|
1335
|
+
// Handle explainability events
|
|
1336
|
+
if (msgType === "explain" && resp.explain_id && resp.explain_graph) {
|
|
1190
1337
|
onExplain?.({
|
|
1191
1338
|
explainId: resp.explain_id,
|
|
1192
1339
|
explainGraph: resp.explain_graph,
|
|
@@ -1194,7 +1341,7 @@ class FlowApi {
|
|
|
1194
1341
|
});
|
|
1195
1342
|
return false;
|
|
1196
1343
|
}
|
|
1197
|
-
// Handle streaming chunks by
|
|
1344
|
+
// Handle streaming chunks by message type
|
|
1198
1345
|
const content = resp.content || "";
|
|
1199
1346
|
const messageId = resp.message_id;
|
|
1200
1347
|
const messageComplete = !!resp.end_of_message;
|
|
@@ -1203,7 +1350,7 @@ class FlowApi {
|
|
|
1203
1350
|
const metadata = dialogComplete && (resp.in_token || resp.out_token || resp.model)
|
|
1204
1351
|
? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model }
|
|
1205
1352
|
: undefined;
|
|
1206
|
-
switch (
|
|
1353
|
+
switch (msgType) {
|
|
1207
1354
|
case "thought":
|
|
1208
1355
|
think(content, messageComplete, messageId, metadata);
|
|
1209
1356
|
break;
|
|
@@ -1227,7 +1374,7 @@ class FlowApi {
|
|
|
1227
1374
|
user: this.api.user,
|
|
1228
1375
|
collection: collection || "default",
|
|
1229
1376
|
streaming: true, // Always use streaming mode
|
|
1230
|
-
}, receiver,
|
|
1377
|
+
}, receiver, 180000, 1, this.flowId)
|
|
1231
1378
|
.catch((err) => {
|
|
1232
1379
|
const errorMessage = err instanceof Error ? err.message : err?.toString() || "Unknown error";
|
|
1233
1380
|
error(`Agent request failed: ${errorMessage}`);
|
|
@@ -1247,7 +1394,7 @@ class FlowApi {
|
|
|
1247
1394
|
const msg = message;
|
|
1248
1395
|
// Check for top-level error
|
|
1249
1396
|
if (msg.error) {
|
|
1250
|
-
onError(msg.error);
|
|
1397
|
+
onError(errorToString(msg.error));
|
|
1251
1398
|
return true;
|
|
1252
1399
|
}
|
|
1253
1400
|
const resp = (msg.response || {});
|
|
@@ -1285,7 +1432,7 @@ class FlowApi {
|
|
|
1285
1432
|
"max-subgraph-size": options?.maxSubgraphSize,
|
|
1286
1433
|
"max-path-length": options?.pathLength,
|
|
1287
1434
|
streaming: true,
|
|
1288
|
-
}, recv,
|
|
1435
|
+
}, recv, 180000, 1, this.flowId);
|
|
1289
1436
|
}
|
|
1290
1437
|
/**
|
|
1291
1438
|
* Performs Document RAG query with streaming response
|
|
@@ -1300,7 +1447,7 @@ class FlowApi {
|
|
|
1300
1447
|
const msg = message;
|
|
1301
1448
|
// Check for top-level error
|
|
1302
1449
|
if (msg.error) {
|
|
1303
|
-
onError(msg.error);
|
|
1450
|
+
onError(errorToString(msg.error));
|
|
1304
1451
|
return true;
|
|
1305
1452
|
}
|
|
1306
1453
|
const resp = (msg.response || {});
|
|
@@ -1333,7 +1480,7 @@ class FlowApi {
|
|
|
1333
1480
|
collection: collection || "default",
|
|
1334
1481
|
"doc-limit": docLimit,
|
|
1335
1482
|
streaming: true,
|
|
1336
|
-
}, recv,
|
|
1483
|
+
}, recv, 180000, 1, this.flowId);
|
|
1337
1484
|
}
|
|
1338
1485
|
/**
|
|
1339
1486
|
* Performs text completion with streaming response
|
|
@@ -1347,7 +1494,7 @@ class FlowApi {
|
|
|
1347
1494
|
const msg = message;
|
|
1348
1495
|
// Check for top-level error
|
|
1349
1496
|
if (msg.error) {
|
|
1350
|
-
onError(msg.error);
|
|
1497
|
+
onError(errorToString(msg.error));
|
|
1351
1498
|
return true;
|
|
1352
1499
|
}
|
|
1353
1500
|
const resp = (msg.response || {});
|
|
@@ -1384,7 +1531,7 @@ class FlowApi {
|
|
|
1384
1531
|
const msg = message;
|
|
1385
1532
|
// Check for top-level error
|
|
1386
1533
|
if (msg.error) {
|
|
1387
|
-
onError(msg.error);
|
|
1534
|
+
onError(errorToString(msg.error));
|
|
1388
1535
|
return true;
|
|
1389
1536
|
}
|
|
1390
1537
|
const resp = (msg.response || {});
|
|
@@ -1405,7 +1552,7 @@ class FlowApi {
|
|
|
1405
1552
|
};
|
|
1406
1553
|
this.api.makeRequestMulti("prompt", {
|
|
1407
1554
|
id: id,
|
|
1408
|
-
|
|
1555
|
+
variables: terms,
|
|
1409
1556
|
streaming: true,
|
|
1410
1557
|
}, recv, 30000, undefined, this.flowId);
|
|
1411
1558
|
}
|
|
@@ -1450,6 +1597,75 @@ class FlowApi {
|
|
|
1450
1597
|
}, 30000, undefined, this.flowId)
|
|
1451
1598
|
.then((r) => r.response);
|
|
1452
1599
|
}
|
|
1600
|
+
sparqlQuery(query, collection, limit, batchSize) {
|
|
1601
|
+
const columns = [];
|
|
1602
|
+
const rows = [];
|
|
1603
|
+
let queryType = "select";
|
|
1604
|
+
let askResult;
|
|
1605
|
+
let triples;
|
|
1606
|
+
let sparqlError = null;
|
|
1607
|
+
const termToString = (val) => {
|
|
1608
|
+
if (!val)
|
|
1609
|
+
return "";
|
|
1610
|
+
if (val.t === "i")
|
|
1611
|
+
return val.i;
|
|
1612
|
+
if (val.t === "l")
|
|
1613
|
+
return val.v;
|
|
1614
|
+
return "";
|
|
1615
|
+
};
|
|
1616
|
+
return this.api
|
|
1617
|
+
.makeRequestMulti("sparql", {
|
|
1618
|
+
query,
|
|
1619
|
+
collection: collection || "default",
|
|
1620
|
+
limit: limit ?? 10000,
|
|
1621
|
+
streaming: true,
|
|
1622
|
+
"batch-size": batchSize ?? 50,
|
|
1623
|
+
}, (resp) => {
|
|
1624
|
+
const msg = resp;
|
|
1625
|
+
const batch = msg.response;
|
|
1626
|
+
const isComplete = msg.complete === true;
|
|
1627
|
+
if (!batch)
|
|
1628
|
+
return isComplete;
|
|
1629
|
+
if (batch.error) {
|
|
1630
|
+
sparqlError = typeof batch.error === "string"
|
|
1631
|
+
? batch.error
|
|
1632
|
+
: batch.error.message || "SPARQL query error";
|
|
1633
|
+
return true;
|
|
1634
|
+
}
|
|
1635
|
+
queryType = batch["query-type"] || queryType;
|
|
1636
|
+
if (queryType === "ask") {
|
|
1637
|
+
askResult = batch["ask-result"];
|
|
1638
|
+
return true;
|
|
1639
|
+
}
|
|
1640
|
+
if (queryType === "construct" || queryType === "describe") {
|
|
1641
|
+
triples = batch.triples || [];
|
|
1642
|
+
return true;
|
|
1643
|
+
}
|
|
1644
|
+
if (batch.variables && columns.length === 0) {
|
|
1645
|
+
columns.push(...batch.variables);
|
|
1646
|
+
}
|
|
1647
|
+
if (batch.bindings) {
|
|
1648
|
+
for (const binding of batch.bindings) {
|
|
1649
|
+
const row = {};
|
|
1650
|
+
for (let i = 0; i < columns.length; i++) {
|
|
1651
|
+
row[columns[i]] = termToString(binding.values[i] ?? null);
|
|
1652
|
+
}
|
|
1653
|
+
rows.push(row);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return isComplete;
|
|
1657
|
+
}, 60000, undefined, this.flowId)
|
|
1658
|
+
.then(() => {
|
|
1659
|
+
if (sparqlError)
|
|
1660
|
+
throw new Error(sparqlError);
|
|
1661
|
+
return { queryType, columns, rows, askResult, triples };
|
|
1662
|
+
})
|
|
1663
|
+
.catch((err) => {
|
|
1664
|
+
if (err instanceof Error)
|
|
1665
|
+
throw err;
|
|
1666
|
+
throw new Error(typeof err === "string" ? err : JSON.stringify(err));
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1453
1669
|
/**
|
|
1454
1670
|
* Loads a document into this flow for processing
|
|
1455
1671
|
*/
|
|
@@ -1598,17 +1814,19 @@ class ConfigApi {
|
|
|
1598
1814
|
deleteConfig(keys) {
|
|
1599
1815
|
return this.api.makeRequest("config", {
|
|
1600
1816
|
operation: "delete",
|
|
1601
|
-
keys: keys,
|
|
1817
|
+
keys: [keys],
|
|
1602
1818
|
}, 30000);
|
|
1603
1819
|
}
|
|
1604
1820
|
// Specialized prompt management methods
|
|
1605
1821
|
/**
|
|
1606
|
-
* Retrieves available prompt
|
|
1822
|
+
* Retrieves available prompt template IDs
|
|
1607
1823
|
*/
|
|
1608
1824
|
getPrompts() {
|
|
1609
|
-
return this.
|
|
1610
|
-
const
|
|
1611
|
-
return
|
|
1825
|
+
return this.list("prompt").then((r) => {
|
|
1826
|
+
const keys = r?.directory || [];
|
|
1827
|
+
return keys
|
|
1828
|
+
.filter((k) => k.startsWith("template."))
|
|
1829
|
+
.map((k) => k.slice("template.".length));
|
|
1612
1830
|
});
|
|
1613
1831
|
}
|
|
1614
1832
|
/**
|
|
@@ -1826,21 +2044,82 @@ class CollectionManagementApi {
|
|
|
1826
2044
|
}
|
|
1827
2045
|
}
|
|
1828
2046
|
/**
|
|
1829
|
-
* Factory function to create a new TrustGraph WebSocket connection
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1833
|
-
* @param
|
|
2047
|
+
* Factory function to create a new TrustGraph WebSocket connection.
|
|
2048
|
+
* The token (JWT or API key) is sent as the first frame after connect;
|
|
2049
|
+
* the gateway derives the user identity and workspace from it.
|
|
2050
|
+
*
|
|
2051
|
+
* @param token - Bearer token (JWT from /auth/login or an API key)
|
|
2052
|
+
* @param socketUrl - Optional WebSocket URL (defaults to /api/v1/socket
|
|
2053
|
+
* for browser, provide full URL for Node.js)
|
|
1834
2054
|
*/
|
|
1835
|
-
const createTrustGraphSocket = (
|
|
1836
|
-
return new BaseApi(
|
|
2055
|
+
const createTrustGraphSocket = (token, socketUrl) => {
|
|
2056
|
+
return new BaseApi(token, socketUrl);
|
|
1837
2057
|
};
|
|
1838
2058
|
|
|
2059
|
+
// HTTP auth client for the TrustGraph IAM gateway endpoints.
|
|
2060
|
+
//
|
|
2061
|
+
// These endpoints run before the WebSocket exists, so they use plain
|
|
2062
|
+
// fetch rather than the socket transport.
|
|
2063
|
+
const DEFAULT_BOOTSTRAP_STATUS_URL = "/api/v1/auth/bootstrap-status";
|
|
2064
|
+
const DEFAULT_LOGIN_URL = "/api/v1/auth/login";
|
|
2065
|
+
class AuthError extends Error {
|
|
2066
|
+
constructor(message, status) {
|
|
2067
|
+
super(message);
|
|
2068
|
+
this.name = "AuthError";
|
|
2069
|
+
this.status = status;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
class AuthApi {
|
|
2073
|
+
constructor(options = {}) {
|
|
2074
|
+
this.bootstrapStatusUrl =
|
|
2075
|
+
options.bootstrapStatusUrl ?? DEFAULT_BOOTSTRAP_STATUS_URL;
|
|
2076
|
+
this.loginUrl = options.loginUrl ?? DEFAULT_LOGIN_URL;
|
|
2077
|
+
this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);
|
|
2078
|
+
}
|
|
2079
|
+
async bootstrapStatus() {
|
|
2080
|
+
const resp = await this.fetchImpl(this.bootstrapStatusUrl, {
|
|
2081
|
+
method: "POST",
|
|
2082
|
+
headers: { "Content-Type": "application/json" },
|
|
2083
|
+
body: "{}",
|
|
2084
|
+
});
|
|
2085
|
+
if (!resp.ok) {
|
|
2086
|
+
throw new AuthError(`bootstrap-status failed: ${resp.status}`, resp.status);
|
|
2087
|
+
}
|
|
2088
|
+
const body = await resp.json();
|
|
2089
|
+
return { bootstrapAvailable: !!body.bootstrap_available };
|
|
2090
|
+
}
|
|
2091
|
+
async login(username, password, default_workspace) {
|
|
2092
|
+
const payload = { username, password };
|
|
2093
|
+
if (default_workspace)
|
|
2094
|
+
payload.default_workspace = default_workspace;
|
|
2095
|
+
const resp = await this.fetchImpl(this.loginUrl, {
|
|
2096
|
+
method: "POST",
|
|
2097
|
+
headers: { "Content-Type": "application/json" },
|
|
2098
|
+
body: JSON.stringify(payload),
|
|
2099
|
+
});
|
|
2100
|
+
if (resp.status === 401) {
|
|
2101
|
+
throw new AuthError("auth failure", 401);
|
|
2102
|
+
}
|
|
2103
|
+
if (!resp.ok) {
|
|
2104
|
+
throw new AuthError(`login failed: ${resp.status}`, resp.status);
|
|
2105
|
+
}
|
|
2106
|
+
const body = await resp.json();
|
|
2107
|
+
if (!body.jwt) {
|
|
2108
|
+
throw new AuthError("login response missing jwt");
|
|
2109
|
+
}
|
|
2110
|
+
return { jwt: body.jwt, jwtExpires: body.jwt_expires ?? "" };
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
const createAuthApi = (options) => new AuthApi(options);
|
|
2114
|
+
|
|
2115
|
+
exports.AuthApi = AuthApi;
|
|
2116
|
+
exports.AuthError = AuthError;
|
|
1839
2117
|
exports.BaseApi = BaseApi;
|
|
1840
2118
|
exports.CollectionManagementApi = CollectionManagementApi;
|
|
1841
2119
|
exports.ConfigApi = ConfigApi;
|
|
1842
2120
|
exports.FlowApi = FlowApi;
|
|
1843
2121
|
exports.FlowsApi = FlowsApi;
|
|
2122
|
+
exports.IamApi = IamApi;
|
|
1844
2123
|
exports.KnowledgeApi = KnowledgeApi;
|
|
1845
2124
|
exports.LibrarianApi = LibrarianApi;
|
|
1846
2125
|
exports.PROV = PROV;
|
|
@@ -1861,13 +2140,15 @@ exports.SCHEMA_NAME = SCHEMA_NAME;
|
|
|
1861
2140
|
exports.SKOS = SKOS;
|
|
1862
2141
|
exports.SKOS_DEFINITION = SKOS_DEFINITION;
|
|
1863
2142
|
exports.TG = TG;
|
|
2143
|
+
exports.TG_CONCEPT = TG_CONCEPT;
|
|
1864
2144
|
exports.TG_CONTENT = TG_CONTENT;
|
|
1865
2145
|
exports.TG_DOCUMENT = TG_DOCUMENT;
|
|
1866
2146
|
exports.TG_EDGE = TG_EDGE;
|
|
1867
2147
|
exports.TG_EDGE_COUNT = TG_EDGE_COUNT;
|
|
1868
2148
|
exports.TG_QUERY = TG_QUERY;
|
|
1869
|
-
exports.TG_REASONING = TG_REASONING;
|
|
1870
2149
|
exports.TG_REIFIES = TG_REIFIES;
|
|
2150
|
+
exports.TG_SCORE = TG_SCORE;
|
|
1871
2151
|
exports.TG_SELECTED_EDGE = TG_SELECTED_EDGE;
|
|
2152
|
+
exports.createAuthApi = createAuthApi;
|
|
1872
2153
|
exports.createTrustGraphSocket = createTrustGraphSocket;
|
|
1873
2154
|
//# sourceMappingURL=index.cjs.map
|