@arbidocs/sdk 0.3.8 → 0.3.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @arbidocs/sdk
2
2
 
3
- TypeScript SDK for the ARBI API zero-knowledge auth, E2E encryption, and type-safe REST client.
3
+ High-level TypeScript SDK for the ARBI platform. Wraps authentication, workspace encryption, and all API operations into a single `Arbi` class.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,7 +8,7 @@ TypeScript SDK for the ARBI API — zero-knowledge auth, E2E encryption, and typ
8
8
  npm install @arbidocs/sdk
9
9
  ```
10
10
 
11
- For Node.js environments (no browser), also install the IndexedDB polyfill:
11
+ For Node.js, also install the IndexedDB polyfill:
12
12
 
13
13
  ```bash
14
14
  npm install fake-indexeddb
@@ -17,279 +17,114 @@ npm install fake-indexeddb
17
17
  ## Quick Start
18
18
 
19
19
  ```typescript
20
- import { createArbiClient } from '@arbidocs/sdk'
20
+ import 'fake-indexeddb/auto' // Node.js only
21
+ import { Arbi } from '@arbidocs/sdk'
21
22
 
22
- const arbi = createArbiClient({
23
- baseUrl: 'https://box-201.arbibox.com',
24
- deploymentDomain: 'box-201.arbibox.com',
25
- })
23
+ const arbi = new Arbi({ url: 'https://arbi.mycompany.com' })
26
24
 
27
25
  // Login
28
- const session = await arbi.auth.login({
29
- email: 'user@example.com',
30
- password: 'mypassword',
31
- })
32
-
33
- // Type-safe API calls — auto-injects Bearer token
34
- const { data: workspaces } = await arbi.fetch.GET('/api/user/workspaces')
35
- console.log(workspaces) // WorkspaceResponse[]
36
- ```
26
+ await arbi.login('user@example.com', 'password')
37
27
 
38
- ## Examples
39
-
40
- ### Register + Login (Node.js)
41
-
42
- ```javascript
43
- // Polyfill IndexedDB for Node.js (must be first import)
44
- require('fake-indexeddb/auto')
45
- const { createArbiClient } = require('@arbidocs/sdk')
46
-
47
- async function main() {
48
- const arbi = createArbiClient({
49
- baseUrl: 'https://box-201.arbibox.com',
50
- deploymentDomain: 'box-201.arbibox.com',
51
- credentials: 'omit', // No cookies in Node.js
52
- })
53
-
54
- // Register a new user
55
- const { signingPrivateKey } = await arbi.auth.register({
56
- email: 'alice@example.com',
57
- password: 'SecurePass123!',
58
- verificationCode: '...', // From /api/user/verify-email flow
59
- firstName: 'Alice',
60
- lastName: 'Smith',
61
- })
62
-
63
- // Login
64
- const session = await arbi.auth.login({
65
- email: 'alice@example.com',
66
- password: 'SecurePass123!',
67
- })
68
-
69
- console.log('Logged in as', session.userExtId)
70
- console.log('Token:', session.accessToken.slice(0, 20) + '...')
71
- }
72
-
73
- main()
74
- ```
28
+ // Pick a workspace
29
+ const workspaces = await arbi.workspaces.list()
30
+ await arbi.selectWorkspace(workspaces[0].external_id)
75
31
 
76
- ### Create a Workspace and Upload a Document
77
-
78
- ```javascript
79
- require('fake-indexeddb/auto')
80
- const fs = require('fs')
81
- const {
82
- createArbiClient,
83
- getSession,
84
- sealedBoxDecrypt,
85
- deriveEncryptionKeypairFromSigning,
86
- createWorkspaceKeyHeader,
87
- } = require('@arbidocs/sdk')
88
-
89
- async function main() {
90
- const arbi = createArbiClient({
91
- baseUrl: 'https://box-201.arbibox.com',
92
- deploymentDomain: 'box-201.arbibox.com',
93
- credentials: 'omit',
94
- })
95
-
96
- // Login
97
- const { accessToken, serverSessionKey } = await arbi.auth.login({
98
- email: 'alice@example.com',
99
- password: 'SecurePass123!',
100
- })
101
-
102
- // Create workspace
103
- const { data: workspace } = await arbi.fetch.POST('/api/workspace/create_protected', {
104
- body: {
105
- name: 'My Case Files',
106
- description: 'Documents for Case #1234',
107
- is_public: false,
108
- },
109
- })
110
-
111
- // Decrypt the workspace key and generate the auth header.
112
- // The server wraps the workspace symmetric key with your public key
113
- // during creation. You unwrap it here to prove you own the workspace.
114
- const session = await getSession()
115
- const pubKey = session.signingPrivateKey.slice(32, 64)
116
- const encKP = deriveEncryptionKeypairFromSigning({
117
- publicKey: pubKey,
118
- secretKey: session.signingPrivateKey,
119
- })
120
- const workspaceKey = sealedBoxDecrypt(workspace.wrapped_key, encKP.secretKey)
121
- const wsHeader = await createWorkspaceKeyHeader(workspaceKey, serverSessionKey)
122
-
123
- // Tell the SDK which workspace is active (middleware auto-injects the header)
124
- arbi.session.setSelectedWorkspace(workspace.external_id)
125
- arbi.session.setCachedWorkspaceHeader(workspace.external_id, wsHeader)
126
-
127
- // Upload a PDF (multipart — use raw fetch since openapi-fetch
128
- // doesn't handle multipart/form-data)
129
- const formData = new FormData()
130
- const pdf = fs.readFileSync('./contract.pdf')
131
- formData.append('files', new Blob([pdf], { type: 'application/pdf' }), 'contract.pdf')
132
-
133
- const uploadRes = await fetch(
134
- `https://box-201.arbibox.com/api/document/upload?workspace_ext_id=${workspace.external_id}`,
135
- {
136
- method: 'POST',
137
- headers: {
138
- Authorization: `Bearer ${accessToken}`,
139
- 'workspace-key': wsHeader,
140
- },
141
- body: formData,
142
- }
143
- )
144
-
145
- const { doc_ext_ids } = await uploadRes.json()
146
- console.log('Uploaded document:', doc_ext_ids[0])
147
-
148
- // List documents (workspace-key header injected automatically by middleware)
149
- const { data: docs } = await arbi.fetch.GET(
150
- '/api/workspace/{workspace_ext_id}/documents',
151
- { params: { path: { workspace_ext_id: workspace.external_id } } }
152
- )
153
-
154
- for (const doc of docs) {
155
- console.log(` ${doc.file_name} — ${doc.status}`)
156
- }
157
- }
158
-
159
- main()
32
+ // Use it
33
+ const docs = await arbi.documents.list()
34
+ const tags = await arbi.tags.list()
35
+ const conversations = await arbi.conversations.list()
160
36
  ```
161
37
 
162
- ### Subscribe to Session State Changes
163
-
164
- The SDK's `SessionManager` supports a subscribe pattern for reactive updates:
38
+ ## Streaming AI Queries
165
39
 
166
40
  ```typescript
167
- const arbi = createArbiClient({ baseUrl, deploymentDomain })
168
-
169
- // React to session changes (useful for custom UI frameworks)
170
- const unsubscribe = arbi.session.subscribe((state) => {
171
- console.log('Session changed:', {
172
- loggedIn: !!state.accessToken,
173
- email: state.userEmail,
174
- workspace: state.selectedWorkspaceId,
175
- })
41
+ const result = await arbi.assistant.query('What does section 3 say?', {
42
+ docIds: [docs[0].external_id],
43
+ onToken: (token) => process.stdout.write(token),
44
+ onStreamStart: ({ assistant_message_ext_id }) => {
45
+ console.log('Stream started:', assistant_message_ext_id)
46
+ },
47
+ onComplete: () => console.log('\nDone'),
176
48
  })
177
-
178
- // Later: stop listening
179
- unsubscribe()
180
49
  ```
181
50
 
182
- ## API Reference
183
-
184
- ### `createArbiClient(options)`
51
+ ## Session Recovery
185
52
 
186
- Creates an SDK client instance.
53
+ After login, the SDK automatically persists your signing key (encrypted) in IndexedDB. You can also recover a session using the raw private key:
187
54
 
188
55
  ```typescript
189
- interface ArbiClientOptions {
190
- baseUrl: string // ARBI API URL (e.g. "https://box-201.arbibox.com")
191
- deploymentDomain: string // Used for deterministic key derivation
192
- credentials?: RequestCredentials // Default: 'include'. Use 'omit' for Node.js
193
- ssoTokenProvider?: { getToken(): Promise<string | null> } | null
194
- onReloginSuccess?: (data) => void
195
- }
196
- ```
56
+ // Save the key after first login (e.g. the recovery key download)
57
+ const signingKeyBase64 = '...'
197
58
 
198
- Returns an `ArbiClient` with these namespaces:
59
+ // Later, recover without password:
60
+ await arbi.loginWithKey('user@example.com', signingKeyBase64)
61
+ ```
199
62
 
200
- | Namespace | Description |
201
- |-----------|-------------|
202
- | `arbi.fetch` | Type-safe `openapi-fetch` client with auto-auth middleware |
203
- | `arbi.session` | In-memory session state (token, user, workspace) |
204
- | `arbi.auth` | High-level auth: `register`, `login`, `loginWithKey`, `logout`, `changePassword`, `relogin` |
205
- | `arbi.crypto` | Crypto utilities: key derivation, signing, encryption, base64 |
206
- | `arbi.storage` | IndexedDB session persistence: `getSession`, `saveSession` |
63
+ ## API Reference
207
64
 
208
- ### Auth Methods
65
+ ### Constructor
209
66
 
210
67
  ```typescript
211
- // Register
212
- await arbi.auth.register({ email, password, verificationCode, firstName?, lastName? })
213
-
214
- // Login with password
215
- await arbi.auth.login({ email, password })
216
-
217
- // Login with recovery key (Uint8Array)
218
- await arbi.auth.loginWithKey({ email, signingPrivateKey })
219
-
220
- // Change password
221
- await arbi.auth.changePassword({ email, currentPassword, newPassword })
222
-
223
- // Logout (clears session + IndexedDB)
224
- await arbi.auth.logout()
225
-
226
- // Re-login using stored private key (auto-called by middleware on 401)
227
- await arbi.auth.relogin()
68
+ const arbi = new Arbi({
69
+ url: 'https://arbi.mycompany.com', // Required: backend URL
70
+ deploymentDomain: 'arbi.mycompany.com', // Optional: defaults to URL hostname
71
+ credentials: 'omit', // Optional: 'omit' | 'include' | 'same-origin'
72
+ })
228
73
  ```
229
74
 
230
- ### Middleware (Auto-Applied)
231
-
232
- The `arbi.fetch` client has three middleware layers applied automatically:
75
+ ### Lifecycle
233
76
 
234
- 1. **Bearer Auth** injects `Authorization: Bearer <token>` on every request
235
- 2. **Workspace Key** — injects `workspace-key` header for workspace-scoped endpoints
236
- 3. **Auto-Relogin** — on 401, re-derives credentials from stored key and retries
77
+ | Method | Description |
78
+ |--------|-------------|
79
+ | `login(email, password)` | Authenticate with email and password |
80
+ | `loginWithKey(email, signingKeyBase64)` | Authenticate with a stored signing key |
81
+ | `selectWorkspace(workspaceId)` | Select a workspace (decrypts workspace key) |
82
+ | `logout()` | Log out and securely clear key material |
237
83
 
238
- ### Standalone Exports
84
+ ### Properties
239
85
 
240
- All building blocks are individually importable for advanced use:
86
+ | Property | Type | Description |
87
+ |----------|------|-------------|
88
+ | `isLoggedIn` | `boolean` | Whether the user is authenticated |
89
+ | `hasWorkspace` | `boolean` | Whether a workspace is selected |
241
90
 
242
- ```typescript
243
- import {
244
- // Crypto primitives
245
- initSodium, signMessage, sealedBoxDecrypt, sealedBoxEncrypt,
246
- deriveEncryptionKeypairFromSigning, createWorkspaceKeyHeader,
247
- generateUserKeypairs, generateLoginCredentials,
248
- encryptMessage, decryptMessage,
249
- base64Encode, base64Decode, base64ToBytes, bytesToBase64,
91
+ ### Operations
250
92
 
251
- // Storage
252
- getSession, saveSession, clearAllData, hasSession,
93
+ All operations are namespaced and require a workspace to be selected (except `workspaces` and `health`).
253
94
 
254
- // Middleware (compose your own client)
255
- createBearerAuthMiddleware, createWorkspaceKeyMiddleware, createAutoReloginMiddleware,
95
+ | Namespace | Methods |
96
+ |-----------|---------|
97
+ | `arbi.workspaces` | `list()`, `create()`, `update()`, `delete()`, `listUsers()`, `addUsers()`, `removeUsers()`, `setUserRole()` |
98
+ | `arbi.documents` | `list()`, `get()`, `upload()`, `update()`, `delete()` |
99
+ | `arbi.conversations` | `list()`, `getThreads()`, `updateTitle()`, `share()`, `delete()` |
100
+ | `arbi.assistant` | `query(question, options)`, `retrieve(messageExtId)` |
101
+ | `arbi.tags` | `list()`, `create()`, `update()`, `delete()` |
102
+ | `arbi.doctags` | `assign()`, `remove()` |
103
+ | `arbi.contacts` | `list()`, `search()` |
104
+ | `arbi.health` | `check()` |
256
105
 
257
- // Relogin handler
258
- createReloginHandler,
106
+ ### Browser Entry Point
259
107
 
260
- // Session manager
261
- createSessionManager,
108
+ For browser environments (no Node.js APIs like `fs`), import from the browser entry:
262
109
 
263
- // OpenAPI schema types
264
- type paths, type components, type operations,
265
- } from '@arbidocs/sdk'
110
+ ```typescript
111
+ import { Arbi } from '@arbidocs/sdk/browser'
266
112
  ```
267
113
 
268
- ## How It Works
269
-
270
- ### Zero-Knowledge Auth
271
-
272
- ARBI uses Ed25519 signatures for authentication. Your password never leaves the client:
114
+ ## Security
273
115
 
274
- 1. **Key derivation**: `Argon2id(email + password + deploymentDomain)` Ed25519 seed signing keypair
275
- 2. **Registration**: Public key sent to server, private key stays local
276
- 3. **Login**: Client signs a timestamp, server verifies the signature
277
- 4. **Session**: Server returns an encrypted session key for workspace operations
116
+ - **Zero-knowledge auth**: Passwords never leave the client. Ed25519 keys are derived locally via Argon2id.
117
+ - **E2E workspace encryption**: Each workspace has a symmetric key, wrapped with your public key.
118
+ - **Encrypted key storage**: Private keys in IndexedDB are encrypted with a non-extractable Web Crypto AES-GCM key.
119
+ - **Key zeroing on logout**: Signing and session keys are scrubbed from memory on logout.
278
120
 
279
- ### Workspace Encryption
280
-
281
- Each workspace has a symmetric key (NaCl SecretBox). The key is:
282
- - Generated server-side on workspace creation
283
- - Wrapped with your X25519 public key (derived from your Ed25519 signing key)
284
- - Unwrapped client-side using `sealedBoxDecrypt`
285
- - Used to generate per-request `workspace-key` headers (HMAC proof of key possession)
286
-
287
- ### IndexedDB Storage
121
+ ## Examples
288
122
 
289
- The SDK stores the signing private key and session key in IndexedDB, encrypted with a non-extractable AES-GCM key from the Web Crypto API. In Node.js, use `fake-indexeddb` as a polyfill.
123
+ - **[Node.js Hello World](../../examples/node-hello-world/)** Login, list docs, ask a streaming question (~40 lines)
124
+ - **[React Chat App](../../examples/react-chat/)** — Login form, workspace selector, document picker, streaming chat
290
125
 
291
126
  ## Requirements
292
127
 
293
- - **Browser**: Any modern browser (Chrome, Firefox, Safari, Edge)
294
- - **Node.js**: v18+ (needs `fetch`, `FormData`, `Blob` globals). Install `fake-indexeddb` for IndexedDB.
295
- - **TypeScript**: 5.0+ (optional — works with plain JS too)
128
+ - **Browser**: Any modern browser with Web Crypto API
129
+ - **Node.js**: v18+ (needs `fetch`, `FormData`, `Blob` globals). Install `fake-indexeddb`.
130
+ - **TypeScript**: 5.0+ (optional)