@assistkick/create 1.9.0 → 1.11.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/dist/src/scaffolder.d.ts +12 -1
- package/dist/src/scaffolder.js +40 -3
- package/dist/src/scaffolder.js.map +1 -1
- package/package.json +1 -1
- package/templates/assistkick-product-system/package.json +1 -1
- package/templates/assistkick-product-system/packages/backend/package.json +1 -0
- package/templates/assistkick-product-system/packages/backend/src/mcp/permission_mcp_server.ts +196 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +31 -7
- package/templates/assistkick-product-system/packages/backend/src/routes/auth.ts +15 -12
- package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.test.ts +95 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.ts +97 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/chat_permission.ts +94 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/chat_sessions.ts +189 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.test.ts +131 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.ts +94 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +12 -3
- package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +2 -2
- package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +391 -23
- package/templates/assistkick-product-system/packages/backend/src/routes/git_branches.test.ts +306 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/git_connect.test.ts +133 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +66 -9
- package/templates/assistkick-product-system/packages/backend/src/routes/preview.ts +204 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/projects.test.ts +205 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +37 -9
- package/templates/assistkick-product-system/packages/backend/src/routes/skills.test.ts +139 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/skills.ts +95 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +5 -4
- package/templates/assistkick-product-system/packages/backend/src/routes/users.ts +4 -4
- package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +8 -8
- package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +5 -5
- package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +6 -6
- package/templates/assistkick-product-system/packages/backend/src/server.ts +107 -27
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +105 -203
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +76 -266
- package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.test.ts +427 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.ts +345 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.test.ts +170 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.ts +106 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.test.ts +217 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.ts +188 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.test.ts +1243 -0
- package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.ts +894 -0
- package/templates/assistkick-product-system/packages/backend/src/services/coherence-review.ts +3 -3
- package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.test.ts +85 -0
- package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.ts +54 -0
- package/templates/assistkick-product-system/packages/backend/src/services/email_service.ts +13 -10
- package/templates/assistkick-product-system/packages/backend/src/services/init.ts +11 -3
- package/templates/assistkick-product-system/packages/backend/src/services/invitation_service.ts +1 -1
- package/templates/assistkick-product-system/packages/backend/src/services/password_reset_service.ts +1 -1
- package/templates/assistkick-product-system/packages/backend/src/services/permission_service.test.ts +243 -0
- package/templates/assistkick-product-system/packages/backend/src/services/permission_service.ts +259 -0
- package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.test.ts +172 -0
- package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.ts +225 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +29 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +17 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +255 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +300 -25
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +44 -0
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +62 -7
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +77 -6
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +149 -14
- package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +2 -1
- package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.test.ts +45 -0
- package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.ts +157 -0
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +4 -3
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +3 -3
- package/templates/assistkick-product-system/packages/frontend/package.json +5 -0
- package/templates/assistkick-product-system/packages/frontend/src/App.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +336 -5
- package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +192 -12
- package/templates/assistkick-product-system/packages/frontend/src/components/AttachmentPreviewList.tsx +98 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/AutocompleteDropdown.tsx +65 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatAttachButton.tsx +56 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatDropZone.tsx +80 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageBubble.tsx +155 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageContent.tsx +182 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageInput.tsx +233 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatSessionSidebar.tsx +218 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatStopButton.tsx +32 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatTodoSidebar.tsx +113 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ChatView.tsx +842 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/CommitMessageModal.tsx +82 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/DiagramOverlay.tsx +160 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +5 -5
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +9 -10
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +5 -5
- package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +112 -41
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +2 -2
- package/templates/assistkick-product-system/packages/frontend/src/components/HighlightedText.tsx +87 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ImageLightbox.tsx +192 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +2 -2
- package/templates/assistkick-product-system/packages/frontend/src/components/MentionPill.tsx +33 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/MermaidBlock.tsx +148 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/PermissionDialog.tsx +91 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/PermissionModeSelector.tsx +229 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +249 -83
- package/templates/assistkick-product-system/packages/frontend/src/components/QueuedMessageBubble.tsx +38 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +212 -117
- package/templates/assistkick-product-system/packages/frontend/src/components/SystemPromptAccordion.tsx +48 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/TaskIcon.tsx +11 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +25 -9
- package/templates/assistkick-product-system/packages/frontend/src/components/ToolDiffView.tsx +114 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ToolResultCard.tsx +87 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ToolUseCard.tsx +149 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +25 -8
- package/templates/assistkick-product-system/packages/frontend/src/components/UnifiedGitWidget.tsx +722 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +2 -1
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/ProgrammableNode.tsx +178 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +3 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +103 -9
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +26 -2
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +42 -1
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useDocumentTitle.ts +11 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +1 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/use_chat_stream.ts +826 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/use_file_tree_cache.ts +69 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/use_mention_autocomplete.ts +284 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.test.ts +183 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.ts +150 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.test.ts +305 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.ts +113 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.test.ts +157 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.ts +95 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.test.ts +65 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.ts +110 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/message_queue.ts +66 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.test.ts +124 -0
- package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.ts +112 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/ChatRoute.tsx +8 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +0 -4
- package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/accept_invitation.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/forgot_password.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/login.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/register.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/reset_password.tsx +2 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useAttachmentStore.ts +66 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useChatSessionStore.ts +107 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useMessageQueueStore.ts +110 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/usePreviewStore.ts +78 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +7 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +6 -1
- package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +30 -357
- package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.test.ts +115 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.ts +91 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.test.ts +30 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.ts +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0015_magenta_jazinda.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0016_giant_xorn.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0017_sloppy_mentor.sql +6 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0018_vengeful_kabuki.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0019_careful_sentinels.sql +8 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0020_clever_spot.sql +27 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0021_graceful_hex.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0022_short_kingpin.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0023_ambiguous_sharon_carter.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0024_fat_unus.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0015_snapshot.json +1552 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0016_snapshot.json +1560 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0017_snapshot.json +1598 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0018_snapshot.json +1657 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0019_snapshot.json +1709 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0020_snapshot.json +1733 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0021_snapshot.json +1740 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0022_snapshot.json +1755 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0023_snapshot.json +1762 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0024_snapshot.json +1769 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
- package/templates/assistkick-product-system/packages/shared/db/schema.ts +40 -1
- package/templates/assistkick-product-system/packages/shared/lib/claude-service.test.ts +236 -0
- package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +46 -5
- package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +65 -39
- package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.test.ts +173 -0
- package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.ts +213 -0
- package/templates/assistkick-product-system/packages/shared/lib/validator.test.ts +70 -0
- package/templates/assistkick-product-system/packages/shared/lib/validator.ts +17 -1
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +803 -27
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +502 -68
- package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +4 -4
- package/templates/assistkick-product-system/packages/shared/package.json +2 -1
- package/templates/assistkick-product-system/packages/shared/test_fixtures/hanging_stream.mjs +46 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +44 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +7 -0
- package/templates/assistkick-product-system/packages/shared/tools/remove_node.ts +2 -1
- package/templates/assistkick-product-system/packages/shared/tools/resolve_question.ts +2 -1
- package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -1
- package/templates/assistkick-product-system/tests/message_queue.test.ts +178 -0
- package/templates/assistkick-product-system/tests/message_queue_per_session.test.ts +143 -0
- package/templates/skills/assistkick-bootstrap/SKILL.md +26 -26
- package/templates/skills/assistkick-code-reviewer/SKILL.md +45 -46
- package/templates/skills/assistkick-db-explorer/SKILL.md +13 -13
- package/templates/skills/assistkick-debugger/SKILL.md +23 -23
- package/templates/skills/assistkick-developer/SKILL.md +59 -63
- package/templates/skills/assistkick-interview/SKILL.md +26 -26
- package/templates/skills/assistkick-video-composition-agent/SKILL.md +231 -0
- package/templates/skills/assistkick-video-script-writer/SKILL.md +136 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills routes — lists available skills from project workspace.
|
|
3
|
+
* GET /api/projects/:id/skills — scans .claude/skills/ directory
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Router } from 'express';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import type { ProjectWorkspaceService } from '../services/project_workspace_service.js';
|
|
11
|
+
|
|
12
|
+
interface SkillRoutesDeps {
|
|
13
|
+
workspaceService: ProjectWorkspaceService;
|
|
14
|
+
log: (tag: string, ...args: unknown[]) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SkillInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
folderName: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse a SKILL.md file to extract name and description.
|
|
25
|
+
* Looks for a # heading for name and the first non-empty, non-heading line
|
|
26
|
+
* after for description.
|
|
27
|
+
*/
|
|
28
|
+
export const parseSkillMd = (content: string): { name: string; description: string } => {
|
|
29
|
+
const lines = content.split('\n');
|
|
30
|
+
let name = '';
|
|
31
|
+
let description = '';
|
|
32
|
+
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
const trimmed = line.trim();
|
|
35
|
+
if (!name && trimmed.startsWith('# ')) {
|
|
36
|
+
name = trimmed.slice(2).trim();
|
|
37
|
+
} else if (name && !description && trimmed && !trimmed.startsWith('#')) {
|
|
38
|
+
description = trimmed;
|
|
39
|
+
}
|
|
40
|
+
if (name && description) break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { name, description };
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const createSkillRoutes = ({ workspaceService, log }: SkillRoutesDeps): Router => {
|
|
47
|
+
const router: Router = Router({ mergeParams: true });
|
|
48
|
+
|
|
49
|
+
router.get('/', async (req, res) => {
|
|
50
|
+
const { id } = req.params;
|
|
51
|
+
log('SKILLS', `GET /api/projects/${id}/skills`);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const wsPath = workspaceService.getWorkspacePath(id);
|
|
55
|
+
const skillsDir = join(wsPath, '.claude', 'skills');
|
|
56
|
+
|
|
57
|
+
if (!existsSync(skillsDir)) {
|
|
58
|
+
res.json({ skills: [] });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
63
|
+
const skills: SkillInfo[] = [];
|
|
64
|
+
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
if (!entry.isDirectory()) continue;
|
|
67
|
+
|
|
68
|
+
const skillMdPath = join(skillsDir, entry.name, 'SKILL.md');
|
|
69
|
+
let name = entry.name;
|
|
70
|
+
let description = '';
|
|
71
|
+
|
|
72
|
+
if (existsSync(skillMdPath)) {
|
|
73
|
+
try {
|
|
74
|
+
const content = await readFile(skillMdPath, 'utf-8');
|
|
75
|
+
const parsed = parseSkillMd(content);
|
|
76
|
+
if (parsed.name) name = parsed.name;
|
|
77
|
+
description = parsed.description;
|
|
78
|
+
} catch {
|
|
79
|
+
// Use folder name as fallback
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
skills.push({ name, description, folderName: entry.name });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
res.json({ skills });
|
|
87
|
+
} catch (err: unknown) {
|
|
88
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
89
|
+
log('SKILLS', `Failed to list skills: ${message}`);
|
|
90
|
+
res.status(500).json({ error: `Failed to list skills: ${message}` });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return router;
|
|
95
|
+
};
|
|
@@ -43,7 +43,7 @@ export const createTerminalRoutes = ({ ptyManager, log }: TerminalRoutesDeps): R
|
|
|
43
43
|
|
|
44
44
|
// POST /api/terminal/sessions
|
|
45
45
|
router.post('/sessions', async (req, res) => {
|
|
46
|
-
const { projectId, projectName } = req.body;
|
|
46
|
+
const { projectId, projectName, sessionType } = req.body;
|
|
47
47
|
|
|
48
48
|
if (!projectId || typeof projectId !== 'string') {
|
|
49
49
|
res.status(400).json({ error: 'projectId is required' });
|
|
@@ -54,8 +54,9 @@ export const createTerminalRoutes = ({ ptyManager, log }: TerminalRoutesDeps): R
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const
|
|
58
|
-
|
|
57
|
+
const type = sessionType === 'terminal' ? 'terminal' as const : 'claude' as const;
|
|
58
|
+
const session = await ptyManager.createSession(projectId.trim(), projectName.trim(), 80, 24, type);
|
|
59
|
+
log('TERMINAL', `Created ${type} session "${session.name}" for project ${projectId}`);
|
|
59
60
|
res.status(201).json({ session });
|
|
60
61
|
});
|
|
61
62
|
|
|
@@ -93,7 +94,7 @@ export const createTerminalRoutes = ({ ptyManager, log }: TerminalRoutesDeps): R
|
|
|
93
94
|
log('TERMINAL', `Uploaded image: ${filePath} (${body.length} bytes)`);
|
|
94
95
|
res.json({ path: filePath });
|
|
95
96
|
} catch (err: any) {
|
|
96
|
-
log('TERMINAL', `Image upload failed
|
|
97
|
+
log('TERMINAL', `Image upload failed:`, err.stack || err.message);
|
|
97
98
|
res.status(500).json({ error: 'Failed to save image' });
|
|
98
99
|
}
|
|
99
100
|
},
|
|
@@ -37,7 +37,7 @@ export const createUserRoutes = ({ userManagementService, log }: UserRoutesDeps)
|
|
|
37
37
|
const userList = await userManagementService.listUsers();
|
|
38
38
|
res.json({ users: userList });
|
|
39
39
|
} catch (err: any) {
|
|
40
|
-
log('USERS', `List users failed
|
|
40
|
+
log('USERS', `List users failed:`, err.stack || err.message);
|
|
41
41
|
res.status(500).json({ error: 'Failed to list users' });
|
|
42
42
|
}
|
|
43
43
|
});
|
|
@@ -52,7 +52,7 @@ export const createUserRoutes = ({ userManagementService, log }: UserRoutesDeps)
|
|
|
52
52
|
await userManagementService.deleteUser(id, user.id);
|
|
53
53
|
res.json({ message: 'User deleted' });
|
|
54
54
|
} catch (err: any) {
|
|
55
|
-
log('USERS', `Delete user failed
|
|
55
|
+
log('USERS', `Delete user failed:`, err.stack || err.message);
|
|
56
56
|
if (err.message === 'Cannot delete your own account') {
|
|
57
57
|
return res.status(400).json({ error: err.message });
|
|
58
58
|
}
|
|
@@ -71,7 +71,7 @@ export const createUserRoutes = ({ userManagementService, log }: UserRoutesDeps)
|
|
|
71
71
|
const invitationList = await userManagementService.listInvitations();
|
|
72
72
|
res.json({ invitations: invitationList });
|
|
73
73
|
} catch (err: any) {
|
|
74
|
-
log('USERS', `List invitations failed
|
|
74
|
+
log('USERS', `List invitations failed:`, err.stack || err.message);
|
|
75
75
|
res.status(500).json({ error: 'Failed to list invitations' });
|
|
76
76
|
}
|
|
77
77
|
});
|
|
@@ -85,7 +85,7 @@ export const createUserRoutes = ({ userManagementService, log }: UserRoutesDeps)
|
|
|
85
85
|
await userManagementService.deleteInvitation(id);
|
|
86
86
|
res.json({ message: 'Invitation deleted' });
|
|
87
87
|
} catch (err: any) {
|
|
88
|
-
log('USERS', `Delete invitation failed
|
|
88
|
+
log('USERS', `Delete invitation failed:`, err.stack || err.message);
|
|
89
89
|
if (err.message === 'Invitation not found') {
|
|
90
90
|
return res.status(404).json({ error: err.message });
|
|
91
91
|
}
|
|
@@ -65,7 +65,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
65
65
|
|
|
66
66
|
res.json(result);
|
|
67
67
|
} catch (err: any) {
|
|
68
|
-
log('VIDEO', `TTS generation failed
|
|
68
|
+
log('VIDEO', `TTS generation failed:`, err.stack || err.message);
|
|
69
69
|
res.status(500).json({ error: err.message });
|
|
70
70
|
}
|
|
71
71
|
});
|
|
@@ -77,7 +77,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
77
77
|
const compositions = bundleService.listCompositions();
|
|
78
78
|
res.json({ compositions });
|
|
79
79
|
} catch (err: any) {
|
|
80
|
-
log('VIDEO', `Failed to list compositions
|
|
80
|
+
log('VIDEO', `Failed to list compositions:`, err.stack || err.message);
|
|
81
81
|
res.status(500).json({ error: 'Failed to list compositions' });
|
|
82
82
|
}
|
|
83
83
|
});
|
|
@@ -95,7 +95,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
95
95
|
const status = await bundleService.buildBundle();
|
|
96
96
|
res.json(status);
|
|
97
97
|
} catch (err: any) {
|
|
98
|
-
log('VIDEO', `Bundle rebuild failed
|
|
98
|
+
log('VIDEO', `Bundle rebuild failed:`, err.stack || err.message);
|
|
99
99
|
res.status(500).json({ error: 'Bundle rebuild failed' });
|
|
100
100
|
}
|
|
101
101
|
});
|
|
@@ -114,7 +114,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
114
114
|
const renders = await videoRenderService.listRenders(projectId);
|
|
115
115
|
res.json({ renders });
|
|
116
116
|
} catch (err: any) {
|
|
117
|
-
log('VIDEO', `Failed to list renders
|
|
117
|
+
log('VIDEO', `Failed to list renders:`, err.stack || err.message);
|
|
118
118
|
res.status(500).json({ error: 'Failed to list renders' });
|
|
119
119
|
}
|
|
120
120
|
});
|
|
@@ -151,7 +151,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
151
151
|
|
|
152
152
|
res.json(status);
|
|
153
153
|
} catch (err: any) {
|
|
154
|
-
log('VIDEO', `Render start failed
|
|
154
|
+
log('VIDEO', `Render start failed:`, err.stack || err.message);
|
|
155
155
|
res.status(500).json({ error: err.message });
|
|
156
156
|
}
|
|
157
157
|
});
|
|
@@ -169,7 +169,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
169
169
|
}
|
|
170
170
|
res.json(status);
|
|
171
171
|
} catch (err: any) {
|
|
172
|
-
log('VIDEO', `Failed to get render status
|
|
172
|
+
log('VIDEO', `Failed to get render status:`, err.stack || err.message);
|
|
173
173
|
res.status(500).json({ error: err.message });
|
|
174
174
|
}
|
|
175
175
|
});
|
|
@@ -187,7 +187,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
187
187
|
}
|
|
188
188
|
res.json({ ok: true });
|
|
189
189
|
} catch (err: any) {
|
|
190
|
-
log('VIDEO', `Failed to delete render
|
|
190
|
+
log('VIDEO', `Failed to delete render:`, err.stack || err.message);
|
|
191
191
|
res.status(500).json({ error: err.message });
|
|
192
192
|
}
|
|
193
193
|
});
|
|
@@ -205,7 +205,7 @@ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderServic
|
|
|
205
205
|
}
|
|
206
206
|
res.sendFile(filePath);
|
|
207
207
|
} catch (err: any) {
|
|
208
|
-
log('VIDEO', `Failed to serve render file
|
|
208
|
+
log('VIDEO', `Failed to serve render file:`, err.stack || err.message);
|
|
209
209
|
res.status(500).json({ error: err.message });
|
|
210
210
|
}
|
|
211
211
|
});
|
|
@@ -27,7 +27,7 @@ export const createWorkflowGroupRoutes = ({ workflowGroupService, log }: Workflo
|
|
|
27
27
|
const groups = await workflowGroupService.list(projectId);
|
|
28
28
|
res.json({ groups });
|
|
29
29
|
} catch (err: any) {
|
|
30
|
-
log('WORKFLOW_GROUPS', `List groups failed
|
|
30
|
+
log('WORKFLOW_GROUPS', `List groups failed:`, err.stack || err.message);
|
|
31
31
|
res.status(500).json({ error: 'Failed to list workflow groups' });
|
|
32
32
|
}
|
|
33
33
|
});
|
|
@@ -45,7 +45,7 @@ export const createWorkflowGroupRoutes = ({ workflowGroupService, log }: Workflo
|
|
|
45
45
|
}
|
|
46
46
|
res.json({ group });
|
|
47
47
|
} catch (err: any) {
|
|
48
|
-
log('WORKFLOW_GROUPS', `Get group failed
|
|
48
|
+
log('WORKFLOW_GROUPS', `Get group failed:`, err.stack || err.message);
|
|
49
49
|
res.status(500).json({ error: 'Failed to get workflow group' });
|
|
50
50
|
}
|
|
51
51
|
});
|
|
@@ -73,7 +73,7 @@ export const createWorkflowGroupRoutes = ({ workflowGroupService, log }: Workflo
|
|
|
73
73
|
});
|
|
74
74
|
res.status(201).json({ group });
|
|
75
75
|
} catch (err: any) {
|
|
76
|
-
log('WORKFLOW_GROUPS', `Create group failed
|
|
76
|
+
log('WORKFLOW_GROUPS', `Create group failed:`, err.stack || err.message);
|
|
77
77
|
res.status(500).json({ error: 'Failed to create workflow group' });
|
|
78
78
|
}
|
|
79
79
|
});
|
|
@@ -88,7 +88,7 @@ export const createWorkflowGroupRoutes = ({ workflowGroupService, log }: Workflo
|
|
|
88
88
|
const group = await workflowGroupService.update(id, { name, graphData });
|
|
89
89
|
res.json({ group });
|
|
90
90
|
} catch (err: any) {
|
|
91
|
-
log('WORKFLOW_GROUPS', `Update group failed
|
|
91
|
+
log('WORKFLOW_GROUPS', `Update group failed:`, err.stack || err.message);
|
|
92
92
|
if (err.message === 'Workflow group not found') {
|
|
93
93
|
res.status(404).json({ error: err.message });
|
|
94
94
|
return;
|
|
@@ -106,7 +106,7 @@ export const createWorkflowGroupRoutes = ({ workflowGroupService, log }: Workflo
|
|
|
106
106
|
await workflowGroupService.delete(id);
|
|
107
107
|
res.json({ success: true });
|
|
108
108
|
} catch (err: any) {
|
|
109
|
-
log('WORKFLOW_GROUPS', `Delete group failed
|
|
109
|
+
log('WORKFLOW_GROUPS', `Delete group failed:`, err.stack || err.message);
|
|
110
110
|
if (err.message === 'Workflow group not found') {
|
|
111
111
|
res.status(404).json({ error: err.message });
|
|
112
112
|
return;
|
|
@@ -30,7 +30,7 @@ export const createWorkflowRoutes = ({ workflowService, log }: WorkflowRoutesDep
|
|
|
30
30
|
const workflowList = await workflowService.list(projectId, featureType, triggerColumn);
|
|
31
31
|
res.json({ workflows: workflowList });
|
|
32
32
|
} catch (err: any) {
|
|
33
|
-
log('WORKFLOWS', `List workflows failed
|
|
33
|
+
log('WORKFLOWS', `List workflows failed:`, err.stack || err.message);
|
|
34
34
|
res.status(500).json({ error: 'Failed to list workflows' });
|
|
35
35
|
}
|
|
36
36
|
});
|
|
@@ -48,7 +48,7 @@ export const createWorkflowRoutes = ({ workflowService, log }: WorkflowRoutesDep
|
|
|
48
48
|
}
|
|
49
49
|
res.json({ workflow });
|
|
50
50
|
} catch (err: any) {
|
|
51
|
-
log('WORKFLOWS', `Get workflow failed
|
|
51
|
+
log('WORKFLOWS', `Get workflow failed:`, err.stack || err.message);
|
|
52
52
|
res.status(500).json({ error: 'Failed to get workflow' });
|
|
53
53
|
}
|
|
54
54
|
});
|
|
@@ -79,7 +79,7 @@ export const createWorkflowRoutes = ({ workflowService, log }: WorkflowRoutesDep
|
|
|
79
79
|
});
|
|
80
80
|
res.status(201).json({ workflow });
|
|
81
81
|
} catch (err: any) {
|
|
82
|
-
log('WORKFLOWS', `Create workflow failed
|
|
82
|
+
log('WORKFLOWS', `Create workflow failed:`, err.stack || err.message);
|
|
83
83
|
res.status(500).json({ error: 'Failed to create workflow' });
|
|
84
84
|
}
|
|
85
85
|
});
|
|
@@ -105,7 +105,7 @@ export const createWorkflowRoutes = ({ workflowService, log }: WorkflowRoutesDep
|
|
|
105
105
|
});
|
|
106
106
|
res.json({ workflow });
|
|
107
107
|
} catch (err: any) {
|
|
108
|
-
log('WORKFLOWS', `Update workflow failed
|
|
108
|
+
log('WORKFLOWS', `Update workflow failed:`, err.stack || err.message);
|
|
109
109
|
if (err.message === 'Workflow not found') {
|
|
110
110
|
res.status(404).json({ error: err.message });
|
|
111
111
|
return;
|
|
@@ -123,7 +123,7 @@ export const createWorkflowRoutes = ({ workflowService, log }: WorkflowRoutesDep
|
|
|
123
123
|
await workflowService.delete(id);
|
|
124
124
|
res.json({ success: true });
|
|
125
125
|
} catch (err: any) {
|
|
126
|
-
log('WORKFLOWS', `Delete workflow failed
|
|
126
|
+
log('WORKFLOWS', `Delete workflow failed:`, err.stack || err.message);
|
|
127
127
|
if (err.message === 'Workflow not found') {
|
|
128
128
|
res.status(404).json({ error: err.message });
|
|
129
129
|
return;
|
|
@@ -145,7 +145,7 @@ export const createWorkflowRoutes = ({ workflowService, log }: WorkflowRoutesDep
|
|
|
145
145
|
const workflow = await workflowService.setDefault(id);
|
|
146
146
|
res.json({ workflow });
|
|
147
147
|
} catch (err: any) {
|
|
148
|
-
log('WORKFLOWS', `Set default workflow failed
|
|
148
|
+
log('WORKFLOWS', `Set default workflow failed:`, err.stack || err.message);
|
|
149
149
|
if (err.message === 'Workflow not found') {
|
|
150
150
|
res.status(404).json({ error: err.message });
|
|
151
151
|
return;
|
|
@@ -16,13 +16,17 @@ import { fileURLToPath } from 'node:url';
|
|
|
16
16
|
import { existsSync } from 'node:fs';
|
|
17
17
|
import { WebSocketServer } from 'ws';
|
|
18
18
|
import * as pty from 'node-pty';
|
|
19
|
-
import { initServices, log, githubAppService, sshKeyService, workspaceService, workflowEngine, orchestrator, workflowService as initWorkflowService, workflowGroupService as initWorkflowGroupService, bundleService, ttsService, videoRenderService } from './services/init.js';
|
|
19
|
+
import { initServices, log, logError, paths, githubAppService, sshKeyService, workspaceService, workflowEngine, orchestrator, workflowService as initWorkflowService, workflowGroupService as initWorkflowGroupService, bundleService, ttsService, videoRenderService } from './services/init.js';
|
|
20
20
|
import { AuthService } from './services/auth_service.js';
|
|
21
21
|
import { EmailService } from './services/email_service.js';
|
|
22
22
|
import { PasswordResetService } from './services/password_reset_service.js';
|
|
23
23
|
import { InvitationService } from './services/invitation_service.js';
|
|
24
24
|
import { PtySessionManager } from './services/pty_session_manager.js';
|
|
25
25
|
import { TerminalWsHandler } from './services/terminal_ws_handler.js';
|
|
26
|
+
import { ChatCliBridge } from './services/chat_cli_bridge.js';
|
|
27
|
+
import { ChatWsHandler } from './services/chat_ws_handler.js';
|
|
28
|
+
import { PermissionService } from './services/permission_service.js';
|
|
29
|
+
import { createChatPermissionRoutes } from './routes/chat_permission.js';
|
|
26
30
|
import { UserManagementService } from './services/user_management_service.js';
|
|
27
31
|
import { ProjectService } from './services/project_service.js';
|
|
28
32
|
import { AuthMiddleware } from './middleware/auth_middleware.js';
|
|
@@ -31,6 +35,7 @@ import { createUserRoutes } from './routes/users.js';
|
|
|
31
35
|
import { createProjectRoutes } from './routes/projects.js';
|
|
32
36
|
import { createTerminalRoutes } from './routes/terminal.js';
|
|
33
37
|
import { getDb } from '@assistkick/shared/lib/db.js';
|
|
38
|
+
import { sql } from 'drizzle-orm';
|
|
34
39
|
import graphRoutes from './routes/graph.js';
|
|
35
40
|
import kanbanRoutes from './routes/kanban.js';
|
|
36
41
|
import { createWorkflowExecutionRoutes } from './routes/pipeline.js';
|
|
@@ -38,10 +43,20 @@ import coherenceRoutes from './routes/coherence.js';
|
|
|
38
43
|
import { createGitRoutes } from './routes/git.js';
|
|
39
44
|
import { createFileRoutes } from './routes/files.js';
|
|
40
45
|
import { AgentService } from './services/agent_service.js';
|
|
41
|
-
import { createAgentRoutes } from './routes/agents.js';
|
|
46
|
+
import { createAgentRoutes, createSkillListRoutes } from './routes/agents.js';
|
|
42
47
|
import { createWorkflowRoutes } from './routes/workflows.js';
|
|
43
48
|
import { createWorkflowGroupRoutes } from './routes/workflow_groups.js';
|
|
44
49
|
import { createVideoRoutes } from './routes/video.js';
|
|
50
|
+
import { ChatSessionService } from './services/chat_session_service.js';
|
|
51
|
+
import { ChatMessageRepository } from './services/chat_message_repository.js';
|
|
52
|
+
import { TitleGeneratorService } from './services/title_generator_service.js';
|
|
53
|
+
import { createChatSessionRoutes } from './routes/chat_sessions.js';
|
|
54
|
+
import { createChatUploadRoutes } from './routes/chat_upload.js';
|
|
55
|
+
import { createChatFilesRoutes } from './routes/chat_files.js';
|
|
56
|
+
import { createSkillRoutes } from './routes/skills.js';
|
|
57
|
+
import { DevCommandDetector } from './services/dev_command_detector.js';
|
|
58
|
+
import { PreviewServerManager } from './services/preview_server_manager.js';
|
|
59
|
+
import { createPreviewApiRoutes, createPreviewProxyMiddleware } from './routes/preview.js';
|
|
45
60
|
|
|
46
61
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
47
62
|
const DEFAULT_PORT = parseInt(process.env.PORT || '3000', 10);
|
|
@@ -68,7 +83,7 @@ initServices(args.verbose);
|
|
|
68
83
|
const app: Express = express();
|
|
69
84
|
|
|
70
85
|
app.use(cors());
|
|
71
|
-
app.use(express.json());
|
|
86
|
+
app.use(express.json({ limit: '10mb' }));
|
|
72
87
|
app.use(cookieParser());
|
|
73
88
|
|
|
74
89
|
// Request/response logging
|
|
@@ -90,7 +105,7 @@ const authService = new AuthService({ jwtSecret, isProduction });
|
|
|
90
105
|
const resendApiKey = process.env.RESEND_API_KEY || '';
|
|
91
106
|
const appBaseUrl = process.env.APP_BASE_URL || 'https://assistkick.localhost';
|
|
92
107
|
const emailFromAddress = process.env.EMAIL_FROM || 'noreply@example.com';
|
|
93
|
-
const emailService = new EmailService({ apiKey: resendApiKey, fromAddress: emailFromAddress });
|
|
108
|
+
const emailService = new EmailService({ apiKey: resendApiKey, fromAddress: emailFromAddress, log });
|
|
94
109
|
const passwordResetService = new PasswordResetService({ getDb, emailService, authService, appBaseUrl, log });
|
|
95
110
|
const invitationService = new InvitationService({ getDb, emailService, authService, appBaseUrl, log });
|
|
96
111
|
const authRoutes = createAuthRoutes({ getDb, authService, passwordResetService, invitationService, log });
|
|
@@ -106,7 +121,7 @@ app.use('/api/users', authMiddleware.requireAuth, userRoutes);
|
|
|
106
121
|
|
|
107
122
|
// Project management routes (authenticated)
|
|
108
123
|
const projectService = new ProjectService({ getDb, log });
|
|
109
|
-
const projectRoutes = createProjectRoutes({ projectService, log });
|
|
124
|
+
const projectRoutes = createProjectRoutes({ projectService, workspaceService, log });
|
|
110
125
|
app.use('/api/projects', authMiddleware.requireAuth, projectRoutes);
|
|
111
126
|
|
|
112
127
|
// Git repository routes (nested under /api/projects/:id/git)
|
|
@@ -117,22 +132,20 @@ app.use('/api/projects/:id/git', authMiddleware.requireAuth, gitRoutes);
|
|
|
117
132
|
const fileRoutes = createFileRoutes({ workspaceService, log });
|
|
118
133
|
app.use('/api/projects/:id/files', authMiddleware.requireAuth, fileRoutes);
|
|
119
134
|
|
|
135
|
+
// Skills routes (nested under /api/projects/:id/skills)
|
|
136
|
+
const skillRoutes = createSkillRoutes({ workspaceService, log });
|
|
137
|
+
app.use('/api/projects/:id/skills', authMiddleware.requireAuth, skillRoutes);
|
|
138
|
+
|
|
120
139
|
// Agent management routes (authenticated)
|
|
121
140
|
const SKILLS_DIR = join(__dirname, '..', '..', '..', '..', '.claude', 'skills');
|
|
122
|
-
const agentService = new AgentService({
|
|
123
|
-
getDb,
|
|
124
|
-
log,
|
|
125
|
-
skillPaths: {
|
|
126
|
-
developer: join(SKILLS_DIR, 'assistkick-developer', 'SKILL.md'),
|
|
127
|
-
reviewer: join(SKILLS_DIR, 'assistkick-code-reviewer', 'SKILL.md'),
|
|
128
|
-
debugger: join(SKILLS_DIR, 'assistkick-debugger', 'SKILL.md'),
|
|
129
|
-
videoScriptWriter: join(SKILLS_DIR, 'video-script-writer', 'SKILL.md'),
|
|
130
|
-
videoCompositionAgent: join(SKILLS_DIR, 'video-composition-agent', 'SKILL.md'),
|
|
131
|
-
},
|
|
132
|
-
});
|
|
141
|
+
const agentService = new AgentService({ getDb, log, skillsDir: SKILLS_DIR });
|
|
133
142
|
const agentRoutes = createAgentRoutes({ agentService, log });
|
|
134
143
|
app.use('/api/agents', authMiddleware.requireAuth, agentRoutes);
|
|
135
144
|
|
|
145
|
+
// Skills listing route (authenticated) — scans skills directory on disk
|
|
146
|
+
const skillListRoutes = createSkillListRoutes({ agentService, log });
|
|
147
|
+
app.use('/api/skills', authMiddleware.requireAuth, skillListRoutes);
|
|
148
|
+
|
|
136
149
|
// Workflow management routes (authenticated)
|
|
137
150
|
const workflowRoutes = createWorkflowRoutes({ workflowService: initWorkflowService, log });
|
|
138
151
|
app.use('/api/workflows', authMiddleware.requireAuth, workflowRoutes);
|
|
@@ -145,16 +158,61 @@ app.use('/api/workflow-groups', authMiddleware.requireAuth, workflowGroupRoutes)
|
|
|
145
158
|
const videoRoutes = createVideoRoutes({ ttsService, bundleService, videoRenderService, log });
|
|
146
159
|
app.use('/api/video', authMiddleware.requireAuth, videoRoutes);
|
|
147
160
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
// Wait for DB connection to be ready before running startup tasks.
|
|
162
|
+
// Turso cold-starts can cause the first query to fail if fired immediately.
|
|
163
|
+
const waitForDb = async (maxRetries = 5, delayMs = 500): Promise<void> => {
|
|
164
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
165
|
+
try {
|
|
166
|
+
const db = getDb();
|
|
167
|
+
await db.select({ one: sql`1` }).from(sql`sqlite_master`).limit(1);
|
|
168
|
+
return;
|
|
169
|
+
} catch (err: any) {
|
|
170
|
+
if (i < maxRetries - 1) {
|
|
171
|
+
log('STARTUP', `DB not ready, retrying in ${delayMs}ms (${i + 1}/${maxRetries}):`, err.message);
|
|
172
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
log('STARTUP', 'DB warmup failed after retries — startup tasks may fail');
|
|
177
|
+
};
|
|
152
178
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
179
|
+
waitForDb().then(async () => {
|
|
180
|
+
try {
|
|
181
|
+
await projectService.ensureDefaultAndAssignOrphans();
|
|
182
|
+
} catch (err: any) {
|
|
183
|
+
logError('STARTUP', 'Failed to ensure default project', err);
|
|
184
|
+
}
|
|
185
|
+
}).catch((err: any) => {
|
|
186
|
+
logError('STARTUP', 'Startup tasks failed', err);
|
|
156
187
|
});
|
|
157
188
|
|
|
189
|
+
// Chat v2 session management routes (authenticated)
|
|
190
|
+
const chatSessionService = new ChatSessionService({ getDb, log });
|
|
191
|
+
const chatMessageRepository = new ChatMessageRepository({ getDb, log });
|
|
192
|
+
const chatSessionRoutes = createChatSessionRoutes({ chatSessionService, chatMessageRepository, log });
|
|
193
|
+
app.use('/api/chat-sessions', authMiddleware.requireAuth, chatSessionRoutes);
|
|
194
|
+
|
|
195
|
+
// Chat v2 file upload routes (authenticated) — base64 file upload for chat attachments
|
|
196
|
+
// Uses a larger body size limit since files are base64-encoded in JSON
|
|
197
|
+
const chatUploadRoutes = createChatUploadRoutes({ workspaceService, log });
|
|
198
|
+
app.use('/api/chat-upload', authMiddleware.requireAuth, express.json({ limit: '15mb' }), chatUploadRoutes);
|
|
199
|
+
|
|
200
|
+
// Chat v2 file serve routes (authenticated) — serves uploaded chat attachment files with caching
|
|
201
|
+
const chatFilesRoutes = createChatFilesRoutes({ workspaceService, log });
|
|
202
|
+
app.use('/api/chat-files', authMiddleware.requireAuth, chatFilesRoutes);
|
|
203
|
+
|
|
204
|
+
// Preview server manager — manages dev server lifecycle
|
|
205
|
+
const devCommandDetector = new DevCommandDetector({ log });
|
|
206
|
+
const previewManager = new PreviewServerManager({ devCommandDetector, log });
|
|
207
|
+
|
|
208
|
+
// Preview API routes (authenticated)
|
|
209
|
+
const previewApiRoutes = createPreviewApiRoutes({ previewManager, workspaceService, projectService, log });
|
|
210
|
+
app.use('/api/preview', authMiddleware.requireAuth, previewApiRoutes);
|
|
211
|
+
|
|
212
|
+
// Preview proxy middleware — must be before SPA fallback so /apps/* is intercepted
|
|
213
|
+
const previewProxy = createPreviewProxyMiddleware({ previewManager, authMiddleware, log });
|
|
214
|
+
app.use(previewProxy);
|
|
215
|
+
|
|
158
216
|
// PTY session manager — initialized early so terminal REST routes can reference it
|
|
159
217
|
// Resolve project root: from packages/backend/src → assistkick-product-system → repo root
|
|
160
218
|
const PROJECT_ROOT = join(__dirname, '..', '..', '..', '..');
|
|
@@ -196,16 +254,37 @@ if (existsSync(FRONTEND_DIST)) {
|
|
|
196
254
|
const server = createServer(app);
|
|
197
255
|
|
|
198
256
|
// Set up WebSocket for terminal
|
|
199
|
-
const
|
|
200
|
-
const terminalHandler = new TerminalWsHandler({ wss, authService, ptyManager, log });
|
|
257
|
+
const terminalWss = new WebSocketServer({ noServer: true });
|
|
258
|
+
const terminalHandler = new TerminalWsHandler({ wss: terminalWss, authService, ptyManager, log });
|
|
259
|
+
|
|
260
|
+
// Set up WebSocket for Chat v2 streaming with permission management
|
|
261
|
+
const chatWss = new WebSocketServer({ noServer: true });
|
|
262
|
+
const chatCliBridge = new ChatCliBridge({ workspacesDir: paths.workspacesDir, log });
|
|
263
|
+
const permissionService = new PermissionService({ getDb, log });
|
|
264
|
+
const titleGeneratorService = new TitleGeneratorService({ log });
|
|
265
|
+
const chatHandler = new ChatWsHandler({ wss: chatWss, authService, chatCliBridge, permissionService, chatMessageRepository, chatSessionService, titleGeneratorService, log });
|
|
266
|
+
|
|
267
|
+
// Chat permission routes — used by MCP permission server (no auth — internal only)
|
|
268
|
+
const chatPermissionRoutes = createChatPermissionRoutes({ permissionService, log });
|
|
269
|
+
app.use('/api/chat/permission', chatPermissionRoutes);
|
|
201
270
|
|
|
202
271
|
server.on('upgrade', (req, socket, head) => {
|
|
203
|
-
|
|
272
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
273
|
+
if (url.pathname === '/api/chat') {
|
|
274
|
+
chatHandler.handleUpgrade(req, socket, head);
|
|
275
|
+
} else if (url.pathname.startsWith('/apps/')) {
|
|
276
|
+
// Let http-proxy-middleware handle WebSocket upgrades for preview apps (HMR)
|
|
277
|
+
// The proxy middleware registers its own upgrade handler on the server
|
|
278
|
+
} else {
|
|
279
|
+
terminalHandler.handleUpgrade(req, socket, head);
|
|
280
|
+
}
|
|
204
281
|
});
|
|
205
282
|
|
|
206
|
-
// Clean up PTY sessions on server shutdown
|
|
283
|
+
// Clean up PTY sessions and chat CLI processes on server shutdown
|
|
207
284
|
process.on('SIGTERM', () => {
|
|
285
|
+
previewManager.stopAll();
|
|
208
286
|
ptyManager.destroyAllPty();
|
|
287
|
+
chatCliBridge.killAll();
|
|
209
288
|
server.close();
|
|
210
289
|
});
|
|
211
290
|
|
|
@@ -213,6 +292,7 @@ server.listen(args.port, () => {
|
|
|
213
292
|
log('SERVER', `API server running at http://localhost:${args.port}`);
|
|
214
293
|
log('SERVER', `Verbose mode: ${args.verbose ? 'ON' : 'OFF'}`);
|
|
215
294
|
log('SERVER', `WebSocket terminal endpoint: ws://localhost:${args.port}/api/terminal`);
|
|
295
|
+
log('SERVER', `WebSocket chat endpoint: ws://localhost:${args.port}/api/chat`);
|
|
216
296
|
if (existsSync(FRONTEND_DIST)) {
|
|
217
297
|
log('SERVER', `Serving frontend from ${FRONTEND_DIST}`);
|
|
218
298
|
} else {
|