@ian2018cs/agenthub 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/LICENSE +675 -0
  2. package/README.md +330 -0
  3. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  4. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  5. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  6. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  7. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  8. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  11. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  12. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  13. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  14. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  17. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  18. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  19. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  20. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  21. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  22. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  23. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  24. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  25. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  26. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  27. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  28. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  29. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  30. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  31. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  32. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  33. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  34. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  35. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  36. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  37. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  38. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  45. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  46. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  47. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  48. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  49. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  50. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  51. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  52. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  53. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  54. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  55. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  56. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  57. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  58. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  59. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  60. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  61. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  62. package/dist/assets/index-B4ru3EJb.css +32 -0
  63. package/dist/assets/index-DDFuyrpY.js +154 -0
  64. package/dist/assets/vendor-codemirror-C_VWDoZS.js +39 -0
  65. package/dist/assets/vendor-icons-CJV4dnDL.js +326 -0
  66. package/dist/assets/vendor-katex-DK8hFnhL.js +261 -0
  67. package/dist/assets/vendor-markdown-VwNYkg_0.js +35 -0
  68. package/dist/assets/vendor-react-BeVl62c0.js +59 -0
  69. package/dist/assets/vendor-syntax-CdGaPJRS.js +16 -0
  70. package/dist/assets/vendor-utils-00TdZexr.js +1 -0
  71. package/dist/assets/vendor-xterm-CvdiG4-n.js +66 -0
  72. package/dist/clear-cache.html +85 -0
  73. package/dist/convert-icons.md +53 -0
  74. package/dist/favicon.png +0 -0
  75. package/dist/favicon.svg +9 -0
  76. package/dist/generate-icons.js +49 -0
  77. package/dist/icons/claude-ai-icon.svg +1 -0
  78. package/dist/icons/codex-white.svg +3 -0
  79. package/dist/icons/codex.svg +3 -0
  80. package/dist/icons/cursor-white.svg +12 -0
  81. package/dist/icons/cursor.svg +1 -0
  82. package/dist/icons/generate-icons.md +19 -0
  83. package/dist/icons/icon-128x128.png +0 -0
  84. package/dist/icons/icon-128x128.svg +12 -0
  85. package/dist/icons/icon-144x144.png +0 -0
  86. package/dist/icons/icon-144x144.svg +12 -0
  87. package/dist/icons/icon-152x152.png +0 -0
  88. package/dist/icons/icon-152x152.svg +12 -0
  89. package/dist/icons/icon-192x192.png +0 -0
  90. package/dist/icons/icon-192x192.svg +12 -0
  91. package/dist/icons/icon-384x384.png +0 -0
  92. package/dist/icons/icon-384x384.svg +12 -0
  93. package/dist/icons/icon-512x512.png +0 -0
  94. package/dist/icons/icon-512x512.svg +12 -0
  95. package/dist/icons/icon-72x72.png +0 -0
  96. package/dist/icons/icon-72x72.svg +12 -0
  97. package/dist/icons/icon-96x96.png +0 -0
  98. package/dist/icons/icon-96x96.svg +12 -0
  99. package/dist/icons/icon-template.svg +12 -0
  100. package/dist/index.html +57 -0
  101. package/dist/logo-128.png +0 -0
  102. package/dist/logo-256.png +0 -0
  103. package/dist/logo-32.png +0 -0
  104. package/dist/logo-512.png +0 -0
  105. package/dist/logo-64.png +0 -0
  106. package/dist/logo.svg +17 -0
  107. package/dist/manifest.json +61 -0
  108. package/dist/screenshots/cli-selection.png +0 -0
  109. package/dist/screenshots/desktop-main.png +0 -0
  110. package/dist/screenshots/mobile-chat.png +0 -0
  111. package/dist/screenshots/tools-modal.png +0 -0
  112. package/dist/sw.js +49 -0
  113. package/package.json +113 -0
  114. package/server/claude-sdk.js +791 -0
  115. package/server/cli.js +330 -0
  116. package/server/database/auth.db +0 -0
  117. package/server/database/db.js +523 -0
  118. package/server/database/init.sql +23 -0
  119. package/server/index.js +1678 -0
  120. package/server/load-env.js +27 -0
  121. package/server/middleware/auth.js +118 -0
  122. package/server/projects.js +899 -0
  123. package/server/routes/admin.js +89 -0
  124. package/server/routes/auth.js +144 -0
  125. package/server/routes/commands.js +570 -0
  126. package/server/routes/mcp-utils.js +37 -0
  127. package/server/routes/mcp.js +593 -0
  128. package/server/routes/projects.js +216 -0
  129. package/server/routes/skills.js +891 -0
  130. package/server/routes/usage.js +206 -0
  131. package/server/services/pricing.js +196 -0
  132. package/server/services/usage-scanner.js +283 -0
  133. package/server/services/user-directories.js +123 -0
  134. package/server/utils/commandParser.js +303 -0
  135. package/server/utils/mcp-detector.js +73 -0
  136. package/shared/modelConstants.js +23 -0
@@ -0,0 +1,216 @@
1
+ import express from 'express';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { spawn } from 'child_process';
5
+ import { addProjectManually } from '../projects.js';
6
+ import { getUserPaths } from '../services/user-directories.js';
7
+
8
+ const router = express.Router();
9
+
10
+ // Project name validation: letters, numbers, hyphens, underscores, 1-100 characters
11
+ const PROJECT_NAME_REGEX = /^[a-zA-Z0-9_-]{1,100}$/;
12
+
13
+ // Trusted git hosting domains
14
+ const TRUSTED_GIT_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.org'];
15
+
16
+ /**
17
+ * Validates a project name
18
+ * @param {string} name - The project name to validate
19
+ * @returns {{valid: boolean, error?: string}}
20
+ */
21
+ function validateProjectName(name) {
22
+ if (!name || typeof name !== 'string') {
23
+ return { valid: false, error: 'Project name is required' };
24
+ }
25
+
26
+ if (!PROJECT_NAME_REGEX.test(name)) {
27
+ return {
28
+ valid: false,
29
+ error: 'Project name must be 1-100 characters and contain only letters, numbers, hyphens, and underscores'
30
+ };
31
+ }
32
+
33
+ return { valid: true };
34
+ }
35
+
36
+ /**
37
+ * Validates a GitHub/Git repository URL
38
+ * @param {string} url - The repository URL to validate
39
+ * @returns {{valid: boolean, error?: string}}
40
+ */
41
+ function validateGitHubUrl(url) {
42
+ if (!url || typeof url !== 'string') {
43
+ return { valid: false, error: 'Repository URL is required' };
44
+ }
45
+
46
+ // Parse the URL
47
+ let parsedUrl;
48
+ try {
49
+ parsedUrl = new URL(url);
50
+ } catch {
51
+ return { valid: false, error: 'Invalid URL format' };
52
+ }
53
+
54
+ // Only allow HTTPS protocol
55
+ if (parsedUrl.protocol !== 'https:') {
56
+ return { valid: false, error: 'Only HTTPS URLs are allowed for security reasons' };
57
+ }
58
+
59
+ // Check if host is a trusted git hosting provider
60
+ const host = parsedUrl.hostname.toLowerCase();
61
+ if (!TRUSTED_GIT_HOSTS.includes(host)) {
62
+ return {
63
+ valid: false,
64
+ error: `Only trusted git hosts are allowed: ${TRUSTED_GIT_HOSTS.join(', ')}`
65
+ };
66
+ }
67
+
68
+ return { valid: true };
69
+ }
70
+
71
+ /**
72
+ * Create a new project
73
+ * POST /api/projects/create-workspace
74
+ *
75
+ * Body:
76
+ * - name: string (project name, 1-100 chars, alphanumeric with hyphens/underscores)
77
+ * - githubUrl?: string (optional, for cloning a public repository)
78
+ */
79
+ router.post('/create-workspace', async (req, res) => {
80
+ try {
81
+ const { name, githubUrl } = req.body;
82
+
83
+ // Validate project name
84
+ const nameValidation = validateProjectName(name);
85
+ if (!nameValidation.valid) {
86
+ return res.status(400).json({ error: nameValidation.error });
87
+ }
88
+
89
+ // Get user UUID from authenticated request
90
+ const userUuid = req.user?.uuid;
91
+ if (!userUuid) {
92
+ return res.status(401).json({ error: 'User authentication required' });
93
+ }
94
+
95
+ // Get user's projects directory
96
+ const { projectsDir } = getUserPaths(userUuid);
97
+
98
+ // Calculate absolute path for the new project
99
+ const absolutePath = path.join(projectsDir, name);
100
+
101
+ // Check if directory already exists
102
+ try {
103
+ await fs.access(absolutePath);
104
+ return res.status(400).json({
105
+ error: 'A project with this name already exists. Please choose a different name.'
106
+ });
107
+ } catch (error) {
108
+ if (error.code !== 'ENOENT') {
109
+ throw error;
110
+ }
111
+ // Path doesn't exist - good, we can create it
112
+ }
113
+
114
+ // If GitHub URL is provided, validate it BEFORE creating the directory
115
+ if (githubUrl) {
116
+ const urlValidation = validateGitHubUrl(githubUrl);
117
+ if (!urlValidation.valid) {
118
+ return res.status(400).json({ error: urlValidation.error });
119
+ }
120
+ }
121
+
122
+ // Create the project directory (also creates parent directories if needed)
123
+ await fs.mkdir(absolutePath, { recursive: true });
124
+
125
+ // If GitHub URL is provided, clone the repository (public repos only)
126
+ if (githubUrl) {
127
+
128
+ try {
129
+ await cloneGitHubRepository(githubUrl, absolutePath);
130
+ } catch (error) {
131
+ // Clean up created directory on failure
132
+ try {
133
+ await fs.rm(absolutePath, { recursive: true, force: true });
134
+ } catch (cleanupError) {
135
+ console.error('Failed to clean up directory after clone failure:', cleanupError);
136
+ }
137
+ throw new Error(`Failed to clone repository: ${error.message}`);
138
+ }
139
+ }
140
+
141
+ // Add the new project to the project list
142
+ const project = await addProjectManually(absolutePath, null, userUuid);
143
+
144
+ return res.json({
145
+ success: true,
146
+ project,
147
+ message: githubUrl
148
+ ? 'Project created and repository cloned successfully'
149
+ : 'Project created successfully'
150
+ });
151
+
152
+ } catch (error) {
153
+ console.error('Error creating project:', error);
154
+ res.status(500).json({
155
+ error: error.message || 'Failed to create project',
156
+ details: process.env.NODE_ENV === 'development' ? error.stack : undefined
157
+ });
158
+ }
159
+ });
160
+
161
+ /**
162
+ * Helper function to clone a GitHub repository (public repos only)
163
+ */
164
+ function cloneGitHubRepository(githubUrl, destinationPath) {
165
+ return new Promise((resolve, reject) => {
166
+ const gitProcess = spawn('git', ['clone', githubUrl, destinationPath], {
167
+ stdio: ['ignore', 'pipe', 'pipe'],
168
+ env: {
169
+ ...process.env,
170
+ GIT_TERMINAL_PROMPT: '0' // Disable git password prompts
171
+ }
172
+ });
173
+
174
+ let stdout = '';
175
+ let stderr = '';
176
+
177
+ gitProcess.stdout.on('data', (data) => {
178
+ stdout += data.toString();
179
+ });
180
+
181
+ gitProcess.stderr.on('data', (data) => {
182
+ stderr += data.toString();
183
+ });
184
+
185
+ gitProcess.on('close', (code) => {
186
+ if (code === 0) {
187
+ resolve({ stdout, stderr });
188
+ } else {
189
+ // Parse git error messages to provide helpful feedback
190
+ let errorMessage = 'Git clone failed';
191
+
192
+ if (stderr.includes('Authentication failed') || stderr.includes('could not read Username')) {
193
+ errorMessage = 'Authentication failed. Please check your GitHub token.';
194
+ } else if (stderr.includes('Repository not found')) {
195
+ errorMessage = 'Repository not found. Please check the URL and ensure you have access.';
196
+ } else if (stderr.includes('already exists')) {
197
+ errorMessage = 'Directory already exists';
198
+ } else if (stderr) {
199
+ errorMessage = stderr;
200
+ }
201
+
202
+ reject(new Error(errorMessage));
203
+ }
204
+ });
205
+
206
+ gitProcess.on('error', (error) => {
207
+ if (error.code === 'ENOENT') {
208
+ reject(new Error('Git is not installed or not in PATH'));
209
+ } else {
210
+ reject(error);
211
+ }
212
+ });
213
+ });
214
+ }
215
+
216
+ export default router;