@arbidocs/sdk 0.3.16 → 0.3.18
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/README.md +58 -81
- package/dist/{browser-DFRwcoc1.d.cts → browser-DUQ3Eyvd.d.cts} +91 -5
- package/dist/{browser-DFRwcoc1.d.ts → browser-DUQ3Eyvd.d.ts} +91 -5
- package/dist/browser.cjs +81 -1
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +78 -2
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +247 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -3
- package/dist/index.d.ts +66 -3
- package/dist/index.js +241 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -129,11 +129,13 @@ var FileConfigStore = class {
|
|
|
129
129
|
configFile;
|
|
130
130
|
credentialsFile;
|
|
131
131
|
sessionFile;
|
|
132
|
+
metadataFile;
|
|
132
133
|
constructor(configDir) {
|
|
133
134
|
this.configDir = configDir ?? process.env.ARBI_CONFIG_DIR ?? path2__default.default.join(os__default.default.homedir(), ".arbi");
|
|
134
135
|
this.configFile = path2__default.default.join(this.configDir, "config.json");
|
|
135
136
|
this.credentialsFile = path2__default.default.join(this.configDir, "credentials.json");
|
|
136
137
|
this.sessionFile = path2__default.default.join(this.configDir, "session.json");
|
|
138
|
+
this.metadataFile = path2__default.default.join(this.configDir, "last-metadata.json");
|
|
137
139
|
}
|
|
138
140
|
ensureConfigDir() {
|
|
139
141
|
if (!fs__default.default.existsSync(this.configDir)) {
|
|
@@ -204,6 +206,13 @@ var FileConfigStore = class {
|
|
|
204
206
|
clearChatSession() {
|
|
205
207
|
this.saveChatSession({ ...DEFAULT_SESSION });
|
|
206
208
|
}
|
|
209
|
+
// ── Last metadata (for citation browsing) ────────────────────────────────
|
|
210
|
+
saveLastMetadata(metadata) {
|
|
211
|
+
this.writeSecureFile(this.metadataFile, metadata);
|
|
212
|
+
}
|
|
213
|
+
loadLastMetadata() {
|
|
214
|
+
return this.readJsonFile(this.metadataFile);
|
|
215
|
+
}
|
|
207
216
|
/**
|
|
208
217
|
* Try to resolve config from multiple sources, in priority order:
|
|
209
218
|
*
|
|
@@ -276,6 +285,107 @@ var FileConfigStore = class {
|
|
|
276
285
|
return null;
|
|
277
286
|
}
|
|
278
287
|
};
|
|
288
|
+
|
|
289
|
+
// src/device-flow.ts
|
|
290
|
+
var device_flow_exports = {};
|
|
291
|
+
__export(device_flow_exports, {
|
|
292
|
+
DeviceFlowAccessDenied: () => DeviceFlowAccessDenied,
|
|
293
|
+
DeviceFlowError: () => DeviceFlowError,
|
|
294
|
+
DeviceFlowExpired: () => DeviceFlowExpired,
|
|
295
|
+
fetchSsoConfig: () => fetchSsoConfig,
|
|
296
|
+
pollForToken: () => pollForToken,
|
|
297
|
+
requestDeviceCode: () => requestDeviceCode
|
|
298
|
+
});
|
|
299
|
+
var DeviceFlowError = class extends Error {
|
|
300
|
+
constructor(message) {
|
|
301
|
+
super(message);
|
|
302
|
+
this.name = "DeviceFlowError";
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
var DeviceFlowExpired = class extends DeviceFlowError {
|
|
306
|
+
constructor() {
|
|
307
|
+
super("Device code expired \u2014 please try again");
|
|
308
|
+
this.name = "DeviceFlowExpired";
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
var DeviceFlowAccessDenied = class extends DeviceFlowError {
|
|
312
|
+
constructor() {
|
|
313
|
+
super("Authorization was denied by the user");
|
|
314
|
+
this.name = "DeviceFlowAccessDenied";
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
async function fetchSsoConfig(baseUrl) {
|
|
318
|
+
const arbi = client.createArbiClient({ baseUrl, deploymentDomain: "", credentials: "omit" });
|
|
319
|
+
const { data, error } = await arbi.fetch.GET("/v1/user/sso-config");
|
|
320
|
+
if (error || !data) {
|
|
321
|
+
throw new DeviceFlowError(`Failed to fetch SSO config`);
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
ssoEnabled: data.sso_enabled,
|
|
325
|
+
domain: data.domain,
|
|
326
|
+
clientId: data.cli_client_id || data.client_id,
|
|
327
|
+
audience: data.audience
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async function requestDeviceCode(domain, clientId, audience, scope = "openid email profile") {
|
|
331
|
+
const body = new URLSearchParams({
|
|
332
|
+
client_id: clientId,
|
|
333
|
+
scope,
|
|
334
|
+
...audience ? { audience } : {}
|
|
335
|
+
});
|
|
336
|
+
const res = await fetch(`https://${domain}/oauth/device/code`, {
|
|
337
|
+
method: "POST",
|
|
338
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
339
|
+
body
|
|
340
|
+
});
|
|
341
|
+
if (!res.ok) {
|
|
342
|
+
const text = await res.text();
|
|
343
|
+
throw new DeviceFlowError(`Device code request failed: ${res.status} ${text}`);
|
|
344
|
+
}
|
|
345
|
+
return await res.json();
|
|
346
|
+
}
|
|
347
|
+
async function pollForToken(domain, clientId, deviceCode, interval, expiresIn, onPoll) {
|
|
348
|
+
const deadline = Date.now() + expiresIn * 1e3;
|
|
349
|
+
let pollInterval = interval * 1e3;
|
|
350
|
+
while (Date.now() < deadline) {
|
|
351
|
+
await sleep(pollInterval);
|
|
352
|
+
onPoll?.(Date.now());
|
|
353
|
+
const res = await fetch(`https://${domain}/oauth/token`, {
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
356
|
+
body: new URLSearchParams({
|
|
357
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
358
|
+
client_id: clientId,
|
|
359
|
+
device_code: deviceCode
|
|
360
|
+
})
|
|
361
|
+
});
|
|
362
|
+
if (res.ok) {
|
|
363
|
+
const data = await res.json();
|
|
364
|
+
return data.access_token;
|
|
365
|
+
}
|
|
366
|
+
const errBody = await res.json().catch(() => ({ error: "unknown" }));
|
|
367
|
+
if (errBody.error === "authorization_pending") {
|
|
368
|
+
continue;
|
|
369
|
+
} else if (errBody.error === "slow_down") {
|
|
370
|
+
pollInterval += 5e3;
|
|
371
|
+
continue;
|
|
372
|
+
} else if (errBody.error === "expired_token") {
|
|
373
|
+
throw new DeviceFlowExpired();
|
|
374
|
+
} else if (errBody.error === "access_denied") {
|
|
375
|
+
throw new DeviceFlowAccessDenied();
|
|
376
|
+
} else {
|
|
377
|
+
throw new DeviceFlowError(
|
|
378
|
+
`Token polling error: ${errBody.error} \u2014 ${errBody.error_description ?? ""}`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
throw new DeviceFlowExpired();
|
|
383
|
+
}
|
|
384
|
+
function sleep(ms) {
|
|
385
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/auth.ts
|
|
279
389
|
function formatWorkspaceChoices(wsList) {
|
|
280
390
|
return wsList.map((ws) => {
|
|
281
391
|
const totalDocs = ws.shared_document_count + ws.private_document_count;
|
|
@@ -296,7 +406,8 @@ async function createAuthenticatedClient(config, creds, store) {
|
|
|
296
406
|
const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
|
|
297
407
|
const loginResult = await arbi.auth.loginWithKey({
|
|
298
408
|
email: creds.email,
|
|
299
|
-
signingPrivateKey
|
|
409
|
+
signingPrivateKey,
|
|
410
|
+
ssoToken: creds.ssoToken
|
|
300
411
|
});
|
|
301
412
|
store.saveCredentials({
|
|
302
413
|
...creds,
|
|
@@ -328,6 +439,40 @@ async function performPasswordLogin(config, email, password, store) {
|
|
|
328
439
|
});
|
|
329
440
|
return { arbi, loginResult, config };
|
|
330
441
|
}
|
|
442
|
+
async function performSsoDeviceFlowLogin(config, email, password, store, callbacks) {
|
|
443
|
+
const ssoConfig = await fetchSsoConfig(config.baseUrl);
|
|
444
|
+
if (!ssoConfig.ssoEnabled) {
|
|
445
|
+
throw new ArbiError("SSO is not enabled on this deployment");
|
|
446
|
+
}
|
|
447
|
+
const dc = await requestDeviceCode(ssoConfig.domain, ssoConfig.clientId, ssoConfig.audience);
|
|
448
|
+
callbacks?.onUserCode?.(dc.user_code, dc.verification_uri_complete);
|
|
449
|
+
const ssoToken = await pollForToken(
|
|
450
|
+
ssoConfig.domain,
|
|
451
|
+
ssoConfig.clientId,
|
|
452
|
+
dc.device_code,
|
|
453
|
+
dc.interval,
|
|
454
|
+
dc.expires_in,
|
|
455
|
+
callbacks?.onPoll
|
|
456
|
+
);
|
|
457
|
+
const arbi = client.createArbiClient({
|
|
458
|
+
baseUrl: config.baseUrl,
|
|
459
|
+
deploymentDomain: config.deploymentDomain,
|
|
460
|
+
credentials: "omit"
|
|
461
|
+
});
|
|
462
|
+
await arbi.crypto.initSodium();
|
|
463
|
+
const loginResult = await arbi.auth.login({ email, password, ssoToken });
|
|
464
|
+
store.saveCredentials({
|
|
465
|
+
email,
|
|
466
|
+
signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
|
|
467
|
+
serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
|
|
468
|
+
ssoToken,
|
|
469
|
+
accessToken: void 0,
|
|
470
|
+
workspaceKeyHeader: void 0,
|
|
471
|
+
workspaceId: void 0,
|
|
472
|
+
tokenTimestamp: void 0
|
|
473
|
+
});
|
|
474
|
+
return { arbi, loginResult, config };
|
|
475
|
+
}
|
|
331
476
|
async function selectWorkspace(arbi, workspaceId, wrappedKey, serverSessionKey, signingPrivateKeyBase64) {
|
|
332
477
|
const signingPrivateKey = client.base64ToBytes(signingPrivateKeyBase64);
|
|
333
478
|
const ed25519PublicKey = signingPrivateKey.slice(32, 64);
|
|
@@ -652,6 +797,27 @@ async function streamSSE(response, callbacks = {}) {
|
|
|
652
797
|
context
|
|
653
798
|
};
|
|
654
799
|
}
|
|
800
|
+
function formatStreamSummary(result, elapsedTime) {
|
|
801
|
+
const parts = [];
|
|
802
|
+
if (result.agentSteps.length > 0) {
|
|
803
|
+
let stepLabel = `${result.agentSteps.length} step${result.agentSteps.length === 1 ? "" : "s"}`;
|
|
804
|
+
if (result.toolCallCount > 0) {
|
|
805
|
+
stepLabel += ` (${result.toolCallCount} tool call${result.toolCallCount === 1 ? "" : "s"})`;
|
|
806
|
+
}
|
|
807
|
+
parts.push(stepLabel);
|
|
808
|
+
}
|
|
809
|
+
if (result.usage) {
|
|
810
|
+
parts.push(`${result.usage.total_tokens.toLocaleString()} tokens`);
|
|
811
|
+
}
|
|
812
|
+
if (result.context && result.context.context_window > 0) {
|
|
813
|
+
const used = result.context.all_llm_calls?.last_input_tokens ?? result.context.total_input;
|
|
814
|
+
parts.push(`${used.toLocaleString()}/${result.context.context_window.toLocaleString()} context`);
|
|
815
|
+
}
|
|
816
|
+
if (elapsedTime != null) {
|
|
817
|
+
parts.push(`${elapsedTime.toFixed(1)}s`);
|
|
818
|
+
}
|
|
819
|
+
return parts.length > 0 ? parts.join(" \xB7 ") : "";
|
|
820
|
+
}
|
|
655
821
|
var consumeSSEStream = streamSSE;
|
|
656
822
|
var AUTH_TIMEOUT_MS = 1e4;
|
|
657
823
|
var MAX_BACKOFF_MS = 3e4;
|
|
@@ -876,6 +1042,66 @@ function formatUserName(user) {
|
|
|
876
1042
|
return [user.given_name, user.family_name].filter(Boolean).join(" ");
|
|
877
1043
|
}
|
|
878
1044
|
|
|
1045
|
+
// src/citations.ts
|
|
1046
|
+
function resolveCitations(metadata) {
|
|
1047
|
+
if (!metadata?.tools) return [];
|
|
1048
|
+
const tools = metadata.tools;
|
|
1049
|
+
const modelCitations = tools.model_citations;
|
|
1050
|
+
const citationMap = modelCitations?.tool_responses;
|
|
1051
|
+
if (!citationMap || Object.keys(citationMap).length === 0) return [];
|
|
1052
|
+
const chunkLookup = buildChunkLookup(tools);
|
|
1053
|
+
const resolved = [];
|
|
1054
|
+
for (const [citationNum, citationData] of Object.entries(citationMap)) {
|
|
1055
|
+
const chunks = [];
|
|
1056
|
+
for (const chunkId of citationData.chunk_ids ?? []) {
|
|
1057
|
+
const chunk = chunkLookup.get(chunkId);
|
|
1058
|
+
if (chunk) chunks.push(chunk);
|
|
1059
|
+
}
|
|
1060
|
+
resolved.push({ citationNum, citationData, chunks });
|
|
1061
|
+
}
|
|
1062
|
+
resolved.sort((a, b) => Number(a.citationNum) - Number(b.citationNum));
|
|
1063
|
+
return resolved;
|
|
1064
|
+
}
|
|
1065
|
+
function summarizeCitations(resolved) {
|
|
1066
|
+
return resolved.map((r) => {
|
|
1067
|
+
const firstChunk = r.chunks[0];
|
|
1068
|
+
return {
|
|
1069
|
+
citationNum: r.citationNum,
|
|
1070
|
+
statement: r.citationData.statement ?? "",
|
|
1071
|
+
docTitle: firstChunk?.metadata?.doc_title ?? "Unknown document",
|
|
1072
|
+
pageNumber: firstChunk?.metadata?.page_number ?? null,
|
|
1073
|
+
chunkCount: r.chunks.length
|
|
1074
|
+
};
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
function countCitations(metadata) {
|
|
1078
|
+
if (!metadata?.tools) return 0;
|
|
1079
|
+
const tools = metadata.tools;
|
|
1080
|
+
const modelCitations = tools.model_citations;
|
|
1081
|
+
const responses = modelCitations?.tool_responses;
|
|
1082
|
+
return responses ? Object.keys(responses).length : 0;
|
|
1083
|
+
}
|
|
1084
|
+
function stripCitationMarkdown(text) {
|
|
1085
|
+
return text.replace(/\[([^\]]+)\]\(#cite-(\d+)\)/g, "$1[$2]");
|
|
1086
|
+
}
|
|
1087
|
+
function buildChunkLookup(tools) {
|
|
1088
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
1089
|
+
for (const toolName of ["retrieval_chunk", "retrieval_full_context"]) {
|
|
1090
|
+
const tool = tools[toolName];
|
|
1091
|
+
if (!tool?.tool_responses) continue;
|
|
1092
|
+
for (const chunks of Object.values(tool.tool_responses)) {
|
|
1093
|
+
if (!Array.isArray(chunks)) continue;
|
|
1094
|
+
for (const chunk of chunks) {
|
|
1095
|
+
const id = chunk.metadata?.chunk_ext_id;
|
|
1096
|
+
if (id && !lookup.has(id)) {
|
|
1097
|
+
lookup.set(id, chunk);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return lookup;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
879
1105
|
// src/operations/documents.ts
|
|
880
1106
|
var documents_exports = {};
|
|
881
1107
|
__export(documents_exports, {
|
|
@@ -1573,6 +1799,19 @@ var Arbi = class {
|
|
|
1573
1799
|
lr.serverSessionKey,
|
|
1574
1800
|
signingPrivateKeyBase64
|
|
1575
1801
|
);
|
|
1802
|
+
const workspaceKeyHeader = client.session.getWorkspaceKeyHeader();
|
|
1803
|
+
if (workspaceKeyHeader) {
|
|
1804
|
+
const { data: openResult, error: openError } = await client.fetch.POST(
|
|
1805
|
+
"/v1/workspace/{workspace_ext_id}/open",
|
|
1806
|
+
{
|
|
1807
|
+
params: { path: { workspace_ext_id: ws.external_id } },
|
|
1808
|
+
body: { workspace_key: workspaceKeyHeader }
|
|
1809
|
+
}
|
|
1810
|
+
);
|
|
1811
|
+
if (!openError && openResult?.access_token) {
|
|
1812
|
+
client.session.setAccessToken(openResult.access_token);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1576
1815
|
this.currentWorkspaceId = ws.external_id;
|
|
1577
1816
|
}
|
|
1578
1817
|
/** Log out and clear internal state. */
|
|
@@ -1991,8 +2230,10 @@ exports.connectWithReconnect = connectWithReconnect;
|
|
|
1991
2230
|
exports.consumeSSEStream = consumeSSEStream;
|
|
1992
2231
|
exports.contacts = contacts_exports;
|
|
1993
2232
|
exports.conversations = conversations_exports;
|
|
2233
|
+
exports.countCitations = countCitations;
|
|
1994
2234
|
exports.createAuthenticatedClient = createAuthenticatedClient;
|
|
1995
2235
|
exports.createDocumentWaiter = createDocumentWaiter;
|
|
2236
|
+
exports.deviceFlow = device_flow_exports;
|
|
1996
2237
|
exports.dm = dm_exports;
|
|
1997
2238
|
exports.doctags = doctags_exports;
|
|
1998
2239
|
exports.documents = documents_exports;
|
|
@@ -2000,6 +2241,7 @@ exports.documentsNode = documents_node_exports;
|
|
|
2000
2241
|
exports.files = files_exports;
|
|
2001
2242
|
exports.formatAgentStepLabel = formatAgentStepLabel;
|
|
2002
2243
|
exports.formatFileSize = formatFileSize;
|
|
2244
|
+
exports.formatStreamSummary = formatStreamSummary;
|
|
2003
2245
|
exports.formatUserName = formatUserName;
|
|
2004
2246
|
exports.formatWorkspaceChoices = formatWorkspaceChoices;
|
|
2005
2247
|
exports.formatWsMessage = formatWsMessage;
|
|
@@ -2009,15 +2251,19 @@ exports.getErrorMessage = getErrorMessage;
|
|
|
2009
2251
|
exports.health = health_exports;
|
|
2010
2252
|
exports.parseSSEEvents = parseSSEEvents;
|
|
2011
2253
|
exports.performPasswordLogin = performPasswordLogin;
|
|
2254
|
+
exports.performSsoDeviceFlowLogin = performSsoDeviceFlowLogin;
|
|
2012
2255
|
exports.requireData = requireData;
|
|
2013
2256
|
exports.requireOk = requireOk;
|
|
2014
2257
|
exports.resolveAuth = resolveAuth;
|
|
2258
|
+
exports.resolveCitations = resolveCitations;
|
|
2015
2259
|
exports.resolveWorkspace = resolveWorkspace;
|
|
2016
2260
|
exports.responses = responses_exports;
|
|
2017
2261
|
exports.selectWorkspace = selectWorkspace;
|
|
2018
2262
|
exports.selectWorkspaceById = selectWorkspaceById;
|
|
2019
2263
|
exports.settings = settings_exports;
|
|
2020
2264
|
exports.streamSSE = streamSSE;
|
|
2265
|
+
exports.stripCitationMarkdown = stripCitationMarkdown;
|
|
2266
|
+
exports.summarizeCitations = summarizeCitations;
|
|
2021
2267
|
exports.tags = tags_exports;
|
|
2022
2268
|
exports.workspaces = workspaces_exports;
|
|
2023
2269
|
//# sourceMappingURL=index.cjs.map
|