@simonfestl/husky-cli 1.18.1 → 1.19.2
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/commands/agent.js
CHANGED
|
@@ -287,6 +287,8 @@ const ROLE_EMOJI = {
|
|
|
287
287
|
worker: "\uD83D\uDD27", // Wrench emoji for worker
|
|
288
288
|
reviewer: "\uD83D\uDCDD", // Memo emoji for reviewer
|
|
289
289
|
support: "\uD83C\uDFA7", // Headphones emoji for support
|
|
290
|
+
e2e_agent: "\uD83E\uDDEA", // Test tube emoji for e2e agent
|
|
291
|
+
pr_agent: "\uD83D\uDE80", // Rocket emoji for pr agent
|
|
290
292
|
};
|
|
291
293
|
// husky agent message
|
|
292
294
|
agentCommand
|
|
@@ -436,7 +438,7 @@ agentCommand
|
|
|
436
438
|
process.exit(1);
|
|
437
439
|
}
|
|
438
440
|
// Validate role
|
|
439
|
-
const validRoles = ["supervisor", "worker", "reviewer", "support"];
|
|
441
|
+
const validRoles = ["supervisor", "worker", "reviewer", "support", "e2e_agent", "pr_agent"];
|
|
440
442
|
if (!validRoles.includes(options.role)) {
|
|
441
443
|
console.error(`Error: Invalid role '${options.role}'.`);
|
|
442
444
|
console.error(` Valid roles: ${validRoles.join(", ")}`);
|
|
@@ -527,11 +529,11 @@ agentCommand
|
|
|
527
529
|
for (const agent of agents) {
|
|
528
530
|
const statusIcon = agent.status === "online" ? "\u2714" :
|
|
529
531
|
agent.status === "busy" ? "\u231B" : "\u2717";
|
|
530
|
-
const
|
|
531
|
-
? new Date(agent.
|
|
532
|
+
const lastSeenDisplay = agent.lastSeen
|
|
533
|
+
? new Date(agent.lastSeen).toLocaleString()
|
|
532
534
|
: "never";
|
|
533
535
|
console.log(` ${statusIcon} ${agent.emoji} ${agent.name} (${agent.id})`);
|
|
534
|
-
console.log(` Role: ${agent.role} | Status: ${agent.status} | Last seen: ${
|
|
536
|
+
console.log(` Role: ${agent.role} | Status: ${agent.status} | Last seen: ${lastSeenDisplay}`);
|
|
535
537
|
if (agent.tmuxSession) {
|
|
536
538
|
console.log(` Tmux: ${agent.tmuxSession}`);
|
|
537
539
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -318,6 +318,8 @@ authCommand
|
|
|
318
318
|
role: session.role,
|
|
319
319
|
agent: session.agent.id,
|
|
320
320
|
});
|
|
321
|
+
// Clear permissions cache so next check uses the new session role
|
|
322
|
+
clearPermissionsCache();
|
|
321
323
|
// Fetch and cache permissions for the new session role
|
|
322
324
|
await fetchAndCacheRole();
|
|
323
325
|
if (options.json) {
|
|
@@ -360,6 +362,7 @@ authCommand
|
|
|
360
362
|
return;
|
|
361
363
|
}
|
|
362
364
|
clearSessionConfig();
|
|
365
|
+
clearPermissionsCache();
|
|
363
366
|
if (options.json) {
|
|
364
367
|
console.log(JSON.stringify({ success: true, agent: session.agent }));
|
|
365
368
|
return;
|
|
@@ -452,7 +455,8 @@ authCommand
|
|
|
452
455
|
role: session.role,
|
|
453
456
|
agent: session.agent.id,
|
|
454
457
|
});
|
|
455
|
-
//
|
|
458
|
+
// Clear permissions cache and refresh for the session role
|
|
459
|
+
clearPermissionsCache();
|
|
456
460
|
await fetchAndCacheRole();
|
|
457
461
|
if (options.json) {
|
|
458
462
|
console.log(JSON.stringify({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getConfig } from "../commands/config.js";
|
|
1
|
+
import { getConfig, isSessionActive } from "../commands/config.js";
|
|
2
2
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
3
3
|
let cache = null;
|
|
4
4
|
let fetchPromise = null;
|
|
@@ -7,7 +7,16 @@ async function fetchPermissions() {
|
|
|
7
7
|
if (!config.apiUrl || !config.apiKey) {
|
|
8
8
|
throw new Error("API not configured");
|
|
9
9
|
}
|
|
10
|
+
// If there's an active session, use the session's role
|
|
11
|
+
// This ensures permissions match the logged-in agent, not the API key
|
|
12
|
+
const roleToFetch = isSessionActive() && config.sessionRole
|
|
13
|
+
? config.sessionRole
|
|
14
|
+
: undefined;
|
|
15
|
+
// Build URL - if we have a session role, request permissions for that specific role
|
|
10
16
|
const url = new URL("/api/auth/whoami", config.apiUrl);
|
|
17
|
+
if (roleToFetch) {
|
|
18
|
+
url.searchParams.set("role", roleToFetch);
|
|
19
|
+
}
|
|
11
20
|
const res = await fetch(url.toString(), {
|
|
12
21
|
method: "GET",
|
|
13
22
|
headers: {
|
|
@@ -20,16 +29,39 @@ async function fetchPermissions() {
|
|
|
20
29
|
throw new Error(error.message || error.error || `HTTP ${res.status}`);
|
|
21
30
|
}
|
|
22
31
|
const data = await res.json();
|
|
23
|
-
|
|
32
|
+
// If we have a session role, override the returned role with the session role
|
|
33
|
+
// and fetch permissions for that role
|
|
34
|
+
const effectiveRole = roleToFetch || data.role;
|
|
35
|
+
const effectivePermissions = roleToFetch
|
|
36
|
+
? await fetchPermissionsForRole(config.apiUrl, config.apiKey, roleToFetch)
|
|
37
|
+
: data.permissions;
|
|
38
|
+
const kbPermissions = effectivePermissions
|
|
24
39
|
.filter((p) => p.startsWith("kb:"))
|
|
25
40
|
.map((p) => p.replace("kb:", ""));
|
|
26
41
|
return {
|
|
27
|
-
role:
|
|
28
|
-
permissions:
|
|
42
|
+
role: effectiveRole,
|
|
43
|
+
permissions: effectivePermissions,
|
|
29
44
|
knowledgeBases: kbPermissions,
|
|
30
45
|
fetchedAt: Date.now(),
|
|
31
46
|
};
|
|
32
47
|
}
|
|
48
|
+
async function fetchPermissionsForRole(apiUrl, apiKey, role) {
|
|
49
|
+
// Use the /permissions/:role endpoint to get permissions for a specific role
|
|
50
|
+
const url = new URL(`/api/auth/permissions/${encodeURIComponent(role)}`, apiUrl);
|
|
51
|
+
const res = await fetch(url.toString(), {
|
|
52
|
+
method: "GET",
|
|
53
|
+
headers: {
|
|
54
|
+
"x-api-key": apiKey,
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
// Fall back to empty permissions if fetch fails
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const data = await res.json();
|
|
63
|
+
return data.permissions || [];
|
|
64
|
+
}
|
|
33
65
|
export async function getPermissions() {
|
|
34
66
|
const now = Date.now();
|
|
35
67
|
if (cache && now - cache.fetchedAt < CACHE_TTL_MS) {
|