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.
- package/LICENSE +26 -0
- package/README.md +237 -0
- package/api/CLAUDE.md +19 -0
- package/api/index.js +265 -0
- package/bin/cli.js +823 -0
- package/bin/local.sh +85 -0
- package/bin/postinstall.js +63 -0
- package/config/index.js +26 -0
- package/config/instrumentation.js +62 -0
- package/drizzle/0000_initial.sql +52 -0
- package/drizzle/0001_nostalgic_sersi.sql +11 -0
- package/drizzle/0002_black_daimon_hellstrom.sql +19 -0
- package/drizzle/0003_rename_code_workspaces.sql +5 -0
- package/drizzle/meta/0000_snapshot.json +321 -0
- package/drizzle/meta/0001_snapshot.json +390 -0
- package/drizzle/meta/0002_snapshot.json +411 -0
- package/drizzle/meta/0003_snapshot.json +419 -0
- package/drizzle/meta/_journal.json +34 -0
- package/lib/actions.js +44 -0
- package/lib/ai/agent.js +86 -0
- package/lib/ai/index.js +342 -0
- package/lib/ai/model.js +180 -0
- package/lib/ai/tools.js +269 -0
- package/lib/ai/web-search.js +42 -0
- package/lib/auth/actions.js +28 -0
- package/lib/auth/config.js +27 -0
- package/lib/auth/edge-config.js +27 -0
- package/lib/auth/index.js +27 -0
- package/lib/auth/middleware.js +62 -0
- package/lib/channels/base.js +56 -0
- package/lib/channels/index.js +15 -0
- package/lib/channels/telegram.js +148 -0
- package/lib/chat/actions.js +579 -0
- package/lib/chat/api.js +140 -0
- package/lib/chat/components/app-sidebar.js +213 -0
- package/lib/chat/components/app-sidebar.jsx +279 -0
- package/lib/chat/components/chat-header.js +192 -0
- package/lib/chat/components/chat-header.jsx +223 -0
- package/lib/chat/components/chat-input.js +236 -0
- package/lib/chat/components/chat-input.jsx +249 -0
- package/lib/chat/components/chat-nav-context.js +11 -0
- package/lib/chat/components/chat-nav-context.jsx +11 -0
- package/lib/chat/components/chat-page.js +99 -0
- package/lib/chat/components/chat-page.jsx +121 -0
- package/lib/chat/components/chat.js +153 -0
- package/lib/chat/components/chat.jsx +199 -0
- package/lib/chat/components/chats-page.js +367 -0
- package/lib/chat/components/chats-page.jsx +394 -0
- package/lib/chat/components/code-mode-toggle.js +132 -0
- package/lib/chat/components/code-mode-toggle.jsx +163 -0
- package/lib/chat/components/crons-page.js +172 -0
- package/lib/chat/components/crons-page.jsx +244 -0
- package/lib/chat/components/greeting.js +11 -0
- package/lib/chat/components/greeting.jsx +16 -0
- package/lib/chat/components/icons.js +805 -0
- package/lib/chat/components/icons.jsx +751 -0
- package/lib/chat/components/index.js +20 -0
- package/lib/chat/components/message.js +363 -0
- package/lib/chat/components/message.jsx +422 -0
- package/lib/chat/components/messages.js +65 -0
- package/lib/chat/components/messages.jsx +74 -0
- package/lib/chat/components/notifications-page.js +56 -0
- package/lib/chat/components/notifications-page.jsx +87 -0
- package/lib/chat/components/page-layout.js +21 -0
- package/lib/chat/components/page-layout.jsx +28 -0
- package/lib/chat/components/pull-requests-page.js +103 -0
- package/lib/chat/components/pull-requests-page.jsx +113 -0
- package/lib/chat/components/settings-layout.js +39 -0
- package/lib/chat/components/settings-layout.jsx +53 -0
- package/lib/chat/components/settings-secrets-page.js +216 -0
- package/lib/chat/components/settings-secrets-page.jsx +264 -0
- package/lib/chat/components/sidebar-history-item.js +138 -0
- package/lib/chat/components/sidebar-history-item.jsx +119 -0
- package/lib/chat/components/sidebar-history.js +167 -0
- package/lib/chat/components/sidebar-history.jsx +220 -0
- package/lib/chat/components/sidebar-user-nav.js +61 -0
- package/lib/chat/components/sidebar-user-nav.jsx +77 -0
- package/lib/chat/components/swarm-page.js +157 -0
- package/lib/chat/components/swarm-page.jsx +210 -0
- package/lib/chat/components/tool-call.js +89 -0
- package/lib/chat/components/tool-call.jsx +107 -0
- package/lib/chat/components/triggers-page.js +153 -0
- package/lib/chat/components/triggers-page.jsx +221 -0
- package/lib/chat/components/ui/combobox.js +98 -0
- package/lib/chat/components/ui/combobox.jsx +114 -0
- package/lib/chat/components/ui/confirm-dialog.js +53 -0
- package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
- package/lib/chat/components/ui/dropdown-menu.js +194 -0
- package/lib/chat/components/ui/dropdown-menu.jsx +215 -0
- package/lib/chat/components/ui/rename-dialog.js +78 -0
- package/lib/chat/components/ui/rename-dialog.jsx +74 -0
- package/lib/chat/components/ui/scroll-area.js +13 -0
- package/lib/chat/components/ui/scroll-area.jsx +17 -0
- package/lib/chat/components/ui/separator.js +21 -0
- package/lib/chat/components/ui/separator.jsx +18 -0
- package/lib/chat/components/ui/sheet.js +75 -0
- package/lib/chat/components/ui/sheet.jsx +95 -0
- package/lib/chat/components/ui/sidebar.js +228 -0
- package/lib/chat/components/ui/sidebar.jsx +246 -0
- package/lib/chat/components/ui/tooltip.js +56 -0
- package/lib/chat/components/ui/tooltip.jsx +66 -0
- package/lib/chat/components/upgrade-dialog.js +151 -0
- package/lib/chat/components/upgrade-dialog.jsx +170 -0
- package/lib/chat/utils.js +11 -0
- package/lib/code/actions.js +153 -0
- package/lib/code/code-page.js +22 -0
- package/lib/code/code-page.jsx +25 -0
- package/lib/code/index.js +1 -0
- package/lib/code/terminal-view.js +201 -0
- package/lib/code/terminal-view.jsx +224 -0
- package/lib/code/ws-proxy.js +80 -0
- package/lib/cron.js +246 -0
- package/lib/db/api-keys.js +163 -0
- package/lib/db/chats.js +168 -0
- package/lib/db/code-workspaces.js +110 -0
- package/lib/db/index.js +52 -0
- package/lib/db/notifications.js +99 -0
- package/lib/db/schema.js +66 -0
- package/lib/db/update-check.js +96 -0
- package/lib/db/users.js +89 -0
- package/lib/paths.js +42 -0
- package/lib/tools/create-job.js +97 -0
- package/lib/tools/docker.js +146 -0
- package/lib/tools/github.js +271 -0
- package/lib/tools/openai.js +35 -0
- package/lib/tools/telegram.js +292 -0
- package/lib/triggers.js +104 -0
- package/lib/utils/render-md.js +111 -0
- package/package.json +118 -0
- package/setup/lib/auth.mjs +81 -0
- package/setup/lib/env.mjs +21 -0
- package/setup/lib/fs-utils.mjs +20 -0
- package/setup/lib/github.mjs +149 -0
- package/setup/lib/prerequisites.mjs +155 -0
- package/setup/lib/prompts.mjs +267 -0
- package/setup/lib/providers.mjs +105 -0
- package/setup/lib/sync.mjs +125 -0
- package/setup/lib/targets.mjs +45 -0
- package/setup/lib/telegram-verify.mjs +63 -0
- package/setup/lib/telegram.mjs +76 -0
- package/setup/setup-cloud.mjs +833 -0
- package/setup/setup-local.mjs +377 -0
- package/setup/setup-telegram.mjs +265 -0
- package/setup/setup.mjs +87 -0
- package/templates/.dockerignore +5 -0
- package/templates/.env.example +104 -0
- package/templates/.github/workflows/auto-merge.yml +117 -0
- package/templates/.github/workflows/notify-job-failed.yml +64 -0
- package/templates/.github/workflows/notify-pr-complete.yml +119 -0
- package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
- package/templates/.github/workflows/run-job.yml +89 -0
- package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
- package/templates/.gitignore.template +45 -0
- package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
- package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
- package/templates/CLAUDE.md +29 -0
- package/templates/CLAUDE.md.template +308 -0
- package/templates/app/api/[...gigaclaw]/route.js +1 -0
- package/templates/app/api/auth/[...nextauth]/route.js +1 -0
- package/templates/app/chat/[chatId]/page.js +9 -0
- package/templates/app/chats/page.js +7 -0
- package/templates/app/code/[codeWorkspaceId]/page.js +9 -0
- package/templates/app/components/ascii-logo.jsx +12 -0
- package/templates/app/components/login-form.jsx +92 -0
- package/templates/app/components/setup-form.jsx +82 -0
- package/templates/app/components/theme-provider.jsx +11 -0
- package/templates/app/components/theme-toggle.jsx +38 -0
- package/templates/app/components/ui/button.jsx +21 -0
- package/templates/app/components/ui/card.jsx +23 -0
- package/templates/app/components/ui/input.jsx +10 -0
- package/templates/app/components/ui/label.jsx +10 -0
- package/templates/app/crons/page.js +5 -0
- package/templates/app/globals.css +90 -0
- package/templates/app/layout.js +33 -0
- package/templates/app/login/page.js +15 -0
- package/templates/app/notifications/page.js +7 -0
- package/templates/app/page.js +7 -0
- package/templates/app/pull-requests/page.js +7 -0
- package/templates/app/settings/crons/page.js +5 -0
- package/templates/app/settings/layout.js +7 -0
- package/templates/app/settings/page.js +5 -0
- package/templates/app/settings/secrets/page.js +5 -0
- package/templates/app/settings/triggers/page.js +5 -0
- package/templates/app/stream/chat/route.js +1 -0
- package/templates/app/swarm/page.js +7 -0
- package/templates/app/triggers/page.js +5 -0
- package/templates/config/CODE_PLANNING.md +14 -0
- package/templates/config/CRONS.json +56 -0
- package/templates/config/HEARTBEAT.md +3 -0
- package/templates/config/JOB_AGENT.md +30 -0
- package/templates/config/JOB_PLANNING.md +240 -0
- package/templates/config/JOB_SUMMARY.md +130 -0
- package/templates/config/SKILL_BUILDING_GUIDE.md +96 -0
- package/templates/config/SOUL.md +48 -0
- package/templates/config/TRIGGERS.json +58 -0
- package/templates/config/WEB_SEARCH_AVAILABLE.md +5 -0
- package/templates/config/WEB_SEARCH_UNAVAILABLE.md +3 -0
- package/templates/docker/claude-code-job/Dockerfile +34 -0
- package/templates/docker/claude-code-job/entrypoint.sh +149 -0
- package/templates/docker/claude-code-workspace/.tmux.conf +5 -0
- package/templates/docker/claude-code-workspace/Dockerfile +61 -0
- package/templates/docker/claude-code-workspace/entrypoint.sh +51 -0
- package/templates/docker/event-handler/Dockerfile +20 -0
- package/templates/docker/event-handler/ecosystem.config.cjs +7 -0
- package/templates/docker/pi-coding-agent-job/Dockerfile +51 -0
- package/templates/docker/pi-coding-agent-job/entrypoint.sh +164 -0
- package/templates/docker-compose.local.yml +78 -0
- package/templates/docker-compose.yml +64 -0
- package/templates/instrumentation.js +6 -0
- package/templates/middleware.js +23 -0
- package/templates/next.config.mjs +3 -0
- package/templates/postcss.config.mjs +5 -0
- package/templates/public/favicon.ico +0 -0
- package/templates/server.js +25 -0
- package/templates/skills/LICENSE +21 -0
- package/templates/skills/README.md +119 -0
- package/templates/skills/brave-search/SKILL.md +79 -0
- package/templates/skills/brave-search/content.js +86 -0
- package/templates/skills/brave-search/package-lock.json +621 -0
- package/templates/skills/brave-search/package.json +14 -0
- package/templates/skills/brave-search/search.js +199 -0
- package/templates/skills/browser-tools/SKILL.md +196 -0
- package/templates/skills/browser-tools/browser-content.js +103 -0
- package/templates/skills/browser-tools/browser-cookies.js +35 -0
- package/templates/skills/browser-tools/browser-eval.js +53 -0
- package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
- package/templates/skills/browser-tools/browser-nav.js +44 -0
- package/templates/skills/browser-tools/browser-pick.js +162 -0
- package/templates/skills/browser-tools/browser-screenshot.js +34 -0
- package/templates/skills/browser-tools/browser-start.js +87 -0
- package/templates/skills/browser-tools/package-lock.json +2556 -0
- package/templates/skills/browser-tools/package.json +19 -0
- package/templates/skills/google-docs/SKILL.md +23 -0
- package/templates/skills/google-docs/create.sh +69 -0
- package/templates/skills/google-drive/SKILL.md +47 -0
- package/templates/skills/google-drive/delete.sh +47 -0
- package/templates/skills/google-drive/download.sh +50 -0
- package/templates/skills/google-drive/list.sh +41 -0
- package/templates/skills/google-drive/upload.sh +76 -0
- package/templates/skills/kie-ai/SKILL.md +38 -0
- package/templates/skills/kie-ai/generate-image.sh +77 -0
- package/templates/skills/kie-ai/generate-video.sh +69 -0
- package/templates/skills/llm-secrets/SKILL.md +34 -0
- package/templates/skills/llm-secrets/llm-secrets.js +33 -0
- package/templates/skills/modify-self/SKILL.md +12 -0
- package/templates/skills/youtube-transcript/SKILL.md +41 -0
- package/templates/skills/youtube-transcript/package-lock.json +24 -0
- package/templates/skills/youtube-transcript/package.json +8 -0
- package/templates/skills/youtube-transcript/transcript.js +84 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { auth } from '../auth/index.js';
|
|
4
|
+
import {
|
|
5
|
+
createChat as dbCreateChat,
|
|
6
|
+
getChatById,
|
|
7
|
+
getChatByWorkspaceId,
|
|
8
|
+
getMessagesByChatId,
|
|
9
|
+
deleteChat as dbDeleteChat,
|
|
10
|
+
deleteAllChatsByUser,
|
|
11
|
+
updateChatTitle,
|
|
12
|
+
toggleChatStarred,
|
|
13
|
+
} from '../db/chats.js';
|
|
14
|
+
import {
|
|
15
|
+
getNotifications as dbGetNotifications,
|
|
16
|
+
getUnreadCount as dbGetUnreadCount,
|
|
17
|
+
markAllRead as dbMarkAllRead,
|
|
18
|
+
} from '../db/notifications.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the authenticated user or throw.
|
|
22
|
+
*/
|
|
23
|
+
async function requireAuth() {
|
|
24
|
+
const session = await auth();
|
|
25
|
+
if (!session?.user?.id) {
|
|
26
|
+
throw new Error('Unauthorized');
|
|
27
|
+
}
|
|
28
|
+
return session.user;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get all chats for the authenticated user (includes Telegram chats).
|
|
33
|
+
* @returns {Promise<object[]>}
|
|
34
|
+
*/
|
|
35
|
+
export async function getChats() {
|
|
36
|
+
const user = await requireAuth();
|
|
37
|
+
const { or, eq, desc } = await import('drizzle-orm');
|
|
38
|
+
const { getDb } = await import('../db/index.js');
|
|
39
|
+
const { chats, codeWorkspaces } = await import('../db/schema.js');
|
|
40
|
+
const db = getDb();
|
|
41
|
+
return db
|
|
42
|
+
.select({
|
|
43
|
+
id: chats.id,
|
|
44
|
+
userId: chats.userId,
|
|
45
|
+
title: chats.title,
|
|
46
|
+
starred: chats.starred,
|
|
47
|
+
codeWorkspaceId: chats.codeWorkspaceId,
|
|
48
|
+
containerName: codeWorkspaces.containerName,
|
|
49
|
+
createdAt: chats.createdAt,
|
|
50
|
+
updatedAt: chats.updatedAt,
|
|
51
|
+
})
|
|
52
|
+
.from(chats)
|
|
53
|
+
.leftJoin(codeWorkspaces, eq(chats.codeWorkspaceId, codeWorkspaces.id))
|
|
54
|
+
.where(or(eq(chats.userId, user.id), eq(chats.userId, 'telegram')))
|
|
55
|
+
.orderBy(desc(chats.updatedAt))
|
|
56
|
+
.all();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get messages for a specific chat (with ownership check).
|
|
61
|
+
* @param {string} chatId
|
|
62
|
+
* @returns {Promise<object[]>}
|
|
63
|
+
*/
|
|
64
|
+
export async function getChatMessages(chatId) {
|
|
65
|
+
const user = await requireAuth();
|
|
66
|
+
const chat = getChatById(chatId);
|
|
67
|
+
if (!chat || (chat.userId !== user.id && chat.userId !== 'telegram')) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return getMessagesByChatId(chatId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create a new chat.
|
|
75
|
+
* @param {string} [id] - Optional chat ID
|
|
76
|
+
* @param {string} [title='New Chat']
|
|
77
|
+
* @returns {Promise<object>}
|
|
78
|
+
*/
|
|
79
|
+
export async function createChat(id, title = 'New Chat') {
|
|
80
|
+
const user = await requireAuth();
|
|
81
|
+
return dbCreateChat(user.id, title, id);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Delete a chat (with ownership check).
|
|
86
|
+
* @param {string} chatId
|
|
87
|
+
* @returns {Promise<{success: boolean}>}
|
|
88
|
+
*/
|
|
89
|
+
export async function deleteChat(chatId) {
|
|
90
|
+
const user = await requireAuth();
|
|
91
|
+
const chat = getChatById(chatId);
|
|
92
|
+
if (!chat || chat.userId !== user.id) {
|
|
93
|
+
return { success: false };
|
|
94
|
+
}
|
|
95
|
+
dbDeleteChat(chatId);
|
|
96
|
+
return { success: true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get the title of a specific chat (with ownership check).
|
|
101
|
+
* @param {string} chatId
|
|
102
|
+
* @returns {Promise<string|null>}
|
|
103
|
+
*/
|
|
104
|
+
export async function getChatTitle(chatId) {
|
|
105
|
+
const user = await requireAuth();
|
|
106
|
+
const chat = getChatById(chatId);
|
|
107
|
+
if (!chat || (chat.userId !== user.id && chat.userId !== 'telegram')) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return chat.title;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get the title of the chat linked to a workspace (with ownership check).
|
|
115
|
+
* @param {string} workspaceId
|
|
116
|
+
* @returns {Promise<string|null>}
|
|
117
|
+
*/
|
|
118
|
+
export async function getChatTitleByWorkspace(workspaceId) {
|
|
119
|
+
const user = await requireAuth();
|
|
120
|
+
const chat = getChatByWorkspaceId(workspaceId);
|
|
121
|
+
if (!chat || (chat.userId !== user.id && chat.userId !== 'telegram')) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return chat.title;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Rename a chat (with ownership check).
|
|
129
|
+
* @param {string} chatId
|
|
130
|
+
* @param {string} title
|
|
131
|
+
* @returns {Promise<{success: boolean}>}
|
|
132
|
+
*/
|
|
133
|
+
export async function renameChat(chatId, title) {
|
|
134
|
+
const user = await requireAuth();
|
|
135
|
+
const chat = getChatById(chatId);
|
|
136
|
+
if (!chat || chat.userId !== user.id) {
|
|
137
|
+
return { success: false };
|
|
138
|
+
}
|
|
139
|
+
updateChatTitle(chatId, title);
|
|
140
|
+
return { success: true };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Toggle a chat's starred status (with ownership check).
|
|
145
|
+
* @param {string} chatId
|
|
146
|
+
* @returns {Promise<{success: boolean, starred?: number}>}
|
|
147
|
+
*/
|
|
148
|
+
export async function starChat(chatId) {
|
|
149
|
+
const user = await requireAuth();
|
|
150
|
+
const chat = getChatById(chatId);
|
|
151
|
+
if (!chat || chat.userId !== user.id) {
|
|
152
|
+
return { success: false };
|
|
153
|
+
}
|
|
154
|
+
const starred = toggleChatStarred(chatId);
|
|
155
|
+
return { success: true, starred };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Delete all chats for the authenticated user.
|
|
160
|
+
* @returns {Promise<{success: boolean}>}
|
|
161
|
+
*/
|
|
162
|
+
export async function deleteAllChats() {
|
|
163
|
+
const user = await requireAuth();
|
|
164
|
+
deleteAllChatsByUser(user.id);
|
|
165
|
+
return { success: true };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get all notifications, newest first.
|
|
170
|
+
* @returns {Promise<object[]>}
|
|
171
|
+
*/
|
|
172
|
+
export async function getNotifications() {
|
|
173
|
+
await requireAuth();
|
|
174
|
+
return dbGetNotifications();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get count of unread notifications.
|
|
179
|
+
* @returns {Promise<number>}
|
|
180
|
+
*/
|
|
181
|
+
export async function getUnreadNotificationCount() {
|
|
182
|
+
await requireAuth();
|
|
183
|
+
return dbGetUnreadCount();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Mark all notifications as read.
|
|
188
|
+
* @returns {Promise<{success: boolean}>}
|
|
189
|
+
*/
|
|
190
|
+
export async function markNotificationsRead() {
|
|
191
|
+
await requireAuth();
|
|
192
|
+
dbMarkAllRead();
|
|
193
|
+
return { success: true };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
197
|
+
// App info actions
|
|
198
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get the installed package version and update status (auth-gated, never in client bundle).
|
|
202
|
+
* @returns {Promise<{ version: string, updateAvailable: string|null }>}
|
|
203
|
+
*/
|
|
204
|
+
export async function getAppVersion() {
|
|
205
|
+
await requireAuth();
|
|
206
|
+
const { getInstalledVersion } = await import('../cron.js');
|
|
207
|
+
const { getAvailableVersion, getReleaseNotes } = await import('../db/update-check.js');
|
|
208
|
+
const version = getInstalledVersion();
|
|
209
|
+
const available = getAvailableVersion();
|
|
210
|
+
const isNewer = available && available !== version;
|
|
211
|
+
return {
|
|
212
|
+
version,
|
|
213
|
+
updateAvailable: isNewer ? available : null,
|
|
214
|
+
changelog: isNewer ? getReleaseNotes() : null,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Trigger the upgrade-event-handler workflow via GitHub Actions.
|
|
220
|
+
* @returns {Promise<{ success: boolean }>}
|
|
221
|
+
*/
|
|
222
|
+
export async function triggerUpgrade() {
|
|
223
|
+
await requireAuth();
|
|
224
|
+
const { triggerWorkflowDispatch } = await import('../tools/github.js');
|
|
225
|
+
const { getAvailableVersion } = await import('../db/update-check.js');
|
|
226
|
+
const targetVersion = getAvailableVersion();
|
|
227
|
+
await triggerWorkflowDispatch('upgrade-event-handler.yml', 'main', {
|
|
228
|
+
target_version: targetVersion || '',
|
|
229
|
+
});
|
|
230
|
+
return { success: true };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
234
|
+
// API Key actions
|
|
235
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Create (or replace) the API key.
|
|
239
|
+
* @returns {Promise<{ key: string, record: object } | { error: string }>}
|
|
240
|
+
*/
|
|
241
|
+
export async function createNewApiKey() {
|
|
242
|
+
const user = await requireAuth();
|
|
243
|
+
try {
|
|
244
|
+
const { createApiKeyRecord } = await import('../db/api-keys.js');
|
|
245
|
+
return createApiKeyRecord(user.id);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
console.error('Failed to create API key:', err);
|
|
248
|
+
return { error: 'Failed to create API key' };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get the current API key metadata (no hash).
|
|
254
|
+
* @returns {Promise<object|null>}
|
|
255
|
+
*/
|
|
256
|
+
export async function getApiKeys() {
|
|
257
|
+
await requireAuth();
|
|
258
|
+
try {
|
|
259
|
+
const { getApiKey } = await import('../db/api-keys.js');
|
|
260
|
+
return getApiKey();
|
|
261
|
+
} catch (err) {
|
|
262
|
+
console.error('Failed to get API key:', err);
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Delete the API key.
|
|
269
|
+
* @returns {Promise<{ success: boolean } | { error: string }>}
|
|
270
|
+
*/
|
|
271
|
+
export async function deleteApiKey() {
|
|
272
|
+
await requireAuth();
|
|
273
|
+
try {
|
|
274
|
+
const mod = await import('../db/api-keys.js');
|
|
275
|
+
mod.deleteApiKey();
|
|
276
|
+
return { success: true };
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error('Failed to delete API key:', err);
|
|
279
|
+
return { error: 'Failed to delete API key' };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
284
|
+
// Code mode actions
|
|
285
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get repositories accessible to the authenticated user.
|
|
289
|
+
* @returns {Promise<{full_name: string, default_branch: string}[]>}
|
|
290
|
+
*/
|
|
291
|
+
export async function getRepositories() {
|
|
292
|
+
await requireAuth();
|
|
293
|
+
try {
|
|
294
|
+
const { listRepositories } = await import('../tools/github.js');
|
|
295
|
+
return await listRepositories();
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.error('Failed to get repositories:', err);
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get branches for a repository.
|
|
304
|
+
* @param {string} repoFullName - e.g. "owner/repo"
|
|
305
|
+
* @returns {Promise<{name: string, isDefault: boolean}[]>}
|
|
306
|
+
*/
|
|
307
|
+
export async function getBranches(repoFullName) {
|
|
308
|
+
await requireAuth();
|
|
309
|
+
try {
|
|
310
|
+
const { listBranches } = await import('../tools/github.js');
|
|
311
|
+
return await listBranches(repoFullName);
|
|
312
|
+
} catch (err) {
|
|
313
|
+
console.error('Failed to get branches:', err);
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get chat metadata (codeWorkspaceId) for an existing chat.
|
|
320
|
+
* @param {string} chatId
|
|
321
|
+
* @returns {Promise<{codeWorkspaceId: string|null}|null>}
|
|
322
|
+
*/
|
|
323
|
+
export async function getChatMeta(chatId) {
|
|
324
|
+
const user = await requireAuth();
|
|
325
|
+
const chat = getChatById(chatId);
|
|
326
|
+
if (!chat || (chat.userId !== user.id && chat.userId !== 'telegram')) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
title: chat.title || null,
|
|
331
|
+
starred: chat.starred || 0,
|
|
332
|
+
codeWorkspaceId: chat.codeWorkspaceId || null,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Get chat metadata by workspace ID (title, starred, chatId).
|
|
338
|
+
* @param {string} workspaceId
|
|
339
|
+
* @returns {Promise<{chatId: string, title: string|null, starred: number, codeWorkspaceId: string|null}|null>}
|
|
340
|
+
*/
|
|
341
|
+
export async function getChatMetaByWorkspace(workspaceId) {
|
|
342
|
+
const user = await requireAuth();
|
|
343
|
+
const chat = getChatByWorkspaceId(workspaceId);
|
|
344
|
+
if (!chat || (chat.userId !== user.id && chat.userId !== 'telegram')) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
chatId: chat.id,
|
|
349
|
+
title: chat.title || null,
|
|
350
|
+
starred: chat.starred || 0,
|
|
351
|
+
codeWorkspaceId: chat.codeWorkspaceId || null,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Get workspace details by ID.
|
|
357
|
+
* @param {string} workspaceId
|
|
358
|
+
* @returns {Promise<{id: string, repo: string, branch: string, containerName: string|null}|null>}
|
|
359
|
+
*/
|
|
360
|
+
export async function getWorkspace(workspaceId) {
|
|
361
|
+
await requireAuth();
|
|
362
|
+
try {
|
|
363
|
+
const { getCodeWorkspaceById } = await import('../db/code-workspaces.js');
|
|
364
|
+
const ws = getCodeWorkspaceById(workspaceId);
|
|
365
|
+
if (!ws) return null;
|
|
366
|
+
return { id: ws.id, repo: ws.repo, branch: ws.branch, containerName: ws.containerName, codingAgent: ws.codingAgent };
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error('Failed to get workspace:', err);
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
374
|
+
// Pull Request actions
|
|
375
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get all open pull requests from GitHub.
|
|
379
|
+
* @returns {Promise<object[]>}
|
|
380
|
+
*/
|
|
381
|
+
export async function getPullRequests() {
|
|
382
|
+
await requireAuth();
|
|
383
|
+
try {
|
|
384
|
+
const { getOpenPullRequests } = await import('../tools/github.js');
|
|
385
|
+
return await getOpenPullRequests();
|
|
386
|
+
} catch (err) {
|
|
387
|
+
console.error('Failed to get pull requests:', err);
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Get the count of open pull requests.
|
|
394
|
+
* @returns {Promise<number>}
|
|
395
|
+
*/
|
|
396
|
+
export async function getPullRequestCount() {
|
|
397
|
+
await requireAuth();
|
|
398
|
+
try {
|
|
399
|
+
const { getOpenPullRequests } = await import('../tools/github.js');
|
|
400
|
+
const prs = await getOpenPullRequests();
|
|
401
|
+
return prs.length;
|
|
402
|
+
} catch (err) {
|
|
403
|
+
console.error('Failed to get pull request count:', err);
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
409
|
+
// Swarm actions
|
|
410
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Get swarm status (active + completed jobs with counts).
|
|
414
|
+
* @returns {Promise<object>}
|
|
415
|
+
*/
|
|
416
|
+
export async function getSwarmStatus(page = 1) {
|
|
417
|
+
await requireAuth();
|
|
418
|
+
try {
|
|
419
|
+
const { getSwarmStatus: fetchStatus } = await import('../tools/github.js');
|
|
420
|
+
return await fetchStatus(page);
|
|
421
|
+
} catch (err) {
|
|
422
|
+
console.error('Failed to get swarm status:', err);
|
|
423
|
+
return { error: 'Failed to get swarm status', runs: [], hasMore: false };
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Get swarm config (crons + triggers).
|
|
429
|
+
* @returns {Promise<{ crons: object[], triggers: object[] }>}
|
|
430
|
+
*/
|
|
431
|
+
export async function getSwarmConfig() {
|
|
432
|
+
await requireAuth();
|
|
433
|
+
const { cronsFile, triggersFile } = await import('../paths.js');
|
|
434
|
+
const fs = await import('fs');
|
|
435
|
+
let crons = [];
|
|
436
|
+
let triggers = [];
|
|
437
|
+
try { crons = JSON.parse(fs.readFileSync(cronsFile, 'utf8')); } catch {}
|
|
438
|
+
try { triggers = JSON.parse(fs.readFileSync(triggersFile, 'utf8')); } catch {}
|
|
439
|
+
return { crons, triggers };
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
444
|
+
// Export actions
|
|
445
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Format a single chat's messages into the requested format string.
|
|
449
|
+
* @param {object} chat - { id, title, createdAt, updatedAt }
|
|
450
|
+
* @param {object[]} msgs - [{ role, content, createdAt }]
|
|
451
|
+
* @param {'json'|'md'|'txt'} format
|
|
452
|
+
* @returns {string}
|
|
453
|
+
*/
|
|
454
|
+
function formatChatExport(chat, msgs, format) {
|
|
455
|
+
const title = chat.title || 'Untitled Chat';
|
|
456
|
+
const exportedAt = new Date().toISOString();
|
|
457
|
+
|
|
458
|
+
if (format === 'json') {
|
|
459
|
+
return JSON.stringify(
|
|
460
|
+
{
|
|
461
|
+
id: chat.id,
|
|
462
|
+
title,
|
|
463
|
+
exportedAt,
|
|
464
|
+
createdAt: new Date(chat.createdAt).toISOString(),
|
|
465
|
+
updatedAt: new Date(chat.updatedAt).toISOString(),
|
|
466
|
+
messages: msgs.map((m) => ({
|
|
467
|
+
id: m.id,
|
|
468
|
+
role: m.role,
|
|
469
|
+
content: m.content,
|
|
470
|
+
createdAt: new Date(m.createdAt).toISOString(),
|
|
471
|
+
})),
|
|
472
|
+
},
|
|
473
|
+
null,
|
|
474
|
+
2
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (format === 'md') {
|
|
479
|
+
const lines = [
|
|
480
|
+
`# ${title}`,
|
|
481
|
+
``,
|
|
482
|
+
`**Exported:** ${exportedAt}`,
|
|
483
|
+
`**Created:** ${new Date(chat.createdAt).toISOString()}`,
|
|
484
|
+
``,
|
|
485
|
+
`---`,
|
|
486
|
+
``,
|
|
487
|
+
];
|
|
488
|
+
for (const m of msgs) {
|
|
489
|
+
const ts = new Date(m.createdAt).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
|
490
|
+
const role = m.role === 'user' ? '**User**' : '**Assistant**';
|
|
491
|
+
lines.push(`### ${role} · ${ts}`);
|
|
492
|
+
lines.push(``);
|
|
493
|
+
lines.push(m.content);
|
|
494
|
+
lines.push(``);
|
|
495
|
+
lines.push(`---`);
|
|
496
|
+
lines.push(``);
|
|
497
|
+
}
|
|
498
|
+
return lines.join('\n');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Plain text (default)
|
|
502
|
+
const lines = [
|
|
503
|
+
`${title}`,
|
|
504
|
+
`Exported: ${exportedAt}`,
|
|
505
|
+
`Created: ${new Date(chat.createdAt).toISOString()}`,
|
|
506
|
+
``,
|
|
507
|
+
`${'─'.repeat(60)}`,
|
|
508
|
+
``,
|
|
509
|
+
];
|
|
510
|
+
for (const m of msgs) {
|
|
511
|
+
const ts = new Date(m.createdAt).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
|
512
|
+
const speaker = m.role === 'user' ? 'User' : 'Assistant';
|
|
513
|
+
lines.push(`[${ts}] ${speaker}:`);
|
|
514
|
+
lines.push(m.content);
|
|
515
|
+
lines.push(``);
|
|
516
|
+
}
|
|
517
|
+
return lines.join('\n');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Export a single chat's messages.
|
|
522
|
+
* @param {string} chatId
|
|
523
|
+
* @param {'json'|'md'|'txt'} format
|
|
524
|
+
* @returns {Promise<{ filename: string, content: string, mimeType: string }>}
|
|
525
|
+
*/
|
|
526
|
+
export async function exportChat(chatId, format = 'md') {
|
|
527
|
+
const user = await requireAuth();
|
|
528
|
+
const chat = getChatById(chatId);
|
|
529
|
+
if (!chat || (chat.userId !== user.id && chat.userId !== 'telegram')) {
|
|
530
|
+
throw new Error('Chat not found or access denied');
|
|
531
|
+
}
|
|
532
|
+
const msgs = getMessagesByChatId(chatId);
|
|
533
|
+
|
|
534
|
+
const ext = format === 'json' ? 'json' : format === 'md' ? 'md' : 'txt';
|
|
535
|
+
const safeName = (chat.title || 'chat').replace(/[^a-z0-9_\-]/gi, '_').slice(0, 60);
|
|
536
|
+
const filename = `${safeName}_${chatId.slice(0, 8)}.${ext}`;
|
|
537
|
+
const mimeType =
|
|
538
|
+
format === 'json' ? 'application/json' : format === 'md' ? 'text/markdown' : 'text/plain';
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
filename,
|
|
542
|
+
content: formatChatExport(chat, msgs, format),
|
|
543
|
+
mimeType,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Export all chats for the current user as an array of export objects.
|
|
549
|
+
* @param {'json'|'md'|'txt'} format
|
|
550
|
+
* @returns {Promise<Array<{ filename: string, content: string, mimeType: string }>>}
|
|
551
|
+
*/
|
|
552
|
+
export async function exportAllChats(format = 'md') {
|
|
553
|
+
const user = await requireAuth();
|
|
554
|
+
const { or, eq, desc } = await import('drizzle-orm');
|
|
555
|
+
const { getDb } = await import('../db/index.js');
|
|
556
|
+
const { chats } = await import('../db/schema.js');
|
|
557
|
+
const db = getDb();
|
|
558
|
+
const userChats = db
|
|
559
|
+
.select()
|
|
560
|
+
.from(chats)
|
|
561
|
+
.where(or(eq(chats.userId, user.id), eq(chats.userId, 'telegram')))
|
|
562
|
+
.orderBy(desc(chats.updatedAt))
|
|
563
|
+
.all();
|
|
564
|
+
|
|
565
|
+
const ext = format === 'json' ? 'json' : format === 'md' ? 'md' : 'txt';
|
|
566
|
+
const mimeType =
|
|
567
|
+
format === 'json' ? 'application/json' : format === 'md' ? 'text/markdown' : 'text/plain';
|
|
568
|
+
|
|
569
|
+
return userChats.map((chat) => {
|
|
570
|
+
const msgs = getMessagesByChatId(chat.id);
|
|
571
|
+
const safeName = (chat.title || 'chat').replace(/[^a-z0-9_\-]/gi, '_').slice(0, 60);
|
|
572
|
+
const filename = `${safeName}_${chat.id.slice(0, 8)}.${ext}`;
|
|
573
|
+
return {
|
|
574
|
+
filename,
|
|
575
|
+
content: formatChatExport(chat, msgs, format),
|
|
576
|
+
mimeType,
|
|
577
|
+
};
|
|
578
|
+
});
|
|
579
|
+
}
|