@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/{browser-Bs2joT0Y.d.cts → browser-6H2O896Y.d.cts} +55 -17
- package/dist/{browser-Bs2joT0Y.d.ts → browser-6H2O896Y.d.ts} +55 -17
- package/dist/browser.cjs +96 -60
- 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 +96 -60
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +110 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +110 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -84,16 +84,40 @@ interface CliConfig {
|
|
|
84
84
|
}
|
|
85
85
|
interface CliCredentials {
|
|
86
86
|
email: string;
|
|
87
|
+
/** User's external ID (``usr-*`` or ``agt-*``). Needed by features
|
|
88
|
+
* that key on user identity (DM crypto, contact resolution, etc.).
|
|
89
|
+
* Set on every login from the API's response; required by
|
|
90
|
+
* ``buildAuthFromCache`` to restore the session client-side
|
|
91
|
+
* without a server round-trip. */
|
|
92
|
+
userExtId?: string;
|
|
87
93
|
signingPrivateKeyBase64: string;
|
|
88
94
|
serverSessionKeyBase64: string;
|
|
89
|
-
/** Cached access token
|
|
95
|
+
/** Cached access token for the live server session. Valid as long as
|
|
96
|
+
* ``tokenTimestamp`` is within ``TOKEN_MAX_AGE_MS``. */
|
|
90
97
|
accessToken?: string;
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
/** Workspace ID the cached token was issued for */
|
|
94
|
-
workspaceId?: string;
|
|
95
|
-
/** ISO timestamp when the token was cached */
|
|
98
|
+
/** ISO timestamp when ``accessToken`` was issued. Used for client-side
|
|
99
|
+
* TTL — the server enforces JWT exp independently. */
|
|
96
100
|
tokenTimestamp?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Workspaces whose keys are already deposited on the current server
|
|
103
|
+
* session — i.e. the keys of ``session.workspaces`` in the backend's
|
|
104
|
+
* Redis entry. Client-side mirror so we can skip a redundant `/open`
|
|
105
|
+
* round-trip when we've already deposited a workspace's key during
|
|
106
|
+
* this session's lifetime.
|
|
107
|
+
*
|
|
108
|
+
* Both grant types end up in this set:
|
|
109
|
+
* - **Permanent** — first `/open` for the workspace POSTs the
|
|
110
|
+
* wrapped_key and the server adds it to ``session.workspaces``.
|
|
111
|
+
* - **Session-only** (PA agents, temporary shares) — the backend
|
|
112
|
+
* pre-deposits via ``store_workspace_key_for_session_pubkey``,
|
|
113
|
+
* then the client's `/open` is just a metadata fetch.
|
|
114
|
+
*
|
|
115
|
+
* Cleared on every event that invalidates the session: fresh login,
|
|
116
|
+
* auto-relogin on 401, logout. The new session starts with an empty
|
|
117
|
+
* ``session.workspaces`` server-side, so this client mirror must
|
|
118
|
+
* start empty too.
|
|
119
|
+
*/
|
|
120
|
+
openedWorkspaces?: string[];
|
|
97
121
|
/** Auth0 SSO token — needed for session recovery of SSO users */
|
|
98
122
|
ssoToken?: string;
|
|
99
123
|
/** Parent user ext_id — set for persistent agent accounts */
|
|
@@ -152,7 +176,6 @@ interface AuthContext {
|
|
|
152
176
|
interface WorkspaceContext extends AuthContext {
|
|
153
177
|
workspaceId: string;
|
|
154
178
|
accessToken: string;
|
|
155
|
-
sealedWorkspaceKey: string;
|
|
156
179
|
}
|
|
157
180
|
/** Minimal workspace shape required by formatWorkspaceChoices. */
|
|
158
181
|
interface WorkspaceForChoice {
|
|
@@ -232,23 +255,40 @@ declare function generateNewWorkspaceKey(arbi: ArbiClient, serverSessionKey: Uin
|
|
|
232
255
|
* never log, persist, or transmit it over the wire.
|
|
233
256
|
*/
|
|
234
257
|
declare function getRawWorkspaceKey(arbi: ArbiClient, workspaceId: string, signingPrivateKeyBase64: string): Promise<Uint8Array>;
|
|
235
|
-
/**
|
|
236
|
-
* Find a workspace by ID in the user's workspace list, select it, and return workspace info.
|
|
237
|
-
*/
|
|
238
258
|
declare function selectWorkspaceById(arbi: ArbiClient, workspaceId: string, serverSessionKey: Uint8Array, signingPrivateKeyBase64: string): Promise<{
|
|
239
259
|
external_id: string;
|
|
240
260
|
name: string;
|
|
241
|
-
wrapped_key: string;
|
|
261
|
+
wrapped_key: string | null;
|
|
262
|
+
/** The session-sealed workspace key — `null` for session-only grants */
|
|
263
|
+
sealed_key: string | null;
|
|
242
264
|
}>;
|
|
243
265
|
/**
|
|
244
266
|
* Authenticate and return the SDK client + config.
|
|
245
|
-
*
|
|
267
|
+
*
|
|
268
|
+
* Fast path: reuse the live server session via cached accessToken.
|
|
269
|
+
* Slow path: fresh ``loginWithKey`` (mints a new session, clears
|
|
270
|
+
* ``openedWorkspaces``).
|
|
246
271
|
*/
|
|
247
272
|
declare function resolveAuth(store: ConfigStore): Promise<AuthContext>;
|
|
248
273
|
/**
|
|
249
|
-
* Authenticate,
|
|
250
|
-
*
|
|
251
|
-
*
|
|
274
|
+
* Authenticate, ensure the workspace's key is deposited on the current
|
|
275
|
+
* server session, and return everything callers need.
|
|
276
|
+
*
|
|
277
|
+
* Decisions are independent:
|
|
278
|
+
* 1. **Session aliveness** — reuse the live session if its accessToken
|
|
279
|
+
* is within TTL; otherwise fresh login. (Fresh login = new session
|
|
280
|
+
* = ``openedWorkspaces`` reset to ``[]``.)
|
|
281
|
+
* 2. **Workspace deposit** — if ``workspaceId`` already appears in
|
|
282
|
+
* ``creds.openedWorkspaces``, the server's session already holds
|
|
283
|
+
* its key; no ``/open`` needed. Otherwise call ``/open`` once and
|
|
284
|
+
* append to the set.
|
|
285
|
+
*
|
|
286
|
+
* Splitting these matters: previously the cache was gated on
|
|
287
|
+
* ``creds.workspaceId === workspaceId``, so any switch between
|
|
288
|
+
* workspaces forced a re-login. That orphaned PA-agent session
|
|
289
|
+
* deposits and broke ``arbi docs`` for them. Now switching workspaces
|
|
290
|
+
* within a live session just adds another entry to
|
|
291
|
+
* ``openedWorkspaces`` — no re-login, no orphaned deposits.
|
|
252
292
|
*/
|
|
253
293
|
declare function resolveWorkspace(store: ConfigStore, workspaceOpt?: string): Promise<WorkspaceContext>;
|
|
254
294
|
|
|
@@ -2489,7 +2529,6 @@ declare class Arbi {
|
|
|
2489
2529
|
muted_users: string[];
|
|
2490
2530
|
premium_model?: string | null | undefined;
|
|
2491
2531
|
picture?: string | null | undefined;
|
|
2492
|
-
deletion_requested_at?: string | null | undefined;
|
|
2493
2532
|
}>;
|
|
2494
2533
|
update: (body: UserSettingsUpdate$1) => Promise<void>;
|
|
2495
2534
|
};
|
|
@@ -3817,7 +3856,6 @@ declare function getSettings(arbi: ArbiClient): Promise<{
|
|
|
3817
3856
|
muted_users: string[];
|
|
3818
3857
|
premium_model?: string | null | undefined;
|
|
3819
3858
|
picture?: string | null | undefined;
|
|
3820
|
-
deletion_requested_at?: string | null | undefined;
|
|
3821
3859
|
}>;
|
|
3822
3860
|
declare function updateSettings(arbi: ArbiClient, body: UserSettingsUpdate): Promise<void>;
|
|
3823
3861
|
|
|
@@ -84,16 +84,40 @@ interface CliConfig {
|
|
|
84
84
|
}
|
|
85
85
|
interface CliCredentials {
|
|
86
86
|
email: string;
|
|
87
|
+
/** User's external ID (``usr-*`` or ``agt-*``). Needed by features
|
|
88
|
+
* that key on user identity (DM crypto, contact resolution, etc.).
|
|
89
|
+
* Set on every login from the API's response; required by
|
|
90
|
+
* ``buildAuthFromCache`` to restore the session client-side
|
|
91
|
+
* without a server round-trip. */
|
|
92
|
+
userExtId?: string;
|
|
87
93
|
signingPrivateKeyBase64: string;
|
|
88
94
|
serverSessionKeyBase64: string;
|
|
89
|
-
/** Cached access token
|
|
95
|
+
/** Cached access token for the live server session. Valid as long as
|
|
96
|
+
* ``tokenTimestamp`` is within ``TOKEN_MAX_AGE_MS``. */
|
|
90
97
|
accessToken?: string;
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
/** Workspace ID the cached token was issued for */
|
|
94
|
-
workspaceId?: string;
|
|
95
|
-
/** ISO timestamp when the token was cached */
|
|
98
|
+
/** ISO timestamp when ``accessToken`` was issued. Used for client-side
|
|
99
|
+
* TTL — the server enforces JWT exp independently. */
|
|
96
100
|
tokenTimestamp?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Workspaces whose keys are already deposited on the current server
|
|
103
|
+
* session — i.e. the keys of ``session.workspaces`` in the backend's
|
|
104
|
+
* Redis entry. Client-side mirror so we can skip a redundant `/open`
|
|
105
|
+
* round-trip when we've already deposited a workspace's key during
|
|
106
|
+
* this session's lifetime.
|
|
107
|
+
*
|
|
108
|
+
* Both grant types end up in this set:
|
|
109
|
+
* - **Permanent** — first `/open` for the workspace POSTs the
|
|
110
|
+
* wrapped_key and the server adds it to ``session.workspaces``.
|
|
111
|
+
* - **Session-only** (PA agents, temporary shares) — the backend
|
|
112
|
+
* pre-deposits via ``store_workspace_key_for_session_pubkey``,
|
|
113
|
+
* then the client's `/open` is just a metadata fetch.
|
|
114
|
+
*
|
|
115
|
+
* Cleared on every event that invalidates the session: fresh login,
|
|
116
|
+
* auto-relogin on 401, logout. The new session starts with an empty
|
|
117
|
+
* ``session.workspaces`` server-side, so this client mirror must
|
|
118
|
+
* start empty too.
|
|
119
|
+
*/
|
|
120
|
+
openedWorkspaces?: string[];
|
|
97
121
|
/** Auth0 SSO token — needed for session recovery of SSO users */
|
|
98
122
|
ssoToken?: string;
|
|
99
123
|
/** Parent user ext_id — set for persistent agent accounts */
|
|
@@ -152,7 +176,6 @@ interface AuthContext {
|
|
|
152
176
|
interface WorkspaceContext extends AuthContext {
|
|
153
177
|
workspaceId: string;
|
|
154
178
|
accessToken: string;
|
|
155
|
-
sealedWorkspaceKey: string;
|
|
156
179
|
}
|
|
157
180
|
/** Minimal workspace shape required by formatWorkspaceChoices. */
|
|
158
181
|
interface WorkspaceForChoice {
|
|
@@ -232,23 +255,40 @@ declare function generateNewWorkspaceKey(arbi: ArbiClient, serverSessionKey: Uin
|
|
|
232
255
|
* never log, persist, or transmit it over the wire.
|
|
233
256
|
*/
|
|
234
257
|
declare function getRawWorkspaceKey(arbi: ArbiClient, workspaceId: string, signingPrivateKeyBase64: string): Promise<Uint8Array>;
|
|
235
|
-
/**
|
|
236
|
-
* Find a workspace by ID in the user's workspace list, select it, and return workspace info.
|
|
237
|
-
*/
|
|
238
258
|
declare function selectWorkspaceById(arbi: ArbiClient, workspaceId: string, serverSessionKey: Uint8Array, signingPrivateKeyBase64: string): Promise<{
|
|
239
259
|
external_id: string;
|
|
240
260
|
name: string;
|
|
241
|
-
wrapped_key: string;
|
|
261
|
+
wrapped_key: string | null;
|
|
262
|
+
/** The session-sealed workspace key — `null` for session-only grants */
|
|
263
|
+
sealed_key: string | null;
|
|
242
264
|
}>;
|
|
243
265
|
/**
|
|
244
266
|
* Authenticate and return the SDK client + config.
|
|
245
|
-
*
|
|
267
|
+
*
|
|
268
|
+
* Fast path: reuse the live server session via cached accessToken.
|
|
269
|
+
* Slow path: fresh ``loginWithKey`` (mints a new session, clears
|
|
270
|
+
* ``openedWorkspaces``).
|
|
246
271
|
*/
|
|
247
272
|
declare function resolveAuth(store: ConfigStore): Promise<AuthContext>;
|
|
248
273
|
/**
|
|
249
|
-
* Authenticate,
|
|
250
|
-
*
|
|
251
|
-
*
|
|
274
|
+
* Authenticate, ensure the workspace's key is deposited on the current
|
|
275
|
+
* server session, and return everything callers need.
|
|
276
|
+
*
|
|
277
|
+
* Decisions are independent:
|
|
278
|
+
* 1. **Session aliveness** — reuse the live session if its accessToken
|
|
279
|
+
* is within TTL; otherwise fresh login. (Fresh login = new session
|
|
280
|
+
* = ``openedWorkspaces`` reset to ``[]``.)
|
|
281
|
+
* 2. **Workspace deposit** — if ``workspaceId`` already appears in
|
|
282
|
+
* ``creds.openedWorkspaces``, the server's session already holds
|
|
283
|
+
* its key; no ``/open`` needed. Otherwise call ``/open`` once and
|
|
284
|
+
* append to the set.
|
|
285
|
+
*
|
|
286
|
+
* Splitting these matters: previously the cache was gated on
|
|
287
|
+
* ``creds.workspaceId === workspaceId``, so any switch between
|
|
288
|
+
* workspaces forced a re-login. That orphaned PA-agent session
|
|
289
|
+
* deposits and broke ``arbi docs`` for them. Now switching workspaces
|
|
290
|
+
* within a live session just adds another entry to
|
|
291
|
+
* ``openedWorkspaces`` — no re-login, no orphaned deposits.
|
|
252
292
|
*/
|
|
253
293
|
declare function resolveWorkspace(store: ConfigStore, workspaceOpt?: string): Promise<WorkspaceContext>;
|
|
254
294
|
|
|
@@ -2489,7 +2529,6 @@ declare class Arbi {
|
|
|
2489
2529
|
muted_users: string[];
|
|
2490
2530
|
premium_model?: string | null | undefined;
|
|
2491
2531
|
picture?: string | null | undefined;
|
|
2492
|
-
deletion_requested_at?: string | null | undefined;
|
|
2493
2532
|
}>;
|
|
2494
2533
|
update: (body: UserSettingsUpdate$1) => Promise<void>;
|
|
2495
2534
|
};
|
|
@@ -3817,7 +3856,6 @@ declare function getSettings(arbi: ArbiClient): Promise<{
|
|
|
3817
3856
|
muted_users: string[];
|
|
3818
3857
|
premium_model?: string | null | undefined;
|
|
3819
3858
|
picture?: string | null | undefined;
|
|
3820
|
-
deletion_requested_at?: string | null | undefined;
|
|
3821
3859
|
}>;
|
|
3822
3860
|
declare function updateSettings(arbi: ArbiClient, body: UserSettingsUpdate): Promise<void>;
|
|
3823
3861
|
|
package/dist/browser.cjs
CHANGED
|
@@ -3568,6 +3568,7 @@ async function authenticatedFetch(options) {
|
|
|
3568
3568
|
}
|
|
3569
3569
|
|
|
3570
3570
|
// src/auth.ts
|
|
3571
|
+
var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
|
|
3571
3572
|
function formatWorkspaceChoices(wsList) {
|
|
3572
3573
|
return wsList.map((ws) => {
|
|
3573
3574
|
const totalDocs = ws.shared_document_count + ws.private_document_count;
|
|
@@ -3598,11 +3599,11 @@ async function createAuthenticatedClient(config, creds, store) {
|
|
|
3598
3599
|
});
|
|
3599
3600
|
store.saveCredentials({
|
|
3600
3601
|
...creds,
|
|
3602
|
+
userExtId: loginResult.userExtId ?? creds.userExtId,
|
|
3601
3603
|
serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
|
|
3602
3604
|
accessToken: void 0,
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
tokenTimestamp: void 0
|
|
3605
|
+
tokenTimestamp: void 0,
|
|
3606
|
+
openedWorkspaces: void 0
|
|
3606
3607
|
});
|
|
3607
3608
|
return { arbi, loginResult };
|
|
3608
3609
|
}
|
|
@@ -3612,13 +3613,18 @@ async function performPasswordLogin(config, email, password, store) {
|
|
|
3612
3613
|
const loginResult = await arbi.auth.login({ email, password });
|
|
3613
3614
|
store.saveCredentials({
|
|
3614
3615
|
email,
|
|
3616
|
+
userExtId: loginResult.userExtId,
|
|
3615
3617
|
signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
|
|
3616
3618
|
serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),
|
|
3617
|
-
//
|
|
3619
|
+
// `parent_ext_id` is set for persistent agent accounts (the user is
|
|
3620
|
+
// an Assistant owned by another user). Persisting it makes the CLI's
|
|
3621
|
+
// `arbi listen` work — it requires this to confirm a real agent
|
|
3622
|
+
// identity. Regular users get `null`, which we store as `undefined`.
|
|
3623
|
+
parentExtId: loginResult.parentExtId ?? void 0,
|
|
3624
|
+
// New session = no workspaces deposited yet.
|
|
3618
3625
|
accessToken: void 0,
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
tokenTimestamp: void 0
|
|
3626
|
+
tokenTimestamp: void 0,
|
|
3627
|
+
openedWorkspaces: void 0
|
|
3622
3628
|
});
|
|
3623
3629
|
return { arbi, loginResult, config };
|
|
3624
3630
|
}
|
|
@@ -3634,16 +3640,6 @@ async function selectWorkspace(arbi, workspaceId, wrappedKey, serverSessionKey,
|
|
|
3634
3640
|
arbi.session.setSelectedWorkspace(workspaceId);
|
|
3635
3641
|
return header;
|
|
3636
3642
|
}
|
|
3637
|
-
async function generateEncryptedWorkspaceKey(arbi, wrappedKey, serverSessionKey, signingPrivateKeyBase64) {
|
|
3638
|
-
const signingPrivateKey = client.base64ToBytes(signingPrivateKeyBase64);
|
|
3639
|
-
const ed25519PublicKey = signingPrivateKey.slice(32, 64);
|
|
3640
|
-
const encryptionKeyPair = client.deriveEncryptionKeypairFromSigning({
|
|
3641
|
-
publicKey: ed25519PublicKey,
|
|
3642
|
-
secretKey: signingPrivateKey
|
|
3643
|
-
});
|
|
3644
|
-
const workspaceKey = client.sealedBoxDecrypt(wrappedKey, encryptionKeyPair.secretKey);
|
|
3645
|
-
return client.sealKeyForSession(workspaceKey, serverSessionKey);
|
|
3646
|
-
}
|
|
3647
3643
|
async function getRawWorkspaceKey(arbi, workspaceId, signingPrivateKeyBase64) {
|
|
3648
3644
|
const { data: workspaces, error } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
3649
3645
|
if (error || !workspaces) {
|
|
@@ -3661,38 +3657,79 @@ async function getRawWorkspaceKey(arbi, workspaceId, signingPrivateKeyBase64) {
|
|
|
3661
3657
|
});
|
|
3662
3658
|
return client.sealedBoxDecrypt(ws.wrapped_key, encryptionKeyPair.secretKey);
|
|
3663
3659
|
}
|
|
3660
|
+
async function openWorkspaceEntry(arbi, ws, serverSessionKey, signingPrivateKeyBase64) {
|
|
3661
|
+
if (ws.wrapped_key) {
|
|
3662
|
+
const sealedKey = await selectWorkspace(
|
|
3663
|
+
arbi,
|
|
3664
|
+
ws.external_id,
|
|
3665
|
+
ws.wrapped_key,
|
|
3666
|
+
serverSessionKey,
|
|
3667
|
+
signingPrivateKeyBase64
|
|
3668
|
+
);
|
|
3669
|
+
const { error: error2 } = await arbi.fetch.POST("/v1/workspace/{workspace_ext_id}/open", {
|
|
3670
|
+
params: { path: { workspace_ext_id: ws.external_id } },
|
|
3671
|
+
body: { workspace_key: sealedKey }
|
|
3672
|
+
});
|
|
3673
|
+
if (error2) {
|
|
3674
|
+
throw new ArbiError(`Workspace open failed for ${ws.external_id}: ${JSON.stringify(error2)}`);
|
|
3675
|
+
}
|
|
3676
|
+
return sealedKey;
|
|
3677
|
+
}
|
|
3678
|
+
arbi.session.setSelectedWorkspace(ws.external_id);
|
|
3679
|
+
const { error } = await arbi.fetch.POST("/v1/workspace/{workspace_ext_id}/open", {
|
|
3680
|
+
params: { path: { workspace_ext_id: ws.external_id } },
|
|
3681
|
+
body: {}
|
|
3682
|
+
});
|
|
3683
|
+
if (error) {
|
|
3684
|
+
throw new ArbiError(`Workspace open failed for ${ws.external_id}: ${JSON.stringify(error)}`);
|
|
3685
|
+
}
|
|
3686
|
+
return null;
|
|
3687
|
+
}
|
|
3664
3688
|
async function selectWorkspaceById(arbi, workspaceId, serverSessionKey, signingPrivateKeyBase64) {
|
|
3665
3689
|
const { data: workspaces, error } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
3666
3690
|
if (error || !workspaces) {
|
|
3667
3691
|
throw new ArbiError("Failed to fetch workspaces");
|
|
3668
3692
|
}
|
|
3669
3693
|
const ws = workspaces.find((w2) => w2.external_id === workspaceId);
|
|
3670
|
-
if (!ws
|
|
3671
|
-
throw new ArbiError(`Workspace ${workspaceId} not found
|
|
3694
|
+
if (!ws) {
|
|
3695
|
+
throw new ArbiError(`Workspace ${workspaceId} not found`);
|
|
3672
3696
|
}
|
|
3673
|
-
const sealedKey = await
|
|
3674
|
-
|
|
3675
|
-
ws.external_id,
|
|
3676
|
-
ws.
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3697
|
+
const sealedKey = await openWorkspaceEntry(arbi, ws, serverSessionKey, signingPrivateKeyBase64);
|
|
3698
|
+
return {
|
|
3699
|
+
external_id: ws.external_id,
|
|
3700
|
+
name: ws.name,
|
|
3701
|
+
wrapped_key: ws.wrapped_key ?? null,
|
|
3702
|
+
sealed_key: sealedKey
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
function isSessionAlive(creds) {
|
|
3706
|
+
return !!(creds.accessToken && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
|
|
3707
|
+
}
|
|
3708
|
+
async function buildAuthFromCache(config, creds, store) {
|
|
3709
|
+
const arbi = client.createArbiClient(buildClientOptions(config, store, creds.email));
|
|
3710
|
+
await arbi.crypto.initSodium();
|
|
3711
|
+
arbi.session.setUser(creds.email, creds.userExtId);
|
|
3712
|
+
arbi.session.setAccessToken(creds.accessToken);
|
|
3713
|
+
const signingPrivateKey = client.base64ToBytes(creds.signingPrivateKeyBase64);
|
|
3714
|
+
const serverSessionKey = client.base64ToBytes(creds.serverSessionKeyBase64);
|
|
3715
|
+
const loginResult = {
|
|
3716
|
+
accessToken: creds.accessToken,
|
|
3717
|
+
userExtId: creds.userExtId,
|
|
3718
|
+
signingPrivateKey,
|
|
3719
|
+
serverSessionKey
|
|
3720
|
+
};
|
|
3721
|
+
return { arbi, loginResult };
|
|
3685
3722
|
}
|
|
3686
3723
|
async function resolveAuth(store) {
|
|
3687
3724
|
const config = store.requireConfig();
|
|
3688
3725
|
const creds = store.requireCredentials();
|
|
3726
|
+
if (isSessionAlive(creds)) {
|
|
3727
|
+
const { arbi: arbi2, loginResult: loginResult2 } = await buildAuthFromCache(config, creds, store);
|
|
3728
|
+
return { arbi: arbi2, loginResult: loginResult2, config };
|
|
3729
|
+
}
|
|
3689
3730
|
const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
|
|
3690
3731
|
return { arbi, loginResult, config };
|
|
3691
3732
|
}
|
|
3692
|
-
var TOKEN_MAX_AGE_MS = 50 * 60 * 1e3;
|
|
3693
|
-
function isCachedTokenValid(creds, workspaceId) {
|
|
3694
|
-
return !!(creds.accessToken && creds.sealedWorkspaceKey && creds.workspaceId === workspaceId && creds.tokenTimestamp && Date.now() - new Date(creds.tokenTimestamp).getTime() < TOKEN_MAX_AGE_MS);
|
|
3695
|
-
}
|
|
3696
3733
|
async function resolveWorkspace(store, workspaceOpt) {
|
|
3697
3734
|
const config = store.requireConfig();
|
|
3698
3735
|
const creds = store.requireCredentials();
|
|
@@ -3700,53 +3737,52 @@ async function resolveWorkspace(store, workspaceOpt) {
|
|
|
3700
3737
|
if (!workspaceId) {
|
|
3701
3738
|
throw new ArbiError("No workspace selected. Run: arbi workspace select <id>");
|
|
3702
3739
|
}
|
|
3703
|
-
if (
|
|
3704
|
-
const arbi2 =
|
|
3705
|
-
await arbi2.crypto.initSodium();
|
|
3706
|
-
arbi2.session.setUser(creds.email);
|
|
3740
|
+
if (isSessionAlive(creds)) {
|
|
3741
|
+
const { arbi: arbi2, loginResult: loginResult2 } = await buildAuthFromCache(config, creds, store);
|
|
3707
3742
|
arbi2.session.setSelectedWorkspace(workspaceId);
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3743
|
+
await selectWorkspaceById(
|
|
3744
|
+
arbi2,
|
|
3745
|
+
workspaceId,
|
|
3746
|
+
loginResult2.serverSessionKey,
|
|
3747
|
+
creds.signingPrivateKeyBase64
|
|
3748
|
+
);
|
|
3749
|
+
const nextOpened = Array.from(/* @__PURE__ */ new Set([...creds.openedWorkspaces ?? [], workspaceId]));
|
|
3750
|
+
store.saveCredentials({
|
|
3751
|
+
...store.requireCredentials(),
|
|
3752
|
+
openedWorkspaces: nextOpened
|
|
3753
|
+
});
|
|
3716
3754
|
return {
|
|
3717
3755
|
arbi: arbi2,
|
|
3718
3756
|
loginResult: loginResult2,
|
|
3719
3757
|
config,
|
|
3720
3758
|
workspaceId,
|
|
3721
|
-
accessToken: creds.accessToken
|
|
3722
|
-
sealedWorkspaceKey: creds.sealedWorkspaceKey
|
|
3759
|
+
accessToken: creds.accessToken
|
|
3723
3760
|
};
|
|
3724
3761
|
}
|
|
3725
3762
|
const { arbi, loginResult } = await createAuthenticatedClient(config, creds, store);
|
|
3726
|
-
|
|
3763
|
+
await selectWorkspaceById(
|
|
3727
3764
|
arbi,
|
|
3728
3765
|
workspaceId,
|
|
3729
3766
|
loginResult.serverSessionKey,
|
|
3730
3767
|
creds.signingPrivateKeyBase64
|
|
3731
3768
|
);
|
|
3732
3769
|
const accessToken = arbi.session.getState().accessToken;
|
|
3733
|
-
const sealedWorkspaceKey = await generateEncryptedWorkspaceKey(
|
|
3734
|
-
arbi,
|
|
3735
|
-
wsInfo.wrapped_key,
|
|
3736
|
-
loginResult.serverSessionKey,
|
|
3737
|
-
creds.signingPrivateKeyBase64
|
|
3738
|
-
);
|
|
3739
3770
|
if (!accessToken) {
|
|
3740
3771
|
throw new ArbiError("Authentication error \u2014 missing token");
|
|
3741
3772
|
}
|
|
3742
3773
|
store.saveCredentials({
|
|
3743
3774
|
...store.requireCredentials(),
|
|
3744
3775
|
accessToken,
|
|
3745
|
-
|
|
3746
|
-
workspaceId
|
|
3747
|
-
tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3776
|
+
tokenTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3777
|
+
openedWorkspaces: [workspaceId]
|
|
3748
3778
|
});
|
|
3749
|
-
return {
|
|
3779
|
+
return {
|
|
3780
|
+
arbi,
|
|
3781
|
+
loginResult,
|
|
3782
|
+
config,
|
|
3783
|
+
workspaceId,
|
|
3784
|
+
accessToken
|
|
3785
|
+
};
|
|
3750
3786
|
}
|
|
3751
3787
|
|
|
3752
3788
|
// src/sse.ts
|