@soulcraft/sdk 1.0.0
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/client/index.d.ts +62 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +60 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/ai/index.d.ts +55 -0
- package/dist/modules/ai/index.d.ts.map +1 -0
- package/dist/modules/ai/index.js +263 -0
- package/dist/modules/ai/index.js.map +1 -0
- package/dist/modules/ai/types.d.ts +216 -0
- package/dist/modules/ai/types.d.ts.map +1 -0
- package/dist/modules/ai/types.js +30 -0
- package/dist/modules/ai/types.js.map +1 -0
- package/dist/modules/auth/backchannel.d.ts +85 -0
- package/dist/modules/auth/backchannel.d.ts.map +1 -0
- package/dist/modules/auth/backchannel.js +168 -0
- package/dist/modules/auth/backchannel.js.map +1 -0
- package/dist/modules/auth/config.d.ts +122 -0
- package/dist/modules/auth/config.d.ts.map +1 -0
- package/dist/modules/auth/config.js +158 -0
- package/dist/modules/auth/config.js.map +1 -0
- package/dist/modules/auth/middleware.d.ts +146 -0
- package/dist/modules/auth/middleware.d.ts.map +1 -0
- package/dist/modules/auth/middleware.js +204 -0
- package/dist/modules/auth/middleware.js.map +1 -0
- package/dist/modules/auth/types.d.ts +162 -0
- package/dist/modules/auth/types.d.ts.map +1 -0
- package/dist/modules/auth/types.js +14 -0
- package/dist/modules/auth/types.js.map +1 -0
- package/dist/modules/billing/types.d.ts +7 -0
- package/dist/modules/billing/types.d.ts.map +1 -0
- package/dist/modules/billing/types.js +7 -0
- package/dist/modules/billing/types.js.map +1 -0
- package/dist/modules/brainy/auth.d.ts +104 -0
- package/dist/modules/brainy/auth.d.ts.map +1 -0
- package/dist/modules/brainy/auth.js +144 -0
- package/dist/modules/brainy/auth.js.map +1 -0
- package/dist/modules/brainy/errors.d.ts +118 -0
- package/dist/modules/brainy/errors.d.ts.map +1 -0
- package/dist/modules/brainy/errors.js +142 -0
- package/dist/modules/brainy/errors.js.map +1 -0
- package/dist/modules/brainy/events.d.ts +63 -0
- package/dist/modules/brainy/events.d.ts.map +1 -0
- package/dist/modules/brainy/events.js +14 -0
- package/dist/modules/brainy/events.js.map +1 -0
- package/dist/modules/brainy/proxy.d.ts +48 -0
- package/dist/modules/brainy/proxy.d.ts.map +1 -0
- package/dist/modules/brainy/proxy.js +95 -0
- package/dist/modules/brainy/proxy.js.map +1 -0
- package/dist/modules/brainy/types.d.ts +83 -0
- package/dist/modules/brainy/types.d.ts.map +1 -0
- package/dist/modules/brainy/types.js +21 -0
- package/dist/modules/brainy/types.js.map +1 -0
- package/dist/modules/events/index.d.ts +41 -0
- package/dist/modules/events/index.d.ts.map +1 -0
- package/dist/modules/events/index.js +53 -0
- package/dist/modules/events/index.js.map +1 -0
- package/dist/modules/events/types.d.ts +129 -0
- package/dist/modules/events/types.d.ts.map +1 -0
- package/dist/modules/events/types.js +32 -0
- package/dist/modules/events/types.js.map +1 -0
- package/dist/modules/formats/types.d.ts +7 -0
- package/dist/modules/formats/types.d.ts.map +1 -0
- package/dist/modules/formats/types.js +7 -0
- package/dist/modules/formats/types.js.map +1 -0
- package/dist/modules/hall/types.d.ts +56 -0
- package/dist/modules/hall/types.d.ts.map +1 -0
- package/dist/modules/hall/types.js +16 -0
- package/dist/modules/hall/types.js.map +1 -0
- package/dist/modules/kits/types.d.ts +7 -0
- package/dist/modules/kits/types.d.ts.map +1 -0
- package/dist/modules/kits/types.js +7 -0
- package/dist/modules/kits/types.js.map +1 -0
- package/dist/modules/license/types.d.ts +7 -0
- package/dist/modules/license/types.d.ts.map +1 -0
- package/dist/modules/license/types.js +7 -0
- package/dist/modules/license/types.js.map +1 -0
- package/dist/modules/notifications/types.d.ts +7 -0
- package/dist/modules/notifications/types.d.ts.map +1 -0
- package/dist/modules/notifications/types.js +7 -0
- package/dist/modules/notifications/types.js.map +1 -0
- package/dist/modules/skills/index.d.ts +60 -0
- package/dist/modules/skills/index.d.ts.map +1 -0
- package/dist/modules/skills/index.js +253 -0
- package/dist/modules/skills/index.js.map +1 -0
- package/dist/modules/skills/types.d.ts +127 -0
- package/dist/modules/skills/types.d.ts.map +1 -0
- package/dist/modules/skills/types.js +23 -0
- package/dist/modules/skills/types.js.map +1 -0
- package/dist/modules/versions/types.d.ts +31 -0
- package/dist/modules/versions/types.d.ts.map +1 -0
- package/dist/modules/versions/types.js +9 -0
- package/dist/modules/versions/types.js.map +1 -0
- package/dist/modules/vfs/types.d.ts +26 -0
- package/dist/modules/vfs/types.d.ts.map +1 -0
- package/dist/modules/vfs/types.js +11 -0
- package/dist/modules/vfs/types.js.map +1 -0
- package/dist/server/create-sdk.d.ts +70 -0
- package/dist/server/create-sdk.d.ts.map +1 -0
- package/dist/server/create-sdk.js +125 -0
- package/dist/server/create-sdk.js.map +1 -0
- package/dist/server/hall-handlers.d.ts +195 -0
- package/dist/server/hall-handlers.d.ts.map +1 -0
- package/dist/server/hall-handlers.js +239 -0
- package/dist/server/hall-handlers.js.map +1 -0
- package/dist/server/handlers.d.ts +216 -0
- package/dist/server/handlers.d.ts.map +1 -0
- package/dist/server/handlers.js +214 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/index.d.ts +52 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +50 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/instance-pool.d.ts +299 -0
- package/dist/server/instance-pool.d.ts.map +1 -0
- package/dist/server/instance-pool.js +359 -0
- package/dist/server/instance-pool.js.map +1 -0
- package/dist/transports/http.d.ts +86 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +134 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/local.d.ts +76 -0
- package/dist/transports/local.d.ts.map +1 -0
- package/dist/transports/local.js +101 -0
- package/dist/transports/local.js.map +1 -0
- package/dist/transports/sse.d.ts +99 -0
- package/dist/transports/sse.d.ts.map +1 -0
- package/dist/transports/sse.js +192 -0
- package/dist/transports/sse.js.map +1 -0
- package/dist/transports/transport.d.ts +68 -0
- package/dist/transports/transport.d.ts.map +1 -0
- package/dist/transports/transport.js +14 -0
- package/dist/transports/transport.js.map +1 -0
- package/dist/transports/ws.d.ts +135 -0
- package/dist/transports/ws.d.ts.map +1 -0
- package/dist/transports/ws.js +331 -0
- package/dist/transports/ws.js.map +1 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/docs/ADR-001-sdk-design.md +282 -0
- package/docs/IMPLEMENTATION-PLAN.md +708 -0
- package/docs/USAGE.md +646 -0
- package/docs/kit-sdk-guide.md +474 -0
- package/package.json +61 -0
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
# @soulcraft/sdk — Detailed Implementation Plan
|
|
2
|
+
|
|
3
|
+
**Status:** Ready to implement
|
|
4
|
+
**Decisions:** See `ADR-001-sdk-design.md` for all architecture decisions and rationale.
|
|
5
|
+
**Research basis:** Full cross-project exploration of Workshop, Venue, Academy, brainy-client,
|
|
6
|
+
and @soulcraft/auth conducted 2026-02-27.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Source Material — Where Code Comes From
|
|
11
|
+
|
|
12
|
+
Every module in this SDK migrates or absorbs code from an existing location.
|
|
13
|
+
A new session MUST read the source file(s) before implementing each module.
|
|
14
|
+
|
|
15
|
+
| SDK Module | Migrates from | Key source files |
|
|
16
|
+
|------------|--------------|-----------------|
|
|
17
|
+
| `transports/local` | `@soulcraft/brainy-client` | `/media/dpsifr/storage/home/Projects/brainy-client/src/transports/local.ts` |
|
|
18
|
+
| `transports/http` | `@soulcraft/brainy-client` | `/media/dpsifr/storage/home/Projects/brainy-client/src/transports/http.ts` |
|
|
19
|
+
| `transports/ws` | `@soulcraft/brainy-client` | `/media/dpsifr/storage/home/Projects/brainy-client/src/transports/ws.ts` |
|
|
20
|
+
| `transports/sse` | Workshop events | `/media/dpsifr/storage/home/Projects/workshop/src/lib/services/vfsEventService.ts` |
|
|
21
|
+
| `modules/brainy` | `@soulcraft/brainy-client` | `/media/dpsifr/storage/home/Projects/brainy-client/src/index.ts` + Brainy types |
|
|
22
|
+
| `modules/vfs` | Brainy VFS sub-API | `node_modules/@soulcraft/brainy/dist/brainy.d.ts` (brain.vfs.*) |
|
|
23
|
+
| `modules/versions` | Brainy versions sub-API | same (brain.versions.*) |
|
|
24
|
+
| `server/instance-pool` | Workshop singleton + Venue cache | `/media/dpsifr/storage/home/Projects/workshop/brainy/brainy-singleton.ts` + `/home/dpsifr/Projects/venue/apps/web/server.ts` |
|
|
25
|
+
| `modules/auth` | `@soulcraft/auth` + Workshop auth | `/media/dpsifr/storage/home/Projects/auth/packages/auth/src/` + `/media/dpsifr/storage/home/Projects/workshop/auth/better-auth.ts` |
|
|
26
|
+
| `modules/license` | Workshop usage + billing | `/media/dpsifr/storage/home/Projects/workshop/brainy/usage-tracking.ts` + `brainy/config-service.ts` |
|
|
27
|
+
| `modules/ai` | Workshop chat | `/media/dpsifr/storage/home/Projects/workshop/chat/` (models.ts, delegator.ts, executor.ts, planner.ts, tools.ts, ui-actions.ts) + `routes/briggy.ts` |
|
|
28
|
+
| `modules/events` | Workshop data-change-emitter | `/media/dpsifr/storage/home/Projects/workshop/events/data-change-emitter.ts` |
|
|
29
|
+
| `modules/skills` | Workshop skill service | `/media/dpsifr/storage/home/Projects/workshop/brainy/skill-service.ts` + `brainy/bundled-skills.ts` |
|
|
30
|
+
| `modules/kits` | Workshop kit loader | `/media/dpsifr/storage/home/Projects/workshop/src/lib/templates/loader.ts` |
|
|
31
|
+
| `modules/formats` | @soulcraft/views + Workshop types | `/media/dpsifr/storage/home/Projects/workshop/packages/views/src/shared/types.ts` + `src/lib/types/richDocument.ts` |
|
|
32
|
+
| `modules/billing` | Workshop billing | `/media/dpsifr/storage/home/Projects/workshop/brainy/usage-tracking.ts` + `brainy/billing-provider.ts` + `brainy/config-service.ts` + `brainy/config-defaults.ts` |
|
|
33
|
+
| `modules/notifications` | Venue notification service | `/home/dpsifr/Projects/venue/apps/web/src/lib/server/notifications/` |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Phase 1 — Foundation ✅ COMPLETE
|
|
38
|
+
|
|
39
|
+
All items done. Repo created, ADR written, scaffold committed.
|
|
40
|
+
|
|
41
|
+
Remaining scaffolding items (minor, do at start of Phase 2):
|
|
42
|
+
- [ ] `vitest.config.ts` — test runner config
|
|
43
|
+
- [ ] `.env.example` — document any env vars the SDK needs
|
|
44
|
+
- [ ] Update `src/modules/*/types.ts` placeholder comments as modules are implemented
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Phase 2 — Core Data Layer
|
|
49
|
+
|
|
50
|
+
**Goal:** The SDK can connect to Brainy via all four transports and expose the full
|
|
51
|
+
`sdk.brainy.*`, `sdk.vfs.*`, `sdk.versions.*` API surface. Server mode can create and
|
|
52
|
+
pool Brainy instances with all three strategies.
|
|
53
|
+
|
|
54
|
+
### 2a. Transport Layer (`src/transports/`)
|
|
55
|
+
|
|
56
|
+
**Source:** `/media/dpsifr/storage/home/Projects/brainy-client/src/transports/`
|
|
57
|
+
|
|
58
|
+
Four files to create:
|
|
59
|
+
|
|
60
|
+
#### `src/transports/transport.ts` — Shared interface
|
|
61
|
+
```typescript
|
|
62
|
+
interface Transport {
|
|
63
|
+
call(method: string, args: unknown[]): Promise<unknown>
|
|
64
|
+
onEvent(handler: (event: BrainyChangeEvent) => void): void
|
|
65
|
+
offEvent(handler: (event: BrainyChangeEvent) => void): void
|
|
66
|
+
isAlive(): boolean
|
|
67
|
+
close(): Promise<void>
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### `src/transports/local.ts` — In-process, zero overhead
|
|
72
|
+
- Wraps a native Brainy instance
|
|
73
|
+
- Resolves dot-separated method names (e.g. `'vfs.readdir'`) by property traversal
|
|
74
|
+
- `call()` directly invokes `target[method].apply(target, args)`
|
|
75
|
+
- `onEvent`/`offEvent` are no-ops (in-process changes are synchronous)
|
|
76
|
+
- `isAlive()` always true, `close()` is a no-op
|
|
77
|
+
- **Migrate directly from brainy-client local.ts — minimal changes**
|
|
78
|
+
|
|
79
|
+
#### `src/transports/http.ts` — JSON RPC over HTTP
|
|
80
|
+
- POSTs to `{baseUrl}/api/brainy/rpc`
|
|
81
|
+
- Request: `{ method: string, args: unknown[] }` — plain JSON
|
|
82
|
+
- Response: `{ result: unknown }` or `{ error: { code: string, message: string } }`
|
|
83
|
+
- Auth: `Authorization: Bearer <token>` or `credentials: 'include'` (session cookies)
|
|
84
|
+
- 30s timeout via AbortController
|
|
85
|
+
- Error mapping: 401 → `SDKAuthError`, 403 → `SDKForbiddenError`, timeout → `SDKTimeoutError`
|
|
86
|
+
- No push events — stateless
|
|
87
|
+
- **Migrate directly from brainy-client http.ts — rename error classes**
|
|
88
|
+
|
|
89
|
+
#### `src/transports/ws.ts` — MessagePack binary RPC + change push
|
|
90
|
+
- Binary MessagePack via `@msgpack/msgpack`
|
|
91
|
+
- Client → Server: `{ id: string, method: string, args: unknown[] }` (encoded)
|
|
92
|
+
- Server → Client:
|
|
93
|
+
- Ready: `{ type: 'ready', scope: string }`
|
|
94
|
+
- RPC response: `{ id: string, result?: unknown, error?: { code, message } }`
|
|
95
|
+
- Change push: `{ type: 'change', event: 'add'|'update'|'delete'|'relate'|'unrelate', entity?, relation? }`
|
|
96
|
+
- Auth: `Authorization: Bearer <token>` on upgrade request
|
|
97
|
+
- Scope: `?scope=<tenantSlug>` query param
|
|
98
|
+
- Reconnection: exponential backoff 1s → 30s, max 10 attempts; never retry on 4001/4003
|
|
99
|
+
- Connection states: `'disconnected' | 'connecting' | 'connected' | 'closed'`
|
|
100
|
+
- `connect()` resolves on `ready` message (15s timeout)
|
|
101
|
+
- **Migrate directly from brainy-client ws.ts — minimal changes**
|
|
102
|
+
|
|
103
|
+
#### `src/transports/sse.ts` — Server-push event stream (NEW — not in brainy-client)
|
|
104
|
+
- Connects to `{baseUrl}/api/workspace/events` via `EventSource`
|
|
105
|
+
- Listens for VFS and entity change events
|
|
106
|
+
- Reconnects automatically (EventSource handles this natively)
|
|
107
|
+
- Emits typed `DataChangeEvent` objects to registered handlers
|
|
108
|
+
- Does NOT support RPC (send-only from server) — complement to http transport
|
|
109
|
+
- Based on: Workshop's `src/lib/services/vfsEventService.ts`
|
|
110
|
+
|
|
111
|
+
### 2b. Brainy API Proxy (`src/modules/brainy/`)
|
|
112
|
+
|
|
113
|
+
**Source:** `/media/dpsifr/storage/home/Projects/brainy-client/src/index.ts` + Brainy types
|
|
114
|
+
|
|
115
|
+
The core of the SDK. A JavaScript `Proxy` that intercepts any property access and routes
|
|
116
|
+
it to the active transport via `transport.call(method, args)`.
|
|
117
|
+
|
|
118
|
+
#### `src/modules/brainy/proxy.ts`
|
|
119
|
+
```typescript
|
|
120
|
+
// Creates a recursive Proxy that maps any property chain to transport.call()
|
|
121
|
+
// client.find(...) → transport.call('find', [...])
|
|
122
|
+
// client.versions.save(...) → transport.call('versions.save', [...])
|
|
123
|
+
// Special root properties handled locally:
|
|
124
|
+
// close(), isAlive(), onDataChange(), offDataChange(), _transport, then
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### `src/modules/brainy/types.ts` — Full Brainy type surface
|
|
128
|
+
All types imported or re-exported from `@soulcraft/brainy`:
|
|
129
|
+
- `Entity<T>`, `Relation<T>`, `Result<T>`, `Vector`
|
|
130
|
+
- `NounType`, `VerbType` (enums)
|
|
131
|
+
- `AddParams`, `UpdateParams`, `FindParams`, `SimilarParams`, `RelateParams`
|
|
132
|
+
- `GetOptions`, `GetRelationsParams`, `FindDuplicatesOptions`
|
|
133
|
+
- `BatchResult`, `IndexStats`, `DiagnosticsResult`, `MigrationResult`
|
|
134
|
+
- `BrainyChangeEvent`, `BrainyChangeEntity`, `BrainyChangeRelation` (from brainy-client)
|
|
135
|
+
- `SoulcraftBrainy` — the full SDK interface (extends `Pick<Brainy, all methods>` + change handlers)
|
|
136
|
+
|
|
137
|
+
Full method list (all must be proxied):
|
|
138
|
+
`add`, `get`, `batchGet`, `update`, `delete`, `find`, `similar`, `embed`, `embedBatch`,
|
|
139
|
+
`similarity`, `relate`, `unrelate`, `getRelations`, `neighbors`, `addMany`, `deleteMany`,
|
|
140
|
+
`updateMany`, `relateMany`, `indexStats`, `findDuplicates`, `cluster`, `defineAggregate`,
|
|
141
|
+
`removeAggregate`, `flush`, `migrate`, `diagnostics`, `clear`
|
|
142
|
+
|
|
143
|
+
Sub-APIs (also proxied via dot-notation):
|
|
144
|
+
`vfs.*`, `versions.*`, `neural()`, `counts.*`
|
|
145
|
+
|
|
146
|
+
#### `src/modules/brainy/auth.ts` — Capability tokens
|
|
147
|
+
Migrated from `brainy-client/src/auth.ts`:
|
|
148
|
+
- `createCapabilityToken(options)` — HMAC-SHA256, `<base64url(payload)>.<base64url(sig)>`
|
|
149
|
+
- `verifyCapabilityToken(token, secret)` — constant-time comparison, expiry check
|
|
150
|
+
- `CreateCapabilityTokenOptions`, `CapabilityTokenClaims` types
|
|
151
|
+
- Default TTL: 1 hour
|
|
152
|
+
|
|
153
|
+
#### `src/modules/brainy/errors.ts`
|
|
154
|
+
- `SDKError` (base)
|
|
155
|
+
- `SDKAuthError` (401)
|
|
156
|
+
- `SDKForbiddenError` (403)
|
|
157
|
+
- `SDKTimeoutError`
|
|
158
|
+
- `SDKDisconnectedError`
|
|
159
|
+
- `SDKRpcError` (server returned error object)
|
|
160
|
+
|
|
161
|
+
### 2c. VFS Namespace (`src/modules/vfs/`)
|
|
162
|
+
|
|
163
|
+
**Source:** `node_modules/@soulcraft/brainy` — `brain.vfs.*` sub-API
|
|
164
|
+
|
|
165
|
+
Thin namespace that delegates to `sdk.brainy.vfs.*` (which routes through the proxy).
|
|
166
|
+
Exists as a first-class namespace so callers write `sdk.vfs.readFile()` not
|
|
167
|
+
`sdk.brainy.vfs.readFile()`.
|
|
168
|
+
|
|
169
|
+
Full method surface (from Brainy's `VirtualFileSystem`):
|
|
170
|
+
`readFile`, `writeFile`, `appendFile`, `unlink`, `mkdir`, `rmdir`, `readdir`, `stat`,
|
|
171
|
+
`exists`, `search`, `rename`, `copy`, `move`, `getDirectChildren`, `getTreeStructure`,
|
|
172
|
+
`getDescendants`, `getEntity`, `resolvePath`, `watch`, `getTodos`
|
|
173
|
+
|
|
174
|
+
Types to re-export: `VFSStats`, `VFSDirent`, `VFSEntity`, `VFSMetadata`
|
|
175
|
+
|
|
176
|
+
### 2d. Versions Namespace (`src/modules/versions/`)
|
|
177
|
+
|
|
178
|
+
Same pattern as vfs — thin namespace delegating to `sdk.brainy.versions.*`.
|
|
179
|
+
|
|
180
|
+
Full method surface (from Brainy's `VersioningAPI`):
|
|
181
|
+
`save`, `list`, `getVersion`, `restore`, `compare`, `prune`
|
|
182
|
+
|
|
183
|
+
Types: `EntityVersion`, `VersionDiff`
|
|
184
|
+
|
|
185
|
+
### 2e. Server Mode Instance Pool (`src/server/instance-pool.ts`)
|
|
186
|
+
|
|
187
|
+
**Source:** Workshop's `brainy/brainy-singleton.ts` + Venue's `server.ts` tenantCache
|
|
188
|
+
|
|
189
|
+
This is the most complex piece in Phase 2. Manages a pool of Brainy instances with
|
|
190
|
+
three strategies.
|
|
191
|
+
|
|
192
|
+
#### Configuration
|
|
193
|
+
```typescript
|
|
194
|
+
interface InstancePoolConfig {
|
|
195
|
+
storage: 'filesystem' | 'mmap-filesystem'
|
|
196
|
+
dataPath: string
|
|
197
|
+
strategy: 'per-user' | 'per-tenant' | 'per-scope'
|
|
198
|
+
scopeKey?: (userId: string, workspaceId: string) => string
|
|
199
|
+
maxInstances: number // default: 200 (Workshop), 50 (Venue)
|
|
200
|
+
flushOnEvict: boolean // default: true
|
|
201
|
+
cortexActivationFile?: string // path to .soulcraft.json
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### API
|
|
206
|
+
```typescript
|
|
207
|
+
class BrainyInstancePool {
|
|
208
|
+
async forUser(userId: string, workspaceId: string): Promise<Brainy>
|
|
209
|
+
async forTenant(tenantSlug: string): Promise<Brainy>
|
|
210
|
+
async forScope(key: string, factory: () => Promise<Brainy>): Promise<Brainy>
|
|
211
|
+
async flush(key: string): Promise<void>
|
|
212
|
+
async flushAll(): Promise<void>
|
|
213
|
+
async shutdown(): Promise<void> // flush all + release
|
|
214
|
+
getStats(): { size: number, maxSize: number, keys: string[] }
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Implementation notes
|
|
219
|
+
- LRU cache with `lru-cache` package (already in dependencies)
|
|
220
|
+
- On eviction: call `brain.flush()` if `flushOnEvict: true`
|
|
221
|
+
- On `forUser` in `per-user` mode: key = `${emailHash}:${workspaceId}`
|
|
222
|
+
- Storage path: `{dataPath}/{emailHash}/{workspaceId}/`
|
|
223
|
+
- On `forTenant`: key = `tenantSlug`, path: `{dataPath}/{tenantSlug}/`
|
|
224
|
+
- Cortex activation: load `.soulcraft.json`, pass plugin to Brainy constructor
|
|
225
|
+
- First `forUser/forTenant` call initializes Brainy (25-30s cold start — normal)
|
|
226
|
+
|
|
227
|
+
### 2f. Server RPC + WS Handler Factories (`src/server/handlers.ts`)
|
|
228
|
+
|
|
229
|
+
Migrated from `brainy-client/src/server/`:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Hono-compatible HTTP RPC handler:
|
|
233
|
+
createBrainyHandler(config: BrainyHandlerConfig): (c: Context) => Promise<Response>
|
|
234
|
+
|
|
235
|
+
// Bun WebSocket handler:
|
|
236
|
+
createBrainyWsHandler(config: BrainyWsHandlerConfig): BrainyWsHandler
|
|
237
|
+
|
|
238
|
+
// SvelteKit adapter:
|
|
239
|
+
sveltekitBrainyHandler(config): RequestHandler
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Config shapes:
|
|
243
|
+
```typescript
|
|
244
|
+
interface BrainyHandlerConfig {
|
|
245
|
+
getBrainy: (scope: string) => Promise<Brainy> // pool.forTenant(scope)
|
|
246
|
+
verifyToken: (token: string) => Promise<CapabilityTokenClaims | null>
|
|
247
|
+
blockedMethods?: string[] // e.g. ['clear'] — default []
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 2g. Tests (Phase 2)
|
|
252
|
+
|
|
253
|
+
- `tests/transports/local.test.ts` — call(), onEvent(), close()
|
|
254
|
+
- `tests/transports/http.test.ts` — mock fetch, JSON RPC, error mapping, auth header
|
|
255
|
+
- `tests/transports/ws.test.ts` — mock WebSocket, MessagePack encode/decode, change push, reconnection
|
|
256
|
+
- `tests/transports/sse.test.ts` — mock EventSource, event parsing, reconnect
|
|
257
|
+
- `tests/brainy/proxy.test.ts` — method proxying, dot-notation sub-APIs, special properties
|
|
258
|
+
- `tests/brainy/auth.test.ts` — createCapabilityToken, verifyCapabilityToken, expiry, timing-safe
|
|
259
|
+
- `tests/server/instance-pool.test.ts` — per-user, per-tenant, per-scope, LRU eviction, flush
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Phase 3 — Auth + License
|
|
264
|
+
|
|
265
|
+
**Goal:** `sdk.auth.*` and `sdk.license.*` are fully functional. Products can drop
|
|
266
|
+
their own auth config and use the SDK middleware directly.
|
|
267
|
+
|
|
268
|
+
### 3a. Auth Module (`src/modules/auth/`)
|
|
269
|
+
|
|
270
|
+
**Source:** `/media/dpsifr/storage/home/Projects/auth/packages/auth/src/` +
|
|
271
|
+
`/media/dpsifr/storage/home/Projects/workshop/auth/better-auth.ts`
|
|
272
|
+
|
|
273
|
+
#### `src/modules/auth/types.ts`
|
|
274
|
+
All types from `@soulcraft/auth`:
|
|
275
|
+
- `PlatformRole` — `'creator' | 'viewer' | 'customer' | 'staff' | 'manager' | 'owner' | 'learner' | 'instructor'`
|
|
276
|
+
- `SoulcraftProduct` — `'workshop' | 'venue' | 'academy' | 'portal'`
|
|
277
|
+
- `SoulcraftUserFields` — `{ platformRole, emailHash }`
|
|
278
|
+
- `SoulcraftSessionUser` — resolved session user with all platform fields
|
|
279
|
+
- `SoulcraftSession` — `{ user, sessionId, expiresAt }`
|
|
280
|
+
- `OIDCClientConfig` — `{ idpUrl, clientId, clientSecret, redirectUri? }`
|
|
281
|
+
- `AuthMode` — `'oidc-client'` (always — we dropped standalone per decision 8)
|
|
282
|
+
|
|
283
|
+
#### `src/modules/auth/config.ts`
|
|
284
|
+
From `@soulcraft/auth/config`:
|
|
285
|
+
- `SOULCRAFT_USER_FIELDS` — better-auth `additionalFields` definition
|
|
286
|
+
- `SOULCRAFT_SESSION_CONFIG` — 30-day sessions, 24-hour rolling refresh
|
|
287
|
+
- `computeEmailHash(email: string): string` — SHA-256 hex of lowercased+trimmed email
|
|
288
|
+
- `getOIDCClientConfig(): OIDCClientConfig` — reads from env
|
|
289
|
+
|
|
290
|
+
#### `src/modules/auth/better-auth.ts`
|
|
291
|
+
Creates the better-auth instance for a product:
|
|
292
|
+
```typescript
|
|
293
|
+
createProductAuth(options: {
|
|
294
|
+
product: SoulcraftProduct
|
|
295
|
+
clientId: string
|
|
296
|
+
clientSecret: string
|
|
297
|
+
dbPath?: string
|
|
298
|
+
defaultRole?: PlatformRole
|
|
299
|
+
}): BetterAuth
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Includes: OIDC client plugin (`genericOAuth`), PKCE, `crossSubDomainCookies`,
|
|
303
|
+
`additionalFields` (platformRole + emailHash), `databaseHooks.user.create.before`
|
|
304
|
+
(computes emailHash, sets defaultRole).
|
|
305
|
+
|
|
306
|
+
#### `src/modules/auth/middleware.ts`
|
|
307
|
+
```typescript
|
|
308
|
+
// Hono middleware:
|
|
309
|
+
createAuthMiddleware(auth: BetterAuth): MiddlewareHandler
|
|
310
|
+
requireAuth(auth: BetterAuth): MiddlewareHandler
|
|
311
|
+
optionalAuth(auth: BetterAuth): MiddlewareHandler
|
|
312
|
+
|
|
313
|
+
// SvelteKit handle:
|
|
314
|
+
createAuthHandle(auth: BetterAuth): Handle
|
|
315
|
+
|
|
316
|
+
// Session proxy (for products verifying sessions against auth.soulcraft.com):
|
|
317
|
+
createRemoteSessionVerifier(options: {
|
|
318
|
+
idpUrl: string
|
|
319
|
+
cacheTtlMs?: number // default 30 000
|
|
320
|
+
cacheMax?: number // default 500
|
|
321
|
+
}): (cookieHeader: string) => Promise<SoulcraftSession | null>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### `src/modules/auth/backchannel.ts`
|
|
325
|
+
```typescript
|
|
326
|
+
// Hono route handler for POST /api/auth/backchannel-logout:
|
|
327
|
+
createBackchannelLogoutHandler(options: {
|
|
328
|
+
auth: BetterAuth
|
|
329
|
+
clientSecret: string
|
|
330
|
+
idpUrl: string
|
|
331
|
+
clientId: string
|
|
332
|
+
}): (c: Context) => Promise<Response>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Implements the full verification flow: parse logout_token, verify HS256 JWT,
|
|
336
|
+
validate iss/aud/events claims, delete all sessions for the subject user.
|
|
337
|
+
|
|
338
|
+
### 3b. License Module (`src/modules/license/`)
|
|
339
|
+
|
|
340
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/brainy/usage-tracking.ts` +
|
|
341
|
+
`brainy/config-service.ts` + `brainy/billing-provider.ts` + `brainy/config-defaults.ts`
|
|
342
|
+
|
|
343
|
+
#### `src/modules/license/cortex.ts`
|
|
344
|
+
```typescript
|
|
345
|
+
// Activate Cortex from .soulcraft.json activation code:
|
|
346
|
+
activateCortex(options: {
|
|
347
|
+
activationFile?: string // default './.soulcraft.json'
|
|
348
|
+
portalUrl?: string // default 'https://soulcraft.com'
|
|
349
|
+
}): Promise<CortexPlugin>
|
|
350
|
+
|
|
351
|
+
// Background heartbeat:
|
|
352
|
+
startHeartbeat(plugin: CortexPlugin, intervalMs?: number): () => void // returns stop fn
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### `src/modules/license/plan.ts`
|
|
356
|
+
```typescript
|
|
357
|
+
// Plan verification via Portal:
|
|
358
|
+
getLicensePlan(userId: string): Promise<LicensePlan>
|
|
359
|
+
// LicensePlan: { plan: 'free'|'standard'|'pro', status: 'active'|'expired'|'cancelled',
|
|
360
|
+
// features: { cortex: boolean, ai: { credits: number } } }
|
|
361
|
+
|
|
362
|
+
isLicenseActive(userId: string): Promise<boolean>
|
|
363
|
+
getLicenseFeatures(userId: string): Promise<LicenseFeatures>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### `src/modules/license/credits.ts`
|
|
367
|
+
```typescript
|
|
368
|
+
// AI usage metering (migrated from usage-tracking.ts):
|
|
369
|
+
checkAICredits(userId: string): Promise<number> // remaining credits
|
|
370
|
+
consumeAICredit(userId: string, cost: { inputTokens: number, outputTokens: number }): Promise<void>
|
|
371
|
+
canAccessAI(userId: string): Promise<boolean>
|
|
372
|
+
getModelForUser(userId: string): Promise<string> // platform model or BYOK
|
|
373
|
+
recordMessageUsage(userId: string, usage: UsageRecord): Promise<void>
|
|
374
|
+
getUsageStatus(userId: string): Promise<UsageStatus>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### `src/modules/license/byok.ts`
|
|
378
|
+
```typescript
|
|
379
|
+
// BYOK (Bring Your Own Key) API key management:
|
|
380
|
+
validateBYOK(apiKey: string): Promise<boolean>
|
|
381
|
+
getAIProvider(userId: string): Promise<AIProviderConfig>
|
|
382
|
+
// AIProviderConfig: { apiKey: string, source: 'platform' | 'byok' }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 3c. Tests (Phase 3)
|
|
386
|
+
|
|
387
|
+
- `tests/auth/config.test.ts` — computeEmailHash, SOULCRAFT_SESSION_CONFIG
|
|
388
|
+
- `tests/auth/middleware.test.ts` — requireAuth 401, optionalAuth null, session extraction
|
|
389
|
+
- `tests/auth/backchannel.test.ts` — JWT verification, claims validation, session deletion
|
|
390
|
+
- `tests/auth/remote-session.test.ts` — LRU cache, TTL, proxy to IdP
|
|
391
|
+
- `tests/license/cortex.test.ts` — activation file parsing, mock Portal exchange
|
|
392
|
+
- `tests/license/credits.test.ts` — quota check, credit consumption, usage recording
|
|
393
|
+
- `tests/license/byok.test.ts` — key validation, provider selection
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Phase 4 — AI + Events + Skills + Kits + Formats
|
|
398
|
+
|
|
399
|
+
**Goal:** The full AI integration, event bus, skill system, kit loader, and Soulcraft
|
|
400
|
+
format types are available in the SDK.
|
|
401
|
+
|
|
402
|
+
### 4a. AI Module (`src/modules/ai/`)
|
|
403
|
+
|
|
404
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/chat/`
|
|
405
|
+
|
|
406
|
+
#### `src/modules/ai/models.ts`
|
|
407
|
+
From `chat/models.ts`:
|
|
408
|
+
- `MODEL_TIERS` — `{ planner: 'claude-opus-4-6', triage: 'claude-sonnet-4-6', executor: 'claude-haiku-4-5-20251001', fallback: 'claude-haiku-4-5-20251001' }`
|
|
409
|
+
- `estimateCost(inputTokens, outputTokens, model)` → `number`
|
|
410
|
+
- `formatCost(cost: number)` → `string`
|
|
411
|
+
- `ModelRole` type
|
|
412
|
+
|
|
413
|
+
#### `src/modules/ai/delegator.ts`
|
|
414
|
+
From `chat/delegator.ts` + `chat/executor.ts` + `chat/planner.ts`:
|
|
415
|
+
- `createDelegator(options)` — tiered model routing (Sonnet triage → Haiku execute → Sonnet synthesize)
|
|
416
|
+
- `TriageDecision` — `'direct' | 'delegate_tasks' | 'recommend_plan'`
|
|
417
|
+
- Tool execution with auto-escalation
|
|
418
|
+
|
|
419
|
+
#### `src/modules/ai/tools.ts`
|
|
420
|
+
From `chat/tools.ts`:
|
|
421
|
+
- `ChatContext` type — `{ mode: 'file' | 'entity' | 'multi' | 'none', ... }`
|
|
422
|
+
- `KitAIContext` type — AI persona + domain knowledge + workflows + graph guidance
|
|
423
|
+
- `generateToolDefinitions(context: ChatContext)` — creates Anthropic tool definitions
|
|
424
|
+
- All tool definition schemas (Workshop-specific tools stay in Workshop)
|
|
425
|
+
|
|
426
|
+
#### `src/modules/ai/ui-actions.ts`
|
|
427
|
+
From `chat/ui-actions.ts`:
|
|
428
|
+
- `UI_ACTIONS` registry — all UI-controllable actions
|
|
429
|
+
- `generateUIToolDefinitions()` — auto-generates Claude tool definitions from registry
|
|
430
|
+
- `isUIAction(name)`, `getUIAction(name)` helpers
|
|
431
|
+
- `UIAction`, `UIActionParam` types
|
|
432
|
+
|
|
433
|
+
#### `src/modules/ai/briggy.ts`
|
|
434
|
+
From `routes/briggy.ts`:
|
|
435
|
+
- `BriggyPersona` type — name, description, systemPrompt, voice, etc.
|
|
436
|
+
- `createBriggyHandler(options)` — Hono route handler factory for `/api/briggy/*`
|
|
437
|
+
- `BriggyGenerateRequest`, `BriggyChatRequest`, `BriggyResponse` types
|
|
438
|
+
- Max tokens: 1024 default, 2048 ceiling
|
|
439
|
+
- Stateless generate + session-based chat endpoints
|
|
440
|
+
|
|
441
|
+
### 4b. Events Module (`src/modules/events/`)
|
|
442
|
+
|
|
443
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/events/data-change-emitter.ts`
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
// All 30+ event types:
|
|
447
|
+
type DataChangeType =
|
|
448
|
+
| 'vfs_write' | 'vfs_edit' | 'vfs_delete' | 'vfs_mkdir' | 'vfs_rename'
|
|
449
|
+
| 'vfs_copy' | 'vfs_upload' | 'vfs_bulk_write'
|
|
450
|
+
| 'entity_create' | 'entity_created' | 'entity_update' | 'entity_updated'
|
|
451
|
+
| 'entity_delete' | 'relationship_created' | 'relation_create' | 'relation_delete'
|
|
452
|
+
| 'activity_message_new' | 'activity_tool_new' | 'activity_task_new'
|
|
453
|
+
| 'activity_note_new' | 'activity_conversation_new'
|
|
454
|
+
| 'file_deleted' | 'directory_deleted' | 'file_renamed' | 'file_copied'
|
|
455
|
+
| 'directory_created' | 'data_imported'
|
|
456
|
+
|
|
457
|
+
interface DataChangeEvent {
|
|
458
|
+
type: DataChangeType
|
|
459
|
+
timestamp: number
|
|
460
|
+
userId: string
|
|
461
|
+
workspaceId?: string
|
|
462
|
+
data: { path?: string; id?: string; nounType?: string; [key: string]: unknown }
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Singleton emitter (server-side):
|
|
466
|
+
class PlatformEventBus extends EventEmitter {
|
|
467
|
+
emitChange(event: DataChangeEvent): void
|
|
468
|
+
onDataChange(handler: (event: DataChangeEvent) => void): void
|
|
469
|
+
offDataChange(handler: (event: DataChangeEvent) => void): void
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
createEventBus(): PlatformEventBus
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
SSE stream factory (for Hono endpoints):
|
|
476
|
+
```typescript
|
|
477
|
+
createSSEStream(options: {
|
|
478
|
+
bus: PlatformEventBus
|
|
479
|
+
filter: (event: DataChangeEvent) => boolean
|
|
480
|
+
heartbeatMs?: number // default 30 000
|
|
481
|
+
}): (c: Context) => Response
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### 4c. Skills Module (`src/modules/skills/`)
|
|
485
|
+
|
|
486
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/brainy/skill-service.ts` +
|
|
487
|
+
`brainy/bundled-skills.ts`
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// Skill metadata (from VFS frontmatter):
|
|
491
|
+
interface SkillMetadata {
|
|
492
|
+
id: string
|
|
493
|
+
name: string
|
|
494
|
+
scope: 'universal' | string // string = kitId
|
|
495
|
+
userInvocable: boolean
|
|
496
|
+
disableModelInvocation?: boolean
|
|
497
|
+
allowedTools?: string[]
|
|
498
|
+
argumentHint?: string
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
class SkillService {
|
|
502
|
+
constructor(private vfs: VfsModule) {}
|
|
503
|
+
listSkills(): Promise<SkillMetadata[]>
|
|
504
|
+
getActiveSkills(): Promise<SkillMetadata[]>
|
|
505
|
+
getUserInvocableSkills(): Promise<SkillMetadata[]>
|
|
506
|
+
getSkillContent(id: string): Promise<string> // the markdown content
|
|
507
|
+
createSkill(meta: SkillMetadata, content: string): Promise<void>
|
|
508
|
+
updateSkill(id: string, updates: Partial<SkillMetadata>, content?: string): Promise<void>
|
|
509
|
+
deleteSkill(id: string): Promise<void>
|
|
510
|
+
toggleSkill(id: string, enabled: boolean): Promise<void>
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Bundled skills (pre-packaged with SDK):
|
|
514
|
+
listBundledSkills(): BundledSkill[]
|
|
515
|
+
installBundledSkill(skillId: string, vfs: VfsModule): Promise<void>
|
|
516
|
+
installAllBundledSkills(vfs: VfsModule): Promise<void>
|
|
517
|
+
getMissingBundledSkills(vfs: VfsModule): Promise<BundledSkill[]>
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### 4d. Kits Module (`src/modules/kits/`)
|
|
521
|
+
|
|
522
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/src/lib/templates/loader.ts` +
|
|
523
|
+
`@soulcraft/kits` package
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
// Kit loading and initialization:
|
|
527
|
+
loadKit(kitId: string): Promise<SoulcraftKitConfig>
|
|
528
|
+
listKits(): Promise<SoulcraftKitConfig[]>
|
|
529
|
+
applyKit(kitId: string, brainy: SoulcraftBrainy, vfs: VfsModule): Promise<void>
|
|
530
|
+
|
|
531
|
+
// Types (re-exported from @soulcraft/kit-schema):
|
|
532
|
+
// SoulcraftKitConfig, WorkshopConfig, WorkshopWorkbench, WorkshopStarterFile,
|
|
533
|
+
// SharedDataModel, AcademyKitBlock, etc.
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### 4e. Formats Module (`src/modules/formats/`)
|
|
537
|
+
|
|
538
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/packages/views/src/shared/types.ts` +
|
|
539
|
+
`src/lib/types/richDocument.ts`
|
|
540
|
+
|
|
541
|
+
Centralizes all Soulcraft portable content format types:
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
// Already exists in @soulcraft/views — migrate here as canonical home:
|
|
545
|
+
export type { WvizDocument, WvizEntity, WvizEdge, WvizViewType } from './wviz.js'
|
|
546
|
+
|
|
547
|
+
// WDOC (Workshop Document — TipTap/ProseMirror JSON):
|
|
548
|
+
export type { WdocDocument, WdocNode, WdocMark } from './wdoc.js'
|
|
549
|
+
|
|
550
|
+
// WSLIDE (Workshop Slide):
|
|
551
|
+
export type { WslideDocument, WslideSlide, WslideBackground, WslideTransition } from './wslide.js'
|
|
552
|
+
|
|
553
|
+
// WQUIZ (Workshop Quiz):
|
|
554
|
+
export type { WquizDocument, WquizQuestion, WquizAnswer } from './wquiz.js'
|
|
555
|
+
|
|
556
|
+
// Common:
|
|
557
|
+
export type { SoulcraftFormat } from './common.js'
|
|
558
|
+
// SoulcraftFormat = 'wviz' | 'wdoc' | 'wslide' | 'wquiz' | 'wform'
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## Phase 5 — Billing + Notifications
|
|
564
|
+
|
|
565
|
+
### 5a. Billing Module (`src/modules/billing/`)
|
|
566
|
+
|
|
567
|
+
**Source:** `/media/dpsifr/storage/home/Projects/workshop/brainy/usage-tracking.ts` +
|
|
568
|
+
`brainy/billing-provider.ts` + `brainy/config-service.ts` + `brainy/config-defaults.ts`
|
|
569
|
+
|
|
570
|
+
Note: billing overlaps with license/credits (Phase 3). Phase 3 covers AI credit
|
|
571
|
+
metering. Phase 5 covers the full Stripe subscription + top-up flow.
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
// Usage tracking (complement to license/credits):
|
|
575
|
+
getCurrentPeriod(): string // 'YYYY-MM'
|
|
576
|
+
getPeriodEnd(userId: string): Promise<Date>
|
|
577
|
+
checkUsageLimits(usage: UsageData, limit: number): LimitCheckResult
|
|
578
|
+
|
|
579
|
+
// Stripe subscription management:
|
|
580
|
+
getSubscriptionStatus(userId: string): Promise<SubscriptionData>
|
|
581
|
+
subscribeToPlan(userId: string, priceId: string): Promise<{ url: string }>
|
|
582
|
+
cancelSubscription(userId: string): Promise<void>
|
|
583
|
+
getPortalUrl(userId: string): Promise<string>
|
|
584
|
+
|
|
585
|
+
// Top-up credits:
|
|
586
|
+
purchaseTopUp(userId: string, amount: number): Promise<void>
|
|
587
|
+
getTopUpBalance(userId: string): Promise<number>
|
|
588
|
+
addTopUp(userId: string, amount: number): Promise<void>
|
|
589
|
+
|
|
590
|
+
// Config (plans, pricing — from Firestore or defaults):
|
|
591
|
+
getPlatformConfig(): Promise<PlatformConfig>
|
|
592
|
+
getModelPricing(model: string): Promise<ModelPricing>
|
|
593
|
+
|
|
594
|
+
// Billing provider (Firestore vs local dev mode):
|
|
595
|
+
createBillingProvider(mode: 'firestore' | 'local'): BillingProvider
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### 5b. Notifications Module (`src/modules/notifications/`)
|
|
599
|
+
|
|
600
|
+
**Source:** `/home/dpsifr/Projects/venue/apps/web/src/lib/server/notifications/`
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
// Provider-agnostic interface:
|
|
604
|
+
interface NotificationSender {
|
|
605
|
+
send(notification: Notification): Promise<NotificationResult>
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
type Notification =
|
|
609
|
+
| { channel: 'email'; to: string; subject: string; html: string; text: string }
|
|
610
|
+
| { channel: 'sms'; to: string; body: string }
|
|
611
|
+
|
|
612
|
+
interface NotificationResult {
|
|
613
|
+
success: boolean
|
|
614
|
+
messageId?: string
|
|
615
|
+
error?: string
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Factory — auto-selects provider from env:
|
|
619
|
+
getNotificationSender(): NotificationSender
|
|
620
|
+
// - POSTMARK_API_KEY set → PostmarkSender
|
|
621
|
+
// - TWILIO_ACCOUNT_SID set → TwilioSender
|
|
622
|
+
// - neither → DevConsoleSender (logs to console)
|
|
623
|
+
|
|
624
|
+
// Individual providers (for explicit use):
|
|
625
|
+
createPostmarkSender(apiKey: string): NotificationSender
|
|
626
|
+
createTwilioSender(config: TwilioConfig): NotificationSender
|
|
627
|
+
createDevSender(): NotificationSender
|
|
628
|
+
|
|
629
|
+
// Template helpers (Venue's booking templates become generic):
|
|
630
|
+
renderEmailTemplate(template: string, data: Record<string, unknown>): string
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## Phase 6 — npm Publish + Migrate All Products
|
|
636
|
+
|
|
637
|
+
### 6a. Pre-publish checklist
|
|
638
|
+
- [ ] All tests passing (`bun test`)
|
|
639
|
+
- [ ] TypeScript builds cleanly (`bun run check`)
|
|
640
|
+
- [ ] No stubs, no TODOs, no `as any`
|
|
641
|
+
- [ ] JSDoc on every exported symbol
|
|
642
|
+
- [ ] Module-level `@module` block on every file
|
|
643
|
+
- [ ] `package.json` version set to `1.0.0`
|
|
644
|
+
- [ ] `README.md` written with usage examples for all three entry points
|
|
645
|
+
|
|
646
|
+
### 6b. Publish
|
|
647
|
+
```bash
|
|
648
|
+
npm publish --access restricted
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### 6c. Migrate Workshop
|
|
652
|
+
Files to update in `/media/dpsifr/storage/home/Projects/workshop/`:
|
|
653
|
+
1. `package.json` — add `@soulcraft/sdk`, remove `@soulcraft/brainy-client`, `@soulcraft/auth`
|
|
654
|
+
2. `build.sh` — add `sdk` to bundle loop, remove `brainy-client` and `auth`
|
|
655
|
+
3. `auth/better-auth.ts` — replace with `createProductAuth('workshop', ...)` from SDK
|
|
656
|
+
4. `server-hono.ts` — replace brainy-client handler factories with SDK handlers
|
|
657
|
+
5. `brainy/brainy-singleton.ts` — replace with `sdk.server.instancePool`
|
|
658
|
+
6. `brainy/remote-brainy.ts` — replace with SDK `WsTransport`
|
|
659
|
+
7. `brainy/usage-tracking.ts` — replace with `sdk.license.credits.*` + `sdk.billing.*`
|
|
660
|
+
8. All `import { ... } from '@soulcraft/auth'` → `from '@soulcraft/sdk'`
|
|
661
|
+
9. All `import { createBrainyClient } from '@soulcraft/brainy-client'` → `from '@soulcraft/sdk/client'`
|
|
662
|
+
|
|
663
|
+
### 6d. Migrate Venue
|
|
664
|
+
Files to update in `/home/dpsifr/Projects/venue/`:
|
|
665
|
+
1. `apps/web/package.json` — same package swap
|
|
666
|
+
2. `apps/web/server.ts` — replace brainy-client WS handler with SDK
|
|
667
|
+
3. `apps/web/src/lib/server/auth.ts` — replace with `createProductAuth('venue', ...)`
|
|
668
|
+
4. `apps/web/src/routes/api/brainy/rpc/+server.ts` — replace with SDK HTTP handler
|
|
669
|
+
5. All notification code → `sdk.notifications.*`
|
|
670
|
+
|
|
671
|
+
### 6e. Migrate Academy
|
|
672
|
+
Files to update in `/media/dpsifr/storage/home/Projects/academy/`:
|
|
673
|
+
1. `package.json`, `auth/better-auth.ts`, `server.ts` — same patterns as Workshop
|
|
674
|
+
|
|
675
|
+
### 6f. Post-migration
|
|
676
|
+
- Deprecate `@soulcraft/brainy-client` on npmjs.com (`npm deprecate`)
|
|
677
|
+
- Deprecate `@soulcraft/auth` on npmjs.com
|
|
678
|
+
- Update `PLATFORM-HANDOFF.md` thread to RESOLVED
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## Key Reference Files (Read Before Starting Each Phase)
|
|
683
|
+
|
|
684
|
+
A new session implementing any phase should read these files FIRST:
|
|
685
|
+
|
|
686
|
+
**Phase 2:**
|
|
687
|
+
- `/media/dpsifr/storage/home/Projects/brainy-client/src/` — all transport implementations
|
|
688
|
+
- `/media/dpsifr/storage/home/Projects/brainy-client/src/server/` — handler factories
|
|
689
|
+
- `/media/dpsifr/storage/home/Projects/workshop/brainy/brainy-singleton.ts` — instance pool pattern
|
|
690
|
+
- `node_modules/@soulcraft/brainy/dist/brainy.d.ts` — full Brainy type surface
|
|
691
|
+
|
|
692
|
+
**Phase 3:**
|
|
693
|
+
- `/media/dpsifr/storage/home/Projects/auth/packages/auth/src/` — shared auth types + config
|
|
694
|
+
- `/media/dpsifr/storage/home/Projects/workshop/auth/better-auth.ts` — product auth pattern
|
|
695
|
+
- `/media/dpsifr/storage/home/Projects/workshop/brainy/usage-tracking.ts` — credit metering
|
|
696
|
+
|
|
697
|
+
**Phase 4:**
|
|
698
|
+
- `/media/dpsifr/storage/home/Projects/workshop/chat/` — entire directory
|
|
699
|
+
- `/media/dpsifr/storage/home/Projects/workshop/events/data-change-emitter.ts`
|
|
700
|
+
- `/media/dpsifr/storage/home/Projects/workshop/brainy/skill-service.ts`
|
|
701
|
+
|
|
702
|
+
**Phase 5:**
|
|
703
|
+
- `/media/dpsifr/storage/home/Projects/workshop/brainy/billing-provider.ts`
|
|
704
|
+
- `/home/dpsifr/Projects/venue/apps/web/src/lib/server/notifications/`
|
|
705
|
+
|
|
706
|
+
**Phase 6:**
|
|
707
|
+
- Workshop's `build.sh` — bundle loop pattern
|
|
708
|
+
- All `import` statements in Workshop/Venue/Academy server files
|