@arbidocs/sdk 0.3.64 → 0.3.66

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
@@ -3602,7 +3602,7 @@ var FileConfigStore = class {
3602
3602
  sessionFile;
3603
3603
  metadataFile;
3604
3604
  constructor(configDir) {
3605
- const arbiBase = process.env.ARBI_CONFIG_DIR ?? path2__default.default.join(os2__default.default.homedir(), ".arbi");
3605
+ const arbiBase = process.env.ARBI_CONFIG_DIR ?? (fs2__default.default.existsSync(path2__default.default.join(process.cwd(), ".arbi", "credentials.json")) ? path2__default.default.join(process.cwd(), ".arbi") : path2__default.default.join(os2__default.default.homedir(), ".arbi"));
3606
3606
  const arbiId = process.env.ARBI_ID;
3607
3607
  this.configDir = configDir ?? (arbiId ? path2__default.default.join(arbiBase, arbiId) : arbiBase);
3608
3608
  this.configFile = path2__default.default.join(this.configDir, "config.json");
@@ -3700,12 +3700,12 @@ var FileConfigStore = class {
3700
3700
  ...existing,
3701
3701
  signingPrivateKeyBase64: b64encode(data.signingPrivateKey),
3702
3702
  serverSessionKeyBase64: data.serverSessionKey ? b64encode(data.serverSessionKey) : existing.serverSessionKeyBase64,
3703
- // A fresh server session key invalidates any cached
3704
- // workspace-scoped token, so wipe those so the next request
3705
- // re-derives them from the new session.
3703
+ // A fresh server session = a fresh ``session.workspaces``
3704
+ // dict. The previous session's deposits do not transfer, so
3705
+ // wipe the client mirror to force re-deposit on next touch.
3706
3706
  accessToken: void 0,
3707
- sealedWorkspaceKey: void 0,
3708
- tokenTimestamp: void 0
3707
+ tokenTimestamp: void 0,
3708
+ openedWorkspaces: void 0
3709
3709
  });
3710
3710
  }
3711
3711
  };
@@ -3904,6 +3904,7 @@ function sleep(ms) {
3904
3904
  }
3905
3905
 
3906
3906
  // src/auth.ts
3907
+ var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
3907
3908
  function formatWorkspaceChoices(wsList) {
3908
3909
  return wsList.map((ws) => {
3909
3910
  const totalDocs = ws.shared_document_count + ws.private_document_count;
@@ -3934,11 +3935,11 @@ async function createAuthenticatedClient(config, creds, store) {
3934
3935
  });
3935
3936
  store.saveCredentials({
3936
3937
  ...creds,
3938
+ userExtId: loginResult.userExtId ?? creds.userExtId,
3937
3939
  serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
3938
3940
  accessToken: void 0,
3939
- sealedWorkspaceKey: void 0,
3940
- workspaceId: void 0,
3941
- tokenTimestamp: void 0
3941
+ tokenTimestamp: void 0,
3942
+ openedWorkspaces: void 0
3942
3943
  });
3943
3944
  return { arbi, loginResult };
3944
3945
  }
@@ -3948,13 +3949,18 @@ async function performPasswordLogin(config, email, password, store) {
3948
3949
  const loginResult = await arbi.auth.login({ email, password });
3949
3950
  store.saveCredentials({
3950
3951
  email,
3952
+ userExtId: loginResult.userExtId,
3951
3953
  signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
3952
3954
  serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
3953
- // Clear any cached workspace tokens new session key invalidates them
3955
+ // `parent_ext_id` is set for persistent agent accounts (the user is
3956
+ // an Assistant owned by another user). Persisting it makes the CLI's
3957
+ // `arbi listen` work — it requires this to confirm a real agent
3958
+ // identity. Regular users get `null`, which we store as `undefined`.
3959
+ parentExtId: loginResult.parentExtId ?? void 0,
3960
+ // New session = no workspaces deposited yet.
3954
3961
  accessToken: void 0,
3955
- sealedWorkspaceKey: void 0,
3956
- workspaceId: void 0,
3957
- tokenTimestamp: void 0
3962
+ tokenTimestamp: void 0,
3963
+ openedWorkspaces: void 0
3958
3964
  });
3959
3965
  return { arbi, loginResult, config };
3960
3966
  }
@@ -3965,12 +3971,13 @@ async function performSigningKeyLogin(config, email, signingPrivateKeyBase64, st
3965
3971
  const loginResult = await arbi.auth.loginWithKey({ email, signingPrivateKey });
3966
3972
  store.saveCredentials({
3967
3973
  email,
3974
+ userExtId: loginResult.userExtId,
3968
3975
  signingPrivateKeyBase64,
3969
3976
  serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
3977
+ parentExtId: loginResult.parentExtId ?? void 0,
3970
3978
  accessToken: void 0,
3971
- sealedWorkspaceKey: void 0,
3972
- workspaceId: void 0,
3973
- tokenTimestamp: void 0
3979
+ tokenTimestamp: void 0,
3980
+ openedWorkspaces: void 0
3974
3981
  });
3975
3982
  return { arbi, loginResult, config };
3976
3983
  }
@@ -3994,13 +4001,14 @@ async function performSsoDeviceFlowLogin(config, email, password, store, callbac
3994
4001
  const loginResult = await arbi.auth.login({ email, password, ssoToken });
3995
4002
  store.saveCredentials({
3996
4003
  email,
4004
+ userExtId: loginResult.userExtId,
3997
4005
  signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
3998
4006
  serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
4007
+ parentExtId: loginResult.parentExtId ?? void 0,
3999
4008
  ssoToken,
4000
4009
  accessToken: void 0,
4001
- sealedWorkspaceKey: void 0,
4002
- workspaceId: void 0,
4003
- tokenTimestamp: void 0
4010
+ tokenTimestamp: void 0,
4011
+ openedWorkspaces: void 0
4004
4012
  });
4005
4013
  return { arbi, loginResult, config };
4006
4014
  }
@@ -4050,38 +4058,79 @@ async function getRawWorkspaceKey(arbi, workspaceId, signingPrivateKeyBase64) {
4050
4058
  });
4051
4059
  return client.sealedBoxDecrypt(ws.wrapped_key, encryptionKeyPair.secretKey);
4052
4060
  }
4061
+ async function openWorkspaceEntry(arbi, ws, serverSessionKey, signingPrivateKeyBase64) {
4062
+ if (ws.wrapped_key) {
4063
+ const sealedKey = await selectWorkspace(
4064
+ arbi,
4065
+ ws.external_id,
4066
+ ws.wrapped_key,
4067
+ serverSessionKey,
4068
+ signingPrivateKeyBase64
4069
+ );
4070
+ const { error: error2 } = await arbi.fetch.POST("/v1/workspace/{workspace_ext_id}/open", {
4071
+ params: { path: { workspace_ext_id: ws.external_id } },
4072
+ body: { workspace_key: sealedKey }
4073
+ });
4074
+ if (error2) {
4075
+ throw new ArbiError(`Workspace open failed for ${ws.external_id}: ${JSON.stringify(error2)}`);
4076
+ }
4077
+ return sealedKey;
4078
+ }
4079
+ arbi.session.setSelectedWorkspace(ws.external_id);
4080
+ const { error } = await arbi.fetch.POST("/v1/workspace/{workspace_ext_id}/open", {
4081
+ params: { path: { workspace_ext_id: ws.external_id } },
4082
+ body: {}
4083
+ });
4084
+ if (error) {
4085
+ throw new ArbiError(`Workspace open failed for ${ws.external_id}: ${JSON.stringify(error)}`);
4086
+ }
4087
+ return null;
4088
+ }
4053
4089
  async function selectWorkspaceById(arbi, workspaceId, serverSessionKey, signingPrivateKeyBase64) {
4054
4090
  const { data: workspaces, error } = await arbi.fetch.GET("/v1/user/workspaces");
4055
4091
  if (error || !workspaces) {
4056
4092
  throw new ArbiError("Failed to fetch workspaces");
4057
4093
  }
4058
4094
  const ws = workspaces.find((w2) => w2.external_id === workspaceId);
4059
- if (!ws || !ws.wrapped_key) {
4060
- throw new ArbiError(`Workspace ${workspaceId} not found or has no encryption key`);
4095
+ if (!ws) {
4096
+ throw new ArbiError(`Workspace ${workspaceId} not found`);
4061
4097
  }
4062
- const sealedKey = await selectWorkspace(
4063
- arbi,
4064
- ws.external_id,
4065
- ws.wrapped_key,
4066
- serverSessionKey,
4067
- signingPrivateKeyBase64
4068
- );
4069
- await arbi.fetch.POST("/v1/workspace/{workspace_ext_id}/open", {
4070
- params: { path: { workspace_ext_id: ws.external_id } },
4071
- body: { workspace_key: sealedKey }
4072
- });
4073
- return { external_id: ws.external_id, name: ws.name, wrapped_key: ws.wrapped_key };
4098
+ const sealedKey = await openWorkspaceEntry(arbi, ws, serverSessionKey, signingPrivateKeyBase64);
4099
+ return {
4100
+ external_id: ws.external_id,
4101
+ name: ws.name,
4102
+ wrapped_key: ws.wrapped_key ?? null,
4103
+ sealed_key: sealedKey
4104
+ };
4105
+ }
4106
+ function isSessionAlive(creds) {
4107
+ return !!(creds.accessToken && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
4108
+ }
4109
+ async function buildAuthFromCache(config, creds, store) {
4110
+ const arbi = client.createArbiClient(buildClientOptions(config, store, creds.email));
4111
+ await arbi.crypto.initSodium();
4112
+ arbi.session.setUser(creds.email, creds.userExtId);
4113
+ arbi.session.setAccessToken(creds.accessToken);
4114
+ const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
4115
+ const serverSessionKey = client.base64ToBytes(creds.serverSessionKeyBase64);
4116
+ const loginResult = {
4117
+ accessToken: creds.accessToken,
4118
+ userExtId: creds.userExtId,
4119
+ signingPrivateKey,
4120
+ serverSessionKey
4121
+ };
4122
+ return { arbi, loginResult };
4074
4123
  }
4075
4124
  async function resolveAuth(store) {
4076
4125
  const config = store.requireConfig();
4077
4126
  const creds = store.requireCredentials();
4127
+ if (isSessionAlive(creds)) {
4128
+ const { arbi: arbi2, loginResult: loginResult2 } = await buildAuthFromCache(config, creds, store);
4129
+ return { arbi: arbi2, loginResult: loginResult2, config };
4130
+ }
4078
4131
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
4079
4132
  return { arbi, loginResult, config };
4080
4133
  }
4081
- var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
4082
- function isCachedTokenValid(creds, workspaceId) {
4083
- return !!(creds.accessToken && creds.sealedWorkspaceKey && creds.workspaceId === workspaceId && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
4084
- }
4085
4134
  async function resolveWorkspace(store, workspaceOpt) {
4086
4135
  const config = store.requireConfig();
4087
4136
  const creds = store.requireCredentials();
@@ -4089,53 +4138,52 @@ async function resolveWorkspace(store, workspaceOpt) {
4089
4138
  if (!workspaceId) {
4090
4139
  throw new ArbiError("No workspace selected. Run: arbi workspace select <id>");
4091
4140
  }
4092
- if (isCachedTokenValid(creds, workspaceId)) {
4093
- const arbi2 = client.createArbiClient(buildClientOptions(config, store, creds.email));
4094
- await arbi2.crypto.initSodium();
4095
- arbi2.session.setUser(creds.email);
4141
+ if (isSessionAlive(creds)) {
4142
+ const { arbi: arbi2, loginResult: loginResult2 } = await buildAuthFromCache(config, creds, store);
4096
4143
  arbi2.session.setSelectedWorkspace(workspaceId);
4097
- arbi2.session.setAccessToken(creds.accessToken);
4098
- const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
4099
- const serverSessionKey = client.base64ToBytes(creds.serverSessionKeyBase64);
4100
- const loginResult2 = {
4101
- accessToken: creds.accessToken,
4102
- signingPrivateKey,
4103
- serverSessionKey
4104
- };
4144
+ await selectWorkspaceById(
4145
+ arbi2,
4146
+ workspaceId,
4147
+ loginResult2.serverSessionKey,
4148
+ creds.signingPrivateKeyBase64
4149
+ );
4150
+ const nextOpened = Array.from(/* @__PURE__ */ new Set([...creds.openedWorkspaces ?? [], workspaceId]));
4151
+ store.saveCredentials({
4152
+ ...store.requireCredentials(),
4153
+ openedWorkspaces: nextOpened
4154
+ });
4105
4155
  return {
4106
4156
  arbi: arbi2,
4107
4157
  loginResult: loginResult2,
4108
4158
  config,
4109
4159
  workspaceId,
4110
- accessToken: creds.accessToken,
4111
- sealedWorkspaceKey: creds.sealedWorkspaceKey
4160
+ accessToken: creds.accessToken
4112
4161
  };
4113
4162
  }
4114
4163
  const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
4115
- const wsInfo = await selectWorkspaceById(
4164
+ await selectWorkspaceById(
4116
4165
  arbi,
4117
4166
  workspaceId,
4118
4167
  loginResult.serverSessionKey,
4119
4168
  creds.signingPrivateKeyBase64
4120
4169
  );
4121
4170
  const accessToken = arbi.session.getState().accessToken;
4122
- const sealedWorkspaceKey = await generateEncryptedWorkspaceKey(
4123
- arbi,
4124
- wsInfo.wrapped_key,
4125
- loginResult.serverSessionKey,
4126
- creds.signingPrivateKeyBase64
4127
- );
4128
4171
  if (!accessToken) {
4129
4172
  throw new ArbiError("Authentication error \u2014 missing token");
4130
4173
  }
4131
4174
  store.saveCredentials({
4132
4175
  ...store.requireCredentials(),
4133
4176
  accessToken,
4134
- sealedWorkspaceKey,
4135
- workspaceId,
4136
- tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString()
4177
+ tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
4178
+ openedWorkspaces: [workspaceId]
4137
4179
  });
4138
- return { arbi, loginResult, config, workspaceId, accessToken, sealedWorkspaceKey };
4180
+ return {
4181
+ arbi,
4182
+ loginResult,
4183
+ config,
4184
+ workspaceId,
4185
+ accessToken
4186
+ };
4139
4187
  }
4140
4188
 
4141
4189
  // src/sse.ts