@elqnt/chat 3.1.0 → 3.5.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/README.md +6 -0
- package/SKILL.md +531 -0
- package/TIER2_AUTH.md +258 -0
- package/dist/api/index.d.mts +11 -0
- package/dist/api/index.d.ts +11 -0
- package/dist/api/index.js +27 -9
- package/dist/api/index.js.map +1 -1
- package/dist/api/index.mjs +27 -10
- package/dist/api/index.mjs.map +1 -1
- package/dist/hooks/index.d.mts +75 -37
- package/dist/hooks/index.d.ts +75 -37
- package/dist/hooks/index.js +116 -16
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +115 -15
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -7
- package/dist/index.d.ts +2 -7
- package/dist/index.js +14 -1420
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -1413
- package/dist/index.mjs.map +1 -1
- package/dist/models/index.d.mts +89 -4
- package/dist/models/index.d.ts +89 -4
- package/dist/models/index.js +12 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/index.mjs +8 -2
- package/dist/models/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +100 -11
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +100 -12
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-CQHtUQ6p.d.mts → types-CLtQA6Qq.d.mts} +16 -0
- package/dist/{types-7UNI1iYv.d.ts → types-CxibhkqW.d.ts} +16 -0
- package/package.json +8 -6
package/TIER2_AUTH.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Chat realtime tier-2 authorization — full reference
|
|
2
|
+
|
|
3
|
+
> Audience: agents and engineers touching the chat realtime path (`@elqnt/chat`
|
|
4
|
+
> realtime hooks, the `chat` Go service, or any app that mounts `useChat`).
|
|
5
|
+
> This is the deep reference; `SKILL.md` has the day-to-day usage. See also
|
|
6
|
+
> `packages/@elqnt/CONVENTIONS.md` → "Transport tiers" / "Security rule for
|
|
7
|
+
> tier 2".
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## TL;DR
|
|
12
|
+
|
|
13
|
+
The chat **realtime** surface (SSE `/stream`, REST `/create` `/send` `/load`
|
|
14
|
+
`/typing` `/end` `/event`, and the WebSocket) is **tier 2**: clients reach the
|
|
15
|
+
`chat` Go service **directly, bypassing the API gateway** (gateways buffer SSE
|
|
16
|
+
and tie up proxy workers on long-lived connections). Because the gateway — and
|
|
17
|
+
its JWT check — is not in the path, **the chat service authorizes every
|
|
18
|
+
connection itself** by validating a short-lived signed token and deriving
|
|
19
|
+
`orgId` / `userId` / `product` from the token's claims.
|
|
20
|
+
|
|
21
|
+
- **Client:** pass `getToken` to `useChat` (or `token`). The package attaches it
|
|
22
|
+
as `?token=` on `/stream` (EventSource can't set headers) and as
|
|
23
|
+
`Authorization: Bearer` on the POSTs.
|
|
24
|
+
- **Server:** validates HS256 with the shared `JWT_SECRET`, audience
|
|
25
|
+
`eloquent-api`, issuer `eloquent-gateway`, expiry. Identity comes **only** from
|
|
26
|
+
the token — body/query `orgId`/`userId` are no longer trusted.
|
|
27
|
+
- **Enforcement:** on by default (`CHAT_AUTH_ENFORCE=true`); a kill-switch allows
|
|
28
|
+
a brief migration window.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Why this exists (the bug it closes)
|
|
33
|
+
|
|
34
|
+
Before this change the realtime handlers authorized on `isOriginAllowed` **only**,
|
|
35
|
+
and that function **returned `true` when the `Origin` header was absent**
|
|
36
|
+
(`// Allow requests without Origin header`). `Origin` is a browser-only courtesy
|
|
37
|
+
header — absent on any server-to-server / CLI / script client and trivially
|
|
38
|
+
forged. So **any non-browser client could `create` / `send` / `stream` as any
|
|
39
|
+
`orgId`/`userId` it supplied in the request body or query**. That is an
|
|
40
|
+
authentication/authorization bypass on an internet-exposed service.
|
|
41
|
+
|
|
42
|
+
Tier 2's whole premise is that the *service* must close this, since the gateway
|
|
43
|
+
isn't there to. Origin is now kept only as **defense-in-depth (CORS)** — it never
|
|
44
|
+
authorizes, and a missing `Origin` no longer grants access.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Browser / non-browser client
|
|
52
|
+
│ signed token: ?token= on /stream, Authorization: Bearer on POSTs
|
|
53
|
+
▼ (DIRECT — no API gateway)
|
|
54
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
55
|
+
│ chat (Go service) │
|
|
56
|
+
│ ServeSSE / RESTHandler / ServeWS │
|
|
57
|
+
│ → TokenValidator.Authorize(r) │
|
|
58
|
+
│ • extract Bearer header OR ?token= │
|
|
59
|
+
│ • verify HS256 (JWT_SECRET), exp, aud, iss │
|
|
60
|
+
│ • reject alg=none / forged / expired / wrong aud|iss │
|
|
61
|
+
│ → derive orgId / userId / product FROM CLAIMS │
|
|
62
|
+
│ (body/query identity is ignored, except migration mode) │
|
|
63
|
+
│ → origin check kept as CORS defense-in-depth only │
|
|
64
|
+
└──────────────────────────────────────────────────────────────┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
History (`useChatHistory`) is **tier 1** — gateway HTTP with a bearer JWT — and
|
|
68
|
+
is unchanged.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## The token (claim shape)
|
|
73
|
+
|
|
74
|
+
One signing path serves both tiers: tokens minted by the API gateway, by
|
|
75
|
+
`@elqnt/api-client/server`'s `generateServerToken`, or by an app's
|
|
76
|
+
`/api/gateway-token` route all validate here.
|
|
77
|
+
|
|
78
|
+
```jsonc
|
|
79
|
+
{
|
|
80
|
+
"org_id": "<org-uuid>", // tenant — the security boundary
|
|
81
|
+
"user_id": "<user / visitor id>", // conversation identity
|
|
82
|
+
"email": "<email>",
|
|
83
|
+
"role": "user",
|
|
84
|
+
"scopes": ["chat:connect"], // (any scope set; chat does not gate on scope today)
|
|
85
|
+
"product": "eloquent", // routing
|
|
86
|
+
"iss": "eloquent-gateway", // always accepted; JWT_ISSUER also accepted
|
|
87
|
+
"aud": "eloquent-api", // must match JWT_AUDIENCE
|
|
88
|
+
"exp": <now + 1h> // short-lived
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Algorithm: **HS256**, signed with `JWT_SECRET` (shared across services via
|
|
93
|
+
`common.env`). `alg=none` and any non-HMAC method are rejected.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Server: `TokenValidator`
|
|
98
|
+
|
|
99
|
+
`backend/services/chat/services/token_validator.go`.
|
|
100
|
+
|
|
101
|
+
| Method | Purpose |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `NewTokenValidator(secret, audience, issuers, enforce, logger)` | Construct. `"eloquent-gateway"` is always an accepted issuer in addition to `issuers`. |
|
|
104
|
+
| `Validate(tokenString) (*ChatTokenClaims, error)` | Verify method/signature/exp/aud/iss; require non-empty `org_id`+`user_id`. |
|
|
105
|
+
| `ExtractToken(r) string` | `Authorization: Bearer …` **or** `?token=`. |
|
|
106
|
+
| `Authorize(r) (claims, ok)` | The one-call gate used by every handler. |
|
|
107
|
+
|
|
108
|
+
`Authorize` return contract:
|
|
109
|
+
|
|
110
|
+
| `claims` | `ok` | Meaning |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| non-nil | `true` | Authorized — use `claims.OrgID/UserID/Product`. |
|
|
113
|
+
| `nil` | `true` | No token, **but enforcement is OFF** (migration). Caller falls back to query identity and logs. |
|
|
114
|
+
| `nil` | `false` | **Reject (HTTP 401).** |
|
|
115
|
+
|
|
116
|
+
Fail-closed: when enforcing with no configured secret, `Authorize` returns
|
|
117
|
+
`ok=false` (never trusts an unverifiable token).
|
|
118
|
+
|
|
119
|
+
### Wiring
|
|
120
|
+
|
|
121
|
+
- `ServeSSE` (`sse-handler.go`): after the origin/method checks, `Authorize`;
|
|
122
|
+
401 on failure; then `orgID/userID = claims.OrgID/UserID`.
|
|
123
|
+
- `RESTHandler` (`rest-handler.go`): `authorizeREST(w, r)` runs CORS + OPTIONS +
|
|
124
|
+
POST + token in one place; each handler then calls
|
|
125
|
+
`applyTokenIdentity(claims, &req.OrgID, &req.UserID)` so body identity is
|
|
126
|
+
overwritten by the token (org-only for agent-join/leave/agent-context).
|
|
127
|
+
- `ServeWS` (`ws-handler.go`): `Authorize` before the upgrade; identity from
|
|
128
|
+
claims; legacy-org remap still applied to the derived org.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Configuration (env)
|
|
133
|
+
|
|
134
|
+
| Var | Default | Meaning |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `JWT_SECRET` | — (required to enforce) | HS256 signing secret (shared; `common.env`). |
|
|
137
|
+
| `JWT_ISSUER` | `eloquent` | Accepted issuer (in addition to the always-accepted `eloquent-gateway`). |
|
|
138
|
+
| `JWT_AUDIENCE` | `eloquent-api` | Required token audience. |
|
|
139
|
+
| `CHAT_AUTH_ENFORCE` | `true` | **Secure default.** `false` = migration window: validate-if-present, allow + log token-less connects. |
|
|
140
|
+
|
|
141
|
+
If `CHAT_AUTH_ENFORCE=true` and `JWT_SECRET` is empty, the service **refuses to
|
|
142
|
+
start** (rather than booting fail-open).
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Client: `@elqnt/chat`
|
|
147
|
+
|
|
148
|
+
`getToken` / `token` exist on `UseChatOptions`, `TransportConfig`, and the
|
|
149
|
+
low-level `stream-api` options. `getToken` is awaited **before every connect and
|
|
150
|
+
every REST send**, so a long-lived stream keeps working as short-lived tokens
|
|
151
|
+
roll over. A static `token` is also accepted but does not refresh.
|
|
152
|
+
|
|
153
|
+
### Authenticated app (logged-in user)
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { useChat } from "@elqnt/chat/hooks";
|
|
157
|
+
import { getGatewayToken } from "@elqnt/api-client/browser";
|
|
158
|
+
|
|
159
|
+
const chat = useChat({
|
|
160
|
+
baseUrl, // the chat service endpoint (CHAT_SERVER_BASE_URL → …/api/v1/chat)
|
|
161
|
+
orgId, userId,
|
|
162
|
+
getToken: getGatewayToken, // session token from /api/gateway-token, scoped to the user's org/user
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Anonymous (public widget, marketing demo)
|
|
167
|
+
|
|
168
|
+
The browser must **never** choose the org. Mint the token in a **server action
|
|
169
|
+
bound to a server-trusted org** — Next.js encrypts bound arguments, so a browser
|
|
170
|
+
caller cannot substitute a different org. The visitor id (non-privileged
|
|
171
|
+
conversation identity) may come from the client.
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
// actions/chat-token-actions.ts
|
|
175
|
+
"use server";
|
|
176
|
+
import * as jose from "jose";
|
|
177
|
+
|
|
178
|
+
export async function mintAnonChatToken(orgId: string, product: string, userId: string) {
|
|
179
|
+
const secret = new TextEncoder().encode(process.env.JWT_SECRET!);
|
|
180
|
+
return new jose.SignJWT({ org_id: orgId, user_id: userId, role: "user",
|
|
181
|
+
scopes: ["chat:connect"], product })
|
|
182
|
+
.setProtectedHeader({ alg: "HS256" })
|
|
183
|
+
.setIssuedAt().setIssuer("eloquent-gateway").setAudience("eloquent-api")
|
|
184
|
+
.setExpirationTime("1h").sign(secret);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
// server component — bind the TRUSTED org (resolved server-side from widgetId / DEMO_ORG_ID)
|
|
190
|
+
<WidgetRuntime getChatToken={mintAnonChatToken.bind(null, widget.orgId, "eloquent")} />
|
|
191
|
+
|
|
192
|
+
// client component
|
|
193
|
+
const chat = useChat({ baseUrl, orgId, userId: visitorId,
|
|
194
|
+
getToken: () => getChatToken(visitorId) }); // org is baked into the bound action
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
> **Security rule:** the bound `orgId` must come from a trusted server source
|
|
198
|
+
> (a widget resolved server-side, a `DEMO_ORG_ID` env, a verified session) —
|
|
199
|
+
> never from a client-supplied query param or POST body.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## How the package attaches the token
|
|
204
|
+
|
|
205
|
+
| Surface | Mechanism |
|
|
206
|
+
|---|---|
|
|
207
|
+
| SSE `/stream` (EventSource, `transport/sse.ts`) | `?token=<jwt>` appended to the URL (headers impossible on EventSource). |
|
|
208
|
+
| SSE `/stream` (fetch fallback, `transport/sse-fetch.ts`) | `?token=` **and** `Authorization: Bearer` (fetch can set headers). |
|
|
209
|
+
| POST `/create` `/send` `/load` `/typing` `/end` `/event` | `Authorization: Bearer <jwt>` header. |
|
|
210
|
+
|
|
211
|
+
`resolveTransportToken(cfg)` (in `transport/types.ts`) prefers `getToken` and
|
|
212
|
+
falls back to `token`; it never throws (a failing provider → tokenless → the
|
|
213
|
+
service rejects when enforcing).
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Rollout / migration
|
|
218
|
+
|
|
219
|
+
1. Deploy the service with `CHAT_AUTH_ENFORCE=false` (migration window): tokens
|
|
220
|
+
are validated when present, token-less connects are allowed + logged.
|
|
221
|
+
2. Ship every client with `getToken` (web app done: home, admin tester, widget,
|
|
222
|
+
demo). Update any native / WhatsApp / server-to-server callers to send a
|
|
223
|
+
token too.
|
|
224
|
+
3. Flip `CHAT_AUTH_ENFORCE=true` (the default) once clients are updated; remove
|
|
225
|
+
the override.
|
|
226
|
+
|
|
227
|
+
The kill-switch defaults secure — leaving it unset is the production posture.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Verification
|
|
232
|
+
|
|
233
|
+
- `go build ./...` + `go vet` clean in `backend/services/chat`.
|
|
234
|
+
- `services/token_validator_test.go` — 11 tests: forged signature, `alg=none`,
|
|
235
|
+
expired, wrong audience/issuer, token-less rejection (enforce), migration-mode
|
|
236
|
+
allow, header-vs-query, and **identity derived from the token, not a spoofed
|
|
237
|
+
query**.
|
|
238
|
+
- `@elqnt/chat` builds (ESM/CJS/DTS); `eloquent-app` `tsc --noEmit` clean.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## File map
|
|
243
|
+
|
|
244
|
+
**Backend (`backend/services/chat/`)**
|
|
245
|
+
- `services/token_validator.go` — validator + `Authorize` gate *(new)*
|
|
246
|
+
- `services/token_validator_test.go` — tests *(new)*
|
|
247
|
+
- `handlers/sse-handler.go`, `handlers/rest-handler.go`, `handlers/ws-handler.go` — wired
|
|
248
|
+
- `main.go` — env + validator construction + kill-switch
|
|
249
|
+
|
|
250
|
+
**Package (`packages/@elqnt/chat/`)**
|
|
251
|
+
- `transport/types.ts` — `getToken`/`token` on `TransportConfig` + `resolveTransportToken`
|
|
252
|
+
- `transport/sse.ts`, `transport/sse-fetch.ts` — attach token
|
|
253
|
+
- `api/stream-api.ts` — `token`/`getToken` on the low-level functions
|
|
254
|
+
- `hooks/use-chat.ts` — `getToken`/`token` options, refreshed via ref
|
|
255
|
+
|
|
256
|
+
**App (`apps/eloquent-app/`)**
|
|
257
|
+
- `actions/chat-token-actions.ts` — anonymous bound-org minter *(new)*
|
|
258
|
+
- home chat wrapper + context, admin agent tester, widget runtime + page, demo page + component
|
package/dist/api/index.d.mts
CHANGED
|
@@ -43,6 +43,17 @@ interface StreamApiOptions {
|
|
|
43
43
|
orgId: string;
|
|
44
44
|
/** User ID */
|
|
45
45
|
userId: string;
|
|
46
|
+
/**
|
|
47
|
+
* Tier-2 signed token (REQUIRED in production). The chat service authorizes
|
|
48
|
+
* the request from this token and derives orgId/userId/product from its
|
|
49
|
+
* claims (the body orgId/userId are not trusted for authorization). Pass a
|
|
50
|
+
* static `token`, or a `getToken` provider that is awaited per call so
|
|
51
|
+
* short-lived tokens refresh. See SKILL.md and @elqnt/api-client/server's
|
|
52
|
+
* generateServerToken for the claim shape.
|
|
53
|
+
*/
|
|
54
|
+
token?: string;
|
|
55
|
+
/** Token provider, awaited per call. Takes precedence over `token`. */
|
|
56
|
+
getToken?: () => Promise<string | null | undefined> | string | null | undefined;
|
|
46
57
|
}
|
|
47
58
|
/**
|
|
48
59
|
* Response from stream API calls
|
package/dist/api/index.d.ts
CHANGED
|
@@ -43,6 +43,17 @@ interface StreamApiOptions {
|
|
|
43
43
|
orgId: string;
|
|
44
44
|
/** User ID */
|
|
45
45
|
userId: string;
|
|
46
|
+
/**
|
|
47
|
+
* Tier-2 signed token (REQUIRED in production). The chat service authorizes
|
|
48
|
+
* the request from this token and derives orgId/userId/product from its
|
|
49
|
+
* claims (the body orgId/userId are not trusted for authorization). Pass a
|
|
50
|
+
* static `token`, or a `getToken` provider that is awaited per call so
|
|
51
|
+
* short-lived tokens refresh. See SKILL.md and @elqnt/api-client/server's
|
|
52
|
+
* generateServerToken for the claim shape.
|
|
53
|
+
*/
|
|
54
|
+
token?: string;
|
|
55
|
+
/** Token provider, awaited per call. Takes precedence over `token`. */
|
|
56
|
+
getToken?: () => Promise<string | null | undefined> | string | null | undefined;
|
|
46
57
|
}
|
|
47
58
|
/**
|
|
48
59
|
* Response from stream API calls
|
package/dist/api/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
"use strict";
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -54,10 +53,22 @@ module.exports = __toCommonJS(api_exports);
|
|
|
54
53
|
var import_browser2 = require("@elqnt/api-client/browser");
|
|
55
54
|
|
|
56
55
|
// api/stream-api.ts
|
|
57
|
-
async function
|
|
56
|
+
async function resolveToken(opts) {
|
|
57
|
+
if (opts.getToken) {
|
|
58
|
+
try {
|
|
59
|
+
return await opts.getToken() ?? void 0;
|
|
60
|
+
} catch {
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return opts.token ?? void 0;
|
|
65
|
+
}
|
|
66
|
+
async function streamFetch(url, body, token) {
|
|
67
|
+
const headers = { "Content-Type": "application/json" };
|
|
68
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
58
69
|
const response = await fetch(url, {
|
|
59
70
|
method: "POST",
|
|
60
|
-
headers
|
|
71
|
+
headers,
|
|
61
72
|
body: JSON.stringify(body)
|
|
62
73
|
});
|
|
63
74
|
if (!response.ok) {
|
|
@@ -72,9 +83,11 @@ async function streamFetch(url, body) {
|
|
|
72
83
|
}
|
|
73
84
|
async function createChat(options) {
|
|
74
85
|
const { baseUrl, orgId, userId, metadata } = options;
|
|
86
|
+
const token = await resolveToken(options);
|
|
75
87
|
const result = await streamFetch(
|
|
76
88
|
`${baseUrl}/create`,
|
|
77
|
-
{ orgId, userId, metadata }
|
|
89
|
+
{ orgId, userId, metadata },
|
|
90
|
+
token
|
|
78
91
|
);
|
|
79
92
|
if (!result.success || !result.data?.chatKey) {
|
|
80
93
|
throw new Error(result.error || "Failed to create chat");
|
|
@@ -83,6 +96,7 @@ async function createChat(options) {
|
|
|
83
96
|
}
|
|
84
97
|
async function sendChatMessage(options) {
|
|
85
98
|
const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } = options;
|
|
99
|
+
const token = await resolveToken(options);
|
|
86
100
|
const message = {
|
|
87
101
|
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
88
102
|
role: "user",
|
|
@@ -99,18 +113,19 @@ async function sendChatMessage(options) {
|
|
|
99
113
|
userId,
|
|
100
114
|
message,
|
|
101
115
|
metadata
|
|
102
|
-
});
|
|
116
|
+
}, token);
|
|
103
117
|
if (!result.success) {
|
|
104
118
|
throw new Error(result.error || "Failed to send message");
|
|
105
119
|
}
|
|
106
120
|
}
|
|
107
121
|
async function loadChat(options) {
|
|
108
122
|
const { baseUrl, orgId, chatKey, userId } = options;
|
|
123
|
+
const token = await resolveToken(options);
|
|
109
124
|
const result = await streamFetch(`${baseUrl}/load`, {
|
|
110
125
|
orgId,
|
|
111
126
|
chatKey,
|
|
112
127
|
userId
|
|
113
|
-
});
|
|
128
|
+
}, token);
|
|
114
129
|
if (!result.success || !result.data?.chat) {
|
|
115
130
|
throw new Error(result.error || "Failed to load chat");
|
|
116
131
|
}
|
|
@@ -118,37 +133,40 @@ async function loadChat(options) {
|
|
|
118
133
|
}
|
|
119
134
|
async function endChat(options) {
|
|
120
135
|
const { baseUrl, orgId, chatKey, userId, reason } = options;
|
|
136
|
+
const token = await resolveToken(options);
|
|
121
137
|
const result = await streamFetch(`${baseUrl}/end`, {
|
|
122
138
|
orgId,
|
|
123
139
|
chatKey,
|
|
124
140
|
userId,
|
|
125
141
|
data: reason ? { reason } : void 0
|
|
126
|
-
});
|
|
142
|
+
}, token);
|
|
127
143
|
if (!result.success) {
|
|
128
144
|
throw new Error(result.error || "Failed to end chat");
|
|
129
145
|
}
|
|
130
146
|
}
|
|
131
147
|
async function sendTypingIndicator(options) {
|
|
132
148
|
const { baseUrl, orgId, chatKey, userId, typing } = options;
|
|
149
|
+
const token = await resolveToken(options);
|
|
133
150
|
const result = await streamFetch(`${baseUrl}/typing`, {
|
|
134
151
|
orgId,
|
|
135
152
|
chatKey,
|
|
136
153
|
userId,
|
|
137
154
|
typing
|
|
138
|
-
});
|
|
155
|
+
}, token);
|
|
139
156
|
if (!result.success) {
|
|
140
157
|
throw new Error(result.error || "Failed to send typing indicator");
|
|
141
158
|
}
|
|
142
159
|
}
|
|
143
160
|
async function sendEvent(options) {
|
|
144
161
|
const { baseUrl, orgId, chatKey, userId, type, data } = options;
|
|
162
|
+
const token = await resolveToken(options);
|
|
145
163
|
const result = await streamFetch(`${baseUrl}/event`, {
|
|
146
164
|
type,
|
|
147
165
|
orgId,
|
|
148
166
|
chatKey,
|
|
149
167
|
userId,
|
|
150
168
|
data
|
|
151
|
-
});
|
|
169
|
+
}, token);
|
|
152
170
|
if (!result.success) {
|
|
153
171
|
throw new Error(result.error || "Failed to send event");
|
|
154
172
|
}
|
package/dist/api/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../api/index.ts","../../api/stream-api.ts","../../api/memory.ts"],"sourcesContent":["/**\n * Chat API\n *\n * API functions for chat operations.\n *\n * ## Browser API (uses @elqnt/api-client)\n * ```typescript\n * import { getChatHistoryApi, getChatApi } from \"@elqnt/chat/api\";\n *\n * const history = await getChatHistoryApi({ baseUrl, orgId, limit: 10 });\n * const chat = await getChatApi(chatKey, { baseUrl, orgId });\n * ```\n *\n * ## Stream API (direct HTTP calls)\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * const chatKey = await createChat({ baseUrl, orgId, userId });\n * await sendChatMessage({ baseUrl, orgId, chatKey, userId, content: \"Hello!\" });\n * ```\n */\n\nimport { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiResponse, ApiClientOptions } from \"@elqnt/api-client\";\nimport type { ResponseMetadata } from \"@elqnt/types\";\nimport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export types from models\nexport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export stream API functions\nexport {\n createChat,\n sendChatMessage,\n loadChat,\n endChat,\n sendTypingIndicator,\n sendEvent,\n} from \"./stream-api\";\n\nexport type {\n StreamApiOptions,\n StreamApiResponse,\n CreateChatApiOptions,\n CreateChatResponseData,\n SendMessageApiOptions,\n LoadChatApiOptions,\n LoadChatResponseData,\n EndChatApiOptions,\n TypingApiOptions,\n SendEventApiOptions,\n} from \"./stream-api\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ChatHistoryResponse {\n chats: ChatSummary[];\n total: number;\n hasMore: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ChatResponse {\n chat: ChatSummary;\n metadata: ResponseMetadata;\n}\n\nexport interface UpdateChatResponse {\n chatKey: string;\n title: string;\n pinned: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsResponse {\n chats: ChatSummary[];\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface WaitingChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface QueueResponse {\n queues: Queue[];\n metadata: ResponseMetadata;\n}\n\nexport interface Queue {\n id: string;\n name: string;\n agentId: string;\n waitingCount: number;\n activeCount: number;\n}\n\nexport interface AgentSession {\n agentId: string;\n userId: string;\n userEmail: string;\n status: string;\n connectedAt: string;\n lastActivityAt: string;\n}\n\nexport interface OnlineSessionsResponse {\n sessions: AgentSession[];\n metadata: ResponseMetadata;\n}\n\nexport interface AgentSessionResponse {\n session: AgentSession;\n metadata: ResponseMetadata;\n}\n\n// =============================================================================\n// CHAT HISTORY\n// =============================================================================\n\nexport async function getChatHistoryApi(\n options: ApiClientOptions & { limit?: number; offset?: number; skipCache?: boolean }\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(\"/api/v1/chats\", {\n method: \"POST\",\n body: {\n limit: options.limit || 15,\n offset: options.offset || 0,\n ...(options.skipCache ? { skipCache: true } : {}),\n },\n ...options,\n });\n}\n\nexport async function getChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function updateChatApi(\n chatKey: string,\n updates: { title?: string; pinned?: boolean },\n options: ApiClientOptions\n): Promise<ApiResponse<UpdateChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"PATCH\",\n body: updates,\n ...options,\n });\n}\n\nexport async function deleteChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"DELETE\",\n ...options,\n });\n}\n\n// =============================================================================\n// ACTIVE CHATS\n// =============================================================================\n\nexport async function getActiveChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<ActiveChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/active/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getActiveChatsApi(\n options: ApiClientOptions & { pastHours?: number }\n): Promise<ApiResponse<ActiveChatsResponse>> {\n const params = new URLSearchParams();\n if (options.pastHours) params.set(\"pastHours\", String(options.pastHours));\n const queryString = params.toString();\n return browserApiRequest(`/api/v1/chats/active${queryString ? `?${queryString}` : \"\"}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// PROJECT CHATS\n// =============================================================================\n\nexport async function updateProjectChatTitleApi(\n projectId: string,\n chatKey: string,\n title: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats/${chatKey}/title`, {\n method: \"PUT\",\n body: { title },\n ...options,\n });\n}\n\nexport async function addChatToProjectApi(\n projectId: string,\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats`, {\n method: \"POST\",\n body: { chatKey },\n ...options,\n });\n}\n\n// =============================================================================\n// WAITING CHATS\n// =============================================================================\n\nexport async function getWaitingChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<WaitingChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/waiting/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getChatsByUserApi(\n userEmail: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(`/api/v1/chats/user/${encodeURIComponent(userEmail)}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// QUEUES\n// =============================================================================\n\nexport async function listQueuesApi(\n options: ApiClientOptions\n): Promise<ApiResponse<QueueResponse>> {\n return browserApiRequest(\"/api/v1/queues\", {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// AGENT SESSIONS\n// =============================================================================\n\nexport async function getOnlineSessionsApi(\n options: ApiClientOptions\n): Promise<ApiResponse<OnlineSessionsResponse>> {\n return browserApiRequest(\"/api/v1/agents/sessions/online\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getAgentSessionApi(\n agentId: string,\n options: ApiClientOptions\n): Promise<ApiResponse<AgentSessionResponse>> {\n return browserApiRequest(`/api/v1/agents/sessions/${agentId}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// MEMORY\n// =============================================================================\n\nexport {\n getProfileApi,\n patchProfileApi,\n replaceProfileApi,\n clearProfileApi,\n deleteContactApi,\n deleteNoteApi,\n getSummaryApi,\n clearSummaryApi,\n regenerateSummaryApi,\n} from \"./memory\";\n\nexport type { SummaryView } from \"./memory\";\n","/**\n * Stream API\n *\n * Low-level functions for chat streaming operations.\n * These are used internally by the transport layer but can also\n * be used directly for custom implementations.\n *\n * @example\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * // Create a new chat\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * });\n *\n * // Send a message\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey,\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\n\nimport type { Chat, ChatMessage } from \"../models\";\n\n/**\n * Base options for all stream API calls\n */\nexport interface StreamApiOptions {\n /** Base URL for the chat server */\n baseUrl: string;\n /** Organization ID */\n orgId: string;\n /** User ID */\n userId: string;\n}\n\n/**\n * Response from stream API calls\n */\nexport interface StreamApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * Create chat options\n */\nexport interface CreateChatApiOptions extends StreamApiOptions {\n /** Optional metadata for the new chat */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create chat response data\n */\nexport interface CreateChatResponseData {\n chatKey: string;\n}\n\n/**\n * Send message options\n */\nexport interface SendMessageApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Message content */\n content: string;\n /** Optional attachments */\n attachments?: unknown[];\n /** Optional message metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Load chat options\n */\nexport interface LoadChatApiOptions extends StreamApiOptions {\n /** Chat key to load */\n chatKey: string;\n}\n\n/**\n * Load chat response data\n */\nexport interface LoadChatResponseData {\n chat: Chat;\n}\n\n/**\n * End chat options\n */\nexport interface EndChatApiOptions extends StreamApiOptions {\n /** Chat key to end */\n chatKey: string;\n /** Optional end reason */\n reason?: string;\n}\n\n/**\n * Typing indicator options\n */\nexport interface TypingApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Whether user is typing */\n typing: boolean;\n}\n\n/**\n * Generic event options\n */\nexport interface SendEventApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Event type */\n type: string;\n /** Event data */\n data?: Record<string, unknown>;\n}\n\n/**\n * Internal fetch helper\n */\nasync function streamFetch<T>(\n url: string,\n body: Record<string, unknown>\n): Promise<StreamApiResponse<T>> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return {\n success: false,\n error: `API error: ${response.status} - ${errorText}`,\n };\n }\n\n const data = await response.json();\n return { success: true, data };\n}\n\n/**\n * Create a new chat session\n *\n * @param options - Create chat options\n * @returns Chat key of the created chat\n *\n * @example\n * ```typescript\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * metadata: { source: \"website\" },\n * });\n * ```\n */\nexport async function createChat(\n options: CreateChatApiOptions\n): Promise<string> {\n const { baseUrl, orgId, userId, metadata } = options;\n\n const result = await streamFetch<CreateChatResponseData>(\n `${baseUrl}/create`,\n { orgId, userId, metadata }\n );\n\n if (!result.success || !result.data?.chatKey) {\n throw new Error(result.error || \"Failed to create chat\");\n }\n\n return result.data.chatKey;\n}\n\n/**\n * Send a message in a chat\n *\n * @param options - Send message options\n *\n * @example\n * ```typescript\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\nexport async function sendChatMessage(\n options: SendMessageApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } =\n options;\n\n const message: Partial<ChatMessage> = {\n id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n role: \"user\",\n content,\n time: Date.now(),\n status: \"sending\",\n senderId: userId,\n createdAt: Date.now(),\n attachments: attachments as ChatMessage[\"attachments\"],\n };\n\n const result = await streamFetch(`${baseUrl}/send`, {\n orgId,\n chatKey,\n userId,\n message,\n metadata,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send message\");\n }\n}\n\n/**\n * Load an existing chat\n *\n * @param options - Load chat options\n * @returns The loaded chat object\n *\n * @example\n * ```typescript\n * const chat = await loadChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * });\n * ```\n */\nexport async function loadChat(options: LoadChatApiOptions): Promise<Chat> {\n const { baseUrl, orgId, chatKey, userId } = options;\n\n const result = await streamFetch<LoadChatResponseData>(`${baseUrl}/load`, {\n orgId,\n chatKey,\n userId,\n });\n\n if (!result.success || !result.data?.chat) {\n throw new Error(result.error || \"Failed to load chat\");\n }\n\n return result.data.chat;\n}\n\n/**\n * End a chat session\n *\n * @param options - End chat options\n *\n * @example\n * ```typescript\n * await endChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * reason: \"User closed chat\",\n * });\n * ```\n */\nexport async function endChat(options: EndChatApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, reason } = options;\n\n const result = await streamFetch(`${baseUrl}/end`, {\n orgId,\n chatKey,\n userId,\n data: reason ? { reason } : undefined,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to end chat\");\n }\n}\n\n/**\n * Send typing indicator\n *\n * @param options - Typing indicator options\n *\n * @example\n * ```typescript\n * // User started typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: true,\n * });\n *\n * // User stopped typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: false,\n * });\n * ```\n */\nexport async function sendTypingIndicator(\n options: TypingApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, typing } = options;\n\n const result = await streamFetch(`${baseUrl}/typing`, {\n orgId,\n chatKey,\n userId,\n typing,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send typing indicator\");\n }\n}\n\n/**\n * Send a generic event\n *\n * @param options - Event options\n *\n * @example\n * ```typescript\n * await sendEvent({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * type: \"skill_activate\",\n * data: { skillId: \"research\" },\n * });\n * ```\n */\nexport async function sendEvent(options: SendEventApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, type, data } = options;\n\n const result = await streamFetch(`${baseUrl}/event`, {\n type,\n orgId,\n chatKey,\n userId,\n data,\n });\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send event\");\n }\n}\n","import { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiClientOptions, ApiResponse } from \"@elqnt/api-client\";\nimport type { MemoryProfile } from \"../models\";\n\nexport interface SummaryView {\n chatKey: string;\n text: string;\n updatedAt: string;\n}\n\nexport const getProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"GET\", ...o });\n\nexport const patchProfileApi = (patch: Partial<MemoryProfile>, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PATCH\", body: patch, ...o });\n\nexport const replaceProfileApi = (p: MemoryProfile, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PUT\", body: p, ...o });\n\nexport const clearProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"DELETE\", ...o });\n\nexport const deleteContactApi = (name: string, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/contacts/${encodeURIComponent(name)}`, { method: \"DELETE\", ...o });\n\nexport const deleteNoteApi = (index: number, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/notes/${index}`, { method: \"DELETE\", ...o });\n\nexport const getSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"GET\", ...o });\n\nexport const clearSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<void>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"DELETE\", ...o });\n\nexport const regenerateSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary/regenerate`, { method: \"POST\", ...o });\n"],"mappings":"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","names":["import_browser"]}
|
|
1
|
+
{"version":3,"sources":["../../api/index.ts","../../api/stream-api.ts","../../api/memory.ts"],"sourcesContent":["/**\n * Chat API\n *\n * API functions for chat operations.\n *\n * ## Browser API (uses @elqnt/api-client)\n * ```typescript\n * import { getChatHistoryApi, getChatApi } from \"@elqnt/chat/api\";\n *\n * const history = await getChatHistoryApi({ baseUrl, orgId, limit: 10 });\n * const chat = await getChatApi(chatKey, { baseUrl, orgId });\n * ```\n *\n * ## Stream API (direct HTTP calls)\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * const chatKey = await createChat({ baseUrl, orgId, userId });\n * await sendChatMessage({ baseUrl, orgId, chatKey, userId, content: \"Hello!\" });\n * ```\n */\n\nimport { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiResponse, ApiClientOptions } from \"@elqnt/api-client\";\nimport type { ResponseMetadata } from \"@elqnt/types\";\nimport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export types from models\nexport type { ChatSummary, Chat } from \"../models\";\n\n// Re-export stream API functions\nexport {\n createChat,\n sendChatMessage,\n loadChat,\n endChat,\n sendTypingIndicator,\n sendEvent,\n} from \"./stream-api\";\n\nexport type {\n StreamApiOptions,\n StreamApiResponse,\n CreateChatApiOptions,\n CreateChatResponseData,\n SendMessageApiOptions,\n LoadChatApiOptions,\n LoadChatResponseData,\n EndChatApiOptions,\n TypingApiOptions,\n SendEventApiOptions,\n} from \"./stream-api\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ChatHistoryResponse {\n chats: ChatSummary[];\n total: number;\n hasMore: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ChatResponse {\n chat: ChatSummary;\n metadata: ResponseMetadata;\n}\n\nexport interface UpdateChatResponse {\n chatKey: string;\n title: string;\n pinned: boolean;\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsResponse {\n chats: ChatSummary[];\n metadata: ResponseMetadata;\n}\n\nexport interface ActiveChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface WaitingChatsCountResponse {\n count: number;\n metadata: ResponseMetadata;\n}\n\nexport interface QueueResponse {\n queues: Queue[];\n metadata: ResponseMetadata;\n}\n\nexport interface Queue {\n id: string;\n name: string;\n agentId: string;\n waitingCount: number;\n activeCount: number;\n}\n\nexport interface AgentSession {\n agentId: string;\n userId: string;\n userEmail: string;\n status: string;\n connectedAt: string;\n lastActivityAt: string;\n}\n\nexport interface OnlineSessionsResponse {\n sessions: AgentSession[];\n metadata: ResponseMetadata;\n}\n\nexport interface AgentSessionResponse {\n session: AgentSession;\n metadata: ResponseMetadata;\n}\n\n// =============================================================================\n// CHAT HISTORY\n// =============================================================================\n\nexport async function getChatHistoryApi(\n options: ApiClientOptions & { limit?: number; offset?: number; skipCache?: boolean }\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(\"/api/v1/chats\", {\n method: \"POST\",\n body: {\n limit: options.limit || 15,\n offset: options.offset || 0,\n ...(options.skipCache ? { skipCache: true } : {}),\n },\n ...options,\n });\n}\n\nexport async function getChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function updateChatApi(\n chatKey: string,\n updates: { title?: string; pinned?: boolean },\n options: ApiClientOptions\n): Promise<ApiResponse<UpdateChatResponse>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"PATCH\",\n body: updates,\n ...options,\n });\n}\n\nexport async function deleteChatApi(\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/chats/${chatKey}`, {\n method: \"DELETE\",\n ...options,\n });\n}\n\n// =============================================================================\n// ACTIVE CHATS\n// =============================================================================\n\nexport async function getActiveChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<ActiveChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/active/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getActiveChatsApi(\n options: ApiClientOptions & { pastHours?: number }\n): Promise<ApiResponse<ActiveChatsResponse>> {\n const params = new URLSearchParams();\n if (options.pastHours) params.set(\"pastHours\", String(options.pastHours));\n const queryString = params.toString();\n return browserApiRequest(`/api/v1/chats/active${queryString ? `?${queryString}` : \"\"}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// PROJECT CHATS\n// =============================================================================\n\nexport async function updateProjectChatTitleApi(\n projectId: string,\n chatKey: string,\n title: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats/${chatKey}/title`, {\n method: \"PUT\",\n body: { title },\n ...options,\n });\n}\n\nexport async function addChatToProjectApi(\n projectId: string,\n chatKey: string,\n options: ApiClientOptions\n): Promise<ApiResponse<{ success: boolean; metadata: ResponseMetadata }>> {\n return browserApiRequest(`/api/v1/projects/${projectId}/chats`, {\n method: \"POST\",\n body: { chatKey },\n ...options,\n });\n}\n\n// =============================================================================\n// WAITING CHATS\n// =============================================================================\n\nexport async function getWaitingChatsCountApi(\n options: ApiClientOptions\n): Promise<ApiResponse<WaitingChatsCountResponse>> {\n return browserApiRequest(\"/api/v1/chats/waiting/count\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getChatsByUserApi(\n userEmail: string,\n options: ApiClientOptions\n): Promise<ApiResponse<ChatHistoryResponse>> {\n return browserApiRequest(`/api/v1/chats/user/${encodeURIComponent(userEmail)}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// QUEUES\n// =============================================================================\n\nexport async function listQueuesApi(\n options: ApiClientOptions\n): Promise<ApiResponse<QueueResponse>> {\n return browserApiRequest(\"/api/v1/queues\", {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// AGENT SESSIONS\n// =============================================================================\n\nexport async function getOnlineSessionsApi(\n options: ApiClientOptions\n): Promise<ApiResponse<OnlineSessionsResponse>> {\n return browserApiRequest(\"/api/v1/agents/sessions/online\", {\n method: \"GET\",\n ...options,\n });\n}\n\nexport async function getAgentSessionApi(\n agentId: string,\n options: ApiClientOptions\n): Promise<ApiResponse<AgentSessionResponse>> {\n return browserApiRequest(`/api/v1/agents/sessions/${agentId}`, {\n method: \"GET\",\n ...options,\n });\n}\n\n// =============================================================================\n// MEMORY\n// =============================================================================\n\nexport {\n getProfileApi,\n patchProfileApi,\n replaceProfileApi,\n clearProfileApi,\n deleteContactApi,\n deleteNoteApi,\n getSummaryApi,\n clearSummaryApi,\n regenerateSummaryApi,\n} from \"./memory\";\n\nexport type { SummaryView } from \"./memory\";\n","/**\n * Stream API\n *\n * Low-level functions for chat streaming operations.\n * These are used internally by the transport layer but can also\n * be used directly for custom implementations.\n *\n * @example\n * ```typescript\n * import { createChat, sendChatMessage, loadChat } from \"@elqnt/chat/api\";\n *\n * // Create a new chat\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * });\n *\n * // Send a message\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey,\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\n\nimport type { Chat, ChatMessage } from \"../models\";\n\n/**\n * Base options for all stream API calls\n */\nexport interface StreamApiOptions {\n /** Base URL for the chat server */\n baseUrl: string;\n /** Organization ID */\n orgId: string;\n /** User ID */\n userId: string;\n /**\n * Tier-2 signed token (REQUIRED in production). The chat service authorizes\n * the request from this token and derives orgId/userId/product from its\n * claims (the body orgId/userId are not trusted for authorization). Pass a\n * static `token`, or a `getToken` provider that is awaited per call so\n * short-lived tokens refresh. See SKILL.md and @elqnt/api-client/server's\n * generateServerToken for the claim shape.\n */\n token?: string;\n /** Token provider, awaited per call. Takes precedence over `token`. */\n getToken?: () => Promise<string | null | undefined> | string | null | undefined;\n}\n\n/** Resolve a per-call token from a static value or provider (never throws). */\nasync function resolveToken(opts: {\n token?: string;\n getToken?: StreamApiOptions[\"getToken\"];\n}): Promise<string | undefined> {\n if (opts.getToken) {\n try {\n return (await opts.getToken()) ?? undefined;\n } catch {\n return undefined;\n }\n }\n return opts.token ?? undefined;\n}\n\n/**\n * Response from stream API calls\n */\nexport interface StreamApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * Create chat options\n */\nexport interface CreateChatApiOptions extends StreamApiOptions {\n /** Optional metadata for the new chat */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create chat response data\n */\nexport interface CreateChatResponseData {\n chatKey: string;\n}\n\n/**\n * Send message options\n */\nexport interface SendMessageApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Message content */\n content: string;\n /** Optional attachments */\n attachments?: unknown[];\n /** Optional message metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Load chat options\n */\nexport interface LoadChatApiOptions extends StreamApiOptions {\n /** Chat key to load */\n chatKey: string;\n}\n\n/**\n * Load chat response data\n */\nexport interface LoadChatResponseData {\n chat: Chat;\n}\n\n/**\n * End chat options\n */\nexport interface EndChatApiOptions extends StreamApiOptions {\n /** Chat key to end */\n chatKey: string;\n /** Optional end reason */\n reason?: string;\n}\n\n/**\n * Typing indicator options\n */\nexport interface TypingApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Whether user is typing */\n typing: boolean;\n}\n\n/**\n * Generic event options\n */\nexport interface SendEventApiOptions extends StreamApiOptions {\n /** Chat key */\n chatKey: string;\n /** Event type */\n type: string;\n /** Event data */\n data?: Record<string, unknown>;\n}\n\n/**\n * Internal fetch helper\n */\nasync function streamFetch<T>(\n url: string,\n body: Record<string, unknown>,\n token?: string\n): Promise<StreamApiResponse<T>> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return {\n success: false,\n error: `API error: ${response.status} - ${errorText}`,\n };\n }\n\n const data = await response.json();\n return { success: true, data };\n}\n\n/**\n * Create a new chat session\n *\n * @param options - Create chat options\n * @returns Chat key of the created chat\n *\n * @example\n * ```typescript\n * const chatKey = await createChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * userId: \"user-456\",\n * metadata: { source: \"website\" },\n * });\n * ```\n */\nexport async function createChat(\n options: CreateChatApiOptions\n): Promise<string> {\n const { baseUrl, orgId, userId, metadata } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch<CreateChatResponseData>(\n `${baseUrl}/create`,\n { orgId, userId, metadata },\n token\n );\n\n if (!result.success || !result.data?.chatKey) {\n throw new Error(result.error || \"Failed to create chat\");\n }\n\n return result.data.chatKey;\n}\n\n/**\n * Send a message in a chat\n *\n * @param options - Send message options\n *\n * @example\n * ```typescript\n * await sendChatMessage({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * content: \"Hello!\",\n * });\n * ```\n */\nexport async function sendChatMessage(\n options: SendMessageApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } =\n options;\n const token = await resolveToken(options);\n\n const message: Partial<ChatMessage> = {\n id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n role: \"user\",\n content,\n time: Date.now(),\n status: \"sending\",\n senderId: userId,\n createdAt: Date.now(),\n attachments: attachments as ChatMessage[\"attachments\"],\n };\n\n const result = await streamFetch(`${baseUrl}/send`, {\n orgId,\n chatKey,\n userId,\n message,\n metadata,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send message\");\n }\n}\n\n/**\n * Load an existing chat\n *\n * @param options - Load chat options\n * @returns The loaded chat object\n *\n * @example\n * ```typescript\n * const chat = await loadChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * });\n * ```\n */\nexport async function loadChat(options: LoadChatApiOptions): Promise<Chat> {\n const { baseUrl, orgId, chatKey, userId } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch<LoadChatResponseData>(`${baseUrl}/load`, {\n orgId,\n chatKey,\n userId,\n }, token);\n\n if (!result.success || !result.data?.chat) {\n throw new Error(result.error || \"Failed to load chat\");\n }\n\n return result.data.chat;\n}\n\n/**\n * End a chat session\n *\n * @param options - End chat options\n *\n * @example\n * ```typescript\n * await endChat({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * reason: \"User closed chat\",\n * });\n * ```\n */\nexport async function endChat(options: EndChatApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, reason } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch(`${baseUrl}/end`, {\n orgId,\n chatKey,\n userId,\n data: reason ? { reason } : undefined,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to end chat\");\n }\n}\n\n/**\n * Send typing indicator\n *\n * @param options - Typing indicator options\n *\n * @example\n * ```typescript\n * // User started typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: true,\n * });\n *\n * // User stopped typing\n * await sendTypingIndicator({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * typing: false,\n * });\n * ```\n */\nexport async function sendTypingIndicator(\n options: TypingApiOptions\n): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, typing } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch(`${baseUrl}/typing`, {\n orgId,\n chatKey,\n userId,\n typing,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send typing indicator\");\n }\n}\n\n/**\n * Send a generic event\n *\n * @param options - Event options\n *\n * @example\n * ```typescript\n * await sendEvent({\n * baseUrl: \"https://api.example.com/chat\",\n * orgId: \"org-123\",\n * chatKey: \"chat-789\",\n * userId: \"user-456\",\n * type: \"skill_activate\",\n * data: { skillId: \"research\" },\n * });\n * ```\n */\nexport async function sendEvent(options: SendEventApiOptions): Promise<void> {\n const { baseUrl, orgId, chatKey, userId, type, data } = options;\n const token = await resolveToken(options);\n\n const result = await streamFetch(`${baseUrl}/event`, {\n type,\n orgId,\n chatKey,\n userId,\n data,\n }, token);\n\n if (!result.success) {\n throw new Error(result.error || \"Failed to send event\");\n }\n}\n","import { browserApiRequest } from \"@elqnt/api-client/browser\";\nimport type { ApiClientOptions, ApiResponse } from \"@elqnt/api-client\";\nimport type { MemoryProfile } from \"../models\";\n\nexport interface SummaryView {\n chatKey: string;\n text: string;\n updatedAt: string;\n}\n\nexport const getProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"GET\", ...o });\n\nexport const patchProfileApi = (patch: Partial<MemoryProfile>, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PATCH\", body: patch, ...o });\n\nexport const replaceProfileApi = (p: MemoryProfile, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"PUT\", body: p, ...o });\n\nexport const clearProfileApi = (o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(\"/api/v1/memory/profile\", { method: \"DELETE\", ...o });\n\nexport const deleteContactApi = (name: string, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/contacts/${encodeURIComponent(name)}`, { method: \"DELETE\", ...o });\n\nexport const deleteNoteApi = (index: number, o: ApiClientOptions): Promise<ApiResponse<MemoryProfile>> =>\n browserApiRequest(`/api/v1/memory/profile/notes/${index}`, { method: \"DELETE\", ...o });\n\nexport const getSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"GET\", ...o });\n\nexport const clearSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<void>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary`, { method: \"DELETE\", ...o });\n\nexport const regenerateSummaryApi = (chatKey: string, o: ApiClientOptions): Promise<ApiResponse<SummaryView>> =>\n browserApiRequest(`/api/v1/chats/${chatKey}/summary/regenerate`, { method: \"POST\", ...o });\n"],"mappings":"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","names":["import_browser"]}
|
package/dist/api/index.mjs
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
1
|
// api/index.ts
|
|
4
2
|
import { browserApiRequest as browserApiRequest2 } from "@elqnt/api-client/browser";
|
|
5
3
|
|
|
6
4
|
// api/stream-api.ts
|
|
7
|
-
async function
|
|
5
|
+
async function resolveToken(opts) {
|
|
6
|
+
if (opts.getToken) {
|
|
7
|
+
try {
|
|
8
|
+
return await opts.getToken() ?? void 0;
|
|
9
|
+
} catch {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return opts.token ?? void 0;
|
|
14
|
+
}
|
|
15
|
+
async function streamFetch(url, body, token) {
|
|
16
|
+
const headers = { "Content-Type": "application/json" };
|
|
17
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
8
18
|
const response = await fetch(url, {
|
|
9
19
|
method: "POST",
|
|
10
|
-
headers
|
|
20
|
+
headers,
|
|
11
21
|
body: JSON.stringify(body)
|
|
12
22
|
});
|
|
13
23
|
if (!response.ok) {
|
|
@@ -22,9 +32,11 @@ async function streamFetch(url, body) {
|
|
|
22
32
|
}
|
|
23
33
|
async function createChat(options) {
|
|
24
34
|
const { baseUrl, orgId, userId, metadata } = options;
|
|
35
|
+
const token = await resolveToken(options);
|
|
25
36
|
const result = await streamFetch(
|
|
26
37
|
`${baseUrl}/create`,
|
|
27
|
-
{ orgId, userId, metadata }
|
|
38
|
+
{ orgId, userId, metadata },
|
|
39
|
+
token
|
|
28
40
|
);
|
|
29
41
|
if (!result.success || !result.data?.chatKey) {
|
|
30
42
|
throw new Error(result.error || "Failed to create chat");
|
|
@@ -33,6 +45,7 @@ async function createChat(options) {
|
|
|
33
45
|
}
|
|
34
46
|
async function sendChatMessage(options) {
|
|
35
47
|
const { baseUrl, orgId, chatKey, userId, content, attachments, metadata } = options;
|
|
48
|
+
const token = await resolveToken(options);
|
|
36
49
|
const message = {
|
|
37
50
|
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
38
51
|
role: "user",
|
|
@@ -49,18 +62,19 @@ async function sendChatMessage(options) {
|
|
|
49
62
|
userId,
|
|
50
63
|
message,
|
|
51
64
|
metadata
|
|
52
|
-
});
|
|
65
|
+
}, token);
|
|
53
66
|
if (!result.success) {
|
|
54
67
|
throw new Error(result.error || "Failed to send message");
|
|
55
68
|
}
|
|
56
69
|
}
|
|
57
70
|
async function loadChat(options) {
|
|
58
71
|
const { baseUrl, orgId, chatKey, userId } = options;
|
|
72
|
+
const token = await resolveToken(options);
|
|
59
73
|
const result = await streamFetch(`${baseUrl}/load`, {
|
|
60
74
|
orgId,
|
|
61
75
|
chatKey,
|
|
62
76
|
userId
|
|
63
|
-
});
|
|
77
|
+
}, token);
|
|
64
78
|
if (!result.success || !result.data?.chat) {
|
|
65
79
|
throw new Error(result.error || "Failed to load chat");
|
|
66
80
|
}
|
|
@@ -68,37 +82,40 @@ async function loadChat(options) {
|
|
|
68
82
|
}
|
|
69
83
|
async function endChat(options) {
|
|
70
84
|
const { baseUrl, orgId, chatKey, userId, reason } = options;
|
|
85
|
+
const token = await resolveToken(options);
|
|
71
86
|
const result = await streamFetch(`${baseUrl}/end`, {
|
|
72
87
|
orgId,
|
|
73
88
|
chatKey,
|
|
74
89
|
userId,
|
|
75
90
|
data: reason ? { reason } : void 0
|
|
76
|
-
});
|
|
91
|
+
}, token);
|
|
77
92
|
if (!result.success) {
|
|
78
93
|
throw new Error(result.error || "Failed to end chat");
|
|
79
94
|
}
|
|
80
95
|
}
|
|
81
96
|
async function sendTypingIndicator(options) {
|
|
82
97
|
const { baseUrl, orgId, chatKey, userId, typing } = options;
|
|
98
|
+
const token = await resolveToken(options);
|
|
83
99
|
const result = await streamFetch(`${baseUrl}/typing`, {
|
|
84
100
|
orgId,
|
|
85
101
|
chatKey,
|
|
86
102
|
userId,
|
|
87
103
|
typing
|
|
88
|
-
});
|
|
104
|
+
}, token);
|
|
89
105
|
if (!result.success) {
|
|
90
106
|
throw new Error(result.error || "Failed to send typing indicator");
|
|
91
107
|
}
|
|
92
108
|
}
|
|
93
109
|
async function sendEvent(options) {
|
|
94
110
|
const { baseUrl, orgId, chatKey, userId, type, data } = options;
|
|
111
|
+
const token = await resolveToken(options);
|
|
95
112
|
const result = await streamFetch(`${baseUrl}/event`, {
|
|
96
113
|
type,
|
|
97
114
|
orgId,
|
|
98
115
|
chatKey,
|
|
99
116
|
userId,
|
|
100
117
|
data
|
|
101
|
-
});
|
|
118
|
+
}, token);
|
|
102
119
|
if (!result.success) {
|
|
103
120
|
throw new Error(result.error || "Failed to send event");
|
|
104
121
|
}
|