gigaclaw 1.4.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 (249) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +237 -0
  3. package/api/CLAUDE.md +19 -0
  4. package/api/index.js +265 -0
  5. package/bin/cli.js +823 -0
  6. package/bin/local.sh +85 -0
  7. package/bin/postinstall.js +63 -0
  8. package/config/index.js +26 -0
  9. package/config/instrumentation.js +62 -0
  10. package/drizzle/0000_initial.sql +52 -0
  11. package/drizzle/0001_nostalgic_sersi.sql +11 -0
  12. package/drizzle/0002_black_daimon_hellstrom.sql +19 -0
  13. package/drizzle/0003_rename_code_workspaces.sql +5 -0
  14. package/drizzle/meta/0000_snapshot.json +321 -0
  15. package/drizzle/meta/0001_snapshot.json +390 -0
  16. package/drizzle/meta/0002_snapshot.json +411 -0
  17. package/drizzle/meta/0003_snapshot.json +419 -0
  18. package/drizzle/meta/_journal.json +34 -0
  19. package/lib/actions.js +44 -0
  20. package/lib/ai/agent.js +86 -0
  21. package/lib/ai/index.js +342 -0
  22. package/lib/ai/model.js +180 -0
  23. package/lib/ai/tools.js +269 -0
  24. package/lib/ai/web-search.js +42 -0
  25. package/lib/auth/actions.js +28 -0
  26. package/lib/auth/config.js +27 -0
  27. package/lib/auth/edge-config.js +27 -0
  28. package/lib/auth/index.js +27 -0
  29. package/lib/auth/middleware.js +62 -0
  30. package/lib/channels/base.js +56 -0
  31. package/lib/channels/index.js +15 -0
  32. package/lib/channels/telegram.js +148 -0
  33. package/lib/chat/actions.js +579 -0
  34. package/lib/chat/api.js +140 -0
  35. package/lib/chat/components/app-sidebar.js +213 -0
  36. package/lib/chat/components/app-sidebar.jsx +279 -0
  37. package/lib/chat/components/chat-header.js +192 -0
  38. package/lib/chat/components/chat-header.jsx +223 -0
  39. package/lib/chat/components/chat-input.js +236 -0
  40. package/lib/chat/components/chat-input.jsx +249 -0
  41. package/lib/chat/components/chat-nav-context.js +11 -0
  42. package/lib/chat/components/chat-nav-context.jsx +11 -0
  43. package/lib/chat/components/chat-page.js +99 -0
  44. package/lib/chat/components/chat-page.jsx +121 -0
  45. package/lib/chat/components/chat.js +153 -0
  46. package/lib/chat/components/chat.jsx +199 -0
  47. package/lib/chat/components/chats-page.js +367 -0
  48. package/lib/chat/components/chats-page.jsx +394 -0
  49. package/lib/chat/components/code-mode-toggle.js +132 -0
  50. package/lib/chat/components/code-mode-toggle.jsx +163 -0
  51. package/lib/chat/components/crons-page.js +172 -0
  52. package/lib/chat/components/crons-page.jsx +244 -0
  53. package/lib/chat/components/greeting.js +11 -0
  54. package/lib/chat/components/greeting.jsx +16 -0
  55. package/lib/chat/components/icons.js +805 -0
  56. package/lib/chat/components/icons.jsx +751 -0
  57. package/lib/chat/components/index.js +20 -0
  58. package/lib/chat/components/message.js +363 -0
  59. package/lib/chat/components/message.jsx +422 -0
  60. package/lib/chat/components/messages.js +65 -0
  61. package/lib/chat/components/messages.jsx +74 -0
  62. package/lib/chat/components/notifications-page.js +56 -0
  63. package/lib/chat/components/notifications-page.jsx +87 -0
  64. package/lib/chat/components/page-layout.js +21 -0
  65. package/lib/chat/components/page-layout.jsx +28 -0
  66. package/lib/chat/components/pull-requests-page.js +103 -0
  67. package/lib/chat/components/pull-requests-page.jsx +113 -0
  68. package/lib/chat/components/settings-layout.js +39 -0
  69. package/lib/chat/components/settings-layout.jsx +53 -0
  70. package/lib/chat/components/settings-secrets-page.js +216 -0
  71. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  72. package/lib/chat/components/sidebar-history-item.js +138 -0
  73. package/lib/chat/components/sidebar-history-item.jsx +119 -0
  74. package/lib/chat/components/sidebar-history.js +167 -0
  75. package/lib/chat/components/sidebar-history.jsx +220 -0
  76. package/lib/chat/components/sidebar-user-nav.js +61 -0
  77. package/lib/chat/components/sidebar-user-nav.jsx +77 -0
  78. package/lib/chat/components/swarm-page.js +157 -0
  79. package/lib/chat/components/swarm-page.jsx +210 -0
  80. package/lib/chat/components/tool-call.js +89 -0
  81. package/lib/chat/components/tool-call.jsx +107 -0
  82. package/lib/chat/components/triggers-page.js +153 -0
  83. package/lib/chat/components/triggers-page.jsx +221 -0
  84. package/lib/chat/components/ui/combobox.js +98 -0
  85. package/lib/chat/components/ui/combobox.jsx +114 -0
  86. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  87. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  88. package/lib/chat/components/ui/dropdown-menu.js +194 -0
  89. package/lib/chat/components/ui/dropdown-menu.jsx +215 -0
  90. package/lib/chat/components/ui/rename-dialog.js +78 -0
  91. package/lib/chat/components/ui/rename-dialog.jsx +74 -0
  92. package/lib/chat/components/ui/scroll-area.js +13 -0
  93. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  94. package/lib/chat/components/ui/separator.js +21 -0
  95. package/lib/chat/components/ui/separator.jsx +18 -0
  96. package/lib/chat/components/ui/sheet.js +75 -0
  97. package/lib/chat/components/ui/sheet.jsx +95 -0
  98. package/lib/chat/components/ui/sidebar.js +228 -0
  99. package/lib/chat/components/ui/sidebar.jsx +246 -0
  100. package/lib/chat/components/ui/tooltip.js +56 -0
  101. package/lib/chat/components/ui/tooltip.jsx +66 -0
  102. package/lib/chat/components/upgrade-dialog.js +151 -0
  103. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  104. package/lib/chat/utils.js +11 -0
  105. package/lib/code/actions.js +153 -0
  106. package/lib/code/code-page.js +22 -0
  107. package/lib/code/code-page.jsx +25 -0
  108. package/lib/code/index.js +1 -0
  109. package/lib/code/terminal-view.js +201 -0
  110. package/lib/code/terminal-view.jsx +224 -0
  111. package/lib/code/ws-proxy.js +80 -0
  112. package/lib/cron.js +246 -0
  113. package/lib/db/api-keys.js +163 -0
  114. package/lib/db/chats.js +168 -0
  115. package/lib/db/code-workspaces.js +110 -0
  116. package/lib/db/index.js +52 -0
  117. package/lib/db/notifications.js +99 -0
  118. package/lib/db/schema.js +66 -0
  119. package/lib/db/update-check.js +96 -0
  120. package/lib/db/users.js +89 -0
  121. package/lib/paths.js +42 -0
  122. package/lib/tools/create-job.js +97 -0
  123. package/lib/tools/docker.js +146 -0
  124. package/lib/tools/github.js +271 -0
  125. package/lib/tools/openai.js +35 -0
  126. package/lib/tools/telegram.js +292 -0
  127. package/lib/triggers.js +104 -0
  128. package/lib/utils/render-md.js +111 -0
  129. package/package.json +118 -0
  130. package/setup/lib/auth.mjs +81 -0
  131. package/setup/lib/env.mjs +21 -0
  132. package/setup/lib/fs-utils.mjs +20 -0
  133. package/setup/lib/github.mjs +149 -0
  134. package/setup/lib/prerequisites.mjs +155 -0
  135. package/setup/lib/prompts.mjs +267 -0
  136. package/setup/lib/providers.mjs +105 -0
  137. package/setup/lib/sync.mjs +125 -0
  138. package/setup/lib/targets.mjs +45 -0
  139. package/setup/lib/telegram-verify.mjs +63 -0
  140. package/setup/lib/telegram.mjs +76 -0
  141. package/setup/setup-cloud.mjs +833 -0
  142. package/setup/setup-local.mjs +377 -0
  143. package/setup/setup-telegram.mjs +265 -0
  144. package/setup/setup.mjs +87 -0
  145. package/templates/.dockerignore +5 -0
  146. package/templates/.env.example +104 -0
  147. package/templates/.github/workflows/auto-merge.yml +117 -0
  148. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  149. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  150. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  151. package/templates/.github/workflows/run-job.yml +89 -0
  152. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  153. package/templates/.gitignore.template +45 -0
  154. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  155. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  156. package/templates/CLAUDE.md +29 -0
  157. package/templates/CLAUDE.md.template +308 -0
  158. package/templates/app/api/[...gigaclaw]/route.js +1 -0
  159. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  160. package/templates/app/chat/[chatId]/page.js +9 -0
  161. package/templates/app/chats/page.js +7 -0
  162. package/templates/app/code/[codeWorkspaceId]/page.js +9 -0
  163. package/templates/app/components/ascii-logo.jsx +12 -0
  164. package/templates/app/components/login-form.jsx +92 -0
  165. package/templates/app/components/setup-form.jsx +82 -0
  166. package/templates/app/components/theme-provider.jsx +11 -0
  167. package/templates/app/components/theme-toggle.jsx +38 -0
  168. package/templates/app/components/ui/button.jsx +21 -0
  169. package/templates/app/components/ui/card.jsx +23 -0
  170. package/templates/app/components/ui/input.jsx +10 -0
  171. package/templates/app/components/ui/label.jsx +10 -0
  172. package/templates/app/crons/page.js +5 -0
  173. package/templates/app/globals.css +90 -0
  174. package/templates/app/layout.js +33 -0
  175. package/templates/app/login/page.js +15 -0
  176. package/templates/app/notifications/page.js +7 -0
  177. package/templates/app/page.js +7 -0
  178. package/templates/app/pull-requests/page.js +7 -0
  179. package/templates/app/settings/crons/page.js +5 -0
  180. package/templates/app/settings/layout.js +7 -0
  181. package/templates/app/settings/page.js +5 -0
  182. package/templates/app/settings/secrets/page.js +5 -0
  183. package/templates/app/settings/triggers/page.js +5 -0
  184. package/templates/app/stream/chat/route.js +1 -0
  185. package/templates/app/swarm/page.js +7 -0
  186. package/templates/app/triggers/page.js +5 -0
  187. package/templates/config/CODE_PLANNING.md +14 -0
  188. package/templates/config/CRONS.json +56 -0
  189. package/templates/config/HEARTBEAT.md +3 -0
  190. package/templates/config/JOB_AGENT.md +30 -0
  191. package/templates/config/JOB_PLANNING.md +240 -0
  192. package/templates/config/JOB_SUMMARY.md +130 -0
  193. package/templates/config/SKILL_BUILDING_GUIDE.md +96 -0
  194. package/templates/config/SOUL.md +48 -0
  195. package/templates/config/TRIGGERS.json +58 -0
  196. package/templates/config/WEB_SEARCH_AVAILABLE.md +5 -0
  197. package/templates/config/WEB_SEARCH_UNAVAILABLE.md +3 -0
  198. package/templates/docker/claude-code-job/Dockerfile +34 -0
  199. package/templates/docker/claude-code-job/entrypoint.sh +149 -0
  200. package/templates/docker/claude-code-workspace/.tmux.conf +5 -0
  201. package/templates/docker/claude-code-workspace/Dockerfile +61 -0
  202. package/templates/docker/claude-code-workspace/entrypoint.sh +51 -0
  203. package/templates/docker/event-handler/Dockerfile +20 -0
  204. package/templates/docker/event-handler/ecosystem.config.cjs +7 -0
  205. package/templates/docker/pi-coding-agent-job/Dockerfile +51 -0
  206. package/templates/docker/pi-coding-agent-job/entrypoint.sh +164 -0
  207. package/templates/docker-compose.local.yml +78 -0
  208. package/templates/docker-compose.yml +64 -0
  209. package/templates/instrumentation.js +6 -0
  210. package/templates/middleware.js +23 -0
  211. package/templates/next.config.mjs +3 -0
  212. package/templates/postcss.config.mjs +5 -0
  213. package/templates/public/favicon.ico +0 -0
  214. package/templates/server.js +25 -0
  215. package/templates/skills/LICENSE +21 -0
  216. package/templates/skills/README.md +119 -0
  217. package/templates/skills/brave-search/SKILL.md +79 -0
  218. package/templates/skills/brave-search/content.js +86 -0
  219. package/templates/skills/brave-search/package-lock.json +621 -0
  220. package/templates/skills/brave-search/package.json +14 -0
  221. package/templates/skills/brave-search/search.js +199 -0
  222. package/templates/skills/browser-tools/SKILL.md +196 -0
  223. package/templates/skills/browser-tools/browser-content.js +103 -0
  224. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  225. package/templates/skills/browser-tools/browser-eval.js +53 -0
  226. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  227. package/templates/skills/browser-tools/browser-nav.js +44 -0
  228. package/templates/skills/browser-tools/browser-pick.js +162 -0
  229. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  230. package/templates/skills/browser-tools/browser-start.js +87 -0
  231. package/templates/skills/browser-tools/package-lock.json +2556 -0
  232. package/templates/skills/browser-tools/package.json +19 -0
  233. package/templates/skills/google-docs/SKILL.md +23 -0
  234. package/templates/skills/google-docs/create.sh +69 -0
  235. package/templates/skills/google-drive/SKILL.md +47 -0
  236. package/templates/skills/google-drive/delete.sh +47 -0
  237. package/templates/skills/google-drive/download.sh +50 -0
  238. package/templates/skills/google-drive/list.sh +41 -0
  239. package/templates/skills/google-drive/upload.sh +76 -0
  240. package/templates/skills/kie-ai/SKILL.md +38 -0
  241. package/templates/skills/kie-ai/generate-image.sh +77 -0
  242. package/templates/skills/kie-ai/generate-video.sh +69 -0
  243. package/templates/skills/llm-secrets/SKILL.md +34 -0
  244. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  245. package/templates/skills/modify-self/SKILL.md +12 -0
  246. package/templates/skills/youtube-transcript/SKILL.md +41 -0
  247. package/templates/skills/youtube-transcript/package-lock.json +24 -0
  248. package/templates/skills/youtube-transcript/package.json +8 -0
  249. package/templates/skills/youtube-transcript/transcript.js +84 -0
@@ -0,0 +1,140 @@
1
+ import { auth } from '../auth/index.js';
2
+ import { chatStream } from '../ai/index.js';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+
5
+ /**
6
+ * POST handler for /stream/chat — streaming chat with session auth.
7
+ * Dedicated route handler separate from the catch-all api/index.js.
8
+ */
9
+ export async function POST(request) {
10
+ const session = await auth();
11
+ if (!session?.user?.id) {
12
+ return Response.json({ error: 'Unauthorized' }, { status: 401 });
13
+ }
14
+
15
+ const body = await request.json();
16
+ const { messages, chatId: rawChatId, trigger, codeMode, repo, branch } = body;
17
+
18
+ if (!messages?.length) {
19
+ return Response.json({ error: 'No messages' }, { status: 400 });
20
+ }
21
+
22
+ // Get the last user message — AI SDK v5 sends UIMessage[] with parts
23
+ const lastUserMessage = [...messages].reverse().find((m) => m.role === 'user');
24
+ if (!lastUserMessage) {
25
+ return Response.json({ error: 'No user message' }, { status: 400 });
26
+ }
27
+
28
+ // Extract text from message parts (AI SDK v5+) or fall back to content
29
+ let userText =
30
+ lastUserMessage.parts
31
+ ?.filter((p) => p.type === 'text')
32
+ .map((p) => p.text)
33
+ .join('\n') ||
34
+ lastUserMessage.content ||
35
+ '';
36
+
37
+ // Extract file parts from message
38
+ const fileParts = lastUserMessage.parts?.filter((p) => p.type === 'file') || [];
39
+ const attachments = [];
40
+
41
+ for (const part of fileParts) {
42
+ const { mediaType, url } = part;
43
+ if (!mediaType || !url) continue;
44
+
45
+ if (mediaType.startsWith('image/') || mediaType === 'application/pdf') {
46
+ // Images and PDFs → pass as visual attachments for the LLM
47
+ attachments.push({ category: 'image', mimeType: mediaType, dataUrl: url });
48
+ } else if (mediaType.startsWith('text/') || mediaType === 'application/json') {
49
+ // Text files → decode base64 data URL and inline into message text
50
+ try {
51
+ const base64Data = url.split(',')[1];
52
+ const textContent = Buffer.from(base64Data, 'base64').toString('utf-8');
53
+ const fileName = part.name || 'file';
54
+ userText += `\n\nFile: ${fileName}\n\`\`\`\n${textContent}\n\`\`\``;
55
+ } catch (e) {
56
+ console.error('Failed to decode text file:', e);
57
+ }
58
+ }
59
+ }
60
+
61
+ if (!userText.trim() && attachments.length === 0) {
62
+ return Response.json({ error: 'Empty message' }, { status: 400 });
63
+ }
64
+
65
+ // Map web channel to thread_id — AI layer handles DB persistence
66
+ const threadId = rawChatId || uuidv4();
67
+ const { createUIMessageStream, createUIMessageStreamResponse } = await import('ai');
68
+
69
+ const stream = createUIMessageStream({
70
+ onError: (error) => {
71
+ console.error('Chat stream error:', error);
72
+ return error?.message || 'An error occurred while processing your message.';
73
+ },
74
+ execute: async ({ writer }) => {
75
+ // chatStream handles: save user msg, invoke agent, save assistant msg, auto-title
76
+ const skipUserPersist = trigger === 'regenerate-message';
77
+ const streamOptions = {
78
+ userId: session.user.id,
79
+ skipUserPersist,
80
+ };
81
+ if (codeMode && repo && branch) {
82
+ streamOptions.repo = repo;
83
+ streamOptions.branch = branch;
84
+ }
85
+ const chunks = chatStream(threadId, userText, attachments, streamOptions);
86
+
87
+ // Signal start of assistant message
88
+ writer.write({ type: 'start' });
89
+
90
+ let textStarted = false;
91
+ let textId = uuidv4();
92
+
93
+ for await (const chunk of chunks) {
94
+ if (chunk.type === 'text') {
95
+ if (!textStarted) {
96
+ textId = uuidv4();
97
+ writer.write({ type: 'text-start', id: textId });
98
+ textStarted = true;
99
+ }
100
+ writer.write({ type: 'text-delta', id: textId, delta: chunk.text });
101
+
102
+ } else if (chunk.type === 'tool-call') {
103
+ // Close any open text block before tool events
104
+ if (textStarted) {
105
+ writer.write({ type: 'text-end', id: textId });
106
+ textStarted = false;
107
+ }
108
+ writer.write({
109
+ type: 'tool-input-start',
110
+ toolCallId: chunk.toolCallId,
111
+ toolName: chunk.toolName,
112
+ });
113
+ writer.write({
114
+ type: 'tool-input-available',
115
+ toolCallId: chunk.toolCallId,
116
+ toolName: chunk.toolName,
117
+ input: chunk.args,
118
+ });
119
+
120
+ } else if (chunk.type === 'tool-result') {
121
+ writer.write({
122
+ type: 'tool-output-available',
123
+ toolCallId: chunk.toolCallId,
124
+ output: chunk.result,
125
+ });
126
+ }
127
+ }
128
+
129
+ // Close final text block if still open
130
+ if (textStarted) {
131
+ writer.write({ type: 'text-end', id: textId });
132
+ }
133
+
134
+ // Signal end of assistant message
135
+ writer.write({ type: 'finish' });
136
+ },
137
+ });
138
+
139
+ return createUIMessageStreamResponse({ stream });
140
+ }
@@ -0,0 +1,213 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { CirclePlusIcon, PanelLeftIcon, MessageIcon, BellIcon, SwarmIcon, ArrowUpCircleIcon, LifeBuoyIcon, GitPullRequestIcon } from "./icons.js";
5
+ import { getUnreadNotificationCount, getPullRequestCount, getAppVersion } from "../actions.js";
6
+ import { SidebarHistory } from "./sidebar-history.js";
7
+ import { SidebarUserNav } from "./sidebar-user-nav.js";
8
+ import { UpgradeDialog } from "./upgrade-dialog.js";
9
+ import {
10
+ Sidebar,
11
+ SidebarContent,
12
+ SidebarFooter,
13
+ SidebarHeader,
14
+ SidebarMenu,
15
+ SidebarMenuItem,
16
+ SidebarMenuButton,
17
+ useSidebar
18
+ } from "./ui/sidebar.js";
19
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip.js";
20
+ import { useChatNav } from "./chat-nav-context.js";
21
+ function AppSidebar({ user }) {
22
+ const { navigateToChat } = useChatNav();
23
+ const { state, open, setOpenMobile, toggleSidebar } = useSidebar();
24
+ const collapsed = state === "collapsed";
25
+ const [unreadCount, setUnreadCount] = useState(0);
26
+ const [prCount, setPrCount] = useState(0);
27
+ const [version, setVersion] = useState("");
28
+ const [updateAvailable, setUpdateAvailable] = useState(null);
29
+ const [changelog, setChangelog] = useState(null);
30
+ const [upgradeOpen, setUpgradeOpen] = useState(false);
31
+ useEffect(() => {
32
+ function fetchCounts() {
33
+ getUnreadNotificationCount().then((count) => setUnreadCount(count)).catch(() => {
34
+ });
35
+ getPullRequestCount().then((count) => setPrCount(count)).catch(() => {
36
+ });
37
+ }
38
+ fetchCounts();
39
+ const interval = setInterval(fetchCounts, 10 * 60 * 1e3);
40
+ return () => clearInterval(interval);
41
+ }, []);
42
+ useEffect(() => {
43
+ getAppVersion().then(({ version: version2, updateAvailable: updateAvailable2, changelog: changelog2 }) => {
44
+ setVersion(version2);
45
+ setUpdateAvailable(updateAvailable2);
46
+ setChangelog(changelog2);
47
+ }).catch(() => {
48
+ });
49
+ }, []);
50
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
51
+ /* @__PURE__ */ jsxs(Sidebar, { children: [
52
+ /* @__PURE__ */ jsxs(SidebarHeader, { children: [
53
+ /* @__PURE__ */ jsxs("div", { className: collapsed ? "flex justify-center" : "flex items-center justify-between", children: [
54
+ !collapsed && /* @__PURE__ */ jsxs("span", { className: "px-2 font-semibold text-lg", children: [
55
+ "GigaClaw",
56
+ version && /* @__PURE__ */ jsxs("span", { className: "text-[11px] font-normal text-muted-foreground", children: [
57
+ " v",
58
+ version
59
+ ] })
60
+ ] }),
61
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
62
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
63
+ "button",
64
+ {
65
+ className: "inline-flex shrink-0 items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-background hover:text-foreground",
66
+ onClick: toggleSidebar,
67
+ children: /* @__PURE__ */ jsx(PanelLeftIcon, { size: 16 })
68
+ }
69
+ ) }),
70
+ /* @__PURE__ */ jsx(TooltipContent, { side: collapsed ? "right" : "bottom", children: collapsed ? "Open sidebar" : "Close sidebar" })
71
+ ] })
72
+ ] }),
73
+ /* @__PURE__ */ jsxs(SidebarMenu, { children: [
74
+ /* @__PURE__ */ jsx(SidebarMenuItem, { className: "mb-2", children: /* @__PURE__ */ jsxs(Tooltip, { children: [
75
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
76
+ SidebarMenuButton,
77
+ {
78
+ href: "/",
79
+ className: collapsed ? "justify-center" : "",
80
+ onClick: (e) => {
81
+ e.preventDefault();
82
+ navigateToChat(null);
83
+ setOpenMobile(false);
84
+ },
85
+ children: [
86
+ /* @__PURE__ */ jsx(CirclePlusIcon, { size: 16 }),
87
+ !collapsed && /* @__PURE__ */ jsx("span", { children: "New chat" })
88
+ ]
89
+ }
90
+ ) }),
91
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "New chat" })
92
+ ] }) }),
93
+ /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
94
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
95
+ SidebarMenuButton,
96
+ {
97
+ href: "/chats",
98
+ className: collapsed ? "justify-center" : "",
99
+ children: [
100
+ /* @__PURE__ */ jsx(MessageIcon, { size: 16 }),
101
+ !collapsed && /* @__PURE__ */ jsx("span", { children: "Chats" })
102
+ ]
103
+ }
104
+ ) }),
105
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Chats" })
106
+ ] }) }),
107
+ /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
108
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
109
+ SidebarMenuButton,
110
+ {
111
+ href: "/swarm",
112
+ className: collapsed ? "justify-center" : "",
113
+ children: [
114
+ /* @__PURE__ */ jsx(SwarmIcon, { size: 16 }),
115
+ !collapsed && /* @__PURE__ */ jsx("span", { children: "Swarm" })
116
+ ]
117
+ }
118
+ ) }),
119
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Swarm" })
120
+ ] }) }),
121
+ /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
122
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
123
+ SidebarMenuButton,
124
+ {
125
+ href: "/notifications",
126
+ className: collapsed ? "justify-center" : "",
127
+ children: [
128
+ /* @__PURE__ */ jsx(BellIcon, { size: 16 }),
129
+ !collapsed && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
130
+ "Notifications",
131
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center rounded-full bg-destructive px-1.5 py-0.5 text-[10px] font-medium leading-none text-destructive-foreground", children: unreadCount })
132
+ ] }),
133
+ collapsed && unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 inline-flex h-4 w-4 items-center justify-center rounded-full bg-destructive text-[10px] font-medium text-destructive-foreground", children: unreadCount })
134
+ ]
135
+ }
136
+ ) }),
137
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Notifications" })
138
+ ] }) }),
139
+ /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
140
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
141
+ SidebarMenuButton,
142
+ {
143
+ href: "/pull-requests",
144
+ className: collapsed ? "justify-center" : "",
145
+ children: [
146
+ /* @__PURE__ */ jsx(GitPullRequestIcon, { size: 16 }),
147
+ !collapsed && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
148
+ "Pending Changes",
149
+ prCount > 0 && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center rounded-full bg-destructive px-1.5 py-0.5 text-[10px] font-medium leading-none text-destructive-foreground", children: prCount })
150
+ ] }),
151
+ collapsed && prCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 inline-flex h-4 w-4 items-center justify-center rounded-full bg-destructive text-[10px] font-medium text-destructive-foreground", children: prCount })
152
+ ]
153
+ }
154
+ ) }),
155
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Pending Changes" })
156
+ ] }) }),
157
+ updateAvailable && /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
158
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
159
+ SidebarMenuButton,
160
+ {
161
+ className: collapsed ? "justify-center" : "",
162
+ onClick: () => setUpgradeOpen(true),
163
+ children: [
164
+ /* @__PURE__ */ jsxs("span", { className: "relative", children: [
165
+ /* @__PURE__ */ jsx(ArrowUpCircleIcon, { size: 16 }),
166
+ collapsed && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-1 inline-block h-2 w-2 rounded-full bg-emerald-500" })
167
+ ] }),
168
+ !collapsed && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
169
+ "Upgrade",
170
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center justify-center rounded-full bg-emerald-500 px-1.5 py-0.5 text-[10px] font-medium leading-none text-white", children: [
171
+ "v",
172
+ updateAvailable
173
+ ] })
174
+ ] })
175
+ ]
176
+ }
177
+ ) }),
178
+ collapsed && /* @__PURE__ */ jsxs(TooltipContent, { side: "right", children: [
179
+ "Upgrade to v",
180
+ updateAvailable
181
+ ] })
182
+ ] }) }),
183
+ /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
184
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
185
+ SidebarMenuButton,
186
+ {
187
+ href: "https://gigaclaw.gignaati.com",
188
+ target: "_blank",
189
+ rel: "noopener noreferrer",
190
+ className: collapsed ? "justify-center" : "",
191
+ children: [
192
+ /* @__PURE__ */ jsx(LifeBuoyIcon, { size: 16 }),
193
+ !collapsed && /* @__PURE__ */ jsx("span", { children: "Support" })
194
+ ]
195
+ }
196
+ ) }),
197
+ collapsed && /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: "Support" })
198
+ ] }) })
199
+ ] })
200
+ ] }),
201
+ !collapsed && /* @__PURE__ */ jsxs(SidebarContent, { children: [
202
+ /* @__PURE__ */ jsx("div", { className: "mx-4 mb-1 border-t border-border" }),
203
+ /* @__PURE__ */ jsx(SidebarHistory, {})
204
+ ] }),
205
+ collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
206
+ /* @__PURE__ */ jsx(SidebarFooter, { children: user && /* @__PURE__ */ jsx(SidebarUserNav, { user, collapsed }) })
207
+ ] }),
208
+ /* @__PURE__ */ jsx(UpgradeDialog, { open: upgradeOpen, onClose: () => setUpgradeOpen(false), version, updateAvailable, changelog })
209
+ ] });
210
+ }
211
+ export {
212
+ AppSidebar
213
+ };
@@ -0,0 +1,279 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import { CirclePlusIcon, PanelLeftIcon, MessageIcon, BellIcon, SwarmIcon, ArrowUpCircleIcon, LifeBuoyIcon, GitPullRequestIcon } from './icons.js';
5
+ import { getUnreadNotificationCount, getPullRequestCount, getAppVersion } from '../actions.js';
6
+ import { SidebarHistory } from './sidebar-history.js';
7
+ import { SidebarUserNav } from './sidebar-user-nav.js';
8
+ import { UpgradeDialog } from './upgrade-dialog.js';
9
+ import {
10
+ Sidebar,
11
+ SidebarContent,
12
+ SidebarFooter,
13
+ SidebarHeader,
14
+ SidebarMenu,
15
+ SidebarMenuItem,
16
+ SidebarMenuButton,
17
+ useSidebar,
18
+ } from './ui/sidebar.js';
19
+ import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip.js';
20
+ import { useChatNav } from './chat-nav-context.js';
21
+
22
+ export function AppSidebar({ user }) {
23
+ const { navigateToChat } = useChatNav();
24
+ const { state, open, setOpenMobile, toggleSidebar } = useSidebar();
25
+ const collapsed = state === 'collapsed';
26
+ const [unreadCount, setUnreadCount] = useState(0);
27
+ const [prCount, setPrCount] = useState(0);
28
+ const [version, setVersion] = useState('');
29
+ const [updateAvailable, setUpdateAvailable] = useState(null);
30
+ const [changelog, setChangelog] = useState(null);
31
+ const [upgradeOpen, setUpgradeOpen] = useState(false);
32
+
33
+ // Fetch badge counts (notifications + PRs) — run immediately, then every 10 minutes
34
+ useEffect(() => {
35
+ function fetchCounts() {
36
+ getUnreadNotificationCount()
37
+ .then((count) => setUnreadCount(count))
38
+ .catch(() => {});
39
+ getPullRequestCount()
40
+ .then((count) => setPrCount(count))
41
+ .catch(() => {});
42
+ }
43
+ fetchCounts();
44
+ const interval = setInterval(fetchCounts, 10 * 60 * 1000);
45
+ return () => clearInterval(interval);
46
+ }, []);
47
+
48
+ // Version check — one-time on mount
49
+ useEffect(() => {
50
+ getAppVersion()
51
+ .then(({ version, updateAvailable, changelog }) => {
52
+ setVersion(version);
53
+ setUpdateAvailable(updateAvailable);
54
+ setChangelog(changelog);
55
+ })
56
+ .catch(() => {});
57
+ }, []);
58
+
59
+ return (
60
+ <>
61
+ <Sidebar>
62
+ <SidebarHeader>
63
+ {/* Top row: brand name + toggle icon (open) or just toggle icon (collapsed) */}
64
+ <div className={collapsed ? 'flex justify-center' : 'flex items-center justify-between'}>
65
+ {!collapsed && (
66
+ <span className="px-2 font-semibold text-lg">GigaClaw{version && <span className="text-[11px] font-normal text-muted-foreground"> v{version}</span>}</span>
67
+ )}
68
+ <Tooltip>
69
+ <TooltipTrigger asChild>
70
+ <button
71
+ className="inline-flex shrink-0 items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-background hover:text-foreground"
72
+ onClick={toggleSidebar}
73
+ >
74
+ <PanelLeftIcon size={16} />
75
+ </button>
76
+ </TooltipTrigger>
77
+ <TooltipContent side={collapsed ? 'right' : 'bottom'}>
78
+ {collapsed ? 'Open sidebar' : 'Close sidebar'}
79
+ </TooltipContent>
80
+ </Tooltip>
81
+ </div>
82
+
83
+ <SidebarMenu>
84
+ {/* New chat */}
85
+ <SidebarMenuItem className="mb-2">
86
+ <Tooltip>
87
+ <TooltipTrigger asChild>
88
+ <SidebarMenuButton
89
+ href="/"
90
+ className={collapsed ? 'justify-center' : ''}
91
+ onClick={(e) => {
92
+ e.preventDefault();
93
+ navigateToChat(null);
94
+ setOpenMobile(false);
95
+ }}
96
+ >
97
+ <CirclePlusIcon size={16} />
98
+ {!collapsed && <span>New chat</span>}
99
+ </SidebarMenuButton>
100
+ </TooltipTrigger>
101
+ {collapsed && (
102
+ <TooltipContent side="right">New chat</TooltipContent>
103
+ )}
104
+ </Tooltip>
105
+ </SidebarMenuItem>
106
+
107
+ {/* Chats history */}
108
+ <SidebarMenuItem>
109
+ <Tooltip>
110
+ <TooltipTrigger asChild>
111
+ <SidebarMenuButton
112
+ href="/chats"
113
+ className={collapsed ? 'justify-center' : ''}
114
+ >
115
+ <MessageIcon size={16} />
116
+ {!collapsed && <span>Chats</span>}
117
+ </SidebarMenuButton>
118
+ </TooltipTrigger>
119
+ {collapsed && (
120
+ <TooltipContent side="right">Chats</TooltipContent>
121
+ )}
122
+ </Tooltip>
123
+ </SidebarMenuItem>
124
+
125
+ {/* Swarm */}
126
+ <SidebarMenuItem>
127
+ <Tooltip>
128
+ <TooltipTrigger asChild>
129
+ <SidebarMenuButton
130
+ href="/swarm"
131
+ className={collapsed ? 'justify-center' : ''}
132
+ >
133
+ <SwarmIcon size={16} />
134
+ {!collapsed && <span>Swarm</span>}
135
+ </SidebarMenuButton>
136
+ </TooltipTrigger>
137
+ {collapsed && (
138
+ <TooltipContent side="right">Swarm</TooltipContent>
139
+ )}
140
+ </Tooltip>
141
+ </SidebarMenuItem>
142
+
143
+ {/* Notifications */}
144
+ <SidebarMenuItem>
145
+ <Tooltip>
146
+ <TooltipTrigger asChild>
147
+ <SidebarMenuButton
148
+ href="/notifications"
149
+ className={collapsed ? 'justify-center' : ''}
150
+ >
151
+ <BellIcon size={16} />
152
+ {!collapsed && (
153
+ <span className="flex items-center gap-2">
154
+ Notifications
155
+ {unreadCount > 0 && (
156
+ <span className="inline-flex items-center justify-center rounded-full bg-destructive px-1.5 py-0.5 text-[10px] font-medium leading-none text-destructive-foreground">
157
+ {unreadCount}
158
+ </span>
159
+ )}
160
+ </span>
161
+ )}
162
+ {collapsed && unreadCount > 0 && (
163
+ <span className="absolute -top-1 -right-1 inline-flex h-4 w-4 items-center justify-center rounded-full bg-destructive text-[10px] font-medium text-destructive-foreground">
164
+ {unreadCount}
165
+ </span>
166
+ )}
167
+ </SidebarMenuButton>
168
+ </TooltipTrigger>
169
+ {collapsed && (
170
+ <TooltipContent side="right">Notifications</TooltipContent>
171
+ )}
172
+ </Tooltip>
173
+ </SidebarMenuItem>
174
+
175
+ {/* Pending Changes */}
176
+ <SidebarMenuItem>
177
+ <Tooltip>
178
+ <TooltipTrigger asChild>
179
+ <SidebarMenuButton
180
+ href="/pull-requests"
181
+ className={collapsed ? 'justify-center' : ''}
182
+ >
183
+ <GitPullRequestIcon size={16} />
184
+ {!collapsed && (
185
+ <span className="flex items-center gap-2">
186
+ Pending Changes
187
+ {prCount > 0 && (
188
+ <span className="inline-flex items-center justify-center rounded-full bg-destructive px-1.5 py-0.5 text-[10px] font-medium leading-none text-destructive-foreground">
189
+ {prCount}
190
+ </span>
191
+ )}
192
+ </span>
193
+ )}
194
+ {collapsed && prCount > 0 && (
195
+ <span className="absolute -top-1 -right-1 inline-flex h-4 w-4 items-center justify-center rounded-full bg-destructive text-[10px] font-medium text-destructive-foreground">
196
+ {prCount}
197
+ </span>
198
+ )}
199
+ </SidebarMenuButton>
200
+ </TooltipTrigger>
201
+ {collapsed && (
202
+ <TooltipContent side="right">Pending Changes</TooltipContent>
203
+ )}
204
+ </Tooltip>
205
+ </SidebarMenuItem>
206
+
207
+ {/* Upgrade (only when update is available) */}
208
+ {updateAvailable && (
209
+ <SidebarMenuItem>
210
+ <Tooltip>
211
+ <TooltipTrigger asChild>
212
+ <SidebarMenuButton
213
+ className={collapsed ? 'justify-center' : ''}
214
+ onClick={() => setUpgradeOpen(true)}
215
+ >
216
+ <span className="relative">
217
+ <ArrowUpCircleIcon size={16} />
218
+ {collapsed && (
219
+ <span className="absolute -top-1 -right-1 inline-block h-2 w-2 rounded-full bg-emerald-500" />
220
+ )}
221
+ </span>
222
+ {!collapsed && (
223
+ <span className="flex items-center gap-2">
224
+ Upgrade
225
+ <span className="inline-flex items-center justify-center rounded-full bg-emerald-500 px-1.5 py-0.5 text-[10px] font-medium leading-none text-white">
226
+ v{updateAvailable}
227
+ </span>
228
+ </span>
229
+ )}
230
+ </SidebarMenuButton>
231
+ </TooltipTrigger>
232
+ {collapsed && (
233
+ <TooltipContent side="right">Upgrade to v{updateAvailable}</TooltipContent>
234
+ )}
235
+ </Tooltip>
236
+ </SidebarMenuItem>
237
+ )}
238
+
239
+ {/* Support */}
240
+ <SidebarMenuItem>
241
+ <Tooltip>
242
+ <TooltipTrigger asChild>
243
+ <SidebarMenuButton
244
+ href="https://gigaclaw.gignaati.com"
245
+ target="_blank"
246
+ rel="noopener noreferrer"
247
+ className={collapsed ? 'justify-center' : ''}
248
+ >
249
+ <LifeBuoyIcon size={16} />
250
+ {!collapsed && <span>Support</span>}
251
+ </SidebarMenuButton>
252
+ </TooltipTrigger>
253
+ {collapsed && (
254
+ <TooltipContent side="right">Support</TooltipContent>
255
+ )}
256
+ </Tooltip>
257
+ </SidebarMenuItem>
258
+
259
+ </SidebarMenu>
260
+ </SidebarHeader>
261
+
262
+ {!collapsed && (
263
+ <SidebarContent>
264
+ <div className="mx-4 mb-1 border-t border-border" />
265
+ <SidebarHistory />
266
+ </SidebarContent>
267
+ )}
268
+
269
+ {/* Spacer when collapsed to push footer down */}
270
+ {collapsed && <div className="flex-1" />}
271
+
272
+ <SidebarFooter>
273
+ {user && <SidebarUserNav user={user} collapsed={collapsed} />}
274
+ </SidebarFooter>
275
+ </Sidebar>
276
+ <UpgradeDialog open={upgradeOpen} onClose={() => setUpgradeOpen(false)} version={version} updateAvailable={updateAvailable} changelog={changelog} />
277
+ </>
278
+ );
279
+ }