@mseep/clawdcursor 1.5.5

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 (354) hide show
  1. package/CHANGELOG.md +2264 -0
  2. package/LICENSE +21 -0
  3. package/README.md +385 -0
  4. package/SECURITY.md +44 -0
  5. package/SKILL.md +503 -0
  6. package/dist/core/agent-loop/agent.d.ts +42 -0
  7. package/dist/core/agent-loop/agent.js +1023 -0
  8. package/dist/core/agent-loop/agent.js.map +1 -0
  9. package/dist/core/agent-loop/batch-tool.d.ts +25 -0
  10. package/dist/core/agent-loop/batch-tool.js +218 -0
  11. package/dist/core/agent-loop/batch-tool.js.map +1 -0
  12. package/dist/core/agent-loop/coord-scale.d.ts +72 -0
  13. package/dist/core/agent-loop/coord-scale.js +89 -0
  14. package/dist/core/agent-loop/coord-scale.js.map +1 -0
  15. package/dist/core/agent-loop/focus-guard.d.ts +24 -0
  16. package/dist/core/agent-loop/focus-guard.js +29 -0
  17. package/dist/core/agent-loop/focus-guard.js.map +1 -0
  18. package/dist/core/agent-loop/project-mcp.d.ts +97 -0
  19. package/dist/core/agent-loop/project-mcp.js +253 -0
  20. package/dist/core/agent-loop/project-mcp.js.map +1 -0
  21. package/dist/core/agent-loop/prompt.d.ts +45 -0
  22. package/dist/core/agent-loop/prompt.js +426 -0
  23. package/dist/core/agent-loop/prompt.js.map +1 -0
  24. package/dist/core/agent-loop/tool-meta.d.ts +93 -0
  25. package/dist/core/agent-loop/tool-meta.js +651 -0
  26. package/dist/core/agent-loop/tool-meta.js.map +1 -0
  27. package/dist/core/agent-loop/tools.d.ts +38 -0
  28. package/dist/core/agent-loop/tools.js +2134 -0
  29. package/dist/core/agent-loop/tools.js.map +1 -0
  30. package/dist/core/agent-loop/types.d.ts +170 -0
  31. package/dist/core/agent-loop/types.js +12 -0
  32. package/dist/core/agent-loop/types.js.map +1 -0
  33. package/dist/core/agent.d.ts +51 -0
  34. package/dist/core/agent.js +245 -0
  35. package/dist/core/agent.js.map +1 -0
  36. package/dist/core/app-categories.d.ts +67 -0
  37. package/dist/core/app-categories.js +108 -0
  38. package/dist/core/app-categories.js.map +1 -0
  39. package/dist/core/banner.d.ts +70 -0
  40. package/dist/core/banner.js +245 -0
  41. package/dist/core/banner.js.map +1 -0
  42. package/dist/core/classify/capability.d.ts +45 -0
  43. package/dist/core/classify/capability.js +78 -0
  44. package/dist/core/classify/capability.js.map +1 -0
  45. package/dist/core/decompose/llm-decomposer.d.ts +35 -0
  46. package/dist/core/decompose/llm-decomposer.js +156 -0
  47. package/dist/core/decompose/llm-decomposer.js.map +1 -0
  48. package/dist/core/decompose/parser.d.ts +27 -0
  49. package/dist/core/decompose/parser.js +101 -0
  50. package/dist/core/decompose/parser.js.map +1 -0
  51. package/dist/core/observability/correlation.d.ts +19 -0
  52. package/dist/core/observability/correlation.js +36 -0
  53. package/dist/core/observability/correlation.js.map +1 -0
  54. package/dist/core/observability/cost-meter.d.ts +51 -0
  55. package/dist/core/observability/cost-meter.js +134 -0
  56. package/dist/core/observability/cost-meter.js.map +1 -0
  57. package/dist/core/observability/logger.d.ts +61 -0
  58. package/dist/core/observability/logger.js +550 -0
  59. package/dist/core/observability/logger.js.map +1 -0
  60. package/dist/core/router/aliases.d.ts +50 -0
  61. package/dist/core/router/aliases.js +104 -0
  62. package/dist/core/router/aliases.js.map +1 -0
  63. package/dist/core/router/normalize.d.ts +41 -0
  64. package/dist/core/router/normalize.js +80 -0
  65. package/dist/core/router/normalize.js.map +1 -0
  66. package/dist/core/safety.d.ts +126 -0
  67. package/dist/core/safety.js +568 -0
  68. package/dist/core/safety.js.map +1 -0
  69. package/dist/core/sense/a11y-resolver.d.ts +73 -0
  70. package/dist/core/sense/a11y-resolver.js +76 -0
  71. package/dist/core/sense/a11y-resolver.js.map +1 -0
  72. package/dist/core/sense/fingerprint.d.ts +41 -0
  73. package/dist/core/sense/fingerprint.js +123 -0
  74. package/dist/core/sense/fingerprint.js.map +1 -0
  75. package/dist/core/sense/rank.d.ts +70 -0
  76. package/dist/core/sense/rank.js +192 -0
  77. package/dist/core/sense/rank.js.map +1 -0
  78. package/dist/core/sense/reactive-check.d.ts +40 -0
  79. package/dist/core/sense/reactive-check.js +48 -0
  80. package/dist/core/sense/reactive-check.js.map +1 -0
  81. package/dist/core/sense/snapshot.d.ts +19 -0
  82. package/dist/core/sense/snapshot.js +100 -0
  83. package/dist/core/sense/snapshot.js.map +1 -0
  84. package/dist/core/sense/types.d.ts +66 -0
  85. package/dist/core/sense/types.js +9 -0
  86. package/dist/core/sense/types.js.map +1 -0
  87. package/dist/core/sense/ui-map-anchors.d.ts +7 -0
  88. package/dist/core/sense/ui-map-anchors.js +24 -0
  89. package/dist/core/sense/ui-map-anchors.js.map +1 -0
  90. package/dist/core/sense/ui-map-elements.d.ts +5 -0
  91. package/dist/core/sense/ui-map-elements.js +33 -0
  92. package/dist/core/sense/ui-map-elements.js.map +1 -0
  93. package/dist/core/sense/ui-map-find.d.ts +56 -0
  94. package/dist/core/sense/ui-map-find.js +153 -0
  95. package/dist/core/sense/ui-map-find.js.map +1 -0
  96. package/dist/core/sense/ui-map-fuse.d.ts +4 -0
  97. package/dist/core/sense/ui-map-fuse.js +44 -0
  98. package/dist/core/sense/ui-map-fuse.js.map +1 -0
  99. package/dist/core/sense/ui-map-geom.d.ts +3 -0
  100. package/dist/core/sense/ui-map-geom.js +16 -0
  101. package/dist/core/sense/ui-map-geom.js.map +1 -0
  102. package/dist/core/sense/ui-map-holder.d.ts +58 -0
  103. package/dist/core/sense/ui-map-holder.js +87 -0
  104. package/dist/core/sense/ui-map-holder.js.map +1 -0
  105. package/dist/core/sense/ui-map-normalize.d.ts +19 -0
  106. package/dist/core/sense/ui-map-normalize.js +65 -0
  107. package/dist/core/sense/ui-map-normalize.js.map +1 -0
  108. package/dist/core/sense/ui-map-render.d.ts +4 -0
  109. package/dist/core/sense/ui-map-render.js +34 -0
  110. package/dist/core/sense/ui-map-render.js.map +1 -0
  111. package/dist/core/sense/ui-map-resolve.d.ts +41 -0
  112. package/dist/core/sense/ui-map-resolve.js +59 -0
  113. package/dist/core/sense/ui-map-resolve.js.map +1 -0
  114. package/dist/core/sense/ui-map-types.d.ts +66 -0
  115. package/dist/core/sense/ui-map-types.js +11 -0
  116. package/dist/core/sense/ui-map-types.js.map +1 -0
  117. package/dist/core/sense/ui-map.d.ts +29 -0
  118. package/dist/core/sense/ui-map.js +113 -0
  119. package/dist/core/sense/ui-map.js.map +1 -0
  120. package/dist/core/verify/assertions.d.ts +132 -0
  121. package/dist/core/verify/assertions.js +284 -0
  122. package/dist/core/verify/assertions.js.map +1 -0
  123. package/dist/index.d.ts +21 -0
  124. package/dist/index.js +24 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/llm/browser-config.d.ts +36 -0
  127. package/dist/llm/browser-config.js +83 -0
  128. package/dist/llm/browser-config.js.map +1 -0
  129. package/dist/llm/client.d.ts +268 -0
  130. package/dist/llm/client.js +1094 -0
  131. package/dist/llm/client.js.map +1 -0
  132. package/dist/llm/config.d.ts +79 -0
  133. package/dist/llm/config.js +375 -0
  134. package/dist/llm/config.js.map +1 -0
  135. package/dist/llm/credentials.d.ts +35 -0
  136. package/dist/llm/credentials.js +491 -0
  137. package/dist/llm/credentials.js.map +1 -0
  138. package/dist/llm/external-creds.d.ts +42 -0
  139. package/dist/llm/external-creds.js +169 -0
  140. package/dist/llm/external-creds.js.map +1 -0
  141. package/dist/llm/providers.d.ts +123 -0
  142. package/dist/llm/providers.js +717 -0
  143. package/dist/llm/providers.js.map +1 -0
  144. package/dist/paths.d.ts +31 -0
  145. package/dist/paths.js +147 -0
  146. package/dist/paths.js.map +1 -0
  147. package/dist/platform/accessibility.d.ts +139 -0
  148. package/dist/platform/accessibility.js +670 -0
  149. package/dist/platform/accessibility.js.map +1 -0
  150. package/dist/platform/cdp-driver.d.ts +318 -0
  151. package/dist/platform/cdp-driver.js +1179 -0
  152. package/dist/platform/cdp-driver.js.map +1 -0
  153. package/dist/platform/index.d.ts +11 -0
  154. package/dist/platform/index.js +69 -0
  155. package/dist/platform/index.js.map +1 -0
  156. package/dist/platform/keys.d.ts +17 -0
  157. package/dist/platform/keys.js +129 -0
  158. package/dist/platform/keys.js.map +1 -0
  159. package/dist/platform/launch-poll.d.ts +101 -0
  160. package/dist/platform/launch-poll.js +177 -0
  161. package/dist/platform/launch-poll.js.map +1 -0
  162. package/dist/platform/linux.d.ts +173 -0
  163. package/dist/platform/linux.js +1253 -0
  164. package/dist/platform/linux.js.map +1 -0
  165. package/dist/platform/macos.d.ts +136 -0
  166. package/dist/platform/macos.js +976 -0
  167. package/dist/platform/macos.js.map +1 -0
  168. package/dist/platform/native-desktop.d.ts +145 -0
  169. package/dist/platform/native-desktop.js +936 -0
  170. package/dist/platform/native-desktop.js.map +1 -0
  171. package/dist/platform/native-helper.d.ts +130 -0
  172. package/dist/platform/native-helper.js +592 -0
  173. package/dist/platform/native-helper.js.map +1 -0
  174. package/dist/platform/ocr-engine.d.ts +78 -0
  175. package/dist/platform/ocr-engine.js +363 -0
  176. package/dist/platform/ocr-engine.js.map +1 -0
  177. package/dist/platform/ps-runner.d.ts +28 -0
  178. package/dist/platform/ps-runner.js +228 -0
  179. package/dist/platform/ps-runner.js.map +1 -0
  180. package/dist/platform/types.d.ts +397 -0
  181. package/dist/platform/types.js +15 -0
  182. package/dist/platform/types.js.map +1 -0
  183. package/dist/platform/uri-handler.d.ts +75 -0
  184. package/dist/platform/uri-handler.js +273 -0
  185. package/dist/platform/uri-handler.js.map +1 -0
  186. package/dist/platform/wayland-backend.d.ts +53 -0
  187. package/dist/platform/wayland-backend.js +348 -0
  188. package/dist/platform/wayland-backend.js.map +1 -0
  189. package/dist/platform/windows.d.ts +232 -0
  190. package/dist/platform/windows.js +1210 -0
  191. package/dist/platform/windows.js.map +1 -0
  192. package/dist/postbuild.d.ts +10 -0
  193. package/dist/postbuild.js +98 -0
  194. package/dist/postbuild.js.map +1 -0
  195. package/dist/schema/snapshot.d.ts +33 -0
  196. package/dist/schema/snapshot.js +90 -0
  197. package/dist/schema/snapshot.js.map +1 -0
  198. package/dist/shortcuts.d.ts +30 -0
  199. package/dist/shortcuts.js +261 -0
  200. package/dist/shortcuts.js.map +1 -0
  201. package/dist/surface/cli.d.ts +7 -0
  202. package/dist/surface/cli.js +1556 -0
  203. package/dist/surface/cli.js.map +1 -0
  204. package/dist/surface/dashboard.d.ts +8 -0
  205. package/dist/surface/dashboard.js +1193 -0
  206. package/dist/surface/dashboard.js.map +1 -0
  207. package/dist/surface/doctor.d.ts +29 -0
  208. package/dist/surface/doctor.js +1514 -0
  209. package/dist/surface/doctor.js.map +1 -0
  210. package/dist/surface/format.d.ts +10 -0
  211. package/dist/surface/format.js +37 -0
  212. package/dist/surface/format.js.map +1 -0
  213. package/dist/surface/http-utility.d.ts +65 -0
  214. package/dist/surface/http-utility.js +336 -0
  215. package/dist/surface/http-utility.js.map +1 -0
  216. package/dist/surface/mcp-server.d.ts +91 -0
  217. package/dist/surface/mcp-server.js +280 -0
  218. package/dist/surface/mcp-server.js.map +1 -0
  219. package/dist/surface/onboarding.d.ts +15 -0
  220. package/dist/surface/onboarding.js +184 -0
  221. package/dist/surface/onboarding.js.map +1 -0
  222. package/dist/surface/pidfile.d.ts +79 -0
  223. package/dist/surface/pidfile.js +263 -0
  224. package/dist/surface/pidfile.js.map +1 -0
  225. package/dist/surface/readiness.d.ts +45 -0
  226. package/dist/surface/readiness.js +230 -0
  227. package/dist/surface/readiness.js.map +1 -0
  228. package/dist/surface/report.d.ts +68 -0
  229. package/dist/surface/report.js +341 -0
  230. package/dist/surface/report.js.map +1 -0
  231. package/dist/surface/skill-register.d.ts +14 -0
  232. package/dist/surface/skill-register.js +150 -0
  233. package/dist/surface/skill-register.js.map +1 -0
  234. package/dist/surface/version.d.ts +6 -0
  235. package/dist/surface/version.js +27 -0
  236. package/dist/surface/version.js.map +1 -0
  237. package/dist/tools/a11y.d.ts +8 -0
  238. package/dist/tools/a11y.js +545 -0
  239. package/dist/tools/a11y.js.map +1 -0
  240. package/dist/tools/a11y_depth.d.ts +19 -0
  241. package/dist/tools/a11y_depth.js +455 -0
  242. package/dist/tools/a11y_depth.js.map +1 -0
  243. package/dist/tools/agent.d.ts +15 -0
  244. package/dist/tools/agent.js +248 -0
  245. package/dist/tools/agent.js.map +1 -0
  246. package/dist/tools/batch.d.ts +46 -0
  247. package/dist/tools/batch.js +230 -0
  248. package/dist/tools/batch.js.map +1 -0
  249. package/dist/tools/cdp.d.ts +8 -0
  250. package/dist/tools/cdp.js +233 -0
  251. package/dist/tools/cdp.js.map +1 -0
  252. package/dist/tools/compact.d.ts +63 -0
  253. package/dist/tools/compact.js +418 -0
  254. package/dist/tools/compact.js.map +1 -0
  255. package/dist/tools/cost-class.d.ts +38 -0
  256. package/dist/tools/cost-class.js +117 -0
  257. package/dist/tools/cost-class.js.map +1 -0
  258. package/dist/tools/desktop.d.ts +9 -0
  259. package/dist/tools/desktop.js +346 -0
  260. package/dist/tools/desktop.js.map +1 -0
  261. package/dist/tools/electron_bridge.d.ts +41 -0
  262. package/dist/tools/electron_bridge.js +261 -0
  263. package/dist/tools/electron_bridge.js.map +1 -0
  264. package/dist/tools/extras.d.ts +22 -0
  265. package/dist/tools/extras.js +942 -0
  266. package/dist/tools/extras.js.map +1 -0
  267. package/dist/tools/favorites.d.ts +13 -0
  268. package/dist/tools/favorites.js +137 -0
  269. package/dist/tools/favorites.js.map +1 -0
  270. package/dist/tools/introspection.d.ts +13 -0
  271. package/dist/tools/introspection.js +55 -0
  272. package/dist/tools/introspection.js.map +1 -0
  273. package/dist/tools/ocr.d.ts +8 -0
  274. package/dist/tools/ocr.js +66 -0
  275. package/dist/tools/ocr.js.map +1 -0
  276. package/dist/tools/orchestration.d.ts +7 -0
  277. package/dist/tools/orchestration.js +377 -0
  278. package/dist/tools/orchestration.js.map +1 -0
  279. package/dist/tools/playbooks/extract-compose.d.ts +22 -0
  280. package/dist/tools/playbooks/extract-compose.js +85 -0
  281. package/dist/tools/playbooks/extract-compose.js.map +1 -0
  282. package/dist/tools/playbooks/find-replace.d.ts +11 -0
  283. package/dist/tools/playbooks/find-replace.js +56 -0
  284. package/dist/tools/playbooks/find-replace.js.map +1 -0
  285. package/dist/tools/playbooks/index.d.ts +63 -0
  286. package/dist/tools/playbooks/index.js +70 -0
  287. package/dist/tools/playbooks/index.js.map +1 -0
  288. package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
  289. package/dist/tools/playbooks/keys-blocklist.js +89 -0
  290. package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
  291. package/dist/tools/registry.d.ts +40 -0
  292. package/dist/tools/registry.js +560 -0
  293. package/dist/tools/registry.js.map +1 -0
  294. package/dist/tools/safety-gate.d.ts +16 -0
  295. package/dist/tools/safety-gate.js +70 -0
  296. package/dist/tools/safety-gate.js.map +1 -0
  297. package/dist/tools/scheduler.d.ts +76 -0
  298. package/dist/tools/scheduler.js +413 -0
  299. package/dist/tools/scheduler.js.map +1 -0
  300. package/dist/tools/shortcuts.d.ts +13 -0
  301. package/dist/tools/shortcuts.js +205 -0
  302. package/dist/tools/shortcuts.js.map +1 -0
  303. package/dist/tools/smart.d.ts +15 -0
  304. package/dist/tools/smart.js +785 -0
  305. package/dist/tools/smart.js.map +1 -0
  306. package/dist/tools/types.d.ts +174 -0
  307. package/dist/tools/types.js +67 -0
  308. package/dist/tools/types.js.map +1 -0
  309. package/dist/tools/window-text.d.ts +15 -0
  310. package/dist/tools/window-text.js +39 -0
  311. package/dist/tools/window-text.js.map +1 -0
  312. package/dist/types.d.ts +122 -0
  313. package/dist/types.js +41 -0
  314. package/dist/types.js.map +1 -0
  315. package/native/Package.swift +38 -0
  316. package/native/README.md +113 -0
  317. package/native/Sources/ClawdCursorHelper/main.swift +602 -0
  318. package/native/Sources/ClawdCursorHost/main.swift +182 -0
  319. package/native/Sources/PermissionCheck/main.swift +53 -0
  320. package/native/Sources/ScreenshotHelper/main.swift +219 -0
  321. package/native/build.sh +139 -0
  322. package/native/entitlements.plist +12 -0
  323. package/package.json +115 -0
  324. package/scripts/banner.ps1 +112 -0
  325. package/scripts/coord-accuracy.ps1 +140 -0
  326. package/scripts/coord-uwp.ps1 +80 -0
  327. package/scripts/edge-glow.ps1 +180 -0
  328. package/scripts/find-element.ps1 +198 -0
  329. package/scripts/get-foreground-window.ps1 +71 -0
  330. package/scripts/get-screen-context.ps1 +183 -0
  331. package/scripts/get-windows.ps1 +66 -0
  332. package/scripts/install-panic-hotkey.ps1 +46 -0
  333. package/scripts/interact-element.ps1 +431 -0
  334. package/scripts/invoke-element.ps1 +314 -0
  335. package/scripts/linux/atspi-bridge.py +356 -0
  336. package/scripts/linux/ocr-recognize.py +154 -0
  337. package/scripts/mac/_window-picker.jxa +163 -0
  338. package/scripts/mac/find-element.jxa +0 -0
  339. package/scripts/mac/find-element.sh +161 -0
  340. package/scripts/mac/focus-window.jxa +284 -0
  341. package/scripts/mac/get-focused-element.jxa +102 -0
  342. package/scripts/mac/get-foreground-window.jxa +173 -0
  343. package/scripts/mac/get-screen-context.jxa +197 -0
  344. package/scripts/mac/get-ui-tree.sh +141 -0
  345. package/scripts/mac/get-windows.jxa +117 -0
  346. package/scripts/mac/interact-element.sh +235 -0
  347. package/scripts/mac/invoke-element.jxa +408 -0
  348. package/scripts/mac/ocr-recognize.swift +124 -0
  349. package/scripts/ocr-recognize.ps1 +102 -0
  350. package/scripts/postinstall-native.js +48 -0
  351. package/scripts/ps-bridge.ps1 +830 -0
  352. package/scripts/smoke-mcp.ps1 +119 -0
  353. package/scripts/sync-version.ts +178 -0
  354. package/scripts/verify-install.js +81 -0
@@ -0,0 +1,1193 @@
1
+ "use strict";
2
+ /**
3
+ * Dashboard — Single-page web dashboard for Clawd Cursor.
4
+ *
5
+ * Exports a mount function that adds GET / to the Express app.
6
+ * All HTML, CSS, and JS are inline — no external files or frameworks.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.mountDashboard = mountDashboard;
10
+ const version_1 = require("./version");
11
+ function mountDashboard(app, getToken) {
12
+ app.get('/', (_req, res) => {
13
+ const token = getToken?.() ?? '';
14
+ if (token) {
15
+ res.cookie('clawdcursor_token', token, {
16
+ httpOnly: true,
17
+ sameSite: 'strict',
18
+ secure: false,
19
+ path: '/',
20
+ });
21
+ }
22
+ res.type('html').send(DASHBOARD_HTML);
23
+ });
24
+ }
25
+ const DASHBOARD_HTML = `<!DOCTYPE html>
26
+ <html lang="en">
27
+ <head>
28
+ <meta charset="UTF-8">
29
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
+ <title>🐾 Clawd Cursor Dashboard</title>
31
+ <style>
32
+ :root {
33
+ --bg-primary: #0d1117;
34
+ --bg-secondary: #161b22;
35
+ --bg-tertiary: #21262d;
36
+ --bg-card: #1c2128;
37
+ --border: #30363d;
38
+ --text-primary: #e6edf3;
39
+ --text-secondary: #8b949e;
40
+ --text-muted: #6e7681;
41
+ --accent: #58a6ff;
42
+ --accent-hover: #79c0ff;
43
+ --green: #3fb950;
44
+ --green-dim: #238636;
45
+ --yellow: #d29922;
46
+ --yellow-dim: #9e6a03;
47
+ --red: #f85149;
48
+ --red-dim: #da3633;
49
+ --red-bg: #490202;
50
+ --font-mono: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', monospace;
51
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
52
+ --radius: 8px;
53
+ --shadow: 0 2px 8px rgba(0,0,0,0.3);
54
+ }
55
+
56
+ * { margin: 0; padding: 0; box-sizing: border-box; }
57
+
58
+ body {
59
+ font-family: var(--font-sans);
60
+ background: var(--bg-primary);
61
+ color: var(--text-primary);
62
+ min-height: 100vh;
63
+ display: flex;
64
+ flex-direction: column;
65
+ }
66
+
67
+ /* Header */
68
+ .header {
69
+ background: var(--bg-secondary);
70
+ border-bottom: 1px solid var(--border);
71
+ padding: 12px 20px;
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 16px;
75
+ flex-wrap: wrap;
76
+ position: sticky;
77
+ top: 0;
78
+ z-index: 100;
79
+ }
80
+
81
+ .header-title {
82
+ font-size: 1.25rem;
83
+ font-weight: 700;
84
+ white-space: nowrap;
85
+ }
86
+
87
+ .header-spacer { flex: 1; }
88
+
89
+ .status-badge {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 8px;
93
+ padding: 6px 14px;
94
+ border-radius: 20px;
95
+ background: var(--bg-tertiary);
96
+ border: 1px solid var(--border);
97
+ font-size: 0.85rem;
98
+ font-weight: 500;
99
+ }
100
+
101
+ .status-dot {
102
+ width: 10px;
103
+ height: 10px;
104
+ border-radius: 50%;
105
+ display: inline-block;
106
+ flex-shrink: 0;
107
+ }
108
+
109
+ .status-dot.idle { background: var(--red); }
110
+ .status-dot.running { background: var(--green); animation: pulse 1.5s ease-in-out infinite; }
111
+ .status-dot.confirming { background: var(--yellow); animation: pulse 2s ease-in-out infinite; }
112
+
113
+ @keyframes pulse {
114
+ 0%, 100% { opacity: 1; transform: scale(1); }
115
+ 50% { opacity: 0.5; transform: scale(1.3); }
116
+ }
117
+
118
+ .conn-indicator {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 5px;
122
+ font-size: 0.75rem;
123
+ color: var(--text-secondary);
124
+ }
125
+
126
+ .conn-dot {
127
+ width: 7px;
128
+ height: 7px;
129
+ border-radius: 50%;
130
+ background: var(--green);
131
+ }
132
+ .conn-dot.disconnected { background: var(--red); }
133
+
134
+ .version-tag {
135
+ font-size: 0.75rem;
136
+ color: var(--text-muted);
137
+ padding: 3px 8px;
138
+ border-radius: 4px;
139
+ background: var(--bg-tertiary);
140
+ border: 1px solid var(--border);
141
+ }
142
+
143
+ .kill-btn {
144
+ background: var(--red-dim);
145
+ color: #fff;
146
+ border: 1px solid var(--red);
147
+ padding: 6px 16px;
148
+ border-radius: 6px;
149
+ font-size: 0.85rem;
150
+ font-weight: 600;
151
+ cursor: pointer;
152
+ transition: all 0.15s;
153
+ }
154
+ .kill-btn:hover { background: var(--red); }
155
+
156
+ /* Tabs */
157
+ .tab-bar {
158
+ display: flex;
159
+ background: var(--bg-secondary);
160
+ border-bottom: 1px solid var(--border);
161
+ }
162
+
163
+ .tab-btn {
164
+ padding: 12px 24px;
165
+ border: none;
166
+ background: none;
167
+ color: var(--text-secondary);
168
+ font-size: 0.95rem;
169
+ font-weight: 500;
170
+ cursor: pointer;
171
+ border-bottom: 2px solid transparent;
172
+ transition: all 0.15s;
173
+ font-family: var(--font-sans);
174
+ }
175
+ .tab-btn:hover { color: var(--text-primary); }
176
+ .tab-btn.active {
177
+ color: var(--accent);
178
+ border-bottom-color: var(--accent);
179
+ }
180
+
181
+ /* Main content */
182
+ .main {
183
+ flex: 1;
184
+ padding: 20px;
185
+ max-width: 1000px;
186
+ margin: 0 auto;
187
+ width: 100%;
188
+ }
189
+
190
+ .tab-content { display: none; }
191
+ .tab-content.active { display: block; }
192
+
193
+ /* Task Tab */
194
+ .task-input-area {
195
+ display: flex;
196
+ gap: 10px;
197
+ margin-bottom: 20px;
198
+ }
199
+
200
+ .task-input {
201
+ flex: 1;
202
+ padding: 14px 16px;
203
+ background: var(--bg-secondary);
204
+ border: 1px solid var(--border);
205
+ border-radius: var(--radius);
206
+ color: var(--text-primary);
207
+ font-size: 1rem;
208
+ font-family: var(--font-sans);
209
+ outline: none;
210
+ transition: border-color 0.15s;
211
+ }
212
+ .task-input:focus { border-color: var(--accent); }
213
+ .task-input::placeholder { color: var(--text-muted); }
214
+
215
+ .send-btn {
216
+ padding: 14px 24px;
217
+ background: var(--green-dim);
218
+ color: #fff;
219
+ border: 1px solid var(--green);
220
+ border-radius: var(--radius);
221
+ font-size: 0.95rem;
222
+ font-weight: 600;
223
+ cursor: pointer;
224
+ transition: all 0.15s;
225
+ white-space: nowrap;
226
+ font-family: var(--font-sans);
227
+ }
228
+ .send-btn:hover { background: var(--green); }
229
+ .send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
230
+
231
+ /* Spinner */
232
+ .spinner {
233
+ display: none;
234
+ text-align: center;
235
+ padding: 20px;
236
+ color: var(--text-secondary);
237
+ }
238
+ .spinner.visible { display: block; }
239
+ .spinner::before {
240
+ content: '';
241
+ display: inline-block;
242
+ width: 24px;
243
+ height: 24px;
244
+ border: 3px solid var(--border);
245
+ border-top-color: var(--accent);
246
+ border-radius: 50%;
247
+ animation: spin 0.8s linear infinite;
248
+ vertical-align: middle;
249
+ margin-right: 10px;
250
+ }
251
+ @keyframes spin { to { transform: rotate(360deg); } }
252
+
253
+ /* Response card */
254
+ .response-card {
255
+ display: none;
256
+ background: var(--bg-card);
257
+ border: 1px solid var(--border);
258
+ border-radius: var(--radius);
259
+ padding: 16px 20px;
260
+ margin-bottom: 20px;
261
+ box-shadow: var(--shadow);
262
+ }
263
+ .response-card.visible { display: block; }
264
+ .response-card .label {
265
+ font-size: 0.8rem;
266
+ color: var(--text-muted);
267
+ text-transform: uppercase;
268
+ letter-spacing: 0.5px;
269
+ margin-bottom: 8px;
270
+ }
271
+ .response-card pre {
272
+ font-family: var(--font-mono);
273
+ font-size: 0.88rem;
274
+ white-space: pre-wrap;
275
+ word-break: break-word;
276
+ color: var(--text-primary);
277
+ line-height: 1.5;
278
+ }
279
+
280
+ /* Confirm banner */
281
+ .confirm-banner {
282
+ display: none;
283
+ background: rgba(210, 153, 34, 0.12);
284
+ border: 1px solid var(--yellow-dim);
285
+ border-radius: var(--radius);
286
+ padding: 16px 20px;
287
+ margin-bottom: 20px;
288
+ align-items: center;
289
+ gap: 12px;
290
+ flex-wrap: wrap;
291
+ }
292
+ .confirm-banner.visible { display: flex; }
293
+ .confirm-banner .msg { flex: 1; color: var(--yellow); font-weight: 500; }
294
+
295
+ .approve-btn, .reject-btn {
296
+ padding: 8px 20px;
297
+ border-radius: 6px;
298
+ font-weight: 600;
299
+ cursor: pointer;
300
+ border: 1px solid;
301
+ font-size: 0.88rem;
302
+ font-family: var(--font-sans);
303
+ }
304
+ .approve-btn { background: var(--green-dim); border-color: var(--green); color: #fff; }
305
+ .approve-btn:hover { background: var(--green); }
306
+ .reject-btn { background: var(--red-dim); border-color: var(--red); color: #fff; }
307
+ .reject-btn:hover { background: var(--red); }
308
+
309
+ /* History */
310
+ .history-title {
311
+ font-size: 0.85rem;
312
+ color: var(--text-muted);
313
+ text-transform: uppercase;
314
+ letter-spacing: 0.5px;
315
+ margin-bottom: 10px;
316
+ padding-bottom: 6px;
317
+ border-bottom: 1px solid var(--border);
318
+ }
319
+
320
+ .history-list {
321
+ list-style: none;
322
+ display: flex;
323
+ flex-direction: column;
324
+ gap: 8px;
325
+ }
326
+
327
+ .history-item {
328
+ background: var(--bg-secondary);
329
+ border: 1px solid var(--border);
330
+ border-radius: 6px;
331
+ padding: 10px 14px;
332
+ font-size: 0.88rem;
333
+ display: flex;
334
+ flex-wrap: wrap;
335
+ align-items: flex-start;
336
+ gap: 6px;
337
+ }
338
+ .history-item .star-btn {
339
+ background: none;
340
+ border: none;
341
+ cursor: pointer;
342
+ font-size: 1.1rem;
343
+ padding: 0 4px;
344
+ line-height: 1;
345
+ flex-shrink: 0;
346
+ transition: transform 0.15s;
347
+ }
348
+ .history-item .star-btn:hover { transform: scale(1.25); }
349
+ .history-item .history-content { flex: 1; min-width: 0; }
350
+ .history-item .task-text { color: var(--accent); font-weight: 500; }
351
+ .history-item .task-time { color: var(--text-muted); font-size: 0.75rem; margin-left: 8px; }
352
+ .history-item .task-result {
353
+ margin-top: 6px;
354
+ font-family: var(--font-mono);
355
+ font-size: 0.8rem;
356
+ color: var(--text-secondary);
357
+ white-space: pre-wrap;
358
+ word-break: break-word;
359
+ }
360
+
361
+ /* Favorites section */
362
+ .favorites-section {
363
+ margin-bottom: 16px;
364
+ display: none;
365
+ }
366
+ .favorites-section.visible { display: block; }
367
+ .favorites-title {
368
+ font-size: 0.85rem;
369
+ color: var(--text-muted);
370
+ text-transform: uppercase;
371
+ letter-spacing: 0.5px;
372
+ margin-bottom: 10px;
373
+ padding-bottom: 6px;
374
+ border-bottom: 1px solid var(--border);
375
+ }
376
+ .favorites-chips {
377
+ display: flex;
378
+ flex-wrap: wrap;
379
+ gap: 8px;
380
+ }
381
+ .fav-chip {
382
+ display: inline-flex;
383
+ align-items: center;
384
+ gap: 6px;
385
+ padding: 8px 14px;
386
+ background: var(--bg-tertiary);
387
+ border: 1px solid var(--border);
388
+ border-radius: 20px;
389
+ color: var(--accent);
390
+ font-size: 0.85rem;
391
+ font-weight: 500;
392
+ cursor: pointer;
393
+ transition: all 0.15s;
394
+ font-family: var(--font-sans);
395
+ max-width: 100%;
396
+ }
397
+ .fav-chip:hover {
398
+ background: var(--accent);
399
+ color: var(--bg-primary);
400
+ border-color: var(--accent);
401
+ }
402
+ .fav-chip .fav-text {
403
+ overflow: hidden;
404
+ text-overflow: ellipsis;
405
+ white-space: nowrap;
406
+ }
407
+ .fav-chip .fav-remove {
408
+ background: none;
409
+ border: none;
410
+ color: var(--text-muted);
411
+ cursor: pointer;
412
+ font-size: 0.9rem;
413
+ padding: 0 2px;
414
+ line-height: 1;
415
+ flex-shrink: 0;
416
+ }
417
+ .fav-chip .fav-remove:hover { color: var(--red); }
418
+ .fav-chip:hover .fav-remove { color: var(--bg-primary); }
419
+ .fav-chip:hover .fav-remove:hover { color: var(--red); }
420
+
421
+ /* Logs Tab */
422
+ .log-area {
423
+ background: #010409;
424
+ border: 1px solid var(--border);
425
+ border-radius: var(--radius);
426
+ padding: 14px;
427
+ font-family: var(--font-mono);
428
+ font-size: 0.82rem;
429
+ line-height: 1.6;
430
+ height: calc(100vh - 200px);
431
+ min-height: 400px;
432
+ overflow-y: auto;
433
+ white-space: pre-wrap;
434
+ word-break: break-word;
435
+ }
436
+
437
+ .log-entry { margin-bottom: 2px; }
438
+ .log-ts { color: var(--text-muted); }
439
+ .log-info { color: var(--text-secondary); }
440
+ .log-success { color: var(--green); }
441
+ .log-error { color: var(--red); }
442
+ .log-warn { color: var(--yellow); }
443
+
444
+ .empty-state {
445
+ text-align: center;
446
+ color: var(--text-muted);
447
+ padding: 60px 20px;
448
+ font-size: 1rem;
449
+ }
450
+
451
+ /* Scheduled tab (v0.9.1) */
452
+ .sched-help {
453
+ background: var(--bg-tertiary);
454
+ border: 1px solid var(--border);
455
+ border-radius: 8px;
456
+ padding: 12px 16px;
457
+ font-size: 0.82rem;
458
+ color: var(--text-muted);
459
+ line-height: 1.55;
460
+ margin-bottom: 18px;
461
+ }
462
+ .sched-help code {
463
+ background: rgba(0,0,0,0.3);
464
+ padding: 1px 6px;
465
+ border-radius: 4px;
466
+ color: var(--accent);
467
+ font-size: 0.85em;
468
+ }
469
+ .sched-form {
470
+ display: grid;
471
+ grid-template-columns: 200px 1fr 180px auto;
472
+ gap: 8px;
473
+ margin-bottom: 20px;
474
+ }
475
+ .sched-input {
476
+ background: var(--bg-tertiary);
477
+ color: var(--text);
478
+ border: 1px solid var(--border);
479
+ border-radius: 8px;
480
+ padding: 10px 12px;
481
+ font-size: 0.92rem;
482
+ font-family: 'JetBrains Mono', monospace;
483
+ }
484
+ .sched-input:focus { outline: none; border-color: var(--accent); }
485
+ .sched-add-btn { padding: 10px 20px; }
486
+ .sched-list-section { }
487
+ .sched-title {
488
+ font-size: 0.75rem;
489
+ color: var(--text-muted);
490
+ text-transform: uppercase;
491
+ letter-spacing: 0.06em;
492
+ margin-bottom: 10px;
493
+ font-weight: 600;
494
+ }
495
+ .sched-list { list-style: none; padding: 0; margin: 0; }
496
+ .sched-item {
497
+ display: grid;
498
+ grid-template-columns: 160px 1fr auto auto auto;
499
+ align-items: center;
500
+ gap: 12px;
501
+ background: var(--bg-tertiary);
502
+ border: 1px solid var(--border);
503
+ border-left: 3px solid var(--accent);
504
+ border-radius: 8px;
505
+ padding: 10px 14px;
506
+ margin-bottom: 8px;
507
+ font-size: 0.88rem;
508
+ }
509
+ .sched-item.disabled { border-left-color: var(--text-muted); opacity: 0.6; }
510
+ .sched-item-cron {
511
+ font-family: 'JetBrains Mono', monospace;
512
+ color: var(--accent);
513
+ font-size: 0.85rem;
514
+ }
515
+ .sched-item-task {
516
+ color: var(--text);
517
+ overflow: hidden;
518
+ text-overflow: ellipsis;
519
+ white-space: nowrap;
520
+ }
521
+ .sched-item-meta {
522
+ font-size: 0.72rem;
523
+ color: var(--text-muted);
524
+ font-family: 'JetBrains Mono', monospace;
525
+ }
526
+ .sched-toggle-btn, .sched-delete-btn {
527
+ background: transparent;
528
+ border: 1px solid var(--border);
529
+ color: var(--text-muted);
530
+ border-radius: 6px;
531
+ padding: 5px 10px;
532
+ cursor: pointer;
533
+ font-size: 0.78rem;
534
+ transition: all 0.15s;
535
+ }
536
+ .sched-toggle-btn:hover { color: var(--accent); border-color: var(--accent); }
537
+ .sched-delete-btn:hover { color: #e74c3c; border-color: #e74c3c; }
538
+
539
+ /* Responsive */
540
+ @media (max-width: 640px) {
541
+ .header { padding: 10px 14px; gap: 10px; }
542
+ .header-title { font-size: 1rem; }
543
+ .main { padding: 14px; }
544
+ .task-input-area { flex-direction: column; }
545
+ .send-btn { width: 100%; }
546
+ .tab-btn { padding: 10px 16px; font-size: 0.88rem; }
547
+ .log-area { height: calc(100vh - 220px); min-height: 300px; }
548
+ }
549
+ </style>
550
+ </head>
551
+ <body>
552
+
553
+ <!-- Header -->
554
+ <div class="header">
555
+ <div class="header-title">🐾 Clawd Cursor Dashboard</div>
556
+ <div class="header-spacer"></div>
557
+ <div class="conn-indicator">
558
+ <span class="conn-dot" id="connDot"></span>
559
+ <span id="connText">Connected</span>
560
+ </div>
561
+ <div class="status-badge">
562
+ <span class="status-dot idle" id="statusDot"></span>
563
+ <span id="statusText">Idle</span>
564
+ </div>
565
+ <span class="version-tag" id="versionTag">v${version_1.VERSION}</span>
566
+ <button class="kill-btn" onclick="killSwitch()">⛔ Kill</button>
567
+ </div>
568
+
569
+ <!-- Tabs -->
570
+ <div class="tab-bar">
571
+ <button class="tab-btn active" onclick="switchTab('task')" id="tabTask">📋 Tasks</button>
572
+ <button class="tab-btn" onclick="switchTab('scheduled')" id="tabScheduled">⏰ Scheduled</button>
573
+ <button class="tab-btn" onclick="switchTab('logs')" id="tabLogs">📜 Logs</button>
574
+ </div>
575
+
576
+ <!-- Main -->
577
+ <div class="main">
578
+
579
+ <!-- Task Tab -->
580
+ <div class="tab-content active" id="panelTask">
581
+
582
+ <!-- Favorites Section -->
583
+ <div class="favorites-section" id="favoritesSection">
584
+ <div class="favorites-title">⭐ Favorites</div>
585
+ <div class="favorites-chips" id="favoritesChips"></div>
586
+ </div>
587
+
588
+ <div class="task-input-area">
589
+ <input type="text" class="task-input" id="taskInput"
590
+ placeholder="Enter a task... e.g. Open Chrome and go to github.com"
591
+ onkeydown="if(event.key==='Enter')sendTask()">
592
+ <button class="send-btn" id="sendBtn" onclick="sendTask()">🐾 Send Task</button>
593
+ </div>
594
+
595
+ <div class="spinner" id="spinner">Processing task...</div>
596
+
597
+ <div class="confirm-banner" id="confirmBanner">
598
+ <span class="msg" id="confirmMsg">⚠️ Action requires confirmation</span>
599
+ <button class="approve-btn" onclick="confirmAction(true)">✅ Approve</button>
600
+ <button class="reject-btn" onclick="confirmAction(false)">❌ Reject</button>
601
+ </div>
602
+
603
+ <div class="response-card" id="responseCard">
604
+ <div class="label">Latest Response</div>
605
+ <pre id="responseText"></pre>
606
+ </div>
607
+
608
+ <div id="historySection" style="display:none">
609
+ <div class="history-title">Task History</div>
610
+ <ul class="history-list" id="historyList"></ul>
611
+ </div>
612
+
613
+ <div class="empty-state" id="emptyTask">
614
+ <p>🐾 No tasks yet. Type a task above and press Send.</p>
615
+ </div>
616
+ </div>
617
+
618
+ <!-- Scheduled Tab (v0.9.1) -->
619
+ <div class="tab-content" id="panelScheduled">
620
+ <div class="sched-help">
621
+ Recurring tasks fire through the same agent pipeline as <code>submit_task</code>.
622
+ If the agent is busy when a tick fires, the tick is skipped (no queue).
623
+ Cron is standard 5-field syntax — <code>0 9 * * 1-5</code> = 9am weekdays.
624
+ </div>
625
+
626
+ <div class="sched-form">
627
+ <input type="text" class="sched-input sched-cron"
628
+ id="schedCron" placeholder='Cron: e.g. "0 9 * * 1-5"'>
629
+ <input type="text" class="sched-input sched-task"
630
+ id="schedTask" placeholder='Task: e.g. "open Outlook and read latest unread"'>
631
+ <input type="text" class="sched-input sched-tz"
632
+ id="schedTz" placeholder='TZ (optional): e.g. America/New_York'>
633
+ <button class="send-btn sched-add-btn" onclick="addSchedule()">+ Add Schedule</button>
634
+ </div>
635
+
636
+ <div class="sched-list-section">
637
+ <div class="sched-title">Active &amp; paused schedules</div>
638
+ <ul class="sched-list" id="schedList"></ul>
639
+ <div class="empty-state" id="emptySched">No schedules yet. Add one above.</div>
640
+ </div>
641
+ </div>
642
+
643
+ <!-- Logs Tab -->
644
+ <div class="tab-content" id="panelLogs">
645
+ <div class="log-area" id="logArea">
646
+ <div class="empty-state" id="emptyLogs">Waiting for logs...</div>
647
+ </div>
648
+ </div>
649
+
650
+ </div>
651
+
652
+ <script>
653
+ (function() {
654
+ function authHeaders(extra) {
655
+ var h = {};
656
+ if (extra) { for (var k in extra) h[k] = extra[k]; }
657
+ return h;
658
+ }
659
+
660
+ // ── MCP-over-HTTP helper (v0.9 PR7.3) ────────────────────────────
661
+ // The daemon mounts a streamable-HTTP MCP transport at /mcp. Every
662
+ // dashboard action is a JSON-RPC tools/call request through this
663
+ // helper. /health and /stop remain plain HTTP — they're operational
664
+ // endpoints, not tools.
665
+ var __mcpRequestId = 1;
666
+ async function mcpCall(name, args) {
667
+ var body = JSON.stringify({
668
+ jsonrpc: '2.0',
669
+ id: __mcpRequestId++,
670
+ method: 'tools/call',
671
+ params: { name: name, arguments: args || {} }
672
+ });
673
+ var res = await fetch('/mcp', {
674
+ method: 'POST',
675
+ headers: authHeaders({
676
+ 'Content-Type': 'application/json',
677
+ 'Accept': 'application/json, text/event-stream'
678
+ }),
679
+ body: body
680
+ });
681
+ if (!res.ok) throw new Error('MCP HTTP ' + res.status);
682
+ var ct = res.headers.get('Content-Type') || '';
683
+ var rpc;
684
+ if (ct.indexOf('text/event-stream') !== -1) {
685
+ // SSE response — parse the first data: chunk as the JSON-RPC reply.
686
+ var raw = await res.text();
687
+ var lines = raw.split(/\\r?\\n/);
688
+ var dataLine = lines.find(function(l) { return l.indexOf('data: ') === 0; });
689
+ if (!dataLine) throw new Error('MCP SSE response missing data line');
690
+ rpc = JSON.parse(dataLine.slice(6));
691
+ } else {
692
+ rpc = await res.json();
693
+ }
694
+ if (rpc.error) throw new Error(rpc.error.message || 'MCP error');
695
+ var result = rpc.result || {};
696
+ if (result.isError) {
697
+ var content = result.content || [];
698
+ var msg = content.map(function(c) { return c.text || ''; }).join(' ').trim();
699
+ throw new Error(msg || 'MCP tool error');
700
+ }
701
+ // Convenience: if the only content piece is text and parses as JSON,
702
+ // return the parsed object so callers don't have to unwrap manually.
703
+ var content = result.content || [];
704
+ var firstText = content.find(function(c) { return c.type === 'text'; });
705
+ if (firstText && firstText.text) {
706
+ try { return JSON.parse(firstText.text); } catch (e) { return firstText.text; }
707
+ }
708
+ return result;
709
+ }
710
+
711
+ // State
712
+ let currentTab = 'task';
713
+ let connected = false;
714
+ let lastLogCount = 0;
715
+ let taskHistory = [];
716
+ let currentStatus = 'idle';
717
+ let favorites = [];
718
+
719
+ // DOM refs
720
+ const statusDot = document.getElementById('statusDot');
721
+ const statusText = document.getElementById('statusText');
722
+ const connDot = document.getElementById('connDot');
723
+ const connText = document.getElementById('connText');
724
+ const versionTag = document.getElementById('versionTag');
725
+ const taskInput = document.getElementById('taskInput');
726
+ const sendBtn = document.getElementById('sendBtn');
727
+ const spinner = document.getElementById('spinner');
728
+ const responseCard = document.getElementById('responseCard');
729
+ const responseText = document.getElementById('responseText');
730
+ const confirmBanner = document.getElementById('confirmBanner');
731
+ const confirmMsg = document.getElementById('confirmMsg');
732
+ const historySection = document.getElementById('historySection');
733
+ const historyList = document.getElementById('historyList');
734
+ const emptyTask = document.getElementById('emptyTask');
735
+ const logArea = document.getElementById('logArea');
736
+ const emptyLogs = document.getElementById('emptyLogs');
737
+ const favoritesSection = document.getElementById('favoritesSection');
738
+ const favoritesChips = document.getElementById('favoritesChips');
739
+
740
+ // Tab switching
741
+ window.switchTab = function(tab) {
742
+ currentTab = tab;
743
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
744
+ document.querySelectorAll('.tab-content').forEach(p => p.classList.remove('active'));
745
+ document.getElementById('tab' + tab.charAt(0).toUpperCase() + tab.slice(1)).classList.add('active');
746
+ document.getElementById('panel' + tab.charAt(0).toUpperCase() + tab.slice(1)).classList.add('active');
747
+ // Refresh the Scheduled tab whenever it's opened.
748
+ if (tab === 'scheduled') loadSchedules();
749
+ };
750
+
751
+ // ── Scheduled tasks (v0.9.1) ──────────────────────────────────────
752
+ // Each handler calls the matching scheduled_task_* MCP tool. Errors are
753
+ // surfaced inline; the panel re-fetches on every mutation so the list
754
+ // stays in sync with the daemon's in-process cron registry.
755
+ async function loadSchedules() {
756
+ var list = document.getElementById('schedList');
757
+ var empty = document.getElementById('emptySched');
758
+ try {
759
+ var res = await mcpCall('scheduled_task_list', {});
760
+ var tasks = (res && res.tasks) || [];
761
+ list.innerHTML = '';
762
+ if (tasks.length === 0) {
763
+ empty.style.display = 'block';
764
+ return;
765
+ }
766
+ empty.style.display = 'none';
767
+ tasks.forEach(function(t) {
768
+ var li = document.createElement('li');
769
+ li.className = 'sched-item' + (t.enabled ? '' : ' disabled');
770
+ var meta = 'runs=' + (t.runCount || 0) + ' skips=' + (t.skipCount || 0);
771
+ if (t.nextRun) meta += ' next=' + new Date(t.nextRun).toLocaleString();
772
+ else if (t.lastError) meta += ' err';
773
+ li.innerHTML =
774
+ '<div class="sched-item-cron">' + escapeHtml(t.cron) + '</div>' +
775
+ '<div class="sched-item-task" title="' + escapeHtml(t.task) + '">' + escapeHtml(t.task) + '</div>' +
776
+ '<div class="sched-item-meta">' + escapeHtml(meta) + '</div>' +
777
+ '<button class="sched-toggle-btn" data-id="' + t.id + '" data-enabled="' + (!t.enabled) + '">' +
778
+ (t.enabled ? '⏸ Pause' : '▶ Resume') + '</button>' +
779
+ '<button class="sched-delete-btn" data-id="' + t.id + '">✕</button>';
780
+ list.appendChild(li);
781
+ });
782
+ // Wire toggle + delete buttons.
783
+ list.querySelectorAll('.sched-toggle-btn').forEach(function(b) {
784
+ b.onclick = function() {
785
+ toggleSchedule(b.getAttribute('data-id'), b.getAttribute('data-enabled') === 'true');
786
+ };
787
+ });
788
+ list.querySelectorAll('.sched-delete-btn').forEach(function(b) {
789
+ b.onclick = function() { deleteSchedule(b.getAttribute('data-id')); };
790
+ });
791
+ } catch (err) {
792
+ list.innerHTML = '<li class="sched-item">Could not load schedules: ' + escapeHtml(String(err && err.message || err)) + '</li>';
793
+ }
794
+ }
795
+
796
+ window.addSchedule = async function() {
797
+ var cronInput = document.getElementById('schedCron');
798
+ var taskInput = document.getElementById('schedTask');
799
+ var tzInput = document.getElementById('schedTz');
800
+ var cron = cronInput.value.trim();
801
+ var task = taskInput.value.trim();
802
+ var tz = tzInput.value.trim();
803
+ if (!cron || !task) {
804
+ alert('Both a cron expression and a task are required.');
805
+ return;
806
+ }
807
+ try {
808
+ var args = { cron: cron, task: task };
809
+ if (tz) args.tz = tz;
810
+ var res = await mcpCall('scheduled_task_create', args);
811
+ if (res && res.ok) {
812
+ cronInput.value = '';
813
+ taskInput.value = '';
814
+ tzInput.value = '';
815
+ loadSchedules();
816
+ } else {
817
+ alert('Could not add schedule: ' + JSON.stringify(res));
818
+ }
819
+ } catch (err) {
820
+ alert('Could not add schedule: ' + (err && err.message || err));
821
+ }
822
+ };
823
+
824
+ async function toggleSchedule(id, enabled) {
825
+ try {
826
+ await mcpCall('scheduled_task_toggle', { id: id, enabled: enabled });
827
+ loadSchedules();
828
+ } catch (err) {
829
+ alert('Toggle failed: ' + (err && err.message || err));
830
+ }
831
+ }
832
+
833
+ async function deleteSchedule(id) {
834
+ if (!confirm('Delete this schedule?')) return;
835
+ try {
836
+ await mcpCall('scheduled_task_delete', { id: id });
837
+ loadSchedules();
838
+ } catch (err) {
839
+ alert('Delete failed: ' + (err && err.message || err));
840
+ }
841
+ }
842
+
843
+ function escapeHtml(s) {
844
+ return String(s || '').replace(/[&<>"']/g, function(c) {
845
+ return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
846
+ });
847
+ }
848
+
849
+ // Status polling
850
+ async function pollStatus() {
851
+ try {
852
+ // v0.9 PR7.3: status now goes through MCP agent_status.
853
+ const data = await mcpCall('agent_status', {});
854
+ setConnected(true);
855
+ updateStatus(data);
856
+ } catch(e) {
857
+ setConnected(false);
858
+ }
859
+ }
860
+
861
+ function setConnected(ok) {
862
+ connected = ok;
863
+ connDot.className = 'conn-dot' + (ok ? '' : ' disconnected');
864
+ connText.textContent = ok ? 'Connected' : 'Disconnected';
865
+ }
866
+
867
+ function updateStatus(data) {
868
+ const st = data.status || 'idle';
869
+ currentStatus = st;
870
+
871
+ // Map agent states to display
872
+ let displayStatus, dotClass;
873
+ if (st === 'idle') {
874
+ displayStatus = 'Idle';
875
+ dotClass = 'idle';
876
+ } else if (st === 'waiting_confirm') {
877
+ displayStatus = 'Confirming';
878
+ dotClass = 'confirming';
879
+ } else {
880
+ displayStatus = 'Running';
881
+ dotClass = 'running';
882
+ }
883
+
884
+ statusDot.className = 'status-dot ' + dotClass;
885
+ statusText.textContent = displayStatus;
886
+
887
+ // Show/hide confirm banner
888
+ if (st === 'waiting_confirm') {
889
+ const desc = data.currentStep || 'An action requires your approval';
890
+ confirmMsg.textContent = '⚠️ ' + desc;
891
+ confirmBanner.classList.add('visible');
892
+ } else {
893
+ confirmBanner.classList.remove('visible');
894
+ }
895
+
896
+ // Show current step info
897
+ if (data.currentTask && (st !== 'idle')) {
898
+ const info = data.currentTask + (data.currentStep ? ' — ' + data.currentStep : '');
899
+ document.title = '⚡ ' + info + ' | Clawd Cursor';
900
+ } else {
901
+ document.title = '🐾 Clawd Cursor Dashboard';
902
+ }
903
+ }
904
+
905
+ // Send task
906
+ window.sendTask = async function() {
907
+ const task = taskInput.value.trim();
908
+ if (!task) return;
909
+
910
+ sendBtn.disabled = true;
911
+ spinner.classList.add('visible');
912
+ responseCard.classList.remove('visible');
913
+ emptyTask.style.display = 'none';
914
+
915
+ try {
916
+ // v0.9 PR7.3: submit_task replaces POST /task.
917
+ const data = await mcpCall('submit_task', { task: task });
918
+ responseCard.classList.add('visible');
919
+ if (data && data.accepted) {
920
+ responseText.textContent = 'Task accepted: ' + task + '\\nWaiting for completion...';
921
+ addHistory(task, 'accepted');
922
+ taskInput.value = '';
923
+ // Poll until completion
924
+ waitForCompletion(task);
925
+ } else {
926
+ responseText.textContent = JSON.stringify(data, null, 2);
927
+ addHistory(task, JSON.stringify(data));
928
+ }
929
+ } catch(e) {
930
+ responseCard.classList.add('visible');
931
+ responseText.textContent = 'Error: ' + e.message;
932
+ addHistory(task, 'Error: ' + e.message);
933
+ }
934
+
935
+ spinner.classList.remove('visible');
936
+ sendBtn.disabled = false;
937
+ };
938
+
939
+ async function waitForCompletion(task) {
940
+ const start = Date.now();
941
+ const maxWait = 300000; // 5 min
942
+ const check = async () => {
943
+ if (Date.now() - start > maxWait) {
944
+ updateHistoryResult(task, 'Timed out after 5 minutes');
945
+ return;
946
+ }
947
+ try {
948
+ // v0.9 PR7.3: agent_status replaces GET /status.
949
+ const data = await mcpCall('agent_status', {});
950
+ if (data && data.status === 'idle') {
951
+ responseText.textContent = 'Task completed: ' + task;
952
+ updateHistoryResult(task, 'Completed');
953
+ return;
954
+ }
955
+ // Update current progress
956
+ if (data && data.currentStep) {
957
+ responseText.textContent = 'Task: ' + task + '\\nStep: ' + data.currentStep +
958
+ '\\nProgress: ' + (data.stepsCompleted || 0) + '/' + (data.stepsTotal || '?');
959
+ }
960
+ } catch(e) { /* ignore */ }
961
+ setTimeout(check, 1500);
962
+ };
963
+ setTimeout(check, 1500);
964
+ }
965
+
966
+ // Confirm/reject — placeholder. The /confirm endpoint was removed in
967
+ // the v0.9 REST→MCP collapse; the human-in-the-loop confirm flow now
968
+ // surfaces through the editor host's tool-approval UI (or in the
969
+ // dashboard's pending-task list, server-side). Left as a no-op visible
970
+ // dismiss so the banner can be cleared from the UI while the new flow
971
+ // is wired up; the LLM-side confirmation is enforced server-side by
972
+ // the safety gate regardless.
973
+ window.confirmAction = async function(approved) {
974
+ try {
975
+ // Server-side: confirmation is decided by the safety-gate module,
976
+ // not by a dashboard POST. This handler only dismisses the banner.
977
+ void approved;
978
+ confirmBanner.classList.remove('visible');
979
+ } catch(e) {
980
+ alert('Failed to send confirmation: ' + e.message);
981
+ }
982
+ };
983
+
984
+ // Kill switch
985
+ window.killSwitch = async function() {
986
+ if (!confirm('Are you sure you want to stop Clawd Cursor?')) return;
987
+ try {
988
+ await fetch('/stop', { method: 'POST', headers: authHeaders() });
989
+ setConnected(false);
990
+ statusDot.className = 'status-dot idle';
991
+ statusText.textContent = 'Stopped';
992
+ alert('Clawd Cursor has been stopped.');
993
+ } catch(e) {
994
+ alert('Failed to stop: ' + e.message);
995
+ }
996
+ };
997
+
998
+ // History
999
+ function addHistory(task, result) {
1000
+ taskHistory.unshift({ task, result, time: new Date().toLocaleTimeString() });
1001
+ renderHistory();
1002
+ }
1003
+
1004
+ function updateHistoryResult(task, result) {
1005
+ const item = taskHistory.find(h => h.task === task);
1006
+ if (item) item.result = result;
1007
+ renderHistory();
1008
+ }
1009
+
1010
+ function renderHistory() {
1011
+ if (taskHistory.length === 0) {
1012
+ historySection.style.display = 'none';
1013
+ return;
1014
+ }
1015
+ historySection.style.display = 'block';
1016
+ historyList.innerHTML = taskHistory.map(function(h, idx) {
1017
+ var isFav = favorites.indexOf(h.task) !== -1;
1018
+ var starIcon = isFav ? '⭐' : '☆';
1019
+ return '<li class="history-item">' +
1020
+ '<button class="star-btn" onclick="toggleStar(' + idx + ')" title="' + (isFav ? 'Unstar' : 'Star') + '">' + starIcon + '</button>' +
1021
+ '<div class="history-content">' +
1022
+ '<span class="task-text">' + escHtml(h.task) + '</span>' +
1023
+ '<span class="task-time">' + escHtml(h.time) + '</span>' +
1024
+ '<div class="task-result">' + escHtml(h.result) + '</div>' +
1025
+ '</div>' +
1026
+ '</li>';
1027
+ }).join('');
1028
+ }
1029
+
1030
+ // Favorites
1031
+ async function loadFavorites() {
1032
+ try {
1033
+ // v0.9 PR7.3: favorites_list replaces GET /favorites.
1034
+ var data = await mcpCall('favorites_list', {});
1035
+ if (Array.isArray(data)) {
1036
+ favorites = data;
1037
+ renderFavorites();
1038
+ renderHistory();
1039
+ }
1040
+ } catch(e) { /* ignore */ }
1041
+ }
1042
+
1043
+ function renderFavorites() {
1044
+ if (favorites.length === 0) {
1045
+ favoritesSection.classList.remove('visible');
1046
+ return;
1047
+ }
1048
+ favoritesSection.classList.add('visible');
1049
+ favoritesChips.innerHTML = favorites.map(function(fav) {
1050
+ return '<div class="fav-chip" title="Click to run: ' + escHtml(fav) + '">' +
1051
+ '<span class="fav-text" onclick="runFavorite(this)">' + escHtml(fav) + '</span>' +
1052
+ '<button class="fav-remove" onclick="event.stopPropagation();removeFavorite(this)" title="Remove from favorites">&times;</button>' +
1053
+ '</div>';
1054
+ }).join('');
1055
+ }
1056
+
1057
+ function looksLikeCredential(text) {
1058
+ // NOTE: this code lives inside an outer JS template literal, so single
1059
+ // backslashes get stripped at parse time (\\s in source → s at runtime).
1060
+ // To preserve a literal backslash for the inner regex, we double-escape.
1061
+ var patterns = [
1062
+ /sk-[a-zA-Z0-9_-]{20,}/, // OpenAI / generic API keys (incl. sk-proj-)
1063
+ /sk-ant-[a-zA-Z0-9-]{20,}/, // Anthropic keys
1064
+ /sk_(?:live|test)_[a-zA-Z0-9]{20,}/, // Stripe secret keys
1065
+ /rk_(?:live|test)_[a-zA-Z0-9]{20,}/, // Stripe restricted keys
1066
+ /gh[posru]_[A-Za-z0-9_]{30,}/, // GitHub PAT (ghp_, gho_, ghu_, ghs_, ghr_)
1067
+ /xox[abprs]-[A-Za-z0-9-]{10,}/, // Slack tokens
1068
+ /AKIA[0-9A-Z]{16}/, // AWS access key id
1069
+ /ya29\\.[A-Za-z0-9_-]{20,}/, // Google OAuth access token
1070
+ /eyJ[A-Za-z0-9_-]+\\.eyJ[A-Za-z0-9_-]+\\./, // JWT (header.payload.…)
1071
+ /password\\s*[:=]\\s*\\S+/i, // password: xxx
1072
+ /secret\\s*[:=]\\s*\\S+/i, // secret: xxx
1073
+ /token\\s*[:=]\\s*\\S+/i, // token: xxx
1074
+ /api[_-]?key\\s*[:=]\\s*\\S+/i, // api_key: xxx
1075
+ /Bearer\\s+[a-zA-Z0-9._-]{20,}/ // Bearer tokens
1076
+ ];
1077
+ for (var i = 0; i < patterns.length; i++) {
1078
+ if (patterns[i].test(text)) return true;
1079
+ }
1080
+ return false;
1081
+ }
1082
+
1083
+ window.toggleStar = async function(idx) {
1084
+ var item = taskHistory[idx];
1085
+ if (!item) return;
1086
+ var isFav = favorites.indexOf(item.task) !== -1;
1087
+
1088
+ // When starring, check for credentials
1089
+ if (!isFav && looksLikeCredential(item.task)) {
1090
+ var msg = '🔒 This task may contain sensitive info (API key, password, or token).\\n\\n' +
1091
+ 'Starred commands are saved locally in .clawdcursor-favorites.json on your machine — ' +
1092
+ 'never sent over the network. Your credentials stay secure on your device.\\n\\n' +
1093
+ 'Star this command anyway?';
1094
+ if (!confirm(msg)) return;
1095
+ }
1096
+
1097
+ try {
1098
+ // v0.9 PR7.3: favorites_add / favorites_remove replace POST/DELETE /favorites.
1099
+ var data = await mcpCall(
1100
+ isFav ? 'favorites_remove' : 'favorites_add',
1101
+ { task: item.task }
1102
+ );
1103
+ favorites = (data && data.favorites) || [];
1104
+ renderFavorites();
1105
+ renderHistory();
1106
+ } catch(e) {
1107
+ console.error('Failed to toggle star:', e);
1108
+ }
1109
+ };
1110
+
1111
+ window.runFavorite = function(el) {
1112
+ var text = el.textContent;
1113
+ taskInput.value = text;
1114
+ sendTask();
1115
+ };
1116
+
1117
+ window.removeFavorite = async function(el) {
1118
+ var chip = el.parentElement;
1119
+ var text = chip.querySelector('.fav-text').textContent;
1120
+ try {
1121
+ // v0.9 PR7.3: favorites_remove replaces DELETE /favorites.
1122
+ var data = await mcpCall('favorites_remove', { task: text });
1123
+ favorites = (data && data.favorites) || [];
1124
+ renderFavorites();
1125
+ renderHistory();
1126
+ } catch(e) {
1127
+ console.error('Failed to remove favorite:', e);
1128
+ }
1129
+ };
1130
+
1131
+ // Logs polling
1132
+ async function pollLogs() {
1133
+ try {
1134
+ // v0.9 PR7.3: logs_recent replaces GET /logs.
1135
+ const logs = await mcpCall('logs_recent', {});
1136
+ if (!Array.isArray(logs) || logs.length === 0) return;
1137
+
1138
+ if (logs.length !== lastLogCount) {
1139
+ lastLogCount = logs.length;
1140
+ renderLogs(logs);
1141
+ }
1142
+ } catch(e) { /* ignore */ }
1143
+ }
1144
+
1145
+ function renderLogs(logs) {
1146
+ if (!logs || logs.length === 0) return;
1147
+ emptyLogs.style.display = 'none';
1148
+
1149
+ const html = logs.map(function(entry) {
1150
+ const ts = entry.timestamp ? new Date(entry.timestamp).toLocaleTimeString() : '';
1151
+ const msg = entry.message || JSON.stringify(entry);
1152
+ const level = entry.level || 'info';
1153
+
1154
+ let cls = 'log-info';
1155
+ if (level === 'error') cls = 'log-error';
1156
+ else if (level === 'warn' || level === 'warning') cls = 'log-warn';
1157
+ else if (level === 'success') cls = 'log-success';
1158
+
1159
+ return '<div class="log-entry"><span class="log-ts">[' + escHtml(ts) + ']</span> ' +
1160
+ '<span class="' + cls + '">' + escHtml(msg) + '</span></div>';
1161
+ }).join('');
1162
+
1163
+ logArea.innerHTML = html;
1164
+
1165
+ // Auto-scroll
1166
+ logArea.scrollTop = logArea.scrollHeight;
1167
+ }
1168
+
1169
+ function escHtml(str) {
1170
+ if (!str) return '';
1171
+ return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
1172
+ }
1173
+
1174
+ // Fetch version from health endpoint
1175
+ async function fetchVersion() {
1176
+ try {
1177
+ const res = await fetch('/health');
1178
+ const data = await res.json();
1179
+ if (data.version) versionTag.textContent = 'v' + data.version;
1180
+ } catch(e) { /* ignore */ }
1181
+ }
1182
+
1183
+ // Init
1184
+ fetchVersion();
1185
+ loadFavorites();
1186
+ pollStatus();
1187
+ setInterval(pollStatus, 2000);
1188
+ setInterval(pollLogs, 2000);
1189
+ })();
1190
+ </script>
1191
+ </body>
1192
+ </html>`;
1193
+ //# sourceMappingURL=dashboard.js.map