botschat 0.1.4 → 0.1.6

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.
Files changed (46) hide show
  1. package/README.md +2 -2
  2. package/package.json +4 -1
  3. package/packages/api/package.json +2 -1
  4. package/packages/api/src/do/connection-do.ts +128 -33
  5. package/packages/api/src/index.ts +103 -6
  6. package/packages/api/src/routes/auth.ts +123 -29
  7. package/packages/api/src/routes/pairing.ts +14 -1
  8. package/packages/api/src/routes/setup.ts +70 -24
  9. package/packages/api/src/routes/upload.ts +12 -8
  10. package/packages/api/src/utils/auth.ts +212 -43
  11. package/packages/api/src/utils/id.ts +30 -14
  12. package/packages/api/src/utils/rate-limit.ts +73 -0
  13. package/packages/plugin/dist/src/channel.js +9 -3
  14. package/packages/plugin/dist/src/channel.js.map +1 -1
  15. package/packages/plugin/package.json +2 -2
  16. package/packages/web/dist/assets/{index-DuGeoFJT.css → index-BST9bfvT.css} +1 -1
  17. package/packages/web/dist/assets/index-Da18EnTa.js +851 -0
  18. package/packages/web/dist/botschat-icon.svg +4 -0
  19. package/packages/web/dist/index.html +23 -3
  20. package/packages/web/dist/manifest.json +24 -0
  21. package/packages/web/dist/sw.js +40 -0
  22. package/packages/web/index.html +21 -1
  23. package/packages/web/src/App.tsx +241 -96
  24. package/packages/web/src/api.ts +63 -3
  25. package/packages/web/src/components/ChatWindow.tsx +11 -11
  26. package/packages/web/src/components/ConnectionSettings.tsx +475 -0
  27. package/packages/web/src/components/CronDetail.tsx +475 -235
  28. package/packages/web/src/components/CronSidebar.tsx +1 -1
  29. package/packages/web/src/components/DebugLogPanel.tsx +116 -3
  30. package/packages/web/src/components/IconRail.tsx +56 -16
  31. package/packages/web/src/components/JobList.tsx +2 -6
  32. package/packages/web/src/components/LoginPage.tsx +126 -103
  33. package/packages/web/src/components/MobileLayout.tsx +480 -0
  34. package/packages/web/src/components/OnboardingPage.tsx +7 -16
  35. package/packages/web/src/components/ResizeHandle.tsx +34 -0
  36. package/packages/web/src/components/Sidebar.tsx +1 -1
  37. package/packages/web/src/components/TaskBar.tsx +2 -2
  38. package/packages/web/src/components/ThreadPanel.tsx +2 -5
  39. package/packages/web/src/hooks/useIsMobile.ts +27 -0
  40. package/packages/web/src/index.css +59 -0
  41. package/packages/web/src/main.tsx +9 -0
  42. package/packages/web/src/store.ts +12 -5
  43. package/packages/web/src/ws.ts +2 -0
  44. package/scripts/dev.sh +13 -13
  45. package/wrangler.toml +3 -1
  46. package/packages/web/dist/assets/index-DyzTR_Y4.js +0 -847
@@ -75,6 +75,12 @@
75
75
  }
76
76
 
77
77
  /* ========== Base styles ========== */
78
+ html, body {
79
+ /* App manages its own scroll containers; root elements should not scroll */
80
+ overflow: hidden;
81
+ height: 100%;
82
+ }
83
+
78
84
  body {
79
85
  margin: 0;
80
86
  font-family: var(--font-sans);
@@ -158,6 +164,22 @@ code:not(pre code) {
158
164
  .no-scrollbar::-webkit-scrollbar { display: none; }
159
165
  .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
160
166
 
167
+ /* ========== Resize handles ========== */
168
+ /* Active state (dragging) — react-resizable-panels v4 uses data-separator + data-state */
169
+ [data-state="dragging"] .resize-handle-line,
170
+ .resize-handle:hover .resize-handle-line {
171
+ background: var(--text-link) !important;
172
+ }
173
+
174
+ [data-state="dragging"] .resize-handle-line {
175
+ width: 3px !important;
176
+ }
177
+
178
+ /* Vertical handles */
179
+ [data-state="dragging"].resize-handle .resize-handle-line[class*="h-"] {
180
+ height: 3px !important;
181
+ }
182
+
161
183
  /* Focus visible */
162
184
  *:focus-visible {
163
185
  outline: 2px solid var(--bg-active);
@@ -168,3 +190,40 @@ code:not(pre code) {
168
190
  body, .theme-transition {
169
191
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
170
192
  }
193
+
194
+ /* ========== Mobile-specific ========== */
195
+ @media (max-width: 767px) {
196
+ /* Prevent iOS rubber-band bounce on main container */
197
+ html, body {
198
+ overscroll-behavior: none;
199
+ -webkit-overflow-scrolling: touch;
200
+ }
201
+
202
+ /* Full-height on mobile (accounts for address bar) */
203
+ html {
204
+ height: -webkit-fill-available;
205
+ }
206
+
207
+ body {
208
+ min-height: -webkit-fill-available;
209
+ }
210
+ }
211
+
212
+ /* Mobile screen transition */
213
+ .mobile-screen-enter {
214
+ animation: slideInRight 0.2s ease-out;
215
+ }
216
+
217
+ .mobile-screen-exit {
218
+ animation: slideOutRight 0.2s ease-in;
219
+ }
220
+
221
+ @keyframes slideInRight {
222
+ from { transform: translateX(30%); opacity: 0; }
223
+ to { transform: translateX(0); opacity: 1; }
224
+ }
225
+
226
+ @keyframes slideOutRight {
227
+ from { transform: translateX(0); opacity: 1; }
228
+ to { transform: translateX(30%); opacity: 0; }
229
+ }
@@ -8,3 +8,12 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
8
8
  <App />
9
9
  </React.StrictMode>,
10
10
  );
11
+
12
+ // Register service worker for PWA support (standalone mode on mobile)
13
+ if ("serviceWorker" in navigator) {
14
+ window.addEventListener("load", () => {
15
+ navigator.serviceWorker.register("/sw.js").catch(() => {
16
+ // SW registration failed — non-critical, app still works
17
+ });
18
+ });
19
+ }
@@ -155,15 +155,22 @@ export function appReducer(state: AppState, action: AppAction): AppState {
155
155
  return { ...state, selectedChannelId: action.channelId, sessions: [], selectedSessionId: null, tasks: [], selectedTaskId: null, jobs: [], selectedJobId: null, messages: [] };
156
156
  case "SET_SESSIONS":
157
157
  return { ...state, sessions: action.sessions };
158
- case "SELECT_SESSION":
158
+ case "SELECT_SESSION": {
159
+ const nextSessionKey = action.sessionKey ?? state.selectedSessionKey;
160
+ const sessionChanged = nextSessionKey !== state.selectedSessionKey;
159
161
  return {
160
162
  ...state,
161
163
  selectedSessionId: action.sessionId,
162
- selectedSessionKey: action.sessionKey ?? state.selectedSessionKey,
163
- messages: [],
164
- activeThreadId: null,
165
- threadMessages: [],
164
+ selectedSessionKey: nextSessionKey,
165
+ // Only clear messages when the session actually changes;
166
+ // otherwise keep whatever was already loaded to avoid the
167
+ // race where SELECT_SESSION arrives *after* SET_MESSAGES
168
+ // (e.g. session-list API returns after message-list API).
169
+ messages: sessionChanged ? [] : state.messages,
170
+ activeThreadId: sessionChanged ? null : state.activeThreadId,
171
+ threadMessages: sessionChanged ? [] : state.threadMessages,
166
172
  };
173
+ }
167
174
  case "ADD_SESSION":
168
175
  return { ...state, sessions: [...state.sessions, action.session] };
169
176
  case "REMOVE_SESSION":
@@ -31,6 +31,8 @@ export class BotsChatWSClient {
31
31
  connect(): void {
32
32
  this.intentionalClose = false;
33
33
  const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
34
+ // Token is NOT included in the URL to avoid leaking it in logs/history.
35
+ // Authentication is handled via the "auth" message after connection.
34
36
  const url = `${protocol}//${window.location.host}/api/ws/${this.opts.userId}/${this.opts.sessionId}`;
35
37
 
36
38
  dlog.info("WS", `Connecting to ${url}`);
package/scripts/dev.sh CHANGED
@@ -54,11 +54,11 @@ do_build_web() {
54
54
  do_start() {
55
55
  kill_port 8787
56
56
  info "Starting wrangler dev on 0.0.0.0:8787…"
57
- exec npx wrangler dev --config wrangler.toml --ip 0.0.0.0
57
+ exec npx wrangler dev --config wrangler.toml --ip 0.0.0.0 --var ENVIRONMENT:development
58
58
  }
59
59
 
60
60
  do_sync_plugin() {
61
- local REMOTE="YinTong@mini.local"
61
+ local REMOTE="mini.local"
62
62
  local REMOTE_DIR="~/Projects/botsChat/packages/plugin"
63
63
 
64
64
  info "Syncing plugin to $REMOTE…"
@@ -66,20 +66,20 @@ do_sync_plugin() {
66
66
  packages/plugin/ "$REMOTE:$REMOTE_DIR/"
67
67
  ok "Plugin files synced"
68
68
 
69
- info "Building plugin + restarting gateway on mini.local…"
70
- ssh "$REMOTE" "$(cat <<'REMOTE_SCRIPT'
71
- export PATH="/opt/homebrew/bin:$PATH"
69
+ info "Building plugin, deploying to extensions, restarting gateway on mini.local…"
70
+ ssh "$REMOTE" 'export PATH="/opt/homebrew/bin:$PATH"
72
71
  cd ~/Projects/botsChat/packages/plugin
73
72
  npm run build
74
- echo "--- Plugin build OK ---"
73
+ EXT_DIR=~/.openclaw/extensions/botschat
74
+ rsync -av --delete dist/ "$EXT_DIR/dist/"
75
+ rsync -av bin/ "$EXT_DIR/bin/" 2>/dev/null || true
76
+ cp -f package.json openclaw.plugin.json "$EXT_DIR/" 2>/dev/null || true
77
+ echo "--- Deployed to $EXT_DIR ---"
75
78
  pkill -9 -f openclaw-gateway 2>/dev/null || true
76
79
  sleep 3
77
- nohup openclaw gateway run --bind loopback --port 18789 --force \
78
- > /tmp/openclaw-gateway.log 2>&1 &
79
- echo "Gateway restarted (PID=$!)"
80
- REMOTE_SCRIPT
81
- )"
82
- ok "Plugin synced and gateway restarted"
80
+ nohup openclaw gateway run --bind loopback --port 18789 --force > /tmp/openclaw-gateway.log 2>&1 &
81
+ echo "Gateway restarted (PID=$!)"'
82
+ ok "Plugin synced, deployed to extensions, gateway restarted"
83
83
 
84
84
  sleep 4
85
85
  info "Checking connection…"
@@ -88,7 +88,7 @@ REMOTE_SCRIPT
88
88
 
89
89
  do_logs() {
90
90
  info "Tailing gateway logs on mini.local…"
91
- ssh YinTong@mini.local 'tail -f /tmp/openclaw-gateway.log'
91
+ ssh mini.local 'tail -f /tmp/openclaw-gateway.log'
92
92
  }
93
93
 
94
94
  # ── Main ─────────────────────────────────────────────────────────────
package/wrangler.toml CHANGED
@@ -33,9 +33,11 @@ new_sqlite_classes = ["ConnectionDO"]
33
33
 
34
34
  # --- Environment Variables ---
35
35
  [vars]
36
- ENVIRONMENT = "development"
36
+ ENVIRONMENT = "production"
37
37
  # Firebase project ID for Google/GitHub Sign-In token verification.
38
38
  FIREBASE_PROJECT_ID = "botschat-130ff"
39
+ # IMPORTANT: For production, set JWT_SECRET via `wrangler secret put JWT_SECRET`.
40
+ # The app will refuse to start in non-development mode without it.
39
41
 
40
42
  # --- Dev settings ---
41
43
  [dev]