@elsium-ai/app 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +502 -17
- package/dist/app.d.ts +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +340 -58
- package/dist/middleware.d.ts +3 -0
- package/dist/middleware.d.ts.map +1 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -8,37 +8,522 @@ App bootstrap, HTTP server, and API routes for [ElsiumAI](https://github.com/els
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm install @elsium-ai/app
|
|
11
|
+
npm install @elsium-ai/app
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## What's Inside
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
| Category | Export | Kind |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| **App** | `createApp` | Function |
|
|
19
|
+
| | `ElsiumApp` | Interface |
|
|
20
|
+
| **Types** | `AppConfig` | Interface |
|
|
21
|
+
| | `ServerConfig` | Interface |
|
|
22
|
+
| | `CorsConfig` | Interface |
|
|
23
|
+
| | `AuthConfig` | Interface |
|
|
24
|
+
| | `RateLimitConfig` | Interface |
|
|
25
|
+
| | `ChatRequest` | Interface |
|
|
26
|
+
| | `ChatResponse` | Interface |
|
|
27
|
+
| | `CompleteRequest` | Interface |
|
|
28
|
+
| | `HealthResponse` | Interface |
|
|
29
|
+
| | `MetricsResponse` | Interface |
|
|
30
|
+
| **Middleware** | `corsMiddleware` | Function |
|
|
31
|
+
| | `authMiddleware` | Function |
|
|
32
|
+
| | `rateLimitMiddleware` | Function |
|
|
33
|
+
| **Routes** | `createRoutes` | Function |
|
|
34
|
+
| | `RoutesDeps` | Interface |
|
|
35
|
+
| **RBAC** | `createRBAC` | Function |
|
|
36
|
+
| | `Permission` | Type |
|
|
37
|
+
| | `Role` | Interface |
|
|
38
|
+
| | `RBACConfig` | Interface |
|
|
39
|
+
| | `RBAC` | Interface |
|
|
21
40
|
|
|
22
|
-
|
|
41
|
+
---
|
|
23
42
|
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
## App
|
|
44
|
+
|
|
45
|
+
### `createApp`
|
|
46
|
+
|
|
47
|
+
Creates and returns a fully configured ElsiumAI application with a gateway, tracer, middleware stack, agent registry, and HTTP routes.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
function createApp(config: AppConfig): ElsiumApp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Parameter | Type | Description |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `config` | `AppConfig` | Full application configuration including gateway, agents, observability, and server settings. |
|
|
56
|
+
|
|
57
|
+
**Returns** `ElsiumApp` -- the application handle exposing the Hono instance, gateway, tracer, and a `listen` method to start the HTTP server.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { createApp } from '@elsium-ai/app'
|
|
61
|
+
|
|
62
|
+
const app = createApp({
|
|
63
|
+
gateway: {
|
|
64
|
+
providers: {
|
|
65
|
+
openai: { apiKey: process.env.OPENAI_API_KEY! },
|
|
66
|
+
},
|
|
67
|
+
defaultModel: 'gpt-4o',
|
|
68
|
+
},
|
|
69
|
+
server: {
|
|
70
|
+
port: 3000,
|
|
71
|
+
cors: { origin: ['http://localhost:5173'], credentials: true },
|
|
72
|
+
auth: { type: 'bearer', token: process.env.API_TOKEN! },
|
|
73
|
+
rateLimit: { windowMs: 60_000, maxRequests: 100 },
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const { port, stop } = app.listen()
|
|
78
|
+
console.log(`Listening on port ${port}`)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `ElsiumApp`
|
|
82
|
+
|
|
83
|
+
The object returned by `createApp`. Provides access to the underlying Hono app, gateway, tracer, and a method to start the HTTP server.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
interface ElsiumApp {
|
|
87
|
+
readonly hono: Hono
|
|
88
|
+
readonly gateway: Gateway
|
|
89
|
+
readonly tracer: Tracer
|
|
90
|
+
listen(port?: number): { port: number; stop: () => void }
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Property / Method | Type | Description |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `hono` | `Hono` | The underlying Hono application instance. Use it to add custom routes or middleware. |
|
|
97
|
+
| `gateway` | `Gateway` | The configured LLM gateway. |
|
|
98
|
+
| `tracer` | `Tracer` | The observability tracer for cost and latency tracking. |
|
|
99
|
+
| `listen(port?)` | `(port?: number) => { port: number; stop: () => void }` | Starts the HTTP server. Falls back to `server.port` from config, then `3000`. Returns the resolved port and a `stop` function to shut down the server. |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Types
|
|
104
|
+
|
|
105
|
+
### `AppConfig`
|
|
106
|
+
|
|
107
|
+
Top-level configuration object passed to `createApp`.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
interface AppConfig {
|
|
111
|
+
gateway: {
|
|
112
|
+
providers: Record<string, { apiKey: string; baseUrl?: string }>
|
|
113
|
+
defaultModel?: string
|
|
114
|
+
}
|
|
115
|
+
agents?: Agent[]
|
|
116
|
+
rag?: RAGPipeline
|
|
117
|
+
observe?: {
|
|
118
|
+
tracing?: boolean
|
|
119
|
+
costTracking?: boolean
|
|
120
|
+
export?: string
|
|
121
|
+
}
|
|
122
|
+
server?: ServerConfig
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `ServerConfig`
|
|
127
|
+
|
|
128
|
+
HTTP server and middleware configuration.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
interface ServerConfig {
|
|
132
|
+
port?: number
|
|
133
|
+
hostname?: string
|
|
134
|
+
cors?: boolean | CorsConfig
|
|
135
|
+
auth?: AuthConfig
|
|
136
|
+
rateLimit?: RateLimitConfig
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `CorsConfig`
|
|
141
|
+
|
|
142
|
+
Fine-grained CORS settings. When `cors` in `ServerConfig` is set to `true`, sensible defaults are used.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
interface CorsConfig {
|
|
146
|
+
origin?: string | string[]
|
|
147
|
+
methods?: string[]
|
|
148
|
+
headers?: string[]
|
|
149
|
+
credentials?: boolean
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `AuthConfig`
|
|
154
|
+
|
|
155
|
+
Bearer-token authentication configuration. The middleware uses timing-safe comparison to validate tokens.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
interface AuthConfig {
|
|
159
|
+
type: 'bearer'
|
|
160
|
+
token: string
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `RateLimitConfig`
|
|
165
|
+
|
|
166
|
+
Per-client sliding-window rate limiting configuration.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
interface RateLimitConfig {
|
|
170
|
+
windowMs: number
|
|
171
|
+
maxRequests: number
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `ChatRequest`
|
|
176
|
+
|
|
177
|
+
Request body for the `POST /chat` endpoint.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
interface ChatRequest {
|
|
181
|
+
message: string
|
|
182
|
+
agent?: string
|
|
183
|
+
stream?: boolean
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `ChatResponse`
|
|
188
|
+
|
|
189
|
+
Response body from the `POST /chat` endpoint.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
interface ChatResponse {
|
|
193
|
+
message: string
|
|
194
|
+
usage: {
|
|
195
|
+
inputTokens: number
|
|
196
|
+
outputTokens: number
|
|
197
|
+
totalTokens: number
|
|
198
|
+
cost: number
|
|
199
|
+
}
|
|
200
|
+
model: string
|
|
201
|
+
traceId: string
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `CompleteRequest`
|
|
206
|
+
|
|
207
|
+
Request body for the `POST /complete` endpoint.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
interface CompleteRequest {
|
|
211
|
+
messages: Array<{ role: string; content: string }>
|
|
212
|
+
model?: string
|
|
213
|
+
system?: string
|
|
214
|
+
maxTokens?: number
|
|
215
|
+
temperature?: number
|
|
216
|
+
stream?: boolean
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### `HealthResponse`
|
|
221
|
+
|
|
222
|
+
Response body from the `GET /health` endpoint.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
interface HealthResponse {
|
|
226
|
+
status: 'ok' | 'degraded'
|
|
227
|
+
version: string
|
|
228
|
+
uptime: number
|
|
229
|
+
providers: string[]
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `MetricsResponse`
|
|
234
|
+
|
|
235
|
+
Response body from the `GET /metrics` endpoint.
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
interface MetricsResponse {
|
|
239
|
+
uptime: number
|
|
240
|
+
totalRequests: number
|
|
241
|
+
totalTokens: number
|
|
242
|
+
totalCost: number
|
|
243
|
+
byModel: Record<string, { requests: number; tokens: number; cost: number }>
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Middleware
|
|
250
|
+
|
|
251
|
+
All middleware functions return a Hono-compatible handler `(c: Context, next: Next) => Promise<...>`. They are applied automatically when the corresponding `ServerConfig` field is set, but they can also be used standalone on any Hono app.
|
|
252
|
+
|
|
253
|
+
### `corsMiddleware`
|
|
254
|
+
|
|
255
|
+
Returns a Hono middleware that sets CORS headers and handles preflight `OPTIONS` requests.
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
function corsMiddleware(config?: CorsConfig | boolean): (c: Context, next: Next) => Promise<Response | void>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
| Parameter | Type | Default | Description |
|
|
262
|
+
| --- | --- | --- | --- |
|
|
263
|
+
| `config` | `CorsConfig \| boolean` | `true` | When `true`, uses default methods `['GET', 'POST', 'OPTIONS']` and an empty origin list. Pass a `CorsConfig` object for fine-grained control. |
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
import { corsMiddleware } from '@elsium-ai/app'
|
|
267
|
+
import { Hono } from 'hono'
|
|
268
|
+
|
|
269
|
+
const app = new Hono()
|
|
270
|
+
|
|
271
|
+
app.use('*', corsMiddleware({
|
|
272
|
+
origin: ['https://myapp.com'],
|
|
273
|
+
methods: ['GET', 'POST'],
|
|
274
|
+
credentials: true,
|
|
275
|
+
}))
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### `authMiddleware`
|
|
279
|
+
|
|
280
|
+
Returns a Hono middleware that validates `Authorization: Bearer <token>` headers using timing-safe comparison. The `/health` endpoint is always excluded from auth checks.
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
function authMiddleware(config: AuthConfig): (c: Context, next: Next) => Promise<Response | void>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
| Parameter | Type | Description |
|
|
287
|
+
| --- | --- | --- |
|
|
288
|
+
| `config` | `AuthConfig` | Must specify `type: 'bearer'` and the expected `token` string. |
|
|
289
|
+
|
|
290
|
+
**Responses on failure:**
|
|
291
|
+
- `401` with `{ error: 'Missing Authorization header' }` when the header is absent.
|
|
292
|
+
- `401` with `{ error: 'Invalid token' }` when the token does not match.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import { authMiddleware } from '@elsium-ai/app'
|
|
296
|
+
import { Hono } from 'hono'
|
|
297
|
+
|
|
298
|
+
const app = new Hono()
|
|
299
|
+
|
|
300
|
+
app.use('*', authMiddleware({
|
|
301
|
+
type: 'bearer',
|
|
302
|
+
token: process.env.API_TOKEN!,
|
|
303
|
+
}))
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### `rateLimitMiddleware`
|
|
307
|
+
|
|
308
|
+
Returns a Hono middleware that enforces per-client rate limiting using an in-memory sliding window. Client identity is determined from the `CF-Connecting-IP` header, then `X-Real-IP`, falling back to `'anonymous'`. Sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` response headers.
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
function rateLimitMiddleware(config: RateLimitConfig): (c: Context, next: Next) => Promise<Response | void>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
| Parameter | Type | Description |
|
|
315
|
+
| --- | --- | --- |
|
|
316
|
+
| `config` | `RateLimitConfig` | `windowMs` is the time window in milliseconds; `maxRequests` is the maximum number of requests allowed per window. |
|
|
317
|
+
|
|
318
|
+
**Responses on failure:**
|
|
319
|
+
- `429` with `{ error: 'Too many requests', retryAfterMs: number }` when the limit is exceeded.
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
import { rateLimitMiddleware } from '@elsium-ai/app'
|
|
323
|
+
import { Hono } from 'hono'
|
|
324
|
+
|
|
325
|
+
const app = new Hono()
|
|
326
|
+
|
|
327
|
+
app.use('*', rateLimitMiddleware({
|
|
328
|
+
windowMs: 60_000,
|
|
329
|
+
maxRequests: 100,
|
|
330
|
+
}))
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Routes
|
|
336
|
+
|
|
337
|
+
### `createRoutes`
|
|
338
|
+
|
|
339
|
+
Creates a Hono sub-application with all built-in API routes: `GET /health`, `GET /metrics`, `POST /chat`, `POST /complete`, and `GET /agents`.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
function createRoutes(deps: RoutesDeps): Hono
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
| Parameter | Type | Description |
|
|
346
|
+
| --- | --- | --- |
|
|
347
|
+
| `deps` | `RoutesDeps` | Dependencies injected into route handlers, including the gateway, agent registry, tracer, and server metadata. |
|
|
348
|
+
|
|
349
|
+
**Returns** a `Hono` instance with the following routes:
|
|
350
|
+
|
|
351
|
+
| Method | Path | Description |
|
|
352
|
+
| --- | --- | --- |
|
|
353
|
+
| `GET` | `/health` | Returns a `HealthResponse` with status, version, uptime, and provider list. |
|
|
354
|
+
| `GET` | `/metrics` | Returns a `MetricsResponse` with request counts, token usage, and cost breakdowns. |
|
|
355
|
+
| `POST` | `/chat` | Accepts a `ChatRequest`, dispatches to the specified (or default) agent, and returns a `ChatResponse`. |
|
|
356
|
+
| `POST` | `/complete` | Accepts a `CompleteRequest`, forwards to the gateway, and returns the completion result. |
|
|
357
|
+
| `GET` | `/agents` | Lists all registered agents with their names, models, and tool names. |
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { createRoutes } from '@elsium-ai/app'
|
|
361
|
+
import { Hono } from 'hono'
|
|
362
|
+
|
|
363
|
+
const routes = createRoutes({
|
|
364
|
+
gateway: myGateway,
|
|
365
|
+
agents: new Map([['assistant', myAgent]]),
|
|
366
|
+
defaultAgent: myAgent,
|
|
367
|
+
tracer: myTracer,
|
|
368
|
+
startTime: Date.now(),
|
|
369
|
+
version: '1.0.0',
|
|
370
|
+
providers: ['openai'],
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
const app = new Hono()
|
|
374
|
+
app.route('/', routes)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### `RoutesDeps`
|
|
378
|
+
|
|
379
|
+
Dependency injection interface for `createRoutes`.
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
interface RoutesDeps {
|
|
383
|
+
gateway: Gateway
|
|
384
|
+
agents: Map<string, Agent>
|
|
385
|
+
defaultAgent?: Agent
|
|
386
|
+
tracer?: Tracer
|
|
387
|
+
startTime: number
|
|
388
|
+
version: string
|
|
389
|
+
providers: string[]
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
| Field | Type | Description |
|
|
394
|
+
| --- | --- | --- |
|
|
395
|
+
| `gateway` | `Gateway` | The LLM gateway used by the `/complete` endpoint. |
|
|
396
|
+
| `agents` | `Map<string, Agent>` | Registry of named agents used by the `/chat` endpoint. |
|
|
397
|
+
| `defaultAgent` | `Agent` (optional) | The agent used when no `agent` field is specified in a chat request. |
|
|
398
|
+
| `tracer` | `Tracer` (optional) | Observability tracer for tracking LLM calls. |
|
|
399
|
+
| `startTime` | `number` | Timestamp (ms) when the server started, used to calculate uptime. |
|
|
400
|
+
| `version` | `string` | Application version string returned by `/health`. |
|
|
401
|
+
| `providers` | `string[]` | List of configured provider names returned by `/health`. |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## RBAC
|
|
406
|
+
|
|
407
|
+
### `createRBAC`
|
|
408
|
+
|
|
409
|
+
Creates a role-based access control system with permission checking, role inheritance, wildcard matching, and Hono middleware generation. Includes four built-in roles (`admin`, `operator`, `user`, `viewer`) that can be overridden by user-defined roles.
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
function createRBAC(config: RBACConfig): RBAC
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
| Parameter | Type | Description |
|
|
416
|
+
| --- | --- | --- |
|
|
417
|
+
| `config` | `RBACConfig` | Defines custom roles, a default role, and how the role is extracted from each request. |
|
|
418
|
+
|
|
419
|
+
**Returns** an `RBAC` object with methods for permission checking and middleware creation.
|
|
420
|
+
|
|
421
|
+
**Built-in roles:**
|
|
422
|
+
|
|
423
|
+
| Role | Permissions |
|
|
424
|
+
| --- | --- |
|
|
425
|
+
| `admin` | `model:use:*`, `agent:execute:*`, `tool:call:*`, `config:read`, `config:write`, `audit:read`, `audit:write` |
|
|
426
|
+
| `operator` | `model:use:*`, `agent:execute:*`, `tool:call:*`, `config:read`, `audit:read` |
|
|
427
|
+
| `user` | `model:use`, `agent:execute`, `tool:call` |
|
|
428
|
+
| `viewer` | `config:read`, `audit:read` |
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
import { createRBAC } from '@elsium-ai/app'
|
|
26
432
|
|
|
27
433
|
const rbac = createRBAC({
|
|
28
434
|
roles: [
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
435
|
+
{
|
|
436
|
+
name: 'analyst',
|
|
437
|
+
permissions: ['model:use:gpt-4o-mini'],
|
|
438
|
+
inherits: ['viewer'],
|
|
439
|
+
},
|
|
32
440
|
],
|
|
441
|
+
defaultRole: 'viewer',
|
|
33
442
|
})
|
|
34
443
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
444
|
+
// Check a permission
|
|
445
|
+
rbac.hasPermission('analyst', 'model:use:gpt-4o-mini') // true
|
|
446
|
+
rbac.hasPermission('analyst', 'config:read') // true (inherited from viewer)
|
|
447
|
+
|
|
448
|
+
// Use as Hono middleware
|
|
449
|
+
app.post('/chat', rbac.middleware('model:use'), handler)
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### `Permission`
|
|
453
|
+
|
|
454
|
+
A union type representing all recognized permissions. Supports resource-specific and wildcard variants.
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
type Permission =
|
|
458
|
+
| 'model:use'
|
|
459
|
+
| 'model:use:*'
|
|
460
|
+
| `model:use:${string}`
|
|
461
|
+
| 'agent:execute'
|
|
462
|
+
| 'agent:execute:*'
|
|
463
|
+
| `agent:execute:${string}`
|
|
464
|
+
| 'tool:call'
|
|
465
|
+
| 'tool:call:*'
|
|
466
|
+
| `tool:call:${string}`
|
|
467
|
+
| 'config:read'
|
|
468
|
+
| 'config:write'
|
|
469
|
+
| 'audit:read'
|
|
470
|
+
| 'audit:write'
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Wildcard permissions (e.g., `model:use:*`) grant access to all resource-specific permissions under that namespace (e.g., `model:use:gpt-4o`) as well as the base permission (`model:use`).
|
|
474
|
+
|
|
475
|
+
### `Role`
|
|
476
|
+
|
|
477
|
+
Defines a named role with a set of permissions and optional inheritance from other roles.
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
interface Role {
|
|
481
|
+
name: string
|
|
482
|
+
permissions: Permission[]
|
|
483
|
+
inherits?: string[]
|
|
484
|
+
}
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### `RBACConfig`
|
|
488
|
+
|
|
489
|
+
Configuration for `createRBAC`.
|
|
490
|
+
|
|
491
|
+
```ts
|
|
492
|
+
interface RBACConfig {
|
|
493
|
+
roles: Role[]
|
|
494
|
+
defaultRole?: string
|
|
495
|
+
roleExtractor?: (c: Context) => string | undefined
|
|
496
|
+
trustRoleHeader?: boolean
|
|
497
|
+
}
|
|
40
498
|
```
|
|
41
499
|
|
|
500
|
+
| Field | Type | Description |
|
|
501
|
+
| --- | --- | --- |
|
|
502
|
+
| `roles` | `Role[]` | Custom role definitions. These override built-in roles with the same name. |
|
|
503
|
+
| `defaultRole` | `string` (optional) | The role assigned when no role can be determined from the request. Defaults to `'viewer'`. |
|
|
504
|
+
| `roleExtractor` | `(c: Context) => string \| undefined` (optional) | Custom function to extract the role name from a Hono request context. |
|
|
505
|
+
| `trustRoleHeader` | `boolean` (optional) | When `true`, reads the role from the `X-Role` request header. **Warning:** only enable this in development or behind a trusted reverse proxy, as any client can self-assign roles. |
|
|
506
|
+
|
|
507
|
+
### `RBAC`
|
|
508
|
+
|
|
509
|
+
The object returned by `createRBAC`.
|
|
510
|
+
|
|
511
|
+
```ts
|
|
512
|
+
interface RBAC {
|
|
513
|
+
hasPermission(role: string, permission: Permission): boolean
|
|
514
|
+
middleware(required: Permission): (c: Context, next: Next) => Promise<Response | undefined>
|
|
515
|
+
getRolePermissions(role: string): Permission[]
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
| Method | Description |
|
|
520
|
+
| --- | --- |
|
|
521
|
+
| `hasPermission(role, permission)` | Returns `true` if the given role (including inherited permissions) grants the specified permission. |
|
|
522
|
+
| `middleware(required)` | Returns a Hono middleware that rejects requests with `403` if the caller's role lacks the required permission. |
|
|
523
|
+
| `getRolePermissions(role)` | Returns the deduplicated list of all permissions for a role, including those inherited from parent roles. |
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
42
527
|
## Part of ElsiumAI
|
|
43
528
|
|
|
44
529
|
This package is the app layer of the [ElsiumAI](https://github.com/elsium-ai/elsium-ai) framework. See the [full documentation](https://github.com/elsium-ai/elsium-ai) for guides and examples.
|
package/dist/app.d.ts
CHANGED
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,OAAO,EAAW,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,oBAAoB,CAAA;AAIzD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAS3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;CAClE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAgItD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createApp } from './app';
|
|
2
2
|
export type { ElsiumApp } from './app';
|
|
3
3
|
export type { AppConfig, ServerConfig, CorsConfig, AuthConfig, RateLimitConfig, ChatRequest, ChatResponse, CompleteRequest, HealthResponse, MetricsResponse, } from './types';
|
|
4
|
-
export { corsMiddleware, authMiddleware, rateLimitMiddleware } from './middleware';
|
|
4
|
+
export { corsMiddleware, authMiddleware, rateLimitMiddleware, requestIdMiddleware, requestLoggerMiddleware, } from './middleware';
|
|
5
5
|
export { createRoutes } from './routes';
|
|
6
6
|
export type { RoutesDeps } from './routes';
|
|
7
7
|
export { createRBAC } from './rbac';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,YAAY,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,eAAe,EACf,WAAW,EACX,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,GACf,MAAM,SAAS,CAAA;AAGhB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,YAAY,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,eAAe,EACf,WAAW,EACX,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,GACf,MAAM,SAAS,CAAA;AAGhB,OAAO,EACN,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,GACvB,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAG1C,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -389,6 +389,101 @@ function createLogger(options = {}) {
|
|
|
389
389
|
}
|
|
390
390
|
};
|
|
391
391
|
}
|
|
392
|
+
// ../core/src/shutdown.ts
|
|
393
|
+
function createShutdownManager(config) {
|
|
394
|
+
const drainTimeoutMs = config?.drainTimeoutMs ?? 30000;
|
|
395
|
+
const signals = config?.signals ?? ["SIGTERM", "SIGINT"];
|
|
396
|
+
if (drainTimeoutMs < 0 || !Number.isFinite(drainTimeoutMs)) {
|
|
397
|
+
throw new ElsiumError({
|
|
398
|
+
code: "CONFIG_ERROR",
|
|
399
|
+
message: "drainTimeoutMs must be >= 0 and finite",
|
|
400
|
+
retryable: false
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
let shuttingDown = false;
|
|
404
|
+
let inFlightCount = 0;
|
|
405
|
+
let drainResolve = null;
|
|
406
|
+
let shutdownPromise = null;
|
|
407
|
+
const signalHandlers = [];
|
|
408
|
+
function checkDrained() {
|
|
409
|
+
if (inFlightCount === 0 && drainResolve) {
|
|
410
|
+
drainResolve();
|
|
411
|
+
drainResolve = null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async function shutdown() {
|
|
415
|
+
if (shutdownPromise)
|
|
416
|
+
return shutdownPromise;
|
|
417
|
+
shuttingDown = true;
|
|
418
|
+
shutdownPromise = (async () => {
|
|
419
|
+
config?.onDrainStart?.();
|
|
420
|
+
if (inFlightCount === 0) {
|
|
421
|
+
config?.onDrainComplete?.();
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const drainPromise = new Promise((resolve) => {
|
|
425
|
+
drainResolve = resolve;
|
|
426
|
+
});
|
|
427
|
+
let drainTimer;
|
|
428
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
429
|
+
drainTimer = setTimeout(() => resolve("timeout"), drainTimeoutMs);
|
|
430
|
+
});
|
|
431
|
+
const result = await Promise.race([
|
|
432
|
+
drainPromise.then(() => "drained"),
|
|
433
|
+
timeoutPromise
|
|
434
|
+
]);
|
|
435
|
+
if (drainTimer !== undefined)
|
|
436
|
+
clearTimeout(drainTimer);
|
|
437
|
+
if (result === "timeout") {
|
|
438
|
+
config?.onForceShutdown?.();
|
|
439
|
+
} else {
|
|
440
|
+
config?.onDrainComplete?.();
|
|
441
|
+
}
|
|
442
|
+
})();
|
|
443
|
+
return shutdownPromise;
|
|
444
|
+
}
|
|
445
|
+
const manager = {
|
|
446
|
+
get inFlight() {
|
|
447
|
+
return inFlightCount;
|
|
448
|
+
},
|
|
449
|
+
get isShuttingDown() {
|
|
450
|
+
return shuttingDown;
|
|
451
|
+
},
|
|
452
|
+
async trackOperation(fn) {
|
|
453
|
+
if (shuttingDown) {
|
|
454
|
+
throw new ElsiumError({
|
|
455
|
+
code: "VALIDATION_ERROR",
|
|
456
|
+
message: "Server is shutting down, not accepting new operations",
|
|
457
|
+
retryable: true
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
inFlightCount++;
|
|
461
|
+
try {
|
|
462
|
+
return await fn();
|
|
463
|
+
} finally {
|
|
464
|
+
inFlightCount--;
|
|
465
|
+
checkDrained();
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
shutdown,
|
|
469
|
+
dispose() {
|
|
470
|
+
for (const { signal, handler } of signalHandlers) {
|
|
471
|
+
process.removeListener(signal, handler);
|
|
472
|
+
}
|
|
473
|
+
signalHandlers.length = 0;
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
if (typeof process !== "undefined" && process.on) {
|
|
477
|
+
for (const signal of signals) {
|
|
478
|
+
const handler = () => {
|
|
479
|
+
manager.shutdown();
|
|
480
|
+
};
|
|
481
|
+
signalHandlers.push({ signal, handler });
|
|
482
|
+
process.on(signal, handler);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return manager;
|
|
486
|
+
}
|
|
392
487
|
// ../gateway/src/provider.ts
|
|
393
488
|
var providerRegistry = new Map;
|
|
394
489
|
var metadataRegistry = new Map;
|
|
@@ -978,7 +1073,19 @@ function createGoogleProvider(config) {
|
|
|
978
1073
|
return { role, parts };
|
|
979
1074
|
}
|
|
980
1075
|
function formatGeminiMultipartContent(msg, role) {
|
|
981
|
-
const parts =
|
|
1076
|
+
const parts = [];
|
|
1077
|
+
for (const p of msg.content) {
|
|
1078
|
+
if (p.type === "text") {
|
|
1079
|
+
parts.push({ text: p.text });
|
|
1080
|
+
} else if (p.type === "image") {
|
|
1081
|
+
const img = p;
|
|
1082
|
+
if (img.source.type === "base64") {
|
|
1083
|
+
parts.push({ inlineData: { mimeType: img.source.mediaType, data: img.source.data } });
|
|
1084
|
+
} else {
|
|
1085
|
+
parts.push({ fileData: { mimeType: "image/jpeg", fileUri: img.source.url } });
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
982
1089
|
return { role, parts };
|
|
983
1090
|
}
|
|
984
1091
|
function formatMessages(messages) {
|
|
@@ -1153,7 +1260,8 @@ async function handleGoogleErrorResponse(response) {
|
|
|
1153
1260
|
throw ElsiumError.authError("google");
|
|
1154
1261
|
}
|
|
1155
1262
|
if (response.status === 429) {
|
|
1156
|
-
|
|
1263
|
+
const retryAfter = response.headers.get("retry-after");
|
|
1264
|
+
throw ElsiumError.rateLimit("google", retryAfter ? Number.parseInt(retryAfter) * 1000 : undefined);
|
|
1157
1265
|
}
|
|
1158
1266
|
throw ElsiumError.providerError(`Google API error ${response.status}: ${errorBody}`, {
|
|
1159
1267
|
provider: "google",
|
|
@@ -1339,6 +1447,24 @@ function createOpenAIProvider(config) {
|
|
|
1339
1447
|
}
|
|
1340
1448
|
return openaiMsg;
|
|
1341
1449
|
}
|
|
1450
|
+
function formatUserContent(msg) {
|
|
1451
|
+
if (typeof msg.content === "string")
|
|
1452
|
+
return msg.content;
|
|
1453
|
+
const parts = [];
|
|
1454
|
+
for (const part of msg.content) {
|
|
1455
|
+
if (part.type === "text") {
|
|
1456
|
+
parts.push({ type: "text", text: part.text });
|
|
1457
|
+
} else if (part.type === "image") {
|
|
1458
|
+
if (part.source.type === "base64") {
|
|
1459
|
+
const url = `data:${part.source.mediaType};base64,${part.source.data}`;
|
|
1460
|
+
parts.push({ type: "image_url", image_url: { url } });
|
|
1461
|
+
} else {
|
|
1462
|
+
parts.push({ type: "image_url", image_url: { url: part.source.url } });
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return parts;
|
|
1467
|
+
}
|
|
1342
1468
|
function formatMessages(messages) {
|
|
1343
1469
|
const formatted = [];
|
|
1344
1470
|
for (const msg of messages) {
|
|
@@ -1354,7 +1480,7 @@ function createOpenAIProvider(config) {
|
|
|
1354
1480
|
formatted.push(formatAssistantMessage(msg));
|
|
1355
1481
|
continue;
|
|
1356
1482
|
}
|
|
1357
|
-
formatted.push({ role: "user", content:
|
|
1483
|
+
formatted.push({ role: "user", content: formatUserContent(msg) });
|
|
1358
1484
|
}
|
|
1359
1485
|
return formatted;
|
|
1360
1486
|
}
|
|
@@ -1590,7 +1716,7 @@ var PROVIDER_FACTORIES = {
|
|
|
1590
1716
|
openai: createOpenAIProvider,
|
|
1591
1717
|
google: createGoogleProvider
|
|
1592
1718
|
};
|
|
1593
|
-
function
|
|
1719
|
+
function validateGatewayConfig(config) {
|
|
1594
1720
|
const factory = PROVIDER_FACTORIES[config.provider];
|
|
1595
1721
|
if (!factory) {
|
|
1596
1722
|
throw new ElsiumError({
|
|
@@ -1599,21 +1725,92 @@ function gateway(config) {
|
|
|
1599
1725
|
retryable: false
|
|
1600
1726
|
});
|
|
1601
1727
|
}
|
|
1728
|
+
if (typeof config.apiKey !== "string" || config.apiKey.trim() === "") {
|
|
1729
|
+
throw new ElsiumError({
|
|
1730
|
+
code: "CONFIG_ERROR",
|
|
1731
|
+
message: "apiKey must be a non-empty string",
|
|
1732
|
+
retryable: false
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
if (config.timeout !== undefined && (!Number.isFinite(config.timeout) || config.timeout <= 0)) {
|
|
1736
|
+
throw new ElsiumError({
|
|
1737
|
+
code: "CONFIG_ERROR",
|
|
1738
|
+
message: "timeout must be a positive finite number",
|
|
1739
|
+
retryable: false
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
if (config.maxRetries !== undefined && (!Number.isFinite(config.maxRetries) || !Number.isInteger(config.maxRetries) || config.maxRetries < 0)) {
|
|
1743
|
+
throw new ElsiumError({
|
|
1744
|
+
code: "CONFIG_ERROR",
|
|
1745
|
+
message: "maxRetries must be a non-negative finite integer",
|
|
1746
|
+
retryable: false
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
return factory;
|
|
1750
|
+
}
|
|
1751
|
+
function autoRegisterProvider(provider) {
|
|
1752
|
+
if (!provider.metadata)
|
|
1753
|
+
return;
|
|
1754
|
+
registerProviderMetadata(provider.name, provider.metadata);
|
|
1755
|
+
if (!provider.metadata.pricing)
|
|
1756
|
+
return;
|
|
1757
|
+
for (const [model, pricing] of Object.entries(provider.metadata.pricing)) {
|
|
1758
|
+
registerPricing(model, pricing);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
function validateRequestLimits(request, maxMessages, maxInputTokens) {
|
|
1762
|
+
if (request.messages.length > maxMessages) {
|
|
1763
|
+
throw ElsiumError.validation(`Message count ${request.messages.length} exceeds limit of ${maxMessages}`);
|
|
1764
|
+
}
|
|
1765
|
+
let estimatedTokens = 0;
|
|
1766
|
+
for (const msg of request.messages) {
|
|
1767
|
+
const text = typeof msg.content === "string" ? msg.content : msg.content.map((p) => p.type === "text" ? p.text : "").join("");
|
|
1768
|
+
estimatedTokens += Math.ceil(text.length / 4);
|
|
1769
|
+
}
|
|
1770
|
+
if (estimatedTokens > maxInputTokens) {
|
|
1771
|
+
throw ElsiumError.validation(`Estimated input tokens (~${estimatedTokens}) exceeds limit of ${maxInputTokens}`);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
function buildMiddlewareContext(req, providerName, defaultModel, metadata) {
|
|
1775
|
+
return {
|
|
1776
|
+
request: req,
|
|
1777
|
+
provider: providerName,
|
|
1778
|
+
model: req.model ?? defaultModel,
|
|
1779
|
+
traceId: generateTraceId(),
|
|
1780
|
+
startTime: performance.now(),
|
|
1781
|
+
metadata
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
async function accumulateStreamEvents(stream, emit) {
|
|
1785
|
+
let textContent = "";
|
|
1786
|
+
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1787
|
+
let stopReason = "end_turn";
|
|
1788
|
+
let id = "";
|
|
1789
|
+
for await (const event of stream) {
|
|
1790
|
+
emit(event);
|
|
1791
|
+
if (event.type === "text_delta") {
|
|
1792
|
+
textContent += event.text;
|
|
1793
|
+
} else if (event.type === "message_end") {
|
|
1794
|
+
usage = event.usage;
|
|
1795
|
+
stopReason = event.stopReason;
|
|
1796
|
+
} else if (event.type === "message_start") {
|
|
1797
|
+
id = event.id;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
return { textContent, usage, stopReason, id };
|
|
1801
|
+
}
|
|
1802
|
+
function gateway(config) {
|
|
1803
|
+
const factory = validateGatewayConfig(config);
|
|
1602
1804
|
const provider = factory({
|
|
1603
1805
|
apiKey: config.apiKey,
|
|
1604
1806
|
baseUrl: config.baseUrl,
|
|
1605
1807
|
timeout: config.timeout,
|
|
1606
1808
|
maxRetries: config.maxRetries
|
|
1607
1809
|
});
|
|
1608
|
-
|
|
1609
|
-
registerProviderMetadata(provider.name, provider.metadata);
|
|
1610
|
-
if (provider.metadata.pricing) {
|
|
1611
|
-
for (const [model, pricing] of Object.entries(provider.metadata.pricing)) {
|
|
1612
|
-
registerPricing(model, pricing);
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1810
|
+
autoRegisterProvider(provider);
|
|
1616
1811
|
const defaultModel = config.model ?? provider.defaultModel;
|
|
1812
|
+
const maxMessages = config.maxMessages ?? 1000;
|
|
1813
|
+
const maxInputTokens = config.maxInputTokens ?? 1e6;
|
|
1617
1814
|
let xrayStore = null;
|
|
1618
1815
|
const allMiddleware = [...config.middleware ?? []];
|
|
1619
1816
|
if (config.xray) {
|
|
@@ -1628,14 +1825,7 @@ function gateway(config) {
|
|
|
1628
1825
|
if (!composedMiddleware) {
|
|
1629
1826
|
return provider.complete(req);
|
|
1630
1827
|
}
|
|
1631
|
-
const ctx = {
|
|
1632
|
-
request: req,
|
|
1633
|
-
provider: provider.name,
|
|
1634
|
-
model: req.model ?? defaultModel,
|
|
1635
|
-
traceId: generateTraceId(),
|
|
1636
|
-
startTime: performance.now(),
|
|
1637
|
-
metadata: request.metadata ?? {}
|
|
1638
|
-
};
|
|
1828
|
+
const ctx = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
|
|
1639
1829
|
return composedMiddleware(ctx, async (c) => provider.complete(c.request));
|
|
1640
1830
|
}
|
|
1641
1831
|
return {
|
|
@@ -1647,34 +1837,27 @@ function gateway(config) {
|
|
|
1647
1837
|
return xrayStore?.callHistory(limit) ?? [];
|
|
1648
1838
|
},
|
|
1649
1839
|
async complete(request) {
|
|
1840
|
+
validateRequestLimits(request, maxMessages, maxInputTokens);
|
|
1650
1841
|
return executeWithMiddleware(request);
|
|
1651
1842
|
},
|
|
1652
1843
|
stream(request) {
|
|
1844
|
+
validateRequestLimits(request, maxMessages, maxInputTokens);
|
|
1653
1845
|
const req = { ...request, model: request.model ?? defaultModel };
|
|
1654
1846
|
if (composedMiddleware) {
|
|
1655
|
-
const ctx = {
|
|
1656
|
-
request: req,
|
|
1657
|
-
provider: provider.name,
|
|
1658
|
-
model: req.model ?? defaultModel,
|
|
1659
|
-
traceId: generateTraceId(),
|
|
1660
|
-
startTime: performance.now(),
|
|
1661
|
-
metadata: request.metadata ?? {}
|
|
1662
|
-
};
|
|
1847
|
+
const ctx = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
|
|
1663
1848
|
return createStream(async (emit) => {
|
|
1664
1849
|
await composedMiddleware(ctx, async (c) => {
|
|
1665
|
-
const
|
|
1666
|
-
|
|
1667
|
-
emit(event);
|
|
1668
|
-
}
|
|
1850
|
+
const result = await accumulateStreamEvents(provider.stream(c.request), emit);
|
|
1851
|
+
const latencyMs = Math.round(performance.now() - ctx.startTime);
|
|
1669
1852
|
return {
|
|
1670
|
-
id:
|
|
1671
|
-
message: { role: "assistant", content:
|
|
1672
|
-
usage:
|
|
1673
|
-
cost:
|
|
1853
|
+
id: result.id,
|
|
1854
|
+
message: { role: "assistant", content: result.textContent },
|
|
1855
|
+
usage: result.usage,
|
|
1856
|
+
cost: calculateCost(c.model, result.usage),
|
|
1674
1857
|
model: c.model,
|
|
1675
1858
|
provider: provider.name,
|
|
1676
|
-
stopReason:
|
|
1677
|
-
latencyMs
|
|
1859
|
+
stopReason: result.stopReason,
|
|
1860
|
+
latencyMs,
|
|
1678
1861
|
traceId: ctx.traceId
|
|
1679
1862
|
};
|
|
1680
1863
|
});
|
|
@@ -1854,6 +2037,7 @@ function createSpan(name, options = {}) {
|
|
|
1854
2037
|
return span;
|
|
1855
2038
|
}
|
|
1856
2039
|
// ../observe/src/tracer.ts
|
|
2040
|
+
import { writeFileSync } from "node:fs";
|
|
1857
2041
|
var log2 = createLogger();
|
|
1858
2042
|
function observe(config = {}) {
|
|
1859
2043
|
const {
|
|
@@ -1869,7 +2053,21 @@ function observe(config = {}) {
|
|
|
1869
2053
|
for (const out of output) {
|
|
1870
2054
|
if (out === "console") {
|
|
1871
2055
|
handlers.push(consoleHandler);
|
|
1872
|
-
} else if (out === "json-file") {
|
|
2056
|
+
} else if (out === "json-file") {
|
|
2057
|
+
exporters.push({
|
|
2058
|
+
name: "json-file",
|
|
2059
|
+
export(spansToExport) {
|
|
2060
|
+
const filename = `.elsium/traces-${Date.now()}.json`;
|
|
2061
|
+
try {
|
|
2062
|
+
writeFileSync(filename, JSON.stringify(spansToExport, null, 2));
|
|
2063
|
+
} catch (err2) {
|
|
2064
|
+
log2.error("Failed to write trace file", {
|
|
2065
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
});
|
|
2070
|
+
} else {
|
|
1873
2071
|
exporters.push(out);
|
|
1874
2072
|
}
|
|
1875
2073
|
}
|
|
@@ -4049,7 +4247,7 @@ var Hono2 = class extends Hono {
|
|
|
4049
4247
|
// src/middleware.ts
|
|
4050
4248
|
import { timingSafeEqual } from "node:crypto";
|
|
4051
4249
|
function corsMiddleware(config = true) {
|
|
4052
|
-
const opts = typeof config === "boolean" ? { origin:
|
|
4250
|
+
const opts = typeof config === "boolean" ? { origin: "*", methods: ["GET", "POST", "OPTIONS"] } : config;
|
|
4053
4251
|
return async (c, next) => {
|
|
4054
4252
|
const requestOrigin = c.req.header("Origin") ?? "";
|
|
4055
4253
|
let allowedOrigin;
|
|
@@ -4126,8 +4324,51 @@ function rateLimitMiddleware(config) {
|
|
|
4126
4324
|
await next();
|
|
4127
4325
|
};
|
|
4128
4326
|
}
|
|
4327
|
+
function requestIdMiddleware() {
|
|
4328
|
+
return async (c, next) => {
|
|
4329
|
+
const raw2 = c.req.header("X-Request-ID");
|
|
4330
|
+
const id = raw2 && /^[\w\-.:]{1,128}$/.test(raw2) ? raw2 : generateId("req");
|
|
4331
|
+
c.set("requestId", id);
|
|
4332
|
+
await next();
|
|
4333
|
+
c.res.headers.set("X-Request-ID", id);
|
|
4334
|
+
};
|
|
4335
|
+
}
|
|
4336
|
+
function requestLoggerMiddleware(logger) {
|
|
4337
|
+
const log4 = logger ?? createLogger();
|
|
4338
|
+
return async (c, next) => {
|
|
4339
|
+
const start = Date.now();
|
|
4340
|
+
await next();
|
|
4341
|
+
const duration = Date.now() - start;
|
|
4342
|
+
log4.info(`${c.req.method} ${c.req.path}`, {
|
|
4343
|
+
method: c.req.method,
|
|
4344
|
+
path: c.req.path,
|
|
4345
|
+
status: c.res.status,
|
|
4346
|
+
durationMs: duration,
|
|
4347
|
+
requestId: c.get("requestId")
|
|
4348
|
+
});
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4129
4351
|
|
|
4130
4352
|
// src/routes.ts
|
|
4353
|
+
function parseJsonBody(raw2) {
|
|
4354
|
+
try {
|
|
4355
|
+
return { ok: true, data: JSON.parse(raw2) };
|
|
4356
|
+
} catch {
|
|
4357
|
+
return { ok: false };
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
function elsiumErrorResponse(c, err2, fallbackMessage) {
|
|
4361
|
+
if (err2 instanceof ElsiumError) {
|
|
4362
|
+
return c.json({ error: err2.message, code: err2.code }, err2.statusCode ?? 500);
|
|
4363
|
+
}
|
|
4364
|
+
return c.json({ error: fallbackMessage }, 500);
|
|
4365
|
+
}
|
|
4366
|
+
function resolveAgent(name, agents, defaultAgent) {
|
|
4367
|
+
const agent = name ? agents.get(name) : defaultAgent;
|
|
4368
|
+
if (agent)
|
|
4369
|
+
return { agent };
|
|
4370
|
+
return { error: name ? `Agent "${name}" not found` : "No default agent configured" };
|
|
4371
|
+
}
|
|
4131
4372
|
function createRoutes(deps) {
|
|
4132
4373
|
const app = new Hono2;
|
|
4133
4374
|
let totalRequests = 0;
|
|
@@ -4168,17 +4409,24 @@ function createRoutes(deps) {
|
|
|
4168
4409
|
if (rawText.length > MAX_BODY_SIZE) {
|
|
4169
4410
|
return c.json({ error: "Request body too large (max 1MB)" }, 413);
|
|
4170
4411
|
}
|
|
4171
|
-
const
|
|
4412
|
+
const parsed = parseJsonBody(rawText);
|
|
4413
|
+
if (!parsed.ok) {
|
|
4414
|
+
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
4415
|
+
}
|
|
4416
|
+
const body = parsed.data;
|
|
4172
4417
|
if (!body.message) {
|
|
4173
4418
|
return c.json({ error: "message is required" }, 400);
|
|
4174
4419
|
}
|
|
4175
|
-
const
|
|
4176
|
-
if (
|
|
4177
|
-
return c.json({
|
|
4178
|
-
|
|
4179
|
-
|
|
4420
|
+
const resolved = resolveAgent(body.agent, deps.agents, deps.defaultAgent);
|
|
4421
|
+
if ("error" in resolved) {
|
|
4422
|
+
return c.json({ error: resolved.error }, 404);
|
|
4423
|
+
}
|
|
4424
|
+
let result;
|
|
4425
|
+
try {
|
|
4426
|
+
result = await resolved.agent.run(body.message);
|
|
4427
|
+
} catch (err2) {
|
|
4428
|
+
return elsiumErrorResponse(c, err2, "Agent execution failed");
|
|
4180
4429
|
}
|
|
4181
|
-
const result = await agent.run(body.message);
|
|
4182
4430
|
deps.tracer?.trackLLMCall({
|
|
4183
4431
|
model: "unknown",
|
|
4184
4432
|
inputTokens: result.usage.totalInputTokens,
|
|
@@ -4195,7 +4443,7 @@ function createRoutes(deps) {
|
|
|
4195
4443
|
totalTokens: result.usage.totalTokens,
|
|
4196
4444
|
cost: result.usage.totalCost
|
|
4197
4445
|
},
|
|
4198
|
-
model: agent.config.model ?? "default",
|
|
4446
|
+
model: resolved.agent.config.model ?? "default",
|
|
4199
4447
|
traceId: result.traceId
|
|
4200
4448
|
};
|
|
4201
4449
|
return c.json(response);
|
|
@@ -4207,7 +4455,11 @@ function createRoutes(deps) {
|
|
|
4207
4455
|
if (rawText.length > MAX_BODY_SIZE) {
|
|
4208
4456
|
return c.json({ error: "Request body too large (max 1MB)" }, 413);
|
|
4209
4457
|
}
|
|
4210
|
-
const
|
|
4458
|
+
const parsed = parseJsonBody(rawText);
|
|
4459
|
+
if (!parsed.ok) {
|
|
4460
|
+
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
4461
|
+
}
|
|
4462
|
+
const body = parsed.data;
|
|
4211
4463
|
if (!body.messages?.length) {
|
|
4212
4464
|
return c.json({ error: "messages array is required" }, 400);
|
|
4213
4465
|
}
|
|
@@ -4215,13 +4467,18 @@ function createRoutes(deps) {
|
|
|
4215
4467
|
role: m.role,
|
|
4216
4468
|
content: m.content
|
|
4217
4469
|
}));
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4470
|
+
let response;
|
|
4471
|
+
try {
|
|
4472
|
+
response = await deps.gateway.complete({
|
|
4473
|
+
messages,
|
|
4474
|
+
model: body.model,
|
|
4475
|
+
system: body.system,
|
|
4476
|
+
maxTokens: body.maxTokens,
|
|
4477
|
+
temperature: body.temperature
|
|
4478
|
+
});
|
|
4479
|
+
} catch (err2) {
|
|
4480
|
+
return elsiumErrorResponse(c, err2, "Completion failed");
|
|
4481
|
+
}
|
|
4225
4482
|
deps.tracer?.trackLLMCall({
|
|
4226
4483
|
model: response.model,
|
|
4227
4484
|
inputTokens: response.usage.inputTokens,
|
|
@@ -4253,6 +4510,15 @@ function createRoutes(deps) {
|
|
|
4253
4510
|
var log4 = createLogger();
|
|
4254
4511
|
function createApp(config) {
|
|
4255
4512
|
const app = new Hono2;
|
|
4513
|
+
app.onError((err2, c) => {
|
|
4514
|
+
const statusCode = err2 instanceof ElsiumError ? err2.statusCode ?? 500 : 500;
|
|
4515
|
+
const code = err2 instanceof ElsiumError ? err2.code : "UNKNOWN";
|
|
4516
|
+
log4.error("Unhandled error", { error: err2.message, code, path: c.req.path });
|
|
4517
|
+
return c.json({ error: err2.message, code }, statusCode);
|
|
4518
|
+
});
|
|
4519
|
+
app.notFound((c) => {
|
|
4520
|
+
return c.json({ error: "Not found" }, 404);
|
|
4521
|
+
});
|
|
4256
4522
|
const providerNames = Object.keys(config.gateway.providers);
|
|
4257
4523
|
const primaryProvider = providerNames[0];
|
|
4258
4524
|
const primaryConfig = config.gateway.providers[primaryProvider];
|
|
@@ -4267,6 +4533,8 @@ function createApp(config) {
|
|
|
4267
4533
|
costTracking: config.observe?.costTracking ?? true
|
|
4268
4534
|
});
|
|
4269
4535
|
const serverConfig = config.server ?? {};
|
|
4536
|
+
app.use("*", requestIdMiddleware());
|
|
4537
|
+
app.use("*", requestLoggerMiddleware(log4));
|
|
4270
4538
|
if (serverConfig.cors) {
|
|
4271
4539
|
app.use("*", corsMiddleware(serverConfig.cors));
|
|
4272
4540
|
}
|
|
@@ -4289,7 +4557,7 @@ function createApp(config) {
|
|
|
4289
4557
|
defaultAgent,
|
|
4290
4558
|
tracer,
|
|
4291
4559
|
startTime: Date.now(),
|
|
4292
|
-
version: "0.
|
|
4560
|
+
version: config.version ?? "0.2.2",
|
|
4293
4561
|
providers: providerNames
|
|
4294
4562
|
});
|
|
4295
4563
|
app.route("/", routes);
|
|
@@ -4305,13 +4573,25 @@ function createApp(config) {
|
|
|
4305
4573
|
port: listenPort,
|
|
4306
4574
|
hostname
|
|
4307
4575
|
});
|
|
4576
|
+
let shutdownManager;
|
|
4577
|
+
if (serverConfig.gracefulShutdown) {
|
|
4578
|
+
const drainTimeoutMs = typeof serverConfig.gracefulShutdown === "object" ? serverConfig.gracefulShutdown.drainTimeoutMs : undefined;
|
|
4579
|
+
shutdownManager = createShutdownManager({
|
|
4580
|
+
drainTimeoutMs,
|
|
4581
|
+
onDrainStart: () => log4.info("Draining connections..."),
|
|
4582
|
+
onDrainComplete: () => log4.info("Drain complete")
|
|
4583
|
+
});
|
|
4584
|
+
}
|
|
4308
4585
|
log4.info("ElsiumAI server started", {
|
|
4309
4586
|
url: `http://${hostname}:${listenPort}`,
|
|
4310
4587
|
routes: ["POST /chat", "POST /complete", "GET /health", "GET /metrics", "GET /agents"]
|
|
4311
4588
|
});
|
|
4312
4589
|
return {
|
|
4313
4590
|
port: listenPort,
|
|
4314
|
-
stop: () => {
|
|
4591
|
+
stop: async () => {
|
|
4592
|
+
if (shutdownManager) {
|
|
4593
|
+
await shutdownManager.shutdown();
|
|
4594
|
+
}
|
|
4315
4595
|
server.close();
|
|
4316
4596
|
}
|
|
4317
4597
|
};
|
|
@@ -4414,6 +4694,8 @@ function createRBAC(config) {
|
|
|
4414
4694
|
};
|
|
4415
4695
|
}
|
|
4416
4696
|
export {
|
|
4697
|
+
requestLoggerMiddleware,
|
|
4698
|
+
requestIdMiddleware,
|
|
4417
4699
|
rateLimitMiddleware,
|
|
4418
4700
|
createRoutes,
|
|
4419
4701
|
createRBAC,
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Logger } from '@elsium-ai/core';
|
|
1
2
|
import type { Context, Next } from 'hono';
|
|
2
3
|
import type { AuthConfig, CorsConfig, RateLimitConfig } from './types';
|
|
3
4
|
export declare function corsMiddleware(config?: CorsConfig | boolean): (c: Context, next: Next) => Promise<(Response & import("hono").TypedResponse<null, 200, "body">) | undefined>;
|
|
@@ -8,4 +9,6 @@ export declare function rateLimitMiddleware(config: RateLimitConfig): (c: Contex
|
|
|
8
9
|
error: string;
|
|
9
10
|
retryAfterMs: number;
|
|
10
11
|
}, 429, "json">) | undefined>;
|
|
12
|
+
export declare function requestIdMiddleware(): (c: Context, next: Next) => Promise<void>;
|
|
13
|
+
export declare function requestLoggerMiddleware(logger?: Logger): (c: Context, next: Next) => Promise<void>;
|
|
11
14
|
//# sourceMappingURL=middleware.d.ts.map
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAItE,wBAAgB,cAAc,CAAC,MAAM,GAAE,UAAU,GAAG,OAAc,IAInD,GAAG,OAAO,EAAE,MAAM,IAAI,uFAiCpC;AAID,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,IAClC,GAAG,OAAO,EAAE,MAAM,IAAI;;kBAwBpC;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,IAG5C,GAAG,OAAO,EAAE,MAAM,IAAI;;;8BAsCpC"}
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAA4B,MAAM,iBAAiB,CAAA;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAItE,wBAAgB,cAAc,CAAC,MAAM,GAAE,UAAU,GAAG,OAAc,IAInD,GAAG,OAAO,EAAE,MAAM,IAAI,uFAiCpC;AAID,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,IAClC,GAAG,OAAO,EAAE,MAAM,IAAI;;kBAwBpC;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,IAG5C,GAAG,OAAO,EAAE,MAAM,IAAI;;;8BAsCpC;AAID,wBAAgB,mBAAmB,KACpB,GAAG,OAAO,EAAE,MAAM,IAAI,mBASpC;AAID,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,MAAM,IAGxC,GAAG,OAAO,EAAE,MAAM,IAAI,mBAcpC"}
|
package/dist/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAkC3B,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAyKnD"}
|
package/dist/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface AppConfig {
|
|
|
16
16
|
export?: string;
|
|
17
17
|
};
|
|
18
18
|
server?: ServerConfig;
|
|
19
|
+
version?: string;
|
|
19
20
|
}
|
|
20
21
|
export interface ServerConfig {
|
|
21
22
|
port?: number;
|
|
@@ -23,6 +24,9 @@ export interface ServerConfig {
|
|
|
23
24
|
cors?: boolean | CorsConfig;
|
|
24
25
|
auth?: AuthConfig;
|
|
25
26
|
rateLimit?: RateLimitConfig;
|
|
27
|
+
gracefulShutdown?: boolean | {
|
|
28
|
+
drainTimeoutMs?: number;
|
|
29
|
+
};
|
|
26
30
|
}
|
|
27
31
|
export interface CorsConfig {
|
|
28
32
|
origin?: string | string[];
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,WAAW,SAAS;IACzB,OAAO,EAAE;QACR,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC/D,YAAY,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,MAAM,CAAC,EAAE,KAAK,EAAE,CAAA;IAChB,GAAG,CAAC,EAAE,WAAW,CAAA;IACjB,OAAO,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,YAAY,CAAC,EAAE,OAAO,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,MAAM,CAAC,EAAE,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,WAAW,SAAS;IACzB,OAAO,EAAE;QACR,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC/D,YAAY,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,MAAM,CAAC,EAAE,KAAK,EAAE,CAAA;IAChB,GAAG,CAAC,EAAE,WAAW,CAAA;IACjB,OAAO,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,YAAY,CAAC,EAAE,OAAO,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,gBAAgB,CAAC,EAAE,OAAO,GAAG;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxD;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACnB;AAID,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE;QACN,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;QACpB,WAAW,EAAE,MAAM,CAAA;QACnB,IAAI,EAAE,MAAM,CAAA;KACZ,CAAA;IACD,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,IAAI,GAAG,UAAU,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC3E"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elsium-ai/app",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "App bootstrap, HTTP server, and API routes for ElsiumAI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eric Utrera <ebutrera9103@gmail.com>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/elsium-ai/elsium-ai",
|
|
9
|
+
"url": "git+https://github.com/elsium-ai/elsium-ai.git",
|
|
10
10
|
"directory": "packages/app"
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"dev": "bun --watch src/index.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@elsium-ai/core": "^0.2.
|
|
30
|
-
"@elsium-ai/gateway": "^0.2.
|
|
31
|
-
"@elsium-ai/agents": "^0.2.
|
|
32
|
-
"@elsium-ai/tools": "^0.2.
|
|
33
|
-
"@elsium-ai/observe": "^0.2.
|
|
34
|
-
"@elsium-ai/rag": "^0.2.
|
|
35
|
-
"@elsium-ai/workflows": "^0.2.
|
|
29
|
+
"@elsium-ai/core": "^0.2.3",
|
|
30
|
+
"@elsium-ai/gateway": "^0.2.3",
|
|
31
|
+
"@elsium-ai/agents": "^0.2.3",
|
|
32
|
+
"@elsium-ai/tools": "^0.2.3",
|
|
33
|
+
"@elsium-ai/observe": "^0.2.3",
|
|
34
|
+
"@elsium-ai/rag": "^0.2.3",
|
|
35
|
+
"@elsium-ai/workflows": "^0.2.3",
|
|
36
36
|
"@hono/node-server": "^1.13.0",
|
|
37
37
|
"hono": "^4.7.0",
|
|
38
38
|
"zod": "^3.24.0"
|