@castlekit/castle 0.1.6 → 0.3.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 (60) hide show
  1. package/drizzle.config.ts +7 -0
  2. package/next.config.ts +1 -0
  3. package/package.json +20 -3
  4. package/src/app/api/avatars/[id]/route.ts +57 -7
  5. package/src/app/api/openclaw/agents/status/route.ts +55 -0
  6. package/src/app/api/openclaw/chat/attachments/route.ts +230 -0
  7. package/src/app/api/openclaw/chat/channels/route.ts +214 -0
  8. package/src/app/api/openclaw/chat/route.ts +272 -0
  9. package/src/app/api/openclaw/chat/search/route.ts +149 -0
  10. package/src/app/api/openclaw/chat/storage/route.ts +75 -0
  11. package/src/app/api/openclaw/logs/route.ts +17 -3
  12. package/src/app/api/openclaw/restart/route.ts +6 -1
  13. package/src/app/api/openclaw/session/status/route.ts +42 -0
  14. package/src/app/api/settings/avatar/route.ts +190 -0
  15. package/src/app/api/settings/route.ts +88 -0
  16. package/src/app/chat/[channelId]/error-boundary.tsx +64 -0
  17. package/src/app/chat/[channelId]/page.tsx +305 -0
  18. package/src/app/chat/layout.tsx +96 -0
  19. package/src/app/chat/page.tsx +52 -0
  20. package/src/app/globals.css +89 -2
  21. package/src/app/layout.tsx +7 -1
  22. package/src/app/page.tsx +49 -17
  23. package/src/app/settings/page.tsx +300 -0
  24. package/src/components/chat/agent-mention-popup.tsx +89 -0
  25. package/src/components/chat/archived-channels.tsx +190 -0
  26. package/src/components/chat/channel-list.tsx +140 -0
  27. package/src/components/chat/chat-input.tsx +310 -0
  28. package/src/components/chat/create-channel-dialog.tsx +171 -0
  29. package/src/components/chat/markdown-content.tsx +205 -0
  30. package/src/components/chat/message-bubble.tsx +152 -0
  31. package/src/components/chat/message-list.tsx +508 -0
  32. package/src/components/chat/message-queue.tsx +68 -0
  33. package/src/components/chat/session-divider.tsx +61 -0
  34. package/src/components/chat/session-stats-panel.tsx +139 -0
  35. package/src/components/chat/storage-indicator.tsx +76 -0
  36. package/src/components/layout/sidebar.tsx +126 -45
  37. package/src/components/layout/user-menu.tsx +29 -4
  38. package/src/components/providers/presence-provider.tsx +8 -0
  39. package/src/components/providers/search-provider.tsx +81 -0
  40. package/src/components/search/search-dialog.tsx +269 -0
  41. package/src/components/ui/avatar.tsx +11 -9
  42. package/src/components/ui/dialog.tsx +10 -4
  43. package/src/components/ui/tooltip.tsx +25 -8
  44. package/src/components/ui/twemoji-text.tsx +37 -0
  45. package/src/lib/api-security.ts +125 -0
  46. package/src/lib/date-utils.ts +79 -0
  47. package/src/lib/db/__tests__/queries.test.ts +318 -0
  48. package/src/lib/db/index.ts +642 -0
  49. package/src/lib/db/queries.ts +1017 -0
  50. package/src/lib/db/schema.ts +160 -0
  51. package/src/lib/hooks/use-agent-status.ts +251 -0
  52. package/src/lib/hooks/use-chat.ts +775 -0
  53. package/src/lib/hooks/use-openclaw.ts +105 -70
  54. package/src/lib/hooks/use-search.ts +113 -0
  55. package/src/lib/hooks/use-session-stats.ts +57 -0
  56. package/src/lib/hooks/use-user-settings.ts +46 -0
  57. package/src/lib/types/chat.ts +186 -0
  58. package/src/lib/types/search.ts +60 -0
  59. package/src/middleware.ts +52 -0
  60. package/vitest.config.ts +13 -0
@@ -0,0 +1,60 @@
1
+ // ============================================================================
2
+ // Universal Search — Type System
3
+ // ============================================================================
4
+ //
5
+ // Discriminated union so every result carries its type and the dialog can
6
+ // render + navigate each type differently. Adding a new searchable content
7
+ // type later requires only:
8
+ //
9
+ // 1. Add a new interface extending SearchResultBase
10
+ // 2. Add it to the SearchResult union
11
+ // 3. Add a renderer in the search dialog
12
+ // 4. Add a search function in the API
13
+ //
14
+ // No changes to the dialog, hook, provider, keyboard shortcuts, or routing.
15
+
16
+ // --- Content type registry ---
17
+
18
+ export type SearchResultType = "message" | "task" | "note" | "project";
19
+ // ^ Extend this union as new content types are added
20
+
21
+ // --- Base result shape (shared fields) ---
22
+
23
+ export interface SearchResultBase {
24
+ id: string;
25
+ type: SearchResultType;
26
+ title: string; // Primary display text
27
+ subtitle?: string; // Secondary line (sender, project name, etc.)
28
+ snippet: string; // Content excerpt with match context
29
+ timestamp: number; // For sorting / display
30
+ href: string; // Where to navigate on click
31
+ }
32
+
33
+ // --- Content-specific result types ---
34
+
35
+ export interface MessageSearchResult extends SearchResultBase {
36
+ type: "message";
37
+ channelId: string;
38
+ channelName: string;
39
+ messageId: string;
40
+ senderType: "user" | "agent";
41
+ senderName: string;
42
+ archived?: boolean;
43
+ }
44
+
45
+ // Future examples (not implemented in v1):
46
+ // export interface TaskSearchResult extends SearchResultBase {
47
+ // type: "task";
48
+ // projectId: string;
49
+ // status: "todo" | "in_progress" | "done";
50
+ // }
51
+ //
52
+ // export interface NoteSearchResult extends SearchResultBase {
53
+ // type: "note";
54
+ // notebookId: string;
55
+ // }
56
+
57
+ // --- Discriminated union ---
58
+
59
+ export type SearchResult = MessageSearchResult;
60
+ // ^ Becomes: MessageSearchResult | TaskSearchResult | NoteSearchResult | ...
@@ -0,0 +1,52 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+
3
+ /**
4
+ * Next.js Middleware — applies security checks to all API routes.
5
+ *
6
+ * In production, rejects requests from non-localhost IPs.
7
+ * Castle is a local-first app and should never be exposed to the internet.
8
+ */
9
+
10
+ const LOCALHOST_HOSTS = new Set([
11
+ "localhost",
12
+ "127.0.0.1",
13
+ "::1",
14
+ "::ffff:127.0.0.1",
15
+ ]);
16
+
17
+ export function middleware(request: NextRequest) {
18
+ // Only enforce in production — dev is always allowed
19
+ if (process.env.NODE_ENV === "development") {
20
+ return NextResponse.next();
21
+ }
22
+
23
+ // Check if the request originates from localhost
24
+ const host = request.headers.get("host") || "";
25
+ const hostname = host.split(":")[0];
26
+
27
+ const forwarded = request.headers.get("x-forwarded-for");
28
+ const clientIp = forwarded?.split(",")[0].trim();
29
+
30
+ // If x-forwarded-for is present and it's not localhost, reject
31
+ if (clientIp && !LOCALHOST_HOSTS.has(clientIp)) {
32
+ return NextResponse.json(
33
+ { error: "Forbidden — Castle is only accessible from localhost" },
34
+ { status: 403 }
35
+ );
36
+ }
37
+
38
+ // If the host header isn't a localhost variant, reject
39
+ if (hostname && !LOCALHOST_HOSTS.has(hostname)) {
40
+ return NextResponse.json(
41
+ { error: "Forbidden — Castle is only accessible from localhost" },
42
+ { status: 403 }
43
+ );
44
+ }
45
+
46
+ return NextResponse.next();
47
+ }
48
+
49
+ // Only run middleware on API routes
50
+ export const config = {
51
+ matcher: "/api/:path*",
52
+ };
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "vitest/config";
2
+ import { resolve } from "path";
3
+
4
+ export default defineConfig({
5
+ test: {
6
+ globals: true,
7
+ environment: "node",
8
+ include: ["src/**/*.test.ts", "src/**/*.test.tsx"],
9
+ alias: {
10
+ "@": resolve(__dirname, "src"),
11
+ },
12
+ },
13
+ });