@arbidocs/sdk 0.3.10 → 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,17 +35,55 @@ 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
49
- var STATUS_MESSAGES = {
59
+ var STATUS_FALLBACKS = {
50
60
  401: "Token has expired. Please run: arbi login",
61
+ 403: "Access denied. You may not have permission for this workspace.",
62
+ 404: "Resource not found.",
51
63
  503: "The selected LLM is not responding. Please retry or select another model."
52
64
  };
65
+ async function extractErrorMessage(res) {
66
+ let bodyDetail;
67
+ try {
68
+ const text = await res.text();
69
+ if (text) {
70
+ try {
71
+ const json = JSON.parse(text);
72
+ bodyDetail = json.detail || json.message || json.error || void 0;
73
+ } catch {
74
+ if (text.length < 200) bodyDetail = text;
75
+ }
76
+ }
77
+ } catch {
78
+ }
79
+ if (bodyDetail) return `Request failed (${res.status}): ${bodyDetail}`;
80
+ const fallback = STATUS_FALLBACKS[res.status];
81
+ if (fallback) return fallback;
82
+ return `Request failed: ${res.status} ${res.statusText}`;
83
+ }
53
84
  async function authenticatedFetch(options) {
54
- const { baseUrl, accessToken, workspaceKeyHeader, path: path2, method, body, headers } = options;
55
- const res = await fetch(`${baseUrl}${path2}`, {
85
+ const { baseUrl, accessToken, workspaceKeyHeader, path, method, body, headers } = options;
86
+ const res = await fetch(`${baseUrl}${path}`, {
56
87
  method: method ?? (body ? "POST" : "GET"),
57
88
  headers: {
58
89
  Authorization: `Bearer ${accessToken}`,
@@ -62,10 +93,8 @@ async function authenticatedFetch(options) {
62
93
  body
63
94
  });
64
95
  if (!res.ok) {
65
- const knownMessage = STATUS_MESSAGES[res.status];
66
- if (knownMessage) throw new Error(knownMessage);
67
- const text = await res.text().catch(() => "");
68
- throw new Error(`Request failed: ${res.status} ${text}`);
96
+ const message = await extractErrorMessage(res);
97
+ throw new ArbiApiError(message, { status: res.status, statusText: res.statusText });
69
98
  }
70
99
  return res;
71
100
  }
@@ -93,7 +122,11 @@ async function createAuthenticatedClient(config, creds, store) {
93
122
  });
94
123
  store.saveCredentials({
95
124
  ...creds,
96
- 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
97
130
  });
98
131
  return { arbi, loginResult };
99
132
  }
@@ -108,7 +141,12 @@ async function performPasswordLogin(config, email, password, store) {
108
141
  store.saveCredentials({
109
142
  email,
110
143
  signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
111
- 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
112
150
  });
113
151
  return { arbi, loginResult, config };
114
152
  }
@@ -161,6 +199,10 @@ async function resolveAuth(store) {
161
199
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
162
200
  return { arbi, loginResult, config };
163
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
+ }
164
206
  async function resolveWorkspace(store, workspaceOpt) {
165
207
  const config = store.requireConfig();
166
208
  const creds = store.requireCredentials();
@@ -168,6 +210,32 @@ async function resolveWorkspace(store, workspaceOpt) {
168
210
  if (!workspaceId) {
169
211
  throw new ArbiError("No workspace selected. Run: arbi workspace select <id>");
170
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
+ }
171
239
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
172
240
  await selectWorkspaceById(
173
241
  arbi,
@@ -180,10 +248,57 @@ async function resolveWorkspace(store, workspaceOpt) {
180
248
  if (!accessToken || !workspaceKeyHeader) {
181
249
  throw new ArbiError("Authentication error \u2014 missing token or workspace key");
182
250
  }
251
+ store.saveCredentials({
252
+ ...store.requireCredentials(),
253
+ accessToken,
254
+ workspaceKeyHeader,
255
+ workspaceId,
256
+ tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString()
257
+ });
183
258
  return { arbi, loginResult, config, workspaceId, accessToken, workspaceKeyHeader };
184
259
  }
185
260
 
186
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
+ }
187
302
  function parseSSEEvents(chunk, buffer) {
188
303
  const combined = buffer + chunk;
189
304
  const events = [];
@@ -254,6 +369,11 @@ async function streamSSE(response, callbacks = {}) {
254
369
  usage = data.response.usage;
255
370
  callbacks.onUsage?.(data.response.usage);
256
371
  }
372
+ if (data.response?.metadata) {
373
+ const meta = data.response.metadata;
374
+ metadata = meta;
375
+ callbacks.onMetadata?.(meta);
376
+ }
257
377
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
258
378
  callbacks.onComplete?.();
259
379
  },
@@ -266,8 +386,8 @@ async function streamSSE(response, callbacks = {}) {
266
386
  // ARBI-specific events (dot-prefixed from server)
267
387
  "arbi.agent_step": (raw) => {
268
388
  const data = JSON.parse(raw);
269
- const focus = data.focus || data.step || "";
270
- if (focus) agentSteps.push(focus);
389
+ const label = formatAgentStepLabel(data);
390
+ if (label) agentSteps.push(label);
271
391
  callbacks.onAgentStep?.(data);
272
392
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
273
393
  },
@@ -446,16 +566,34 @@ function formatUserName(user) {
446
566
  // src/operations/documents.ts
447
567
  var documents_exports = {};
448
568
  __export(documents_exports, {
569
+ SUPPORTED_EXTENSIONS: () => SUPPORTED_EXTENSIONS,
449
570
  deleteDocuments: () => deleteDocuments,
450
571
  downloadDocument: () => downloadDocument,
451
572
  getDocuments: () => getDocuments,
452
573
  getParsedContent: () => getParsedContent,
453
574
  listDocuments: () => listDocuments,
575
+ sanitizeFolderPath: () => sanitizeFolderPath,
454
576
  updateDocuments: () => updateDocuments,
455
577
  uploadFile: () => uploadFile,
456
- uploadLocalFile: () => uploadLocalFile,
578
+ uploadFiles: () => uploadFiles,
457
579
  uploadUrl: () => uploadUrl
458
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
+ }
459
597
  async function listDocuments(arbi) {
460
598
  return requireData(await arbi.fetch.GET("/v1/document/list"), "Failed to fetch documents");
461
599
  }
@@ -494,22 +632,31 @@ async function getParsedContent(auth, docId, stage) {
494
632
  });
495
633
  return res.json();
496
634
  }
497
- async function uploadFile(auth, workspaceId, fileData, fileName) {
635
+ async function uploadFile(auth, workspaceId, fileData, fileName, options) {
498
636
  const formData = new FormData();
499
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));
500
640
  const res = await authenticatedFetch({
501
641
  ...auth,
502
- path: `/v1/document/upload?workspace_ext_id=${workspaceId}`,
642
+ path: `/v1/document/upload?${params.toString()}`,
503
643
  method: "POST",
504
644
  body: formData
505
645
  });
506
646
  return res.json();
507
647
  }
508
- async function uploadLocalFile(auth, workspaceId, filePath) {
509
- const fileBuffer = fs__default.default.readFileSync(filePath);
510
- const fileName = path__default.default.basename(filePath);
511
- const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName);
512
- 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();
513
660
  }
514
661
  async function downloadDocument(auth, docId) {
515
662
  return authenticatedFetch({
@@ -709,6 +856,8 @@ async function retrieve(options) {
709
856
  input: query,
710
857
  workspace_ext_id: workspaceId,
711
858
  stream: false,
859
+ background: false,
860
+ store: true,
712
861
  tools,
713
862
  ...model ? { model } : {}
714
863
  };
@@ -724,6 +873,8 @@ async function queryAssistant(options) {
724
873
  input: question,
725
874
  workspace_ext_id: workspaceId,
726
875
  stream: true,
876
+ background: false,
877
+ store: true,
727
878
  tools: {
728
879
  retrieval_chunk: buildRetrievalChunkTool(docIds),
729
880
  retrieval_full_context: buildRetrievalFullContextTool([])
@@ -1183,11 +1334,12 @@ var Arbi = class {
1183
1334
  workspaceId ?? this.requireWorkspace(),
1184
1335
  shared
1185
1336
  ),
1186
- uploadFile: (fileData, fileName, workspaceId) => uploadFile(
1337
+ uploadFile: (fileData, fileName, options) => uploadFile(
1187
1338
  this.getAuthHeaders(),
1188
- workspaceId ?? this.requireWorkspace(),
1339
+ options?.workspaceId ?? this.requireWorkspace(),
1189
1340
  fileData,
1190
- fileName
1341
+ fileName,
1342
+ options?.folder ? { folder: options.folder } : void 0
1191
1343
  ),
1192
1344
  download: (docId) => downloadDocument(this.getAuthHeaders(), docId),
1193
1345
  getParsedContent: (docId, stage) => getParsedContent(this.getAuthHeaders(), docId, stage)
@@ -1332,6 +1484,8 @@ var Arbi = class {
1332
1484
  exports.Arbi = Arbi;
1333
1485
  exports.ArbiApiError = ArbiApiError;
1334
1486
  exports.ArbiError = ArbiError;
1487
+ exports.LIFECYCLE_LABELS = LIFECYCLE_LABELS;
1488
+ exports.TOOL_LABELS = TOOL_LABELS;
1335
1489
  exports.agentconfig = agentconfig_exports;
1336
1490
  exports.assistant = assistant_exports;
1337
1491
  exports.authenticatedFetch = authenticatedFetch;
@@ -1348,6 +1502,7 @@ exports.dm = dm_exports;
1348
1502
  exports.doctags = doctags_exports;
1349
1503
  exports.documents = documents_exports;
1350
1504
  exports.files = files_exports;
1505
+ exports.formatAgentStepLabel = formatAgentStepLabel;
1351
1506
  exports.formatFileSize = formatFileSize;
1352
1507
  exports.formatUserName = formatUserName;
1353
1508
  exports.formatWorkspaceChoices = formatWorkspaceChoices;