@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 +74 -239
- package/dist/browser.cjs +1369 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +2 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +1327 -0
- package/dist/browser.js.map +1 -0
- package/dist/index.cjs +1455 -1076
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2919 -7321
- package/dist/index.d.ts +2919 -7321
- package/dist/index.js +1413 -1034
- package/dist/index.js.map +1 -1
- package/package.json +21 -11
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @arbidocs/sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for the ARBI
|
|
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
|
|
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
|
|
20
|
+
import 'fake-indexeddb/auto' // Node.js only
|
|
21
|
+
import { Arbi } from '@arbidocs/sdk'
|
|
21
22
|
|
|
22
|
-
const arbi =
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
The SDK's `SessionManager` supports a subscribe pattern for reactive updates:
|
|
38
|
+
## Streaming AI Queries
|
|
165
39
|
|
|
166
40
|
```typescript
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
##
|
|
183
|
-
|
|
184
|
-
### `createArbiClient(options)`
|
|
51
|
+
## Session Recovery
|
|
185
52
|
|
|
186
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
59
|
+
// Later, recover without password:
|
|
60
|
+
await arbi.loginWithKey('user@example.com', signingKeyBase64)
|
|
61
|
+
```
|
|
199
62
|
|
|
200
|
-
|
|
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
|
-
###
|
|
65
|
+
### Constructor
|
|
209
66
|
|
|
210
67
|
```typescript
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
//
|
|
215
|
-
|
|
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
|
-
###
|
|
231
|
-
|
|
232
|
-
The `arbi.fetch` client has three middleware layers applied automatically:
|
|
75
|
+
### Lifecycle
|
|
233
76
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
###
|
|
84
|
+
### Properties
|
|
239
85
|
|
|
240
|
-
|
|
86
|
+
| Property | Type | Description |
|
|
87
|
+
|----------|------|-------------|
|
|
88
|
+
| `isLoggedIn` | `boolean` | Whether the user is authenticated |
|
|
89
|
+
| `hasWorkspace` | `boolean` | Whether a workspace is selected |
|
|
241
90
|
|
|
242
|
-
|
|
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
|
-
|
|
252
|
-
getSession, saveSession, clearAllData, hasSession,
|
|
93
|
+
All operations are namespaced and require a workspace to be selected (except `workspaces` and `health`).
|
|
253
94
|
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
258
|
-
createReloginHandler,
|
|
106
|
+
### Browser Entry Point
|
|
259
107
|
|
|
260
|
-
|
|
261
|
-
createSessionManager,
|
|
108
|
+
For browser environments (no Node.js APIs like `fs`), import from the browser entry:
|
|
262
109
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
} from '@arbidocs/sdk'
|
|
110
|
+
```typescript
|
|
111
|
+
import { Arbi } from '@arbidocs/sdk/browser'
|
|
266
112
|
```
|
|
267
113
|
|
|
268
|
-
##
|
|
269
|
-
|
|
270
|
-
### Zero-Knowledge Auth
|
|
271
|
-
|
|
272
|
-
ARBI uses Ed25519 signatures for authentication. Your password never leaves the client:
|
|
114
|
+
## Security
|
|
273
115
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
294
|
-
- **Node.js**: v18+ (needs `fetch`, `FormData`, `Blob` globals). Install `fake-indexeddb
|
|
295
|
-
- **TypeScript**: 5.0+ (optional
|
|
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)
|