@arbidocs/sdk 0.4.4 → 0.4.6

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/browser.cjs CHANGED
@@ -3557,14 +3557,15 @@ async function extractErrorMessage(res) {
3557
3557
  return `Request failed: ${res.status} ${res.statusText}`;
3558
3558
  }
3559
3559
  async function authenticatedFetch(options) {
3560
- const { baseUrl, accessToken, path, method, body, headers } = options;
3560
+ const { baseUrl, accessToken, path, method, body, headers, signal } = options;
3561
3561
  const res = await fetch(`${baseUrl}${path}`, {
3562
3562
  method: method ?? (body ? "POST" : "GET"),
3563
3563
  headers: {
3564
3564
  Authorization: `Bearer ${accessToken}`,
3565
3565
  ...headers
3566
3566
  },
3567
- body
3567
+ body,
3568
+ signal
3568
3569
  });
3569
3570
  if (!res.ok) {
3570
3571
  const message = await extractErrorMessage(res);
@@ -4479,6 +4480,7 @@ async function uploadFile(auth, fileData, fileName, options) {
4479
4480
  if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
4480
4481
  if (options?.configExtId) params.set("config_ext_id", options.configExtId);
4481
4482
  if (options?.wpType) params.set("wp_type", options.wpType);
4483
+ if (options?.parentExtId) params.set("parent_ext_id", options.parentExtId);
4482
4484
  const qs = params.toString();
4483
4485
  const res = await authenticatedFetch({
4484
4486
  ...auth,
@@ -4495,6 +4497,7 @@ async function uploadFiles(auth, files, options) {
4495
4497
  if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
4496
4498
  if (options?.configExtId) params.set("config_ext_id", options.configExtId);
4497
4499
  if (options?.wpType) params.set("wp_type", options.wpType);
4500
+ if (options?.parentExtId) params.set("parent_ext_id", options.parentExtId);
4498
4501
  const qs = params.toString();
4499
4502
  const res = await authenticatedFetch({
4500
4503
  ...auth,
@@ -4925,7 +4928,7 @@ async function retrieve(options) {
4925
4928
  return data;
4926
4929
  }
4927
4930
  async function queryAssistant(options) {
4928
- const { workspaceId, question, docIds, previousResponseId, model, ...auth } = options;
4931
+ const { workspaceId, question, docIds, previousResponseId, model, signal, ...auth } = options;
4929
4932
  const body = {
4930
4933
  input: question,
4931
4934
  workspace_ext_id: workspaceId,
@@ -4944,7 +4947,8 @@ async function queryAssistant(options) {
4944
4947
  path: "/v1/assistant/query",
4945
4948
  method: "POST",
4946
4949
  body: JSON.stringify(body),
4947
- headers: { "Content-Type": "application/json" }
4950
+ headers: { "Content-Type": "application/json" },
4951
+ signal
4948
4952
  });
4949
4953
  }
4950
4954
  async function respondToAgent(arbi, assistantMessageExtId, answer) {
@@ -5480,9 +5484,78 @@ async function getFileContent(auth, fileId) {
5480
5484
  }
5481
5485
 
5482
5486
  // src/operations/sessions.ts
5487
+ var sessions_exports = {};
5488
+ __export(sessions_exports, {
5489
+ currentSessionId: () => currentSessionId,
5490
+ listSessions: () => listSessions,
5491
+ logoutAllSessions: () => logoutAllSessions,
5492
+ terminateSession: () => terminateSession
5493
+ });
5483
5494
  async function listSessions(arbi) {
5484
5495
  return requireData(await arbi.fetch.GET("/v1/user/sessions"), "Failed to fetch sessions");
5485
5496
  }
5497
+ async function terminateSession(arbi, sessionId) {
5498
+ requireOk(
5499
+ await arbi.fetch.PATCH("/v1/user/sessions/{session_id}", {
5500
+ params: { path: { session_id: sessionId } },
5501
+ body: { ttl: 0 }
5502
+ }),
5503
+ "Failed to end session"
5504
+ );
5505
+ }
5506
+ async function logoutAllSessions(arbi) {
5507
+ requireOk(
5508
+ await arbi.fetch.POST("/v1/user/logout", { params: { query: { all_sessions: true } } }),
5509
+ "Failed to sign out of all sessions"
5510
+ );
5511
+ }
5512
+ function currentSessionId(accessToken) {
5513
+ const payload = accessToken?.split(".")[1];
5514
+ if (!payload) return null;
5515
+ try {
5516
+ const claims = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
5517
+ return typeof claims.sk === "string" ? claims.sk.slice(0, 12) : null;
5518
+ } catch {
5519
+ return null;
5520
+ }
5521
+ }
5522
+
5523
+ // src/operations/profile.ts
5524
+ var profile_exports = {};
5525
+ __export(profile_exports, {
5526
+ updateProfile: () => updateProfile
5527
+ });
5528
+ async function updateProfile(arbi, body) {
5529
+ return requireData(
5530
+ await arbi.fetch.PATCH("/v1/user/profile", { body }),
5531
+ "Failed to update profile"
5532
+ );
5533
+ }
5534
+
5535
+ // src/operations/account.ts
5536
+ var account_exports = {};
5537
+ __export(account_exports, {
5538
+ confirmEmailChange: () => confirmEmailChange,
5539
+ requestDeletion: () => requestDeletion,
5540
+ requestEmailChange: () => requestEmailChange
5541
+ });
5542
+ async function requestEmailChange(arbi, newEmail) {
5543
+ requireOk(
5544
+ await arbi.fetch.POST("/v1/user/request-email-change", { body: { new_email: newEmail } }),
5545
+ "Failed to send the verification code"
5546
+ );
5547
+ }
5548
+ async function confirmEmailChange(arbi, newEmail, verificationCode) {
5549
+ return requireData(
5550
+ await arbi.fetch.POST("/v1/user/confirm-email-change", {
5551
+ body: { new_email: newEmail, verification_code: verificationCode }
5552
+ }),
5553
+ "Invalid or expired verification code"
5554
+ );
5555
+ }
5556
+ async function requestDeletion(arbi) {
5557
+ return requireData(await arbi.fetch.POST("/v1/user/request-deletion"), "Failed to delete account");
5558
+ }
5486
5559
 
5487
5560
  // src/operations/projects.ts
5488
5561
  var projects_exports = {};
@@ -5692,6 +5765,8 @@ function tokenExpiresWithin(token, seconds) {
5692
5765
  var Arbi = class {
5693
5766
  client = null;
5694
5767
  loginResult = null;
5768
+ userProfile = null;
5769
+ profileListeners = /* @__PURE__ */ new Set();
5695
5770
  currentWorkspaceId = null;
5696
5771
  dmCryptoContext = null;
5697
5772
  sessionExpiredListeners = /* @__PURE__ */ new Set();
@@ -5740,6 +5815,7 @@ var Arbi = class {
5740
5815
  async login(email, password) {
5741
5816
  const client = await this.init();
5742
5817
  this.loginResult = await client.auth.login({ email, password });
5818
+ this.userProfile = this.loginResult.profile ?? null;
5743
5819
  }
5744
5820
  /**
5745
5821
  * Recover a session using a stored signing private key (base64).
@@ -5749,6 +5825,7 @@ var Arbi = class {
5749
5825
  const client = await this.init();
5750
5826
  const signingPrivateKey = client.crypto.base64ToBytes(signingPrivateKeyBase64);
5751
5827
  this.loginResult = await client.auth.loginWithKey({ email, signingPrivateKey });
5828
+ this.userProfile = this.loginResult.profile ?? null;
5752
5829
  }
5753
5830
  /** A workspace's key, sealed to the caller's own session (what `/open` and `POST /workspace/users` take). */
5754
5831
  async sealWorkspaceKey(workspaceId) {
@@ -5808,6 +5885,7 @@ var Arbi = class {
5808
5885
  this.loginResult.serverSessionKey.fill(0);
5809
5886
  }
5810
5887
  this.loginResult = null;
5888
+ this.userProfile = null;
5811
5889
  this.currentWorkspaceId = null;
5812
5890
  this.dmCryptoContext = null;
5813
5891
  this.client?.session.clear();
@@ -5838,6 +5916,10 @@ var Arbi = class {
5838
5916
  return run();
5839
5917
  }
5840
5918
  }
5919
+ setUserProfile(profile) {
5920
+ this.userProfile = profile;
5921
+ for (const listener of [...this.profileListeners]) listener(profile);
5922
+ }
5841
5923
  /** Log out and clear internal state. */
5842
5924
  async logout() {
5843
5925
  if (this.client) {
@@ -5848,6 +5930,7 @@ var Arbi = class {
5848
5930
  this.loginResult.serverSessionKey.fill(0);
5849
5931
  }
5850
5932
  this.loginResult = null;
5933
+ this.userProfile = null;
5851
5934
  this.currentWorkspaceId = null;
5852
5935
  this.dmCryptoContext = null;
5853
5936
  }
@@ -5921,9 +6004,76 @@ var Arbi = class {
5921
6004
  const state = this.requireClient().session.getState();
5922
6005
  return state.userExtId ? { externalId: state.userExtId, email: state.userEmail ?? null } : null;
5923
6006
  },
6007
+ /**
6008
+ * The signed-in user's profile (name, picture, email, SSO flag) as the login response
6009
+ * carried it, kept current by `profile.update` and `account.confirmEmailChange`. `null` when
6010
+ * not logged in or when the session was restored without a login (then read the workspace
6011
+ * roster instead).
6012
+ */
6013
+ profile: () => this.userProfile,
6014
+ /** Be told when the profile changes (name, picture or email saved). Returns the unsubscribe. */
6015
+ onProfileChange: (listener) => {
6016
+ this.profileListeners.add(listener);
6017
+ return () => this.profileListeners.delete(listener);
6018
+ },
5924
6019
  /** Today's deployment-wide credit spend for the current user. */
5925
6020
  usageToday: () => getUserUsageToday(this.requireClient())
5926
6021
  };
6022
+ // ── Profile ───────────────────────────────────────────────────────────
6023
+ profile = {
6024
+ /** Update the display profile; pass only what changes (`''` clears family name or picture). */
6025
+ update: async (body) => {
6026
+ const updated = await updateProfile(this.requireClient(), body);
6027
+ if (this.userProfile) {
6028
+ this.setUserProfile({
6029
+ ...this.userProfile,
6030
+ givenName: updated.given_name ?? this.userProfile.givenName,
6031
+ familyName: updated.family_name ?? null,
6032
+ picture: updated.picture ?? null
6033
+ });
6034
+ }
6035
+ return updated;
6036
+ }
6037
+ };
6038
+ // ── Account ───────────────────────────────────────────────────────────
6039
+ account = {
6040
+ /** Send a verification code to the new address (not for SSO accounts). */
6041
+ requestEmailChange: (newEmail) => requestEmailChange(this.requireClient(), newEmail),
6042
+ /** Confirm the email change with the emailed code; the session and profile follow. */
6043
+ confirmEmailChange: async (newEmail, verificationCode) => {
6044
+ const client = this.requireClient();
6045
+ const result = await confirmEmailChange(client, newEmail, verificationCode);
6046
+ client.session.setUser(result.email, client.session.getState().userExtId ?? void 0);
6047
+ if (this.userProfile) this.setUserProfile({ ...this.userProfile, email: result.email });
6048
+ return result;
6049
+ },
6050
+ /**
6051
+ * Change (or, after recovery, set) the master password: re-keys the workspaces and direct
6052
+ * messages. Omit `currentPassword` in recovery mode. Resolves with the new signing key, whose
6053
+ * recovery file the user should save.
6054
+ */
6055
+ changePassword: async (params) => {
6056
+ const client$1 = this.requireClient();
6057
+ const lr2 = this.requireLogin();
6058
+ const email = client$1.session.getState().userEmail ?? this.userProfile?.email;
6059
+ if (!email) throw new ArbiError("The account email is unknown; sign in again.");
6060
+ const result = await client.changePasswordWithRekey(
6061
+ { ...params, email, userExtId: lr2.userExtId },
6062
+ {
6063
+ fetchClient: client$1.fetch,
6064
+ deploymentDomain: this.options.deploymentDomain ?? new URL(this.options.url).hostname
6065
+ }
6066
+ );
6067
+ this.loginResult = { ...lr2, signingPrivateKey: result.newSigningPrivateKey };
6068
+ return result;
6069
+ },
6070
+ /** Close the account (soft delete) and sign out. Refused while a subscription is active. */
6071
+ requestDeletion: async () => {
6072
+ const result = await requestDeletion(this.requireClient());
6073
+ await this.logout();
6074
+ return result;
6075
+ }
6076
+ };
5927
6077
  // ── Workspaces ────────────────────────────────────────────────────────
5928
6078
  workspaces = {
5929
6079
  list: () => listWorkspaces(this.requireClient()),
@@ -6029,12 +6179,7 @@ var Arbi = class {
6029
6179
  uploadFile: (fileData, fileName, options) => {
6030
6180
  this.requireWorkspace();
6031
6181
  return this.recovering(
6032
- () => uploadFile(
6033
- this.getAuthHeaders(),
6034
- fileData,
6035
- fileName,
6036
- options?.folder ? { folder: options.folder } : void 0
6037
- )
6182
+ () => uploadFile(this.getAuthHeaders(), fileData, fileName, options)
6038
6183
  );
6039
6184
  },
6040
6185
  download: (docId) => this.recovering(() => downloadDocument(this.getAuthHeaders(), docId)),
@@ -6066,7 +6211,8 @@ var Arbi = class {
6066
6211
  question,
6067
6212
  docIds: options.docIds,
6068
6213
  previousResponseId: options.previousResponseId,
6069
- model: options.model
6214
+ model: options.model,
6215
+ signal: options.signal
6070
6216
  })
6071
6217
  );
6072
6218
  const callbacks = {
@@ -6314,7 +6460,16 @@ var Arbi = class {
6314
6460
  };
6315
6461
  // ── Sessions ────────────────────────────────────────────────────────
6316
6462
  sessions = {
6317
- list: () => listSessions(this.requireClient())
6463
+ list: () => listSessions(this.requireClient()),
6464
+ /** The id this device's session is listed under in `list()`; `null` when unknown. */
6465
+ currentId: () => currentSessionId(this.client?.session.getState().accessToken),
6466
+ /** End one of your own sessions now (not the one this device is on: that is `logout`). */
6467
+ terminate: (sessionId) => terminateSession(this.requireClient(), sessionId),
6468
+ /** Sign out of every device on the server, then clear this instance. */
6469
+ logoutAll: async () => {
6470
+ await logoutAllSessions(this.requireClient());
6471
+ await this.logout();
6472
+ }
6318
6473
  };
6319
6474
  // ── Internal guards ───────────────────────────────────────────────────
6320
6475
  requireClient() {
@@ -6404,6 +6559,7 @@ exports.Arbi = Arbi;
6404
6559
  exports.ArbiApiError = ArbiApiError;
6405
6560
  exports.ArbiError = ArbiError;
6406
6561
  exports.WebSocketAuthError = WebSocketAuthError;
6562
+ exports.account = account_exports;
6407
6563
  exports.agentconfig = agentconfig_exports;
6408
6564
  exports.assistant = assistant_exports;
6409
6565
  exports.authenticatedFetch = authenticatedFetch;
@@ -6435,6 +6591,7 @@ exports.parseSSEEvents = parseSSEEvents;
6435
6591
  exports.parseSlashCommand = parseSlashCommand;
6436
6592
  exports.parseSlashTokenInProgress = parseSlashTokenInProgress;
6437
6593
  exports.performPasswordLogin = performPasswordLogin;
6594
+ exports.profile = profile_exports;
6438
6595
  exports.projects = projects_exports;
6439
6596
  exports.requireData = requireData;
6440
6597
  exports.requireOk = requireOk;
@@ -6444,6 +6601,7 @@ exports.resolveWorkspace = resolveWorkspace;
6444
6601
  exports.responses = responses_exports;
6445
6602
  exports.selectWorkspace = selectWorkspace;
6446
6603
  exports.selectWorkspaceById = selectWorkspaceById;
6604
+ exports.sessions = sessions_exports;
6447
6605
  exports.settings = settings_exports;
6448
6606
  exports.streamSSE = streamSSE;
6449
6607
  exports.stripCitationMarkdown = stripCitationMarkdown;