@trustgraph/client 1.7.1 → 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/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 TG_REASONING = TG + "reasoning";
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 connects - immediately retry if we were waiting
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
- // Restore retry count since we didn't actually fail
88
- this.retries++;
89
- // Attempt immediately
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 ready
112
- if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) {
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 ready
295
- if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) {
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(user, token, socketUrl) {
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; // Store authentication 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...", token ? "with auth" : "without auth", "user:", user);
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 and reconnection 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
- status = hasApiKey ? "authenticated" : "unauthenticated";
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
- // Build WebSocket URL with optional token parameter
442
- const wsUrl = this.token
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
- // Notify UI of successful connection
503
- this.notifyStateChange();
504
- // Immediately retry any pending requests that were waiting for connection
505
- for (const mid in this.inflight) {
506
- this.inflight[mid].retryNow();
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 = 10000;
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 = 10000;
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 templates
1160
+ * Retrieves list of available prompt template IDs
1018
1161
  */
1019
1162
  getPrompts() {
1020
- return this.getConfigAll().then((r) => {
1021
- const config = r;
1022
- return JSON.parse(config.config.prompt["template-index"]);
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 (resp.chunk_type === "error" || resp.error) {
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 (agent uses chunk_type="explain")
1189
- if ((resp.chunk_type === "explain" || resp.message_type === "explain") && resp.explain_id && resp.explain_graph) {
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 chunk_type
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 (resp.chunk_type) {
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, 120000, 2, this.flowId)
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, 60000, undefined, this.flowId);
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, 60000, undefined, this.flowId);
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
- terms: terms,
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 templates
1822
+ * Retrieves available prompt template IDs
1607
1823
  */
1608
1824
  getPrompts() {
1609
- return this.getConfigAll().then((r) => {
1610
- const config = r;
1611
- return JSON.parse(config.config.prompt["template-index"]);
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
- * This is the main entry point for using the TrustGraph API
1831
- * @param user - User identifier for API requests
1832
- * @param token - Optional authentication token for secure connections
1833
- * @param socketUrl - Optional WebSocket URL (defaults to /api/socket for browser, provide full URL for Node.js)
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 = (user, token, socketUrl) => {
1836
- return new BaseApi(user, token, socketUrl);
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