tanuki-telemetry 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/Dockerfile +22 -0
  2. package/bin/tanuki.mjs +251 -0
  3. package/frontend/eslint.config.js +23 -0
  4. package/frontend/index.html +13 -0
  5. package/frontend/package.json +39 -0
  6. package/frontend/src/App.tsx +232 -0
  7. package/frontend/src/assets/hero.png +0 -0
  8. package/frontend/src/assets/react.svg +1 -0
  9. package/frontend/src/assets/vite.svg +1 -0
  10. package/frontend/src/components/ArtifactsPanel.tsx +429 -0
  11. package/frontend/src/components/ChildStreams.tsx +176 -0
  12. package/frontend/src/components/CoordinatorPage.tsx +317 -0
  13. package/frontend/src/components/Header.tsx +108 -0
  14. package/frontend/src/components/InsightsPanel.tsx +142 -0
  15. package/frontend/src/components/IterationsTable.tsx +98 -0
  16. package/frontend/src/components/KnowledgePage.tsx +308 -0
  17. package/frontend/src/components/LoginPage.tsx +55 -0
  18. package/frontend/src/components/PlanProgress.tsx +163 -0
  19. package/frontend/src/components/QualityReport.tsx +276 -0
  20. package/frontend/src/components/ScreenshotUpload.tsx +117 -0
  21. package/frontend/src/components/ScreenshotsGrid.tsx +266 -0
  22. package/frontend/src/components/SessionDetail.tsx +265 -0
  23. package/frontend/src/components/SessionList.tsx +234 -0
  24. package/frontend/src/components/SettingsPage.tsx +213 -0
  25. package/frontend/src/components/StreamComms.tsx +228 -0
  26. package/frontend/src/components/TanukiLogo.tsx +16 -0
  27. package/frontend/src/components/Timeline.tsx +416 -0
  28. package/frontend/src/components/WalkthroughPage.tsx +458 -0
  29. package/frontend/src/hooks/useApi.ts +81 -0
  30. package/frontend/src/hooks/useAuth.ts +54 -0
  31. package/frontend/src/hooks/useKnowledge.ts +33 -0
  32. package/frontend/src/hooks/useWebSocket.ts +95 -0
  33. package/frontend/src/index.css +66 -0
  34. package/frontend/src/lib/api.ts +15 -0
  35. package/frontend/src/lib/utils.ts +58 -0
  36. package/frontend/src/main.tsx +10 -0
  37. package/frontend/src/types.ts +181 -0
  38. package/frontend/tsconfig.app.json +32 -0
  39. package/frontend/tsconfig.json +7 -0
  40. package/frontend/vite.config.ts +25 -0
  41. package/install.sh +87 -0
  42. package/package.json +63 -0
  43. package/src/api-keys.ts +97 -0
  44. package/src/auth.ts +165 -0
  45. package/src/coordinator.ts +136 -0
  46. package/src/dashboard-server.ts +5 -0
  47. package/src/dashboard.ts +826 -0
  48. package/src/db.ts +1009 -0
  49. package/src/index.ts +20 -0
  50. package/src/middleware.ts +76 -0
  51. package/src/tools.ts +864 -0
  52. package/src/types-shim.d.ts +18 -0
  53. package/src/types.ts +171 -0
  54. package/tsconfig.json +19 -0
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { getDb } from "./db.js";
4
+ import { registerTools } from "./tools.js";
5
+
6
+ // Initialize database on startup
7
+ getDb();
8
+
9
+ const server = new McpServer({
10
+ name: "telemetry-mcp",
11
+ version: "1.0.0",
12
+ });
13
+
14
+ // Register all tools — stdio is always local, use DEFAULT_USER_EMAIL or fallback
15
+ const localUserEmail = process.env.DEFAULT_USER_EMAIL || "local@localhost";
16
+ registerTools(server, localUserEmail);
17
+
18
+ // Stdio transport — used when spawned by Claude Code locally
19
+ const transport = new StdioServerTransport();
20
+ await server.connect(transport);
@@ -0,0 +1,76 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ import { validateApiKey } from "./api-keys.js";
3
+
4
+ export const AUTH_ENABLED = !!process.env.GOOGLE_CLIENT_ID;
5
+
6
+ export interface AuthenticatedRequest extends Request {
7
+ apiKeyUserId?: string;
8
+ apiKeyEmail?: string;
9
+ }
10
+
11
+ /**
12
+ * Protects dashboard API routes — checks for an active session.
13
+ * Bypassed when AUTH_ENABLED is false (local dev).
14
+ */
15
+ export function dashboardAuthMiddleware(req: Request, res: Response, next: NextFunction): void {
16
+ if (!AUTH_ENABLED) {
17
+ next();
18
+ return;
19
+ }
20
+
21
+ if (req.isAuthenticated()) {
22
+ next();
23
+ return;
24
+ }
25
+
26
+ res.status(401).json({ error: "Not authenticated" });
27
+ }
28
+
29
+ /**
30
+ * Protects MCP SSE routes — checks for API key in Authorization header.
31
+ * Bypassed when AUTH_ENABLED is false (local dev).
32
+ */
33
+ export function apiKeyMiddleware(req: Request, res: Response, next: NextFunction): void {
34
+ if (!AUTH_ENABLED) {
35
+ next();
36
+ return;
37
+ }
38
+
39
+ const authHeader = req.headers.authorization;
40
+ if (!authHeader?.startsWith("Bearer tlm_")) {
41
+ res.status(401).json({ error: "Missing or invalid API key. Expected: Authorization: Bearer tlm_..." });
42
+ return;
43
+ }
44
+
45
+ const rawKey = authHeader.slice(7); // Remove "Bearer "
46
+ const result = validateApiKey(rawKey);
47
+ if (!result) {
48
+ res.status(401).json({ error: "Invalid API key" });
49
+ return;
50
+ }
51
+
52
+ // Attach user info to request for downstream use
53
+ (req as AuthenticatedRequest).apiKeyUserId = result.user_id;
54
+ (req as AuthenticatedRequest).apiKeyEmail = result.email;
55
+ next();
56
+ }
57
+
58
+ /**
59
+ * Extract user email from an MCP request (set by apiKeyMiddleware).
60
+ * Returns the email or a local fallback.
61
+ */
62
+ export function getUserEmailFromRequest(req: Request): string {
63
+ const email = (req as AuthenticatedRequest).apiKeyEmail;
64
+ if (email) return email;
65
+ // Local dev fallback — use system user
66
+ return process.env.DEFAULT_USER_EMAIL || "local@localhost";
67
+ }
68
+
69
+ /**
70
+ * Parse session from WebSocket upgrade request.
71
+ * Returns the user from the session cookie, or null if auth is disabled/no session.
72
+ */
73
+ export function wsAuthCheck(req: Request): boolean {
74
+ if (!AUTH_ENABLED) return true;
75
+ return req.isAuthenticated?.() ?? false;
76
+ }