@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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from 'fs';
2
- import path from 'path';
2
+ import path2 from 'path';
3
3
  import os from 'os';
4
4
  import { createArbiClient, base64ToBytes, deriveEncryptionKeypairFromSigning, sealedBoxDecrypt, createWorkspaceKeyHeader, buildWebSocketUrl, createAuthMessage, parseServerMessage, isMessageType } from '@arbidocs/client';
5
5
 
@@ -36,7 +36,36 @@ function requireOk(result, message) {
36
36
  }
37
37
  }
38
38
  function getErrorMessage(err) {
39
- return err instanceof Error ? err.message : String(err);
39
+ if (!(err instanceof Error)) return String(err);
40
+ const rootCause = getDeepestCause(err);
41
+ if (rootCause !== err) {
42
+ const rootMsg = rootCause.message || String(rootCause);
43
+ if (err.message && !err.message.includes(rootMsg)) {
44
+ return `${err.message}: ${rootMsg}`;
45
+ }
46
+ }
47
+ return err.message;
48
+ }
49
+ function getDeepestCause(err) {
50
+ let current = err;
51
+ const seen = /* @__PURE__ */ new Set();
52
+ while (current.cause instanceof Error && !seen.has(current.cause)) {
53
+ seen.add(current);
54
+ current = current.cause;
55
+ }
56
+ return current;
57
+ }
58
+ function getErrorCode(err) {
59
+ if (!(err instanceof Error)) return void 0;
60
+ let current = err;
61
+ const seen = /* @__PURE__ */ new Set();
62
+ while (current && !seen.has(current)) {
63
+ seen.add(current);
64
+ const code = current.code;
65
+ if (typeof code === "string") return code;
66
+ current = current.cause instanceof Error ? current.cause : void 0;
67
+ }
68
+ return void 0;
40
69
  }
41
70
 
42
71
  // src/fetch.ts
@@ -93,10 +122,10 @@ var FileConfigStore = class {
93
122
  credentialsFile;
94
123
  sessionFile;
95
124
  constructor(configDir) {
96
- this.configDir = configDir ?? process.env.ARBI_CONFIG_DIR ?? path.join(os.homedir(), ".arbi");
97
- this.configFile = path.join(this.configDir, "config.json");
98
- this.credentialsFile = path.join(this.configDir, "credentials.json");
99
- this.sessionFile = path.join(this.configDir, "session.json");
125
+ this.configDir = configDir ?? process.env.ARBI_CONFIG_DIR ?? path2.join(os.homedir(), ".arbi");
126
+ this.configFile = path2.join(this.configDir, "config.json");
127
+ this.credentialsFile = path2.join(this.configDir, "credentials.json");
128
+ this.sessionFile = path2.join(this.configDir, "session.json");
100
129
  }
101
130
  ensureConfigDir() {
102
131
  if (!fs.existsSync(this.configDir)) {
@@ -167,6 +196,77 @@ var FileConfigStore = class {
167
196
  clearChatSession() {
168
197
  this.saveChatSession({ ...DEFAULT_SESSION });
169
198
  }
199
+ /**
200
+ * Try to resolve config from multiple sources, in priority order:
201
+ *
202
+ * 1. Existing `~/.arbi/config.json` (highest priority)
203
+ * 2. `ARBI_SERVER_URL` environment variable
204
+ * 3. `.env` file in `searchDir` → read `VITE_DEPLOYMENT_DOMAIN`
205
+ * 4. `public/config.json` in `searchDir` → read `deployment.domain`
206
+ * 5. Default to `https://localhost`
207
+ *
208
+ * Returns `{ config, source }` where source describes where the config came from.
209
+ * Saves auto-detected config to disk so subsequent calls use the fast path.
210
+ */
211
+ resolveConfigWithFallbacks(searchDir) {
212
+ const existing = this.getConfig();
213
+ if (existing?.baseUrl) {
214
+ return { config: existing, source: "config" };
215
+ }
216
+ const envUrl = process.env.ARBI_SERVER_URL;
217
+ if (envUrl) {
218
+ const domain = new URL(envUrl).hostname;
219
+ const config2 = { baseUrl: envUrl.replace(/\/$/, ""), deploymentDomain: domain };
220
+ this.saveConfig(config2);
221
+ return { config: config2, source: "ARBI_SERVER_URL env var" };
222
+ }
223
+ const dir = searchDir || process.cwd();
224
+ const dotenvConfig = this.readDotEnvDomain(path2.join(dir, ".env"));
225
+ if (dotenvConfig) {
226
+ this.saveConfig(dotenvConfig);
227
+ return { config: dotenvConfig, source: ".env file" };
228
+ }
229
+ const publicConfig = this.readPublicConfigDomain(path2.join(dir, "public", "config.json"));
230
+ if (publicConfig) {
231
+ this.saveConfig(publicConfig);
232
+ return { config: publicConfig, source: "public/config.json" };
233
+ }
234
+ const config = { baseUrl: "https://localhost", deploymentDomain: "localhost" };
235
+ this.saveConfig(config);
236
+ return { config, source: "default (https://localhost)" };
237
+ }
238
+ /**
239
+ * Read VITE_DEPLOYMENT_DOMAIN from a .env file.
240
+ */
241
+ readDotEnvDomain(filePath) {
242
+ try {
243
+ const content = fs.readFileSync(filePath, "utf-8");
244
+ const match = content.match(/^VITE_DEPLOYMENT_DOMAIN\s*=\s*(.+)$/m);
245
+ if (match) {
246
+ const domain = match[1].trim().replace(/^["']|["']$/g, "");
247
+ if (domain) {
248
+ return { baseUrl: `https://${domain}`, deploymentDomain: domain };
249
+ }
250
+ }
251
+ } catch {
252
+ }
253
+ return null;
254
+ }
255
+ /**
256
+ * Read deployment.domain from a public/config.json file.
257
+ */
258
+ readPublicConfigDomain(filePath) {
259
+ try {
260
+ const content = fs.readFileSync(filePath, "utf-8");
261
+ const json = JSON.parse(content);
262
+ const domain = json?.deployment?.domain;
263
+ if (domain) {
264
+ return { baseUrl: `https://${domain}`, deploymentDomain: domain };
265
+ }
266
+ } catch {
267
+ }
268
+ return null;
269
+ }
170
270
  };
171
271
  function formatWorkspaceChoices(wsList) {
172
272
  return wsList.map((ws) => {
@@ -192,7 +292,11 @@ async function createAuthenticatedClient(config, creds, store) {
192
292
  });
193
293
  store.saveCredentials({
194
294
  ...creds,
195
- serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
295
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
296
+ accessToken: void 0,
297
+ workspaceKeyHeader: void 0,
298
+ workspaceId: void 0,
299
+ tokenTimestamp: void 0
196
300
  });
197
301
  return { arbi, loginResult };
198
302
  }
@@ -207,7 +311,12 @@ async function performPasswordLogin(config, email, password, store) {
207
311
  store.saveCredentials({
208
312
  email,
209
313
  signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
210
- serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
314
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
315
+ // Clear any cached workspace tokens — new session key invalidates them
316
+ accessToken: void 0,
317
+ workspaceKeyHeader: void 0,
318
+ workspaceId: void 0,
319
+ tokenTimestamp: void 0
211
320
  });
212
321
  return { arbi, loginResult, config };
213
322
  }
@@ -270,6 +379,10 @@ async function resolveAuth(store) {
270
379
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
271
380
  return { arbi, loginResult, config };
272
381
  }
382
+ var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
383
+ function isCachedTokenValid(creds, workspaceId) {
384
+ return !!(creds.accessToken && creds.workspaceKeyHeader && creds.workspaceId === workspaceId && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
385
+ }
273
386
  async function resolveWorkspace(store, workspaceOpt) {
274
387
  const config = store.requireConfig();
275
388
  const creds = store.requireCredentials();
@@ -277,6 +390,32 @@ async function resolveWorkspace(store, workspaceOpt) {
277
390
  if (!workspaceId) {
278
391
  throw new ArbiError("No workspace selected. Run: arbi workspace select <id>");
279
392
  }
393
+ if (isCachedTokenValid(creds, workspaceId)) {
394
+ const arbi2 = createArbiClient({
395
+ baseUrl: config.baseUrl,
396
+ deploymentDomain: config.deploymentDomain,
397
+ credentials: "omit"
398
+ });
399
+ await arbi2.crypto.initSodium();
400
+ arbi2.session.setSelectedWorkspace(workspaceId);
401
+ arbi2.session.setAccessToken(creds.accessToken);
402
+ arbi2.session.setCachedWorkspaceHeader(workspaceId, creds.workspaceKeyHeader);
403
+ const signingPrivateKey = base64ToBytes(creds.signingPrivateKeyBase64);
404
+ const serverSessionKey = base64ToBytes(creds.serverSessionKeyBase64);
405
+ const loginResult2 = {
406
+ accessToken: creds.accessToken,
407
+ signingPrivateKey,
408
+ serverSessionKey
409
+ };
410
+ return {
411
+ arbi: arbi2,
412
+ loginResult: loginResult2,
413
+ config,
414
+ workspaceId,
415
+ accessToken: creds.accessToken,
416
+ workspaceKeyHeader: creds.workspaceKeyHeader
417
+ };
418
+ }
280
419
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
281
420
  await selectWorkspaceById(
282
421
  arbi,
@@ -289,10 +428,57 @@ async function resolveWorkspace(store, workspaceOpt) {
289
428
  if (!accessToken || !workspaceKeyHeader) {
290
429
  throw new ArbiError("Authentication error \u2014 missing token or workspace key");
291
430
  }
431
+ store.saveCredentials({
432
+ ...store.requireCredentials(),
433
+ accessToken,
434
+ workspaceKeyHeader,
435
+ workspaceId,
436
+ tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString()
437
+ });
292
438
  return { arbi, loginResult, config, workspaceId, accessToken, workspaceKeyHeader };
293
439
  }
294
440
 
295
441
  // src/sse.ts
442
+ var TOOL_LABELS = {
443
+ search_documents: "Searching documents",
444
+ get_document_passages: "Reading document",
445
+ get_table_of_contents: "Getting table of contents",
446
+ view_document_pages: "Viewing document pages",
447
+ get_full_document: "Reading full document",
448
+ web_search: "Searching the web",
449
+ read_url: "Reading web pages",
450
+ ask_user: "Asking user",
451
+ compaction: "Compacting conversation",
452
+ personal_agent: "Running agent",
453
+ create_artifact: "Creating artifact",
454
+ create_plan: "Creating plan",
455
+ save_skill: "Saving skill",
456
+ run_code: "Running code"
457
+ };
458
+ var LIFECYCLE_LABELS = {
459
+ evaluation: "Evaluating results",
460
+ answering: "Writing answer",
461
+ reviewing: "Reviewing answer",
462
+ planning: "Planning",
463
+ tool_progress: "Working"
464
+ };
465
+ function formatAgentStepLabel(step) {
466
+ if (step.focus) return step.focus;
467
+ const detail = step.detail;
468
+ if (step.step === "tool_progress" && detail && detail.length > 0) {
469
+ const toolName = detail[0].tool;
470
+ const label = toolName && TOOL_LABELS[toolName] || LIFECYCLE_LABELS.tool_progress;
471
+ const message = detail[0].message;
472
+ return message ? `${label}: ${message}` : label;
473
+ }
474
+ if (step.step) {
475
+ return LIFECYCLE_LABELS[step.step] || step.step;
476
+ }
477
+ if (detail && detail.length > 0 && detail[0].tool) {
478
+ return TOOL_LABELS[detail[0].tool] || detail[0].tool;
479
+ }
480
+ return "";
481
+ }
296
482
  function parseSSEEvents(chunk, buffer) {
297
483
  const combined = buffer + chunk;
298
484
  const events = [];
@@ -380,8 +566,8 @@ async function streamSSE(response, callbacks = {}) {
380
566
  // ARBI-specific events (dot-prefixed from server)
381
567
  "arbi.agent_step": (raw) => {
382
568
  const data = JSON.parse(raw);
383
- const focus = data.focus || data.step || "";
384
- if (focus) agentSteps.push(focus);
569
+ const label = formatAgentStepLabel(data);
570
+ if (label) agentSteps.push(label);
385
571
  callbacks.onAgentStep?.(data);
386
572
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
387
573
  },
@@ -659,16 +845,34 @@ function formatUserName(user) {
659
845
  // src/operations/documents.ts
660
846
  var documents_exports = {};
661
847
  __export(documents_exports, {
848
+ SUPPORTED_EXTENSIONS: () => SUPPORTED_EXTENSIONS,
662
849
  deleteDocuments: () => deleteDocuments,
663
850
  downloadDocument: () => downloadDocument,
664
851
  getDocuments: () => getDocuments,
665
852
  getParsedContent: () => getParsedContent,
666
853
  listDocuments: () => listDocuments,
854
+ sanitizeFolderPath: () => sanitizeFolderPath,
667
855
  updateDocuments: () => updateDocuments,
668
856
  uploadFile: () => uploadFile,
669
- uploadLocalFile: () => uploadLocalFile,
857
+ uploadFiles: () => uploadFiles,
670
858
  uploadUrl: () => uploadUrl
671
859
  });
860
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
861
+ ".pdf",
862
+ ".txt",
863
+ ".md",
864
+ ".html",
865
+ ".doc",
866
+ ".docx",
867
+ ".rtf",
868
+ ".ppt",
869
+ ".pptx",
870
+ ".xls",
871
+ ".xlsx"
872
+ ]);
873
+ function sanitizeFolderPath(folderPath) {
874
+ return folderPath.replace(/[^a-zA-Z0-9_\-/]/g, "_").replace(/_{2,}/g, "_");
875
+ }
672
876
  async function listDocuments(arbi) {
673
877
  return requireData(await arbi.fetch.GET("/v1/document/list"), "Failed to fetch documents");
674
878
  }
@@ -707,22 +911,31 @@ async function getParsedContent(auth, docId, stage) {
707
911
  });
708
912
  return res.json();
709
913
  }
710
- async function uploadFile(auth, workspaceId, fileData, fileName) {
914
+ async function uploadFile(auth, workspaceId, fileData, fileName, options) {
711
915
  const formData = new FormData();
712
916
  formData.append("files", fileData, fileName);
917
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
918
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
713
919
  const res = await authenticatedFetch({
714
920
  ...auth,
715
- path: `/v1/document/upload?workspace_ext_id=${workspaceId}`,
921
+ path: `/v1/document/upload?${params.toString()}`,
716
922
  method: "POST",
717
923
  body: formData
718
924
  });
719
925
  return res.json();
720
926
  }
721
- async function uploadLocalFile(auth, workspaceId, filePath) {
722
- const fileBuffer = fs.readFileSync(filePath);
723
- const fileName = path.basename(filePath);
724
- const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName);
725
- return { ...result, fileName };
927
+ async function uploadFiles(auth, workspaceId, files, options) {
928
+ const formData = new FormData();
929
+ for (const f of files) formData.append("files", f.data, f.name);
930
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
931
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
932
+ const res = await authenticatedFetch({
933
+ ...auth,
934
+ path: `/v1/document/upload?${params.toString()}`,
935
+ method: "POST",
936
+ body: formData
937
+ });
938
+ return res.json();
726
939
  }
727
940
  async function downloadDocument(auth, docId) {
728
941
  return authenticatedFetch({
@@ -922,6 +1135,8 @@ async function retrieve(options) {
922
1135
  input: query,
923
1136
  workspace_ext_id: workspaceId,
924
1137
  stream: false,
1138
+ background: false,
1139
+ store: true,
925
1140
  tools,
926
1141
  ...model ? { model } : {}
927
1142
  };
@@ -937,6 +1152,8 @@ async function queryAssistant(options) {
937
1152
  input: question,
938
1153
  workspace_ext_id: workspaceId,
939
1154
  stream: true,
1155
+ background: false,
1156
+ store: true,
940
1157
  tools: {
941
1158
  retrieval_chunk: buildRetrievalChunkTool(docIds),
942
1159
  retrieval_full_context: buildRetrievalFullContextTool([])
@@ -1396,11 +1613,12 @@ var Arbi = class {
1396
1613
  workspaceId ?? this.requireWorkspace(),
1397
1614
  shared
1398
1615
  ),
1399
- uploadFile: (fileData, fileName, workspaceId) => uploadFile(
1616
+ uploadFile: (fileData, fileName, options) => uploadFile(
1400
1617
  this.getAuthHeaders(),
1401
- workspaceId ?? this.requireWorkspace(),
1618
+ options?.workspaceId ?? this.requireWorkspace(),
1402
1619
  fileData,
1403
- fileName
1620
+ fileName,
1621
+ options?.folder ? { folder: options.folder } : void 0
1404
1622
  ),
1405
1623
  download: (docId) => downloadDocument(this.getAuthHeaders(), docId),
1406
1624
  getParsedContent: (docId, stage) => getParsedContent(this.getAuthHeaders(), docId, stage)
@@ -1542,6 +1760,123 @@ var Arbi = class {
1542
1760
  }
1543
1761
  };
1544
1762
 
1545
- export { Arbi, ArbiApiError, ArbiError, FileConfigStore, agentconfig_exports as agentconfig, assistant_exports as assistant, authenticatedFetch, buildRetrievalChunkTool, buildRetrievalFullContextTool, buildRetrievalTocTool, connectWebSocket, connectWithReconnect, consumeSSEStream, contacts_exports as contacts, conversations_exports as conversations, createAuthenticatedClient, createDocumentWaiter, dm_exports as dm, doctags_exports as doctags, documents_exports as documents, files_exports as files, formatFileSize, formatUserName, formatWorkspaceChoices, formatWsMessage, generateEncryptedWorkspaceKey, getErrorMessage, health_exports as health, parseSSEEvents, performPasswordLogin, requireData, requireOk, resolveAuth, resolveWorkspace, selectWorkspace, selectWorkspaceById, settings_exports as settings, streamSSE, tags_exports as tags, workspaces_exports as workspaces };
1763
+ // src/operations/documents-node.ts
1764
+ var documents_node_exports = {};
1765
+ __export(documents_node_exports, {
1766
+ uploadDirectory: () => uploadDirectory,
1767
+ uploadLocalFile: () => uploadLocalFile,
1768
+ uploadZip: () => uploadZip
1769
+ });
1770
+ function collectFiles(dirPath, baseDir) {
1771
+ const root = baseDir ?? dirPath;
1772
+ const results = [];
1773
+ for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
1774
+ const fullPath = path2.join(dirPath, entry.name);
1775
+ if (entry.isDirectory()) {
1776
+ results.push(...collectFiles(fullPath, root));
1777
+ } else if (entry.isFile()) {
1778
+ const ext = path2.extname(entry.name).toLowerCase();
1779
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
1780
+ results.push({ absolutePath: fullPath, relativePath: path2.relative(root, fullPath) });
1781
+ }
1782
+ }
1783
+ }
1784
+ return results;
1785
+ }
1786
+ async function uploadLocalFile(auth, workspaceId, filePath, options) {
1787
+ const fileBuffer = fs.readFileSync(filePath);
1788
+ const fileName = path2.basename(filePath);
1789
+ const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName, options);
1790
+ return { ...result, fileName };
1791
+ }
1792
+ async function uploadDirectory(auth, workspaceId, dirPath) {
1793
+ const resolvedDir = path2.resolve(dirPath);
1794
+ const dirName = path2.basename(resolvedDir);
1795
+ const entries = collectFiles(resolvedDir);
1796
+ if (entries.length === 0) {
1797
+ return { doc_ext_ids: [], duplicates: [], folders: /* @__PURE__ */ new Map() };
1798
+ }
1799
+ const groups = /* @__PURE__ */ new Map();
1800
+ for (const entry of entries) {
1801
+ const relDir = path2.dirname(entry.relativePath);
1802
+ const folder = relDir === "." ? dirName : `${dirName}/${relDir}`;
1803
+ if (!groups.has(folder)) groups.set(folder, []);
1804
+ groups.get(folder).push({ absolutePath: entry.absolutePath, name: path2.basename(entry.relativePath) });
1805
+ }
1806
+ const allDocIds = [];
1807
+ const allDuplicates = [];
1808
+ const folders = /* @__PURE__ */ new Map();
1809
+ for (const [folder, files] of groups) {
1810
+ const blobs = files.map((f) => ({
1811
+ data: new Blob([fs.readFileSync(f.absolutePath)]),
1812
+ name: f.name
1813
+ }));
1814
+ const result = await uploadFiles(auth, workspaceId, blobs, { folder });
1815
+ const dups = result.duplicates ?? [];
1816
+ allDocIds.push(...result.doc_ext_ids);
1817
+ allDuplicates.push(...dups);
1818
+ folders.set(folder, {
1819
+ fileCount: files.length,
1820
+ doc_ext_ids: result.doc_ext_ids,
1821
+ duplicates: dups
1822
+ });
1823
+ }
1824
+ return { doc_ext_ids: allDocIds, duplicates: allDuplicates, folders };
1825
+ }
1826
+ async function uploadZip(auth, workspaceId, zipPath) {
1827
+ const JSZip = (await import('jszip')).default;
1828
+ const zipBuffer = fs.readFileSync(zipPath);
1829
+ const zip = await JSZip.loadAsync(zipBuffer);
1830
+ const zipBaseName = path2.basename(zipPath).replace(/\.zip$/i, "");
1831
+ const entries = [];
1832
+ zip.forEach((relativePath, zipEntry) => {
1833
+ if (zipEntry.dir) return;
1834
+ const ext = path2.extname(relativePath).toLowerCase();
1835
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
1836
+ entries.push({ zipEntryPath: relativePath, zipEntry });
1837
+ }
1838
+ });
1839
+ if (entries.length === 0) {
1840
+ return { doc_ext_ids: [], duplicates: [], folders: /* @__PURE__ */ new Map() };
1841
+ }
1842
+ const firstParts = new Set(entries.map((e) => e.zipEntryPath.split("/")[0]));
1843
+ const hasSingleRoot = firstParts.size === 1 && entries.every((e) => e.zipEntryPath.includes("/"));
1844
+ const groups = /* @__PURE__ */ new Map();
1845
+ for (const entry of entries) {
1846
+ const data = await entry.zipEntry.async("uint8array");
1847
+ const fileName = path2.basename(entry.zipEntryPath);
1848
+ const relDir = path2.dirname(entry.zipEntryPath);
1849
+ let folder;
1850
+ if (hasSingleRoot) {
1851
+ folder = relDir === "." ? zipBaseName : relDir;
1852
+ } else {
1853
+ folder = relDir === "." ? zipBaseName : `${zipBaseName}/${relDir}`;
1854
+ }
1855
+ folder = sanitizeFolderPath(folder);
1856
+ if (!groups.has(folder)) groups.set(folder, []);
1857
+ groups.get(folder).push({ name: fileName, data });
1858
+ }
1859
+ const allDocIds = [];
1860
+ const allDuplicates = [];
1861
+ const folders = /* @__PURE__ */ new Map();
1862
+ for (const [folder, files] of groups) {
1863
+ const blobs = files.map((f) => ({
1864
+ data: new Blob([f.data]),
1865
+ name: f.name
1866
+ }));
1867
+ const result = await uploadFiles(auth, workspaceId, blobs, { folder });
1868
+ const dups = result.duplicates ?? [];
1869
+ allDocIds.push(...result.doc_ext_ids);
1870
+ allDuplicates.push(...dups);
1871
+ folders.set(folder, {
1872
+ fileCount: files.length,
1873
+ doc_ext_ids: result.doc_ext_ids,
1874
+ duplicates: dups
1875
+ });
1876
+ }
1877
+ return { doc_ext_ids: allDocIds, duplicates: allDuplicates, folders };
1878
+ }
1879
+
1880
+ export { Arbi, ArbiApiError, ArbiError, FileConfigStore, LIFECYCLE_LABELS, TOOL_LABELS, agentconfig_exports as agentconfig, assistant_exports as assistant, authenticatedFetch, buildRetrievalChunkTool, buildRetrievalFullContextTool, buildRetrievalTocTool, connectWebSocket, connectWithReconnect, consumeSSEStream, contacts_exports as contacts, conversations_exports as conversations, createAuthenticatedClient, createDocumentWaiter, dm_exports as dm, doctags_exports as doctags, documents_exports as documents, documents_node_exports as documentsNode, files_exports as files, formatAgentStepLabel, formatFileSize, formatUserName, formatWorkspaceChoices, formatWsMessage, generateEncryptedWorkspaceKey, getErrorCode, getErrorMessage, health_exports as health, parseSSEEvents, performPasswordLogin, requireData, requireOk, resolveAuth, resolveWorkspace, selectWorkspace, selectWorkspaceById, settings_exports as settings, streamSSE, tags_exports as tags, workspaces_exports as workspaces };
1546
1881
  //# sourceMappingURL=index.js.map
1547
1882
  //# sourceMappingURL=index.js.map