@cogitator-ai/tetsu 0.1.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/LICENSE +21 -0
- package/README.md +268 -0
- package/dist/auth.d.ts +25 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +30 -0
- package/dist/auth.js.map +1 -0
- package/dist/controller.d.ts +804 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +476 -0
- package/dist/controller.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +70 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/operations.d.ts +41 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +162 -0
- package/dist/operations.js.map +1 -0
- package/dist/schemas.d.ts +305 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +185 -0
- package/dist/schemas.js.map +1 -0
- package/dist/socket.d.ts +40 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +120 -0
- package/dist/socket.js.map +1 -0
- package/dist/streaming.d.ts +16 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +82 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Cogitator Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# @cogitator-ai/tetsu
|
|
2
|
+
|
|
3
|
+
[Tetsu](https://tetsujs.com) adapter for the Cogitator AI runtime. The whole Cogitator HTTP API — agents, memory threads, workflows, swarms, SSE streams and a WebSocket — as one Tetsu controller, with Zod schemas that validate requests and responses and describe them in the OpenAPI document.
|
|
4
|
+
|
|
5
|
+
Tetsu runs on **Bun 1.4 or later**.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @cogitator-ai/tetsu @cogitator-ai/core @tetsujs/core @tetsujs/sse
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@cogitator-ai/workflows` and `@cogitator-ai/swarms` are optional: add them to serve the workflow and swarm endpoints. `@tetsujs/openapi` is optional too, for the generated document.
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { Agent, Cogitator } from '@cogitator-ai/core';
|
|
19
|
+
import { cogitatorController } from '@cogitator-ai/tetsu';
|
|
20
|
+
import { createApp, group } from '@tetsujs/core';
|
|
21
|
+
import { docs } from '@tetsujs/openapi';
|
|
22
|
+
|
|
23
|
+
const cogitator = new Cogitator({
|
|
24
|
+
llm: { defaultModel: 'google/gemini-3.5-flash-lite' },
|
|
25
|
+
memory: { adapter: 'memory' },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const chat = new Agent({
|
|
29
|
+
name: 'chat',
|
|
30
|
+
model: 'google/gemini-3.5-flash-lite',
|
|
31
|
+
instructions: 'You are a helpful assistant.',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const app = createApp({
|
|
35
|
+
routes: [
|
|
36
|
+
group('/cogitator', { children: [cogitatorController({ cogitator, agents: { chat } })] }),
|
|
37
|
+
docs({ info: { title: 'Agents', version: '1.0.0' } }),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
Bun.serve({ ...app, port: 3000 });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
curl -X POST http://localhost:3000/cogitator/agents/chat/run \
|
|
46
|
+
-H 'Content-Type: application/json' -d '{"input": "Hello"}'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## `cogitatorController(deps)`
|
|
50
|
+
|
|
51
|
+
A Tetsu controller named `Cogitator`: `operationId`s in the OpenAPI document are `cogitatorRunAgent`, `cogitatorGetThread` and so on.
|
|
52
|
+
|
|
53
|
+
| Option | Type | Description |
|
|
54
|
+
| ----------------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
55
|
+
| `cogitator` | `Cogitator` | **Required.** The runtime |
|
|
56
|
+
| `agents` | `Record<string, Agent>` | Agents by name |
|
|
57
|
+
| `workflows` | `Record<string, Workflow>` | Workflows by name |
|
|
58
|
+
| `swarms` | `Record<string, SwarmConfig>` | Swarms by name |
|
|
59
|
+
| `auth` | `(ctx) => AuthContext \| undefined` or hook | Establishes the caller; see [Authentication](#authentication) |
|
|
60
|
+
| `authorizeThread` | `(auth, threadId) => boolean` | Decides who may use a memory thread; see [Users and threads](#users-and-threads) |
|
|
61
|
+
| `websocket` | `boolean \| { path?: string }` | Serve the WebSocket endpoint (default path `/ws`) |
|
|
62
|
+
| `until` | `AbortSignal \| (() => AbortSignal)` | Ends open streams and sockets, such as `draining` from `@tetsujs/lifecycle` |
|
|
63
|
+
|
|
64
|
+
## Endpoints
|
|
65
|
+
|
|
66
|
+
| Method | Path | Description |
|
|
67
|
+
| -------- | -------------------------- | --------------------------------------- |
|
|
68
|
+
| `GET` | `/health`, `/ready` | Liveness and readiness, without `auth` |
|
|
69
|
+
| `GET` | `/agents` | Agents with their description and tools |
|
|
70
|
+
| `POST` | `/agents/:name/run` | Run an agent and wait for the answer |
|
|
71
|
+
| `POST` | `/agents/:name/stream` | Run an agent over SSE |
|
|
72
|
+
| `GET` | `/tools` | Tools of every agent, as JSON Schema |
|
|
73
|
+
| `GET` | `/threads/:id` | Messages of a memory thread |
|
|
74
|
+
| `POST` | `/threads/:id/messages` | Append a message |
|
|
75
|
+
| `DELETE` | `/threads/:id` | Delete a thread |
|
|
76
|
+
| `GET` | `/workflows` | Workflows with their nodes |
|
|
77
|
+
| `POST` | `/workflows/:name/run` | Run a workflow |
|
|
78
|
+
| `POST` | `/workflows/:name/stream` | Run a workflow over SSE |
|
|
79
|
+
| `GET` | `/swarms` | Swarms with their agents |
|
|
80
|
+
| `POST` | `/swarms/:name/run` | Run a swarm |
|
|
81
|
+
| `POST` | `/swarms/:name/stream` | Run a swarm over SSE |
|
|
82
|
+
| `GET` | `/swarms/:name/blackboard` | Configured blackboard sections |
|
|
83
|
+
| `GET` | `/ws` | WebSocket, when `websocket` is set |
|
|
84
|
+
|
|
85
|
+
Request bodies:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// POST /agents/:name/run, /agents/:name/stream
|
|
89
|
+
{ input: string; context?: Record<string, unknown>; threadId?: string }
|
|
90
|
+
|
|
91
|
+
// POST /swarms/:name/run, /swarms/:name/stream
|
|
92
|
+
{ input: string; context?: Record<string, unknown>; threadId?: string; timeout?: number }
|
|
93
|
+
|
|
94
|
+
// POST /workflows/:name/run, /workflows/:name/stream
|
|
95
|
+
{ input?: Record<string, unknown>; options?: { maxConcurrency?: number; maxIterations?: number; checkpoint?: boolean } }
|
|
96
|
+
|
|
97
|
+
// POST /threads/:id/messages
|
|
98
|
+
{ role: 'user' | 'assistant' | 'system'; content: string; metadata?: Record<string, unknown> }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Errors
|
|
102
|
+
|
|
103
|
+
Every error is answered in Tetsu's envelope:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{ "status": 404, "message": "Agent 'ghost' not found", "error": "AGENT_NOT_FOUND" }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Status | `error` | When |
|
|
110
|
+
| ------- | ---------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
111
|
+
| 401 | `UNAUTHORIZED` | `auth` returned `undefined` |
|
|
112
|
+
| 403 | `THREAD_FORBIDDEN` | `authorizeThread` refused the thread |
|
|
113
|
+
| 404 | `AGENT_NOT_FOUND`, `WORKFLOW_NOT_FOUND`, `SWARM_NOT_FOUND` | No such name |
|
|
114
|
+
| 409 | `BLACKBOARD_DISABLED` | The swarm has no blackboard |
|
|
115
|
+
| 422 | `VALIDATION_FAILED` | The body or the path failed its schema; `issues` lists every problem |
|
|
116
|
+
| 501 | `PACKAGE_NOT_INSTALLED` | `@cogitator-ai/workflows` or `@cogitator-ai/swarms` is missing |
|
|
117
|
+
| 503 | `MEMORY_NOT_CONFIGURED` | A thread endpoint was called on a runtime without memory |
|
|
118
|
+
| 4xx/5xx | the `CogitatorError` code | The run failed, e.g. `429 LLM_RATE_LIMITED` with `Retry-After` |
|
|
119
|
+
| 500 | `INTERNAL_SERVER_ERROR` | Anything else; the detail goes to `reportError`, never to the client |
|
|
120
|
+
|
|
121
|
+
Every route mounts an `onError` hook made by `cogitatorErrors()`. Mount another on the application to answer `CogitatorError`s from your own routes the same way:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { cogitatorErrors } from '@cogitator-ai/tetsu';
|
|
125
|
+
|
|
126
|
+
createApp({ hooks: { onError: [cogitatorErrors()] }, routes });
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Authentication
|
|
130
|
+
|
|
131
|
+
`auth` runs as a `beforeParse` hook, so a refused request costs no body parsing. It receives the Tetsu context and returns the caller, or `undefined` to answer `401`. Throw an `HttpError` to answer with another status. `/health` and `/ready` stay open for probes.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
cogitatorController({
|
|
135
|
+
cogitator,
|
|
136
|
+
agents: { chat },
|
|
137
|
+
auth: (ctx) => {
|
|
138
|
+
const token = ctx.req.headers.get('authorization')?.replace(/^Bearer /, '');
|
|
139
|
+
const user = token ? sessions.find(token) : undefined;
|
|
140
|
+
return user && { userId: user.id, roles: user.roles };
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The caller's `userId` is passed to every run, so memory, cost tracking and tools (`context.userId`) know who is asking.
|
|
146
|
+
|
|
147
|
+
To have the OpenAPI document describe the scheme and the `401`, build the hook with `callerHook()` and wrap it in `secured()`. The same hook can guard your own routes, where the caller is `ctx.cogitatorAuth`:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { callerHook, cogitatorController } from '@cogitator-ai/tetsu';
|
|
151
|
+
import { secured } from '@tetsujs/openapi';
|
|
152
|
+
|
|
153
|
+
const signedIn = secured(callerHook(authenticate), {
|
|
154
|
+
name: 'bearerAuth',
|
|
155
|
+
scheme: { type: 'http', scheme: 'bearer' },
|
|
156
|
+
error: 'UNAUTHORIZED',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const me = controller('Me', () => ({
|
|
160
|
+
profile: route({
|
|
161
|
+
method: 'GET',
|
|
162
|
+
path: '/me',
|
|
163
|
+
hooks: { beforeParse: [signedIn] },
|
|
164
|
+
handler: (ctx) => profiles.get(ctx.cogitatorAuth?.userId ?? ''),
|
|
165
|
+
}),
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
createApp({
|
|
169
|
+
routes: [
|
|
170
|
+
me(),
|
|
171
|
+
group('/agent', { children: [cogitatorController({ cogitator, agents, auth: signedIn })] }),
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Users and threads
|
|
177
|
+
|
|
178
|
+
`authorizeThread` is checked on the thread endpoints and on every run, stream and WebSocket run that names a `threadId`, before the model is called:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
cogitatorController({
|
|
182
|
+
cogitator,
|
|
183
|
+
agents: { chat },
|
|
184
|
+
auth,
|
|
185
|
+
authorizeThread: (auth, threadId) => threadId.startsWith(`${auth?.userId}:`),
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Without it, any caller that passes `auth` may read and write any thread.
|
|
190
|
+
|
|
191
|
+
## Streaming
|
|
192
|
+
|
|
193
|
+
`/stream` endpoints answer with `text/event-stream` through `@tetsujs/sse`: keep-alive comments every 15 seconds, backpressure, and the run is aborted when the client goes away. Events follow the Cogitator stream protocol shared with the other adapters, one JSON object per `data:` line, ending with `data: [DONE]`:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
data: {"type":"start","messageId":"msg_…"}
|
|
197
|
+
data: {"type":"text-start","id":"txt_…"}
|
|
198
|
+
data: {"type":"text-delta","id":"txt_…","delta":"Hel"}
|
|
199
|
+
data: {"type":"tool-call-start","id":"call_1","toolName":"get_weather"}
|
|
200
|
+
data: {"type":"tool-call-delta","id":"call_1","argsTextDelta":"{\"city\":\"Paris\"}"}
|
|
201
|
+
data: {"type":"tool-call-end","id":"call_1"}
|
|
202
|
+
data: {"type":"tool-result","id":"res_…","toolCallId":"call_1","result":"Sunny"}
|
|
203
|
+
data: {"type":"text-end","id":"txt_…"}
|
|
204
|
+
data: {"type":"finish","messageId":"msg_…","usage":{"inputTokens":12,"outputTokens":30,"totalTokens":42}}
|
|
205
|
+
data: [DONE]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
A run that fails ends with `{"type":"error","message":"…","code":"…"}` instead of `finish`. An unexpected failure is also reported to the application's `reportError` with `source: "stream"`. Workflow streams send `workflow` events (`node_started`, `node_completed`, `node_error`, `node_progress`, `workflow_completed`), swarm streams send `swarm` events (`agent_start`, `agent_complete`, `agent_error`, `message`, `swarm_completed`).
|
|
209
|
+
|
|
210
|
+
Validation, `401`, `403` and `404` are answered as JSON before the stream opens.
|
|
211
|
+
|
|
212
|
+
## WebSocket
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
cogitatorController({ cogitator, agents, auth, websocket: true });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The handshake runs `auth` like any route. Each socket runs one agent, workflow or swarm at a time.
|
|
219
|
+
|
|
220
|
+
| Client sends | Server answers |
|
|
221
|
+
| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
222
|
+
| `{ type: 'run', id?, payload: { type: 'agent' \| 'workflow' \| 'swarm', name, input, context?, threadId? } }` | `event` frames: `token`, `tool-call`, `tool-result`, then `complete` with the result, or `cancelled` |
|
|
223
|
+
| `{ type: 'stop' }` | cancels the current run |
|
|
224
|
+
| `{ type: 'ping', id? }` | `{ type: 'pong', id }` |
|
|
225
|
+
|
|
226
|
+
Errors arrive as `{ type: 'error', id, error, code }` and leave the socket open; an invalid frame gets `code: 'INVALID_MESSAGE'`. Closing the socket cancels its run. Bun's server-level options such as `maxPayloadLength` are set where the app is served:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
Bun.serve({ ...app, websocket: { ...app.websocket, maxPayloadLength: 1024 * 1024 } });
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Shutdown
|
|
233
|
+
|
|
234
|
+
Streams and sockets can stay open for minutes. Pass the `draining` signal of `@tetsujs/lifecycle` so a stopping server ends them and clients reconnect to one that stays:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
let shutdown: ReturnType<typeof onShutdownSignals> | undefined;
|
|
238
|
+
|
|
239
|
+
const app = createApp({
|
|
240
|
+
routes: cogitatorController({ cogitator, agents, until: () => shutdown?.draining }),
|
|
241
|
+
});
|
|
242
|
+
const server = Bun.serve({ ...app });
|
|
243
|
+
shutdown = onShutdownSignals(server);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## OpenAPI
|
|
247
|
+
|
|
248
|
+
Request and response schemas are Zod, so `docs()` and `openapi()` from `@tetsujs/openapi` describe every route, the run failures a `CogitatorError` can produce, and the schemes of a `secured()` caller hook. SSE and WebSocket endpoints are documented by their description only, since OpenAPI cannot describe what follows the headers.
|
|
249
|
+
|
|
250
|
+
## Testing
|
|
251
|
+
|
|
252
|
+
The package is tested with `bun test`: handlers called with `testCtx()`, and the full pipeline through `serve()` from `@tetsujs/core/testing`, including SSE, WebSocket and `assertDescribed()` checks against the OpenAPI document.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
pnpm --filter @cogitator-ai/tetsu test
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Example
|
|
259
|
+
|
|
260
|
+
[`examples/integrations/08-tetsu-server.ts`](../../examples/integrations/08-tetsu-server.ts) — an app with its own users, its own MCP server for its domain tools, and an agent that acts for the signed-in user:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
bun examples/integrations/08-tetsu-server.ts
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
MIT
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuthContext, Authenticate, CogitatorDeps } from './types.js';
|
|
2
|
+
/** What the caller hook adds to the context of every guarded route. */
|
|
3
|
+
export interface CallerFields {
|
|
4
|
+
cogitatorAuth: AuthContext | undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The `beforeParse` hook that establishes the caller as `ctx.cogitatorAuth`.
|
|
8
|
+
*
|
|
9
|
+
* It runs before the body is read, so a refused request costs no parsing. Without
|
|
10
|
+
* an `authenticate` function every caller is anonymous.
|
|
11
|
+
*/
|
|
12
|
+
export declare function callerHook(authenticate: Authenticate | undefined): import("@tetsujs/core").Hook<"beforeParse", import("@tetsujs/core").BaseCtx & {
|
|
13
|
+
readonly params: {
|
|
14
|
+
[x: string]: string;
|
|
15
|
+
};
|
|
16
|
+
}, Pick<CallerFields, "cogitatorAuth">>;
|
|
17
|
+
/** The hook `callerHook()` builds, which `auth` also accepts ready-made. */
|
|
18
|
+
export type CallerHook = ReturnType<typeof callerHook>;
|
|
19
|
+
/**
|
|
20
|
+
* The caller hook for `auth`: built from a function, or taken as it is when it is
|
|
21
|
+
* already a hook — a `callerHook()` wrapped in `secured()` from `@tetsujs/openapi`,
|
|
22
|
+
* so the generated document describes the security scheme and the `401`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveCaller(auth: CogitatorDeps['auth']): CallerHook;
|
|
25
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3E,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,WAAW,GAAG,SAAS,CAAC;CACxC;AASD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS;;;;wCAMhE;AAED,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,UAAU,CAErE"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { hook, httpError } from '@tetsujs/core';
|
|
2
|
+
const ANONYMOUS = { cogitatorAuth: undefined };
|
|
3
|
+
function admit(auth) {
|
|
4
|
+
if (!auth)
|
|
5
|
+
throw httpError(401, 'UNAUTHORIZED', 'Unauthorized');
|
|
6
|
+
return { cogitatorAuth: auth };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The `beforeParse` hook that establishes the caller as `ctx.cogitatorAuth`.
|
|
10
|
+
*
|
|
11
|
+
* It runs before the body is read, so a refused request costs no parsing. Without
|
|
12
|
+
* an `authenticate` function every caller is anonymous.
|
|
13
|
+
*/
|
|
14
|
+
export function callerHook(authenticate) {
|
|
15
|
+
return hook.beforeParse((ctx) => {
|
|
16
|
+
if (!authenticate)
|
|
17
|
+
return { ...ANONYMOUS };
|
|
18
|
+
const auth = authenticate(ctx);
|
|
19
|
+
return auth instanceof Promise ? auth.then(admit) : admit(auth);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The caller hook for `auth`: built from a function, or taken as it is when it is
|
|
24
|
+
* already a hook — a `callerHook()` wrapped in `secured()` from `@tetsujs/openapi`,
|
|
25
|
+
* so the generated document describes the security scheme and the `401`.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveCaller(auth) {
|
|
28
|
+
return typeof auth === 'function' || auth === undefined ? callerHook(auth) : auth;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAQhD,MAAM,SAAS,GAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;AAE7D,SAAS,KAAK,CAAC,IAA6B;IAC1C,IAAI,CAAC,IAAI;QAAE,MAAM,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;IAChE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,YAAsC;IAC/D,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,EAAwC,EAAE;QACpE,IAAI,CAAC,YAAY;YAAE,OAAO,EAAE,GAAG,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,IAAI,YAAY,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAKD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAA2B;IACvD,OAAO,OAAO,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpF,CAAC"}
|