@ian2018cs/agenthub 0.1.71 → 0.1.72
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/package.json
CHANGED
package/server/routes/agents.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import AdmZip from 'adm-zip';
|
|
7
7
|
import { getUserPaths, getPublicPaths } from '../services/user-directories.js';
|
|
8
|
-
import { scanAgents, ensureAgentRepo, incrementPatchVersion, publishAgentToRepo } from '../services/system-agent-repo.js';
|
|
8
|
+
import { scanAgents, ensureAgentRepo, incrementPatchVersion, publishAgentToRepo, invalidateAgentCache } from '../services/system-agent-repo.js';
|
|
9
9
|
import { ensureSystemRepo, SYSTEM_REPO_URL } from '../services/system-repo.js';
|
|
10
10
|
import { ensureSystemMcpRepo, SYSTEM_MCP_REPO_URL } from '../services/system-mcp-repo.js';
|
|
11
11
|
import { addProjectManually, loadProjectConfig, saveProjectConfig } from '../projects.js';
|
|
@@ -444,8 +444,7 @@ router.get('/', async (req, res) => {
|
|
|
444
444
|
*/
|
|
445
445
|
router.post('/refresh', async (_req, res) => {
|
|
446
446
|
try {
|
|
447
|
-
await
|
|
448
|
-
const agents = await scanAgents();
|
|
447
|
+
const agents = await scanAgents(true);
|
|
449
448
|
res.json({ success: true, agentCount: agents.length });
|
|
450
449
|
} catch (error) {
|
|
451
450
|
console.error('Error refreshing agent repo:', error);
|
|
@@ -1295,7 +1294,7 @@ router.post('/submissions/:id/approve', async (req, res) => {
|
|
|
1295
1294
|
if (updateAllUsers) {
|
|
1296
1295
|
// Force-update all users who have this agent installed
|
|
1297
1296
|
try {
|
|
1298
|
-
const freshAgents = await scanAgents();
|
|
1297
|
+
const freshAgents = await scanAgents(true);
|
|
1299
1298
|
const publishedAgent = freshAgents.find(a =>
|
|
1300
1299
|
a.dirName === submission.agent_name || a.name === submission.agent_name
|
|
1301
1300
|
);
|
|
@@ -7,6 +7,14 @@ export const SYSTEM_AGENT_REPO_URL = 'git@git.amberweather.com:mcp-server/hupoer
|
|
|
7
7
|
export const SYSTEM_AGENT_REPO_OWNER = 'mcp-server';
|
|
8
8
|
export const SYSTEM_AGENT_REPO_NAME = 'hupoer-agents';
|
|
9
9
|
|
|
10
|
+
// ─── In-memory cache ──────────────────────────────────────────────────────────
|
|
11
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
12
|
+
let agentCache = { agents: null, cachedAt: 0 };
|
|
13
|
+
|
|
14
|
+
export function invalidateAgentCache() {
|
|
15
|
+
agentCache = { agents: null, cachedAt: 0 };
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
function runGit(args, cwd = null) {
|
|
11
19
|
return new Promise((resolve, reject) => {
|
|
12
20
|
const opts = {
|
|
@@ -153,15 +161,23 @@ async function parseAgentYaml(agentDir) {
|
|
|
153
161
|
|
|
154
162
|
/**
|
|
155
163
|
* Scan the agent repo for available agents.
|
|
164
|
+
* @param {boolean} force - When true, always git pull and re-scan (bypasses cache).
|
|
165
|
+
* When false (default), returns cached result if < 5 min old.
|
|
156
166
|
* Returns array of agent metadata objects.
|
|
157
167
|
*/
|
|
158
|
-
export async function scanAgents() {
|
|
168
|
+
export async function scanAgents(force = false) {
|
|
169
|
+
// Return cached result if fresh and not forced
|
|
170
|
+
if (!force && agentCache.agents !== null && Date.now() - agentCache.cachedAt < CACHE_TTL_MS) {
|
|
171
|
+
return agentCache.agents;
|
|
172
|
+
}
|
|
173
|
+
|
|
159
174
|
let repoPath;
|
|
160
175
|
try {
|
|
161
176
|
repoPath = await ensureAgentRepo();
|
|
162
177
|
} catch (err) {
|
|
163
178
|
console.error('[AgentRepo] Could not access agent repo:', err.message);
|
|
164
|
-
|
|
179
|
+
// Return stale cache on network error rather than empty list
|
|
180
|
+
return agentCache.agents ?? [];
|
|
165
181
|
}
|
|
166
182
|
|
|
167
183
|
const agents = [];
|
|
@@ -195,6 +211,7 @@ export async function scanAgents() {
|
|
|
195
211
|
});
|
|
196
212
|
}
|
|
197
213
|
|
|
214
|
+
agentCache = { agents, cachedAt: Date.now() };
|
|
198
215
|
return agents;
|
|
199
216
|
}
|
|
200
217
|
|
|
@@ -7,39 +7,35 @@ const SYSTEM_MCP_REPO_URL = 'git@git.amberweather.com:mcp-server/hupoer-mcps.git
|
|
|
7
7
|
const SYSTEM_MCP_REPO_OWNER = 'mcp-server';
|
|
8
8
|
const SYSTEM_MCP_REPO_NAME = 'hupoer-mcps';
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function runGit(args, cwd) {
|
|
11
11
|
return new Promise((resolve, reject) => {
|
|
12
|
-
const gitProcess = spawn('git',
|
|
12
|
+
const gitProcess = spawn('git', args, {
|
|
13
|
+
cwd,
|
|
13
14
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
14
15
|
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }
|
|
15
16
|
});
|
|
16
|
-
|
|
17
17
|
let stderr = '';
|
|
18
18
|
gitProcess.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
19
19
|
gitProcess.on('close', (code) => {
|
|
20
20
|
if (code === 0) resolve();
|
|
21
|
-
else reject(new Error(stderr || `Git
|
|
21
|
+
else reject(new Error(stderr || `Git ${args[0]} failed with code ${code}`));
|
|
22
22
|
});
|
|
23
23
|
gitProcess.on('error', (err) => { reject(err); });
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
function updateRepository(repoPath) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
else reject(new Error(stderr || `Git pull failed with code ${code}`));
|
|
40
|
-
});
|
|
41
|
-
gitProcess.on('error', (err) => { reject(err); });
|
|
42
|
-
});
|
|
27
|
+
async function updateRepository(repoPath) {
|
|
28
|
+
try {
|
|
29
|
+
await runGit(['pull', '--ff-only'], repoPath);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.message.includes('overwritten by merge') || err.message.includes('overwritten by checkout') || err.message.includes('local changes')) {
|
|
32
|
+
console.log('[SystemMcpRepo] git pull failed due to local changes, discarding and retrying:', err.message);
|
|
33
|
+
await runGit(['checkout', '--', '.'], repoPath);
|
|
34
|
+
await runGit(['pull', '--ff-only'], repoPath);
|
|
35
|
+
} else {
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
export async function ensureSystemMcpRepo() {
|
|
@@ -58,7 +54,7 @@ export async function ensureSystemMcpRepo() {
|
|
|
58
54
|
// Not yet cloned
|
|
59
55
|
await fs.mkdir(path.dirname(publicRepoPath), { recursive: true });
|
|
60
56
|
try {
|
|
61
|
-
await
|
|
57
|
+
await runGit(['clone', '--depth', '1', SYSTEM_MCP_REPO_URL, publicRepoPath], undefined);
|
|
62
58
|
console.log('[SystemMcpRepo] Cloned system MCP repo to', publicRepoPath);
|
|
63
59
|
} catch (err) {
|
|
64
60
|
console.error('[SystemMcpRepo] Failed to clone system MCP repo:', err.message);
|
|
@@ -7,43 +7,35 @@ const SYSTEM_REPO_URL = 'git@git.amberweather.com:mcp-server/hupoer-skills.git';
|
|
|
7
7
|
const SYSTEM_REPO_OWNER = 'mcp-server';
|
|
8
8
|
const SYSTEM_REPO_NAME = 'hupoer-skills';
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function runGit(args, cwd) {
|
|
11
11
|
return new Promise((resolve, reject) => {
|
|
12
|
-
const gitProcess = spawn('git',
|
|
12
|
+
const gitProcess = spawn('git', args, {
|
|
13
|
+
cwd,
|
|
13
14
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
14
15
|
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }
|
|
15
16
|
});
|
|
16
|
-
|
|
17
17
|
let stderr = '';
|
|
18
18
|
gitProcess.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
19
|
-
|
|
20
19
|
gitProcess.on('close', (code) => {
|
|
21
20
|
if (code === 0) resolve();
|
|
22
|
-
else reject(new Error(stderr || `Git
|
|
21
|
+
else reject(new Error(stderr || `Git ${args[0]} failed with code ${code}`));
|
|
23
22
|
});
|
|
24
|
-
|
|
25
23
|
gitProcess.on('error', (err) => { reject(err); });
|
|
26
24
|
});
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
function updateRepository(repoPath) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (code === 0) resolve();
|
|
42
|
-
else reject(new Error(stderr || `Git pull failed with code ${code}`));
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
gitProcess.on('error', (err) => { reject(err); });
|
|
46
|
-
});
|
|
27
|
+
async function updateRepository(repoPath) {
|
|
28
|
+
try {
|
|
29
|
+
await runGit(['pull', '--ff-only'], repoPath);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.message.includes('overwritten by merge') || err.message.includes('overwritten by checkout') || err.message.includes('local changes')) {
|
|
32
|
+
console.log('[SystemRepo] git pull failed due to local changes, discarding and retrying:', err.message);
|
|
33
|
+
await runGit(['checkout', '--', '.'], repoPath);
|
|
34
|
+
await runGit(['pull', '--ff-only'], repoPath);
|
|
35
|
+
} else {
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
47
39
|
}
|
|
48
40
|
|
|
49
41
|
/**
|
|
@@ -67,7 +59,7 @@ export async function ensureSystemRepo() {
|
|
|
67
59
|
// Not yet cloned
|
|
68
60
|
await fs.mkdir(path.dirname(publicRepoPath), { recursive: true });
|
|
69
61
|
try {
|
|
70
|
-
await
|
|
62
|
+
await runGit(['clone', '--depth', '1', SYSTEM_REPO_URL, publicRepoPath], undefined);
|
|
71
63
|
console.log('[SystemRepo] Cloned system repo to', publicRepoPath);
|
|
72
64
|
} catch (err) {
|
|
73
65
|
console.error('[SystemRepo] Failed to clone system repo:', err.message);
|