wealth-alpha-chat-widget 1.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/README.md +139 -0
- package/dist/index.cjs +1110 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +388 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +219 -0
- package/dist/index.d.ts +219 -0
- package/dist/index.mjs +1079 -0
- package/dist/index.mjs.map +1 -0
- package/docs/BACKEND_CHAT_WIDGET.md +357 -0
- package/docs/DEPLOY.md +283 -0
- package/docs/PUBLISH.md +202 -0
- package/docs/SETUP.md +180 -0
- package/package.json +77 -0
package/docs/DEPLOY.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Deployment guide
|
|
2
|
+
|
|
3
|
+
Production rollout for the full stack:
|
|
4
|
+
- **Backend** — FastAPI + `chat_widget.py` + `chat_formatters.py`
|
|
5
|
+
- **Library** — published to npm
|
|
6
|
+
- **Frontend** — Next.js consuming the library
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Backend (`WealthAlpha-Backend`)
|
|
11
|
+
|
|
12
|
+
### 1.1 Files that must ship
|
|
13
|
+
|
|
14
|
+
The chat router introduces two new files on top of the team's existing backend:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
app/api_v1/chat_widget.py ← chip router, multi-step state, intent routing
|
|
18
|
+
app/api_v1/chat_formatters.py ← 18 template formatters
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
And `app/api_v1/router.py` must include the widget:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from app.api_v1 import ..., chat_widget
|
|
25
|
+
|
|
26
|
+
api_v1_router.include_router(
|
|
27
|
+
chat_widget.chat_widget_router,
|
|
28
|
+
prefix="/chat-widget",
|
|
29
|
+
tags=["chat-widget"],
|
|
30
|
+
)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 1.2 Dependencies
|
|
34
|
+
|
|
35
|
+
`requirements.txt` already includes `httpx`, `fastapi`, `pydantic`, `asyncpg`. No extra dependencies needed for the chat widget — `chat_widget.py` reuses what's there.
|
|
36
|
+
|
|
37
|
+
### 1.3 Environment variables
|
|
38
|
+
|
|
39
|
+
Reuses the team's existing env vars (`SECRET_KEY`, `DATABASE_URL`, etc.). The widget doesn't introduce new env vars.
|
|
40
|
+
|
|
41
|
+
### 1.4 Run with uvicorn (single instance)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd WealthAlpha-Backend
|
|
45
|
+
source venv/bin/activate
|
|
46
|
+
pip install -r requirements.txt
|
|
47
|
+
|
|
48
|
+
uvicorn main:app \
|
|
49
|
+
--host 0.0.0.0 \
|
|
50
|
+
--port 8013 \
|
|
51
|
+
--proxy-headers \
|
|
52
|
+
--forwarded-allow-ips='*' \
|
|
53
|
+
--workers 4
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `--proxy-headers` — trusts `X-Forwarded-Proto`/`X-Forwarded-Host` from the reverse proxy so `_base_url_from_request()` returns the correct `https://` URL for the httpx loopback.
|
|
57
|
+
- `--forwarded-allow-ips='*'` — accepts forwarded headers from any upstream proxy IP. Lock down to your specific reverse proxy IP in production.
|
|
58
|
+
- `--workers 4` — fork worker processes. Each has its own `_SessionStore` dict (see §1.6).
|
|
59
|
+
|
|
60
|
+
### 1.5 Reverse proxy (Nginx)
|
|
61
|
+
|
|
62
|
+
```nginx
|
|
63
|
+
upstream wealth_backend {
|
|
64
|
+
server 127.0.0.1:8013;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
server {
|
|
68
|
+
server_name api.your-domain.com;
|
|
69
|
+
listen 443 ssl http2;
|
|
70
|
+
|
|
71
|
+
location /api/v1/chat-widget/ {
|
|
72
|
+
proxy_pass http://wealth_backend;
|
|
73
|
+
proxy_set_header Host $host;
|
|
74
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
75
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
76
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
77
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
78
|
+
proxy_read_timeout 60s;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# All other /api/v1/* routes
|
|
82
|
+
location /api/v1/ {
|
|
83
|
+
proxy_pass http://wealth_backend;
|
|
84
|
+
proxy_set_header Host $host;
|
|
85
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
86
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
87
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 1.6 Scaling beyond one process
|
|
93
|
+
|
|
94
|
+
`chat_widget.py` uses an **in-memory `_SessionStore` dict** for multi-step state (`{action, collected, awaiting}` per `sessionId`). This is single-process only.
|
|
95
|
+
|
|
96
|
+
For multi-worker / multi-instance deploys, swap `_SessionStore` for a Redis-backed implementation:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import redis.asyncio as redis
|
|
100
|
+
|
|
101
|
+
class _SessionStore:
|
|
102
|
+
_TTL_SECONDS = 1800
|
|
103
|
+
def __init__(self):
|
|
104
|
+
self._r = redis.from_url(os.environ["REDIS_URL"])
|
|
105
|
+
|
|
106
|
+
async def get(self, session_id):
|
|
107
|
+
raw = await self._r.get(f"wac:{session_id}")
|
|
108
|
+
return json.loads(raw) if raw else None
|
|
109
|
+
|
|
110
|
+
async def set(self, session_id, action, collected, awaiting):
|
|
111
|
+
await self._r.setex(
|
|
112
|
+
f"wac:{session_id}",
|
|
113
|
+
self._TTL_SECONDS,
|
|
114
|
+
json.dumps({"action": action, "collected": collected, "awaiting": awaiting}),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def clear(self, session_id):
|
|
118
|
+
await self._r.delete(f"wac:{session_id}")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Update all `sessions.get(...)` / `sessions.set(...)` / `sessions.clear(...)` calls to `await ...`. The TTL stays at 30 min.
|
|
122
|
+
|
|
123
|
+
### 1.7 Systemd unit (optional)
|
|
124
|
+
|
|
125
|
+
```ini
|
|
126
|
+
# /etc/systemd/system/wealth-alpha-backend.service
|
|
127
|
+
[Unit]
|
|
128
|
+
Description=Wealth Alpha FastAPI
|
|
129
|
+
After=network.target postgresql.service redis.service
|
|
130
|
+
|
|
131
|
+
[Service]
|
|
132
|
+
WorkingDirectory=/srv/wealth-alpha/backend
|
|
133
|
+
ExecStart=/srv/wealth-alpha/backend/venv/bin/uvicorn main:app \
|
|
134
|
+
--host 0.0.0.0 --port 8013 \
|
|
135
|
+
--proxy-headers --forwarded-allow-ips='*' \
|
|
136
|
+
--workers 4
|
|
137
|
+
EnvironmentFile=/etc/wealth-alpha/backend.env
|
|
138
|
+
Restart=always
|
|
139
|
+
User=wealth
|
|
140
|
+
Group=wealth
|
|
141
|
+
|
|
142
|
+
[Install]
|
|
143
|
+
WantedBy=multi-user.target
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
sudo systemctl daemon-reload
|
|
148
|
+
sudo systemctl enable wealth-alpha-backend
|
|
149
|
+
sudo systemctl start wealth-alpha-backend
|
|
150
|
+
sudo journalctl -u wealth-alpha-backend -f # tail logs
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 2. Library (npm)
|
|
156
|
+
|
|
157
|
+
See **[PUBLISH.md](./PUBLISH.md)** — full release flow.
|
|
158
|
+
|
|
159
|
+
Short version for an existing published library:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd Wealth-alpha-chat-UI
|
|
163
|
+
npm version patch # or minor / major
|
|
164
|
+
npm run build
|
|
165
|
+
npm publish --access public
|
|
166
|
+
git push --follow-tags
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 3. Frontend (Next.js host app)
|
|
172
|
+
|
|
173
|
+
### 3.1 Install the published library
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
cd WealthAlpha-Frontend
|
|
177
|
+
npm install wealth-alpha-chat@latest
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 3.2 Environment variables
|
|
181
|
+
|
|
182
|
+
In `.env.production` (or your platform's dashboard — Vercel, Netlify, etc.):
|
|
183
|
+
|
|
184
|
+
```env
|
|
185
|
+
NEXT_PUBLIC_API_BASE=https://api.your-domain.com/api/v1/chat-widget
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The library uses this as `apiBase`. No other env vars are required by the widget itself.
|
|
189
|
+
|
|
190
|
+
### 3.3 Build & deploy
|
|
191
|
+
|
|
192
|
+
#### Vercel
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
vercel --prod
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Vercel auto-detects Next.js, builds with `next build`, and serves the static + serverless output.
|
|
199
|
+
|
|
200
|
+
#### Self-hosted
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
cd WealthAlpha-Frontend
|
|
204
|
+
npm ci
|
|
205
|
+
npm run build
|
|
206
|
+
npm run start # serves on PORT (default 3000)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Behind Nginx:
|
|
210
|
+
|
|
211
|
+
```nginx
|
|
212
|
+
server {
|
|
213
|
+
server_name app.your-domain.com;
|
|
214
|
+
listen 443 ssl http2;
|
|
215
|
+
|
|
216
|
+
location / {
|
|
217
|
+
proxy_pass http://127.0.0.1:3000;
|
|
218
|
+
proxy_set_header Host $host;
|
|
219
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
220
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 4. End-to-end smoke test after deploy
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# 1. Backend up?
|
|
231
|
+
curl -s https://api.your-domain.com/api/v1/chat-widget/me
|
|
232
|
+
# → {"detail":"Not authenticated"} ✓ route exists, auth-gated
|
|
233
|
+
|
|
234
|
+
# 2. Frontend serves the bundled library?
|
|
235
|
+
curl -s https://app.your-domain.com/ | grep -oE "wealth-alpha-chat" | head -1
|
|
236
|
+
# → some occurrence of the package name in the HTML or chunk references
|
|
237
|
+
|
|
238
|
+
# 3. CORS allows your frontend domain?
|
|
239
|
+
curl -s -I -X OPTIONS https://api.your-domain.com/api/v1/chat-widget/me \
|
|
240
|
+
-H "Origin: https://app.your-domain.com" \
|
|
241
|
+
-H "Access-Control-Request-Method: GET"
|
|
242
|
+
# → Access-Control-Allow-Origin: * (or your specific origin)
|
|
243
|
+
|
|
244
|
+
# 4. Open https://app.your-domain.com in browser
|
|
245
|
+
# 5. Log in normally
|
|
246
|
+
# 6. Click 💬 → confirm welcome with your name + chips
|
|
247
|
+
# 7. Click any chip → confirm result renders with bold/italic/bullets
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 5. Common production issues
|
|
253
|
+
|
|
254
|
+
| Symptom | Cause | Fix |
|
|
255
|
+
|---|---|---|
|
|
256
|
+
| `httpx.ConnectError: SSL` when chat calls upstream | Backend behind reverse proxy but `--proxy-headers` not set | Add `--proxy-headers --forwarded-allow-ips='*'` to uvicorn |
|
|
257
|
+
| `401` on `/chat-widget/me` for all users | Different JWT secret in prod vs the one issuing tokens | Verify `SECRET_KEY` env var matches across services |
|
|
258
|
+
| `403 premium required` on every chip | `telegram_id` not linked for users | Check that `users.telegram_id` is populated. Widget extracts from `current_user.telegram_id`. |
|
|
259
|
+
| Multi-step flow loses state between requests | Multiple uvicorn workers, in-memory `_SessionStore` | Swap to Redis-backed store (§1.6) |
|
|
260
|
+
| Chat history endpoint returns nothing | `ChatService` DB writes failing silently | Check uvicorn logs for "chat_widget: persist_turn failed" errors |
|
|
261
|
+
| Bold rendering doesn't work | Library cached at CDN | Bust CDN cache; verify `wealth-alpha-chat` version in `node_modules` is current |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 6. Monitoring
|
|
266
|
+
|
|
267
|
+
Recommended log lines to alert on:
|
|
268
|
+
|
|
269
|
+
- `chat_widget: persist_turn failed` — DB write failed
|
|
270
|
+
- `upstream … unreachable` — telegram-api endpoint down
|
|
271
|
+
- `intent unavailable for user=` — intent classifier down
|
|
272
|
+
- HTTP 5xx on `/chat-widget/*` — chat router crashed
|
|
273
|
+
|
|
274
|
+
`logger = logging.getLogger("wealth_alpha.chat_widget")` namespace is used throughout. Pipe it to your aggregator (Datadog, Loki, CloudWatch).
|
|
275
|
+
|
|
276
|
+
For per-request tracing, the library sends `X-Request-Id` header on every chat call. Match it in backend logs:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
# In chat_widget.py — add at the top of each endpoint
|
|
280
|
+
logger.info("chat_widget %s %s req_id=%s",
|
|
281
|
+
request.method, request.url.path,
|
|
282
|
+
request.headers.get("x-request-id"))
|
|
283
|
+
```
|
package/docs/PUBLISH.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Publishing `wealth-alpha-chat` to npm
|
|
2
|
+
|
|
3
|
+
How to cut a new version of the library, build it, and push it to the npm registry.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Pre-flight checklist
|
|
8
|
+
|
|
9
|
+
Run before every publish. Any failure → fix before continuing.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Clean working tree
|
|
13
|
+
git status
|
|
14
|
+
# (should show nothing besides files you intend to publish)
|
|
15
|
+
|
|
16
|
+
# 2. Typecheck clean
|
|
17
|
+
npm run typecheck
|
|
18
|
+
|
|
19
|
+
# 3. Build produces dist/ with ESM + CJS + types + CSS
|
|
20
|
+
npm run build
|
|
21
|
+
ls -la dist/
|
|
22
|
+
# Expect: index.mjs, index.cjs, index.d.ts, index.d.cts, index.css
|
|
23
|
+
|
|
24
|
+
# 4. "use client" directive is preserved at top of both JS outputs (required for Next.js App Router)
|
|
25
|
+
head -1 dist/index.mjs # "use client";
|
|
26
|
+
head -1 dist/index.cjs # "use client";
|
|
27
|
+
|
|
28
|
+
# 5. Bundle size sanity check (< 60 KB total uncompressed is healthy)
|
|
29
|
+
du -sh dist/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Version bump
|
|
35
|
+
|
|
36
|
+
Follow [semver](https://semver.org/):
|
|
37
|
+
|
|
38
|
+
| Change | Bump |
|
|
39
|
+
|---|---|
|
|
40
|
+
| Bug fix, no API change | patch (0.1.0 → 0.1.1) |
|
|
41
|
+
| New prop / new export / chip tree extension | minor (0.1.0 → 0.2.0) |
|
|
42
|
+
| Removed/renamed prop, breaking chat-widget protocol | major (0.1.0 → 1.0.0) |
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm version patch # → 0.1.1
|
|
46
|
+
npm version minor # → 0.2.0
|
|
47
|
+
npm version major # → 1.0.0
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This automatically updates `package.json` and creates a `vX.Y.Z` git tag.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Dry-run with `npm pack`
|
|
55
|
+
|
|
56
|
+
Always pack first to inspect what npm will publish:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm pack
|
|
60
|
+
# → wealth-alpha-chat-0.1.0.tgz
|
|
61
|
+
|
|
62
|
+
tar -tzf wealth-alpha-chat-0.1.0.tgz | head -30
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The tarball should contain:
|
|
66
|
+
- `package/package.json`
|
|
67
|
+
- `package/README.md`
|
|
68
|
+
- `package/dist/index.mjs`, `dist/index.cjs`, `dist/index.d.ts`, `dist/index.d.cts`, `dist/index.css`
|
|
69
|
+
- `dist/*.map` (source maps)
|
|
70
|
+
|
|
71
|
+
It should **NOT** contain:
|
|
72
|
+
- `src/` (we ship compiled output only — `.npmignore` blocks it)
|
|
73
|
+
- `node_modules/`
|
|
74
|
+
- `examples/`
|
|
75
|
+
- `*.config.ts`, `tsconfig.json`
|
|
76
|
+
- internal docs `WEALTH_ALPHA_CHAT_PLAN.md`, `api_response.md`
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. Test the tarball locally in a fresh project
|
|
81
|
+
|
|
82
|
+
Before pushing to npm, sanity-check by installing the tarball into a real consumer:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cd ~/test-project
|
|
86
|
+
npm install /abs/path/to/wealth-alpha-chat-0.1.0.tgz --force
|
|
87
|
+
npm run dev
|
|
88
|
+
# manually click through chat flows
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 5. Login to npm
|
|
94
|
+
|
|
95
|
+
First time only:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm login
|
|
99
|
+
# Enter username, password, email, OTP
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Verify:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm whoami
|
|
106
|
+
# → your-npm-username
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 6. Publish
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm publish --access public
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`--access public` is required for scoped packages and any package on a new account. For an unscoped public package it's still safe to include.
|
|
118
|
+
|
|
119
|
+
You should see:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
+ wealth-alpha-chat@0.1.0
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Confirm on npmjs.com:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm view wealth-alpha-chat
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 7. Push the git tag
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git push --follow-tags
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This pushes both the version bump commit and the `vX.Y.Z` tag.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 8. Post-publish smoke test
|
|
144
|
+
|
|
145
|
+
Spin up an empty Next.js project, install from npm (not the tarball), and verify the widget mounts:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx create-next-app@latest test-app --typescript --app
|
|
149
|
+
cd test-app
|
|
150
|
+
npm install wealth-alpha-chat
|
|
151
|
+
# Add <WealthChat /> to src/app/layout.tsx per SETUP.md
|
|
152
|
+
npm run dev
|
|
153
|
+
# Open http://localhost:3000, confirm 💬 appears, click → AuthGate renders
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 9. Handling failures
|
|
159
|
+
|
|
160
|
+
| Failure | Fix |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `403 Forbidden — version already exists` | Bump the version (`npm version patch`) and try again — npm doesn't allow republishing the same version. |
|
|
163
|
+
| `EPUBLISHCONFLICT` | Same as above. |
|
|
164
|
+
| `EINVALIDTAGNAME` | `npm version` couldn't create the git tag — usually because the working tree has uncommitted changes. Commit or stash, retry. |
|
|
165
|
+
| `ENEEDAUTH` | Re-run `npm login`. |
|
|
166
|
+
| `Bundle size unexpectedly grew` | Check `package.json` `dependencies` — only `markdown-it` should be there. React/React-DOM must stay in `peerDependencies`. |
|
|
167
|
+
| `Next.js App Router consumers see "Cannot use React Hook"` | The `"use client"` directive got stripped. Re-run `npm run build` and verify with `head -1 dist/index.mjs` that the directive is still there. The `scripts/add-use-client.mjs` post-build script re-injects it. |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 10. Rollback / deprecate
|
|
172
|
+
|
|
173
|
+
If a version is broken in the wild:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Mark a version as deprecated (doesn't unpublish, but warns on install)
|
|
177
|
+
npm deprecate wealth-alpha-chat@0.1.0 "Use 0.1.1 instead — fixes critical chip-routing bug"
|
|
178
|
+
|
|
179
|
+
# Within 72 hours of publish, you CAN unpublish
|
|
180
|
+
npm unpublish wealth-alpha-chat@0.1.0
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
After 72 hours npm doesn't allow unpublish. Always prefer deprecate + push a patch.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 11. Release notes
|
|
188
|
+
|
|
189
|
+
Write release notes in `CHANGELOG.md` (one section per version) and tag the GitHub release with them:
|
|
190
|
+
|
|
191
|
+
```md
|
|
192
|
+
## 0.1.1 — 2026-05-28
|
|
193
|
+
|
|
194
|
+
### Fixed
|
|
195
|
+
- Chip path no longer doubles when apiBase ends with `/chat-widget`.
|
|
196
|
+
- Library reads `localStorage["token"]` as a fallback when `wac_session` is empty.
|
|
197
|
+
|
|
198
|
+
### Added
|
|
199
|
+
- markdown-it rendering for bot bubbles (bold, italic, bullets, links).
|
|
200
|
+
- Drill-down chips on list results (Smart Money Picks, Discovery, Crypto List, Peer).
|
|
201
|
+
- Telegram-style chip flows for Stock Analysis, Portfolio Risk, Crypto, Market Forecast.
|
|
202
|
+
```
|
package/docs/SETUP.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Setup guide — integrating `wealth-alpha-chat` into a host app
|
|
2
|
+
|
|
3
|
+
This is what a consumer (React or Next.js project) needs to do to mount the chat widget.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install wealth-alpha-chat
|
|
11
|
+
# or
|
|
12
|
+
yarn add wealth-alpha-chat
|
|
13
|
+
# or
|
|
14
|
+
pnpm add wealth-alpha-chat
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Peer requirements**: React ≥ 17, React DOM ≥ 17. No other peers.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Import the CSS once
|
|
22
|
+
|
|
23
|
+
The library ships scoped CSS Modules, but the consumer must include the stylesheet once globally.
|
|
24
|
+
|
|
25
|
+
### Next.js App Router (`src/app/layout.tsx`)
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import "wealth-alpha-chat/styles.css";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Vite / CRA (`src/main.tsx` or `src/index.tsx`)
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import "wealth-alpha-chat/styles.css";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 3. Mount the widget
|
|
40
|
+
|
|
41
|
+
Add `<WealthChat />` somewhere it stays mounted on every page — typically the root layout, outside the page content. The widget renders a floating button that opens a chat panel.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { WealthChat } from "wealth-alpha-chat";
|
|
45
|
+
|
|
46
|
+
<WealthChat
|
|
47
|
+
apiBase="https://api.your-domain.com/api/v1/chat-widget"
|
|
48
|
+
authCheck="/me"
|
|
49
|
+
loginUrl="/login"
|
|
50
|
+
sessionTTL={1800}
|
|
51
|
+
brandName="Wealth Alpha AI"
|
|
52
|
+
brandColor="#1a2d5a"
|
|
53
|
+
position="bottom-right"
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Props
|
|
58
|
+
|
|
59
|
+
| Prop | Type | Required | Default | Purpose |
|
|
60
|
+
|---|---|---|---|---|
|
|
61
|
+
| `apiBase` | string | ✅ | — | Base URL of the chat-widget backend. Library appends `/me`, `/chip/{id}`, `/message`. **Do not include `/chat/`** in the suffix or the path doubles. |
|
|
62
|
+
| `authCheck` | string | ❌ | `"/me"` | Path appended to `apiBase` for the auth check. |
|
|
63
|
+
| `loginUrl` | string | ✅ | — | Where the AuthGate's "Sign in" button redirects. |
|
|
64
|
+
| `sessionTTL` | number (seconds) | ❌ | `1800` | Sliding-window session expiry. Resets on each chip/message. |
|
|
65
|
+
| `welcomeMessage` | string | ❌ | uses backend's `/me` `welcomeMessage` | Override the welcome bubble text. |
|
|
66
|
+
| `brandName` | string | ❌ | `"Wealth Alpha AI"` | Shown in the chat header and AuthGate. |
|
|
67
|
+
| `brandColor` | string (CSS color) | ❌ | `"#1a2d5a"` | Header background, floating button, primary buttons. Exposed as `--wac-brand` CSS variable. |
|
|
68
|
+
| `position` | `"bottom-right"` \| `"bottom-left"` | ❌ | `"bottom-right"` | Floating button + panel anchor. |
|
|
69
|
+
| `defaultOpen` | boolean | ❌ | `false` | Open the chat panel on mount instead of just showing the floating button. |
|
|
70
|
+
| `showCountdown` | boolean | ❌ | `true` | Show "Session: 23m left" in the header. |
|
|
71
|
+
| `onLogin` | `() => void` | ❌ | — | Fires when the chat detects a valid JWT and shows the welcome message. |
|
|
72
|
+
| `onLogout` | `() => void` | ❌ | — | Fires when the session is cleared. |
|
|
73
|
+
| `onSessionExpire` | `() => void` | ❌ | — | Fires when the sliding-window TTL expires. |
|
|
74
|
+
| `onError` | `(err: Error) => void` | ❌ | — | Fires on auth or network errors. |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 4. Authentication — automatic JWT pickup
|
|
79
|
+
|
|
80
|
+
The library auto-discovers a JWT from `localStorage`. It checks these keys in order:
|
|
81
|
+
|
|
82
|
+
1. `wac_session.token` (full session object — set by the library itself or a bridge)
|
|
83
|
+
2. `token` ← **your normal login key**
|
|
84
|
+
3. `access_token`
|
|
85
|
+
4. `auth_token`
|
|
86
|
+
5. `jwt`
|
|
87
|
+
|
|
88
|
+
So if your existing login already stores the JWT under `localStorage["token"]`, **no bridge code is needed**. The widget will pick it up on next render or `storage` event.
|
|
89
|
+
|
|
90
|
+
### Auto-logout
|
|
91
|
+
|
|
92
|
+
When the chat adopts a JWT from a fallback key it stamps the session with `bootSource: "fallback"`. On every read, if the fallback key is gone, the cached `wac_session` is cleared too — so when the host app calls `localStorage.removeItem("token")` on logout, the chat falls back to the AuthGate within one tick. No `onLogout` plumbing required.
|
|
93
|
+
|
|
94
|
+
If you set `wac_session` directly (not via a fallback key), the auto-logout doesn't apply — you control its lifecycle.
|
|
95
|
+
|
|
96
|
+
### What if my token is in a different key?
|
|
97
|
+
|
|
98
|
+
Either rename your storage key to one of the above, or set `wac_session` directly:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
localStorage.setItem("wac_session", JSON.stringify({
|
|
102
|
+
token: yourJwtString,
|
|
103
|
+
userId: "u_xxx",
|
|
104
|
+
sessionId: crypto.randomUUID(),
|
|
105
|
+
expiresAt: Date.now() + 30 * 60 * 1000,
|
|
106
|
+
lastActive: Date.now(),
|
|
107
|
+
history: [],
|
|
108
|
+
}));
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### What if my token is in an httpOnly cookie?
|
|
112
|
+
|
|
113
|
+
The library only reads JWTs from `localStorage`. For cookie-based auth, you need to either:
|
|
114
|
+
- Set `credentials: "include"` in your fetch wrapper (would require library change), or
|
|
115
|
+
- Expose a small endpoint that returns the JWT for the logged-in user and write it to `localStorage` on login.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 5. Environment variables
|
|
120
|
+
|
|
121
|
+
| Variable | Where | Purpose |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `NEXT_PUBLIC_API_BASE` | Next.js host app | Override the default `apiBase`. Recommended pattern: `process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:8013/api/v1/chat-widget"` |
|
|
124
|
+
|
|
125
|
+
You can also wire `brandColor`, `brandName`, etc. from env vars if you want them per-environment.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 6. Hiding the widget on auth pages
|
|
130
|
+
|
|
131
|
+
Don't render `<WealthChat />` on `/login`, `/register`, or admin pages:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
const AUTH_PATHS = ["/login", "/register"];
|
|
135
|
+
const isAuthPage = AUTH_PATHS.includes(pathname);
|
|
136
|
+
const isAdmin = pathname.startsWith("/admin");
|
|
137
|
+
|
|
138
|
+
{!isAuthPage && !isAdmin && <WealthChat ... />}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 7. Theming
|
|
144
|
+
|
|
145
|
+
Override the brand color via prop **or** with a CSS variable in your global styles:
|
|
146
|
+
|
|
147
|
+
```css
|
|
148
|
+
.landing-theme {
|
|
149
|
+
--wac-brand: #1a2d5a; /* default */
|
|
150
|
+
--wac-brand-contrast: #ffffff;
|
|
151
|
+
--wac-bg: #ffffff;
|
|
152
|
+
--wac-fg: #1f2937;
|
|
153
|
+
--wac-muted: #6b7280;
|
|
154
|
+
--wac-border: #e5e7eb;
|
|
155
|
+
--wac-bot-bg: #f3f4f6;
|
|
156
|
+
--wac-user-bg: var(--wac-brand);
|
|
157
|
+
--wac-chip-bg: #eef2ff;
|
|
158
|
+
--wac-chip-fg: #1e293b;
|
|
159
|
+
--wac-radius: 14px;
|
|
160
|
+
--wac-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
All chat colors are CSS-variable-driven so dark mode can be added by toggling a class on `<html>`.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 8. Troubleshooting
|
|
169
|
+
|
|
170
|
+
| Symptom | Cause | Fix |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| 💬 button doesn't appear | Library not bundled, or `<WealthChat>` not mounted | Verify import compiles; check the layout actually renders it |
|
|
173
|
+
| Clicking 💬 shows AuthGate even when logged in | JWT not in `localStorage` under one of the auto-discovered keys (`token`, `access_token`, `auth_token`, `jwt`) | DevTools → Application → Local Storage → rename your key, or write `wac_session` directly (see §3) |
|
|
174
|
+
| `401 Unauthorized` on `/chat-widget/me` | JWT invalid / expired / wrong issuer | Re-login; verify your backend's `get_current_user` accepts the JWT format |
|
|
175
|
+
| `404 /chat-widget/chat/chip/...` (path doubled) | `apiBase` already includes `/chat-widget` AND old library version | Update to library ≥ 0.1.0 which uses `/chip/{id}` not `/chat/chip/{id}` |
|
|
176
|
+
| Stuck "compiling" on first chat open | Next.js dev cold-start | Wait ~5 s for first compile, subsequent loads are instant |
|
|
177
|
+
| Bold text shows as literal `*asterisks*` | Library not rebuilt after markdown-it added | Update to library ≥ 0.1.0 |
|
|
178
|
+
| Floating button + dark area visible behind chat panel | Page background bleeding through | Library widget is positioned correctly; check that no global CSS overrides `position: fixed` on the widget div |
|
|
179
|
+
|
|
180
|
+
For backend-side issues (chip not routing, wrong endpoint called, `telegram_id` missing) see **[BACKEND_CHAT_WIDGET.md](./BACKEND_CHAT_WIDGET.md)**.
|