@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/index.cjs CHANGED
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var fs = require('fs');
4
- var path = require('path');
4
+ var path2 = require('path');
5
5
  var os = require('os');
6
6
  var client = require('@arbidocs/client');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
10
10
  var fs__default = /*#__PURE__*/_interopDefault(fs);
11
- var path__default = /*#__PURE__*/_interopDefault(path);
11
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
12
12
  var os__default = /*#__PURE__*/_interopDefault(os);
13
13
 
14
14
  var __defProp = Object.defineProperty;
@@ -44,14 +44,64 @@ function requireOk(result, message) {
44
44
  }
45
45
  }
46
46
  function getErrorMessage(err) {
47
- return err instanceof Error ? err.message : String(err);
47
+ if (!(err instanceof Error)) return String(err);
48
+ const rootCause = getDeepestCause(err);
49
+ if (rootCause !== err) {
50
+ const rootMsg = rootCause.message || String(rootCause);
51
+ if (err.message && !err.message.includes(rootMsg)) {
52
+ return `${err.message}: ${rootMsg}`;
53
+ }
54
+ }
55
+ return err.message;
56
+ }
57
+ function getDeepestCause(err) {
58
+ let current = err;
59
+ const seen = /* @__PURE__ */ new Set();
60
+ while (current.cause instanceof Error && !seen.has(current.cause)) {
61
+ seen.add(current);
62
+ current = current.cause;
63
+ }
64
+ return current;
65
+ }
66
+ function getErrorCode(err) {
67
+ if (!(err instanceof Error)) return void 0;
68
+ let current = err;
69
+ const seen = /* @__PURE__ */ new Set();
70
+ while (current && !seen.has(current)) {
71
+ seen.add(current);
72
+ const code = current.code;
73
+ if (typeof code === "string") return code;
74
+ current = current.cause instanceof Error ? current.cause : void 0;
75
+ }
76
+ return void 0;
48
77
  }
49
78
 
50
79
  // src/fetch.ts
51
- var STATUS_MESSAGES = {
80
+ var STATUS_FALLBACKS = {
52
81
  401: "Token has expired. Please run: arbi login",
82
+ 403: "Access denied. You may not have permission for this workspace.",
83
+ 404: "Resource not found.",
53
84
  503: "The selected LLM is not responding. Please retry or select another model."
54
85
  };
86
+ async function extractErrorMessage(res) {
87
+ let bodyDetail;
88
+ try {
89
+ const text = await res.text();
90
+ if (text) {
91
+ try {
92
+ const json = JSON.parse(text);
93
+ bodyDetail = json.detail || json.message || json.error || void 0;
94
+ } catch {
95
+ if (text.length < 200) bodyDetail = text;
96
+ }
97
+ }
98
+ } catch {
99
+ }
100
+ if (bodyDetail) return `Request failed (${res.status}): ${bodyDetail}`;
101
+ const fallback = STATUS_FALLBACKS[res.status];
102
+ if (fallback) return fallback;
103
+ return `Request failed: ${res.status} ${res.statusText}`;
104
+ }
55
105
  async function authenticatedFetch(options) {
56
106
  const { baseUrl, accessToken, workspaceKeyHeader, path: path3, method, body, headers } = options;
57
107
  const res = await fetch(`${baseUrl}${path3}`, {
@@ -64,16 +114,15 @@ async function authenticatedFetch(options) {
64
114
  body
65
115
  });
66
116
  if (!res.ok) {
67
- const knownMessage = STATUS_MESSAGES[res.status];
68
- if (knownMessage) throw new Error(knownMessage);
69
- const text = await res.text().catch(() => "");
70
- throw new Error(`Request failed: ${res.status} ${text}`);
117
+ const message = await extractErrorMessage(res);
118
+ throw new ArbiApiError(message, { status: res.status, statusText: res.statusText });
71
119
  }
72
120
  return res;
73
121
  }
74
122
  var DEFAULT_SESSION = {
75
123
  lastMessageExtId: null,
76
- conversationExtId: null
124
+ conversationExtId: null,
125
+ workspaceId: null
77
126
  };
78
127
  var FileConfigStore = class {
79
128
  configDir;
@@ -81,10 +130,10 @@ var FileConfigStore = class {
81
130
  credentialsFile;
82
131
  sessionFile;
83
132
  constructor(configDir) {
84
- this.configDir = configDir ?? process.env.ARBI_CONFIG_DIR ?? path__default.default.join(os__default.default.homedir(), ".arbi");
85
- this.configFile = path__default.default.join(this.configDir, "config.json");
86
- this.credentialsFile = path__default.default.join(this.configDir, "credentials.json");
87
- this.sessionFile = path__default.default.join(this.configDir, "session.json");
133
+ this.configDir = configDir ?? process.env.ARBI_CONFIG_DIR ?? path2__default.default.join(os__default.default.homedir(), ".arbi");
134
+ this.configFile = path2__default.default.join(this.configDir, "config.json");
135
+ this.credentialsFile = path2__default.default.join(this.configDir, "credentials.json");
136
+ this.sessionFile = path2__default.default.join(this.configDir, "session.json");
88
137
  }
89
138
  ensureConfigDir() {
90
139
  if (!fs__default.default.existsSync(this.configDir)) {
@@ -155,6 +204,77 @@ var FileConfigStore = class {
155
204
  clearChatSession() {
156
205
  this.saveChatSession({ ...DEFAULT_SESSION });
157
206
  }
207
+ /**
208
+ * Try to resolve config from multiple sources, in priority order:
209
+ *
210
+ * 1. Existing `~/.arbi/config.json` (highest priority)
211
+ * 2. `ARBI_SERVER_URL` environment variable
212
+ * 3. `.env` file in `searchDir` → read `VITE_DEPLOYMENT_DOMAIN`
213
+ * 4. `public/config.json` in `searchDir` → read `deployment.domain`
214
+ * 5. Default to `https://localhost`
215
+ *
216
+ * Returns `{ config, source }` where source describes where the config came from.
217
+ * Saves auto-detected config to disk so subsequent calls use the fast path.
218
+ */
219
+ resolveConfigWithFallbacks(searchDir) {
220
+ const existing = this.getConfig();
221
+ if (existing?.baseUrl) {
222
+ return { config: existing, source: "config" };
223
+ }
224
+ const envUrl = process.env.ARBI_SERVER_URL;
225
+ if (envUrl) {
226
+ const domain = new URL(envUrl).hostname;
227
+ const config2 = { baseUrl: envUrl.replace(/\/$/, ""), deploymentDomain: domain };
228
+ this.saveConfig(config2);
229
+ return { config: config2, source: "ARBI_SERVER_URL env var" };
230
+ }
231
+ const dir = searchDir || process.cwd();
232
+ const dotenvConfig = this.readDotEnvDomain(path2__default.default.join(dir, ".env"));
233
+ if (dotenvConfig) {
234
+ this.saveConfig(dotenvConfig);
235
+ return { config: dotenvConfig, source: ".env file" };
236
+ }
237
+ const publicConfig = this.readPublicConfigDomain(path2__default.default.join(dir, "public", "config.json"));
238
+ if (publicConfig) {
239
+ this.saveConfig(publicConfig);
240
+ return { config: publicConfig, source: "public/config.json" };
241
+ }
242
+ const config = { baseUrl: "https://localhost", deploymentDomain: "localhost" };
243
+ this.saveConfig(config);
244
+ return { config, source: "default (https://localhost)" };
245
+ }
246
+ /**
247
+ * Read VITE_DEPLOYMENT_DOMAIN from a .env file.
248
+ */
249
+ readDotEnvDomain(filePath) {
250
+ try {
251
+ const content = fs__default.default.readFileSync(filePath, "utf-8");
252
+ const match = content.match(/^VITE_DEPLOYMENT_DOMAIN\s*=\s*(.+)$/m);
253
+ if (match) {
254
+ const domain = match[1].trim().replace(/^["']|["']$/g, "");
255
+ if (domain) {
256
+ return { baseUrl: `https://${domain}`, deploymentDomain: domain };
257
+ }
258
+ }
259
+ } catch {
260
+ }
261
+ return null;
262
+ }
263
+ /**
264
+ * Read deployment.domain from a public/config.json file.
265
+ */
266
+ readPublicConfigDomain(filePath) {
267
+ try {
268
+ const content = fs__default.default.readFileSync(filePath, "utf-8");
269
+ const json = JSON.parse(content);
270
+ const domain = json?.deployment?.domain;
271
+ if (domain) {
272
+ return { baseUrl: `https://${domain}`, deploymentDomain: domain };
273
+ }
274
+ } catch {
275
+ }
276
+ return null;
277
+ }
158
278
  };
159
279
  function formatWorkspaceChoices(wsList) {
160
280
  return wsList.map((ws) => {
@@ -180,7 +300,11 @@ async function createAuthenticatedClient(config, creds, store) {
180
300
  });
181
301
  store.saveCredentials({
182
302
  ...creds,
183
- serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
303
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
304
+ accessToken: void 0,
305
+ workspaceKeyHeader: void 0,
306
+ workspaceId: void 0,
307
+ tokenTimestamp: void 0
184
308
  });
185
309
  return { arbi, loginResult };
186
310
  }
@@ -195,7 +319,12 @@ async function performPasswordLogin(config, email, password, store) {
195
319
  store.saveCredentials({
196
320
  email,
197
321
  signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
198
- serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
322
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
323
+ // Clear any cached workspace tokens — new session key invalidates them
324
+ accessToken: void 0,
325
+ workspaceKeyHeader: void 0,
326
+ workspaceId: void 0,
327
+ tokenTimestamp: void 0
199
328
  });
200
329
  return { arbi, loginResult, config };
201
330
  }
@@ -258,6 +387,10 @@ async function resolveAuth(store) {
258
387
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
259
388
  return { arbi, loginResult, config };
260
389
  }
390
+ var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
391
+ function isCachedTokenValid(creds, workspaceId) {
392
+ return !!(creds.accessToken && creds.workspaceKeyHeader && creds.workspaceId === workspaceId && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
393
+ }
261
394
  async function resolveWorkspace(store, workspaceOpt) {
262
395
  const config = store.requireConfig();
263
396
  const creds = store.requireCredentials();
@@ -265,6 +398,32 @@ async function resolveWorkspace(store, workspaceOpt) {
265
398
  if (!workspaceId) {
266
399
  throw new ArbiError("No workspace selected. Run: arbi workspace select <id>");
267
400
  }
401
+ if (isCachedTokenValid(creds, workspaceId)) {
402
+ const arbi2 = client.createArbiClient({
403
+ baseUrl: config.baseUrl,
404
+ deploymentDomain: config.deploymentDomain,
405
+ credentials: "omit"
406
+ });
407
+ await arbi2.crypto.initSodium();
408
+ arbi2.session.setSelectedWorkspace(workspaceId);
409
+ arbi2.session.setAccessToken(creds.accessToken);
410
+ arbi2.session.setCachedWorkspaceHeader(workspaceId, creds.workspaceKeyHeader);
411
+ const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
412
+ const serverSessionKey = client.base64ToBytes(creds.serverSessionKeyBase64);
413
+ const loginResult2 = {
414
+ accessToken: creds.accessToken,
415
+ signingPrivateKey,
416
+ serverSessionKey
417
+ };
418
+ return {
419
+ arbi: arbi2,
420
+ loginResult: loginResult2,
421
+ config,
422
+ workspaceId,
423
+ accessToken: creds.accessToken,
424
+ workspaceKeyHeader: creds.workspaceKeyHeader
425
+ };
426
+ }
268
427
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
269
428
  await selectWorkspaceById(
270
429
  arbi,
@@ -277,10 +436,57 @@ async function resolveWorkspace(store, workspaceOpt) {
277
436
  if (!accessToken || !workspaceKeyHeader) {
278
437
  throw new ArbiError("Authentication error \u2014 missing token or workspace key");
279
438
  }
439
+ store.saveCredentials({
440
+ ...store.requireCredentials(),
441
+ accessToken,
442
+ workspaceKeyHeader,
443
+ workspaceId,
444
+ tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString()
445
+ });
280
446
  return { arbi, loginResult, config, workspaceId, accessToken, workspaceKeyHeader };
281
447
  }
282
448
 
283
449
  // src/sse.ts
450
+ var TOOL_LABELS = {
451
+ search_documents: "Searching documents",
452
+ get_document_passages: "Reading document",
453
+ get_table_of_contents: "Getting table of contents",
454
+ view_document_pages: "Viewing document pages",
455
+ get_full_document: "Reading full document",
456
+ web_search: "Searching the web",
457
+ read_url: "Reading web pages",
458
+ ask_user: "Asking user",
459
+ compaction: "Compacting conversation",
460
+ personal_agent: "Running agent",
461
+ create_artifact: "Creating artifact",
462
+ create_plan: "Creating plan",
463
+ save_skill: "Saving skill",
464
+ run_code: "Running code"
465
+ };
466
+ var LIFECYCLE_LABELS = {
467
+ evaluation: "Evaluating results",
468
+ answering: "Writing answer",
469
+ reviewing: "Reviewing answer",
470
+ planning: "Planning",
471
+ tool_progress: "Working"
472
+ };
473
+ function formatAgentStepLabel(step) {
474
+ if (step.focus) return step.focus;
475
+ const detail = step.detail;
476
+ if (step.step === "tool_progress" && detail && detail.length > 0) {
477
+ const toolName = detail[0].tool;
478
+ const label = toolName && TOOL_LABELS[toolName] || LIFECYCLE_LABELS.tool_progress;
479
+ const message = detail[0].message;
480
+ return message ? `${label}: ${message}` : label;
481
+ }
482
+ if (step.step) {
483
+ return LIFECYCLE_LABELS[step.step] || step.step;
484
+ }
485
+ if (detail && detail.length > 0 && detail[0].tool) {
486
+ return TOOL_LABELS[detail[0].tool] || detail[0].tool;
487
+ }
488
+ return "";
489
+ }
284
490
  function parseSSEEvents(chunk, buffer) {
285
491
  const combined = buffer + chunk;
286
492
  const events = [];
@@ -351,6 +557,11 @@ async function streamSSE(response, callbacks = {}) {
351
557
  usage = data.response.usage;
352
558
  callbacks.onUsage?.(data.response.usage);
353
559
  }
560
+ if (data.response?.metadata) {
561
+ const meta = data.response.metadata;
562
+ metadata = meta;
563
+ callbacks.onMetadata?.(meta);
564
+ }
354
565
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
355
566
  callbacks.onComplete?.();
356
567
  },
@@ -363,8 +574,8 @@ async function streamSSE(response, callbacks = {}) {
363
574
  // ARBI-specific events (dot-prefixed from server)
364
575
  "arbi.agent_step": (raw) => {
365
576
  const data = JSON.parse(raw);
366
- const focus = data.focus || data.step || "";
367
- if (focus) agentSteps.push(focus);
577
+ const label = formatAgentStepLabel(data);
578
+ if (label) agentSteps.push(label);
368
579
  callbacks.onAgentStep?.(data);
369
580
  if (data.t != null) callbacks.onElapsedTime?.(data.t);
370
581
  },
@@ -642,16 +853,34 @@ function formatUserName(user) {
642
853
  // src/operations/documents.ts
643
854
  var documents_exports = {};
644
855
  __export(documents_exports, {
856
+ SUPPORTED_EXTENSIONS: () => SUPPORTED_EXTENSIONS,
645
857
  deleteDocuments: () => deleteDocuments,
646
858
  downloadDocument: () => downloadDocument,
647
859
  getDocuments: () => getDocuments,
648
860
  getParsedContent: () => getParsedContent,
649
861
  listDocuments: () => listDocuments,
862
+ sanitizeFolderPath: () => sanitizeFolderPath,
650
863
  updateDocuments: () => updateDocuments,
651
864
  uploadFile: () => uploadFile,
652
- uploadLocalFile: () => uploadLocalFile,
865
+ uploadFiles: () => uploadFiles,
653
866
  uploadUrl: () => uploadUrl
654
867
  });
868
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
869
+ ".pdf",
870
+ ".txt",
871
+ ".md",
872
+ ".html",
873
+ ".doc",
874
+ ".docx",
875
+ ".rtf",
876
+ ".ppt",
877
+ ".pptx",
878
+ ".xls",
879
+ ".xlsx"
880
+ ]);
881
+ function sanitizeFolderPath(folderPath) {
882
+ return folderPath.replace(/[^a-zA-Z0-9_\-/]/g, "_").replace(/_{2,}/g, "_");
883
+ }
655
884
  async function listDocuments(arbi) {
656
885
  return requireData(await arbi.fetch.GET("/v1/document/list"), "Failed to fetch documents");
657
886
  }
@@ -690,22 +919,31 @@ async function getParsedContent(auth, docId, stage) {
690
919
  });
691
920
  return res.json();
692
921
  }
693
- async function uploadFile(auth, workspaceId, fileData, fileName) {
922
+ async function uploadFile(auth, workspaceId, fileData, fileName, options) {
694
923
  const formData = new FormData();
695
924
  formData.append("files", fileData, fileName);
925
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
926
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
696
927
  const res = await authenticatedFetch({
697
928
  ...auth,
698
- path: `/v1/document/upload?workspace_ext_id=${workspaceId}`,
929
+ path: `/v1/document/upload?${params.toString()}`,
699
930
  method: "POST",
700
931
  body: formData
701
932
  });
702
933
  return res.json();
703
934
  }
704
- async function uploadLocalFile(auth, workspaceId, filePath) {
705
- const fileBuffer = fs__default.default.readFileSync(filePath);
706
- const fileName = path__default.default.basename(filePath);
707
- const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName);
708
- return { ...result, fileName };
935
+ async function uploadFiles(auth, workspaceId, files, options) {
936
+ const formData = new FormData();
937
+ for (const f of files) formData.append("files", f.data, f.name);
938
+ const params = new URLSearchParams({ workspace_ext_id: workspaceId });
939
+ if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
940
+ const res = await authenticatedFetch({
941
+ ...auth,
942
+ path: `/v1/document/upload?${params.toString()}`,
943
+ method: "POST",
944
+ body: formData
945
+ });
946
+ return res.json();
709
947
  }
710
948
  async function downloadDocument(auth, docId) {
711
949
  return authenticatedFetch({
@@ -905,6 +1143,8 @@ async function retrieve(options) {
905
1143
  input: query,
906
1144
  workspace_ext_id: workspaceId,
907
1145
  stream: false,
1146
+ background: false,
1147
+ store: true,
908
1148
  tools,
909
1149
  ...model ? { model } : {}
910
1150
  };
@@ -920,6 +1160,8 @@ async function queryAssistant(options) {
920
1160
  input: question,
921
1161
  workspace_ext_id: workspaceId,
922
1162
  stream: true,
1163
+ background: false,
1164
+ store: true,
923
1165
  tools: {
924
1166
  retrieval_chunk: buildRetrievalChunkTool(docIds),
925
1167
  retrieval_full_context: buildRetrievalFullContextTool([])
@@ -1379,11 +1621,12 @@ var Arbi = class {
1379
1621
  workspaceId ?? this.requireWorkspace(),
1380
1622
  shared
1381
1623
  ),
1382
- uploadFile: (fileData, fileName, workspaceId) => uploadFile(
1624
+ uploadFile: (fileData, fileName, options) => uploadFile(
1383
1625
  this.getAuthHeaders(),
1384
- workspaceId ?? this.requireWorkspace(),
1626
+ options?.workspaceId ?? this.requireWorkspace(),
1385
1627
  fileData,
1386
- fileName
1628
+ fileName,
1629
+ options?.folder ? { folder: options.folder } : void 0
1387
1630
  ),
1388
1631
  download: (docId) => downloadDocument(this.getAuthHeaders(), docId),
1389
1632
  getParsedContent: (docId, stage) => getParsedContent(this.getAuthHeaders(), docId, stage)
@@ -1525,10 +1768,129 @@ var Arbi = class {
1525
1768
  }
1526
1769
  };
1527
1770
 
1771
+ // src/operations/documents-node.ts
1772
+ var documents_node_exports = {};
1773
+ __export(documents_node_exports, {
1774
+ uploadDirectory: () => uploadDirectory,
1775
+ uploadLocalFile: () => uploadLocalFile,
1776
+ uploadZip: () => uploadZip
1777
+ });
1778
+ function collectFiles(dirPath, baseDir) {
1779
+ const root = baseDir ?? dirPath;
1780
+ const results = [];
1781
+ for (const entry of fs__default.default.readdirSync(dirPath, { withFileTypes: true })) {
1782
+ const fullPath = path2__default.default.join(dirPath, entry.name);
1783
+ if (entry.isDirectory()) {
1784
+ results.push(...collectFiles(fullPath, root));
1785
+ } else if (entry.isFile()) {
1786
+ const ext = path2__default.default.extname(entry.name).toLowerCase();
1787
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
1788
+ results.push({ absolutePath: fullPath, relativePath: path2__default.default.relative(root, fullPath) });
1789
+ }
1790
+ }
1791
+ }
1792
+ return results;
1793
+ }
1794
+ async function uploadLocalFile(auth, workspaceId, filePath, options) {
1795
+ const fileBuffer = fs__default.default.readFileSync(filePath);
1796
+ const fileName = path2__default.default.basename(filePath);
1797
+ const result = await uploadFile(auth, workspaceId, new Blob([fileBuffer]), fileName, options);
1798
+ return { ...result, fileName };
1799
+ }
1800
+ async function uploadDirectory(auth, workspaceId, dirPath) {
1801
+ const resolvedDir = path2__default.default.resolve(dirPath);
1802
+ const dirName = path2__default.default.basename(resolvedDir);
1803
+ const entries = collectFiles(resolvedDir);
1804
+ if (entries.length === 0) {
1805
+ return { doc_ext_ids: [], duplicates: [], folders: /* @__PURE__ */ new Map() };
1806
+ }
1807
+ const groups = /* @__PURE__ */ new Map();
1808
+ for (const entry of entries) {
1809
+ const relDir = path2__default.default.dirname(entry.relativePath);
1810
+ const folder = relDir === "." ? dirName : `${dirName}/${relDir}`;
1811
+ if (!groups.has(folder)) groups.set(folder, []);
1812
+ groups.get(folder).push({ absolutePath: entry.absolutePath, name: path2__default.default.basename(entry.relativePath) });
1813
+ }
1814
+ const allDocIds = [];
1815
+ const allDuplicates = [];
1816
+ const folders = /* @__PURE__ */ new Map();
1817
+ for (const [folder, files] of groups) {
1818
+ const blobs = files.map((f) => ({
1819
+ data: new Blob([fs__default.default.readFileSync(f.absolutePath)]),
1820
+ name: f.name
1821
+ }));
1822
+ const result = await uploadFiles(auth, workspaceId, blobs, { folder });
1823
+ const dups = result.duplicates ?? [];
1824
+ allDocIds.push(...result.doc_ext_ids);
1825
+ allDuplicates.push(...dups);
1826
+ folders.set(folder, {
1827
+ fileCount: files.length,
1828
+ doc_ext_ids: result.doc_ext_ids,
1829
+ duplicates: dups
1830
+ });
1831
+ }
1832
+ return { doc_ext_ids: allDocIds, duplicates: allDuplicates, folders };
1833
+ }
1834
+ async function uploadZip(auth, workspaceId, zipPath) {
1835
+ const JSZip = (await import('jszip')).default;
1836
+ const zipBuffer = fs__default.default.readFileSync(zipPath);
1837
+ const zip = await JSZip.loadAsync(zipBuffer);
1838
+ const zipBaseName = path2__default.default.basename(zipPath).replace(/\.zip$/i, "");
1839
+ const entries = [];
1840
+ zip.forEach((relativePath, zipEntry) => {
1841
+ if (zipEntry.dir) return;
1842
+ const ext = path2__default.default.extname(relativePath).toLowerCase();
1843
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
1844
+ entries.push({ zipEntryPath: relativePath, zipEntry });
1845
+ }
1846
+ });
1847
+ if (entries.length === 0) {
1848
+ return { doc_ext_ids: [], duplicates: [], folders: /* @__PURE__ */ new Map() };
1849
+ }
1850
+ const firstParts = new Set(entries.map((e) => e.zipEntryPath.split("/")[0]));
1851
+ const hasSingleRoot = firstParts.size === 1 && entries.every((e) => e.zipEntryPath.includes("/"));
1852
+ const groups = /* @__PURE__ */ new Map();
1853
+ for (const entry of entries) {
1854
+ const data = await entry.zipEntry.async("uint8array");
1855
+ const fileName = path2__default.default.basename(entry.zipEntryPath);
1856
+ const relDir = path2__default.default.dirname(entry.zipEntryPath);
1857
+ let folder;
1858
+ if (hasSingleRoot) {
1859
+ folder = relDir === "." ? zipBaseName : relDir;
1860
+ } else {
1861
+ folder = relDir === "." ? zipBaseName : `${zipBaseName}/${relDir}`;
1862
+ }
1863
+ folder = sanitizeFolderPath(folder);
1864
+ if (!groups.has(folder)) groups.set(folder, []);
1865
+ groups.get(folder).push({ name: fileName, data });
1866
+ }
1867
+ const allDocIds = [];
1868
+ const allDuplicates = [];
1869
+ const folders = /* @__PURE__ */ new Map();
1870
+ for (const [folder, files] of groups) {
1871
+ const blobs = files.map((f) => ({
1872
+ data: new Blob([f.data]),
1873
+ name: f.name
1874
+ }));
1875
+ const result = await uploadFiles(auth, workspaceId, blobs, { folder });
1876
+ const dups = result.duplicates ?? [];
1877
+ allDocIds.push(...result.doc_ext_ids);
1878
+ allDuplicates.push(...dups);
1879
+ folders.set(folder, {
1880
+ fileCount: files.length,
1881
+ doc_ext_ids: result.doc_ext_ids,
1882
+ duplicates: dups
1883
+ });
1884
+ }
1885
+ return { doc_ext_ids: allDocIds, duplicates: allDuplicates, folders };
1886
+ }
1887
+
1528
1888
  exports.Arbi = Arbi;
1529
1889
  exports.ArbiApiError = ArbiApiError;
1530
1890
  exports.ArbiError = ArbiError;
1531
1891
  exports.FileConfigStore = FileConfigStore;
1892
+ exports.LIFECYCLE_LABELS = LIFECYCLE_LABELS;
1893
+ exports.TOOL_LABELS = TOOL_LABELS;
1532
1894
  exports.agentconfig = agentconfig_exports;
1533
1895
  exports.assistant = assistant_exports;
1534
1896
  exports.authenticatedFetch = authenticatedFetch;
@@ -1545,12 +1907,15 @@ exports.createDocumentWaiter = createDocumentWaiter;
1545
1907
  exports.dm = dm_exports;
1546
1908
  exports.doctags = doctags_exports;
1547
1909
  exports.documents = documents_exports;
1910
+ exports.documentsNode = documents_node_exports;
1548
1911
  exports.files = files_exports;
1912
+ exports.formatAgentStepLabel = formatAgentStepLabel;
1549
1913
  exports.formatFileSize = formatFileSize;
1550
1914
  exports.formatUserName = formatUserName;
1551
1915
  exports.formatWorkspaceChoices = formatWorkspaceChoices;
1552
1916
  exports.formatWsMessage = formatWsMessage;
1553
1917
  exports.generateEncryptedWorkspaceKey = generateEncryptedWorkspaceKey;
1918
+ exports.getErrorCode = getErrorCode;
1554
1919
  exports.getErrorMessage = getErrorMessage;
1555
1920
  exports.health = health_exports;
1556
1921
  exports.parseSSEEvents = parseSSEEvents;