@prmichaelsen/agentbase-core 0.1.0 → 0.1.2

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.
Files changed (91) hide show
  1. package/README.md +351 -0
  2. package/dist/client/api-types.generated.d.ts +4024 -0
  3. package/dist/client/api-types.generated.d.ts.map +1 -0
  4. package/dist/client/api-types.generated.js +6 -0
  5. package/dist/client/api-types.generated.js.map +1 -0
  6. package/dist/client/app.d.ts +126 -0
  7. package/dist/client/app.d.ts.map +1 -0
  8. package/dist/client/app.js +107 -0
  9. package/dist/client/app.js.map +1 -0
  10. package/dist/client/http-transport.d.ts +42 -0
  11. package/dist/client/http-transport.d.ts.map +1 -0
  12. package/dist/client/http-transport.js +113 -0
  13. package/dist/client/http-transport.js.map +1 -0
  14. package/dist/client/index.d.ts +6 -0
  15. package/dist/client/index.d.ts.map +1 -0
  16. package/dist/client/index.js +9 -0
  17. package/dist/client/index.js.map +1 -0
  18. package/dist/client/oauth.d.ts +86 -0
  19. package/dist/client/oauth.d.ts.map +1 -0
  20. package/dist/client/oauth.js +148 -0
  21. package/dist/client/oauth.js.map +1 -0
  22. package/dist/client/svc.d.ts +511 -0
  23. package/dist/client/svc.d.ts.map +1 -0
  24. package/dist/client/svc.js +409 -0
  25. package/dist/client/svc.js.map +1 -0
  26. package/dist/config/index.d.ts +34 -0
  27. package/dist/config/index.d.ts.map +1 -0
  28. package/dist/config/index.js +50 -0
  29. package/dist/config/index.js.map +1 -0
  30. package/dist/errors/app-errors.d.ts +34 -0
  31. package/dist/errors/app-errors.d.ts.map +1 -0
  32. package/dist/errors/app-errors.js +34 -0
  33. package/dist/errors/app-errors.js.map +1 -0
  34. package/dist/errors/base.error.d.ts +8 -0
  35. package/dist/errors/base.error.d.ts.map +1 -0
  36. package/dist/errors/base.error.js +9 -0
  37. package/dist/errors/base.error.js.map +1 -0
  38. package/dist/errors/index.d.ts +6 -0
  39. package/dist/errors/index.d.ts.map +1 -0
  40. package/dist/errors/index.js +12 -0
  41. package/dist/errors/index.js.map +1 -0
  42. package/dist/index.d.ts +6 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +5 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/lib/auth/error-handler.d.ts +6 -0
  47. package/dist/lib/auth/error-handler.d.ts.map +1 -0
  48. package/dist/lib/auth/error-handler.js +18 -0
  49. package/dist/lib/auth/error-handler.js.map +1 -0
  50. package/dist/lib/auth/guards.d.ts +5 -6
  51. package/dist/lib/auth/guards.d.ts.map +1 -1
  52. package/dist/lib/auth/guards.js +7 -18
  53. package/dist/lib/auth/guards.js.map +1 -1
  54. package/dist/lib/auth/index.d.ts +1 -0
  55. package/dist/lib/auth/index.d.ts.map +1 -1
  56. package/dist/lib/auth/index.js +1 -0
  57. package/dist/lib/auth/index.js.map +1 -1
  58. package/dist/lib/auth/session.d.ts.map +1 -1
  59. package/dist/lib/auth/session.js +8 -7
  60. package/dist/lib/auth/session.js.map +1 -1
  61. package/dist/lib/rate-limiter.d.ts.map +1 -1
  62. package/dist/lib/rate-limiter.js +2 -1
  63. package/dist/lib/rate-limiter.js.map +1 -1
  64. package/dist/services/auth.interface.d.ts +15 -0
  65. package/dist/services/auth.interface.d.ts.map +1 -0
  66. package/dist/services/auth.interface.js +2 -0
  67. package/dist/services/auth.interface.js.map +1 -0
  68. package/dist/services/base.service.d.ts +17 -1
  69. package/dist/services/base.service.d.ts.map +1 -1
  70. package/dist/services/base.service.js +26 -3
  71. package/dist/services/base.service.js.map +1 -1
  72. package/dist/services/confirmation-token.service.d.ts +5 -1
  73. package/dist/services/confirmation-token.service.d.ts.map +1 -1
  74. package/dist/services/confirmation-token.service.js.map +1 -1
  75. package/dist/services/index.d.ts +3 -2
  76. package/dist/services/index.d.ts.map +1 -1
  77. package/dist/services/index.js +1 -1
  78. package/dist/services/index.js.map +1 -1
  79. package/dist/types/branded.d.ts +24 -0
  80. package/dist/types/branded.d.ts.map +1 -0
  81. package/dist/types/branded.js +9 -0
  82. package/dist/types/branded.js.map +1 -0
  83. package/dist/types/index.d.ts +4 -0
  84. package/dist/types/index.d.ts.map +1 -1
  85. package/dist/types/index.js +3 -1
  86. package/dist/types/index.js.map +1 -1
  87. package/dist/types/result.d.ts +20 -0
  88. package/dist/types/result.d.ts.map +1 -0
  89. package/dist/types/result.js +44 -0
  90. package/dist/types/result.js.map +1 -0
  91. package/package.json +22 -3
package/README.md ADDED
@@ -0,0 +1,351 @@
1
+ # @prmichaelsen/agentbase-core
2
+
3
+ Shared service infrastructure for agentbase projects — BaseService, auth, Firebase wrappers, logging, typed errors, Result type, client SDK, and common utilities.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @prmichaelsen/agentbase-core
9
+ ```
10
+
11
+ ### Peer Dependencies
12
+
13
+ All peer dependencies are **optional** — install only what you use:
14
+
15
+ | Package | When needed |
16
+ |---------|-------------|
17
+ | `@prmichaelsen/firebase-admin-sdk-v8` | Server-side auth (`getServerSession`, `requireAuth`, `createSessionCookie`) |
18
+ | `firebase` | Client-side auth (`signIn`, `signUp`, `initializeFirebase`) |
19
+ | `jsonwebtoken` | JWT handling (if used directly) |
20
+
21
+ ## Export Entry Points
22
+
23
+ ```ts
24
+ import { ... } from '@prmichaelsen/agentbase-core' // everything
25
+ import { ... } from '@prmichaelsen/agentbase-core/services' // BaseService, ConfirmationTokenService
26
+ import { ... } from '@prmichaelsen/agentbase-core/lib' // logger, firebase, utilities
27
+ import { ... } from '@prmichaelsen/agentbase-core/lib/auth' // server auth guards
28
+ import { ... } from '@prmichaelsen/agentbase-core/types' // AuthUser, Result, branded types
29
+ import { ... } from '@prmichaelsen/agentbase-core/client' // HTTP client, SvcClient, AppClient, OAuth
30
+ import { ... } from '@prmichaelsen/agentbase-core/config' // loadConfig, validateConfig
31
+ ```
32
+
33
+ ## Services
34
+
35
+ ### BaseService
36
+
37
+ Abstract base class with config injection, structured logging, lifecycle hooks, and state tracking.
38
+
39
+ ```ts
40
+ import { BaseService, ServiceState, type Logger } from '@prmichaelsen/agentbase-core/services'
41
+
42
+ class UserService extends BaseService<{ dbUrl: string }> {
43
+ constructor(config: { dbUrl: string }, logger: Logger) {
44
+ super(config, logger)
45
+ }
46
+
47
+ async initialize() {
48
+ await super.initialize() // sets state to Initialized
49
+ this.logger.info('Connected', { url: this.config.dbUrl })
50
+ }
51
+
52
+ async findUser(id: string) {
53
+ this.ensureInitialized() // throws if not initialized
54
+ // ...
55
+ }
56
+ }
57
+
58
+ const svc = new UserService({ dbUrl: '...' }, logger)
59
+ svc.getState() // ServiceState.Uninitialized
60
+ await svc.initialize()
61
+ svc.getState() // ServiceState.Initialized
62
+ ```
63
+
64
+ ### ConfirmationTokenService
65
+
66
+ In-memory token store for two-step confirmation flows. Tokens are single-use, user-scoped, and expire after TTL.
67
+
68
+ ```ts
69
+ import { ConfirmationTokenService } from '@prmichaelsen/agentbase-core/services'
70
+
71
+ const confirmations = new ConfirmationTokenService(5 * 60 * 1000) // 5 min TTL
72
+
73
+ // Step 1: Generate token
74
+ const token = confirmations.generateToken({
75
+ type: 'delete-account',
76
+ userId: 'user-123',
77
+ params: { accountId: 'abc' },
78
+ summary: 'Delete account abc',
79
+ createdAt: Date.now(),
80
+ })
81
+
82
+ // Step 2: Consume token (single-use)
83
+ const action = confirmations.consumeToken(token, 'user-123')
84
+ if (action) { /* execute */ }
85
+ ```
86
+
87
+ ## Error Types
88
+
89
+ Typed error hierarchy with discriminated `kind` field and HTTP status mapping.
90
+
91
+ ```ts
92
+ import {
93
+ UnauthorizedError, ForbiddenError, ValidationError, NotFoundError,
94
+ ConflictError, RateLimitError, ExternalError, InternalError,
95
+ isAppError, errorToStatusCode,
96
+ } from '@prmichaelsen/agentbase-core'
97
+
98
+ // Throw typed errors
99
+ throw new ValidationError('Email is required', { field: 'email' })
100
+ throw new NotFoundError('User not found')
101
+
102
+ // Type guard
103
+ try {
104
+ await doSomething()
105
+ } catch (err) {
106
+ if (isAppError(err)) {
107
+ console.log(err.kind) // 'VALIDATION' | 'NOT_FOUND' | ...
108
+ console.log(err.statusCode) // 400, 404, ...
109
+ console.log(err.context) // { field: 'email' }
110
+ }
111
+ }
112
+
113
+ // Map to HTTP status
114
+ const status = errorToStatusCode(err) // 400 for ValidationError, 500 for unknown
115
+ ```
116
+
117
+ | Error | Kind | Status |
118
+ |-------|------|--------|
119
+ | `ValidationError` | `VALIDATION` | 400 |
120
+ | `UnauthorizedError` | `UNAUTHORIZED` | 401 |
121
+ | `ForbiddenError` | `FORBIDDEN` | 403 |
122
+ | `NotFoundError` | `NOT_FOUND` | 404 |
123
+ | `ConflictError` | `CONFLICT` | 409 |
124
+ | `RateLimitError` | `RATE_LIMIT` | 429 |
125
+ | `ExternalError` | `EXTERNAL` | 502 |
126
+ | `InternalError` | `INTERNAL` | 500 |
127
+
128
+ ## Result Type
129
+
130
+ Discriminated `Result<T, E>` for operations where failure is expected.
131
+
132
+ ```ts
133
+ import { ok, err, isOk, isErr, mapOk, andThen, getOrElse, tryCatchAsync } from '@prmichaelsen/agentbase-core'
134
+ import type { Result } from '@prmichaelsen/agentbase-core'
135
+
136
+ // Create results
137
+ const success: Result<number> = ok(42)
138
+ const failure: Result<number, string> = err('not found')
139
+
140
+ // Type guards
141
+ if (isOk(success)) console.log(success.value) // 42
142
+ if (isErr(failure)) console.log(failure.error) // 'not found'
143
+
144
+ // Combinators
145
+ const doubled = mapOk(ok(5), x => x * 2) // Ok(10)
146
+ const chained = andThen(ok(5), x => x > 0 ? ok(x) : err('negative'))
147
+ const value = getOrElse(err('fail'), 0) // 0
148
+
149
+ // Wrap async functions
150
+ const result = await tryCatchAsync(
151
+ () => fetchUser(id),
152
+ (e) => new NotFoundError('User not found')
153
+ )
154
+ ```
155
+
156
+ ## Auth
157
+
158
+ ### Server-Side (requires `@prmichaelsen/firebase-admin-sdk-v8`)
159
+
160
+ ```ts
161
+ import {
162
+ getServerSession, requireAuth, requireAdmin, isAdmin,
163
+ handleAuthError,
164
+ } from '@prmichaelsen/agentbase-core/lib/auth'
165
+
166
+ // Get session from request cookies
167
+ const session = await getServerSession(request)
168
+
169
+ // Guards throw typed errors — use try-catch
170
+ try {
171
+ await requireAuth(request) // throws UnauthorizedError
172
+ await requireAdmin(request, 'admin@example.com') // throws Unauthorized or Forbidden
173
+ } catch (error) {
174
+ return handleAuthError(error) // converts to Response(401/403/500)
175
+ }
176
+
177
+ // Boolean check (does not throw)
178
+ const admin = await isAdmin(request, 'admin@example.com')
179
+ ```
180
+
181
+ ### Client-Side (requires `firebase`)
182
+
183
+ ```ts
184
+ import {
185
+ initializeFirebase, signIn, signUp, signInAnonymously,
186
+ upgradeAnonymousAccount, logout, onAuthChange, getIdToken,
187
+ } from '@prmichaelsen/agentbase-core/lib'
188
+
189
+ initializeFirebase({ apiKey: '...', authDomain: '...', projectId: '...' })
190
+
191
+ await signIn('user@example.com', 'password')
192
+ await signUp('user@example.com', 'password')
193
+ await signInAnonymously()
194
+ await upgradeAnonymousAccount('user@example.com', 'password')
195
+ await logout()
196
+
197
+ const unsubscribe = onAuthChange((user) => { /* ... */ })
198
+ const token = await getIdToken()
199
+ ```
200
+
201
+ ### Helpers
202
+
203
+ ```ts
204
+ import { isRealUser, isRealUserServer } from '@prmichaelsen/agentbase-core/lib/auth'
205
+
206
+ if (isRealUser(user)) { /* non-anonymous */ }
207
+ if (isRealUserServer(authUser)) { /* non-anonymous, typed for AuthUser */ }
208
+ ```
209
+
210
+ ## Client SDK
211
+
212
+ Full REST client for agentbase.me with 17 service classes, compound workflows, and OAuth support.
213
+
214
+ ### Setup
215
+
216
+ ```ts
217
+ import { HttpClient, AuthSvc, MemoriesSvc, OAuthClient } from '@prmichaelsen/agentbase-core/client'
218
+
219
+ // With API key
220
+ const http = new HttpClient({
221
+ baseUrl: 'https://agentbase.me',
222
+ auth: { type: 'apiKey', key: 'your-api-key' },
223
+ })
224
+
225
+ // With OAuth bearer token
226
+ const http = new HttpClient({
227
+ baseUrl: 'https://agentbase.me',
228
+ auth: { type: 'bearer', token: () => oauthClient.getValidToken() },
229
+ })
230
+
231
+ // With session cookie (browser)
232
+ const http = new HttpClient({
233
+ baseUrl: 'https://agentbase.me',
234
+ auth: { type: 'cookie' },
235
+ })
236
+ ```
237
+
238
+ ### SvcClient (1:1 REST mirror)
239
+
240
+ ```ts
241
+ const memories = new MemoriesSvc(http)
242
+
243
+ // All methods return SdkResponse<T> — never throws
244
+ const result = await memories.search({ query: 'vacation photos', mode: 'semantic' })
245
+ if (result.error) {
246
+ console.error(result.error.message)
247
+ } else {
248
+ console.log(result.data) // { data: Memory[], total: number }
249
+ }
250
+
251
+ // Or use throwOnError() for cleaner async/await
252
+ const { data } = await memories.feed({ limit: 10 }).then(r => r.throwOnError())
253
+ ```
254
+
255
+ Available services: `AuthSvc`, `OAuthSvc`, `MemoriesSvc`, `ConversationsSvc`, `ProfilesSvc`, `GroupsSvc`, `DmsSvc`, `SearchSvc`, `NotificationsSvc`, `RelationshipsSvc`, `BoardsSvc`, `TokensSvc`, `IntegrationsSvc`, `SettingsSvc`, `PaymentsSvc`, `UsageSvc`, `JobsSvc`
256
+
257
+ ### AppClient (compound workflows)
258
+
259
+ ```ts
260
+ import { AppClient } from '@prmichaelsen/agentbase-core/client'
261
+
262
+ const app = new AppClient({ auth, memories, conversations, profiles, groups, dms, search, notifications })
263
+
264
+ await app.loginAndGetSession(idToken)
265
+ await app.searchAndFetchMemories('vacation', 5)
266
+ await app.createGroupAndInvite('Book Club', 'Monthly reads', ['user-1', 'user-2'])
267
+ await app.getFullProfile('user-123') // profile + memory count
268
+ await app.startDm('user-456') // find existing or create new
269
+ ```
270
+
271
+ ### OAuth Integration
272
+
273
+ ```ts
274
+ import {
275
+ OAuthClient, generateCodeVerifier, generateCodeChallenge,
276
+ buildAuthorizationUrl,
277
+ } from '@prmichaelsen/agentbase-core/client'
278
+
279
+ // 1. Generate PKCE params
280
+ const verifier = generateCodeVerifier()
281
+ const challenge = await generateCodeChallenge(verifier)
282
+
283
+ // 2. Build authorization URL
284
+ const url = buildAuthorizationUrl('https://agentbase.me', {
285
+ clientId: 'your-client-id',
286
+ redirectUri: 'http://localhost:3000/callback',
287
+ scope: 'read write',
288
+ codeChallenge: challenge,
289
+ })
290
+
291
+ // 3. After redirect, exchange code for tokens
292
+ const oauth = new OAuthClient(http, { clientId: 'your-client-id' })
293
+ await oauth.exchangeCode(code, redirectUri, verifier)
294
+
295
+ // 4. Get valid token (auto-refreshes if expired)
296
+ const token = await oauth.getValidToken()
297
+
298
+ // API token shortcut
299
+ await oauth.exchangeApiToken('your-api-token')
300
+ ```
301
+
302
+ ## Configuration
303
+
304
+ Centralized environment variable loading and validation.
305
+
306
+ ```ts
307
+ import { loadConfig, validateConfig } from '@prmichaelsen/agentbase-core/config'
308
+
309
+ const config = loadConfig() // reads from process.env
310
+ validateConfig(config, { requireFirebaseAdmin: true }) // throws ValidationError if missing
311
+
312
+ // Or pass custom env
313
+ const config = loadConfig({ FIREBASE_PROJECT_ID: 'my-project', ... })
314
+ ```
315
+
316
+ ## Logger
317
+
318
+ Structured logger with automatic sensitive data redaction.
319
+
320
+ ```ts
321
+ import { createLogger, authLogger, apiLogger } from '@prmichaelsen/agentbase-core/lib'
322
+
323
+ const logger = createLogger('MyService')
324
+ logger.info('Request', { userId: 'abc', apiKey: 'secret' })
325
+ // Output: [MyService] Request { userId: 'abc', apiKey: '[REDACTED]' }
326
+ ```
327
+
328
+ ### Sanitization Utilities
329
+
330
+ ```ts
331
+ import { sanitizeToken, sanitizeEmail, sanitizeUserId, sanitizeObject } from '@prmichaelsen/agentbase-core/lib'
332
+
333
+ sanitizeToken('abc123...') // "a1b2c3d4..."
334
+ sanitizeEmail('john@x.com') // "jo***@x.com"
335
+ sanitizeObject({ password: 'secret', name: 'ok' }) // { password: '[REDACTED]', name: 'ok' }
336
+ ```
337
+
338
+ ## Types
339
+
340
+ ```ts
341
+ import type { AuthUser, ServerSession, Result, UserId, EmailAddress } from '@prmichaelsen/agentbase-core/types'
342
+ import { toUserId, toEmailAddress, toTimestamp } from '@prmichaelsen/agentbase-core/types'
343
+
344
+ // Branded primitives prevent mixing IDs
345
+ const uid: UserId = toUserId('firebase-uid')
346
+ const email: EmailAddress = toEmailAddress('user@example.com')
347
+ ```
348
+
349
+ ## License
350
+
351
+ MIT