@arbidocs/sdk 0.3.11 → 0.3.13

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/browser.cjs CHANGED
@@ -1,13 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var client = require('@arbidocs/client');
4
- var fs = require('fs');
5
- var path = require('path');
6
-
7
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
-
9
- var fs__default = /*#__PURE__*/_interopDefault(fs);
10
- var path__default = /*#__PURE__*/_interopDefault(path);
11
4
 
12
5
  var __defProp = Object.defineProperty;
13
6
  var __export = (target, all) => {
@@ -42,7 +35,24 @@ function requireOk(result, message) {
42
35
  }
43
36
  }
44
37
  function getErrorMessage(err) {
45
- return err instanceof Error ? err.message : String(err);
38
+ if (!(err instanceof Error)) return String(err);
39
+ const rootCause = getDeepestCause(err);
40
+ if (rootCause !== err) {
41
+ const rootMsg = rootCause.message || String(rootCause);
42
+ if (err.message && !err.message.includes(rootMsg)) {
43
+ return `${err.message}: ${rootMsg}`;
44
+ }
45
+ }
46
+ return err.message;
47
+ }
48
+ function getDeepestCause(err) {
49
+ let current = err;
50
+ const seen = /* @__PURE__ */ new Set();
51
+ while (current.cause instanceof Error && !seen.has(current.cause)) {
52
+ seen.add(current);
53
+ current = current.cause;
54
+ }
55
+ return current;
46
56
  }
47
57
 
48
58
  // src/fetch.ts
@@ -72,8 +82,8 @@ async function extractErrorMessage(res) {
72
82
  return `Request failed: ${res.status} ${res.statusText}`;
73
83
  }
74
84
  async function authenticatedFetch(options) {
75
- const { baseUrl, accessToken, workspaceKeyHeader, path: path2, method, body, headers } = options;
76
- const res = await fetch(`${baseUrl}${path2}`, {
85
+ const { baseUrl, accessToken, workspaceKeyHeader, path, method, body, headers } = options;
86
+ const res = await fetch(`${baseUrl}${path}`, {
77
87
  method: method ?? (body ? "POST" : "GET"),
78
88
  headers: {
79
89
  Authorization: `Bearer ${accessToken}`,
@@ -112,7 +122,11 @@ async function createAuthenticatedClient(config, creds, store) {
112
122
  });
113
123
  store.saveCredentials({
114
124
  ...creds,
115
- serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
125
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
126
+ accessToken: void 0,
127
+ workspaceKeyHeader: void 0,
128
+ workspaceId: void 0,
129
+ tokenTimestamp: void 0
116
130
  });
117
131
  return { arbi, loginResult };
118
132
  }
@@ -127,7 +141,12 @@ async function performPasswordLogin(config, email, password, store) {
127
141
  store.saveCredentials({
128
142
  email,
129
143
  signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
130
- serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
144
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
145
+ // Clear any cached workspace tokens — new session key invalidates them
146
+ accessToken: void 0,
147
+ workspaceKeyHeader: void 0,
148
+ workspaceId: void 0,
149
+ tokenTimestamp: void 0
131
150
  });
132
151
  return { arbi, loginResult, config };
133
152
  }
@@ -180,6 +199,10 @@ async function resolveAuth(store) {
180
199
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
181
200
  return { arbi, loginResult, config };
182
201
  }
202
+ var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
203
+ function isCachedTokenValid(creds, workspaceId) {
204
+ return !!(creds.accessToken && creds.workspaceKeyHeader && creds.workspaceId === workspaceId && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
205
+ }
183
206
  async function resolveWorkspace(store, workspaceOpt) {
184
207
  const config = store.requireConfig();
185
208
  const creds = store.requireCredentials();
@@ -187,6 +210,32 @@ async function resolveWorkspace(store, workspaceOpt) {
187
210
  if (!workspaceId) {
188
211
  throw new ArbiError("No workspace selected. Run: arbi workspace select <id>");
189
212
  }
213
+ if (isCachedTokenValid(creds, workspaceId)) {
214
+ const arbi2 = client.createArbiClient({
215
+ baseUrl: config.baseUrl,
216
+ deploymentDomain: config.deploymentDomain,
217
+ credentials: "omit"
218
+ });
219
+ await arbi2.crypto.initSodium();
220
+ arbi2.session.setSelectedWorkspace(workspaceId);
221
+ arbi2.session.setAccessToken(creds.accessToken);
222
+ arbi2.session.setCachedWorkspaceHeader(workspaceId, creds.workspaceKeyHeader);
223
+ const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
224
+ const serverSessionKey = client.base64ToBytes(creds.serverSessionKeyBase64);
225
+ const loginResult2 = {
226
+ accessToken: creds.accessToken,
227
+ signingPrivateKey,
228
+ serverSessionKey
229
+ };
230
+ return {
231
+ arbi: arbi2,
232
+ loginResult: loginResult2,
233
+ config,
234
+ workspaceId,
235
+ accessToken: creds.accessToken,
236
+ workspaceKeyHeader: creds.workspaceKeyHeader
237
+ };
238
+ }
190
239
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
191
240
  await selectWorkspaceById(
192
241
  arbi,
@@ -199,10 +248,57 @@ async function resolveWorkspace(store, workspaceOpt) {
199
248
  if (!accessToken || !workspaceKeyHeader) {
200
249
  throw new ArbiError("Authentication error \u2014 missing token or workspace key");
201
250
  }
251
+ store.saveCredentials({
252
+ ...store.requireCredentials(),
253
+ accessToken,
254
+ workspaceKeyHeader,
255
+ workspaceId,
256
+ tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString()
257
+ });
202
258
  return { arbi, loginResult, config, workspaceId, accessToken, workspaceKeyHeader };
203
259
  }
204
260
 
205
261
  // src/sse.ts
262
+ var TOOL_LABELS = {
263
+ search_documents: "Searching documents",
264
+ get_document_passages: "Reading document",
265
+ get_table_of_contents: "Getting table of contents",
266
+ view_document_pages: "Viewing document pages",
267
+ get_full_document: "Reading full document",
268
+ web_search: "Searching the web",
269
+ read_url: "Reading web pages",
270
+ ask_user: "Asking user",
271
+ compaction: "Compacting conversation",
272
+ personal_agent: "Running agent",
273
+ create_artifact: "Creating artifact",
274
+ create_plan: "Creating plan",
275
+ save_skill: "Saving skill",
276
+ run_code: "Running code"
277
+ };
278
+ var LIFECYCLE_LABELS = {
279
+ evaluation: "Evaluating results",
280
+ answering: "Writing answer",
281
+ reviewing: "Reviewing answer",
282
+ planning: "Planning",
283
+ tool_progress: "Working"
284
+ };
285
+ function formatAgentStepLabel(step) {
286
+ if (step.focus) return step.focus;
287
+ const detail = step.detail;
288
+ if (step.step === "tool_progress" && detail && detail.length > 0) {
289
+ const toolName = detail[0].tool;
290
+ const label = toolName && TOOL_LABELS[toolName] || LIFECYCLE_LABELS.tool_progress;
291
+ const message = detail[0].message;
292
+ return message ? `${label}: ${message}` : label;
293
+ }
294
+ if (step.step) {
295
+ return LIFECYCLE_LABELS[step.step] || step.step;
296
+ }
297
+ if (detail && detail.length > 0 && detail[0].tool) {
298
+ return TOOL_LABELS[detail[0].tool] || detail[0].tool;
299
+ }
300
+ return "";
301
+ }
206
302
  function parseSSEEvents(chunk, buffer) {
207
303
  const combined = buffer + chunk;
208
304
  const events = [];
@@ -290,8 +386,8 @@ async function streamSSE(response, callbacks = {}) {
290
386
  // ARBI-specific events (dot-prefixed from server)
291
387
  "arbi.agent_step": (raw) => {
292
388
  const data = JSON.parse(raw);
293
- const focus = data.focus || data.step || "";
294
- if (focus) agentSteps.push(focus);
389
+ const label = formatAgentStepLabel(data);
390
+ if (label) agentSteps.push(label);
295
391
  callbacks.onAgentStep?.(data);
296
392
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
297
393
  },
@@ -470,16 +566,34 @@ function formatUserName(user) {
470
566
  // src/operations/documents.ts
471
567
  var documents_exports = {};
472
568
  __export(documents_exports, {
569
+ SUPPORTED_EXTENSIONS: () => SUPPORTED_EXTENSIONS,
473
570
  deleteDocuments: () => deleteDocuments,
474
571
  downloadDocument: () => downloadDocument,
475
572
  getDocuments: () => getDocuments,
476
573
  getParsedContent: () => getParsedContent,
477
574
  listDocuments: () => listDocuments,
575
+ sanitizeFolderPath: () => sanitizeFolderPath,
478
576
  updateDocuments: () => updateDocuments,
479
577
  uploadFile: () => uploadFile,
480
- uploadLocalFile: () => uploadLocalFile,
578
+ uploadFiles: () => uploadFiles,
481
579
  uploadUrl: () => uploadUrl
482
580
  });
581
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
582
+ ".pdf",
583
+ ".txt",
584
+ ".md",
585
+ ".html",
586
+ ".doc",
587
+ ".docx",
588
+ ".rtf",
589
+ ".ppt",
590
+ ".pptx",
591
+ ".xls",
592
+ ".xlsx"
593
+ ]);
594
+ function sanitizeFolderPath(folderPath) {
595
+ return folderPath.replace(/[^a-zA-Z0-9_\-/]/g, "_").replace(/_{2,}/g, "_");
596
+ }
483
597
  async function listDocuments(arbi) {
484
598
  return requireData(await arbi.fetch.GET("/v1/document/list"), "Failed to fetch documents");
485
599
  }
@@ -518,22 +632,31 @@ async function getParsedContent(auth, docId, stage) {
518
632
  });
519
633
  return res.json();
520
634
  }
521
- async function uploadFile(auth, workspaceId, fileData, fileName) {
635
+ async function uploadFile(auth, workspaceId, fileData, fileName, options) {
522
636
  const formData = new FormData();
523
637
  formData.append("files", fileData, fileName);
638
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
639
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
524
640
  const res = await authenticatedFetch({
525
641
  ...auth,
526
- path: `/v1/document/upload?workspace_ext_id=${workspaceId}`,
642
+ path: `/v1/document/upload?${params.toString()}`,
527
643
  method: "POST",
528
644
  body: formData
529
645
  });
530
646
  return res.json();
531
647
  }
532
- async function uploadLocalFile(auth, workspaceId, filePath) {
533
- const fileBuffer = fs__default.default.readFileSync(filePath);
534
- const fileName = path__default.default.basename(filePath);
535
- const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName);
536
- return { ...result, fileName };
648
+ async function uploadFiles(auth, workspaceId, files, options) {
649
+ const formData = new FormData();
650
+ for (const f of files) formData.append("files", f.data, f.name);
651
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
652
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
653
+ const res = await authenticatedFetch({
654
+ ...auth,
655
+ path: `/v1/document/upload?${params.toString()}`,
656
+ method: "POST",
657
+ body: formData
658
+ });
659
+ return res.json();
537
660
  }
538
661
  async function downloadDocument(auth, docId) {
539
662
  return authenticatedFetch({
@@ -733,6 +856,8 @@ async function retrieve(options) {
733
856
  input: query,
734
857
  workspace_ext_id: workspaceId,
735
858
  stream: false,
859
+ background: false,
860
+ store: true,
736
861
  tools,
737
862
  ...model ? { model } : {}
738
863
  };
@@ -748,6 +873,8 @@ async function queryAssistant(options) {
748
873
  input: question,
749
874
  workspace_ext_id: workspaceId,
750
875
  stream: true,
876
+ background: false,
877
+ store: true,
751
878
  tools: {
752
879
  retrieval_chunk: buildRetrievalChunkTool(docIds),
753
880
  retrieval_full_context: buildRetrievalFullContextTool([])
@@ -1207,11 +1334,12 @@ var Arbi = class {
1207
1334
  workspaceId ?? this.requireWorkspace(),
1208
1335
  shared
1209
1336
  ),
1210
- uploadFile: (fileData, fileName, workspaceId) => uploadFile(
1337
+ uploadFile: (fileData, fileName, options) => uploadFile(
1211
1338
  this.getAuthHeaders(),
1212
- workspaceId ?? this.requireWorkspace(),
1339
+ options?.workspaceId ?? this.requireWorkspace(),
1213
1340
  fileData,
1214
- fileName
1341
+ fileName,
1342
+ options?.folder ? { folder: options.folder } : void 0
1215
1343
  ),
1216
1344
  download: (docId) => downloadDocument(this.getAuthHeaders(), docId),
1217
1345
  getParsedContent: (docId, stage) => getParsedContent(this.getAuthHeaders(), docId, stage)
@@ -1356,6 +1484,8 @@ var Arbi = class {
1356
1484
  exports.Arbi = Arbi;
1357
1485
  exports.ArbiApiError = ArbiApiError;
1358
1486
  exports.ArbiError = ArbiError;
1487
+ exports.LIFECYCLE_LABELS = LIFECYCLE_LABELS;
1488
+ exports.TOOL_LABELS = TOOL_LABELS;
1359
1489
  exports.agentconfig = agentconfig_exports;
1360
1490
  exports.assistant = assistant_exports;
1361
1491
  exports.authenticatedFetch = authenticatedFetch;
@@ -1372,6 +1502,7 @@ exports.dm = dm_exports;
1372
1502
  exports.doctags = doctags_exports;
1373
1503
  exports.documents = documents_exports;
1374
1504
  exports.files = files_exports;
1505
+ exports.formatAgentStepLabel = formatAgentStepLabel;
1375
1506
  exports.formatFileSize = formatFileSize;
1376
1507
  exports.formatUserName = formatUserName;
1377
1508
  exports.formatWorkspaceChoices = formatWorkspaceChoices;