@flowajs/chat-service 0.0.1
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 +210 -0
- package/dist/artifact.d.ts +87 -0
- package/dist/artifact.d.ts.map +1 -0
- package/dist/artifact.js +99 -0
- package/dist/artifact.js.map +1 -0
- package/dist/audit.d.ts +28 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +73 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/jwt.d.ts +18 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +23 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/oidc.d.ts +30 -0
- package/dist/auth/oidc.d.ts.map +1 -0
- package/dist/auth/oidc.js +58 -0
- package/dist/auth/oidc.js.map +1 -0
- package/dist/chat.d.ts +161 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +636 -0
- package/dist/chat.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +47 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +80 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation.d.ts +31 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +151 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/llm/anthropic.d.ts +22 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +40 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/bedrock.d.ts +34 -0
- package/dist/llm/bedrock.d.ts.map +1 -0
- package/dist/llm/bedrock.js +54 -0
- package/dist/llm/bedrock.js.map +1 -0
- package/dist/llm/factory.d.ts +3 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +59 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/google-gla.d.ts +22 -0
- package/dist/llm/google-gla.d.ts.map +1 -0
- package/dist/llm/google-gla.js +29 -0
- package/dist/llm/google-gla.js.map +1 -0
- package/dist/llm/google-vertex.d.ts +21 -0
- package/dist/llm/google-vertex.d.ts.map +1 -0
- package/dist/llm/google-vertex.js +28 -0
- package/dist/llm/google-vertex.js.map +1 -0
- package/dist/llm/interface.d.ts +45 -0
- package/dist/llm/interface.d.ts.map +1 -0
- package/dist/llm/interface.js +2 -0
- package/dist/llm/interface.js.map +1 -0
- package/dist/llm/openai.d.ts +19 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +25 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/prompts.d.ts +7 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +17 -0
- package/dist/prompts.js.map +1 -0
- package/dist/server.d.ts +39 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +106 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +68 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +245 -0
- package/dist/session.js.map +1 -0
- package/dist/storage/factory.d.ts +28 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +33 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/fs.d.ts +14 -0
- package/dist/storage/fs.d.ts.map +1 -0
- package/dist/storage/fs.js +116 -0
- package/dist/storage/fs.js.map +1 -0
- package/dist/storage/gcs.d.ts +27 -0
- package/dist/storage/gcs.d.ts.map +1 -0
- package/dist/storage/gcs.js +81 -0
- package/dist/storage/gcs.js.map +1 -0
- package/dist/storage/interface.d.ts +33 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +12 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/s3.d.ts +29 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +109 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage-keys.d.ts +33 -0
- package/dist/storage-keys.d.ts.map +1 -0
- package/dist/storage-keys.js +76 -0
- package/dist/storage-keys.js.map +1 -0
- package/dist/telemetry.d.ts +29 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +116 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/yaml.d.ts +42 -0
- package/dist/yaml.d.ts.map +1 -0
- package/dist/yaml.js +121 -0
- package/dist/yaml.js.map +1 -0
- package/package.json +124 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Centre for Population Genomics
|
|
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,210 @@
|
|
|
1
|
+
# `@flowajs/chat-service`
|
|
2
|
+
|
|
3
|
+
Stateless service that orchestrates LLM conversations over flowa artifacts.
|
|
4
|
+
Reads aggregates and papers from a configurable storage backend, applies edits
|
|
5
|
+
to a Zod-validated artifact schema, persists draft versions back to storage,
|
|
6
|
+
and streams SSE chat responses.
|
|
7
|
+
|
|
8
|
+
Two consumption modes:
|
|
9
|
+
|
|
10
|
+
- The flowa demo (`examples/demo`) depends on it via `workspace:*` and runs
|
|
11
|
+
the default env-driven entry (`node dist/index.js`).
|
|
12
|
+
- External and production deployments install via npm (`pnpm add
|
|
13
|
+
@flowajs/chat-service`) and wrap the programmatic API (`createApp(...)`)
|
|
14
|
+
with custom storage / LLM / auth wiring. See
|
|
15
|
+
[Production deployment](#production-deployment).
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
┌─ HTTP (Hono) ───────────────────────────────────────────┐
|
|
21
|
+
│ POST /sessions POST /chat/:id GET /health │
|
|
22
|
+
└────────────┬────────────────────────────────────────────┘
|
|
23
|
+
│
|
|
24
|
+
createApp({ storage, provider, jwtSecret, ... })
|
|
25
|
+
│
|
|
26
|
+
┌─────────┴─────────┐
|
|
27
|
+
▼ ▼
|
|
28
|
+
Storage LlmProvider
|
|
29
|
+
fs | s3 | gcs anthropic | bedrock | google-gla
|
|
30
|
+
google-vertex | openai
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The HTTP surface is provider-agnostic. Backends and the LLM provider are
|
|
34
|
+
constructed by the caller (env-driven default in `index.ts`, or programmatic
|
|
35
|
+
in a custom entry).
|
|
36
|
+
|
|
37
|
+
### Storage
|
|
38
|
+
|
|
39
|
+
`Storage` is a thin interface (5 ops: `read` / `write` / `writeIfAbsent` /
|
|
40
|
+
`exists` / `list`). Three backends are available:
|
|
41
|
+
|
|
42
|
+
- **`fs`** — POSIX local filesystem. Atomic create-only via
|
|
43
|
+
`O_CREAT|O_EXCL`.
|
|
44
|
+
- **`s3`** — `@aws-sdk/client-s3`. Atomic create-only via `IfNoneMatch: '*'`.
|
|
45
|
+
Region, endpoint, and credentials are resolved from the AWS SDK's standard
|
|
46
|
+
env vars (`AWS_REGION`, `AWS_ENDPOINT_URL_S3`, `fromNodeProviderChain`); for
|
|
47
|
+
S3-compatible providers (Cloudflare R2, Backblaze B2, MinIO, DigitalOcean
|
|
48
|
+
Spaces, Wasabi, Hetzner, etc.) set those env vars to point at the provider.
|
|
49
|
+
For knobs the SDK doesn't surface as env vars (e.g. `forcePathStyle`,
|
|
50
|
+
custom retry policy), use the `{ client }` programmatic form.
|
|
51
|
+
- **`gcs`** — `@google-cloud/storage`. Atomic create-only via the
|
|
52
|
+
`ifGenerationMatch: 0` precondition. Credentials come from Google Cloud's
|
|
53
|
+
Application Default Credentials chain (`GOOGLE_APPLICATION_CREDENTIALS`,
|
|
54
|
+
gcloud user creds, GCE metadata server, etc.). For deployments needing
|
|
55
|
+
Workload Identity Federation or other custom cred-mint flows, use the
|
|
56
|
+
`{ client }` programmatic form with a pre-built `Storage` client.
|
|
57
|
+
|
|
58
|
+
An Azure Blob backend is the next adapter when a consumer surfaces it; same
|
|
59
|
+
factory shape (`{ bucket, prefix? } | { client, bucket, prefix? }`).
|
|
60
|
+
|
|
61
|
+
### LLM providers
|
|
62
|
+
|
|
63
|
+
`LlmProvider` is a thin interface around a Vercel AI SDK `LanguageModel` plus
|
|
64
|
+
two optional knobs: `providerOptions` (per-provider thinking/reasoning
|
|
65
|
+
config) and `prepareStep(messages)` (per-step messages transformation, used
|
|
66
|
+
by Bedrock for prompt-cache point injection).
|
|
67
|
+
|
|
68
|
+
Five providers are supported: `anthropic`, `bedrock`, `google-gla`,
|
|
69
|
+
`google-vertex`, `openai`. Each ai-sdk package is an optional peer — install
|
|
70
|
+
only what you use. Selection at runtime via `LLM_MODEL=<provider>:<model>`.
|
|
71
|
+
|
|
72
|
+
### Authentication
|
|
73
|
+
|
|
74
|
+
chat-service issues and verifies its own session JWT (`POST /sessions` →
|
|
75
|
+
token; `POST /chat/:id` requires the token). Upstream-IDP authentication
|
|
76
|
+
(validating that the caller of `POST /sessions` is who they claim) is a
|
|
77
|
+
separate concern, exposed as a generic OIDC middleware:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { createOidcMiddleware } from "@flowajs/chat-service/auth/oidc";
|
|
81
|
+
|
|
82
|
+
app.use("/sessions", createOidcMiddleware({
|
|
83
|
+
jwksUrl: process.env.OIDC_JWKS_URL!,
|
|
84
|
+
issuer: process.env.OIDC_ISSUER!,
|
|
85
|
+
audience: process.env.OIDC_AUDIENCE!,
|
|
86
|
+
// dev mode: decode the JWT body without verifying signature.
|
|
87
|
+
// Useful for local dev with a stub IDP. NEVER enable in production.
|
|
88
|
+
devMode: process.env.NODE_ENV === "development",
|
|
89
|
+
}));
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The middleware works against any OIDC IDP (Auth0, Keycloak, Okta, GitHub,
|
|
93
|
+
etc.). The default env-driven `index.ts` applies it on `/sessions` when
|
|
94
|
+
`OIDC_JWKS_URL` is set; otherwise the route is unauthenticated.
|
|
95
|
+
|
|
96
|
+
## Environment configuration (default `index.ts`)
|
|
97
|
+
|
|
98
|
+
| Var | Required? | Purpose |
|
|
99
|
+
|-----|-----------|---------|
|
|
100
|
+
| `LLM_MODEL` | yes | `<provider>:<model>` — e.g. `bedrock:au.anthropic.claude-sonnet-4-6`, `anthropic:claude-sonnet-4-6`, `google-gla:gemini-2.5-pro`, `google-vertex:gemini-2.5-pro`, `openai:gpt-5`. |
|
|
101
|
+
| `STORAGE_BACKEND` | yes | One of `fs`, `s3`, `gcs`. |
|
|
102
|
+
| `STORAGE_FS_ROOT` | when `fs` | Absolute path to the storage root directory. |
|
|
103
|
+
| `STORAGE_S3_BUCKET` | when `s3` | Bucket name. Region, endpoint, and credentials come from the AWS SDK's standard env vars (`AWS_REGION`, `AWS_ENDPOINT_URL_S3`, `AWS_ACCESS_KEY_ID`, etc.); set those to point at AWS S3 or any S3-compat provider. |
|
|
104
|
+
| `STORAGE_GCS_BUCKET` | when `gcs` | Bucket name. Credentials come from Google Cloud's Application Default Credentials chain (`GOOGLE_APPLICATION_CREDENTIALS`, gcloud user creds, GCE metadata server, etc.). |
|
|
105
|
+
| `STORAGE_PREFIX` | no | Prefix prepended to every storage key (regardless of backend). |
|
|
106
|
+
| `CHAT_JWT_SECRET` | yes | Session JWT signing key. |
|
|
107
|
+
| `CHAT_PROMPT_DIR` | no, default `./prompts` | Directory containing `aggregation_edit_prompt.txt`. |
|
|
108
|
+
| `CHAT_JWT_TTL_SECONDS` | no, default `14400` | Session token lifetime (4h). |
|
|
109
|
+
| `PORT` | no, default `8000` | HTTP listen port. |
|
|
110
|
+
| `OTEL_ENABLED` | no, default `false` | When `true`, `instrumentation.ts` boots the OpenTelemetry SDK with SigV4 OTLP transport. The demo leaves this unset; production sets it to enable CloudWatch / X-Ray. |
|
|
111
|
+
| `BEDROCK_INFERENCE_PROFILE` | no | Application inference profile ARN for Bedrock cost attribution; only used when `LLM_MODEL` starts with `bedrock:`. |
|
|
112
|
+
| `OIDC_JWKS_URL`, `OIDC_ISSUER`, `OIDC_AUDIENCE` | no | When all three are set, `index.ts` applies the OIDC middleware on `POST /sessions`. |
|
|
113
|
+
|
|
114
|
+
Provider creds are read by each ai-sdk package via standard env vars
|
|
115
|
+
(`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`,
|
|
116
|
+
`GOOGLE_APPLICATION_CREDENTIALS` + `GOOGLE_VERTEX_PROJECT` +
|
|
117
|
+
`GOOGLE_VERTEX_LOCATION`, `AWS_REGION` + `AWS_ACCESS_KEY_ID` etc.). AWS S3
|
|
118
|
+
storage uses the AWS SDK's default credential chain
|
|
119
|
+
(`fromNodeProviderChain`).
|
|
120
|
+
|
|
121
|
+
## HTTP API
|
|
122
|
+
|
|
123
|
+
- `POST /sessions { variant_id, user_id, category, initial_artifact, initial_version }`
|
|
124
|
+
→ `{ session_id, token, expires_at }`
|
|
125
|
+
- `POST /chat/:sessionId { messages, triage_state? }` → SSE stream
|
|
126
|
+
- `GET /health` → `{ status: "ok" }`
|
|
127
|
+
|
|
128
|
+
## Production deployment
|
|
129
|
+
|
|
130
|
+
The default `index.ts` is sufficient when standard credential chains
|
|
131
|
+
(AWS env vars, IAM role, IRSA, etc.) cover your cred-mint flow. For
|
|
132
|
+
deployments that need custom cred minting (e.g. minting AWS STS credentials
|
|
133
|
+
from an OIDC IDP token in-process), write a small entry that uses the
|
|
134
|
+
programmatic API and injects pre-built SDK clients:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { serve } from "@hono/node-server";
|
|
138
|
+
import { Hono } from "hono";
|
|
139
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
140
|
+
import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
|
|
141
|
+
import { createApp } from "@flowajs/chat-service/server";
|
|
142
|
+
import { createS3Storage } from "@flowajs/chat-service/storage/s3";
|
|
143
|
+
import { createBedrockProvider } from "@flowajs/chat-service/llm/bedrock";
|
|
144
|
+
import { createOidcMiddleware } from "@flowajs/chat-service/auth/oidc";
|
|
145
|
+
import { yourCustomCredentialProvider } from "./creds.js";
|
|
146
|
+
|
|
147
|
+
// Build a credential provider that refreshes on expiry. The AWS SDK
|
|
148
|
+
// invokes it on each request that needs creds.
|
|
149
|
+
const credentials = yourCustomCredentialProvider();
|
|
150
|
+
|
|
151
|
+
// Storage: pre-built S3Client bound to the custom credential provider.
|
|
152
|
+
const s3 = new S3Client({ region: "us-east-1", credentials });
|
|
153
|
+
const storage = createS3Storage({
|
|
154
|
+
client: s3,
|
|
155
|
+
bucket: "my-bucket",
|
|
156
|
+
prefix: "flowa/",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// LLM: pre-built bedrock client bound to the same credential provider.
|
|
160
|
+
const bedrockClient = createAmazonBedrock({
|
|
161
|
+
region: "us-east-1",
|
|
162
|
+
credentialProvider: async () => {
|
|
163
|
+
const c = await credentials();
|
|
164
|
+
return {
|
|
165
|
+
accessKeyId: c.accessKeyId,
|
|
166
|
+
secretAccessKey: c.secretAccessKey,
|
|
167
|
+
sessionToken: c.sessionToken,
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
const provider = createBedrockProvider({
|
|
172
|
+
modelId: "us.anthropic.claude-sonnet-4-6",
|
|
173
|
+
client: bedrockClient,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const chatApp = createApp({
|
|
177
|
+
storage,
|
|
178
|
+
provider,
|
|
179
|
+
jwtSecret: process.env.CHAT_JWT_SECRET!,
|
|
180
|
+
promptDir: "./prompts",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Compose with custom auth middleware. The OIDC validator shipped with
|
|
184
|
+
// chat-service handles standard JWKS verification; substitute your own
|
|
185
|
+
// middleware if your IDP needs something else.
|
|
186
|
+
const app = new Hono();
|
|
187
|
+
app.use("/sessions", createOidcMiddleware({
|
|
188
|
+
jwksUrl: process.env.OIDC_JWKS_URL!,
|
|
189
|
+
issuer: process.env.OIDC_ISSUER!,
|
|
190
|
+
audience: process.env.OIDC_AUDIENCE!,
|
|
191
|
+
}));
|
|
192
|
+
app.route("/", chatApp);
|
|
193
|
+
|
|
194
|
+
serve({ fetch: app.fetch, port: 8000 });
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Build and run
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pnpm install
|
|
201
|
+
pnpm --filter @flowajs/chat-service typecheck
|
|
202
|
+
pnpm --filter @flowajs/chat-service test
|
|
203
|
+
pnpm --filter @flowajs/chat-service build
|
|
204
|
+
node packages/chat-service/dist/index.js
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
A reference `Dockerfile` is included as a starting point. Production
|
|
208
|
+
deployments that need a custom entry install `@flowajs/chat-service` from
|
|
209
|
+
npm and write their own thin entry that calls `createApp(...)` — see
|
|
210
|
+
[Production deployment](#production-deployment).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Citation-grounded fields shared by every chat-service artifact. Deployments
|
|
4
|
+
* extend the schema by calling `ArtifactSchema.extend({...})` with their
|
|
5
|
+
* additional fields:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { ArtifactSchema } from "@flowajs/chat-service";
|
|
9
|
+
*
|
|
10
|
+
* export const MyArtifactSchema = ArtifactSchema.extend({
|
|
11
|
+
* classification: z.string(),
|
|
12
|
+
* classification_rationale: z.string(),
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* `.extend(...)` preserves the shape's TypeScript type, so the resulting
|
|
17
|
+
* schema stays assignable to `z.ZodType<Artifact>` at the
|
|
18
|
+
* `createApp({ schema })` call site. Spreading `...artifactFields` into a
|
|
19
|
+
* fresh `z.object({...})` works at runtime but widens the inferred shape
|
|
20
|
+
* to `Record<string, any>`, which fails the static type. `artifactFields`
|
|
21
|
+
* is exported anyway for runtime inspection / reflection.
|
|
22
|
+
*
|
|
23
|
+
* Deployments must keep these fields with these names; chat-service reads
|
|
24
|
+
* them directly. Refining their types (e.g. constraining `category` to a
|
|
25
|
+
* Zod enum) is fine; renaming is not.
|
|
26
|
+
*/
|
|
27
|
+
export declare const artifactFields: {
|
|
28
|
+
readonly category: z.ZodString;
|
|
29
|
+
readonly description: z.ZodString;
|
|
30
|
+
readonly notes: z.ZodString;
|
|
31
|
+
readonly papers: z.ZodArray<z.ZodObject<{
|
|
32
|
+
paper_id: z.ZodString;
|
|
33
|
+
rank_rationale: z.ZodString;
|
|
34
|
+
}, z.core.$strip>>;
|
|
35
|
+
readonly claims: z.ZodArray<z.ZodObject<{
|
|
36
|
+
paper_id: z.ZodString;
|
|
37
|
+
text: z.ZodString;
|
|
38
|
+
citations: z.ZodArray<z.ZodObject<{
|
|
39
|
+
quote: z.ZodString;
|
|
40
|
+
bboxes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
41
|
+
page: z.ZodNumber;
|
|
42
|
+
top: z.ZodNumber;
|
|
43
|
+
left: z.ZodNumber;
|
|
44
|
+
bottom: z.ZodNumber;
|
|
45
|
+
right: z.ZodNumber;
|
|
46
|
+
}, z.core.$strip>>>;
|
|
47
|
+
}, z.core.$strip>>;
|
|
48
|
+
}, z.core.$strip>>;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The minimal Zod schema chat-service can operate on. Any plugin's schema is
|
|
52
|
+
* a superset: it spreads `artifactFields` and adds deployment fields.
|
|
53
|
+
*/
|
|
54
|
+
export declare const ArtifactSchema: z.ZodObject<{
|
|
55
|
+
category: z.ZodString;
|
|
56
|
+
description: z.ZodString;
|
|
57
|
+
notes: z.ZodString;
|
|
58
|
+
papers: z.ZodArray<z.ZodObject<{
|
|
59
|
+
paper_id: z.ZodString;
|
|
60
|
+
rank_rationale: z.ZodString;
|
|
61
|
+
}, z.core.$strip>>;
|
|
62
|
+
claims: z.ZodArray<z.ZodObject<{
|
|
63
|
+
paper_id: z.ZodString;
|
|
64
|
+
text: z.ZodString;
|
|
65
|
+
citations: z.ZodArray<z.ZodObject<{
|
|
66
|
+
quote: z.ZodString;
|
|
67
|
+
bboxes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
68
|
+
page: z.ZodNumber;
|
|
69
|
+
top: z.ZodNumber;
|
|
70
|
+
left: z.ZodNumber;
|
|
71
|
+
bottom: z.ZodNumber;
|
|
72
|
+
right: z.ZodNumber;
|
|
73
|
+
}, z.core.$strip>>>;
|
|
74
|
+
}, z.core.$strip>>;
|
|
75
|
+
}, z.core.$strip>>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
/**
|
|
78
|
+
* The citation-grounded artifact type. Deployments may name their own
|
|
79
|
+
* extended type; this is the base chat-service operates on.
|
|
80
|
+
*/
|
|
81
|
+
export type Artifact = z.infer<typeof ArtifactSchema>;
|
|
82
|
+
/**
|
|
83
|
+
* Render a Zod schema as a JSON Schema string for inclusion in the system
|
|
84
|
+
* prompt. The descriptions on each field serve as documentation.
|
|
85
|
+
*/
|
|
86
|
+
export declare function schemaForPrompt(schema: z.ZodType): string;
|
|
87
|
+
//# sourceMappingURL=artifact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoDxB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CAwBjB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;iBAA2B,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,MAAM,CAEzD"}
|
package/dist/artifact.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Citation-grounded artifact core. chat-service hardcodes a small fixed
|
|
2
|
+
// set of fields (`category`, `description`, `notes`, `papers`, `claims`)
|
|
3
|
+
// because those are what the chat-tool design — `view`, `search`,
|
|
4
|
+
// `str_replace`, `insert`, `write` over a citation-grounded synthesis —
|
|
5
|
+
// actually depends on. Deployments extend by calling
|
|
6
|
+
// `ArtifactSchema.extend({...})` with their additional fields; they do
|
|
7
|
+
// not rename the core fields.
|
|
8
|
+
//
|
|
9
|
+
// Field names mirror flowa's `prompts/generic/aggregation_schema.py`.
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
const CitationBbox = z.object({
|
|
12
|
+
page: z.number().describe("Page number in the source document."),
|
|
13
|
+
top: z.number().describe("Top edge of the highlight rectangle."),
|
|
14
|
+
left: z.number().describe("Left edge of the highlight rectangle."),
|
|
15
|
+
bottom: z.number().describe("Bottom edge of the highlight rectangle."),
|
|
16
|
+
right: z.number().describe("Right edge of the highlight rectangle."),
|
|
17
|
+
});
|
|
18
|
+
const ArtifactCitation = z.object({
|
|
19
|
+
quote: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe("Verbatim passage from the source document. Must be copied exactly — enough context to validate the claim."),
|
|
22
|
+
bboxes: z
|
|
23
|
+
.array(CitationBbox)
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Pre-computed highlight rectangles for the quote. Present on pipeline-generated citations; absent on citations added during editing."),
|
|
26
|
+
});
|
|
27
|
+
const ArtifactClaim = z.object({
|
|
28
|
+
paper_id: z
|
|
29
|
+
.string()
|
|
30
|
+
.describe("Identifier of the source document. Must appear in the papers[] list."),
|
|
31
|
+
text: z
|
|
32
|
+
.string()
|
|
33
|
+
.describe("The factual statement supported by the source document. One atomic fact per claim."),
|
|
34
|
+
citations: z
|
|
35
|
+
.array(ArtifactCitation)
|
|
36
|
+
.min(1)
|
|
37
|
+
.describe("One or more supporting verbatim quotes from the document identified by paper_id."),
|
|
38
|
+
});
|
|
39
|
+
const RankedPaper = z.object({
|
|
40
|
+
paper_id: z.string().describe("Identifier of the source document."),
|
|
41
|
+
rank_rationale: z
|
|
42
|
+
.string()
|
|
43
|
+
.describe("One sentence explaining why this document sits at this rank in the synthesis."),
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Citation-grounded fields shared by every chat-service artifact. Deployments
|
|
47
|
+
* extend the schema by calling `ArtifactSchema.extend({...})` with their
|
|
48
|
+
* additional fields:
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { ArtifactSchema } from "@flowajs/chat-service";
|
|
52
|
+
*
|
|
53
|
+
* export const MyArtifactSchema = ArtifactSchema.extend({
|
|
54
|
+
* classification: z.string(),
|
|
55
|
+
* classification_rationale: z.string(),
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* `.extend(...)` preserves the shape's TypeScript type, so the resulting
|
|
60
|
+
* schema stays assignable to `z.ZodType<Artifact>` at the
|
|
61
|
+
* `createApp({ schema })` call site. Spreading `...artifactFields` into a
|
|
62
|
+
* fresh `z.object({...})` works at runtime but widens the inferred shape
|
|
63
|
+
* to `Record<string, any>`, which fails the static type. `artifactFields`
|
|
64
|
+
* is exported anyway for runtime inspection / reflection.
|
|
65
|
+
*
|
|
66
|
+
* Deployments must keep these fields with these names; chat-service reads
|
|
67
|
+
* them directly. Refining their types (e.g. constraining `category` to a
|
|
68
|
+
* Zod enum) is fine; renaming is not.
|
|
69
|
+
*/
|
|
70
|
+
export const artifactFields = {
|
|
71
|
+
category: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe("Selector identifying which result this artifact represents within its aggregate. Mirrors aggregation.results[].category in flowa's prompts/generic/aggregation_schema.py."),
|
|
74
|
+
description: z
|
|
75
|
+
.string()
|
|
76
|
+
.describe("Short human-readable summary of the synthesis."),
|
|
77
|
+
notes: z
|
|
78
|
+
.string()
|
|
79
|
+
.describe('Long-form synthesis in Markdown. Uses inline citation links: [text](#cite:paper_id "verbatim quote").'),
|
|
80
|
+
papers: z
|
|
81
|
+
.array(RankedPaper)
|
|
82
|
+
.describe("Source documents contributing to the synthesis, ordered by contribution. List position is the rank. paper_id values must be unique."),
|
|
83
|
+
claims: z
|
|
84
|
+
.array(ArtifactClaim)
|
|
85
|
+
.describe("Factual claims supporting the synthesis. Grouped by paper_id in the same order as papers[]."),
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The minimal Zod schema chat-service can operate on. Any plugin's schema is
|
|
89
|
+
* a superset: it spreads `artifactFields` and adds deployment fields.
|
|
90
|
+
*/
|
|
91
|
+
export const ArtifactSchema = z.object(artifactFields);
|
|
92
|
+
/**
|
|
93
|
+
* Render a Zod schema as a JSON Schema string for inclusion in the system
|
|
94
|
+
* prompt. The descriptions on each field serve as documentation.
|
|
95
|
+
*/
|
|
96
|
+
export function schemaForPrompt(schema) {
|
|
97
|
+
return JSON.stringify(z.toJSONSchema(schema), null, 2);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=artifact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.js","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,yEAAyE;AACzE,kEAAkE;AAClE,wEAAwE;AACxE,qDAAqD;AACrD,uEAAuE;AACvE,8BAA8B;AAC9B,EAAE;AACF,sEAAsE;AAEtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAChE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;IAChE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAClE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;IACtE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;CACrE,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,CACP,2GAA2G,CAC5G;IACH,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,YAAY,CAAC;SACnB,QAAQ,EAAE;SACV,QAAQ,CACP,qIAAqI,CACtI;CACJ,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CACP,sEAAsE,CACvE;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CACP,oFAAoF,CACrF;IACH,SAAS,EAAE,CAAC;SACT,KAAK,CAAC,gBAAgB,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,kFAAkF,CACnF;CACJ,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACnE,cAAc,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,CACP,+EAA+E,CAChF;CACJ,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CACP,2KAA2K,CAC5K;IACH,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,CAAC,gDAAgD,CAAC;IAC7D,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,CACP,uGAAuG,CACxG;IACH,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,WAAW,CAAC;SAClB,QAAQ,CACP,qIAAqI,CACtI;IACH,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,aAAa,CAAC;SACpB,QAAQ,CACP,6FAA6F,CAC9F;CACK,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAQvD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Audit logging — per-session JSON files written to storage. */
|
|
2
|
+
import type { UIMessage } from "ai";
|
|
3
|
+
import type { Storage } from "./storage/interface.js";
|
|
4
|
+
export declare function logRequest(params: {
|
|
5
|
+
sessionId: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
variantId: string;
|
|
8
|
+
messages: UIMessage[];
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export declare function logStep(params: {
|
|
11
|
+
sessionId: string;
|
|
12
|
+
finishReason: string;
|
|
13
|
+
text: string;
|
|
14
|
+
toolCalls: unknown[];
|
|
15
|
+
toolResults: unknown[];
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
export declare function markTurnTruncated(sessionId: string): void;
|
|
18
|
+
export declare function logResponse(storage: Storage, params: {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
userId: string;
|
|
21
|
+
response: string;
|
|
22
|
+
toolCalls: unknown;
|
|
23
|
+
usage: unknown;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
export declare function logError(storage: Storage, sessionId: string, error: string): Promise<void>;
|
|
26
|
+
/** For tests: clear the per-session audit log map. */
|
|
27
|
+
export declare function clearAuditLogs(): void;
|
|
28
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAgDtD,wBAAsB,UAAU,CAAC,MAAM,EAAE;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,EAAE,CAAC;IACrB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE;IACN,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB,GACA,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,sDAAsD;AACtD,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Audit logging — per-session JSON files written to storage. */
|
|
2
|
+
import { auditLogKey } from "./storage-keys.js";
|
|
3
|
+
/** In-memory accumulator per session, flushed to storage after each turn completes. */
|
|
4
|
+
const logs = new Map();
|
|
5
|
+
function getOrCreateLog(sessionId, userId, variantId) {
|
|
6
|
+
let log = logs.get(sessionId);
|
|
7
|
+
if (!log) {
|
|
8
|
+
log = { sessionId, userId, variantId, turns: [] };
|
|
9
|
+
logs.set(sessionId, log);
|
|
10
|
+
}
|
|
11
|
+
return log;
|
|
12
|
+
}
|
|
13
|
+
export async function logRequest(params) {
|
|
14
|
+
const log = getOrCreateLog(params.sessionId, params.userId, params.variantId);
|
|
15
|
+
log.turns.push({
|
|
16
|
+
timestamp: new Date().toISOString(),
|
|
17
|
+
request: { messages: params.messages },
|
|
18
|
+
steps: [],
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function logStep(params) {
|
|
22
|
+
const log = logs.get(params.sessionId);
|
|
23
|
+
if (!log)
|
|
24
|
+
return;
|
|
25
|
+
const lastTurn = log.turns.at(-1);
|
|
26
|
+
if (lastTurn) {
|
|
27
|
+
lastTurn.steps ??= [];
|
|
28
|
+
lastTurn.steps.push({
|
|
29
|
+
finishReason: params.finishReason,
|
|
30
|
+
text: params.text,
|
|
31
|
+
toolCalls: params.toolCalls,
|
|
32
|
+
toolResults: params.toolResults,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function markTurnTruncated(sessionId) {
|
|
37
|
+
const log = logs.get(sessionId);
|
|
38
|
+
if (!log)
|
|
39
|
+
return;
|
|
40
|
+
const lastTurn = log.turns.at(-1);
|
|
41
|
+
if (lastTurn) {
|
|
42
|
+
lastTurn.truncated = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function logResponse(storage, params) {
|
|
46
|
+
const log = logs.get(params.sessionId);
|
|
47
|
+
if (!log)
|
|
48
|
+
return;
|
|
49
|
+
const lastTurn = log.turns.at(-1);
|
|
50
|
+
if (lastTurn) {
|
|
51
|
+
lastTurn.response = {
|
|
52
|
+
text: params.response,
|
|
53
|
+
toolCalls: params.toolCalls,
|
|
54
|
+
usage: params.usage,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
await storage.writeJson(auditLogKey(log.variantId, log.sessionId), log);
|
|
58
|
+
}
|
|
59
|
+
export async function logError(storage, sessionId, error) {
|
|
60
|
+
const log = logs.get(sessionId);
|
|
61
|
+
if (!log)
|
|
62
|
+
return;
|
|
63
|
+
const lastTurn = log.turns.at(-1);
|
|
64
|
+
if (lastTurn) {
|
|
65
|
+
lastTurn.error = error;
|
|
66
|
+
}
|
|
67
|
+
await storage.writeJson(auditLogKey(log.variantId, log.sessionId), log);
|
|
68
|
+
}
|
|
69
|
+
/** For tests: clear the per-session audit log map. */
|
|
70
|
+
export function clearAuditLogs() {
|
|
71
|
+
logs.clear();
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SessionClaims {
|
|
2
|
+
session_id: string;
|
|
3
|
+
variant_id: string;
|
|
4
|
+
user_id: string;
|
|
5
|
+
category: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SessionTokenConfig {
|
|
8
|
+
secret: string;
|
|
9
|
+
ttlSeconds: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function signSessionToken(claims: SessionClaims, config: SessionTokenConfig): Promise<{
|
|
12
|
+
token: string;
|
|
13
|
+
expiresAt: Date;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function verifySessionToken(token: string, config: SessionTokenConfig): Promise<SessionClaims & {
|
|
16
|
+
expiresAt: Date;
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAS7C;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAU9C"}
|
package/dist/auth/jwt.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify } from "jose";
|
|
2
|
+
export async function signSessionToken(claims, config) {
|
|
3
|
+
const expiresAt = new Date(Date.now() + config.ttlSeconds * 1000);
|
|
4
|
+
const secret = new TextEncoder().encode(config.secret);
|
|
5
|
+
const token = await new SignJWT(claims)
|
|
6
|
+
.setProtectedHeader({ alg: "HS256" })
|
|
7
|
+
.setExpirationTime(expiresAt)
|
|
8
|
+
.setIssuedAt()
|
|
9
|
+
.sign(secret);
|
|
10
|
+
return { token, expiresAt };
|
|
11
|
+
}
|
|
12
|
+
export async function verifySessionToken(token, config) {
|
|
13
|
+
const secret = new TextEncoder().encode(config.secret);
|
|
14
|
+
const { payload } = await jwtVerify(token, secret);
|
|
15
|
+
return {
|
|
16
|
+
session_id: payload.session_id,
|
|
17
|
+
variant_id: payload.variant_id,
|
|
18
|
+
user_id: payload.user_id,
|
|
19
|
+
category: payload.category,
|
|
20
|
+
expiresAt: new Date(payload.exp * 1000),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAc1C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAqB,EACrB,MAA0B;IAE1B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAC,MAA4C,CAAC;SAC1E,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;SACpC,iBAAiB,CAAC,SAAS,CAAC;SAC5B,WAAW,EAAE;SACb,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,MAA0B;IAE1B,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnD,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAoB;QACxC,UAAU,EAAE,OAAO,CAAC,UAAoB;QACxC,OAAO,EAAE,OAAO,CAAC,OAAiB;QAClC,QAAQ,EAAE,OAAO,CAAC,QAAkB;QACpC,SAAS,EAAE,IAAI,IAAI,CAAE,OAAO,CAAC,GAAc,GAAG,IAAI,CAAC;KACpD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type JWTPayload } from "jose";
|
|
2
|
+
import type { MiddlewareHandler } from "hono";
|
|
3
|
+
export interface OidcValidatorConfig {
|
|
4
|
+
jwksUrl: string;
|
|
5
|
+
issuer: string;
|
|
6
|
+
audience: string;
|
|
7
|
+
/**
|
|
8
|
+
* When true, decode the JWT body without verifying the signature. Useful
|
|
9
|
+
* for local development against a stub IDP. NEVER enable in production —
|
|
10
|
+
* the token is trusted as-is.
|
|
11
|
+
*/
|
|
12
|
+
devMode?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate a Bearer token from an Authorization header against an OIDC
|
|
16
|
+
* IDP. Returns the decoded claims on success; throws on missing header,
|
|
17
|
+
* malformed token, or signature/audience/issuer mismatch.
|
|
18
|
+
*
|
|
19
|
+
* Generic over IDP — works against any OIDC-compliant provider (Auth0,
|
|
20
|
+
* Keycloak, Okta, GitHub, etc.).
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateOidcToken(authHeader: string | undefined, config: OidcValidatorConfig): Promise<JWTPayload>;
|
|
23
|
+
/**
|
|
24
|
+
* Hono middleware factory that validates the Authorization header against
|
|
25
|
+
* the configured OIDC IDP. The decoded claims are stored on the context
|
|
26
|
+
* variable `oidcClaims` for downstream handlers to read via
|
|
27
|
+
* `c.get("oidcClaims")`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createOidcMiddleware(config: OidcValidatorConfig): MiddlewareHandler;
|
|
30
|
+
//# sourceMappingURL=oidc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../../src/auth/oidc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAaD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA0BrB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,mBAAmB,GAC1B,iBAAiB,CAanB"}
|