@soulcraft/sdk 1.2.0 → 1.4.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 +6 -3
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -3
- package/dist/client/index.js.map +1 -1
- package/dist/modules/license/index.js +2 -2
- package/dist/modules/license/index.js.map +1 -1
- package/dist/server/from-license.d.ts +213 -0
- package/dist/server/from-license.d.ts.map +1 -0
- package/dist/server/from-license.js +305 -0
- package/dist/server/from-license.js.map +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/postmessage-handler.d.ts +127 -0
- package/dist/server/postmessage-handler.d.ts.map +1 -0
- package/dist/server/postmessage-handler.js +125 -0
- package/dist/server/postmessage-handler.js.map +1 -0
- package/dist/transports/postmessage.d.ts +153 -0
- package/dist/transports/postmessage.d.ts.map +1 -0
- package/dist/transports/postmessage.js +191 -0
- package/dist/transports/postmessage.js.map +1 -0
- package/package.json +1 -1
package/dist/client/index.d.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* @description Browser/client entry point for @soulcraft/sdk. Exports everything needed
|
|
4
4
|
* for kit apps running in a browser context to connect to a Soulcraft product backend.
|
|
5
5
|
*
|
|
6
|
-
* Includes: HTTP (JSON RPC), WebSocket (MessagePack binary RPC + change push),
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Includes: HTTP (JSON RPC), WebSocket (MessagePack binary RPC + change push), SSE
|
|
7
|
+
* (server-push), and PostMessage (WebContainer iframe ↔ parent frame) transports.
|
|
8
|
+
* Cookie-based and Bearer token auth. The Brainy proxy factory for creating a
|
|
9
|
+
* fully-typed `sdk.brainy` API surface over any transport.
|
|
9
10
|
*
|
|
10
11
|
* This entry point has no server-only dependencies — it is safe to bundle for the
|
|
11
12
|
* browser. The trade-off is that it cannot create Brainy instances directly.
|
|
@@ -53,6 +54,8 @@
|
|
|
53
54
|
export { HttpTransport } from '../transports/http.js';
|
|
54
55
|
export { WsTransport } from '../transports/ws.js';
|
|
55
56
|
export { SseTransport } from '../transports/sse.js';
|
|
57
|
+
export { PostMessageTransport } from '../transports/postmessage.js';
|
|
58
|
+
export type { PostMessageRequest, PostMessageResponse } from '../transports/postmessage.js';
|
|
56
59
|
export type { SDKTransport } from '../transports/transport.js';
|
|
57
60
|
export { createBrainyProxy } from '../modules/brainy/proxy.js';
|
|
58
61
|
export { joinHallRoom } from '../modules/hall/browser.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AACnE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAC3F,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,YAAY,GACb,MAAM,0BAA0B,CAAA;AAGjC,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AACjE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,6BAA6B,CAAA"}
|
package/dist/client/index.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* @description Browser/client entry point for @soulcraft/sdk. Exports everything needed
|
|
4
4
|
* for kit apps running in a browser context to connect to a Soulcraft product backend.
|
|
5
5
|
*
|
|
6
|
-
* Includes: HTTP (JSON RPC), WebSocket (MessagePack binary RPC + change push),
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Includes: HTTP (JSON RPC), WebSocket (MessagePack binary RPC + change push), SSE
|
|
7
|
+
* (server-push), and PostMessage (WebContainer iframe ↔ parent frame) transports.
|
|
8
|
+
* Cookie-based and Bearer token auth. The Brainy proxy factory for creating a
|
|
9
|
+
* fully-typed `sdk.brainy` API surface over any transport.
|
|
9
10
|
*
|
|
10
11
|
* This entry point has no server-only dependencies — it is safe to bundle for the
|
|
11
12
|
* browser. The trade-off is that it cannot create Brainy instances directly.
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
export { HttpTransport } from '../transports/http.js';
|
|
55
56
|
export { WsTransport } from '../transports/ws.js';
|
|
56
57
|
export { SseTransport } from '../transports/sse.js';
|
|
58
|
+
export { PostMessageTransport } from '../transports/postmessage.js';
|
|
57
59
|
// ── Proxy factory ─────────────────────────────────────────────────────────────
|
|
58
60
|
export { createBrainyProxy } from '../modules/brainy/proxy.js';
|
|
59
61
|
// ── Hall room client (browser WebRTC) ─────────────────────────────────────────
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAInE,iFAAiF;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAE9D,iFAAiF;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAoBzD,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,6BAA6B,CAAA"}
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
33
|
/** Default heartbeat interval: 4 hours. */
|
|
34
34
|
const DEFAULT_CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000;
|
|
35
|
-
/** Portal license validation endpoint. */
|
|
36
|
-
const PORTAL_LICENSE_URL = 'https://
|
|
35
|
+
/** Portal license validation endpoint (routed via soulcraft.com hosting). */
|
|
36
|
+
const PORTAL_LICENSE_URL = 'https://soulcraft.com/api/license/validate-sdk';
|
|
37
37
|
/**
|
|
38
38
|
* Validate the license key against Portal.
|
|
39
39
|
* Reads `SOULCRAFT_LICENSE_KEY` from the environment.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAaH,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF,2CAA2C;AAC3C,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAEpD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/license/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module server/from-license
|
|
3
|
+
* @description License-driven server SDK factory.
|
|
4
|
+
*
|
|
5
|
+
* `createServerSDK.fromLicense({ product })` is the recommended production entry point
|
|
6
|
+
* for product backends. It replaces the manual environment-variable wiring required by
|
|
7
|
+
* `createSDK()` with a single boot-time call to Portal:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* SOULCRAFT_LICENSE_KEY=SC-XXXX-XXXX-XXXX ← only secret needed
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* At startup the factory:
|
|
14
|
+
* 1. Reads `SOULCRAFT_LICENSE_KEY` from the environment.
|
|
15
|
+
* 2. POSTs `POST https://soulcraft.com/api/license/activate` with the key and product name.
|
|
16
|
+
* 3. Receives a config bundle (`anthropicApiKey`, `portalCreditSecret`, `hallSecret`, etc.).
|
|
17
|
+
* 4. Injects the bundle values into `process.env` so all SDK module factories pick them
|
|
18
|
+
* up automatically — no factory API changes required.
|
|
19
|
+
* 5. Writes the bundle to `.soulcraft-config.json` in the working directory for offline
|
|
20
|
+
* restarts (e.g. if Portal is temporarily unreachable after a product restart).
|
|
21
|
+
* 6. Schedules a background re-validation every 4 hours to pick up rotated secrets.
|
|
22
|
+
*
|
|
23
|
+
* The returned `ServerSDK` object is long-lived (one per process). Its `createSDK(brain)`
|
|
24
|
+
* method returns a per-request `SoulcraftSDK`, identical to calling `createSDK({ brain })`
|
|
25
|
+
* directly, but with all secrets already wired from the bundle.
|
|
26
|
+
*
|
|
27
|
+
* ## Backward compatibility
|
|
28
|
+
*
|
|
29
|
+
* `createSDK()` is unchanged — products that manage their own env vars continue to work
|
|
30
|
+
* exactly as before. `fromLicense()` is additive.
|
|
31
|
+
*
|
|
32
|
+
* ## Offline / degraded start
|
|
33
|
+
*
|
|
34
|
+
* If Portal is unreachable on first boot, the factory falls back to a cached bundle from
|
|
35
|
+
* `.soulcraft-config.json`. If no cache exists either, it throws — a product cannot start
|
|
36
|
+
* in production without a resolved config bundle.
|
|
37
|
+
*
|
|
38
|
+
* @example Academy startup
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { createServerSDK, BrainyInstancePool } from '@soulcraft/sdk/server'
|
|
41
|
+
*
|
|
42
|
+
* const server = await createServerSDK.fromLicense({ product: 'academy' })
|
|
43
|
+
*
|
|
44
|
+
* const pool = new BrainyInstancePool({
|
|
45
|
+
* storage: 'mmap-filesystem',
|
|
46
|
+
* dataPath: '/mnt/brainy-data',
|
|
47
|
+
* strategy: 'per-user',
|
|
48
|
+
* })
|
|
49
|
+
*
|
|
50
|
+
* // Per-request:
|
|
51
|
+
* app.get('/api/brainy', requireAuth, async (c) => {
|
|
52
|
+
* const brain = await pool.forUser(user.emailHash, 'main')
|
|
53
|
+
* const sdk = server.createSDK(brain)
|
|
54
|
+
* const items = await sdk.brainy.find({ query: 'candle inventory' })
|
|
55
|
+
* return c.json({ items })
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* // On shutdown:
|
|
59
|
+
* process.on('SIGTERM', () => server.shutdown())
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
import type { Brainy } from '@soulcraft/brainy';
|
|
63
|
+
import type { HallModule } from '../modules/hall/types.js';
|
|
64
|
+
import type { SoulcraftSDK, SoulcraftProduct } from '../types.js';
|
|
65
|
+
/**
|
|
66
|
+
* Config bundle returned by the Portal `licenseActivate` endpoint.
|
|
67
|
+
* Only non-null fields are included in the response.
|
|
68
|
+
*/
|
|
69
|
+
export interface LicenseConfigBundle {
|
|
70
|
+
/** Anthropic API key for `sdk.ai.*` calls. */
|
|
71
|
+
anthropicApiKey?: string;
|
|
72
|
+
/** Shared secret for the Portal credit API (`sdk.billing.*`). */
|
|
73
|
+
portalCreditSecret?: string;
|
|
74
|
+
/** WebSocket URL of the Hall server (e.g. `wss://hall.soulcraft.com`). */
|
|
75
|
+
hallUrl?: string;
|
|
76
|
+
/** Per-product shared secret for Hall authentication. */
|
|
77
|
+
hallSecret?: string;
|
|
78
|
+
/** Stripe secret key for `sdk.billing.*` Stripe operations. */
|
|
79
|
+
stripeSecretKey?: string;
|
|
80
|
+
/** Stripe webhook signing secret. */
|
|
81
|
+
stripeWebhookSecret?: string;
|
|
82
|
+
/** Workshop deploy bearer token secret. */
|
|
83
|
+
workshopDeploySecret?: string;
|
|
84
|
+
/** Academy publish bearer token secret. */
|
|
85
|
+
academyPublishSecret?: string;
|
|
86
|
+
/** Venue RPC capability token secret. */
|
|
87
|
+
venueRpcSecret?: string;
|
|
88
|
+
/** Postmark server token for email notifications. */
|
|
89
|
+
postmarkToken?: string;
|
|
90
|
+
/** From-address for Postmark email. */
|
|
91
|
+
postmarkFromEmail?: string;
|
|
92
|
+
/** Twilio account SID for SMS notifications. */
|
|
93
|
+
twilioSid?: string;
|
|
94
|
+
/** Twilio auth token for SMS notifications. */
|
|
95
|
+
twilioToken?: string;
|
|
96
|
+
/** Twilio from-number for SMS. */
|
|
97
|
+
twilioFromNumber?: string;
|
|
98
|
+
/** Firebase service account key (base64-encoded JSON) for Firestore billing. */
|
|
99
|
+
firebaseServiceAccountKey?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Options for `createServerSDK.fromLicense()`.
|
|
103
|
+
*/
|
|
104
|
+
export interface FromLicenseOptions {
|
|
105
|
+
/**
|
|
106
|
+
* The Soulcraft product name.
|
|
107
|
+
* Used to retrieve the correct per-product secrets (e.g. Hall secret, deploy secret)
|
|
108
|
+
* from the Portal config bundle.
|
|
109
|
+
*
|
|
110
|
+
* @example `'workshop'` | `'venue'` | `'academy'`
|
|
111
|
+
*/
|
|
112
|
+
product: SoulcraftProduct;
|
|
113
|
+
/**
|
|
114
|
+
* Override the license key.
|
|
115
|
+
* When not provided, `SOULCRAFT_LICENSE_KEY` is read from the environment.
|
|
116
|
+
*/
|
|
117
|
+
licenseKey?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A long-lived server SDK object returned by `createServerSDK.fromLicense()`.
|
|
121
|
+
*
|
|
122
|
+
* Wraps the Portal config bundle and exposes `createSDK(brain)` for per-request SDK
|
|
123
|
+
* construction. The Hall module (if configured) is available as `server.hall`.
|
|
124
|
+
*/
|
|
125
|
+
export interface ServerSDK {
|
|
126
|
+
/**
|
|
127
|
+
* Create a per-request `SoulcraftSDK` wrapping a live Brainy instance.
|
|
128
|
+
*
|
|
129
|
+
* All secrets are already injected from the license bundle — pass the resolved
|
|
130
|
+
* Brainy instance and the SDK is ready to use.
|
|
131
|
+
*
|
|
132
|
+
* @param brain - A Brainy instance from a `BrainyInstancePool`.
|
|
133
|
+
* @returns A fully assembled `SoulcraftSDK`.
|
|
134
|
+
*/
|
|
135
|
+
createSDK(brain: Brainy): SoulcraftSDK;
|
|
136
|
+
/**
|
|
137
|
+
* The Hall module, connected to the Hall server at startup.
|
|
138
|
+
* Present only when the license bundle includes `hallSecret` and `hallUrl`.
|
|
139
|
+
*/
|
|
140
|
+
readonly hall: HallModule | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* The resolved license config bundle.
|
|
143
|
+
* Contains all secrets injected into the environment at startup.
|
|
144
|
+
*/
|
|
145
|
+
readonly config: LicenseConfigBundle;
|
|
146
|
+
/**
|
|
147
|
+
* The product tier from the license response.
|
|
148
|
+
* `'free'` | `'pro'` | `'enterprise'` | `'internal'`
|
|
149
|
+
*/
|
|
150
|
+
readonly tier: string;
|
|
151
|
+
/**
|
|
152
|
+
* Gracefully shut down the server SDK.
|
|
153
|
+
*
|
|
154
|
+
* Stops the background re-validation timer and disconnects the Hall module.
|
|
155
|
+
*/
|
|
156
|
+
shutdown(): void;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Activate the SDK from a Soulcraft license key.
|
|
160
|
+
*
|
|
161
|
+
* Calls Portal's `licenseActivate` endpoint, injects the returned config bundle
|
|
162
|
+
* into `process.env`, and returns a `ServerSDK` ready for per-request use.
|
|
163
|
+
*
|
|
164
|
+
* Falls back to a cached bundle (`.soulcraft-config.json`) if Portal is unreachable.
|
|
165
|
+
* Throws if neither Portal nor a valid cache is available.
|
|
166
|
+
*
|
|
167
|
+
* @param options - Product name and optional license key override.
|
|
168
|
+
* @returns A `ServerSDK` instance, fully wired from the license bundle.
|
|
169
|
+
*
|
|
170
|
+
* @throws {Error} If the license key is invalid, expired, or Portal is unreachable with no cache.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const server = await createServerSDK.fromLicense({ product: 'academy' })
|
|
175
|
+
* const pool = new BrainyInstancePool({ storage: 'mmap-filesystem', dataPath: '/mnt/brainy-data', strategy: 'per-user' })
|
|
176
|
+
*
|
|
177
|
+
* app.use(requireAuth)
|
|
178
|
+
* app.get('/api/brainy', async (c) => {
|
|
179
|
+
* const brain = await pool.forUser(c.get('user').emailHash, 'main')
|
|
180
|
+
* const sdk = server.createSDK(brain)
|
|
181
|
+
* return c.json(await sdk.brainy.find({ query: c.req.query('q') ?? '' }))
|
|
182
|
+
* })
|
|
183
|
+
*
|
|
184
|
+
* process.on('SIGTERM', () => server.shutdown())
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare function fromLicense(options: FromLicenseOptions): Promise<ServerSDK>;
|
|
188
|
+
/**
|
|
189
|
+
* Server SDK factory namespace.
|
|
190
|
+
*
|
|
191
|
+
* The primary entry point is `createServerSDK.fromLicense({ product })`.
|
|
192
|
+
* For lower-level control (custom Brainy instance, no license key), use
|
|
193
|
+
* `createSDK({ brain })` directly.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* import { createServerSDK, BrainyInstancePool } from '@soulcraft/sdk/server'
|
|
198
|
+
*
|
|
199
|
+
* const server = await createServerSDK.fromLicense({ product: 'workshop' })
|
|
200
|
+
* const pool = new BrainyInstancePool({ storage: 'mmap-filesystem', dataPath: '/mnt/data', strategy: 'per-user' })
|
|
201
|
+
*
|
|
202
|
+
* app.get('/api/brainy', requireAuth, async (c) => {
|
|
203
|
+
* const brain = await pool.forUser(c.get('user').emailHash, 'main')
|
|
204
|
+
* const sdk = server.createSDK(brain)
|
|
205
|
+
* return c.json(await sdk.brainy.find({ query: c.req.query('q') ?? '' }))
|
|
206
|
+
* })
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export declare const createServerSDK: {
|
|
210
|
+
fromLicense: typeof fromLicense;
|
|
211
|
+
};
|
|
212
|
+
export {};
|
|
213
|
+
//# sourceMappingURL=from-license.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"from-license.d.ts","sourceRoot":"","sources":["../../src/server/from-license.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAG/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAmBjE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qCAAqC;IACrC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gFAAgF;IAChF,yBAAyB,CAAC,EAAE,MAAM,CAAA;CACnC;AAmBD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,OAAO,EAAE,gBAAgB,CAAA;IAEzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;;;OAQG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAA;IAEtC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,CAAA;IAErC;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAA;IAEpC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;;OAIG;IACH,QAAQ,IAAI,IAAI,CAAA;CACjB;AAuGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,iBAAe,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CA4G1E;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe;;CAE3B,CAAA"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module server/from-license
|
|
3
|
+
* @description License-driven server SDK factory.
|
|
4
|
+
*
|
|
5
|
+
* `createServerSDK.fromLicense({ product })` is the recommended production entry point
|
|
6
|
+
* for product backends. It replaces the manual environment-variable wiring required by
|
|
7
|
+
* `createSDK()` with a single boot-time call to Portal:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* SOULCRAFT_LICENSE_KEY=SC-XXXX-XXXX-XXXX ← only secret needed
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* At startup the factory:
|
|
14
|
+
* 1. Reads `SOULCRAFT_LICENSE_KEY` from the environment.
|
|
15
|
+
* 2. POSTs `POST https://soulcraft.com/api/license/activate` with the key and product name.
|
|
16
|
+
* 3. Receives a config bundle (`anthropicApiKey`, `portalCreditSecret`, `hallSecret`, etc.).
|
|
17
|
+
* 4. Injects the bundle values into `process.env` so all SDK module factories pick them
|
|
18
|
+
* up automatically — no factory API changes required.
|
|
19
|
+
* 5. Writes the bundle to `.soulcraft-config.json` in the working directory for offline
|
|
20
|
+
* restarts (e.g. if Portal is temporarily unreachable after a product restart).
|
|
21
|
+
* 6. Schedules a background re-validation every 4 hours to pick up rotated secrets.
|
|
22
|
+
*
|
|
23
|
+
* The returned `ServerSDK` object is long-lived (one per process). Its `createSDK(brain)`
|
|
24
|
+
* method returns a per-request `SoulcraftSDK`, identical to calling `createSDK({ brain })`
|
|
25
|
+
* directly, but with all secrets already wired from the bundle.
|
|
26
|
+
*
|
|
27
|
+
* ## Backward compatibility
|
|
28
|
+
*
|
|
29
|
+
* `createSDK()` is unchanged — products that manage their own env vars continue to work
|
|
30
|
+
* exactly as before. `fromLicense()` is additive.
|
|
31
|
+
*
|
|
32
|
+
* ## Offline / degraded start
|
|
33
|
+
*
|
|
34
|
+
* If Portal is unreachable on first boot, the factory falls back to a cached bundle from
|
|
35
|
+
* `.soulcraft-config.json`. If no cache exists either, it throws — a product cannot start
|
|
36
|
+
* in production without a resolved config bundle.
|
|
37
|
+
*
|
|
38
|
+
* @example Academy startup
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { createServerSDK, BrainyInstancePool } from '@soulcraft/sdk/server'
|
|
41
|
+
*
|
|
42
|
+
* const server = await createServerSDK.fromLicense({ product: 'academy' })
|
|
43
|
+
*
|
|
44
|
+
* const pool = new BrainyInstancePool({
|
|
45
|
+
* storage: 'mmap-filesystem',
|
|
46
|
+
* dataPath: '/mnt/brainy-data',
|
|
47
|
+
* strategy: 'per-user',
|
|
48
|
+
* })
|
|
49
|
+
*
|
|
50
|
+
* // Per-request:
|
|
51
|
+
* app.get('/api/brainy', requireAuth, async (c) => {
|
|
52
|
+
* const brain = await pool.forUser(user.emailHash, 'main')
|
|
53
|
+
* const sdk = server.createSDK(brain)
|
|
54
|
+
* const items = await sdk.brainy.find({ query: 'candle inventory' })
|
|
55
|
+
* return c.json({ items })
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* // On shutdown:
|
|
59
|
+
* process.on('SIGTERM', () => server.shutdown())
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
63
|
+
import { resolve } from 'node:path';
|
|
64
|
+
import { createSDK } from './create-sdk.js';
|
|
65
|
+
import { createHallModule } from './hall-handlers.js';
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
// Constants
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
/** Portal license activation endpoint. */
|
|
70
|
+
const ACTIVATE_URL = 'https://soulcraft.com/api/license/activate';
|
|
71
|
+
/** Re-validation interval: 4 hours. */
|
|
72
|
+
const REVALIDATE_INTERVAL_MS = 4 * 60 * 60 * 1000;
|
|
73
|
+
/** Path to the cached config bundle on disk. */
|
|
74
|
+
const CACHE_PATH = resolve(process.cwd(), '.soulcraft-config.json');
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// Internal helpers
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Call the Portal `licenseActivate` endpoint and return the full response.
|
|
80
|
+
*
|
|
81
|
+
* @throws {Error} If the network call fails or Portal returns a non-200 status.
|
|
82
|
+
*/
|
|
83
|
+
async function _activate(key, product) {
|
|
84
|
+
const response = await fetch(ACTIVATE_URL, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
headers: { 'Content-Type': 'application/json' },
|
|
87
|
+
body: JSON.stringify({ key, product, sdkVersion: '1.3.0' }),
|
|
88
|
+
signal: AbortSignal.timeout(15_000),
|
|
89
|
+
});
|
|
90
|
+
if (response.status === 403) {
|
|
91
|
+
const body = (await response.json().catch(() => ({})));
|
|
92
|
+
throw new Error(`License key rejected by Portal: ${body.reason ?? 'key_not_found'}. ` +
|
|
93
|
+
`Check SOULCRAFT_LICENSE_KEY is correct and the license has not expired.`);
|
|
94
|
+
}
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`Portal license activation failed (HTTP ${response.status}). ` +
|
|
97
|
+
`The service may be temporarily unavailable.`);
|
|
98
|
+
}
|
|
99
|
+
return response.json();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Read the cached bundle from `.soulcraft-config.json`.
|
|
103
|
+
* Returns `undefined` if the file does not exist or cannot be parsed.
|
|
104
|
+
*/
|
|
105
|
+
function _readCache() {
|
|
106
|
+
try {
|
|
107
|
+
const raw = readFileSync(CACHE_PATH, 'utf8');
|
|
108
|
+
return JSON.parse(raw);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Write the activation response to `.soulcraft-config.json`.
|
|
116
|
+
* Failures are non-fatal — if the disk is read-only, the cache is simply not written.
|
|
117
|
+
*/
|
|
118
|
+
function _writeCache(product, response) {
|
|
119
|
+
try {
|
|
120
|
+
const bundle = {
|
|
121
|
+
activatedAt: new Date().toISOString(),
|
|
122
|
+
product,
|
|
123
|
+
response,
|
|
124
|
+
};
|
|
125
|
+
writeFileSync(CACHE_PATH, JSON.stringify(bundle, null, 2), 'utf8');
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Non-fatal — read-only FS or permission error.
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Inject config bundle values into `process.env`.
|
|
133
|
+
*
|
|
134
|
+
* Only sets variables that are present in the bundle and not already set by the host
|
|
135
|
+
* environment. Host-set values take precedence, allowing per-deployment overrides.
|
|
136
|
+
*/
|
|
137
|
+
function _injectEnv(config) {
|
|
138
|
+
const mappings = [
|
|
139
|
+
['anthropicApiKey', 'ANTHROPIC_API_KEY'],
|
|
140
|
+
['portalCreditSecret', 'PORTAL_CREDIT_SECRET'],
|
|
141
|
+
['hallUrl', 'HALL_URL'],
|
|
142
|
+
['stripeSecretKey', 'STRIPE_SECRET_KEY'],
|
|
143
|
+
['stripeWebhookSecret', 'STRIPE_WEBHOOK_SECRET'],
|
|
144
|
+
['workshopDeploySecret', 'WORKSHOP_DEPLOY_SECRET'],
|
|
145
|
+
['academyPublishSecret', 'ACADEMY_PUBLISH_SECRET'],
|
|
146
|
+
['venueRpcSecret', 'VENUE_RPC_SECRET'],
|
|
147
|
+
['postmarkToken', 'POSTMARK_SERVER_TOKEN'],
|
|
148
|
+
['postmarkFromEmail', 'POSTMARK_FROM_EMAIL'],
|
|
149
|
+
['twilioSid', 'TWILIO_ACCOUNT_SID'],
|
|
150
|
+
['twilioToken', 'TWILIO_AUTH_TOKEN'],
|
|
151
|
+
['twilioFromNumber', 'TWILIO_FROM_NUMBER'],
|
|
152
|
+
['firebaseServiceAccountKey', 'FIREBASE_SERVICE_ACCOUNT_KEY'],
|
|
153
|
+
];
|
|
154
|
+
for (const [configKey, envKey] of mappings) {
|
|
155
|
+
const value = config[configKey];
|
|
156
|
+
if (value && !process.env[envKey]) {
|
|
157
|
+
process.env[envKey] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
+
// fromLicense factory
|
|
163
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
164
|
+
/**
|
|
165
|
+
* Activate the SDK from a Soulcraft license key.
|
|
166
|
+
*
|
|
167
|
+
* Calls Portal's `licenseActivate` endpoint, injects the returned config bundle
|
|
168
|
+
* into `process.env`, and returns a `ServerSDK` ready for per-request use.
|
|
169
|
+
*
|
|
170
|
+
* Falls back to a cached bundle (`.soulcraft-config.json`) if Portal is unreachable.
|
|
171
|
+
* Throws if neither Portal nor a valid cache is available.
|
|
172
|
+
*
|
|
173
|
+
* @param options - Product name and optional license key override.
|
|
174
|
+
* @returns A `ServerSDK` instance, fully wired from the license bundle.
|
|
175
|
+
*
|
|
176
|
+
* @throws {Error} If the license key is invalid, expired, or Portal is unreachable with no cache.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* const server = await createServerSDK.fromLicense({ product: 'academy' })
|
|
181
|
+
* const pool = new BrainyInstancePool({ storage: 'mmap-filesystem', dataPath: '/mnt/brainy-data', strategy: 'per-user' })
|
|
182
|
+
*
|
|
183
|
+
* app.use(requireAuth)
|
|
184
|
+
* app.get('/api/brainy', async (c) => {
|
|
185
|
+
* const brain = await pool.forUser(c.get('user').emailHash, 'main')
|
|
186
|
+
* const sdk = server.createSDK(brain)
|
|
187
|
+
* return c.json(await sdk.brainy.find({ query: c.req.query('q') ?? '' }))
|
|
188
|
+
* })
|
|
189
|
+
*
|
|
190
|
+
* process.on('SIGTERM', () => server.shutdown())
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
async function fromLicense(options) {
|
|
194
|
+
const { product } = options;
|
|
195
|
+
const key = options.licenseKey ?? process.env['SOULCRAFT_LICENSE_KEY'];
|
|
196
|
+
if (!key) {
|
|
197
|
+
throw new Error('SOULCRAFT_LICENSE_KEY is not set. ' +
|
|
198
|
+
'Set this environment variable to your Soulcraft license key, ' +
|
|
199
|
+
'or pass licenseKey explicitly to fromLicense().');
|
|
200
|
+
}
|
|
201
|
+
// ── Activate ──────────────────────────────────────────────────────────────
|
|
202
|
+
let activateResponse;
|
|
203
|
+
try {
|
|
204
|
+
activateResponse = await _activate(key, product);
|
|
205
|
+
_writeCache(product, activateResponse);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
// Portal unreachable — try the cached bundle.
|
|
209
|
+
const cached = _readCache();
|
|
210
|
+
if (cached?.product === product && cached.response.valid) {
|
|
211
|
+
console.warn(`[SDK] Portal unreachable (${err instanceof Error ? err.message : String(err)}). ` +
|
|
212
|
+
`Using cached config from ${cached.activatedAt}.`);
|
|
213
|
+
activateResponse = cached.response;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
throw new Error(`Failed to activate SDK license and no valid cache found. ` +
|
|
217
|
+
`Original error: ${err instanceof Error ? err.message : String(err)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (!activateResponse.valid) {
|
|
221
|
+
throw new Error(`License key is not valid: ${activateResponse.reason ?? 'unknown reason'}. ` +
|
|
222
|
+
`Check that your license has not expired.`);
|
|
223
|
+
}
|
|
224
|
+
const config = activateResponse.config;
|
|
225
|
+
const tier = activateResponse.tier ?? 'free';
|
|
226
|
+
// ── Inject env ────────────────────────────────────────────────────────────
|
|
227
|
+
_injectEnv(config);
|
|
228
|
+
// ── Wire Hall ─────────────────────────────────────────────────────────────
|
|
229
|
+
let hall;
|
|
230
|
+
if (config.hallSecret && config.hallUrl) {
|
|
231
|
+
hall = createHallModule({
|
|
232
|
+
url: config.hallUrl,
|
|
233
|
+
productName: product,
|
|
234
|
+
secret: config.hallSecret,
|
|
235
|
+
});
|
|
236
|
+
await hall.connect();
|
|
237
|
+
}
|
|
238
|
+
// ── Background re-validation ──────────────────────────────────────────────
|
|
239
|
+
let _revalidateTimer;
|
|
240
|
+
async function _revalidate() {
|
|
241
|
+
try {
|
|
242
|
+
const fresh = await _activate(key, product);
|
|
243
|
+
_writeCache(product, fresh);
|
|
244
|
+
_injectEnv(fresh.config);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Non-fatal — the in-memory injected values remain valid until next attempt.
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
_revalidateTimer = setInterval(() => { void _revalidate(); }, REVALIDATE_INTERVAL_MS);
|
|
251
|
+
if (_revalidateTimer.unref)
|
|
252
|
+
_revalidateTimer.unref();
|
|
253
|
+
// ── Return ServerSDK ──────────────────────────────────────────────────────
|
|
254
|
+
return {
|
|
255
|
+
createSDK(brain) {
|
|
256
|
+
return createSDK({ brain });
|
|
257
|
+
},
|
|
258
|
+
get hall() {
|
|
259
|
+
return hall;
|
|
260
|
+
},
|
|
261
|
+
get config() {
|
|
262
|
+
return config;
|
|
263
|
+
},
|
|
264
|
+
get tier() {
|
|
265
|
+
return tier;
|
|
266
|
+
},
|
|
267
|
+
shutdown() {
|
|
268
|
+
if (_revalidateTimer) {
|
|
269
|
+
clearInterval(_revalidateTimer);
|
|
270
|
+
_revalidateTimer = undefined;
|
|
271
|
+
}
|
|
272
|
+
if (hall) {
|
|
273
|
+
void hall.close();
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
279
|
+
// createServerSDK namespace
|
|
280
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
281
|
+
/**
|
|
282
|
+
* Server SDK factory namespace.
|
|
283
|
+
*
|
|
284
|
+
* The primary entry point is `createServerSDK.fromLicense({ product })`.
|
|
285
|
+
* For lower-level control (custom Brainy instance, no license key), use
|
|
286
|
+
* `createSDK({ brain })` directly.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```typescript
|
|
290
|
+
* import { createServerSDK, BrainyInstancePool } from '@soulcraft/sdk/server'
|
|
291
|
+
*
|
|
292
|
+
* const server = await createServerSDK.fromLicense({ product: 'workshop' })
|
|
293
|
+
* const pool = new BrainyInstancePool({ storage: 'mmap-filesystem', dataPath: '/mnt/data', strategy: 'per-user' })
|
|
294
|
+
*
|
|
295
|
+
* app.get('/api/brainy', requireAuth, async (c) => {
|
|
296
|
+
* const brain = await pool.forUser(c.get('user').emailHash, 'main')
|
|
297
|
+
* const sdk = server.createSDK(brain)
|
|
298
|
+
* return c.json(await sdk.brainy.find({ query: c.req.query('q') ?? '' }))
|
|
299
|
+
* })
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
export const createServerSDK = {
|
|
303
|
+
fromLicense,
|
|
304
|
+
};
|
|
305
|
+
//# sourceMappingURL=from-license.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"from-license.js","sourceRoot":"","sources":["../../src/server/from-license.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAIrD,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,0CAA0C;AAC1C,MAAM,YAAY,GAAG,4CAA4C,CAAA;AAEjE,uCAAuC;AACvC,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAEjD,gDAAgD;AAChD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,wBAAwB,CAAC,CAAA;AA4HnE,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;GAIG;AACH,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,OAAe;IACnD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE;QACzC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;QAC3D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAA;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAwB,CAAA;QAC7E,MAAM,IAAI,KAAK,CACb,mCAAmC,IAAI,CAAC,MAAM,IAAI,eAAe,IAAI;YACrE,yEAAyE,CAC1E,CAAA;IACH,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,0CAA0C,QAAQ,CAAC,MAAM,KAAK;YAC9D,6CAA6C,CAC9C,CAAA;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAA+B,CAAA;AACrD,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAA;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,OAAe,EAAE,QAA0B;IAC9D,IAAI,CAAC;QACH,MAAM,MAAM,GAAiB;YAC3B,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACrC,OAAO;YACP,QAAQ;SACT,CAAA;QACD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,MAA2B;IAC7C,MAAM,QAAQ,GAA+C;QAC3D,CAAC,iBAAiB,EAAU,mBAAmB,CAAC;QAChD,CAAC,oBAAoB,EAAO,sBAAsB,CAAC;QACnD,CAAC,SAAS,EAAkB,UAAU,CAAC;QACvC,CAAC,iBAAiB,EAAU,mBAAmB,CAAC;QAChD,CAAC,qBAAqB,EAAM,uBAAuB,CAAC;QACpD,CAAC,sBAAsB,EAAK,wBAAwB,CAAC;QACrD,CAAC,sBAAsB,EAAK,wBAAwB,CAAC;QACrD,CAAC,gBAAgB,EAAW,kBAAkB,CAAC;QAC/C,CAAC,eAAe,EAAY,uBAAuB,CAAC;QACpD,CAAC,mBAAmB,EAAQ,qBAAqB,CAAC;QAClD,CAAC,WAAW,EAAgB,oBAAoB,CAAC;QACjD,CAAC,aAAa,EAAc,mBAAmB,CAAC;QAChD,CAAC,kBAAkB,EAAS,oBAAoB,CAAC;QACjD,CAAC,2BAA2B,EAAE,8BAA8B,CAAC;KAC9D,CAAA;IAED,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;QAC/B,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,CAAA;QAC7B,CAAC;IACH,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,KAAK,UAAU,WAAW,CAAC,OAA2B;IACpD,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;IAEtE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,oCAAoC;YACpC,+DAA+D;YAC/D,iDAAiD,CAClD,CAAA;IACH,CAAC;IAED,6EAA6E;IAE7E,IAAI,gBAAkC,CAAA;IAEtC,IAAI,CAAC;QACH,gBAAgB,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAChD,WAAW,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAA;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8CAA8C;QAC9C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;QAC3B,IAAI,MAAM,EAAE,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACzD,OAAO,CAAC,IAAI,CACV,6BAA6B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK;gBAClF,4BAA4B,MAAM,CAAC,WAAW,GAAG,CAClD,CAAA;YACD,gBAAgB,GAAG,MAAM,CAAC,QAAQ,CAAA;QACpC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,2DAA2D;gBAC3D,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACtE,CAAA;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,6BAA6B,gBAAgB,CAAC,MAAM,IAAI,gBAAgB,IAAI;YAC5E,0CAA0C,CAC3C,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAA;IACtC,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,IAAI,MAAM,CAAA;IAE5C,6EAA6E;IAE7E,UAAU,CAAC,MAAM,CAAC,CAAA;IAElB,6EAA6E;IAE7E,IAAI,IAA4B,CAAA;IAChC,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACxC,IAAI,GAAG,gBAAgB,CAAC;YACtB,GAAG,EAAE,MAAM,CAAC,OAAO;YACnB,WAAW,EAAE,OAAO;YACpB,MAAM,EAAE,MAAM,CAAC,UAAU;SAC1B,CAAC,CAAA;QACF,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;IACtB,CAAC;IAED,6EAA6E;IAE7E,IAAI,gBAA4D,CAAA;IAEhE,KAAK,UAAU,WAAW;QACxB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAI,EAAE,OAAO,CAAC,CAAA;YAC5C,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;YAC3B,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,6EAA6E;QAC/E,CAAC;IACH,CAAC;IAED,gBAAgB,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,WAAW,EAAE,CAAA,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAA;IACpF,IAAI,gBAAgB,CAAC,KAAK;QAAE,gBAAgB,CAAC,KAAK,EAAE,CAAA;IAEpD,6EAA6E;IAE7E,OAAO;QACL,SAAS,CAAC,KAAa;YACrB,OAAO,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAC7B,CAAC;QAED,IAAI,IAAI;YACN,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,MAAM;YACR,OAAO,MAAM,CAAA;QACf,CAAC;QAED,IAAI,IAAI;YACN,OAAO,IAAI,CAAA;QACb,CAAC;QAED,QAAQ;YACN,IAAI,gBAAgB,EAAE,CAAC;gBACrB,aAAa,CAAC,gBAAgB,CAAC,CAAA;gBAC/B,gBAAgB,GAAG,SAAS,CAAA;YAC9B,CAAC;YACD,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,IAAI,CAAC,KAAK,EAAE,CAAA;YACnB,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,WAAW;CACZ,CAAA"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export { BrainyInstancePool, BrainyInitializingError, computeEmailHash, } from '
|
|
|
36
36
|
export type { InstancePoolConfig, InstancePoolStats, } from './instance-pool.js';
|
|
37
37
|
export { createBrainyHandler, createBrainyWsHandler, } from './handlers.js';
|
|
38
38
|
export type { BrainyHandlerConfig, BrainyWsHandlerConfig, BrainyWsHandler, WsSession, } from './handlers.js';
|
|
39
|
+
export { createBrainyPostMessageHandler } from './postmessage-handler.js';
|
|
40
|
+
export type { BrainyPostMessageHandlerConfig, BrainyPostMessageHandler, } from './postmessage-handler.js';
|
|
39
41
|
export { createHallModule, generateTurnCredentials, HallClient, } from './hall-handlers.js';
|
|
40
42
|
export type { HallConnectionOptions, HallModule, TurnCredentialOptions, TurnCredentials, } from './hall-handlers.js';
|
|
41
43
|
export type { HallRoom, HallRoomEvents, RoomOptions, ConceptInput, RecordingManifest, TranscriptEvent, ConceptMentionEvent, RelationProposedEvent, SpeakerChangedEvent, PeerJoinedEvent, PeerLeftEvent, } from '../modules/hall/types.js';
|
|
@@ -47,6 +49,8 @@ export { verifyServiceToken, extractBearerToken, } from '../modules/auth/service
|
|
|
47
49
|
export { SOULCRAFT_USER_FIELDS, SOULCRAFT_SESSION_CONFIG, getAuthMode, getOIDCClientConfig, } from '../modules/auth/config.js';
|
|
48
50
|
export { createSDK } from './create-sdk.js';
|
|
49
51
|
export type { CreateSDKOptions } from './create-sdk.js';
|
|
52
|
+
export { createServerSDK } from './from-license.js';
|
|
53
|
+
export type { ServerSDK, FromLicenseOptions, LicenseConfigBundle, } from './from-license.js';
|
|
50
54
|
export { createBillingModule } from '../modules/billing/index.js';
|
|
51
55
|
export type { CreateBillingModuleOptions } from '../modules/billing/index.js';
|
|
52
56
|
export { createLicenseModule } from '../modules/license/index.js';
|