@ian2018cs/agenthub 0.1.6 → 0.1.8

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/index.html CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  <!-- Prevent zoom on iOS -->
27
27
  <meta name="format-detection" content="telephone=no" />
28
- <script type="module" crossorigin src="/assets/index-CwlbfX7i.js"></script>
28
+ <script type="module" crossorigin src="/assets/index-BJ5g44kL.js"></script>
29
29
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BeVl62c0.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-C_VWDoZS.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/vendor-utils-00TdZexr.js">
@@ -34,7 +34,7 @@
34
34
  <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-VwNYkg_0.js">
35
35
  <link rel="modulepreload" crossorigin href="/assets/vendor-syntax-CdGaPJRS.js">
36
36
  <link rel="modulepreload" crossorigin href="/assets/vendor-xterm-CvdiG4-n.js">
37
- <link rel="stylesheet" crossorigin href="/assets/index-C6Eb2GB-.css">
37
+ <link rel="stylesheet" crossorigin href="/assets/index-B5iudMdK.css">
38
38
  </head>
39
39
  <body>
40
40
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian2018cs/agenthub",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A web-based UI for AI Agents",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
package/server/index.js CHANGED
@@ -541,28 +541,39 @@ app.put('/api/projects/:projectName/file', authenticateToken, async (req, res) =
541
541
 
542
542
  app.get('/api/projects/:projectName/files', authenticateToken, async (req, res) => {
543
543
  try {
544
-
545
- // Using fsPromises from import
544
+ // Query parameters for lazy loading
545
+ const { dirPath, depth = '1' } = req.query;
546
+ const maxDepth = Math.min(parseInt(depth) || 1, 10); // Limit max depth to 10
546
547
 
547
548
  // Use extractProjectDirectory to get the actual project path
548
- let actualPath;
549
+ let projectPath;
549
550
  try {
550
- actualPath = await extractProjectDirectory(req.params.projectName, req.user.uuid);
551
+ projectPath = await extractProjectDirectory(req.params.projectName, req.user.uuid);
551
552
  } catch (error) {
552
553
  console.error('Error extracting project directory:', error);
553
554
  // Fallback to simple dash replacement
554
- actualPath = req.params.projectName.replace(/-/g, '/');
555
+ projectPath = req.params.projectName.replace(/-/g, '/');
556
+ }
557
+
558
+ // Determine target path (project root or specific directory)
559
+ let targetPath = projectPath;
560
+ if (dirPath) {
561
+ // Ensure the requested path is within the project directory (security check)
562
+ const normalizedDirPath = path.normalize(dirPath);
563
+ if (!normalizedDirPath.startsWith(projectPath)) {
564
+ return res.status(403).json({ error: 'Access denied: path outside project directory' });
565
+ }
566
+ targetPath = normalizedDirPath;
555
567
  }
556
568
 
557
569
  // Check if path exists
558
570
  try {
559
- await fsPromises.access(actualPath);
571
+ await fsPromises.access(targetPath);
560
572
  } catch (e) {
561
- return res.status(404).json({ error: `Project path not found: ${actualPath}` });
573
+ return res.status(404).json({ error: `Path not found: ${targetPath}` });
562
574
  }
563
575
 
564
- const files = await getFileTree(actualPath, 10, 0, true);
565
- const hiddenFiles = files.filter(f => f.name.startsWith('.'));
576
+ const files = await getFileTree(targetPath, maxDepth, 0, true);
566
577
  res.json(files);
567
578
  } catch (error) {
568
579
  console.error('[ERROR] File tree error:', error.message);
@@ -1555,7 +1566,24 @@ function permToRwx(perm) {
1555
1566
  return r + w + x;
1556
1567
  }
1557
1568
 
1558
- async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = true) {
1569
+ // List of directories to skip when building file tree
1570
+ const SKIP_DIRECTORIES = new Set([
1571
+ 'node_modules', 'dist', 'build', '.git', '.svn', '.hg',
1572
+ '__pycache__', '.pytest_cache', '.venv', 'venv',
1573
+ '.next', '.nuxt', 'coverage', '.cache'
1574
+ ]);
1575
+
1576
+ // Check if a directory has any visible children (for lazy loading indicator)
1577
+ async function hasDirectoryChildren(dirPath) {
1578
+ try {
1579
+ const entries = await fsPromises.readdir(dirPath, { withFileTypes: true });
1580
+ return entries.some(entry => !SKIP_DIRECTORIES.has(entry.name));
1581
+ } catch {
1582
+ return false;
1583
+ }
1584
+ }
1585
+
1586
+ async function getFileTree(dirPath, maxDepth = 1, currentDepth = 0, showHidden = true) {
1559
1587
  // Using fsPromises from import
1560
1588
  const items = [];
1561
1589
 
@@ -1563,16 +1591,8 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden =
1563
1591
  const entries = await fsPromises.readdir(dirPath, { withFileTypes: true });
1564
1592
 
1565
1593
  for (const entry of entries) {
1566
- // Debug: log all entries including hidden files
1567
-
1568
-
1569
1594
  // Skip heavy build directories and VCS directories
1570
- if (entry.name === 'node_modules' ||
1571
- entry.name === 'dist' ||
1572
- entry.name === 'build' ||
1573
- entry.name === '.git' ||
1574
- entry.name === '.svn' ||
1575
- entry.name === '.hg') continue;
1595
+ if (SKIP_DIRECTORIES.has(entry.name)) continue;
1576
1596
 
1577
1597
  const itemPath = path.join(dirPath, entry.name);
1578
1598
  const item = {
@@ -1602,15 +1622,22 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden =
1602
1622
  item.permissionsRwx = '---------';
1603
1623
  }
1604
1624
 
1605
- if (entry.isDirectory() && currentDepth < maxDepth) {
1606
- // Recursively get subdirectories but limit depth
1607
- try {
1608
- // Check if we can access the directory before trying to read it
1609
- await fsPromises.access(item.path, fs.constants.R_OK);
1610
- item.children = await getFileTree(item.path, maxDepth, currentDepth + 1, showHidden);
1611
- } catch (e) {
1612
- // Silently skip directories we can't access (permission denied, etc.)
1613
- item.children = [];
1625
+ if (entry.isDirectory()) {
1626
+ // For lazy loading: check if directory has children without loading them
1627
+ if (currentDepth >= maxDepth) {
1628
+ // Don't load children, just check if they exist
1629
+ item.hasChildren = await hasDirectoryChildren(item.path);
1630
+ item.children = []; // Empty array, will be loaded on demand
1631
+ } else {
1632
+ // Load children up to maxDepth
1633
+ try {
1634
+ await fsPromises.access(item.path, fs.constants.R_OK);
1635
+ item.children = await getFileTree(item.path, maxDepth, currentDepth + 1, showHidden);
1636
+ item.hasChildren = item.children.length > 0;
1637
+ } catch (e) {
1638
+ item.children = [];
1639
+ item.hasChildren = false;
1640
+ }
1614
1641
  }
1615
1642
  }
1616
1643
 
@@ -81,43 +81,25 @@ async function scanCommandsDirectory(dir, baseDir, namespace) {
81
81
  const builtInCommands = [
82
82
  {
83
83
  name: '/help',
84
- description: 'Show help documentation for Claude Code',
85
- namespace: 'builtin',
86
- metadata: { type: 'builtin' }
87
- },
88
- {
89
- name: '/clear',
90
- description: 'Clear the conversation history',
84
+ description: '显示帮助文档',
91
85
  namespace: 'builtin',
92
86
  metadata: { type: 'builtin' }
93
87
  },
94
88
  {
95
89
  name: '/model',
96
- description: 'Switch or view the current AI model',
90
+ description: '切换或查看当前 AI 模型',
97
91
  namespace: 'builtin',
98
92
  metadata: { type: 'builtin' }
99
93
  },
100
94
  {
101
95
  name: '/memory',
102
- description: 'Open CLAUDE.md memory file for editing',
96
+ description: '编辑 CLAUDE.md 记忆文件',
103
97
  namespace: 'builtin',
104
98
  metadata: { type: 'builtin' }
105
99
  },
106
100
  {
107
101
  name: '/config',
108
- description: 'Open settings and configuration',
109
- namespace: 'builtin',
110
- metadata: { type: 'builtin' }
111
- },
112
- {
113
- name: '/status',
114
- description: 'Show system status and version information',
115
- namespace: 'builtin',
116
- metadata: { type: 'builtin' }
117
- },
118
- {
119
- name: '/rewind',
120
- description: 'Rewind the conversation to a previous state',
102
+ description: '打开设置和配置',
121
103
  namespace: 'builtin',
122
104
  metadata: { type: 'builtin' }
123
105
  }
@@ -129,27 +111,27 @@ const builtInCommands = [
129
111
  */
130
112
  const builtInHandlers = {
131
113
  '/help': async (args, context) => {
132
- const helpText = `# Claude Code Commands
114
+ const helpText = `# Claude Code 命令
133
115
 
134
- ## Built-in Commands
116
+ ## 内置命令
135
117
 
136
118
  ${builtInCommands.map(cmd => `### ${cmd.name}
137
119
  ${cmd.description}
138
120
  `).join('\n')}
139
121
 
140
- ## Custom Commands
122
+ ## 自定义命令
141
123
 
142
- Custom commands can be created in:
143
- - Project: \`.claude/commands/\` (project-specific)
144
- - User: \`~/.claude/commands/\` (available in all projects)
124
+ 自定义命令可以创建在:
125
+ - 项目级别:\`.claude/commands/\`(仅当前项目可用)
126
+ - 用户级别:\`~/.claude/commands/\`(所有项目可用)
145
127
 
146
- ### Command Syntax
128
+ ### 命令语法
147
129
 
148
- - **Arguments**: Use \`$ARGUMENTS\` for all args or \`$1\`, \`$2\`, etc. for positional
149
- - **File Includes**: Use \`@filename\` to include file contents
150
- - **Bash Commands**: Use \`!command\` to execute bash commands
130
+ - **参数**:使用 \`$ARGUMENTS\` 获取所有参数,或 \`$1\`、\`$2\` 等获取位置参数
131
+ - **文件包含**:使用 \`@filename\` 包含文件内容
132
+ - **Bash 命令**:使用 \`!command\` 执行 bash 命令
151
133
 
152
- ### Examples
134
+ ### 示例
153
135
 
154
136
  \`\`\`markdown
155
137
  /mycommand arg1 arg2
@@ -166,16 +148,6 @@ Custom commands can be created in:
166
148
  };
167
149
  },
168
150
 
169
- '/clear': async (args, context) => {
170
- return {
171
- type: 'builtin',
172
- action: 'clear',
173
- data: {
174
- message: 'Conversation history cleared'
175
- }
176
- };
177
- },
178
-
179
151
  '/model': async (args, context) => {
180
152
  // Read available models from centralized constants
181
153
  const availableModels = {
@@ -201,43 +173,6 @@ Custom commands can be created in:
201
173
  };
202
174
  },
203
175
 
204
- '/status': async (args, context) => {
205
- // Read version from package.json
206
- const packageJsonPath = path.join(path.dirname(__dirname), '..', 'package.json');
207
- let version = 'unknown';
208
- let packageName = 'claude-code-ui';
209
-
210
- try {
211
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
212
- version = packageJson.version;
213
- packageName = packageJson.name;
214
- } catch (err) {
215
- console.error('Error reading package.json:', err);
216
- }
217
-
218
- const uptime = process.uptime();
219
- const uptimeMinutes = Math.floor(uptime / 60);
220
- const uptimeHours = Math.floor(uptimeMinutes / 60);
221
- const uptimeFormatted = uptimeHours > 0
222
- ? `${uptimeHours}h ${uptimeMinutes % 60}m`
223
- : `${uptimeMinutes}m`;
224
-
225
- return {
226
- type: 'builtin',
227
- action: 'status',
228
- data: {
229
- version,
230
- packageName,
231
- uptime: uptimeFormatted,
232
- uptimeSeconds: Math.floor(uptime),
233
- model: context?.model || 'claude-sonnet-4.5',
234
- provider: context?.provider || 'claude',
235
- nodeVersion: process.version,
236
- platform: process.platform
237
- }
238
- };
239
- },
240
-
241
176
  '/memory': async (args, context) => {
242
177
  const projectPath = context?.projectPath;
243
178
 
@@ -284,30 +219,6 @@ Custom commands can be created in:
284
219
  message: 'Opening settings...'
285
220
  }
286
221
  };
287
- },
288
-
289
- '/rewind': async (args, context) => {
290
- const steps = args[0] ? parseInt(args[0]) : 1;
291
-
292
- if (isNaN(steps) || steps < 1) {
293
- return {
294
- type: 'builtin',
295
- action: 'rewind',
296
- data: {
297
- error: 'Invalid steps parameter',
298
- message: 'Usage: /rewind [number] - Rewind conversation by N steps (default: 1)'
299
- }
300
- };
301
- }
302
-
303
- return {
304
- type: 'builtin',
305
- action: 'rewind',
306
- data: {
307
- steps,
308
- message: `Rewinding conversation by ${steps} step${steps > 1 ? 's' : ''}...`
309
- }
310
- };
311
222
  }
312
223
 
313
224
  };