@arbidocs/sdk 0.3.17 → 0.3.19

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
@@ -285,6 +285,107 @@ var FileConfigStore = class {
285
285
  return null;
286
286
  }
287
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
288
389
  function formatWorkspaceChoices(wsList) {
289
390
  return wsList.map((ws) => {
290
391
  const totalDocs = ws.shared_document_count + ws.private_document_count;
@@ -305,7 +406,8 @@ async function createAuthenticatedClient(config, creds, store) {
305
406
  const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
306
407
  const loginResult = await arbi.auth.loginWithKey({
307
408
  email: creds.email,
308
- signingPrivateKey
409
+ signingPrivateKey,
410
+ ssoToken: creds.ssoToken
309
411
  });
310
412
  store.saveCredentials({
311
413
  ...creds,
@@ -337,6 +439,40 @@ async function performPasswordLogin(config, email, password, store) {
337
439
  });
338
440
  return { arbi, loginResult, config };
339
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
+ }
340
476
  async function selectWorkspace(arbi, workspaceId, wrappedKey, serverSessionKey, signingPrivateKeyBase64) {
341
477
  const signingPrivateKey = client.base64ToBytes(signingPrivateKeyBase64);
342
478
  const ed25519PublicKey = signingPrivateKey.slice(32, 64);
@@ -456,23 +592,7 @@ async function resolveWorkspace(store, workspaceOpt) {
456
592
  }
457
593
 
458
594
  // src/sse.ts
459
- var TOOL_LABELS = {
460
- search_documents: "Searching documents",
461
- get_document_passages: "Reading document",
462
- get_table_of_contents: "Getting table of contents",
463
- view_document_pages: "Viewing document pages",
464
- get_full_document: "Reading full document",
465
- web_search: "Searching the web",
466
- read_url: "Reading web pages",
467
- ask_user: "Asking user",
468
- compaction: "Compacting conversation",
469
- personal_agent: "Running agent",
470
- create_artifact: "Creating artifact",
471
- create_plan: "Creating plan",
472
- save_skill: "Saving skill",
473
- run_code: "Running code"
474
- };
475
- var LIFECYCLE_LABELS = {
595
+ var LIFECYCLE_LABELS_FALLBACK = {
476
596
  evaluation: "Evaluating results",
477
597
  answering: "Writing answer",
478
598
  reviewing: "Reviewing answer",
@@ -481,18 +601,19 @@ var LIFECYCLE_LABELS = {
481
601
  };
482
602
  function formatAgentStepLabel(step) {
483
603
  if (step.focus) return step.focus;
604
+ if (step.label) return step.label;
484
605
  const detail = step.detail;
485
606
  if (step.step === "tool_progress" && detail && detail.length > 0) {
486
- const toolName = detail[0].tool;
487
- const label = toolName && TOOL_LABELS[toolName] || LIFECYCLE_LABELS.tool_progress;
607
+ const label = detail[0].label || LIFECYCLE_LABELS_FALLBACK.tool_progress;
488
608
  const message = detail[0].message;
489
609
  return message ? `${label}: ${message}` : label;
490
610
  }
491
611
  if (step.step) {
492
- return LIFECYCLE_LABELS[step.step] || step.step;
612
+ return LIFECYCLE_LABELS_FALLBACK[step.step] || step.step;
493
613
  }
494
- if (detail && detail.length > 0 && detail[0].tool) {
495
- return TOOL_LABELS[detail[0].tool] || detail[0].tool;
614
+ if (detail && detail.length > 0) {
615
+ if (detail[0].label) return detail[0].label;
616
+ if (detail[0].tool) return detail[0].tool;
496
617
  }
497
618
  return "";
498
619
  }
@@ -2081,8 +2202,6 @@ exports.Arbi = Arbi;
2081
2202
  exports.ArbiApiError = ArbiApiError;
2082
2203
  exports.ArbiError = ArbiError;
2083
2204
  exports.FileConfigStore = FileConfigStore;
2084
- exports.LIFECYCLE_LABELS = LIFECYCLE_LABELS;
2085
- exports.TOOL_LABELS = TOOL_LABELS;
2086
2205
  exports.agentconfig = agentconfig_exports;
2087
2206
  exports.assistant = assistant_exports;
2088
2207
  exports.authenticatedFetch = authenticatedFetch;
@@ -2097,6 +2216,7 @@ exports.conversations = conversations_exports;
2097
2216
  exports.countCitations = countCitations;
2098
2217
  exports.createAuthenticatedClient = createAuthenticatedClient;
2099
2218
  exports.createDocumentWaiter = createDocumentWaiter;
2219
+ exports.deviceFlow = device_flow_exports;
2100
2220
  exports.dm = dm_exports;
2101
2221
  exports.doctags = doctags_exports;
2102
2222
  exports.documents = documents_exports;
@@ -2114,6 +2234,7 @@ exports.getErrorMessage = getErrorMessage;
2114
2234
  exports.health = health_exports;
2115
2235
  exports.parseSSEEvents = parseSSEEvents;
2116
2236
  exports.performPasswordLogin = performPasswordLogin;
2237
+ exports.performSsoDeviceFlowLogin = performSsoDeviceFlowLogin;
2117
2238
  exports.requireData = requireData;
2118
2239
  exports.requireOk = requireOk;
2119
2240
  exports.resolveAuth = resolveAuth;