@ian2018cs/agenthub 0.1.49 → 0.1.50
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/assets/{index-BSCDGWeH.js → index-D2E4W2Y9.js} +34 -34
- package/dist/assets/{index-BhhJnwtA.css → index-D3HUILHG.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/database/db.js +4 -0
- package/server/index.js +11 -1
- package/server/projects.js +39 -5
- package/server/routes/auth.js +17 -3
- package/server/routes/projects.js +27 -1
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-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-D2E4W2Y9.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-
|
|
37
|
+
<link rel="stylesheet" crossorigin href="/assets/index-D3HUILHG.css">
|
|
38
38
|
</head>
|
|
39
39
|
<body>
|
|
40
40
|
<div id="root"></div>
|
package/package.json
CHANGED
package/server/database/db.js
CHANGED
|
@@ -1175,6 +1175,10 @@ const feishuDb = {
|
|
|
1175
1175
|
db.prepare('DELETE FROM feishu_bindings WHERE feishu_open_id = ?').run(feishuOpenId);
|
|
1176
1176
|
},
|
|
1177
1177
|
|
|
1178
|
+
getBindingByUserUuid: (userUuid) => {
|
|
1179
|
+
return db.prepare('SELECT * FROM feishu_bindings WHERE user_uuid = ?').get(userUuid) || null;
|
|
1180
|
+
},
|
|
1181
|
+
|
|
1178
1182
|
getSessionState: (feishuOpenId) => {
|
|
1179
1183
|
return db.prepare('SELECT * FROM feishu_session_state WHERE feishu_open_id = ?').get(feishuOpenId) || null;
|
|
1180
1184
|
},
|
package/server/index.js
CHANGED
|
@@ -43,7 +43,7 @@ import pty from 'node-pty';
|
|
|
43
43
|
import fetch from 'node-fetch';
|
|
44
44
|
import mime from 'mime-types';
|
|
45
45
|
|
|
46
|
-
import { getProjects, getSessions, getSessionMessages, renameProject, deleteSession, deleteProject, addProjectManually, extractProjectDirectory, clearProjectDirectoryCache } from './projects.js';
|
|
46
|
+
import { getProjects, getSessions, getSessionMessages, renameProject, deleteSession, deleteProject, addProjectManually, extractProjectDirectory, clearProjectDirectoryCache, updateProjectLastActivity } from './projects.js';
|
|
47
47
|
import { queryClaudeSDK, abortClaudeSDKSession, isClaudeSDKSessionActive, getActiveClaudeSDKSessions, resolveToolApproval } from './claude-sdk.js';
|
|
48
48
|
import authRoutes from './routes/auth.js';
|
|
49
49
|
import mcpRoutes from './routes/mcp.js';
|
|
@@ -1278,6 +1278,11 @@ function handleCodexConnection(ws, userData) {
|
|
|
1278
1278
|
|
|
1279
1279
|
console.log('🟢 Codex process started with PTY, PID:', codexProcess.pid);
|
|
1280
1280
|
|
|
1281
|
+
// Update project lastActivity so it sorts correctly in the sidebar
|
|
1282
|
+
if (userUuid) {
|
|
1283
|
+
updateProjectLastActivity(projectPath, userUuid).catch(() => {});
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1281
1286
|
codexPtySessionsMap.set(ptySessionKey, {
|
|
1282
1287
|
pty: codexProcess,
|
|
1283
1288
|
ws: ws,
|
|
@@ -1513,6 +1518,11 @@ function handleGeminiConnection(ws, userData) {
|
|
|
1513
1518
|
|
|
1514
1519
|
console.log('🟢 Gemini process started with PTY, PID:', geminiProcess.pid);
|
|
1515
1520
|
|
|
1521
|
+
// Update project lastActivity so it sorts correctly in the sidebar
|
|
1522
|
+
if (userUuid) {
|
|
1523
|
+
updateProjectLastActivity(projectPath, userUuid).catch(() => {});
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1516
1526
|
geminiPtySessionsMap.set(ptySessionKey, {
|
|
1517
1527
|
pty: geminiProcess,
|
|
1518
1528
|
ws: ws,
|
package/server/projects.js
CHANGED
|
@@ -334,6 +334,8 @@ async function getProjects(userUuid) {
|
|
|
334
334
|
fullPath: actualProjectDir,
|
|
335
335
|
isCustomName: !!projectConfig.displayName,
|
|
336
336
|
isManuallyAdded: true,
|
|
337
|
+
createdAt: projectConfig.createdAt || null,
|
|
338
|
+
lastActivity: projectConfig.lastActivity || projectConfig.createdAt || null,
|
|
337
339
|
sessions: []
|
|
338
340
|
};
|
|
339
341
|
|
|
@@ -721,11 +723,19 @@ async function renameProject(projectName, newDisplayName, userUuid) {
|
|
|
721
723
|
const config = await loadProjectConfig(userUuid);
|
|
722
724
|
|
|
723
725
|
if (!newDisplayName || newDisplayName.trim() === '') {
|
|
724
|
-
// Remove
|
|
725
|
-
|
|
726
|
+
// Remove only the displayName, preserve manuallyAdded/originalPath/createdAt/lastActivity
|
|
727
|
+
if (config[projectName]) {
|
|
728
|
+
const { displayName, ...rest } = config[projectName];
|
|
729
|
+
if (Object.keys(rest).length === 0) {
|
|
730
|
+
delete config[projectName];
|
|
731
|
+
} else {
|
|
732
|
+
config[projectName] = rest;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
726
735
|
} else {
|
|
727
|
-
//
|
|
736
|
+
// Merge displayName into existing entry to preserve manuallyAdded/originalPath/createdAt/lastActivity
|
|
728
737
|
config[projectName] = {
|
|
738
|
+
...config[projectName],
|
|
729
739
|
displayName: newDisplayName.trim()
|
|
730
740
|
};
|
|
731
741
|
}
|
|
@@ -878,10 +888,14 @@ async function addProjectManually(projectPath, displayName = null, userUuid) {
|
|
|
878
888
|
// Allow adding projects even if the directory exists - this enables tracking
|
|
879
889
|
// existing Claude Code or Cursor projects in the UI
|
|
880
890
|
|
|
891
|
+
const createdAt = new Date().toISOString();
|
|
892
|
+
|
|
881
893
|
// Add to config as manually added project
|
|
882
894
|
config[projectName] = {
|
|
883
895
|
manuallyAdded: true,
|
|
884
|
-
originalPath: absolutePath
|
|
896
|
+
originalPath: absolutePath,
|
|
897
|
+
createdAt,
|
|
898
|
+
lastActivity: createdAt
|
|
885
899
|
};
|
|
886
900
|
|
|
887
901
|
if (displayName) {
|
|
@@ -897,10 +911,29 @@ async function addProjectManually(projectPath, displayName = null, userUuid) {
|
|
|
897
911
|
fullPath: absolutePath,
|
|
898
912
|
displayName: displayName || await generateDisplayName(projectName, absolutePath),
|
|
899
913
|
isManuallyAdded: true,
|
|
914
|
+
createdAt,
|
|
915
|
+
lastActivity: createdAt,
|
|
900
916
|
sessions: []
|
|
901
917
|
};
|
|
902
918
|
}
|
|
903
919
|
|
|
920
|
+
// Update lastActivity for a project by its absolute path (used by Gemini/Codex shell sessions)
|
|
921
|
+
async function updateProjectLastActivity(projectPath, userUuid) {
|
|
922
|
+
if (!userUuid || !projectPath) return;
|
|
923
|
+
try {
|
|
924
|
+
const absolutePath = path.resolve(projectPath);
|
|
925
|
+
const projectName = absolutePath.replace(/\//g, '-');
|
|
926
|
+
const config = await loadProjectConfig(userUuid);
|
|
927
|
+
if (config[projectName]) {
|
|
928
|
+
config[projectName].lastActivity = new Date().toISOString();
|
|
929
|
+
await saveProjectConfig(config, userUuid);
|
|
930
|
+
}
|
|
931
|
+
} catch (err) {
|
|
932
|
+
// Non-fatal - just log
|
|
933
|
+
console.warn('[WARN] Failed to update project lastActivity:', err.message);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
904
937
|
export {
|
|
905
938
|
getProjects,
|
|
906
939
|
getSessions,
|
|
@@ -914,5 +947,6 @@ export {
|
|
|
914
947
|
loadProjectConfig,
|
|
915
948
|
saveProjectConfig,
|
|
916
949
|
extractProjectDirectory,
|
|
917
|
-
clearProjectDirectoryCache
|
|
950
|
+
clearProjectDirectoryCache,
|
|
951
|
+
updateProjectLastActivity
|
|
918
952
|
};
|
package/server/routes/auth.js
CHANGED
|
@@ -2,7 +2,7 @@ import express from 'express';
|
|
|
2
2
|
import bcrypt from 'bcrypt';
|
|
3
3
|
import jwt from 'jsonwebtoken';
|
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
-
import { userDb, verificationDb, domainWhitelistDb, usageDb, settingsDb } from '../database/db.js';
|
|
5
|
+
import { userDb, verificationDb, domainWhitelistDb, usageDb, settingsDb, feishuDb } from '../database/db.js';
|
|
6
6
|
import { generateToken, authenticateToken, JWT_SECRET } from '../middleware/auth.js';
|
|
7
7
|
import { initUserDirectories } from '../services/user-directories.js';
|
|
8
8
|
import { initSystemRepoForUser } from '../services/system-repo.js';
|
|
@@ -285,10 +285,24 @@ router.get('/limit-status', authenticateToken, (req, res) => {
|
|
|
285
285
|
}
|
|
286
286
|
});
|
|
287
287
|
|
|
288
|
+
// Get current user's Feishu binding status
|
|
289
|
+
router.get('/feishu-status', authenticateToken, (req, res) => {
|
|
290
|
+
try {
|
|
291
|
+
const binding = feishuDb.getBindingByUserUuid(req.user.uuid);
|
|
292
|
+
if (binding) {
|
|
293
|
+
res.json({ bound: true, feishuOpenId: binding.feishu_open_id, createdAt: binding.created_at });
|
|
294
|
+
} else {
|
|
295
|
+
res.json({ bound: false });
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error('Error fetching Feishu binding status:', error);
|
|
299
|
+
res.status(500).json({ error: '查询绑定状态失败' });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
288
303
|
// Generate a short-lived token for binding a Feishu account
|
|
289
304
|
// The user opens the web UI, calls this endpoint, then sends /auth <token> in Feishu private chat
|
|
290
|
-
router.post('/feishu-bind-token', authenticateToken, (req, res) => {
|
|
291
|
-
try {
|
|
305
|
+
router.post('/feishu-bind-token', authenticateToken, (req, res) => { try {
|
|
292
306
|
const token = jwt.sign(
|
|
293
307
|
{ userId: req.user.id, uuid: req.user.uuid, email: req.user.email },
|
|
294
308
|
JWT_SECRET,
|
|
@@ -2,7 +2,7 @@ import express from 'express';
|
|
|
2
2
|
import { promises as fs } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
|
-
import { addProjectManually } from '../projects.js';
|
|
5
|
+
import { addProjectManually, loadProjectConfig } from '../projects.js';
|
|
6
6
|
import { getUserPaths } from '../services/user-directories.js';
|
|
7
7
|
|
|
8
8
|
const router = express.Router();
|
|
@@ -101,6 +101,32 @@ router.post('/create-workspace', async (req, res) => {
|
|
|
101
101
|
// Check if directory already exists
|
|
102
102
|
try {
|
|
103
103
|
await fs.access(absolutePath);
|
|
104
|
+
// Folder exists - check if it's registered in project config
|
|
105
|
+
const config = await loadProjectConfig(userUuid);
|
|
106
|
+
const projectKey = absolutePath.replace(/\//g, '-');
|
|
107
|
+
if (!config[projectKey] || !config[projectKey].manuallyAdded) {
|
|
108
|
+
// Orphan folder: registered in config without manuallyAdded, or not registered at all
|
|
109
|
+
// Re-register it so the user can find it in the sidebar
|
|
110
|
+
const project = await addProjectManually(absolutePath, null, userUuid).catch(async () => {
|
|
111
|
+
// Already configured (shouldn't happen here, but handle gracefully)
|
|
112
|
+
const existingConfig = await loadProjectConfig(userUuid);
|
|
113
|
+
const existingEntry = existingConfig[projectKey];
|
|
114
|
+
return {
|
|
115
|
+
name: projectKey,
|
|
116
|
+
path: absolutePath,
|
|
117
|
+
fullPath: absolutePath,
|
|
118
|
+
displayName: existingEntry?.displayName || absolutePath.split('/').pop(),
|
|
119
|
+
isManuallyAdded: true,
|
|
120
|
+
sessions: []
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
return res.json({
|
|
124
|
+
success: true,
|
|
125
|
+
project,
|
|
126
|
+
recovered: true,
|
|
127
|
+
message: 'Project folder already exists and has been re-registered successfully'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
104
130
|
return res.status(400).json({
|
|
105
131
|
error: 'A project with this name already exists. Please choose a different name.'
|
|
106
132
|
});
|