@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.esm.js CHANGED
@@ -8,7 +8,8 @@ const TG_QUERY = TG + "query";
8
8
  const TG_EDGE_COUNT = TG + "edgeCount";
9
9
  const TG_SELECTED_EDGE = TG + "selectedEdge";
10
10
  const TG_EDGE = TG + "edge";
11
- const TG_REASONING = TG + "reasoning";
11
+ const TG_CONCEPT = TG + "concept";
12
+ const TG_SCORE = TG + "score";
12
13
  const TG_CONTENT = TG + "content";
13
14
  const TG_REIFIES = TG + "reifies";
14
15
  const TG_DOCUMENT = TG + "document";
@@ -74,18 +75,17 @@ class ServiceCallMulti {
74
75
  }
75
76
  }
76
77
  /**
77
- * Called when socket connects - immediately retry if we were waiting
78
+ * Called when socket reconnects. Streaming requests cannot be resumed
79
+ * after a disconnect — the backend state is gone — so fail immediately.
78
80
  */
79
81
  retryNow() {
80
82
  if (this.complete)
81
83
  return;
82
- // Clear any pending backoff timer
83
84
  clearTimeout(this.timeoutId);
84
85
  this.timeoutId = undefined;
85
- // Restore retry count since we didn't actually fail
86
- this.retries++;
87
- // Attempt immediately
88
- this.attempt();
86
+ this.complete = true;
87
+ delete this.socket.inflight[this.mid];
88
+ this.error("Connection lost during streaming request");
89
89
  }
90
90
  onTimeout() {
91
91
  if (this.complete == true)
@@ -106,8 +106,11 @@ class ServiceCallMulti {
106
106
  this.error("Ran out of retries");
107
107
  return; // Exit early - no more attempts
108
108
  }
109
- // Check if WebSocket connection is available and ready
110
- if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) {
109
+ // Check if WebSocket connection is available, open, and authenticated.
110
+ const authReady = this.socket.isAuthReady?.() ?? true;
111
+ if (this.socket.ws &&
112
+ this.socket.ws.readyState === WebSocket.OPEN &&
113
+ authReady) {
111
114
  try {
112
115
  this.socket.ws.send(JSON.stringify(this.msg));
113
116
  this.timeoutId = setTimeout(this.onTimeout.bind(this), this.timeout);
@@ -289,8 +292,14 @@ class ServiceCall {
289
292
  this.error("Ran out of retries");
290
293
  return; // Exit early - no more attempts
291
294
  }
292
- // Check if WebSocket connection is available and ready
293
- if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) {
295
+ // Check if WebSocket connection is available, open, and authenticated.
296
+ // The IAM gateway rejects all non-auth frames until the first-frame
297
+ // auth handshake has completed, so sends gated only on readyState
298
+ // would race the auth response.
299
+ const authReady = this.socket.isAuthReady?.() ?? true;
300
+ if (this.socket.ws &&
301
+ this.socket.ws.readyState === WebSocket.OPEN &&
302
+ authReady) {
294
303
  try {
295
304
  // Attempt to send the message as JSON
296
305
  this.socket.ws.send(JSON.stringify(this.msg));
@@ -319,12 +328,23 @@ class ServiceCall {
319
328
  // Configuration constants
320
329
  const SOCKET_RECONNECTION_TIMEOUT = 2000; // 2 seconds between reconnection
321
330
  // attempts
322
- const SOCKET_URL = "/api/socket"; // WebSocket endpoint path
331
+ const SOCKET_URL = "/api/v1/socket"; // WebSocket endpoint path
323
332
  /**
324
333
  * Generates a random message ID using cryptographically secure random values
325
334
  * @param length - Number of random characters to generate
326
335
  * @returns Random string of specified length
327
336
  */
337
+ // Normalise the top-level `error` field from a WebSocket response frame.
338
+ // The gateway may send it as a plain string *or* as an object with a
339
+ // `message` field; callers always want a string.
340
+ function errorToString(err) {
341
+ if (typeof err === "string")
342
+ return err;
343
+ if (err && typeof err === "object" && "message" in err) {
344
+ return String(err.message);
345
+ }
346
+ return String(err);
347
+ }
328
348
  function makeid(length) {
329
349
  const array = new Uint32Array(length);
330
350
  crypto.getRandomValues(array);
@@ -332,23 +352,46 @@ function makeid(length) {
332
352
  return array.reduce((acc, current) => acc + characters[current % characters.length], "");
333
353
  }
334
354
  class BaseApi {
335
- constructor(user, token, socketUrl) {
355
+ constructor(token, socketUrl) {
356
+ // Legacy honour-system field. The gateway now derives identity from the
357
+ // authenticated token; this is retained as an empty string so the
358
+ // existing request-builder code paths that read `api.user` keep
359
+ // compiling. It will be removed when the request payloads are cleaned
360
+ // up.
361
+ this.user = "";
362
+ // Active workspace for outbound requests. Sent as the envelope
363
+ // `workspace` field (sibling of `flow`); the gateway reconciles it
364
+ // against the authenticated identity and, when empty, defaults to
365
+ // the token's bound workspace. Kept in sync with the active
366
+ // workspace by the state layer.
367
+ this.workspace = "";
336
368
  this.inflight = {}; // Track active requests by
337
369
  // message ID
338
370
  this.reconnectAttempts = 0; // Track reconnection attempts
339
371
  this.maxReconnectAttempts = 10; // Maximum reconnection attempts
340
372
  this.reconnectionState = "idle"; // Connection state
373
+ // Auth-handshake state for the current socket. Reset on each open.
374
+ this.authState = "pending";
341
375
  // Connection state tracking for UI
342
376
  this.connectionStateListeners = [];
343
377
  this.tag = makeid(16); // Generate unique client tag
344
378
  this.id = 1; // Start message ID counter
345
- this.token = token; // Store authentication token
346
- this.user = user; // Store user identifier
379
+ this.token = token;
347
380
  this.socketUrl = socketUrl || SOCKET_URL; // Use provided URL or default
348
- console.log("SOCKET: opening socket...", token ? "with auth" : "without auth", "user:", user);
381
+ console.log("SOCKET: opening socket...");
349
382
  this.openSocket(); // Establish WebSocket connection
350
383
  console.log("SOCKET: socket opened");
351
384
  }
385
+ /**
386
+ * True once the WebSocket is OPEN and the first-frame auth handshake
387
+ * has been accepted by the gateway. ServiceCall consults this before
388
+ * sending request frames.
389
+ */
390
+ isAuthReady() {
391
+ return (!!this.ws &&
392
+ this.ws.readyState === WebSocket.OPEN &&
393
+ this.authState === "ok");
394
+ }
352
395
  /**
353
396
  * Subscribe to connection state changes for UI updates
354
397
  */
@@ -369,7 +412,8 @@ class BaseApi {
369
412
  */
370
413
  getConnectionState() {
371
414
  const hasApiKey = !!this.token;
372
- // Determine status based on WebSocket state and reconnection state
415
+ // Determine status based on WebSocket state, auth handshake state, and
416
+ // reconnection state.
373
417
  let status;
374
418
  if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
375
419
  if (this.reconnectionState === "failed") {
@@ -386,7 +430,12 @@ class BaseApi {
386
430
  status = "connecting";
387
431
  }
388
432
  else if (this.ws.readyState === WebSocket.OPEN) {
389
- status = hasApiKey ? "authenticated" : "unauthenticated";
433
+ if (this.authState === "ok")
434
+ status = "authenticated";
435
+ else if (this.authState === "failed")
436
+ status = "auth-failed";
437
+ else
438
+ status = "authenticating";
390
439
  }
391
440
  else {
392
441
  status = "connecting";
@@ -435,13 +484,12 @@ class BaseApi {
435
484
  this.ws.removeEventListener("error", this.onError);
436
485
  this.ws = undefined;
437
486
  }
487
+ // Reset the auth handshake state for the new connection. The token
488
+ // is sent as the first frame after onOpen, never on the URL.
489
+ this.authState = "pending";
438
490
  try {
439
- // Build WebSocket URL with optional token parameter
440
- const wsUrl = this.token
441
- ? `${this.socketUrl}?token=${this.token}`
442
- : this.socketUrl;
443
- console.log("SOCKET: connecting to", wsUrl.replace(/token=[^&]*/, "token=***"));
444
- this.ws = new WebSocket(wsUrl);
491
+ console.log("SOCKET: connecting to", this.socketUrl);
492
+ this.ws = new WebSocket(this.socketUrl);
445
493
  }
446
494
  catch (e) {
447
495
  console.error("[socket creation error]", e);
@@ -465,6 +513,34 @@ class BaseApi {
465
513
  return;
466
514
  try {
467
515
  const obj = JSON.parse(message.data);
516
+ // Auth handshake frames are addressed by `type`, not by request id.
517
+ if (obj.type === "auth-ok") {
518
+ console.log("[socket] auth-ok", obj.default_workspace ?? "");
519
+ this.authState = "ok";
520
+ this.lastError = undefined;
521
+ this.notifyStateChange();
522
+ // Auth complete — release any requests that were waiting for the
523
+ // socket to become usable.
524
+ for (const mid in this.inflight) {
525
+ this.inflight[mid].retryNow();
526
+ }
527
+ return;
528
+ }
529
+ if (obj.type === "auth-failed") {
530
+ console.warn("[socket] auth-failed", obj.error);
531
+ this.authState = "failed";
532
+ this.lastError = obj.error || "auth failure";
533
+ this.notifyStateChange();
534
+ // Per the IAM spec the server keeps the socket open so the client
535
+ // can re-authenticate without reconnecting. Don't auto-close.
536
+ // Surface the failure to any inflight requests so they don't sit
537
+ // forever — callers should clear the token and reauth.
538
+ for (const mid in this.inflight) {
539
+ this.inflight[mid].error(new Error("auth failure"));
540
+ }
541
+ this.inflight = {};
542
+ return;
543
+ }
468
544
  // Skip messages without ID (can't route them)
469
545
  if (!obj.id)
470
546
  return;
@@ -497,12 +573,19 @@ class BaseApi {
497
573
  clearTimeout(this.reconnectTimer);
498
574
  this.reconnectTimer = undefined;
499
575
  }
500
- // Notify UI of successful connection
501
- this.notifyStateChange();
502
- // Immediately retry any pending requests that were waiting for connection
503
- for (const mid in this.inflight) {
504
- this.inflight[mid].retryNow();
576
+ // Send the auth frame as the first message. The server rejects all
577
+ // non-auth messages until it sends back auth-ok, so we hold off
578
+ // releasing inflight requests here that happens in onMessage when
579
+ // auth-ok arrives.
580
+ this.authState = "pending";
581
+ try {
582
+ this.ws?.send(JSON.stringify({ type: "auth", token: this.token }));
505
583
  }
584
+ catch (e) {
585
+ console.error("[socket] failed to send auth frame", e);
586
+ }
587
+ // Notify UI that we're now in the authenticating phase.
588
+ this.notifyStateChange();
506
589
  }
507
590
  // Handle socket errors
508
591
  onError(event) {
@@ -605,7 +688,7 @@ class BaseApi {
605
688
  const mid = this.getNextId();
606
689
  // Set default values
607
690
  if (timeout == undefined)
608
- timeout = 10000;
691
+ timeout = 30000;
609
692
  if (retries == undefined)
610
693
  retries = 3;
611
694
  // Construct the request message
@@ -617,6 +700,10 @@ class BaseApi {
617
700
  // Add flow identifier if provided
618
701
  if (flow)
619
702
  msg.flow = flow;
703
+ // Stamp the active workspace onto the envelope. When empty the
704
+ // gateway falls back to the token's bound workspace.
705
+ if (this.workspace)
706
+ msg.workspace = this.workspace;
620
707
  // Return a Promise that will be resolved/rejected by the ServiceCall
621
708
  return new Promise((resolve, reject) => {
622
709
  const call = new ServiceCall(mid, msg, resolve, reject, timeout, retries, this);
@@ -636,7 +723,7 @@ class BaseApi {
636
723
  const mid = this.getNextId();
637
724
  // Set defaults
638
725
  if (timeout == undefined)
639
- timeout = 10000;
726
+ timeout = 30000;
640
727
  if (retries == undefined)
641
728
  retries = 3;
642
729
  // Construct request message
@@ -647,6 +734,8 @@ class BaseApi {
647
734
  };
648
735
  if (flow)
649
736
  msg.flow = flow;
737
+ if (this.workspace)
738
+ msg.workspace = this.workspace;
650
739
  return new Promise((resolve, reject) => {
651
740
  const call = new ServiceCallMulti(mid, msg, resolve, reject, timeout, retries, this, // eslint-disable-line @typescript-eslint/no-explicit-any
652
741
  receiver);
@@ -683,6 +772,49 @@ class BaseApi {
683
772
  collectionManagement() {
684
773
  return new CollectionManagementApi(this);
685
774
  }
775
+ iam() {
776
+ return new IamApi(this);
777
+ }
778
+ }
779
+ /**
780
+ * IamApi - Identity and workspace discovery over the authenticated
781
+ * socket. IAM is the one surface that lives outside workspace tenancy;
782
+ * these calls run as the `iam` service and the gateway injects the
783
+ * caller's identity from the connection's token.
784
+ */
785
+ class IamApi {
786
+ constructor(api) {
787
+ this.api = api;
788
+ }
789
+ // The caller's own user record, including their home workspace.
790
+ whoami() {
791
+ return this.api
792
+ .makeRequest("iam", { operation: "whoami" })
793
+ .then((r) => {
794
+ const user = r.user ?? {};
795
+ return {
796
+ id: String(user.id ?? ""),
797
+ username: String(user.username ?? ""),
798
+ name: String(user.name ?? ""),
799
+ email: String(user.email ?? ""),
800
+ default_workspace: String(user.default_workspace ?? ""),
801
+ roles: Array.isArray(user.roles) ? user.roles : [],
802
+ enabled: !!user.enabled,
803
+ };
804
+ });
805
+ }
806
+ // The workspaces the caller has access to (one for an ordinary user,
807
+ // all for an admin — driven entirely by what the gateway returns).
808
+ listMyWorkspaces() {
809
+ return this.api
810
+ .makeRequest("iam", { operation: "list-my-workspaces" })
811
+ .then((r) => (r.workspaces ?? []).map((w) => ({
812
+ id: String(w.id ?? ""),
813
+ name: String(w.name ?? ""),
814
+ enabled: !!w.enabled,
815
+ created: String(w.created ?? ""),
816
+ })));
817
+ }
686
818
  }
687
819
  /**
688
820
  * LibrarianApi - Manages document storage and retrieval
@@ -921,7 +1053,7 @@ class LibrarianApi {
921
1053
  const msg = message;
922
1054
  // Check for top-level error
923
1055
  if (msg.error) {
924
- onError(msg.error);
1056
+ onError(errorToString(msg.error));
925
1057
  return true;
926
1058
  }
927
1059
  const resp = msg.response;
@@ -1007,17 +1139,30 @@ class FlowsApi {
1007
1139
  deleteConfig(keys) {
1008
1140
  return this.api.makeRequest("config", {
1009
1141
  operation: "delete",
1010
- keys: keys,
1142
+ keys: [keys],
1011
1143
  }, 30000);
1012
1144
  }
1145
+ /**
1146
+ * Lists available configuration keys for a given type
1147
+ */
1148
+ list(type) {
1149
+ return this.api
1150
+ .makeRequest("config", {
1151
+ operation: "list",
1152
+ type: type,
1153
+ }, 60000)
1154
+ .then((r) => r);
1155
+ }
1013
1156
  // Prompt management - specialized config operations for AI prompts
1014
1157
  /**
1015
- * Retrieves list of available prompt templates
1158
+ * Retrieves list of available prompt template IDs
1016
1159
  */
1017
1160
  getPrompts() {
1018
- return this.getConfigAll().then((r) => {
1019
- const config = r;
1020
- return JSON.parse(config.config.prompt["template-index"]);
1161
+ return this.list("prompt").then((r) => {
1162
+ const keys = r?.directory || [];
1163
+ return keys
1164
+ .filter((k) => k.startsWith("template."))
1165
+ .map((k) => k.slice("template.".length));
1021
1166
  });
1022
1167
  }
1023
1168
  /**
@@ -1174,17 +1319,19 @@ class FlowApi {
1174
1319
  const msg = message;
1175
1320
  // Check for top-level error
1176
1321
  if (msg.error) {
1177
- error(msg.error);
1322
+ error(errorToString(msg.error));
1178
1323
  return true;
1179
1324
  }
1180
1325
  const resp = msg.response || {};
1326
+ // Prefer message_type, fall back to chunk_type for older backends
1327
+ const msgType = resp.message_type || resp.chunk_type;
1181
1328
  // Check for errors in response
1182
- if (resp.chunk_type === "error" || resp.error) {
1329
+ if (msgType === "error" || resp.error) {
1183
1330
  error(resp.error?.message || "Unknown agent error");
1184
1331
  return true; // End streaming on error
1185
1332
  }
1186
- // Handle explainability events (agent uses chunk_type="explain")
1187
- if ((resp.chunk_type === "explain" || resp.message_type === "explain") && resp.explain_id && resp.explain_graph) {
1333
+ // Handle explainability events
1334
+ if (msgType === "explain" && resp.explain_id && resp.explain_graph) {
1188
1335
  onExplain?.({
1189
1336
  explainId: resp.explain_id,
1190
1337
  explainGraph: resp.explain_graph,
@@ -1192,7 +1339,7 @@ class FlowApi {
1192
1339
  });
1193
1340
  return false;
1194
1341
  }
1195
- // Handle streaming chunks by chunk_type
1342
+ // Handle streaming chunks by message type
1196
1343
  const content = resp.content || "";
1197
1344
  const messageId = resp.message_id;
1198
1345
  const messageComplete = !!resp.end_of_message;
@@ -1201,7 +1348,7 @@ class FlowApi {
1201
1348
  const metadata = dialogComplete && (resp.in_token || resp.out_token || resp.model)
1202
1349
  ? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model }
1203
1350
  : undefined;
1204
- switch (resp.chunk_type) {
1351
+ switch (msgType) {
1205
1352
  case "thought":
1206
1353
  think(content, messageComplete, messageId, metadata);
1207
1354
  break;
@@ -1225,7 +1372,7 @@ class FlowApi {
1225
1372
  user: this.api.user,
1226
1373
  collection: collection || "default",
1227
1374
  streaming: true, // Always use streaming mode
1228
- }, receiver, 120000, 2, this.flowId)
1375
+ }, receiver, 180000, 1, this.flowId)
1229
1376
  .catch((err) => {
1230
1377
  const errorMessage = err instanceof Error ? err.message : err?.toString() || "Unknown error";
1231
1378
  error(`Agent request failed: ${errorMessage}`);
@@ -1245,7 +1392,7 @@ class FlowApi {
1245
1392
  const msg = message;
1246
1393
  // Check for top-level error
1247
1394
  if (msg.error) {
1248
- onError(msg.error);
1395
+ onError(errorToString(msg.error));
1249
1396
  return true;
1250
1397
  }
1251
1398
  const resp = (msg.response || {});
@@ -1283,7 +1430,7 @@ class FlowApi {
1283
1430
  "max-subgraph-size": options?.maxSubgraphSize,
1284
1431
  "max-path-length": options?.pathLength,
1285
1432
  streaming: true,
1286
- }, recv, 60000, undefined, this.flowId);
1433
+ }, recv, 180000, 1, this.flowId);
1287
1434
  }
1288
1435
  /**
1289
1436
  * Performs Document RAG query with streaming response
@@ -1298,7 +1445,7 @@ class FlowApi {
1298
1445
  const msg = message;
1299
1446
  // Check for top-level error
1300
1447
  if (msg.error) {
1301
- onError(msg.error);
1448
+ onError(errorToString(msg.error));
1302
1449
  return true;
1303
1450
  }
1304
1451
  const resp = (msg.response || {});
@@ -1331,7 +1478,7 @@ class FlowApi {
1331
1478
  collection: collection || "default",
1332
1479
  "doc-limit": docLimit,
1333
1480
  streaming: true,
1334
- }, recv, 60000, undefined, this.flowId);
1481
+ }, recv, 180000, 1, this.flowId);
1335
1482
  }
1336
1483
  /**
1337
1484
  * Performs text completion with streaming response
@@ -1345,7 +1492,7 @@ class FlowApi {
1345
1492
  const msg = message;
1346
1493
  // Check for top-level error
1347
1494
  if (msg.error) {
1348
- onError(msg.error);
1495
+ onError(errorToString(msg.error));
1349
1496
  return true;
1350
1497
  }
1351
1498
  const resp = (msg.response || {});
@@ -1382,7 +1529,7 @@ class FlowApi {
1382
1529
  const msg = message;
1383
1530
  // Check for top-level error
1384
1531
  if (msg.error) {
1385
- onError(msg.error);
1532
+ onError(errorToString(msg.error));
1386
1533
  return true;
1387
1534
  }
1388
1535
  const resp = (msg.response || {});
@@ -1403,7 +1550,7 @@ class FlowApi {
1403
1550
  };
1404
1551
  this.api.makeRequestMulti("prompt", {
1405
1552
  id: id,
1406
- terms: terms,
1553
+ variables: terms,
1407
1554
  streaming: true,
1408
1555
  }, recv, 30000, undefined, this.flowId);
1409
1556
  }
@@ -1448,6 +1595,75 @@ class FlowApi {
1448
1595
  }, 30000, undefined, this.flowId)
1449
1596
  .then((r) => r.response);
1450
1597
  }
1598
+ sparqlQuery(query, collection, limit, batchSize) {
1599
+ const columns = [];
1600
+ const rows = [];
1601
+ let queryType = "select";
1602
+ let askResult;
1603
+ let triples;
1604
+ let sparqlError = null;
1605
+ const termToString = (val) => {
1606
+ if (!val)
1607
+ return "";
1608
+ if (val.t === "i")
1609
+ return val.i;
1610
+ if (val.t === "l")
1611
+ return val.v;
1612
+ return "";
1613
+ };
1614
+ return this.api
1615
+ .makeRequestMulti("sparql", {
1616
+ query,
1617
+ collection: collection || "default",
1618
+ limit: limit ?? 10000,
1619
+ streaming: true,
1620
+ "batch-size": batchSize ?? 50,
1621
+ }, (resp) => {
1622
+ const msg = resp;
1623
+ const batch = msg.response;
1624
+ const isComplete = msg.complete === true;
1625
+ if (!batch)
1626
+ return isComplete;
1627
+ if (batch.error) {
1628
+ sparqlError = typeof batch.error === "string"
1629
+ ? batch.error
1630
+ : batch.error.message || "SPARQL query error";
1631
+ return true;
1632
+ }
1633
+ queryType = batch["query-type"] || queryType;
1634
+ if (queryType === "ask") {
1635
+ askResult = batch["ask-result"];
1636
+ return true;
1637
+ }
1638
+ if (queryType === "construct" || queryType === "describe") {
1639
+ triples = batch.triples || [];
1640
+ return true;
1641
+ }
1642
+ if (batch.variables && columns.length === 0) {
1643
+ columns.push(...batch.variables);
1644
+ }
1645
+ if (batch.bindings) {
1646
+ for (const binding of batch.bindings) {
1647
+ const row = {};
1648
+ for (let i = 0; i < columns.length; i++) {
1649
+ row[columns[i]] = termToString(binding.values[i] ?? null);
1650
+ }
1651
+ rows.push(row);
1652
+ }
1653
+ }
1654
+ return isComplete;
1655
+ }, 60000, undefined, this.flowId)
1656
+ .then(() => {
1657
+ if (sparqlError)
1658
+ throw new Error(sparqlError);
1659
+ return { queryType, columns, rows, askResult, triples };
1660
+ })
1661
+ .catch((err) => {
1662
+ if (err instanceof Error)
1663
+ throw err;
1664
+ throw new Error(typeof err === "string" ? err : JSON.stringify(err));
1665
+ });
1666
+ }
1451
1667
  /**
1452
1668
  * Loads a document into this flow for processing
1453
1669
  */
@@ -1596,17 +1812,19 @@ class ConfigApi {
1596
1812
  deleteConfig(keys) {
1597
1813
  return this.api.makeRequest("config", {
1598
1814
  operation: "delete",
1599
- keys: keys,
1815
+ keys: [keys],
1600
1816
  }, 30000);
1601
1817
  }
1602
1818
  // Specialized prompt management methods
1603
1819
  /**
1604
- * Retrieves available prompt templates
1820
+ * Retrieves available prompt template IDs
1605
1821
  */
1606
1822
  getPrompts() {
1607
- return this.getConfigAll().then((r) => {
1608
- const config = r;
1609
- return JSON.parse(config.config.prompt["template-index"]);
1823
+ return this.list("prompt").then((r) => {
1824
+ const keys = r?.directory || [];
1825
+ return keys
1826
+ .filter((k) => k.startsWith("template."))
1827
+ .map((k) => k.slice("template.".length));
1610
1828
  });
1611
1829
  }
1612
1830
  /**
@@ -1824,15 +2042,73 @@ class CollectionManagementApi {
1824
2042
  }
1825
2043
  }
1826
2044
  /**
1827
- * Factory function to create a new TrustGraph WebSocket connection
1828
- * This is the main entry point for using the TrustGraph API
1829
- * @param user - User identifier for API requests
1830
- * @param token - Optional authentication token for secure connections
1831
- * @param socketUrl - Optional WebSocket URL (defaults to /api/socket for browser, provide full URL for Node.js)
2045
+ * Factory function to create a new TrustGraph WebSocket connection.
2046
+ * The token (JWT or API key) is sent as the first frame after connect;
2047
+ * the gateway derives the user identity and workspace from it.
2048
+ *
2049
+ * @param token - Bearer token (JWT from /auth/login or an API key)
2050
+ * @param socketUrl - Optional WebSocket URL (defaults to /api/v1/socket
2051
+ * for browser, provide full URL for Node.js)
1832
2052
  */
1833
- const createTrustGraphSocket = (user, token, socketUrl) => {
1834
- return new BaseApi(user, token, socketUrl);
2053
+ const createTrustGraphSocket = (token, socketUrl) => {
2054
+ return new BaseApi(token, socketUrl);
1835
2055
  };
1836
2056
 
1837
- export { BaseApi, CollectionManagementApi, ConfigApi, FlowApi, FlowsApi, KnowledgeApi, LibrarianApi, PROV, PROV_ACTIVITY, PROV_ENTITY, PROV_STARTED_AT_TIME, PROV_WAS_DERIVED_FROM, PROV_WAS_GENERATED_BY, RDF, RDFS, RDFS_LABEL, RDF_TYPE, SCHEMA, SCHEMA_AUTHOR, SCHEMA_DESCRIPTION, SCHEMA_KEYWORDS, SCHEMA_NAME, SKOS, SKOS_DEFINITION, TG, TG_CONTENT, TG_DOCUMENT, TG_EDGE, TG_EDGE_COUNT, TG_QUERY, TG_REASONING, TG_REIFIES, TG_SELECTED_EDGE, createTrustGraphSocket };
2057
+ // HTTP auth client for the TrustGraph IAM gateway endpoints.
2058
+ //
2059
+ // These endpoints run before the WebSocket exists, so they use plain
2060
+ // fetch rather than the socket transport.
2061
+ const DEFAULT_BOOTSTRAP_STATUS_URL = "/api/v1/auth/bootstrap-status";
2062
+ const DEFAULT_LOGIN_URL = "/api/v1/auth/login";
2063
+ class AuthError extends Error {
2064
+ constructor(message, status) {
2065
+ super(message);
2066
+ this.name = "AuthError";
2067
+ this.status = status;
2068
+ }
2069
+ }
2070
+ class AuthApi {
2071
+ constructor(options = {}) {
2072
+ this.bootstrapStatusUrl =
2073
+ options.bootstrapStatusUrl ?? DEFAULT_BOOTSTRAP_STATUS_URL;
2074
+ this.loginUrl = options.loginUrl ?? DEFAULT_LOGIN_URL;
2075
+ this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);
2076
+ }
2077
+ async bootstrapStatus() {
2078
+ const resp = await this.fetchImpl(this.bootstrapStatusUrl, {
2079
+ method: "POST",
2080
+ headers: { "Content-Type": "application/json" },
2081
+ body: "{}",
2082
+ });
2083
+ if (!resp.ok) {
2084
+ throw new AuthError(`bootstrap-status failed: ${resp.status}`, resp.status);
2085
+ }
2086
+ const body = await resp.json();
2087
+ return { bootstrapAvailable: !!body.bootstrap_available };
2088
+ }
2089
+ async login(username, password, default_workspace) {
2090
+ const payload = { username, password };
2091
+ if (default_workspace)
2092
+ payload.default_workspace = default_workspace;
2093
+ const resp = await this.fetchImpl(this.loginUrl, {
2094
+ method: "POST",
2095
+ headers: { "Content-Type": "application/json" },
2096
+ body: JSON.stringify(payload),
2097
+ });
2098
+ if (resp.status === 401) {
2099
+ throw new AuthError("auth failure", 401);
2100
+ }
2101
+ if (!resp.ok) {
2102
+ throw new AuthError(`login failed: ${resp.status}`, resp.status);
2103
+ }
2104
+ const body = await resp.json();
2105
+ if (!body.jwt) {
2106
+ throw new AuthError("login response missing jwt");
2107
+ }
2108
+ return { jwt: body.jwt, jwtExpires: body.jwt_expires ?? "" };
2109
+ }
2110
+ }
2111
+ const createAuthApi = (options) => new AuthApi(options);
2112
+
2113
+ export { AuthApi, AuthError, BaseApi, CollectionManagementApi, ConfigApi, FlowApi, FlowsApi, IamApi, KnowledgeApi, LibrarianApi, PROV, PROV_ACTIVITY, PROV_ENTITY, PROV_STARTED_AT_TIME, PROV_WAS_DERIVED_FROM, PROV_WAS_GENERATED_BY, RDF, RDFS, RDFS_LABEL, RDF_TYPE, SCHEMA, SCHEMA_AUTHOR, SCHEMA_DESCRIPTION, SCHEMA_KEYWORDS, SCHEMA_NAME, SKOS, SKOS_DEFINITION, TG, TG_CONCEPT, TG_CONTENT, TG_DOCUMENT, TG_EDGE, TG_EDGE_COUNT, TG_QUERY, TG_REIFIES, TG_SCORE, TG_SELECTED_EDGE, createAuthApi, createTrustGraphSocket };
1838
2114
  //# sourceMappingURL=index.esm.js.map