@arbidocs/sdk 0.3.11 → 0.3.14

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 = [];
@@ -320,11 +506,13 @@ async function streamSSE(response, callbacks = {}) {
320
506
  let text = "";
321
507
  let assistantMessageExtId = null;
322
508
  const agentSteps = [];
509
+ let toolCallCount = 0;
323
510
  const errors = [];
324
511
  const artifacts = [];
325
512
  let userMessage = null;
326
513
  let metadata = null;
327
514
  let usage = null;
515
+ let context = null;
328
516
  const eventHandlers = {
329
517
  // OpenAI Responses API events (dot-separated names from server)
330
518
  "response.created": (raw) => {
@@ -368,6 +556,9 @@ async function streamSSE(response, callbacks = {}) {
368
556
  metadata = meta;
369
557
  callbacks.onMetadata?.(meta);
370
558
  }
559
+ if (data.context) {
560
+ context = data.context;
561
+ }
371
562
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
372
563
  callbacks.onComplete?.();
373
564
  },
@@ -380,8 +571,12 @@ async function streamSSE(response, callbacks = {}) {
380
571
  // ARBI-specific events (dot-prefixed from server)
381
572
  "arbi.agent_step": (raw) => {
382
573
  const data = JSON.parse(raw);
383
- const focus = data.focus || data.step || "";
384
- if (focus) agentSteps.push(focus);
574
+ const label = formatAgentStepLabel(data);
575
+ if (label) agentSteps.push(label);
576
+ const detail = data.detail;
577
+ if (detail && Array.isArray(detail)) {
578
+ toolCallCount += detail.filter((d) => d.tool).length;
579
+ }
385
580
  callbacks.onAgentStep?.(data);
386
581
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
387
582
  },
@@ -432,11 +627,13 @@ async function streamSSE(response, callbacks = {}) {
432
627
  text,
433
628
  assistantMessageExtId,
434
629
  agentSteps,
630
+ toolCallCount,
435
631
  errors,
436
632
  userMessage,
437
633
  metadata,
438
634
  artifacts,
439
- usage
635
+ usage,
636
+ context
440
637
  };
441
638
  }
442
639
  var consumeSSEStream = streamSSE;
@@ -635,6 +832,13 @@ function formatWsMessage(msg) {
635
832
  if (isMessageType(msg, "presence_update")) {
636
833
  return { text: `${msg.user_id} is ${msg.status}`, level: "info" };
637
834
  }
835
+ if (isMessageType(msg, "response_complete")) {
836
+ const icon = msg.status === "completed" ? "\u2713" : "\u2717";
837
+ return {
838
+ text: `${icon} Task ${msg.response_id} ${msg.status}`,
839
+ level: msg.status === "completed" ? "success" : "error"
840
+ };
841
+ }
638
842
  if (isNotification(msg)) {
639
843
  const sender = msg.sender?.email || "someone";
640
844
  const content = msg.content ?? msg.type;
@@ -659,16 +863,34 @@ function formatUserName(user) {
659
863
  // src/operations/documents.ts
660
864
  var documents_exports = {};
661
865
  __export(documents_exports, {
866
+ SUPPORTED_EXTENSIONS: () => SUPPORTED_EXTENSIONS,
662
867
  deleteDocuments: () => deleteDocuments,
663
868
  downloadDocument: () => downloadDocument,
664
869
  getDocuments: () => getDocuments,
665
870
  getParsedContent: () => getParsedContent,
666
871
  listDocuments: () => listDocuments,
872
+ sanitizeFolderPath: () => sanitizeFolderPath,
667
873
  updateDocuments: () => updateDocuments,
668
874
  uploadFile: () => uploadFile,
669
- uploadLocalFile: () => uploadLocalFile,
875
+ uploadFiles: () => uploadFiles,
670
876
  uploadUrl: () => uploadUrl
671
877
  });
878
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
879
+ ".pdf",
880
+ ".txt",
881
+ ".md",
882
+ ".html",
883
+ ".doc",
884
+ ".docx",
885
+ ".rtf",
886
+ ".ppt",
887
+ ".pptx",
888
+ ".xls",
889
+ ".xlsx"
890
+ ]);
891
+ function sanitizeFolderPath(folderPath) {
892
+ return folderPath.replace(/[^a-zA-Z0-9_\-/]/g, "_").replace(/_{2,}/g, "_");
893
+ }
672
894
  async function listDocuments(arbi) {
673
895
  return requireData(await arbi.fetch.GET("/v1/document/list"), "Failed to fetch documents");
674
896
  }
@@ -707,22 +929,31 @@ async function getParsedContent(auth, docId, stage) {
707
929
  });
708
930
  return res.json();
709
931
  }
710
- async function uploadFile(auth, workspaceId, fileData, fileName) {
932
+ async function uploadFile(auth, workspaceId, fileData, fileName, options) {
711
933
  const formData = new FormData();
712
934
  formData.append("files", fileData, fileName);
935
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
936
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
713
937
  const res = await authenticatedFetch({
714
938
  ...auth,
715
- path: `/v1/document/upload?workspace_ext_id=${workspaceId}`,
939
+ path: `/v1/document/upload?${params.toString()}`,
716
940
  method: "POST",
717
941
  body: formData
718
942
  });
719
943
  return res.json();
720
944
  }
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 };
945
+ async function uploadFiles(auth, workspaceId, files, options) {
946
+ const formData = new FormData();
947
+ for (const f of files) formData.append("files", f.data, f.name);
948
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
949
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
950
+ const res = await authenticatedFetch({
951
+ ...auth,
952
+ path: `/v1/document/upload?${params.toString()}`,
953
+ method: "POST",
954
+ body: formData
955
+ });
956
+ return res.json();
726
957
  }
727
958
  async function downloadDocument(auth, docId) {
728
959
  return authenticatedFetch({
@@ -922,6 +1153,8 @@ async function retrieve(options) {
922
1153
  input: query,
923
1154
  workspace_ext_id: workspaceId,
924
1155
  stream: false,
1156
+ background: false,
1157
+ store: true,
925
1158
  tools,
926
1159
  ...model ? { model } : {}
927
1160
  };
@@ -937,6 +1170,8 @@ async function queryAssistant(options) {
937
1170
  input: question,
938
1171
  workspace_ext_id: workspaceId,
939
1172
  stream: true,
1173
+ background: false,
1174
+ store: true,
940
1175
  tools: {
941
1176
  retrieval_chunk: buildRetrievalChunkTool(docIds),
942
1177
  retrieval_full_context: buildRetrievalFullContextTool([])
@@ -1396,11 +1631,12 @@ var Arbi = class {
1396
1631
  workspaceId ?? this.requireWorkspace(),
1397
1632
  shared
1398
1633
  ),
1399
- uploadFile: (fileData, fileName, workspaceId) => uploadFile(
1634
+ uploadFile: (fileData, fileName, options) => uploadFile(
1400
1635
  this.getAuthHeaders(),
1401
- workspaceId ?? this.requireWorkspace(),
1636
+ options?.workspaceId ?? this.requireWorkspace(),
1402
1637
  fileData,
1403
- fileName
1638
+ fileName,
1639
+ options?.folder ? { folder: options.folder } : void 0
1404
1640
  ),
1405
1641
  download: (docId) => downloadDocument(this.getAuthHeaders(), docId),
1406
1642
  getParsedContent: (docId, stage) => getParsedContent(this.getAuthHeaders(), docId, stage)
@@ -1542,6 +1778,186 @@ var Arbi = class {
1542
1778
  }
1543
1779
  };
1544
1780
 
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 };
1781
+ // src/operations/documents-node.ts
1782
+ var documents_node_exports = {};
1783
+ __export(documents_node_exports, {
1784
+ uploadDirectory: () => uploadDirectory,
1785
+ uploadLocalFile: () => uploadLocalFile,
1786
+ uploadZip: () => uploadZip
1787
+ });
1788
+ function collectFiles(dirPath, baseDir) {
1789
+ const root = baseDir ?? dirPath;
1790
+ const results = [];
1791
+ for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
1792
+ const fullPath = path2.join(dirPath, entry.name);
1793
+ if (entry.isDirectory()) {
1794
+ results.push(...collectFiles(fullPath, root));
1795
+ } else if (entry.isFile()) {
1796
+ const ext = path2.extname(entry.name).toLowerCase();
1797
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
1798
+ results.push({ absolutePath: fullPath, relativePath: path2.relative(root, fullPath) });
1799
+ }
1800
+ }
1801
+ }
1802
+ return results;
1803
+ }
1804
+ async function uploadLocalFile(auth, workspaceId, filePath, options) {
1805
+ const fileBuffer = fs.readFileSync(filePath);
1806
+ const fileName = path2.basename(filePath);
1807
+ const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName, options);
1808
+ return { ...result, fileName };
1809
+ }
1810
+ async function uploadDirectory(auth, workspaceId, dirPath) {
1811
+ const resolvedDir = path2.resolve(dirPath);
1812
+ const dirName = path2.basename(resolvedDir);
1813
+ const entries = collectFiles(resolvedDir);
1814
+ if (entries.length === 0) {
1815
+ return { doc_ext_ids: [], duplicates: [], folders: /* @__PURE__ */ new Map() };
1816
+ }
1817
+ const groups = /* @__PURE__ */ new Map();
1818
+ for (const entry of entries) {
1819
+ const relDir = path2.dirname(entry.relativePath);
1820
+ const folder = relDir === "." ? dirName : `${dirName}/${relDir}`;
1821
+ if (!groups.has(folder)) groups.set(folder, []);
1822
+ groups.get(folder).push({ absolutePath: entry.absolutePath, name: path2.basename(entry.relativePath) });
1823
+ }
1824
+ const allDocIds = [];
1825
+ const allDuplicates = [];
1826
+ const folders = /* @__PURE__ */ new Map();
1827
+ for (const [folder, files] of groups) {
1828
+ const blobs = files.map((f) => ({
1829
+ data: new Blob([fs.readFileSync(f.absolutePath)]),
1830
+ name: f.name
1831
+ }));
1832
+ const result = await uploadFiles(auth, workspaceId, blobs, { folder });
1833
+ const dups = result.duplicates ?? [];
1834
+ allDocIds.push(...result.doc_ext_ids);
1835
+ allDuplicates.push(...dups);
1836
+ folders.set(folder, {
1837
+ fileCount: files.length,
1838
+ doc_ext_ids: result.doc_ext_ids,
1839
+ duplicates: dups
1840
+ });
1841
+ }
1842
+ return { doc_ext_ids: allDocIds, duplicates: allDuplicates, folders };
1843
+ }
1844
+ async function uploadZip(auth, workspaceId, zipPath) {
1845
+ const JSZip = (await import('jszip')).default;
1846
+ const zipBuffer = fs.readFileSync(zipPath);
1847
+ const zip = await JSZip.loadAsync(zipBuffer);
1848
+ const zipBaseName = path2.basename(zipPath).replace(/\.zip$/i, "");
1849
+ const entries = [];
1850
+ zip.forEach((relativePath, zipEntry) => {
1851
+ if (zipEntry.dir) return;
1852
+ const ext = path2.extname(relativePath).toLowerCase();
1853
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
1854
+ entries.push({ zipEntryPath: relativePath, zipEntry });
1855
+ }
1856
+ });
1857
+ if (entries.length === 0) {
1858
+ return { doc_ext_ids: [], duplicates: [], folders: /* @__PURE__ */ new Map() };
1859
+ }
1860
+ const firstParts = new Set(entries.map((e) => e.zipEntryPath.split("/")[0]));
1861
+ const hasSingleRoot = firstParts.size === 1 && entries.every((e) => e.zipEntryPath.includes("/"));
1862
+ const groups = /* @__PURE__ */ new Map();
1863
+ for (const entry of entries) {
1864
+ const data = await entry.zipEntry.async("uint8array");
1865
+ const fileName = path2.basename(entry.zipEntryPath);
1866
+ const relDir = path2.dirname(entry.zipEntryPath);
1867
+ let folder;
1868
+ if (hasSingleRoot) {
1869
+ folder = relDir === "." ? zipBaseName : relDir;
1870
+ } else {
1871
+ folder = relDir === "." ? zipBaseName : `${zipBaseName}/${relDir}`;
1872
+ }
1873
+ folder = sanitizeFolderPath(folder);
1874
+ if (!groups.has(folder)) groups.set(folder, []);
1875
+ groups.get(folder).push({ name: fileName, data });
1876
+ }
1877
+ const allDocIds = [];
1878
+ const allDuplicates = [];
1879
+ const folders = /* @__PURE__ */ new Map();
1880
+ for (const [folder, files] of groups) {
1881
+ const blobs = files.map((f) => ({
1882
+ data: new Blob([f.data]),
1883
+ name: f.name
1884
+ }));
1885
+ const result = await uploadFiles(auth, workspaceId, blobs, { folder });
1886
+ const dups = result.duplicates ?? [];
1887
+ allDocIds.push(...result.doc_ext_ids);
1888
+ allDuplicates.push(...dups);
1889
+ folders.set(folder, {
1890
+ fileCount: files.length,
1891
+ doc_ext_ids: result.doc_ext_ids,
1892
+ duplicates: dups
1893
+ });
1894
+ }
1895
+ return { doc_ext_ids: allDocIds, duplicates: allDuplicates, folders };
1896
+ }
1897
+
1898
+ // src/operations/responses.ts
1899
+ var responses_exports = {};
1900
+ __export(responses_exports, {
1901
+ extractResponseText: () => extractResponseText,
1902
+ getResponse: () => getResponse,
1903
+ submitBackgroundQuery: () => submitBackgroundQuery
1904
+ });
1905
+ async function submitBackgroundQuery(options) {
1906
+ const { workspaceId, question, docIds, previousResponseId, model, ...auth } = options;
1907
+ const tools = {};
1908
+ if (docIds.length > 0) {
1909
+ tools.retrieval_chunk = {
1910
+ name: "retrieval_chunk",
1911
+ description: "retrieval chunk",
1912
+ tool_args: { doc_ext_ids: docIds },
1913
+ tool_responses: {}
1914
+ };
1915
+ tools.retrieval_full_context = {
1916
+ name: "retrieval_full_context",
1917
+ description: "retrieval full context",
1918
+ tool_args: { doc_ext_ids: [] },
1919
+ tool_responses: {}
1920
+ };
1921
+ }
1922
+ const body = {
1923
+ input: question,
1924
+ workspace_ext_id: workspaceId,
1925
+ stream: false,
1926
+ background: true,
1927
+ store: true,
1928
+ tools,
1929
+ ...previousResponseId ? { previous_response_id: previousResponseId } : {},
1930
+ ...model ? { model } : {}
1931
+ };
1932
+ const res = await authenticatedFetch({
1933
+ ...auth,
1934
+ path: "/v1/responses",
1935
+ method: "POST",
1936
+ body: JSON.stringify(body),
1937
+ headers: { "Content-Type": "application/json" }
1938
+ });
1939
+ return await res.json();
1940
+ }
1941
+ async function getResponse(auth, responseId) {
1942
+ const res = await authenticatedFetch({
1943
+ ...auth,
1944
+ path: `/v1/responses/${responseId}`,
1945
+ method: "GET"
1946
+ });
1947
+ return await res.json();
1948
+ }
1949
+ function extractResponseText(response) {
1950
+ const parts = [];
1951
+ for (const msg of response.output) {
1952
+ for (const item of msg.content) {
1953
+ if ("type" in item && item.type === "output_text" && "text" in item) {
1954
+ parts.push(item.text);
1955
+ }
1956
+ }
1957
+ }
1958
+ return parts.join("");
1959
+ }
1960
+
1961
+ 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, responses_exports as responses, selectWorkspace, selectWorkspaceById, settings_exports as settings, streamSSE, tags_exports as tags, workspaces_exports as workspaces };
1546
1962
  //# sourceMappingURL=index.js.map
1547
1963
  //# sourceMappingURL=index.js.map